Changeset 44973
- Timestamp:
- 03/21/2019 09:52:07 PM (5 years ago)
- Location:
- trunk
- Files:
-
- 9 added
- 19 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-admin/css/list-tables.css
r44791 r44973 1311 1311 } 1312 1312 1313 .plugins tr.paused th.check-column { 1314 border-left: 4px solid #d54e21; 1315 } 1316 1317 .plugins tr.paused th, 1318 .plugins tr.paused td { 1319 background-color: #fef7f1; 1320 } 1321 1322 .plugins tr.paused .plugin-title, 1323 .plugins .paused .dashicons-warning { 1324 color: #dc3232; 1325 } 1326 1327 .plugins .paused .error-display p, 1328 .plugins .paused .error-display code { 1329 font-size: 90%; 1330 font-style: italic; 1331 color: rgb( 0, 0, 0, 0.7 ); 1332 } 1333 1334 .plugins .resume-link { 1335 color: #dc3232; 1336 } 1337 1313 1338 .plugin-card .update-now:before { 1314 1339 color: #f56e28; -
trunk/src/wp-admin/includes/admin-filters.php
r44717 r44973 118 118 119 119 add_action( 'admin_notices', 'update_nag', 3 ); 120 add_action( 'admin_notices', 'paused_plugins_notice', 5 ); 121 add_action( 'admin_notices', 'paused_themes_notice', 5 ); 120 122 add_action( 'admin_notices', 'maintenance_nag', 10 ); 121 123 -
trunk/src/wp-admin/includes/class-wp-plugins-list-table.php
r44937 r44973 41 41 42 42 $status = 'all'; 43 if ( isset( $_REQUEST['plugin_status'] ) && in_array( $_REQUEST['plugin_status'], array( 'active', 'inactive', 'recently_activated', 'upgrade', 'mustuse', 'dropins', 'search' ) ) ) {43 if ( isset( $_REQUEST['plugin_status'] ) && in_array( $_REQUEST['plugin_status'], array( 'active', 'inactive', 'recently_activated', 'upgrade', 'mustuse', 'dropins', 'search', 'paused' ) ) ) { 44 44 $status = $_REQUEST['plugin_status']; 45 45 } … … 100 100 'mustuse' => array(), 101 101 'dropins' => array(), 102 'paused' => array(), 102 103 ); 103 104 … … 184 185 // Extra info if known. array_merge() ensures $plugin_data has precedence if keys collide. 185 186 if ( isset( $plugin_info->response[ $plugin_file ] ) ) { 186 $plugins['all'][ $plugin_file ] = $plugin_data = array_merge( (array) $plugin_info->response[ $plugin_file ], $plugin_data ); 187 $plugin_data = array_merge( (array) $plugin_info->response[ $plugin_file ], $plugin_data ); 188 $plugins['all'][ $plugin_file ] = $plugin_data; 187 189 // Make sure that $plugins['upgrade'] also receives the extra info since it is used on ?plugin_status=upgrade 188 190 if ( isset( $plugins['upgrade'][ $plugin_file ] ) ) { 189 $plugins['upgrade'][ $plugin_file ] = $plugin_data = array_merge( (array) $plugin_info->response[ $plugin_file ], $plugin_data );191 $plugins['upgrade'][ $plugin_file ] = $plugin_data; 190 192 } 191 193 } elseif ( isset( $plugin_info->no_update[ $plugin_file ] ) ) { 192 $plugins['all'][ $plugin_file ] = $plugin_data = array_merge( (array) $plugin_info->no_update[ $plugin_file ], $plugin_data ); 194 $plugin_data = array_merge( (array) $plugin_info->no_update[ $plugin_file ], $plugin_data ); 195 $plugins['all'][ $plugin_file ] = $plugin_data; 193 196 // Make sure that $plugins['upgrade'] also receives the extra info since it is used on ?plugin_status=upgrade 194 197 if ( isset( $plugins['upgrade'][ $plugin_file ] ) ) { 195 $plugins['upgrade'][ $plugin_file ] = $plugin_data = array_merge( (array) $plugin_info->no_update[ $plugin_file ], $plugin_data );198 $plugins['upgrade'][ $plugin_file ] = $plugin_data; 196 199 } 197 200 } … … 219 222 // On the network-admin screen, populate the active list with plugins that are network activated 220 223 $plugins['active'][ $plugin_file ] = $plugin_data; 224 225 if ( ! $screen->in_admin( 'network' ) && is_plugin_paused( $plugin_file ) ) { 226 $plugins['paused'][ $plugin_file ] = $plugin_data; 227 } 221 228 } else { 222 229 if ( isset( $recently_activated[ $plugin_file ] ) ) { … … 445 452 /* translators: %s: plugin count */ 446 453 $text = _n( 'Drop-ins <span class="count">(%s)</span>', 'Drop-ins <span class="count">(%s)</span>', $count ); 454 break; 455 case 'paused': 456 /* translators: %s: plugin count */ 457 $text = _n( 'Paused <span class="count">(%s)</span>', 'Paused <span class="count">(%s)</span>', $count ); 447 458 break; 448 459 case 'upgrade': … … 658 669 $actions['deactivate'] = '<a href="' . wp_nonce_url( 'plugins.php?action=deactivate&plugin=' . urlencode( $plugin_file ) . '&plugin_status=' . $context . '&paged=' . $page . '&s=' . $s, 'deactivate-plugin_' . $plugin_file ) . '" aria-label="' . esc_attr( sprintf( _x( 'Deactivate %s', 'plugin' ), $plugin_data['Name'] ) ) . '">' . __( 'Deactivate' ) . '</a>'; 659 670 } 671 if ( current_user_can( 'resume_plugin', $plugin_file ) && is_plugin_paused( $plugin_file ) ) { 672 /* translators: %s: plugin name */ 673 $actions['resume'] = '<a class="resume-link" href="' . wp_nonce_url( 'plugins.php?action=resume&plugin=' . urlencode( $plugin_file ) . '&plugin_status=' . $context . '&paged=' . $page . '&s=' . $s, 'resume-plugin_' . $plugin_file ) . '" aria-label="' . esc_attr( sprintf( _x( 'Resume %s', 'plugin' ), $plugin_data['Name'] ) ) . '">' . __( 'Resume' ) . '</a>'; 674 } 660 675 } else { 661 676 if ( current_user_can( 'activate_plugin', $plugin_file ) ) { … … 764 779 if ( ! empty( $totals['upgrade'] ) && ! empty( $plugin_data['update'] ) ) { 765 780 $class .= ' update'; 781 } 782 783 $paused = ! $screen->in_admin( 'network' ) && is_plugin_paused( $plugin_file ); 784 785 if ( $paused ) { 786 $class .= ' paused'; 766 787 } 767 788 … … 847 868 * @param string $status Status of the plugin. Defaults are 'All', 'Active', 848 869 * 'Inactive', 'Recently Activated', 'Upgrade', 'Must-Use', 849 * 'Drop-ins', 'Search' .870 * 'Drop-ins', 'Search', 'Paused'. 850 871 */ 851 872 $plugin_meta = apply_filters( 'plugin_row_meta', $plugin_meta, $plugin_file, $plugin_data, $status ); … … 853 874 854 875 echo '</div>'; 876 877 if ( $paused ) { 878 $notice_text = __( 'This plugin failed to load properly and is paused during recovery mode.' ); 879 880 printf( '<p><span class="dashicons dashicons-warning"></span> <strong>%s</strong></p>', $notice_text ); 881 882 $error = wp_get_plugin_error( $plugin_file ); 883 884 if ( false !== $error ) { 885 printf( '<div class="error-display"><p>%s</p></div>', wp_get_extension_error_description( $error ) ); 886 } 887 } 855 888 856 889 echo '</td>'; … … 887 920 * @param string $status Status of the plugin. Defaults are 'All', 'Active', 888 921 * 'Inactive', 'Recently Activated', 'Upgrade', 'Must-Use', 889 * 'Drop-ins', 'Search' .922 * 'Drop-ins', 'Search', 'Paused'. 890 923 */ 891 924 do_action( 'after_plugin_row', $plugin_file, $plugin_data, $status ); … … 903 936 * @param string $status Status of the plugin. Defaults are 'All', 'Active', 904 937 * 'Inactive', 'Recently Activated', 'Upgrade', 'Must-Use', 905 * 'Drop-ins', 'Search' .938 * 'Drop-ins', 'Search', 'Paused'. 906 939 */ 907 940 do_action( "after_plugin_row_{$plugin_file}", $plugin_file, $plugin_data, $status ); -
trunk/src/wp-admin/includes/plugin.php
r44717 r44973 469 469 function _get_dropins() { 470 470 $dropins = array( 471 'advanced-cache.php' => array( __( 'Advanced caching plugin.' ), 'WP_CACHE' ), // WP_CACHE 472 'db.php' => array( __( 'Custom database class.' ), true ), // auto on load 473 'db-error.php' => array( __( 'Custom database error message.' ), true ), // auto on error 474 'install.php' => array( __( 'Custom installation script.' ), true ), // auto on installation 475 'maintenance.php' => array( __( 'Custom maintenance message.' ), true ), // auto on maintenance 476 'object-cache.php' => array( __( 'External object cache.' ), true ), // auto on load 471 'advanced-cache.php' => array( __( 'Advanced caching plugin.' ), 'WP_CACHE' ), // WP_CACHE 472 'db.php' => array( __( 'Custom database class.' ), true ), // auto on load 473 'db-error.php' => array( __( 'Custom database error message.' ), true ), // auto on error 474 'install.php' => array( __( 'Custom installation script.' ), true ), // auto on installation 475 'maintenance.php' => array( __( 'Custom maintenance message.' ), true ), // auto on maintenance 476 'object-cache.php' => array( __( 'External object cache.' ), true ), // auto on load 477 'php-error.php' => array( __( 'Custom PHP error message.' ), true ), // auto on error 478 'fatal-error-handler.php' => array( __( 'Custom PHP fatal error handler.' ), true ), // auto on error 477 479 ); 478 480 … … 2102 2104 WP_Privacy_Policy_Content::add( $plugin_name, $policy_text ); 2103 2105 } 2106 2107 /** 2108 * Determines whether a plugin is technically active but was paused while 2109 * loading. 2110 * 2111 * For more information on this and similar theme functions, check out 2112 * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/ 2113 * Conditional Tags} article in the Theme Developer Handbook. 2114 * 2115 * @since 5.2.0 2116 * 2117 * @param string $plugin Path to the plugin file relative to the plugins directory. 2118 * @return bool True, if in the list of paused plugins. False, not in the list. 2119 */ 2120 function is_plugin_paused( $plugin ) { 2121 if ( ! isset( $GLOBALS['_paused_plugins'] ) ) { 2122 return false; 2123 } 2124 2125 if ( ! is_plugin_active( $plugin ) ) { 2126 return false; 2127 } 2128 2129 list( $plugin ) = explode( '/', $plugin ); 2130 2131 return array_key_exists( $plugin, $GLOBALS['_paused_plugins'] ); 2132 } 2133 2134 /** 2135 * Gets the error that was recorded for a paused plugin. 2136 * 2137 * @since 5.2.0 2138 * 2139 * @param string $plugin Path to the plugin file relative to the plugins 2140 * directory. 2141 * @return array|false Array of error information as it was returned by 2142 * `error_get_last()`, or false if none was recorded. 2143 */ 2144 function wp_get_plugin_error( $plugin ) { 2145 if ( ! isset( $GLOBALS['_paused_plugins'] ) ) { 2146 return false; 2147 } 2148 2149 list( $plugin ) = explode( '/', $plugin ); 2150 2151 if ( ! array_key_exists( $plugin, $GLOBALS['_paused_plugins'] ) ) { 2152 return false; 2153 } 2154 2155 return $GLOBALS['_paused_plugins'][ $plugin ]; 2156 } 2157 2158 /** 2159 * Tries to resume a single plugin. 2160 * 2161 * If a redirect was provided, we first ensure the plugin does not throw fatal 2162 * errors anymore. 2163 * 2164 * The way it works is by setting the redirection to the error before trying to 2165 * include the plugin file. If the plugin fails, then the redirection will not 2166 * be overwritten with the success message and the plugin will not be resumed. 2167 * 2168 * @since 5.2.0 2169 * 2170 * @param string $plugin Single plugin to resume. 2171 * @param string $redirect Optional. URL to redirect to. Default empty string. 2172 * @return bool|WP_Error True on success, false if `$plugin` was not paused, 2173 * `WP_Error` on failure. 2174 */ 2175 function resume_plugin( $plugin, $redirect = '' ) { 2176 /* 2177 * We'll override this later if the plugin could be resumed without 2178 * creating a fatal error. 2179 */ 2180 if ( ! empty( $redirect ) ) { 2181 wp_redirect( 2182 add_query_arg( 2183 '_error_nonce', 2184 wp_create_nonce( 'plugin-resume-error_' . $plugin ), 2185 $redirect 2186 ) 2187 ); 2188 2189 // Load the plugin to test whether it throws a fatal error. 2190 ob_start(); 2191 plugin_sandbox_scrape( $plugin ); 2192 ob_clean(); 2193 } 2194 2195 list( $extension ) = explode( '/', $plugin ); 2196 2197 $result = wp_paused_plugins()->delete( $extension ); 2198 2199 if ( ! $result ) { 2200 return new WP_Error( 2201 'could_not_resume_plugin', 2202 __( 'Could not resume the plugin.' ) 2203 ); 2204 } 2205 2206 return true; 2207 } 2208 2209 /** 2210 * Renders an admin notice in case some plugins have been paused due to errors. 2211 * 2212 * @since 5.2.0 2213 */ 2214 function paused_plugins_notice() { 2215 if ( 'plugins.php' === $GLOBALS['pagenow'] ) { 2216 return; 2217 } 2218 2219 if ( ! current_user_can( 'resume_plugins' ) ) { 2220 return; 2221 } 2222 2223 if ( ! isset( $GLOBALS['_paused_plugins'] ) || empty( $GLOBALS['_paused_plugins'] ) ) { 2224 return; 2225 } 2226 2227 printf( 2228 '<div class="notice notice-error"><p><strong>%s</strong><br>%s</p><p><a href="%s">%s</a></p></div>', 2229 __( 'One or more plugins failed to load properly.' ), 2230 __( 'You can find more details and make changes on the Plugins screen.' ), 2231 esc_url( admin_url( 'plugins.php?plugin_status=paused' ) ), 2232 __( 'Go to the Plugins screen' ) 2233 ); 2234 } -
trunk/src/wp-admin/includes/theme.php
r44717 r44973 769 769 <?php 770 770 } 771 772 /** 773 * Determines whether a theme is technically active but was paused while 774 * loading. 775 * 776 * For more information on this and similar theme functions, check out 777 * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/ 778 * Conditional Tags} article in the Theme Developer Handbook. 779 * 780 * @since 5.2.0 781 * 782 * @param string $theme Path to the theme directory relative to the themes directory. 783 * @return bool True, if in the list of paused themes. False, not in the list. 784 */ 785 function is_theme_paused( $theme ) { 786 if ( ! isset( $GLOBALS['_paused_themes'] ) ) { 787 return false; 788 } 789 790 if ( get_stylesheet() !== $theme && get_template() !== $theme ) { 791 return false; 792 } 793 794 return array_key_exists( $theme, $GLOBALS['_paused_themes'] ); 795 } 796 797 /** 798 * Gets the error that was recorded for a paused theme. 799 * 800 * @since 5.2.0 801 * 802 * @param string $theme Path to the theme directory relative to the themes 803 * directory. 804 * @return array|false Array of error information as it was returned by 805 * `error_get_last()`, or false if none was recorded. 806 */ 807 function wp_get_theme_error( $theme ) { 808 if ( ! isset( $GLOBALS['_paused_themes'] ) ) { 809 return false; 810 } 811 812 if ( ! array_key_exists( $theme, $GLOBALS['_paused_themes'] ) ) { 813 return false; 814 } 815 816 return $GLOBALS['_paused_themes'][ $theme ]; 817 } 818 819 /** 820 * Tries to resume a single theme. 821 * 822 * If a redirect was provided and a functions.php file was found, we first ensure that 823 * functions.php file does not throw fatal errors anymore. 824 * 825 * The way it works is by setting the redirection to the error before trying to 826 * include the file. If the theme fails, then the redirection will not be overwritten 827 * with the success message and the theme will not be resumed. 828 * 829 * @since 5.2.0 830 * 831 * @param string $theme Single theme to resume. 832 * @param string $redirect Optional. URL to redirect to. Default empty string. 833 * @return bool|WP_Error True on success, false if `$theme` was not paused, 834 * `WP_Error` on failure. 835 */ 836 function resume_theme( $theme, $redirect = '' ) { 837 list( $extension ) = explode( '/', $theme ); 838 839 /* 840 * We'll override this later if the theme could be resumed without 841 * creating a fatal error. 842 */ 843 if ( ! empty( $redirect ) ) { 844 $functions_path = ''; 845 if ( strpos( STYLESHEETPATH, $extension ) ) { 846 $functions_path = STYLESHEETPATH . '/functions.php'; 847 } elseif ( strpos( TEMPLATEPATH, $extension ) ) { 848 $functions_path = TEMPLATEPATH . '/functions.php'; 849 } 850 851 if ( ! empty( $functions_path ) ) { 852 wp_redirect( 853 add_query_arg( 854 '_error_nonce', 855 wp_create_nonce( 'theme-resume-error_' . $theme ), 856 $redirect 857 ) 858 ); 859 860 // Load the theme's functions.php to test whether it throws a fatal error. 861 ob_start(); 862 include $functions_path; 863 ob_clean(); 864 } 865 } 866 867 $result = wp_paused_themes()->delete( $extension ); 868 869 if ( ! $result ) { 870 return new WP_Error( 871 'could_not_resume_theme', 872 __( 'Could not resume the theme.' ) 873 ); 874 } 875 876 return true; 877 } 878 879 /** 880 * Renders an admin notice in case some themes have been paused due to errors. 881 * 882 * @since 5.2.0 883 */ 884 function paused_themes_notice() { 885 if ( 'themes.php' === $GLOBALS['pagenow'] ) { 886 return; 887 } 888 889 if ( ! current_user_can( 'resume_themes' ) ) { 890 return; 891 } 892 893 if ( ! isset( $GLOBALS['_paused_themes'] ) || empty( $GLOBALS['_paused_themes'] ) ) { 894 return; 895 } 896 897 printf( 898 '<div class="notice notice-error"><p><strong>%s</strong><br>%s</p><p><a href="%s">%s</a></p></div>', 899 __( 'One or more themes failed to load properly.' ), 900 __( 'You can find more details and make changes on the Themes screen.' ), 901 esc_url( admin_url( 'themes.php' ) ), 902 __( 'Go to the Themes screen' ) 903 ); 904 } -
trunk/src/wp-admin/plugins.php
r44717 r44973 390 390 break; 391 391 392 case 'resume': 393 if ( is_multisite() ) { 394 return; 395 } 396 397 if ( ! current_user_can( 'resume_plugin', $plugin ) ) { 398 wp_die( __( 'Sorry, you are not allowed to resume this plugin.' ) ); 399 } 400 401 check_admin_referer( 'resume-plugin_' . $plugin ); 402 403 $result = resume_plugin( $plugin, self_admin_url( "plugins.php?error=resuming&plugin_status=$status&paged=$page&s=$s" ) ); 404 405 if ( is_wp_error( $result ) ) { 406 wp_die( $result ); 407 } 408 409 wp_redirect( self_admin_url( "plugins.php?resume=true&plugin_status=$status&paged=$page&s=$s" ) ); 410 exit; 411 392 412 default: 393 413 if ( isset( $_POST['checked'] ) ) { … … 489 509 ); 490 510 $errmsg .= ' ' . __( 'If you notice “headers already sent” messages, problems with syndication feeds or other issues, try deactivating or removing this plugin.' ); 511 } elseif ( 'resuming' === $_GET['error'] ) { 512 $errmsg = __( 'Plugin could not be resumed because it triggered a <strong>fatal error</strong>.' ); 491 513 } else { 492 514 $errmsg = __( 'Plugin could not be activated because it triggered a <strong>fatal error</strong>.' ); … … 542 564 <?php elseif ( 'update-selected' == $action ) : ?> 543 565 <div id="message" class="updated notice is-dismissible"><p><?php _e( 'All selected plugins are up to date.' ); ?></p></div> 566 <?php elseif ( isset( $_GET['resume'] ) ) : ?> 567 <div id="message" class="updated notice is-dismissible"><p><?php _e( 'Plugin <strong>resumed</strong>.' ); ?></p></div> 544 568 <?php endif; ?> 545 569 -
trunk/src/wp-admin/themes.php
r44717 r44973 33 33 switch_theme( $theme->get_stylesheet() ); 34 34 wp_redirect( admin_url( 'themes.php?activated=true' ) ); 35 exit; 36 } elseif ( 'resume' === $_GET['action'] ) { 37 check_admin_referer( 'resume-theme_' . $_GET['stylesheet'] ); 38 $theme = wp_get_theme( $_GET['stylesheet'] ); 39 40 if ( ! current_user_can( 'resume_theme', $_GET['stylesheet'] ) ) { 41 wp_die( 42 '<h1>' . __( 'You need a higher level of permission.' ) . '</h1>' . 43 '<p>' . __( 'Sorry, you are not allowed to resume this theme.' ) . '</p>', 44 403 45 ); 46 } 47 48 $result = resume_theme( $theme->get_stylesheet(), self_admin_url( 'themes.php?error=resuming' ) ); 49 50 if ( is_wp_error( $result ) ) { 51 wp_die( $result ); 52 } 53 54 wp_redirect( admin_url( 'themes.php?resumed=true' ) ); 35 55 exit; 36 56 } elseif ( 'delete' == $_GET['action'] ) { … … 196 216 <div id="message4" class="error"><p><?php _e( 'You cannot delete a theme while it has an active child theme.' ); ?></p></div> 197 217 <?php 218 } elseif ( isset( $_GET['resumed'] ) ) { 219 ?> 220 <div id="message5" class="updated notice is-dismissible"><p><?php _e( 'Theme resumed.' ); ?></p></div> 221 <?php 222 } elseif ( isset( $_GET['error'] ) && 'resuming' === $_GET['error'] ) { 223 ?> 224 <div id="message6" class="error"><p><?php _e( 'Theme could not be resumed because it triggered a <strong>fatal error</strong>.' ); ?></p></div> 225 <?php 198 226 } 199 227 … … 349 377 350 378 <?php 379 $can_resume = current_user_can( 'resume_themes' ); 351 380 $can_delete = current_user_can( 'delete_themes' ); 352 381 $can_install = current_user_can( 'install_themes' ); … … 356 385 <th><?php _ex( 'Name', 'theme name' ); ?></th> 357 386 <th><?php _e( 'Description' ); ?></th> 387 <?php if ( $can_resume ) { ?> 388 <td></td> 389 <?php } ?> 358 390 <?php if ( $can_delete ) { ?> 359 391 <td></td> … … 368 400 <td><?php echo $broken_theme->errors()->get_error_message(); ?></td> 369 401 <?php 402 if ( $can_resume ) { 403 if ( 'theme_paused' === $broken_theme->errors()->get_error_code() ) { 404 $stylesheet = $broken_theme->get_stylesheet(); 405 $resume_url = add_query_arg( 406 array( 407 'action' => 'resume', 408 'stylesheet' => urlencode( $stylesheet ), 409 ), 410 admin_url( 'themes.php' ) 411 ); 412 $resume_url = wp_nonce_url( $resume_url, 'resume-theme_' . $stylesheet ); 413 ?> 414 <td><a href="<?php echo esc_url( $resume_url ); ?>" class="button resume-theme"><?php _e( 'Resume' ); ?></a></td> 415 <?php 416 } else { 417 ?> 418 <td></td> 419 <?php 420 } 421 } 422 370 423 if ( $can_delete ) { 371 424 $stylesheet = $broken_theme->get_stylesheet(); -
trunk/src/wp-includes/admin-bar.php
r44924 r44973 1049 1049 1050 1050 /** 1051 * Add a link to exit recovery mode when Recovery Mode is active. 1052 * 1053 * @since 5.2.0 1054 * 1055 * @param WP_Admin_Bar $wp_admin_bar 1056 */ 1057 function wp_admin_bar_recovery_mode_menu( $wp_admin_bar ) { 1058 if ( ! wp_is_recovery_mode() ) { 1059 return; 1060 } 1061 1062 $url = wp_login_url(); 1063 $url = add_query_arg( 'action', WP_Recovery_Mode::EXIT_ACTION, $url ); 1064 $url = wp_nonce_url( $url, WP_Recovery_Mode::EXIT_ACTION ); 1065 1066 $wp_admin_bar->add_menu( 1067 array( 1068 'parent' => 'top-secondary', 1069 'id' => 'recovery-mode', 1070 'title' => __( 'Exit Recovery Mode' ), 1071 'href' => $url, 1072 'meta' => array( 1073 'title' => __( 'Exit Recovery Mode' ), 1074 ), 1075 ) 1076 ); 1077 } 1078 1079 /** 1051 1080 * Add secondary menus. 1052 1081 * -
trunk/src/wp-includes/capabilities.php
r44717 r44973 465 465 } 466 466 break; 467 case 'resume_plugin': 468 $caps[] = 'resume_plugins'; 469 break; 470 case 'resume_theme': 471 $caps[] = 'resume_themes'; 472 break; 467 473 case 'delete_user': 468 474 case 'delete_users': … … 951 957 return $allcaps; 952 958 } 959 960 /** 961 * Filters the user capabilities to grant the 'resume_plugins' and 'resume_themes' capabilities as necessary. 962 * 963 * @since 5.2.0 964 * 965 * @param bool[] $allcaps An array of all the user's capabilities. 966 * @return bool[] Filtered array of the user's capabilities. 967 */ 968 function wp_maybe_grant_resume_extensions_caps( $allcaps ) { 969 // Even in a multisite, regular administrators should be able to resume plugins. 970 if ( ! empty( $allcaps['activate_plugins'] ) ) { 971 $allcaps['resume_plugins'] = true; 972 } 973 974 // Even in a multisite, regular administrators should be able to resume themes. 975 if ( ! empty( $allcaps['switch_themes'] ) ) { 976 $allcaps['resume_themes'] = true; 977 } 978 979 return $allcaps; 980 } -
trunk/src/wp-includes/class-wp-admin-bar.php
r44793 r44973 597 597 add_action( 'admin_bar_menu', 'wp_admin_bar_search_menu', 4 ); 598 598 add_action( 'admin_bar_menu', 'wp_admin_bar_my_account_item', 7 ); 599 add_action( 'admin_bar_menu', 'wp_admin_bar_recovery_mode_menu', 8 ); 599 600 600 601 // Site related. -
trunk/src/wp-includes/class-wp-fatal-error-handler.php
r44962 r44973 37 37 if ( ! $error ) { 38 38 return; 39 } 40 41 if ( ! is_multisite() && wp_recovery_mode()->is_initialized() ) { 42 wp_recovery_mode()->handle_error( $error ); 39 43 } 40 44 -
trunk/src/wp-includes/class-wp-theme.php
r44717 r44973 370 370 // Set the parent. Pass the current instance so we can do the crazy checks above and assess errors. 371 371 $this->parent = new WP_Theme( $this->template, isset( $theme_root_template ) ? $theme_root_template : $this->theme_root, $this ); 372 } 373 374 if ( wp_paused_themes()->get( $this->stylesheet ) && ( ! is_wp_error( $this->errors ) || ! isset( $this->errors->errors['theme_paused'] ) ) ) { 375 $this->errors = new WP_Error( 'theme_paused', __( 'This theme failed to load properly and was paused within the admin backend.' ) ); 372 376 } 373 377 -
trunk/src/wp-includes/default-constants.php
r44151 r44973 303 303 define( 'COOKIE_DOMAIN', false ); 304 304 } 305 306 if ( ! defined( 'RECOVERY_MODE_COOKIE' ) ) { 307 /** 308 * @since 5.2.0 309 */ 310 define( 'RECOVERY_MODE_COOKIE', 'wordpress_rec_' . COOKIEHASH ); 311 } 305 312 } 306 313 -
trunk/src/wp-includes/default-filters.php
r44942 r44973 580 580 // Capabilities 581 581 add_filter( 'user_has_cap', 'wp_maybe_grant_install_languages_cap', 1 ); 582 add_filter( 'user_has_cap', 'wp_maybe_grant_resume_extensions_caps', 1 ); 582 583 583 584 unset( $filter, $action ); -
trunk/src/wp-includes/error-protection.php
r44962 r44973 6 6 * @since 5.2.0 7 7 */ 8 9 /** 10 * Get the instance for storing paused plugins. 11 * 12 * @return WP_Paused_Extensions_Storage 13 */ 14 function wp_paused_plugins() { 15 static $storage = null; 16 17 if ( null === $storage ) { 18 $storage = new WP_Paused_Extensions_Storage( 'plugin' ); 19 } 20 21 return $storage; 22 } 23 24 /** 25 * Get the instance for storing paused extensions. 26 * 27 * @return WP_Paused_Extensions_Storage 28 */ 29 function wp_paused_themes() { 30 static $storage = null; 31 32 if ( null === $storage ) { 33 $storage = new WP_Paused_Extensions_Storage( 'theme' ); 34 } 35 36 return $storage; 37 } 38 39 /** 40 * Get a human readable description of an extension's error. 41 * 42 * @since 5.2.0 43 * 44 * @param array $error Error details {@see error_get_last()} 45 * 46 * @return string Formatted error description. 47 */ 48 function wp_get_extension_error_description( $error ) { 49 $constants = get_defined_constants( true ); 50 $constants = isset( $constants['Core'] ) ? $constants['Core'] : $constants['internal']; 51 $core_errors = array(); 52 53 foreach ( $constants as $constant => $value ) { 54 if ( 0 === strpos( $constant, 'E_' ) ) { 55 $core_errors[ $value ] = $constant; 56 } 57 } 58 59 if ( isset( $core_errors[ $error['type'] ] ) ) { 60 $error['type'] = $core_errors[ $error['type'] ]; 61 } 62 63 /* translators: 1: error type, 2: error line number, 3: error file name, 4: error message */ 64 $error_message = __( 'An error of type %1$s was caused in line %2$s of the file %3$s. Error message: %4$s' ); 65 66 return sprintf( 67 $error_message, 68 "<code>{$error['type']}</code>", 69 "<code>{$error['line']}</code>", 70 "<code>{$error['file']}</code>", 71 "<code>{$error['message']}</code>" 72 ); 73 } 8 74 9 75 /** … … 53 119 return apply_filters( 'wp_fatal_error_handler_enabled', $enabled ); 54 120 } 121 122 /** 123 * Access the WordPress Recovery Mode instance. 124 * 125 * @since 5.2.0 126 * 127 * @return WP_Recovery_Mode 128 */ 129 function wp_recovery_mode() { 130 static $wp_recovery_mode; 131 132 if ( ! $wp_recovery_mode ) { 133 $wp_recovery_mode = new WP_Recovery_Mode(); 134 } 135 136 return $wp_recovery_mode; 137 } -
trunk/src/wp-includes/load.php
r44717 r44973 698 698 } 699 699 700 /* 701 * Remove plugins from the list of active plugins when we're on an endpoint 702 * that should be protected against WSODs and the plugin is paused. 703 */ 704 if ( wp_is_recovery_mode() ) { 705 $plugins = wp_skip_paused_plugins( $plugins ); 706 } 707 708 return $plugins; 709 } 710 711 /** 712 * Filters a given list of plugins, removing any paused plugins from it. 713 * 714 * @since 5.2.0 715 * 716 * @param array $plugins List of absolute plugin main file paths. 717 * @return array Filtered value of $plugins, without any paused plugins. 718 */ 719 function wp_skip_paused_plugins( array $plugins ) { 720 $paused_plugins = wp_paused_plugins()->get_all(); 721 722 if ( empty( $paused_plugins ) ) { 723 return $plugins; 724 } 725 726 foreach ( $plugins as $index => $plugin ) { 727 list( $plugin ) = explode( '/', plugin_basename( $plugin ) ); 728 729 if ( array_key_exists( $plugin, $paused_plugins ) ) { 730 unset( $plugins[ $index ] ); 731 732 // Store list of paused plugins for displaying an admin notice. 733 $GLOBALS['_paused_plugins'][ $plugin ] = $paused_plugins[ $plugin ]; 734 } 735 } 736 700 737 return $plugins; 701 738 } … … 726 763 $themes[] = TEMPLATEPATH; 727 764 765 /* 766 * Remove themes from the list of active themes when we're on an endpoint 767 * that should be protected against WSODs and the theme is paused. 768 */ 769 if ( wp_is_recovery_mode() ) { 770 $themes = wp_skip_paused_themes( $themes ); 771 772 // If no active and valid themes exist, skip loading themes. 773 if ( empty( $themes ) ) { 774 add_filter( 'wp_using_themes', '__return_false' ); 775 } 776 } 777 728 778 return $themes; 779 } 780 781 /** 782 * Filters a given list of themes, removing any paused themes from it. 783 * 784 * @since 5.2.0 785 * 786 * @param array $themes List of absolute theme directory paths. 787 * @return array Filtered value of $themes, without any paused themes. 788 */ 789 function wp_skip_paused_themes( array $themes ) { 790 $paused_themes = wp_paused_themes()->get_all(); 791 792 if ( empty( $paused_themes ) ) { 793 return $themes; 794 } 795 796 foreach ( $themes as $index => $theme ) { 797 $theme = basename( $theme ); 798 799 if ( array_key_exists( $theme, $paused_themes ) ) { 800 unset( $themes[ $index ] ); 801 802 // Store list of paused themes for displaying an admin notice. 803 $GLOBALS['_paused_themes'][ $theme ] = $paused_themes[ $theme ]; 804 } 805 } 806 807 return $themes; 808 } 809 810 /** 811 * Is WordPress in Recovery Mode. 812 * 813 * In this mode, plugins or themes that cause WSODs will be paused. 814 * 815 * @since 5.2.0 816 * 817 * @return bool 818 */ 819 function wp_is_recovery_mode() { 820 return wp_recovery_mode()->is_active(); 821 } 822 823 /** 824 * Determines whether we are currently on an endpoint that should be protected against WSODs. 825 * 826 * @since 5.2.0 827 * 828 * @return bool True if the current endpoint should be protected. 829 */ 830 function is_protected_endpoint() { 831 // Protect login pages. 832 if ( isset( $GLOBALS['pagenow'] ) && 'wp-login.php' === $GLOBALS['pagenow'] ) { 833 return true; 834 } 835 836 // Protect the admin backend. 837 if ( is_admin() && ! wp_doing_ajax() ) { 838 return true; 839 } 840 841 // Protect AJAX actions that could help resolve a fatal error should be available. 842 if ( is_protected_ajax_action() ) { 843 return true; 844 } 845 846 /** 847 * Filters whether the current request is against a protected endpoint. 848 * 849 * This filter is only fired when an endpoint is requested which is not already protected by 850 * WordPress core. As such, it exclusively allows providing further protected endpoints in 851 * addition to the admin backend, login pages and protected AJAX actions. 852 * 853 * @since 5.2.0 854 * 855 * @param bool $is_protected_endpoint Whether the currently requested endpoint is protected. Default false. 856 */ 857 return (bool) apply_filters( 'is_protected_endpoint', false ); 858 } 859 860 /** 861 * Determines whether we are currently handling an AJAX action that should be protected against WSODs. 862 * 863 * @since 5.2.0 864 * 865 * @return bool True if the current AJAX action should be protected. 866 */ 867 function is_protected_ajax_action() { 868 if ( ! wp_doing_ajax() ) { 869 return false; 870 } 871 872 if ( ! isset( $_REQUEST['action'] ) ) { 873 return false; 874 } 875 876 $actions_to_protect = array( 877 'edit-theme-plugin-file', // Saving changes in the core code editor. 878 'heartbeat', // Keep the heart beating. 879 'install-plugin', // Installing a new plugin. 880 'install-theme', // Installing a new theme. 881 'search-plugins', // Searching in the list of plugins. 882 'search-install-plugins', // Searching for a plugin in the plugin install screen. 883 'update-plugin', // Update an existing plugin. 884 'update-theme', // Update an existing theme. 885 ); 886 887 /** 888 * Filters the array of protected AJAX actions. 889 * 890 * This filter is only fired when doing AJAX and the AJAX request has an 'action' property. 891 * 892 * @since 5.2.0 893 * 894 * @param array $actions_to_protect Array of strings with AJAX actions to protect. 895 */ 896 $actions_to_protect = (array) apply_filters( 'wp_protected_ajax_actions', $actions_to_protect ); 897 898 if ( ! in_array( $_REQUEST['action'], $actions_to_protect, true ) ) { 899 return false; 900 } 901 902 return true; 729 903 } 730 904 -
trunk/src/wp-login.php
r44932 r44973 440 440 441 441 // Validate action so as to default to the login screen. 442 if ( ! in_array( $action, array( 'postpass', 'logout', 'lostpassword', 'retrievepassword', 'resetpass', 'rp', 'register', 'login', 'confirmaction' ), true ) && false === has_filter( 'login_form_' . $action ) ) {442 if ( ! in_array( $action, array( 'postpass', 'logout', 'lostpassword', 'retrievepassword', 'resetpass', 'rp', 'register', 'login', 'confirmaction', WP_Recovery_Mode_Link_Service::LOGIN_ACTION_ENTERED ), true ) && false === has_filter( 'login_form_' . $action ) ) { 443 443 $action = 'login'; 444 444 } … … 1029 1029 } elseif ( strpos( $redirect_to, 'about.php?updated' ) ) { 1030 1030 $errors->add( 'updated', __( '<strong>You have successfully updated WordPress!</strong> Please log back in to see what’s new.' ), 'message' ); 1031 } elseif ( WP_Recovery_Mode_Link_Service::LOGIN_ACTION_ENTERED === $action ) { 1032 $errors->add( 'enter_recovery_mode', __( 'Recovery Mode Initialized. Please log in to continue.' ), 'message' ); 1031 1033 } 1032 1034 } -
trunk/src/wp-settings.php
r44962 r44973 18 18 // Include files required for initialization. 19 19 require( ABSPATH . WPINC . '/load.php' ); 20 require( ABSPATH . WPINC . '/class-wp-paused-extensions-storage.php' ); 20 21 require( ABSPATH . WPINC . '/class-wp-fatal-error-handler.php' ); 22 require( ABSPATH . WPINC . '/class-wp-recovery-mode-cookie-service.php' ); 23 require( ABSPATH . WPINC . '/class-wp-recovery-mode-key-service.php' ); 24 require( ABSPATH . WPINC . '/class-wp-recovery-mode-link-service.php' ); 25 require( ABSPATH . WPINC . '/class-wp-recovery-mode-email-service.php' ); 26 require( ABSPATH . WPINC . '/class-wp-recovery-mode.php' ); 21 27 require( ABSPATH . WPINC . '/error-protection.php' ); 22 28 require( ABSPATH . WPINC . '/default-constants.php' ); … … 346 352 register_theme_directory( get_theme_root() ); 347 353 354 if ( ! is_multisite() ) { 355 // Handle users requesting a recovery mode link and initiating recovery mode. 356 wp_recovery_mode()->initialize(); 357 } 358 348 359 // Load active plugins. 349 360 foreach ( wp_get_active_and_valid_plugins() as $plugin ) { -
trunk/tests/phpunit/tests/user/capabilities.php
r44717 r44973 102 102 'switch_themes' => array( 'administrator' ), 103 103 'edit_dashboard' => array( 'administrator' ), 104 'resume_plugins' => array( 'administrator' ), 105 'resume_themes' => array( 'administrator' ), 104 106 105 107 'moderate_comments' => array( 'administrator', 'editor' ), … … 182 184 'switch_themes' => array( 'administrator' ), 183 185 'edit_dashboard' => array( 'administrator' ), 186 'resume_plugins' => array( 'administrator' ), 187 'resume_themes' => array( 'administrator' ), 184 188 185 189 'moderate_comments' => array( 'administrator', 'editor' ), … … 393 397 $actual['author'], 394 398 $actual['subscriber'], 395 $actual['contributor'] 399 $actual['contributor'], 400 // the following two are granted via `user_has_cap`: 401 $actual['resume_plugins'], 402 $actual['resume_themes'] 396 403 ); 397 404 … … 455 462 $expected['activate_plugin'], 456 463 $expected['deactivate_plugin'], 464 $expected['resume_plugin'], 465 $expected['resume_theme'], 457 466 $expected['remove_user'], 458 467 $expected['promote_user'],
Note: See TracChangeset
for help on using the changeset viewer.