Make WordPress Core

Ticket #34281: 34281.11.diff

File 34281.11.diff, 27.8 KB (added by adamsilverstein, 3 years ago)
  • src/js/_enqueues/admin/user-profile.js

    diff --git src/js/_enqueues/admin/user-profile.js src/js/_enqueues/admin/user-profile.js
    index df17620f48..25252bda18 100644
     
    9191                });
    9292        }
    9393
     94        /**
     95         * Handle the password reset button. Sets up an ajax callback to trigger sending
     96         * a password reset email.
     97         */
     98        function bindPasswordRestLink() {
     99                $( '#generate-reset-link' ).on( 'click', function() {
     100                        var $this  = $(this),
     101                                data = {
     102                                        'user_id': userProfileL10n.user_id, // The user to send a reset to.
     103                                        'nonce':   userProfileL10n.nonce    // Nonce to validate the action.
     104                                };
     105
     106                                // Remove any previous error messages.
     107                                $this.parent().find( '.notice-error' ).remove();
     108
     109                                // Send the reset request.
     110                                var resetAction =  wp.ajax.post( 'send-password-reset', data );
     111
     112                                // Handle reset success.
     113                                resetAction.done( function( response ) {
     114                                        addInlineNotice( $this, true, response );
     115                                } );
     116
     117                                // Handle reset failure.
     118                                resetAction.fail( function( response ) {
     119                                        addInlineNotice( $this, false, response );
     120                                } );
     121
     122                });
     123
     124        }
     125
     126        /**
     127         * Helper function to insert an inline notice of success or failure.
     128         *
     129         * @param {jQuery Object} $this   The button element: the message will be inserted
     130         *                                above this button
     131         * @param {bool}          success Whether the message is a success message.
     132         * @param {string}        message The message to insert.
     133         */
     134        function addInlineNotice( $this, success, message ) {
     135                var resultDiv = $( '<div />' );
     136
     137                // Set up the notice div.
     138                resultDiv.addClass( 'notice inline' );
     139
     140                // Add a class indicating success or failure.
     141                resultDiv.addClass( 'notice-' + ( success ? 'success' : 'error' ) );
     142
     143                // Add the message, wrapping in a p tag, with a fadein to highlight each message.
     144                resultDiv.text( $( $.parseHTML( message ) ).text() ).wrapInner( '<p />');
     145
     146                // Disable the button when the callback has succeeded.
     147                $this.prop( 'disabled', success );
     148
     149                // Remove any previous notices.
     150                $this.siblings( '.notice' ).remove();
     151
     152                // Insert the notice.
     153                $this.before( resultDiv );
     154        }
     155
    94156        function bindPasswordForm() {
    95157                var $generateButton,
    96158                        $cancelButton;
     
    369431                });
    370432
    371433                bindPasswordForm();
     434                bindPasswordRestLink();
    372435        });
    373436
    374437        $( '#destroy-sessions' ).on( 'click', function( e ) {
  • src/wp-admin/admin-ajax.php

    diff --git src/wp-admin/admin-ajax.php src/wp-admin/admin-ajax.php
    index 144facf7fe..30e9a414b5 100644
    $core_actions_post = array( 
    140140        'health-check-loopback-requests',
    141141        'health-check-get-sizes',
    142142        'toggle-auto-updates',
     143        'send-password-reset',
    143144);
    144145
    145146// Deprecated.
  • src/wp-admin/includes/ajax-actions.php

    diff --git src/wp-admin/includes/ajax-actions.php src/wp-admin/includes/ajax-actions.php
    index 6ff2d2d9fd..1a9f1c65c6 100644
    function wp_ajax_toggle_auto_updates() { 
    53985398
    53995399        wp_send_json_success();
    54005400}
     5401
     5402/**
     5403 * Ajax handler sends a password reset link.
     5404 *
     5405 * @since 5.4.0
     5406 */
     5407function wp_ajax_send_password_reset() {
     5408
     5409        // Validate the nonce for this action.
     5410        $user_id = isset( $_POST['user_id'] ) ? (int) $_POST['user_id'] : 0;
     5411        check_ajax_referer( 'reset-password-for-' . $user_id, 'nonce' );
     5412
     5413        // Verify user capabilities.
     5414        if ( ! current_user_can( 'edit_user', $user_id ) ) {
     5415                wp_send_json_error( __( 'Cannot send password reset, permission denied.' ) );
     5416        }
     5417
     5418        // Send the password reset link.
     5419        $user    = get_userdata( $user_id );
     5420        $results = retrieve_password( $user->user_login );
     5421
     5422        if ( true === $results ) {
     5423                wp_send_json_success(
     5424                        /* translators: 1: User's display name. */
     5425                        sprintf( __( 'A password reset link was emailed to %s.' ), $user->display_name )
     5426                );
     5427        } else {
     5428                wp_send_json_error( $results );
     5429        }
     5430}
     5431 No newline at end of file
  • src/wp-admin/includes/class-wp-users-list-table.php

    diff --git src/wp-admin/includes/class-wp-users-list-table.php src/wp-admin/includes/class-wp-users-list-table.php
    index 7e1e79f500..dff630dc05 100644
    class WP_Users_List_Table extends WP_List_Table { 
    274274                        }
    275275                }
    276276
     277                // Add a password reset link to the bulk actions dropdown.
     278                if ( current_user_can( 'edit_users' ) ) {
     279                        $actions['resetpassword'] = __( 'Send password reset' );
     280                }
     281
    277282                return $actions;
    278283        }
    279284
    class WP_Users_List_Table extends WP_List_Table { 
    450455                                $edit = "<strong>{$user_object->user_login}{$super_admin}</strong><br />";
    451456                        }
    452457
    453                         if ( ! is_multisite() && get_current_user_id() != $user_object->ID && current_user_can( 'delete_user', $user_object->ID ) ) {
     458                        if ( ! is_multisite() && get_current_user_id() !== $user_object->ID && current_user_can( 'delete_user', $user_object->ID ) ) {
    454459                                $actions['delete'] = "<a class='submitdelete' href='" . wp_nonce_url( "users.php?action=delete&amp;user=$user_object->ID", 'bulk-users' ) . "'>" . __( 'Delete' ) . '</a>';
    455460                        }
    456461                        if ( is_multisite() && current_user_can( 'remove_user', $user_object->ID ) ) {
    class WP_Users_List_Table extends WP_List_Table { 
    469474                                );
    470475                        }
    471476
     477                        // Add a link to send the user a reset password link by email.
     478                        if ( get_current_user_id() !== $user_object->ID && current_user_can( 'edit_user', $user_object->ID ) ) {
     479                                $actions['resetpassword'] = "<a class='resetpassword' href='" . wp_nonce_url( "users.php?action=resetpassword&amp;users=$user_object->ID", 'bulk-users' ) . "'>" . __( 'Send password reset' ) . '</a>';
     480                        }
     481
    472482                        /**
    473483                         * Filters the action links displayed under each user in the Users list table.
    474484                         *
  • src/wp-admin/user-edit.php

    diff --git src/wp-admin/user-edit.php src/wp-admin/user-edit.php
    index b09f14fac5..2c0c7b0a32 100644
    wp_reset_vars( array( 'action', 'user_id', 'wp_http_referer' ) ); 
    1414$user_id      = (int) $user_id;
    1515$current_user = wp_get_current_user();
    1616if ( ! defined( 'IS_PROFILE_PAGE' ) ) {
    17         define( 'IS_PROFILE_PAGE', ( $user_id == $current_user->ID ) );
     17        define( 'IS_PROFILE_PAGE', ( $user_id === $current_user->ID ) );
    1818}
    1919
    2020if ( ! $user_id && IS_PROFILE_PAGE ) {
    $user_can_edit = current_user_can( 'edit_posts' ) || current_user_can( 'edit_pag 
    9191 */
    9292if ( is_multisite()
    9393        && ! current_user_can( 'manage_network_users' )
    94         && $user_id != $current_user->ID
     94        && $user_id !== $current_user->ID
    9595        && ! apply_filters( 'enable_edit_any_user_configuration', true )
    9696) {
    9797        wp_die( __( 'Sorry, you are not allowed to edit this user.' ) );
    switch ( $action ) { 
    164164                $errors = edit_user( $user_id );
    165165
    166166                // Grant or revoke super admin status if requested.
    167                 if ( is_multisite() && is_network_admin() && ! IS_PROFILE_PAGE && current_user_can( 'manage_network_options' ) && ! isset( $super_admins ) && empty( $_POST['super_admin'] ) == is_super_admin( $user_id ) ) {
     167                if ( is_multisite() && is_network_admin() && ! IS_PROFILE_PAGE && current_user_can( 'manage_network_options' ) && ! isset( $super_admins ) && empty( $_POST['super_admin'] ) === is_super_admin( $user_id ) ) {
    168168                        empty( $_POST['super_admin'] ) ? revoke_super_admin( $user_id ) : grant_super_admin( $user_id );
    169169                }
    170170
    endif; 
    506506                <th><label for="email"><?php _e( 'Email' ); ?> <span class="description"><?php _e( '(required)' ); ?></span></label></th>
    507507                <td><input type="email" name="email" id="email" aria-describedby="email-description" value="<?php echo esc_attr( $profileuser->user_email ); ?>" class="regular-text ltr" />
    508508                <?php
    509                 if ( $profileuser->ID == $current_user->ID ) :
     509                if ( $profileuser->ID === $current_user->ID ) :
    510510                        ?>
    511511                <p class="description" id="email-description">
    512512                        <?php _e( 'If you change this, we will send you an email at your new address to confirm it. <strong>The new address will not become active until confirmed.</strong>' ); ?>
    endif; 
    515515                endif;
    516516
    517517                $new_email = get_user_meta( $current_user->ID, '_new_email', true );
    518                 if ( $new_email && $new_email['newemail'] != $current_user->user_email && $profileuser->ID == $current_user->ID ) :
     518                if ( $new_email && $new_email['newemail'] !== $current_user->user_email && $profileuser->ID === $current_user->ID ) :
    519519                        ?>
    520520                <div class="updated inline">
    521521                <p>
    endif; 
    609609        </td>
    610610</tr>
    611611<?php endif; ?>
     612                <?php
     613                // Allow admins to send reset password link
     614                if ( ! IS_PROFILE_PAGE ) :
     615                        ?>
     616        <tr class="user-sessions-wrap hide-if-no-js">
     617                <th><?php _e( 'Password Reset' ); ?></th>
     618                <td>
     619                        <div class="generate-reset-link">
     620                                <button type="button" class="button button-secondary" id="generate-reset-link">
     621                                        <?php _e( 'Send Reset Link' ); ?>
     622                                </button>
     623                        </div>
     624                        <p class="description">
     625                                <?php
     626                                /* translators: 1: User's display name. */
     627                                printf( __( 'Send %s a link to reset their password. This will not change their password, nor will it force a change.' ), esc_html( $profileuser->display_name ) );
     628                                ?>
     629                        </p>
     630                </td>
     631        </tr>
     632                <?php endif; ?>
    612633
    613634                <?php
    614635                /**
    endif; 
    827848                        $output = '';
    828849                        foreach ( $profileuser->caps as $cap => $value ) {
    829850                                if ( ! $wp_roles->is_role( $cap ) ) {
    830                                         if ( '' != $output ) {
     851                                        if ( '' !== $output ) {
    831852                                                $output .= ', ';
    832853                                        }
    833854
  • src/wp-admin/users.php

    diff --git src/wp-admin/users.php src/wp-admin/users.php
    index e8f006b9f4..95bfe8c75c 100644
    switch ( $wp_list_table->current_action() ) { 
    128128                        }
    129129
    130130                        // The new role of the current user must also have the promote_users cap or be a multisite super admin.
    131                         if ( $id == $current_user->ID && ! $wp_roles->role_objects[ $role ]->has_cap( 'promote_users' )
     131                        if ( $id === $current_user->ID && ! $wp_roles->role_objects[ $role ]->has_cap( 'promote_users' )
    132132                        && ! ( is_multisite() && current_user_can( 'manage_network_users' ) ) ) {
    133133                                        $update = 'err_admin_role';
    134134                                        continue;
    switch ( $wp_list_table->current_action() ) { 
    183183                                wp_die( __( 'Sorry, you are not allowed to delete that user.' ), 403 );
    184184                        }
    185185
    186                         if ( $id == $current_user->ID ) {
     186                        if ( $id === $current_user->ID ) {
    187187                                $update = 'err_admin_del';
    188188                                continue;
    189189                        }
    switch ( $wp_list_table->current_action() ) { 
    208208                wp_redirect( $redirect );
    209209                exit;
    210210
     211        case 'resetpassword':
     212                check_admin_referer( 'bulk-users' );
     213                if ( ! current_user_can( 'edit_users' ) ) {
     214                        $errors = new WP_Error( 'edit_users', __( 'You can&#8217;t edit users.' ) );
     215                }
     216                if ( empty( $_REQUEST['users'] ) ) {
     217                        wp_redirect( $redirect );
     218                        exit();
     219                }
     220                $userids = array_map( 'intval', (array) $_REQUEST['users'] );
     221
     222                $reset_count = 0;
     223
     224                foreach ( $userids as $id ) {
     225                        if ( ! current_user_can( 'edit_user', $id ) ) {
     226                                wp_die( __( 'You can&#8217;t edit that user.' ) );
     227                        }
     228
     229                        if ( $id === $current_user->ID ) {
     230                                $update = 'err_admin_reset';
     231                                continue;
     232                        }
     233
     234                        // Send the password reset link.
     235                        $user = get_userdata( $id );
     236                        if ( retrieve_password( $user->user_login ) ) {
     237                                ++$reset_count;
     238                        }
     239                }
     240
     241                $redirect = add_query_arg(
     242                        array(
     243                                'reset_count' => $reset_count,
     244                                'update'      => 'resetpassword',
     245                        ),
     246                        $redirect
     247                );
     248                wp_redirect( $redirect );
     249                exit;
     250
    211251        case 'delete':
    212252                if ( is_multisite() ) {
    213253                        wp_die( __( 'User deletion is not allowed from this screen.' ), 400 );
    switch ( $wp_list_table->current_action() ) { 
    248288                $users_have_content = (bool) apply_filters( 'users_have_additional_content', false, $userids );
    249289
    250290                if ( $userids && ! $users_have_content ) {
    251                         if ( $wpdb->get_var( "SELECT ID FROM {$wpdb->posts} WHERE post_author IN( " . implode( ',', $userids ) . ' ) LIMIT 1' ) ) {
     291                        if ( $wpdb->get_var( "SELECT ID FROM {$wpdb->posts} WHERE post_author IN( " . implode( ',', $userids ) . ' ) LIMIT 1' ) ) { // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
    252292                                $users_have_content = true;
    253                         } elseif ( $wpdb->get_var( "SELECT link_id FROM {$wpdb->links} WHERE link_owner IN( " . implode( ',', $userids ) . ' ) LIMIT 1' ) ) {
     293                        } elseif ( $wpdb->get_var( "SELECT link_id FROM {$wpdb->links} WHERE link_owner IN( " . implode( ',', $userids ) . ' ) LIMIT 1' ) ) { // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
    254294                                $users_have_content = true;
    255295                        }
    256296                }
    switch ( $wp_list_table->current_action() ) { 
    284324                $go_delete = 0;
    285325                foreach ( $all_userids as $id ) {
    286326                        $user = get_userdata( $id );
    287                         if ( $id == $current_user->ID ) {
     327                        if ( $id === $current_user->ID ) {
    288328                                /* translators: 1: User ID, 2: User login. */
    289329                                echo '<li>' . sprintf( __( 'ID #%1$s: %2$s <strong>The current user will not be deleted.</strong>' ), $id, $user->user_login ) . "</li>\n";
    290330                        } else {
    switch ( $wp_list_table->current_action() ) { 
    302342                                ?>
    303343                        <input type="hidden" name="delete_option" value="delete" />
    304344                        <?php else : ?>
    305                                 <?php if ( 1 == $go_delete ) : ?>
     345                                <?php if ( 1 === $go_delete ) : ?>
    306346                        <fieldset><p><legend><?php _e( 'What should be done with content owned by this user?' ); ?></legend></p>
    307347                <?php else : ?>
    308348                        <fieldset><p><legend><?php _e( 'What should be done with content owned by these users?' ); ?></legend></p>
    switch ( $wp_list_table->current_action() ) { 
    478518                                case 'del':
    479519                                case 'del_many':
    480520                                        $delete_count = isset( $_GET['delete_count'] ) ? (int) $_GET['delete_count'] : 0;
    481                                         if ( 1 == $delete_count ) {
     521                                        if ( 1 === $delete_count ) {
    482522                                                $message = __( 'User deleted.' );
    483523                                        } else {
    484524                                                /* translators: %s: Number of users. */
    switch ( $wp_list_table->current_action() ) { 
    504544                                                );
    505545                                        }
    506546
     547                                        $messages[] = '<div id="message" class="updated notice is-dismissible"><p>' . $message . '</p></div>';
     548                                        break;
     549                                case 'resetpassword':
     550                                        $reset_count = isset( $_GET['reset_count'] ) ? (int) $_GET['reset_count'] : 0;
     551                                        if ( 1 === $reset_count ) {
     552                                                $message = __( 'Password reset link sent.' );
     553                                        } else {
     554                                                /* translators: %s: Number of users. */
     555                                                $message = sprintf( __( 'Password reset links sent to %s users.' ), $reset_count );
     556                                        }
    507557                                        $messages[] = '<div id="message" class="updated notice is-dismissible"><p>' . $message . '</p></div>';
    508558                                        break;
    509559                                case 'promote':
  • src/wp-includes/functions.php

    diff --git src/wp-includes/functions.php src/wp-includes/functions.php
    index 536f195b14..f3bc6119e7 100644
    function is_php_version_compatible( $required ) { 
    77817781function wp_fuzzy_number_match( $expected, $actual, $precision = 1 ) {
    77827782        return abs( (float) $expected - (float) $actual ) <= $precision;
    77837783}
     7784
     7785
     7786/**
     7787 * Handles sending password retrieval email to user.
     7788 *
     7789 * @since 2.5.0
     7790 * @since 5.7.0 Added `$user_login` parameter.
     7791 *
     7792 * Note: prior to 5.7.0 this function was in wp_login.php.
     7793 *
     7794 * @global wpdb         $wpdb       WordPress database abstraction object.
     7795 * @global PasswordHash $wp_hasher  Portable PHP password hashing framework.
     7796 * @param  string       $user_login Optional user_login, default null. Uses
     7797 *                      $_POST['user_login'] if $user_login not set.
     7798 *
     7799 * @return bool|WP_Error True: when finish. WP_Error on error
     7800 */
     7801function retrieve_password( $user_login = null ) {
     7802        $errors    = new WP_Error();
     7803        $user_data = false;
     7804
     7805
     7806        // Use the passed $user_login if available, otherwise use $_POST['user_login'].
     7807        if ( !$user_login && !empty( $_POST['user_login'] ) ) {
     7808                $user_login = $_POST['user_login'];
     7809        }
     7810
     7811        if ( empty( $user_login ) ) {
     7812                        $errors->add('empty_username', __('<strong>ERROR</strong>: Enter a username or email address.'));
     7813        } elseif ( strpos( $user_login, '@' ) ) {
     7814                        $user_data = get_user_by( 'email', sanitize_email( $user_login ) );
     7815                if ( empty( $user_data ) )
     7816                        $errors->add('invalid_email', __('<strong>ERROR</strong>: There is no user registered with that email address.'));
     7817        } else {
     7818                        $user_data = get_user_by('login', sanitize_user( $user_login ) );
     7819        }
     7820
     7821        /**
     7822         * Filters the user data during a password reset request.
     7823         *
     7824         * Allows, for example, custom validation using data other than username or email address.
     7825         *
     7826         * @since 5.7.0
     7827         *
     7828         * @param WP_User|false $user_data WP_User object if found, false if the user does not exist.
     7829         * @param WP_Error      $errors    A WP_Error object containing any errors generated
     7830         *                                 by using invalid credentials.
     7831         */
     7832        $user_data = apply_filters( 'lostpassword_user_data', $user_data, $errors );
     7833
     7834        /**
     7835         * Fires before errors are returned from a password reset request.
     7836         *
     7837         * @since 2.1.0
     7838         * @since 4.4.0 Added the `$errors` parameter.
     7839         * @since 5.4.0 Added the `$user_data` parameter.
     7840         *
     7841         * @param WP_Error      $errors    A WP_Error object containing any errors generated
     7842         *                                 by using invalid credentials.
     7843         * @param WP_User|false $user_data WP_User object if found, false if the user does not exist.
     7844         */
     7845        do_action( 'lostpassword_post', $errors, $user_data );
     7846
     7847        /**
     7848         * Filters the errors encountered on a password reset request.
     7849         *
     7850         * The filtered WP_Error object may, for example, contain errors for an invalid
     7851         * username or email address. A WP_Error object should always be returned,
     7852         * but may or may not contain errors.
     7853         *
     7854         * If any errors are present in $errors, this will abort the password reset request.
     7855         *
     7856         * @since 5.5.0
     7857         *
     7858         * @param WP_Error      $errors    A WP_Error object containing any errors generated
     7859         *                                 by using invalid credentials.
     7860         * @param WP_User|false $user_data WP_User object if found, false if the user does not exist.
     7861         */
     7862        $errors = apply_filters( 'lostpassword_errors', $errors, $user_data );
     7863
     7864        if ( $errors->has_errors() ) {
     7865                return $errors;
     7866        }
     7867
     7868        if ( ! $user_data ) {
     7869                $errors->add( 'invalidcombo', __( '<strong>Error</strong>: There is no account with that username or email address.' ) );
     7870                return $errors;
     7871        }
     7872
     7873        // Redefining user_login ensures we return the right case in the email.
     7874        $user_login = $user_data->user_login;
     7875        $user_email = $user_data->user_email;
     7876        $key        = get_password_reset_key( $user_data );
     7877
     7878        if ( is_wp_error( $key ) ) {
     7879                return $key;
     7880        }
     7881
     7882        if ( is_multisite() ) {
     7883                $site_name = get_network()->site_name;
     7884        } else {
     7885                /*
     7886                 * The blogname option is escaped with esc_html on the way into the database
     7887                 * in sanitize_option. We want to reverse this for the plain text arena of emails.
     7888                 */
     7889                $site_name = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
     7890        }
     7891
     7892        $message = __( 'Someone has requested a password reset for the following account:' ) . "\r\n\r\n";
     7893        /* translators: %s: Site name. */
     7894        $message .= sprintf( __( 'Site Name: %s' ), $site_name ) . "\r\n\r\n";
     7895        /* translators: %s: User login. */
     7896        $message .= sprintf( __( 'Username: %s' ), $user_login ) . "\r\n\r\n";
     7897        $message .= __( 'If this was a mistake, ignore this email and nothing will happen.' ) . "\r\n\r\n";
     7898        $message .= __( 'To reset your password, visit the following address:' ) . "\r\n\r\n";
     7899        $message .= network_site_url( "wp-login.php?action=rp&key=$key&login=" . rawurlencode( $user_login ), 'login' ) . "\r\n\r\n";
     7900
     7901        $requester_ip = $_SERVER['REMOTE_ADDR'];
     7902        if ( $requester_ip ) {
     7903                $message .= sprintf(
     7904                        /* translators: %s: IP address of password reset requester. */
     7905                        __( 'This password reset request originated from the IP address %s.' ),
     7906                        $requester_ip
     7907                ) . "\r\n";
     7908        }
     7909
     7910        /* translators: Password reset notification email subject. %s: Site title. */
     7911        $title = sprintf( __( '[%s] Password Reset' ), $site_name );
     7912
     7913        /**
     7914         * Filters the subject of the password reset email.
     7915         *
     7916         * @since 2.8.0
     7917         * @since 4.4.0 Added the `$user_login` and `$user_data` parameters.
     7918         *
     7919         * @param string  $title      Email subject.
     7920         * @param string  $user_login The username for the user.
     7921         * @param WP_User $user_data  WP_User object.
     7922         */
     7923        $title = apply_filters( 'retrieve_password_title', $title, $user_login, $user_data );
     7924
     7925        /**
     7926         * Filters the message body of the password reset mail.
     7927         *
     7928         * If the filtered message is empty, the password reset email will not be sent.
     7929         *
     7930         * @since 2.8.0
     7931         * @since 4.1.0 Added `$user_login` and `$user_data` parameters.
     7932         *
     7933         * @param string  $message    Email message.
     7934         * @param string  $key        The activation key.
     7935         * @param string  $user_login The username for the user.
     7936         * @param WP_User $user_data  WP_User object.
     7937         */
     7938        $message = apply_filters( 'retrieve_password_message', $message, $key, $user_login, $user_data );
     7939
     7940        if ( $message && ! wp_mail( $user_email, wp_specialchars_decode( $title ), $message ) ) {
     7941                $errors->add(
     7942                        'retrieve_password_email_failure',
     7943                        sprintf(
     7944                                /* translators: %s: Documentation URL. */
     7945                                __( '<strong>Error</strong>: The email could not be sent. Your site may not be correctly configured to send emails. <a href="%s">Get support for resetting your password</a>.' ),
     7946                                esc_url( __( 'https://wordpress.org/support/article/resetting-your-password/' ) )
     7947                        )
     7948                );
     7949                return $errors;
     7950        }
     7951
     7952        return true;
     7953}
  • src/wp-includes/script-loader.php

    diff --git src/wp-includes/script-loader.php src/wp-includes/script-loader.php
    index 59fef34afa..9d9fe39825 100644
    function wp_default_scripts( $scripts ) { 
    10781078
    10791079        $scripts->add( 'user-profile', "/wp-admin/js/user-profile$suffix.js", array( 'jquery', 'password-strength-meter', 'wp-util' ), false, 1 );
    10801080        $scripts->set_translations( 'user-profile' );
     1081        $user_id = isset( $_GET['user_id'] ) ? (int) $_GET['user_id'] : 0;
     1082        did_action( 'init' ) && $scripts->localize(
     1083                'user-profile',
     1084                'userProfileL10n',
     1085                array(
     1086                        'user_id'  => $user_id,
     1087                        'nonce'    => wp_create_nonce( 'reset-password-for-' . $user_id ),
     1088                )
     1089        );
    10811090
    10821091        $scripts->add( 'language-chooser', "/wp-admin/js/language-chooser$suffix.js", array( 'jquery' ), false, 1 );
    10831092
  • src/wp-login.php

    diff --git src/wp-login.php src/wp-login.php
    index 75c65865d9..128e3b254c 100644
    function wp_login_viewport_meta() { 
    360360        <?php
    361361}
    362362
    363 /**
    364  * Handles sending a password retrieval email to a user.
    365  *
    366  * @since 2.5.0
    367  *
    368  * @return true|WP_Error True when finished, WP_Error object on error.
    369  */
    370 function retrieve_password() {
    371         $errors    = new WP_Error();
    372         $user_data = false;
    373 
    374         if ( empty( $_POST['user_login'] ) || ! is_string( $_POST['user_login'] ) ) {
    375                 $errors->add( 'empty_username', __( '<strong>Error</strong>: Please enter a username or email address.' ) );
    376         } elseif ( strpos( $_POST['user_login'], '@' ) ) {
    377                 $user_data = get_user_by( 'email', trim( wp_unslash( $_POST['user_login'] ) ) );
    378                 if ( empty( $user_data ) ) {
    379                         $errors->add( 'invalid_email', __( '<strong>Error</strong>: There is no account with that username or email address.' ) );
    380                 }
    381         } else {
    382                 $login     = trim( wp_unslash( $_POST['user_login'] ) );
    383                 $user_data = get_user_by( 'login', $login );
    384         }
    385 
    386         /**
    387          * Filters the user data during a password reset request.
    388          *
    389          * Allows, for example, custom validation using data other than username or email address.
    390          *
    391          * @since 5.7.0
    392          *
    393          * @param WP_User|false $user_data WP_User object if found, false if the user does not exist.
    394          * @param WP_Error      $errors    A WP_Error object containing any errors generated
    395          *                                 by using invalid credentials.
    396          */
    397         $user_data = apply_filters( 'lostpassword_user_data', $user_data, $errors );
    398 
    399         /**
    400          * Fires before errors are returned from a password reset request.
    401          *
    402          * @since 2.1.0
    403          * @since 4.4.0 Added the `$errors` parameter.
    404          * @since 5.4.0 Added the `$user_data` parameter.
    405          *
    406          * @param WP_Error      $errors    A WP_Error object containing any errors generated
    407          *                                 by using invalid credentials.
    408          * @param WP_User|false $user_data WP_User object if found, false if the user does not exist.
    409          */
    410         do_action( 'lostpassword_post', $errors, $user_data );
    411 
    412         /**
    413          * Filters the errors encountered on a password reset request.
    414          *
    415          * The filtered WP_Error object may, for example, contain errors for an invalid
    416          * username or email address. A WP_Error object should always be returned,
    417          * but may or may not contain errors.
    418          *
    419          * If any errors are present in $errors, this will abort the password reset request.
    420          *
    421          * @since 5.5.0
    422          *
    423          * @param WP_Error      $errors    A WP_Error object containing any errors generated
    424          *                                 by using invalid credentials.
    425          * @param WP_User|false $user_data WP_User object if found, false if the user does not exist.
    426          */
    427         $errors = apply_filters( 'lostpassword_errors', $errors, $user_data );
    428 
    429         if ( $errors->has_errors() ) {
    430                 return $errors;
    431         }
    432 
    433         if ( ! $user_data ) {
    434                 $errors->add( 'invalidcombo', __( '<strong>Error</strong>: There is no account with that username or email address.' ) );
    435                 return $errors;
    436         }
    437 
    438         // Redefining user_login ensures we return the right case in the email.
    439         $user_login = $user_data->user_login;
    440         $user_email = $user_data->user_email;
    441         $key        = get_password_reset_key( $user_data );
    442 
    443         if ( is_wp_error( $key ) ) {
    444                 return $key;
    445         }
    446 
    447         if ( is_multisite() ) {
    448                 $site_name = get_network()->site_name;
    449         } else {
    450                 /*
    451                  * The blogname option is escaped with esc_html on the way into the database
    452                  * in sanitize_option. We want to reverse this for the plain text arena of emails.
    453                  */
    454                 $site_name = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
    455         }
    456 
    457         $message = __( 'Someone has requested a password reset for the following account:' ) . "\r\n\r\n";
    458         /* translators: %s: Site name. */
    459         $message .= sprintf( __( 'Site Name: %s' ), $site_name ) . "\r\n\r\n";
    460         /* translators: %s: User login. */
    461         $message .= sprintf( __( 'Username: %s' ), $user_login ) . "\r\n\r\n";
    462         $message .= __( 'If this was a mistake, ignore this email and nothing will happen.' ) . "\r\n\r\n";
    463         $message .= __( 'To reset your password, visit the following address:' ) . "\r\n\r\n";
    464         $message .= network_site_url( "wp-login.php?action=rp&key=$key&login=" . rawurlencode( $user_login ), 'login' ) . "\r\n\r\n";
    465 
    466         $requester_ip = $_SERVER['REMOTE_ADDR'];
    467         if ( $requester_ip ) {
    468                 $message .= sprintf(
    469                         /* translators: %s: IP address of password reset requester. */
    470                         __( 'This password reset request originated from the IP address %s.' ),
    471                         $requester_ip
    472                 ) . "\r\n";
    473         }
    474 
    475         /* translators: Password reset notification email subject. %s: Site title. */
    476         $title = sprintf( __( '[%s] Password Reset' ), $site_name );
    477 
    478         /**
    479          * Filters the subject of the password reset email.
    480          *
    481          * @since 2.8.0
    482          * @since 4.4.0 Added the `$user_login` and `$user_data` parameters.
    483          *
    484          * @param string  $title      Email subject.
    485          * @param string  $user_login The username for the user.
    486          * @param WP_User $user_data  WP_User object.
    487          */
    488         $title = apply_filters( 'retrieve_password_title', $title, $user_login, $user_data );
    489 
    490         /**
    491          * Filters the message body of the password reset mail.
    492          *
    493          * If the filtered message is empty, the password reset email will not be sent.
    494          *
    495          * @since 2.8.0
    496          * @since 4.1.0 Added `$user_login` and `$user_data` parameters.
    497          *
    498          * @param string  $message    Email message.
    499          * @param string  $key        The activation key.
    500          * @param string  $user_login The username for the user.
    501          * @param WP_User $user_data  WP_User object.
    502          */
    503         $message = apply_filters( 'retrieve_password_message', $message, $key, $user_login, $user_data );
    504 
    505         if ( $message && ! wp_mail( $user_email, wp_specialchars_decode( $title ), $message ) ) {
    506                 $errors->add(
    507                         'retrieve_password_email_failure',
    508                         sprintf(
    509                                 /* translators: %s: Documentation URL. */
    510                                 __( '<strong>Error</strong>: The email could not be sent. Your site may not be correctly configured to send emails. <a href="%s">Get support for resetting your password</a>.' ),
    511                                 esc_url( __( 'https://wordpress.org/support/article/resetting-your-password/' ) )
    512                         )
    513                 );
    514                 return $errors;
    515         }
    516 
    517         return true;
    518 }
    519 
    520363//
    521364// Main.
    522365//