rebase on oct-10-2023
This commit is contained in:
@@ -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.
|
||||
*
|
||||
@@ -129,20 +149,38 @@ class WP_Upgrader {
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the upgrader.
|
||||
* Initializes the 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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the generic strings to WP_Upgrader::$strings.
|
||||
* 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' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the generic strings to WP_Upgrader::$strings.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*/
|
||||
@@ -151,7 +189,7 @@ class WP_Upgrader {
|
||||
$this->strings['fs_unavailable'] = __( 'Could not access filesystem.' );
|
||||
$this->strings['fs_error'] = __( 'Filesystem error.' );
|
||||
$this->strings['fs_no_root_dir'] = __( 'Unable to locate WordPress root directory.' );
|
||||
$this->strings['fs_no_content_dir'] = __( 'Unable to locate WordPress content directory (wp-content).' );
|
||||
$this->strings['fs_no_content_dir'] = sprintf( __( 'Unable to locate WordPress content directory (%s).' ), 'wp-content' );
|
||||
$this->strings['fs_no_plugins_dir'] = __( 'Unable to locate WordPress plugin directory.' );
|
||||
$this->strings['fs_no_themes_dir'] = __( 'Unable to locate WordPress theme directory.' );
|
||||
/* translators: %s: Directory name. */
|
||||
@@ -167,10 +205,19 @@ 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.' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to the filesystem.
|
||||
* Connects to the filesystem.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
@@ -242,7 +289,7 @@ class WP_Upgrader {
|
||||
}
|
||||
|
||||
/**
|
||||
* Download a package.
|
||||
* Downloads a package.
|
||||
*
|
||||
* @since 2.8.0
|
||||
* @since 5.2.0 Added the `$check_signatures` parameter.
|
||||
@@ -292,7 +339,7 @@ class WP_Upgrader {
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpack a compressed package file.
|
||||
* Unpacks a compressed package file.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
@@ -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.
|
||||
@@ -346,7 +397,7 @@ class WP_Upgrader {
|
||||
}
|
||||
|
||||
/**
|
||||
* Flatten the results of WP_Filesystem_Base::dirlist() for iterating over.
|
||||
* Flattens the results of WP_Filesystem_Base::dirlist() for iterating over.
|
||||
*
|
||||
* @since 4.9.0
|
||||
* @access protected
|
||||
@@ -512,8 +563,10 @@ class WP_Upgrader {
|
||||
// There are no files?
|
||||
return new WP_Error( 'incompatible_archive_empty', $this->strings['incompatible_archive'], $this->strings['no_files'] );
|
||||
} else {
|
||||
// It's only a single file, the upgrader will use the folder name of this file as the destination folder.
|
||||
// Folder name is based on zip filename.
|
||||
/*
|
||||
* It's only a single file, the upgrader will use the folder name of this file as the destination folder.
|
||||
* Folder name is based on zip filename.
|
||||
*/
|
||||
$source = trailingslashit( $args['source'] );
|
||||
}
|
||||
|
||||
@@ -534,6 +587,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 ) );
|
||||
@@ -580,8 +643,10 @@ class WP_Upgrader {
|
||||
return $removed;
|
||||
}
|
||||
} elseif ( $args['abort_if_destination_exists'] && $wp_filesystem->exists( $remote_destination ) ) {
|
||||
// If we're not clearing the destination folder and something exists there already, bail.
|
||||
// But first check to see if there are actually any files in the folder.
|
||||
/*
|
||||
* If we're not clearing the destination folder and something exists there already, bail.
|
||||
* But first check to see if there are actually any files in the folder.
|
||||
*/
|
||||
$_files = $wp_filesystem->dirlist( $remote_destination );
|
||||
if ( ! empty( $_files ) ) {
|
||||
$wp_filesystem->delete( $remote_source, true ); // Clear out the source files.
|
||||
@@ -614,7 +679,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 );
|
||||
}
|
||||
@@ -651,7 +716,7 @@ class WP_Upgrader {
|
||||
}
|
||||
|
||||
/**
|
||||
* Run an upgrade/installation.
|
||||
* Runs an upgrade/installation.
|
||||
*
|
||||
* Attempts to download the package (if it is not a local file), unpack it, and
|
||||
* install it in the destination folder.
|
||||
@@ -760,8 +825,10 @@ class WP_Upgrader {
|
||||
*/
|
||||
$download = $this->download_package( $options['package'], true, $options['hook_extra'] );
|
||||
|
||||
// Allow for signature soft-fail.
|
||||
// WARNING: This may be removed in the future.
|
||||
/*
|
||||
* Allow for signature soft-fail.
|
||||
* WARNING: This may be removed in the future.
|
||||
*/
|
||||
if ( is_wp_error( $download ) && $download->get_error_data( 'softfail-filename' ) ) {
|
||||
|
||||
// Don't output the 'no signature could be found' failure message for now.
|
||||
@@ -827,7 +894,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 +919,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'] ) {
|
||||
|
||||
/**
|
||||
@@ -881,7 +966,7 @@ class WP_Upgrader {
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle maintenance mode for the site.
|
||||
* Toggles maintenance mode for the site.
|
||||
*
|
||||
* Creates/deletes the maintenance file to enable/disable maintenance mode.
|
||||
*
|
||||
@@ -966,6 +1051,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 */
|
||||
|
||||
Reference in New Issue
Block a user