Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 20 additions & 11 deletions includes/class-reader-activation.php
Original file line number Diff line number Diff line change
Expand Up @@ -1093,17 +1093,7 @@ public static function register_reader( $email, $display_name = '', $authenticat
// Send a magic link to new, unverified readers to verify their email address.
$user = \get_user_by( 'id', $user_id );
if ( ! $existing_user && ! self::is_reader_verified( $user ) ) {
$redirect_to = function_exists( '\wc_get_account_endpoint_url' ) ? \wc_get_account_endpoint_url( 'dashboard' ) : '';
$blogname = \wp_specialchars_decode( \get_option( 'blogname' ), ENT_QUOTES );

$subject = __( 'Please verify your account', 'newspack' );

/* translators: %s: Site title. */
$message = sprintf( __( 'Welcome to %s!', 'newspack' ), $blogname ) . "\r\n\r\n";
$message .= __( 'To manage your account, please verify your email address by visiting the following URL:', 'newspack' ) . "\r\n\r\n";

Magic_Link::send_email( $user, $redirect_to, $subject, $message );
Logger::log( 'Sent verification email to new user ' . $email );
self::send_verification_email( $user );
}

/**
Expand All @@ -1120,6 +1110,25 @@ public static function register_reader( $email, $display_name = '', $authenticat
return $user_id;
}

/**
* Send a magic link with special messaging to verify the user.
*
* @param WP_User $user WP_User object to be verified.
*/
public static function send_verification_email( $user ) {
$redirect_to = function_exists( '\wc_get_account_endpoint_url' ) ? \wc_get_account_endpoint_url( 'dashboard' ) : '';
$blogname = \wp_specialchars_decode( \get_option( 'blogname' ), ENT_QUOTES );

$subject = __( 'Please verify your account', 'newspack' );

/* translators: %s: Site title. */
$message = sprintf( __( 'Welcome to %s!', 'newspack' ), $blogname ) . "\r\n\r\n";
$message .= __( 'To manage your account, please verify your email address by visiting the following URL:', 'newspack' ) . "\r\n\r\n";

Logger::log( 'Sending verification email to new user ' . $user->user_email );
return Magic_Link::send_email( $user, $redirect_to, $subject, $message );
}

/**
* Get value of the client ID bearing cookie.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class WooCommerce_My_Account {
const BILLING_ENDPOINT = 'billing';
const STRIPE_CUSTOMER_ID_USER_META = '_newspack_stripe_customer_id';
const RESET_PASSWORD_URL_PARAM = 'reset-password';
const SEND_MAGIC_LINK_PARAM = 'magic-link';

/**
* Cached Stripe customer ID of the current user.
Expand All @@ -38,6 +39,7 @@ public static function init() {
add_filter( 'wc_get_template', [ __CLASS__, 'wc_get_template' ], 10, 5 );
add_action( 'init', [ __CLASS__, 'add_rewrite_endpoints' ] );
add_action( 'template_redirect', [ __CLASS__, 'handle_password_reset_request' ] );
add_action( 'template_redirect', [ __CLASS__, 'handle_magic_link_request' ] );
add_action( 'template_redirect', [ __CLASS__, 'redirect_to_account_details' ] );
add_action( 'template_redirect', [ __CLASS__, 'edit_account_prevent_email_update' ] );
add_filter( 'woocommerce_save_account_details_required_fields', [ __CLASS__, 'remove_required_fields' ] );
Expand Down Expand Up @@ -65,6 +67,23 @@ public static function my_account_menu_items( $items ) {
if ( ! Donations::is_platform_stripe() ) {
return $items;
}

// Rename 'Logout' action to 'Log out', for grammatical reasons.
if ( isset( $items['customer-logout'] ) ) {
$items['customer-logout'] = __( 'Log out', 'newspack' );
}

// If the reader hasn't verified their account, only show options to verify or log out.
if ( ! self::is_user_verified() ) {
$minimum_items = [ 'edit-account', 'customer-logout' ];
foreach ( $items as $key => $label ) {
if ( ! in_array( $key, $minimum_items, true ) ) {
unset( $items[ $key ] );
}
}
return $items;
}

$default_disabled_items = [ 'dashboard', 'downloads', 'members-area', 'edit-address' ];
if ( function_exists( 'wcs_user_has_subscription' ) && ! wcs_user_has_subscription() ) {
$default_disabled_items[] = 'subscriptions';
Expand Down Expand Up @@ -96,7 +115,7 @@ function ( $order ) {
}

/**
* Handle password reset requestl.
* Handle password reset request.
*/
public static function handle_password_reset_request() {
if ( ! is_user_logged_in() ) {
Expand All @@ -108,7 +127,7 @@ public static function handle_password_reset_request() {
$is_error = false;
if ( wp_verify_nonce( $nonce, self::RESET_PASSWORD_URL_PARAM ) ) {
$result = retrieve_password( wp_get_current_user()->user_email );
$message = __( 'Password reset link sent!', 'newspack' );
$message = __( 'Please check your email inbox for instructions on how to set a new password.', 'newspack' );
if ( is_wp_error( $result ) ) {
Logger::log( 'Error resetting password: ' . $result->get_error_message() );
$message = __( 'Something went wrong.', 'newspack' );
Expand All @@ -131,16 +150,71 @@ public static function handle_password_reset_request() {
}
}

/**
* Handle magic link request.
*/
public static function handle_magic_link_request() {
if ( ! is_user_logged_in() ) {
return;
}
$nonce = filter_input( INPUT_GET, self::SEND_MAGIC_LINK_PARAM, FILTER_SANITIZE_STRING );

if ( $nonce ) {
$is_error = false;
if ( wp_verify_nonce( $nonce, self::SEND_MAGIC_LINK_PARAM ) ) {
$result = Reader_Activation::send_verification_email( wp_get_current_user() );
$message = __( 'Please check your email inbox for a link to verify your account.', 'newspack' );
if ( is_wp_error( $result ) ) {
Logger::log( 'Error sending verification email: ' . $result->get_error_message() );
$message = __( 'Something went wrong.', 'newspack' );
$is_error = true;
}
} else {
$message = __( 'Something went wrong.', 'newspack' );
$is_error = true;
}
wp_safe_redirect(
add_query_arg(
[
'message' => $message,
'is_error' => $is_error,
],
remove_query_arg( self::SEND_MAGIC_LINK_PARAM )
)
);
exit;
}
}

/**
* Check if the user is logged in and verified.
*/
public static function is_user_verified() {
// Don't lock access if Reader Activation features aren't enabled.
if ( ! Reader_Activation::is_enabled() ) {
return true;
}
// Don't lock access if the user is not a reader.
if ( \is_user_logged_in() && ! Reader_Activation::is_user_reader( wp_get_current_user() ) ) {
return true;
}

return \is_user_logged_in() && Reader_Activation::is_reader_verified( \wp_get_current_user() );
}

/**
* Redirect to "Account details" if accessing "My Account" directly.
*/
public static function redirect_to_account_details() {
if ( Donations::is_platform_stripe() && function_exists( 'wc_get_page_permalink' ) ) {
global $wp;
$current_url = home_url( $wp->request );
$my_account_page_permalink = wc_get_page_permalink( 'myaccount' );
if ( trailingslashit( $current_url ) === trailingslashit( $my_account_page_permalink ) ) {
wp_safe_redirect( '/my-account/edit-account/' );
$current_url = home_url( $wp->request );
$my_account_page_permalink = wc_get_page_permalink( 'myaccount' );
$my_account_details_permalink = wc_get_account_endpoint_url( 'edit-account' );
$is_my_account_root = trailingslashit( $current_url ) === trailingslashit( $my_account_page_permalink );
$is_my_account_details = trailingslashit( $current_url ) === trailingslashit( $my_account_details_permalink );
if ( $is_my_account_root || ( ! $is_my_account_details && is_account_page() && ! self::is_user_verified() ) ) {
wp_safe_redirect( $my_account_details_permalink );
exit;
}
}
Expand Down Expand Up @@ -233,8 +307,13 @@ public static function remove_required_fields( $required_fields ) {
*/
public static function wc_get_template( $template, $template_name ) {
if ( 'myaccount/form-edit-account.php' === $template_name ) {
global $newspack_reset_password_arg;
$newspack_reset_password_arg = self::RESET_PASSWORD_URL_PARAM;
$newspack_reset_password_arg = self::RESET_PASSWORD_URL_PARAM;
$newspack_send_magic_link_arg = self::SEND_MAGIC_LINK_PARAM;

if ( ! self::is_user_verified() ) {
return dirname( NEWSPACK_PLUGIN_FILE ) . '/includes/reader-revenue/templates/myaccount-verify.php';
}

return dirname( NEWSPACK_PLUGIN_FILE ) . '/includes/reader-revenue/templates/myaccount-edit-account.php';
}
return $template;
Expand Down
38 changes: 20 additions & 18 deletions includes/reader-revenue/templates/myaccount-edit-account.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@
* @package Newspack
*/

use \Newspack\WooCommerce_My_Account;

defined( 'ABSPATH' ) || exit;

do_action( 'woocommerce_before_edit_account_form' );
\do_action( 'woocommerce_before_edit_account_form' );

global $newspack_reset_password_arg;
$newspack_reset_password_arg = WooCommerce_My_Account::RESET_PASSWORD_URL_PARAM;

$message = false;
if ( isset( $_GET['message'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
Expand All @@ -27,46 +29,46 @@
if ( $message ) :
?>
<div class="newspack-wc-message <?php echo $is_error ? 'newspack-wc-message--error' : ''; ?>">
<p><?php echo esc_html( $message ); ?></p>
<p><?php echo \esc_html( $message ); ?></p>
</div>
<?php
endif;
?>

<form class="woocommerce-EditAccountForm edit-account" action="" method="post" <?php do_action( 'woocommerce_edit_account_form_tag' ); ?> >
<form class="woocommerce-EditAccountForm edit-account" action="" method="post" <?php \do_action( 'woocommerce_edit_account_form_tag' ); ?> >

<?php do_action( 'woocommerce_edit_account_form_start' ); ?>
<?php \do_action( 'woocommerce_edit_account_form_start' ); ?>

<p class="woocommerce-form-row woocommerce-form-row--first form-row form-row-first">
<label for="account_first_name"><?php esc_html_e( 'First name', 'newspack' ); ?>&nbsp;<span class="required">*</span></label>
<input type="text" class="woocommerce-Input woocommerce-Input--text input-text" name="account_first_name" id="account_first_name" autocomplete="given-name" value="<?php echo esc_attr( $user->first_name ); ?>" />
<label for="account_first_name"><?php \esc_html_e( 'First name', 'newspack' ); ?>&nbsp;<span class="required">*</span></label>
<input type="text" class="woocommerce-Input woocommerce-Input--text input-text" name="account_first_name" id="account_first_name" autocomplete="given-name" value="<?php echo \esc_attr( $user->first_name ); ?>" />
</p>
<p class="woocommerce-form-row woocommerce-form-row--last form-row form-row-last">
<label for="account_last_name"><?php esc_html_e( 'Last name', 'newspack' ); ?>&nbsp;<span class="required">*</span></label>
<input type="text" class="woocommerce-Input woocommerce-Input--text input-text" name="account_last_name" id="account_last_name" autocomplete="family-name" value="<?php echo esc_attr( $user->last_name ); ?>" />
<label for="account_last_name"><?php \esc_html_e( 'Last name', 'newspack' ); ?>&nbsp;<span class="required">*</span></label>
<input type="text" class="woocommerce-Input woocommerce-Input--text input-text" name="account_last_name" id="account_last_name" autocomplete="family-name" value="<?php echo \esc_attr( $user->last_name ); ?>" />
</p>
<div class="clear"></div>

<p class="woocommerce-form-row woocommerce-form-row--wide form-row form-row-wide">
<label for="account_email"><?php esc_html_e( 'Email address', 'newspack' ); ?>&nbsp;<span class="required">*</span></label>
<input type="email" disabled class="woocommerce-Input woocommerce-Input--email input-text" name="account_email" id="account_email" autocomplete="email" value="<?php echo esc_attr( $user->user_email ); ?>" />
<label for="account_email"><?php \esc_html_e( 'Email address', 'newspack' ); ?>&nbsp;<span class="required">*</span></label>
<input type="email" disabled class="woocommerce-Input woocommerce-Input--email input-text" name="account_email" id="account_email" autocomplete="email" value="<?php echo \esc_attr( $user->user_email ); ?>" />
</p>

<?php do_action( 'woocommerce_edit_account_form' ); ?>
<?php \do_action( 'woocommerce_edit_account_form' ); ?>

<p>
<?php wp_nonce_field( 'save_account_details', 'save-account-details-nonce' ); ?>
<button type="submit" class="woocommerce-Button button" name="save_account_details" value="<?php esc_attr_e( 'Save changes', 'newspack' ); ?>"><?php esc_html_e( 'Save changes', 'newspack' ); ?></button>
<?php \wp_nonce_field( 'save_account_details', 'save-account-details-nonce' ); ?>
<button type="submit" class="woocommerce-Button button" name="save_account_details" value="<?php \esc_attr_e( 'Save changes', 'newspack' ); ?>"><?php \esc_html_e( 'Save changes', 'newspack' ); ?></button>
<input type="hidden" name="action" value="save_account_details" />
</p>

<?php do_action( 'woocommerce_edit_account_form_end' ); ?>
<?php \do_action( 'woocommerce_edit_account_form_end' ); ?>
</form>

<p class="woocommerce-form-row woocommerce-form-row--wide form-row form-row-wide">
<a href="<?php echo '?' . esc_attr( $newspack_reset_password_arg ) . '=' . esc_attr( wp_create_nonce( $newspack_reset_password_arg ) ); ?>">
<?php esc_html_e( 'Email me a password reset link', 'newspack' ); ?>
<a href="<?php echo '?' . \esc_attr( $newspack_reset_password_arg ) . '=' . \esc_attr( \wp_create_nonce( $newspack_reset_password_arg ) ); ?>">
<?php \esc_html_e( 'Email me a password reset link', 'newspack' ); ?>
</a>
</p>

<?php do_action( 'woocommerce_after_edit_account_form' ); ?>
<?php \do_action( 'woocommerce_after_edit_account_form' ); ?>
66 changes: 66 additions & 0 deletions includes/reader-revenue/templates/myaccount-verify.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php
/**
* My Account page before account has been verified.
* The user will be asked to verify before they can manage account settings.
*
* @package Newspack
*/

use \Newspack\WooCommerce_My_Account;

defined( 'ABSPATH' ) || exit;

\do_action( 'woocommerce_before_edit_account_form' );

$newspack_reset_password_arg = WooCommerce_My_Account::RESET_PASSWORD_URL_PARAM;
$newspack_send_magic_link_arg = WooCommerce_My_Account::SEND_MAGIC_LINK_PARAM;

$message = false;
if ( isset( $_GET['message'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$message = sanitize_text_field( $_GET['message'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
}

$is_error = false;
if ( isset( $_GET['is_error'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$is_error = boolval( $_GET['is_error'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
}
?>

<?php
if ( $message ) :
?>
<div class="newspack-wc-message <?php echo $is_error ? 'newspack-wc-message--error' : ''; ?>">
<p><?php echo \esc_html( $message ); ?></p>
</div>
<?php
endif;

$magic_link_args = [];
$magic_link_args[ $newspack_send_magic_link_arg ] = wp_create_nonce( $newspack_send_magic_link_arg );
$magic_link_url = \add_query_arg(
$magic_link_args,
\wc_get_account_endpoint_url( 'edit-account' )
);
$reset_password_args = [];
$reset_password_args[ $newspack_reset_password_arg ] = wp_create_nonce( $newspack_reset_password_arg );
$reset_password_url = \add_query_arg(
$reset_password_args,
\wc_get_account_endpoint_url( 'edit-account' )
);
?>

<div class="newspack-verify-account-message">
<p>
<?php esc_html_e( 'You must verify your account before you can manage account settings. Verify with a link or by setting a password.', 'newspack' ); ?>
</p>
<p>
<a class="woocommerce-Button button" href="<?php echo esc_url( $magic_link_url ); ?>">
<?php esc_html_e( 'Send me a link', 'newspack' ); ?>
</a>
<a class="woocommerce-Button button" href="<?php echo esc_url( $reset_password_url ); ?>">
<?php esc_html_e( 'Set a new password', 'newspack' ); ?>
</a>
</p>
</div>

<?php \do_action( 'woocommerce_after_edit_account_form' ); ?>