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
This commit is contained in:
Sergey Biryukov 2020-01-12 13:33:05 +00:00
parent 26ce3c9f19
commit 2c4480958b
4 changed files with 267 additions and 26 deletions

View File

@ -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 ) );
}
}

View File

@ -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;
?>
<div class="health-check-title-section site-health-progress-wrapper loading hide-if-no-js">
<div class="site-health-progress">
<svg role="img" aria-hidden="true" focusable="false" width="100%" height="100%" viewBox="0 0 200 200" version="1.1" xmlns="http://www.w3.org/2000/svg">
<circle r="90" cx="100" cy="100" fill="transparent" stroke-dasharray="565.48" stroke-dashoffset="0"></circle>
<circle id="bar" r="90" cx="100" cy="100" fill="transparent" stroke-dasharray="565.48" stroke-dashoffset="0"></circle>
</svg>
</div>
<div class="site-health-progress-label">
<?php if ( false === $get_issues ) : ?>
<?php _e( 'No information yet&hellip;' ); ?>
<?php else : ?>
<?php _e( 'Results are still loading&hellip;' ); ?>
<?php endif; ?>
</div>
</div>
<?php if ( false === $get_issues ) : ?>
<p>
<?php _e( 'No Site Health information has been gathered yet, you can do so by visiting the Site Health screen, alternatively the checks will run periodically.' ); ?>
</p>
<p>
<?php
printf(
/* translators: %s: URL to Site Health screen. */
__( '<a href="%s">Visit the Site Health screen</a> to gather information on about your site.' ),
esc_url( admin_url( 'site-health.php' ) )
);
?>
</p>
<?php else : ?>
<p>
<?php if ( $issue_counts->critical > 0 ) : ?>
<?php _e( 'Your site has critical issues that should be addressed as soon as possible to improve the performance or security of your website.' ); ?>
<?php elseif ( $issues_total <= 0 ) : ?>
<?php _e( 'Great job! Your site currently passes all site health checks.' ); ?>
<?php else : ?>
<?php _e( 'Your site health is looking quite good, but there are still some things you can do to improve the performance and security of your website.' ); ?>
<?php endif; ?>
</p>
<?php endif; ?>
<?php if ( $issues_total > 0 && false !== $get_issues ) : ?>
<p>
<?php
printf(
/* translators: 1: Number of issues. 2: URL to Site Health screen. */
__( 'Take a look at the <strong>%1$d items</strong> on the <a href="%2$s">Site Health Status screen</a>.' ),
$issues_total,
esc_url( admin_url( 'site-health.php' ) )
);
?>
</p>
<?php endif; ?>
<?php
}
/**
* Empty function usable by plugins to output empty dashboard widget (to be populated later by JS).
*

View File

@ -13,7 +13,7 @@
*
* @global string $wp_version
*/
$wp_version = '5.4-alpha-47062';
$wp_version = '5.4-alpha-47063';
/**
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.

View File

@ -351,6 +351,12 @@ if ( ! is_multisite() ) {
wp_recovery_mode()->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 );