WordPress.org

Make WordPress Core

Ticket #47192: 47192.3.diff

File 47192.3.diff, 9.3 KB (added by TimothyBlynJacobs, 21 months ago)
  • src/wp-includes/class-wp-fatal-error-handler.php

    diff --git a/src/wp-includes/class-wp-fatal-error-handler.php b/src/wp-includes/class-wp-fatal-error-handler.php
    index 7e4f6d8c3c..f3ee4985d4 100644
    a b class WP_Fatal_Error_Handler { 
    183183                        $message = __( 'There has been a critical error on your website.' );
    184184                }
    185185
     186                $message .= sprintf(
     187                        '<a href="%s">%s</a>',
     188                        esc_url( add_query_arg( 'action', 'request_rm', wp_login_url() ) ),
     189                        __( 'Request an email with a Recovery Mode link.' )
     190                );
     191
    186192                $message = sprintf(
    187193                        '<p>%s</p><p><a href="%s">%s</a></p>',
    188194                        $message,
  • 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..db4efc1bf3 100644
    a b If your site appears broken and you can\'t access your dashboard normally, WordP 
    161161
    162162###LINK###
    163163
    164 To keep your site safe, this link will expire in ###EXPIRES###. Don\'t worry about that, though: a new link will be emailed to you if the error occurs again after it expires.
     164To keep your site safe, this link will expire in ###EXPIRES###. Don\'t worry about that, though: you can request a new link at the page below:
     165
     166###REQUEST_LINK###
    165167
    166168When seeking help with this issue, you may be asked for some of the following information:
    167169###DEBUG###
    When seeking help with this issue, you may be asked for some of the following in 
    178180                                '###PAGEURL###',
    179181                                '###SUPPORT###',
    180182                                '###DEBUG###',
     183                                '###REQUEST_LINK###',
    181184                        ),
    182185                        array(
    183186                                $url,
    When seeking help with this issue, you may be asked for some of the following in 
    188191                                home_url( $_SERVER['REQUEST_URI'] ),
    189192                                $support,
    190193                                implode( "\r\n", $debug ),
     194                                add_query_arg( 'action', 'request_rm', wp_login_url() ),
    191195                        ),
    192196                        $message
    193197                );
    When seeking help with this issue, you may be asked for some of the following in 
    224228                return $sent;
    225229        }
    226230
     231        /**
     232         * Sends the Recovery Mode email when a user requests it.
     233         *
     234         * @since 5.4.0
     235         *
     236         * @param string $to         The email address of the recipient.
     237         * @param int    $rate_limit Number of seconds before another email can be sent.
     238         *
     239         * @return bool Whether the email was sent successfully.
     240         */
     241        public function send_requested_recovery_mode_email( $to, $rate_limit ) {
     242                $url      = $this->link_service->generate_url();
     243                $blogname = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
     244
     245                $switched_locale = false;
     246
     247                // The switch_to_locale() function is loaded before it can actually be used.
     248                if ( function_exists( 'switch_to_locale' ) && isset( $GLOBALS['wp_locale_switcher'] ) ) {
     249                        $switched_locale = switch_to_locale( get_locale() );
     250                }
     251
     252                // This filter is already documented in wp-includes/class-wp-recovery-mode-email-service.php
     253                $support = apply_filters( 'recovery_email_support_info', __( 'Please contact your host for assistance with investigating this issue further.' ) );
     254
     255                /* translators: Do not translate LINK, EXPIRES, CAUSE, DETAILS, SITEURL, PAGEURL, SUPPORT. DEBUG: those are placeholders. */
     256                $message = __(
     257                        'Howdy!
     258
     259You requested a link to enter Recovery Mode.
     260
     261First, 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.
     262
     263###SUPPORT###
     264
     265If 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.
     266
     267###LINK###
     268
     269To 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.'
     270                );
     271                $message = str_replace(
     272                        array(
     273                                '###LINK###',
     274                                '###EXPIRES###',
     275                                '###SITEURL###',
     276                                '###SUPPORT###',
     277                        ),
     278                        array(
     279                                $url,
     280                                human_time_diff( time() + $rate_limit ),
     281                                home_url( '/' ),
     282                                $support,
     283                        ),
     284                        $message
     285                );
     286
     287                $email = array(
     288                        'to'      => $to,
     289                        /* translators: %s: Site title. */
     290                        'subject' => __( '[%s] Your Recovery Mode Link' ),
     291                        'message' => $message,
     292                        'headers' => '',
     293                );
     294
     295                /**
     296                 * Filter the contents of the requested Recovery Mode email.
     297                 *
     298                 * @since 5.4.0
     299                 *
     300                 * @param array  $email Used to build wp_mail().
     301                 * @param string $url   URL to enter recovery mode.
     302                 */
     303                $email = apply_filters( 'requested_recovery_mode_email', $email, $url );
     304
     305                $sent = wp_mail(
     306                        $email['to'],
     307                        wp_specialchars_decode( sprintf( $email['subject'], $blogname ) ),
     308                        $email['message'],
     309                        $email['headers']
     310                );
     311
     312                if ( $switched_locale ) {
     313                        restore_previous_locale();
     314                }
     315
     316                return $sent;
     317        }
     318
     319
    227320        /**
    228321         * Gets the email address to send the recovery mode link to.
    229322         *
  • 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..f3d007a481 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                        $allowed   = get_option( 'recovery_mode_emails', array() );
     539                        $allowed[] = get_option( 'admin_email' );
     540
     541                        if ( defined( 'RECOVERY_MODE_EMAIL' ) && RECOVERY_MODE_EMAIL ) {
     542                                $allowed[] = RECOVERY_MODE_EMAIL;
     543                        }
     544
     545                        $email = $_POST['rm_email'];
     546
     547                        if ( ! in_array( $email, get_option( 'recovery_mode_emails', array() ), true ) ) {
     548                                wp_die(
     549                                        __( 'Email does not belong to a user with recovery mode capabilities.' ),
     550                                        '',
     551                                        array(
     552                                                'back_link' => true,
     553                                                'response'  => 403,
     554                                        )
     555                                );
     556                        }
     557
     558                        if ( ! function_exists( 'wp_generate_password' ) ) {
     559                                require_once ABSPATH . WPINC . '/pluggable.php';
     560                        }
     561
     562                        $sent = $this->email_service->send_requested_recovery_mode_email( $email, $this->get_email_rate_limit() );
     563
     564                        if ( ! $sent ) {
     565                                wp_die(
     566                                        sprintf(
     567                                        /* translators: %s: mail() */
     568                                                __( 'The email could not be sent. Possible reason: your host may have disabled the %s function.' ),
     569                                                'mail()'
     570                                        ),
     571                                        500
     572                                );
     573                        }
     574
     575                        wp_die( __( 'Recovery email sent.' ), __( 'Request a Recovery Mode Link' ), 200 );
     576                }
     577
     578                ob_start();
     579                ?>
     580                <form method="POST" action="<?php esc_url( add_query_arg( 'action', 'request_rm', wp_login_url() ) ); ?>">
     581                        <label for="email"><?php _e( 'Recovery Mode Email' ); ?></label>
     582                        <input type="email" id="email" name="rm_email">
     583
     584                        <button class="button"><?php _e( 'Request Email' ); ?></button>
     585                </form>
     586                <?php
     587                wp_die( ob_get_clean(), __( 'Request a Recovery Mode Link' ), 200 );
     588        }
    469589}