Upgrade/install: Allow plugin and theme updates from a uploaded .zip file.

Props mariovalney, cyberhobo, imath, shaunandrews, mariovalney, earnjam, desrosj, dd32, folletto, swissspidy, melchoyce, pento, joshuawold, psykro, clorith, ahortin, galbaras, pingram3541, joyously, doobeedoo, karmatosed, poena, whyisjake, earnjam, sergeybiryukov, audrasjb, azaozz.

Fixes #9757.
Built from https://develop.svn.wordpress.org/trunk@48390


git-svn-id: http://core.svn.wordpress.org/trunk@48159 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
Andrew Ozz 2020-07-07 17:49:05 +00:00
parent bc15db7b97
commit 49bbff551b
14 changed files with 646 additions and 59 deletions

View File

@ -985,10 +985,15 @@ body.folded .theme-browser ~ .theme-overlay .theme-wrap {
16.2 - Install Themes
------------------------------------------------------------------------------*/
.update-php .wrap {
max-width: 40rem;
}
/* Already installed theme */
.theme-browser .theme .theme-installed {
background: #0073aa;
}
.theme-browser .theme .notice-success p:before {
color: #79ba49;
content: "\f147";
@ -1031,10 +1036,7 @@ body.folded .theme-browser ~ .theme-overlay .theme-wrap {
overflow: hidden;
position: relative;
top: 10px;
}
.upload-plugin-wrap {
display: none;
text-align: center;
}
.show-upload-view .upload-theme,
@ -1050,12 +1052,16 @@ body.folded .theme-browser ~ .theme-overlay .theme-wrap {
border: 1px solid #ccd0d4;
padding: 30px;
margin: 30px auto;
max-width: 380px;
display: flex;
display: inline-flex;
justify-content: space-between;
align-items: center;
}
.upload-theme .wp-upload-form input[type="file"],
.upload-plugin .wp-upload-form input[type="file"] {
margin-left: 10px;
}
.upload-theme .install-help,
.upload-plugin .install-help {
color: #555d66; /* #f1f1f1 background */
@ -1094,7 +1100,6 @@ p.no-themes-local {
.upload-theme .install-help {
font-size: 15px;
padding: 20px 0 0;
text-align: right;
}
}
@ -1117,6 +1122,43 @@ p.no-themes-local {
line-height: 1.9;
}
.update-from-upload-comparison {
border-top: 1px solid #ddd;
border-bottom: 1px solid #ddd;
text-align: right;
margin: 1rem 0 1.4rem;
border-collapse: collapse;
width: 100%;
}
.update-from-upload-comparison tr:last-child td {
height: 1.4rem;
vertical-align: top;
}
.update-from-upload-comparison tr:first-child th {
font-weight: bold;
height: 1.4rem;
vertical-align: bottom;
}
.update-from-upload-comparison td.name-label {
text-align: left;
}
.update-from-upload-comparison td,
.update-from-upload-comparison th {
padding: 0.4rem 1.4rem;
}
.update-from-upload-comparison td.warning {
color: #a00;
}
.update-from-upload-actions {
margin-top: 1.4rem;
}
/*------------------------------------------------------------------------------
16.3 - Custom Header Screen
------------------------------------------------------------------------------*/

File diff suppressed because one or more lines are too long

View File

@ -984,10 +984,15 @@ body.folded .theme-browser ~ .theme-overlay .theme-wrap {
16.2 - Install Themes
------------------------------------------------------------------------------*/
.update-php .wrap {
max-width: 40rem;
}
/* Already installed theme */
.theme-browser .theme .theme-installed {
background: #0073aa;
}
.theme-browser .theme .notice-success p:before {
color: #79ba49;
content: "\f147";
@ -1030,10 +1035,7 @@ body.folded .theme-browser ~ .theme-overlay .theme-wrap {
overflow: hidden;
position: relative;
top: 10px;
}
.upload-plugin-wrap {
display: none;
text-align: center;
}
.show-upload-view .upload-theme,
@ -1049,12 +1051,16 @@ body.folded .theme-browser ~ .theme-overlay .theme-wrap {
border: 1px solid #ccd0d4;
padding: 30px;
margin: 30px auto;
max-width: 380px;
display: flex;
display: inline-flex;
justify-content: space-between;
align-items: center;
}
.upload-theme .wp-upload-form input[type="file"],
.upload-plugin .wp-upload-form input[type="file"] {
margin-right: 10px;
}
.upload-theme .install-help,
.upload-plugin .install-help {
color: #555d66; /* #f1f1f1 background */
@ -1093,7 +1099,6 @@ p.no-themes-local {
.upload-theme .install-help {
font-size: 15px;
padding: 20px 0 0;
text-align: left;
}
}
@ -1116,6 +1121,43 @@ p.no-themes-local {
line-height: 1.9;
}
.update-from-upload-comparison {
border-top: 1px solid #ddd;
border-bottom: 1px solid #ddd;
text-align: left;
margin: 1rem 0 1.4rem;
border-collapse: collapse;
width: 100%;
}
.update-from-upload-comparison tr:last-child td {
height: 1.4rem;
vertical-align: top;
}
.update-from-upload-comparison tr:first-child th {
font-weight: bold;
height: 1.4rem;
vertical-align: bottom;
}
.update-from-upload-comparison td.name-label {
text-align: right;
}
.update-from-upload-comparison td,
.update-from-upload-comparison th {
padding: 0.4rem 1.4rem;
}
.update-from-upload-comparison td.warning {
color: #a00;
}
.update-from-upload-actions {
margin-top: 1.4rem;
}
/*------------------------------------------------------------------------------
16.3 - Custom Header Screen
------------------------------------------------------------------------------*/

File diff suppressed because one or more lines are too long

View File

@ -18,22 +18,29 @@
class Plugin_Installer_Skin extends WP_Upgrader_Skin {
public $api;
public $type;
public $url;
public $overwrite;
private $is_downgrading = false;
/**
* @param array $args
*/
public function __construct( $args = array() ) {
$defaults = array(
'type' => 'web',
'url' => '',
'plugin' => '',
'nonce' => '',
'title' => '',
'type' => 'web',
'url' => '',
'plugin' => '',
'nonce' => '',
'title' => '',
'overwrite' => '',
);
$args = wp_parse_args( $args, $defaults );
$this->type = $args['type'];
$this->api = isset( $args['api'] ) ? $args['api'] : array();
$this->type = $args['type'];
$this->url = $args['url'];
$this->api = isset( $args['api'] ) ? $args['api'] : array();
$this->overwrite = $args['overwrite'];
parent::__construct( $args );
}
@ -43,17 +50,41 @@ class Plugin_Installer_Skin extends WP_Upgrader_Skin {
public function before() {
if ( ! empty( $this->api ) ) {
$this->upgrader->strings['process_success'] = sprintf(
/* translators: 1: Plugin name, 2: Plugin version. */
__( 'Successfully installed the plugin <strong>%1$s %2$s</strong>.' ),
$this->upgrader->strings['process_success_specific'],
$this->api->name,
$this->api->version
);
}
}
/**
* Hides the `process_failed` error when updating a plugin by uploading a zip file.
*
* @since 5.5.0
*
* @param $wp_error WP_Error.
* @return bool
*/
public function hide_process_failed( $wp_error ) {
if (
'upload' === $this->type &&
'' === $this->overwrite &&
$wp_error->get_error_code() === 'folder_exists'
) {
return true;
}
return false;
}
/**
*/
public function after() {
// Check if the plugin can be overwritten and output the HTML.
if ( $this->do_overwrite() ) {
return;
}
$plugin_file = $this->upgrader->plugin_info();
$install_actions = array();
@ -117,7 +148,7 @@ class Plugin_Installer_Skin extends WP_Upgrader_Skin {
if ( ! $this->result || is_wp_error( $this->result ) ) {
unset( $install_actions['activate_plugin'], $install_actions['network_activate'] );
} elseif ( ! current_user_can( 'activate_plugin', $plugin_file ) ) {
} elseif ( ! current_user_can( 'activate_plugin', $plugin_file ) || is_plugin_active( $plugin_file ) ) {
unset( $install_actions['activate_plugin'] );
}
@ -138,4 +169,158 @@ class Plugin_Installer_Skin extends WP_Upgrader_Skin {
$this->feedback( implode( ' ', (array) $install_actions ) );
}
}
/**
* Check if the plugin can be overwritten and output the HTML for overwriting a plugin on upload.
*
* @since 5.5.0
*
* @return bool Whether the plugin can be overwritten and HTML was outputted.
*/
private function do_overwrite() {
if ( 'upload' !== $this->type || ! is_wp_error( $this->result ) || 'folder_exists' !== $this->result->get_error_code() ) {
return false;
}
$folder = $this->result->get_error_data( 'folder_exists' );
$folder = ltrim( substr( $folder, strlen( WP_PLUGIN_DIR ) ), '/' );
$current_plugin_data = false;
foreach ( get_plugins() as $plugin => $plugin_data ) {
if ( strrpos( $plugin, $folder ) !== 0 ) {
continue;
}
$current_plugin_data = $plugin_data;
}
if ( empty( $current_plugin_data ) || empty( $this->upgrader->new_plugin_data ) ) {
return false;
}
echo '<h2 class="update-from-upload-heading">' . esc_html( __( 'This plugin is already installed.' ) ) . '</h2>';
$this->is_downgrading = version_compare( $current_plugin_data['Version'], $this->upgrader->new_plugin_data['Version'], '>' );
$rows = array(
'Name' => __( 'Plugin name' ),
'Version' => __( 'Version' ),
'Author' => __( 'Author' ),
'RequiresWP' => __( 'Required WordPress version' ),
'RequiresPHP' => __( 'Required PHP version' ),
);
$table = '<table class="update-from-upload-comparison"><tbody>';
$table .= '<tr><th></th><th>' . esc_html( __( 'Current' ) ) . '</th>';
$table .= '<th>' . esc_html( __( 'Uploaded' ) ) . '</th></tr>';
$is_same_plugin = true; // Let's consider only these rows
foreach ( $rows as $field => $label ) {
$old_value = ! empty( $current_plugin_data[ $field ] ) ? $current_plugin_data[ $field ] : '-';
$new_value = ! empty( $this->upgrader->new_plugin_data[ $field ] ) ? $this->upgrader->new_plugin_data[ $field ] : '-';
$is_same_plugin = $is_same_plugin && ( $old_value === $new_value );
$diff_field = ( 'Version' !== $field && $new_value !== $old_value );
$diff_version = ( 'Version' === $field && $this->is_downgrading );
$table .= '<tr><td class="name-label">' . $label . '</td><td>' . esc_html( $old_value ) . '</td>';
$table .= ( $diff_field || $diff_version ) ? '<td class="warning">' : '<td>';
$table .= esc_html( $new_value ) . '</td></tr>';
}
$table .= '</tbody></table>';
/**
* Filters the compare table output for overwrite a plugin package on upload.
*
* @since 5.5.0
*
* @param string $table The output table with Name, Version, Author, RequiresWP and RequiresPHP info.
* @param array $current_plugin_data Array with current plugin data.
* @param array $new_plugin_data Array with uploaded plugin data.
*/
echo apply_filters( 'install_plugin_ovewrite_comparison', $table, $current_plugin_data, $this->upgrader->new_plugin_data );
$install_actions = array();
$can_update = true;
$blocked_message = '<p>' . esc_html( __( 'The plugin cannot be updated due to the following:' ) ) . '</p>';
$blocked_message .= '<ul class="ul-disc">';
if (
! empty( $this->upgrader->new_plugin_data['RequiresPHP'] ) &&
version_compare( phpversion(), $this->upgrader->new_plugin_data['RequiresPHP'], '<' )
) {
$error = sprintf(
/* translators: 1: Current PHP version, 2: Version required by the uploaded plugin. */
__( 'The PHP version on your server is %1$s, however the uploaded plugin requires %2$s.' ),
phpversion(),
$this->upgrader->new_plugin_data['RequiresPHP']
);
$blocked_message .= '<li>' . esc_html( $error ) . '</li>';
$can_update = false;
}
if (
! empty( $this->upgrader->new_plugin_data['RequiresWP'] ) &&
version_compare( $GLOBALS['wp_version'], $this->upgrader->new_plugin_data['RequiresWP'], '<' )
) {
$error = sprintf(
/* translators: 1: Current WordPress version, 2: Version required by the uploaded plugin. */
__( 'Your WordPress version is %1$s, however the uploaded plugin requires %2$s.' ),
$GLOBALS['wp_version'],
$this->upgrader->new_plugin_data['RequiresWP']
);
$blocked_message .= '<li>' . esc_html( $error ) . '</li>';
$can_update = false;
}
$blocked_message .= '</ul>';
if ( $can_update ) {
if ( $this->is_downgrading ) {
$warning = __( 'You are uploading an older version of a current plugin. You can continue to install the older version, but be sure to <a href="https://wordpress.org/support/article/wordpress-backups/">backup your database and files</a> first.' );
} else {
$warning = __( 'You are updating a plugin. Be sure to <a href="https://wordpress.org/support/article/wordpress-backups/">backup your database and files</a> first.' );
}
echo '<p class="update-from-upload-notice">' . $warning . '</p>';
$overwrite = $this->is_downgrading ? 'downgrade-plugin' : 'update-plugin';
$install_actions['ovewrite_plugin'] = sprintf(
'<a class="button button-primary" href="%s" target="_parent">%s</a>',
wp_nonce_url( add_query_arg( 'overwrite', $overwrite, $this->url ), 'plugin-upload' ),
esc_html( __( 'Replace current with uploaded' ) )
);
} else {
echo $blocked_message;
}
$install_actions['plugins_page'] = sprintf(
'<a class="button" href="%s">%s</a>',
self_admin_url( 'plugin-install.php' ),
__( 'Cancel and go back' )
);
/**
* Filters the list of action links available following a single plugin installation failed but ovewrite is allowed.
*
* @since 5.5.0
*
* @param string[] $install_actions Array of plugin action links.
* @param object $api Object containing WordPress.org API plugin data.
* @param array $new_plugin_data Array with uploaded plugin data.
*/
$install_actions = apply_filters( 'install_plugin_ovewrite_actions', $install_actions, $this->api, $this->upgrader->new_plugin_data );
if ( ! empty( $install_actions ) ) {
echo '<p class="update-from-upload-actions">' . implode( ' ', (array) $install_actions ) . '</p>';
}
return true;
}
}

View File

@ -38,6 +38,16 @@ class Plugin_Upgrader extends WP_Upgrader {
*/
public $bulk = false;
/**
* New plugin info.
*
* @since 5.5.0
* @var array $new_plugin_data
*
* @see check_package()
*/
public $new_plugin_data = array();
/**
* Initialize the upgrade strings.
*
@ -54,6 +64,9 @@ class Plugin_Upgrader extends WP_Upgrader {
$this->strings['process_failed'] = __( 'Plugin update failed.' );
$this->strings['process_success'] = __( 'Plugin updated successfully.' );
$this->strings['process_bulk_success'] = __( 'Plugins updated successfully.' );
/* translators: 1: Plugin name, 2: Plugin version. */
$this->strings['process_success_specific'] = __( 'Successfully installed the plugin <strong>%1$s %2$s</strong>.' );
}
/**
@ -67,9 +80,25 @@ class Plugin_Upgrader extends WP_Upgrader {
$this->strings['downloading_package'] = sprintf( __( 'Downloading installation package from %s&#8230;' ), '<span class="code">%s</span>' );
$this->strings['unpack_package'] = __( 'Unpacking the package&#8230;' );
$this->strings['installing_package'] = __( 'Installing the plugin&#8230;' );
$this->strings['remove_old'] = __( 'Removing the current plugin&#8230;' );
$this->strings['remove_old_failed'] = __( 'Could not remove the current plugin.' );
$this->strings['no_files'] = __( 'The plugin contains no files.' );
$this->strings['process_failed'] = __( 'Plugin installation failed.' );
$this->strings['process_success'] = __( 'Plugin installed successfully.' );
if ( ! empty( $this->skin->overwrite ) ) {
if ( 'update-plugin' === $this->skin->overwrite ) {
$this->strings['installing_package'] = __( 'Updating the plugin&#8230;' );
$this->strings['process_failed'] = __( 'Plugin update failed.' );
$this->strings['process_success'] = __( 'Plugin updated successfully.' );
}
if ( 'downgrade-plugin' === $this->skin->overwrite ) {
$this->strings['installing_package'] = __( 'Downgrading the plugin&#8230;' );
$this->strings['process_failed'] = __( 'Plugin downgrade failed.' );
$this->strings['process_success'] = __( 'Plugin downgraded successfully.' );
}
}
}
/**
@ -88,9 +117,9 @@ class Plugin_Upgrader extends WP_Upgrader {
* @return bool|WP_Error True if the installation was successful, false or a WP_Error otherwise.
*/
public function install( $package, $args = array() ) {
$defaults = array(
'clear_update_cache' => true,
'overwrite_package' => false, // Do not overwrite files.
);
$parsed_args = wp_parse_args( $args, $defaults );
@ -107,7 +136,7 @@ class Plugin_Upgrader extends WP_Upgrader {
array(
'package' => $package,
'destination' => WP_PLUGIN_DIR,
'clear_destination' => false, // Do not overwrite files.
'clear_destination' => $parsed_args['overwrite_package'],
'clear_working' => true,
'hook_extra' => array(
'type' => 'plugin',
@ -126,6 +155,20 @@ class Plugin_Upgrader extends WP_Upgrader {
// Force refresh of plugin update information.
wp_clean_plugins_cache( $parsed_args['clear_update_cache'] );
if ( $parsed_args['overwrite_package'] ) {
/**
* Fires when the upgrader has successfully overwritten a currently installed
* plugin or theme with an uploaded zip package.
*
* @since 5.5.0
*
* @param string $package The package file.
* @param array $new_plugin_data The new plugin data.
* @param string $package_type The package type (plugin or theme).
*/
do_action( 'upgrader_overwrote_package', $package, $this->new_plugin_data, 'plugin' );
}
return true;
}
@ -145,7 +188,6 @@ class Plugin_Upgrader extends WP_Upgrader {
* @return bool|WP_Error True if the upgrade was successful, false or a WP_Error object otherwise.
*/
public function upgrade( $plugin, $args = array() ) {
$defaults = array(
'clear_update_cache' => true,
);
@ -223,7 +265,6 @@ class Plugin_Upgrader extends WP_Upgrader {
* @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() ) {
$defaults = array(
'clear_update_cache' => true,
);
@ -349,6 +390,8 @@ class Plugin_Upgrader extends WP_Upgrader {
public function check_package( $source ) {
global $wp_filesystem;
$this->new_plugin_data = array();
if ( is_wp_error( $source ) ) {
return $source;
}
@ -359,19 +402,18 @@ class Plugin_Upgrader extends WP_Upgrader {
}
// Check that the folder contains at least 1 valid plugin.
$plugins_found = false;
$files = glob( $working_directory . '*.php' );
$files = glob( $working_directory . '*.php' );
if ( $files ) {
foreach ( $files as $file ) {
$info = get_plugin_data( $file, false, false );
if ( ! empty( $info['Name'] ) ) {
$plugins_found = true;
$this->new_plugin_data = $info;
break;
}
}
}
if ( ! $plugins_found ) {
if ( empty( $this->new_plugin_data ) ) {
return new WP_Error( 'incompatible_archive_no_plugins', $this->strings['incompatible_archive'], __( 'No valid plugins were found.' ) );
}

View File

@ -18,22 +18,29 @@
class Theme_Installer_Skin extends WP_Upgrader_Skin {
public $api;
public $type;
public $url;
public $overwrite;
private $is_downgrading = false;
/**
* @param array $args
*/
public function __construct( $args = array() ) {
$defaults = array(
'type' => 'web',
'url' => '',
'theme' => '',
'nonce' => '',
'title' => '',
'type' => 'web',
'url' => '',
'theme' => '',
'nonce' => '',
'title' => '',
'overwrite' => '',
);
$args = wp_parse_args( $args, $defaults );
$this->type = $args['type'];
$this->api = isset( $args['api'] ) ? $args['api'] : array();
$this->type = $args['type'];
$this->url = $args['url'];
$this->api = isset( $args['api'] ) ? $args['api'] : array();
$this->overwrite = $args['overwrite'];
parent::__construct( $args );
}
@ -50,9 +57,33 @@ class Theme_Installer_Skin extends WP_Upgrader_Skin {
}
}
/**
* Hides the `process_failed` error when updating a theme by uploading a zip file.
*
* @since 5.5.0
*
* @param $wp_error WP_Error.
* @return bool
*/
public function hide_process_failed( $wp_error ) {
if (
'upload' === $this->type &&
'' === $this->overwrite &&
$wp_error->get_error_code() === 'folder_exists'
) {
return true;
}
return false;
}
/**
*/
public function after() {
if ( $this->do_overwrite() ) {
return;
}
if ( empty( $this->upgrader->result['destination_name'] ) ) {
return;
}
@ -130,6 +161,8 @@ class Theme_Installer_Skin extends WP_Upgrader_Skin {
if ( ! $this->result || is_wp_error( $this->result ) || is_network_admin() || ! current_user_can( 'switch_themes' ) ) {
unset( $install_actions['activate'], $install_actions['preview'] );
} elseif ( get_option( 'template' ) === $stylesheet ) {
unset( $install_actions['activate'] );
}
/**
@ -147,4 +180,177 @@ class Theme_Installer_Skin extends WP_Upgrader_Skin {
$this->feedback( implode( ' | ', (array) $install_actions ) );
}
}
/**
* Check if the theme can be overwritten and output the HTML for overwriting a theme on upload.
*
* @since 5.5.0
*
* @return bool Whether the theme can be overwritten and HTML was outputted.
*/
private function do_overwrite() {
if ( 'upload' !== $this->type || ! is_wp_error( $this->result ) || 'folder_exists' !== $this->result->get_error_code() ) {
return false;
}
$folder = $this->result->get_error_data( 'folder_exists' );
$folder = rtrim( $folder, '/' );
$current_theme_data = false;
$all_themes = wp_get_themes( array( 'errors' => null ) );
foreach ( $all_themes as $theme ) {
if ( rtrim( $theme->get_stylesheet_directory(), '/' ) !== $folder ) {
continue;
}
$current_theme_data = $theme;
}
if ( empty( $current_theme_data ) || empty( $this->upgrader->new_theme_data ) ) {
return false;
}
echo '<h2 class="update-from-upload-heading">' . esc_html( __( 'This theme is already installed.' ) ) . '</h2>';
// Check errors for current theme
if ( is_wp_error( $current_theme_data->errors() ) ) {
$this->feedback( 'current_theme_has_errors', $current_theme_data->errors()->get_error_message() );
}
$this->is_downgrading = version_compare( $current_theme_data['Version'], $this->upgrader->new_theme_data['Version'], '>' );
$is_invalid_parent = false;
if ( ! empty( $this->upgrader->new_theme_data['Template'] ) ) {
$is_invalid_parent = ! in_array( $this->upgrader->new_theme_data['Template'], array_keys( $all_themes ), true );
}
$rows = array(
'Name' => __( 'Theme name' ),
'Version' => __( 'Version' ),
'Author' => __( 'Author' ),
'RequiresWP' => __( 'Required WordPress version' ),
'RequiresPHP' => __( 'Required PHP version' ),
'Template' => __( 'Parent theme' ),
);
$table = '<table class="update-from-upload-comparison"><tbody>';
$table .= '<tr><th></th><th>' . esc_html( __( 'Current' ) ) . '</th><th>' . esc_html( __( 'Uploaded' ) ) . '</th></tr>';
$is_same_theme = true; // Let's consider only these rows
foreach ( $rows as $field => $label ) {
$old_value = $current_theme_data->display( $field, false );
$old_value = $old_value ? $old_value : '-';
$new_value = ! empty( $this->upgrader->new_theme_data[ $field ] ) ? $this->upgrader->new_theme_data[ $field ] : '-';
if ( $old_value === $new_value && '-' === $new_value && 'Template' === $field ) {
continue;
}
$is_same_theme = $is_same_theme && ( $old_value === $new_value );
$diff_field = ( 'Version' !== $field && $new_value !== $old_value );
$diff_version = ( 'Version' === $field && $this->is_downgrading );
$invalid_parent = false;
if ( 'Template' === $field && $is_invalid_parent ) {
$invalid_parent = true;
$new_value .= ' ' . __( '(not found)' );
}
$table .= '<tr><td class="name-label">' . $label . '</td><td>' . esc_html( $old_value ) . '</td>';
$table .= ( $diff_field || $diff_version || $invalid_parent ) ? '<td class="warning">' : '<td>';
$table .= esc_html( $new_value ) . '</td></tr>';
}
$table .= '</tbody></table>';
/**
* Filters the compare table output for overwrite a theme package on upload.
*
* @since 5.5.0
*
* @param string $table The output table with Name, Version, Author, RequiresWP and RequiresPHP info.
* @param array $current_theme_data Array with current theme data.
* @param array $new_theme_data Array with uploaded theme data.
*/
echo apply_filters( 'install_theme_overwrite_comparison', $table, $current_theme_data, $this->upgrader->new_theme_data );
$install_actions = array();
$can_update = true;
$blocked_message = '<p>' . esc_html( __( 'The theme cannot be updated due to the following:' ) ) . '</p>';
$blocked_message .= '<ul class="ul-disc">';
if ( ! empty( $this->upgrader->new_theme_data['RequiresPHP'] ) && version_compare( phpversion(), $this->upgrader->new_theme_data['RequiresPHP'], '<' ) ) {
$error = sprintf(
/* translators: 1: Current PHP version, 2: Version required by the uploaded theme. */
__( 'The PHP version on your server is %1$s, however the uploaded theme requires %2$s.' ),
phpversion(),
$this->upgrader->new_theme_data['RequiresPHP']
);
$blocked_message .= '<li>' . esc_html( $error ) . '</li>';
$can_update = false;
}
if ( ! empty( $this->upgrader->new_theme_data['RequiresWP'] ) && version_compare( $GLOBALS['wp_version'], $this->upgrader->new_theme_data['RequiresWP'], '<' ) ) {
$error = sprintf(
/* translators: 1: Current WordPress version, 2: Version required by the uploaded theme. */
__( 'Your WordPress version is %1$s, however the uploaded theme requires %2$s.' ),
$GLOBALS['wp_version'],
$this->upgrader->new_theme_data['RequiresWP']
);
$blocked_message .= '<li>' . esc_html( $error ) . '</li>';
$can_update = false;
}
$blocked_message .= '</ul>';
if ( $can_update ) {
if ( $this->is_downgrading ) {
$warning = __( 'You are uploading an older version of a current theme. You can continue to install the older version, but be sure to <a href="https://wordpress.org/support/article/wordpress-backups/">backup your database and files</a> first.' );
} else {
$warning = __( 'You are updating a theme. Be sure to <a href="https://wordpress.org/support/article/wordpress-backups/">backup your database and files</a> first.' );
}
echo '<p class="update-from-upload-notice">' . $warning . '</p>';
$overwrite = $this->is_downgrading ? 'downgrade-theme' : 'update-theme';
$install_actions['ovewrite_theme'] = sprintf(
'<a class="button button-primary" href="%s" target="_parent">%s</a>',
wp_nonce_url( add_query_arg( 'overwrite', $overwrite, $this->url ), 'theme-upload' ),
esc_html( __( 'Replace current with uploaded' ) )
);
} else {
echo $blocked_message;
}
$install_actions['themes_page'] = sprintf(
'<a class="button" href="%s" target="_parent">%s</a>',
self_admin_url( 'theme-install.php' ),
__( 'Cancel and go back' )
);
/**
* Filters the list of action links available following a single theme installation failed but ovewrite is allowed.
*
* @since 5.5.0
*
* @param string[] $install_actions Array of theme action links.
* @param object $api Object containing WordPress.org API theme data.
* @param array $new_theme_data Array with uploaded theme data.
*/
$install_actions = apply_filters( 'install_theme_ovewrite_actions', $install_actions, $this->api, $this->upgrader->new_theme_data );
if ( ! empty( $install_actions ) ) {
echo '<p class="update-from-upload-actions">' . implode( ' ', (array) $install_actions ) . '</p>';
}
return true;
}
}

View File

@ -37,6 +37,16 @@ class Theme_Upgrader extends WP_Upgrader {
*/
public $bulk = false;
/**
* New theme info.
*
* @since 5.5.0
* @var array $new_theme_data
*
* @see check_package()
*/
public $new_theme_data = array();
/**
* Initialize the upgrade strings.
*
@ -65,6 +75,8 @@ class Theme_Upgrader extends WP_Upgrader {
$this->strings['downloading_package'] = sprintf( __( 'Downloading installation package from %s&#8230;' ), '<span class="code">%s</span>' );
$this->strings['unpack_package'] = __( 'Unpacking the package&#8230;' );
$this->strings['installing_package'] = __( 'Installing the theme&#8230;' );
$this->strings['remove_old'] = __( 'Removing the old version of the theme&#8230;' );
$this->strings['remove_old_failed'] = __( 'Could not remove the old theme.' );
$this->strings['no_files'] = __( 'The theme contains no files.' );
$this->strings['process_failed'] = __( 'Theme installation failed.' );
$this->strings['process_success'] = __( 'Theme installed successfully.' );
@ -79,6 +91,20 @@ class Theme_Upgrader extends WP_Upgrader {
$this->strings['parent_theme_install_success'] = __( 'Successfully installed the parent theme, <strong>%1$s %2$s</strong>.' );
/* translators: %s: Theme name. */
$this->strings['parent_theme_not_found'] = sprintf( __( '<strong>The parent theme could not be found.</strong> You will need to install the parent theme, %s, before you can use this child theme.' ), '<strong>%s</strong>' );
/* translators: %s: Theme error. */
$this->strings['current_theme_has_errors'] = __( 'The current theme has the following error: "%s".' );
if ( 'update-theme' === $this->skin->overwrite ) {
$this->strings['installing_package'] = __( 'Updating the theme&#8230;' );
$this->strings['process_failed'] = __( 'Theme update failed.' );
$this->strings['process_success'] = __( 'Theme updated successfully.' );
}
if ( 'downgrade-theme' === $this->skin->overwrite ) {
$this->strings['installing_package'] = __( 'Downgrading the theme&#8230;' );
$this->strings['process_failed'] = __( 'Theme downgrade failed.' );
$this->strings['process_success'] = __( 'Theme downgraded successfully.' );
}
}
/**
@ -200,9 +226,9 @@ class Theme_Upgrader extends WP_Upgrader {
* @return bool|WP_Error True if the installation was successful, false or a WP_Error object otherwise.
*/
public function install( $package, $args = array() ) {
$defaults = array(
'clear_update_cache' => true,
'overwrite_package' => false, // Do not overwrite files.
);
$parsed_args = wp_parse_args( $args, $defaults );
@ -220,7 +246,7 @@ class Theme_Upgrader extends WP_Upgrader {
array(
'package' => $package,
'destination' => get_theme_root(),
'clear_destination' => false, // Do not overwrite files.
'clear_destination' => $args['overwrite_package'],
'clear_working' => true,
'hook_extra' => array(
'type' => 'theme',
@ -240,6 +266,20 @@ class Theme_Upgrader extends WP_Upgrader {
// Refresh the Theme Update information.
wp_clean_themes_cache( $parsed_args['clear_update_cache'] );
if ( $parsed_args['overwrite_package'] ) {
/**
* Fires when the upgrader has successfully overwritten a currently installed
* plugin or theme with an uploaded zip package.
*
* @since 5.5.0
*
* @param string $package The package file.
* @param array $new_plugin_data The new theme data.
* @param string $package_type The package type (plugin or theme).
*/
do_action( 'upgrader_overwrote_package', $package, $this->new_theme_data, 'theme' );
}
return true;
}
@ -259,7 +299,6 @@ class Theme_Upgrader extends WP_Upgrader {
* @return bool|WP_Error True if the upgrade was successful, false or a WP_Error object otherwise.
*/
public function upgrade( $theme, $args = array() ) {
$defaults = array(
'clear_update_cache' => true,
);
@ -332,7 +371,6 @@ class Theme_Upgrader extends WP_Upgrader {
* @return array[]|false An array of results, or false if unable to connect to the filesystem.
*/
public function bulk_upgrade( $themes, $args = array() ) {
$defaults = array(
'clear_update_cache' => true,
);
@ -461,6 +499,8 @@ class Theme_Upgrader extends WP_Upgrader {
public function check_package( $source ) {
global $wp_filesystem;
$this->new_theme_data = array();
if ( is_wp_error( $source ) ) {
return $source;
}
@ -484,11 +524,16 @@ class Theme_Upgrader extends WP_Upgrader {
);
}
// All these headers are needed on Theme_Installer_Skin::do_overwrite().
$info = get_file_data(
$working_directory . 'style.css',
array(
'Name' => 'Theme Name',
'Template' => 'Template',
'Name' => 'Theme Name',
'Version' => 'Version',
'Author' => 'Author',
'Template' => 'Template',
'RequiresWP' => 'Requires at least',
'RequiresPHP' => 'Requires PHP',
)
);
@ -517,6 +562,7 @@ class Theme_Upgrader extends WP_Upgrader {
);
}
$this->new_theme_data = $info;
return $source;
}
@ -640,7 +686,6 @@ class Theme_Upgrader extends WP_Upgrader {
* and the last result isn't set.
*/
public function theme_info( $theme = null ) {
if ( empty( $theme ) ) {
if ( ! empty( $this->result['destination_name'] ) ) {
$theme = $this->result['destination_name'];
@ -648,7 +693,11 @@ class Theme_Upgrader extends WP_Upgrader {
return false;
}
}
return wp_get_theme( $theme );
$theme = wp_get_theme( $theme );
$theme->cache_delete();
return $theme;
}
}

View File

@ -205,4 +205,16 @@ class WP_Upgrader_Skin {
/**
*/
public function bulk_footer() {}
/**
* Hides the `process_failed` error message when updating by uploading a zip file.
*
* @since 5.5.0
*
* @param $wp_error WP_Error
* @return bool
*/
public function hide_process_failed( $wp_error ) {
return false;
}
}

View File

@ -798,7 +798,10 @@ class WP_Upgrader {
$this->skin->set_result( $result );
if ( is_wp_error( $result ) ) {
$this->skin->error( $result );
$this->skin->feedback( 'process_failed' );
if ( ! method_exists( $this->skin, 'hide_process_failed' ) || ! $this->skin->hide_process_failed( $result ) ) {
$this->skin->feedback( 'process_failed' );
}
} else {
// Installation succeeded.
$this->skin->feedback( 'process_success' );

View File

@ -353,7 +353,7 @@ function install_plugins_upload() {
<form method="post" enctype="multipart/form-data" class="wp-upload-form" action="<?php echo self_admin_url( 'update.php?action=upload-plugin' ); ?>">
<?php wp_nonce_field( 'plugin-upload' ); ?>
<label class="screen-reader-text" for="pluginzip"><?php _e( 'Plugin zip file' ); ?></label>
<input type="file" id="pluginzip" name="pluginzip" />
<input type="file" id="pluginzip" name="pluginzip" accept=".zip" />
<?php submit_button( __( 'Install Now' ), '', 'install-plugin-submit', false ); ?>
</form>
</div>

View File

@ -183,7 +183,7 @@ function install_themes_upload() {
<form method="post" enctype="multipart/form-data" class="wp-upload-form" action="<?php echo self_admin_url( 'update.php?action=upload-theme' ); ?>">
<?php wp_nonce_field( 'theme-upload' ); ?>
<label class="screen-reader-text" for="themezip"><?php _e( 'Theme zip file' ); ?></label>
<input type="file" id="themezip" name="themezip" />
<input type="file" id="themezip" name="themezip" accept=".zip"/>
<?php submit_button( __( 'Install Now' ), '', 'install-theme-submit', false ); ?>
</form>
<?php

View File

@ -157,13 +157,16 @@ if ( isset( $_GET['action'] ) ) {
require_once ABSPATH . 'wp-admin/admin-header.php';
/* translators: %s: File name. */
$title = sprintf( __( 'Installing Plugin from uploaded file: %s' ), esc_html( basename( $file_upload->filename ) ) );
$title = sprintf( __( 'Installing plugin from uploaded file: %s' ), esc_html( basename( $file_upload->filename ) ) );
$nonce = 'plugin-upload';
$url = add_query_arg( array( 'package' => $file_upload->id ), 'update.php?action=upload-plugin' );
$type = 'upload'; // Install plugin type, From Web or an Upload.
$upgrader = new Plugin_Upgrader( new Plugin_Installer_Skin( compact( 'type', 'title', 'nonce', 'url' ) ) );
$result = $upgrader->install( $file_upload->package );
$overwrite = isset( $_GET['overwrite'] ) ? sanitize_text_field( $_GET['overwrite'] ) : '';
$overwrite = in_array( $overwrite, array( 'update-plugin', 'downgrade-plugin' ), true ) ? $overwrite : '';
$upgrader = new Plugin_Upgrader( new Plugin_Installer_Skin( compact( 'type', 'title', 'nonce', 'url', 'overwrite' ) ) );
$result = $upgrader->install( $file_upload->package, array( 'overwrite_package' => $overwrite ) );
if ( $result || is_wp_error( $result ) ) {
$file_upload->cleanup();
@ -277,13 +280,16 @@ if ( isset( $_GET['action'] ) ) {
require_once ABSPATH . 'wp-admin/admin-header.php';
/* translators: %s: File name. */
$title = sprintf( __( 'Installing Theme from uploaded file: %s' ), esc_html( basename( $file_upload->filename ) ) );
$title = sprintf( __( 'Installing theme from uploaded file: %s' ), esc_html( basename( $file_upload->filename ) ) );
$nonce = 'theme-upload';
$url = add_query_arg( array( 'package' => $file_upload->id ), 'update.php?action=upload-theme' );
$type = 'upload'; // Install theme type, From Web or an Upload.
$upgrader = new Theme_Upgrader( new Theme_Installer_Skin( compact( 'type', 'title', 'nonce', 'url' ) ) );
$result = $upgrader->install( $file_upload->package );
$overwrite = isset( $_GET['overwrite'] ) ? sanitize_text_field( $_GET['overwrite'] ) : '';
$overwrite = in_array( $overwrite, array( 'update-theme', 'downgrade-theme' ), true ) ? $overwrite : '';
$upgrader = new Theme_Upgrader( new Theme_Installer_Skin( compact( 'type', 'title', 'nonce', 'url', 'overwrite' ) ) );
$result = $upgrader->install( $file_upload->package, array( 'overwrite_package' => $overwrite ) );
if ( $result || is_wp_error( $result ) ) {
$file_upload->cleanup();

View File

@ -13,7 +13,7 @@
*
* @global string $wp_version
*/
$wp_version = '5.5-alpha-48389';
$wp_version = '5.5-alpha-48390';
/**
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.