balmet.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

class-theme-upgrader.php (24349B)


      1 <?php
      2 /**
      3  * Upgrade API: Theme_Upgrader class
      4  *
      5  * @package WordPress
      6  * @subpackage Upgrader
      7  * @since 4.6.0
      8  */
      9 
     10 /**
     11  * Core class used for upgrading/installing themes.
     12  *
     13  * It is designed to upgrade/install themes from a local zip, remote zip URL,
     14  * or uploaded zip file.
     15  *
     16  * @since 2.8.0
     17  * @since 4.6.0 Moved to its own file from wp-admin/includes/class-wp-upgrader.php.
     18  *
     19  * @see WP_Upgrader
     20  */
     21 class Theme_Upgrader extends WP_Upgrader {
     22 
     23 	/**
     24 	 * Result of the theme upgrade offer.
     25 	 *
     26 	 * @since 2.8.0
     27 	 * @var array|WP_Error $result
     28 	 * @see WP_Upgrader::$result
     29 	 */
     30 	public $result;
     31 
     32 	/**
     33 	 * Whether multiple themes are being upgraded/installed in bulk.
     34 	 *
     35 	 * @since 2.9.0
     36 	 * @var bool $bulk
     37 	 */
     38 	public $bulk = false;
     39 
     40 	/**
     41 	 * New theme info.
     42 	 *
     43 	 * @since 5.5.0
     44 	 * @var array $new_theme_data
     45 	 *
     46 	 * @see check_package()
     47 	 */
     48 	public $new_theme_data = array();
     49 
     50 	/**
     51 	 * Initialize the upgrade strings.
     52 	 *
     53 	 * @since 2.8.0
     54 	 */
     55 	public function upgrade_strings() {
     56 		$this->strings['up_to_date'] = __( 'The theme is at the latest version.' );
     57 		$this->strings['no_package'] = __( 'Update package not available.' );
     58 		/* translators: %s: Package URL. */
     59 		$this->strings['downloading_package'] = sprintf( __( 'Downloading update from %s&#8230;' ), '<span class="code">%s</span>' );
     60 		$this->strings['unpack_package']      = __( 'Unpacking the update&#8230;' );
     61 		$this->strings['remove_old']          = __( 'Removing the old version of the theme&#8230;' );
     62 		$this->strings['remove_old_failed']   = __( 'Could not remove the old theme.' );
     63 		$this->strings['process_failed']      = __( 'Theme update failed.' );
     64 		$this->strings['process_success']     = __( 'Theme updated successfully.' );
     65 	}
     66 
     67 	/**
     68 	 * Initialize the installation strings.
     69 	 *
     70 	 * @since 2.8.0
     71 	 */
     72 	public function install_strings() {
     73 		$this->strings['no_package'] = __( 'Installation package not available.' );
     74 		/* translators: %s: Package URL. */
     75 		$this->strings['downloading_package'] = sprintf( __( 'Downloading installation package from %s&#8230;' ), '<span class="code">%s</span>' );
     76 		$this->strings['unpack_package']      = __( 'Unpacking the package&#8230;' );
     77 		$this->strings['installing_package']  = __( 'Installing the theme&#8230;' );
     78 		$this->strings['remove_old']          = __( 'Removing the old version of the theme&#8230;' );
     79 		$this->strings['remove_old_failed']   = __( 'Could not remove the old theme.' );
     80 		$this->strings['no_files']            = __( 'The theme contains no files.' );
     81 		$this->strings['process_failed']      = __( 'Theme installation failed.' );
     82 		$this->strings['process_success']     = __( 'Theme installed successfully.' );
     83 		/* translators: 1: Theme name, 2: Theme version. */
     84 		$this->strings['process_success_specific'] = __( 'Successfully installed the theme <strong>%1$s %2$s</strong>.' );
     85 		$this->strings['parent_theme_search']      = __( 'This theme requires a parent theme. Checking if it is installed&#8230;' );
     86 		/* translators: 1: Theme name, 2: Theme version. */
     87 		$this->strings['parent_theme_prepare_install'] = __( 'Preparing to install <strong>%1$s %2$s</strong>&#8230;' );
     88 		/* translators: 1: Theme name, 2: Theme version. */
     89 		$this->strings['parent_theme_currently_installed'] = __( 'The parent theme, <strong>%1$s %2$s</strong>, is currently installed.' );
     90 		/* translators: 1: Theme name, 2: Theme version. */
     91 		$this->strings['parent_theme_install_success'] = __( 'Successfully installed the parent theme, <strong>%1$s %2$s</strong>.' );
     92 		/* translators: %s: Theme name. */
     93 		$this->strings['parent_theme_not_found'] = sprintf( __( '<strong>The parent theme could not be found.</strong> You will need to install the parent theme, %s, before you can use this child theme.' ), '<strong>%s</strong>' );
     94 		/* translators: %s: Theme error. */
     95 		$this->strings['current_theme_has_errors'] = __( 'The current theme has the following error: "%s".' );
     96 
     97 		if ( ! empty( $this->skin->overwrite ) ) {
     98 			if ( 'update-theme' === $this->skin->overwrite ) {
     99 				$this->strings['installing_package'] = __( 'Updating the theme&#8230;' );
    100 				$this->strings['process_failed']     = __( 'Theme update failed.' );
    101 				$this->strings['process_success']    = __( 'Theme updated successfully.' );
    102 			}
    103 
    104 			if ( 'downgrade-theme' === $this->skin->overwrite ) {
    105 				$this->strings['installing_package'] = __( 'Downgrading the theme&#8230;' );
    106 				$this->strings['process_failed']     = __( 'Theme downgrade failed.' );
    107 				$this->strings['process_success']    = __( 'Theme downgraded successfully.' );
    108 			}
    109 		}
    110 	}
    111 
    112 	/**
    113 	 * Check if a child theme is being installed and we need to install its parent.
    114 	 *
    115 	 * Hooked to the {@see 'upgrader_post_install'} filter by Theme_Upgrader::install().
    116 	 *
    117 	 * @since 3.4.0
    118 	 *
    119 	 * @param bool  $install_result
    120 	 * @param array $hook_extra
    121 	 * @param array $child_result
    122 	 * @return bool
    123 	 */
    124 	public function check_parent_theme_filter( $install_result, $hook_extra, $child_result ) {
    125 		// Check to see if we need to install a parent theme.
    126 		$theme_info = $this->theme_info();
    127 
    128 		if ( ! $theme_info->parent() ) {
    129 			return $install_result;
    130 		}
    131 
    132 		$this->skin->feedback( 'parent_theme_search' );
    133 
    134 		if ( ! $theme_info->parent()->errors() ) {
    135 			$this->skin->feedback( 'parent_theme_currently_installed', $theme_info->parent()->display( 'Name' ), $theme_info->parent()->display( 'Version' ) );
    136 			// We already have the theme, fall through.
    137 			return $install_result;
    138 		}
    139 
    140 		// We don't have the parent theme, let's install it.
    141 		$api = themes_api(
    142 			'theme_information',
    143 			array(
    144 				'slug'   => $theme_info->get( 'Template' ),
    145 				'fields' => array(
    146 					'sections' => false,
    147 					'tags'     => false,
    148 				),
    149 			)
    150 		); // Save on a bit of bandwidth.
    151 
    152 		if ( ! $api || is_wp_error( $api ) ) {
    153 			$this->skin->feedback( 'parent_theme_not_found', $theme_info->get( 'Template' ) );
    154 			// Don't show activate or preview actions after installation.
    155 			add_filter( 'install_theme_complete_actions', array( $this, 'hide_activate_preview_actions' ) );
    156 			return $install_result;
    157 		}
    158 
    159 		// Backup required data we're going to override:
    160 		$child_api             = $this->skin->api;
    161 		$child_success_message = $this->strings['process_success'];
    162 
    163 		// Override them.
    164 		$this->skin->api = $api;
    165 
    166 		$this->strings['process_success_specific'] = $this->strings['parent_theme_install_success'];
    167 
    168 		$this->skin->feedback( 'parent_theme_prepare_install', $api->name, $api->version );
    169 
    170 		add_filter( 'install_theme_complete_actions', '__return_false', 999 ); // Don't show any actions after installing the theme.
    171 
    172 		// Install the parent theme.
    173 		$parent_result = $this->run(
    174 			array(
    175 				'package'           => $api->download_link,
    176 				'destination'       => get_theme_root(),
    177 				'clear_destination' => false, // Do not overwrite files.
    178 				'clear_working'     => true,
    179 			)
    180 		);
    181 
    182 		if ( is_wp_error( $parent_result ) ) {
    183 			add_filter( 'install_theme_complete_actions', array( $this, 'hide_activate_preview_actions' ) );
    184 		}
    185 
    186 		// Start cleaning up after the parent's installation.
    187 		remove_filter( 'install_theme_complete_actions', '__return_false', 999 );
    188 
    189 		// Reset child's result and data.
    190 		$this->result                     = $child_result;
    191 		$this->skin->api                  = $child_api;
    192 		$this->strings['process_success'] = $child_success_message;
    193 
    194 		return $install_result;
    195 	}
    196 
    197 	/**
    198 	 * Don't display the activate and preview actions to the user.
    199 	 *
    200 	 * Hooked to the {@see 'install_theme_complete_actions'} filter by
    201 	 * Theme_Upgrader::check_parent_theme_filter() when installing
    202 	 * a child theme and installing the parent theme fails.
    203 	 *
    204 	 * @since 3.4.0
    205 	 *
    206 	 * @param array $actions Preview actions.
    207 	 * @return array
    208 	 */
    209 	public function hide_activate_preview_actions( $actions ) {
    210 		unset( $actions['activate'], $actions['preview'] );
    211 		return $actions;
    212 	}
    213 
    214 	/**
    215 	 * Install a theme package.
    216 	 *
    217 	 * @since 2.8.0
    218 	 * @since 3.7.0 The `$args` parameter was added, making clearing the update cache optional.
    219 	 *
    220 	 * @param string $package The full local path or URI of the package.
    221 	 * @param array  $args {
    222 	 *     Optional. Other arguments for installing a theme package. Default empty array.
    223 	 *
    224 	 *     @type bool $clear_update_cache Whether to clear the updates cache if successful.
    225 	 *                                    Default true.
    226 	 * }
    227 	 *
    228 	 * @return bool|WP_Error True if the installation was successful, false or a WP_Error object otherwise.
    229 	 */
    230 	public function install( $package, $args = array() ) {
    231 		$defaults    = array(
    232 			'clear_update_cache' => true,
    233 			'overwrite_package'  => false, // Do not overwrite files.
    234 		);
    235 		$parsed_args = wp_parse_args( $args, $defaults );
    236 
    237 		$this->init();
    238 		$this->install_strings();
    239 
    240 		add_filter( 'upgrader_source_selection', array( $this, 'check_package' ) );
    241 		add_filter( 'upgrader_post_install', array( $this, 'check_parent_theme_filter' ), 10, 3 );
    242 
    243 		if ( $parsed_args['clear_update_cache'] ) {
    244 			// Clear cache so wp_update_themes() knows about the new theme.
    245 			add_action( 'upgrader_process_complete', 'wp_clean_themes_cache', 9, 0 );
    246 		}
    247 
    248 		$this->run(
    249 			array(
    250 				'package'           => $package,
    251 				'destination'       => get_theme_root(),
    252 				'clear_destination' => $parsed_args['overwrite_package'],
    253 				'clear_working'     => true,
    254 				'hook_extra'        => array(
    255 					'type'   => 'theme',
    256 					'action' => 'install',
    257 				),
    258 			)
    259 		);
    260 
    261 		remove_action( 'upgrader_process_complete', 'wp_clean_themes_cache', 9 );
    262 		remove_filter( 'upgrader_source_selection', array( $this, 'check_package' ) );
    263 		remove_filter( 'upgrader_post_install', array( $this, 'check_parent_theme_filter' ) );
    264 
    265 		if ( ! $this->result || is_wp_error( $this->result ) ) {
    266 			return $this->result;
    267 		}
    268 
    269 		// Refresh the Theme Update information.
    270 		wp_clean_themes_cache( $parsed_args['clear_update_cache'] );
    271 
    272 		if ( $parsed_args['overwrite_package'] ) {
    273 			/** This action is documented in wp-admin/includes/class-plugin-upgrader.php */
    274 			do_action( 'upgrader_overwrote_package', $package, $this->new_theme_data, 'theme' );
    275 		}
    276 
    277 		return true;
    278 	}
    279 
    280 	/**
    281 	 * Upgrade a theme.
    282 	 *
    283 	 * @since 2.8.0
    284 	 * @since 3.7.0 The `$args` parameter was added, making clearing the update cache optional.
    285 	 *
    286 	 * @param string $theme The theme slug.
    287 	 * @param array  $args {
    288 	 *     Optional. Other arguments for upgrading a theme. Default empty array.
    289 	 *
    290 	 *     @type bool $clear_update_cache Whether to clear the update cache if successful.
    291 	 *                                    Default true.
    292 	 * }
    293 	 * @return bool|WP_Error True if the upgrade was successful, false or a WP_Error object otherwise.
    294 	 */
    295 	public function upgrade( $theme, $args = array() ) {
    296 		$defaults    = array(
    297 			'clear_update_cache' => true,
    298 		);
    299 		$parsed_args = wp_parse_args( $args, $defaults );
    300 
    301 		$this->init();
    302 		$this->upgrade_strings();
    303 
    304 		// Is an update available?
    305 		$current = get_site_transient( 'update_themes' );
    306 		if ( ! isset( $current->response[ $theme ] ) ) {
    307 			$this->skin->before();
    308 			$this->skin->set_result( false );
    309 			$this->skin->error( 'up_to_date' );
    310 			$this->skin->after();
    311 			return false;
    312 		}
    313 
    314 		$r = $current->response[ $theme ];
    315 
    316 		add_filter( 'upgrader_pre_install', array( $this, 'current_before' ), 10, 2 );
    317 		add_filter( 'upgrader_post_install', array( $this, 'current_after' ), 10, 2 );
    318 		add_filter( 'upgrader_clear_destination', array( $this, 'delete_old_theme' ), 10, 4 );
    319 		if ( $parsed_args['clear_update_cache'] ) {
    320 			// Clear cache so wp_update_themes() knows about the new theme.
    321 			add_action( 'upgrader_process_complete', 'wp_clean_themes_cache', 9, 0 );
    322 		}
    323 
    324 		$this->run(
    325 			array(
    326 				'package'           => $r['package'],
    327 				'destination'       => get_theme_root( $theme ),
    328 				'clear_destination' => true,
    329 				'clear_working'     => true,
    330 				'hook_extra'        => array(
    331 					'theme'  => $theme,
    332 					'type'   => 'theme',
    333 					'action' => 'update',
    334 				),
    335 			)
    336 		);
    337 
    338 		remove_action( 'upgrader_process_complete', 'wp_clean_themes_cache', 9 );
    339 		remove_filter( 'upgrader_pre_install', array( $this, 'current_before' ) );
    340 		remove_filter( 'upgrader_post_install', array( $this, 'current_after' ) );
    341 		remove_filter( 'upgrader_clear_destination', array( $this, 'delete_old_theme' ) );
    342 
    343 		if ( ! $this->result || is_wp_error( $this->result ) ) {
    344 			return $this->result;
    345 		}
    346 
    347 		wp_clean_themes_cache( $parsed_args['clear_update_cache'] );
    348 
    349 		// Ensure any future auto-update failures trigger a failure email by removing
    350 		// the last failure notification from the list when themes update successfully.
    351 		$past_failure_emails = get_option( 'auto_plugin_theme_update_emails', array() );
    352 
    353 		if ( isset( $past_failure_emails[ $theme ] ) ) {
    354 			unset( $past_failure_emails[ $theme ] );
    355 			update_option( 'auto_plugin_theme_update_emails', $past_failure_emails );
    356 		}
    357 
    358 		return true;
    359 	}
    360 
    361 	/**
    362 	 * Upgrade several themes at once.
    363 	 *
    364 	 * @since 3.0.0
    365 	 * @since 3.7.0 The `$args` parameter was added, making clearing the update cache optional.
    366 	 *
    367 	 * @param string[] $themes Array of the theme slugs.
    368 	 * @param array    $args {
    369 	 *     Optional. Other arguments for upgrading several themes at once. Default empty array.
    370 	 *
    371 	 *     @type bool $clear_update_cache Whether to clear the update cache if successful.
    372 	 *                                    Default true.
    373 	 * }
    374 	 * @return array[]|false An array of results, or false if unable to connect to the filesystem.
    375 	 */
    376 	public function bulk_upgrade( $themes, $args = array() ) {
    377 		$defaults    = array(
    378 			'clear_update_cache' => true,
    379 		);
    380 		$parsed_args = wp_parse_args( $args, $defaults );
    381 
    382 		$this->init();
    383 		$this->bulk = true;
    384 		$this->upgrade_strings();
    385 
    386 		$current = get_site_transient( 'update_themes' );
    387 
    388 		add_filter( 'upgrader_pre_install', array( $this, 'current_before' ), 10, 2 );
    389 		add_filter( 'upgrader_post_install', array( $this, 'current_after' ), 10, 2 );
    390 		add_filter( 'upgrader_clear_destination', array( $this, 'delete_old_theme' ), 10, 4 );
    391 
    392 		$this->skin->header();
    393 
    394 		// Connect to the filesystem first.
    395 		$res = $this->fs_connect( array( WP_CONTENT_DIR ) );
    396 		if ( ! $res ) {
    397 			$this->skin->footer();
    398 			return false;
    399 		}
    400 
    401 		$this->skin->bulk_header();
    402 
    403 		/*
    404 		 * Only start maintenance mode if:
    405 		 * - running Multisite and there are one or more themes specified, OR
    406 		 * - a theme with an update available is currently in use.
    407 		 * @todo For multisite, maintenance mode should only kick in for individual sites if at all possible.
    408 		 */
    409 		$maintenance = ( is_multisite() && ! empty( $themes ) );
    410 		foreach ( $themes as $theme ) {
    411 			$maintenance = $maintenance || get_stylesheet() === $theme || get_template() === $theme;
    412 		}
    413 		if ( $maintenance ) {
    414 			$this->maintenance_mode( true );
    415 		}
    416 
    417 		$results = array();
    418 
    419 		$this->update_count   = count( $themes );
    420 		$this->update_current = 0;
    421 		foreach ( $themes as $theme ) {
    422 			$this->update_current++;
    423 
    424 			$this->skin->theme_info = $this->theme_info( $theme );
    425 
    426 			if ( ! isset( $current->response[ $theme ] ) ) {
    427 				$this->skin->set_result( true );
    428 				$this->skin->before();
    429 				$this->skin->feedback( 'up_to_date' );
    430 				$this->skin->after();
    431 				$results[ $theme ] = true;
    432 				continue;
    433 			}
    434 
    435 			// Get the URL to the zip file.
    436 			$r = $current->response[ $theme ];
    437 
    438 			$result = $this->run(
    439 				array(
    440 					'package'           => $r['package'],
    441 					'destination'       => get_theme_root( $theme ),
    442 					'clear_destination' => true,
    443 					'clear_working'     => true,
    444 					'is_multi'          => true,
    445 					'hook_extra'        => array(
    446 						'theme' => $theme,
    447 					),
    448 				)
    449 			);
    450 
    451 			$results[ $theme ] = $this->result;
    452 
    453 			// Prevent credentials auth screen from displaying multiple times.
    454 			if ( false === $result ) {
    455 				break;
    456 			}
    457 		} // End foreach $themes.
    458 
    459 		$this->maintenance_mode( false );
    460 
    461 		// Refresh the Theme Update information.
    462 		wp_clean_themes_cache( $parsed_args['clear_update_cache'] );
    463 
    464 		/** This action is documented in wp-admin/includes/class-wp-upgrader.php */
    465 		do_action(
    466 			'upgrader_process_complete',
    467 			$this,
    468 			array(
    469 				'action' => 'update',
    470 				'type'   => 'theme',
    471 				'bulk'   => true,
    472 				'themes' => $themes,
    473 			)
    474 		);
    475 
    476 		$this->skin->bulk_footer();
    477 
    478 		$this->skin->footer();
    479 
    480 		// Cleanup our hooks, in case something else does a upgrade on this connection.
    481 		remove_filter( 'upgrader_pre_install', array( $this, 'current_before' ) );
    482 		remove_filter( 'upgrader_post_install', array( $this, 'current_after' ) );
    483 		remove_filter( 'upgrader_clear_destination', array( $this, 'delete_old_theme' ) );
    484 
    485 		// Ensure any future auto-update failures trigger a failure email by removing
    486 		// the last failure notification from the list when themes update successfully.
    487 		$past_failure_emails = get_option( 'auto_plugin_theme_update_emails', array() );
    488 
    489 		foreach ( $results as $theme => $result ) {
    490 			// Maintain last failure notification when themes failed to update manually.
    491 			if ( ! $result || is_wp_error( $result ) || ! isset( $past_failure_emails[ $theme ] ) ) {
    492 				continue;
    493 			}
    494 
    495 			unset( $past_failure_emails[ $theme ] );
    496 		}
    497 
    498 		update_option( 'auto_plugin_theme_update_emails', $past_failure_emails );
    499 
    500 		return $results;
    501 	}
    502 
    503 	/**
    504 	 * Checks that the package source contains a valid theme.
    505 	 *
    506 	 * Hooked to the {@see 'upgrader_source_selection'} filter by Theme_Upgrader::install().
    507 	 *
    508 	 * @since 3.3.0
    509 	 *
    510 	 * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
    511 	 * @global string             $wp_version    The WordPress version string.
    512 	 *
    513 	 * @param string $source The path to the downloaded package source.
    514 	 * @return string|WP_Error The source as passed, or a WP_Error object on failure.
    515 	 */
    516 	public function check_package( $source ) {
    517 		global $wp_filesystem, $wp_version;
    518 
    519 		$this->new_theme_data = array();
    520 
    521 		if ( is_wp_error( $source ) ) {
    522 			return $source;
    523 		}
    524 
    525 		// Check that the folder contains a valid theme.
    526 		$working_directory = str_replace( $wp_filesystem->wp_content_dir(), trailingslashit( WP_CONTENT_DIR ), $source );
    527 		if ( ! is_dir( $working_directory ) ) { // Sanity check, if the above fails, let's not prevent installation.
    528 			return $source;
    529 		}
    530 
    531 		// A proper archive should have a style.css file in the single subdirectory.
    532 		if ( ! file_exists( $working_directory . 'style.css' ) ) {
    533 			return new WP_Error(
    534 				'incompatible_archive_theme_no_style',
    535 				$this->strings['incompatible_archive'],
    536 				sprintf(
    537 					/* translators: %s: style.css */
    538 					__( 'The theme is missing the %s stylesheet.' ),
    539 					'<code>style.css</code>'
    540 				)
    541 			);
    542 		}
    543 
    544 		// All these headers are needed on Theme_Installer_Skin::do_overwrite().
    545 		$info = get_file_data(
    546 			$working_directory . 'style.css',
    547 			array(
    548 				'Name'        => 'Theme Name',
    549 				'Version'     => 'Version',
    550 				'Author'      => 'Author',
    551 				'Template'    => 'Template',
    552 				'RequiresWP'  => 'Requires at least',
    553 				'RequiresPHP' => 'Requires PHP',
    554 			)
    555 		);
    556 
    557 		if ( empty( $info['Name'] ) ) {
    558 			return new WP_Error(
    559 				'incompatible_archive_theme_no_name',
    560 				$this->strings['incompatible_archive'],
    561 				sprintf(
    562 					/* translators: %s: style.css */
    563 					__( 'The %s stylesheet doesn&#8217;t contain a valid theme header.' ),
    564 					'<code>style.css</code>'
    565 				)
    566 			);
    567 		}
    568 
    569 		// If it's not a child theme, it must have at least an index.php to be legit.
    570 		if ( empty( $info['Template'] ) && ! file_exists( $working_directory . 'index.php' ) ) {
    571 			return new WP_Error(
    572 				'incompatible_archive_theme_no_index',
    573 				$this->strings['incompatible_archive'],
    574 				sprintf(
    575 					/* translators: %s: index.php */
    576 					__( 'The theme is missing the %s file.' ),
    577 					'<code>index.php</code>'
    578 				)
    579 			);
    580 		}
    581 
    582 		$requires_php = isset( $info['RequiresPHP'] ) ? $info['RequiresPHP'] : null;
    583 		$requires_wp  = isset( $info['RequiresWP'] ) ? $info['RequiresWP'] : null;
    584 
    585 		if ( ! is_php_version_compatible( $requires_php ) ) {
    586 			$error = sprintf(
    587 				/* translators: 1: Current PHP version, 2: Version required by the uploaded theme. */
    588 				__( 'The PHP version on your server is %1$s, however the uploaded theme requires %2$s.' ),
    589 				phpversion(),
    590 				$requires_php
    591 			);
    592 
    593 			return new WP_Error( 'incompatible_php_required_version', $this->strings['incompatible_archive'], $error );
    594 		}
    595 		if ( ! is_wp_version_compatible( $requires_wp ) ) {
    596 			$error = sprintf(
    597 				/* translators: 1: Current WordPress version, 2: Version required by the uploaded theme. */
    598 				__( 'Your WordPress version is %1$s, however the uploaded theme requires %2$s.' ),
    599 				$wp_version,
    600 				$requires_wp
    601 			);
    602 
    603 			return new WP_Error( 'incompatible_wp_required_version', $this->strings['incompatible_archive'], $error );
    604 		}
    605 
    606 		$this->new_theme_data = $info;
    607 
    608 		return $source;
    609 	}
    610 
    611 	/**
    612 	 * Turn on maintenance mode before attempting to upgrade the current theme.
    613 	 *
    614 	 * Hooked to the {@see 'upgrader_pre_install'} filter by Theme_Upgrader::upgrade() and
    615 	 * Theme_Upgrader::bulk_upgrade().
    616 	 *
    617 	 * @since 2.8.0
    618 	 *
    619 	 * @param bool|WP_Error $return Upgrade offer return.
    620 	 * @param array         $theme  Theme arguments.
    621 	 * @return bool|WP_Error The passed in $return param or WP_Error.
    622 	 */
    623 	public function current_before( $return, $theme ) {
    624 		if ( is_wp_error( $return ) ) {
    625 			return $return;
    626 		}
    627 
    628 		$theme = isset( $theme['theme'] ) ? $theme['theme'] : '';
    629 
    630 		// Only run if current theme
    631 		if ( get_stylesheet() !== $theme ) {
    632 			return $return;
    633 		}
    634 
    635 		// Change to maintenance mode. Bulk edit handles this separately.
    636 		if ( ! $this->bulk ) {
    637 			$this->maintenance_mode( true );
    638 		}
    639 
    640 		return $return;
    641 	}
    642 
    643 	/**
    644 	 * Turn off maintenance mode after upgrading the current theme.
    645 	 *
    646 	 * Hooked to the {@see 'upgrader_post_install'} filter by Theme_Upgrader::upgrade()
    647 	 * and Theme_Upgrader::bulk_upgrade().
    648 	 *
    649 	 * @since 2.8.0
    650 	 *
    651 	 * @param bool|WP_Error $return Upgrade offer return.
    652 	 * @param array         $theme  Theme arguments.
    653 	 * @return bool|WP_Error The passed in $return param or WP_Error.
    654 	 */
    655 	public function current_after( $return, $theme ) {
    656 		if ( is_wp_error( $return ) ) {
    657 			return $return;
    658 		}
    659 
    660 		$theme = isset( $theme['theme'] ) ? $theme['theme'] : '';
    661 
    662 		// Only run if current theme.
    663 		if ( get_stylesheet() !== $theme ) {
    664 			return $return;
    665 		}
    666 
    667 		// Ensure stylesheet name hasn't changed after the upgrade:
    668 		if ( get_stylesheet() === $theme && $theme !== $this->result['destination_name'] ) {
    669 			wp_clean_themes_cache();
    670 			$stylesheet = $this->result['destination_name'];
    671 			switch_theme( $stylesheet );
    672 		}
    673 
    674 		// Time to remove maintenance mode. Bulk edit handles this separately.
    675 		if ( ! $this->bulk ) {
    676 			$this->maintenance_mode( false );
    677 		}
    678 		return $return;
    679 	}
    680 
    681 	/**
    682 	 * Delete the old theme during an upgrade.
    683 	 *
    684 	 * Hooked to the {@see 'upgrader_clear_destination'} filter by Theme_Upgrader::upgrade()
    685 	 * and Theme_Upgrader::bulk_upgrade().
    686 	 *
    687 	 * @since 2.8.0
    688 	 *
    689 	 * @global WP_Filesystem_Base $wp_filesystem Subclass
    690 	 *
    691 	 * @param bool   $removed
    692 	 * @param string $local_destination
    693 	 * @param string $remote_destination
    694 	 * @param array  $theme
    695 	 * @return bool
    696 	 */
    697 	public function delete_old_theme( $removed, $local_destination, $remote_destination, $theme ) {
    698 		global $wp_filesystem;
    699 
    700 		if ( is_wp_error( $removed ) ) {
    701 			return $removed; // Pass errors through.
    702 		}
    703 
    704 		if ( ! isset( $theme['theme'] ) ) {
    705 			return $removed;
    706 		}
    707 
    708 		$theme      = $theme['theme'];
    709 		$themes_dir = trailingslashit( $wp_filesystem->wp_themes_dir( $theme ) );
    710 		if ( $wp_filesystem->exists( $themes_dir . $theme ) ) {
    711 			if ( ! $wp_filesystem->delete( $themes_dir . $theme, true ) ) {
    712 				return false;
    713 			}
    714 		}
    715 
    716 		return true;
    717 	}
    718 
    719 	/**
    720 	 * Get the WP_Theme object for a theme.
    721 	 *
    722 	 * @since 2.8.0
    723 	 * @since 3.0.0 The `$theme` argument was added.
    724 	 *
    725 	 * @param string $theme The directory name of the theme. This is optional, and if not supplied,
    726 	 *                      the directory name from the last result will be used.
    727 	 * @return WP_Theme|false The theme's info object, or false `$theme` is not supplied
    728 	 *                        and the last result isn't set.
    729 	 */
    730 	public function theme_info( $theme = null ) {
    731 		if ( empty( $theme ) ) {
    732 			if ( ! empty( $this->result['destination_name'] ) ) {
    733 				$theme = $this->result['destination_name'];
    734 			} else {
    735 				return false;
    736 			}
    737 		}
    738 
    739 		$theme = wp_get_theme( $theme );
    740 		$theme->cache_delete();
    741 
    742 		return $theme;
    743 	}
    744 
    745 }