wp/wp-admin/includes/class-wp-automatic-updater.php
changeset 18 be944660c56a
parent 16 a86126ab1dd4
child 19 3d72ae0968f4
equal deleted inserted replaced
17:34716fd837a4 18:be944660c56a
    60 	 *
    60 	 *
    61 	 * Checks for Subversion, Git, Mercurial, and Bazaar. It recursively looks up the
    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
    62 	 * filesystem to the top of the drive, erring on the side of detecting a VCS
    63 	 * checkout somewhere.
    63 	 * checkout somewhere.
    64 	 *
    64 	 *
    65 	 * ABSPATH is always checked in addition to whatever $context is (which may be the
    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
    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
    67 	 * using version control *anywhere*, then you should be making decisions for
    68 	 * how things get updated.
    68 	 * how things get updated.
    69 	 *
    69 	 *
    70 	 * @since 3.7.0
    70 	 * @since 3.7.0
    71 	 *
    71 	 *
    72 	 * @param string $context The filesystem path to check, in addition to ABSPATH.
    72 	 * @param string $context The filesystem path to check, in addition to ABSPATH.
       
    73 	 * @return bool True if a VCS checkout was discovered at `$context` or ABSPATH,
       
    74 	 *              or anywhere higher. False otherwise.
    73 	 */
    75 	 */
    74 	public function is_vcs_checkout( $context ) {
    76 	public function is_vcs_checkout( $context ) {
    75 		$context_dirs = array( untrailingslashit( $context ) );
    77 		$context_dirs = array( untrailingslashit( $context ) );
    76 		if ( ABSPATH !== $context ) {
    78 		if ( ABSPATH !== $context ) {
    77 			$context_dirs[] = untrailingslashit( ABSPATH );
    79 			$context_dirs[] = untrailingslashit( ABSPATH );
   110 		 * Filters whether the automatic updater should consider a filesystem
   112 		 * Filters whether the automatic updater should consider a filesystem
   111 		 * location to be potentially managed by a version control system.
   113 		 * location to be potentially managed by a version control system.
   112 		 *
   114 		 *
   113 		 * @since 3.7.0
   115 		 * @since 3.7.0
   114 		 *
   116 		 *
   115 		 * @param bool $checkout  Whether a VCS checkout was discovered at $context
   117 		 * @param bool $checkout  Whether a VCS checkout was discovered at `$context`
   116 		 *                        or ABSPATH, or anywhere higher.
   118 		 *                        or ABSPATH, or anywhere higher.
   117 		 * @param string $context The filesystem context (a path) against which
   119 		 * @param string $context The filesystem context (a path) against which
   118 		 *                        filesystem status should be checked.
   120 		 *                        filesystem status should be checked.
   119 		 */
   121 		 */
   120 		return apply_filters( 'automatic_updates_is_vcs_checkout', $checkout, $context );
   122 		return apply_filters( 'automatic_updates_is_vcs_checkout', $checkout, $context );
   130 	 * @param string $type    The type of update being checked: 'core', 'theme',
   132 	 * @param string $type    The type of update being checked: 'core', 'theme',
   131 	 *                        'plugin', 'translation'.
   133 	 *                        'plugin', 'translation'.
   132 	 * @param object $item    The update offer.
   134 	 * @param object $item    The update offer.
   133 	 * @param string $context The filesystem context (a path) against which filesystem
   135 	 * @param string $context The filesystem context (a path) against which filesystem
   134 	 *                        access and status should be checked.
   136 	 *                        access and status should be checked.
       
   137 	 * @return bool True if the item should be updated, false otherwise.
   135 	 */
   138 	 */
   136 	public function should_update( $type, $item, $context ) {
   139 	public function should_update( $type, $item, $context ) {
   137 		// Used to see if WP_Filesystem is set up to allow unattended updates.
   140 		// Used to see if WP_Filesystem is set up to allow unattended updates.
   138 		$skin = new Automatic_Upgrader_Skin;
   141 		$skin = new Automatic_Upgrader_Skin;
   139 
   142 
   146 		if ( 'core' === $type && isset( $item->new_files ) && ! $item->new_files ) {
   149 		if ( 'core' === $type && isset( $item->new_files ) && ! $item->new_files ) {
   147 			$allow_relaxed_file_ownership = true;
   150 			$allow_relaxed_file_ownership = true;
   148 		}
   151 		}
   149 
   152 
   150 		// If we can't do an auto core update, we may still be able to email the user.
   153 		// 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 ) ) {
   154 		if ( ! $skin->request_filesystem_credentials( false, $context, $allow_relaxed_file_ownership )
       
   155 			|| $this->is_vcs_checkout( $context )
       
   156 		) {
   152 			if ( 'core' === $type ) {
   157 			if ( 'core' === $type ) {
   153 				$this->send_core_update_notification_email( $item );
   158 				$this->send_core_update_notification_email( $item );
   154 			}
   159 			}
   155 			return false;
   160 			return false;
   156 		}
   161 		}
   177 
   182 
   178 		/**
   183 		/**
   179 		 * Filters whether to automatically update core, a plugin, a theme, or a language.
   184 		 * Filters whether to automatically update core, a plugin, a theme, or a language.
   180 		 *
   185 		 *
   181 		 * The dynamic portion of the hook name, `$type`, refers to the type of update
   186 		 * The dynamic portion of the hook name, `$type`, refers to the type of update
   182 		 * being checked. Potential hook names include:
   187 		 * being checked.
       
   188 		 *
       
   189 		 * Possible hook names include:
   183 		 *
   190 		 *
   184 		 *  - `auto_update_core`
   191 		 *  - `auto_update_core`
   185 		 *  - `auto_update_plugin`
   192 		 *  - `auto_update_plugin`
   186 		 *  - `auto_update_theme`
   193 		 *  - `auto_update_theme`
   187 		 *  - `auto_update_translation`
   194 		 *  - `auto_update_translation`
   240 	 * Notifies an administrator of a core update.
   247 	 * Notifies an administrator of a core update.
   241 	 *
   248 	 *
   242 	 * @since 3.7.0
   249 	 * @since 3.7.0
   243 	 *
   250 	 *
   244 	 * @param object $item The update offer.
   251 	 * @param object $item The update offer.
       
   252 	 * @return bool True if the site administrator is notified of a core update,
       
   253 	 *              false otherwise.
   245 	 */
   254 	 */
   246 	protected function send_core_update_notification_email( $item ) {
   255 	protected function send_core_update_notification_email( $item ) {
   247 		$notified = get_site_option( 'auto_core_update_notified' );
   256 		$notified = get_site_option( 'auto_core_update_notified' );
   248 
   257 
   249 		// Don't notify if we've already notified the same email address of the same version.
   258 		// Don't notify if we've already notified the same email address of the same version.
   250 		if ( $notified && get_site_option( 'admin_email' ) === $notified['email'] && $notified['version'] == $item->current ) {
   259 		if ( $notified
       
   260 			&& get_site_option( 'admin_email' ) === $notified['email']
       
   261 			&& $notified['version'] === $item->current
       
   262 		) {
   251 			return false;
   263 			return false;
   252 		}
   264 		}
   253 
   265 
   254 		// See if we need to notify users of a core update.
   266 		// See if we need to notify users of a core update.
   255 		$notify = ! empty( $item->notify_email );
   267 		$notify = ! empty( $item->notify_email );
   341 				break;
   353 				break;
   342 			case 'theme':
   354 			case 'theme':
   343 				$upgrader_item = $item->theme;
   355 				$upgrader_item = $item->theme;
   344 				$theme         = wp_get_theme( $upgrader_item );
   356 				$theme         = wp_get_theme( $upgrader_item );
   345 				$item_name     = $theme->Get( 'Name' );
   357 				$item_name     = $theme->Get( 'Name' );
       
   358 				// Add the current version so that it can be reported in the notification email.
       
   359 				$item->current_version = $theme->get( 'Version' );
       
   360 				if ( empty( $item->current_version ) ) {
       
   361 					$item->current_version = false;
       
   362 				}
   346 				/* translators: %s: Theme name. */
   363 				/* translators: %s: Theme name. */
   347 				$skin->feedback( __( 'Updating theme: %s' ), $item_name );
   364 				$skin->feedback( __( 'Updating theme: %s' ), $item_name );
   348 				break;
   365 				break;
   349 			case 'plugin':
   366 			case 'plugin':
   350 				$upgrader_item = $item->plugin;
   367 				$upgrader_item = $item->plugin;
   351 				$plugin_data   = get_plugin_data( $context . '/' . $upgrader_item );
   368 				$plugin_data   = get_plugin_data( $context . '/' . $upgrader_item );
   352 				$item_name     = $plugin_data['Name'];
   369 				$item_name     = $plugin_data['Name'];
       
   370 				// Add the current version so that it can be reported in the notification email.
       
   371 				$item->current_version = $plugin_data['Version'];
       
   372 				if ( empty( $item->current_version ) ) {
       
   373 					$item->current_version = false;
       
   374 				}
   353 				/* translators: %s: Plugin name. */
   375 				/* translators: %s: Plugin name. */
   354 				$skin->feedback( __( 'Updating plugin: %s' ), $item_name );
   376 				$skin->feedback( __( 'Updating plugin: %s' ), $item_name );
   355 				break;
   377 				break;
   356 			case 'translation':
   378 			case 'translation':
   357 				$language_item_name = $upgrader->get_name_for_update( $item );
   379 				$language_item_name = $upgrader->get_name_for_update( $item );
   605 		if ( in_array( $error_code, $transient_failures, true ) && ! get_site_option( 'auto_core_update_failed' ) ) {
   627 		if ( in_array( $error_code, $transient_failures, true ) && ! get_site_option( 'auto_core_update_failed' ) ) {
   606 			wp_schedule_single_event( time() + HOUR_IN_SECONDS, 'wp_maybe_auto_update' );
   628 			wp_schedule_single_event( time() + HOUR_IN_SECONDS, 'wp_maybe_auto_update' );
   607 			$send = false;
   629 			$send = false;
   608 		}
   630 		}
   609 
   631 
   610 		$n = get_site_option( 'auto_core_update_notified' );
   632 		$notified = get_site_option( 'auto_core_update_notified' );
       
   633 
   611 		// Don't notify if we've already notified the same email address of the same version of the same notification type.
   634 		// Don't notify if we've already notified the same email address of the same version of the same notification type.
   612 		if ( $n && 'fail' === $n['type'] && get_site_option( 'admin_email' ) === $n['email'] && $n['version'] == $core_update->current ) {
   635 		if ( $notified
       
   636 			&& 'fail' === $notified['type']
       
   637 			&& get_site_option( 'admin_email' ) === $notified['email']
       
   638 			&& $notified['version'] === $core_update->current
       
   639 		) {
   613 			$send = false;
   640 			$send = false;
   614 		}
   641 		}
   615 
   642 
   616 		update_site_option(
   643 		update_site_option(
   617 			'auto_core_update_failed',
   644 			'auto_core_update_failed',
   655 		// If the update transient is empty, use the update we just performed.
   682 		// If the update transient is empty, use the update we just performed.
   656 		if ( ! $next_user_core_update ) {
   683 		if ( ! $next_user_core_update ) {
   657 			$next_user_core_update = $core_update;
   684 			$next_user_core_update = $core_update;
   658 		}
   685 		}
   659 
   686 
   660 		$newer_version_available = ( 'upgrade' === $next_user_core_update->response && version_compare( $next_user_core_update->version, $core_update->version, '>' ) );
   687 		if ( 'upgrade' === $next_user_core_update->response
       
   688 			&& version_compare( $next_user_core_update->version, $core_update->version, '>' )
       
   689 		) {
       
   690 			$newer_version_available = true;
       
   691 		} else {
       
   692 			$newer_version_available = false;
       
   693 		}
   661 
   694 
   662 		/**
   695 		/**
   663 		 * Filters whether to send an email following an automatic background core update.
   696 		 * Filters whether to send an email following an automatic background core update.
   664 		 *
   697 		 *
   665 		 * @since 3.7.0
   698 		 * @since 3.7.0
   885 	 */
   918 	 */
   886 	protected function after_plugin_theme_update( $update_results ) {
   919 	protected function after_plugin_theme_update( $update_results ) {
   887 		$successful_updates = array();
   920 		$successful_updates = array();
   888 		$failed_updates     = array();
   921 		$failed_updates     = array();
   889 
   922 
   890 		/**
   923 		if ( ! empty( $update_results['plugin'] ) ) {
   891 		 * Filters whether to send an email following an automatic background plugin update.
   924 			/**
   892 		 *
   925 			 * Filters whether to send an email following an automatic background plugin update.
   893 		 * @since 5.5.0
   926 			 *
   894 		 * @since 5.5.1 Added the $update_results parameter.
   927 			 * @since 5.5.0
   895 		 *
   928 			 * @since 5.5.1 Added the `$update_results` parameter.
   896 		 * @param bool  $enabled        True if plugins notifications are enabled, false otherwise.
   929 			 *
   897 		 * @param array $update_results The results of plugins update tasks.
   930 			 * @param bool  $enabled        True if plugin update notifications are enabled, false otherwise.
   898 		 */
   931 			 * @param array $update_results The results of plugins update tasks.
   899 		$notifications_enabled = apply_filters( 'auto_plugin_update_send_email', true, $update_results['plugin'] );
   932 			 */
   900 
   933 			$notifications_enabled = apply_filters( 'auto_plugin_update_send_email', true, $update_results['plugin'] );
   901 		if ( ! empty( $update_results['plugin'] ) && $notifications_enabled ) {
   934 
   902 			foreach ( $update_results['plugin'] as $update_result ) {
   935 			if ( $notifications_enabled ) {
   903 				if ( true === $update_result->result ) {
   936 				foreach ( $update_results['plugin'] as $update_result ) {
   904 					$successful_updates['plugin'][] = $update_result;
   937 					if ( true === $update_result->result ) {
   905 				} else {
   938 						$successful_updates['plugin'][] = $update_result;
   906 					$failed_updates['plugin'][] = $update_result;
   939 					} else {
   907 				}
   940 						$failed_updates['plugin'][] = $update_result;
   908 			}
   941 					}
   909 		}
   942 				}
   910 
   943 			}
   911 		/**
   944 		}
   912 		 * Filters whether to send an email following an automatic background theme update.
   945 
   913 		 *
   946 		if ( ! empty( $update_results['theme'] ) ) {
   914 		 * @since 5.5.0
   947 			/**
   915 		 * @since 5.5.1 Added the $update_results parameter.
   948 			 * Filters whether to send an email following an automatic background theme update.
   916 		 *
   949 			 *
   917 		 * @param bool  $enabled True if notifications are enabled, false otherwise.
   950 			 * @since 5.5.0
   918 		 * @param array $update_results The results of theme update tasks.
   951 			 * @since 5.5.1 Added the `$update_results` parameter.
   919 		 */
   952 			 *
   920 		$notifications_enabled = apply_filters( 'auto_theme_update_send_email', true, $update_results['theme'] );
   953 			 * @param bool  $enabled        True if theme update notifications are enabled, false otherwise.
   921 
   954 			 * @param array $update_results The results of theme update tasks.
   922 		if ( ! empty( $update_results['theme'] ) && $notifications_enabled ) {
   955 			 */
   923 			foreach ( $update_results['theme'] as $update_result ) {
   956 			$notifications_enabled = apply_filters( 'auto_theme_update_send_email', true, $update_results['theme'] );
   924 				if ( true === $update_result->result ) {
   957 
   925 					$successful_updates['theme'][] = $update_result;
   958 			if ( $notifications_enabled ) {
   926 				} else {
   959 				foreach ( $update_results['theme'] as $update_result ) {
   927 					$failed_updates['theme'][] = $update_result;
   960 					if ( true === $update_result->result ) {
       
   961 						$successful_updates['theme'][] = $update_result;
       
   962 					} else {
       
   963 						$failed_updates['theme'][] = $update_result;
       
   964 					}
   928 				}
   965 				}
   929 			}
   966 			}
   930 		}
   967 		}
   931 
   968 
   932 		if ( empty( $successful_updates ) && empty( $failed_updates ) ) {
   969 		if ( empty( $successful_updates ) && empty( $failed_updates ) ) {
  1059 			// List failed plugin updates.
  1096 			// List failed plugin updates.
  1060 			if ( ! empty( $failed_updates['plugin'] ) ) {
  1097 			if ( ! empty( $failed_updates['plugin'] ) ) {
  1061 				$body[] = __( 'These plugins failed to update:' );
  1098 				$body[] = __( 'These plugins failed to update:' );
  1062 
  1099 
  1063 				foreach ( $failed_updates['plugin'] as $item ) {
  1100 				foreach ( $failed_updates['plugin'] as $item ) {
  1064 					$body[] = sprintf(
  1101 					if ( $item->item->current_version ) {
  1065 						/* translators: 1: Plugin name, 2: Version number. */
  1102 						$body[] = sprintf(
  1066 						__( '- %1$s version %2$s' ),
  1103 							/* translators: 1: Plugin name, 2: Current version number, 3: New version number. */
  1067 						$item->name,
  1104 							__( '- %1$s (from version %2$s to %3$s)' ),
  1068 						$item->item->new_version
  1105 							$item->name,
  1069 					);
  1106 							$item->item->current_version,
       
  1107 							$item->item->new_version
       
  1108 						);
       
  1109 					} else {
       
  1110 						$body[] = sprintf(
       
  1111 							/* translators: 1: Plugin name, 2: Version number. */
       
  1112 							__( '- %1$s version %2$s' ),
       
  1113 							$item->name,
       
  1114 							$item->item->new_version
       
  1115 						);
       
  1116 					}
  1070 
  1117 
  1071 					$past_failure_emails[ $item->item->plugin ] = $item->item->new_version;
  1118 					$past_failure_emails[ $item->item->plugin ] = $item->item->new_version;
  1072 				}
  1119 				}
  1073 
  1120 
  1074 				$body[] = "\n";
  1121 				$body[] = "\n";
  1077 			// List failed theme updates.
  1124 			// List failed theme updates.
  1078 			if ( ! empty( $failed_updates['theme'] ) ) {
  1125 			if ( ! empty( $failed_updates['theme'] ) ) {
  1079 				$body[] = __( 'These themes failed to update:' );
  1126 				$body[] = __( 'These themes failed to update:' );
  1080 
  1127 
  1081 				foreach ( $failed_updates['theme'] as $item ) {
  1128 				foreach ( $failed_updates['theme'] as $item ) {
  1082 					$body[] = sprintf(
  1129 					if ( $item->item->current_version ) {
  1083 						/* translators: 1: Theme name, 2: Version number. */
  1130 						$body[] = sprintf(
  1084 						__( '- %1$s version %2$s' ),
  1131 							/* translators: 1: Theme name, 2: Current version number, 3: New version number. */
  1085 						$item->name,
  1132 							__( '- %1$s (from version %2$s to %3$s)' ),
  1086 						$item->item->new_version
  1133 							$item->name,
  1087 					);
  1134 							$item->item->current_version,
       
  1135 							$item->item->new_version
       
  1136 						);
       
  1137 					} else {
       
  1138 						$body[] = sprintf(
       
  1139 							/* translators: 1: Theme name, 2: Version number. */
       
  1140 							__( '- %1$s version %2$s' ),
       
  1141 							$item->name,
       
  1142 							$item->item->new_version
       
  1143 						);
       
  1144 					}
  1088 
  1145 
  1089 					$past_failure_emails[ $item->item->theme ] = $item->item->new_version;
  1146 					$past_failure_emails[ $item->item->theme ] = $item->item->new_version;
  1090 				}
  1147 				}
  1091 
  1148 
  1092 				$body[] = "\n";
  1149 				$body[] = "\n";
  1100 			// List successful plugin updates.
  1157 			// List successful plugin updates.
  1101 			if ( ! empty( $successful_updates['plugin'] ) ) {
  1158 			if ( ! empty( $successful_updates['plugin'] ) ) {
  1102 				$body[] = __( 'These plugins are now up to date:' );
  1159 				$body[] = __( 'These plugins are now up to date:' );
  1103 
  1160 
  1104 				foreach ( $successful_updates['plugin'] as $item ) {
  1161 				foreach ( $successful_updates['plugin'] as $item ) {
  1105 					$body[] = sprintf(
  1162 					if ( $item->item->current_version ) {
  1106 						/* translators: 1: Plugin name, 2: Version number. */
  1163 						$body[] = sprintf(
  1107 						__( '- %1$s version %2$s' ),
  1164 							/* translators: 1: Plugin name, 2: Current version number, 3: New version number. */
  1108 						$item->name,
  1165 							__( '- %1$s (from version %2$s to %3$s)' ),
  1109 						$item->item->new_version
  1166 							$item->name,
  1110 					);
  1167 							$item->item->current_version,
       
  1168 							$item->item->new_version
       
  1169 						);
       
  1170 					} else {
       
  1171 						$body[] = sprintf(
       
  1172 							/* translators: 1: Plugin name, 2: Version number. */
       
  1173 							__( '- %1$s version %2$s' ),
       
  1174 							$item->name,
       
  1175 							$item->item->new_version
       
  1176 						);
       
  1177 					}
  1111 
  1178 
  1112 					unset( $past_failure_emails[ $item->item->plugin ] );
  1179 					unset( $past_failure_emails[ $item->item->plugin ] );
  1113 				}
  1180 				}
  1114 
  1181 
  1115 				$body[] = "\n";
  1182 				$body[] = "\n";
  1118 			// List successful theme updates.
  1185 			// List successful theme updates.
  1119 			if ( ! empty( $successful_updates['theme'] ) ) {
  1186 			if ( ! empty( $successful_updates['theme'] ) ) {
  1120 				$body[] = __( 'These themes are now up to date:' );
  1187 				$body[] = __( 'These themes are now up to date:' );
  1121 
  1188 
  1122 				foreach ( $successful_updates['theme'] as $item ) {
  1189 				foreach ( $successful_updates['theme'] as $item ) {
  1123 					$body[] = sprintf(
  1190 					if ( $item->item->current_version ) {
  1124 						/* translators: 1: Theme name, 2: Version number. */
  1191 						$body[] = sprintf(
  1125 						__( '- %1$s version %2$s' ),
  1192 							/* translators: 1: Theme name, 2: Current version number, 3: New version number. */
  1126 						$item->name,
  1193 							__( '- %1$s (from version %2$s to %3$s)' ),
  1127 						$item->item->new_version
  1194 							$item->name,
  1128 					);
  1195 							$item->item->current_version,
       
  1196 							$item->item->new_version
       
  1197 						);
       
  1198 					} else {
       
  1199 						$body[] = sprintf(
       
  1200 							/* translators: 1: Theme name, 2: Version number. */
       
  1201 							__( '- %1$s version %2$s' ),
       
  1202 							$item->name,
       
  1203 							$item->item->new_version
       
  1204 						);
       
  1205 					}
  1129 
  1206 
  1130 					unset( $past_failure_emails[ $item->item->theme ] );
  1207 					unset( $past_failure_emails[ $item->item->theme ] );
  1131 				}
  1208 				}
  1132 
  1209 
  1133 				$body[] = "\n";
  1210 				$body[] = "\n";
  1209 		$body[] = sprintf( __( 'WordPress site: %s' ), network_home_url( '/' ) );
  1286 		$body[] = sprintf( __( 'WordPress site: %s' ), network_home_url( '/' ) );
  1210 
  1287 
  1211 		// Core.
  1288 		// Core.
  1212 		if ( isset( $this->update_results['core'] ) ) {
  1289 		if ( isset( $this->update_results['core'] ) ) {
  1213 			$result = $this->update_results['core'][0];
  1290 			$result = $this->update_results['core'][0];
       
  1291 
  1214 			if ( $result->result && ! is_wp_error( $result->result ) ) {
  1292 			if ( $result->result && ! is_wp_error( $result->result ) ) {
  1215 				/* translators: %s: WordPress version. */
  1293 				/* translators: %s: WordPress version. */
  1216 				$body[] = sprintf( __( 'SUCCESS: WordPress was successfully updated to %s' ), $result->name );
  1294 				$body[] = sprintf( __( 'SUCCESS: WordPress was successfully updated to %s' ), $result->name );
  1217 			} else {
  1295 			} else {
  1218 				/* translators: %s: WordPress version. */
  1296 				/* translators: %s: WordPress version. */
  1219 				$body[] = sprintf( __( 'FAILED: WordPress failed to update to %s' ), $result->name );
  1297 				$body[] = sprintf( __( 'FAILED: WordPress failed to update to %s' ), $result->name );
  1220 				$failures++;
  1298 				$failures++;
  1221 			}
  1299 			}
       
  1300 
  1222 			$body[] = '';
  1301 			$body[] = '';
  1223 		}
  1302 		}
  1224 
  1303 
  1225 		// Plugins, Themes, Translations.
  1304 		// Plugins, Themes, Translations.
  1226 		foreach ( array( 'plugin', 'theme', 'translation' ) as $type ) {
  1305 		foreach ( array( 'plugin', 'theme', 'translation' ) as $type ) {
  1227 			if ( ! isset( $this->update_results[ $type ] ) ) {
  1306 			if ( ! isset( $this->update_results[ $type ] ) ) {
  1228 				continue;
  1307 				continue;
  1229 			}
  1308 			}
       
  1309 
  1230 			$success_items = wp_list_filter( $this->update_results[ $type ], array( 'result' => true ) );
  1310 			$success_items = wp_list_filter( $this->update_results[ $type ], array( 'result' => true ) );
       
  1311 
  1231 			if ( $success_items ) {
  1312 			if ( $success_items ) {
  1232 				$messages = array(
  1313 				$messages = array(
  1233 					'plugin'      => __( 'The following plugins were successfully updated:' ),
  1314 					'plugin'      => __( 'The following plugins were successfully updated:' ),
  1234 					'theme'       => __( 'The following themes were successfully updated:' ),
  1315 					'theme'       => __( 'The following themes were successfully updated:' ),
  1235 					'translation' => __( 'The following translations were successfully updated:' ),
  1316 					'translation' => __( 'The following translations were successfully updated:' ),
  1239 				foreach ( wp_list_pluck( $success_items, 'name' ) as $name ) {
  1320 				foreach ( wp_list_pluck( $success_items, 'name' ) as $name ) {
  1240 					/* translators: %s: Name of plugin / theme / translation. */
  1321 					/* translators: %s: Name of plugin / theme / translation. */
  1241 					$body[] = ' * ' . sprintf( __( 'SUCCESS: %s' ), $name );
  1322 					$body[] = ' * ' . sprintf( __( 'SUCCESS: %s' ), $name );
  1242 				}
  1323 				}
  1243 			}
  1324 			}
  1244 			if ( $success_items != $this->update_results[ $type ] ) {
  1325 
       
  1326 			if ( $success_items !== $this->update_results[ $type ] ) {
  1245 				// Failed updates.
  1327 				// Failed updates.
  1246 				$messages = array(
  1328 				$messages = array(
  1247 					'plugin'      => __( 'The following plugins failed to update:' ),
  1329 					'plugin'      => __( 'The following plugins failed to update:' ),
  1248 					'theme'       => __( 'The following themes failed to update:' ),
  1330 					'theme'       => __( 'The following themes failed to update:' ),
  1249 					'translation' => __( 'The following translations failed to update:' ),
  1331 					'translation' => __( 'The following translations failed to update:' ),
  1250 				);
  1332 				);
  1251 
  1333 
  1252 				$body[] = $messages[ $type ];
  1334 				$body[] = $messages[ $type ];
       
  1335 
  1253 				foreach ( $this->update_results[ $type ] as $item ) {
  1336 				foreach ( $this->update_results[ $type ] as $item ) {
  1254 					if ( ! $item->result || is_wp_error( $item->result ) ) {
  1337 					if ( ! $item->result || is_wp_error( $item->result ) ) {
  1255 						/* translators: %s: Name of plugin / theme / translation. */
  1338 						/* translators: %s: Name of plugin / theme / translation. */
  1256 						$body[] = ' * ' . sprintf( __( 'FAILED: %s' ), $item->name );
  1339 						$body[] = ' * ' . sprintf( __( 'FAILED: %s' ), $item->name );
  1257 						$failures++;
  1340 						$failures++;
  1258 					}
  1341 					}
  1259 				}
  1342 				}
  1260 			}
  1343 			}
       
  1344 
  1261 			$body[] = '';
  1345 			$body[] = '';
  1262 		}
  1346 		}
  1263 
  1347 
  1264 		$site_title = wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES );
  1348 		$site_title = wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES );
       
  1349 
  1265 		if ( $failures ) {
  1350 		if ( $failures ) {
  1266 			$body[] = trim(
  1351 			$body[] = trim(
  1267 				__(
  1352 				__(
  1268 					"BETA TESTING?
  1353 					"BETA TESTING?
  1269 =============
  1354 =============
  1296 
  1381 
  1297 		foreach ( array( 'core', 'plugin', 'theme', 'translation' ) as $type ) {
  1382 		foreach ( array( 'core', 'plugin', 'theme', 'translation' ) as $type ) {
  1298 			if ( ! isset( $this->update_results[ $type ] ) ) {
  1383 			if ( ! isset( $this->update_results[ $type ] ) ) {
  1299 				continue;
  1384 				continue;
  1300 			}
  1385 			}
       
  1386 
  1301 			foreach ( $this->update_results[ $type ] as $update ) {
  1387 			foreach ( $this->update_results[ $type ] as $update ) {
  1302 				$body[] = $update->name;
  1388 				$body[] = $update->name;
  1303 				$body[] = str_repeat( '-', strlen( $update->name ) );
  1389 				$body[] = str_repeat( '-', strlen( $update->name ) );
       
  1390 
  1304 				foreach ( $update->messages as $message ) {
  1391 				foreach ( $update->messages as $message ) {
  1305 					$body[] = '  ' . html_entity_decode( str_replace( '…', '...', $message ) );
  1392 					$body[] = '  ' . html_entity_decode( str_replace( '…', '...', $message ) );
  1306 				}
  1393 				}
       
  1394 
  1307 				if ( is_wp_error( $update->result ) ) {
  1395 				if ( is_wp_error( $update->result ) ) {
  1308 					$results = array( 'update' => $update->result );
  1396 					$results = array( 'update' => $update->result );
       
  1397 
  1309 					// If we rolled back, we want to know an error that occurred then too.
  1398 					// If we rolled back, we want to know an error that occurred then too.
  1310 					if ( 'rollback_was_required' === $update->result->get_error_code() ) {
  1399 					if ( 'rollback_was_required' === $update->result->get_error_code() ) {
  1311 						$results = (array) $update->result->get_error_data();
  1400 						$results = (array) $update->result->get_error_data();
  1312 					}
  1401 					}
       
  1402 
  1313 					foreach ( $results as $result_type => $result ) {
  1403 					foreach ( $results as $result_type => $result ) {
  1314 						if ( ! is_wp_error( $result ) ) {
  1404 						if ( ! is_wp_error( $result ) ) {
  1315 							continue;
  1405 							continue;
  1316 						}
  1406 						}
  1317 
  1407 
  1326 						if ( $result->get_error_data() ) {
  1416 						if ( $result->get_error_data() ) {
  1327 							$body[] = '         ' . implode( ', ', (array) $result->get_error_data() );
  1417 							$body[] = '         ' . implode( ', ', (array) $result->get_error_data() );
  1328 						}
  1418 						}
  1329 					}
  1419 					}
  1330 				}
  1420 				}
       
  1421 
  1331 				$body[] = '';
  1422 				$body[] = '';
  1332 			}
  1423 			}
  1333 		}
  1424 		}
  1334 
  1425 
  1335 		$email = array(
  1426 		$email = array(