Make WordPress Core

source: tags/6.5/src/wp-admin/includes/class-theme-upgrader.php

Last change on this file was 57252, checked in by jorbin, 6 months ago

Upgrade/Install: Check theme compatibility during bulk upgrades.

Previously, bulk upgrades did not verify that a theme package was compatible with the site's WordPress version or the server's PHP version.

This was previusly done for plugins in #59198, but themes were missed.

Follow-up to: [56525].

Props salcode, lakshmananphp.
Fixes #59758.

  • Property svn:eol-style set to native
File size: 26.2 KB
Line 
1<?php
2/**
3 * Upgrade API: Theme_Upgrader class
4 *
5 * @package WordPress
6 * @subpackage Upgrader
7 * @since 4.6.0
8 */
9
10/**
11 * Core class used for upgrading/installing themes.
12 *
13 * It is designed to upgrade/install themes from a local zip, remote zip URL,
14 * or uploaded zip file.
15 *
16 * @since 2.8.0
17 * @since 4.6.0 Moved to its own file from wp-admin/includes/class-wp-upgrader.php.
18 *
19 * @see WP_Upgrader
20 */
21class Theme_Upgrader extends WP_Upgrader {
22
23        /**
24         * Result of the theme upgrade offer.
25         *
26         * @since 2.8.0
27         * @var array|WP_Error $result
28         * @see WP_Upgrader::$result
29         */
30        public $result;
31
32        /**
33         * Whether multiple themes are being upgraded/installed in bulk.
34         *
35         * @since 2.9.0
36         * @var bool $bulk
37         */
38        public $bulk = false;
39
40        /**
41         * New theme info.
42         *
43         * @since 5.5.0
44         * @var array $new_theme_data
45         *
46         * @see check_package()
47         */
48        public $new_theme_data = array();
49
50        /**
51         * Initializes the upgrade strings.
52         *
53         * @since 2.8.0
54         */
55        public function upgrade_strings() {
56                $this->strings['up_to_date'] = __( 'The theme is at the latest version.' );
57                $this->strings['no_package'] = __( 'Update package not available.' );
58                /* translators: %s: Package URL. */
59                $this->strings['downloading_package'] = sprintf( __( 'Downloading update from %s&#8230;' ), '<span class="code pre">%s</span>' );
60                $this->strings['unpack_package']      = __( 'Unpacking the update&#8230;' );
61                $this->strings['remove_old']          = __( 'Removing the old version of the theme&#8230;' );
62                $this->strings['remove_old_failed']   = __( 'Could not remove the old theme.' );
63                $this->strings['process_failed']      = __( 'Theme update failed.' );
64                $this->strings['process_success']     = __( 'Theme updated successfully.' );
65        }
66
67        /**
68         * Initializes the installation strings.
69         *
70         * @since 2.8.0
71         */
72        public function install_strings() {
73                $this->strings['no_package'] = __( 'Installation package not available.' );
74                /* translators: %s: Package URL. */
75                $this->strings['downloading_package'] = sprintf( __( 'Downloading installation package from %s&#8230;' ), '<span class="code pre">%s</span>' );
76                $this->strings['unpack_package']      = __( 'Unpacking the package&#8230;' );
77                $this->strings['installing_package']  = __( 'Installing the theme&#8230;' );
78                $this->strings['remove_old']          = __( 'Removing the old version of the theme&#8230;' );
79                $this->strings['remove_old_failed']   = __( 'Could not remove the old theme.' );
80                $this->strings['no_files']            = __( 'The theme contains no files.' );
81                $this->strings['process_failed']      = __( 'Theme installation failed.' );
82                $this->strings['process_success']     = __( 'Theme installed successfully.' );
83                /* translators: 1: Theme name, 2: Theme version. */
84                $this->strings['process_success_specific'] = __( 'Successfully installed the theme <strong>%1$s %2$s</strong>.' );
85                $this->strings['parent_theme_search']      = __( 'This theme requires a parent theme. Checking if it is installed&#8230;' );
86                /* translators: 1: Theme name, 2: Theme version. */
87                $this->strings['parent_theme_prepare_install'] = __( 'Preparing to install <strong>%1$s %2$s</strong>&#8230;' );
88                /* translators: 1: Theme name, 2: Theme version. */
89                $this->strings['parent_theme_currently_installed'] = __( 'The parent theme, <strong>%1$s %2$s</strong>, is currently installed.' );
90                /* translators: 1: Theme name, 2: Theme version. */
91                $this->strings['parent_theme_install_success'] = __( 'Successfully installed the parent theme, <strong>%1$s %2$s</strong>.' );
92                /* translators: %s: Theme name. */
93                $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>' );
94                /* translators: %s: Theme error. */
95                $this->strings['current_theme_has_errors'] = __( 'The active theme has the following error: "%s".' );
96
97                if ( ! empty( $this->skin->overwrite ) ) {
98                        if ( 'update-theme' === $this->skin->overwrite ) {
99                                $this->strings['installing_package'] = __( 'Updating the theme&#8230;' );
100                                $this->strings['process_failed']     = __( 'Theme update failed.' );
101                                $this->strings['process_success']    = __( 'Theme updated successfully.' );
102                        }
103
104                        if ( 'downgrade-theme' === $this->skin->overwrite ) {
105                                $this->strings['installing_package'] = __( 'Downgrading the theme&#8230;' );
106                                $this->strings['process_failed']     = __( 'Theme downgrade failed.' );
107                                $this->strings['process_success']    = __( 'Theme downgraded successfully.' );
108                        }
109                }
110        }
111
112        /**
113         * Checks if a child theme is being installed and its parent also needs to be installed.
114         *
115         * Hooked to the {@see 'upgrader_post_install'} filter by Theme_Upgrader::install().
116         *
117         * @since 3.4.0
118         *
119         * @param bool  $install_result
120         * @param array $hook_extra
121         * @param array $child_result
122         * @return bool
123         */
124        public function check_parent_theme_filter( $install_result, $hook_extra, $child_result ) {
125                // Check to see if we need to install a parent theme.
126                $theme_info = $this->theme_info();
127
128                if ( ! $theme_info->parent() ) {
129                        return $install_result;
130                }
131
132                $this->skin->feedback( 'parent_theme_search' );
133
134                if ( ! $theme_info->parent()->errors() ) {
135                        $this->skin->feedback( 'parent_theme_currently_installed', $theme_info->parent()->display( 'Name' ), $theme_info->parent()->display( 'Version' ) );
136                        // We already have the theme, fall through.
137                        return $install_result;
138                }
139
140                // We don't have the parent theme, let's install it.
141                $api = themes_api(
142                        'theme_information',
143                        array(
144                                'slug'   => $theme_info->get( 'Template' ),
145                                'fields' => array(
146                                        'sections' => false,
147                                        'tags'     => false,
148                                ),
149                        )
150                ); // Save on a bit of bandwidth.
151
152                if ( ! $api || is_wp_error( $api ) ) {
153                        $this->skin->feedback( 'parent_theme_not_found', $theme_info->get( 'Template' ) );
154                        // Don't show activate or preview actions after installation.
155                        add_filter( 'install_theme_complete_actions', array( $this, 'hide_activate_preview_actions' ) );
156                        return $install_result;
157                }
158
159                // Backup required data we're going to override:
160                $child_api             = $this->skin->api;
161                $child_success_message = $this->strings['process_success'];
162
163                // Override them.
164                $this->skin->api = $api;
165
166                $this->strings['process_success_specific'] = $this->strings['parent_theme_install_success'];
167
168                $this->skin->feedback( 'parent_theme_prepare_install', $api->name, $api->version );
169
170                add_filter( 'install_theme_complete_actions', '__return_false', 999 ); // Don't show any actions after installing the theme.
171
172                // Install the parent theme.
173                $parent_result = $this->run(
174                        array(
175                                'package'           => $api->download_link,
176                                'destination'       => get_theme_root(),
177                                'clear_destination' => false, // Do not overwrite files.
178                                'clear_working'     => true,
179                        )
180                );
181
182                if ( is_wp_error( $parent_result ) ) {
183                        add_filter( 'install_theme_complete_actions', array( $this, 'hide_activate_preview_actions' ) );
184                }
185
186                // Start cleaning up after the parent's installation.
187                remove_filter( 'install_theme_complete_actions', '__return_false', 999 );
188
189                // Reset child's result and data.
190                $this->result                     = $child_result;
191                $this->skin->api                  = $child_api;
192                $this->strings['process_success'] = $child_success_message;
193
194                return $install_result;
195        }
196
197        /**
198         * Don't display the activate and preview actions to the user.
199         *
200         * Hooked to the {@see 'install_theme_complete_actions'} filter by
201         * Theme_Upgrader::check_parent_theme_filter() when installing
202         * a child theme and installing the parent theme fails.
203         *
204         * @since 3.4.0
205         *
206         * @param array $actions Preview actions.
207         * @return array
208         */
209        public function hide_activate_preview_actions( $actions ) {
210                unset( $actions['activate'], $actions['preview'] );
211                return $actions;
212        }
213
214        /**
215         * Install a theme package.
216         *
217         * @since 2.8.0
218         * @since 3.7.0 The `$args` parameter was added, making clearing the update cache optional.
219         *
220         * @param string $package The full local path or URI of the package.
221         * @param array  $args {
222         *     Optional. Other arguments for installing a theme package. Default empty array.
223         *
224         *     @type bool $clear_update_cache Whether to clear the updates cache if successful.
225         *                                    Default true.
226         * }
227         *
228         * @return bool|WP_Error True if the installation was successful, false or a WP_Error object otherwise.
229         */
230        public function install( $package, $args = array() ) {
231                $defaults    = array(
232                        'clear_update_cache' => true,
233                        'overwrite_package'  => false, // Do not overwrite files.
234                );
235                $parsed_args = wp_parse_args( $args, $defaults );
236
237                $this->init();
238                $this->install_strings();
239
240                add_filter( 'upgrader_source_selection', array( $this, 'check_package' ) );
241                add_filter( 'upgrader_post_install', array( $this, 'check_parent_theme_filter' ), 10, 3 );
242
243                if ( $parsed_args['clear_update_cache'] ) {
244                        // Clear cache so wp_update_themes() knows about the new theme.
245                        add_action( 'upgrader_process_complete', 'wp_clean_themes_cache', 9, 0 );
246                }
247
248                $this->run(
249                        array(
250                                'package'           => $package,
251                                'destination'       => get_theme_root(),
252                                'clear_destination' => $parsed_args['overwrite_package'],
253                                'clear_working'     => true,
254                                'hook_extra'        => array(
255                                        'type'   => 'theme',
256                                        'action' => 'install',
257                                ),
258                        )
259                );
260
261                remove_action( 'upgrader_process_complete', 'wp_clean_themes_cache', 9 );
262                remove_filter( 'upgrader_source_selection', array( $this, 'check_package' ) );
263                remove_filter( 'upgrader_post_install', array( $this, 'check_parent_theme_filter' ) );
264
265                if ( ! $this->result || is_wp_error( $this->result ) ) {
266                        return $this->result;
267                }
268
269                // Refresh the Theme Update information.
270                wp_clean_themes_cache( $parsed_args['clear_update_cache'] );
271
272                if ( $parsed_args['overwrite_package'] ) {
273                        /** This action is documented in wp-admin/includes/class-plugin-upgrader.php */
274                        do_action( 'upgrader_overwrote_package', $package, $this->new_theme_data, 'theme' );
275                }
276
277                return true;
278        }
279
280        /**
281         * Upgrades a theme.
282         *
283         * @since 2.8.0
284         * @since 3.7.0 The `$args` parameter was added, making clearing the update cache optional.
285         *
286         * @param string $theme The theme slug.
287         * @param array  $args {
288         *     Optional. Other arguments for upgrading a theme. Default empty array.
289         *
290         *     @type bool $clear_update_cache Whether to clear the update cache if successful.
291         *                                    Default true.
292         * }
293         * @return bool|WP_Error True if the upgrade was successful, false or a WP_Error object otherwise.
294         */
295        public function upgrade( $theme, $args = array() ) {
296                $defaults    = array(
297                        'clear_update_cache' => true,
298                );
299                $parsed_args = wp_parse_args( $args, $defaults );
300
301                $this->init();
302                $this->upgrade_strings();
303
304                // Is an update available?
305                $current = get_site_transient( 'update_themes' );
306                if ( ! isset( $current->response[ $theme ] ) ) {
307                        $this->skin->before();
308                        $this->skin->set_result( false );
309                        $this->skin->error( 'up_to_date' );
310                        $this->skin->after();
311                        return false;
312                }
313
314                $r = $current->response[ $theme ];
315
316                add_filter( 'upgrader_pre_install', array( $this, 'current_before' ), 10, 2 );
317                add_filter( 'upgrader_post_install', array( $this, 'current_after' ), 10, 2 );
318                add_filter( 'upgrader_clear_destination', array( $this, 'delete_old_theme' ), 10, 4 );
319                if ( $parsed_args['clear_update_cache'] ) {
320                        // Clear cache so wp_update_themes() knows about the new theme.
321                        add_action( 'upgrader_process_complete', 'wp_clean_themes_cache', 9, 0 );
322                }
323
324                $this->run(
325                        array(
326                                'package'           => $r['package'],
327                                'destination'       => get_theme_root( $theme ),
328                                'clear_destination' => true,
329                                'clear_working'     => true,
330                                'hook_extra'        => array(
331                                        'theme'       => $theme,
332                                        'type'        => 'theme',
333                                        'action'      => 'update',
334                                        'temp_backup' => array(
335                                                'slug' => $theme,
336                                                'src'  => get_theme_root( $theme ),
337                                                'dir'  => 'themes',
338                                        ),
339                                ),
340                        )
341                );
342
343                remove_action( 'upgrader_process_complete', 'wp_clean_themes_cache', 9 );
344                remove_filter( 'upgrader_pre_install', array( $this, 'current_before' ) );
345                remove_filter( 'upgrader_post_install', array( $this, 'current_after' ) );
346                remove_filter( 'upgrader_clear_destination', array( $this, 'delete_old_theme' ) );
347
348                if ( ! $this->result || is_wp_error( $this->result ) ) {
349                        return $this->result;
350                }
351
352                wp_clean_themes_cache( $parsed_args['clear_update_cache'] );
353
354                /*
355                 * Ensure any future auto-update failures trigger a failure email by removing
356                 * the last failure notification from the list when themes update successfully.
357                 */
358                $past_failure_emails = get_option( 'auto_plugin_theme_update_emails', array() );
359
360                if ( isset( $past_failure_emails[ $theme ] ) ) {
361                        unset( $past_failure_emails[ $theme ] );
362                        update_option( 'auto_plugin_theme_update_emails', $past_failure_emails );
363                }
364
365                return true;
366        }
367
368        /**
369         * Upgrades several themes at once.
370         *
371         * @since 3.0.0
372         * @since 3.7.0 The `$args` parameter was added, making clearing the update cache optional.
373         *
374         * @global string $wp_version The WordPress version string.
375         *
376         * @param string[] $themes Array of the theme slugs.
377         * @param array    $args {
378         *     Optional. Other arguments for upgrading several themes at once. Default empty array.
379         *
380         *     @type bool $clear_update_cache Whether to clear the update cache if successful.
381         *                                    Default true.
382         * }
383         * @return array[]|false An array of results, or false if unable to connect to the filesystem.
384         */
385        public function bulk_upgrade( $themes, $args = array() ) {
386                global $wp_version;
387
388                $defaults    = array(
389                        'clear_update_cache' => true,
390                );
391                $parsed_args = wp_parse_args( $args, $defaults );
392
393                $this->init();
394                $this->bulk = true;
395                $this->upgrade_strings();
396
397                $current = get_site_transient( 'update_themes' );
398
399                add_filter( 'upgrader_pre_install', array( $this, 'current_before' ), 10, 2 );
400                add_filter( 'upgrader_post_install', array( $this, 'current_after' ), 10, 2 );
401                add_filter( 'upgrader_clear_destination', array( $this, 'delete_old_theme' ), 10, 4 );
402
403                $this->skin->header();
404
405                // Connect to the filesystem first.
406                $res = $this->fs_connect( array( WP_CONTENT_DIR ) );
407                if ( ! $res ) {
408                        $this->skin->footer();
409                        return false;
410                }
411
412                $this->skin->bulk_header();
413
414                /*
415                 * Only start maintenance mode if:
416                 * - running Multisite and there are one or more themes specified, OR
417                 * - a theme with an update available is currently in use.
418                 * @todo For multisite, maintenance mode should only kick in for individual sites if at all possible.
419                 */
420                $maintenance = ( is_multisite() && ! empty( $themes ) );
421                foreach ( $themes as $theme ) {
422                        $maintenance = $maintenance || get_stylesheet() === $theme || get_template() === $theme;
423                }
424                if ( $maintenance ) {
425                        $this->maintenance_mode( true );
426                }
427
428                $results = array();
429
430                $this->update_count   = count( $themes );
431                $this->update_current = 0;
432                foreach ( $themes as $theme ) {
433                        ++$this->update_current;
434
435                        $this->skin->theme_info = $this->theme_info( $theme );
436
437                        if ( ! isset( $current->response[ $theme ] ) ) {
438                                $this->skin->set_result( true );
439                                $this->skin->before();
440                                $this->skin->feedback( 'up_to_date' );
441                                $this->skin->after();
442                                $results[ $theme ] = true;
443                                continue;
444                        }
445
446                        // Get the URL to the zip file.
447                        $r = $current->response[ $theme ];
448
449                        if ( isset( $r['requires'] ) && ! is_wp_version_compatible( $r['requires'] ) ) {
450                                $result = new WP_Error(
451                                        'incompatible_wp_required_version',
452                                        sprintf(
453                                                /* translators: 1: Current WordPress version, 2: WordPress version required by the new theme version. */
454                                                __( 'Your WordPress version is %1$s, however the new theme version requires %2$s.' ),
455                                                $wp_version,
456                                                $r['requires']
457                                        )
458                                );
459
460                                $this->skin->before( $result );
461                                $this->skin->error( $result );
462                                $this->skin->after();
463                        } elseif ( isset( $r['requires_php'] ) && ! is_php_version_compatible( $r['requires_php'] ) ) {
464                                $result = new WP_Error(
465                                        'incompatible_php_required_version',
466                                        sprintf(
467                                                /* translators: 1: Current PHP version, 2: PHP version required by the new theme version. */
468                                                __( 'The PHP version on your server is %1$s, however the new theme version requires %2$s.' ),
469                                                PHP_VERSION,
470                                                $r['requires_php']
471                                        )
472                                );
473
474                                $this->skin->before( $result );
475                                $this->skin->error( $result );
476                                $this->skin->after();
477                        } else {
478                                add_filter( 'upgrader_source_selection', array( $this, 'check_package' ) );
479                                $result = $this->run(
480                                        array(
481                                                'package'           => $r['package'],
482                                                'destination'       => get_theme_root( $theme ),
483                                                'clear_destination' => true,
484                                                'clear_working'     => true,
485                                                'is_multi'          => true,
486                                                'hook_extra'        => array(
487                                                        'theme'       => $theme,
488                                                        'temp_backup' => array(
489                                                                'slug' => $theme,
490                                                                'src'  => get_theme_root( $theme ),
491                                                                'dir'  => 'themes',
492                                                        ),
493                                                ),
494                                        )
495                                );
496                                remove_filter( 'upgrader_source_selection', array( $this, 'check_package' ) );
497                        }
498
499                        $results[ $theme ] = $result;
500
501                        // Prevent credentials auth screen from displaying multiple times.
502                        if ( false === $result ) {
503                                break;
504                        }
505                } // End foreach $themes.
506
507                $this->maintenance_mode( false );
508
509                // Refresh the Theme Update information.
510                wp_clean_themes_cache( $parsed_args['clear_update_cache'] );
511
512                /** This action is documented in wp-admin/includes/class-wp-upgrader.php */
513                do_action(
514                        'upgrader_process_complete',
515                        $this,
516                        array(
517                                'action' => 'update',
518                                'type'   => 'theme',
519                                'bulk'   => true,
520                                'themes' => $themes,
521                        )
522                );
523
524                $this->skin->bulk_footer();
525
526            ��   $this->skin->footer();
527
528                // Cleanup our hooks, in case something else does an upgrade on this connection.
529                remove_filter( 'upgrader_pre_install', array( $this, 'current_before' ) );
530                remove_filter( 'upgrader_post_install', array( $this, 'current_after' ) );
531                remove_filter( 'upgrader_clear_destination', array( $this, 'delete_old_theme' ) );
532
533                /*
534                 * Ensure any future auto-update failures trigger a failure email by removing
535                 * the last failure notification from the list when themes update successfully.
536                 */
537                $past_failure_emails = get_option( 'auto_plugin_theme_update_emails', array() );
538
539                foreach ( $results as $theme => $result ) {
540                        // Maintain last failure notification when themes failed to update manually.
541                        if ( ! $result || is_wp_error( $result ) || ! isset( $past_failure_emails[ $theme ] ) ) {
542                                continue;
543                        }
544
545                        unset( $past_failure_emails[ $theme ] );
546                }
547
548                update_option( 'auto_plugin_theme_update_emails', $past_failure_emails );
549
550                return $results;
551        }
552
553        /**
554         * Checks that the package source contains a valid theme.
555         *
556         * Hooked to the {@see 'upgrader_source_selection'} filter by Theme_Upgrader::install().
557         *
558         * @since 3.3.0
559         *
560         * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
561         * @global string             $wp_version    The WordPress version string.
562         *
563         * @param string $source The path to the downloaded package source.
564         * @return string|WP_Error The source as passed, or a WP_Error object on failure.
565         */
566        public function check_package( $source ) {
567                global $wp_filesystem, $wp_version;
568
569                $this->new_theme_data = array();
570
571                if ( is_wp_error( $source ) ) {
572                        return $source;
573                }
574
575                // Check that the folder contains a valid theme.
576                $working_directory = str_replace( $wp_filesystem->wp_content_dir(), trailingslashit( WP_CONTENT_DIR ), $source );
577                if ( ! is_dir( $working_directory ) ) { // Confidence check, if the above fails, let's not prevent installation.
578                        return $source;
579                }
580
581                // A proper archive should have a style.css file in the single subdirectory.
582                if ( ! file_exists( $working_directory . 'style.css' ) ) {
583                        return new WP_Error(
584                                'incompatible_archive_theme_no_style',
585                                $this->strings['incompatible_archive'],
586                                sprintf(
587                                        /* translators: %s: style.css */
588                                        __( 'The theme is missing the %s stylesheet.' ),
589                                        '<code>style.css</code>'
590                                )
591                        );
592                }
593
594                // All these headers are needed on Theme_Installer_Skin::do_overwrite().
595                $info = get_file_data(
596                        $working_directory . 'style.css',
597                        array(
598                                'Name'        => 'Theme Name',
599                                'Version'     => 'Version',
600                                'Author'      => 'Author',
601                                'Template'    => 'Template',
602                                'RequiresWP'  => 'Requires at least',
603                                'RequiresPHP' => 'Requires PHP',
604                        )
605                );
606
607                if ( empty( $info['Name'] ) ) {
608                        return new WP_Error(
609                                'incompatible_archive_theme_no_name',
610                                $this->strings['incompatible_archive'],
611                                sprintf(
612                                        /* translators: %s: style.css */
613                                        __( 'The %s stylesheet does not contain a valid theme header.' ),
614                                        '<code>style.css</code>'
615                                )
616                        );
617                }
618
619                /*
620                 * Parent themes must contain an index file:
621                 * - classic themes require /index.php
622                 * - block themes require /templates/index.html or block-templates/index.html (deprecated 5.9.0).
623                 */
624                if (
625                        empty( $info['Template'] ) &&
626                        ! file_exists( $working_directory . 'index.php' ) &&
627                        ! file_exists( $working_directory . 'templates/index.html' ) &&
628                        ! file_exists( $working_directory . 'block-templates/index.html' )
629                ) {
630                        return new WP_Error(
631                                'incompatible_archive_theme_no_index',
632                                $this->strings['incompatible_archive'],
633                                sprintf(
634                                        /* translators: 1: templates/index.html, 2: index.php, 3: Documentation URL, 4: Template, 5: style.css */
635                                        __( 'Template is missing. Standalone themes need to have a %1$s or %2$s template file. <a href="%3$s">Child themes</a> need to have a %4$s header in the %5$s stylesheet.' ),
636                                        '<code>templates/index.html</code>',
637                                        '<code>index.php</code>',
638                                        __( 'https://developer.wordpress.org/themes/advanced-topics/child-themes/' ),
639                                        '<code>Template</code>',
640                                        '<code>style.css</code>'
641                                )
642                        );
643                }
644
645                $requires_php = isset( $info['RequiresPHP'] ) ? $info['RequiresPHP'] : null;
646                $requires_wp  = isset( $info['RequiresWP'] ) ? $info['RequiresWP'] : null;
647
648                if ( ! is_php_version_compatible( $requires_php ) ) {
649                        $error = sprintf(
650                                /* translators: 1: Current PHP version, 2: Version required by the uploaded theme. */
651                                __( 'The PHP version on your server is %1$s, however the uploaded theme requires %2$s.' ),
652                                PHP_VERSION,
653                                $requires_php
654                        );
655
656                        return new WP_Error( 'incompatible_php_required_version', $this->strings['incompatible_archive'], $error );
657                }
658                if ( ! is_wp_version_compatible( $requires_wp ) ) {
659                        $error = sprintf(
660                                /* translators: 1: Current WordPress version, 2: Version required by the uploaded theme. */
661                                __( 'Your WordPress version is %1$s, however the uploaded theme requires %2$s.' ),
662                                $wp_version,
663                                $requires_wp
664                        );
665
666                        return new WP_Error( 'incompatible_wp_required_version', $this->strings['incompatible_archive'], $error );
667                }
668
669                $this->new_theme_data = $info;
670
671                return $source;
672        }
673
674        /**
675         * Turns on maintenance mode before attempting to upgrade the active theme.
676         *
677         * Hooked to the {@see 'upgrader_pre_install'} filter by Theme_Upgrader::upgrade() and
678         * Theme_Upgrader::bulk_upgrade().
679         *
680         * @since 2.8.0
681         *
682         * @param bool|WP_Error $response The installation response before the installation has started.
683         * @param array         $theme    Theme arguments.
684         * @return bool|WP_Error The original `$response` parameter or WP_Error.
685         */
686        public function current_before( $response, $theme ) {
687                if ( is_wp_error( $response ) ) {
688                        return $response;
689                }
690
691                $theme = isset( $theme['theme'] ) ? $theme['theme'] : '';
692
693                // Only run if active theme.
694                if ( get_stylesheet() !== $theme ) {
695                        return $response;
696                }
697
698                // Change to maintenance mode. Bulk edit handles this separately.
699                if ( ! $this->bulk ) {
700                        $this->maintenance_mode( true );
701                }
702
703                return $response;
704        }
705
706        /**
707         * Turns off maintenance mode after upgrading the active theme.
708         *
709         * Hooked to the {@see 'upgrader_post_install'} filter by Theme_Upgrader::upgrade()
710         * and Theme_Upgrader::bulk_upgrade().
711         *
712         * @since 2.8.0
713         *
714         * @param bool|WP_Error $response The installation response after the installation has finished.
715         * @param array         $theme    Theme arguments.
716         * @return bool|WP_Error The original `$response` parameter or WP_Error.
717         */
718        public function current_after( $response, $theme ) {
719                if ( is_wp_error( $response ) ) {
720                        return $response;
721                }
722
723                $theme = isset( $theme['theme'] ) ? $theme['theme'] : '';
724
725                // Only run if active theme.
726                if ( get_stylesheet() !== $theme ) {
727                        return $response;
728                }
729
730                // Ensure stylesheet name hasn't changed after the upgrade:
731                if ( get_stylesheet() === $theme && $theme !== $this->result['destination_name'] ) {
732                        wp_clean_themes_cache();
733                        $stylesheet = $this->result['destination_name'];
734                        switch_theme( $stylesheet );
735                }
736
737                // Time to remove maintenance mode. Bulk edit handles this separately.
738                if ( ! $this->bulk ) {
739                        $this->maintenance_mode( false );
740                }
741                return $response;
742        }
743
744        /**
745         * Deletes the old theme during an upgrade.
746         *
747         * Hooked to the {@see 'upgrader_clear_destination'} filter by Theme_Upgrader::upgrade()
748         * and Theme_Upgrader::bulk_upgrade().
749         *
750         * @since 2.8.0
751         *
752         * @global WP_Filesystem_Base $wp_filesystem Subclass
753         *
754         * @param bool   $removed
755         * @param string $local_destination
756         * @param string $remote_destination
757         * @param array  $theme
758         * @return bool
759         */
760        public function delete_old_theme( $removed, $local_destination, $remote_destination, $theme ) {
761                global $wp_filesystem;
762
763                if ( is_wp_error( $removed ) ) {
764                        return $removed; // Pass errors through.
765                }
766
767                if ( ! isset( $theme['theme'] ) ) {
768                        return $removed;
769                }
770
771                $theme      = $theme['theme'];
772                $themes_dir = trailingslashit( $wp_filesystem->wp_themes_dir( $theme ) );
773                if ( $wp_filesystem->exists( $themes_dir . $theme ) ) {
774                        if ( ! $wp_filesystem->delete( $themes_dir . $theme, true ) ) {
775                                return false;
776                        }
777                }
778
779                return true;
780        }
781
782        /**
783         * Gets the WP_Theme object for a theme.
784         *
785         * @since 2.8.0
786         * @since 3.0.0 The `$theme` argument was added.
787         *
788         * @param string $theme The directory name of the theme. This is optional, and if not supplied,
789         *��                     the directory name from the last result will be used.
790         * @return WP_Theme|false The theme's info object, or false `$theme` is not supplied
791         *                        and the last result isn't set.
792         */
793        public function theme_info( $theme = null ) {
794                if ( empty( $theme ) ) {
795                        if ( ! empty( $this->result['destination_name'] ) ) {
796                                $theme = $this->result['destination_name'];
797                        } else {
798                                return false;
799                        }
800                }
801
802                $theme = wp_get_theme( $theme );
803                $theme->cache_delete();
804
805                return $theme;
806        }
807}
Note: See TracBrowser for help on using the repository browser.