[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-admin/includes/ -> class-wp-automatic-updater.php (source)

   1  <?php
   2  /**
   3   * Upgrade API: WP_Automatic_Updater class
   4   *
   5   * @package WordPress
   6   * @subpackage Upgrader
   7   * @since 4.6.0
   8   */
   9  
  10  /**
  11   * Core class used for handling automatic background updates.
  12   *
  13   * @since 3.7.0
  14   * @since 4.6.0 Moved to its own file from wp-admin/includes/class-wp-upgrader.php.
  15   */
  16  class WP_Automatic_Updater {
  17  
  18      /**
  19       * Tracks update results during processing.
  20       *
  21       * @var array
  22       */
  23      protected $update_results = array();
  24  
  25      /**
  26       * Whether the entire automatic updater is disabled.
  27       *
  28       * @since 3.7.0
  29       */
  30  	public function is_disabled() {
  31          // Background updates are disabled if you don't want file changes.
  32          if ( ! wp_is_file_mod_allowed( 'automatic_updater' ) ) {
  33              return true;
  34          }
  35  
  36          if ( wp_installing() ) {
  37              return true;
  38          }
  39  
  40          // More fine grained control can be done through the WP_AUTO_UPDATE_CORE constant and filters.
  41          $disabled = defined( 'AUTOMATIC_UPDATER_DISABLED' ) && AUTOMATIC_UPDATER_DISABLED;
  42  
  43          /**
  44           * Filters whether to entirely disable background updates.
  45           *
  46           * There are more fine-grained filters and controls for selective disabling.
  47           * This filter parallels the AUTOMATIC_UPDATER_DISABLED constant in name.
  48           *
  49           * This also disables update notification emails. That may change in the future.
  50           *
  51           * @since 3.7.0
  52           *
  53           * @param bool $disabled Whether the updater should be disabled.
  54           */
  55          return apply_filters( 'automatic_updater_disabled', $disabled );
  56      }
  57  
  58      /**
  59       * Check for version control checkouts.
  60       *
  61       * Checks for Subversion, Git, Mercurial, and Bazaar. It recursively looks up the
  62       * filesystem to the top of the drive, erring on the side of detecting a VCS
  63       * checkout somewhere.
  64       *
  65       * ABSPATH is always checked in addition to whatever $context is (which may be the
  66       * wp-content directory, for example). The underlying assumption is that if you are
  67       * using version control *anywhere*, then you should be making decisions for
  68       * how things get updated.
  69       *
  70       * @since 3.7.0
  71       *
  72       * @param string $context The filesystem path to check, in addition to ABSPATH.
  73       */
  74  	public function is_vcs_checkout( $context ) {
  75          $context_dirs = array( untrailingslashit( $context ) );
  76          if ( ABSPATH !== $context ) {
  77              $context_dirs[] = untrailingslashit( ABSPATH );
  78          }
  79  
  80          $vcs_dirs   = array( '.svn', '.git', '.hg', '.bzr' );
  81          $check_dirs = array();
  82  
  83          foreach ( $context_dirs as $context_dir ) {
  84              // Walk up from $context_dir to the root.
  85              do {
  86                  $check_dirs[] = $context_dir;
  87  
  88                  // Once we've hit '/' or 'C:\', we need to stop. dirname will keep returning the input here.
  89                  if ( dirname( $context_dir ) === $context_dir ) {
  90                      break;
  91                  }
  92  
  93                  // Continue one level at a time.
  94              } while ( $context_dir = dirname( $context_dir ) );
  95          }
  96  
  97          $check_dirs = array_unique( $check_dirs );
  98  
  99          // Search all directories we've found for evidence of version control.
 100          foreach ( $vcs_dirs as $vcs_dir ) {
 101              foreach ( $check_dirs as $check_dir ) {
 102                  $checkout = @is_dir( rtrim( $check_dir, '\\/' ) . "/$vcs_dir" );
 103                  if ( $checkout ) {
 104                      break 2;
 105                  }
 106              }
 107          }
 108  
 109          /**
 110           * Filters whether the automatic updater should consider a filesystem
 111           * location to be potentially managed by a version control system.
 112           *
 113           * @since 3.7.0
 114           *
 115           * @param bool $checkout  Whether a VCS checkout was discovered at $context
 116           *                        or ABSPATH, or anywhere higher.
 117           * @param string $context The filesystem context (a path) against which
 118           *                        filesystem status should be checked.
 119           */
 120          return apply_filters( 'automatic_updates_is_vcs_checkout', $checkout, $context );
 121      }
 122  
 123      /**
 124       * Tests to see if we can and should update a specific item.
 125       *
 126       * @since 3.7.0
 127       *
 128       * @global wpdb $wpdb WordPress database abstraction object.
 129       *
 130       * @param string $type    The type of update being checked: 'core', 'theme',
 131       *                        'plugin', 'translation'.
 132       * @param object $item    The update offer.
 133       * @param string $context The filesystem context (a path) against which filesystem
 134       *                        access and status should be checked.
 135       */
 136  	public function should_update( $type, $item, $context ) {
 137          // Used to see if WP_Filesystem is set up to allow unattended updates.
 138          $skin = new Automatic_Upgrader_Skin;
 139  
 140          if ( $this->is_disabled() ) {
 141              return false;
 142          }
 143  
 144          // Only relax the filesystem checks when the update doesn't include new files.
 145          $allow_relaxed_file_ownership = false;
 146          if ( 'core' === $type && isset( $item->new_files ) && ! $item->new_files ) {
 147              $allow_relaxed_file_ownership = true;
 148          }
 149  
 150          // If we can't do an auto core update, we may still be able to email the user.
 151          if ( ! $skin->request_filesystem_credentials( false, $context, $allow_relaxed_file_ownership ) || $this->is_vcs_checkout( $context ) ) {
 152              if ( 'core' === $type ) {
 153                  $this->send_core_update_notification_email( $item );
 154              }
 155              return false;
 156          }
 157  
 158          // Next up, is this an item we can update?
 159          if ( 'core' === $type ) {
 160              $update = Core_Upgrader::should_update_to_version( $item->current );
 161          } elseif ( 'plugin' === $type || 'theme' === $type ) {
 162              $update = ! empty( $item->autoupdate );
 163  
 164              if ( ! $update && wp_is_auto_update_enabled_for_type( $type ) ) {
 165                  // Check if the site admin has enabled auto-updates by default for the specific item.
 166                  $auto_updates = (array) get_site_option( "auto_update_{$type}s", array() );
 167                  $update       = in_array( $item->{$type}, $auto_updates, true );
 168              }
 169          } else {
 170              $update = ! empty( $item->autoupdate );
 171          }
 172  
 173          /**
 174           * Filters whether to automatically update core, a plugin, a theme, or a language.
 175           *
 176           * The dynamic portion of the hook name, `$type`, refers to the type of update
 177           * being checked. Can be 'core', 'theme', 'plugin', or 'translation'.
 178           *
 179           * Generally speaking, plugins, themes, and major core versions are not updated
 180           * by default, while translations and minor and development versions for core
 181           * are updated by default.
 182           *
 183           * See the {@see 'allow_dev_auto_core_updates'}, {@see 'allow_minor_auto_core_updates'},
 184           * and {@see 'allow_major_auto_core_updates'} filters for a more straightforward way to
 185           * adjust core updates.
 186           *
 187           * @since 3.7.0
 188           *
 189           * @param bool   $update Whether to update.
 190           * @param object $item   The update offer.
 191           */
 192          $update = apply_filters( "auto_update_{$type}", $update, $item );
 193  
 194          if ( ! $update ) {
 195              if ( 'core' === $type ) {
 196                  $this->send_core_update_notification_email( $item );
 197              }
 198              return false;
 199          }
 200  
 201          // If it's a core update, are we actually compatible with its requirements?
 202          if ( 'core' === $type ) {
 203              global $wpdb;
 204  
 205              $php_compat = version_compare( phpversion(), $item->php_version, '>=' );
 206              if ( file_exists( WP_CONTENT_DIR . '/db.php' ) && empty( $wpdb->is_mysql ) ) {
 207                  $mysql_compat = true;
 208              } else {
 209                  $mysql_compat = version_compare( $wpdb->db_version(), $item->mysql_version, '>=' );
 210              }
 211  
 212              if ( ! $php_compat || ! $mysql_compat ) {
 213                  return false;
 214              }
 215          }
 216  
 217          // If updating a plugin or theme, ensure the minimum PHP version requirements are satisfied.
 218          if ( in_array( $type, array( 'plugin', 'theme' ), true ) ) {
 219              if ( ! empty( $item->requires_php ) && version_compare( phpversion(), $item->requires_php, '<' ) ) {
 220                  return false;
 221              }
 222          }
 223  
 224          return true;
 225      }
 226  
 227      /**
 228       * Notifies an administrator of a core update.
 229       *
 230       * @since 3.7.0
 231       *
 232       * @param object $item The update offer.
 233       */
 234  	protected function send_core_update_notification_email( $item ) {
 235          $notified = get_site_option( 'auto_core_update_notified' );
 236  
 237          // Don't notify if we've already notified the same email address of the same version.
 238          if ( $notified && get_site_option( 'admin_email' ) === $notified['email'] && $notified['version'] == $item->current ) {
 239              return false;
 240          }
 241  
 242          // See if we need to notify users of a core update.
 243          $notify = ! empty( $item->notify_email );
 244  
 245          /**
 246           * Filters whether to notify the site administrator of a new core update.
 247           *
 248           * By default, administrators are notified when the update offer received
 249           * from WordPress.org sets a particular flag. This allows some discretion
 250           * in if and when to notify.
 251           *
 252           * This filter is only evaluated once per release. If the same email address
 253           * was already notified of the same new version, WordPress won't repeatedly
 254           * email the administrator.
 255           *
 256           * This filter is also used on about.php to check if a plugin has disabled
 257           * these notifications.
 258           *
 259           * @since 3.7.0
 260           *
 261           * @param bool   $notify Whether the site administrator is notified.
 262           * @param object $item   The update offer.
 263           */
 264          if ( ! apply_filters( 'send_core_update_notification_email', $notify, $item ) ) {
 265              return false;
 266          }
 267  
 268          $this->send_email( 'manual', $item );
 269          return true;
 270      }
 271  
 272      /**
 273       * Update an item, if appropriate.
 274       *
 275       * @since 3.7.0
 276       *
 277       * @param string $type The type of update being checked: 'core', 'theme', 'plugin', 'translation'.
 278       * @param object $item The update offer.
 279       * @return null|WP_Error
 280       */
 281  	public function update( $type, $item ) {
 282          $skin = new Automatic_Upgrader_Skin;
 283  
 284          switch ( $type ) {
 285              case 'core':
 286                  // The Core upgrader doesn't use the Upgrader's skin during the actual main part of the upgrade, instead, firing a filter.
 287                  add_filter( 'update_feedback', array( $skin, 'feedback' ) );
 288                  $upgrader = new Core_Upgrader( $skin );
 289                  $context  = ABSPATH;
 290                  break;
 291              case 'plugin':
 292                  $upgrader = new Plugin_Upgrader( $skin );
 293                  $context  = WP_PLUGIN_DIR; // We don't support custom Plugin directories, or updates for WPMU_PLUGIN_DIR.
 294                  break;
 295              case 'theme':
 296                  $upgrader = new Theme_Upgrader( $skin );
 297                  $context  = get_theme_root( $item->theme );
 298                  break;
 299              case 'translation':
 300                  $upgrader = new Language_Pack_Upgrader( $skin );
 301                  $context  = WP_CONTENT_DIR; // WP_LANG_DIR;
 302                  break;
 303          }
 304  
 305          // Determine whether we can and should perform this update.
 306          if ( ! $this->should_update( $type, $item, $context ) ) {
 307              return false;
 308          }
 309  
 310          /**
 311           * Fires immediately prior to an auto-update.
 312           *
 313           * @since 4.4.0
 314           *
 315           * @param string $type    The type of update being checked: 'core', 'theme', 'plugin', or 'translation'.
 316           * @param object $item    The update offer.
 317           * @param string $context The filesystem context (a path) against which filesystem access and status
 318           *                        should be checked.
 319           */
 320          do_action( 'pre_auto_update', $type, $item, $context );
 321  
 322          $upgrader_item = $item;
 323          switch ( $type ) {
 324              case 'core':
 325                  /* translators: %s: WordPress version. */
 326                  $skin->feedback( __( 'Updating to WordPress %s' ), $item->version );
 327                  /* translators: %s: WordPress version. */
 328                  $item_name = sprintf( __( 'WordPress %s' ), $item->version );
 329                  break;
 330              case 'theme':
 331                  $upgrader_item = $item->theme;
 332                  $theme         = wp_get_theme( $upgrader_item );
 333                  $item_name     = $theme->Get( 'Name' );
 334                  /* translators: %s: Theme name. */
 335                  $skin->feedback( __( 'Updating theme: %s' ), $item_name );
 336                  break;
 337              case 'plugin':
 338                  $upgrader_item = $item->plugin;
 339                  $plugin_data   = get_plugin_data( $context . '/' . $upgrader_item );
 340                  $item_name     = $plugin_data['Name'];
 341                  /* translators: %s: Plugin name. */
 342                  $skin->feedback( __( 'Updating plugin: %s' ), $item_name );
 343                  break;
 344              case 'translation':
 345                  $language_item_name = $upgrader->get_name_for_update( $item );
 346                  /* translators: %s: Project name (plugin, theme, or WordPress). */
 347                  $item_name = sprintf( __( 'Translations for %s' ), $language_item_name );
 348                  /* translators: 1: Project name (plugin, theme, or WordPress), 2: Language. */
 349                  $skin->feedback( sprintf( __( 'Updating translations for %1$s (%2$s)&#8230;' ), $language_item_name, $item->language ) );
 350                  break;
 351          }
 352  
 353          $allow_relaxed_file_ownership = false;
 354          if ( 'core' === $type && isset( $item->new_files ) && ! $item->new_files ) {
 355              $allow_relaxed_file_ownership = true;
 356          }
 357  
 358          // Boom, this site's about to get a whole new splash of paint!
 359          $upgrade_result = $upgrader->upgrade(
 360              $upgrader_item,
 361              array(
 362                  'clear_update_cache'           => false,
 363                  // Always use partial builds if possible for core updates.
 364                  'pre_check_md5'                => false,
 365                  // Only available for core updates.
 366                  'attempt_rollback'             => true,
 367                  // Allow relaxed file ownership in some scenarios.
 368                  'allow_relaxed_file_ownership' => $allow_relaxed_file_ownership,
 369              )
 370          );
 371  
 372          // If the filesystem is unavailable, false is returned.
 373          if ( false === $upgrade_result ) {
 374              $upgrade_result = new WP_Error( 'fs_unavailable', __( 'Could not access filesystem.' ) );
 375          }
 376  
 377          if ( 'core' === $type ) {
 378              if ( is_wp_error( $upgrade_result )
 379                  && ( 'up_to_date' === $upgrade_result->get_error_code()
 380                      || 'locked' === $upgrade_result->get_error_code() )
 381              ) {
 382                  // These aren't actual errors, treat it as a skipped-update instead
 383                  // to avoid triggering the post-core update failure routines.
 384                  return false;
 385              }
 386  
 387              // Core doesn't output this, so let's append it so we don't get confused.
 388              if ( is_wp_error( $upgrade_result ) ) {
 389                  $skin->error( __( 'Installation Failed' ), $upgrade_result );
 390              } else {
 391                  $skin->feedback( __( 'WordPress updated successfully' ) );
 392              }
 393          }
 394  
 395          $this->update_results[ $type ][] = (object) array(
 396              'item'     => $item,
 397              'result'   => $upgrade_result,
 398              'name'     => $item_name,
 399              'messages' => $skin->get_upgrade_messages(),
 400          );
 401  
 402          return $upgrade_result;
 403      }
 404  
 405      /**
 406       * Kicks off the background update process, looping through all pending updates.
 407       *
 408       * @since 3.7.0
 409       */
 410  	public function run() {
 411          if ( $this->is_disabled() ) {
 412              return;
 413          }
 414  
 415          if ( ! is_main_network() || ! is_main_site() ) {
 416              return;
 417          }
 418  
 419          if ( ! WP_Upgrader::create_lock( 'auto_updater' ) ) {
 420              return;
 421          }
 422  
 423          // Don't automatically run these things, as we'll handle it ourselves.
 424          remove_action( 'upgrader_process_complete', array( 'Language_Pack_Upgrader', 'async_upgrade' ), 20 );
 425          remove_action( 'upgrader_process_complete', 'wp_version_check' );
 426          remove_action( 'upgrader_process_complete', 'wp_update_plugins' );
 427          remove_action( 'upgrader_process_complete', 'wp_update_themes' );
 428  
 429          // Next, plugins.
 430          wp_update_plugins(); // Check for plugin updates.
 431          $plugin_updates = get_site_transient( 'update_plugins' );
 432          if ( $plugin_updates && ! empty( $plugin_updates->response ) ) {
 433              foreach ( $plugin_updates->response as $plugin ) {
 434                  $this->update( 'plugin', $plugin );
 435              }
 436              // Force refresh of plugin update information.
 437              wp_clean_plugins_cache();
 438          }
 439  
 440          // Next, those themes we all love.
 441          wp_update_themes();  // Check for theme updates.
 442          $theme_updates = get_site_transient( 'update_themes' );
 443          if ( $theme_updates && ! empty( $theme_updates->response ) ) {
 444              foreach ( $theme_updates->response as $theme ) {
 445                  $this->update( 'theme', (object) $theme );
 446              }
 447              // Force refresh of theme update information.
 448              wp_clean_themes_cache();
 449          }
 450  
 451          // Next, process any core update.
 452          wp_version_check(); // Check for core updates.
 453          $core_update = find_core_auto_update();
 454  
 455          if ( $core_update ) {
 456              $this->update( 'core', $core_update );
 457          }
 458  
 459          // Clean up, and check for any pending translations.
 460          // (Core_Upgrader checks for core updates.)
 461          $theme_stats = array();
 462          if ( isset( $this->update_results['theme'] ) ) {
 463              foreach ( $this->update_results['theme'] as $upgrade ) {
 464                  $theme_stats[ $upgrade->item->theme ] = ( true === $upgrade->result );
 465              }
 466          }
 467          wp_update_themes( $theme_stats ); // Check for theme updates.
 468  
 469          $plugin_stats = array();
 470          if ( isset( $this->update_results['plugin'] ) ) {
 471              foreach ( $this->update_results['plugin'] as $upgrade ) {
 472                  $plugin_stats[ $upgrade->item->plugin ] = ( true === $upgrade->result );
 473              }
 474          }
 475          wp_update_plugins( $plugin_stats ); // Check for plugin updates.
 476  
 477          // Finally, process any new translations.
 478          $language_updates = wp_get_translation_updates();
 479          if ( $language_updates ) {
 480              foreach ( $language_updates as $update ) {
 481                  $this->update( 'translation', $update );
 482              }
 483  
 484              // Clear existing caches.
 485              wp_clean_update_cache();
 486  
 487              wp_version_check();  // Check for core updates.
 488              wp_update_themes();  // Check for theme updates.
 489              wp_update_plugins(); // Check for plugin updates.
 490          }
 491  
 492          // Send debugging email to admin for all development installations.
 493          if ( ! empty( $this->update_results ) ) {
 494              $development_version = false !== strpos( get_bloginfo( 'version' ), '-' );
 495  
 496              /**
 497               * Filters whether to send a debugging email for each automatic background update.
 498               *
 499               * @since 3.7.0
 500               *
 501               * @param bool $development_version By default, emails are sent if the
 502               *                                  install is a development version.
 503               *                                  Return false to avoid the email.
 504               */
 505              if ( apply_filters( 'automatic_updates_send_debug_email', $development_version ) ) {
 506                  $this->send_debug_email();
 507              }
 508  
 509              if ( ! empty( $this->update_results['core'] ) ) {
 510                  $this->after_core_update( $this->update_results['core'][0] );
 511              } elseif ( ! empty( $this->update_results['plugin'] ) || ! empty( $this->update_results['theme'] ) ) {
 512                  $this->after_plugin_theme_update( $this->update_results );
 513              }
 514  
 515              /**
 516               * Fires after all automatic updates have run.
 517               *
 518               * @since 3.8.0
 519               *
 520               * @param array $update_results The results of all attempted updates.
 521               */
 522              do_action( 'automatic_updates_complete', $this->update_results );
 523          }
 524  
 525          WP_Upgrader::release_lock( 'auto_updater' );
 526      }
 527  
 528      /**
 529       * If we tried to perform a core update, check if we should send an email,
 530       * and if we need to avoid processing future updates.
 531       *
 532       * @since 3.7.0
 533       *
 534       * @param object $update_result The result of the core update. Includes the update offer and result.
 535       */
 536  	protected function after_core_update( $update_result ) {
 537          $wp_version = get_bloginfo( 'version' );
 538  
 539          $core_update = $update_result->item;
 540          $result      = $update_result->result;
 541  
 542          if ( ! is_wp_error( $result ) ) {
 543              $this->send_email( 'success', $core_update );
 544              return;
 545          }
 546  
 547          $error_code = $result->get_error_code();
 548  
 549          // Any of these WP_Error codes are critical failures, as in they occurred after we started to copy core files.
 550          // We should not try to perform a background update again until there is a successful one-click update performed by the user.
 551          $critical = false;
 552          if ( 'disk_full' === $error_code || false !== strpos( $error_code, '__copy_dir' ) ) {
 553              $critical = true;
 554          } elseif ( 'rollback_was_required' === $error_code && is_wp_error( $result->get_error_data()->rollback ) ) {
 555              // A rollback is only critical if it failed too.
 556              $critical        = true;
 557              $rollback_result = $result->get_error_data()->rollback;
 558          } elseif ( false !== strpos( $error_code, 'do_rollback' ) ) {
 559              $critical = true;
 560          }
 561  
 562          if ( $critical ) {
 563              $critical_data = array(
 564                  'attempted'  => $core_update->current,
 565                  'current'    => $wp_version,
 566                  'error_code' => $error_code,
 567                  'error_data' => $result->get_error_data(),
 568                  'timestamp'  => time(),
 569                  'critical'   => true,
 570              );
 571              if ( isset( $rollback_result ) ) {
 572                  $critical_data['rollback_code'] = $rollback_result->get_error_code();
 573                  $critical_data['rollback_data'] = $rollback_result->get_error_data();
 574              }
 575              update_site_option( 'auto_core_update_failed', $critical_data );
 576              $this->send_email( 'critical', $core_update, $result );
 577              return;
 578          }
 579  
 580          /*
 581           * Any other WP_Error code (like download_failed or files_not_writable) occurs before
 582           * we tried to copy over core files. Thus, the failures are early and graceful.
 583           *
 584           * We should avoid trying to perform a background update again for the same version.
 585           * But we can try again if another version is released.
 586           *
 587           * For certain 'transient' failures, like download_failed, we should allow retries.
 588           * In fact, let's schedule a special update for an hour from now. (It's possible
 589           * the issue could actually be on WordPress.org's side.) If that one fails, then email.
 590           */
 591          $send               = true;
 592          $transient_failures = array( 'incompatible_archive', 'download_failed', 'insane_distro', 'locked' );
 593          if ( in_array( $error_code, $transient_failures, true ) && ! get_site_option( 'auto_core_update_failed' ) ) {
 594              wp_schedule_single_event( time() + HOUR_IN_SECONDS, 'wp_maybe_auto_update' );
 595              $send = false;
 596          }
 597  
 598          $n = get_site_option( 'auto_core_update_notified' );
 599          // Don't notify if we've already notified the same email address of the same version of the same notification type.
 600          if ( $n && 'fail' === $n['type'] && get_site_option( 'admin_email' ) === $n['email'] && $n['version'] == $core_update->current ) {
 601              $send = false;
 602          }
 603  
 604          update_site_option(
 605              'auto_core_update_failed',
 606              array(
 607                  'attempted'  => $core_update->current,
 608                  'current'    => $wp_version,
 609                  'error_code' => $error_code,
 610                  'error_data' => $result->get_error_data(),
 611                  'timestamp'  => time(),
 612                  'retry'      => in_array( $error_code, $transient_failures, true ),
 613              )
 614          );
 615  
 616          if ( $send ) {
 617              $this->send_email( 'fail', $core_update, $result );
 618          }
 619      }
 620  
 621      /**
 622       * Sends an email upon the completion or failure of a background core update.
 623       *
 624       * @since 3.7.0
 625       *
 626       * @param string $type        The type of email to send. Can be one of 'success', 'fail', 'manual', 'critical'.
 627       * @param object $core_update The update offer that was attempted.
 628       * @param mixed  $result      Optional. The result for the core update. Can be WP_Error.
 629       */
 630  	protected function send_email( $type, $core_update, $result = null ) {
 631          update_site_option(
 632              'auto_core_update_notified',
 633              array(
 634                  'type'      => $type,
 635                  'email'     => get_site_option( 'admin_email' ),
 636                  'version'   => $core_update->current,
 637                  'timestamp' => time(),
 638              )
 639          );
 640  
 641          $next_user_core_update = get_preferred_from_update_core();
 642  
 643          // If the update transient is empty, use the update we just performed.
 644          if ( ! $next_user_core_update ) {
 645              $next_user_core_update = $core_update;
 646          }
 647  
 648          $newer_version_available = ( 'upgrade' === $next_user_core_update->response && version_compare( $next_user_core_update->version, $core_update->version, '>' ) );
 649  
 650          /**
 651           * Filters whether to send an email following an automatic background core update.
 652           *
 653           * @since 3.7.0
 654           *
 655           * @param bool   $send        Whether to send the email. Default true.
 656           * @param string $type        The type of email to send. Can be one of
 657           *                            'success', 'fail', 'critical'.
 658           * @param object $core_update The update offer that was attempted.
 659           * @param mixed  $result      The result for the core update. Can be WP_Error.
 660           */
 661          if ( 'manual' !== $type && ! apply_filters( 'auto_core_update_send_email', true, $type, $core_update, $result ) ) {
 662              return;
 663          }
 664  
 665          switch ( $type ) {
 666              case 'success': // We updated.
 667                  /* translators: Site updated notification email subject. 1: Site title, 2: WordPress version. */
 668                  $subject = __( '[%1$s] Your site has updated to WordPress %2$s' );
 669                  break;
 670  
 671              case 'fail':   // We tried to update but couldn't.
 672              case 'manual': // We can't update (and made no attempt).
 673                  /* translators: Update available notification email subject. 1: Site title, 2: WordPress version. */
 674                  $subject = __( '[%1$s] WordPress %2$s is available. Please update!' );
 675                  break;
 676  
 677              case 'critical': // We tried to update, started to copy files, then things went wrong.
 678                  /* translators: Site down notification email subject. 1: Site title. */
 679                  $subject = __( '[%1$s] URGENT: Your site may be down due to a failed update' );
 680                  break;
 681  
 682              default:
 683                  return;
 684          }
 685  
 686          // If the auto-update is not to the latest version, say that the current version of WP is available instead.
 687          $version = 'success' === $type ? $core_update->current : $next_user_core_update->current;
 688          $subject = sprintf( $subject, wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ), $version );
 689  
 690          $body = '';
 691  
 692          switch ( $type ) {
 693              case 'success':
 694                  $body .= sprintf(
 695                      /* translators: 1: Home URL, 2: WordPress version. */
 696                      __( 'Howdy! Your site at %1$s has been updated automatically to WordPress %2$s.' ),
 697                      home_url(),
 698                      $core_update->current
 699                  );
 700                  $body .= "\n\n";
 701                  if ( ! $newer_version_available ) {
 702                      $body .= __( 'No further action is needed on your part.' ) . ' ';
 703                  }
 704  
 705                  // Can only reference the About screen if their update was successful.
 706                  list( $about_version ) = explode( '-', $core_update->current, 2 );
 707                  /* translators: %s: WordPress version. */
 708                  $body .= sprintf( __( 'For more on version %s, see the About WordPress screen:' ), $about_version );
 709                  $body .= "\n" . admin_url( 'about.php' );
 710  
 711                  if ( $newer_version_available ) {
 712                      /* translators: %s: WordPress latest version. */
 713                      $body .= "\n\n" . sprintf( __( 'WordPress %s is also now available.' ), $next_user_core_update->current ) . ' ';
 714                      $body .= __( 'Updating is easy and only takes a few moments:' );
 715                      $body .= "\n" . network_admin_url( 'update-core.php' );
 716                  }
 717  
 718                  break;
 719  
 720              case 'fail':
 721              case 'manual':
 722                  $body .= sprintf(
 723                      /* translators: 1: Home URL, 2: WordPress version. */
 724                      __( 'Please update your site at %1$s to WordPress %2$s.' ),
 725                      home_url(),
 726                      $next_user_core_update->current
 727                  );
 728  
 729                  $body .= "\n\n";
 730  
 731                  // Don't show this message if there is a newer version available.
 732                  // Potential for confusion, and also not useful for them to know at this point.
 733                  if ( 'fail' === $type && ! $newer_version_available ) {
 734                      $body .= __( 'We tried but were unable to update your site automatically.' ) . ' ';
 735                  }
 736  
 737                  $body .= __( 'Updating is easy and only takes a few moments:' );
 738                  $body .= "\n" . network_admin_url( 'update-core.php' );
 739                  break;
 740  
 741              case 'critical':
 742                  if ( $newer_version_available ) {
 743                      $body .= sprintf(
 744                          /* translators: 1: Home URL, 2: WordPress version. */
 745                          __( 'Your site at %1$s experienced a critical failure while trying to update WordPress to version %2$s.' ),
 746                          home_url(),
 747                          $core_update->current
 748                      );
 749                  } else {
 750                      $body .= sprintf(
 751                          /* translators: 1: Home URL, 2: WordPress latest version. */
 752                          __( 'Your site at %1$s experienced a critical failure while trying to update to the latest version of WordPress, %2$s.' ),
 753                          home_url(),
 754                          $core_update->current
 755                      );
 756                  }
 757  
 758                  $body .= "\n\n" . __( "This means your site may be offline or broken. Don't panic; this can be fixed." );
 759  
 760                  $body .= "\n\n" . __( "Please check out your site now. It's possible that everything is working. If it says you need to update, you should do so:" );
 761                  $body .= "\n" . network_admin_url( 'update-core.php' );
 762                  break;
 763          }
 764  
 765          $critical_support = 'critical' === $type && ! empty( $core_update->support_email );
 766          if ( $critical_support ) {
 767              // Support offer if available.
 768              $body .= "\n\n" . sprintf(
 769                  /* translators: %s: Support email address. */
 770                  __( 'The WordPress team is willing to help you. Forward this email to %s and the team will work with you to make sure your site is working.' ),
 771                  $core_update->support_email
 772              );
 773          } else {
 774              // Add a note about the support forums.
 775              $body .= "\n\n" . __( 'If you experience any issues or need support, the volunteers in the WordPress.org support forums may be able to help.' );
 776              $body .= "\n" . __( 'https://wordpress.org/support/forums/' );
 777          }
 778  
 779          // Updates are important!
 780          if ( 'success' !== $type || $newer_version_available ) {
 781              $body .= "\n\n" . __( 'Keeping your site updated is important for security. It also makes the internet a safer place for you and your readers.' );
 782          }
 783  
 784          if ( $critical_support ) {
 785              $body .= ' ' . __( "If you reach out to us, we'll also ensure you'll never have this problem again." );
 786          }
 787  
 788          // If things are successful and we're now on the latest, mention plugins and themes if any are out of date.
 789          if ( 'success' === $type && ! $newer_version_available && ( get_plugin_updates() || get_theme_updates() ) ) {
 790              $body .= "\n\n" . __( 'You also have some plugins or themes with updates available. Update them now:' );
 791              $body .= "\n" . network_admin_url();
 792          }
 793  
 794          $body .= "\n\n" . __( 'The WordPress Team' ) . "\n";
 795  
 796          if ( 'critical' === $type && is_wp_error( $result ) ) {
 797              $body .= "\n***\n\n";
 798              /* translators: %s: WordPress version. */
 799              $body .= sprintf( __( 'Your site was running version %s.' ), get_bloginfo( 'version' ) );
 800              $body .= ' ' . __( 'We have some data that describes the error your site encountered.' );
 801              $body .= ' ' . __( 'Your hosting company, support forum volunteers, or a friendly developer may be able to use this information to help you:' );
 802  
 803              // If we had a rollback and we're still critical, then the rollback failed too.
 804              // Loop through all errors (the main WP_Error, the update result, the rollback result) for code, data, etc.
 805              if ( 'rollback_was_required' === $result->get_error_code() ) {
 806                  $errors = array( $result, $result->get_error_data()->update, $result->get_error_data()->rollback );
 807              } else {
 808                  $errors = array( $result );
 809              }
 810  
 811              foreach ( $errors as $error ) {
 812                  if ( ! is_wp_error( $error ) ) {
 813                      continue;
 814                  }
 815  
 816                  $error_code = $error->get_error_code();
 817                  /* translators: %s: Error code. */
 818                  $body .= "\n\n" . sprintf( __( 'Error code: %s' ), $error_code );
 819  
 820                  if ( 'rollback_was_required' === $error_code ) {
 821                      continue;
 822                  }
 823  
 824                  if ( $error->get_error_message() ) {
 825                      $body .= "\n" . $error->get_error_message();
 826                  }
 827  
 828                  $error_data = $error->get_error_data();
 829                  if ( $error_data ) {
 830                      $body .= "\n" . implode( ', ', (array) $error_data );
 831                  }
 832              }
 833  
 834              $body .= "\n";
 835          }
 836  
 837          $to      = get_site_option( 'admin_email' );
 838          $headers = '';
 839  
 840          $email = compact( 'to', 'subject', 'body', 'headers' );
 841  
 842          /**
 843           * Filters the email sent following an automatic background core update.
 844           *
 845           * @since 3.7.0
 846           *
 847           * @param array $email {
 848           *     Array of email arguments that will be passed to wp_mail().
 849           *
 850           *     @type string $to      The email recipient. An array of emails
 851           *                            can be returned, as handled by wp_mail().
 852           *     @type string $subject The email's subject.
 853           *     @type string $body    The email message body.
 854           *     @type string $headers Any email headers, defaults to no headers.
 855           * }
 856           * @param string $type        The type of email being sent. Can be one of
 857           *                            'success', 'fail', 'manual', 'critical'.
 858           * @param object $core_update The update offer that was attempted.
 859           * @param mixed  $result      The result for the core update. Can be WP_Error.
 860           */
 861          $email = apply_filters( 'auto_core_update_email', $email, $type, $core_update, $result );
 862  
 863          wp_mail( $email['to'], wp_specialchars_decode( $email['subject'] ), $email['body'], $email['headers'] );
 864      }
 865  
 866  
 867      /**
 868       * If we tried to perform plugin or theme updates, check if we should send an email.
 869       *
 870       * @since 5.5.0
 871       *
 872       * @param array $update_results The result of updates tasks.
 873       */
 874  	protected function after_plugin_theme_update( $update_results ) {
 875          $successful_updates = array();
 876          $failed_updates     = array();
 877  
 878          /**
 879           * Filters whether to send an email following an automatic background plugin update.
 880           *
 881           * @since 5.5.0
 882           *
 883           * @param bool $enabled True if plugins notifications are enabled, false otherwise.
 884           */
 885          $notifications_enabled = apply_filters( 'auto_plugin_update_send_email', true );
 886  
 887          if ( ! empty( $update_results['plugin'] ) && $notifications_enabled ) {
 888              foreach ( $update_results['plugin'] as $update_result ) {
 889                  if ( true === $update_result->result ) {
 890                      $successful_updates['plugin'][] = $update_result;
 891                  } else {
 892                      $failed_updates['plugin'][] = $update_result;
 893                  }
 894              }
 895          }
 896  
 897          /**
 898           * Filters whether to send an email following an automatic background theme update.
 899           *
 900           * @since 5.5.0
 901           *
 902           * @param bool $enabled True if notifications are enabled, false otherwise.
 903           */
 904          $notifications_enabled = apply_filters( 'auto_theme_update_send_email', true );
 905  
 906          if ( ! empty( $update_results['theme'] ) && $notifications_enabled ) {
 907              foreach ( $update_results['theme'] as $update_result ) {
 908                  if ( true === $update_result->result ) {
 909                      $successful_updates['theme'][] = $update_result;
 910                  } else {
 911                      $failed_updates['theme'][] = $update_result;
 912                  }
 913              }
 914          }
 915  
 916          if ( empty( $successful_updates ) && empty( $failed_updates ) ) {
 917              return;
 918          }
 919  
 920          if ( empty( $failed_updates ) ) {
 921              $this->send_plugin_theme_email( 'success', $successful_updates, $failed_updates );
 922          } elseif ( empty( $successful_updates ) ) {
 923              $this->send_plugin_theme_email( 'fail', $successful_updates, $failed_updates );
 924          } else {
 925              $this->send_plugin_theme_email( 'mixed', $successful_updates, $failed_updates );
 926          }
 927      }
 928  
 929      /**
 930       * Sends an email upon the completion or failure of a plugin or theme background update.
 931       *
 932       * @since 5.5.0
 933       *
 934       * @param string $type               The type of email to send. Can be one of 'success', 'fail', 'mixed'.
 935       * @param array  $successful_updates A list of updates that succeeded.
 936       * @param array  $failed_updates     A list of updates that failed.
 937       */
 938  	protected function send_plugin_theme_email( $type, $successful_updates, $failed_updates ) {
 939          // No updates were attempted.
 940          if ( empty( $successful_updates ) && empty( $failed_updates ) ) {
 941              return;
 942          }
 943  
 944          $unique_failures     = false;
 945          $past_failure_emails = get_option( 'auto_plugin_theme_update_emails', array() );
 946  
 947          /*
 948           * When only failures have occurred, an email should only be sent if there are unique failures.
 949           * A failure is considered unique if an email has not been sent for an update attempt failure
 950           * to a plugin or theme with the same new_version.
 951           */
 952          if ( 'fail' === $type ) {
 953              foreach ( $failed_updates as $update_type => $failures ) {
 954                  foreach ( $failures as $failed_update ) {
 955                      if ( ! isset( $past_failure_emails[ $failed_update->item->{$update_type} ] ) ) {
 956                          $unique_failures = true;
 957                          continue;
 958                      }
 959  
 960                      // Check that the failure represents a new failure based on the new_version.
 961                      if ( version_compare( $past_failure_emails[ $failed_update->item->{$update_type} ], $failed_update->item->new_version, '<' ) ) {
 962                          $unique_failures = true;
 963                      }
 964                  }
 965              }
 966  
 967              if ( ! $unique_failures ) {
 968                  return;
 969              }
 970          }
 971  
 972          $body               = array();
 973          $successful_plugins = ( ! empty( $successful_updates['plugin'] ) );
 974          $successful_themes  = ( ! empty( $successful_updates['theme'] ) );
 975          $failed_plugins     = ( ! empty( $failed_updates['plugin'] ) );
 976          $failed_themes      = ( ! empty( $failed_updates['theme'] ) );
 977  
 978          switch ( $type ) {
 979              case 'success':
 980                  if ( $successful_plugins && $successful_themes ) {
 981                      /* translators: %s: Site title. */
 982                      $subject = __( '[%s] Some plugins and themes have automatically updated' );
 983                      $body[]  = sprintf(
 984                          /* translators: %s: Home URL. */
 985                          __( 'Howdy! Some plugins and themes have automatically updated to their latest versions on your site at %s. No further action is needed on your part.' ),
 986                          home_url()
 987                      );
 988                  } elseif ( $successful_plugins ) {
 989                      /* translators: %s: Site title. */
 990                      $subject = __( '[%s] Some plugins were automatically updated' );
 991                      $body[]  = sprintf(
 992                          /* translators: %s: Home URL. */
 993                          __( 'Howdy! Some plugins have automatically updated to their latest versions on your site at %s. No further action is needed on your part.' ),
 994                          home_url()
 995                      );
 996                  } else {
 997                      /* translators: %s: Site title. */
 998                      $subject = __( '[%s] Some themes were automatically updated' );
 999                      $body[]  = sprintf(
1000                          /* translators: %s: Home URL. */
1001                          __( 'Howdy! Some themes have automatically updated to their latest versions on your site at %s. No further action is needed on your part.' ),
1002                          home_url()
1003                      );
1004                  }
1005  
1006                  break;
1007              case 'fail':
1008              case 'mixed':
1009                  if ( $failed_plugins && $failed_themes ) {
1010                      /* translators: %s: Site title. */
1011                      $subject = __( '[%s] Some plugins and themes have failed to update' );
1012                      $body[]  = sprintf(
1013                          /* translators: %s: Home URL. */
1014                          __( 'Howdy! Plugins and themes failed to update on your site at %s.' ),
1015                          home_url()
1016                      );
1017                  } elseif ( $failed_plugins ) {
1018                      /* translators: %s: Site title. */
1019                      $subject = __( '[%s] Some plugins have failed to update' );
1020                      $body[]  = sprintf(
1021                          /* translators: %s: Home URL. */
1022                          __( 'Howdy! Plugins failed to update on your site at %s.' ),
1023                          home_url()
1024                      );
1025                  } else {
1026                      /* translators: %s: Site title. */
1027                      $subject = __( '[%s] Some themes have failed to update' );
1028                      $body[]  = sprintf(
1029                          /* translators: %s: Home URL. */
1030                          __( 'Howdy! Themes failed to update on your site at %s.' ),
1031                          home_url()
1032                      );
1033                  }
1034  
1035                  break;
1036          }
1037  
1038          if ( in_array( $type, array( 'fail', 'mixed' ), true ) ) {
1039              $body[] = "\n";
1040              $body[] = __( 'Please check your site now. It’s possible that everything is working. If there are updates available, you should update.' );
1041              $body[] = "\n";
1042  
1043              // List failed plugin updates.
1044              if ( ! empty( $failed_updates['plugin'] ) ) {
1045                  $body[] = __( 'These plugins failed to update:' );
1046  
1047                  foreach ( $failed_updates['plugin'] as $item ) {
1048                      $body[] = "- {$item->name}";
1049  
1050                      $past_failure_emails[ $item->item->plugin ] = $item->item->new_version;
1051                  }
1052  
1053                  $body[] = "\n";
1054              }
1055  
1056              // List failed theme updates.
1057              if ( ! empty( $failed_updates['theme'] ) ) {
1058                  $body[] = __( 'These themes failed to update:' );
1059  
1060                  foreach ( $failed_updates['theme'] as $item ) {
1061                      $body[] = "- {$item->name}";
1062  
1063                      $past_failure_emails[ $item->item->theme ] = $item->item->new_version;
1064                  }
1065  
1066                  $body[] = "\n";
1067              }
1068          }
1069  
1070          // List successful updates.
1071          if ( in_array( $type, array( 'success', 'mixed' ), true ) ) {
1072              $body[] = "\n";
1073  
1074              // List successful plugin updates.
1075              if ( ! empty( $successful_updates['plugin'] ) ) {
1076                  $body[] = __( 'These plugins are now up to date:' );
1077  
1078                  foreach ( $successful_updates['plugin'] as $item ) {
1079                      $body[] = "- {$item->name}";
1080  
1081                      unset( $past_failure_emails[ $item->item->plugin ] );
1082                  }
1083  
1084                  $body[] = "\n";
1085              }
1086  
1087              // List successful theme updates.
1088              if ( ! empty( $successful_updates['theme'] ) ) {
1089                  $body[] = __( 'These themes are now up to date:' );
1090  
1091                  foreach ( $successful_updates['theme'] as $item ) {
1092                      $body[] = "- {$item->name}";
1093  
1094                      unset( $past_failure_emails[ $item->item->theme ] );
1095                  }
1096  
1097                  $body[] = "\n";
1098              }
1099          }
1100  
1101          if ( $failed_plugins ) {
1102              $body[] = sprintf(
1103                  /* translators: %s: Plugins screen URL. */
1104                  __( 'To manage plugins on your site, visit the Plugins page: %s' ),
1105                  admin_url( 'plugins.php' )
1106              );
1107              $body[] = "\n";
1108          }
1109  
1110          if ( $failed_themes ) {
1111              $body[] = sprintf(
1112                  /* translators: %s: Themes screen URL. */
1113                  __( 'To manage themes on your site, visit the Themes page: %s' ),
1114                  admin_url( 'themes.php' )
1115              );
1116              $body[] = "\n";
1117          }
1118  
1119          // Add a note about the support forums.
1120          $body[] = __( 'If you experience any issues or need support, the volunteers in the WordPress.org support forums may be able to help.' );
1121          $body[] = __( 'https://wordpress.org/support/forums/' );
1122          $body[] = "\n" . __( 'The WordPress Team' );
1123  
1124          $body    = implode( "\n", $body );
1125          $to      = get_site_option( 'admin_email' );
1126          $subject = sprintf( $subject, wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ) );
1127          $headers = '';
1128  
1129          $email = compact( 'to', 'subject', 'body', 'headers' );
1130  
1131          /**
1132           * Filters the email sent following an automatic background update for plugins and themes.
1133           *
1134           * @since 5.5.0
1135           *
1136           * @param array  $email {
1137           *     Array of email arguments that will be passed to wp_mail().
1138           *
1139           *     @type string $to      The email recipient. An array of emails
1140           *                           can be returned, as handled by wp_mail().
1141           *     @type string $subject The email's subject.
1142           *     @type string $body    The email message body.
1143           *     @type string $headers Any email headers, defaults to no headers.
1144           * }
1145           * @param string $type               The type of email being sent. Can be one of 'success', 'fail', 'mixed'.
1146           * @param array  $successful_updates A list of updates that succeeded.
1147           * @param array  $failed_updates     A list of updates that failed.
1148           */
1149          $email = apply_filters( 'auto_plugin_theme_update_email', $email, $type, $successful_updates, $failed_updates );
1150  
1151          $result = wp_mail( $email['to'], wp_specialchars_decode( $email['subject'] ), $email['body'], $email['headers'] );
1152  
1153          if ( $result ) {
1154              update_option( 'auto_plugin_theme_update_emails', $past_failure_emails );
1155          }
1156      }
1157  
1158      /**
1159       * Prepares and sends an email of a full log of background update results, useful for debugging and geekery.
1160       *
1161       * @since 3.7.0
1162       */
1163  	protected function send_debug_email() {
1164          $update_count = 0;
1165          foreach ( $this->update_results as $type => $updates ) {
1166              $update_count += count( $updates );
1167          }
1168  
1169          $body     = array();
1170          $failures = 0;
1171  
1172          /* translators: %s: Network home URL. */
1173          $body[] = sprintf( __( 'WordPress site: %s' ), network_home_url( '/' ) );
1174  
1175          // Core.
1176          if ( isset( $this->update_results['core'] ) ) {
1177              $result = $this->update_results['core'][0];
1178              if ( $result->result && ! is_wp_error( $result->result ) ) {
1179                  /* translators: %s: WordPress version. */
1180                  $body[] = sprintf( __( 'SUCCESS: WordPress was successfully updated to %s' ), $result->name );
1181              } else {
1182                  /* translators: %s: WordPress version. */
1183                  $body[] = sprintf( __( 'FAILED: WordPress failed to update to %s' ), $result->name );
1184                  $failures++;
1185              }
1186              $body[] = '';
1187          }
1188  
1189          // Plugins, Themes, Translations.
1190          foreach ( array( 'plugin', 'theme', 'translation' ) as $type ) {
1191              if ( ! isset( $this->update_results[ $type ] ) ) {
1192                  continue;
1193              }
1194              $success_items = wp_list_filter( $this->update_results[ $type ], array( 'result' => true ) );
1195              if ( $success_items ) {
1196                  $messages = array(
1197                      'plugin'      => __( 'The following plugins were successfully updated:' ),
1198                      'theme'       => __( 'The following themes were successfully updated:' ),
1199                      'translation' => __( 'The following translations were successfully updated:' ),
1200                  );
1201  
1202                  $body[] = $messages[ $type ];
1203                  foreach ( wp_list_pluck( $success_items, 'name' ) as $name ) {
1204                      /* translators: %s: Name of plugin / theme / translation. */
1205                      $body[] = ' * ' . sprintf( __( 'SUCCESS: %s' ), $name );
1206                  }
1207              }
1208              if ( $success_items != $this->update_results[ $type ] ) {
1209                  // Failed updates.
1210                  $messages = array(
1211                      'plugin'      => __( 'The following plugins failed to update:' ),
1212                      'theme'       => __( 'The following themes failed to update:' ),
1213                      'translation' => __( 'The following translations failed to update:' ),
1214                  );
1215  
1216                  $body[] = $messages[ $type ];
1217                  foreach ( $this->update_results[ $type ] as $item ) {
1218                      if ( ! $item->result || is_wp_error( $item->result ) ) {
1219                          /* translators: %s: Name of plugin / theme / translation. */
1220                          $body[] = ' * ' . sprintf( __( 'FAILED: %s' ), $item->name );
1221                          $failures++;
1222                      }
1223                  }
1224              }
1225              $body[] = '';
1226          }
1227  
1228          $site_title = wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES );
1229          if ( $failures ) {
1230              $body[] = trim(
1231                  __(
1232                      "BETA TESTING?
1233  =============
1234  
1235  This debugging email is sent when you are using a development version of WordPress.
1236  
1237  If you think these failures might be due to a bug in WordPress, could you report it?
1238   * Open a thread in the support forums: https://wordpress.org/support/forum/alphabeta
1239   * Or, if you're comfortable writing a bug report: https://core.trac.wordpress.org/
1240  
1241  Thanks! -- The WordPress Team"
1242                  )
1243              );
1244              $body[] = '';
1245  
1246              /* translators: Background update failed notification email subject. %s: Site title. */
1247              $subject = sprintf( __( '[%s] Background Update Failed' ), $site_title );
1248          } else {
1249              /* translators: Background update finished notification email subject. %s: Site title. */
1250              $subject = sprintf( __( '[%s] Background Update Finished' ), $site_title );
1251          }
1252  
1253          $body[] = trim(
1254              __(
1255                  'UPDATE LOG
1256  =========='
1257              )
1258          );
1259          $body[] = '';
1260  
1261          foreach ( array( 'core', 'plugin', 'theme', 'translation' ) as $type ) {
1262              if ( ! isset( $this->update_results[ $type ] ) ) {
1263                  continue;
1264              }
1265              foreach ( $this->update_results[ $type ] as $update ) {
1266                  $body[] = $update->name;
1267                  $body[] = str_repeat( '-', strlen( $update->name ) );
1268                  foreach ( $update->messages as $message ) {
1269                      $body[] = '  ' . html_entity_decode( str_replace( '&#8230;', '...', $message ) );
1270                  }
1271                  if ( is_wp_error( $update->result ) ) {
1272                      $results = array( 'update' => $update->result );
1273                      // If we rolled back, we want to know an error that occurred then too.
1274                      if ( 'rollback_was_required' === $update->result->get_error_code() ) {
1275                          $results = (array) $update->result->get_error_data();
1276                      }
1277                      foreach ( $results as $result_type => $result ) {
1278                          if ( ! is_wp_error( $result ) ) {
1279                              continue;
1280                          }
1281  
1282                          if ( 'rollback' === $result_type ) {
1283                              /* translators: 1: Error code, 2: Error message. */
1284                              $body[] = '  ' . sprintf( __( 'Rollback Error: [%1$s] %2$s' ), $result->get_error_code(), $result->get_error_message() );
1285                          } else {
1286                              /* translators: 1: Error code, 2: Error message. */
1287                              $body[] = '  ' . sprintf( __( 'Error: [%1$s] %2$s' ), $result->get_error_code(), $result->get_error_message() );
1288                          }
1289  
1290                          if ( $result->get_error_data() ) {
1291                              $body[] = '         ' . implode( ', ', (array) $result->get_error_data() );
1292                          }
1293                      }
1294                  }
1295                  $body[] = '';
1296              }
1297          }
1298  
1299          $email = array(
1300              'to'      => get_site_option( 'admin_email' ),
1301              'subject' => $subject,
1302              'body'    => implode( "\n", $body ),
1303              'headers' => '',
1304          );
1305  
1306          /**
1307           * Filters the debug email that can be sent following an automatic
1308           * background core update.
1309           *
1310           * @since 3.8.0
1311           *
1312           * @param array $email {
1313           *     Array of email arguments that will be passed to wp_mail().
1314           *
1315           *     @type string $to      The email recipient. An array of emails
1316           *                           can be returned, as handled by wp_mail().
1317           *     @type string $subject Email subject.
1318           *     @type string $body    Email message body.
1319           *     @type string $headers Any email headers. Default empty.
1320           * }
1321           * @param int   $failures The number of failures encountered while upgrading.
1322           * @param mixed $results  The results of all attempted updates.
1323           */
1324          $email = apply_filters( 'automatic_updates_debug_email', $email, $failures, $this->update_results );
1325  
1326          wp_mail( $email['to'], wp_specialchars_decode( $email['subject'] ), $email['body'], $email['headers'] );
1327      }
1328  }


Generated : Thu Jul 16 08:20:02 2020 Cross-referenced by PHPXref