mirror of
https://github.com/gosticks/wordpress-develop.git
synced 2026-08-11 20:30:23 +00:00
Bootstrap/Load: Introduce a recovery mode for fixing fatal errors.
Using the new fatal handler introduced in [44962], an email is sent to the admin when a fatal error occurs. This email includes a secret link to enter recovery mode. When clicked, the link will be validated and on success a cookie will be placed on the client, enabling recovery mode for that user. This functionality is executed early before plugins and themes are loaded, in order to be unaffected by potential fatal errors these might be causing. When in recovery mode, broken plugins and themes will be paused for that client, so that they are able to access the admin backend despite of these errors. They are notified about the broken extensions and the errors caused, and can then decide whether they would like to temporarily deactivate the extension or fix the problem and resume the extension. A link in the admin bar allows the client to exit recovery mode. Props timothyblynjacobs, afragen, flixos90, nerrad, miss_jwo, schlessera, spacedmonkey, swissspidy. Fixes #46130, #44458. git-svn-id: https://develop.svn.wordpress.org/trunk@44973 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
@@ -697,6 +697,43 @@ function wp_get_active_and_valid_plugins() {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove plugins from the list of active plugins when we're on an endpoint
|
||||
* that should be protected against WSODs and the plugin is paused.
|
||||
*/
|
||||
if ( wp_is_recovery_mode() ) {
|
||||
$plugins = wp_skip_paused_plugins( $plugins );
|
||||
}
|
||||
|
||||
return $plugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters a given list of plugins, removing any paused plugins from it.
|
||||
*
|
||||
* @since 5.2.0
|
||||
*
|
||||
* @param array $plugins List of absolute plugin main file paths.
|
||||
* @return array Filtered value of $plugins, without any paused plugins.
|
||||
*/
|
||||
function wp_skip_paused_plugins( array $plugins ) {
|
||||
$paused_plugins = wp_paused_plugins()->get_all();
|
||||
|
||||
if ( empty( $paused_plugins ) ) {
|
||||
return $plugins;
|
||||
}
|
||||
|
||||
foreach ( $plugins as $index => $plugin ) {
|
||||
list( $plugin ) = explode( '/', plugin_basename( $plugin ) );
|
||||
|
||||
if ( array_key_exists( $plugin, $paused_plugins ) ) {
|
||||
unset( $plugins[ $index ] );
|
||||
|
||||
// Store list of paused plugins for displaying an admin notice.
|
||||
$GLOBALS['_paused_plugins'][ $plugin ] = $paused_plugins[ $plugin ];
|
||||
}
|
||||
}
|
||||
|
||||
return $plugins;
|
||||
}
|
||||
|
||||
@@ -725,9 +762,146 @@ function wp_get_active_and_valid_themes() {
|
||||
|
||||
$themes[] = TEMPLATEPATH;
|
||||
|
||||
/*
|
||||
* Remove themes from the list of active themes when we're on an endpoint
|
||||
* that should be protected against WSODs and the theme is paused.
|
||||
*/
|
||||
if ( wp_is_recovery_mode() ) {
|
||||
$themes = wp_skip_paused_themes( $themes );
|
||||
|
||||
// If no active and valid themes exist, skip loading themes.
|
||||
if ( empty( $themes ) ) {
|
||||
add_filter( 'wp_using_themes', '__return_false' );
|
||||
}
|
||||
}
|
||||
|
||||
return $themes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters a given list of themes, removing any paused themes from it.
|
||||
*
|
||||
* @since 5.2.0
|
||||
*
|
||||
* @param array $themes List of absolute theme directory paths.
|
||||
* @return array Filtered value of $themes, without any paused themes.
|
||||
*/
|
||||
function wp_skip_paused_themes( array $themes ) {
|
||||
$paused_themes = wp_paused_themes()->get_all();
|
||||
|
||||
if ( empty( $paused_themes ) ) {
|
||||
return $themes;
|
||||
}
|
||||
|
||||
foreach ( $themes as $index => $theme ) {
|
||||
$theme = basename( $theme );
|
||||
|
||||
if ( array_key_exists( $theme, $paused_themes ) ) {
|
||||
unset( $themes[ $index ] );
|
||||
|
||||
// Store list of paused themes for displaying an admin notice.
|
||||
$GLOBALS['_paused_themes'][ $theme ] = $paused_themes[ $theme ];
|
||||
}
|
||||
}
|
||||
|
||||
return $themes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is WordPress in Recovery Mode.
|
||||
*
|
||||
* In this mode, plugins or themes that cause WSODs will be paused.
|
||||
*
|
||||
* @since 5.2.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
function wp_is_recovery_mode() {
|
||||
return wp_recovery_mode()->is_active();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether we are currently on an endpoint that should be protected against WSODs.
|
||||
*
|
||||
* @since 5.2.0
|
||||
*
|
||||
* @return bool True if the current endpoint should be protected.
|
||||
*/
|
||||
function is_protected_endpoint() {
|
||||
// Protect login pages.
|
||||
if ( isset( $GLOBALS['pagenow'] ) && 'wp-login.php' === $GLOBALS['pagenow'] ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Protect the admin backend.
|
||||
if ( is_admin() && ! wp_doing_ajax() ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Protect AJAX actions that could help resolve a fatal error should be available.
|
||||
if ( is_protected_ajax_action() ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters whether the current request is against a protected endpoint.
|
||||
*
|
||||
* This filter is only fired when an endpoint is requested which is not already protected by
|
||||
* WordPress core. As such, it exclusively allows providing further protected endpoints in
|
||||
* addition to the admin backend, login pages and protected AJAX actions.
|
||||
*
|
||||
* @since 5.2.0
|
||||
*
|
||||
* @param bool $is_protected_endpoint Whether the currently requested endpoint is protected. Default false.
|
||||
*/
|
||||
return (bool) apply_filters( 'is_protected_endpoint', false );
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether we are currently handling an AJAX action that should be protected against WSODs.
|
||||
*
|
||||
* @since 5.2.0
|
||||
*
|
||||
* @return bool True if the current AJAX action should be protected.
|
||||
*/
|
||||
function is_protected_ajax_action() {
|
||||
if ( ! wp_doing_ajax() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! isset( $_REQUEST['action'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$actions_to_protect = array(
|
||||
'edit-theme-plugin-file', // Saving changes in the core code editor.
|
||||
'heartbeat', // Keep the heart beating.
|
||||
'install-plugin', // Installing a new plugin.
|
||||
'install-theme', // Installing a new theme.
|
||||
'search-plugins', // Searching in the list of plugins.
|
||||
'search-install-plugins', // Searching for a plugin in the plugin install screen.
|
||||
'update-plugin', // Update an existing plugin.
|
||||
'update-theme', // Update an existing theme.
|
||||
);
|
||||
|
||||
/**
|
||||
* Filters the array of protected AJAX actions.
|
||||
*
|
||||
* This filter is only fired when doing AJAX and the AJAX request has an 'action' property.
|
||||
*
|
||||
* @since 5.2.0
|
||||
*
|
||||
* @param array $actions_to_protect Array of strings with AJAX actions to protect.
|
||||
*/
|
||||
$actions_to_protect = (array) apply_filters( 'wp_protected_ajax_actions', $actions_to_protect );
|
||||
|
||||
if ( ! in_array( $_REQUEST['action'], $actions_to_protect, true ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set internal encoding.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user