diff --git a/wp-admin/includes/class-plugin-upgrader.php b/wp-admin/includes/class-plugin-upgrader.php index 812a5691a8..67d04ccc75 100644 --- a/wp-admin/includes/class-plugin-upgrader.php +++ b/wp-admin/includes/class-plugin-upgrader.php @@ -226,9 +226,14 @@ class Plugin_Upgrader extends WP_Upgrader { 'clear_destination' => true, 'clear_working' => true, 'hook_extra' => array( - 'plugin' => $plugin, - 'type' => 'plugin', - 'action' => 'update', + 'plugin' => $plugin, + 'type' => 'plugin', + 'action' => 'update', + 'temp_backup' => array( + 'slug' => dirname( $plugin ), + 'src' => WP_PLUGIN_DIR, + 'dir' => 'plugins', + ), ), ) ); @@ -342,7 +347,12 @@ class Plugin_Upgrader extends WP_Upgrader { 'clear_working' => true, 'is_multi' => true, 'hook_extra' => array( - 'plugin' => $plugin, + 'plugin' => $plugin, + 'temp_backup' => array( + 'slug' => dirname( $plugin ), + 'src' => WP_PLUGIN_DIR, + 'dir' => 'plugins', + ), ), ) ); diff --git a/wp-admin/includes/class-theme-upgrader.php b/wp-admin/includes/class-theme-upgrader.php index 9907c44feb..418888b262 100644 --- a/wp-admin/includes/class-theme-upgrader.php +++ b/wp-admin/includes/class-theme-upgrader.php @@ -328,9 +328,14 @@ class Theme_Upgrader extends WP_Upgrader { 'clear_destination' => true, 'clear_working' => true, 'hook_extra' => array( - 'theme' => $theme, - 'type' => 'theme', - 'action' => 'update', + 'theme' => $theme, + 'type' => 'theme', + 'action' => 'update', + 'temp_backup' => array( + 'slug' => $theme, + 'src' => get_theme_root( $theme ), + 'dir' => 'themes', + ), ), ) ); @@ -443,7 +448,12 @@ class Theme_Upgrader extends WP_Upgrader { 'clear_working' => true, 'is_multi' => true, 'hook_extra' => array( - 'theme' => $theme, + 'theme' => $theme, + 'temp_backup' => array( + 'slug' => $theme, + 'src' => get_theme_root( $theme ), + 'dir' => 'themes', + ), ), ) ); diff --git a/wp-admin/includes/class-wp-site-health.php b/wp-admin/includes/class-wp-site-health.php index b375633050..2a91be3f60 100644 --- a/wp-admin/includes/class-wp-site-health.php +++ b/wp-admin/includes/class-wp-site-health.php @@ -1925,6 +1925,193 @@ class WP_Site_Health { return $result; } + /** + * Tests available disk space for updates. + * + * @since 6.3.0 + * + * @return array The test results. + */ + public function get_test_available_updates_disk_space() { + $available_space = function_exists( 'disk_free_space' ) ? @disk_free_space( WP_CONTENT_DIR . '/upgrade/' ) : false; + + $available_space = false !== $available_space + ? (int) $available_space + : 0; + + $result = array( + 'label' => __( 'Disk space available to safely perform updates' ), + 'status' => 'good', + 'badge' => array( + 'label' => __( 'Security' ), + 'color' => 'blue', + ), + 'description' => sprintf( + /* translators: %s: Available disk space in MB or GB. */ + '

' . __( '%s available disk space was detected, update routines can be performed safely.' ) . '

', + size_format( $available_space ) + ), + 'actions' => '', + 'test' => 'available_updates_disk_space', + ); + + if ( $available_space < 100 * MB_IN_BYTES ) { + $result['description'] = __( 'Available disk space is low, less than 100 MB available.' ); + $result['status'] = 'recommended'; + } + + if ( $available_space < 20 * MB_IN_BYTES ) { + $result['description'] = __( 'Available disk space is critically low, less than 20 MB available. Proceed with caution, updates may fail.' ); + $result['status'] = 'critical'; + } + + if ( ! $available_space ) { + $result['description'] = __( 'Could not determine available disk space for updates.' ); + $result['status'] = 'recommended'; + } + + return $result; + } + + /** + * Tests if plugin and theme temporary backup directories are writable or can be created. + * + * @since 6.3.0 + * + * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. + * + * @return array The test results. + */ + public function get_test_update_temp_backup_writable() { + global $wp_filesystem; + + $result = array( + 'label' => __( 'Plugin and theme temporary backup directory is writable' ), + 'status' => 'good', + 'badge' => array( + 'label' => __( 'Security' ), + 'color' => 'blue', + ), + 'description' => sprintf( + /* translators: %s: wp-content/upgrade-temp-backup */ + '

' . __( 'The %s directory used to improve the stability of plugin and theme updates is writable.' ) . '

', + 'wp-content/upgrade-temp-backup' + ), + 'actions' => '', + 'test' => 'update_temp_backup_writable', + ); + + if ( ! $wp_filesystem ) { + require_once ABSPATH . '/wp-admin/includes/file.php'; + WP_Filesystem(); + } + + $wp_content = $wp_filesystem->wp_content_dir(); + + if ( ! $wp_content ) { + $result['status'] = 'critical'; + $result['label'] = sprintf( + /* translators: %s: wp-content */ + __( 'Unable to locate WordPress content directory (%s)' ), + 'wp-content' + ); + $result['description'] = sprintf( + /* translators: %s: wp-content */ + '

' . __( 'The %s directory cannot be located.' ) . '

', + 'wp-content' + ); + return $result; + } + + $upgrade_dir_exists = $wp_filesystem->is_dir( "$wp_content/upgrade" ); + $upgrade_dir_is_writable = $wp_filesystem->is_writable( "$wp_content/upgrade" ); + $backup_dir_exists = $wp_filesystem->is_dir( "$wp_content/upgrade-temp-backup" ); + $backup_dir_is_writable = $wp_filesystem->is_writable( "$wp_content/upgrade-temp-backup" ); + + $plugins_dir_exists = $wp_filesystem->is_dir( "$wp_content/upgrade-temp-backup/plugins" ); + $plugins_dir_is_writable = $wp_filesystem->is_writable( "$wp_content/upgrade-temp-backup/plugins" ); + $themes_dir_exists = $wp_filesystem->is_dir( "$wp_content/upgrade-temp-backup/themes" ); + $themes_dir_is_writable = $wp_filesystem->is_writable( "$wp_content/upgrade-temp-backup/themes" ); + + if ( $plugins_dir_exists && ! $plugins_dir_is_writable && $themes_dir_exists && ! $themes_dir_is_writable ) { + $result['status'] = 'critical'; + $result['label'] = __( 'Plugin and theme temporary backup directories exist but are not writable' ); + $result['description'] = sprintf( + /* translators: 1: wp-content/upgrade-temp-backup/plugins, 2: wp-content/upgrade-temp-backup/themes. */ + '

' . __( 'The %1$s and %2$s directories exist but are not writable. These directories are used to improve the stability of plugin updates. Please make sure the server has write permissions to these directories.' ) . '

', + 'wp-content/upgrade-temp-backup/plugins', + 'wp-content/upgrade-temp-backup/themes' + ); + return $result; + } + + if ( $plugins_dir_exists && ! $plugins_dir_is_writable ) { + $result['status'] = 'critical'; + $result['label'] = __( 'Plugin temporary backup directory exists but is not writable' ); + $result['description'] = sprintf( + /* translators: %s: wp-content/upgrade-temp-backup/plugins */ + '

' . __( 'The %s directory exists but is not writable. This directory is used to improve the stability of plugin updates. Please make sure the server has write permissions to this directory.' ) . '

', + 'wp-content/upgrade-temp-backup/plugins' + ); + return $result; + } + + if ( $themes_dir_exists && ! $themes_dir_is_writable ) { + $result['status'] = 'critical'; + $result['label'] = __( 'Theme temporary backup directory exists but is not writable' ); + $result['description'] = sprintf( + /* translators: %s: wp-content/upgrade-temp-backup/themes */ + '

' . __( 'The %s directory exists but is not writable. This directory is used to improve the stability of theme updates. Please make sure the server has write permissions to this directory.' ) . '

', + 'wp-content/upgrade-temp-backup/themes' + ); + return $result; + } + + if ( ( ! $plugins_dir_exists || ! $themes_dir_exists ) && $backup_dir_exists && ! $backup_dir_is_writable ) { + $result['status'] = 'critical'; + $result['label'] = __( 'The temporary backup directory exists but is not writable' ); + $result['description'] = sprintf( + /* translators: %s: wp-content/upgrade-temp-backup */ + '

' . __( 'The %s directory exists but is not writable. This directory is used to improve the stability of plugin and theme updates. Please make sure the server has write permissions to this directory.' ) . '

', + 'wp-content/upgrade-temp-backup' + ); + return $result; + } + + if ( ! $backup_dir_exists && $upgrade_dir_exists && ! $upgrade_dir_is_writable ) { + $result['status'] = 'critical'; + $result['label'] = sprintf( + /* translators: %s: upgrade */ + __( 'The %s directory exists but is not writable' ), + 'upgrade' + ); + $result['description'] = sprintf( + /* translators: %s: wp-content/upgrade */ + '

' . __( 'The %s directory exists but is not writable. This directory is used for plugin and theme updates. Please make sure the server has write permissions to this directory.' ) . '

', + 'wp-content/upgrade' + ); + return $result; + } + + if ( ! $upgrade_dir_exists && ! $wp_filesystem->is_writable( $wp_content ) ) { + $result['status'] = 'critical'; + $result['label'] = sprintf( + /* translators: %s: upgrade */ + __( 'The %s directory cannot be created' ), + 'upgrade' + ); + $result['description'] = sprintf( + /* translators: 1: wp-content/upgrade, 2: wp-content. */ + '

' . __( 'The %1$s directory does not exist, and the server does not have write permissions in %2$s to create it. This directory is used for plugin and theme updates. Please make sure the server has write permissions in %2$s.' ) . '

', + 'wp-content/upgrade', + 'wp-content' + ); + return $result; + } + + return $result; + } + /** * Tests if loopbacks work as expected. * @@ -2532,71 +2719,79 @@ class WP_Site_Health { public static function get_tests() { $tests = array( 'direct' => array( - 'wordpress_version' => array( + 'wordpress_version' => array( 'label' => __( 'WordPress Version' ), 'test' => 'wordpress_version', ), - 'plugin_version' => array( + 'plugin_version' => array( 'label' => __( 'Plugin Versions' ), 'test' => 'plugin_version', ), - 'theme_version' => array( + 'theme_version' => array( 'label' => __( 'Theme Versions' ), 'test' => 'theme_version', ), - 'php_version' => array( + 'php_version' => array( 'label' => __( 'PHP Version' ), 'test' => 'php_version', ), - 'php_extensions' => array( + 'php_extensions' => array( 'label' => __( 'PHP Extensions' ), 'test' => 'php_extensions', ), - 'php_default_timezone' => array( + 'php_default_timezone' => array( 'label' => __( 'PHP Default Timezone' ), 'test' => 'php_default_timezone', ), - 'php_sessions' => array( + 'php_sessions' => array( 'label' => __( 'PHP Sessions' ), 'test' => 'php_sessions', ), - 'sql_server' => array( + 'sql_server' => array( 'label' => __( 'Database Server version' ), 'test' => 'sql_server', ), - 'utf8mb4_support' => array( + 'utf8mb4_support' => array( 'label' => __( 'MySQL utf8mb4 support' ), 'test' => 'utf8mb4_support', ), - 'ssl_support' => array( + 'ssl_support' => array( 'label' => __( 'Secure communication' ), 'test' => 'ssl_support', ), - 'scheduled_events' => array( + 'scheduled_events' => array( 'label' => __( 'Scheduled events' ), 'test' => 'scheduled_events', ), - 'http_requests' => array( + 'http_requests' => array( 'label' => __( 'HTTP Requests' ), 'test' => 'http_requests', ), - 'rest_availability' => array( + 'rest_availability' => array( 'label' => __( 'REST API availability' ), 'test' => 'rest_availability', 'skip_cron' => true, ), - 'debug_enabled' => array( + 'debug_enabled' => array( 'label' => __( 'Debugging enabled' ), 'test' => 'is_in_debug_mode', ), - 'file_uploads' => array( + 'file_uploads' => array( 'label' => __( 'File uploads' ), 'test' => 'file_uploads', ), - 'plugin_theme_auto_updates' => array( + 'plugin_theme_auto_updates' => array( 'label' => __( 'Plugin and theme auto-updates' ), 'test' => 'plugin_theme_auto_updates', ), + 'update_temp_backup_writable' => array( + 'label' => __( 'Plugin and theme temporary backup directory access' ), + 'test' => 'update_temp_backup_writable', + ), + 'available_updates_disk_space' => array( + 'label' => __( 'Available disk space' ), + 'test' => 'available_updates_disk_space', + ), ), 'async' => array( 'dotorg_communication' => array( diff --git a/wp-admin/includes/class-wp-upgrader.php b/wp-admin/includes/class-wp-upgrader.php index f6653af742..99a28b61ba 100644 --- a/wp-admin/includes/class-wp-upgrader.php +++ b/wp-admin/includes/class-wp-upgrader.php @@ -112,6 +112,26 @@ class WP_Upgrader { */ public $update_current = 0; + /** + * Stores the list of plugins or themes added to temporary backup directory. + * + * Used by the rollback functions. + * + * @since 6.3.0 + * @var array + */ + private $temp_backups = array(); + + /** + * Stores the list of plugins or themes to be restored from temporary backup directory. + * + * Used by the rollback functions. + * + * @since 6.3.0 + * @var array + */ + private $temp_restores = array(); + /** * Construct the upgrader with a skin. * @@ -134,11 +154,29 @@ class WP_Upgrader { * This will set the relationship between the skin being used and this upgrader, * and also add the generic strings to `WP_Upgrader::$strings`. * + * Additionally, it will schedule a weekly task to clean up the temporary backup directory. + * * @since 2.8.0 + * @since 6.3.0 Added the `schedule_temp_backup_cleanup()` task. */ public function init() { $this->skin->set_upgrader( $this ); $this->generic_strings(); + + if ( ! wp_installing() ) { + $this->schedule_temp_backup_cleanup(); + } + } + + /** + * Schedules the cleanup of the temporary backup directory. + * + * @since 6.3.0 + */ + protected function schedule_temp_backup_cleanup() { + if ( false === wp_next_scheduled( 'wp_delete_temp_updater_backups' ) ) { + wp_schedule_event( time(), 'weekly', 'wp_delete_temp_updater_backups' ); + } } /** @@ -167,6 +205,15 @@ class WP_Upgrader { $this->strings['maintenance_start'] = __( 'Enabling Maintenance mode…' ); $this->strings['maintenance_end'] = __( 'Disabling Maintenance mode…' ); + + /* translators: %s: upgrade-temp-backup */ + $this->strings['temp_backup_mkdir_failed'] = sprintf( __( 'Could not create the %s directory.' ), 'upgrade-temp-backup' ); + /* translators: %s: upgrade-temp-backup */ + $this->strings['temp_backup_move_failed'] = sprintf( __( 'Could not move the old version to the %s directory.' ), 'upgrade-temp-backup' ); + /* translators: %s: The plugin or theme slug. */ + $this->strings['temp_backup_restore_failed'] = __( 'Could not restore the original version of %s.' ); + /* translators: %s: The plugin or theme slug. */ + $this->strings['temp_backup_delete_failed'] = __( 'Could not delete the temporary backup directory for %s.' ); } /** @@ -308,6 +355,10 @@ class WP_Upgrader { $this->skin->feedback( 'unpack_package' ); + if ( ! $wp_filesystem->wp_content_dir() ) { + return new WP_Error( 'fs_no_content_dir', $this->strings['fs_no_content_dir'] ); + } + $upgrade_folder = $wp_filesystem->wp_content_dir() . 'upgrade/'; // Clean up contents of upgrade directory beforehand. @@ -534,6 +585,16 @@ class WP_Upgrader { return $source; } + if ( ! empty( $args['hook_extra']['temp_backup'] ) ) { + $temp_backup = $this->move_to_temp_backup_dir( $args['hook_extra']['temp_backup'] ); + + if ( is_wp_error( $temp_backup ) ) { + return $temp_backup; + } + + $this->temp_backups[] = $args['hook_extra']['temp_backup']; + } + // Has the source location changed? If so, we need a new source_files list. if ( $source !== $remote_source ) { $source_files = array_keys( $wp_filesystem->dirlist( $source ) ); @@ -614,7 +675,7 @@ class WP_Upgrader { $result = copy_dir( $source, $remote_destination ); } - // Clear the working folder? + // Clear the working directory? if ( $args['clear_working'] ) { $wp_filesystem->delete( $remote_source, true ); } @@ -827,7 +888,19 @@ class WP_Upgrader { $result = apply_filters( 'upgrader_install_package_result', $result, $options['hook_extra'] ); $this->skin->set_result( $result ); + if ( is_wp_error( $result ) ) { + if ( ! empty( $options['hook_extra']['temp_backup'] ) ) { + $this->temp_restores[] = $options['hook_extra']['temp_backup']; + + /* + * Restore the backup on shutdown. + * Actions running on `shutdown` are immune to PHP timeouts, + * so in case the failure was due to a PHP timeout, + * it will still be able to properly restore the previous version. + */ + add_action( 'shutdown', array( $this, 'restore_temp_backup' ) ); + } $this->skin->error( $result ); if ( ! method_exists( $this->skin, 'hide_process_failed' ) || ! $this->skin->hide_process_failed( $result ) ) { @@ -840,6 +913,12 @@ class WP_Upgrader { $this->skin->after(); + // Clean up the backup kept in the temporary backup directory. + if ( ! empty( $options['hook_extra']['temp_backup'] ) ) { + // Delete the backup on `shutdown` to avoid a PHP timeout. + add_action( 'shutdown', array( $this, 'delete_temp_backup' ), 100, 0 ); + } + if ( ! $options['is_multi'] ) { /** @@ -966,6 +1045,170 @@ class WP_Upgrader { public static function release_lock( $lock_name ) { return delete_option( $lock_name . '.lock' ); } + + /** + * Moves the plugin or theme being updated into a temporary backup directory. + * + * @since 6.3.0 + * + * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. + * + * @param string[] $args { + * Array of data for the temporary backup. + * + * @type string $slug Plugin or theme slug. + * @type string $src Path to the root directory for plugins or themes. + * @type string $dir Destination subdirectory name. Accepts 'plugins' or 'themes'. + * } + * + * @return bool|WP_Error True on success, false on early exit, otherwise WP_Error. + */ + public function move_to_temp_backup_dir( $args ) { + global $wp_filesystem; + + if ( empty( $args['slug'] ) || empty( $args['src'] ) || empty( $args['dir'] ) ) { + return false; + } + + /* + * Skip any plugin that has "." as its slug. + * A slug of "." will result in a `$src` value ending in a period. + * + * On Windows, this will cause the 'plugins' folder to be moved, + * and will cause a failure when attempting to call `mkdir()`. + */ + if ( '.' === $args['slug'] ) { + return false; + } + + if ( ! $wp_filesystem->wp_content_dir() ) { + return new WP_Error( 'fs_no_content_dir', $this->strings['fs_no_content_dir'] ); + } + + $dest_dir = $wp_filesystem->wp_content_dir() . 'upgrade-temp-backup/'; + $sub_dir = $dest_dir . $args['dir'] . '/'; + + // Create the temporary backup directory if it does not exist. + if ( ! $wp_filesystem->is_dir( $sub_dir ) ) { + if ( ! $wp_filesystem->is_dir( $dest_dir ) ) { + $wp_filesystem->mkdir( $dest_dir, FS_CHMOD_DIR ); + } + + if ( ! $wp_filesystem->mkdir( $sub_dir, FS_CHMOD_DIR ) ) { + // Could not create the backup directory. + return new WP_Error( 'fs_temp_backup_mkdir', $this->strings['temp_backup_mkdir_failed'] ); + } + } + + $src_dir = $wp_filesystem->find_folder( $args['src'] ); + $src = trailingslashit( $src_dir ) . $args['slug']; + $dest = $dest_dir . trailingslashit( $args['dir'] ) . $args['slug']; + + // Delete the temporary backup directory if it already exists. + if ( $wp_filesystem->is_dir( $dest ) ) { + $wp_filesystem->delete( $dest, true ); + } + + // Move to the temporary backup directory. + $result = move_dir( $src, $dest, true ); + if ( is_wp_error( $result ) ) { + return new WP_Error( 'fs_temp_backup_move', $this->strings['temp_backup_move_failed'] ); + } + + return true; + } + + /** + * Restores the plugin or theme from temporary backup. + * + * @since 6.3.0 + * + * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. + * + * @return bool|WP_Error True on success, false on early exit, otherwise WP_Error. + */ + public function restore_temp_backup() { + global $wp_filesystem; + + $errors = new WP_Error(); + + foreach ( $this->temp_restores as $args ) { + if ( empty( $args['slug'] ) || empty( $args['src'] ) || empty( $args['dir'] ) ) { + return false; + } + + if ( ! $wp_filesystem->wp_content_dir() ) { + $errors->add( 'fs_no_content_dir', $this->strings['fs_no_content_dir'] ); + return $errors; + } + + $src = $wp_filesystem->wp_content_dir() . 'upgrade-temp-backup/' . $args['dir'] . '/' . $args['slug']; + $dest_dir = $wp_filesystem->find_folder( $args['src'] ); + $dest = trailingslashit( $dest_dir ) . $args['slug']; + + if ( $wp_filesystem->is_dir( $src ) ) { + // Cleanup. + if ( $wp_filesystem->is_dir( $dest ) && ! $wp_filesystem->delete( $dest, true ) ) { + $errors->add( + 'fs_temp_backup_delete', + sprintf( $this->strings['temp_backup_restore_failed'], $args['slug'] ) + ); + continue; + } + + // Move it. + $result = move_dir( $src, $dest, true ); + if ( is_wp_error( $result ) ) { + $errors->add( + 'fs_temp_backup_delete', + sprintf( $this->strings['temp_backup_restore_failed'], $args['slug'] ) + ); + continue; + } + } + } + + return $errors->has_errors() ? $errors : true; + } + + /** + * Deletes a temporary backup. + * + * @since 6.3.0 + * + * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. + * + * @return bool|WP_Error True on success, false on early exit, otherwise WP_Error. + */ + public function delete_temp_backup() { + global $wp_filesystem; + + $errors = new WP_Error(); + + foreach ( $this->temp_backups as $args ) { + if ( empty( $args['slug'] ) || empty( $args['dir'] ) ) { + return false; + } + + if ( ! $wp_filesystem->wp_content_dir() ) { + $errors->add( 'fs_no_content_dir', $this->strings['fs_no_content_dir'] ); + return $errors; + } + + $temp_backup_dir = $wp_filesystem->wp_content_dir() . "upgrade-temp-backup/{$args['dir']}/{$args['slug']}"; + + if ( ! $wp_filesystem->delete( $temp_backup_dir, true ) ) { + $errors->add( + 'temp_backup_delete_failed', + sprintf( $this->strings['temp_backup_delete_failed'] ), + $args['slug'] + ); + continue; + } + } + + return $errors->has_errors() ? $errors : true; + } } /** Plugin_Upgrader class */ diff --git a/wp-includes/update.php b/wp-includes/update.php index c640e816fe..8e5302afab 100644 --- a/wp-includes/update.php +++ b/wp-includes/update.php @@ -1077,6 +1077,62 @@ function wp_clean_update_cache() { delete_site_transient( 'update_core' ); } +/** + * Schedules the removal of all contents in the temporary backup directory. + * + * @since 6.3.0 + */ +function wp_delete_all_temp_backups() { + /* + * Check if there is a lock, or if currently performing an Ajax request, + * in which case there is a chance an update is running. + * Reschedule for an hour from now and exit early. + */ + if ( get_option( 'core_updater.lock' ) || get_option( 'auto_updater.lock' ) || wp_doing_ajax() ) { + wp_schedule_single_event( time() + HOUR_IN_SECONDS, 'wp_delete_temp_updater_backups' ); + return; + } + + // This action runs on shutdown to make sure there are no plugin updates currently running. + add_action( 'shutdown', '_wp_delete_all_temp_backups' ); +} + +/** + * Deletes all contents in the temporary backup directory. + * + * @since 6.3.0 + * + * @access private + * + * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. + * + * @return void|WP_Error Void on success, or a WP_Error object on failure. + */ +function _wp_delete_all_temp_backups() { + global $wp_filesystem; + + if ( ! $wp_filesystem ) { + require_once ABSPATH . '/wp-admin/includes/file.php'; + WP_Filesystem(); + } + + if ( ! $wp_filesystem->wp_content_dir() ) { + return new WP_Error( 'fs_no_content_dir', __( 'Unable to locate WordPress content directory (wp-content).' ) ); + } + + $temp_backup_dir = $wp_filesystem->wp_content_dir() . 'upgrade-temp-backup/'; + $dirlist = $wp_filesystem->dirlist( $temp_backup_dir ); + $dirlist = $dirlist ? $dirlist : array(); + + foreach ( array_keys( $dirlist ) as $dir ) { + if ( '.' === $dir || '..' === $dir ) { + continue; + } + + $wp_filesystem->delete( $temp_backup_dir . $dir, true ); + } +} + if ( ( ! is_main_site() && ! is_network_admin() ) || wp_doing_ajax() ) { return; } @@ -1101,3 +1157,5 @@ add_action( 'update_option_WPLANG', 'wp_clean_update_cache', 10, 0 ); add_action( 'wp_maybe_auto_update', 'wp_maybe_auto_update' ); add_action( 'init', 'wp_schedule_update_checks' ); + +add_action( 'wp_delete_temp_updater_backups', 'wp_delete_all_temp_backups' ); diff --git a/wp-includes/version.php b/wp-includes/version.php index 4ad7cada78..3e50eb6f93 100644 --- a/wp-includes/version.php +++ b/wp-includes/version.php @@ -16,7 +16,7 @@ * * @global string $wp_version */ -$wp_version = '6.3-alpha-55719'; +$wp_version = '6.3-alpha-55720'; /** * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.