Ticket #46141: 46141.diff
File 46141.diff, 54.9 KB (added by , 4 years ago) |
---|
-
src/wp-admin/css/list-tables.css
1301 1301 text-decoration: underline; 1302 1302 } 1303 1303 1304 .plugins tr.paused th.check-column {1305 border-left: 4px solid #d54e21;1306 }1307 1308 .plugins tr.paused th,1309 .plugins tr.paused td {1310 background-color: #fef7f1;1311 }1312 1313 .plugins tr.paused .plugin-title,1314 .plugins .paused .dashicons-warning {1315 color: #dc3232;1316 }1317 1318 .plugins .paused .error-display p,1319 .plugins .paused .error-display code {1320 font-size: 90%;1321 font-style: italic;1322 color: rgb( 0, 0, 0, 0.7 );1323 }1324 1325 .plugins .resume-link {1326 color: #dc3232;1327 }1328 1329 1304 .plugin-card .update-now:before { 1330 1305 color: #f56e28; 1331 1306 content: "\f463"; -
src/wp-admin/includes/admin-filters.php
117 117 add_action( 'load-themes.php', 'wp_theme_update_rows', 20 ); // After wp_update_themes() is called. 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 );122 120 add_action( 'admin_notices', 'maintenance_nag', 10 ); 123 121 124 122 add_filter( 'update_footer', 'core_update_footer' ); -
src/wp-admin/includes/class-wp-plugins-list-table.php
40 40 ); 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' , 'paused') ) ) {43 if ( isset( $_REQUEST['plugin_status'] ) && in_array( $_REQUEST['plugin_status'], array( 'active', 'inactive', 'recently_activated', 'upgrade', 'mustuse', 'dropins', 'search' ) ) ) { 44 44 $status = $_REQUEST['plugin_status']; 45 45 } 46 46 … … 99 99 'upgrade' => array(), 100 100 'mustuse' => array(), 101 101 'dropins' => array(), 102 'paused' => array(),103 102 ); 104 103 105 104 $screen = $this->screen; … … 210 209 if ( $show_network_active ) { 211 210 // On the non-network screen, show network-active plugins if allowed 212 211 $plugins['active'][ $plugin_file ] = $plugin_data; 213 if ( is_plugin_paused( $plugin_file ) ) {214 $plugins['paused'][ $plugin_file ] = $plugin_data;215 }216 212 } else { 217 213 // On the non-network screen, filter out network-active plugins 218 214 unset( $plugins['all'][ $plugin_file ] ); … … 222 218 // On the non-network screen, populate the active list with plugins that are individually activated 223 219 // On the network-admin screen, populate the active list with plugins that are network activated 224 220 $plugins['active'][ $plugin_file ] = $plugin_data; 225 if ( is_plugin_paused( $plugin_file ) ) {226 $plugins['paused'][ $plugin_file ] = $plugin_data;227 }228 221 } else { 229 222 if ( isset( $recently_activated[ $plugin_file ] ) ) { 230 223 // Populate the recently activated list with plugins that have been recently activated … … 452 445 /* translators: %s: plugin count */ 453 446 $text = _n( 'Drop-ins <span class="count">(%s)</span>', 'Drop-ins <span class="count">(%s)</span>', $count ); 454 447 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 );458 break;459 448 case 'upgrade': 460 449 /* translators: %s: plugin count */ 461 450 $text = _n( 'Update Available <span class="count">(%s)</span>', 'Update Available <span class="count">(%s)</span>', $count ); … … 644 633 /* translators: %s: plugin name */ 645 634 $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( 'Network Deactivate %s', 'plugin' ), $plugin_data['Name'] ) ) . '">' . __( 'Network Deactivate' ) . '</a>'; 646 635 } 647 if ( current_user_can( 'manage_network_plugins' ) && count_paused_plugin_sites_for_network( $plugin_file ) ) {648 /* translators: %s: plugin name */649 $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( 'Network Resume %s', 'plugin' ), $plugin_data['Name'] ) ) . '">' . __( 'Network Resume' ) . '</a>';650 }651 636 } else { 652 637 if ( current_user_can( 'manage_network_plugins' ) ) { 653 638 /* translators: %s: plugin name */ 654 639 $actions['activate'] = '<a href="' . wp_nonce_url( 'plugins.php?action=activate&plugin=' . urlencode( $plugin_file ) . '&plugin_status=' . $context . '&paged=' . $page . '&s=' . $s, 'activate-plugin_' . $plugin_file ) . '" class="edit" aria-label="' . esc_attr( sprintf( _x( 'Network Activate %s', 'plugin' ), $plugin_data['Name'] ) ) . '">' . __( 'Network Activate' ) . '</a>'; 655 640 } 656 if ( current_user_can( 'manage_network_plugins' ) && count_paused_plugin_sites_for_network( $plugin_file ) ) {657 /* translators: %s: plugin name */658 $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( 'Network Resume %s', 'plugin' ), $plugin_data['Name'] ) ) . '">' . __( 'Network Resume' ) . '</a>';659 }660 641 if ( current_user_can( 'delete_plugins' ) && ! is_plugin_active( $plugin_file ) ) { 661 642 /* translators: %s: plugin name */ 662 643 $actions['delete'] = '<a href="' . wp_nonce_url( 'plugins.php?action=delete-selected&checked[]=' . urlencode( $plugin_file ) . '&plugin_status=' . $context . '&paged=' . $page . '&s=' . $s, 'bulk-plugins' ) . '" class="delete" aria-label="' . esc_attr( sprintf( _x( 'Delete %s', 'plugin' ), $plugin_data['Name'] ) ) . '">' . __( 'Delete' ) . '</a>'; … … 667 648 $actions = array( 668 649 'network_active' => __( 'Network Active' ), 669 650 ); 670 if ( ! $restrict_network_only && current_user_can( 'resume_plugin', $plugin_file ) && is_plugin_paused( $plugin_file ) ) {671 /* translators: %s: plugin name */672 $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>';673 }674 651 } elseif ( $restrict_network_only ) { 675 652 $actions = array( 676 653 'network_only' => __( 'Network Only' ), … … 680 657 /* translators: %s: plugin name */ 681 658 $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>'; 682 659 } 683 if ( current_user_can( 'resume_plugin', $plugin_file ) && is_plugin_paused( $plugin_file ) ) {684 /* translators: %s: plugin name */685 $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>';686 }687 660 } else { 688 661 if ( current_user_can( 'activate_plugin', $plugin_file ) ) { 689 662 /* translators: %s: plugin name */ … … 791 764 $class .= ' update'; 792 765 } 793 766 794 $paused = is_plugin_paused( $plugin_file );795 $paused_on_network_sites_count = $screen->in_admin( 'network' ) ? count_paused_plugin_sites_for_network( $plugin_file ) : 0;796 if ( $paused || $paused_on_network_sites_count ) {797 $class .= ' paused';798 }799 800 767 $plugin_slug = isset( $plugin_data['slug'] ) ? $plugin_data['slug'] : sanitize_title( $plugin_name ); 801 768 printf( 802 769 '<tr class="%s" data-slug="%s" data-plugin="%s">', … … 878 845 * @param array $plugin_data An array of plugin data. 879 846 * @param string $status Status of the plugin. Defaults are 'All', 'Active', 880 847 * 'Inactive', 'Recently Activated', 'Upgrade', 'Must-Use', 881 * 'Drop-ins', 'Search' , 'Paused'.848 * 'Drop-ins', 'Search'. 882 849 */ 883 850 $plugin_meta = apply_filters( 'plugin_row_meta', $plugin_meta, $plugin_file, $plugin_data, $status ); 884 851 echo implode( ' | ', $plugin_meta ); … … 885 852 886 853 echo '</div>'; 887 854 888 if ( $paused || $paused_on_network_sites_count ) {889 $notice_text = __( 'This plugin failed to load properly and was paused within the admin backend.' );890 if ( $screen->in_admin( 'network' ) && $paused_on_network_sites_count ) {891 $notice_text = sprintf(892 /* translators: %s: number of sites */893 _n( 'This plugin failed to load properly and was paused within the admin backend for %s site.', 'This plugin failed to load properly and was paused within the admin backend for %s sites.', $paused_on_network_sites_count ),894 number_format_i18n( $paused_on_network_sites_count )895 );896 }897 898 printf( '<p><span class="dashicons dashicons-warning"></span> <strong>%s</strong></p>', $notice_text );899 900 $error = wp_get_plugin_error( $plugin_file );901 902 if ( false !== $error ) {903 $constants = get_defined_constants( true );904 $constants = isset( $constants['Core'] ) ? $constants['Core'] : $constants['internal'];905 906 foreach ( $constants as $constant => $value ) {907 if ( 0 === strpos( $constant, 'E_' ) ) {908 $core_errors[ $value ] = $constant;909 }910 }911 912 $error['type'] = $core_errors[ $error['type'] ];913 914 printf(915 '<div class="error-display"><p>%s</p></div>',916 sprintf(917 /* translators: 1: error type, 2: error line number, 3: error file name, 4: error message */918 __( 'The plugin caused an error of type %1$s in line %2$s of the file %3$s. Error message: %4$s' ),919 "<code>{$error['type']}</code>",920 "<code>{$error['line']}</code>",921 "<code>{$error['file']}</code>",922 "<code>{$error['message']}</code>"923 )924 );925 }926 }927 928 855 echo '</td>'; 929 856 break; 930 857 default: … … 958 885 * @param array $plugin_data An array of plugin data. 959 886 * @param string $status Status of the plugin. Defaults are 'All', 'Active', 960 887 * 'Inactive', 'Recently Activated', 'Upgrade', 'Must-Use', 961 * 'Drop-ins', 'Search' , 'Paused'.888 * 'Drop-ins', 'Search'. 962 889 */ 963 890 do_action( 'after_plugin_row', $plugin_file, $plugin_data, $status ); 964 891 … … 974 901 * @param array $plugin_data An array of plugin data. 975 902 * @param string $status Status of the plugin. Defaults are 'All', 'Active', 976 903 * 'Inactive', 'Recently Activated', 'Upgrade', 'Must-Use', 977 * 'Drop-ins', 'Search' , 'Paused'.904 * 'Drop-ins', 'Search'. 978 905 */ 979 906 do_action( "after_plugin_row_{$plugin_file}", $plugin_file, $plugin_data, $status ); 980 907 } -
src/wp-admin/includes/plugin.php
468 468 */ 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 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 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 479 477 ); 480 478 481 479 if ( is_multisite() ) { … … 529 527 } 530 528 531 529 /** 532 * Determines whether a plugin is technically active but was paused while533 * loading.534 *535 * For more information on this and similar theme functions, check out536 * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/537 * Conditional Tags} article in the Theme Developer Handbook.538 *539 * @since 5.1.0540 *541 * @param string $plugin Path to the plugin file relative to the plugins directory.542 * @return bool True, if in the list of paused plugins. False, not in the list.543 */544 function is_plugin_paused( $plugin ) {545 if ( ! isset( $GLOBALS['_paused_plugins'] ) ) {546 return false;547 }548 549 if ( ! is_plugin_active( $plugin ) && ! is_plugin_active_for_network( $plugin ) ) {550 return false;551 }552 553 list( $plugin ) = explode( '/', $plugin );554 555 return array_key_exists( $plugin, $GLOBALS['_paused_plugins'] );556 }557 558 /**559 * Gets the error that was recorded for a paused plugin.560 *561 * @since 5.1.0562 *563 * @param string $plugin Path to the plugin file relative to the plugins564 * directory.565 * @return array|false Array of error information as it was returned by566 * `error_get_last()`, or false if none was recorded.567 */568 function wp_get_plugin_error( $plugin ) {569 if ( ! isset( $GLOBALS['_paused_plugins'] ) ) {570 return false;571 }572 573 list( $plugin ) = explode( '/', $plugin );574 575 if ( ! array_key_exists( $plugin, $GLOBALS['_paused_plugins'] ) ) {576 return false;577 }578 579 return $GLOBALS['_paused_plugins'][ $plugin ];580 }581 582 /**583 * Gets the number of sites on which a specific plugin is paused.584 *585 * @since 5.1.0586 *587 * @param string $plugin Path to the plugin file relative to the plugins directory.588 * @return int Site count.589 */590 function count_paused_plugin_sites_for_network( $plugin ) {591 if ( ! is_multisite() ) {592 return is_plugin_paused( $plugin ) ? 1 : 0;593 }594 595 list( $plugin ) = explode( '/', $plugin );596 597 $query_args = array(598 'count' => true,599 'number' => 0,600 'network_id' => get_current_network_id(),601 'meta_query' => array(602 wp_paused_plugins()->get_site_meta_query_clause( $plugin ),603 ),604 );605 606 return get_sites( $query_args );607 }608 609 /**610 530 * Determines whether the plugin is active for the entire network. 611 531 * 612 532 * Only plugins installed in the plugins/ folder can be active. … … 803 723 continue; 804 724 } 805 725 806 // Clean up the database before deactivating the plugin.807 if ( is_plugin_paused( $plugin ) ) {808 resume_plugin( $plugin );809 }810 811 726 $network_deactivating = false !== $network_wide && is_plugin_active_for_network( $plugin ); 812 727 813 728 if ( ! $silent ) { … … 1002 917 uninstall_plugin( $plugin_file ); 1003 918 } 1004 919 1005 // Clean up the database before removing the plugin.1006 if ( is_plugin_paused( $plugin_file ) ) {1007 resume_plugin( $plugin_file );1008 }1009 1010 920 /** 1011 921 * Fires immediately before a plugin deletion attempt. 1012 922 * … … 1085 995 } 1086 996 1087 997 /** 1088 * Tries to resume a single plugin.1089 *1090 * If a redirect was provided, we first ensure the plugin does not throw fatal1091 * errors anymore.1092 *1093 * The way it works is by setting the redirection to the error before trying to1094 * include the plugin file. If the plugin fails, then the redirection will not1095 * be overwritten with the success message and the plugin will not be resumed.1096 *1097 * @since 5.1.01098 *1099 * @param string $plugin Single plugin to resume.1100 * @param string $redirect Optional. URL to redirect to. Default empty string.1101 * @param bool $network_wide Optional. Whether to resume the plugin for the entire1102 * network. Default false.1103 * @return bool|WP_Error True on success, false if `$plugin` was not paused,1104 * `WP_Error` on failure.1105 */1106 function resume_plugin( $plugin, $redirect = '', $network_wide = false ) {1107 /*1108 * We'll override this later if the plugin could be included without1109 * creating a fatal error.1110 */1111 if ( ! empty( $redirect ) ) {1112 wp_redirect(1113 add_query_arg(1114 '_error_nonce',1115 wp_create_nonce( 'plugin-resume-error_' . $plugin ),1116 $redirect1117 )1118 );1119 1120 // Load the plugin to test whether it throws a fatal error.1121 ob_start();1122 plugin_sandbox_scrape( $plugin );1123 ob_clean();1124 }1125 1126 $result = wp_forget_extension_error( 'plugins', $plugin, $network_wide );1127 1128 if ( ! $result ) {1129 return new WP_Error(1130 'could_not_resume_plugin',1131 __( 'Could not resume the plugin.' )1132 );1133 }1134 1135 return true;1136 }1137 1138 /**1139 998 * Validate active plugins 1140 999 * 1141 1000 * Validate all active plugins, deactivates invalid and … … 2242 2101 2243 2102 WP_Privacy_Policy_Content::add( $plugin_name, $policy_text ); 2244 2103 } 2245 2246 /**2247 * Renders an admin notice in case some plugins have been paused due to errors.2248 *2249 * @since 5.1.02250 */2251 function paused_plugins_notice() {2252 if ( 'plugins.php' === $GLOBALS['pagenow'] ) {2253 return;2254 }2255 2256 if ( ! current_user_can( 'deactivate_plugins' ) ) {2257 return;2258 }2259 2260 if ( ! isset( $GLOBALS['_paused_plugins'] ) || empty( $GLOBALS['_paused_plugins'] ) ) {2261 return;2262 }2263 2264 printf(2265 '<div class="notice notice-error"><p><strong>%s</strong><br>%s</p><p>%s</p></div>',2266 __( 'One or more plugins failed to load properly.' ),2267 __( 'You can find more details and make changes on the Plugins screen.' ),2268 sprintf(2269 '<a href="%s">%s</a>',2270 admin_url( 'plugins.php?plugin_status=paused' ),2271 'Go to the Plugins screen'2272 )2273 );2274 } -
src/wp-admin/includes/theme.php
768 768 </script> 769 769 <?php 770 770 } 771 772 /**773 * Determines whether a theme is technically active but was paused while774 * loading.775 *776 * For more information on this and similar theme functions, check out777 * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/778 * Conditional Tags} article in the Theme Developer Handbook.779 *780 * @since 5.1.0781 *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 ( $theme !== get_stylesheet() && $theme !== get_template() ) {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.1.0801 *802 * @param string $theme Path to the theme directory relative to the themes803 * directory.804 * @return array|false Array of error information as it was returned by805 * `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 * Gets the number of sites on which a specific theme is paused.821 *822 * @since 5.1.0823 *824 * @param string $theme Path to the theme directory relative to the themes directory.825 * @return int Site count.826 */827 function count_paused_theme_sites_for_network( $theme ) {828 if ( ! is_multisite() ) {829 return is_theme_paused( $theme ) ? 1 : 0;830 }831 832 $query_args = array(833 'count' => true,834 'number' => 0,835 'network_id' => get_current_network_id(),836 'meta_query' => array(837 wp_paused_themes()->get_site_meta_query_clause( $theme ),838 ),839 );840 841 return get_sites( $query_args );842 }843 844 /**845 * Tries to resume a single theme.846 *847 * @since 5.1.0848 *849 * @param string $theme Single theme to resume.850 * @return bool|WP_Error True on success, false if `$theme` was not paused,851 * `WP_Error` on failure.852 */853 function resume_theme( $theme ) {854 $result = wp_forget_extension_error( 'themes', $theme );855 856 if ( ! $result ) {857 return new WP_Error(858 'could_not_resume_theme',859 __( 'Could not resume the theme.' )860 );861 }862 863 return true;864 }865 866 /**867 * Renders an admin notice in case some themes have been paused due to errors.868 *869 * @since 5.1.0870 */871 function paused_themes_notice() {872 if ( 'themes.php' === $GLOBALS['pagenow'] ) {873 return;874 }875 876 if ( ! current_user_can( 'switch_themes' ) ) {877 return;878 }879 880 if ( ! isset( $GLOBALS['_paused_themes'] ) || empty( $GLOBALS['_paused_themes'] ) ) {881 return;882 }883 884 printf(885 '<div class="notice notice-error"><p><strong>%s</strong><br>%s</p><p>%s</p></div>',886 __( 'One or more themes failed to load properly.' ),887 __( 'You can find more details and make changes on the Themes screen.' ),888 sprintf(889 '<a href="%s">%s</a>',890 admin_url( 'themes.php' ),891 'Go to the Themes screen'892 )893 );894 } -
src/wp-admin/plugins.php
389 389 } 390 390 break; 391 391 392 case 'resume':393 if ( ! current_user_can( 'resume_plugin', $plugin ) ) {394 wp_die( __( 'Sorry, you are not allowed to resume this plugin.' ) );395 }396 397 if ( is_multisite() && ! is_network_admin() && is_network_only_plugin( $plugin ) ) {398 wp_redirect( self_admin_url( "plugins.php?plugin_status=$status&paged=$page&s=$s" ) );399 exit;400 }401 402 check_admin_referer( 'resume-plugin_' . $plugin );403 404 $result = resume_plugin( $plugin, self_admin_url( 'plugins.php?error=resuming' ), is_network_admin() );405 406 if ( is_wp_error( $result ) ) {407 wp_die( $result );408 }409 410 wp_redirect( self_admin_url( "plugins.php?resume=true&plugin_status=$status&paged=$page&s=$s" ) );411 exit;412 413 392 default: 414 393 if ( isset( $_POST['checked'] ) ) { 415 394 check_admin_referer( 'bulk-plugins' ); … … 509 488 $_GET['charsout'] 510 489 ); 511 490 $errmsg .= ' ' . __( 'If you notice “headers already sent” messages, problems with syndication feeds or other issues, try deactivating or removing this plugin.' ); 512 } elseif ( 'resuming' === $_GET['error'] ) {513 $errmsg = __( 'Plugin could not be resumed because it triggered a <strong>fatal error</strong>.' );514 491 } else { 515 492 $errmsg = __( 'Plugin could not be activated because it triggered a <strong>fatal error</strong>.' ); 516 493 } … … 564 541 <div id="message" class="updated notice is-dismissible"><p><?php _e( 'Selected plugins <strong>deactivated</strong>.' ); ?></p></div> 565 542 <?php elseif ( 'update-selected' == $action ) : ?> 566 543 <div id="message" class="updated notice is-dismissible"><p><?php _e( 'All selected plugins are up to date.' ); ?></p></div> 567 <?php elseif ( isset( $_GET['resume'] ) ) : ?>568 <div id="message" class="updated notice is-dismissible"><p><?php _e( 'Plugin <strong>resumed</strong>.' ); ?></p></div>569 544 <?php endif; ?> 570 545 571 546 <div class="wrap"> -
src/wp-admin/themes.php
33 33 switch_theme( $theme->get_stylesheet() ); 34 34 wp_redirect( admin_url( 'themes.php?activated=true' ) ); 35 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_themes' ) ) {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 40345 );46 }47 48 $result = resume_theme( $theme->get_stylesheet() );49 50 if ( is_wp_error( $result ) ) {51 wp_die( $result );52 }53 54 wp_redirect( admin_url( 'themes.php?resumed=true' ) );55 exit;56 36 } elseif ( 'delete' == $_GET['action'] ) { 57 37 check_admin_referer( 'delete-theme_' . $_GET['stylesheet'] ); 58 38 $theme = wp_get_theme( $_GET['stylesheet'] ); … … 215 195 ?> 216 196 <div id="message4" class="error"><p><?php _e( 'You cannot delete a theme while it has an active child theme.' ); ?></p></div> 217 197 <?php 218 } elseif ( isset( $_GET['resumed'] ) ) {219 ?>220 <div id="message5" class="updated notice is-dismissible"><p><?php _e( 'Theme resumed.' ); ?></p></div>221 <?php222 198 } 223 199 224 200 $ct = wp_get_theme(); … … 372 348 <p><?php _e( 'The following themes are installed but incomplete.' ); ?></p> 373 349 374 350 <?php 375 $can_resume = current_user_can( 'resume_themes' );376 351 $can_delete = current_user_can( 'delete_themes' ); 377 352 $can_install = current_user_can( 'install_themes' ); 378 353 ?> … … 380 355 <tr> 381 356 <th><?php _ex( 'Name', 'theme name' ); ?></th> 382 357 <th><?php _e( 'Description' ); ?></th> 383 <?php if ( $can_resume ) { ?>384 <td></td>385 <?php } ?>386 358 <?php if ( $can_delete ) { ?> 387 359 <td></td> 388 360 <?php } ?> … … 395 367 <td><?php echo $broken_theme->get( 'Name' ) ? $broken_theme->display( 'Name' ) : $broken_theme->get_stylesheet(); ?></td> 396 368 <td><?php echo $broken_theme->errors()->get_error_message(); ?></td> 397 369 <?php 398 if ( $can_resume ) {399 if ( 'theme_paused' === $broken_theme->errors()->get_error_code() ) {400 $stylesheet = $broken_theme->get_stylesheet();401 $resume_url = add_query_arg(402 array(403 'action' => 'resume',404 'stylesheet' => urlencode( $stylesheet ),405 ),406 admin_url( 'themes.php' )407 );408 $resume_url = wp_nonce_url( $resume_url, 'resume-theme_' . $stylesheet );409 ?>410 <td><a href="<?php echo esc_url( $resume_url ); ?>" class="button resume-theme"><?php _e( 'Resume' ); ?></a></td>411 <?php412 } else {413 ?>414 <td></td>415 <?php416 }417 }418 419 370 if ( $can_delete ) { 420 371 $stylesheet = $broken_theme->get_stylesheet(); 421 372 $delete_url = add_query_arg( -
src/wp-includes/capabilities.php
464 464 } 465 465 } 466 466 break; 467 case 'resume_plugin':468 // Even in a multisite, regular administrators should be able to resume a plugin.469 $caps[] = 'activate_plugins';470 break;471 case 'resume_themes':472 // Even in a multisite, regular administrators should be able to resume a theme.473 $caps[] = 'switch_themes';474 break;475 467 case 'delete_user': 476 468 case 'delete_users': 477 469 // If multisite only super admins can delete users. -
src/wp-includes/class-wp-fatal-error-handler.php
1 <?php2 /**3 * Error Protection API: WP_Fatal_Error_Handler class4 *5 * @package WordPress6 * @since 5.1.07 */8 9 /**10 * Core class used as the default shutdown handler for fatal errors.11 *12 * A drop-in 'fatal-error-handler.php' can be used to override the instance of this class and use a custom13 * implementation for the fatal error handler that WordPress registers. The custom class should extend this class and14 * can override its methods individually as necessary. The file must return the instance of the class that should be15 * registered.16 *17 * @since 5.1.018 */19 class WP_Fatal_Error_Handler {20 21 /**22 * Runs the shutdown handler.23 *24 * This method is registered via `register_shutdown_function()`.25 *26 * @since 5.1.027 */28 public function handle() {29 // Bail if WordPress executed successfully.30 if ( defined( 'WP_EXECUTION_SUCCEEDED' ) && WP_EXECUTION_SUCCEEDED ) {31 return;32 }33 34 try {35 // Bail if no error found.36 $error = $this->detect_error();37 if ( ! $error ) {38 return;39 }40 41 // If the error was stored and thus the extension paused,42 // redirect the request to catch multiple errors in one go.43 if ( $this->store_error( $error ) ) {44 $this->redirect_protected();45 }46 47 // Display the PHP error template.48 $this->display_error_template();49 } catch ( Exception $e ) {50 // Catch exceptions and remain silent.51 }52 }53 54 /**55 * Detects the error causing the crash if it should be handled.56 *57 * @since 5.1.058 *59 * @return array|null Error that was triggered, or null if no error received or if the error should not be handled.60 */61 protected function detect_error() {62 $error = error_get_last();63 64 // No error, just skip the error handling code.65 if ( null === $error ) {66 return null;67 }68 69 // Bail if this error should not be handled.70 if ( ! wp_should_handle_error( $error ) ) {71 return null;72 }73 74 return $error;75 }76 77 /**78 * Stores the given error so that the extension causing it is paused.79 *80 * @since 5.1.081 *82 * @param array $error Error that was triggered.83 * @return bool True if the error was stored successfully, false otherwise.84 */85 protected function store_error( $error ) {86 // Do not pause extensions if they only crash on a non-protected endpoint.87 if ( ! is_protected_endpoint() ) {88 return false;89 }90 91 return wp_record_extension_error( $error );92 }93 94 /**95 * Redirects the current request to allow recovering multiple errors in one go.96 *97 * The redirection will only happen when on a protected endpoint.98 *99 * It must be ensured that this method is only called when an error actually occurred and will not occur on the100 * next request again. Otherwise it will create a redirect loop.101 *102 * @since 5.1.0103 */104 protected function redirect_protected() {105 // Do not redirect requests on non-protected endpoints.106 if ( ! is_protected_endpoint() ) {107 return;108 }109 110 // Pluggable is usually loaded after plugins, so we manually include it here for redirection functionality.111 if ( ! function_exists( 'wp_redirect' ) ) {112 include ABSPATH . WPINC . '/pluggable.php';113 }114 115 $scheme = is_ssl() ? 'https://' : 'http://';116 117 $url = "{$scheme}{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}";118 wp_redirect( $url );119 exit;120 }121 122 /**123 * Displays the PHP error template and sends the HTTP status code, typically 500.124 *125 * A drop-in 'php-error.php' can be used as a custom template. This drop-in should control the HTTP status code and126 * print the HTML markup indicating that a PHP error occurred. Note that this drop-in may potentially be executed127 * very early in the WordPress bootstrap process, so any core functions used that are not part of128 * `wp-includes/load.php` should be checked for before being called.129 *130 * If no such drop-in is available, this will call {@see WP_Fatal_Error_Handler::display_default_error_template()}.131 *132 * @since 5.1.0133 */134 protected function display_error_template() {135 if ( defined( 'WP_CONTENT_DIR' ) ) {136 // Load custom PHP error template, if present.137 $php_error_pluggable = WP_CONTENT_DIR . '/php-error.php';138 if ( is_readable( $php_error_pluggable ) ) {139 require_once $php_error_pluggable;140 return;141 }142 }143 144 // Otherwise, display the default error template.145 $this->display_default_error_template();146 }147 148 /**149 * Displays the default PHP error template.150 *151 * This method is called conditionally if no 'php-error.php' drop-in is available.152 *153 * It calls {@see wp_die()} with a message indicating that the site is experiencing technical difficulties and a154 * login link to the admin backend. The {@see 'wp_php_error_message'} and {@see 'wp_php_error_args'} filters can155 * be used to modify these parameters.156 *157 * @since 5.1.0158 */159 protected function display_default_error_template() {160 if ( ! function_exists( '__' ) ) {161 wp_load_translations_early();162 }163 164 if ( ! function_exists( 'wp_die' ) ) {165 require_once ABSPATH . WPINC . '/functions.php';166 }167 168 $message = __( 'The site is experiencing technical difficulties.' );169 170 $args = array(171 'response' => 500,172 'exit' => false,173 );174 if ( function_exists( 'admin_url' ) ) {175 $args['link_url'] = admin_url();176 $args['link_text'] = __( 'Log into the admin backend to fix this.' );177 }178 179 /**180 * Filters the message that the default PHP error template displays.181 *182 * @since 5.1.0183 *184 * @param string $message HTML error message to display.185 */186 $message = apply_filters( 'wp_php_error_message', $message );187 188 /**189 * Filters the arguments passed to {@see wp_die()} for the default PHP error template.190 *191 * @since 5.1.0192 *193 * @param array $args Associative array of arguments passed to `wp_die()`. By default these contain a194 * 'response' key, and optionally 'link_url' and 'link_text' keys.195 */196 $args = apply_filters( 'wp_php_error_args', $args );197 198 wp_die( $message, '', $args );199 }200 } -
src/wp-includes/class-wp-paused-extensions-storage.php
1 <?php2 /**3 * Error Protection API: WP_Paused_Extensions_Storage class4 *5 * @package WordPress6 * @since 5.1.07 */8 9 /**10 * Core class used for storing paused extensions.11 *12 * @since 5.1.013 */14 class WP_Paused_Extensions_Storage {15 16 /**17 * Option name for storing paused extensions.18 *19 * @since 5.1.020 * @var string21 */22 protected $option_name;23 24 /**25 * Prefix for paused extensions stored as site metadata.26 *27 * @since 5.1.028 * @var string29 */30 protected $meta_prefix;31 32 /**33 * Constructor.34 *35 * @since 5.1.036 *37 * @param string $option_name Option name for storing paused extensions.38 * @param string $meta_prefix Prefix for paused extensions stored as site metadata.39 */40 public function __construct( $option_name, $meta_prefix ) {41 $this->option_name = $option_name;42 $this->meta_prefix = $meta_prefix;43 }44 45 /**46 * Records an extension error.47 *48 * Only one error is stored per extension, with subsequent errors for the same extension overriding the49 * previously stored error.50 *51 * @since 5.1.052 *53 * @param string $extension Plugin or theme directory name.54 * @param array $error {55 * Error that was triggered.56 *57 * @type string $type The error type.58 * @type string $file The name of the file in which the error occurred.59 * @type string $line The line number in which the error occurred.60 * @type string $message The error message.61 * }62 * @return bool True on success, false on failure.63 */64 public function record( $extension, $error ) {65 if ( ! $this->is_api_loaded() ) {66 return false;67 }68 69 if ( is_multisite() && is_site_meta_supported() ) {70 // Do not update if the error is already stored.71 if ( get_site_meta( get_current_blog_id(), $this->meta_prefix . $extension, true ) === $error ) {72 return true;73 }74 75 return (bool) update_site_meta( get_current_blog_id(), $this->meta_prefix . $extension, $error );76 }77 78 $paused_extensions = $this->get_all();79 80 // Do not update if the error is already stored.81 if ( isset( $paused_extensions[ $extension ] ) && $paused_extensions[ $extension ] === $error ) {82 return true;83 }84 85 $paused_extensions[ $extension ] = $error;86 87 return update_option( $this->option_name, $paused_extensions );88 }89 90 /**91 * Forgets a previously recorded extension error.92 *93 * @since 5.1.094 *95 * @param string $extension Plugin or theme directory name.96 * @return bool True on success, false on failure.97 */98 public function forget( $extension ) {99 if ( ! $this->is_api_loaded() ) {100 return false;101 }102 103 if ( is_multisite() && is_site_meta_supported() ) {104 // Do not delete if no error is stored.105 if ( get_site_meta( get_current_blog_id(), $this->meta_prefix . $extension ) === array() ) {106 return true;107 }108 109 return (bool) delete_site_meta( get_current_blog_id(), $this->meta_prefix . $extension );110 }111 112 $paused_extensions = $this->get_all();113 114 // Do not delete if no error is stored.115 if ( ! isset( $paused_extensions[ $extension ] ) ) {116 return true;117 }118 119 // Clean up the entire option if we're removing the only error.120 if ( count( $paused_extensions ) === 1 ) {121 return delete_option( $this->option_name );122 }123 124 unset( $paused_extensions[ $extension ] );125 126 return update_option( $this->option_name, $paused_extensions );127 }128 129 /**130 * Gets the error for an extension, if paused.131 *132 * @since 5.1.0133 *134 * @param string $extension Plugin or theme directory name.135 * @return array|null Error that is stored, or null if the extension is not paused.136 */137 public function get( $extension ) {138 if ( ! $this->is_api_loaded() ) {139 return null;140 }141 142 if ( is_multisite() && is_site_meta_supported() ) {143 $error = get_site_meta( get_current_blog_id(), $this->meta_prefix . $extension, true );144 if ( ! $error ) {145 return null;146 }147 148 return $error;149 }150 151 $paused_extensions = $this->get_all();152 153 if ( ! isset( $paused_extensions[ $extension ] ) ) {154 return null;155 }156 157 return $paused_extensions[ $extension ];158 }159 160 /**161 * Gets the paused extensions with their errors.162 *163 * @since 5.1.0164 *165 * @return array Associative array of $extension => $error pairs.166 */167 public function get_all() {168 if ( ! $this->is_api_loaded() ) {169 return array();170 }171 172 if ( is_multisite() && is_site_meta_supported() ) {173 $site_metadata = get_site_meta( get_current_blog_id() );174 175 $paused_extensions = array();176 foreach ( $site_metadata as $meta_key => $meta_values ) {177 if ( 0 !== strpos( $meta_key, $this->meta_prefix ) ) {178 continue;179 }180 181 $error = maybe_unserialize( array_shift( $meta_values ) );182 183 $paused_extensions[ substr( $meta_key, strlen( $this->meta_prefix ) ) ] = $error;184 }185 186 return $paused_extensions;187 }188 189 return (array) get_option( $this->option_name, array() );190 }191 192 /**193 * Gets the site meta query clause for querying sites with paused extensions.194 *195 * @since 5.1.0196 *197 * @param string $extension Plugin or theme directory name.198 * @return array A single clause to add to a meta query.199 */200 public function get_site_meta_query_clause( $extension ) {201 return array(202 'key' => $this->meta_prefix . $extension,203 'compare_key' => '=',204 );205 }206 207 /**208 * Checks whether the underlying API to store paused extensions is loaded.209 *210 * @since 5.1.0211 *212 * @return bool True if the API is loaded, false otherwise.213 */214 protected function is_api_loaded() {215 if ( is_multisite() ) {216 return function_exists( 'is_site_meta_supported' ) && function_exists( 'get_site_meta' );217 }218 219 return function_exists( 'get_option' );220 }221 } -
src/wp-includes/class-wp-theme.php
371 371 $this->parent = new WP_Theme( $this->template, isset( $theme_root_template ) ? $theme_root_template : $this->theme_root, $this ); 372 372 } 373 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.' ) );376 }377 378 374 // We're good. If we didn't retrieve from cache, set it. 379 375 if ( ! is_array( $cache ) ) { 380 376 $cache = array( -
src/wp-includes/error-protection.php
1 <?php2 /**3 * Error Protection API: Functions4 *5 * @package WordPress6 * @since 5.1.07 */8 9 /**10 * Gets the instance for storing paused plugins.11 *12 * @since 5.1.013 *14 * @return WP_Paused_Extensions_Storage Paused plugins storage.15 */16 function wp_paused_plugins() {17 static $wp_paused_plugins_storage = null;18 19 if ( null === $wp_paused_plugins_storage ) {20 $wp_paused_plugins_storage = new WP_Paused_Extensions_Storage( 'paused_plugins', 'paused_plugin_' );21 }22 23 return $wp_paused_plugins_storage;24 }25 26 /**27 * Gets the instance for storing paused themes.28 *29 * @since 5.1.030 *31 * @return WP_Paused_Extensions_Storage Paused themes storage.32 */33 function wp_paused_themes() {34 static $wp_paused_themes_storage = null;35 36 if ( null === $wp_paused_themes_storage ) {37 $wp_paused_themes_storage = new WP_Paused_Extensions_Storage( 'paused_themes', 'paused_theme_' );38 }39 40 return $wp_paused_themes_storage;41 }42 43 /**44 * Records the extension error as a database option.45 *46 * @since 5.1.047 *48 * @global array $wp_theme_directories49 *50 * @param array $error Error that was triggered.51 * @return bool Whether the error was correctly recorded.52 */53 function wp_record_extension_error( $error ) {54 global $wp_theme_directories;55 56 if ( ! isset( $error['file'] ) ) {57 return false;58 }59 60 if ( ! defined( 'WP_PLUGIN_DIR' ) ) {61 return false;62 }63 64 $error_file = wp_normalize_path( $error['file'] );65 $wp_plugin_dir = wp_normalize_path( WP_PLUGIN_DIR );66 67 if ( 0 === strpos( $error_file, $wp_plugin_dir ) ) {68 $callback = 'wp_paused_plugins';69 $path = str_replace( $wp_plugin_dir . '/', '', $error_file );70 } else {71 if ( empty( $wp_theme_directories ) ) {72 return false;73 }74 75 foreach ( $wp_theme_directories as $theme_directory ) {76 $theme_directory = wp_normalize_path( $theme_directory );77 if ( 0 === strpos( $error_file, $theme_directory ) ) {78 $callback = 'wp_paused_themes';79 $path = str_replace( $theme_directory . '/', '', $error_file );80 }81 }82 }83 84 if ( empty( $callback ) || empty( $path ) ) {85 return false;86 }87 88 $parts = explode( '/', $path );89 $extension = array_shift( $parts );90 91 return call_user_func( $callback )->record( $extension, $error );92 }93 94 /**95 * Forgets a previously recorded extension error again.96 *97 * @since 5.1.098 *99 * @param string $type Type of the extension.100 * @param string $extension Relative path of the extension.101 * @param bool $network_wide Optional. Whether to resume the plugin for the entire102 * network. Default false.103 * @return bool Whether the extension error was successfully forgotten.104 */105 function wp_forget_extension_error( $type, $extension, $network_wide = false ) {106 switch ( $type ) {107 case 'plugins':108 $callback = 'wp_paused_plugins';109 list( $extension ) = explode( '/', $extension );110 break;111 case 'themes':112 $callback = 'wp_paused_themes';113 list( $extension ) = explode( '/', $extension );114 break;115 }116 117 if ( empty( $callback ) || empty( $extension ) ) {118 return false;119 }120 121 // Handle manually since the regular APIs do not expose this functionality.122 if ( $network_wide && is_site_meta_supported() ) {123 $site_meta_query_clause = call_user_func( $callback )->get_site_meta_query_clause( $extension );124 return delete_metadata( 'blog', 0, $site_meta_query_clause['key'], '', true );125 }126 127 return call_user_func( $callback )->forget( $extension );128 }129 130 /**131 * Determines whether we are dealing with an error that WordPress should handle132 * in order to protect the admin backend against WSODs.133 *134 * @param array $error Error information retrieved from error_get_last().135 *136 * @return bool Whether WordPress should handle this error.137 */138 function wp_should_handle_error( $error ) {139 if ( ! isset( $error['type'] ) ) {140 return false;141 }142 143 $error_types_to_handle = array(144 E_ERROR,145 E_PARSE,146 E_USER_ERROR,147 E_COMPILE_ERROR,148 E_RECOVERABLE_ERROR,149 );150 151 return in_array( $error['type'], $error_types_to_handle, true );152 }153 154 /**155 * Registers the shutdown handler for fatal errors.156 *157 * The handler will only be registered if {@see wp_is_fatal_error_handler_enabled()} returns true.158 *159 * @since 5.1.0160 */161 function wp_register_fatal_error_handler() {162 if ( ! wp_is_fatal_error_handler_enabled() ) {163 return;164 }165 166 $handler = null;167 if ( defined( 'WP_CONTENT_DIR' ) && is_readable( WP_CONTENT_DIR . '/fatal-error-handler.php' ) ) {168 $handler = include WP_CONTENT_DIR . '/fatal-error-handler.php';169 }170 171 if ( ! is_object( $handler ) || ! is_callable( array( $handler, 'handle' ) ) ) {172 $handler = new WP_Fatal_Error_Handler();173 }174 175 register_shutdown_function( array( $handler, 'handle' ) );176 }177 178 /**179 * Checks whether the fatal error handler is enabled.180 *181 * A constant `WP_DISABLE_FATAL_ERROR_HANDLER` can be set in `wp-config.php` to disable it, or alternatively the182 * {@see 'wp_fatal_error_handler_enabled'} filter can be used to modify the return value.183 *184 * @since 5.1.0185 *186 * @return bool True if the fatal error handler is enabled, false otherwise.187 */188 function wp_is_fatal_error_handler_enabled() {189 $enabled = ! defined( 'WP_DISABLE_FATAL_ERROR_HANDLER' ) || ! WP_DISABLE_FATAL_ERROR_HANDLER;190 191 /**192 * Filters whether the fatal error handler is enabled.193 *194 * @since 5.1.0195 *196 * @param bool $enabled True if the fatal error handler is enabled, false otherwise.197 */198 return apply_filters( 'wp_fatal_error_handler_enabled', $enabled );199 } -
src/wp-includes/load.php
697 697 } 698 698 } 699 699 700 /*701 * Remove plugins from the list of active plugins when we're on an endpoint702 * that should be protected against WSODs and the plugin is paused.703 */704 if ( is_protected_endpoint() ) {705 $plugins = wp_skip_paused_plugins( $plugins );706 }707 708 700 return $plugins; 709 701 } 710 702 711 703 /** 712 * Filters a given list of plugins, removing any paused plugins from it.713 *714 * @since 5.1.0715 *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 737 return $plugins;738 }739 740 /**741 704 * Retrieves an array of active and valid themes. 742 705 * 743 706 * While upgrading or installing WordPress, no themes are returned. … … 762 725 763 726 $themes[] = TEMPLATEPATH; 764 727 765 /*766 * Remove themes from the list of active themes when we're on an endpoint767 * that should be protected against WSODs and the theme is paused.768 */769 if ( is_protected_endpoint() ) {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 778 728 return $themes; 779 729 } 780 730 781 731 /** 782 * Filters a given list of themes, removing any paused themes from it.783 *784 * @since 5.1.0785 *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 732 * Set internal encoding. 812 733 * 813 734 * In most cases the default internal encoding is latin1, which is … … 1290 1211 } 1291 1212 1292 1213 /** 1293 * Determines whether we are currently on an endpoint that should be protected against WSODs.1294 *1295 * @since 5.1.01296 *1297 * @return bool True if the current endpoint should be protected.1298 */1299 function is_protected_endpoint() {1300 // Protect login pages.1301 if ( isset( $GLOBALS['pagenow'] ) && 'wp-login.php' === $GLOBALS['pagenow'] ) {1302 return true;1303 }1304 1305 // Protect the admin backend.1306 if ( is_admin() && ! wp_doing_ajax() ) {1307 return true;1308 }1309 1310 // Protect AJAX actions that could help resolve a fatal error should be available.1311 if ( is_protected_ajax_action() ) {1312 return true;1313 }1314 1315 /**1316 * Filters whether the current request is against a protected endpoint.1317 *1318 * This filter is only fired when an endpoint is requested which is not already protected by1319 * WordPress core. As such, it exclusively allows providing further protected endpoints in1320 * addition to the admin backend, login pages and protected AJAX actions.1321 *1322 * @since 5.1.01323 *1324 * @param bool $is_protected_endpoint Whether the currently requested endpoint is protected. Default false.1325 */1326 return (bool) apply_filters( 'is_protected_endpoint', false );1327 }1328 1329 /**1330 * Determines whether we are currently handling an AJAX action that should be protected against WSODs.1331 *1332 * @since 5.1.01333 *1334 * @return bool True if the current AJAX action should be protected.1335 */1336 function is_protected_ajax_action() {1337 if ( ! wp_doing_ajax() ) {1338 return false;1339 }1340 1341 if ( ! isset( $_REQUEST['action'] ) ) {1342 return false;1343 }1344 1345 $actions_to_protect = array(1346 'edit-theme-plugin-file', // Saving changes in the core code editor.1347 'heartbeat', // Keep the heart beating.1348 'install-plugin', // Installing a new plugin.1349 'install-theme', // Installing a new theme.1350 'search-plugins', // Searching in the list of plugins.1351 'search-install-plugins', // Searching for a plugin in the plugin install screen.1352 'update-plugin', // Update an existing plugin.1353 'update-theme', // Update an existing theme.1354 );1355 1356 /**1357 * Filters the array of protected AJAX actions.1358 *1359 * This filter is only fired when doing AJAX and the AJAX request has an 'action' property.1360 *1361 * @since 5.1.01362 *1363 * @param array $actions_to_protect Array of strings with AJAX actions to protect.1364 */1365 $actions_to_protect = (array) apply_filters( 'wp_protected_ajax_actions', $actions_to_protect );1366 1367 if ( ! in_array( $_REQUEST['action'], $actions_to_protect, true ) ) {1368 return false;1369 }1370 1371 return true;1372 }1373 1374 /**1375 1214 * Determines whether the current request is a WordPress cron request. 1376 1215 * 1377 1216 * @since 4.8.0 -
src/wp-includes/ms-load.php
53 53 } 54 54 } 55 55 56 /*57 * Remove plugins from the list of active plugins when we're on an endpoint58 * that should be protected against WSODs and the plugin is paused.59 */60 if ( is_protected_endpoint() ) {61 $plugins = wp_skip_paused_plugins( $plugins );62 }63 64 56 return $plugins; 65 57 } 66 58 -
src/wp-settings.php
17 17 18 18 // Include files required for initialization. 19 19 require( ABSPATH . WPINC . '/load.php' ); 20 require( ABSPATH . WPINC . '/class-wp-paused-extensions-storage.php' );21 require( ABSPATH . WPINC . '/class-wp-fatal-error-handler.php' );22 require( ABSPATH . WPINC . '/error-protection.php' );23 20 require( ABSPATH . WPINC . '/default-constants.php' ); 24 21 require_once( ABSPATH . WPINC . '/plugin.php' ); 25 22 26 // Make sure we register the shutdown handler for fatal errors as soon as possible.27 wp_register_fatal_error_handler();28 29 23 /* 30 24 * These can't be directly globalized in version.php. When updating, 31 25 * we're including version.php from another installation and don't want … … 530 524 * @since 3.0.0 531 525 */ 532 526 do_action( 'wp_loaded' ); 533 534 /*535 * Store the fact that we could successfully execute the entire WordPress536 * lifecycle. This is used to skip the premature shutdown handler, as it cannot537 * be unregistered.538 */539 if ( ! defined( 'WP_EXECUTION_SUCCEEDED' ) ) {540 define( 'WP_EXECUTION_SUCCEEDED', true );541 } -
tests/phpunit/tests/user/capabilities.php
257 257 'export_others_personal_data' => array( 'administrator' ), 258 258 'erase_others_personal_data' => array( 'administrator' ), 259 259 'manage_privacy_options' => array( 'administrator' ), 260 'resume_themes' => array( 'administrator' ),261 260 262 261 'edit_categories' => array( 'administrator', 'editor' ), 263 262 'delete_categories' => array( 'administrator', 'editor' ), … … 297 296 'customize' => array( 'administrator' ), 298 297 'delete_site' => array( 'administrator' ), 299 298 'add_users' => array( 'administrator' ), 300 'resume_themes' => array( 'administrator' ),301 299 302 300 'edit_categories' => array( 'administrator', 'editor' ), 303 301 'delete_categories' => array( 'administrator', 'editor' ), … … 456 454 // Singular object meta capabilities (where an object ID is passed) are not tested: 457 455 $expected['activate_plugin'], 458 456 $expected['deactivate_plugin'], 459 $expected['resume_plugin'],460 457 $expected['remove_user'], 461 458 $expected['promote_user'], 462 459 $expected['edit_user'],