Make WordPress Core


Ignore:
Timestamp:
07/07/2020 05:47:37 PM (4 years ago)
Author:
azaozz
Message:

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.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-admin/includes/class-plugin-installer-skin.php

    r47808 r48390  
    1919    public $api;
    2020    public $type;
     21    public $url;
     22    public $overwrite;
     23
     24    private $is_downgrading = false;
    2125
    2226    /**
     
    2529    public function __construct( $args = array() ) {
    2630        $defaults = array(
    27             'type'   => 'web',
    28             'url'    => '',
    29             'plugin' => '',
    30             'nonce'  => '',
    31             'title'  => '',
     31            'type'      => 'web',
     32            'url'       => '',
     33            'plugin'    => '',
     34            'nonce'     => '',
     35            'title'     => '',
     36            'overwrite' => '',
    3237        );
    3338        $args     = wp_parse_args( $args, $defaults );
    3439
    35         $this->type = $args['type'];
    36         $this->api  = isset( $args['api'] ) ? $args['api'] : array();
     40        $this->type      = $args['type'];
     41        $this->url       = $args['url'];
     42        $this->api       = isset( $args['api'] ) ? $args['api'] : array();
     43        $this->overwrite = $args['overwrite'];
    3744
    3845        parent::__construct( $args );
     
    4451        if ( ! empty( $this->api ) ) {
    4552            $this->upgrader->strings['process_success'] = sprintf(
    46                 /* translators: 1: Plugin name, 2: Plugin version. */
    47                 __( 'Successfully installed the plugin <strong>%1$s %2$s</strong>.' ),
     53                $this->upgrader->strings['process_success_specific'],
    4854                $this->api->name,
    4955                $this->api->version
     
    5359
    5460    /**
     61     * Hides the `process_failed` error when updating a plugin by uploading a zip file.
     62     *
     63     * @since 5.5.0
     64     *
     65     * @param $wp_error WP_Error.
     66     * @return bool
     67     */
     68    public function hide_process_failed( $wp_error ) {
     69        if (
     70            'upload' === $this->type &&
     71            '' === $this->overwrite &&
     72            $wp_error->get_error_code() === 'folder_exists'
     73        ) {
     74            return true;
     75        }
     76
     77        return false;
     78    }
     79
     80    /**
    5581     */
    5682    public function after() {
     83        // Check if the plugin can be overwritten and output the HTML.
     84        if ( $this->do_overwrite() ) {
     85            return;
     86        }
     87
    5788        $plugin_file = $this->upgrader->plugin_info();
    5889
     
    118149        if ( ! $this->result || is_wp_error( $this->result ) ) {
    119150            unset( $install_actions['activate_plugin'], $install_actions['network_activate'] );
    120         } elseif ( ! current_user_can( 'activate_plugin', $plugin_file ) ) {
     151        } elseif ( ! current_user_can( 'activate_plugin', $plugin_file ) || is_plugin_active( $plugin_file ) ) {
    121152            unset( $install_actions['activate_plugin'] );
    122153        }
     
    139170        }
    140171    }
     172
     173    /**
     174     * Check if the plugin can be overwritten and output the HTML for overwriting a plugin on upload.
     175     *
     176     * @since 5.5.0
     177     *
     178     * @return bool Whether the plugin can be overwritten and HTML was outputted.
     179     */
     180    private function do_overwrite() {
     181        if ( 'upload' !== $this->type || ! is_wp_error( $this->result ) || 'folder_exists' !== $this->result->get_error_code() ) {
     182            return false;
     183        }
     184
     185        $folder = $this->result->get_error_data( 'folder_exists' );
     186        $folder = ltrim( substr( $folder, strlen( WP_PLUGIN_DIR ) ), '/' );
     187
     188        $current_plugin_data = false;
     189        foreach ( get_plugins() as $plugin => $plugin_data ) {
     190            if ( strrpos( $plugin, $folder ) !== 0 ) {
     191                continue;
     192            }
     193
     194            $current_plugin_data = $plugin_data;
     195        }
     196
     197        if ( empty( $current_plugin_data ) || empty( $this->upgrader->new_plugin_data ) ) {
     198            return false;
     199        }
     200
     201        echo '<h2 class="update-from-upload-heading">' . esc_html( __( 'This plugin is already installed.' ) ) . '</h2>';
     202
     203        $this->is_downgrading = version_compare( $current_plugin_data['Version'], $this->upgrader->new_plugin_data['Version'], '>' );
     204
     205        $rows = array(
     206            'Name'        => __( 'Plugin name' ),
     207            'Version'     => __( 'Version' ),
     208            'Author'      => __( 'Author' ),
     209            'RequiresWP'  => __( 'Required WordPress version' ),
     210            'RequiresPHP' => __( 'Required PHP version' ),
     211        );
     212
     213        $table  = '<table class="update-from-upload-comparison"><tbody>';
     214        $table .= '<tr><th></th><th>' . esc_html( __( 'Current' ) ) . '</th>';
     215        $table .= '<th>' . esc_html( __( 'Uploaded' ) ) . '</th></tr>';
     216
     217        $is_same_plugin = true; // Let's consider only these rows
     218        foreach ( $rows as $field => $label ) {
     219            $old_value = ! empty( $current_plugin_data[ $field ] ) ? $current_plugin_data[ $field ] : '-';
     220            $new_value = ! empty( $this->upgrader->new_plugin_data[ $field ] ) ? $this->upgrader->new_plugin_data[ $field ] : '-';
     221
     222            $is_same_plugin = $is_same_plugin && ( $old_value === $new_value );
     223
     224            $diff_field   = ( 'Version' !== $field && $new_value !== $old_value );
     225            $diff_version = ( 'Version' === $field && $this->is_downgrading );
     226
     227            $table .= '<tr><td class="name-label">' . $label . '</td><td>' . esc_html( $old_value ) . '</td>';
     228            $table .= ( $diff_field || $diff_version ) ? '<td class="warning">' : '<td>';
     229            $table .= esc_html( $new_value ) . '</td></tr>';
     230        }
     231
     232        $table .= '</tbody></table>';
     233
     234        /**
     235         * Filters the compare table output for overwrite a plugin package on upload.
     236         *
     237         * @since 5.5.0
     238         *
     239         * @param string   $table                The output table with Name, Version, Author, RequiresWP and RequiresPHP info.
     240         * @param array    $current_plugin_data  Array with current plugin data.
     241         * @param array    $new_plugin_data      Array with uploaded plugin data.
     242         */
     243        echo apply_filters( 'install_plugin_ovewrite_comparison', $table, $current_plugin_data, $this->upgrader->new_plugin_data );
     244
     245        $install_actions = array();
     246        $can_update      = true;
     247
     248        $blocked_message  = '<p>' . esc_html( __( 'The plugin cannot be updated due to the following:' ) ) . '</p>';
     249        $blocked_message .= '<ul class="ul-disc">';
     250
     251        if (
     252            ! empty( $this->upgrader->new_plugin_data['RequiresPHP'] ) &&
     253            version_compare( phpversion(), $this->upgrader->new_plugin_data['RequiresPHP'], '<' )
     254        ) {
     255            $error = sprintf(
     256                /* translators: 1: Current PHP version, 2: Version required by the uploaded plugin. */
     257                __( 'The PHP version on your server is %1$s, however the uploaded plugin requires %2$s.' ),
     258                phpversion(),
     259                $this->upgrader->new_plugin_data['RequiresPHP']
     260            );
     261
     262            $blocked_message .= '<li>' . esc_html( $error ) . '</li>';
     263            $can_update       = false;
     264        }
     265
     266        if (
     267            ! empty( $this->upgrader->new_plugin_data['RequiresWP'] ) &&
     268            version_compare( $GLOBALS['wp_version'], $this->upgrader->new_plugin_data['RequiresWP'], '<' )
     269        ) {
     270            $error = sprintf(
     271                /* translators: 1: Current WordPress version, 2: Version required by the uploaded plugin. */
     272                __( 'Your WordPress version is %1$s, however the uploaded plugin requires %2$s.' ),
     273                $GLOBALS['wp_version'],
     274                $this->upgrader->new_plugin_data['RequiresWP']
     275            );
     276
     277            $blocked_message .= '<li>' . esc_html( $error ) . '</li>';
     278            $can_update       = false;
     279        }
     280
     281        $blocked_message .= '</ul>';
     282
     283        if ( $can_update ) {
     284            if ( $this->is_downgrading ) {
     285                $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.' );
     286            } else {
     287                $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.' );
     288            }
     289
     290            echo '<p class="update-from-upload-notice">' . $warning . '</p>';
     291
     292            $overwrite = $this->is_downgrading ? 'downgrade-plugin' : 'update-plugin';
     293
     294            $install_actions['ovewrite_plugin'] = sprintf(
     295                '<a class="button button-primary" href="%s" target="_parent">%s</a>',
     296                wp_nonce_url( add_query_arg( 'overwrite', $overwrite, $this->url ), 'plugin-upload' ),
     297                esc_html( __( 'Replace current with uploaded' ) )
     298            );
     299        } else {
     300            echo $blocked_message;
     301        }
     302
     303        $install_actions['plugins_page'] = sprintf(
     304            '<a class="button" href="%s">%s</a>',
     305            self_admin_url( 'plugin-install.php' ),
     306            __( 'Cancel and go back' )
     307        );
     308
     309        /**
     310         * Filters the list of action links available following a single plugin installation failed but ovewrite is allowed.
     311         *
     312         * @since 5.5.0
     313         *
     314         * @param string[] $install_actions Array of plugin action links.
     315         * @param object   $api             Object containing WordPress.org API plugin data.
     316         * @param array    $new_plugin_data Array with uploaded plugin data.
     317         */
     318        $install_actions = apply_filters( 'install_plugin_ovewrite_actions', $install_actions, $this->api, $this->upgrader->new_plugin_data );
     319
     320        if ( ! empty( $install_actions ) ) {
     321            echo '<p class="update-from-upload-actions">' . implode( ' ', (array) $install_actions ) . '</p>';
     322        }
     323
     324        return true;
     325    }
    141326}
Note: See TracChangeset for help on using the changeset viewer.