diff --git a/wp-admin/includes/class-wp-upgrader.php b/wp-admin/includes/class-wp-upgrader.php index 22b26ac519..81178a4200 100644 --- a/wp-admin/includes/class-wp-upgrader.php +++ b/wp-admin/includes/class-wp-upgrader.php @@ -1224,13 +1224,17 @@ class Core_Upgrader extends WP_Upgrader { $this->strings['unpack_package'] = __('Unpacking the update…'); $this->strings['copy_failed'] = __('Could not copy files.'); $this->strings['copy_failed_space'] = __('Could not copy files. You may have run out of disk space.' ); + $this->strings['start_rollback'] = __( 'Attempting to rollback to previous version.' ); + $this->strings['rollback_was_required'] = __( 'Due to an error during updating, WordPress has rolled back to your previous version' ); } function upgrade( $current, $args = array() ) { global $wp_filesystem, $wp_version; $defaults = array( - 'pre_check_md5' => true, + 'pre_check_md5' => true, + 'attempt_rollback' => false, + 'do_rollback' => false, ); $parsed_args = wp_parse_args( $args, $defaults ); @@ -1251,14 +1255,18 @@ class Core_Upgrader extends WP_Upgrader { get_core_checksums( array( $wp_version, $current->version ) ); $no_partial = false; - if ( $parsed_args['pre_check_md5'] && ! $this->check_files() ) + if ( $parsed_args['do_rollback'] ) + $no_partial = true; + elseif ( $parsed_args['pre_check_md5'] && ! $this->check_files() ) $no_partial = true; // If partial update is returned from the API, use that, unless we're doing a reinstall. // If we cross the new_bundled version number, then use the new_bundled zip. // Don't though if the constant is set to skip bundled items. // If the API returns a no_content zip, go with it. Finally, default to the full zip. - if ( $current->packages->partial && 'reinstall' != $current->response && $wp_version == $current->partial_version && ! $no_partial ) + if ( $parsed_args['do_rollback'] && $current->packages->rollback ) + $to_download = 'rollback'; + elseif ( $current->packages->partial && 'reinstall' != $current->response && $wp_version == $current->partial_version && ! $no_partial ) $to_download = 'partial'; elseif ( $current->packages->new_bundled && version_compare( $wp_version, $current->new_bundled, '<' ) && ( ! defined( 'CORE_UPGRADE_SKIP_NEW_BUNDLED' ) || ! CORE_UPGRADE_SKIP_NEW_BUNDLED ) ) @@ -1283,12 +1291,22 @@ class Core_Upgrader extends WP_Upgrader { } $wp_filesystem->chmod($wp_dir . 'wp-admin/includes/update-core.php', FS_CHMOD_FILE); - require(ABSPATH . 'wp-admin/includes/update-core.php'); + require_once( ABSPATH . 'wp-admin/includes/update-core.php' ); if ( ! function_exists( 'update_core' ) ) return new WP_Error( 'copy_failed_space', $this->strings['copy_failed_space'] ); - $result = update_core( $working_dir, $wp_dir ); + $result = update_core( $working_dir, $wp_dir, $parsed_args['do_rollback'] ); + + // In the event of an error, rollback to the previous version + if ( is_wp_error( $result ) && $parsed_args['attempt_rollback'] && $current->packages->rollback ) { + apply_filters( 'update_feedback', $result ); + apply_filters( 'update_feedback', $this->strings['start_rollback'] ); + + $this->upgrade( $current, array_merge( $parsed_args, array( 'do_rollback' => true ) ) ); + + $result = new WP_Error( 'rollback_was_required', $this->strings['rollback_was_required'] ); + } do_action( 'upgrader_process_complete', $this, array( 'action' => 'update', 'type' => 'core' ), $result ); return $result; } @@ -1630,6 +1648,7 @@ class WP_Automatic_Upgrader { $upgrade_result = $upgrader->upgrade( $item, array( 'clear_update_cache' => false, 'pre_check_md5' => false, /* always use partial builds if possible for core updates */ + 'attempt_rollback' => true, /* only available for core updates */ ) ); // Core doesn't output this, so lets append it so we don't get confused diff --git a/wp-admin/includes/update-core.php b/wp-admin/includes/update-core.php index 1d228956c6..043dface30 100644 --- a/wp-admin/includes/update-core.php +++ b/wp-admin/includes/update-core.php @@ -665,7 +665,7 @@ function update_core($from, $to) { } $wp_filesystem->chmod( $versions_file, FS_CHMOD_FILE ); - require_once( WP_CONTENT_DIR . '/upgrade/version-current.php' ); + require( WP_CONTENT_DIR . '/upgrade/version-current.php' ); $wp_filesystem->delete( $versions_file ); $php_version = phpversion(); diff --git a/wp-admin/includes/update.php b/wp-admin/includes/update.php index 26c2fb6448..8c086b1c6f 100644 --- a/wp-admin/includes/update.php +++ b/wp-admin/includes/update.php @@ -136,6 +136,8 @@ function get_core_checksums( $version ) { // Cache the checksums for later foreach ( $version as $v ) { + if ( ! isset( $body['checksums'][ $v ] ) ) + $body['checksums'][ $v ] = false; set_site_transient( "core_checksums_$v", $body['checksums'][ $v ], HOUR_IN_SECONDS ); $return[ $v ] = $body['checksums'][ $v ]; } diff --git a/wp-includes/update.php b/wp-includes/update.php index 9cfa5a7368..1d2e1cba17 100644 --- a/wp-includes/update.php +++ b/wp-includes/update.php @@ -105,7 +105,7 @@ function wp_version_check() { foreach ( $offer as $offer_key => $value ) { if ( 'packages' == $offer_key ) $offer['packages'] = (object) array_intersect_key( array_map( 'esc_url', $offer['packages'] ), - array_fill_keys( array( 'full', 'no_content', 'new_bundled', 'partial' ), '' ) ); + array_fill_keys( array( 'full', 'no_content', 'new_bundled', 'partial', 'rollback' ), '' ) ); elseif ( 'download' == $offer_key ) $offer['download'] = esc_url( $value ); else