47 * @see check_package() |
47 * @see check_package() |
48 */ |
48 */ |
49 public $new_plugin_data = array(); |
49 public $new_plugin_data = array(); |
50 |
50 |
51 /** |
51 /** |
52 * Initialize the upgrade strings. |
52 * Initializes the upgrade strings. |
53 * |
53 * |
54 * @since 2.8.0 |
54 * @since 2.8.0 |
55 */ |
55 */ |
56 public function upgrade_strings() { |
56 public function upgrade_strings() { |
57 $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.' ); |
58 $this->strings['no_package'] = __( 'Update package not available.' ); |
58 $this->strings['no_package'] = __( 'Update package not available.' ); |
59 /* translators: %s: Package URL. */ |
59 /* translators: %s: Package URL. */ |
60 $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 pre">%s</span>' ); |
61 $this->strings['unpack_package'] = __( 'Unpacking the update…' ); |
61 $this->strings['unpack_package'] = __( 'Unpacking the update…' ); |
62 $this->strings['remove_old'] = __( 'Removing the old version of the plugin…' ); |
62 $this->strings['remove_old'] = __( 'Removing the old version of the plugin…' ); |
63 $this->strings['remove_old_failed'] = __( 'Could not remove the old plugin.' ); |
63 $this->strings['remove_old_failed'] = __( 'Could not remove the old plugin.' ); |
64 $this->strings['process_failed'] = __( 'Plugin update failed.' ); |
64 $this->strings['process_failed'] = __( 'Plugin update failed.' ); |
65 $this->strings['process_success'] = __( 'Plugin updated successfully.' ); |
65 $this->strings['process_success'] = __( 'Plugin updated successfully.' ); |
66 $this->strings['process_bulk_success'] = __( 'Plugins updated successfully.' ); |
66 $this->strings['process_bulk_success'] = __( 'Plugins updated successfully.' ); |
67 } |
67 } |
68 |
68 |
69 /** |
69 /** |
70 * Initialize the installation strings. |
70 * Initializes the installation strings. |
71 * |
71 * |
72 * @since 2.8.0 |
72 * @since 2.8.0 |
73 */ |
73 */ |
74 public function install_strings() { |
74 public function install_strings() { |
75 $this->strings['no_package'] = __( 'Installation package not available.' ); |
75 $this->strings['no_package'] = __( 'Installation package not available.' ); |
76 /* translators: %s: Package URL. */ |
76 /* translators: %s: Package URL. */ |
77 $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 pre">%s</span>' ); |
78 $this->strings['unpack_package'] = __( 'Unpacking the package…' ); |
78 $this->strings['unpack_package'] = __( 'Unpacking the package…' ); |
79 $this->strings['installing_package'] = __( 'Installing the plugin…' ); |
79 $this->strings['installing_package'] = __( 'Installing the plugin…' ); |
80 $this->strings['remove_old'] = __( 'Removing the current plugin…' ); |
80 $this->strings['remove_old'] = __( 'Removing the current plugin…' ); |
81 $this->strings['remove_old_failed'] = __( 'Could not remove the current plugin.' ); |
81 $this->strings['remove_old_failed'] = __( 'Could not remove the current plugin.' ); |
82 $this->strings['no_files'] = __( 'The plugin contains no files.' ); |
82 $this->strings['no_files'] = __( 'The plugin contains no files.' ); |
210 |
210 |
211 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 ); |
212 add_filter( 'upgrader_pre_install', array( $this, 'active_before' ), 10, 2 ); |
213 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 ); |
214 add_filter( 'upgrader_post_install', array( $this, 'active_after' ), 10, 2 ); |
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. |
215 /* |
216 // 'source_selection' => array( $this, 'source_selection' ), |
216 * There's a Trac ticket to move up the directory for zips which are made a bit differently, useful for non-.org plugins. |
|
217 * 'source_selection' => array( $this, 'source_selection' ), |
|
218 */ |
217 if ( $parsed_args['clear_update_cache'] ) { |
219 if ( $parsed_args['clear_update_cache'] ) { |
218 // Clear cache so wp_update_plugins() knows about the new plugin. |
220 // Clear cache so wp_update_plugins() knows about the new plugin. |
219 add_action( 'upgrader_process_complete', 'wp_clean_plugins_cache', 9, 0 ); |
221 add_action( 'upgrader_process_complete', 'wp_clean_plugins_cache', 9, 0 ); |
220 } |
222 } |
221 |
223 |
224 'package' => $r->package, |
226 'package' => $r->package, |
225 'destination' => WP_PLUGIN_DIR, |
227 'destination' => WP_PLUGIN_DIR, |
226 'clear_destination' => true, |
228 'clear_destination' => true, |
227 'clear_working' => true, |
229 'clear_working' => true, |
228 'hook_extra' => array( |
230 'hook_extra' => array( |
229 'plugin' => $plugin, |
231 'plugin' => $plugin, |
230 'type' => 'plugin', |
232 'type' => 'plugin', |
231 'action' => 'update', |
233 'action' => 'update', |
|
234 'temp_backup' => array( |
|
235 'slug' => dirname( $plugin ), |
|
236 'src' => WP_PLUGIN_DIR, |
|
237 'dir' => 'plugins', |
|
238 ), |
232 ), |
239 ), |
233 ) |
240 ) |
234 ); |
241 ); |
235 |
242 |
236 // Cleanup our hooks, in case something else does a upgrade on this connection. |
243 // Cleanup our hooks, in case something else does an upgrade on this connection. |
237 remove_action( 'upgrader_process_complete', 'wp_clean_plugins_cache', 9 ); |
244 remove_action( 'upgrader_process_complete', 'wp_clean_plugins_cache', 9 ); |
238 remove_filter( 'upgrader_pre_install', array( $this, 'deactivate_plugin_before_upgrade' ) ); |
245 remove_filter( 'upgrader_pre_install', array( $this, 'deactivate_plugin_before_upgrade' ) ); |
239 remove_filter( 'upgrader_pre_install', array( $this, 'active_before' ) ); |
246 remove_filter( 'upgrader_pre_install', array( $this, 'active_before' ) ); |
240 remove_filter( 'upgrader_clear_destination', array( $this, 'delete_old_plugin' ) ); |
247 remove_filter( 'upgrader_clear_destination', array( $this, 'delete_old_plugin' ) ); |
241 remove_filter( 'upgrader_post_install', array( $this, 'active_after' ) ); |
248 remove_filter( 'upgrader_post_install', array( $this, 'active_after' ) ); |
245 } |
252 } |
246 |
253 |
247 // Force refresh of plugin update information. |
254 // Force refresh of plugin update information. |
248 wp_clean_plugins_cache( $parsed_args['clear_update_cache'] ); |
255 wp_clean_plugins_cache( $parsed_args['clear_update_cache'] ); |
249 |
256 |
250 // Ensure any future auto-update failures trigger a failure email by removing |
257 /* |
251 // the last failure notification from the list when plugins update successfully. |
258 * Ensure any future auto-update failures trigger a failure email by removing |
|
259 * the last failure notification from the list when plugins update successfully. |
|
260 */ |
252 $past_failure_emails = get_option( 'auto_plugin_theme_update_emails', array() ); |
261 $past_failure_emails = get_option( 'auto_plugin_theme_update_emails', array() ); |
253 |
262 |
254 if ( isset( $past_failure_emails[ $plugin ] ) ) { |
263 if ( isset( $past_failure_emails[ $plugin ] ) ) { |
255 unset( $past_failure_emails[ $plugin ] ); |
264 unset( $past_failure_emails[ $plugin ] ); |
256 update_option( 'auto_plugin_theme_update_emails', $past_failure_emails ); |
265 update_option( 'auto_plugin_theme_update_emails', $past_failure_emails ); |
258 |
267 |
259 return true; |
268 return true; |
260 } |
269 } |
261 |
270 |
262 /** |
271 /** |
263 * Bulk upgrade several plugins at once. |
272 * Upgrades several plugins at once. |
264 * |
273 * |
265 * @since 2.8.0 |
274 * @since 2.8.0 |
266 * @since 3.7.0 The `$args` parameter was added, making clearing the plugin update cache optional. |
275 * @since 3.7.0 The `$args` parameter was added, making clearing the plugin update cache optional. |
|
276 * |
|
277 * @global string $wp_version The WordPress version string. |
267 * |
278 * |
268 * @param string[] $plugins Array of paths to plugin files relative to the plugins directory. |
279 * @param string[] $plugins Array of paths to plugin files relative to the plugins directory. |
269 * @param array $args { |
280 * @param array $args { |
270 * Optional. Other arguments for upgrading several plugins at once. |
281 * Optional. Other arguments for upgrading several plugins at once. |
271 * |
282 * |
272 * @type bool $clear_update_cache Whether to clear the plugin updates cache if successful. Default true. |
283 * @type bool $clear_update_cache Whether to clear the plugin updates cache if successful. Default true. |
273 * } |
284 * } |
274 * @return array|false An array of results indexed by plugin file, or false if unable to connect to the filesystem. |
285 * @return array|false An array of results indexed by plugin file, or false if unable to connect to the filesystem. |
275 */ |
286 */ |
276 public function bulk_upgrade( $plugins, $args = array() ) { |
287 public function bulk_upgrade( $plugins, $args = array() ) { |
|
288 global $wp_version; |
|
289 |
277 $defaults = array( |
290 $defaults = array( |
278 'clear_update_cache' => true, |
291 'clear_update_cache' => true, |
279 ); |
292 ); |
280 $parsed_args = wp_parse_args( $args, $defaults ); |
293 $parsed_args = wp_parse_args( $args, $defaults ); |
281 |
294 |
315 $results = array(); |
328 $results = array(); |
316 |
329 |
317 $this->update_count = count( $plugins ); |
330 $this->update_count = count( $plugins ); |
318 $this->update_current = 0; |
331 $this->update_current = 0; |
319 foreach ( $plugins as $plugin ) { |
332 foreach ( $plugins as $plugin ) { |
320 $this->update_current++; |
333 ++$this->update_current; |
321 $this->skin->plugin_info = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin, false, true ); |
334 $this->skin->plugin_info = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin, false, true ); |
322 |
335 |
323 if ( ! isset( $current->response[ $plugin ] ) ) { |
336 if ( ! isset( $current->response[ $plugin ] ) ) { |
324 $this->skin->set_result( 'up_to_date' ); |
337 $this->skin->set_result( 'up_to_date' ); |
325 $this->skin->before(); |
338 $this->skin->before(); |
332 // Get the URL to the zip file. |
345 // Get the URL to the zip file. |
333 $r = $current->response[ $plugin ]; |
346 $r = $current->response[ $plugin ]; |
334 |
347 |
335 $this->skin->plugin_active = is_plugin_active( $plugin ); |
348 $this->skin->plugin_active = is_plugin_active( $plugin ); |
336 |
349 |
337 $result = $this->run( |
350 if ( isset( $r->requires ) && ! is_wp_version_compatible( $r->requires ) ) { |
338 array( |
351 $result = new WP_Error( |
339 'package' => $r->package, |
352 'incompatible_wp_required_version', |
340 'destination' => WP_PLUGIN_DIR, |
353 sprintf( |
341 'clear_destination' => true, |
354 /* translators: 1: Current WordPress version, 2: WordPress version required by the new plugin version. */ |
342 'clear_working' => true, |
355 __( 'Your WordPress version is %1$s, however the new plugin version requires %2$s.' ), |
343 'is_multi' => true, |
356 $wp_version, |
344 'hook_extra' => array( |
357 $r->requires |
345 'plugin' => $plugin, |
358 ) |
346 ), |
359 ); |
347 ) |
360 |
348 ); |
361 $this->skin->before( $result ); |
|
362 $this->skin->error( $result ); |
|
363 $this->skin->after(); |
|
364 } elseif ( isset( $r->requires_php ) && ! is_php_version_compatible( $r->requires_php ) ) { |
|
365 $result = new WP_Error( |
|
366 'incompatible_php_required_version', |
|
367 sprintf( |
|
368 /* translators: 1: Current PHP version, 2: PHP version required by the new plugin version. */ |
|
369 __( 'The PHP version on your server is %1$s, however the new plugin version requires %2$s.' ), |
|
370 PHP_VERSION, |
|
371 $r->requires_php |
|
372 ) |
|
373 ); |
|
374 |
|
375 $this->skin->before( $result ); |
|
376 $this->skin->error( $result ); |
|
377 $this->skin->after(); |
|
378 } else { |
|
379 add_filter( 'upgrader_source_selection', array( $this, 'check_package' ) ); |
|
380 $result = $this->run( |
|
381 array( |
|
382 'package' => $r->package, |
|
383 'destination' => WP_PLUGIN_DIR, |
|
384 'clear_destination' => true, |
|
385 'clear_working' => true, |
|
386 'is_multi' => true, |
|
387 'hook_extra' => array( |
|
388 'plugin' => $plugin, |
|
389 'temp_backup' => array( |
|
390 'slug' => dirname( $plugin ), |
|
391 'src' => WP_PLUGIN_DIR, |
|
392 'dir' => 'plugins', |
|
393 ), |
|
394 ), |
|
395 ) |
|
396 ); |
|
397 remove_filter( 'upgrader_source_selection', array( $this, 'check_package' ) ); |
|
398 } |
349 |
399 |
350 $results[ $plugin ] = $result; |
400 $results[ $plugin ] = $result; |
351 |
401 |
352 // Prevent credentials auth screen from displaying multiple times. |
402 // Prevent credentials auth screen from displaying multiple times. |
353 if ( false === $result ) { |
403 if ( false === $result ) { |
374 |
424 |
375 $this->skin->bulk_footer(); |
425 $this->skin->bulk_footer(); |
376 |
426 |
377 $this->skin->footer(); |
427 $this->skin->footer(); |
378 |
428 |
379 // Cleanup our hooks, in case something else does a upgrade on this connection. |
429 // Cleanup our hooks, in case something else does an upgrade on this connection. |
380 remove_filter( 'upgrader_clear_destination', array( $this, 'delete_old_plugin' ) ); |
430 remove_filter( 'upgrader_clear_destination', array( $this, 'delete_old_plugin' ) ); |
381 |
431 |
382 // Ensure any future auto-update failures trigger a failure email by removing |
432 /* |
383 // the last failure notification from the list when plugins update successfully. |
433 * Ensure any future auto-update failures trigger a failure email by removing |
|
434 * the last failure notification from the list when plugins update successfully. |
|
435 */ |
384 $past_failure_emails = get_option( 'auto_plugin_theme_update_emails', array() ); |
436 $past_failure_emails = get_option( 'auto_plugin_theme_update_emails', array() ); |
385 |
437 |
386 foreach ( $results as $plugin => $result ) { |
438 foreach ( $results as $plugin => $result ) { |
387 // Maintain last failure notification when plugins failed to update manually. |
439 // Maintain last failure notification when plugins failed to update manually. |
388 if ( ! $result || is_wp_error( $result ) || ! isset( $past_failure_emails[ $plugin ] ) ) { |
440 if ( ! $result || is_wp_error( $result ) || ! isset( $past_failure_emails[ $plugin ] ) ) { |
418 if ( is_wp_error( $source ) ) { |
470 if ( is_wp_error( $source ) ) { |
419 return $source; |
471 return $source; |
420 } |
472 } |
421 |
473 |
422 $working_directory = str_replace( $wp_filesystem->wp_content_dir(), trailingslashit( WP_CONTENT_DIR ), $source ); |
474 $working_directory = str_replace( $wp_filesystem->wp_content_dir(), trailingslashit( WP_CONTENT_DIR ), $source ); |
423 if ( ! is_dir( $working_directory ) ) { // Sanity check, if the above fails, let's not prevent installation. |
475 if ( ! is_dir( $working_directory ) ) { // Confidence check, if the above fails, let's not prevent installation. |
424 return $source; |
476 return $source; |
425 } |
477 } |
426 |
478 |
427 // Check that the folder contains at least 1 valid plugin. |
479 // Check that the folder contains at least 1 valid plugin. |
428 $files = glob( $working_directory . '*.php' ); |
480 $files = glob( $working_directory . '*.php' ); |
445 |
497 |
446 if ( ! is_php_version_compatible( $requires_php ) ) { |
498 if ( ! is_php_version_compatible( $requires_php ) ) { |
447 $error = sprintf( |
499 $error = sprintf( |
448 /* translators: 1: Current PHP version, 2: Version required by the uploaded plugin. */ |
500 /* translators: 1: Current PHP version, 2: Version required by the uploaded plugin. */ |
449 __( 'The PHP version on your server is %1$s, however the uploaded plugin requires %2$s.' ), |
501 __( 'The PHP version on your server is %1$s, however the uploaded plugin requires %2$s.' ), |
450 phpversion(), |
502 PHP_VERSION, |
451 $requires_php |
503 $requires_php |
452 ); |
504 ); |
453 |
505 |
454 return new WP_Error( 'incompatible_php_required_version', $this->strings['incompatible_archive'], $error ); |
506 return new WP_Error( 'incompatible_php_required_version', $this->strings['incompatible_archive'], $error ); |
455 } |
507 } |
639 |
691 |
640 if ( ! $wp_filesystem->exists( $this_plugin_dir ) ) { // If it's already vanished. |
692 if ( ! $wp_filesystem->exists( $this_plugin_dir ) ) { // If it's already vanished. |
641 return $removed; |
693 return $removed; |
642 } |
694 } |
643 |
695 |
644 // If plugin is in its own directory, recursively delete the directory. |
696 /* |
645 // Base check on if plugin includes directory separator AND that it's not the root plugin folder. |
697 * If plugin is in its own directory, recursively delete the directory. |
|
698 * Base check on if plugin includes directory separator AND that it's not the root plugin folder. |
|
699 */ |
646 if ( strpos( $plugin, '/' ) && $this_plugin_dir !== $plugins_dir ) { |
700 if ( strpos( $plugin, '/' ) && $this_plugin_dir !== $plugins_dir ) { |
647 $deleted = $wp_filesystem->delete( $this_plugin_dir, true ); |
701 $deleted = $wp_filesystem->delete( $this_plugin_dir, true ); |
648 } else { |
702 } else { |
649 $deleted = $wp_filesystem->delete( $plugins_dir . $plugin ); |
703 $deleted = $wp_filesystem->delete( $plugins_dir . $plugin ); |
650 } |
704 } |