diff --git a/src/js/_enqueues/admin/site-health.js b/src/js/_enqueues/admin/site-health.js index 1a31be1b5e..d2fbc32f44 100644 --- a/src/js/_enqueues/admin/site-health.js +++ b/src/js/_enqueues/admin/site-health.js @@ -9,8 +9,8 @@ jQuery( document ).ready( function( $ ) { var data; - var clipboard = new ClipboardJS( '.site-health-copy-buttons .copy-button' ); + var isDebugTab = $( '.health-check-body.health-check-debug-tab' ).length; // Debug information copy section. clipboard.on( 'success', function( e ) { @@ -118,16 +118,18 @@ jQuery( document ).ready( function( $ ) { $progressCount.text( val + '%' ); - $.post( - ajaxurl, - { - 'action': 'health-check-site-status-result', - '_wpnonce': SiteHealth.nonce.site_status_result, - 'counts': SiteHealth.site_status.issues - } - ); + if ( ! isDebugTab ) { + $.post( + ajaxurl, + { + 'action': 'health-check-site-status-result', + '_wpnonce': SiteHealth.nonce.site_status_result, + 'counts': SiteHealth.site_status.issues + } + ); - wp.a11y.speak( SiteHealth.string.site_health_complete_screen_reader.replace( '%s', val + '%' ) ); + wp.a11y.speak( SiteHealth.string.site_health_complete_screen_reader.replace( '%s', val + '%' ) ); + } } /** @@ -171,7 +173,7 @@ jQuery( document ).ready( function( $ ) { } } - if ( 'undefined' !== typeof SiteHealth ) { + if ( 'undefined' !== typeof SiteHealth && ! isDebugTab ) { if ( 0 === SiteHealth.site_status.direct.length && 0 === SiteHealth.site_status.async.length ) { RecalculateProgression(); } else { @@ -209,4 +211,77 @@ jQuery( document ).ready( function( $ ) { } } + function getDirectorySizes() { + var data = { + action: 'health-check-get-sizes', + _wpnonce: SiteHealth.nonce.site_status_result + }; + + var timestamp = ( new Date().getTime() ); + + // After 3 seconds announce that we're still waiting for directory sizes. + var timeout = window.setTimeout( function() { + wp.a11y.speak( SiteHealth.string.please_wait ); + }, 3000 ); + + $.post( { + type: 'POST', + url: ajaxurl, + data: data, + dataType: 'json' + } ).done( function( response ) { + updateDirSizes( response.data || {} ); + } ).always( function() { + var delay = ( new Date().getTime() ) - timestamp; + + $( '.health-check-wp-paths-sizes.spinner' ).css( 'visibility', 'hidden' ); + RecalculateProgression(); + + if ( delay > 3000 ) { + // We have announced that we're waiting. + // Announce that we're ready after giving at least 3 seconds for the first announcement + // to be read out, or the two may collide. + if ( delay > 6000 ) { + delay = 0; + } else { + delay = 6500 - delay; + } + + window.setTimeout( function() { + wp.a11y.speak( SiteHealth.string.site_health_complete ); + }, delay ); + } else { + // Cancel the announcement. + window.clearTimeout( timeout ); + } + } ); + } + + function updateDirSizes( data ) { + var copyButton = $( 'button.button.copy-button' ); + var clipdoardText = copyButton.attr( 'data-clipboard-text' ); + + $.each( data, function( name, value ) { + var text = value.debug || value.size; + + if ( typeof text !== 'undefined' ) { + clipdoardText = clipdoardText.replace( name + ': not calculated', name + ': ' + text ); + } + } ); + + copyButton.attr( 'data-clipboard-text', clipdoardText ); + + $( '#health-check-accordion-block-wp-paths-sizes' ).find( 'td[class]' ).each( function( i, element ) { + var td = $( element ); + var name = td.attr( 'class' ); + + if ( data.hasOwnProperty( name ) && data[ name ].size ) { + td.text( data[ name ].size ); + } + } ); + } + + if ( isDebugTab ) { + getDirectorySizes(); + } } ); diff --git a/src/wp-admin/admin-ajax.php b/src/wp-admin/admin-ajax.php index e0194ceba8..8d412dd9b8 100644 --- a/src/wp-admin/admin-ajax.php +++ b/src/wp-admin/admin-ajax.php @@ -136,6 +136,7 @@ $core_actions_post = array( 'health-check-is-in-debug-mode', 'health-check-background-updates', 'health-check-loopback-requests', + 'health-check-get-sizes', ); // Deprecated diff --git a/src/wp-admin/css/site-health.css b/src/wp-admin/css/site-health.css index 99b179dfe8..bcc69e1f02 100644 --- a/src/wp-admin/css/site-health.css +++ b/src/wp-admin/css/site-health.css @@ -377,6 +377,13 @@ margin-left: 22px; } +.health-check-wp-paths-sizes.spinner { + position: absolute; + visibility: visible; + float: none; + margin: 0 4px; +} + @media screen and (max-width: 782px) { .health-check-body { margin: 0 12px; diff --git a/src/wp-admin/includes/ajax-actions.php b/src/wp-admin/includes/ajax-actions.php index 206fa189a4..89c8474222 100644 --- a/src/wp-admin/includes/ajax-actions.php +++ b/src/wp-admin/includes/ajax-actions.php @@ -4958,3 +4958,52 @@ function wp_ajax_health_check_site_status_result() { wp_send_json_success(); } + +/** + * Ajax handler for site health check to get directories and database sizes. + * + * @since 5.2.0 + */ +function wp_ajax_health_check_get_sizes() { + check_ajax_referer( 'health-check-site-status-result' ); + + if ( ! current_user_can( 'install_plugins' ) ) { + wp_send_json_error(); + } + + if ( ! class_exists( 'WP_Debug_Data' ) ) { + require_once( ABSPATH . 'wp-admin/includes/class-wp-debug-data.php' ); + } + + $sizes_data = WP_Debug_Data::get_sizes(); + $all_sizes = array(); + + foreach ( $sizes_data as $name => $value ) { + $name = sanitize_text_field( $name ); + $data = array(); + + if ( isset( $value['size'] ) ) { + if ( is_string( $value['size'] ) ) { + $data['size'] = sanitize_text_field( $value['size'] ); + } else { + $data['size'] = (int) $value['size']; + } + } + + if ( isset( $value['debug'] ) ) { + if ( is_string( $value['debug'] ) ) { + $data['debug'] = sanitize_text_field( $value['debug'] ); + } else { + $data['debug'] = (int) $value['debug']; + } + } + + $all_sizes[ $name ] = $data; + } + + if ( isset( $all_sizes['total_size']['debug'] ) && 'not available' === $all_sizes['total_size']['debug'] ) { + wp_send_json_error( $all_sizes ); + } + + wp_send_json_success( $all_sizes ); +} diff --git a/src/wp-admin/includes/class-wp-debug-data.php b/src/wp-admin/includes/class-wp-debug-data.php index 3f339acdc1..758ed51c24 100644 --- a/src/wp-admin/includes/class-wp-debug-data.php +++ b/src/wp-admin/includes/class-wp-debug-data.php @@ -387,110 +387,21 @@ class WP_Debug_Data { ); } - $size_db = WP_Debug_Data::get_database_size(); - - /* - * We will be using the PHP max execution time to prevent the size calculations - * from causing a timeout. The default value is 30 seconds, and some - * hosts do not allow you to read configuration values. - */ - if ( function_exists( 'ini_get' ) ) { - $max_execution_time = ini_get( 'max_execution_time' ); - } - - // The max_execution_time defaults to 0 when PHP runs from cli. - // We still want to limit it below. - if ( empty( $max_execution_time ) ) { - $max_execution_time = 30; - } - - // Here 20 seconds is a "sensible default" for how long to make the user wait for the directory size calculation. - // When testing 20 seconds seem enough in nearly all cases. The remaining edge cases are likely testing or development sites - // that have very large number of files, for example `node_modules` in plugins or themes, etc. - if ( $max_execution_time > 20 ) { - $max_execution_time = 20; - } elseif ( $max_execution_time > 10 ) { - // If the max_execution_time is set to lower than 20 seconds, reduce it a bit to prevent - // edge-case timeouts that may happen after the size loop has finished running. - $max_execution_time -= 1; - } - - // Go through the various installation directories and calculate their sizes. - $size_directories = array( - 'wordpress' => array( - 'path' => ABSPATH, - 'size' => 0, - ), - 'themes' => array( - 'path' => trailingslashit( get_theme_root() ), - 'size' => 0, - ), - 'plugins' => array( - 'path' => trailingslashit( WP_PLUGIN_DIR ), - 'size' => 0, - ), - 'uploads' => array( - 'path' => $upload_dir['basedir'], - 'size' => 0, - ), - ); - - $size_total = 0; - - // Loop over all the directories we want to gather the sizes for. - foreach ( $size_directories as $size => $attributes ) { - $dir_size = null; // Default to timeout. - - if ( microtime( true ) - WP_START_TIMESTAMP < $max_execution_time ) { - $dir_size = get_dirsize( $attributes['path'], $max_execution_time ); - } - - if ( false === $dir_size ) { - // Error reading. - $size_directories[ $size ]['size'] = __( 'The size cannot be calculated. The directory is not accessible. Usually caused by invalid permissions.' ); - $size_directories[ $size ]['debug'] = 'not accessible'; - - // Stop total size calculation. - $size_total = null; - } elseif ( null === $dir_size ) { - // Timeout. - $size_directories[ $size ]['size'] = __( 'The directory size calculation has timed out. Usually caused by a very large number of sub-directories and files.' ); - $size_directories[ $size ]['debug'] = 'timeout while calculating size'; - - // Stop total size calculation. - $size_total = null; - } else { - $is_subdir = ( strpos( $size_directories[ $size ]['path'], ABSPATH ) === 0 ); - - // phpcs:ignore WordPress.WP.CapitalPDangit.Misspelled - if ( null !== $size_total && ( 'wordpress' === $size || ! $is_subdir ) ) { - $size_total += $dir_size; - } - - $size_directories[ $size ]['size'] = size_format( $dir_size, 2 ); - $size_directories[ $size ]['debug'] = $size_directories[ $size ]['size']; - } - } - - if ( null !== $size_total && $size_db > 0 ) { - $size_total = size_format( $size_total + $size_db, 2 ); - } else { - $size_total = 0; - } + $not_calculated = __( 'Not calculated' ); $info['wp-paths-sizes']['fields'] = array( 'uploads_path' => array( 'label' => __( 'Uploads Directory Location' ), - 'value' => $size_directories['uploads']['path'], + 'value' => $upload_dir['basedir'], ), 'uploads_size' => array( 'label' => __( 'Uploads Directory Size' ), - 'value' => $size_directories['uploads']['size'], - 'debug' => $size_directories['uploads']['debug'], + 'value' => $not_calculated, + 'debug' => 'not calculated', ), 'themes_path' => array( 'label' => __( 'Themes Directory Location' ), - 'value' => $size_directories['themes']['path'], + 'value' => trailingslashit( get_theme_root() ), ), 'current_theme_path' => array( 'label' => __( 'Current Theme Directory' ), @@ -498,35 +409,36 @@ class WP_Debug_Data { ), 'themes_size' => array( 'label' => __( 'Themes Directory Size' ), - 'value' => $size_directories['themes']['size'], - 'debug' => $size_directories['themes']['debug'], + 'value' => $not_calculated, + 'debug' => 'not calculated', ), 'plugins_path' => array( 'label' => __( 'Plugins Directory Location' ), - 'value' => $size_directories['plugins']['path'], + 'value' => trailingslashit( WP_PLUGIN_DIR ), ), 'plugins_size' => array( 'label' => __( 'Plugins Directory Size' ), - 'value' => $size_directories['plugins']['size'], - 'debug' => $size_directories['plugins']['debug'], + 'value' => $not_calculated, + 'debug' => 'not calculated', ), 'wordpress_path' => array( 'label' => __( 'WordPress Directory Location' ), - 'value' => $size_directories['wordpress']['path'], + 'value' => ABSPATH, ), 'wordpress_size' => array( 'label' => __( 'WordPress Directory Size' ), - 'value' => $size_directories['wordpress']['size'], - 'debug' => $size_directories['wordpress']['debug'], + 'value' => $not_calculated, + 'debug' => 'not calculated', ), 'database_size' => array( 'label' => __( 'Database size' ), - 'value' => size_format( $size_db, 2 ), + 'value' => $not_calculated, + 'debug' => 'not calculated', ), 'total_size' => array( 'label' => __( 'Total installation size' ), - 'value' => $size_total > 0 ? $size_total : __( 'Total size is not available. Some errors were encountered when determining the size of your installation.' ), - 'debug' => $size_total > 0 ? $size_total : 'not available', + 'value' => $not_calculated, + 'debug' => 'not calculated', ), ); @@ -1188,4 +1100,125 @@ class WP_Debug_Data { return (int) $size; } + + /** + * Fetch the sizes of the WordPress directories: `wordpress` (ABSPATH), `plugins`, `themes`, and `uploads`. + * Intended to supplement the array returned by `WP_Debug_Data::debug_data()`. + * + * @since 5.2.0 + * + * @return array The sizes of the directories, also the database size and total installation size. + */ + public static function get_sizes() { + $size_db = self::get_database_size(); + $upload_dir = wp_get_upload_dir(); + + /* + * We will be using the PHP max execution time to prevent the size calculations + * from causing a timeout. The default value is 30 seconds, and some + * hosts do not allow you to read configuration values. + */ + if ( function_exists( 'ini_get' ) ) { + $max_execution_time = ini_get( 'max_execution_time' ); + } + + // The max_execution_time defaults to 0 when PHP runs from cli. + // We still want to limit it below. + if ( empty( $max_execution_time ) ) { + $max_execution_time = 30; + } + + if ( $max_execution_time > 20 ) { + // If the max_execution_time is set to lower than 20 seconds, reduce it a bit to prevent + // edge-case timeouts that may happen after the size loop has finished running. + $max_execution_time -= 2; + } + + // Go through the various installation directories and calculate their sizes. + $all_sizes = array( + 'wordpress_size' => array( + 'path' => ABSPATH, + 'size' => 0, + ), + 'themes_size' => array( + 'path' => trailingslashit( get_theme_root() ), + 'size' => 0, + ), + 'plugins_size' => array( + 'path' => trailingslashit( WP_PLUGIN_DIR ), + 'size' => 0, + ), + 'uploads_size' => array( + 'path' => $upload_dir['basedir'], + 'size' => 0, + ), + ); + + $size_total = 0; + + // Loop over all the directories we want to gather the sizes for. + foreach ( $all_sizes as $name => $attributes ) { + $dir_size = null; // Default to timeout. + + if ( microtime( true ) - WP_START_TIMESTAMP < $max_execution_time ) { + $dir_size = recurse_dirsize( $attributes['path'], null, $max_execution_time ); + } + + if ( false === $dir_size ) { + // Error reading. + $all_sizes[ $name ]['size'] = __( 'The size cannot be calculated. The directory is not accessible. Usually caused by invalid permissions.' ); + $all_sizes[ $name ]['debug'] = 'not accessible'; + + // Stop total size calculation. + $size_total = null; + } elseif ( null === $dir_size ) { + // Timeout. + $all_sizes[ $name ]['size'] = __( 'The directory size calculation has timed out. Usually caused by a very large number of sub-directories and files.' ); + $all_sizes[ $name ]['debug'] = 'timeout while calculating size'; + + // Stop total size calculation. + $size_total = null; + } else { + $is_subdir = ( strpos( $attributes['path'], ABSPATH ) === 0 ); + + // phpcs:ignore WordPress.WP.CapitalPDangit.Misspelled + if ( null !== $size_total && ( 'wordpress_size' === $name || ! $is_subdir ) ) { + $size_total += $dir_size; + } + + $all_sizes[ $name ]['size'] = size_format( $dir_size, 2 ); + $all_sizes[ $name ]['debug'] = $all_sizes[ $name ]['size']; + } + } + + if ( $size_db > 0 ) { + $database_size = size_format( $size_db, 2 ); + + $all_sizes['database_size'] = array( + 'size' => $database_size, + 'debug' => $database_size, + ); + } else { + $all_sizes['database_size'] = array( + 'size' => __( 'Not available' ), + 'debug' => 'not available', + ); + } + + if ( null !== $size_total && $size_db > 0 ) { + $total_size = size_format( $size_total + $size_db, 2 ); + + $all_sizes['total_size'] = array( + 'size' => $total_size, + 'debug' => $total_size, + ); + } else { + $all_sizes['total_size'] = array( + 'size' => __( 'Total size is not available. Some errors were encountered when determining the size of your installation.' ), + 'debug' => 'not available', + ); + } + + return $all_sizes; + } } diff --git a/src/wp-admin/site-health-info.php b/src/wp-admin/site-health-info.php index 08bf002fbb..da4f7a4ac7 100644 --- a/src/wp-admin/site-health-info.php +++ b/src/wp-admin/site-health-info.php @@ -62,8 +62,9 @@ require_once( ABSPATH . 'wp-admin/admin-header.php' );

-
+
$details ) { if ( ! isset( $details['fields'] ) || empty( $details['fields'] ) ) { continue; } + ?>