51 * @since 3.7.0 |
54 * @since 3.7.0 |
52 * |
55 * |
53 * @param bool $disabled Whether the updater should be disabled. |
56 * @param bool $disabled Whether the updater should be disabled. |
54 */ |
57 */ |
55 return apply_filters( 'automatic_updater_disabled', $disabled ); |
58 return apply_filters( 'automatic_updater_disabled', $disabled ); |
|
59 } |
|
60 |
|
61 /** |
|
62 * Checks whether access to a given directory is allowed. |
|
63 * |
|
64 * This is used when detecting version control checkouts. Takes into account |
|
65 * the PHP `open_basedir` restrictions, so that WordPress does not try to access |
|
66 * directories it is not allowed to. |
|
67 * |
|
68 * @since 6.2.0 |
|
69 * |
|
70 * @param string $dir The directory to check. |
|
71 * @return bool True if access to the directory is allowed, false otherwise. |
|
72 */ |
|
73 public function is_allowed_dir( $dir ) { |
|
74 if ( is_string( $dir ) ) { |
|
75 $dir = trim( $dir ); |
|
76 } |
|
77 |
|
78 if ( ! is_string( $dir ) || '' === $dir ) { |
|
79 _doing_it_wrong( |
|
80 __METHOD__, |
|
81 sprintf( |
|
82 /* translators: %s: The "$dir" argument. */ |
|
83 __( 'The "%s" argument must be a non-empty string.' ), |
|
84 '$dir' |
|
85 ), |
|
86 '6.2.0' |
|
87 ); |
|
88 |
|
89 return false; |
|
90 } |
|
91 |
|
92 $open_basedir = ini_get( 'open_basedir' ); |
|
93 |
|
94 if ( empty( $open_basedir ) ) { |
|
95 return true; |
|
96 } |
|
97 |
|
98 $open_basedir_list = explode( PATH_SEPARATOR, $open_basedir ); |
|
99 |
|
100 foreach ( $open_basedir_list as $basedir ) { |
|
101 if ( '' !== trim( $basedir ) && str_starts_with( $dir, $basedir ) ) { |
|
102 return true; |
|
103 } |
|
104 } |
|
105 |
|
106 return false; |
56 } |
107 } |
57 |
108 |
58 /** |
109 /** |
59 * Checks for version control checkouts. |
110 * Checks for version control checkouts. |
60 * |
111 * |
220 |
276 |
221 // If it's a core update, are we actually compatible with its requirements? |
277 // If it's a core update, are we actually compatible with its requirements? |
222 if ( 'core' === $type ) { |
278 if ( 'core' === $type ) { |
223 global $wpdb; |
279 global $wpdb; |
224 |
280 |
225 $php_compat = version_compare( phpversion(), $item->php_version, '>=' ); |
281 $php_compat = version_compare( PHP_VERSION, $item->php_version, '>=' ); |
226 if ( file_exists( WP_CONTENT_DIR . '/db.php' ) && empty( $wpdb->is_mysql ) ) { |
282 if ( file_exists( WP_CONTENT_DIR . '/db.php' ) && empty( $wpdb->is_mysql ) ) { |
227 $mysql_compat = true; |
283 $mysql_compat = true; |
228 } else { |
284 } else { |
229 $mysql_compat = version_compare( $wpdb->db_version(), $item->mysql_version, '>=' ); |
285 $mysql_compat = version_compare( $wpdb->db_version(), $item->mysql_version, '>=' ); |
230 } |
286 } |
302 * @param string $type The type of update being checked: 'core', 'theme', 'plugin', 'translation'. |
358 * @param string $type The type of update being checked: 'core', 'theme', 'plugin', 'translation'. |
303 * @param object $item The update offer. |
359 * @param object $item The update offer. |
304 * @return null|WP_Error |
360 * @return null|WP_Error |
305 */ |
361 */ |
306 public function update( $type, $item ) { |
362 public function update( $type, $item ) { |
307 $skin = new Automatic_Upgrader_Skin; |
363 $skin = new Automatic_Upgrader_Skin(); |
308 |
364 |
309 switch ( $type ) { |
365 switch ( $type ) { |
310 case 'core': |
366 case 'core': |
311 // The Core upgrader doesn't use the Upgrader's skin during the actual main part of the upgrade, instead, firing a filter. |
367 // The Core upgrader doesn't use the Upgrader's skin during the actual main part of the upgrade, instead, firing a filter. |
312 add_filter( 'update_feedback', array( $skin, 'feedback' ) ); |
368 add_filter( 'update_feedback', array( $skin, 'feedback' ) ); |
388 $allow_relaxed_file_ownership = false; |
444 $allow_relaxed_file_ownership = false; |
389 if ( 'core' === $type && isset( $item->new_files ) && ! $item->new_files ) { |
445 if ( 'core' === $type && isset( $item->new_files ) && ! $item->new_files ) { |
390 $allow_relaxed_file_ownership = true; |
446 $allow_relaxed_file_ownership = true; |
391 } |
447 } |
392 |
448 |
|
449 $is_debug = WP_DEBUG && WP_DEBUG_LOG; |
|
450 if ( 'plugin' === $type ) { |
|
451 $was_active = is_plugin_active( $upgrader_item ); |
|
452 if ( $is_debug ) { |
|
453 error_log( ' Upgrading plugin ' . var_export( $item->slug, true ) . '...' ); |
|
454 } |
|
455 } |
|
456 |
|
457 if ( 'theme' === $type && $is_debug ) { |
|
458 error_log( ' Upgrading theme ' . var_export( $item->theme, true ) . '...' ); |
|
459 } |
|
460 |
|
461 /* |
|
462 * Enable maintenance mode before upgrading the plugin or theme. |
|
463 * |
|
464 * This avoids potential non-fatal errors being detected |
|
465 * while scraping for a fatal error if some files are still |
|
466 * being moved. |
|
467 * |
|
468 * While these checks are intended only for plugins, |
|
469 * maintenance mode is enabled for all upgrade types as any |
|
470 * update could contain an error or warning, which could cause |
|
471 * the scrape to miss a fatal error in the plugin update. |
|
472 */ |
|
473 if ( 'translation' !== $type ) { |
|
474 $upgrader->maintenance_mode( true ); |
|
475 } |
|
476 |
393 // Boom, this site's about to get a whole new splash of paint! |
477 // Boom, this site's about to get a whole new splash of paint! |
394 $upgrade_result = $upgrader->upgrade( |
478 $upgrade_result = $upgrader->upgrade( |
395 $upgrader_item, |
479 $upgrader_item, |
396 array( |
480 array( |
397 'clear_update_cache' => false, |
481 'clear_update_cache' => false, |
402 // Allow relaxed file ownership in some scenarios. |
486 // Allow relaxed file ownership in some scenarios. |
403 'allow_relaxed_file_ownership' => $allow_relaxed_file_ownership, |
487 'allow_relaxed_file_ownership' => $allow_relaxed_file_ownership, |
404 ) |
488 ) |
405 ); |
489 ); |
406 |
490 |
|
491 /* |
|
492 * After WP_Upgrader::upgrade() completes, maintenance mode is disabled. |
|
493 * |
|
494 * Re-enable maintenance mode while attempting to detect fatal errors |
|
495 * and potentially rolling back. |
|
496 * |
|
497 * This avoids errors if the site is visited while fatal errors exist |
|
498 * or while files are still being moved. |
|
499 */ |
|
500 if ( 'translation' !== $type ) { |
|
501 $upgrader->maintenance_mode( true ); |
|
502 } |
|
503 |
407 // If the filesystem is unavailable, false is returned. |
504 // If the filesystem is unavailable, false is returned. |
408 if ( false === $upgrade_result ) { |
505 if ( false === $upgrade_result ) { |
409 $upgrade_result = new WP_Error( 'fs_unavailable', __( 'Could not access filesystem.' ) ); |
506 $upgrade_result = new WP_Error( 'fs_unavailable', __( 'Could not access filesystem.' ) ); |
410 } |
507 } |
411 |
508 |
412 if ( 'core' === $type ) { |
509 if ( 'core' === $type ) { |
413 if ( is_wp_error( $upgrade_result ) |
510 if ( is_wp_error( $upgrade_result ) |
414 && ( 'up_to_date' === $upgrade_result->get_error_code() |
511 && ( 'up_to_date' === $upgrade_result->get_error_code() |
415 || 'locked' === $upgrade_result->get_error_code() ) |
512 || 'locked' === $upgrade_result->get_error_code() ) |
416 ) { |
513 ) { |
417 // These aren't actual errors, treat it as a skipped-update instead |
514 // Allow visitors to browse the site again. |
418 // to avoid triggering the post-core update failure routines. |
515 $upgrader->maintenance_mode( false ); |
|
516 |
|
517 /* |
|
518 * These aren't actual errors, treat it as a skipped-update instead |
|
519 * to avoid triggering the post-core update failure routines. |
|
520 */ |
419 return false; |
521 return false; |
420 } |
522 } |
421 |
523 |
422 // Core doesn't output this, so let's append it, so we don't get confused. |
524 // Core doesn't output this, so let's append it, so we don't get confused. |
423 if ( is_wp_error( $upgrade_result ) ) { |
525 if ( is_wp_error( $upgrade_result ) ) { |
426 } else { |
528 } else { |
427 $skin->feedback( __( 'WordPress updated successfully.' ) ); |
529 $skin->feedback( __( 'WordPress updated successfully.' ) ); |
428 } |
530 } |
429 } |
531 } |
430 |
532 |
|
533 $is_debug = WP_DEBUG && WP_DEBUG_LOG; |
|
534 |
|
535 if ( 'theme' === $type && $is_debug ) { |
|
536 error_log( ' Theme ' . var_export( $item->theme, true ) . ' has been upgraded.' ); |
|
537 } |
|
538 |
|
539 if ( 'plugin' === $type ) { |
|
540 if ( $is_debug ) { |
|
541 error_log( ' Plugin ' . var_export( $item->slug, true ) . ' has been upgraded.' ); |
|
542 if ( is_plugin_inactive( $upgrader_item ) ) { |
|
543 error_log( ' ' . var_export( $upgrader_item, true ) . ' is inactive and will not be checked for fatal errors.' ); |
|
544 } |
|
545 } |
|
546 |
|
547 if ( $was_active && ! is_wp_error( $upgrade_result ) ) { |
|
548 |
|
549 /* |
|
550 * The usual time limit is five minutes. However, as a loopback request |
|
551 * is about to be performed, increase the time limit to account for this. |
|
552 */ |
|
553 if ( function_exists( 'set_time_limit' ) ) { |
|
554 set_time_limit( 10 * MINUTE_IN_SECONDS ); |
|
555 } |
|
556 |
|
557 /* |
|
558 * Avoids a race condition when there are 2 sequential plugins that have |
|
559 * fatal errors. It seems a slight delay is required for the loopback to |
|
560 * use the updated plugin code in the request. This can cause the second |
|
561 * plugin's fatal error checking to be inaccurate, and may also affect |
|
562 * subsequent plugin checks. |
|
563 */ |
|
564 sleep( 2 ); |
|
565 |
|
566 if ( $this->has_fatal_error() ) { |
|
567 $upgrade_result = new WP_Error(); |
|
568 $temp_backup = array( |
|
569 array( |
|
570 'dir' => 'plugins', |
|
571 'slug' => $item->slug, |
|
572 'src' => WP_PLUGIN_DIR, |
|
573 ), |
|
574 ); |
|
575 |
|
576 $backup_restored = $upgrader->restore_temp_backup( $temp_backup ); |
|
577 if ( is_wp_error( $backup_restored ) ) { |
|
578 $upgrade_result->add( |
|
579 'plugin_update_fatal_error_rollback_failed', |
|
580 sprintf( |
|
581 /* translators: %s: The plugin's slug. */ |
|
582 __( "The update for '%s' contained a fatal error. The previously installed version could not be restored." ), |
|
583 $item->slug |
|
584 ) |
|
585 ); |
|
586 |
|
587 $upgrade_result->merge_from( $backup_restored ); |
|
588 } else { |
|
589 $upgrade_result->add( |
|
590 'plugin_update_fatal_error_rollback_successful', |
|
591 sprintf( |
|
592 /* translators: %s: The plugin's slug. */ |
|
593 __( "The update for '%s' contained a fatal error. The previously installed version has been restored." ), |
|
594 $item->slug |
|
595 ) |
|
596 ); |
|
597 |
|
598 $backup_deleted = $upgrader->delete_temp_backup( $temp_backup ); |
|
599 if ( is_wp_error( $backup_deleted ) ) { |
|
600 $upgrade_result->merge_from( $backup_deleted ); |
|
601 } |
|
602 } |
|
603 |
|
604 /* |
|
605 * Should emails not be working, log the message(s) so that |
|
606 * the log file contains context for the fatal error, |
|
607 * and whether a rollback was performed. |
|
608 * |
|
609 * `trigger_error()` is not used as it outputs a stack trace |
|
610 * to this location rather than to the fatal error, which will |
|
611 * appear above this entry in the log file. |
|
612 */ |
|
613 if ( $is_debug ) { |
|
614 error_log( ' ' . implode( "\n", $upgrade_result->get_error_messages() ) ); |
|
615 } |
|
616 } elseif ( $is_debug ) { |
|
617 error_log( ' The update for ' . var_export( $item->slug, true ) . ' has no fatal errors.' ); |
|
618 } |
|
619 } |
|
620 } |
|
621 |
|
622 // All processes are complete. Allow visitors to browse the site again. |
|
623 if ( 'translation' !== $type ) { |
|
624 $upgrader->maintenance_mode( false ); |
|
625 } |
|
626 |
431 $this->update_results[ $type ][] = (object) array( |
627 $this->update_results[ $type ][] = (object) array( |
432 'item' => $item, |
628 'item' => $item, |
433 'result' => $upgrade_result, |
629 'result' => $upgrade_result, |
434 'name' => $item_name, |
630 'name' => $item_name, |
435 'messages' => $skin->get_upgrade_messages(), |
631 'messages' => $skin->get_upgrade_messages(), |
454 |
650 |
455 if ( ! WP_Upgrader::create_lock( 'auto_updater' ) ) { |
651 if ( ! WP_Upgrader::create_lock( 'auto_updater' ) ) { |
456 return; |
652 return; |
457 } |
653 } |
458 |
654 |
|
655 $is_debug = WP_DEBUG && WP_DEBUG_LOG; |
|
656 |
|
657 if ( $is_debug ) { |
|
658 error_log( 'Automatic updates starting...' ); |
|
659 } |
|
660 |
459 // Don't automatically run these things, as we'll handle it ourselves. |
661 // Don't automatically run these things, as we'll handle it ourselves. |
460 remove_action( 'upgrader_process_complete', array( 'Language_Pack_Upgrader', 'async_upgrade' ), 20 ); |
662 remove_action( 'upgrader_process_complete', array( 'Language_Pack_Upgrader', 'async_upgrade' ), 20 ); |
461 remove_action( 'upgrader_process_complete', 'wp_version_check' ); |
663 remove_action( 'upgrader_process_complete', 'wp_version_check' ); |
462 remove_action( 'upgrader_process_complete', 'wp_update_plugins' ); |
664 remove_action( 'upgrader_process_complete', 'wp_update_plugins' ); |
463 remove_action( 'upgrader_process_complete', 'wp_update_themes' ); |
665 remove_action( 'upgrader_process_complete', 'wp_update_themes' ); |
464 |
666 |
465 // Next, plugins. |
667 // Next, plugins. |
466 wp_update_plugins(); // Check for plugin updates. |
668 wp_update_plugins(); // Check for plugin updates. |
467 $plugin_updates = get_site_transient( 'update_plugins' ); |
669 $plugin_updates = get_site_transient( 'update_plugins' ); |
468 if ( $plugin_updates && ! empty( $plugin_updates->response ) ) { |
670 if ( $plugin_updates && ! empty( $plugin_updates->response ) ) { |
|
671 if ( $is_debug ) { |
|
672 error_log( ' Automatic plugin updates starting...' ); |
|
673 } |
|
674 |
469 foreach ( $plugin_updates->response as $plugin ) { |
675 foreach ( $plugin_updates->response as $plugin ) { |
470 $this->update( 'plugin', $plugin ); |
676 $this->update( 'plugin', $plugin ); |
471 } |
677 } |
|
678 |
472 // Force refresh of plugin update information. |
679 // Force refresh of plugin update information. |
473 wp_clean_plugins_cache(); |
680 wp_clean_plugins_cache(); |
|
681 |
|
682 if ( $is_debug ) { |
|
683 error_log( ' Automatic plugin updates complete.' ); |
|
684 } |
474 } |
685 } |
475 |
686 |
476 // Next, those themes we all love. |
687 // Next, those themes we all love. |
477 wp_update_themes(); // Check for theme updates. |
688 wp_update_themes(); // Check for theme updates. |
478 $theme_updates = get_site_transient( 'update_themes' ); |
689 $theme_updates = get_site_transient( 'update_themes' ); |
479 if ( $theme_updates && ! empty( $theme_updates->response ) ) { |
690 if ( $theme_updates && ! empty( $theme_updates->response ) ) { |
|
691 if ( $is_debug ) { |
|
692 error_log( ' Automatic theme updates starting...' ); |
|
693 } |
|
694 |
480 foreach ( $theme_updates->response as $theme ) { |
695 foreach ( $theme_updates->response as $theme ) { |
481 $this->update( 'theme', (object) $theme ); |
696 $this->update( 'theme', (object) $theme ); |
482 } |
697 } |
483 // Force refresh of theme update information. |
698 // Force refresh of theme update information. |
484 wp_clean_themes_cache(); |
699 wp_clean_themes_cache(); |
|
700 |
|
701 if ( $is_debug ) { |
|
702 error_log( ' Automatic theme updates complete.' ); |
|
703 } |
|
704 } |
|
705 |
|
706 if ( $is_debug ) { |
|
707 error_log( 'Automatic updates complete.' ); |
485 } |
708 } |
486 |
709 |
487 // Next, process any core update. |
710 // Next, process any core update. |
488 wp_version_check(); // Check for core updates. |
711 wp_version_check(); // Check for core updates. |
489 $core_update = find_core_auto_update(); |
712 $core_update = find_core_auto_update(); |
490 |
713 |
491 if ( $core_update ) { |
714 if ( $core_update ) { |
492 $this->update( 'core', $core_update ); |
715 $this->update( 'core', $core_update ); |
493 } |
716 } |
494 |
717 |
495 // Clean up, and check for any pending translations. |
718 /* |
496 // (Core_Upgrader checks for core updates.) |
719 * Clean up, and check for any pending translations. |
|
720 * (Core_Upgrader checks for core updates.) |
|
721 */ |
497 $theme_stats = array(); |
722 $theme_stats = array(); |
498 if ( isset( $this->update_results['theme'] ) ) { |
723 if ( isset( $this->update_results['theme'] ) ) { |
499 foreach ( $this->update_results['theme'] as $upgrade ) { |
724 foreach ( $this->update_results['theme'] as $upgrade ) { |
500 $theme_stats[ $upgrade->item->theme ] = ( true === $upgrade->result ); |
725 $theme_stats[ $upgrade->item->theme ] = ( true === $upgrade->result ); |
501 } |
726 } |
580 return; |
805 return; |
581 } |
806 } |
582 |
807 |
583 $error_code = $result->get_error_code(); |
808 $error_code = $result->get_error_code(); |
584 |
809 |
585 // Any of these WP_Error codes are critical failures, as in they occurred after we started to copy core files. |
810 /* |
586 // We should not try to perform a background update again until there is a successful one-click update performed by the user. |
811 * Any of these WP_Error codes are critical failures, as in they occurred after we started to copy core files. |
|
812 * We should not try to perform a background update again until there is a successful one-click update performed by the user. |
|
813 */ |
587 $critical = false; |
814 $critical = false; |
588 if ( 'disk_full' === $error_code || false !== strpos( $error_code, '__copy_dir' ) ) { |
815 if ( 'disk_full' === $error_code || str_contains( $error_code, '__copy_dir' ) ) { |
589 $critical = true; |
816 $critical = true; |
590 } elseif ( 'rollback_was_required' === $error_code && is_wp_error( $result->get_error_data()->rollback ) ) { |
817 } elseif ( 'rollback_was_required' === $error_code && is_wp_error( $result->get_error_data()->rollback ) ) { |
591 // A rollback is only critical if it failed too. |
818 // A rollback is only critical if it failed too. |
592 $critical = true; |
819 $critical = true; |
593 $rollback_result = $result->get_error_data()->rollback; |
820 $rollback_result = $result->get_error_data()->rollback; |
594 } elseif ( false !== strpos( $error_code, 'do_rollback' ) ) { |
821 } elseif ( str_contains( $error_code, 'do_rollback' ) ) { |
595 $critical = true; |
822 $critical = true; |
596 } |
823 } |
597 |
824 |
598 if ( $critical ) { |
825 if ( $critical ) { |
599 $critical_data = array( |
826 $critical_data = array( |
827 if ( 'success' !== $type || $newer_version_available ) { |
1056 if ( 'success' !== $type || $newer_version_available ) { |
828 $body .= "\n\n" . __( 'Keeping your site updated is important for security. It also makes the internet a safer place for you and your readers.' ); |
1057 $body .= "\n\n" . __( 'Keeping your site updated is important for security. It also makes the internet a safer place for you and your readers.' ); |
829 } |
1058 } |
830 |
1059 |
831 if ( $critical_support ) { |
1060 if ( $critical_support ) { |
832 $body .= ' ' . __( "If you reach out to us, we'll also ensure you'll never have this problem again." ); |
1061 $body .= ' ' . __( "Reach out to WordPress Core developers to ensure you'll never have this problem again." ); |
833 } |
1062 } |
834 |
1063 |
835 // If things are successful and we're now on the latest, mention plugins and themes if any are out of date. |
1064 // If things are successful and we're now on the latest, mention plugins and themes if any are out of date. |
836 if ( 'success' === $type && ! $newer_version_available && ( get_plugin_updates() || get_theme_updates() ) ) { |
1065 if ( 'success' === $type && ! $newer_version_available && ( get_plugin_updates() || get_theme_updates() ) ) { |
837 $body .= "\n\n" . __( 'You also have some plugins or themes with updates available. Update them now:' ); |
1066 $body .= "\n\n" . __( 'You also have some plugins or themes with updates available. Update them now:' ); |
845 /* translators: %s: WordPress version. */ |
1074 /* translators: %s: WordPress version. */ |
846 $body .= sprintf( __( 'Your site was running version %s.' ), get_bloginfo( 'version' ) ); |
1075 $body .= sprintf( __( 'Your site was running version %s.' ), get_bloginfo( 'version' ) ); |
847 $body .= ' ' . __( 'Some data that describes the error your site encountered has been put together.' ); |
1076 $body .= ' ' . __( 'Some data that describes the error your site encountered has been put together.' ); |
848 $body .= ' ' . __( 'Your hosting company, support forum volunteers, or a friendly developer may be able to use this information to help you:' ); |
1077 $body .= ' ' . __( 'Your hosting company, support forum volunteers, or a friendly developer may be able to use this information to help you:' ); |
849 |
1078 |
850 // If we had a rollback and we're still critical, then the rollback failed too. |
1079 /* |
851 // Loop through all errors (the main WP_Error, the update result, the rollback result) for code, data, etc. |
1080 * If we had a rollback and we're still critical, then the rollback failed too. |
|
1081 * Loop through all errors (the main WP_Error, the update result, the rollback result) for code, data, etc. |
|
1082 */ |
852 if ( 'rollback_was_required' === $result->get_error_code() ) { |
1083 if ( 'rollback_was_required' === $result->get_error_code() ) { |
853 $errors = array( $result, $result->get_error_data()->update, $result->get_error_data()->rollback ); |
1084 $errors = array( $result, $result->get_error_data()->update, $result->get_error_data()->rollback ); |
854 } else { |
1085 } else { |
855 $errors = array( $result ); |
1086 $errors = array( $result ); |
856 } |
1087 } |
1095 $body[] = __( 'Please check your site now. It’s possible that everything is working. If there are updates available, you should update.' ); |
1326 $body[] = __( 'Please check your site now. It’s possible that everything is working. If there are updates available, you should update.' ); |
1096 $body[] = "\n"; |
1327 $body[] = "\n"; |
1097 |
1328 |
1098 // List failed plugin updates. |
1329 // List failed plugin updates. |
1099 if ( ! empty( $failed_updates['plugin'] ) ) { |
1330 if ( ! empty( $failed_updates['plugin'] ) ) { |
1100 $body[] = __( 'These plugins failed to update:' ); |
1331 $body[] = __( 'The following plugins failed to update. If there was a fatal error in the update, the previously installed version has been restored.' ); |
1101 |
1332 |
1102 foreach ( $failed_updates['plugin'] as $item ) { |
1333 foreach ( $failed_updates['plugin'] as $item ) { |
|
1334 $body_message = ''; |
|
1335 $item_url = ''; |
|
1336 |
|
1337 if ( ! empty( $item->item->url ) ) { |
|
1338 $item_url = ' : ' . esc_url( $item->item->url ); |
|
1339 } |
|
1340 |
1103 if ( $item->item->current_version ) { |
1341 if ( $item->item->current_version ) { |
1104 $body[] = sprintf( |
1342 $body_message .= sprintf( |
1105 /* translators: 1: Plugin name, 2: Current version number, 3: New version number. */ |
1343 /* translators: 1: Plugin name, 2: Current version number, 3: New version number, 4: Plugin URL. */ |
1106 __( '- %1$s (from version %2$s to %3$s)' ), |
1344 __( '- %1$s (from version %2$s to %3$s)%4$s' ), |
1107 $item->name, |
1345 html_entity_decode( $item->name ), |
1108 $item->item->current_version, |
1346 $item->item->current_version, |
1109 $item->item->new_version |
1347 $item->item->new_version, |
|
1348 $item_url |
1110 ); |
1349 ); |
1111 } else { |
1350 } else { |
1112 $body[] = sprintf( |
1351 $body_message .= sprintf( |
1113 /* translators: 1: Plugin name, 2: Version number. */ |
1352 /* translators: 1: Plugin name, 2: Version number, 3: Plugin URL. */ |
1114 __( '- %1$s version %2$s' ), |
1353 __( '- %1$s version %2$s%3$s' ), |
1115 $item->name, |
1354 html_entity_decode( $item->name ), |
1116 $item->item->new_version |
1355 $item->item->new_version, |
|
1356 $item_url |
1117 ); |
1357 ); |
1118 } |
1358 } |
|
1359 |
|
1360 $body[] = $body_message; |
1119 |
1361 |
1120 $past_failure_emails[ $item->item->plugin ] = $item->item->new_version; |
1362 $past_failure_emails[ $item->item->plugin ] = $item->item->new_version; |
1121 } |
1363 } |
1122 |
1364 |
1123 $body[] = "\n"; |
1365 $body[] = "\n"; |
1130 foreach ( $failed_updates['theme'] as $item ) { |
1372 foreach ( $failed_updates['theme'] as $item ) { |
1131 if ( $item->item->current_version ) { |
1373 if ( $item->item->current_version ) { |
1132 $body[] = sprintf( |
1374 $body[] = sprintf( |
1133 /* translators: 1: Theme name, 2: Current version number, 3: New version number. */ |
1375 /* translators: 1: Theme name, 2: Current version number, 3: New version number. */ |
1134 __( '- %1$s (from version %2$s to %3$s)' ), |
1376 __( '- %1$s (from version %2$s to %3$s)' ), |
1135 $item->name, |
1377 html_entity_decode( $item->name ), |
1136 $item->item->current_version, |
1378 $item->item->current_version, |
1137 $item->item->new_version |
1379 $item->item->new_version |
1138 ); |
1380 ); |
1139 } else { |
1381 } else { |
1140 $body[] = sprintf( |
1382 $body[] = sprintf( |
1141 /* translators: 1: Theme name, 2: Version number. */ |
1383 /* translators: 1: Theme name, 2: Version number. */ |
1142 __( '- %1$s version %2$s' ), |
1384 __( '- %1$s version %2$s' ), |
1143 $item->name, |
1385 html_entity_decode( $item->name ), |
1144 $item->item->new_version |
1386 $item->item->new_version |
1145 ); |
1387 ); |
1146 } |
1388 } |
1147 |
1389 |
1148 $past_failure_emails[ $item->item->theme ] = $item->item->new_version; |
1390 $past_failure_emails[ $item->item->theme ] = $item->item->new_version; |
1159 // List successful plugin updates. |
1401 // List successful plugin updates. |
1160 if ( ! empty( $successful_updates['plugin'] ) ) { |
1402 if ( ! empty( $successful_updates['plugin'] ) ) { |
1161 $body[] = __( 'These plugins are now up to date:' ); |
1403 $body[] = __( 'These plugins are now up to date:' ); |
1162 |
1404 |
1163 foreach ( $successful_updates['plugin'] as $item ) { |
1405 foreach ( $successful_updates['plugin'] as $item ) { |
|
1406 $body_message = ''; |
|
1407 $item_url = ''; |
|
1408 |
|
1409 if ( ! empty( $item->item->url ) ) { |
|
1410 $item_url = ' : ' . esc_url( $item->item->url ); |
|
1411 } |
|
1412 |
1164 if ( $item->item->current_version ) { |
1413 if ( $item->item->current_version ) { |
1165 $body[] = sprintf( |
1414 $body_message .= sprintf( |
1166 /* translators: 1: Plugin name, 2: Current version number, 3: New version number. */ |
1415 /* translators: 1: Plugin name, 2: Current version number, 3: New version number, 4: Plugin URL. */ |
1167 __( '- %1$s (from version %2$s to %3$s)' ), |
1416 __( '- %1$s (from version %2$s to %3$s)%4$s' ), |
1168 $item->name, |
1417 html_entity_decode( $item->name ), |
1169 $item->item->current_version, |
1418 $item->item->current_version, |
1170 $item->item->new_version |
1419 $item->item->new_version, |
|
1420 $item_url |
1171 ); |
1421 ); |
1172 } else { |
1422 } else { |
1173 $body[] = sprintf( |
1423 $body_message .= sprintf( |
1174 /* translators: 1: Plugin name, 2: Version number. */ |
1424 /* translators: 1: Plugin name, 2: Version number, 3: Plugin URL. */ |
1175 __( '- %1$s version %2$s' ), |
1425 __( '- %1$s version %2$s%3$s' ), |
1176 $item->name, |
1426 html_entity_decode( $item->name ), |
1177 $item->item->new_version |
1427 $item->item->new_version, |
|
1428 $item_url |
1178 ); |
1429 ); |
1179 } |
1430 } |
|
1431 $body[] = $body_message; |
1180 |
1432 |
1181 unset( $past_failure_emails[ $item->item->plugin ] ); |
1433 unset( $past_failure_emails[ $item->item->plugin ] ); |
1182 } |
1434 } |
1183 |
1435 |
1184 $body[] = "\n"; |
1436 $body[] = "\n"; |
1191 foreach ( $successful_updates['theme'] as $item ) { |
1443 foreach ( $successful_updates['theme'] as $item ) { |
1192 if ( $item->item->current_version ) { |
1444 if ( $item->item->current_version ) { |
1193 $body[] = sprintf( |
1445 $body[] = sprintf( |
1194 /* translators: 1: Theme name, 2: Current version number, 3: New version number. */ |
1446 /* translators: 1: Theme name, 2: Current version number, 3: New version number. */ |
1195 __( '- %1$s (from version %2$s to %3$s)' ), |
1447 __( '- %1$s (from version %2$s to %3$s)' ), |
1196 $item->name, |
1448 html_entity_decode( $item->name ), |
1197 $item->item->current_version, |
1449 $item->item->current_version, |
1198 $item->item->new_version |
1450 $item->item->new_version |
1199 ); |
1451 ); |
1200 } else { |
1452 } else { |
1201 $body[] = sprintf( |
1453 $body[] = sprintf( |
1202 /* translators: 1: Theme name, 2: Version number. */ |
1454 /* translators: 1: Theme name, 2: Version number. */ |
1203 __( '- %1$s version %2$s' ), |
1455 __( '- %1$s version %2$s' ), |
1204 $item->name, |
1456 html_entity_decode( $item->name ), |
1205 $item->item->new_version |
1457 $item->item->new_version |
1206 ); |
1458 ); |
1207 } |
1459 } |
1208 |
1460 |
1209 unset( $past_failure_emails[ $item->item->theme ] ); |
1461 unset( $past_failure_emails[ $item->item->theme ] ); |
1462 */ |
1714 */ |
1463 $email = apply_filters( 'automatic_updates_debug_email', $email, $failures, $this->update_results ); |
1715 $email = apply_filters( 'automatic_updates_debug_email', $email, $failures, $this->update_results ); |
1464 |
1716 |
1465 wp_mail( $email['to'], wp_specialchars_decode( $email['subject'] ), $email['body'], $email['headers'] ); |
1717 wp_mail( $email['to'], wp_specialchars_decode( $email['subject'] ), $email['body'], $email['headers'] ); |
1466 } |
1718 } |
|
1719 |
|
1720 /** |
|
1721 * Performs a loopback request to check for potential fatal errors. |
|
1722 * |
|
1723 * Fatal errors cannot be detected unless maintenance mode is enabled. |
|
1724 * |
|
1725 * @since 6.6.0 |
|
1726 * |
|
1727 * @global int $upgrading The Unix timestamp marking when upgrading WordPress began. |
|
1728 * |
|
1729 * @return bool Whether a fatal error was detected. |
|
1730 */ |
|
1731 protected function has_fatal_error() { |
|
1732 global $upgrading; |
|
1733 |
|
1734 $maintenance_file = ABSPATH . '.maintenance'; |
|
1735 if ( ! file_exists( $maintenance_file ) ) { |
|
1736 return false; |
|
1737 } |
|
1738 |
|
1739 require $maintenance_file; |
|
1740 if ( ! is_int( $upgrading ) ) { |
|
1741 return false; |
|
1742 } |
|
1743 |
|
1744 $scrape_key = md5( $upgrading ); |
|
1745 $scrape_nonce = (string) $upgrading; |
|
1746 $transient = 'scrape_key_' . $scrape_key; |
|
1747 set_transient( $transient, $scrape_nonce, 30 ); |
|
1748 |
|
1749 $cookies = wp_unslash( $_COOKIE ); |
|
1750 $scrape_params = array( |
|
1751 'wp_scrape_key' => $scrape_key, |
|
1752 'wp_scrape_nonce' => $scrape_nonce, |
|
1753 ); |
|
1754 $headers = array( |
|
1755 'Cache-Control' => 'no-cache', |
|
1756 ); |
|
1757 |
|
1758 /** This filter is documented in wp-includes/class-wp-http-streams.php */ |
|
1759 $sslverify = apply_filters( 'https_local_ssl_verify', false ); |
|
1760 |
|
1761 // Include Basic auth in the loopback request. |
|
1762 if ( isset( $_SERVER['PHP_AUTH_USER'] ) && isset( $_SERVER['PHP_AUTH_PW'] ) ) { |
|
1763 $headers['Authorization'] = 'Basic ' . base64_encode( wp_unslash( $_SERVER['PHP_AUTH_USER'] ) . ':' . wp_unslash( $_SERVER['PHP_AUTH_PW'] ) ); |
|
1764 } |
|
1765 |
|
1766 // Time to wait for loopback request to finish. |
|
1767 $timeout = 50; // 50 seconds. |
|
1768 |
|
1769 $is_debug = WP_DEBUG && WP_DEBUG_LOG; |
|
1770 if ( $is_debug ) { |
|
1771 error_log( ' Scraping home page...' ); |
|
1772 } |
|
1773 |
|
1774 $needle_start = "###### wp_scraping_result_start:$scrape_key ######"; |
|
1775 $needle_end = "###### wp_scraping_result_end:$scrape_key ######"; |
|
1776 $url = add_query_arg( $scrape_params, home_url( '/' ) ); |
|
1777 $response = wp_remote_get( $url, compact( 'cookies', 'headers', 'timeout', 'sslverify' ) ); |
|
1778 |
|
1779 if ( is_wp_error( $response ) ) { |
|
1780 if ( $is_debug ) { |
|
1781 error_log( 'Loopback request failed: ' . $response->get_error_message() ); |
|
1782 } |
|
1783 return true; |
|
1784 } |
|
1785 |
|
1786 // If this outputs `true` in the log, it means there were no fatal errors detected. |
|
1787 if ( $is_debug ) { |
|
1788 error_log( var_export( substr( $response['body'], strpos( $response['body'], '###### wp_scraping_result_start:' ) ), true ) ); |
|
1789 } |
|
1790 |
|
1791 $body = wp_remote_retrieve_body( $response ); |
|
1792 $scrape_result_position = strpos( $body, $needle_start ); |
|
1793 $result = null; |
|
1794 |
|
1795 if ( false !== $scrape_result_position ) { |
|
1796 $error_output = substr( $body, $scrape_result_position + strlen( $needle_start ) ); |
|
1797 $error_output = substr( $error_output, 0, strpos( $error_output, $needle_end ) ); |
|
1798 $result = json_decode( trim( $error_output ), true ); |
|
1799 } |
|
1800 |
|
1801 delete_transient( $transient ); |
|
1802 |
|
1803 // Only fatal errors will result in a 'type' key. |
|
1804 return isset( $result['type'] ); |
|
1805 } |
1467 } |
1806 } |