LLMS_Controller_Account
LLMS_Controller_Account class.
Contents
Source Source
File: includes/forms/controllers/class.llms.controller.account.php
class LLMS_Controller_Account { /** * Constructor * * @since 3.7.0 * @since 3.10.0 Add student subscription cancellation handler. * @since 5.0.0 Add reset password link redirection handler. * * @return void */ public function __construct() { add_action( 'wp', array( $this, 'reset_password_link_redirect' ), 1 ); add_action( 'init', array( $this, 'update' ) ); add_action( 'init', array( $this, 'lost_password' ) ); add_action( 'init', array( $this, 'reset_password' ) ); add_action( 'init', array( $this, 'cancel_subscription' ) ); add_action( 'init', array( $this, 'redeem_voucher' ) ); } /** * Lets student cancel recurring access plan subscriptions from the student dashboard view order screen * * @since 3.10.0 * @since 3.19.0 Unknown. * @since 3.35.0 Sanitize `$_POST` data. * * @return void */ public function cancel_subscription() { // Invalid nonce or the form wasn't submitted. if ( ! llms_verify_nonce( '_cancel_sub_nonce', 'llms_cancel_subscription', 'POST' ) ) { return; } elseif ( empty( $_POST['order_id'] ) ) { return llms_add_notice( __( 'Something went wrong. Please try again.', 'lifterlms' ), 'error' ); } $order = llms_get_post( llms_filter_input( INPUT_POST, 'order_id', FILTER_SANITIZE_NUMBER_INT ) ); $uid = get_current_user_id(); if ( ! $order || $uid != $order->get( 'user_id' ) ) { return llms_add_notice( __( 'Something went wrong. Please try again.', 'lifterlms' ), 'error' ); } $note = __( 'Subscription cancelled by student from account page.', 'lifterlms' ); // Active subscriptions move to pending-cancel. // All other statuses are cancelled immediately. if ( 'llms-active' === $order->get( 'status' ) ) { $new_status = 'pending-cancel'; $note .= ' ' . __( 'Enrollment will be cancelled at the end of the prepaid period.', 'lifterlms' ); } else { $new_status = 'cancelled'; } $order->set_status( $new_status ); $order->add_note( $note ); /** * Action triggered after a recurring subscription is cancelled from the student dashboard by the student. * * @since 3.17.8 * * @param LLMS_Order $order The order object. * @param integer $uid The WP_User ID the student who cancelled the subscription. */ do_action( 'llms_subscription_cancelled_by_student', $order, $uid ); } /** * Handle submission of user account edit form * * @since 3.7.0 * @since 3.24.0 Unknown. * * @return void */ public function update() { if ( ! llms_verify_nonce( '_llms_update_person_nonce', 'llms_update_person' ) ) { return; } do_action( 'llms_before_user_account_update_submit' ); // No user logged in, can't update! // This shouldn't happen but let's check anyway. if ( ! get_current_user_id() ) { return llms_add_notice( __( 'Please log in and try again.', 'lifterlms' ), 'error' ); } $person_id = llms_update_user( $_POST, 'account' ); // Validation or update issues. if ( is_wp_error( $person_id ) ) { foreach ( $person_id->get_error_messages() as $msg ) { llms_add_notice( $msg, 'error' ); } return; } elseif ( ! is_numeric( $person_id ) ) { return llms_add_notice( __( 'An unknown error occurred when attempting to create an account, please try again.', 'lifterlms' ), 'error' ); } else { llms_add_notice( __( 'Your account information has been saved.', 'lifterlms' ), 'success' ); // Handle redirect. llms_redirect_and_exit( apply_filters( 'lifterlms_update_account_redirect', llms_get_endpoint_url( 'edit-account', '', llms_get_page_url( 'myaccount' ) ) ) ); } } /** * Handle form submission of the Lost Password form * * This is the form that sends a password recovery email with a link to reset the password. * * @since 3.8.0 * @since 3.9.5 Unknown. * @since 3.35.0 Sanitize `$_POST` data. * @since 3.37.17 Refactored for readability and added new hooks. * @since 4.21.3 Increase 3rd party support for WP core hooks. * @since 5.9.0 Stop using deprecated `FILTER_SANITIZE_STRING`. * * @return null|WP_Error|true `null` when nonce cannot be verified. * `WP_Error` when an error is encountered. * `true` on success. */ public function lost_password() { // Invalid nonce or the form wasn't submitted. if ( ! llms_verify_nonce( '_lost_password_nonce', 'llms_lost_password', 'POST' ) ) { return null; } /** * Fire an action immediately prior to the lost password form submission processing. * * @since 3.37.17 */ do_action( 'llms_before_lost_password_form_submit' ); $err = new WP_Error(); $user = false; $login = llms_filter_input_sanitize_string( INPUT_POST, 'llms_login' ); // Login is required. if ( empty( $login ) ) { $err->add( 'llms_pass_reset_missing_login', __( 'Enter a username or e-mail address.', 'lifterlms' ) ); } else { // Locate the user. $field = strpos( $login, '@' ) ? 'email' : 'login'; $user = get_user_by( $field, $login ); // No user found. if ( ! $user ) { $err->add( 'llms_pass_reset_invalid_login', __( 'Invalid username or e-mail address.', 'lifterlms' ) ); } } /** * Ensure 3rd parties that don't use the 2nd param of `lostpassword_post` still work with our reset functionality. * * This specifically adds support for WordFence's "max allowed password resets" under brute force protection, but * might be useful in other scenarios. */ $_POST['user_login'] = $login; /** * Fires before errors are returned from a password reset request. * * Mimics WordPress core behavior so 3rd parties don't need to add special handlers for LifterLMS * password reset flows. * * @since 3.37.17 * * @link https://developer.wordpress.org/reference/hooks/lostpassword_post/ * * @param WP_Error $err A WP_Error object containing any errors generated by using invalid credentials. * @param WP_User|false $user WP_User object if found, false if the user does not exist. */ do_action( 'lostpassword_post', $err, $user ); // If we have errors, output them and return. if ( ! empty( $err->errors ) ) { // @todo: When we can drop support for WP 5.0 and earlier we can switch to $err->has_errors(). foreach ( $err->get_error_messages() as $message ) { llms_add_notice( $message, 'error' ); } return $err; } // Set the user's password reset key. $key = get_password_reset_key( $user ); if ( is_wp_error( $key ) ) { llms_add_notice( $key->get_error_message(), 'error' ); return $key; } // Setup the email. $email = llms()->mailer()->get_email( 'reset_password', array( 'key' => $key, 'user' => $user, 'login_display' => 'email' === $field ? $user->user_email : $user->user_login, ) ); // Error generating or sending the email. if ( ! $email || ! $email->send() ) { $err->add( 'llms_pass_reset_email_failure', __( 'Unable to reset password due to an unknown error. Please try again.', 'lifterlms' ) ); llms_add_notice( $err->get_error_message(), 'error' ); return $err; } // Success. llms_add_notice( __( 'Check your e-mail for the confirmation link.', 'lifterlms' ) ); return true; } /** * Redeem a voucher from the "Redeem Voucher" endpoint of the student dashboard * * @since 4.12.0 * @since 5.9.0 Stop using deprecated `FILTER_SANITIZE_STRING`. * * @return null|true|WP_Error Returns `null` when the form hasn't been submitted, there's a nonce error, or there's no logged in user. * Returns `true` on success and an error object when an error is encountered redeeming the voucher. */ public function redeem_voucher() { if ( ! llms_verify_nonce( 'lifterlms_voucher_nonce', 'lifterlms_voucher_check' ) || ! get_current_user_id() ) { return null; } $voucher = new LLMS_Voucher(); $redeemed = $voucher->use_voucher( llms_filter_input_sanitize_string( INPUT_POST, 'llms_voucher_code' ), get_current_user_id() ); if ( is_wp_error( $redeemed ) ) { llms_add_notice( $redeemed->get_error_message(), 'error' ); return $redeemed; } llms_add_notice( __( 'Voucher redeemed successfully!', 'lifterlms' ), 'success' ); return true; } /** * Handle form submission of the Reset Password form * * This is the form that actually updates a users password. * * @since 3.8.0 * @since 3.35.0 Sanitize `$_POST` data. * @since 3.37.17 Use WP core functions in favor of their (deprecated) LifterLMS clones. * @since 4.21.0 Use `addslashes()` and `FILTER_UNSAFE_RAW` to mimic magic quotes behavior of the WP core reset flow. * @since 5.0.0 Refactored to move reset logic into it's own method. * * @return null|WP_Error|true Returns `null` for nonce errors or when the form hasn't been submitted, an error object when * errors are encountered, and `true` on success. */ public function reset_password() { $result = $this->reset_password_handler(); if ( ! $result ) { return null; } elseif ( is_wp_error( $result ) ) { llms_add_notice( implode( '<br>', $result->get_error_messages() ), 'error' ); return $result; } // Success. llms_add_notice( __( 'Your password has been updated.', 'lifterlms' ) ); llms_redirect_and_exit( add_query_arg( 'password-reset', 1, llms_get_page_url( 'myaccount' ) ) ); } /** * Handle the submission of the password reset form. * * @since 5.0.0 * @since 5.9.0 Stop using deprecated `FILTER_SANITIZE_STRING`. * * @return null|WP_Error|true Returns `null` when the nonce can't be verified, on failure a `WP_Error` object, and `true` on success. */ private function reset_password_handler() { // Invalid nonce or the form wasn't submitted. if ( ! llms_verify_nonce( '_reset_password_nonce', 'llms_reset_password' ) ) { return null; } /** * Fire an action before the user password reset form is handled. * * @since 5.0.0 */ do_action( 'llms_before_user_reset_password_submit' ); /** * Add custom validations to the password reset form. * * @since 5.0.0 * * @param WP_Error|true $valid Whether or not the submitted data is valid. Return `true` for valid data or a `WP_Error` when invalid. */ $valid = apply_filters( 'llms_validate_password_reset_form', $this->validate_password_reset( wp_unslash( $_POST ) ) ); if ( is_wp_error( $valid ) ) { return $valid; } $login = llms_filter_input_sanitize_string( INPUT_POST, 'llms_reset_login' ); $key = llms_filter_input_sanitize_string( INPUT_POST, 'llms_reset_key' ); $user = check_password_reset_key( $key, $login ); if ( is_wp_error( $user ) ) { // Error code is either "llms_password_reset_invalid_key" or "llms_password_reset_expired_key". return new WP_Error( sprintf( 'llms_password_reset_%s', $user->get_error_code() ), __( 'This password reset key is invalid or has already been used. Please reset your password again if needed.', 'lifterlms' ) ); } reset_password( $user, addslashes( llms_filter_input( INPUT_POST, 'password' ) ) ); /** * Send the WP Core admin notification when a user's password is changed via the password reset form. * * @since 3.37.17 * * @param bool $notify_admin If `true`, the admin will be notified. * @param WP_User $user User object. */ $notify_admin = apply_filters( 'llms_password_reset_send_admin_notification', true, $user ); if ( $notify_admin ) { wp_password_change_notification( $user ); } /** * Fire an action the the user's password is reset. * * @since 5.0.0 * * @param WP_User $user User object. */ do_action( 'llms_user_password_reset', $user ); return true; } /** * Automatically redirect password reset links to the password reset form page. * * Strips the `key` and `login` query string parameters and sets them in a cookie * (which is accessed later to populate the hidden fields on the reset form) and then * redirect to the password reset form. * * @since 5.0.0 * @since 5.9.0 Stop using deprecated `FILTER_SANITIZE_STRING`. * @since 6.6.0 Prevented client and server caching of the password reset form page. * * @return void */ public function reset_password_link_redirect() { if ( is_llms_account_page() && isset( $_GET['key'] ) && isset( $_GET['login'] ) ) { $user = get_user_by( 'login', wp_unslash( llms_filter_input_sanitize_string( INPUT_GET, 'login' ) ) ); $uid = $user ? $user->ID : 0; $val = sprintf( '%1$d:%2$s', $uid, wp_unslash( llms_filter_input_sanitize_string( INPUT_GET, 'key' ) ) ); ( new LLMS_Cache_Helper() )->maybe_no_cache(); llms_set_password_reset_cookie( $val ); llms_redirect_and_exit( add_query_arg( 'reset-pass', 1, llms_lostpassword_url() ) ); } } /** * Validates the password reset form. * * @since 5.0.0 * * @param array $posted_data User submitted data. * @return WP_Error|true */ protected function validate_password_reset( $posted_data ) { $err = new WP_Error(); $fields = LLMS_Person_Handler::get_password_reset_fields(); // Validate required fields. foreach ( $fields as &$field ) { $obj = new LLMS_Form_Field( $field ); $field = $obj->get_settings(); // Field is required, submittable, and wasn't posted. if ( ! empty( $field['required'] ) && ! empty( $field['name'] ) && empty( $posted_data[ $field['name'] ] ) ) { // Translators: %s = field label or id. $msg = sprintf( __( '%s is a required field.', 'lifterlms' ), isset( $field['label'] ) ? $field['label'] : $field['name'] ); $err->add( 'llms-password-reset-missing-field', $msg ); } } if ( count( $err->errors ) ) { return $err; } // If we have a password and password confirm and they don't match. if ( isset( $posted_data['password'] ) && isset( $posted_data['password_confirm'] ) && $posted_data['password'] !== $posted_data['password_confirm'] ) { $msg = __( 'The submitted passwords do must match.', 'lifterlms' ); $err->add( 'llms-passwords-must-match', $msg ); return $err; } return true; } }
Expand full source code Collapse full source code View on GitHub
Methods Methods
- __construct — Constructor
- cancel_subscription — Lets student cancel recurring access plan subscriptions from the student dashboard view order screen
- lost_password — Handle form submission of the Lost Password form
- redeem_voucher — Redeem a voucher from the "Redeem Voucher" endpoint of the student dashboard
- reset_password — Handle form submission of the Reset Password form
- reset_password_handler — Handle the submission of the password reset form.
- reset_password_link_redirect — Automatically redirect password reset links to the password reset form page.
- update — Handle submission of user account edit form
- validate_password_reset — Validates the password reset form.
Changelog Changelog
Version | Description |
---|---|
3.7.0 | |
3.37.17 | Refactored lost_password() and reset_password() methods. |
3.35.0 | Introduced. |