wp/wp-admin/includes/class-wp-upgrader.php
changeset 9 177826044cd9
parent 7 cf61fcea0001
child 16 a86126ab1dd4
equal deleted inserted replaced
8:c7c34916027a 9:177826044cd9
   118 	 *
   118 	 *
   119 	 * @param WP_Upgrader_Skin $skin The upgrader skin to use. Default is a WP_Upgrader_Skin.
   119 	 * @param WP_Upgrader_Skin $skin The upgrader skin to use. Default is a WP_Upgrader_Skin.
   120 	 *                               instance.
   120 	 *                               instance.
   121 	 */
   121 	 */
   122 	public function __construct( $skin = null ) {
   122 	public function __construct( $skin = null ) {
   123 		if ( null == $skin )
   123 		if ( null == $skin ) {
   124 			$this->skin = new WP_Upgrader_Skin();
   124 			$this->skin = new WP_Upgrader_Skin();
   125 		else
   125 		} else {
   126 			$this->skin = $skin;
   126 			$this->skin = $skin;
       
   127 		}
   127 	}
   128 	}
   128 
   129 
   129 	/**
   130 	/**
   130 	 * Initialize the upgrader.
   131 	 * Initialize the upgrader.
   131 	 *
   132 	 *
   133 	 * and also add the generic strings to `WP_Upgrader::$strings`.
   134 	 * and also add the generic strings to `WP_Upgrader::$strings`.
   134 	 *
   135 	 *
   135 	 * @since 2.8.0
   136 	 * @since 2.8.0
   136 	 */
   137 	 */
   137 	public function init() {
   138 	public function init() {
   138 		$this->skin->set_upgrader($this);
   139 		$this->skin->set_upgrader( $this );
   139 		$this->generic_strings();
   140 		$this->generic_strings();
   140 	}
   141 	}
   141 
   142 
   142 	/**
   143 	/**
   143 	 * Add the generic strings to WP_Upgrader::$strings.
   144 	 * Add the generic strings to WP_Upgrader::$strings.
   144 	 *
   145 	 *
   145 	 * @since 2.8.0
   146 	 * @since 2.8.0
   146 	 */
   147 	 */
   147 	public function generic_strings() {
   148 	public function generic_strings() {
   148 		$this->strings['bad_request'] = __('Invalid data provided.');
   149 		$this->strings['bad_request']       = __( 'Invalid data provided.' );
   149 		$this->strings['fs_unavailable'] = __('Could not access filesystem.');
   150 		$this->strings['fs_unavailable']    = __( 'Could not access filesystem.' );
   150 		$this->strings['fs_error'] = __('Filesystem error.');
   151 		$this->strings['fs_error']          = __( 'Filesystem error.' );
   151 		$this->strings['fs_no_root_dir'] = __('Unable to locate WordPress root directory.');
   152 		$this->strings['fs_no_root_dir']    = __( 'Unable to locate WordPress root directory.' );
   152 		$this->strings['fs_no_content_dir'] = __('Unable to locate WordPress content directory (wp-content).');
   153 		$this->strings['fs_no_content_dir'] = __( 'Unable to locate WordPress content directory (wp-content).' );
   153 		$this->strings['fs_no_plugins_dir'] = __('Unable to locate WordPress plugin directory.');
   154 		$this->strings['fs_no_plugins_dir'] = __( 'Unable to locate WordPress plugin directory.' );
   154 		$this->strings['fs_no_themes_dir'] = __('Unable to locate WordPress theme directory.');
   155 		$this->strings['fs_no_themes_dir']  = __( 'Unable to locate WordPress theme directory.' );
   155 		/* translators: %s: directory name */
   156 		/* translators: %s: directory name */
   156 		$this->strings['fs_no_folder'] = __('Unable to locate needed folder (%s).');
   157 		$this->strings['fs_no_folder'] = __( 'Unable to locate needed folder (%s).' );
   157 
   158 
   158 		$this->strings['download_failed'] = __('Download failed.');
   159 		$this->strings['download_failed']      = __( 'Download failed.' );
   159 		$this->strings['installing_package'] = __('Installing the latest version…');
   160 		$this->strings['installing_package']   = __( 'Installing the latest version…' );
   160 		$this->strings['no_files'] = __('The package contains no files.');
   161 		$this->strings['no_files']             = __( 'The package contains no files.' );
   161 		$this->strings['folder_exists'] = __('Destination folder already exists.');
   162 		$this->strings['folder_exists']        = __( 'Destination folder already exists.' );
   162 		$this->strings['mkdir_failed'] = __('Could not create directory.');
   163 		$this->strings['mkdir_failed']         = __( 'Could not create directory.' );
   163 		$this->strings['incompatible_archive'] = __('The package could not be installed.');
   164 		$this->strings['incompatible_archive'] = __( 'The package could not be installed.' );
   164 		$this->strings['files_not_writable'] = __( 'The update cannot be installed because we will be unable to copy some files. This is usually due to inconsistent file permissions.' );
   165 		$this->strings['files_not_writable']   = __( 'The update cannot be installed because we will be unable to copy some files. This is usually due to inconsistent file permissions.' );
   165 
   166 
   166 		$this->strings['maintenance_start'] = __('Enabling Maintenance mode…');
   167 		$this->strings['maintenance_start'] = __( 'Enabling Maintenance mode…' );
   167 		$this->strings['maintenance_end'] = __('Disabling Maintenance mode…');
   168 		$this->strings['maintenance_end']   = __( 'Disabling Maintenance mode…' );
   168 	}
   169 	}
   169 
   170 
   170 	/**
   171 	/**
   171 	 * Connect to the filesystem.
   172 	 * Connect to the filesystem.
   172 	 *
   173 	 *
   173 	 * @since 2.8.0
   174 	 * @since 2.8.0
   174 	 *
   175 	 *
   175 	 * @global WP_Filesystem_Base $wp_filesystem Subclass
   176 	 * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
   176 	 *
   177 	 *
   177 	 * @param array $directories                  Optional. A list of directories. If any of these do
   178 	 * @param array $directories                  Optional. A list of directories. If any of these do
   178 	 *                                            not exist, a WP_Error object will be returned.
   179 	 *                                            not exist, a WP_Error object will be returned.
   179 	 *                                            Default empty array.
   180 	 *                                            Default empty array.
   180 	 * @param bool  $allow_relaxed_file_ownership Whether to allow relaxed file ownership.
   181 	 * @param bool  $allow_relaxed_file_ownership Whether to allow relaxed file ownership.
   188 			return false;
   189 			return false;
   189 		}
   190 		}
   190 
   191 
   191 		if ( ! WP_Filesystem( $credentials, $directories[0], $allow_relaxed_file_ownership ) ) {
   192 		if ( ! WP_Filesystem( $credentials, $directories[0], $allow_relaxed_file_ownership ) ) {
   192 			$error = true;
   193 			$error = true;
   193 			if ( is_object($wp_filesystem) && $wp_filesystem->errors->get_error_code() )
   194 			if ( is_object( $wp_filesystem ) && $wp_filesystem->errors->has_errors() ) {
   194 				$error = $wp_filesystem->errors;
   195 				$error = $wp_filesystem->errors;
       
   196 			}
   195 			// Failed to connect, Error and request again
   197 			// Failed to connect, Error and request again
   196 			$this->skin->request_filesystem_credentials( $error, $directories[0], $allow_relaxed_file_ownership );
   198 			$this->skin->request_filesystem_credentials( $error, $directories[0], $allow_relaxed_file_ownership );
   197 			return false;
   199 			return false;
   198 		}
   200 		}
   199 
   201 
   200 		if ( ! is_object($wp_filesystem) )
   202 		if ( ! is_object( $wp_filesystem ) ) {
   201 			return new WP_Error('fs_unavailable', $this->strings['fs_unavailable'] );
   203 			return new WP_Error( 'fs_unavailable', $this->strings['fs_unavailable'] );
   202 
   204 		}
   203 		if ( is_wp_error($wp_filesystem->errors) && $wp_filesystem->errors->get_error_code() )
   205 
   204 			return new WP_Error('fs_error', $this->strings['fs_error'], $wp_filesystem->errors);
   206 		if ( is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->has_errors() ) {
   205 
   207 			return new WP_Error( 'fs_error', $this->strings['fs_error'], $wp_filesystem->errors );
   206 		foreach ( (array)$directories as $dir ) {
   208 		}
       
   209 
       
   210 		foreach ( (array) $directories as $dir ) {
   207 			switch ( $dir ) {
   211 			switch ( $dir ) {
   208 				case ABSPATH:
   212 				case ABSPATH:
   209 					if ( ! $wp_filesystem->abspath() )
   213 					if ( ! $wp_filesystem->abspath() ) {
   210 						return new WP_Error('fs_no_root_dir', $this->strings['fs_no_root_dir']);
   214 						return new WP_Error( 'fs_no_root_dir', $this->strings['fs_no_root_dir'] );
       
   215 					}
   211 					break;
   216 					break;
   212 				case WP_CONTENT_DIR:
   217 				case WP_CONTENT_DIR:
   213 					if ( ! $wp_filesystem->wp_content_dir() )
   218 					if ( ! $wp_filesystem->wp_content_dir() ) {
   214 						return new WP_Error('fs_no_content_dir', $this->strings['fs_no_content_dir']);
   219 						return new WP_Error( 'fs_no_content_dir', $this->strings['fs_no_content_dir'] );
       
   220 					}
   215 					break;
   221 					break;
   216 				case WP_PLUGIN_DIR:
   222 				case WP_PLUGIN_DIR:
   217 					if ( ! $wp_filesystem->wp_plugins_dir() )
   223 					if ( ! $wp_filesystem->wp_plugins_dir() ) {
   218 						return new WP_Error('fs_no_plugins_dir', $this->strings['fs_no_plugins_dir']);
   224 						return new WP_Error( 'fs_no_plugins_dir', $this->strings['fs_no_plugins_dir'] );
       
   225 					}
   219 					break;
   226 					break;
   220 				case get_theme_root():
   227 				case get_theme_root():
   221 					if ( ! $wp_filesystem->wp_themes_dir() )
   228 					if ( ! $wp_filesystem->wp_themes_dir() ) {
   222 						return new WP_Error('fs_no_themes_dir', $this->strings['fs_no_themes_dir']);
   229 						return new WP_Error( 'fs_no_themes_dir', $this->strings['fs_no_themes_dir'] );
       
   230 					}
   223 					break;
   231 					break;
   224 				default:
   232 				default:
   225 					if ( ! $wp_filesystem->find_folder($dir) )
   233 					if ( ! $wp_filesystem->find_folder( $dir ) ) {
   226 						return new WP_Error( 'fs_no_folder', sprintf( $this->strings['fs_no_folder'], esc_html( basename( $dir ) ) ) );
   234 						return new WP_Error( 'fs_no_folder', sprintf( $this->strings['fs_no_folder'], esc_html( basename( $dir ) ) ) );
       
   235 					}
   227 					break;
   236 					break;
   228 			}
   237 			}
   229 		}
   238 		}
   230 		return true;
   239 		return true;
   231 	} //end fs_connect();
   240 	} //end fs_connect();
   233 	/**
   242 	/**
   234 	 * Download a package.
   243 	 * Download a package.
   235 	 *
   244 	 *
   236 	 * @since 2.8.0
   245 	 * @since 2.8.0
   237 	 *
   246 	 *
   238 	 * @param string $package The URI of the package. If this is the full path to an
   247 	 * @param string $package          The URI of the package. If this is the full path to an
   239 	 *                        existing local file, it will be returned untouched.
   248 	 *                                 existing local file, it will be returned untouched.
       
   249 	 * @param bool   $check_signatures Whether to validate file signatures. Default false.
   240 	 * @return string|WP_Error The full path to the downloaded package file, or a WP_Error object.
   250 	 * @return string|WP_Error The full path to the downloaded package file, or a WP_Error object.
   241 	 */
   251 	 */
   242 	public function download_package( $package ) {
   252 	public function download_package( $package, $check_signatures = false ) {
   243 
   253 
   244 		/**
   254 		/**
   245 		 * Filters whether to return the package.
   255 		 * Filters whether to return the package.
   246 		 *
   256 		 *
   247 		 * @since 3.7.0
   257 		 * @since 3.7.0
   250 		 *                             Default false.
   260 		 *                             Default false.
   251 		 * @param string      $package The package file name.
   261 		 * @param string      $package The package file name.
   252 		 * @param WP_Upgrader $this    The WP_Upgrader instance.
   262 		 * @param WP_Upgrader $this    The WP_Upgrader instance.
   253 		 */
   263 		 */
   254 		$reply = apply_filters( 'upgrader_pre_download', false, $package, $this );
   264 		$reply = apply_filters( 'upgrader_pre_download', false, $package, $this );
   255 		if ( false !== $reply )
   265 		if ( false !== $reply ) {
   256 			return $reply;
   266 			return $reply;
   257 
   267 		}
   258 		if ( ! preg_match('!^(http|https|ftp)://!i', $package) && file_exists($package) ) //Local file or remote?
   268 
       
   269 		if ( ! preg_match( '!^(http|https|ftp)://!i', $package ) && file_exists( $package ) ) { //Local file or remote?
   259 			return $package; //must be a local file..
   270 			return $package; //must be a local file..
   260 
   271 		}
   261 		if ( empty($package) )
   272 
   262 			return new WP_Error('no_package', $this->strings['no_package']);
   273 		if ( empty( $package ) ) {
   263 
   274 			return new WP_Error( 'no_package', $this->strings['no_package'] );
   264 		$this->skin->feedback('downloading_package', $package);
   275 		}
   265 
   276 
   266 		$download_file = download_url($package);
   277 		$this->skin->feedback( 'downloading_package', $package );
   267 
   278 
   268 		if ( is_wp_error($download_file) )
   279 		$download_file = download_url( $package, 300, $check_signatures );
   269 			return new WP_Error('download_failed', $this->strings['download_failed'], $download_file->get_error_message());
   280 
       
   281 		if ( is_wp_error( $download_file ) && ! $download_file->get_error_data( 'softfail-filename' ) ) {
       
   282 			return new WP_Error( 'download_failed', $this->strings['download_failed'], $download_file->get_error_message() );
       
   283 		}
   270 
   284 
   271 		return $download_file;
   285 		return $download_file;
   272 	}
   286 	}
   273 
   287 
   274 	/**
   288 	/**
   275 	 * Unpack a compressed package file.
   289 	 * Unpack a compressed package file.
   276 	 *
   290 	 *
   277 	 * @since 2.8.0
   291 	 * @since 2.8.0
   278 	 *
   292 	 *
   279 	 * @global WP_Filesystem_Base $wp_filesystem Subclass
   293 	 * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
   280 	 *
   294 	 *
   281 	 * @param string $package        Full path to the package file.
   295 	 * @param string $package        Full path to the package file.
   282 	 * @param bool   $delete_package Optional. Whether to delete the package file after attempting
   296 	 * @param bool   $delete_package Optional. Whether to delete the package file after attempting
   283 	 *                               to unpack it. Default true.
   297 	 *                               to unpack it. Default true.
   284 	 * @return string|WP_Error The path to the unpacked contents, or a WP_Error on failure.
   298 	 * @return string|WP_Error The path to the unpacked contents, or a WP_Error on failure.
   285 	 */
   299 	 */
   286 	public function unpack_package( $package, $delete_package = true ) {
   300 	public function unpack_package( $package, $delete_package = true ) {
   287 		global $wp_filesystem;
   301 		global $wp_filesystem;
   288 
   302 
   289 		$this->skin->feedback('unpack_package');
   303 		$this->skin->feedback( 'unpack_package' );
   290 
   304 
   291 		$upgrade_folder = $wp_filesystem->wp_content_dir() . 'upgrade/';
   305 		$upgrade_folder = $wp_filesystem->wp_content_dir() . 'upgrade/';
   292 
   306 
   293 		//Clean up contents of upgrade directory beforehand.
   307 		//Clean up contents of upgrade directory beforehand.
   294 		$upgrade_files = $wp_filesystem->dirlist($upgrade_folder);
   308 		$upgrade_files = $wp_filesystem->dirlist( $upgrade_folder );
   295 		if ( !empty($upgrade_files) ) {
   309 		if ( ! empty( $upgrade_files ) ) {
   296 			foreach ( $upgrade_files as $file )
   310 			foreach ( $upgrade_files as $file ) {
   297 				$wp_filesystem->delete($upgrade_folder . $file['name'], true);
   311 				$wp_filesystem->delete( $upgrade_folder . $file['name'], true );
       
   312 			}
   298 		}
   313 		}
   299 
   314 
   300 		// We need a working directory - Strip off any .tmp or .zip suffixes
   315 		// We need a working directory - Strip off any .tmp or .zip suffixes
   301 		$working_dir = $upgrade_folder . basename( basename( $package, '.tmp' ), '.zip' );
   316 		$working_dir = $upgrade_folder . basename( basename( $package, '.tmp' ), '.zip' );
   302 
   317 
   303 		// Clean up working directory
   318 		// Clean up working directory
   304 		if ( $wp_filesystem->is_dir($working_dir) )
   319 		if ( $wp_filesystem->is_dir( $working_dir ) ) {
   305 			$wp_filesystem->delete($working_dir, true);
   320 			$wp_filesystem->delete( $working_dir, true );
       
   321 		}
   306 
   322 
   307 		// Unzip package to working directory
   323 		// Unzip package to working directory
   308 		$result = unzip_file( $package, $working_dir );
   324 		$result = unzip_file( $package, $working_dir );
   309 
   325 
   310 		// Once extracted, delete the package if required.
   326 		// Once extracted, delete the package if required.
   311 		if ( $delete_package )
   327 		if ( $delete_package ) {
   312 			unlink($package);
   328 			unlink( $package );
   313 
   329 		}
   314 		if ( is_wp_error($result) ) {
   330 
   315 			$wp_filesystem->delete($working_dir, true);
   331 		if ( is_wp_error( $result ) ) {
       
   332 			$wp_filesystem->delete( $working_dir, true );
   316 			if ( 'incompatible_archive' == $result->get_error_code() ) {
   333 			if ( 'incompatible_archive' == $result->get_error_code() ) {
   317 				return new WP_Error( 'incompatible_archive', $this->strings['incompatible_archive'], $result->get_error_data() );
   334 				return new WP_Error( 'incompatible_archive', $this->strings['incompatible_archive'], $result->get_error_data() );
   318 			}
   335 			}
   319 			return $result;
   336 			return $result;
   320 		}
   337 		}
   353 	/**
   370 	/**
   354 	 * Clears the directory where this item is going to be installed into.
   371 	 * Clears the directory where this item is going to be installed into.
   355 	 *
   372 	 *
   356 	 * @since 4.3.0
   373 	 * @since 4.3.0
   357 	 *
   374 	 *
   358 	 * @global WP_Filesystem_Base $wp_filesystem Subclass
   375 	 * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
   359 	 *
   376 	 *
   360 	 * @param string $remote_destination The location on the remote filesystem to be cleared
   377 	 * @param string $remote_destination The location on the remote filesystem to be cleared
   361 	 * @return bool|WP_Error True upon success, WP_Error on failure.
   378 	 * @return bool|WP_Error True upon success, WP_Error on failure.
   362 	 */
   379 	 */
   363 	public function clear_destination( $remote_destination ) {
   380 	public function clear_destination( $remote_destination ) {
   405 	 * a destination directory. Optionally removes the source. It can also optionally
   422 	 * a destination directory. Optionally removes the source. It can also optionally
   406 	 * clear out the destination folder if it already exists.
   423 	 * clear out the destination folder if it already exists.
   407 	 *
   424 	 *
   408 	 * @since 2.8.0
   425 	 * @since 2.8.0
   409 	 *
   426 	 *
   410 	 * @global WP_Filesystem_Base $wp_filesystem Subclass
   427 	 * @global WP_Filesystem_Base $wp_filesystem        WordPress filesystem subclass.
   411 	 * @global array              $wp_theme_directories
   428 	 * @global array              $wp_theme_directories
   412 	 *
   429 	 *
   413 	 * @param array|string $args {
   430 	 * @param array|string $args {
   414 	 *     Optional. Array or string of arguments for installing a package. Default empty array.
   431 	 *     Optional. Array or string of arguments for installing a package. Default empty array.
   415 	 *
   432 	 *
   430 	 */
   447 	 */
   431 	public function install_package( $args = array() ) {
   448 	public function install_package( $args = array() ) {
   432 		global $wp_filesystem, $wp_theme_directories;
   449 		global $wp_filesystem, $wp_theme_directories;
   433 
   450 
   434 		$defaults = array(
   451 		$defaults = array(
   435 			'source' => '', // Please always pass this
   452 			'source'                      => '', // Please always pass this
   436 			'destination' => '', // and this
   453 			'destination'                 => '', // and this
   437 			'clear_destination' => false,
   454 			'clear_destination'           => false,
   438 			'clear_working' => false,
   455 			'clear_working'               => false,
   439 			'abort_if_destination_exists' => true,
   456 			'abort_if_destination_exists' => true,
   440 			'hook_extra' => array()
   457 			'hook_extra'                  => array(),
   441 		);
   458 		);
   442 
   459 
   443 		$args = wp_parse_args($args, $defaults);
   460 		$args = wp_parse_args( $args, $defaults );
   444 
   461 
   445 		// These were previously extract()'d.
   462 		// These were previously extract()'d.
   446 		$source = $args['source'];
   463 		$source            = $args['source'];
   447 		$destination = $args['destination'];
   464 		$destination       = $args['destination'];
   448 		$clear_destination = $args['clear_destination'];
   465 		$clear_destination = $args['clear_destination'];
   449 
   466 
   450 		@set_time_limit( 300 );
   467 		@set_time_limit( 300 );
   451 
   468 
   452 		if ( empty( $source ) || empty( $destination ) ) {
   469 		if ( empty( $source ) || empty( $destination ) ) {
   471 		if ( is_wp_error( $res ) ) {
   488 		if ( is_wp_error( $res ) ) {
   472 			return $res;
   489 			return $res;
   473 		}
   490 		}
   474 
   491 
   475 		//Retain the Original source and destinations
   492 		//Retain the Original source and destinations
   476 		$remote_source = $args['source'];
   493 		$remote_source     = $args['source'];
   477 		$local_destination = $destination;
   494 		$local_destination = $destination;
   478 
   495 
   479 		$source_files = array_keys( $wp_filesystem->dirlist( $remote_source ) );
   496 		$source_files       = array_keys( $wp_filesystem->dirlist( $remote_source ) );
   480 		$remote_destination = $wp_filesystem->find_folder( $local_destination );
   497 		$remote_destination = $wp_filesystem->find_folder( $local_destination );
   481 
   498 
   482 		//Locate which directory to copy to the new folder, This is based on the actual folder holding the files.
   499 		//Locate which directory to copy to the new folder, This is based on the actual folder holding the files.
   483 		if ( 1 == count( $source_files ) && $wp_filesystem->is_dir( trailingslashit( $args['source'] ) . $source_files[0] . '/' ) ) { //Only one folder? Then we want its contents.
   500 		if ( 1 == count( $source_files ) && $wp_filesystem->is_dir( trailingslashit( $args['source'] ) . $source_files[0] . '/' ) ) { //Only one folder? Then we want its contents.
   484 			$source = trailingslashit( $args['source'] ) . trailingslashit( $source_files[0] );
   501 			$source = trailingslashit( $args['source'] ) . trailingslashit( $source_files[0] );
   523 			$protected_directories = array_merge( $protected_directories, $wp_theme_directories );
   540 			$protected_directories = array_merge( $protected_directories, $wp_theme_directories );
   524 		}
   541 		}
   525 
   542 
   526 		if ( in_array( $destination, $protected_directories ) ) {
   543 		if ( in_array( $destination, $protected_directories ) ) {
   527 			$remote_destination = trailingslashit( $remote_destination ) . trailingslashit( basename( $source ) );
   544 			$remote_destination = trailingslashit( $remote_destination ) . trailingslashit( basename( $source ) );
   528 			$destination = trailingslashit( $destination ) . trailingslashit( basename( $source ) );
   545 			$destination        = trailingslashit( $destination ) . trailingslashit( basename( $source ) );
   529 		}
   546 		}
   530 
   547 
   531 		if ( $clear_destination ) {
   548 		if ( $clear_destination ) {
   532 			// We're going to clear the destination if there's something there.
   549 			// We're going to clear the destination if there's something there.
   533 			$this->skin->feedback('remove_old');
   550 			$this->skin->feedback( 'remove_old' );
   534 
   551 
   535 			$removed = $this->clear_destination( $remote_destination );
   552 			$removed = $this->clear_destination( $remote_destination );
   536 
   553 
   537 			/**
   554 			/**
   538 			 * Filters whether the upgrader cleared the destination.
   555 			 * Filters whether the upgrader cleared the destination.
   547 			$removed = apply_filters( 'upgrader_clear_destination', $removed, $local_destination, $remote_destination, $args['hook_extra'] );
   564 			$removed = apply_filters( 'upgrader_clear_destination', $removed, $local_destination, $remote_destination, $args['hook_extra'] );
   548 
   565 
   549 			if ( is_wp_error( $removed ) ) {
   566 			if ( is_wp_error( $removed ) ) {
   550 				return $removed;
   567 				return $removed;
   551 			}
   568 			}
   552 		} elseif ( $args['abort_if_destination_exists'] && $wp_filesystem->exists($remote_destination) ) {
   569 		} elseif ( $args['abort_if_destination_exists'] && $wp_filesystem->exists( $remote_destination ) ) {
   553 			//If we're not clearing the destination folder and something exists there already, Bail.
   570 			//If we're not clearing the destination folder and something exists there already, Bail.
   554 			//But first check to see if there are actually any files in the folder.
   571 			//But first check to see if there are actually any files in the folder.
   555 			$_files = $wp_filesystem->dirlist($remote_destination);
   572 			$_files = $wp_filesystem->dirlist( $remote_destination );
   556 			if ( ! empty($_files) ) {
   573 			if ( ! empty( $_files ) ) {
   557 				$wp_filesystem->delete($remote_source, true); //Clear out the source files.
   574 				$wp_filesystem->delete( $remote_source, true ); //Clear out the source files.
   558 				return new WP_Error('folder_exists', $this->strings['folder_exists'], $remote_destination );
   575 				return new WP_Error( 'folder_exists', $this->strings['folder_exists'], $remote_destination );
   559 			}
   576 			}
   560 		}
   577 		}
   561 
   578 
   562 		//Create destination if needed
   579 		//Create destination if needed
   563 		if ( ! $wp_filesystem->exists( $remote_destination ) ) {
   580 		if ( ! $wp_filesystem->exists( $remote_destination ) ) {
   564 			if ( ! $wp_filesystem->mkdir( $remote_destination, FS_CHMOD_DIR ) ) {
   581 			if ( ! $wp_filesystem->mkdir( $remote_destination, FS_CHMOD_DIR ) ) {
   565 				return new WP_Error( 'mkdir_failed_destination', $this->strings['mkdir_failed'], $remote_destination );
   582 				return new WP_Error( 'mkdir_failed_destination', $this->strings['mkdir_failed'], $remote_destination );
   566 			}
   583 			}
   567 		}
   584 		}
   568 		// Copy new version of item into place.
   585 		// Copy new version of item into place.
   569 		$result = copy_dir($source, $remote_destination);
   586 		$result = copy_dir( $source, $remote_destination );
   570 		if ( is_wp_error($result) ) {
   587 		if ( is_wp_error( $result ) ) {
   571 			if ( $args['clear_working'] ) {
   588 			if ( $args['clear_working'] ) {
   572 				$wp_filesystem->delete( $remote_source, true );
   589 				$wp_filesystem->delete( $remote_source, true );
   573 			}
   590 			}
   574 			return $result;
   591 			return $result;
   575 		}
   592 		}
   577 		//Clear the Working folder?
   594 		//Clear the Working folder?
   578 		if ( $args['clear_working'] ) {
   595 		if ( $args['clear_working'] ) {
   579 			$wp_filesystem->delete( $remote_source, true );
   596 			$wp_filesystem->delete( $remote_source, true );
   580 		}
   597 		}
   581 
   598 
   582 		$destination_name = basename( str_replace($local_destination, '', $destination) );
   599 		$destination_name = basename( str_replace( $local_destination, '', $destination ) );
   583 		if ( '.' == $destination_name ) {
   600 		if ( '.' == $destination_name ) {
   584 			$destination_name = '';
   601 			$destination_name = '';
   585 		}
   602 		}
   586 
   603 
   587 		$this->result = compact( 'source', 'source_files', 'destination', 'destination_name', 'local_destination', 'remote_destination', 'clear_destination' );
   604 		$this->result = compact( 'source', 'source_files', 'destination', 'destination_name', 'local_destination', 'remote_destination', 'clear_destination' );
   595 		 * @param array $hook_extra Extra arguments passed to hooked filters.
   612 		 * @param array $hook_extra Extra arguments passed to hooked filters.
   596 		 * @param array $result     Installation result data.
   613 		 * @param array $result     Installation result data.
   597 		 */
   614 		 */
   598 		$res = apply_filters( 'upgrader_post_install', true, $args['hook_extra'], $this->result );
   615 		$res = apply_filters( 'upgrader_post_install', true, $args['hook_extra'], $this->result );
   599 
   616 
   600 		if ( is_wp_error($res) ) {
   617 		if ( is_wp_error( $res ) ) {
   601 			$this->result = $res;
   618 			$this->result = $res;
   602 			return $res;
   619 			return $res;
   603 		}
   620 		}
   604 
   621 
   605 		//Bombard the calling function will all the info which we've just used.
   622 		//Bombard the calling function will all the info which we've just used.
   640 	 *                              or false if unable to connect to the filesystem.
   657 	 *                              or false if unable to connect to the filesystem.
   641 	 */
   658 	 */
   642 	public function run( $options ) {
   659 	public function run( $options ) {
   643 
   660 
   644 		$defaults = array(
   661 		$defaults = array(
   645 			'package' => '', // Please always pass this.
   662 			'package'                     => '', // Please always pass this.
   646 			'destination' => '', // And this
   663 			'destination'                 => '', // And this
   647 			'clear_destination' => false,
   664 			'clear_destination'           => false,
   648 			'abort_if_destination_exists' => true, // Abort if the Destination directory exists, Pass clear_destination as false please
   665 			'abort_if_destination_exists' => true, // Abort if the Destination directory exists, Pass clear_destination as false please
   649 			'clear_working' => true,
   666 			'clear_working'               => true,
   650 			'is_multi' => false,
   667 			'is_multi'                    => false,
   651 			'hook_extra' => array() // Pass any extra $hook_extra args here, this will be passed to any hooked filters.
   668 			'hook_extra'                  => array(), // Pass any extra $hook_extra args here, this will be passed to any hooked filters.
   652 		);
   669 		);
   653 
   670 
   654 		$options = wp_parse_args( $options, $defaults );
   671 		$options = wp_parse_args( $options, $defaults );
   655 
   672 
   656 		/**
   673 		/**
   673 		 *         Extra hook arguments.
   690 		 *         Extra hook arguments.
   674 		 *
   691 		 *
   675 		 *         @type string $action               Type of action. Default 'update'.
   692 		 *         @type string $action               Type of action. Default 'update'.
   676 		 *         @type string $type                 Type of update process. Accepts 'plugin', 'theme', or 'core'.
   693 		 *         @type string $type                 Type of update process. Accepts 'plugin', 'theme', or 'core'.
   677 		 *         @type bool   $bulk                 Whether the update process is a bulk update. Default true.
   694 		 *         @type bool   $bulk                 Whether the update process is a bulk update. Default true.
   678 		 *         @type string $plugin               The base plugin path from the plugins directory.
   695 		 *         @type string $plugin               Path to the plugin file relative to the plugins directory.
   679 		 *         @type string $theme                The stylesheet or template name of the theme.
   696 		 *         @type string $theme                The stylesheet or template name of the theme.
   680 		 *         @type string $language_update_type The language pack update type. Accepts 'plugin', 'theme',
   697 		 *         @type string $language_update_type The language pack update type. Accepts 'plugin', 'theme',
   681 		 *                                            or 'core'.
   698 		 *                                            or 'core'.
   682 		 *         @type object $language_update      The language pack update offer.
   699 		 *         @type object $language_update      The language pack update offer.
   683 		 *     }
   700 		 *     }
   699 			return false;
   716 			return false;
   700 		}
   717 		}
   701 
   718 
   702 		$this->skin->before();
   719 		$this->skin->before();
   703 
   720 
   704 		if ( is_wp_error($res) ) {
   721 		if ( is_wp_error( $res ) ) {
   705 			$this->skin->error($res);
   722 			$this->skin->error( $res );
   706 			$this->skin->after();
   723 			$this->skin->after();
   707 			if ( ! $options['is_multi'] ) {
   724 			if ( ! $options['is_multi'] ) {
   708 				$this->skin->footer();
   725 				$this->skin->footer();
   709 			}
   726 			}
   710 			return $res;
   727 			return $res;
   712 
   729 
   713 		/*
   730 		/*
   714 		 * Download the package (Note, This just returns the filename
   731 		 * Download the package (Note, This just returns the filename
   715 		 * of the file if the package is a local file)
   732 		 * of the file if the package is a local file)
   716 		 */
   733 		 */
   717 		$download = $this->download_package( $options['package'] );
   734 		$download = $this->download_package( $options['package'], true );
   718 		if ( is_wp_error($download) ) {
   735 
   719 			$this->skin->error($download);
   736 		// Allow for signature soft-fail.
       
   737 		// WARNING: This may be removed in the future.
       
   738 		if ( is_wp_error( $download ) && $download->get_error_data( 'softfail-filename' ) ) {
       
   739 
       
   740 			// Don't output the 'no signature could be found' failure message for now.
       
   741 			if ( 'signature_verification_no_signature' != $download->get_error_code() || WP_DEBUG ) {
       
   742 				// Outout the failure error as a normal feedback, and not as an error:
       
   743 				$this->skin->feedback( $download->get_error_message() );
       
   744 
       
   745 				// Report this failure back to WordPress.org for debugging purposes.
       
   746 				wp_version_check(
       
   747 					array(
       
   748 						'signature_failure_code' => $download->get_error_code(),
       
   749 						'signature_failure_data' => $download->get_error_data(),
       
   750 					)
       
   751 				);
       
   752 			}
       
   753 
       
   754 			// Pretend this error didn't happen.
       
   755 			$download = $download->get_error_data( 'softfail-filename' );
       
   756 		}
       
   757 
       
   758 		if ( is_wp_error( $download ) ) {
       
   759 			$this->skin->error( $download );
   720 			$this->skin->after();
   760 			$this->skin->after();
   721 			if ( ! $options['is_multi'] ) {
   761 			if ( ! $options['is_multi'] ) {
   722 				$this->skin->footer();
   762 				$this->skin->footer();
   723 			}
   763 			}
   724 			return $download;
   764 			return $download;
   726 
   766 
   727 		$delete_package = ( $download != $options['package'] ); // Do not delete a "local" file
   767 		$delete_package = ( $download != $options['package'] ); // Do not delete a "local" file
   728 
   768 
   729 		// Unzips the file into a temporary directory.
   769 		// Unzips the file into a temporary directory.
   730 		$working_dir = $this->unpack_package( $download, $delete_package );
   770 		$working_dir = $this->unpack_package( $download, $delete_package );
   731 		if ( is_wp_error($working_dir) ) {
   771 		if ( is_wp_error( $working_dir ) ) {
   732 			$this->skin->error($working_dir);
   772 			$this->skin->error( $working_dir );
   733 			$this->skin->after();
   773 			$this->skin->after();
   734 			if ( ! $options['is_multi'] ) {
   774 			if ( ! $options['is_multi'] ) {
   735 				$this->skin->footer();
   775 				$this->skin->footer();
   736 			}
   776 			}
   737 			return $working_dir;
   777 			return $working_dir;
   738 		}
   778 		}
   739 
   779 
   740 		// With the given options, this installs it to the destination directory.
   780 		// With the given options, this installs it to the destination directory.
   741 		$result = $this->install_package( array(
   781 		$result = $this->install_package(
   742 			'source' => $working_dir,
   782 			array(
   743 			'destination' => $options['destination'],
   783 				'source'                      => $working_dir,
   744 			'clear_destination' => $options['clear_destination'],
   784 				'destination'                 => $options['destination'],
   745 			'abort_if_destination_exists' => $options['abort_if_destination_exists'],
   785 				'clear_destination'           => $options['clear_destination'],
   746 			'clear_working' => $options['clear_working'],
   786 				'abort_if_destination_exists' => $options['abort_if_destination_exists'],
   747 			'hook_extra' => $options['hook_extra']
   787 				'clear_working'               => $options['clear_working'],
   748 		) );
   788 				'hook_extra'                  => $options['hook_extra'],
   749 
   789 			)
   750 		$this->skin->set_result($result);
   790 		);
   751 		if ( is_wp_error($result) ) {
   791 
   752 			$this->skin->error($result);
   792 		$this->skin->set_result( $result );
   753 			$this->skin->feedback('process_failed');
   793 		if ( is_wp_error( $result ) ) {
       
   794 			$this->skin->error( $result );
       
   795 			$this->skin->feedback( 'process_failed' );
   754 		} else {
   796 		} else {
   755 			// Installation succeeded.
   797 			// Installation succeeded.
   756 			$this->skin->feedback('process_success');
   798 			$this->skin->feedback( 'process_success' );
   757 		}
   799 		}
   758 
   800 
   759 		$this->skin->after();
   801 		$this->skin->after();
   760 
   802 
   761 		if ( ! $options['is_multi'] ) {
   803 		if ( ! $options['is_multi'] ) {
   811 	 */
   853 	 */
   812 	public function maintenance_mode( $enable = false ) {
   854 	public function maintenance_mode( $enable = false ) {
   813 		global $wp_filesystem;
   855 		global $wp_filesystem;
   814 		$file = $wp_filesystem->abspath() . '.maintenance';
   856 		$file = $wp_filesystem->abspath() . '.maintenance';
   815 		if ( $enable ) {
   857 		if ( $enable ) {
   816 			$this->skin->feedback('maintenance_start');
   858 			$this->skin->feedback( 'maintenance_start' );
   817 			// Create maintenance file to signal that we are upgrading
   859 			// Create maintenance file to signal that we are upgrading
   818 			$maintenance_string = '<?php $upgrading = ' . time() . '; ?>';
   860 			$maintenance_string = '<?php $upgrading = ' . time() . '; ?>';
   819 			$wp_filesystem->delete($file);
   861 			$wp_filesystem->delete( $file );
   820 			$wp_filesystem->put_contents($file, $maintenance_string, FS_CHMOD_FILE);
   862 			$wp_filesystem->put_contents( $file, $maintenance_string, FS_CHMOD_FILE );
   821 		} elseif ( ! $enable && $wp_filesystem->exists( $file ) ) {
   863 		} elseif ( ! $enable && $wp_filesystem->exists( $file ) ) {
   822 			$this->skin->feedback('maintenance_end');
   864 			$this->skin->feedback( 'maintenance_end' );
   823 			$wp_filesystem->delete($file);
   865 			$wp_filesystem->delete( $file );
   824 		}
   866 		}
   825 	}
   867 	}
   826 
   868 
   827 	/**
   869 	/**
   828  	 * Creates a lock using WordPress options.
   870 	 * Creates a lock using WordPress options.
   829  	 *
   871 	 *
   830  	 * @since 4.5.0
   872 	 * @since 4.5.0
   831  	 * @static
   873 	 *
   832  	 *
   874 	 * @param string $lock_name       The name of this unique lock.
   833  	 * @param string $lock_name       The name of this unique lock.
   875 	 * @param int    $release_timeout Optional. The duration in seconds to respect an existing lock.
   834  	 * @param int    $release_timeout Optional. The duration in seconds to respect an existing lock.
       
   835 	 *                                Default: 1 hour.
   876 	 *                                Default: 1 hour.
   836  	 * @return bool False if a lock couldn't be created or if the lock is still valid. True otherwise.
   877 	 * @return bool False if a lock couldn't be created or if the lock is still valid. True otherwise.
   837  	 */
   878 	 */
   838 	public static function create_lock( $lock_name, $release_timeout = null ) {
   879 	public static function create_lock( $lock_name, $release_timeout = null ) {
   839 		global $wpdb;
   880 		global $wpdb;
   840 		if ( ! $release_timeout ) {
   881 		if ( ! $release_timeout ) {
   841 			$release_timeout = HOUR_IN_SECONDS;
   882 			$release_timeout = HOUR_IN_SECONDS;
   842 		}
   883 		}
   869 
   910 
   870 		return true;
   911 		return true;
   871 	}
   912 	}
   872 
   913 
   873 	/**
   914 	/**
   874  	 * Releases an upgrader lock.
   915 	 * Releases an upgrader lock.
   875  	 *
   916 	 *
   876  	 * @since 4.5.0
   917 	 * @since 4.5.0
   877  	 * @static
       
   878 	 *
   918 	 *
   879 	 * @see WP_Upgrader::create_lock()
   919 	 * @see WP_Upgrader::create_lock()
   880  	 *
   920 	 *
   881  	 * @param string $lock_name The name of this unique lock.
   921 	 * @param string $lock_name The name of this unique lock.
   882 	 * @return bool True if the lock was successfully released. False on failure.
   922 	 * @return bool True if the lock was successfully released. False on failure.
   883  	 */
   923 	 */
   884 	public static function release_lock( $lock_name ) {
   924 	public static function release_lock( $lock_name ) {
   885 		return delete_option( $lock_name . '.lock' );
   925 		return delete_option( $lock_name . '.lock' );
   886 	}
   926 	}
   887 
   927 
   888 }
   928 }