2016-05-10 07:45:42 -04:00
< ? php
/**
2016-05-10 07:52:28 -04:00
* Upgrade API : Plugin_Upgrader class
2016-05-10 07:45:42 -04:00
*
* @ package WordPress
* @ subpackage Upgrader
2016-05-10 07:52:28 -04:00
* @ since 4.6 . 0
2016-05-10 07:45:42 -04:00
*/
/**
* Core class used for upgrading / installing plugins .
*
* It is designed to upgrade / install plugins from a local zip , remote zip URL ,
* or uploaded zip file .
*
* @ since 2.8 . 0
2016-05-13 16:59:27 -04:00
* @ since 4.6 . 0 Moved to its own file from wp - admin / includes / class - wp - upgrader . php .
2016-05-10 07:45:42 -04:00
*
* @ see WP_Upgrader
*/
class Plugin_Upgrader extends WP_Upgrader {
/**
* Plugin upgrade result .
*
* @ since 2.8 . 0
* @ var array | WP_Error $result
*
* @ see WP_Upgrader :: $result
*/
public $result ;
/**
2017-08-22 07:52:48 -04:00
* Whether a bulk upgrade / installation is being performed .
2016-05-10 07:45:42 -04:00
*
* @ since 2.9 . 0
* @ var bool $bulk
*/
public $bulk = false ;
/**
* Initialize the upgrade strings .
*
* @ since 2.8 . 0
*/
public function upgrade_strings () {
2017-11-30 18:11:00 -05:00
$this -> strings [ 'up_to_date' ] = __ ( 'The plugin is at the latest version.' );
$this -> strings [ 'no_package' ] = __ ( 'Update package not available.' );
2017-10-18 13:15:47 -04:00
/* translators: %s: package URL */
2017-11-30 18:11:00 -05:00
$this -> strings [ 'downloading_package' ] = sprintf ( __ ( 'Downloading update from %s…' ), '<span class="code">%s</span>' );
$this -> strings [ 'unpack_package' ] = __ ( 'Unpacking the update…' );
$this -> strings [ 'remove_old' ] = __ ( 'Removing the old version of the plugin…' );
$this -> strings [ 'remove_old_failed' ] = __ ( 'Could not remove the old plugin.' );
$this -> strings [ 'process_failed' ] = __ ( 'Plugin update failed.' );
$this -> strings [ 'process_success' ] = __ ( 'Plugin updated successfully.' );
$this -> strings [ 'process_bulk_success' ] = __ ( 'Plugins updated successfully.' );
2016-05-10 07:45:42 -04:00
}
/**
2017-08-22 07:52:48 -04:00
* Initialize the installation strings .
2016-05-10 07:45:42 -04:00
*
* @ since 2.8 . 0
*/
public function install_strings () {
2017-11-30 18:11:00 -05:00
$this -> strings [ 'no_package' ] = __ ( 'Installation package not available.' );
2017-10-18 13:15:47 -04:00
/* translators: %s: package URL */
$this -> strings [ 'downloading_package' ] = sprintf ( __ ( 'Downloading installation package from %s…' ), '<span class="code">%s</span>' );
2017-11-30 18:11:00 -05:00
$this -> strings [ 'unpack_package' ] = __ ( 'Unpacking the package…' );
$this -> strings [ 'installing_package' ] = __ ( 'Installing the plugin…' );
$this -> strings [ 'no_files' ] = __ ( 'The plugin contains no files.' );
$this -> strings [ 'process_failed' ] = __ ( 'Plugin installation failed.' );
$this -> strings [ 'process_success' ] = __ ( 'Plugin installed successfully.' );
2016-05-10 07:45:42 -04:00
}
/**
* Install a plugin package .
*
* @ since 2.8 . 0
* @ since 3.7 . 0 The `$args` parameter was added , making clearing the plugin update cache optional .
*
* @ param string $package The full local path or URI of the package .
* @ param array $args {
* Optional . Other arguments for installing a plugin package . Default empty array .
*
* @ type bool $clear_update_cache Whether to clear the plugin updates cache if successful .
* Default true .
* }
2017-08-22 07:52:48 -04:00
* @ return bool | WP_Error True if the installation was successful , false or a WP_Error otherwise .
2016-05-10 07:45:42 -04:00
*/
public function install ( $package , $args = array () ) {
2017-11-30 18:11:00 -05:00
$defaults = array (
2016-05-10 07:45:42 -04:00
'clear_update_cache' => true ,
);
$parsed_args = wp_parse_args ( $args , $defaults );
$this -> init ();
$this -> install_strings ();
2017-11-30 18:11:00 -05:00
add_filter ( 'upgrader_source_selection' , array ( $this , 'check_package' ) );
2016-11-13 04:53:32 -05:00
if ( $parsed_args [ 'clear_update_cache' ] ) {
// Clear cache so wp_update_plugins() knows about the new plugin.
add_action ( 'upgrader_process_complete' , 'wp_clean_plugins_cache' , 9 , 0 );
}
2016-05-10 07:45:42 -04:00
2017-11-30 18:11:00 -05:00
$this -> run (
array (
'package' => $package ,
'destination' => WP_PLUGIN_DIR ,
'clear_destination' => false , // Do not overwrite files.
'clear_working' => true ,
'hook_extra' => array (
'type' => 'plugin' ,
'action' => 'install' ,
),
2016-05-10 07:45:42 -04:00
)
2017-11-30 18:11:00 -05:00
);
2016-05-10 07:45:42 -04:00
remove_action ( 'upgrader_process_complete' , 'wp_clean_plugins_cache' , 9 );
2017-11-30 18:11:00 -05:00
remove_filter ( 'upgrader_source_selection' , array ( $this , 'check_package' ) );
2016-05-10 07:45:42 -04:00
2017-11-30 18:11:00 -05:00
if ( ! $this -> result || is_wp_error ( $this -> result ) ) {
2016-05-10 07:45:42 -04:00
return $this -> result ;
2017-11-30 18:11:00 -05:00
}
2016-05-10 07:45:42 -04:00
// Force refresh of plugin update information
wp_clean_plugins_cache ( $parsed_args [ 'clear_update_cache' ] );
return true ;
}
/**
* Upgrade a plugin .
*
* @ since 2.8 . 0
* @ since 3.7 . 0 The `$args` parameter was added , making clearing the plugin update cache optional .
*
2018-03-05 16:50:31 -05:00
* @ param string $plugin Path to the plugin file relative to the plugins directory .
2016-05-10 07:45:42 -04:00
* @ param array $args {
* Optional . Other arguments for upgrading a plugin package . Default empty array .
*
* @ type bool $clear_update_cache Whether to clear the plugin updates cache if successful .
* Default true .
* }
* @ return bool | WP_Error True if the upgrade was successful , false or a WP_Error object otherwise .
*/
public function upgrade ( $plugin , $args = array () ) {
2017-11-30 18:11:00 -05:00
$defaults = array (
2016-05-10 07:45:42 -04:00
'clear_update_cache' => true ,
);
$parsed_args = wp_parse_args ( $args , $defaults );
$this -> init ();
$this -> upgrade_strings ();
$current = get_site_transient ( 'update_plugins' );
2017-11-30 18:11:00 -05:00
if ( ! isset ( $current -> response [ $plugin ] ) ) {
2016-05-10 07:45:42 -04:00
$this -> skin -> before ();
2017-11-30 18:11:00 -05:00
$this -> skin -> set_result ( false );
$this -> skin -> error ( 'up_to_date' );
2016-05-10 07:45:42 -04:00
$this -> skin -> after ();
return false ;
}
// Get the URL to the zip file
$r = $current -> response [ $plugin ];
2017-11-30 18:11:00 -05:00
add_filter ( 'upgrader_pre_install' , array ( $this , 'deactivate_plugin_before_upgrade' ), 10 , 2 );
add_filter ( 'upgrader_clear_destination' , array ( $this , 'delete_old_plugin' ), 10 , 4 );
2016-05-10 07:45:42 -04:00
//'source_selection' => array($this, 'source_selection'), //there's a trac ticket to move up the directory for zip's which are made a bit differently, useful for non-.org plugins.
2016-11-13 04:53:32 -05:00
if ( $parsed_args [ 'clear_update_cache' ] ) {
// Clear cache so wp_update_plugins() knows about the new plugin.
add_action ( 'upgrader_process_complete' , 'wp_clean_plugins_cache' , 9 , 0 );
}
2016-05-10 07:45:42 -04:00
2017-11-30 18:11:00 -05:00
$this -> run (
array (
'package' => $r -> package ,
'destination' => WP_PLUGIN_DIR ,
'clear_destination' => true ,
'clear_working' => true ,
'hook_extra' => array (
'plugin' => $plugin ,
'type' => 'plugin' ,
'action' => 'update' ,
),
)
);
2016-05-10 07:45:42 -04:00
// Cleanup our hooks, in case something else does a upgrade on this connection.
remove_action ( 'upgrader_process_complete' , 'wp_clean_plugins_cache' , 9 );
2017-11-30 18:11:00 -05:00
remove_filter ( 'upgrader_pre_install' , array ( $this , 'deactivate_plugin_before_upgrade' ) );
remove_filter ( 'upgrader_clear_destination' , array ( $this , 'delete_old_plugin' ) );
2016-05-10 07:45:42 -04:00
2017-11-30 18:11:00 -05:00
if ( ! $this -> result || is_wp_error ( $this -> result ) ) {
2016-05-10 07:45:42 -04:00
return $this -> result ;
2017-11-30 18:11:00 -05:00
}
2016-05-10 07:45:42 -04:00
// Force refresh of plugin update information
wp_clean_plugins_cache ( $parsed_args [ 'clear_update_cache' ] );
return true ;
}
/**
* Bulk upgrade several plugins at once .
*
* @ since 2.8 . 0
* @ since 3.7 . 0 The `$args` parameter was added , making clearing the plugin update cache optional .
*
2018-03-05 16:50:31 -05:00
* @ param string [] $plugins Array of paths to plugin files relative to the plugins directory .
* @ param array $args {
* Optional . Other arguments for upgrading several plugins at once .
2016-05-10 07:45:42 -04:00
*
2018-03-05 16:50:31 -05:00
* @ type bool $clear_update_cache Whether to clear the plugin updates cache if successful . Default true .
2016-05-10 07:45:42 -04:00
* }
* @ return array | false An array of results indexed by plugin file , or false if unable to connect to the filesystem .
*/
public function bulk_upgrade ( $plugins , $args = array () ) {
2017-11-30 18:11:00 -05:00
$defaults = array (
2016-05-10 07:45:42 -04:00
'clear_update_cache' => true ,
);
$parsed_args = wp_parse_args ( $args , $defaults );
$this -> init ();
$this -> bulk = true ;
$this -> upgrade_strings ();
$current = get_site_transient ( 'update_plugins' );
2017-11-30 18:11:00 -05:00
add_filter ( 'upgrader_clear_destination' , array ( $this , 'delete_old_plugin' ), 10 , 4 );
2016-05-10 07:45:42 -04:00
$this -> skin -> header ();
// Connect to the Filesystem first.
2017-11-30 18:11:00 -05:00
$res = $this -> fs_connect ( array ( WP_CONTENT_DIR , WP_PLUGIN_DIR ) );
2016-05-10 07:45:42 -04:00
if ( ! $res ) {
$this -> skin -> footer ();
return false ;
}
$this -> skin -> bulk_header ();
/*
* Only start maintenance mode if :
* - running Multisite and there are one or more plugins specified , OR
* - a plugin with an update available is currently active .
* @ TODO : For multisite , maintenance mode should only kick in for individual sites if at all possible .
*/
$maintenance = ( is_multisite () && ! empty ( $plugins ) );
2017-11-30 18:11:00 -05:00
foreach ( $plugins as $plugin ) {
$maintenance = $maintenance || ( is_plugin_active ( $plugin ) && isset ( $current -> response [ $plugin ] ) );
}
if ( $maintenance ) {
$this -> maintenance_mode ( true );
}
2016-05-10 07:45:42 -04:00
$results = array ();
2017-11-30 18:11:00 -05:00
$this -> update_count = count ( $plugins );
2016-05-10 07:45:42 -04:00
$this -> update_current = 0 ;
foreach ( $plugins as $plugin ) {
$this -> update_current ++ ;
2017-11-30 18:11:00 -05:00
$this -> skin -> plugin_info = get_plugin_data ( WP_PLUGIN_DIR . '/' . $plugin , false , true );
2016-05-10 07:45:42 -04:00
2017-11-30 18:11:00 -05:00
if ( ! isset ( $current -> response [ $plugin ] ) ) {
$this -> skin -> set_result ( 'up_to_date' );
2016-05-10 07:45:42 -04:00
$this -> skin -> before ();
2017-11-30 18:11:00 -05:00
$this -> skin -> feedback ( 'up_to_date' );
2016-05-10 07:45:42 -04:00
$this -> skin -> after ();
2017-11-30 18:11:00 -05:00
$results [ $plugin ] = true ;
2016-05-10 07:45:42 -04:00
continue ;
}
// Get the URL to the zip file.
$r = $current -> response [ $plugin ];
2017-11-30 18:11:00 -05:00
$this -> skin -> plugin_active = is_plugin_active ( $plugin );
$result = $this -> run (
array (
'package' => $r -> package ,
'destination' => WP_PLUGIN_DIR ,
'clear_destination' => true ,
'clear_working' => true ,
'is_multi' => true ,
'hook_extra' => array (
'plugin' => $plugin ,
),
2016-05-10 07:45:42 -04:00
)
2017-11-30 18:11:00 -05:00
);
2016-05-10 07:45:42 -04:00
2017-11-30 18:11:00 -05:00
$results [ $plugin ] = $this -> result ;
2016-05-10 07:45:42 -04:00
// Prevent credentials auth screen from displaying multiple times
2017-11-30 18:11:00 -05:00
if ( false === $result ) {
2016-05-10 07:45:42 -04:00
break ;
2017-11-30 18:11:00 -05:00
}
2016-05-10 07:45:42 -04:00
} //end foreach $plugins
2017-11-30 18:11:00 -05:00
$this -> maintenance_mode ( false );
2016-05-10 07:45:42 -04:00
2016-11-13 04:53:32 -05:00
// Force refresh of plugin update information.
wp_clean_plugins_cache ( $parsed_args [ 'clear_update_cache' ] );
2016-05-10 08:20:27 -04:00
/** This action is documented in wp-admin/includes/class-wp-upgrader.php */
2018-03-05 16:44:30 -05:00
do_action ( 'upgrader_process_complete' , $this , array (
'action' => 'update' ,
'type' => 'plugin' ,
'bulk' => true ,
'plugins' => $plugins ,
) );
2016-05-10 07:45:42 -04:00
$this -> skin -> bulk_footer ();
$this -> skin -> footer ();
// Cleanup our hooks, in case something else does a upgrade on this connection.
2017-11-30 18:11:00 -05:00
remove_filter ( 'upgrader_clear_destination' , array ( $this , 'delete_old_plugin' ) );
2016-05-10 07:45:42 -04:00
return $results ;
}
/**
* Check a source package to be sure it contains a plugin .
*
* This function is added to the { @ see 'upgrader_source_selection' } filter by
* Plugin_Upgrader :: install () .
*
* @ since 3.3 . 0
*
2018-03-04 16:58:30 -05:00
* @ global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass .
2016-05-10 07:45:42 -04:00
*
* @ param string $source The path to the downloaded package source .
* @ return string | WP_Error The source as passed , or a WP_Error object
* if no plugins were found .
*/
2017-11-30 18:11:00 -05:00
public function check_package ( $source ) {
2016-05-10 07:45:42 -04:00
global $wp_filesystem ;
2017-11-30 18:11:00 -05:00
if ( is_wp_error ( $source ) ) {
2016-05-10 07:45:42 -04:00
return $source ;
2017-11-30 18:11:00 -05:00
}
2016-05-10 07:45:42 -04:00
2017-11-30 18:11:00 -05:00
$working_directory = str_replace ( $wp_filesystem -> wp_content_dir (), trailingslashit ( WP_CONTENT_DIR ), $source );
if ( ! is_dir ( $working_directory ) ) { // Sanity check, if the above fails, let's not prevent installation.
2016-05-10 07:45:42 -04:00
return $source ;
2017-11-30 18:11:00 -05:00
}
2016-05-10 07:45:42 -04:00
// Check the folder contains at least 1 valid plugin.
$plugins_found = false ;
2017-11-30 18:11:00 -05:00
$files = glob ( $working_directory . '*.php' );
2016-05-10 07:45:42 -04:00
if ( $files ) {
foreach ( $files as $file ) {
$info = get_plugin_data ( $file , false , false );
if ( ! empty ( $info [ 'Name' ] ) ) {
$plugins_found = true ;
break ;
}
}
}
2017-11-30 18:11:00 -05:00
if ( ! $plugins_found ) {
2016-05-10 07:45:42 -04:00
return new WP_Error ( 'incompatible_archive_no_plugins' , $this -> strings [ 'incompatible_archive' ], __ ( 'No valid plugins were found.' ) );
2017-11-30 18:11:00 -05:00
}
2016-05-10 07:45:42 -04:00
return $source ;
}
/**
* Retrieve the path to the file that contains the plugin info .
*
* This isn ' t used internally in the class , but is called by the skins .
*
* @ since 2.8 . 0
*
* @ return string | false The full path to the main plugin file , or false .
*/
public function plugin_info () {
2017-11-30 18:11:00 -05:00
if ( ! is_array ( $this -> result ) ) {
2016-05-10 07:45:42 -04:00
return false ;
2017-11-30 18:11:00 -05:00
}
if ( empty ( $this -> result [ 'destination_name' ] ) ) {
2016-05-10 07:45:42 -04:00
return false ;
2017-11-30 18:11:00 -05:00
}
2016-05-10 07:45:42 -04:00
2017-11-30 18:11:00 -05:00
$plugin = get_plugins ( '/' . $this -> result [ 'destination_name' ] ); //Ensure to pass with leading slash
if ( empty ( $plugin ) ) {
2016-05-10 07:45:42 -04:00
return false ;
2017-11-30 18:11:00 -05:00
}
2016-05-10 07:45:42 -04:00
2017-11-30 18:11:00 -05:00
$pluginfiles = array_keys ( $plugin ); //Assume the requested plugin is the first in the list
2016-05-10 07:45:42 -04:00
return $this -> result [ 'destination_name' ] . '/' . $pluginfiles [ 0 ];
}
/**
* Deactivates a plugin before it is upgraded .
*
* Hooked to the { @ see 'upgrader_pre_install' } filter by Plugin_Upgrader :: upgrade () .
*
* @ since 2.8 . 0
* @ since 4.1 . 0 Added a return value .
*
* @ param bool | WP_Error $return Upgrade offer return .
* @ param array $plugin Plugin package arguments .
* @ return bool | WP_Error The passed in $return param or WP_Error .
*/
2017-11-30 18:11:00 -05:00
public function deactivate_plugin_before_upgrade ( $return , $plugin ) {
2016-05-10 07:45:42 -04:00
2017-11-30 18:11:00 -05:00
if ( is_wp_error ( $return ) ) { //Bypass.
2016-05-10 07:45:42 -04:00
return $return ;
2017-11-30 18:11:00 -05:00
}
2016-05-10 07:45:42 -04:00
// When in cron (background updates) don't deactivate the plugin, as we require a browser to reactivate it
2017-11-30 18:11:00 -05:00
if ( wp_doing_cron () ) {
2016-05-10 07:45:42 -04:00
return $return ;
2017-11-30 18:11:00 -05:00
}
2016-05-10 07:45:42 -04:00
2017-11-30 18:11:00 -05:00
$plugin = isset ( $plugin [ 'plugin' ] ) ? $plugin [ 'plugin' ] : '' ;
if ( empty ( $plugin ) ) {
return new WP_Error ( 'bad_request' , $this -> strings [ 'bad_request' ] );
}
2016-05-10 07:45:42 -04:00
2017-11-30 18:11:00 -05:00
if ( is_plugin_active ( $plugin ) ) {
2016-05-10 07:45:42 -04:00
//Deactivate the plugin silently, Prevent deactivation hooks from running.
2017-11-30 18:11:00 -05:00
deactivate_plugins ( $plugin , true );
2016-05-10 07:45:42 -04:00
}
return $return ;
}
/**
* Delete the old plugin during an upgrade .
*
* Hooked to the { @ see 'upgrader_clear_destination' } filter by
* Plugin_Upgrader :: upgrade () and Plugin_Upgrader :: bulk_upgrade () .
*
* @ since 2.8 . 0
*
2018-03-04 16:58:30 -05:00
* @ global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass .
2017-11-30 18:11:00 -05:00
*
2016-05-10 07:45:42 -04:00
* @ param bool | WP_Error $removed
* @ param string $local_destination
* @ param string $remote_destination
* @ param array $plugin
* @ return WP_Error | bool
*/
2017-11-30 18:11:00 -05:00
public function delete_old_plugin ( $removed , $local_destination , $remote_destination , $plugin ) {
2016-05-10 07:45:42 -04:00
global $wp_filesystem ;
2017-11-30 18:11:00 -05:00
if ( is_wp_error ( $removed ) ) {
2016-05-10 07:45:42 -04:00
return $removed ; //Pass errors through.
2017-11-30 18:11:00 -05:00
}
2016-05-10 07:45:42 -04:00
2017-11-30 18:11:00 -05:00
$plugin = isset ( $plugin [ 'plugin' ] ) ? $plugin [ 'plugin' ] : '' ;
if ( empty ( $plugin ) ) {
return new WP_Error ( 'bad_request' , $this -> strings [ 'bad_request' ] );
}
2016-05-10 07:45:42 -04:00
2017-11-30 18:11:00 -05:00
$plugins_dir = $wp_filesystem -> wp_plugins_dir ();
$this_plugin_dir = trailingslashit ( dirname ( $plugins_dir . $plugin ) );
2016-05-10 07:45:42 -04:00
2017-11-30 18:11:00 -05:00
if ( ! $wp_filesystem -> exists ( $this_plugin_dir ) ) { //If it's already vanished.
2016-05-10 07:45:42 -04:00
return $removed ;
2017-11-30 18:11:00 -05:00
}
2016-05-10 07:45:42 -04:00
// If plugin is in its own directory, recursively delete the directory.
2017-11-30 18:11:00 -05:00
if ( strpos ( $plugin , '/' ) && $this_plugin_dir != $plugins_dir ) { //base check on if plugin includes directory separator AND that it's not the root plugin folder
$deleted = $wp_filesystem -> delete ( $this_plugin_dir , true );
} else {
$deleted = $wp_filesystem -> delete ( $plugins_dir . $plugin );
}
2016-05-10 07:45:42 -04:00
2017-11-30 18:11:00 -05:00
if ( ! $deleted ) {
return new WP_Error ( 'remove_old_failed' , $this -> strings [ 'remove_old_failed' ] );
}
2016-05-10 07:45:42 -04:00
return true ;
}
}