diff --git a/assets/blocks/reader-registration/index.php b/assets/blocks/reader-registration/index.php index ac362dd378..06bb00c284 100644 --- a/assets/blocks/reader-registration/index.php +++ b/assets/blocks/reader-registration/index.php @@ -181,7 +181,10 @@ function process_form() { } return send_form_response( - [ 'email' => $email ], + [ + 'email' => $email, + 'authenticated' => false !== $user_id, + ], false === $user_id ? __( 'Check your email for a confirmation link!', 'newspack' ) : __( 'Thank you for registering!', 'newspack' ) ); } diff --git a/assets/blocks/reader-registration/view.js b/assets/blocks/reader-registration/view.js index 05602321ff..666e856aaa 100644 --- a/assets/blocks/reader-registration/view.js +++ b/assets/blocks/reader-registration/view.js @@ -27,8 +27,9 @@ import './style.scss'; if ( status === 200 ) { container.replaceChild( messageNode, form ); if ( data?.email ) { - readerActivation.setReader( data.email ); + readerActivation.setReaderEmail( data.email ); } + readerActivation.setAuthenticated( data?.authenticated ); } else { messageElement.appendChild( messageNode ); } diff --git a/assets/reader-activation/auth.js b/assets/reader-activation/auth.js new file mode 100644 index 0000000000..e93c4d55a8 --- /dev/null +++ b/assets/reader-activation/auth.js @@ -0,0 +1,189 @@ +/** + * Internal dependencies. + */ +import './auth.scss'; + +( function ( readerActivation ) { + window.onload = function () { + if ( ! readerActivation ) { + return; + } + + const container = document.querySelector( '#newspack-reader-auth' ); + if ( ! container ) { + return; + } + + const initialForm = container.querySelector( 'form' ); + let form; + /** Workaround AMP's enforced XHR strategy. */ + if ( initialForm.getAttribute( 'action-xhr' ) ) { + initialForm.removeAttribute( 'action-xhr' ); + form = initialForm.cloneNode( true ); + initialForm.replaceWith( form ); + } else { + form = initialForm; + } + + const actionInput = form.querySelector( 'input[name="action"]' ); + const emailInput = form.querySelector( 'input[name="email"]' ); + const passwordInput = form.querySelector( 'input[name="password"]' ); + const submitButtons = form.querySelectorAll( '[type="submit"]' ); + + container.querySelector( 'button[data-close]' ).addEventListener( 'click', function ( ev ) { + ev.preventDefault(); + container.classList.remove( 'newspack-reader__auth-form__visible' ); + container.style.display = 'none'; + } ); + + const messageContentElement = container.querySelector( + '.newspack-reader__auth-form__response__content' + ); + + const authLinkMessage = container.querySelector( '[data-has-auth-link]' ); + authLinkMessage.hidden = true; + + const accountLinks = document.querySelectorAll( '.newspack-reader__account-link' ); + const triggerLinks = document.querySelectorAll( '[data-newspack-reader-account-link]' ); + + /** + * Handle reader changes. + */ + readerActivation.on( 'reader', ( { detail: { email, authenticated } } ) => { + emailInput.value = email || ''; + if ( accountLinks?.length ) { + accountLinks.forEach( link => { + try { + const labels = JSON.parse( link.getAttribute( 'data-labels' ) ); + link.querySelector( '.newspack-reader__account-link__label' ).textContent = + authenticated ? labels.signedin : labels.signedout; + } catch {} + } ); + } + if ( authenticated ) { + container.style.display = 'none'; + } + } ); + + /** + * Handle account links. + */ + function handleAccountLinkClick( ev ) { + const reader = readerActivation.getReader(); + /** If logged in, bail and allow page redirection. */ + if ( reader?.authenticated ) { + return; + } + ev.preventDefault(); + + if ( readerActivation.hasAuthLink() ) { + authLinkMessage.style.display = 'block'; + } else { + authLinkMessage.style.display = 'none'; + } + + emailInput.value = reader?.email || ''; + + container.hidden = false; + container.style.display = 'flex'; + + if ( emailInput.value && 'pwd' === actionInput.value ) { + passwordInput.focus(); + } else { + emailInput.focus(); + } + } + triggerLinks.forEach( link => { + link.addEventListener( 'click', handleAccountLinkClick ); + } ); + + /** + * Handle auth form action selection. + */ + function setFormAction( action ) { + actionInput.value = action; + container.removeAttribute( 'data-form-status' ); + messageContentElement.innerHTML = ''; + container.querySelectorAll( '[data-action]' ).forEach( item => { + if ( 'none' !== item.style.display ) { + item.prevDisplay = item.style.display; + } + item.style.display = 'none'; + } ); + container.querySelectorAll( '[data-action~="' + action + '"]' ).forEach( item => { + item.style.display = item.prevDisplay; + } ); + try { + const labels = JSON.parse( container.getAttribute( 'data-labels' ) ); + const label = 'register' === action ? labels.register : labels.signin; + container.querySelector( 'h2' ).textContent = label; + } catch {} + if ( action === 'pwd' && emailInput.value ) { + passwordInput.focus(); + } else { + emailInput.focus(); + } + } + setFormAction( actionInput.value ); + container.querySelectorAll( '[data-set-action]' ).forEach( item => { + item.addEventListener( 'click', function ( ev ) { + ev.preventDefault(); + setFormAction( ev.target.getAttribute( 'data-set-action' ) ); + } ); + } ); + + /** + * Handle auth form submission. + */ + form.addEventListener( 'submit', function ( ev ) { + ev.preventDefault(); + container.removeAttribute( 'data-form-status' ); + const body = new FormData( ev.target ); + if ( ! body.has( 'email' ) || ! body.get( 'email' ) ) { + return; + } + submitButtons.forEach( button => { + button.disabled = true; + } ); + messageContentElement.innerHTML = ''; + form.style.opacity = 0.5; + readerActivation.setReaderEmail( body.get( 'email' ) ); + fetch( form.getAttribute( 'action' ) || window.location.pathname, { + method: 'POST', + headers: { + Accept: 'application/json', + }, + body, + } ) + .then( res => { + container.setAttribute( 'data-form-status', res.status ); + res.json().then( ( { message, data } ) => { + const authenticated = !! data?.authenticated; + const messageNode = document.createElement( 'p' ); + messageNode.textContent = message; + messageContentElement.appendChild( messageNode ); + if ( res.status === 200 ) { + readerActivation.setReaderEmail( data.email ); + readerActivation.setAuthenticated( authenticated ); + if ( authenticated ) { + if ( body.get( 'redirect' ) ) { + window.location = body.get( 'redirect' ); + } + } else { + form.replaceWith( messageContentElement.parentNode ); + } + } + } ); + } ) + .catch( err => { + throw err; + } ) + .finally( () => { + form.style.opacity = 1; + submitButtons.forEach( button => { + button.disabled = false; + } ); + } ); + } ); + }; +} )( window.newspackReaderActivation ); diff --git a/assets/reader-activation/auth.scss b/assets/reader-activation/auth.scss new file mode 100644 index 0000000000..1a25fd24a9 --- /dev/null +++ b/assets/reader-activation/auth.scss @@ -0,0 +1,251 @@ +.newspack-reader { + /** + * Account Link + */ + &__account-link { + span { + display: inline-block; + } + + &__mobile { + @media screen and ( min-width: 960px ) { + display: none; + } + margin-left: 0.5rem; + } + + &__icon { + position: relative; + width: 24px; + svg { + position: absolute; + top: -17px; + left: 0; + } + } + + &__label { + margin-left: 0.2rem; + @media screen and ( max-width: 959px ) { + border: 0; + clip: rect( 1px, 1px, 1px, 1px ); + clip-path: inset( 50% ); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute !important; + width: 1px; + word-wrap: normal !important; + } + } + } + + /** + * Sign-In/Register Form + */ + &__auth-form { + display: none; + background: rgba( 0, 0, 0, 0.75 ); + position: fixed; + width: 100% !important; + top: 0; + left: 0; + right: 0; + bottom: 0; + justify-content: center; + align-items: center; + z-index: 999999; + font-size: 0.9em; + + &__visible { + display: flex !important; + } + + &[data-form-status='200'] { + .newspack-reader__auth-form { + &__response { + background: rgba( 74, 184, 102, 0.1 ); + text-align: center; + padding: 2.5rem; + margin: 1rem 0; + font-size: 1em; + &__icon[data-form-status='200'] { + display: flex; + margin: 0 auto 2rem; + border-radius: 100%; + width: 50px; + height: 50px; + justify-content: center; + align-items: center; + color: #fff; + background: #4ab866; + } + } + } + } + + &[data-form-status='400'] { + .newspack-reader__auth-form { + &__response { + width: 100%; + margin: 0; + padding: 0.5rem; + background: rgba( 217, 79, 79, 0.1 ); + display: flex; + justify-content: center; + &__icon[data-form-status='400'] { + display: flex; + flex: 1 1 0; + color: #d94f4f; + margin: 0 0.25rem 0 0; + } + &__content { + flex: 1 1 100%; + } + } + } + } + + @media screen and ( max-width: 744px ) { + align-items: flex-end; + } + + a { + color: #36f; + text-decoration: underline; + } + + p { + margin: 0.5rem 0; + &.small { + margin: 0; + font-size: 0.8em; + a { + color: inherit; + } + } + } + + &__wrapper { + width: 100%; + max-width: 544px; + background: #fff; + position: relative; + } + + &__header { + display: flex; + justify-content: space-between; + align-items: center; + h2 { + font-size: 1.3em; + } + a { + font-size: 0.8em; + color: #36f; + margin-left: 1rem; + } + @media screen and ( max-width: 744px ) { + justify-content: flex-start; + } + } + + &__content { + padding: 64px; + + @media screen and ( max-width: 744px ) { + padding: 0 5vw 5vw 5vw; + } + form { + transition: opacity 0.2s ease-in-out; + } + input[type='email'], + input[type='password'] { + width: 100%; + } + [type='submit'] { + background-color: var( --newspack-primary-color ); + } + } + + &__actions { + display: flex; + justify-content: space-between; + flex-wrap: wrap; + p:not( .small ) { + flex: 1 1 100%; + } + button { + display: block; + width: 100%; + } + } + + &__response { + font-size: 0.8em; + &__content { + p { + margin: 0; + } + } + &__icon { + display: none; + } + } + + &__close { + align-items: center; + background: white; + border: none; + border-radius: 0; + box-shadow: none; + color: inherit; + cursor: pointer; + display: flex; + font-size: inherit; + height: 36px; + justify-content: center; + margin: 0; + padding: 6px; + position: absolute; + right: 0; + top: 0; + width: 36px; + + svg { + fill: currentcolor; + flex: 0 0 24px; + } + + &:active, + &:hover { + opacity: 0.6; + } + + &:focus { + outline: 1px solid; + outline-offset: -1px; + } + } + } +} + +/** + * Hide desktop account link on mobile. + */ +ul.newspack-reader__account-menu, +li.menu-item .newspack-reader__account-link { + @media screen and ( max-width: 959px ) { + display: none !important; + } +} + +/** + * Handle header with solid background. + */ +.h-sb { + .newspack-reader__account-link a { + color: #fff; + } +} diff --git a/assets/reader-activation/index.js b/assets/reader-activation/index.js index f172654bcf..d8ded73f38 100644 --- a/assets/reader-activation/index.js +++ b/assets/reader-activation/index.js @@ -46,17 +46,6 @@ function setCookie( name, value, expirationDays = 365 ) { document.cookie = `${ name }=${ value }; expires=${ date.toUTCString() }; path=/`; } -/** - * Initialize store data. - */ -function init() { - const data = window.newspack_reader_activation_data; - const initialEmail = data?.reader_email || getCookie( data?.auth_intention_cookie ); - store.reader = initialEmail ? { email: initialEmail } : null; -} - -init(); - /** * Handling events. */ @@ -127,12 +116,27 @@ export function off( event, callback ) { * * @param {string} email Email. */ -export function setReader( email ) { - store.reader = null; +export function setReaderEmail( email ) { if ( ! email ) { return; } - store.reader = { email }; + if ( ! store.reader ) { + store.reader = {}; + } + store.reader.email = email; + emit( EVENTS.reader, store.reader ); +} + +/** + * Set whether the current reader is authenticated. + * + * @param {boolean} authenticated Whether the current reader is authenticated. Default is true. + */ +export function setAuthenticated( authenticated = true ) { + if ( ! store.reader?.email ) { + throw 'Reader email not set'; + } + store.reader.authenticated = !! authenticated; emit( EVENTS.reader, store.reader ); } @@ -145,7 +149,38 @@ export function getReader() { return store.reader; } -const readerActivation = { on, off, setReader, getReader }; +/** + * Whether the current reader has a valid email link attached to the session. + * + * @return {boolean} Whether the current reader has a valid email link attached to the session. + */ +export function hasAuthLink() { + const reader = getReader(); + const emailLinkSecret = getCookie( 'np_auth_link' ); + return !! ( reader?.email && emailLinkSecret ); +} + +/** + * Initialize store data. + */ +function init() { + const data = window.newspack_reader_activation_data; + const initialEmail = data?.authenticated_email || getCookie( 'np_auth_intention' ); + const authenticated = !! data?.authenticated_email; + store.reader = initialEmail ? { email: initialEmail, authenticated } : null; + emit( EVENTS.reader, store.reader ); +} + +init(); + +const readerActivation = { + on, + off, + setReaderEmail, + setAuthenticated, + getReader, + hasAuthLink, +}; window.newspackReaderActivation = readerActivation; const clientIDCookieName = window.newspack_reader_activation_data.cid_cookie; diff --git a/includes/class-reader-activation.php b/includes/class-reader-activation.php index 04cf632128..4aed0a58a3 100644 --- a/includes/class-reader-activation.php +++ b/includes/class-reader-activation.php @@ -16,6 +16,7 @@ final class Reader_Activation { const AUTH_INTENTION_COOKIE = 'np_auth_intention'; const SCRIPT_HANDLE = 'newspack-reader-activation'; + const AUTH_SCRIPT_HANDLE = 'newspack-reader-auth'; /** * Reader user meta keys. @@ -23,6 +24,17 @@ final class Reader_Activation { const READER = 'np_reader'; const EMAIL_VERIFIED = 'np_reader_email_verified'; + /** + * Auth form. + */ + const ACCOUNT_LINK_MENU_LOCATIONS = [ 'tertiary-menu' ]; + const AUTH_FORM_ACTION = 'reader-activation-auth-form'; + const AUTH_FORM_OPTIONS = [ + 'pwd', + 'link', + 'register', + ]; + /** * Whether the session is authenticating a newly registered reader * @@ -42,6 +54,10 @@ public static function init() { \add_action( 'resetpass_form', [ __CLASS__, 'set_reader_verified' ] ); \add_action( 'password_reset', [ __CLASS__, 'set_reader_verified' ] ); \add_action( 'auth_cookie_expiration', [ __CLASS__, 'auth_cookie_expiration' ], 10, 3 ); + \add_action( 'init', [ __CLASS__, 'setup_nav_menu' ] ); + \add_action( 'wp_footer', [ __CLASS__, 'render_auth_form' ] ); + \add_action( 'template_redirect', [ __CLASS__, 'process_auth_form' ] ); + \add_filter( 'amp_native_post_form_allowed', '__return_true' ); } } @@ -56,9 +72,9 @@ public static function enqueue_scripts() { NEWSPACK_PLUGIN_VERSION, true ); - $reader_email = ''; + $authenticated_email = ''; if ( \is_user_logged_in() && self::is_user_reader( \wp_get_current_user() ) ) { - $reader_email = \wp_get_current_user()->user_email; + $authenticated_email = \wp_get_current_user()->user_email; } \wp_localize_script( self::SCRIPT_HANDLE, @@ -67,11 +83,30 @@ public static function enqueue_scripts() { 'auth_intention_cookie' => self::AUTH_INTENTION_COOKIE, 'cid_cookie' => NEWSPACK_CLIENT_ID_COOKIE_NAME, 'nonce' => wp_create_nonce( 'wp_rest' ), - 'reader_email' => $reader_email, + 'authenticated_email' => $authenticated_email, ] ); \wp_script_add_data( self::SCRIPT_HANDLE, 'async', true ); \wp_script_add_data( self::SCRIPT_HANDLE, 'amp-plus', true ); + + /** + * Nav menu items script. + */ + \wp_enqueue_script( + self::AUTH_SCRIPT_HANDLE, + Newspack::plugin_url() . '/dist/reader-auth.js', + [ self::SCRIPT_HANDLE ], + NEWSPACK_PLUGIN_VERSION, + true + ); + \wp_script_add_data( self::AUTH_SCRIPT_HANDLE, 'async', true ); + \wp_script_add_data( self::AUTH_SCRIPT_HANDLE, 'amp-plus', true ); + \wp_enqueue_style( + self::AUTH_SCRIPT_HANDLE, + Newspack::plugin_url() . '/dist/reader-auth.css', + [], + NEWSPACK_PLUGIN_VERSION + ); } /** @@ -257,6 +292,367 @@ public static function auth_cookie_expiration( $length, $user_id, $remember ) { return $length; } + /** + * Get a BEM formatted class name. + * + * @param string ...$parts The parts of the class name. + * + * @return string The BEM formatted class name. + */ + private static function get_element_class_name( ...$parts ) { + if ( is_array( $parts[0] ) ) { + $parts = $parts[0]; + } + $parts = array_filter( $parts ); + array_unshift( $parts, 'newspack-reader' ); + return empty( $parts ) ? '' : implode( '__', $parts ); + } + + /** + * Setup nav menu hooks. + */ + public static function setup_nav_menu() { + $self = new self(); + + /** Always have location enabled for account link. */ + \add_filter( + 'has_nav_menu', + function( $has_nav_menu, $location ) { + if ( in_array( $location, self::ACCOUNT_LINK_MENU_LOCATIONS, true ) ) { + $has_nav_menu = true; + } + return $has_nav_menu; + }, + 10, + 2 + ); + + /** Fallback location to always print nav menu args */ + \add_filter( + 'wp_nav_menu_args', + function( $args ) use ( $self ) { + if ( in_array( $args['theme_location'], self::ACCOUNT_LINK_MENU_LOCATIONS, true ) ) { + $args['fallback_cb'] = function( $args ) use ( $self ) { + echo $self->nav_menu_items( '', $args ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + }; + } + return $args; + } + ); + + /** Add as menu item */ + \add_filter( 'wp_nav_menu_items', [ __CLASS__, 'nav_menu_items' ], 20, 2 ); + + /** Add mobile icon */ + \add_action( + 'newspack_header_after_mobile_toggle', + function() use ( $self ) { + ?> + + get_account_link(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> + + theme_location, self::ACCOUNT_LINK_MENU_LOCATIONS, true ) ) { + return $output; + } + + $link = self::get_account_link(); + if ( empty( $link ) ) { + return $output; + } + + $item = ''; + + if ( empty( $output ) ) { + $menu_class = sprintf( '%s %s', $args->menu_class, self::get_element_class_name( 'account-menu' ) ); + $output = sprintf( $args->items_wrap ?? '', $args->menu_id, $menu_class, $item ); + } else { + $output = $output . $item; + } + return $output; + } + + /** + * Get the account icon SVG markup. + * + * @return string The account icon SVG markup. + */ + private static function get_account_icon() { + return ''; + } + + /** + * Get account link. + * + * @return string Account link HTML or empty string. + */ + private static function get_account_link() { + $account_url = ''; + if ( function_exists( 'wc_get_account_endpoint_url' ) ) { + $account_url = \wc_get_account_endpoint_url( 'dashboard' ); + } + + /** Do not render link for authenticated readers if account page doesn't exist. */ + if ( empty( $account_url ) && \is_user_logged_in() ) { + return ''; + } + + $class = function( ...$parts ) { + array_unshift( $parts, 'account-link' ); + return self::get_element_class_name( $parts ); + }; + + $labels = [ + 'signedin' => \__( 'Account', 'newspack' ), + 'signedout' => \__( 'Sign In', 'newspack' ), + ]; + $label = \is_user_logged_in() ? 'signedin' : 'signedout'; + + $link = ''; + $link .= ''; + $link .= self::get_account_icon(); + $link .= ''; + $link .= '' . \esc_html( $labels[ $label ] ) . ''; + $link .= ''; + + /** + * Filters the HTML for the reader account link. + * + * @param string $link HTML for the reader account link. + */ + return apply_filters( 'newspack_reader_account_link', $link ); + } + + /** + * Renders reader authentication form + */ + public static function render_auth_form() { + if ( \is_user_logged_in() ) { + return; + } + + $class = function( ...$parts ) { + array_unshift( $parts, 'auth-form' ); + return self::get_element_class_name( $parts ); + }; + + $labels = [ + 'signin' => \__( 'Sign In', 'newspack' ), + 'register' => \__( 'Sign Up', 'newspack' ), + ]; + + $message = ''; + $classnames = [ $class() ]; + // phpcs:disable WordPress.Security.NonceVerification.Recommended + if ( isset( $_GET['reader_authenticated'] ) && isset( $_GET['message'] ) ) { + $message = \sanitize_text_field( $_GET['message'] ); + $classnames[] = $class( 'visible' ); + } + // phpcs:enable + ?> +
+
+ +
+
+ + +
+

+ + +
+

+ +

+

+ +

+ +

+ +

+
+

+
+
+ + + + + + +
+ +

+ +
+
+
+

+

+ +

+

+ +

+
+
+

+

+
+ . +

+
+
+

+

+ +

+
+
+
+
+
+ get_error_message() : __( 'You are authenticated!', 'newspack' ); + } + if ( \wp_is_json_request() ) { + \wp_send_json( compact( 'message', 'data' ), \is_wp_error( $data ) ? 400 : 200 ); + exit; + } elseif ( isset( $_SERVER['REQUEST_METHOD'] ) && 'POST' === $_SERVER['REQUEST_METHOD'] ) { + \wp_safe_redirect( + \add_query_arg( + [ + 'reader_authenticated' => $is_error ? '0' : '1', + 'message' => $message, + ], + $redirect_url + ) + ); + exit; + } + } + + /** + * Process reader authentication form. + */ + public static function process_auth_form() { + if ( \is_user_logged_in() ) { + return; + } + + // phpcs:disable WordPress.Security.NonceVerification.Missing + // Nonce not required for an authentication attempt. + if ( ! isset( $_POST[ self::AUTH_FORM_ACTION ] ) ) { + return; + } + $action = isset( $_POST['action'] ) ? \sanitize_text_field( $_POST['action'] ) : ''; + $email = isset( $_POST['email'] ) ? \sanitize_email( $_POST['email'] ) : ''; + $password = isset( $_POST['password'] ) ? \sanitize_text_field( $_POST['password'] ) : ''; + $redirect = isset( $_POST['redirect'] ) ? \esc_url_raw( $_POST['redirect'] ) : ''; + // phpcs:enable + + if ( ! in_array( $action, self::AUTH_FORM_OPTIONS, true ) ) { + return self::send_auth_form_response( new \WP_Error( 'invalid_request', __( 'Invalid request.', 'newspack' ) ) ); + } + + if ( $redirect && false === strpos( $redirect, home_url(), 0 ) ) { + return self::send_auth_form_response( new \WP_Error( 'invalid_request', __( 'Invalid request.', 'newspack' ) ) ); + } + + if ( empty( $email ) ) { + return self::send_auth_form_response( new \WP_Error( 'invalid_email', __( 'You must enter a valid email address.', 'newspack' ) ) ); + } + + self::set_auth_intention_cookie( $email ); + + $user = \get_user_by( 'email', $email ); + if ( ( ! $user && 'register' !== $action ) || ( $user && ! self::is_user_reader( $user ) ) ) { + return self::send_auth_form_response( new \WP_Error( 'unauthorized', __( 'Invalid account.', 'newspack' ) ) ); + } + + $payload = [ + 'email' => $email, + 'authenticated' => 0, + ]; + + switch ( $action ) { + case 'pwd': + if ( empty( $password ) ) { + return self::send_auth_form_response( new \WP_Error( 'invalid_password', __( 'You must enter a valid password.', 'newspack' ) ) ); + } + $user = \wp_authenticate( $user->user_login, $password ); + if ( \is_wp_error( $user ) ) { + return self::send_auth_form_response( new \WP_Error( 'unauthorized', __( 'Invalid credentials.', 'newspack' ) ) ); + } + $authenticated = self::set_current_reader( $user->ID ); + $payload['authenticated'] = \is_wp_error( $authenticated ) ? 0 : 1; + return self::send_auth_form_response( $payload, false, $redirect ); + case 'link': + $sent = Magic_Link::send_email( $user ); + if ( true !== $sent ) { + return self::send_auth_form_response( new \WP_Error( 'unauthorized', __( 'Invalid account.', 'newspack' ) ) ); + } + return self::send_auth_form_response( $payload, __( "We've sent you an authentication link, please check your inbox.", 'newspack' ), $redirect ); + case 'register': + $user_id = self::register_reader( $email ); + if ( false === $user_id ) { + return self::send_auth_form_response( $payload, __( "We've sent you an authentication link, please check your inbox.", 'newspack' ), $redirect ); + } + if ( \is_wp_error( $user_id ) ) { + return self::send_auth_form_response( + new \WP_Error( 'unauthorized', __( 'Unable to register your account. Try a different email.', 'newspack' ) ) + ); + } + $payload['authenticated'] = \absint( $user_id ) ? 1 : 0; + return self::send_auth_form_response( $payload, false, $redirect ); + } + } + /** * Check if current reader has its email verified. * diff --git a/webpack.config.js b/webpack.config.js index ef1e02a66d..7a80efb263 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -48,6 +48,7 @@ const webpackConfig = getBaseWebpackConfig( ...wizardsScriptFiles, blocks: path.join( __dirname, 'assets', 'blocks', 'index.js' ), 'reader-activation': path.join( __dirname, 'assets', 'reader-activation', 'index.js' ), + 'reader-auth': path.join( __dirname, 'assets', 'reader-activation', 'auth.js' ), 'reader-registration-block': path.join( __dirname, 'assets',