From 2c4480958bc230cbe89978738a26156c5346e785 Mon Sep 17 00:00:00 2001 From: Sergey Biryukov Date: Sun, 12 Jan 2020 13:33:05 +0000 Subject: [PATCH] Site Health: Introduce Site Health Status dashboard widget. The widget informs administrators of any potential issues that should be addressed to improve the performance or security of their website, and directs them to the Site Health screen for more details. Props Clorith, hedgefield, guddu1315. See #47606. Built from https://develop.svn.wordpress.org/trunk@47063 git-svn-id: http://core.svn.wordpress.org/trunk@46863 1a063a9b-81f0-0310-95a4-ce76da25c4cd --- wp-admin/includes/class-wp-site-health.php | 188 ++++++++++++++++++--- wp-admin/includes/dashboard.php | 97 +++++++++++ wp-includes/version.php | 2 +- wp-settings.php | 6 + 4 files changed, 267 insertions(+), 26 deletions(-) diff --git a/wp-admin/includes/class-wp-site-health.php b/wp-admin/includes/class-wp-site-health.php index 62f089b941..0da1f9e607 100644 --- a/wp-admin/includes/class-wp-site-health.php +++ b/wp-admin/includes/class-wp-site-health.php @@ -8,6 +8,8 @@ */ class WP_Site_Health { + private static $instance = null; + private $mysql_min_version_check; private $mysql_rec_version_check; @@ -29,6 +31,8 @@ class WP_Site_Health { * @since 5.2.0 */ public function __construct() { + $this->maybe_create_scheduled_event(); + $this->prepare_sql_data(); $this->timeout_late_cron = 0; @@ -42,6 +46,22 @@ class WP_Site_Health { add_filter( 'admin_body_class', array( $this, 'admin_body_class' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) ); + add_action( 'wp_site_health_scheduled_check', array( $this, 'wp_cron_scheduled_check' ) ); + } + + /** + * Return an instance of the WP_Site_Health class, or create one if none exist yet. + * + * @since 5.4.0 + * + * @return WP_Site_Health|null + */ + public static function initialize() { + if ( null === self::$instance ) { + self::$instance = new WP_Site_Health(); + } + + return self::$instance; } /** @@ -51,7 +71,7 @@ class WP_Site_Health { */ public function enqueue_scripts() { $screen = get_current_screen(); - if ( 'site-health' !== $screen->id ) { + if ( 'site-health' !== $screen->id && 'dashboard' !== $screen->id ) { return; } @@ -96,35 +116,13 @@ class WP_Site_Health { ); if ( method_exists( $this, $test_function ) && is_callable( array( $this, $test_function ) ) ) { - /** - * Filter the output of a finished Site Health test. - * - * @since 5.3.0 - * - * @param array $test_result { - * An associated array of test result data. - * - * @param string $label A label describing the test, and is used as a header in the output. - * @param string $status The status of the test, which can be a value of `good`, `recommended` or `critical`. - * @param array $badge { - * Tests are put into categories which have an associated badge shown, these can be modified and assigned here. - * - * @param string $label The test label, for example `Performance`. - * @param string $color Default `blue`. A string representing a color to use for the label. - * } - * @param string $description A more descriptive explanation of what the test looks for, and why it is important for the end user. - * @param string $actions An action to direct the user to where they can resolve the issue, if one exists. - * @param string $test The name of the test being ran, used as a reference point. - * } - */ - $health_check_js_variables['site_status']['direct'][] = apply_filters( 'site_status_test_result', call_user_func( array( $this, $test_function ) ) ); + $health_check_js_variables['site_status']['direct'][] = $this->perform_test( array( $this, $test_function ) ); continue; } } if ( is_callable( $test['test'] ) ) { - /** This filter is documented in wp-admin/includes/class-wp-site-health.php */ - $health_check_js_variables['site_status']['direct'][] = apply_filters( 'site_status_test_result', call_user_func( $test['test'] ) ); + $health_check_js_variables['site_status']['direct'][] = $this->perform_test( $test['test'] ); } } @@ -141,6 +139,40 @@ class WP_Site_Health { wp_localize_script( 'site-health', 'SiteHealth', $health_check_js_variables ); } + /** + * Run a Site Health test directly. + * + * @since 5.4.0 + * + * @param $callback + * + * @return mixed|void + */ + private function perform_test( $callback ) { + /** + * Filter the output of a finished Site Health test. + * + * @since 5.3.0 + * + * @param array $test_result { + * An associated array of test result data. + * + * @param string $label A label describing the test, and is used as a header in the output. + * @param string $status The status of the test, which can be a value of `good`, `recommended` or `critical`. + * @param array $badge { + * Tests are put into categories which have an associated badge shown, these can be modified and assigned here. + * + * @param string $label The test label, for example `Performance`. + * @param string $color Default `blue`. A string representing a color to use for the label. + * } + * @param string $description A more descriptive explanation of what the test looks for, and why it is important for the end user. + * @param string $actions An action to direct the user to where they can resolve the issue, if one exists. + * @param string $test The name of the test being ran, used as a reference point. + * } + */ + return apply_filters( 'site_status_test_result', call_user_func( $callback ) ); + } + /** * Run the SQL version checks. * @@ -2005,6 +2037,11 @@ class WP_Site_Health { * @return string The modified body class string. */ public function admin_body_class( $body_class ) { + $screen = get_current_screen(); + if ( 'site-health' !== $screen->id ) { + return $body_class; + } + $body_class .= ' site-health'; return $body_class; @@ -2167,4 +2204,105 @@ class WP_Site_Health { 'message' => __( 'The loopback request to your site completed successfully.' ), ); } + + /** + * Create a weekly cron event, if one does not already exist. + * + * @since 5.4.0 + */ + public function maybe_create_scheduled_event() { + if ( ! wp_next_scheduled( 'wp_site_health_scheduled_check' ) && ! wp_installing() ) { + wp_schedule_event( time(), 'weekly', 'wp_site_health_scheduled_check' ); + } + } + + /** + * Run our scheduled event to check and update the latest site health status for the website. + * + * @since 5.4.0 + */ + public function wp_cron_scheduled_check() { + // Bootstrap wp-admin, as WP_Cron doesn't do this for us. + require_once( trailingslashit( ABSPATH ) . 'wp-admin/includes/admin.php' ); + + $tests = WP_Site_Health::get_tests(); + + $results = array(); + + $site_status = array( + 'good' => 0, + 'recommended' => 0, + 'critical' => 0, + ); + + // Don't run https test on localhost + if ( 'localhost' === preg_replace( '|https?://|', '', get_site_url() ) ) { + unset( $tests['direct']['https_status'] ); + } + + foreach ( $tests['direct'] as $test ) { + + if ( is_string( $test['test'] ) ) { + $test_function = sprintf( + 'get_test_%s', + $test['test'] + ); + + if ( method_exists( $this, $test_function ) && is_callable( array( $this, $test_function ) ) ) { + $results[] = $this->perform_test( array( $this, $test_function ) ); + continue; + } + } + + if ( is_callable( $test['test'] ) ) { + $results[] = $this->perform_test( $test['test'] ); + } + } + + foreach ( $tests['async'] as $test ) { + if ( is_string( $test['test'] ) ) { + if ( isset( $test['has_rest'] ) && $test['has_rest'] ) { + $result_fetch = wp_remote_post( + rest_url( $test['test'] ), + array( + 'body' => array( + '_wpnonce' => wp_create_nonce( 'wp_rest' ), + ), + ) + ); + } else { + $result_fetch = wp_remote_post( + admin_url( 'admin-ajax.php' ), + array( + 'body' => array( + 'action' => $test['test'], + '_wpnonce' => wp_create_nonce( 'health-check-site-status' ), + ), + ) + ); + } + + if ( ! is_wp_error( $result_fetch ) ) { + $results[] = json_decode( wp_remote_retrieve_body( $result_fetch ) ); + } else { + $results[] = array( + 'status' => 'recommended', + 'label' => __( 'A test is unavailable' ), + ); + } + } + } + + foreach ( $results as $result ) { + if ( 'critical' === $result['status'] ) { + $site_status['critical']++; + } elseif ( 'recommended' === $result['status'] ) { + $site_status['recommended']++; + } else { + $site_status['good']++; + } + } + + set_transient( 'health-check-site-status-result', wp_json_encode( $site_status ) ); + } } diff --git a/wp-admin/includes/dashboard.php b/wp-admin/includes/dashboard.php index 4ae72b2d62..5a917f6b11 100644 --- a/wp-admin/includes/dashboard.php +++ b/wp-admin/includes/dashboard.php @@ -42,6 +42,20 @@ function wp_dashboard_setup() { wp_add_dashboard_widget( 'dashboard_php_nag', __( 'PHP Update Required' ), 'wp_dashboard_php_nag' ); } + // Site Health. + if ( current_user_can( 'view_site_health_checks' ) ) { + if ( ! class_exists( 'WP_Site_Health' ) ) { + require_once( ABSPATH . 'wp-admin/includes/class-wp-site-health.php' ); + } + + WP_Site_Health::initialize(); + + wp_enqueue_style( 'site-health' ); + wp_enqueue_script( 'site-health' ); + + wp_add_dashboard_widget( 'dashboard_site_health', __( 'Site Health Status' ), 'wp_dashboard_site_health' ); + } + // Right Now if ( is_blog_admin() && current_user_can( 'edit_posts' ) ) { wp_add_dashboard_widget( 'dashboard_right_now', __( 'At a Glance' ), 'wp_dashboard_right_now' ); @@ -1750,6 +1764,89 @@ function dashboard_php_nag_class( $classes ) { return $classes; } +/** + * Displays the Site Health Status widget. + * + * @since 5.4.0 + */ +function wp_dashboard_site_health() { + $get_issues = get_transient( 'health-check-site-status-result' ); + + $issue_counts = new stdClass(); + + if ( false !== $get_issues ) { + $issue_counts = json_decode( $get_issues ); + } + + if ( ! is_object( $issue_counts ) || empty( $issue_counts ) ) { + $issue_counts = (object) array( + 'good' => 0, + 'recommended' => 0, + 'critical' => 0, + ); + } + + $issues_total = $issue_counts->recommended + $issue_counts->critical; + ?> +
+
+ +
+
+ + + + + +
+
+ + +

+ +

+ +

+ Visit the Site Health screen to gather information on about your site.' ), + esc_url( admin_url( 'site-health.php' ) ) + ); + ?> +

+ + +

+ critical > 0 ) : ?> + + + + + + +

+ + + 0 && false !== $get_issues ) : ?> +

+ %1$d items on the Site Health Status screen.' ), + $issues_total, + esc_url( admin_url( 'site-health.php' ) ) + ); + ?> +

+ + + initialize(); } +// Create an instance of WP_Site_Health so that Cron events may fire. +if ( ! class_exists( 'WP_Site_Health' ) ) { + require_once( ABSPATH . 'wp-admin/includes/class-wp-site-health.php' ); +} +WP_Site_Health::initialize(); + // Load active plugins. foreach ( wp_get_active_and_valid_plugins() as $plugin ) { wp_register_plugin_realpath( $plugin );