WordPress.org

Make WordPress Core

Ticket #47192: 47192.2.diff

File 47192.2.diff, 7.4 KB (added by TimothyBlynJacobs, 21 months ago)
  • src/wp-includes/class-wp-recovery-mode-email-service.php

    diff --git a/src/wp-includes/class-wp-recovery-mode-email-service.php b/src/wp-includes/class-wp-recovery-mode-email-service.php
    index 5b0dd2e4db..953bf56004 100644
    a b When seeking help with this issue, you may be asked for some of the following in 
    224224                return $sent;
    225225        }
    226226
     227        /**
     228         * Sends the Recovery Mode email when a user requests it.
     229         *
     230         * @since 5.4.0
     231         *
     232         * @param string $to         The email address of the recipient.
     233         * @param int    $rate_limit Number of seconds before another email can be sent.
     234         *
     235         * @return bool Whether the email was sent successfully.
     236         */
     237        public function send_requested_recovery_mode_email( $to, $rate_limit ) {
     238                $url      = $this->link_service->generate_url();
     239                $blogname = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
     240
     241                $switched_locale = false;
     242
     243                // The switch_to_locale() function is loaded before it can actually be used.
     244                if ( function_exists( 'switch_to_locale' ) && isset( $GLOBALS['wp_locale_switcher'] ) ) {
     245                        $switched_locale = switch_to_locale( get_locale() );
     246                }
     247
     248                // This filter is already documented in wp-includes/class-wp-recovery-mode-email-service.php
     249                $support = apply_filters( 'recovery_email_support_info', __( 'Please contact your host for assistance with investigating this issue further.' ) );
     250
     251                /* translators: Do not translate LINK, EXPIRES, CAUSE, DETAILS, SITEURL, PAGEURL, SUPPORT. DEBUG: those are placeholders. */
     252                $message = __(
     253                        'Howdy!
     254
     255You requested a link to enter Recovery Mode.
     256
     257First, visit your website (###SITEURL###) and check for any visible issues. Next, visit the page where the error was caught and check for any visible issues.
     258
     259###SUPPORT###
     260
     261If your site appears broken and you can\'t access your dashboard normally, WordPress now has a special "recovery mode". This lets you safely login to your dashboard and investigate further.
     262
     263###LINK###
     264
     265To keep your site safe, this link will expire in ###EXPIRES###. Don\'t worry about that, though: you can request a new link at any time.'
     266                );
     267                $message = str_replace(
     268                        array(
     269                                '###LINK###',
     270                                '###EXPIRES###',
     271                                '###SITEURL###',
     272                                '###SUPPORT###',
     273                        ),
     274                        array(
     275                                $url,
     276                                human_time_diff( time() + $rate_limit ),
     277                                home_url( '/' ),
     278                                $support,
     279                        ),
     280                        $message
     281                );
     282
     283                $email = array(
     284                        'to'      => $to,
     285                        /* translators: %s: Site title. */
     286                        'subject' => __( '[%s] Your Recovery Mode Link' ),
     287                        'message' => $message,
     288                        'headers' => '',
     289                );
     290
     291                /**
     292                 * Filter the contents of the requested Recovery Mode email.
     293                 *
     294                 * @since 5.4.0
     295                 *
     296                 * @param array  $email Used to build wp_mail().
     297                 * @param string $url   URL to enter recovery mode.
     298                 */
     299                $email = apply_filters( 'requested_recovery_mode_email', $email, $url );
     300
     301                $sent = wp_mail(
     302                        $email['to'],
     303                        wp_specialchars_decode( sprintf( $email['subject'], $blogname ) ),
     304                        $email['message'],
     305                        $email['headers']
     306                );
     307
     308                if ( $switched_locale ) {
     309                        restore_previous_locale();
     310                }
     311
     312                return $sent;
     313        }
     314
     315
    227316        /**
    228317         * Gets the email address to send the recovery mode link to.
    229318         *
  • src/wp-includes/class-wp-recovery-mode.php

    diff --git a/src/wp-includes/class-wp-recovery-mode.php b/src/wp-includes/class-wp-recovery-mode.php
    index 608864d6ee..b13b4fb90f 100644
    a b class WP_Recovery_Mode { 
    9595                add_action( 'login_form_' . self::EXIT_ACTION, array( $this, 'handle_exit_recovery_mode' ) );
    9696                add_action( 'recovery_mode_clean_expired_keys', array( $this, 'clean_expired_keys' ) );
    9797
     98                add_action( 'wp_login', array( $this, 'store_recovery_mode_email' ), 10, 2 );
     99                add_action( 'profile_update', array( $this, 'update_email_list' ) );
     100                add_action( 'add_user_role', array( $this, 'update_email_list' ) );
     101                add_action( 'set_user_role', array( $this, 'update_email_list' ) );
     102                add_action( 'remove_user_role', array( $this, 'update_email_list' ) );
     103
    98104                if ( ! wp_next_scheduled( 'recovery_mode_clean_expired_keys' ) && ! wp_installing() ) {
    99105                        wp_schedule_event( time(), 'daily', 'recovery_mode_clean_expired_keys' );
    100106                }
    class WP_Recovery_Mode { 
    112118                        return;
    113119                }
    114120
     121                $this->request_recovery_mode();
    115122                $this->link_service->handle_begin_link( $this->get_link_ttl() );
    116123        }
    117124
    class WP_Recovery_Mode { 
    195202                $this->redirect_protected();
    196203        }
    197204
     205        /**
     206         * When a user logs in, check if they have recovery mode permissions, and add them
     207         * to the list of valid recovery mode emails.
     208         *
     209         * @param string $username Username.
     210         * @param WP_User $user    WP_User object of the logged-in user.
     211         */
     212        public function store_recovery_mode_email( $username, $user ) {
     213                $this->update_email_list( $user->ID );
     214        }
     215
    198216        /**
    199217         * Ends the current recovery mode session.
    200218         *
    class WP_Recovery_Mode { 
    466484                wp_safe_redirect( $url );
    467485                exit;
    468486        }
     487
     488        /**
     489         * Updates the list of Recovery Mode email addresses.
     490         *
     491         * @since 5.4.0
     492         *
     493         * @param int $user_id The user to add or remove from the list.
     494         */
     495        private function update_email_list( $user_id ) {
     496                $user = get_userdata( $user_id );
     497
     498                if ( ! $user ) {
     499                        return;
     500                }
     501
     502                $has_cap  = $user->has_cap( 'resume_plugins' ) || $user->has_cap( 'resume_themes' );
     503                $emails   = get_option( 'recovery_mode_emails', array() );
     504                $included = in_array( $user->user_email, $emails, true );
     505
     506                if ( $has_cap && $included ) {
     507                        return;
     508                }
     509
     510                if ( ! $has_cap && ! $included ) {
     511                        return;
     512                }
     513
     514                if ( $has_cap ) {
     515                        $emails[] = $user->user_email;
     516                } else {
     517                        unset( $emails[ array_search( $user->user_email, $emails, true ) ] );
     518                }
     519
     520                update_option( 'recovery_mode_emails', $emails );
     521        }
     522
     523        /**
     524         * Handles when a user requests a recovery mode link for their email address.
     525         *
     526         * @since 5.2.0
     527         */
     528        private function request_recovery_mode() {
     529                if ( ! isset( $GLOBALS['pagenow'] ) || 'wp-login.php' !== $GLOBALS['pagenow'] ) {
     530                        return;
     531                }
     532
     533                if ( ! isset( $_GET['action'] ) || 'request_rm' !== $_GET['action'] ) {
     534                        return;
     535                }
     536
     537                if ( ! empty( $_POST['rm_email'] ) ) {
     538                        $email = $_POST['rm_email'];
     539
     540                        if ( ! in_array( $email, get_option( 'recovery_mode_emails', array() ), true ) ) {
     541                                wp_die(
     542                                        __( 'Email does not belong to a user with recovery mode capabilities.' ),
     543                                        '',
     544                                        array(
     545                                                'back_link' => true,
     546                                                'response'  => 403,
     547                                        )
     548                                );
     549                        }
     550
     551                        if ( ! function_exists( 'wp_generate_password' ) ) {
     552                                require_once ABSPATH . WPINC . '/pluggable.php';
     553                        }
     554
     555                        $sent = $this->email_service->send_requested_recovery_mode_email( $email, $this->get_email_rate_limit() );
     556
     557                        if ( ! $sent ) {
     558                                wp_die(
     559                                        sprintf(
     560                                        /* translators: %s: mail() */
     561                                                __( 'The email could not be sent. Possible reason: your host may have disabled the %s function.' ),
     562                                                'mail()'
     563                                        ),
     564                                        500
     565                                );
     566                        }
     567
     568                        wp_die( __( 'Recovery email sent.' ), __( 'Request a Recovery Mode Link' ), 200 );
     569                }
     570
     571                ob_start();
     572                ?>
     573                <form method="POST" action="<?php esc_url( add_query_arg( 'action', 'request_rm', wp_login_url() ) ); ?>">
     574                        <label for="email"><?php _e( 'Recovery Mode Email' ); ?></label>
     575                        <input type="email" id="email" name="rm_email">
     576
     577                        <button class="button"><?php _e( 'Request Email' ); ?></button>
     578                </form>
     579                <?php
     580                wp_die( ob_get_clean(), __( 'Request a Recovery Mode Link' ), 200 );
     581        }
    469582}