37 * @var bool $bulk |
37 * @var bool $bulk |
38 */ |
38 */ |
39 public $bulk = false; |
39 public $bulk = false; |
40 |
40 |
41 /** |
41 /** |
|
42 * New plugin info. |
|
43 * |
|
44 * @since 5.5.0 |
|
45 * @var array $new_plugin_data |
|
46 * |
|
47 * @see check_package() |
|
48 */ |
|
49 public $new_plugin_data = array(); |
|
50 |
|
51 /** |
42 * Initialize the upgrade strings. |
52 * Initialize the upgrade strings. |
43 * |
53 * |
44 * @since 2.8.0 |
54 * @since 2.8.0 |
45 */ |
55 */ |
46 public function upgrade_strings() { |
56 public function upgrade_strings() { |
47 $this->strings['up_to_date'] = __( 'The plugin is at the latest version.' ); |
57 $this->strings['up_to_date'] = __( 'The plugin is at the latest version.' ); |
48 $this->strings['no_package'] = __( 'Update package not available.' ); |
58 $this->strings['no_package'] = __( 'Update package not available.' ); |
49 /* translators: %s: package URL */ |
59 /* translators: %s: Package URL. */ |
50 $this->strings['downloading_package'] = sprintf( __( 'Downloading update from %s…' ), '<span class="code">%s</span>' ); |
60 $this->strings['downloading_package'] = sprintf( __( 'Downloading update from %s…' ), '<span class="code">%s</span>' ); |
51 $this->strings['unpack_package'] = __( 'Unpacking the update…' ); |
61 $this->strings['unpack_package'] = __( 'Unpacking the update…' ); |
52 $this->strings['remove_old'] = __( 'Removing the old version of the plugin…' ); |
62 $this->strings['remove_old'] = __( 'Removing the old version of the plugin…' ); |
53 $this->strings['remove_old_failed'] = __( 'Could not remove the old plugin.' ); |
63 $this->strings['remove_old_failed'] = __( 'Could not remove the old plugin.' ); |
54 $this->strings['process_failed'] = __( 'Plugin update failed.' ); |
64 $this->strings['process_failed'] = __( 'Plugin update failed.' ); |
61 * |
71 * |
62 * @since 2.8.0 |
72 * @since 2.8.0 |
63 */ |
73 */ |
64 public function install_strings() { |
74 public function install_strings() { |
65 $this->strings['no_package'] = __( 'Installation package not available.' ); |
75 $this->strings['no_package'] = __( 'Installation package not available.' ); |
66 /* translators: %s: package URL */ |
76 /* translators: %s: Package URL. */ |
67 $this->strings['downloading_package'] = sprintf( __( 'Downloading installation package from %s…' ), '<span class="code">%s</span>' ); |
77 $this->strings['downloading_package'] = sprintf( __( 'Downloading installation package from %s…' ), '<span class="code">%s</span>' ); |
68 $this->strings['unpack_package'] = __( 'Unpacking the package…' ); |
78 $this->strings['unpack_package'] = __( 'Unpacking the package…' ); |
69 $this->strings['installing_package'] = __( 'Installing the plugin…' ); |
79 $this->strings['installing_package'] = __( 'Installing the plugin…' ); |
|
80 $this->strings['remove_old'] = __( 'Removing the current plugin…' ); |
|
81 $this->strings['remove_old_failed'] = __( 'Could not remove the current plugin.' ); |
70 $this->strings['no_files'] = __( 'The plugin contains no files.' ); |
82 $this->strings['no_files'] = __( 'The plugin contains no files.' ); |
71 $this->strings['process_failed'] = __( 'Plugin installation failed.' ); |
83 $this->strings['process_failed'] = __( 'Plugin installation failed.' ); |
72 $this->strings['process_success'] = __( 'Plugin installed successfully.' ); |
84 $this->strings['process_success'] = __( 'Plugin installed successfully.' ); |
|
85 /* translators: 1: Plugin name, 2: Plugin version. */ |
|
86 $this->strings['process_success_specific'] = __( 'Successfully installed the plugin <strong>%1$s %2$s</strong>.' ); |
|
87 |
|
88 if ( ! empty( $this->skin->overwrite ) ) { |
|
89 if ( 'update-plugin' === $this->skin->overwrite ) { |
|
90 $this->strings['installing_package'] = __( 'Updating the plugin…' ); |
|
91 $this->strings['process_failed'] = __( 'Plugin update failed.' ); |
|
92 $this->strings['process_success'] = __( 'Plugin updated successfully.' ); |
|
93 } |
|
94 |
|
95 if ( 'downgrade-plugin' === $this->skin->overwrite ) { |
|
96 $this->strings['installing_package'] = __( 'Downgrading the plugin…' ); |
|
97 $this->strings['process_failed'] = __( 'Plugin downgrade failed.' ); |
|
98 $this->strings['process_success'] = __( 'Plugin downgraded successfully.' ); |
|
99 } |
|
100 } |
73 } |
101 } |
74 |
102 |
75 /** |
103 /** |
76 * Install a plugin package. |
104 * Install a plugin package. |
77 * |
105 * |
86 * Default true. |
114 * Default true. |
87 * } |
115 * } |
88 * @return bool|WP_Error True if the installation was successful, false or a WP_Error otherwise. |
116 * @return bool|WP_Error True if the installation was successful, false or a WP_Error otherwise. |
89 */ |
117 */ |
90 public function install( $package, $args = array() ) { |
118 public function install( $package, $args = array() ) { |
91 |
|
92 $defaults = array( |
119 $defaults = array( |
93 'clear_update_cache' => true, |
120 'clear_update_cache' => true, |
|
121 'overwrite_package' => false, // Do not overwrite files. |
94 ); |
122 ); |
95 $parsed_args = wp_parse_args( $args, $defaults ); |
123 $parsed_args = wp_parse_args( $args, $defaults ); |
96 |
124 |
97 $this->init(); |
125 $this->init(); |
98 $this->install_strings(); |
126 $this->install_strings(); |
99 |
127 |
100 add_filter( 'upgrader_source_selection', array( $this, 'check_package' ) ); |
128 add_filter( 'upgrader_source_selection', array( $this, 'check_package' ) ); |
|
129 |
101 if ( $parsed_args['clear_update_cache'] ) { |
130 if ( $parsed_args['clear_update_cache'] ) { |
102 // Clear cache so wp_update_plugins() knows about the new plugin. |
131 // Clear cache so wp_update_plugins() knows about the new plugin. |
103 add_action( 'upgrader_process_complete', 'wp_clean_plugins_cache', 9, 0 ); |
132 add_action( 'upgrader_process_complete', 'wp_clean_plugins_cache', 9, 0 ); |
104 } |
133 } |
105 |
134 |
106 $this->run( |
135 $this->run( |
107 array( |
136 array( |
108 'package' => $package, |
137 'package' => $package, |
109 'destination' => WP_PLUGIN_DIR, |
138 'destination' => WP_PLUGIN_DIR, |
110 'clear_destination' => false, // Do not overwrite files. |
139 'clear_destination' => $parsed_args['overwrite_package'], |
111 'clear_working' => true, |
140 'clear_working' => true, |
112 'hook_extra' => array( |
141 'hook_extra' => array( |
113 'type' => 'plugin', |
142 'type' => 'plugin', |
114 'action' => 'install', |
143 'action' => 'install', |
115 ), |
144 ), |
161 $this->skin->error( 'up_to_date' ); |
203 $this->skin->error( 'up_to_date' ); |
162 $this->skin->after(); |
204 $this->skin->after(); |
163 return false; |
205 return false; |
164 } |
206 } |
165 |
207 |
166 // Get the URL to the zip file |
208 // Get the URL to the zip file. |
167 $r = $current->response[ $plugin ]; |
209 $r = $current->response[ $plugin ]; |
168 |
210 |
169 add_filter( 'upgrader_pre_install', array( $this, 'deactivate_plugin_before_upgrade' ), 10, 2 ); |
211 add_filter( 'upgrader_pre_install', array( $this, 'deactivate_plugin_before_upgrade' ), 10, 2 ); |
|
212 add_filter( 'upgrader_pre_install', array( $this, 'active_before' ), 10, 2 ); |
170 add_filter( 'upgrader_clear_destination', array( $this, 'delete_old_plugin' ), 10, 4 ); |
213 add_filter( 'upgrader_clear_destination', array( $this, 'delete_old_plugin' ), 10, 4 ); |
171 //'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. |
214 add_filter( 'upgrader_post_install', array( $this, 'active_after' ), 10, 2 ); |
|
215 // There's a Trac ticket to move up the directory for zips which are made a bit differently, useful for non-.org plugins. |
|
216 // 'source_selection' => array( $this, 'source_selection' ), |
172 if ( $parsed_args['clear_update_cache'] ) { |
217 if ( $parsed_args['clear_update_cache'] ) { |
173 // Clear cache so wp_update_plugins() knows about the new plugin. |
218 // Clear cache so wp_update_plugins() knows about the new plugin. |
174 add_action( 'upgrader_process_complete', 'wp_clean_plugins_cache', 9, 0 ); |
219 add_action( 'upgrader_process_complete', 'wp_clean_plugins_cache', 9, 0 ); |
175 } |
220 } |
176 |
221 |
189 ); |
234 ); |
190 |
235 |
191 // Cleanup our hooks, in case something else does a upgrade on this connection. |
236 // Cleanup our hooks, in case something else does a upgrade on this connection. |
192 remove_action( 'upgrader_process_complete', 'wp_clean_plugins_cache', 9 ); |
237 remove_action( 'upgrader_process_complete', 'wp_clean_plugins_cache', 9 ); |
193 remove_filter( 'upgrader_pre_install', array( $this, 'deactivate_plugin_before_upgrade' ) ); |
238 remove_filter( 'upgrader_pre_install', array( $this, 'deactivate_plugin_before_upgrade' ) ); |
|
239 remove_filter( 'upgrader_pre_install', array( $this, 'active_before' ) ); |
194 remove_filter( 'upgrader_clear_destination', array( $this, 'delete_old_plugin' ) ); |
240 remove_filter( 'upgrader_clear_destination', array( $this, 'delete_old_plugin' ) ); |
|
241 remove_filter( 'upgrader_post_install', array( $this, 'active_after' ) ); |
195 |
242 |
196 if ( ! $this->result || is_wp_error( $this->result ) ) { |
243 if ( ! $this->result || is_wp_error( $this->result ) ) { |
197 return $this->result; |
244 return $this->result; |
198 } |
245 } |
199 |
246 |
200 // Force refresh of plugin update information |
247 // Force refresh of plugin update information. |
201 wp_clean_plugins_cache( $parsed_args['clear_update_cache'] ); |
248 wp_clean_plugins_cache( $parsed_args['clear_update_cache'] ); |
|
249 |
|
250 // Ensure any future auto-update failures trigger a failure email by removing |
|
251 // the last failure notification from the list when plugins update successfully. |
|
252 $past_failure_emails = get_option( 'auto_plugin_theme_update_emails', array() ); |
|
253 |
|
254 if ( isset( $past_failure_emails[ $plugin ] ) ) { |
|
255 unset( $past_failure_emails[ $plugin ] ); |
|
256 update_option( 'auto_plugin_theme_update_emails', $past_failure_emails ); |
|
257 } |
202 |
258 |
203 return true; |
259 return true; |
204 } |
260 } |
205 |
261 |
206 /** |
262 /** |
342 * if no plugins were found. |
412 * if no plugins were found. |
343 */ |
413 */ |
344 public function check_package( $source ) { |
414 public function check_package( $source ) { |
345 global $wp_filesystem; |
415 global $wp_filesystem; |
346 |
416 |
|
417 $this->new_plugin_data = array(); |
|
418 |
347 if ( is_wp_error( $source ) ) { |
419 if ( is_wp_error( $source ) ) { |
348 return $source; |
420 return $source; |
349 } |
421 } |
350 |
422 |
351 $working_directory = str_replace( $wp_filesystem->wp_content_dir(), trailingslashit( WP_CONTENT_DIR ), $source ); |
423 $working_directory = str_replace( $wp_filesystem->wp_content_dir(), trailingslashit( WP_CONTENT_DIR ), $source ); |
352 if ( ! is_dir( $working_directory ) ) { // Sanity check, if the above fails, let's not prevent installation. |
424 if ( ! is_dir( $working_directory ) ) { // Sanity check, if the above fails, let's not prevent installation. |
353 return $source; |
425 return $source; |
354 } |
426 } |
355 |
427 |
356 // Check the folder contains at least 1 valid plugin. |
428 // Check that the folder contains at least 1 valid plugin. |
357 $plugins_found = false; |
429 $files = glob( $working_directory . '*.php' ); |
358 $files = glob( $working_directory . '*.php' ); |
|
359 if ( $files ) { |
430 if ( $files ) { |
360 foreach ( $files as $file ) { |
431 foreach ( $files as $file ) { |
361 $info = get_plugin_data( $file, false, false ); |
432 $info = get_plugin_data( $file, false, false ); |
362 if ( ! empty( $info['Name'] ) ) { |
433 if ( ! empty( $info['Name'] ) ) { |
363 $plugins_found = true; |
434 $this->new_plugin_data = $info; |
364 break; |
435 break; |
365 } |
436 } |
366 } |
437 } |
367 } |
438 } |
368 |
439 |
369 if ( ! $plugins_found ) { |
440 if ( empty( $this->new_plugin_data ) ) { |
370 return new WP_Error( 'incompatible_archive_no_plugins', $this->strings['incompatible_archive'], __( 'No valid plugins were found.' ) ); |
441 return new WP_Error( 'incompatible_archive_no_plugins', $this->strings['incompatible_archive'], __( 'No valid plugins were found.' ) ); |
|
442 } |
|
443 |
|
444 $requires_php = isset( $info['RequiresPHP'] ) ? $info['RequiresPHP'] : null; |
|
445 $requires_wp = isset( $info['RequiresWP'] ) ? $info['RequiresWP'] : null; |
|
446 |
|
447 if ( ! is_php_version_compatible( $requires_php ) ) { |
|
448 $error = sprintf( |
|
449 /* translators: 1: Current PHP version, 2: Version required by the uploaded plugin. */ |
|
450 __( 'The PHP version on your server is %1$s, however the uploaded plugin requires %2$s.' ), |
|
451 phpversion(), |
|
452 $requires_php |
|
453 ); |
|
454 |
|
455 return new WP_Error( 'incompatible_php_required_version', $this->strings['incompatible_archive'], $error ); |
|
456 } |
|
457 |
|
458 if ( ! is_wp_version_compatible( $requires_wp ) ) { |
|
459 $error = sprintf( |
|
460 /* translators: 1: Current WordPress version, 2: Version required by the uploaded plugin. */ |
|
461 __( 'Your WordPress version is %1$s, however the uploaded plugin requires %2$s.' ), |
|
462 $GLOBALS['wp_version'], |
|
463 $requires_wp |
|
464 ); |
|
465 |
|
466 return new WP_Error( 'incompatible_wp_required_version', $this->strings['incompatible_archive'], $error ); |
371 } |
467 } |
372 |
468 |
373 return $source; |
469 return $source; |
374 } |
470 } |
375 |
471 |
406 * Hooked to the {@see 'upgrader_pre_install'} filter by Plugin_Upgrader::upgrade(). |
504 * Hooked to the {@see 'upgrader_pre_install'} filter by Plugin_Upgrader::upgrade(). |
407 * |
505 * |
408 * @since 2.8.0 |
506 * @since 2.8.0 |
409 * @since 4.1.0 Added a return value. |
507 * @since 4.1.0 Added a return value. |
410 * |
508 * |
411 * @param bool|WP_Error $return Upgrade offer return. |
509 * @param bool|WP_Error $return Upgrade offer return. |
412 * @param array $plugin Plugin package arguments. |
510 * @param array $plugin Plugin package arguments. |
413 * @return bool|WP_Error The passed in $return param or WP_Error. |
511 * @return bool|WP_Error The passed in $return param or WP_Error. |
414 */ |
512 */ |
415 public function deactivate_plugin_before_upgrade( $return, $plugin ) { |
513 public function deactivate_plugin_before_upgrade( $return, $plugin ) { |
416 |
514 |
417 if ( is_wp_error( $return ) ) { //Bypass. |
515 if ( is_wp_error( $return ) ) { // Bypass. |
418 return $return; |
516 return $return; |
419 } |
517 } |
420 |
518 |
421 // When in cron (background updates) don't deactivate the plugin, as we require a browser to reactivate it |
519 // When in cron (background updates) don't deactivate the plugin, as we require a browser to reactivate it. |
422 if ( wp_doing_cron() ) { |
520 if ( wp_doing_cron() ) { |
423 return $return; |
521 return $return; |
424 } |
522 } |
425 |
523 |
426 $plugin = isset( $plugin['plugin'] ) ? $plugin['plugin'] : ''; |
524 $plugin = isset( $plugin['plugin'] ) ? $plugin['plugin'] : ''; |
427 if ( empty( $plugin ) ) { |
525 if ( empty( $plugin ) ) { |
428 return new WP_Error( 'bad_request', $this->strings['bad_request'] ); |
526 return new WP_Error( 'bad_request', $this->strings['bad_request'] ); |
429 } |
527 } |
430 |
528 |
431 if ( is_plugin_active( $plugin ) ) { |
529 if ( is_plugin_active( $plugin ) ) { |
432 //Deactivate the plugin silently, Prevent deactivation hooks from running. |
530 // Deactivate the plugin silently, Prevent deactivation hooks from running. |
433 deactivate_plugins( $plugin, true ); |
531 deactivate_plugins( $plugin, true ); |
434 } |
532 } |
435 |
533 |
436 return $return; |
534 return $return; |
437 } |
535 } |
438 |
536 |
439 /** |
537 /** |
440 * Delete the old plugin during an upgrade. |
538 * Turns on maintenance mode before attempting to background update an active plugin. |
|
539 * |
|
540 * Hooked to the {@see 'upgrader_pre_install'} filter by Plugin_Upgrader::upgrade(). |
|
541 * |
|
542 * @since 5.4.0 |
|
543 * |
|
544 * @param bool|WP_Error $return Upgrade offer return. |
|
545 * @param array $plugin Plugin package arguments. |
|
546 * @return bool|WP_Error The passed in $return param or WP_Error. |
|
547 */ |
|
548 public function active_before( $return, $plugin ) { |
|
549 if ( is_wp_error( $return ) ) { |
|
550 return $return; |
|
551 } |
|
552 |
|
553 // Only enable maintenance mode when in cron (background update). |
|
554 if ( ! wp_doing_cron() ) { |
|
555 return $return; |
|
556 } |
|
557 |
|
558 $plugin = isset( $plugin['plugin'] ) ? $plugin['plugin'] : ''; |
|
559 |
|
560 // Only run if plugin is active. |
|
561 if ( ! is_plugin_active( $plugin ) ) { |
|
562 return $return; |
|
563 } |
|
564 |
|
565 // Change to maintenance mode. Bulk edit handles this separately. |
|
566 if ( ! $this->bulk ) { |
|
567 $this->maintenance_mode( true ); |
|
568 } |
|
569 |
|
570 return $return; |
|
571 } |
|
572 |
|
573 /** |
|
574 * Turns off maintenance mode after upgrading an active plugin. |
|
575 * |
|
576 * Hooked to the {@see 'upgrader_post_install'} filter by Plugin_Upgrader::upgrade(). |
|
577 * |
|
578 * @since 5.4.0 |
|
579 * |
|
580 * @param bool|WP_Error $return Upgrade offer return. |
|
581 * @param array $plugin Plugin package arguments. |
|
582 * @return bool|WP_Error The passed in $return param or WP_Error. |
|
583 */ |
|
584 public function active_after( $return, $plugin ) { |
|
585 if ( is_wp_error( $return ) ) { |
|
586 return $return; |
|
587 } |
|
588 |
|
589 // Only disable maintenance mode when in cron (background update). |
|
590 if ( ! wp_doing_cron() ) { |
|
591 return $return; |
|
592 } |
|
593 |
|
594 $plugin = isset( $plugin['plugin'] ) ? $plugin['plugin'] : ''; |
|
595 |
|
596 // Only run if plugin is active |
|
597 if ( ! is_plugin_active( $plugin ) ) { |
|
598 return $return; |
|
599 } |
|
600 |
|
601 // Time to remove maintenance mode. Bulk edit handles this separately. |
|
602 if ( ! $this->bulk ) { |
|
603 $this->maintenance_mode( false ); |
|
604 } |
|
605 |
|
606 return $return; |
|
607 } |
|
608 |
|
609 /** |
|
610 * Deletes the old plugin during an upgrade. |
441 * |
611 * |
442 * Hooked to the {@see 'upgrader_clear_destination'} filter by |
612 * Hooked to the {@see 'upgrader_clear_destination'} filter by |
443 * Plugin_Upgrader::upgrade() and Plugin_Upgrader::bulk_upgrade(). |
613 * Plugin_Upgrader::upgrade() and Plugin_Upgrader::bulk_upgrade(). |
444 * |
614 * |
445 * @since 2.8.0 |
615 * @since 2.8.0 |
446 * |
616 * |
447 * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. |
617 * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. |
448 * |
618 * |
449 * @param bool|WP_Error $removed |
619 * @param bool|WP_Error $removed Whether the destination was cleared. |
450 * @param string $local_destination |
620 * True on success, WP_Error on failure. |
451 * @param string $remote_destination |
621 * @param string $local_destination The local package destination. |
452 * @param array $plugin |
622 * @param string $remote_destination The remote package destination. |
453 * @return WP_Error|bool |
623 * @param array $plugin Extra arguments passed to hooked filters. |
|
624 * @return bool|WP_Error |
454 */ |
625 */ |
455 public function delete_old_plugin( $removed, $local_destination, $remote_destination, $plugin ) { |
626 public function delete_old_plugin( $removed, $local_destination, $remote_destination, $plugin ) { |
456 global $wp_filesystem; |
627 global $wp_filesystem; |
457 |
628 |
458 if ( is_wp_error( $removed ) ) { |
629 if ( is_wp_error( $removed ) ) { |
459 return $removed; //Pass errors through. |
630 return $removed; // Pass errors through. |
460 } |
631 } |
461 |
632 |
462 $plugin = isset( $plugin['plugin'] ) ? $plugin['plugin'] : ''; |
633 $plugin = isset( $plugin['plugin'] ) ? $plugin['plugin'] : ''; |
463 if ( empty( $plugin ) ) { |
634 if ( empty( $plugin ) ) { |
464 return new WP_Error( 'bad_request', $this->strings['bad_request'] ); |
635 return new WP_Error( 'bad_request', $this->strings['bad_request'] ); |
465 } |
636 } |
466 |
637 |
467 $plugins_dir = $wp_filesystem->wp_plugins_dir(); |
638 $plugins_dir = $wp_filesystem->wp_plugins_dir(); |
468 $this_plugin_dir = trailingslashit( dirname( $plugins_dir . $plugin ) ); |
639 $this_plugin_dir = trailingslashit( dirname( $plugins_dir . $plugin ) ); |
469 |
640 |
470 if ( ! $wp_filesystem->exists( $this_plugin_dir ) ) { //If it's already vanished. |
641 if ( ! $wp_filesystem->exists( $this_plugin_dir ) ) { // If it's already vanished. |
471 return $removed; |
642 return $removed; |
472 } |
643 } |
473 |
644 |
474 // If plugin is in its own directory, recursively delete the directory. |
645 // If plugin is in its own directory, recursively delete the directory. |
475 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 |
646 // Base check on if plugin includes directory separator AND that it's not the root plugin folder. |
|
647 if ( strpos( $plugin, '/' ) && $this_plugin_dir !== $plugins_dir ) { |
476 $deleted = $wp_filesystem->delete( $this_plugin_dir, true ); |
648 $deleted = $wp_filesystem->delete( $this_plugin_dir, true ); |
477 } else { |
649 } else { |
478 $deleted = $wp_filesystem->delete( $plugins_dir . $plugin ); |
650 $deleted = $wp_filesystem->delete( $plugins_dir . $plugin ); |
479 } |
651 } |
480 |
652 |