wp/wp-admin/includes/class-wp-upgrader.php
changeset 5 5e2f62d02dcd
parent 0 d970ebf37754
child 7 cf61fcea0001
equal deleted inserted replaced
4:346c88efed21 5:5e2f62d02dcd
     2 /**
     2 /**
     3  * A File upgrader class for WordPress.
     3  * A File upgrader class for WordPress.
     4  *
     4  *
     5  * This set of classes are designed to be used to upgrade/install a local set of files on the filesystem via the Filesystem Abstraction classes.
     5  * This set of classes are designed to be used to upgrade/install a local set of files on the filesystem via the Filesystem Abstraction classes.
     6  *
     6  *
     7  * @link http://trac.wordpress.org/ticket/7875 consolidate plugin/theme/core upgrade/install functions
     7  * @link https://core.trac.wordpress.org/ticket/7875 consolidate plugin/theme/core upgrade/install functions
     8  *
     8  *
     9  * @package WordPress
     9  * @package WordPress
    10  * @subpackage Upgrader
    10  * @subpackage Upgrader
    11  * @since 2.8.0
    11  * @since 2.8.0
    12  */
    12  */
    19  * @package WordPress
    19  * @package WordPress
    20  * @subpackage Upgrader
    20  * @subpackage Upgrader
    21  * @since 2.8.0
    21  * @since 2.8.0
    22  */
    22  */
    23 class WP_Upgrader {
    23 class WP_Upgrader {
    24 	var $strings = array();
    24 
    25 	var $skin = null;
    25 	/**
    26 	var $result = array();
    26 	 * The error/notification strings used to update the user on the progress.
    27 
    27 	 *
    28 	function __construct($skin = null) {
    28 	 * @since 2.8.0
       
    29 	 * @var string $strings
       
    30 	 */
       
    31 	public $strings = array();
       
    32 
       
    33 	/**
       
    34 	 * The upgrader skin being used.
       
    35 	 *
       
    36 	 * @since 2.8.0
       
    37 	 * @var WP_Upgrader_Skin $skin
       
    38 	 */
       
    39 	public $skin = null;
       
    40 
       
    41 	/**
       
    42 	 * The result of the installation.
       
    43 	 *
       
    44 	 * This is set by {@see WP_Upgrader::install_package()}, only when the package is installed
       
    45 	 * successfully. It will then be an array, unless a {@see WP_Error} is returned by the
       
    46 	 * {@see 'upgrader_post_install'} filter. In that case, the `WP_Error` will be assigned to
       
    47 	 * it.
       
    48 	 *
       
    49 	 * @since 2.8.0
       
    50 	 * @var WP_Error|array $result {
       
    51 	 *      @type string $source             The full path to the source the files were installed from.
       
    52 	 *      @type string $source_files       List of all the files in the source directory.
       
    53 	 *      @type string $destination        The full path to the install destination folder.
       
    54 	 *      @type string $destination_name   The name of the destination folder, or empty if `$destination`
       
    55 	 *                                       and `$local_destination` are the same.
       
    56 	 *      @type string $local_destination  The full local path to the destination folder. This is usually
       
    57 	 *                                       the same as `$destination`.
       
    58 	 *      @type string $remote_destination The full remote path to the destination folder
       
    59 	 *                                       (i.e., from `$wp_filesystem`).
       
    60 	 *      @type bool   $clear_destination  Whether the destination folder was cleared.
       
    61 	 * }
       
    62 	 */
       
    63 	public $result = array();
       
    64 
       
    65 	/**
       
    66 	 * The total number of updates being performed.
       
    67 	 *
       
    68 	 * Set by the bulk update methods.
       
    69 	 *
       
    70 	 * @since 3.0.0
       
    71 	 * @var int $update_count
       
    72 	 */
       
    73 	public $update_count = 0;
       
    74 
       
    75 	/**
       
    76 	 * The current update if multiple updates are being performed.
       
    77 	 *
       
    78 	 * Used by the bulk update methods, and incremented for each update.
       
    79 	 *
       
    80 	 * @since 3.0.0
       
    81 	 * @var int
       
    82 	 */
       
    83 	public $update_current = 0;
       
    84 
       
    85 	/**
       
    86 	 * Construct the upgrader with a skin.
       
    87 	 *
       
    88 	 * @since 2.8.0
       
    89 	 *
       
    90 	 * @param WP_Upgrader_Skin $skin The upgrader skin to use. Default is a {@see WP_Upgrader_Skin}
       
    91 	 *                               instance.
       
    92 	 */
       
    93 	public function __construct( $skin = null ) {
    29 		if ( null == $skin )
    94 		if ( null == $skin )
    30 			$this->skin = new WP_Upgrader_Skin();
    95 			$this->skin = new WP_Upgrader_Skin();
    31 		else
    96 		else
    32 			$this->skin = $skin;
    97 			$this->skin = $skin;
    33 	}
    98 	}
    34 
    99 
    35 	function init() {
   100 	/**
       
   101 	 * Initialize the upgrader.
       
   102 	 *
       
   103 	 * This will set the relationship between the skin being used and this upgrader,
       
   104 	 * and also add the generic strings to `WP_Upgrader::$strings`.
       
   105 	 *
       
   106 	 * @since 2.8.0
       
   107 	 */
       
   108 	public function init() {
    36 		$this->skin->set_upgrader($this);
   109 		$this->skin->set_upgrader($this);
    37 		$this->generic_strings();
   110 		$this->generic_strings();
    38 	}
   111 	}
    39 
   112 
    40 	function generic_strings() {
   113 	/**
       
   114 	 * Add the generic strings to WP_Upgrader::$strings.
       
   115 	 *
       
   116 	 * @since 2.8.0
       
   117 	 */
       
   118 	public function generic_strings() {
    41 		$this->strings['bad_request'] = __('Invalid Data provided.');
   119 		$this->strings['bad_request'] = __('Invalid Data provided.');
    42 		$this->strings['fs_unavailable'] = __('Could not access filesystem.');
   120 		$this->strings['fs_unavailable'] = __('Could not access filesystem.');
    43 		$this->strings['fs_error'] = __('Filesystem error.');
   121 		$this->strings['fs_error'] = __('Filesystem error.');
    44 		$this->strings['fs_no_root_dir'] = __('Unable to locate WordPress Root directory.');
   122 		$this->strings['fs_no_root_dir'] = __('Unable to locate WordPress Root directory.');
    45 		$this->strings['fs_no_content_dir'] = __('Unable to locate WordPress Content directory (wp-content).');
   123 		$this->strings['fs_no_content_dir'] = __('Unable to locate WordPress Content directory (wp-content).');
    57 
   135 
    58 		$this->strings['maintenance_start'] = __('Enabling Maintenance mode…');
   136 		$this->strings['maintenance_start'] = __('Enabling Maintenance mode…');
    59 		$this->strings['maintenance_end'] = __('Disabling Maintenance mode…');
   137 		$this->strings['maintenance_end'] = __('Disabling Maintenance mode…');
    60 	}
   138 	}
    61 
   139 
    62 	function fs_connect( $directories = array() ) {
   140 	/**
       
   141 	 * Connect to the filesystem.
       
   142 	 *
       
   143 	 * @since 2.8.0
       
   144 	 *
       
   145 	 * @param array $directories                  Optional. A list of directories. If any of these do
       
   146 	 *                                            not exist, a {@see WP_Error} object will be returned.
       
   147 	 *                                            Default empty array.
       
   148 	 * @param bool  $allow_relaxed_file_ownership Whether to allow relaxed file ownership.
       
   149 	 *                                            Default false.
       
   150 	 * @return bool|WP_Error True if able to connect, false or a {@see WP_Error} otherwise.
       
   151 	 */
       
   152 	public function fs_connect( $directories = array(), $allow_relaxed_file_ownership = false ) {
    63 		global $wp_filesystem;
   153 		global $wp_filesystem;
    64 
   154 
    65 		if ( false === ($credentials = $this->skin->request_filesystem_credentials()) )
   155 		if ( false === ( $credentials = $this->skin->request_filesystem_credentials( false, $directories[0], $allow_relaxed_file_ownership ) ) ) {
    66 			return false;
   156 			return false;
    67 
   157 		}
    68 		if ( ! WP_Filesystem($credentials) ) {
   158 
       
   159 		if ( ! WP_Filesystem( $credentials, $directories[0], $allow_relaxed_file_ownership ) ) {
    69 			$error = true;
   160 			$error = true;
    70 			if ( is_object($wp_filesystem) && $wp_filesystem->errors->get_error_code() )
   161 			if ( is_object($wp_filesystem) && $wp_filesystem->errors->get_error_code() )
    71 				$error = $wp_filesystem->errors;
   162 				$error = $wp_filesystem->errors;
    72 			$this->skin->request_filesystem_credentials($error); //Failed to connect, Error and request again
   163 			// Failed to connect, Error and request again
       
   164 			$this->skin->request_filesystem_credentials( $error, $directories[0], $allow_relaxed_file_ownership );
    73 			return false;
   165 			return false;
    74 		}
   166 		}
    75 
   167 
    76 		if ( ! is_object($wp_filesystem) )
   168 		if ( ! is_object($wp_filesystem) )
    77 			return new WP_Error('fs_unavailable', $this->strings['fs_unavailable'] );
   169 			return new WP_Error('fs_unavailable', $this->strings['fs_unavailable'] );
   104 			}
   196 			}
   105 		}
   197 		}
   106 		return true;
   198 		return true;
   107 	} //end fs_connect();
   199 	} //end fs_connect();
   108 
   200 
   109 	function download_package($package) {
   201 	/**
       
   202 	 * Download a package.
       
   203 	 *
       
   204 	 * @since 2.8.0
       
   205 	 *
       
   206 	 * @param string $package The URI of the package. If this is the full path to an
       
   207 	 *                        existing local file, it will be returned untouched.
       
   208 	 * @return string|WP_Error The full path to the downloaded package file, or a {@see WP_Error} object.
       
   209 	 */
       
   210 	public function download_package( $package ) {
   110 
   211 
   111 		/**
   212 		/**
   112 		 * Filter whether to return the package.
   213 		 * Filter whether to return the package.
   113 		 *
   214 		 *
   114 		 * @since 3.7.0
   215 		 * @since 3.7.0
   115 		 *
   216 		 *
   116 		 * @param bool    $reply   Whether to bail without returning the package. Default is false.
   217 		 * @param bool        $reply   Whether to bail without returning the package.
   117 		 * @param string  $package The package file name.
   218 		 *                             Default false.
   118 		 * @param object  $this    The WP_Upgrader instance.
   219 		 * @param string      $package The package file name.
       
   220 		 * @param WP_Upgrader $this    The WP_Upgrader instance.
   119 		 */
   221 		 */
   120 		$reply = apply_filters( 'upgrader_pre_download', false, $package, $this );
   222 		$reply = apply_filters( 'upgrader_pre_download', false, $package, $this );
   121 		if ( false !== $reply )
   223 		if ( false !== $reply )
   122 			return $reply;
   224 			return $reply;
   123 
   225 
   135 			return new WP_Error('download_failed', $this->strings['download_failed'], $download_file->get_error_message());
   237 			return new WP_Error('download_failed', $this->strings['download_failed'], $download_file->get_error_message());
   136 
   238 
   137 		return $download_file;
   239 		return $download_file;
   138 	}
   240 	}
   139 
   241 
   140 	function unpack_package($package, $delete_package = true) {
   242 	/**
       
   243 	 * Unpack a compressed package file.
       
   244 	 *
       
   245 	 * @since 2.8.0
       
   246 	 *
       
   247 	 * @param string $package        Full path to the package file.
       
   248 	 * @param bool   $delete_package Optional. Whether to delete the package file after attempting
       
   249 	 *                               to unpack it. Default true.
       
   250 	 * @return string|WP_Error The path to the unpacked contents, or a {@see WP_Error} on failure.
       
   251 	 */
       
   252 	public function unpack_package( $package, $delete_package = true ) {
   141 		global $wp_filesystem;
   253 		global $wp_filesystem;
   142 
   254 
   143 		$this->skin->feedback('unpack_package');
   255 		$this->skin->feedback('unpack_package');
   144 
   256 
   145 		$upgrade_folder = $wp_filesystem->wp_content_dir() . 'upgrade/';
   257 		$upgrade_folder = $wp_filesystem->wp_content_dir() . 'upgrade/';
   149 		if ( !empty($upgrade_files) ) {
   261 		if ( !empty($upgrade_files) ) {
   150 			foreach ( $upgrade_files as $file )
   262 			foreach ( $upgrade_files as $file )
   151 				$wp_filesystem->delete($upgrade_folder . $file['name'], true);
   263 				$wp_filesystem->delete($upgrade_folder . $file['name'], true);
   152 		}
   264 		}
   153 
   265 
   154 		//We need a working directory
   266 		// We need a working directory - Strip off any .tmp or .zip suffixes
   155 		$working_dir = $upgrade_folder . basename($package, '.zip');
   267 		$working_dir = $upgrade_folder . basename( basename( $package, '.tmp' ), '.zip' );
   156 
   268 
   157 		// Clean up working directory
   269 		// Clean up working directory
   158 		if ( $wp_filesystem->is_dir($working_dir) )
   270 		if ( $wp_filesystem->is_dir($working_dir) )
   159 			$wp_filesystem->delete($working_dir, true);
   271 			$wp_filesystem->delete($working_dir, true);
   160 
   272 
   174 		}
   286 		}
   175 
   287 
   176 		return $working_dir;
   288 		return $working_dir;
   177 	}
   289 	}
   178 
   290 
   179 	function install_package( $args = array() ) {
   291 	/**
       
   292 	 * Install a package.
       
   293 	 *
       
   294 	 * Copies the contents of a package form a source directory, and installs them in
       
   295 	 * a destination directory. Optionally removes the source. It can also optionally
       
   296 	 * clear out the destination folder if it already exists.
       
   297 	 *
       
   298 	 * @since 2.8.0
       
   299 	 *
       
   300 	 * @param array|string $args {
       
   301 	 *     Optional. Array or string of arguments for installing a package. Default empty array.
       
   302 	 *
       
   303 	 *     @type string $source                      Required path to the package source. Default empty.
       
   304 	 *     @type string $destination                 Required path to a folder to install the package in.
       
   305 	 *                                               Default empty.
       
   306 	 *     @type bool   $clear_destination           Whether to delete any files already in the destination
       
   307 	 *                                               folder. Default false.
       
   308 	 *     @type bool   $clear_working               Whether to delete the files form the working directory
       
   309 	 *                                               after copying to the destination. Default false.
       
   310 	 *     @type bool   $abort_if_destination_exists Whether to abort the installation if
       
   311 	 *                                               the destination folder already exists. Default true.
       
   312 	 *     @type array  $hook_extra                  Extra arguments to pass to the filter hooks called by
       
   313 	 *                                               {@see WP_Upgrader::install_package()}. Default empty array.
       
   314 	 * }
       
   315 	 *
       
   316 	 * @return array|WP_Error The result (also stored in `WP_Upgrader:$result`), or a {@see WP_Error} on failure.
       
   317 	 */
       
   318 	public function install_package( $args = array() ) {
   180 		global $wp_filesystem, $wp_theme_directories;
   319 		global $wp_filesystem, $wp_theme_directories;
   181 
   320 
   182 		$defaults = array(
   321 		$defaults = array(
   183 			'source' => '', // Please always pass this
   322 			'source' => '', // Please always pass this
   184 			'destination' => '', // and this
   323 			'destination' => '', // and this
   187 			'abort_if_destination_exists' => true,
   326 			'abort_if_destination_exists' => true,
   188 			'hook_extra' => array()
   327 			'hook_extra' => array()
   189 		);
   328 		);
   190 
   329 
   191 		$args = wp_parse_args($args, $defaults);
   330 		$args = wp_parse_args($args, $defaults);
   192 		extract($args);
   331 
       
   332 		// These were previously extract()'d.
       
   333 		$source = $args['source'];
       
   334 		$destination = $args['destination'];
       
   335 		$clear_destination = $args['clear_destination'];
   193 
   336 
   194 		@set_time_limit( 300 );
   337 		@set_time_limit( 300 );
   195 
   338 
   196 		if ( empty($source) || empty($destination) )
   339 		if ( empty( $source ) || empty( $destination ) ) {
   197 			return new WP_Error('bad_request', $this->strings['bad_request']);
   340 			return new WP_Error( 'bad_request', $this->strings['bad_request'] );
   198 
   341 		}
   199 		$this->skin->feedback('installing_package');
   342 		$this->skin->feedback( 'installing_package' );
   200 
   343 
   201 		$res = apply_filters('upgrader_pre_install', true, $hook_extra);
   344 		/**
   202 		if ( is_wp_error($res) )
   345 		 * Filter the install response before the installation has started.
       
   346 		 *
       
   347 		 * Returning a truthy value, or one that could be evaluated as a WP_Error
       
   348 		 * will effectively short-circuit the installation, returning that value
       
   349 		 * instead.
       
   350 		 *
       
   351 		 * @since 2.8.0
       
   352 		 *
       
   353 		 * @param bool|WP_Error $response   Response.
       
   354 		 * @param array         $hook_extra Extra arguments passed to hooked filters.
       
   355 		 */
       
   356 		$res = apply_filters( 'upgrader_pre_install', true, $args['hook_extra'] );
       
   357 		if ( is_wp_error( $res ) ) {
   203 			return $res;
   358 			return $res;
       
   359 		}
   204 
   360 
   205 		//Retain the Original source and destinations
   361 		//Retain the Original source and destinations
   206 		$remote_source = $source;
   362 		$remote_source = $args['source'];
   207 		$local_destination = $destination;
   363 		$local_destination = $destination;
   208 
   364 
   209 		$source_files = array_keys( $wp_filesystem->dirlist($remote_source) );
   365 		$source_files = array_keys( $wp_filesystem->dirlist( $remote_source ) );
   210 		$remote_destination = $wp_filesystem->find_folder($local_destination);
   366 		$remote_destination = $wp_filesystem->find_folder( $local_destination );
   211 
   367 
   212 		//Locate which directory to copy to the new folder, This is based on the actual folder holding the files.
   368 		//Locate which directory to copy to the new folder, This is based on the actual folder holding the files.
   213 		if ( 1 == count($source_files) && $wp_filesystem->is_dir( trailingslashit($source) . $source_files[0] . '/') ) //Only one folder? Then we want its contents.
   369 		if ( 1 == count( $source_files ) && $wp_filesystem->is_dir( trailingslashit( $args['source'] ) . $source_files[0] . '/' ) ) { //Only one folder? Then we want its contents.
   214 			$source = trailingslashit($source) . trailingslashit($source_files[0]);
   370 			$source = trailingslashit( $args['source'] ) . trailingslashit( $source_files[0] );
   215 		elseif ( count($source_files) == 0 )
   371 		} elseif ( count( $source_files ) == 0 ) {
   216 			return new WP_Error( 'incompatible_archive_empty', $this->strings['incompatible_archive'], $this->strings['no_files'] ); // There are no files?
   372 			return new WP_Error( 'incompatible_archive_empty', $this->strings['incompatible_archive'], $this->strings['no_files'] ); // There are no files?
   217 		else //It's only a single file, the upgrader will use the foldername of this file as the destination folder. foldername is based on zip filename.
   373 		} else { //It's only a single file, the upgrader will use the foldername of this file as the destination folder. foldername is based on zip filename.
   218 			$source = trailingslashit($source);
   374 			$source = trailingslashit( $args['source'] );
   219 
   375 		}
   220 		//Hook ability to change the source file location..
   376 
   221 		$source = apply_filters('upgrader_source_selection', $source, $remote_source, $this);
   377 		/**
   222 		if ( is_wp_error($source) )
   378 		 * Filter the source file location for the upgrade package.
       
   379 		 *
       
   380 		 * @since 2.8.0
       
   381 		 *
       
   382 		 * @param string      $source        File source location.
       
   383 		 * @param string      $remote_source Remove file source location.
       
   384 		 * @param WP_Upgrader $this          WP_Upgrader instance.
       
   385 		 */
       
   386 		$source = apply_filters( 'upgrader_source_selection', $source, $remote_source, $this );
       
   387 		if ( is_wp_error( $source ) ) {
   223 			return $source;
   388 			return $source;
   224 
   389 		}
   225 		//Has the source location changed? If so, we need a new source_files list.
   390 
   226 		if ( $source !== $remote_source )
   391 		// Has the source location changed? If so, we need a new source_files list.
   227 			$source_files = array_keys( $wp_filesystem->dirlist($source) );
   392 		if ( $source !== $remote_source ) {
   228 
   393 			$source_files = array_keys( $wp_filesystem->dirlist( $source ) );
   229 		// Protection against deleting files in any important base directories.
   394 		}
   230 		// Theme_Upgrader & Plugin_Upgrader also trigger this, as they pass the destination directory (WP_PLUGIN_DIR / wp-content/themes)
   395 		/*
   231 		// intending to copy the directory into the directory, whilst they pass the source as the actual files to copy.
   396 		 * Protection against deleting files in any important base directories.
       
   397 		 * Theme_Upgrader & Plugin_Upgrader also trigger this, as they pass the
       
   398 		 * destination directory (WP_PLUGIN_DIR / wp-content/themes) intending
       
   399 		 * to copy the directory into the directory, whilst they pass the source
       
   400 		 * as the actual files to copy.
       
   401 		 */
   232 		$protected_directories = array( ABSPATH, WP_CONTENT_DIR, WP_PLUGIN_DIR, WP_CONTENT_DIR . '/themes' );
   402 		$protected_directories = array( ABSPATH, WP_CONTENT_DIR, WP_PLUGIN_DIR, WP_CONTENT_DIR . '/themes' );
   233 		if ( is_array( $wp_theme_directories ) )
   403 
       
   404 		if ( is_array( $wp_theme_directories ) ) {
   234 			$protected_directories = array_merge( $protected_directories, $wp_theme_directories );
   405 			$protected_directories = array_merge( $protected_directories, $wp_theme_directories );
       
   406 		}
   235 		if ( in_array( $destination, $protected_directories ) ) {
   407 		if ( in_array( $destination, $protected_directories ) ) {
   236 			$remote_destination = trailingslashit($remote_destination) . trailingslashit(basename($source));
   408 			$remote_destination = trailingslashit( $remote_destination ) . trailingslashit( basename( $source ) );
   237 			$destination = trailingslashit($destination) . trailingslashit(basename($source));
   409 			$destination = trailingslashit( $destination ) . trailingslashit( basename( $source ) );
   238 		}
   410 		}
   239 
   411 
   240 		if ( $clear_destination ) {
   412 		if ( $clear_destination ) {
   241 			//We're going to clear the destination if there's something there
   413 			//We're going to clear the destination if there's something there
   242 			$this->skin->feedback('remove_old');
   414 			$this->skin->feedback('remove_old');
   243 			$removed = true;
   415 			$removed = true;
   244 			if ( $wp_filesystem->exists($remote_destination) )
   416 			if ( $wp_filesystem->exists( $remote_destination ) ) {
   245 				$removed = $wp_filesystem->delete($remote_destination, true);
   417 				$removed = $wp_filesystem->delete( $remote_destination, true );
   246 			$removed = apply_filters('upgrader_clear_destination', $removed, $local_destination, $remote_destination, $hook_extra);
   418 			}
   247 
   419 
   248 			if ( is_wp_error($removed) )
   420 			/**
       
   421 			 * Filter whether the upgrader cleared the destination.
       
   422 			 *
       
   423 			 * @since 2.8.0
       
   424 			 *
       
   425 			 * @param bool   $removed            Whether the destination was cleared.
       
   426 			 * @param string $local_destination  The local package destination.
       
   427 			 * @param string $remote_destination The remote package destination.
       
   428 			 * @param array  $hook_extra         Extra arguments passed to hooked filters.
       
   429 			 */
       
   430 			$removed = apply_filters( 'upgrader_clear_destination', $removed, $local_destination, $remote_destination, $args['hook_extra'] );
       
   431 
       
   432 			if ( is_wp_error($removed) ) {
   249 				return $removed;
   433 				return $removed;
   250 			else if ( ! $removed )
   434 			} elseif ( ! $removed ) {
   251 				return new WP_Error('remove_old_failed', $this->strings['remove_old_failed']);
   435 				return new WP_Error('remove_old_failed', $this->strings['remove_old_failed']);
   252 		} elseif ( $abort_if_destination_exists && $wp_filesystem->exists($remote_destination) ) {
   436 			}
       
   437 		} elseif ( $args['abort_if_destination_exists'] && $wp_filesystem->exists($remote_destination) ) {
   253 			//If we're not clearing the destination folder and something exists there already, Bail.
   438 			//If we're not clearing the destination folder and something exists there already, Bail.
   254 			//But first check to see if there are actually any files in the folder.
   439 			//But first check to see if there are actually any files in the folder.
   255 			$_files = $wp_filesystem->dirlist($remote_destination);
   440 			$_files = $wp_filesystem->dirlist($remote_destination);
   256 			if ( ! empty($_files) ) {
   441 			if ( ! empty($_files) ) {
   257 				$wp_filesystem->delete($remote_source, true); //Clear out the source files.
   442 				$wp_filesystem->delete($remote_source, true); //Clear out the source files.
   258 				return new WP_Error('folder_exists', $this->strings['folder_exists'], $remote_destination );
   443 				return new WP_Error('folder_exists', $this->strings['folder_exists'], $remote_destination );
   259 			}
   444 			}
   260 		}
   445 		}
   261 
   446 
   262 		//Create destination if needed
   447 		//Create destination if needed
   263 		if ( !$wp_filesystem->exists($remote_destination) )
   448 		if ( ! $wp_filesystem->exists( $remote_destination ) ) {
   264 			if ( !$wp_filesystem->mkdir($remote_destination, FS_CHMOD_DIR) )
   449 			if ( ! $wp_filesystem->mkdir( $remote_destination, FS_CHMOD_DIR ) ) {
   265 				return new WP_Error( 'mkdir_failed_destination', $this->strings['mkdir_failed'], $remote_destination );
   450 				return new WP_Error( 'mkdir_failed_destination', $this->strings['mkdir_failed'], $remote_destination );
   266 
   451 			}
       
   452 		}
   267 		// Copy new version of item into place.
   453 		// Copy new version of item into place.
   268 		$result = copy_dir($source, $remote_destination);
   454 		$result = copy_dir($source, $remote_destination);
   269 		if ( is_wp_error($result) ) {
   455 		if ( is_wp_error($result) ) {
   270 			if ( $clear_working )
   456 			if ( $args['clear_working'] ) {
   271 				$wp_filesystem->delete($remote_source, true);
   457 				$wp_filesystem->delete( $remote_source, true );
       
   458 			}
   272 			return $result;
   459 			return $result;
   273 		}
   460 		}
   274 
   461 
   275 		//Clear the Working folder?
   462 		//Clear the Working folder?
   276 		if ( $clear_working )
   463 		if ( $args['clear_working'] ) {
   277 			$wp_filesystem->delete($remote_source, true);
   464 			$wp_filesystem->delete( $remote_source, true );
       
   465 		}
   278 
   466 
   279 		$destination_name = basename( str_replace($local_destination, '', $destination) );
   467 		$destination_name = basename( str_replace($local_destination, '', $destination) );
   280 		if ( '.' == $destination_name )
   468 		if ( '.' == $destination_name ) {
   281 			$destination_name = '';
   469 			$destination_name = '';
   282 
   470 		}
   283 		$this->result = compact('local_source', 'source', 'source_name', 'source_files', 'destination', 'destination_name', 'local_destination', 'remote_destination', 'clear_destination', 'delete_source_dir');
   471 
   284 
   472 		$this->result = compact( 'source', 'source_files', 'destination', 'destination_name', 'local_destination', 'remote_destination', 'clear_destination' );
   285 		$res = apply_filters('upgrader_post_install', true, $hook_extra, $this->result);
   473 
       
   474 		/**
       
   475 		 * Filter the install response after the installation has finished.
       
   476 		 *
       
   477 		 * @since 2.8.0
       
   478 		 *
       
   479 		 * @param bool  $response   Install response.
       
   480 		 * @param array $hook_extra Extra arguments passed to hooked filters.
       
   481 		 * @param array $result     Installation result data.
       
   482 		 */
       
   483 		$res = apply_filters( 'upgrader_post_install', true, $args['hook_extra'], $this->result );
       
   484 
   286 		if ( is_wp_error($res) ) {
   485 		if ( is_wp_error($res) ) {
   287 			$this->result = $res;
   486 			$this->result = $res;
   288 			return $res;
   487 			return $res;
   289 		}
   488 		}
   290 
   489 
   291 		//Bombard the calling function will all the info which we've just used.
   490 		//Bombard the calling function will all the info which we've just used.
   292 		return $this->result;
   491 		return $this->result;
   293 	}
   492 	}
   294 
   493 
   295 	function run($options) {
   494 	/**
       
   495 	 * Run an upgrade/install.
       
   496 	 *
       
   497 	 * Attempts to download the package (if it is not a local file), unpack it, and
       
   498 	 * install it in the destination folder.
       
   499 	 *
       
   500 	 * @since 2.8.0
       
   501 	 *
       
   502 	 * @param array $options {
       
   503 	 *     Array or string of arguments for upgrading/installing a package.
       
   504 	 *
       
   505 	 *     @type string $package                     The full path or URI of the package to install.
       
   506 	 *                                               Default empty.
       
   507 	 *     @type string $destination                 The full path to the destination folder.
       
   508 	 *                                               Default empty.
       
   509 	 *     @type bool   $clear_destination           Whether to delete any files already in the
       
   510 	 *                                               destination folder. Default false.
       
   511 	 *     @type bool   $clear_working               Whether to delete the files form the working
       
   512 	 *                                               directory after copying to the destination.
       
   513 	 *                                               Default false.
       
   514 	 *     @type bool   $abort_if_destination_exists Whether to abort the installation if the destination
       
   515 	 *                                               folder already exists. When true, `$clear_destination`
       
   516 	 *                                               should be false. Default true.
       
   517 	 *     @type bool   $is_multi                    Whether this run is one of multiple upgrade/install
       
   518 	 *                                               actions being performed in bulk. When true, the skin
       
   519 	 *                                               {@see WP_Upgrader::header()} and {@see WP_Upgrader::footer()}
       
   520 	 *                                               aren't called. Default false.
       
   521 	 *     @type array  $hook_extra                  Extra arguments to pass to the filter hooks called by
       
   522 	 *                                               {@see WP_Upgrader::run()}.
       
   523 	 * }
       
   524 	 *
       
   525 	 * @return array|false|WP_error The result from self::install_package() on success, otherwise a WP_Error,
       
   526 	 *                              or false if unable to connect to the filesystem.
       
   527 	 */
       
   528 	public function run( $options ) {
   296 
   529 
   297 		$defaults = array(
   530 		$defaults = array(
   298 			'package' => '', // Please always pass this.
   531 			'package' => '', // Please always pass this.
   299 			'destination' => '', // And this
   532 			'destination' => '', // And this
   300 			'clear_destination' => false,
   533 			'clear_destination' => false,
   302 			'clear_working' => true,
   535 			'clear_working' => true,
   303 			'is_multi' => false,
   536 			'is_multi' => false,
   304 			'hook_extra' => array() // Pass any extra $hook_extra args here, this will be passed to any hooked filters.
   537 			'hook_extra' => array() // Pass any extra $hook_extra args here, this will be passed to any hooked filters.
   305 		);
   538 		);
   306 
   539 
   307 		$options = wp_parse_args($options, $defaults);
   540 		$options = wp_parse_args( $options, $defaults );
   308 		extract($options);
   541 
   309 
   542 		if ( ! $options['is_multi'] ) { // call $this->header separately if running multiple times
   310 		if ( ! $is_multi ) // call $this->header separately if running multiple times
       
   311 			$this->skin->header();
   543 			$this->skin->header();
       
   544 		}
   312 
   545 
   313 		// Connect to the Filesystem first.
   546 		// Connect to the Filesystem first.
   314 		$res = $this->fs_connect( array(WP_CONTENT_DIR, $destination) );
   547 		$res = $this->fs_connect( array( WP_CONTENT_DIR, $options['destination'] ) );
   315 		// Mainly for non-connected filesystem.
   548 		// Mainly for non-connected filesystem.
   316 		if ( ! $res ) {
   549 		if ( ! $res ) {
   317 			if ( ! $is_multi )
   550 			if ( ! $options['is_multi'] ) {
   318 				$this->skin->footer();
   551 				$this->skin->footer();
       
   552 			}
   319 			return false;
   553 			return false;
   320 		}
   554 		}
   321 
   555 
   322 		$this->skin->before();
   556 		$this->skin->before();
   323 
   557 
   324 		if ( is_wp_error($res) ) {
   558 		if ( is_wp_error($res) ) {
   325 			$this->skin->error($res);
   559 			$this->skin->error($res);
   326 			$this->skin->after();
   560 			$this->skin->after();
   327 			if ( ! $is_multi )
   561 			if ( ! $options['is_multi'] ) {
   328 				$this->skin->footer();
   562 				$this->skin->footer();
       
   563 			}
   329 			return $res;
   564 			return $res;
   330 		}
   565 		}
   331 
   566 
   332 		//Download the package (Note, This just returns the filename of the file if the package is a local file)
   567 		//Download the package (Note, This just returns the filename of the file if the package is a local file)
   333 		$download = $this->download_package( $package );
   568 		$download = $this->download_package( $options['package'] );
   334 		if ( is_wp_error($download) ) {
   569 		if ( is_wp_error($download) ) {
   335 			$this->skin->error($download);
   570 			$this->skin->error($download);
   336 			$this->skin->after();
   571 			$this->skin->after();
   337 			if ( ! $is_multi )
   572 			if ( ! $options['is_multi'] ) {
   338 				$this->skin->footer();
   573 				$this->skin->footer();
       
   574 			}
   339 			return $download;
   575 			return $download;
   340 		}
   576 		}
   341 
   577 
   342 		$delete_package = ($download != $package); // Do not delete a "local" file
   578 		$delete_package = ( $download != $options['package'] ); // Do not delete a "local" file
   343 
   579 
   344 		//Unzips the file into a temporary directory
   580 		//Unzips the file into a temporary directory
   345 		$working_dir = $this->unpack_package( $download, $delete_package );
   581 		$working_dir = $this->unpack_package( $download, $delete_package );
   346 		if ( is_wp_error($working_dir) ) {
   582 		if ( is_wp_error($working_dir) ) {
   347 			$this->skin->error($working_dir);
   583 			$this->skin->error($working_dir);
   348 			$this->skin->after();
   584 			$this->skin->after();
   349 			if ( ! $is_multi )
   585 			if ( ! $options['is_multi'] ) {
   350 				$this->skin->footer();
   586 				$this->skin->footer();
       
   587 			}
   351 			return $working_dir;
   588 			return $working_dir;
   352 		}
   589 		}
   353 
   590 
   354 		//With the given options, this installs it to the destination directory.
   591 		//With the given options, this installs it to the destination directory.
   355 		$result = $this->install_package( array(
   592 		$result = $this->install_package( array(
   356 			'source' => $working_dir,
   593 			'source' => $working_dir,
   357 			'destination' => $destination,
   594 			'destination' => $options['destination'],
   358 			'clear_destination' => $clear_destination,
   595 			'clear_destination' => $options['clear_destination'],
   359 			'abort_if_destination_exists' => $abort_if_destination_exists,
   596 			'abort_if_destination_exists' => $options['abort_if_destination_exists'],
   360 			'clear_working' => $clear_working,
   597 			'clear_working' => $options['clear_working'],
   361 			'hook_extra' => $hook_extra
   598 			'hook_extra' => $options['hook_extra']
   362 		) );
   599 		) );
   363 
   600 
   364 		$this->skin->set_result($result);
   601 		$this->skin->set_result($result);
   365 		if ( is_wp_error($result) ) {
   602 		if ( is_wp_error($result) ) {
   366 			$this->skin->error($result);
   603 			$this->skin->error($result);
   370 			$this->skin->feedback('process_success');
   607 			$this->skin->feedback('process_success');
   371 		}
   608 		}
   372 
   609 
   373 		$this->skin->after();
   610 		$this->skin->after();
   374 
   611 
   375 		if ( ! $is_multi ) {
   612 		if ( ! $options['is_multi'] ) {
   376 			do_action( 'upgrader_process_complete', $this, $hook_extra );
   613 
       
   614 			/** This action is documented in wp-admin/includes/class-wp-upgrader.php */
       
   615 			do_action( 'upgrader_process_complete', $this, $options['hook_extra'] );
   377 			$this->skin->footer();
   616 			$this->skin->footer();
   378 		}
   617 		}
   379 
   618 
   380 		return $result;
   619 		return $result;
   381 	}
   620 	}
   382 
   621 
   383 	function maintenance_mode($enable = false) {
   622 	/**
       
   623 	 * Toggle maintenance mode for the site.
       
   624 	 *
       
   625 	 * Creates/deletes the maintenance file to enable/disable maintenance mode.
       
   626 	 *
       
   627 	 * @since 2.8.0
       
   628 	 *
       
   629 	 * @param bool $enable True to enable maintenance mode, false to disable.
       
   630 	 */
       
   631 	public function maintenance_mode( $enable = false ) {
   384 		global $wp_filesystem;
   632 		global $wp_filesystem;
   385 		$file = $wp_filesystem->abspath() . '.maintenance';
   633 		$file = $wp_filesystem->abspath() . '.maintenance';
   386 		if ( $enable ) {
   634 		if ( $enable ) {
   387 			$this->skin->feedback('maintenance_start');
   635 			$this->skin->feedback('maintenance_start');
   388 			// Create maintenance file to signal that we are upgrading
   636 			// Create maintenance file to signal that we are upgrading
   389 			$maintenance_string = '<?php $upgrading = ' . time() . '; ?>';
   637 			$maintenance_string = '<?php $upgrading = ' . time() . '; ?>';
   390 			$wp_filesystem->delete($file);
   638 			$wp_filesystem->delete($file);
   391 			$wp_filesystem->put_contents($file, $maintenance_string, FS_CHMOD_FILE);
   639 			$wp_filesystem->put_contents($file, $maintenance_string, FS_CHMOD_FILE);
   392 		} else if ( !$enable && $wp_filesystem->exists($file) ) {
   640 		} elseif ( ! $enable && $wp_filesystem->exists( $file ) ) {
   393 			$this->skin->feedback('maintenance_end');
   641 			$this->skin->feedback('maintenance_end');
   394 			$wp_filesystem->delete($file);
   642 			$wp_filesystem->delete($file);
   395 		}
   643 		}
   396 	}
   644 	}
   397 
   645 
   404  * @subpackage Upgrader
   652  * @subpackage Upgrader
   405  * @since 2.8.0
   653  * @since 2.8.0
   406  */
   654  */
   407 class Plugin_Upgrader extends WP_Upgrader {
   655 class Plugin_Upgrader extends WP_Upgrader {
   408 
   656 
   409 	var $result;
   657 	/**
   410 	var $bulk = false;
   658 	 * Plugin upgrade result.
   411 	var $show_before = '';
   659 	 *
   412 
   660 	 * @since 2.8.0
   413 	function upgrade_strings() {
   661 	 * @var array|WP_Error $result
       
   662 	 * @see WP_Upgrader::$result
       
   663 	 */
       
   664 	public $result;
       
   665 
       
   666 	/**
       
   667 	 * Whether a bulk upgrade/install is being performed.
       
   668 	 *
       
   669 	 * @since 2.9.0
       
   670 	 * @var bool $bulk
       
   671 	 */
       
   672 	public $bulk = false;
       
   673 
       
   674 	/**
       
   675 	 * Initialize the upgrade strings.
       
   676 	 *
       
   677 	 * @since 2.8.0
       
   678 	 */
       
   679 	public function upgrade_strings() {
   414 		$this->strings['up_to_date'] = __('The plugin is at the latest version.');
   680 		$this->strings['up_to_date'] = __('The plugin is at the latest version.');
   415 		$this->strings['no_package'] = __('Update package not available.');
   681 		$this->strings['no_package'] = __('Update package not available.');
   416 		$this->strings['downloading_package'] = __('Downloading update from <span class="code">%s</span>&#8230;');
   682 		$this->strings['downloading_package'] = __('Downloading update from <span class="code">%s</span>&#8230;');
   417 		$this->strings['unpack_package'] = __('Unpacking the update&#8230;');
   683 		$this->strings['unpack_package'] = __('Unpacking the update&#8230;');
   418 		$this->strings['remove_old'] = __('Removing the old version of the plugin&#8230;');
   684 		$this->strings['remove_old'] = __('Removing the old version of the plugin&#8230;');
   419 		$this->strings['remove_old_failed'] = __('Could not remove the old plugin.');
   685 		$this->strings['remove_old_failed'] = __('Could not remove the old plugin.');
   420 		$this->strings['process_failed'] = __('Plugin update failed.');
   686 		$this->strings['process_failed'] = __('Plugin update failed.');
   421 		$this->strings['process_success'] = __('Plugin updated successfully.');
   687 		$this->strings['process_success'] = __('Plugin updated successfully.');
   422 	}
   688 		$this->strings['process_bulk_success'] = __('Plugins updated successfully.');
   423 
   689 	}
   424 	function install_strings() {
   690 
       
   691 	/**
       
   692 	 * Initialize the install strings.
       
   693 	 *
       
   694 	 * @since 2.8.0
       
   695 	 */
       
   696 	public function install_strings() {
   425 		$this->strings['no_package'] = __('Install package not available.');
   697 		$this->strings['no_package'] = __('Install package not available.');
   426 		$this->strings['downloading_package'] = __('Downloading install package from <span class="code">%s</span>&#8230;');
   698 		$this->strings['downloading_package'] = __('Downloading install package from <span class="code">%s</span>&#8230;');
   427 		$this->strings['unpack_package'] = __('Unpacking the package&#8230;');
   699 		$this->strings['unpack_package'] = __('Unpacking the package&#8230;');
   428 		$this->strings['installing_package'] = __('Installing the plugin&#8230;');
   700 		$this->strings['installing_package'] = __('Installing the plugin&#8230;');
   429 		$this->strings['no_files'] = __('The plugin contains no files.');
   701 		$this->strings['no_files'] = __('The plugin contains no files.');
   430 		$this->strings['process_failed'] = __('Plugin install failed.');
   702 		$this->strings['process_failed'] = __('Plugin install failed.');
   431 		$this->strings['process_success'] = __('Plugin installed successfully.');
   703 		$this->strings['process_success'] = __('Plugin installed successfully.');
   432 	}
   704 	}
   433 
   705 
   434 	function install( $package, $args = array() ) {
   706 	/**
       
   707 	 * Install a plugin package.
       
   708 	 *
       
   709 	 * @since 2.8.0
       
   710 	 * @since 3.7.0 The `$args` parameter was added, making clearing the plugin update cache optional.
       
   711 	 *
       
   712 	 * @param string $package The full local path or URI of the package.
       
   713 	 * @param array  $args {
       
   714 	 *     Optional. Other arguments for installing a plugin package. Default empty array.
       
   715 	 *
       
   716 	 *     @type bool $clear_update_cache Whether to clear the plugin updates cache if successful.
       
   717 	 *                                    Default true.
       
   718 	 * }
       
   719 	 *
       
   720 	 * @return bool|WP_Error True if the install was successful, false or a WP_Error otherwise.
       
   721 	 */
       
   722 	public function install( $package, $args = array() ) {
   435 
   723 
   436 		$defaults = array(
   724 		$defaults = array(
   437 			'clear_update_cache' => true,
   725 			'clear_update_cache' => true,
   438 		);
   726 		);
   439 		$parsed_args = wp_parse_args( $args, $defaults );
   727 		$parsed_args = wp_parse_args( $args, $defaults );
   463 		wp_clean_plugins_cache( $parsed_args['clear_update_cache'] );
   751 		wp_clean_plugins_cache( $parsed_args['clear_update_cache'] );
   464 
   752 
   465 		return true;
   753 		return true;
   466 	}
   754 	}
   467 
   755 
   468 	function upgrade( $plugin, $args = array() ) {
   756 	/**
       
   757 	 * Upgrade a plugin.
       
   758 	 *
       
   759 	 * @since 2.8.0
       
   760 	 * @since 3.7.0 The `$args` parameter was added, making clearing the plugin update cache optional.
       
   761 	 *
       
   762 	 * @param string $plugin The basename path to the main plugin file.
       
   763 	 * @param array  $args {
       
   764 	 *     Optional. Other arguments for upgrading a plugin package. Defualt empty array.
       
   765 	 *
       
   766 	 *     @type bool $clear_update_cache Whether to clear the plugin updates cache if successful.
       
   767 	 *                                    Default true.
       
   768 	 * }
       
   769 	 * @return bool|WP_Error True if the upgrade was successful, false or a {@see WP_Error} object otherwise.
       
   770 	 */
       
   771 	public function upgrade( $plugin, $args = array() ) {
   469 
   772 
   470 		$defaults = array(
   773 		$defaults = array(
   471 			'clear_update_cache' => true,
   774 			'clear_update_cache' => true,
   472 		);
   775 		);
   473 		$parsed_args = wp_parse_args( $args, $defaults );
   776 		$parsed_args = wp_parse_args( $args, $defaults );
   514 		wp_clean_plugins_cache( $parsed_args['clear_update_cache'] );
   817 		wp_clean_plugins_cache( $parsed_args['clear_update_cache'] );
   515 
   818 
   516 		return true;
   819 		return true;
   517 	}
   820 	}
   518 
   821 
   519 	function bulk_upgrade( $plugins, $args = array() ) {
   822 	/**
       
   823 	 * Bulk upgrade several plugins at once.
       
   824 	 *
       
   825 	 * @since 2.8.0
       
   826 	 * @since 3.7.0 The `$args` parameter was added, making clearing the plugin update cache optional.
       
   827 	 *
       
   828 	 * @param array $plugins Array of the basename paths of the plugins' main files.
       
   829 	 * @param array $args {
       
   830 	 *     Optional. Other arguments for upgrading several plugins at once. Default empty array.
       
   831 	 *
       
   832 	 *     @type bool $clear_update_cache Whether to clear the plugin updates cache if successful.
       
   833 	 *                                    Default true.
       
   834 	 * }
       
   835 	 *
       
   836 	 * @return array|false An array of results indexed by plugin file, or false if unable to connect to the filesystem.
       
   837 	 */
       
   838 	public function bulk_upgrade( $plugins, $args = array() ) {
   520 
   839 
   521 		$defaults = array(
   840 		$defaults = array(
   522 			'clear_update_cache' => true,
   841 			'clear_update_cache' => true,
   523 		);
   842 		);
   524 		$parsed_args = wp_parse_args( $args, $defaults );
   843 		$parsed_args = wp_parse_args( $args, $defaults );
   559 		foreach ( $plugins as $plugin ) {
   878 		foreach ( $plugins as $plugin ) {
   560 			$this->update_current++;
   879 			$this->update_current++;
   561 			$this->skin->plugin_info = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin, false, true);
   880 			$this->skin->plugin_info = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin, false, true);
   562 
   881 
   563 			if ( !isset( $current->response[ $plugin ] ) ) {
   882 			if ( !isset( $current->response[ $plugin ] ) ) {
   564 				$this->skin->set_result(true);
   883 				$this->skin->set_result('up_to_date');
   565 				$this->skin->before();
   884 				$this->skin->before();
   566 				$this->skin->feedback('up_to_date');
   885 				$this->skin->feedback('up_to_date');
   567 				$this->skin->after();
   886 				$this->skin->after();
   568 				$results[$plugin] = true;
   887 				$results[$plugin] = true;
   569 				continue;
   888 				continue;
   592 				break;
   911 				break;
   593 		} //end foreach $plugins
   912 		} //end foreach $plugins
   594 
   913 
   595 		$this->maintenance_mode(false);
   914 		$this->maintenance_mode(false);
   596 
   915 
       
   916 		/**
       
   917 		 * Fires when the bulk upgrader process is complete.
       
   918 		 *
       
   919 		 * @since 3.6.0
       
   920 		 *
       
   921 		 * @param Plugin_Upgrader $this Plugin_Upgrader instance. In other contexts, $this, might
       
   922 		 *                              be a Theme_Upgrader or Core_Upgrade instance.
       
   923 		 * @param array           $data {
       
   924 		 *     Array of bulk item update data.
       
   925 		 *
       
   926 		 *     @type string $action   Type of action. Default 'update'.
       
   927 		 *     @type string $type     Type of update process. Accepts 'plugin', 'theme', or 'core'.
       
   928 		 *     @type bool   $bulk     Whether the update process is a bulk update. Default true.
       
   929 		 *     @type array  $packages Array of plugin, theme, or core packages to update.
       
   930 		 * }
       
   931 		 */
   597 		do_action( 'upgrader_process_complete', $this, array(
   932 		do_action( 'upgrader_process_complete', $this, array(
   598 			'action' => 'update',
   933 			'action' => 'update',
   599 			'type' => 'plugin',
   934 			'type' => 'plugin',
   600 			'bulk' => true,
   935 			'bulk' => true,
   601 			'plugins' => $plugins,
   936 			'plugins' => $plugins,
   612 		wp_clean_plugins_cache( $parsed_args['clear_update_cache'] );
   947 		wp_clean_plugins_cache( $parsed_args['clear_update_cache'] );
   613 
   948 
   614 		return $results;
   949 		return $results;
   615 	}
   950 	}
   616 
   951 
   617 	function check_package($source) {
   952 	/**
       
   953 	 * Check a source package to be sure it contains a plugin.
       
   954 	 *
       
   955 	 * This function is added to the {@see 'upgrader_source_selection'} filter by
       
   956 	 * {@see Plugin_Upgrader::install()}.
       
   957 	 *
       
   958 	 * @since 3.3.0
       
   959 	 *
       
   960 	 * @param string $source The path to the downloaded package source.
       
   961 	 * @return string|WP_Error The source as passed, or a {@see WP_Error} object if no plugins were found.
       
   962 	 */
       
   963 	public function check_package($source) {
   618 		global $wp_filesystem;
   964 		global $wp_filesystem;
   619 
   965 
   620 		if ( is_wp_error($source) )
   966 		if ( is_wp_error($source) )
   621 			return $source;
   967 			return $source;
   622 
   968 
   623 		$working_directory = str_replace( $wp_filesystem->wp_content_dir(), trailingslashit(WP_CONTENT_DIR), $source);
   969 		$working_directory = str_replace( $wp_filesystem->wp_content_dir(), trailingslashit(WP_CONTENT_DIR), $source);
   624 		if ( ! is_dir($working_directory) ) // Sanity check, if the above fails, lets not prevent installation.
   970 		if ( ! is_dir($working_directory) ) // Sanity check, if the above fails, let's not prevent installation.
   625 			return $source;
   971 			return $source;
   626 
   972 
   627 		// Check the folder contains at least 1 valid plugin.
   973 		// Check the folder contains at least 1 valid plugin.
   628 		$plugins_found = false;
   974 		$plugins_found = false;
   629 		foreach ( glob( $working_directory . '*.php' ) as $file ) {
   975 		foreach ( glob( $working_directory . '*.php' ) as $file ) {
   638 			return new WP_Error( 'incompatible_archive_no_plugins', $this->strings['incompatible_archive'], __( 'No valid plugins were found.' ) );
   984 			return new WP_Error( 'incompatible_archive_no_plugins', $this->strings['incompatible_archive'], __( 'No valid plugins were found.' ) );
   639 
   985 
   640 		return $source;
   986 		return $source;
   641 	}
   987 	}
   642 
   988 
   643 	//return plugin info.
   989 	/**
   644 	function plugin_info() {
   990 	 * Retrieve the path to the file that contains the plugin info.
       
   991 	 *
       
   992 	 * This isn't used internally in the class, but is called by the skins.
       
   993 	 *
       
   994 	 * @since 2.8.0
       
   995 	 *
       
   996 	 * @return string|false The full path to the main plugin file, or false.
       
   997 	 */
       
   998 	public function plugin_info() {
   645 		if ( ! is_array($this->result) )
   999 		if ( ! is_array($this->result) )
   646 			return false;
  1000 			return false;
   647 		if ( empty($this->result['destination_name']) )
  1001 		if ( empty($this->result['destination_name']) )
   648 			return false;
  1002 			return false;
   649 
  1003 
   654 		$pluginfiles = array_keys($plugin); //Assume the requested plugin is the first in the list
  1008 		$pluginfiles = array_keys($plugin); //Assume the requested plugin is the first in the list
   655 
  1009 
   656 		return $this->result['destination_name'] . '/' . $pluginfiles[0];
  1010 		return $this->result['destination_name'] . '/' . $pluginfiles[0];
   657 	}
  1011 	}
   658 
  1012 
   659 	//Hooked to pre_install
  1013 	/**
   660 	function deactivate_plugin_before_upgrade($return, $plugin) {
  1014 	 * Deactivates a plugin before it is upgraded.
       
  1015 	 *
       
  1016 	 * Hooked to the {@see 'upgrader_pre_install'} filter by {@see Plugin_Upgrader::upgrade()}.
       
  1017 	 *
       
  1018 	 * @since 2.8.0
       
  1019 	 * @since 4.1.0 Added a return value.
       
  1020 	 *
       
  1021 	 * @param bool|WP_Error  $return Upgrade offer return.
       
  1022 	 * @param array          $plugin Plugin package arguments.
       
  1023 	 * @return bool|WP_Error The passed in $return param or {@see WP_Error}.
       
  1024 	 */
       
  1025 	public function deactivate_plugin_before_upgrade($return, $plugin) {
   661 
  1026 
   662 		if ( is_wp_error($return) ) //Bypass.
  1027 		if ( is_wp_error($return) ) //Bypass.
   663 			return $return;
  1028 			return $return;
   664 
  1029 
   665 		// When in cron (background updates) don't deactivate the plugin, as we require a browser to reactivate it
  1030 		// When in cron (background updates) don't deactivate the plugin, as we require a browser to reactivate it
   672 
  1037 
   673 		if ( is_plugin_active($plugin) ) {
  1038 		if ( is_plugin_active($plugin) ) {
   674 			//Deactivate the plugin silently, Prevent deactivation hooks from running.
  1039 			//Deactivate the plugin silently, Prevent deactivation hooks from running.
   675 			deactivate_plugins($plugin, true);
  1040 			deactivate_plugins($plugin, true);
   676 		}
  1041 		}
   677 	}
  1042 
   678 
  1043 		return $return;
   679 	//Hooked to upgrade_clear_destination
  1044 	}
   680 	function delete_old_plugin($removed, $local_destination, $remote_destination, $plugin) {
  1045 
       
  1046 	/**
       
  1047 	 * Delete the old plugin during an upgrade.
       
  1048 	 *
       
  1049 	 * Hooked to the {@see 'upgrader_clear_destination'} filter by
       
  1050 	 * {@see Plugin_Upgrader::upgrade()} and {@see Plugin_Upgrader::bulk_upgrade()}.
       
  1051 	 *
       
  1052 	 * @since 2.8.0
       
  1053 	 */
       
  1054 	public function delete_old_plugin($removed, $local_destination, $remote_destination, $plugin) {
   681 		global $wp_filesystem;
  1055 		global $wp_filesystem;
   682 
  1056 
   683 		if ( is_wp_error($removed) )
  1057 		if ( is_wp_error($removed) )
   684 			return $removed; //Pass errors through.
  1058 			return $removed; //Pass errors through.
   685 
  1059 
   713  * @subpackage Upgrader
  1087  * @subpackage Upgrader
   714  * @since 2.8.0
  1088  * @since 2.8.0
   715  */
  1089  */
   716 class Theme_Upgrader extends WP_Upgrader {
  1090 class Theme_Upgrader extends WP_Upgrader {
   717 
  1091 
   718 	var $result;
  1092 	/**
   719 	var $bulk = false;
  1093 	 * Result of the theme upgrade offer.
   720 
  1094 	 *
   721 	function upgrade_strings() {
  1095 	 * @since 2.8.0
       
  1096 	 * @var array|WP_Erorr $result
       
  1097 	 * @see WP_Upgrader::$result
       
  1098 	 */
       
  1099 	public $result;
       
  1100 
       
  1101 	/**
       
  1102 	 * Whether multiple plugins are being upgraded/installed in bulk.
       
  1103 	 *
       
  1104 	 * @since 2.9.0
       
  1105 	 * @var bool $bulk
       
  1106 	 */
       
  1107 	public $bulk = false;
       
  1108 
       
  1109 	/**
       
  1110 	 * Initialize the upgrade strings.
       
  1111 	 *
       
  1112 	 * @since 2.8.0
       
  1113 	 */
       
  1114 	public function upgrade_strings() {
   722 		$this->strings['up_to_date'] = __('The theme is at the latest version.');
  1115 		$this->strings['up_to_date'] = __('The theme is at the latest version.');
   723 		$this->strings['no_package'] = __('Update package not available.');
  1116 		$this->strings['no_package'] = __('Update package not available.');
   724 		$this->strings['downloading_package'] = __('Downloading update from <span class="code">%s</span>&#8230;');
  1117 		$this->strings['downloading_package'] = __('Downloading update from <span class="code">%s</span>&#8230;');
   725 		$this->strings['unpack_package'] = __('Unpacking the update&#8230;');
  1118 		$this->strings['unpack_package'] = __('Unpacking the update&#8230;');
   726 		$this->strings['remove_old'] = __('Removing the old version of the theme&#8230;');
  1119 		$this->strings['remove_old'] = __('Removing the old version of the theme&#8230;');
   727 		$this->strings['remove_old_failed'] = __('Could not remove the old theme.');
  1120 		$this->strings['remove_old_failed'] = __('Could not remove the old theme.');
   728 		$this->strings['process_failed'] = __('Theme update failed.');
  1121 		$this->strings['process_failed'] = __('Theme update failed.');
   729 		$this->strings['process_success'] = __('Theme updated successfully.');
  1122 		$this->strings['process_success'] = __('Theme updated successfully.');
   730 	}
  1123 	}
   731 
  1124 
   732 	function install_strings() {
  1125 	/**
       
  1126 	 * Initialize the install strings.
       
  1127 	 *
       
  1128 	 * @since 2.8.0
       
  1129 	 */
       
  1130 	public function install_strings() {
   733 		$this->strings['no_package'] = __('Install package not available.');
  1131 		$this->strings['no_package'] = __('Install package not available.');
   734 		$this->strings['downloading_package'] = __('Downloading install package from <span class="code">%s</span>&#8230;');
  1132 		$this->strings['downloading_package'] = __('Downloading install package from <span class="code">%s</span>&#8230;');
   735 		$this->strings['unpack_package'] = __('Unpacking the package&#8230;');
  1133 		$this->strings['unpack_package'] = __('Unpacking the package&#8230;');
   736 		$this->strings['installing_package'] = __('Installing the theme&#8230;');
  1134 		$this->strings['installing_package'] = __('Installing the theme&#8230;');
   737 		$this->strings['no_files'] = __('The theme contains no files.');
  1135 		$this->strings['no_files'] = __('The theme contains no files.');
   747 		/* translators: 1: theme name, 2: version */
  1145 		/* translators: 1: theme name, 2: version */
   748 		$this->strings['parent_theme_install_success'] = __('Successfully installed the parent theme, <strong>%1$s %2$s</strong>.');
  1146 		$this->strings['parent_theme_install_success'] = __('Successfully installed the parent theme, <strong>%1$s %2$s</strong>.');
   749 		$this->strings['parent_theme_not_found'] = __('<strong>The parent theme could not be found.</strong> You will need to install the parent theme, <strong>%s</strong>, before you can use this child theme.');
  1147 		$this->strings['parent_theme_not_found'] = __('<strong>The parent theme could not be found.</strong> You will need to install the parent theme, <strong>%s</strong>, before you can use this child theme.');
   750 	}
  1148 	}
   751 
  1149 
   752 	function check_parent_theme_filter($install_result, $hook_extra, $child_result) {
  1150 	/**
       
  1151 	 * Check if a child theme is being installed and we need to install its parent.
       
  1152 	 *
       
  1153 	 * Hooked to the {@see 'upgrader_post_install'} filter by {@see Theme_Upgrader::install()}.
       
  1154 	 *
       
  1155 	 * @since 3.4.0
       
  1156 	 */
       
  1157 	public function check_parent_theme_filter( $install_result, $hook_extra, $child_result ) {
   753 		// Check to see if we need to install a parent theme
  1158 		// Check to see if we need to install a parent theme
   754 		$theme_info = $this->theme_info();
  1159 		$theme_info = $this->theme_info();
   755 
  1160 
   756 		if ( ! $theme_info->parent() )
  1161 		if ( ! $theme_info->parent() )
   757 			return $install_result;
  1162 			return $install_result;
   762 			$this->skin->feedback( 'parent_theme_currently_installed', $theme_info->parent()->display('Name'), $theme_info->parent()->display('Version') );
  1167 			$this->skin->feedback( 'parent_theme_currently_installed', $theme_info->parent()->display('Name'), $theme_info->parent()->display('Version') );
   763 			// We already have the theme, fall through.
  1168 			// We already have the theme, fall through.
   764 			return $install_result;
  1169 			return $install_result;
   765 		}
  1170 		}
   766 
  1171 
   767 		// We don't have the parent theme, lets install it
  1172 		// We don't have the parent theme, let's install it.
   768 		$api = themes_api('theme_information', array('slug' => $theme_info->get('Template'), 'fields' => array('sections' => false, 'tags' => false) ) ); //Save on a bit of bandwidth.
  1173 		$api = themes_api('theme_information', array('slug' => $theme_info->get('Template'), 'fields' => array('sections' => false, 'tags' => false) ) ); //Save on a bit of bandwidth.
   769 
  1174 
   770 		if ( ! $api || is_wp_error($api) ) {
  1175 		if ( ! $api || is_wp_error($api) ) {
   771 			$this->skin->feedback( 'parent_theme_not_found', $theme_info->get('Template') );
  1176 			$this->skin->feedback( 'parent_theme_not_found', $theme_info->get('Template') );
   772 			// Don't show activate or preview actions after install
  1177 			// Don't show activate or preview actions after install
   806 		$this->strings['process_success'] = $child_success_message;
  1211 		$this->strings['process_success'] = $child_success_message;
   807 
  1212 
   808 		return $install_result;
  1213 		return $install_result;
   809 	}
  1214 	}
   810 
  1215 
   811 	function hide_activate_preview_actions($actions) {
  1216 	/**
       
  1217 	 * Don't display the activate and preview actions to the user.
       
  1218 	 *
       
  1219 	 * Hooked to the {@see 'install_theme_complete_actions'} filter by
       
  1220 	 * {@see Theme_Upgrader::check_parent_theme_filter()} when installing
       
  1221 	 * a child theme and installing the parent theme fails.
       
  1222 	 *
       
  1223 	 * @since 3.4.0
       
  1224 	 *
       
  1225 	 * @param array $actions Preview actions.
       
  1226 	 */
       
  1227 	public function hide_activate_preview_actions( $actions ) {
   812 		unset($actions['activate'], $actions['preview']);
  1228 		unset($actions['activate'], $actions['preview']);
   813 		return $actions;
  1229 		return $actions;
   814 	}
  1230 	}
   815 
  1231 
   816 	function install( $package, $args = array() ) {
  1232 	/**
       
  1233 	 * Install a theme package.
       
  1234 	 *
       
  1235 	 * @since 2.8.0
       
  1236 	 * @since 3.7.0 The `$args` parameter was added, making clearing the update cache optional.
       
  1237 	 *
       
  1238 	 * @param string $package The full local path or URI of the package.
       
  1239 	 * @param array  $args {
       
  1240 	 *     Optional. Other arguments for installing a theme package. Default empty array.
       
  1241 	 *
       
  1242 	 *     @type bool $clear_update_cache Whether to clear the updates cache if successful.
       
  1243 	 *                                    Default true.
       
  1244 	 * }
       
  1245 	 *
       
  1246 	 * @return bool|WP_Error True if the install was successful, false or a {@see WP_Error} object otherwise.
       
  1247 	 */
       
  1248 	public function install( $package, $args = array() ) {
   817 
  1249 
   818 		$defaults = array(
  1250 		$defaults = array(
   819 			'clear_update_cache' => true,
  1251 			'clear_update_cache' => true,
   820 		);
  1252 		);
   821 		$parsed_args = wp_parse_args( $args, $defaults );
  1253 		$parsed_args = wp_parse_args( $args, $defaults );
   847 		wp_clean_themes_cache( $parsed_args['clear_update_cache'] );
  1279 		wp_clean_themes_cache( $parsed_args['clear_update_cache'] );
   848 
  1280 
   849 		return true;
  1281 		return true;
   850 	}
  1282 	}
   851 
  1283 
   852 	function upgrade( $theme, $args = array() ) {
  1284 	/**
       
  1285 	 * Upgrade a theme.
       
  1286 	 *
       
  1287 	 * @since 2.8.0
       
  1288 	 * @since 3.7.0 The `$args` parameter was added, making clearing the update cache optional.
       
  1289 	 *
       
  1290 	 * @param string $theme The theme slug.
       
  1291 	 * @param array  $args {
       
  1292 	 *     Optional. Other arguments for upgrading a theme. Default empty array.
       
  1293 	 *
       
  1294 	 *     @type bool $clear_update_cache Whether to clear the update cache if successful.
       
  1295 	 *                                    Default true.
       
  1296 	 * }
       
  1297 	 * @return bool|WP_Error True if the upgrade was successful, false or a {@see WP_Error} object otherwise.
       
  1298 	 */
       
  1299 	public function upgrade( $theme, $args = array() ) {
   853 
  1300 
   854 		$defaults = array(
  1301 		$defaults = array(
   855 			'clear_update_cache' => true,
  1302 			'clear_update_cache' => true,
   856 		);
  1303 		);
   857 		$parsed_args = wp_parse_args( $args, $defaults );
  1304 		$parsed_args = wp_parse_args( $args, $defaults );
   862 		// Is an update available?
  1309 		// Is an update available?
   863 		$current = get_site_transient( 'update_themes' );
  1310 		$current = get_site_transient( 'update_themes' );
   864 		if ( !isset( $current->response[ $theme ] ) ) {
  1311 		if ( !isset( $current->response[ $theme ] ) ) {
   865 			$this->skin->before();
  1312 			$this->skin->before();
   866 			$this->skin->set_result(false);
  1313 			$this->skin->set_result(false);
   867 			$this->skin->error('up_to_date');
  1314 			$this->skin->error( 'up_to_date' );
   868 			$this->skin->after();
  1315 			$this->skin->after();
   869 			return false;
  1316 			return false;
   870 		}
  1317 		}
   871 
  1318 
   872 		$r = $current->response[ $theme ];
  1319 		$r = $current->response[ $theme ];
   897 		wp_clean_themes_cache( $parsed_args['clear_update_cache'] );
  1344 		wp_clean_themes_cache( $parsed_args['clear_update_cache'] );
   898 
  1345 
   899 		return true;
  1346 		return true;
   900 	}
  1347 	}
   901 
  1348 
   902 	function bulk_upgrade( $themes, $args = array() ) {
  1349 	/**
       
  1350 	 * Upgrade several themes at once.
       
  1351 	 *
       
  1352 	 * @since 3.0.0
       
  1353 	 * @since 3.7.0 The `$args` parameter was added, making clearing the update cache optional.
       
  1354 	 *
       
  1355 	 * @param array $themes The theme slugs.
       
  1356 	 * @param array $args {
       
  1357 	 *     Optional. Other arguments for upgrading several themes at once. Default empty array.
       
  1358 	 *
       
  1359 	 *     @type bool $clear_update_cache Whether to clear the update cache if successful.
       
  1360 	 *                                    Default true.
       
  1361 	 * }
       
  1362 	 * @return array[]|false An array of results, or false if unable to connect to the filesystem.
       
  1363 	 */
       
  1364 	public function bulk_upgrade( $themes, $args = array() ) {
   903 
  1365 
   904 		$defaults = array(
  1366 		$defaults = array(
   905 			'clear_update_cache' => true,
  1367 			'clear_update_cache' => true,
   906 		);
  1368 		);
   907 		$parsed_args = wp_parse_args( $args, $defaults );
  1369 		$parsed_args = wp_parse_args( $args, $defaults );
   947 			$this->skin->theme_info = $this->theme_info($theme);
  1409 			$this->skin->theme_info = $this->theme_info($theme);
   948 
  1410 
   949 			if ( !isset( $current->response[ $theme ] ) ) {
  1411 			if ( !isset( $current->response[ $theme ] ) ) {
   950 				$this->skin->set_result(true);
  1412 				$this->skin->set_result(true);
   951 				$this->skin->before();
  1413 				$this->skin->before();
   952 				$this->skin->feedback('up_to_date');
  1414 				$this->skin->feedback( 'up_to_date' );
   953 				$this->skin->after();
  1415 				$this->skin->after();
   954 				$results[$theme] = true;
  1416 				$results[$theme] = true;
   955 				continue;
  1417 				continue;
   956 			}
  1418 			}
   957 
  1419 
   961 			$result = $this->run( array(
  1423 			$result = $this->run( array(
   962 				'package' => $r['package'],
  1424 				'package' => $r['package'],
   963 				'destination' => get_theme_root( $theme ),
  1425 				'destination' => get_theme_root( $theme ),
   964 				'clear_destination' => true,
  1426 				'clear_destination' => true,
   965 				'clear_working' => true,
  1427 				'clear_working' => true,
       
  1428 				'is_multi' => true,
   966 				'hook_extra' => array(
  1429 				'hook_extra' => array(
   967 					'theme' => $theme
  1430 					'theme' => $theme
   968 				),
  1431 				),
   969 			) );
  1432 			) );
   970 
  1433 
   975 				break;
  1438 				break;
   976 		} //end foreach $plugins
  1439 		} //end foreach $plugins
   977 
  1440 
   978 		$this->maintenance_mode(false);
  1441 		$this->maintenance_mode(false);
   979 
  1442 
       
  1443 		/** This action is documented in wp-admin/includes/class-wp-upgrader.php */
   980 		do_action( 'upgrader_process_complete', $this, array(
  1444 		do_action( 'upgrader_process_complete', $this, array(
   981 			'action' => 'update',
  1445 			'action' => 'update',
   982 			'type' => 'plugin',
  1446 			'type' => 'theme',
   983 			'bulk' => true,
  1447 			'bulk' => true,
   984 			'themes' => $themes,
  1448 			'themes' => $themes,
   985 		) );
  1449 		) );
   986 
  1450 
   987 		$this->skin->bulk_footer();
  1451 		$this->skin->bulk_footer();
   997 		wp_clean_themes_cache( $parsed_args['clear_update_cache'] );
  1461 		wp_clean_themes_cache( $parsed_args['clear_update_cache'] );
   998 
  1462 
   999 		return $results;
  1463 		return $results;
  1000 	}
  1464 	}
  1001 
  1465 
  1002 	function check_package($source) {
  1466 	/**
       
  1467 	 * Check that the package source contains a valid theme.
       
  1468 	 *
       
  1469 	 * Hooked to the {@see 'upgrader_source_selection'} filter by {@see Theme_Upgrader::install()}.
       
  1470 	 * It will return an error if the theme doesn't have style.css or index.php
       
  1471 	 * files.
       
  1472 	 *
       
  1473 	 * @since 3.3.0
       
  1474 	 *
       
  1475 	 * @param string $source The full path to the package source.
       
  1476 	 * @return string|WP_Error The source or a WP_Error.
       
  1477 	 */
       
  1478 	public function check_package( $source ) {
  1003 		global $wp_filesystem;
  1479 		global $wp_filesystem;
  1004 
  1480 
  1005 		if ( is_wp_error($source) )
  1481 		if ( is_wp_error($source) )
  1006 			return $source;
  1482 			return $source;
  1007 
  1483 
  1008 		// Check the folder contains a valid theme
  1484 		// Check the folder contains a valid theme
  1009 		$working_directory = str_replace( $wp_filesystem->wp_content_dir(), trailingslashit(WP_CONTENT_DIR), $source);
  1485 		$working_directory = str_replace( $wp_filesystem->wp_content_dir(), trailingslashit(WP_CONTENT_DIR), $source);
  1010 		if ( ! is_dir($working_directory) ) // Sanity check, if the above fails, lets not prevent installation.
  1486 		if ( ! is_dir($working_directory) ) // Sanity check, if the above fails, let's not prevent installation.
  1011 			return $source;
  1487 			return $source;
  1012 
  1488 
  1013 		// A proper archive should have a style.css file in the single subdirectory
  1489 		// A proper archive should have a style.css file in the single subdirectory
  1014 		if ( ! file_exists( $working_directory . 'style.css' ) )
  1490 		if ( ! file_exists( $working_directory . 'style.css' ) )
  1015 			return new WP_Error( 'incompatible_archive_theme_no_style', $this->strings['incompatible_archive'], __( 'The theme is missing the <code>style.css</code> stylesheet.' ) );
  1491 			return new WP_Error( 'incompatible_archive_theme_no_style', $this->strings['incompatible_archive'], __( 'The theme is missing the <code>style.css</code> stylesheet.' ) );
  1024 			return new WP_Error( 'incompatible_archive_theme_no_index', $this->strings['incompatible_archive'], __( 'The theme is missing the <code>index.php</code> file.' ) );
  1500 			return new WP_Error( 'incompatible_archive_theme_no_index', $this->strings['incompatible_archive'], __( 'The theme is missing the <code>index.php</code> file.' ) );
  1025 
  1501 
  1026 		return $source;
  1502 		return $source;
  1027 	}
  1503 	}
  1028 
  1504 
  1029 	function current_before($return, $theme) {
  1505 	/**
       
  1506 	 * Turn on maintenance mode before attempting to upgrade the current theme.
       
  1507 	 *
       
  1508 	 * Hooked to the {@see 'upgrader_pre_install'} filter by {@see Theme_Upgrader::upgrade()} and
       
  1509 	 * {@see Theme_Upgrader::bulk_upgrade()}.
       
  1510 	 *
       
  1511 	 * @since 2.8.0
       
  1512 	 */
       
  1513 	public function current_before($return, $theme) {
  1030 
  1514 
  1031 		if ( is_wp_error($return) )
  1515 		if ( is_wp_error($return) )
  1032 			return $return;
  1516 			return $return;
  1033 
  1517 
  1034 		$theme = isset($theme['theme']) ? $theme['theme'] : '';
  1518 		$theme = isset($theme['theme']) ? $theme['theme'] : '';
  1040 			$this->maintenance_mode(true);
  1524 			$this->maintenance_mode(true);
  1041 
  1525 
  1042 		return $return;
  1526 		return $return;
  1043 	}
  1527 	}
  1044 
  1528 
  1045 	function current_after($return, $theme) {
  1529 	/**
       
  1530 	 * Turn off maintenance mode after upgrading the current theme.
       
  1531 	 *
       
  1532 	 * Hooked to the {@see 'upgrader_post_install'} filter by {@see Theme_Upgrader::upgrade()}
       
  1533 	 * and {@see Theme_Upgrader::bulk_upgrade()}.
       
  1534 	 *
       
  1535 	 * @since 2.8.0
       
  1536 	 */
       
  1537 	public function current_after($return, $theme) {
  1046 		if ( is_wp_error($return) )
  1538 		if ( is_wp_error($return) )
  1047 			return $return;
  1539 			return $return;
  1048 
  1540 
  1049 		$theme = isset($theme['theme']) ? $theme['theme'] : '';
  1541 		$theme = isset($theme['theme']) ? $theme['theme'] : '';
  1050 
  1542 
  1062 		if ( ! $this->bulk )
  1554 		if ( ! $this->bulk )
  1063 			$this->maintenance_mode(false);
  1555 			$this->maintenance_mode(false);
  1064 		return $return;
  1556 		return $return;
  1065 	}
  1557 	}
  1066 
  1558 
  1067 	function delete_old_theme( $removed, $local_destination, $remote_destination, $theme ) {
  1559 	/**
       
  1560 	 * Delete the old theme during an upgrade.
       
  1561 	 *
       
  1562 	 * Hooked to the {@see 'upgrader_clear_destination'} filter by {@see Theme_Upgrader::upgrade()}
       
  1563 	 * and {@see Theme_Upgrader::bulk_upgrade()}.
       
  1564 	 *
       
  1565 	 * @since 2.8.0
       
  1566 	 */
       
  1567 	public function delete_old_theme( $removed, $local_destination, $remote_destination, $theme ) {
  1068 		global $wp_filesystem;
  1568 		global $wp_filesystem;
  1069 
  1569 
  1070 		if ( is_wp_error( $removed ) )
  1570 		if ( is_wp_error( $removed ) )
  1071 			return $removed; // Pass errors through.
  1571 			return $removed; // Pass errors through.
  1072 
  1572 
  1081 		}
  1581 		}
  1082 
  1582 
  1083 		return true;
  1583 		return true;
  1084 	}
  1584 	}
  1085 
  1585 
  1086 	function theme_info($theme = null) {
  1586 	/**
       
  1587 	 * Get the WP_Theme object for a theme.
       
  1588 	 *
       
  1589 	 * @since 2.8.0
       
  1590 	 * @since 3.0.0 The `$theme` argument was added.
       
  1591 	 *
       
  1592 	 * @param string $theme The directory name of the theme. This is optional, and if not supplied,
       
  1593 	 *                      the directory name from the last result will be used.
       
  1594 	 * @return WP_Theme|false The theme's info object, or false `$theme` is not supplied
       
  1595 	 *                        and the last result isn't set.
       
  1596 	 */
       
  1597 	public function theme_info($theme = null) {
  1087 
  1598 
  1088 		if ( empty($theme) ) {
  1599 		if ( empty($theme) ) {
  1089 			if ( !empty($this->result['destination_name']) )
  1600 			if ( !empty($this->result['destination_name']) )
  1090 				$theme = $this->result['destination_name'];
  1601 				$theme = $this->result['destination_name'];
  1091 			else
  1602 			else
  1105  * @subpackage Upgrader
  1616  * @subpackage Upgrader
  1106  * @since 3.7.0
  1617  * @since 3.7.0
  1107  */
  1618  */
  1108 class Language_Pack_Upgrader extends WP_Upgrader {
  1619 class Language_Pack_Upgrader extends WP_Upgrader {
  1109 
  1620 
  1110 	var $result;
  1621 	/**
  1111 	var $bulk = true;
  1622 	 * Result of the language pack upgrade.
  1112 
  1623 	 *
  1113 	static function async_upgrade( $upgrader = false ) {
  1624 	 * @since 3.7.0
       
  1625 	 * @var array|WP_Error $result
       
  1626 	 * @see WP_Upgrader::$result
       
  1627 	 */
       
  1628 	public $result;
       
  1629 
       
  1630 	/**
       
  1631 	 * Whether a bulk upgrade/install is being performed.
       
  1632 	 *
       
  1633 	 * @since 3.7.0
       
  1634 	 * @var bool $bulk
       
  1635 	 */
       
  1636 	public $bulk = true;
       
  1637 
       
  1638 	/**
       
  1639 	 * Asynchronously upgrade language packs after other upgrades have been made.
       
  1640 	 *
       
  1641 	 * Hooked to the {@see 'upgrader_process_complete'} action by default.
       
  1642 	 *
       
  1643 	 * @since 3.7.0
       
  1644 	 */
       
  1645 	public static function async_upgrade( $upgrader = false ) {
  1114 		// Avoid recursion.
  1646 		// Avoid recursion.
  1115 		if ( $upgrader && $upgrader instanceof Language_Pack_Upgrader )
  1647 		if ( $upgrader && $upgrader instanceof Language_Pack_Upgrader ) {
  1116 			return;
  1648 			return;
       
  1649 		}
  1117 
  1650 
  1118 		// Nothing to do?
  1651 		// Nothing to do?
  1119 		$language_updates = wp_get_translation_updates();
  1652 		$language_updates = wp_get_translation_updates();
  1120 		if ( ! $language_updates )
  1653 		if ( ! $language_updates ) {
  1121 			return;
  1654 			return;
       
  1655 		}
       
  1656 
       
  1657 		// Avoid messing with VCS installs, at least for now.
       
  1658 		// Noted: this is not the ideal way to accomplish this.
       
  1659 		$check_vcs = new WP_Automatic_Updater;
       
  1660 		if ( $check_vcs->is_vcs_checkout( WP_CONTENT_DIR ) ) {
       
  1661 			return;
       
  1662 		}
       
  1663 
       
  1664 		foreach ( $language_updates as $key => $language_update ) {
       
  1665 			$update = ! empty( $language_update->autoupdate );
       
  1666 
       
  1667 			/**
       
  1668 			 * Filter whether to asynchronously update translation for core, a plugin, or a theme.
       
  1669 			 *
       
  1670 			 * @since 4.0.0
       
  1671 			 *
       
  1672 			 * @param bool   $update          Whether to update.
       
  1673 			 * @param object $language_update The update offer.
       
  1674 			 */
       
  1675 			$update = apply_filters( 'async_update_translation', $update, $language_update );
       
  1676 
       
  1677 			if ( ! $update ) {
       
  1678 				unset( $language_updates[ $key ] );
       
  1679 			}
       
  1680 		}
       
  1681 
       
  1682 		if ( empty( $language_updates ) ) {
       
  1683 			return;
       
  1684 		}
  1122 
  1685 
  1123 		$skin = new Language_Pack_Upgrader_Skin( array(
  1686 		$skin = new Language_Pack_Upgrader_Skin( array(
  1124 			'skip_header_footer' => true,
  1687 			'skip_header_footer' => true,
  1125 		) );
  1688 		) );
  1126 
  1689 
  1127 		$lp_upgrader = new Language_Pack_Upgrader( $skin );
  1690 		$lp_upgrader = new Language_Pack_Upgrader( $skin );
  1128 		$lp_upgrader->upgrade();
  1691 		$lp_upgrader->bulk_upgrade( $language_updates );
  1129 	}
  1692 	}
  1130 
  1693 
  1131 	function upgrade_strings() {
  1694 	/**
       
  1695 	 * Initialize the upgrade strings.
       
  1696 	 *
       
  1697 	 * @since 3.7.0
       
  1698 	 */
       
  1699 	public function upgrade_strings() {
  1132 		$this->strings['starting_upgrade'] = __( 'Some of your translations need updating. Sit tight for a few more seconds while we update them as well.' );
  1700 		$this->strings['starting_upgrade'] = __( 'Some of your translations need updating. Sit tight for a few more seconds while we update them as well.' );
  1133 		$this->strings['up_to_date'] = __( 'The translation is up to date.' ); // We need to silently skip this case
  1701 		$this->strings['up_to_date'] = __( 'The translation is up to date.' ); // We need to silently skip this case
  1134 		$this->strings['no_package'] = __( 'Update package not available.' );
  1702 		$this->strings['no_package'] = __( 'Update package not available.' );
  1135 		$this->strings['downloading_package'] = __( 'Downloading translation from <span class="code">%s</span>&#8230;' );
  1703 		$this->strings['downloading_package'] = __( 'Downloading translation from <span class="code">%s</span>&#8230;' );
  1136 		$this->strings['unpack_package'] = __( 'Unpacking the update&#8230;' );
  1704 		$this->strings['unpack_package'] = __( 'Unpacking the update&#8230;' );
  1137 		$this->strings['process_failed'] = __( 'Translation update failed.' );
  1705 		$this->strings['process_failed'] = __( 'Translation update failed.' );
  1138 		$this->strings['process_success'] = __( 'Translation updated successfully.' );
  1706 		$this->strings['process_success'] = __( 'Translation updated successfully.' );
  1139 	}
  1707 	}
  1140 
  1708 
  1141 	function upgrade( $update = false, $args = array() ) {
  1709 	/**
  1142 		if ( $update )
  1710 	 * Upgrade a language pack.
       
  1711 	 *
       
  1712 	 * @since 3.7.0
       
  1713 	 *
       
  1714 	 * @param string|false $update Optional. Whether an update offer is available. Default false.
       
  1715 	 * @param array        $args   Optional. Other optional arguments, see
       
  1716 	 *                             {@see Language_Pack_Upgrader::bulk_upgrade()}. Default empty array.
       
  1717 	 * @return array|WP_Error The result of the upgrade, or a {@see wP_Error} object instead.
       
  1718 	 */
       
  1719 	public function upgrade( $update = false, $args = array() ) {
       
  1720 		if ( $update ) {
  1143 			$update = array( $update );
  1721 			$update = array( $update );
       
  1722 		}
       
  1723 
  1144 		$results = $this->bulk_upgrade( $update, $args );
  1724 		$results = $this->bulk_upgrade( $update, $args );
       
  1725 
       
  1726 		if ( ! is_array( $results ) ) {
       
  1727 			return $results;
       
  1728 		}
       
  1729 
  1145 		return $results[0];
  1730 		return $results[0];
  1146 	}
  1731 	}
  1147 
  1732 
  1148 	function bulk_upgrade( $language_updates = array(), $args = array() ) {
  1733 	/**
       
  1734 	 * Bulk upgrade language packs.
       
  1735 	 *
       
  1736 	 * @since 3.7.0
       
  1737 	 *
       
  1738 	 * @param array $language_updates Optional. Language pack updates. Default empty array.
       
  1739 	 * @param array $args {
       
  1740 	 *     Optional. Other arguments for upgrading multiple language packs. Default empty array
       
  1741 	 *
       
  1742 	 *     @type bool $clear_update_cache Whether to clear the update cache when done.
       
  1743 	 *                                    Default true.
       
  1744 	 * }
       
  1745 	 * @return array|true|false|WP_Error Will return an array of results, or true if there are no updates,
       
  1746 	 *                                   false or WP_Error for initial errors.
       
  1747 	 */
       
  1748 	public function bulk_upgrade( $language_updates = array(), $args = array() ) {
  1149 		global $wp_filesystem;
  1749 		global $wp_filesystem;
  1150 
  1750 
  1151 		$defaults = array(
  1751 		$defaults = array(
  1152 			'clear_update_cache' => true,
  1752 			'clear_update_cache' => true,
  1153 		);
  1753 		);
  1171 		}
  1771 		}
  1172 
  1772 
  1173 		if ( 'upgrader_process_complete' == current_filter() )
  1773 		if ( 'upgrader_process_complete' == current_filter() )
  1174 			$this->skin->feedback( 'starting_upgrade' );
  1774 			$this->skin->feedback( 'starting_upgrade' );
  1175 
  1775 
  1176 		add_filter( 'upgrader_source_selection', array( &$this, 'check_package' ), 10, 3 );
  1776 		// Remove any existing upgrade filters from the plugin/theme upgraders #WP29425 & #WP29230
       
  1777 		remove_all_filters( 'upgrader_pre_install' );
       
  1778 		remove_all_filters( 'upgrader_clear_destination' );
       
  1779 		remove_all_filterS( 'upgrader_post_install' );
       
  1780 		remove_all_filters( 'upgrader_source_selection' );
       
  1781 
       
  1782 		add_filter( 'upgrader_source_selection', array( $this, 'check_package' ), 10, 2 );
  1177 
  1783 
  1178 		$this->skin->header();
  1784 		$this->skin->header();
  1179 
  1785 
  1180 		// Connect to the Filesystem first.
  1786 		// Connect to the Filesystem first.
  1181 		$res = $this->fs_connect( array( WP_CONTENT_DIR, WP_LANG_DIR ) );
  1787 		$res = $this->fs_connect( array( WP_CONTENT_DIR, WP_LANG_DIR ) );
  1187 		$results = array();
  1793 		$results = array();
  1188 
  1794 
  1189 		$this->update_count = count( $language_updates );
  1795 		$this->update_count = count( $language_updates );
  1190 		$this->update_current = 0;
  1796 		$this->update_current = 0;
  1191 
  1797 
  1192 		// The filesystem's mkdir() is not recursive. Make sure WP_LANG_DIR exists,
  1798 		/*
  1193 		// as we then may need to create a /plugins or /themes directory inside of it.
  1799 		 * The filesystem's mkdir() is not recursive. Make sure WP_LANG_DIR exists,
       
  1800 		 * as we then may need to create a /plugins or /themes directory inside of it.
       
  1801 		 */
  1194 		$remote_destination = $wp_filesystem->find_folder( WP_LANG_DIR );
  1802 		$remote_destination = $wp_filesystem->find_folder( WP_LANG_DIR );
  1195 		if ( ! $wp_filesystem->exists( $remote_destination ) )
  1803 		if ( ! $wp_filesystem->exists( $remote_destination ) )
  1196 			if ( ! $wp_filesystem->mkdir( $remote_destination, FS_CHMOD_DIR ) )
  1804 			if ( ! $wp_filesystem->mkdir( $remote_destination, FS_CHMOD_DIR ) )
  1197 				return new WP_Error( 'mkdir_failed_lang_dir', $this->strings['mkdir_failed'], $remote_destination );
  1805 				return new WP_Error( 'mkdir_failed_lang_dir', $this->strings['mkdir_failed'], $remote_destination );
  1198 
  1806 
  1233 		$this->skin->bulk_footer();
  1841 		$this->skin->bulk_footer();
  1234 
  1842 
  1235 		$this->skin->footer();
  1843 		$this->skin->footer();
  1236 
  1844 
  1237 		// Clean up our hooks, in case something else does an upgrade on this connection.
  1845 		// Clean up our hooks, in case something else does an upgrade on this connection.
  1238 		remove_filter( 'upgrader_source_selection', array( &$this, 'check_package' ), 10, 2 );
  1846 		remove_filter( 'upgrader_source_selection', array( $this, 'check_package' ) );
  1239 
  1847 
  1240 		if ( $parsed_args['clear_update_cache'] ) {
  1848 		if ( $parsed_args['clear_update_cache'] ) {
  1241 			wp_clean_themes_cache( true );
  1849 			wp_clean_update_cache();
  1242 			wp_clean_plugins_cache( true );
       
  1243 			delete_site_transient( 'update_core' );
       
  1244 		}
  1850 		}
  1245 
  1851 
  1246 		return $results;
  1852 		return $results;
  1247 	}
  1853 	}
  1248 
  1854 
  1249 	function check_package( $source, $remote_source ) {
  1855 	/**
       
  1856 	 * Check the package source to make sure there are .mo and .po files.
       
  1857 	 *
       
  1858 	 * Hooked to the {@see 'upgrader_source_selection'} filter by
       
  1859 	 * {@see Language_Pack_Upgrader::bulk_upgrade()}.
       
  1860 	 *
       
  1861 	 * @since 3.7.0
       
  1862 	 */
       
  1863 	public function check_package( $source, $remote_source ) {
  1250 		global $wp_filesystem;
  1864 		global $wp_filesystem;
  1251 
  1865 
  1252 		if ( is_wp_error( $source ) )
  1866 		if ( is_wp_error( $source ) )
  1253 			return $source;
  1867 			return $source;
  1254 
  1868 
  1269 				__( 'The language pack is missing either the <code>.po</code> or <code>.mo</code> files.' ) );
  1883 				__( 'The language pack is missing either the <code>.po</code> or <code>.mo</code> files.' ) );
  1270 
  1884 
  1271 		return $source;
  1885 		return $source;
  1272 	}
  1886 	}
  1273 
  1887 
  1274 	function get_name_for_update( $update ) {
  1888 	/**
       
  1889 	 * Get the name of an item being updated.
       
  1890 	 *
       
  1891 	 * @since 3.7.0
       
  1892 	 *
       
  1893 	 * @param object The data for an update.
       
  1894 	 * @return string The name of the item being updated.
       
  1895 	 */
       
  1896 	public function get_name_for_update( $update ) {
  1275 		switch ( $update->type ) {
  1897 		switch ( $update->type ) {
  1276 			case 'core':
  1898 			case 'core':
  1277 				return 'WordPress'; // Not translated
  1899 				return 'WordPress'; // Not translated
  1278 				break;
  1900 
  1279 			case 'theme':
  1901 			case 'theme':
  1280 				$theme = wp_get_theme( $update->slug );
  1902 				$theme = wp_get_theme( $update->slug );
  1281 				if ( $theme->exists() )
  1903 				if ( $theme->exists() )
  1282 					return $theme->Get( 'Name' );
  1904 					return $theme->Get( 'Name' );
  1283 				break;
  1905 				break;
  1284 			case 'plugin':
  1906 			case 'plugin':
  1285 				$plugin_data = get_plugins( '/' . $update->slug );
  1907 				$plugin_data = get_plugins( '/' . $update->slug );
  1286 				$plugin_data = array_shift( $plugin_data );
  1908 				$plugin_data = reset( $plugin_data );
  1287 				if ( $plugin_data )
  1909 				if ( $plugin_data )
  1288 					return $plugin_data['Name'];
  1910 					return $plugin_data['Name'];
  1289 				break;
  1911 				break;
  1290 		}
  1912 		}
  1291 		return '';
  1913 		return '';
  1300  * @subpackage Upgrader
  1922  * @subpackage Upgrader
  1301  * @since 2.8.0
  1923  * @since 2.8.0
  1302  */
  1924  */
  1303 class Core_Upgrader extends WP_Upgrader {
  1925 class Core_Upgrader extends WP_Upgrader {
  1304 
  1926 
  1305 	function upgrade_strings() {
  1927 	/**
       
  1928 	 * Initialize the upgrade strings.
       
  1929 	 *
       
  1930 	 * @since 2.8.0
       
  1931 	 */
       
  1932 	public function upgrade_strings() {
  1306 		$this->strings['up_to_date'] = __('WordPress is at the latest version.');
  1933 		$this->strings['up_to_date'] = __('WordPress is at the latest version.');
  1307 		$this->strings['no_package'] = __('Update package not available.');
  1934 		$this->strings['no_package'] = __('Update package not available.');
  1308 		$this->strings['downloading_package'] = __('Downloading update from <span class="code">%s</span>&#8230;');
  1935 		$this->strings['downloading_package'] = __('Downloading update from <span class="code">%s</span>&#8230;');
  1309 		$this->strings['unpack_package'] = __('Unpacking the update&#8230;');
  1936 		$this->strings['unpack_package'] = __('Unpacking the update&#8230;');
  1310 		$this->strings['copy_failed'] = __('Could not copy files.');
  1937 		$this->strings['copy_failed'] = __('Could not copy files.');
  1311 		$this->strings['copy_failed_space'] = __('Could not copy files. You may have run out of disk space.' );
  1938 		$this->strings['copy_failed_space'] = __('Could not copy files. You may have run out of disk space.' );
  1312 		$this->strings['start_rollback'] = __( 'Attempting to roll back to previous version.' );
  1939 		$this->strings['start_rollback'] = __( 'Attempting to roll back to previous version.' );
  1313 		$this->strings['rollback_was_required'] = __( 'Due to an error during updating, WordPress has rolled back to your previous version.' );
  1940 		$this->strings['rollback_was_required'] = __( 'Due to an error during updating, WordPress has rolled back to your previous version.' );
  1314 	}
  1941 	}
  1315 
  1942 
  1316 	function upgrade( $current, $args = array() ) {
  1943 	/**
  1317 		global $wp_filesystem, $wp_version;
  1944 	 * Upgrade WordPress core.
       
  1945 	 *
       
  1946 	 * @since 2.8.0
       
  1947 	 *
       
  1948 	 * @param object $current Response object for whether WordPress is current.
       
  1949 	 * @param array  $args {
       
  1950 	 *        Optional. Arguments for upgrading WordPress core. Default empty array.
       
  1951 	 *
       
  1952 	 *        @type bool $pre_check_md5    Whether to check the file checksums before
       
  1953 	 *                                     attempting the upgrade. Default true.
       
  1954 	 *        @type bool $attempt_rollback Whether to attempt to rollback the chances if
       
  1955 	 *                                     there is a problem. Default false.
       
  1956 	 *        @type bool $do_rollback      Whether to perform this "upgrade" as a rollback.
       
  1957 	 *                                     Default false.
       
  1958 	 * }
       
  1959 	 * @return null|false|WP_Error False or WP_Error on failure, null on success.
       
  1960 	 */
       
  1961 	public function upgrade( $current, $args = array() ) {
       
  1962 		global $wp_filesystem;
       
  1963 
       
  1964 		include( ABSPATH . WPINC . '/version.php' ); // $wp_version;
  1318 
  1965 
  1319 		$start_time = time();
  1966 		$start_time = time();
  1320 
  1967 
  1321 		$defaults = array(
  1968 		$defaults = array(
  1322 			'pre_check_md5'    => true,
  1969 			'pre_check_md5'    => true,
  1323 			'attempt_rollback' => false,
  1970 			'attempt_rollback' => false,
  1324 			'do_rollback'      => false,
  1971 			'do_rollback'      => false,
       
  1972 			'allow_relaxed_file_ownership' => false,
  1325 		);
  1973 		);
  1326 		$parsed_args = wp_parse_args( $args, $defaults );
  1974 		$parsed_args = wp_parse_args( $args, $defaults );
  1327 
  1975 
  1328 		$this->init();
  1976 		$this->init();
  1329 		$this->upgrade_strings();
  1977 		$this->upgrade_strings();
  1330 
  1978 
  1331 		// Is an update available?
  1979 		// Is an update available?
  1332 		if ( !isset( $current->response ) || $current->response == 'latest' )
  1980 		if ( !isset( $current->response ) || $current->response == 'latest' )
  1333 			return new WP_Error('up_to_date', $this->strings['up_to_date']);
  1981 			return new WP_Error('up_to_date', $this->strings['up_to_date']);
  1334 
  1982 
  1335 		$res = $this->fs_connect( array(ABSPATH, WP_CONTENT_DIR) );
  1983 		$res = $this->fs_connect( array( ABSPATH, WP_CONTENT_DIR ), $parsed_args['allow_relaxed_file_ownership'] );
  1336 		if ( is_wp_error($res) )
  1984 		if ( ! $res || is_wp_error( $res ) ) {
  1337 			return $res;
  1985 			return $res;
       
  1986 		}
  1338 
  1987 
  1339 		$wp_dir = trailingslashit($wp_filesystem->abspath());
  1988 		$wp_dir = trailingslashit($wp_filesystem->abspath());
  1340 
  1989 
  1341 		$partial = true;
  1990 		$partial = true;
  1342 		if ( $parsed_args['do_rollback'] )
  1991 		if ( $parsed_args['do_rollback'] )
  1343 			$partial = false;
  1992 			$partial = false;
  1344 		elseif ( $parsed_args['pre_check_md5'] && ! $this->check_files() )
  1993 		elseif ( $parsed_args['pre_check_md5'] && ! $this->check_files() )
  1345 			$partial = false;
  1994 			$partial = false;
  1346 
  1995 
  1347 		// If partial update is returned from the API, use that, unless we're doing a reinstall.
  1996 		/*
  1348 		// If we cross the new_bundled version number, then use the new_bundled zip.
  1997 		 * If partial update is returned from the API, use that, unless we're doing
  1349 		// Don't though if the constant is set to skip bundled items.
  1998 		 * a reinstall. If we cross the new_bundled version number, then use
  1350 		// If the API returns a no_content zip, go with it. Finally, default to the full zip.
  1999 		 * the new_bundled zip. Don't though if the constant is set to skip bundled items.
       
  2000 		 * If the API returns a no_content zip, go with it. Finally, default to the full zip.
       
  2001 		 */
  1351 		if ( $parsed_args['do_rollback'] && $current->packages->rollback )
  2002 		if ( $parsed_args['do_rollback'] && $current->packages->rollback )
  1352 			$to_download = 'rollback';
  2003 			$to_download = 'rollback';
  1353 		elseif ( $current->packages->partial && 'reinstall' != $current->response && $wp_version == $current->partial_version && $partial )
  2004 		elseif ( $current->packages->partial && 'reinstall' != $current->response && $wp_version == $current->partial_version && $partial )
  1354 			$to_download = 'partial';
  2005 			$to_download = 'partial';
  1355 		elseif ( $current->packages->new_bundled && version_compare( $wp_version, $current->new_bundled, '<' )
  2006 		elseif ( $current->packages->new_bundled && version_compare( $wp_version, $current->new_bundled, '<' )
  1385 		// In the event of an issue, we may be able to roll back.
  2036 		// In the event of an issue, we may be able to roll back.
  1386 		if ( $parsed_args['attempt_rollback'] && $current->packages->rollback && ! $parsed_args['do_rollback'] ) {
  2037 		if ( $parsed_args['attempt_rollback'] && $current->packages->rollback && ! $parsed_args['do_rollback'] ) {
  1387 			$try_rollback = false;
  2038 			$try_rollback = false;
  1388 			if ( is_wp_error( $result ) ) {
  2039 			if ( is_wp_error( $result ) ) {
  1389 				$error_code = $result->get_error_code();
  2040 				$error_code = $result->get_error_code();
  1390 				// Not all errors are equal. These codes are critical: copy_failed__copy_dir,
  2041 				/*
  1391 				// mkdir_failed__copy_dir, copy_failed__copy_dir_retry, and disk_full.
  2042 				 * Not all errors are equal. These codes are critical: copy_failed__copy_dir,
  1392 				// do_rollback allows for update_core() to trigger a rollback if needed.
  2043 				 * mkdir_failed__copy_dir, copy_failed__copy_dir_retry, and disk_full.
       
  2044 				 * do_rollback allows for update_core() to trigger a rollback if needed.
       
  2045 				 */
  1393 				if ( false !== strpos( $error_code, 'do_rollback' ) )
  2046 				if ( false !== strpos( $error_code, 'do_rollback' ) )
  1394 					$try_rollback = true;
  2047 					$try_rollback = true;
  1395 				elseif ( false !== strpos( $error_code, '__copy_dir' ) )
  2048 				elseif ( false !== strpos( $error_code, '__copy_dir' ) )
  1396 					$try_rollback = true;
  2049 					$try_rollback = true;
  1397 				elseif ( 'disk_full' === $error_code )
  2050 				elseif ( 'disk_full' === $error_code )
  1398 					$try_rollback = true;
  2051 					$try_rollback = true;
  1399 			}
  2052 			}
  1400 
  2053 
  1401 			if ( $try_rollback ) {
  2054 			if ( $try_rollback ) {
       
  2055 				/** This filter is documented in wp-admin/includes/update-core.php */
  1402 				apply_filters( 'update_feedback', $result );
  2056 				apply_filters( 'update_feedback', $result );
       
  2057 
       
  2058 				/** This filter is documented in wp-admin/includes/update-core.php */
  1403 				apply_filters( 'update_feedback', $this->strings['start_rollback'] );
  2059 				apply_filters( 'update_feedback', $this->strings['start_rollback'] );
  1404 
  2060 
  1405 				$rollback_result = $this->upgrade( $current, array_merge( $parsed_args, array( 'do_rollback' => true ) ) );
  2061 				$rollback_result = $this->upgrade( $current, array_merge( $parsed_args, array( 'do_rollback' => true ) ) );
  1406 
  2062 
  1407 				$original_result = $result;
  2063 				$original_result = $result;
  1408 				$result = new WP_Error( 'rollback_was_required', $this->strings['rollback_was_required'], (object) array( 'update' => $original_result, 'rollback' => $rollback_result ) );
  2064 				$result = new WP_Error( 'rollback_was_required', $this->strings['rollback_was_required'], (object) array( 'update' => $original_result, 'rollback' => $rollback_result ) );
  1409 			}
  2065 			}
  1410 		}
  2066 		}
  1411 
  2067 
       
  2068 		/** This action is documented in wp-admin/includes/class-wp-upgrader.php */
  1412 		do_action( 'upgrader_process_complete', $this, array( 'action' => 'update', 'type' => 'core' ) );
  2069 		do_action( 'upgrader_process_complete', $this, array( 'action' => 'update', 'type' => 'core' ) );
  1413 
  2070 
  1414 		// Clear the current updates
  2071 		// Clear the current updates
  1415 		delete_site_transient( 'update_core' );
  2072 		delete_site_transient( 'update_core' );
  1416 
  2073 
  1418 			$stats = array(
  2075 			$stats = array(
  1419 				'update_type'      => $current->response,
  2076 				'update_type'      => $current->response,
  1420 				'success'          => true,
  2077 				'success'          => true,
  1421 				'fs_method'        => $wp_filesystem->method,
  2078 				'fs_method'        => $wp_filesystem->method,
  1422 				'fs_method_forced' => defined( 'FS_METHOD' ) || has_filter( 'filesystem_method' ),
  2079 				'fs_method_forced' => defined( 'FS_METHOD' ) || has_filter( 'filesystem_method' ),
       
  2080 				'fs_method_direct' => !empty( $GLOBALS['_wp_filesystem_direct_method'] ) ? $GLOBALS['_wp_filesystem_direct_method'] : '',
  1423 				'time_taken'       => time() - $start_time,
  2081 				'time_taken'       => time() - $start_time,
       
  2082 				'reported'         => $wp_version,
  1424 				'attempted'        => $current->version,
  2083 				'attempted'        => $current->version,
  1425 			);
  2084 			);
  1426 
  2085 
  1427 			if ( is_wp_error( $result ) ) {
  2086 			if ( is_wp_error( $result ) ) {
  1428 				$stats['success'] = false;
  2087 				$stats['success'] = false;
  1446 		}
  2105 		}
  1447 
  2106 
  1448 		return $result;
  2107 		return $result;
  1449 	}
  2108 	}
  1450 
  2109 
  1451 	// Determines if this WordPress Core version should update to $offered_ver or not
  2110 	/**
  1452 	static function should_update_to_version( $offered_ver /* x.y.z */ ) {
  2111 	 * Determines if this WordPress Core version should update to an offered version or not.
  1453 		include ABSPATH . WPINC . '/version.php'; // $wp_version; // x.y.z
  2112 	 *
       
  2113 	 * @since 3.7.0
       
  2114 	 *
       
  2115 	 * @param string $offered_ver The offered version, of the format x.y.z.
       
  2116 	 * @return bool True if we should update to the offered version, otherwise false.
       
  2117 	 */
       
  2118 	public static function should_update_to_version( $offered_ver ) {
       
  2119 		include( ABSPATH . WPINC . '/version.php' ); // $wp_version; // x.y.z
  1454 
  2120 
  1455 		$current_branch = implode( '.', array_slice( preg_split( '/[.-]/', $wp_version  ), 0, 2 ) ); // x.y
  2121 		$current_branch = implode( '.', array_slice( preg_split( '/[.-]/', $wp_version  ), 0, 2 ) ); // x.y
  1456 		$new_branch     = implode( '.', array_slice( preg_split( '/[.-]/', $offered_ver ), 0, 2 ) ); // x.y
  2122 		$new_branch     = implode( '.', array_slice( preg_split( '/[.-]/', $offered_ver ), 0, 2 ) ); // x.y
  1457 		$current_is_development_version = (bool) strpos( $wp_version, '-' );
  2123 		$current_is_development_version = (bool) strpos( $wp_version, '-' );
  1458 
  2124 
  1501 				return false;
  2167 				return false;
  1502 		}
  2168 		}
  1503 
  2169 
  1504 		// 3: 3.7-alpha-25000 -> 3.7-alpha-25678 -> 3.7-beta1 -> 3.7-beta2
  2170 		// 3: 3.7-alpha-25000 -> 3.7-alpha-25678 -> 3.7-beta1 -> 3.7-beta2
  1505 		if ( $current_is_development_version ) {
  2171 		if ( $current_is_development_version ) {
       
  2172 
       
  2173 			/**
       
  2174 			 * Filter whether to enable automatic core updates for development versions.
       
  2175 			 *
       
  2176 			 * @since 3.7.0
       
  2177 			 *
       
  2178 			 * @param bool $upgrade_dev Whether to enable automatic updates for
       
  2179 			 *                          development versions.
       
  2180 			 */
  1506 			if ( ! apply_filters( 'allow_dev_auto_core_updates', $upgrade_dev ) )
  2181 			if ( ! apply_filters( 'allow_dev_auto_core_updates', $upgrade_dev ) )
  1507 				return false;
  2182 				return false;
  1508 			// else fall through to minor + major branches below
  2183 			// Else fall through to minor + major branches below.
  1509 		}
  2184 		}
  1510 
  2185 
  1511 		// 4: Minor In-branch updates (3.7.0 -> 3.7.1 -> 3.7.2 -> 3.7.4)
  2186 		// 4: Minor In-branch updates (3.7.0 -> 3.7.1 -> 3.7.2 -> 3.7.4)
  1512 		if ( $current_branch == $new_branch )
  2187 		if ( $current_branch == $new_branch ) {
       
  2188 
       
  2189 			/**
       
  2190 			 * Filter whether to enable minor automatic core updates.
       
  2191 			 *
       
  2192 			 * @since 3.7.0
       
  2193 			 *
       
  2194 			 * @param bool $upgrade_minor Whether to enable minor automatic core updates.
       
  2195 			 */
  1513 			return apply_filters( 'allow_minor_auto_core_updates', $upgrade_minor );
  2196 			return apply_filters( 'allow_minor_auto_core_updates', $upgrade_minor );
       
  2197 		}
  1514 
  2198 
  1515 		// 5: Major version updates (3.7.0 -> 3.8.0 -> 3.9.1)
  2199 		// 5: Major version updates (3.7.0 -> 3.8.0 -> 3.9.1)
  1516 		if ( version_compare( $new_branch, $current_branch, '>' ) )
  2200 		if ( version_compare( $new_branch, $current_branch, '>' ) ) {
       
  2201 
       
  2202 			/**
       
  2203 			 * Filter whether to enable major automatic core updates.
       
  2204 			 *
       
  2205 			 * @since 3.7.0
       
  2206 			 *
       
  2207 			 * @param bool $upgrade_major Whether to enable major automatic core updates.
       
  2208 			 */
  1517 			return apply_filters( 'allow_major_auto_core_updates', $upgrade_major );
  2209 			return apply_filters( 'allow_major_auto_core_updates', $upgrade_major );
       
  2210 		}
  1518 
  2211 
  1519 		// If we're not sure, we don't want it
  2212 		// If we're not sure, we don't want it
  1520 		return false;
  2213 		return false;
  1521 	}
  2214 	}
  1522 
  2215 
  1523 	function check_files() {
  2216 	/**
       
  2217 	 * Compare the disk file checksums agains the expected checksums.
       
  2218 	 *
       
  2219 	 * @since 3.7.0
       
  2220 	 *
       
  2221 	 * @return bool True if the checksums match, otherwise false.
       
  2222 	 */
       
  2223 	public function check_files() {
  1524 		global $wp_version, $wp_local_package;
  2224 		global $wp_version, $wp_local_package;
  1525 
  2225 
  1526 		$checksums = get_core_checksums( $wp_version, isset( $wp_local_package ) ? $wp_local_package : 'en_US' );
  2226 		$checksums = get_core_checksums( $wp_version, isset( $wp_local_package ) ? $wp_local_package : 'en_US' );
  1527 
  2227 
  1528 		if ( ! is_array( $checksums ) )
  2228 		if ( ! is_array( $checksums ) )
  1546  * @package WordPress
  2246  * @package WordPress
  1547  * @subpackage Upgrader
  2247  * @subpackage Upgrader
  1548  * @since 2.8.0
  2248  * @since 2.8.0
  1549  */
  2249  */
  1550 class File_Upload_Upgrader {
  2250 class File_Upload_Upgrader {
  1551 	var $package;
  2251 
  1552 	var $filename;
  2252 	/**
  1553 	var $id = 0;
  2253 	 * The full path to the file package.
  1554 
  2254 	 *
  1555 	function __construct($form, $urlholder) {
  2255 	 * @since 2.8.0
       
  2256 	 * @var string $package
       
  2257 	 */
       
  2258 	public $package;
       
  2259 
       
  2260 	/**
       
  2261 	 * The name of the file.
       
  2262 	 *
       
  2263 	 * @since 2.8.0
       
  2264 	 * @var string $filename
       
  2265 	 */
       
  2266 	public $filename;
       
  2267 
       
  2268 	/**
       
  2269 	 * The ID of the attachment post for this file.
       
  2270 	 *
       
  2271 	 * @since 3.3.0
       
  2272 	 * @var int $id
       
  2273 	 */
       
  2274 	public $id = 0;
       
  2275 
       
  2276 	/**
       
  2277 	 * Construct the upgrader for a form.
       
  2278 	 *
       
  2279 	 * @since 2.8.0
       
  2280 	 *
       
  2281 	 * @param string $form      The name of the form the file was uploaded from.
       
  2282 	 * @param string $urlholder The name of the `GET` parameter that holds the filename.
       
  2283 	 */
       
  2284 	public function __construct( $form, $urlholder ) {
  1556 
  2285 
  1557 		if ( empty($_FILES[$form]['name']) && empty($_GET[$urlholder]) )
  2286 		if ( empty($_FILES[$form]['name']) && empty($_GET[$urlholder]) )
  1558 			wp_die(__('Please select a file'));
  2287 			wp_die(__('Please select a file'));
  1559 
  2288 
  1560 		//Handle a newly uploaded file, Else assume it's already been uploaded
  2289 		//Handle a newly uploaded file, Else assume it's already been uploaded
  1576 				'guid' => $file['url'],
  2305 				'guid' => $file['url'],
  1577 				'context' => 'upgrader',
  2306 				'context' => 'upgrader',
  1578 				'post_status' => 'private'
  2307 				'post_status' => 'private'
  1579 			);
  2308 			);
  1580 
  2309 
  1581 			// Save the data
  2310 			// Save the data.
  1582 			$this->id = wp_insert_attachment( $object, $file['file'] );
  2311 			$this->id = wp_insert_attachment( $object, $file['file'] );
  1583 
  2312 
  1584 			// schedule a cleanup for 2 hours from now in case of failed install
  2313 			// Schedule a cleanup for 2 hours from now in case of failed install.
  1585 			wp_schedule_single_event( time() + 7200, 'upgrader_scheduled_cleanup', array( $this->id ) );
  2314 			wp_schedule_single_event( time() + 2 * HOUR_IN_SECONDS, 'upgrader_scheduled_cleanup', array( $this->id ) );
  1586 
  2315 
  1587 		} elseif ( is_numeric( $_GET[$urlholder] ) ) {
  2316 		} elseif ( is_numeric( $_GET[$urlholder] ) ) {
  1588 			// Numeric Package = previously uploaded file, see above.
  2317 			// Numeric Package = previously uploaded file, see above.
  1589 			$this->id = (int) $_GET[$urlholder];
  2318 			$this->id = (int) $_GET[$urlholder];
  1590 			$attachment = get_post( $this->id );
  2319 			$attachment = get_post( $this->id );
  1601 			$this->filename = $_GET[$urlholder];
  2330 			$this->filename = $_GET[$urlholder];
  1602 			$this->package = $uploads['basedir'] . '/' . $this->filename;
  2331 			$this->package = $uploads['basedir'] . '/' . $this->filename;
  1603 		}
  2332 		}
  1604 	}
  2333 	}
  1605 
  2334 
  1606 	function cleanup() {
  2335 	/**
       
  2336 	 * Delete the attachment/uploaded file.
       
  2337 	 *
       
  2338 	 * @since 3.2.2
       
  2339 	 *
       
  2340 	 * @return bool Whether the cleanup was successful.
       
  2341 	 */
       
  2342 	public function cleanup() {
  1607 		if ( $this->id )
  2343 		if ( $this->id )
  1608 			wp_delete_attachment( $this->id );
  2344 			wp_delete_attachment( $this->id );
  1609 
  2345 
  1610 		elseif ( file_exists( $this->package ) )
  2346 		elseif ( file_exists( $this->package ) )
  1611 			return @unlink( $this->package );
  2347 			return @unlink( $this->package );
  1653 		 * This filter parallels the AUTOMATIC_UPDATER_DISABLED constant in name.
  2389 		 * This filter parallels the AUTOMATIC_UPDATER_DISABLED constant in name.
  1654 		 *
  2390 		 *
  1655 		 * This also disables update notification emails. That may change in the future.
  2391 		 * This also disables update notification emails. That may change in the future.
  1656 		 *
  2392 		 *
  1657 		 * @since 3.7.0
  2393 		 * @since 3.7.0
       
  2394 		 *
  1658 		 * @param bool $disabled Whether the updater should be disabled.
  2395 		 * @param bool $disabled Whether the updater should be disabled.
  1659 		 */
  2396 		 */
  1660 		return apply_filters( 'automatic_updater_disabled', $disabled );
  2397 		return apply_filters( 'automatic_updater_disabled', $disabled );
  1661 	}
  2398 	}
  1662 
  2399 
  1706 					break 2;
  2443 					break 2;
  1707 			}
  2444 			}
  1708 		}
  2445 		}
  1709 
  2446 
  1710 		/**
  2447 		/**
  1711 		 * Filter whether the automatic updater should consider a filesystem location to be potentially
  2448 		 * Filter whether the automatic updater should consider a filesystem
  1712 		 * managed by a version control system.
  2449 		 * location to be potentially managed by a version control system.
  1713 		 *
  2450 		 *
  1714 		 * @since 3.7.0
  2451 		 * @since 3.7.0
  1715 		 *
  2452 		 *
  1716 		 * @param bool $checkout  Whether a VCS checkout was discovered at $context or ABSPATH, or anywhere higher.
  2453 		 * @param bool $checkout  Whether a VCS checkout was discovered at $context
  1717 		 * @param string $context The filesystem context (a path) against which filesystem status should be checked.
  2454 		 *                        or ABSPATH, or anywhere higher.
       
  2455 		 * @param string $context The filesystem context (a path) against which
       
  2456 		 *                        filesystem status should be checked.
  1718 		 */
  2457 		 */
  1719 		return apply_filters( 'automatic_updates_is_vcs_checkout', $checkout, $context );
  2458 		return apply_filters( 'automatic_updates_is_vcs_checkout', $checkout, $context );
  1720 	}
  2459 	}
  1721 
  2460 
  1722 	/**
  2461 	/**
  1723 	 * Tests to see if we can and should update a specific item.
  2462 	 * Tests to see if we can and should update a specific item.
  1724 	 *
  2463 	 *
  1725 	 * @since 3.7.0
  2464 	 * @since 3.7.0
  1726 	 *
  2465 	 *
  1727 	 * @param string $type    The type of update being checked: 'core', 'theme', 'plugin', 'translation'.
  2466 	 * @param string $type    The type of update being checked: 'core', 'theme',
       
  2467 	 *                        'plugin', 'translation'.
  1728 	 * @param object $item    The update offer.
  2468 	 * @param object $item    The update offer.
  1729 	 * @param string $context The filesystem context (a path) against which filesystem access and status
  2469 	 * @param string $context The filesystem context (a path) against which filesystem
  1730 	 *                        should be checked.
  2470 	 *                        access and status should be checked.
  1731 	 */
  2471 	 */
  1732 	public function should_update( $type, $item, $context ) {
  2472 	public function should_update( $type, $item, $context ) {
  1733 		// Used to see if WP_Filesystem is set up to allow unattended updates.
  2473 		// Used to see if WP_Filesystem is set up to allow unattended updates.
  1734 		$skin = new Automatic_Upgrader_Skin;
  2474 		$skin = new Automatic_Upgrader_Skin;
  1735 
  2475 
  1736 		if ( $this->is_disabled() )
  2476 		if ( $this->is_disabled() )
  1737 			return false;
  2477 			return false;
  1738 
  2478 
       
  2479 		// Only relax the filesystem checks when the update doesn't include new files
       
  2480 		$allow_relaxed_file_ownership = false;
       
  2481 		if ( 'core' == $type && isset( $item->new_files ) && ! $item->new_files ) {
       
  2482 			$allow_relaxed_file_ownership = true;
       
  2483 		}
       
  2484 
  1739 		// If we can't do an auto core update, we may still be able to email the user.
  2485 		// If we can't do an auto core update, we may still be able to email the user.
  1740 		if ( ! $skin->request_filesystem_credentials( false, $context ) || $this->is_vcs_checkout( $context ) ) {
  2486 		if ( ! $skin->request_filesystem_credentials( false, $context, $allow_relaxed_file_ownership ) || $this->is_vcs_checkout( $context ) ) {
  1741 			if ( 'core' == $type )
  2487 			if ( 'core' == $type )
  1742 				$this->send_core_update_notification_email( $item );
  2488 				$this->send_core_update_notification_email( $item );
  1743 			return false;
  2489 			return false;
  1744 		}
  2490 		}
  1745 
  2491 
  1750 			$update = ! empty( $item->autoupdate );
  2496 			$update = ! empty( $item->autoupdate );
  1751 
  2497 
  1752 		/**
  2498 		/**
  1753 		 * Filter whether to automatically update core, a plugin, a theme, or a language.
  2499 		 * Filter whether to automatically update core, a plugin, a theme, or a language.
  1754 		 *
  2500 		 *
  1755 		 * The dynamic portion of the hook name, $type, refers to the type of update
  2501 		 * The dynamic portion of the hook name, `$type`, refers to the type of update
  1756 		 * being checked. Can be 'core', 'theme', 'plugin', or 'translation'.
  2502 		 * being checked. Can be 'core', 'theme', 'plugin', or 'translation'.
  1757 		 *
  2503 		 *
  1758 		 * Generally speaking, plugins, themes, and major core versions are not updated by default,
  2504 		 * Generally speaking, plugins, themes, and major core versions are not updated
  1759 		 * while translations and minor and development versions for core are updated by default.
  2505 		 * by default, while translations and minor and development versions for core
  1760 		 *
  2506 		 * are updated by default.
  1761 		 * See the filters allow_dev_auto_core_updates, allow_minor_auto_core_updates, and
  2507 		 *
  1762 		 * allow_major_auto_core_updates more straightforward filters to adjust core updates.
  2508 		 * See the {@see 'allow_dev_auto_core_updates', {@see 'allow_minor_auto_core_updates'},
       
  2509 		 * and {@see 'allow_major_auto_core_updates'} filters for a more straightforward way to
       
  2510 		 * adjust core updates.
  1763 		 *
  2511 		 *
  1764 		 * @since 3.7.0
  2512 		 * @since 3.7.0
  1765 		 *
  2513 		 *
  1766 		 * @param bool   $update Whether to update.
  2514 		 * @param bool   $update Whether to update.
  1767 		 * @param object $item   The update offer.
  2515 		 * @param object $item   The update offer.
  1797 	 * @since 3.7.0
  2545 	 * @since 3.7.0
  1798 	 *
  2546 	 *
  1799 	 * @param object $item The update offer.
  2547 	 * @param object $item The update offer.
  1800 	 */
  2548 	 */
  1801 	protected function send_core_update_notification_email( $item ) {
  2549 	protected function send_core_update_notification_email( $item ) {
  1802 		$notify   = true;
       
  1803 		$notified = get_site_option( 'auto_core_update_notified' );
  2550 		$notified = get_site_option( 'auto_core_update_notified' );
  1804 
  2551 
  1805 		// Don't notify if we've already notified the same email address of the same version.
  2552 		// Don't notify if we've already notified the same email address of the same version.
  1806 		if ( $notified && $notified['email'] == get_site_option( 'admin_email' ) && $notified['version'] == $item->current )
  2553 		if ( $notified && $notified['email'] == get_site_option( 'admin_email' ) && $notified['version'] == $item->current )
  1807 			return false;
  2554 			return false;
  1808 
  2555 
  1809 		// See if we need to notify users of a core update.
  2556 		// See if we need to notify users of a core update.
  1810 		$notify = ! empty( $item->notify_email );
  2557 		$notify = ! empty( $item->notify_email );
  1811 
  2558 
  1812 		/**
  2559 		/**
  1813 		 * Whether to notify the site administrator of a new core update.
  2560 		 * Filter whether to notify the site administrator of a new core update.
  1814 		 *
  2561 		 *
  1815 		 * By default, administrators are notified when the update offer received from WordPress.org
  2562 		 * By default, administrators are notified when the update offer received
  1816 		 * sets a particular flag. This allows for discretion in if and when to notify.
  2563 		 * from WordPress.org sets a particular flag. This allows some discretion
  1817 		 *
  2564 		 * in if and when to notify.
  1818 		 * This filter only fires once per release -- if the same email address was already
  2565 		 *
  1819 		 * notified of the same new version, we won't repeatedly email the administrator.
  2566 		 * This filter is only evaluated once per release. If the same email address
  1820 		 *
  2567 		 * was already notified of the same new version, WordPress won't repeatedly
  1821 		 * This filter is also used on about.php to check if a plugin has disabled these notifications.
  2568 		 * email the administrator.
       
  2569 		 *
       
  2570 		 * This filter is also used on about.php to check if a plugin has disabled
       
  2571 		 * these notifications.
  1822 		 *
  2572 		 *
  1823 		 * @since 3.7.0
  2573 		 * @since 3.7.0
  1824 		 *
  2574 		 *
  1825 		 * @param bool $notify Whether the site administrator is notified.
  2575 		 * @param bool   $notify Whether the site administrator is notified.
  1826 		 * @param object $item The update offer.
  2576 		 * @param object $item   The update offer.
  1827 		 */
  2577 		 */
  1828 		if ( ! apply_filters( 'send_core_update_notification_email', $notify, $item ) )
  2578 		if ( ! apply_filters( 'send_core_update_notification_email', $notify, $item ) )
  1829 			return false;
  2579 			return false;
  1830 
  2580 
  1831 		$this->send_email( 'manual', $item );
  2581 		$this->send_email( 'manual', $item );
  1854 				$upgrader = new Plugin_Upgrader( $skin );
  2604 				$upgrader = new Plugin_Upgrader( $skin );
  1855 				$context  = WP_PLUGIN_DIR; // We don't support custom Plugin directories, or updates for WPMU_PLUGIN_DIR
  2605 				$context  = WP_PLUGIN_DIR; // We don't support custom Plugin directories, or updates for WPMU_PLUGIN_DIR
  1856 				break;
  2606 				break;
  1857 			case 'theme':
  2607 			case 'theme':
  1858 				$upgrader = new Theme_Upgrader( $skin );
  2608 				$upgrader = new Theme_Upgrader( $skin );
  1859 				$context  = get_theme_root( $item );
  2609 				$context  = get_theme_root( $item->theme );
  1860 				break;
  2610 				break;
  1861 			case 'translation':
  2611 			case 'translation':
  1862 				$upgrader = new Language_Pack_Upgrader( $skin );
  2612 				$upgrader = new Language_Pack_Upgrader( $skin );
  1863 				$context  = WP_CONTENT_DIR; // WP_LANG_DIR;
  2613 				$context  = WP_CONTENT_DIR; // WP_LANG_DIR;
  1864 				break;
  2614 				break;
  1866 
  2616 
  1867 		// Determine whether we can and should perform this update.
  2617 		// Determine whether we can and should perform this update.
  1868 		if ( ! $this->should_update( $type, $item, $context ) )
  2618 		if ( ! $this->should_update( $type, $item, $context ) )
  1869 			return false;
  2619 			return false;
  1870 
  2620 
       
  2621 		$upgrader_item = $item;
  1871 		switch ( $type ) {
  2622 		switch ( $type ) {
  1872 			case 'core':
  2623 			case 'core':
  1873 				$skin->feedback( __( 'Updating to WordPress %s' ), $item->version );
  2624 				$skin->feedback( __( 'Updating to WordPress %s' ), $item->version );
  1874 				$item_name = sprintf( __( 'WordPress %s' ), $item->version );
  2625 				$item_name = sprintf( __( 'WordPress %s' ), $item->version );
  1875 				break;
  2626 				break;
  1876 			case 'theme':
  2627 			case 'theme':
  1877 				$theme = wp_get_theme( $item );
  2628 				$upgrader_item = $item->theme;
       
  2629 				$theme = wp_get_theme( $upgrader_item );
  1878 				$item_name = $theme->Get( 'Name' );
  2630 				$item_name = $theme->Get( 'Name' );
  1879 				$skin->feedback( __( 'Updating theme: %s' ), $item_name );
  2631 				$skin->feedback( __( 'Updating theme: %s' ), $item_name );
  1880 				break;
  2632 				break;
  1881 			case 'plugin':
  2633 			case 'plugin':
  1882 				$plugin_data = get_plugin_data( $context . '/' . $item );
  2634 				$upgrader_item = $item->plugin;
       
  2635 				$plugin_data = get_plugin_data( $context . '/' . $upgrader_item );
  1883 				$item_name = $plugin_data['Name'];
  2636 				$item_name = $plugin_data['Name'];
  1884 				$skin->feedback( __( 'Updating plugin: %s' ), $item_name );
  2637 				$skin->feedback( __( 'Updating plugin: %s' ), $item_name );
  1885 				break;
  2638 				break;
  1886 			case 'translation':
  2639 			case 'translation':
  1887 				$language_item_name = $upgrader->get_name_for_update( $item );
  2640 				$language_item_name = $upgrader->get_name_for_update( $item );
  1888 				$item_name = sprintf( __( 'Translations for %s' ), $language_item_name );
  2641 				$item_name = sprintf( __( 'Translations for %s' ), $language_item_name );
  1889 				$skin->feedback( sprintf( __( 'Updating translations for %1$s (%2$s)&#8230;' ), $language_item_name, $item->language ) );
  2642 				$skin->feedback( sprintf( __( 'Updating translations for %1$s (%2$s)&#8230;' ), $language_item_name, $item->language ) );
  1890 				break;
  2643 				break;
  1891 		}
  2644 		}
  1892 
  2645 
       
  2646 		$allow_relaxed_file_ownership = false;
       
  2647 		if ( 'core' == $type && isset( $item->new_files ) && ! $item->new_files ) {
       
  2648 			$allow_relaxed_file_ownership = true;
       
  2649 		}
       
  2650 
  1893 		// Boom, This sites about to get a whole new splash of paint!
  2651 		// Boom, This sites about to get a whole new splash of paint!
  1894 		$upgrade_result = $upgrader->upgrade( $item, array(
  2652 		$upgrade_result = $upgrader->upgrade( $upgrader_item, array(
  1895 			'clear_update_cache' => false,
  2653 			'clear_update_cache' => false,
  1896 			'pre_check_md5'      => false, /* always use partial builds if possible for core updates */
  2654 			// Always use partial builds if possible for core updates.
  1897 			'attempt_rollback'   => true, /* only available for core updates */
  2655 			'pre_check_md5'      => false,
       
  2656 			// Only available for core updates.
       
  2657 			'attempt_rollback'   => true,
       
  2658 			// Allow relaxed file ownership in some scenarios
       
  2659 			'allow_relaxed_file_ownership' => $allow_relaxed_file_ownership,
  1898 		) );
  2660 		) );
  1899 
  2661 
  1900 		// Core doesn't output this, so lets append it so we don't get confused
  2662 		// If the filesystem is unavailable, false is returned.
       
  2663 		if ( false === $upgrade_result ) {
       
  2664 			$upgrade_result = new WP_Error( 'fs_unavailable', __( 'Could not access filesystem.' ) );
       
  2665 		}
       
  2666 
       
  2667 		// Core doesn't output this, so let's append it so we don't get confused.
  1901 		if ( 'core' == $type ) {
  2668 		if ( 'core' == $type ) {
  1902 			if ( is_wp_error( $upgrade_result ) ) {
  2669 			if ( is_wp_error( $upgrade_result ) ) {
  1903 				$skin->error( __( 'Installation Failed' ), $upgrade_result );
  2670 				$skin->error( __( 'Installation Failed' ), $upgrade_result );
  1904 			} else {
  2671 			} else {
  1905 				$skin->feedback( __( 'WordPress updated successfully' ) );
  2672 				$skin->feedback( __( 'WordPress updated successfully' ) );
  1945 			// Check to see if the lock is still valid
  2712 			// Check to see if the lock is still valid
  1946 			if ( $lock_result > ( time() - HOUR_IN_SECONDS ) )
  2713 			if ( $lock_result > ( time() - HOUR_IN_SECONDS ) )
  1947 				return;
  2714 				return;
  1948 		}
  2715 		}
  1949 
  2716 
  1950 		// Update the lock, as by this point we've definately got a lock, just need to fire the actions
  2717 		// Update the lock, as by this point we've definitely got a lock, just need to fire the actions
  1951 		update_option( $lock_name, time() );
  2718 		update_option( $lock_name, time() );
  1952 
  2719 
  1953 		// Don't automatically run these thins, as we'll handle it ourselves
  2720 		// Don't automatically run these thins, as we'll handle it ourselves
  1954 		remove_action( 'upgrader_process_complete', array( 'Language_Pack_Upgrader', 'async_upgrade' ), 20 );
  2721 		remove_action( 'upgrader_process_complete', array( 'Language_Pack_Upgrader', 'async_upgrade' ), 20 );
  1955 		remove_action( 'upgrader_process_complete', 'wp_version_check' );
  2722 		remove_action( 'upgrader_process_complete', 'wp_version_check' );
  1958 
  2725 
  1959 		// Next, Plugins
  2726 		// Next, Plugins
  1960 		wp_update_plugins(); // Check for Plugin updates
  2727 		wp_update_plugins(); // Check for Plugin updates
  1961 		$plugin_updates = get_site_transient( 'update_plugins' );
  2728 		$plugin_updates = get_site_transient( 'update_plugins' );
  1962 		if ( $plugin_updates && !empty( $plugin_updates->response ) ) {
  2729 		if ( $plugin_updates && !empty( $plugin_updates->response ) ) {
  1963 			foreach ( array_keys( $plugin_updates->response ) as $plugin ) {
  2730 			foreach ( $plugin_updates->response as $plugin ) {
  1964 				$this->update( 'plugin', $plugin );
  2731 				$this->update( 'plugin', $plugin );
  1965 			}
  2732 			}
  1966 			// Force refresh of plugin update information
  2733 			// Force refresh of plugin update information
  1967 			wp_clean_plugins_cache();
  2734 			wp_clean_plugins_cache();
  1968 		}
  2735 		}
  1969 
  2736 
  1970 		// Next, those themes we all love
  2737 		// Next, those themes we all love
  1971 		wp_update_themes();  // Check for Theme updates
  2738 		wp_update_themes();  // Check for Theme updates
  1972 		$theme_updates = get_site_transient( 'update_themes' );
  2739 		$theme_updates = get_site_transient( 'update_themes' );
  1973 		if ( $theme_updates && !empty( $theme_updates->response ) ) {
  2740 		if ( $theme_updates && !empty( $theme_updates->response ) ) {
  1974 			foreach ( array_keys( $theme_updates->response ) as $theme ) {
  2741 			foreach ( $theme_updates->response as $theme ) {
  1975 				$this->update( 'theme', $theme );
  2742 				$this->update( 'theme', (object) $theme );
  1976 			}
  2743 			}
  1977 			// Force refresh of theme update information
  2744 			// Force refresh of theme update information
  1978 			wp_clean_themes_cache();
  2745 			wp_clean_themes_cache();
  1979 		}
  2746 		}
  1980 
  2747 
  1985 		if ( $core_update )
  2752 		if ( $core_update )
  1986 			$this->update( 'core', $core_update );
  2753 			$this->update( 'core', $core_update );
  1987 
  2754 
  1988 		// Clean up, and check for any pending translations
  2755 		// Clean up, and check for any pending translations
  1989 		// (Core_Upgrader checks for core updates)
  2756 		// (Core_Upgrader checks for core updates)
  1990 		wp_update_themes();  // Check for Theme updates
  2757 		$theme_stats = array();
  1991 		wp_update_plugins(); // Check for Plugin updates
  2758 		if ( isset( $this->update_results['theme'] ) ) {
       
  2759 			foreach ( $this->update_results['theme'] as $upgrade ) {
       
  2760 				$theme_stats[ $upgrade->item->theme ] = ( true === $upgrade->result );
       
  2761 			}
       
  2762 		}
       
  2763 		wp_update_themes( $theme_stats );  // Check for Theme updates
       
  2764 
       
  2765 		$plugin_stats = array();
       
  2766 		if ( isset( $this->update_results['plugin'] ) ) {
       
  2767 			foreach ( $this->update_results['plugin'] as $upgrade ) {
       
  2768 				$plugin_stats[ $upgrade->item->plugin ] = ( true === $upgrade->result );
       
  2769 			}
       
  2770 		}
       
  2771 		wp_update_plugins( $plugin_stats ); // Check for Plugin updates
  1992 
  2772 
  1993 		// Finally, Process any new translations
  2773 		// Finally, Process any new translations
  1994 		$language_updates = wp_get_translation_updates();
  2774 		$language_updates = wp_get_translation_updates();
  1995 		if ( $language_updates ) {
  2775 		if ( $language_updates ) {
  1996 			foreach ( $language_updates as $update ) {
  2776 			foreach ( $language_updates as $update ) {
  1997 				$this->update( 'translation', $update );
  2777 				$this->update( 'translation', $update );
  1998 			}
  2778 			}
  1999 
  2779 
  2000 			// Clear existing caches
  2780 			// Clear existing caches
  2001 			wp_clean_plugins_cache();
  2781 			wp_clean_update_cache();
  2002 			wp_clean_themes_cache();
       
  2003 			delete_site_transient( 'update_core' );
       
  2004 
  2782 
  2005 			wp_version_check();  // check for Core updates
  2783 			wp_version_check();  // check for Core updates
  2006 			wp_update_themes();  // Check for Theme updates
  2784 			wp_update_themes();  // Check for Theme updates
  2007 			wp_update_plugins(); // Check for Plugin updates
  2785 			wp_update_plugins(); // Check for Plugin updates
  2008 		}
  2786 		}
  2009 
  2787 
  2010 		// Send debugging email to all development installs.
  2788 		// Send debugging email to all development installs.
  2011 		if ( ! empty( $this->update_results ) ) {
  2789 		if ( ! empty( $this->update_results ) ) {
  2012 			$development_version = false !== strpos( $wp_version, '-' );
  2790 			$development_version = false !== strpos( $wp_version, '-' );
       
  2791 
  2013 			/**
  2792 			/**
  2014 			 * Filter whether to send a debugging email for each automatic background update.
  2793 			 * Filter whether to send a debugging email for each automatic background update.
  2015 			 *
  2794 			 *
  2016 			 * @since 3.7.0
  2795 			 * @since 3.7.0
  2017 			 * @param bool $development_version By default, emails are sent if the install is a development version.
  2796 			 *
       
  2797 			 * @param bool $development_version By default, emails are sent if the
       
  2798 			 *                                  install is a development version.
  2018 			 *                                  Return false to avoid the email.
  2799 			 *                                  Return false to avoid the email.
  2019 			 */
  2800 			 */
  2020 			if ( apply_filters( 'automatic_updates_send_debug_email', $development_version ) )
  2801 			if ( apply_filters( 'automatic_updates_send_debug_email', $development_version ) )
  2021 				$this->send_debug_email();
  2802 				$this->send_debug_email();
  2022 
  2803 
  2023 			if ( ! empty( $this->update_results['core'] ) )
  2804 			if ( ! empty( $this->update_results['core'] ) )
  2024 				$this->after_core_update( $this->update_results['core'][0] );
  2805 				$this->after_core_update( $this->update_results['core'][0] );
       
  2806 
       
  2807 			/**
       
  2808 			 * Fires after all automatic updates have run.
       
  2809 			 *
       
  2810 			 * @since 3.8.0
       
  2811 			 *
       
  2812 			 * @param array $update_results The results of all attempted updates.
       
  2813 			 */
       
  2814 			do_action( 'automatic_updates_complete', $this->update_results );
  2025 		}
  2815 		}
  2026 
  2816 
  2027 		// Clear the lock
  2817 		// Clear the lock
  2028 		delete_option( $lock_name );
  2818 		delete_option( $lock_name );
  2029 	}
  2819 	}
  2141 		 * Filter whether to send an email following an automatic background core update.
  2931 		 * Filter whether to send an email following an automatic background core update.
  2142 		 *
  2932 		 *
  2143 		 * @since 3.7.0
  2933 		 * @since 3.7.0
  2144 		 *
  2934 		 *
  2145 		 * @param bool   $send        Whether to send the email. Default true.
  2935 		 * @param bool   $send        Whether to send the email. Default true.
  2146 		 * @param string $type        The type of email to send. Can be one of 'success', 'fail', 'critical'.
  2936 		 * @param string $type        The type of email to send. Can be one of
       
  2937 		 *                            'success', 'fail', 'critical'.
  2147 		 * @param object $core_update The update offer that was attempted.
  2938 		 * @param object $core_update The update offer that was attempted.
  2148 		 * @param mixed  $result      The result for the core update. Can be WP_Error.
  2939 		 * @param mixed  $result      The result for the core update. Can be WP_Error.
  2149 		 */
  2940 		 */
  2150 		if ( 'manual' !== $type && ! apply_filters( 'auto_core_update_send_email', true, $type, $core_update, $result ) )
  2941 		if ( 'manual' !== $type && ! apply_filters( 'auto_core_update_send_email', true, $type, $core_update, $result ) )
  2151 			return;
  2942 			return;
  2223 				$body .= "\n\n" . __( "Please check out your site now. It's possible that everything is working. If it says you need to update, you should do so:" );
  3014 				$body .= "\n\n" . __( "Please check out your site now. It's possible that everything is working. If it says you need to update, you should do so:" );
  2224 				$body .= "\n" . network_admin_url( 'update-core.php' );
  3015 				$body .= "\n" . network_admin_url( 'update-core.php' );
  2225 				break;
  3016 				break;
  2226 		}
  3017 		}
  2227 
  3018 
       
  3019 		$critical_support = 'critical' === $type && ! empty( $core_update->support_email );
       
  3020 		if ( $critical_support ) {
       
  3021 			// Support offer if available.
       
  3022 			$body .= "\n\n" . sprintf( __( "The WordPress team is willing to help you. Forward this email to %s and the team will work with you to make sure your site is working." ), $core_update->support_email );
       
  3023 		} else {
       
  3024 			// Add a note about the support forums.
       
  3025 			$body .= "\n\n" . __( 'If you experience any issues or need support, the volunteers in the WordPress.org support forums may be able to help.' );
       
  3026 			$body .= "\n" . __( 'https://wordpress.org/support/' );
       
  3027 		}
       
  3028 
  2228 		// Updates are important!
  3029 		// Updates are important!
  2229 		if ( $type != 'success' || $newer_version_available )
  3030 		if ( $type != 'success' || $newer_version_available ) {
  2230 			$body .= "\n\n" . __( 'Keeping your site updated is important for security. It also makes the internet a safer place for you and your readers.' );
  3031 			$body .= "\n\n" . __( 'Keeping your site updated is important for security. It also makes the internet a safer place for you and your readers.' );
  2231 
  3032 		}
  2232 		// Add a note about the support forums to all emails.
  3033 
  2233 		$body .= "\n\n" . __( 'If you experience any issues or need support, the volunteers in the WordPress.org support forums may be able to help.' );
  3034 		if ( $critical_support ) {
  2234 		$body .= "\n" . __( 'http://wordpress.org/support/' );
  3035 			$body .= " " . __( "If you reach out to us, we'll also ensure you'll never have this problem again." );
       
  3036 		}
  2235 
  3037 
  2236 		// If things are successful and we're now on the latest, mention plugins and themes if any are out of date.
  3038 		// If things are successful and we're now on the latest, mention plugins and themes if any are out of date.
  2237 		if ( $type == 'success' && ! $newer_version_available && ( get_plugin_updates() || get_theme_updates() ) ) {
  3039 		if ( $type == 'success' && ! $newer_version_available && ( get_plugin_updates() || get_theme_updates() ) ) {
  2238 			$body .= "\n\n" . __( 'You also have some plugins or themes with updates available. Update them now:' );
  3040 			$body .= "\n\n" . __( 'You also have some plugins or themes with updates available. Update them now:' );
  2239 			$body .= "\n" . network_admin_url();
  3041 			$body .= "\n" . network_admin_url();
  2272 
  3074 
  2273 		$to  = get_site_option( 'admin_email' );
  3075 		$to  = get_site_option( 'admin_email' );
  2274 		$headers = '';
  3076 		$headers = '';
  2275 
  3077 
  2276 		$email = compact( 'to', 'subject', 'body', 'headers' );
  3078 		$email = compact( 'to', 'subject', 'body', 'headers' );
       
  3079 
  2277 		/**
  3080 		/**
  2278 		 * Filter the email sent following an automatic background core update.
  3081 		 * Filter the email sent following an automatic background core update.
  2279 		 *
  3082 		 *
  2280 		 * @since 3.7.0
  3083 		 * @since 3.7.0
  2281 		 *
  3084 		 *
  2282 		 * @param array $email {
  3085 		 * @param array $email {
  2283 		 *     Array of email arguments that will be passed to wp_mail().
  3086 		 *     Array of email arguments that will be passed to wp_mail().
  2284 		 *
  3087 		 *
  2285 		 *     @type string $to      The email recipient. An array of emails can be returned, as handled by wp_mail().
  3088 		 *     @type string $to      The email recipient. An array of emails
       
  3089 		 *                            can be returned, as handled by wp_mail().
  2286 		 *     @type string $subject The email's subject.
  3090 		 *     @type string $subject The email's subject.
  2287 		 *     @type string $body    The email message body.
  3091 		 *     @type string $body    The email message body.
  2288 		 *     @type string $headers Any email headers, defaults to no headers.
  3092 		 *     @type string $headers Any email headers, defaults to no headers.
  2289 		 * }
  3093 		 * }
  2290 		 * @param string $type        The type of email being sent. Can be one of 'success', 'fail', 'manual', 'critical'.
  3094 		 * @param string $type        The type of email being sent. Can be one of
       
  3095 		 *                            'success', 'fail', 'manual', 'critical'.
  2291 		 * @param object $core_update The update offer that was attempted.
  3096 		 * @param object $core_update The update offer that was attempted.
  2292 		 * @param mixed  $result      The result for the core update. Can be WP_Error.
  3097 		 * @param mixed  $result      The result for the core update. Can be WP_Error.
  2293 		 */
  3098 		 */
  2294 		$email = apply_filters( 'auto_core_update_email', $email, $type, $core_update, $result );
  3099 		$email = apply_filters( 'auto_core_update_email', $email, $type, $core_update, $result );
  2295 
  3100 
  2296 		wp_mail( $email['to'], $email['subject'], $email['body'], $email['headers'] );
  3101 		wp_mail( $email['to'], wp_specialchars_decode( $email['subject'] ), $email['body'], $email['headers'] );
  2297 	}
  3102 	}
  2298 
  3103 
  2299 	/**
  3104 	/**
  2300 	 * Prepares and sends an email of a full log of background update results, useful for debugging and geekery.
  3105 	 * Prepares and sends an email of a full log of background update results, useful for debugging and geekery.
  2301 	 *
  3106 	 *
  2307 			$update_count += count( $updates );
  3112 			$update_count += count( $updates );
  2308 
  3113 
  2309 		$body = array();
  3114 		$body = array();
  2310 		$failures = 0;
  3115 		$failures = 0;
  2311 
  3116 
  2312 		$body[] = 'WordPress site: ' . network_home_url( '/' );
  3117 		$body[] = sprintf( __( 'WordPress site: %s' ), network_home_url( '/' ) );
  2313 
  3118 
  2314 		// Core
  3119 		// Core
  2315 		if ( isset( $this->update_results['core'] ) ) {
  3120 		if ( isset( $this->update_results['core'] ) ) {
  2316 			$result = $this->update_results['core'][0];
  3121 			$result = $this->update_results['core'][0];
  2317 			if ( $result->result && ! is_wp_error( $result->result ) ) {
  3122 			if ( $result->result && ! is_wp_error( $result->result ) ) {
  2318 				$body[] = sprintf( 'SUCCESS: WordPress was successfully updated to %s', $result->name );
  3123 				$body[] = sprintf( __( 'SUCCESS: WordPress was successfully updated to %s' ), $result->name );
  2319 			} else {
  3124 			} else {
  2320 				$body[] = sprintf( 'FAILED: WordPress failed to update to %s', $result->name );
  3125 				$body[] = sprintf( __( 'FAILED: WordPress failed to update to %s' ), $result->name );
  2321 				$failures++;
  3126 				$failures++;
  2322 			}
  3127 			}
  2323 			$body[] = '';
  3128 			$body[] = '';
  2324 		}
  3129 		}
  2325 
  3130 
  2327 		foreach ( array( 'plugin', 'theme', 'translation' ) as $type ) {
  3132 		foreach ( array( 'plugin', 'theme', 'translation' ) as $type ) {
  2328 			if ( ! isset( $this->update_results[ $type ] ) )
  3133 			if ( ! isset( $this->update_results[ $type ] ) )
  2329 				continue;
  3134 				continue;
  2330 			$success_items = wp_list_filter( $this->update_results[ $type ], array( 'result' => true ) );
  3135 			$success_items = wp_list_filter( $this->update_results[ $type ], array( 'result' => true ) );
  2331 			if ( $success_items ) {
  3136 			if ( $success_items ) {
  2332 				$body[] = "The following {$type}s were successfully updated:";
  3137 				$messages = array(
  2333 				foreach ( wp_list_pluck( $success_items, 'name' ) as $name )
  3138 					'plugin'      => __( 'The following plugins were successfully updated:' ),
  2334 					$body[] = ' * SUCCESS: ' . $name;
  3139 					'theme'       => __( 'The following themes were successfully updated:' ),
       
  3140 					'translation' => __( 'The following translations were successfully updated:' ),
       
  3141 				);
       
  3142 
       
  3143 				$body[] = $messages[ $type ];
       
  3144 				foreach ( wp_list_pluck( $success_items, 'name' ) as $name ) {
       
  3145 					$body[] = ' * ' . sprintf( __( 'SUCCESS: %s' ), $name );
       
  3146 				}
  2335 			}
  3147 			}
  2336 			if ( $success_items != $this->update_results[ $type ] ) {
  3148 			if ( $success_items != $this->update_results[ $type ] ) {
  2337 				// Failed updates
  3149 				// Failed updates
  2338 				$body[] = "The following {$type}s failed to update:";
  3150 				$messages = array(
       
  3151 					'plugin'      => __( 'The following plugins failed to update:' ),
       
  3152 					'theme'       => __( 'The following themes failed to update:' ),
       
  3153 					'translation' => __( 'The following translations failed to update:' ),
       
  3154 				);
       
  3155 
       
  3156 				$body[] = $messages[ $type ];
  2339 				foreach ( $this->update_results[ $type ] as $item ) {
  3157 				foreach ( $this->update_results[ $type ] as $item ) {
  2340 					if ( ! $item->result || is_wp_error( $item->result ) ) {
  3158 					if ( ! $item->result || is_wp_error( $item->result ) ) {
  2341 						$body[] = ' * FAILED: ' . $item->name;
  3159 						$body[] = ' * ' . sprintf( __( 'FAILED: %s' ), $item->name );
  2342 						$failures++;
  3160 						$failures++;
  2343 					}
  3161 					}
  2344 				}
  3162 				}
  2345 			}
  3163 			}
  2346 			$body[] = '';
  3164 			$body[] = '';
  2347 		}
  3165 		}
  2348 
  3166 
       
  3167 		$site_title = wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES );
  2349 		if ( $failures ) {
  3168 		if ( $failures ) {
       
  3169 			$body[] = trim( __(
       
  3170 "BETA TESTING?
       
  3171 =============
       
  3172 
       
  3173 This debugging email is sent when you are using a development version of WordPress.
       
  3174 
       
  3175 If you think these failures might be due to a bug in WordPress, could you report it?
       
  3176  * Open a thread in the support forums: https://wordpress.org/support/forum/alphabeta
       
  3177  * Or, if you're comfortable writing a bug report: https://core.trac.wordpress.org/
       
  3178 
       
  3179 Thanks! -- The WordPress Team" ) );
  2350 			$body[] = '';
  3180 			$body[] = '';
  2351 			$body[] = 'BETA TESTING?';
  3181 
  2352 			$body[] = '=============';
  3182 			$subject = sprintf( __( '[%s] There were failures during background updates' ), $site_title );
  2353 			$body[] = '';
       
  2354 			$body[] = 'This debugging email is sent when you are using a development version of WordPress.';
       
  2355 			$body[] = '';
       
  2356 			$body[] = 'If you think these failures might be due to a bug in WordPress, could you report it?';
       
  2357 			$body[] = ' * Open a thread in the support forums: http://wordpress.org/support/forum/alphabeta';
       
  2358 			$body[] = " * Or, if you're comfortable writing a bug report: http://core.trac.wordpress.org/";
       
  2359 			$body[] = '';
       
  2360 			$body[] = 'Thanks! -- The WordPress Team';
       
  2361 			$body[] = '';
       
  2362 			$subject = sprintf( '[%s] There were failures during background updates', get_bloginfo( 'name' ) );
       
  2363 		} else {
  3183 		} else {
  2364 			$subject = sprintf( '[%s] Background updates have finished', get_bloginfo( 'name' ) );
  3184 			$subject = sprintf( __( '[%s] Background updates have finished' ), $site_title );
  2365 		}
  3185 		}
  2366 
  3186 
  2367 		$body[] = 'UPDATE LOG';
  3187 		$body[] = trim( __(
  2368 		$body[] = '==========';
  3188 'UPDATE LOG
       
  3189 ==========' ) );
  2369 		$body[] = '';
  3190 		$body[] = '';
  2370 
  3191 
  2371 		foreach ( array( 'core', 'plugin', 'theme', 'translation' ) as $type ) {
  3192 		foreach ( array( 'core', 'plugin', 'theme', 'translation' ) as $type ) {
  2372 			if ( ! isset( $this->update_results[ $type ] ) )
  3193 			if ( ! isset( $this->update_results[ $type ] ) )
  2373 				continue;
  3194 				continue;
  2382 					if ( 'rollback_was_required' === $update->result->get_error_code() )
  3203 					if ( 'rollback_was_required' === $update->result->get_error_code() )
  2383 						$results = (array) $update->result->get_error_data();
  3204 						$results = (array) $update->result->get_error_data();
  2384 					foreach ( $results as $result_type => $result ) {
  3205 					foreach ( $results as $result_type => $result ) {
  2385 						if ( ! is_wp_error( $result ) )
  3206 						if ( ! is_wp_error( $result ) )
  2386 							continue;
  3207 							continue;
  2387 						$body[] = '  ' . ( 'rollback' === $result_type ? 'Rollback ' : '' ) . 'Error: [' . $result->get_error_code() . '] ' . $result->get_error_message();
  3208 
       
  3209 						if ( 'rollback' === $result_type ) {
       
  3210 							/* translators: 1: Error code, 2: Error message. */
       
  3211 							$body[] = '  ' . sprintf( __( 'Rollback Error: [%1$s] %2$s' ), $result->get_error_code(), $result->get_error_message() );
       
  3212 						} else {
       
  3213 							/* translators: 1: Error code, 2: Error message. */
       
  3214 							$body[] = '  ' . sprintf( __( 'Error: [%1$s] %2$s' ), $result->get_error_code(), $result->get_error_message() );
       
  3215 						}
       
  3216 
  2388 						if ( $result->get_error_data() )
  3217 						if ( $result->get_error_data() )
  2389 							$body[] = '         ' . implode( ', ', (array) $result->get_error_data() );
  3218 							$body[] = '         ' . implode( ', ', (array) $result->get_error_data() );
  2390 					}
  3219 					}
  2391 				}
  3220 				}
  2392 				$body[] = '';
  3221 				$body[] = '';
  2393 			}
  3222 			}
  2394 		}
  3223 		}
  2395 
  3224 
  2396 		//echo "<h1>\n$subject\n</h1>\n";
  3225 		$email = array(
  2397 		//echo "<pre>\n" . implode( "\n", $body ) . "\n</pre>";
  3226 			'to'      => get_site_option( 'admin_email' ),
  2398 
  3227 			'subject' => $subject,
  2399 		wp_mail( get_site_option( 'admin_email' ), $subject, implode( "\n", $body ) );
  3228 			'body'    => implode( "\n", $body ),
       
  3229 			'headers' => ''
       
  3230 		);
       
  3231 
       
  3232 		/**
       
  3233 		 * Filter the debug email that can be sent following an automatic
       
  3234 		 * background core update.
       
  3235 		 *
       
  3236 		 * @since 3.8.0
       
  3237 		 *
       
  3238 		 * @param array $email {
       
  3239 		 *     Array of email arguments that will be passed to wp_mail().
       
  3240 		 *
       
  3241 		 *     @type string $to      The email recipient. An array of emails
       
  3242 		 *                           can be returned, as handled by wp_mail().
       
  3243 		 *     @type string $subject Email subject.
       
  3244 		 *     @type string $body    Email message body.
       
  3245 		 *     @type string $headers Any email headers. Default empty.
       
  3246 		 * }
       
  3247 		 * @param int   $failures The number of failures encountered while upgrading.
       
  3248 		 * @param mixed $results  The results of all attempted updates.
       
  3249 		 */
       
  3250 		$email = apply_filters( 'automatic_updates_debug_email', $email, $failures, $this->update_results );
       
  3251 
       
  3252 		wp_mail( $email['to'], wp_specialchars_decode( $email['subject'] ), $email['body'], $email['headers'] );
  2400 	}
  3253 	}
  2401 }
  3254 }