ru-se.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

update.php (34989B)


      1 <?php
      2 /**
      3  * WordPress Administration Update API
      4  *
      5  * @package WordPress
      6  * @subpackage Administration
      7  */
      8 
      9 /**
     10  * Selects the first update version from the update_core option.
     11  *
     12  * @since 2.7.0
     13  *
     14  * @return object|array|false The response from the API on success, false on failure.
     15  */
     16 function get_preferred_from_update_core() {
     17 	$updates = get_core_updates();
     18 	if ( ! is_array( $updates ) ) {
     19 		return false;
     20 	}
     21 	if ( empty( $updates ) ) {
     22 		return (object) array( 'response' => 'latest' );
     23 	}
     24 	return $updates[0];
     25 }
     26 
     27 /**
     28  * Gets available core updates.
     29  *
     30  * @since 2.7.0
     31  *
     32  * @param array $options Set $options['dismissed'] to true to show dismissed upgrades too,
     33  *                       set $options['available'] to false to skip not-dismissed updates.
     34  * @return array|false Array of the update objects on success, false on failure.
     35  */
     36 function get_core_updates( $options = array() ) {
     37 	$options   = array_merge(
     38 		array(
     39 			'available' => true,
     40 			'dismissed' => false,
     41 		),
     42 		$options
     43 	);
     44 	$dismissed = get_site_option( 'dismissed_update_core' );
     45 
     46 	if ( ! is_array( $dismissed ) ) {
     47 		$dismissed = array();
     48 	}
     49 
     50 	$from_api = get_site_transient( 'update_core' );
     51 
     52 	if ( ! isset( $from_api->updates ) || ! is_array( $from_api->updates ) ) {
     53 		return false;
     54 	}
     55 
     56 	$updates = $from_api->updates;
     57 	$result  = array();
     58 	foreach ( $updates as $update ) {
     59 		if ( 'autoupdate' === $update->response ) {
     60 			continue;
     61 		}
     62 
     63 		if ( array_key_exists( $update->current . '|' . $update->locale, $dismissed ) ) {
     64 			if ( $options['dismissed'] ) {
     65 				$update->dismissed = true;
     66 				$result[]          = $update;
     67 			}
     68 		} else {
     69 			if ( $options['available'] ) {
     70 				$update->dismissed = false;
     71 				$result[]          = $update;
     72 			}
     73 		}
     74 	}
     75 	return $result;
     76 }
     77 
     78 /**
     79  * Gets the best available (and enabled) Auto-Update for WordPress core.
     80  *
     81  * If there's 1.2.3 and 1.3 on offer, it'll choose 1.3 if the installation allows it, else, 1.2.3.
     82  *
     83  * @since 3.7.0
     84  *
     85  * @return object|false The core update offering on success, false on failure.
     86  */
     87 function find_core_auto_update() {
     88 	$updates = get_site_transient( 'update_core' );
     89 	if ( ! $updates || empty( $updates->updates ) ) {
     90 		return false;
     91 	}
     92 
     93 	require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
     94 
     95 	$auto_update = false;
     96 	$upgrader    = new WP_Automatic_Updater;
     97 	foreach ( $updates->updates as $update ) {
     98 		if ( 'autoupdate' !== $update->response ) {
     99 			continue;
    100 		}
    101 
    102 		if ( ! $upgrader->should_update( 'core', $update, ABSPATH ) ) {
    103 			continue;
    104 		}
    105 
    106 		if ( ! $auto_update || version_compare( $update->current, $auto_update->current, '>' ) ) {
    107 			$auto_update = $update;
    108 		}
    109 	}
    110 	return $auto_update;
    111 }
    112 
    113 /**
    114  * Gets and caches the checksums for the given version of WordPress.
    115  *
    116  * @since 3.7.0
    117  *
    118  * @param string $version Version string to query.
    119  * @param string $locale  Locale to query.
    120  * @return array|false An array of checksums on success, false on failure.
    121  */
    122 function get_core_checksums( $version, $locale ) {
    123 	$http_url = 'http://api.wordpress.org/core/checksums/1.0/?' . http_build_query( compact( 'version', 'locale' ), null, '&' );
    124 	$url      = $http_url;
    125 
    126 	$ssl = wp_http_supports( array( 'ssl' ) );
    127 	if ( $ssl ) {
    128 		$url = set_url_scheme( $url, 'https' );
    129 	}
    130 
    131 	$options = array(
    132 		'timeout' => wp_doing_cron() ? 30 : 3,
    133 	);
    134 
    135 	$response = wp_remote_get( $url, $options );
    136 	if ( $ssl && is_wp_error( $response ) ) {
    137 		trigger_error(
    138 			sprintf(
    139 				/* translators: %s: Support forums URL. */
    140 				__( 'An unexpected error occurred. Something may be wrong with WordPress.org or this server&#8217;s configuration. If you continue to have problems, please try the <a href="%s">support forums</a>.' ),
    141 				__( 'https://wordpress.org/support/forums/' )
    142 			) . ' ' . __( '(WordPress could not establish a secure connection to WordPress.org. Please contact your server administrator.)' ),
    143 			headers_sent() || WP_DEBUG ? E_USER_WARNING : E_USER_NOTICE
    144 		);
    145 		$response = wp_remote_get( $http_url, $options );
    146 	}
    147 
    148 	if ( is_wp_error( $response ) || 200 != wp_remote_retrieve_response_code( $response ) ) {
    149 		return false;
    150 	}
    151 
    152 	$body = trim( wp_remote_retrieve_body( $response ) );
    153 	$body = json_decode( $body, true );
    154 
    155 	if ( ! is_array( $body ) || ! isset( $body['checksums'] ) || ! is_array( $body['checksums'] ) ) {
    156 		return false;
    157 	}
    158 
    159 	return $body['checksums'];
    160 }
    161 
    162 /**
    163  * Dismisses core update.
    164  *
    165  * @since 2.7.0
    166  *
    167  * @param object $update
    168  * @return bool
    169  */
    170 function dismiss_core_update( $update ) {
    171 	$dismissed = get_site_option( 'dismissed_update_core' );
    172 	$dismissed[ $update->current . '|' . $update->locale ] = true;
    173 	return update_site_option( 'dismissed_update_core', $dismissed );
    174 }
    175 
    176 /**
    177  * Undismisses core update.
    178  *
    179  * @since 2.7.0
    180  *
    181  * @param string $version
    182  * @param string $locale
    183  * @return bool
    184  */
    185 function undismiss_core_update( $version, $locale ) {
    186 	$dismissed = get_site_option( 'dismissed_update_core' );
    187 	$key       = $version . '|' . $locale;
    188 
    189 	if ( ! isset( $dismissed[ $key ] ) ) {
    190 		return false;
    191 	}
    192 
    193 	unset( $dismissed[ $key ] );
    194 	return update_site_option( 'dismissed_update_core', $dismissed );
    195 }
    196 
    197 /**
    198  * Finds the available update for WordPress core.
    199  *
    200  * @since 2.7.0
    201  *
    202  * @param string $version Version string to find the update for.
    203  * @param string $locale  Locale to find the update for.
    204  * @return object|false The core update offering on success, false on failure.
    205  */
    206 function find_core_update( $version, $locale ) {
    207 	$from_api = get_site_transient( 'update_core' );
    208 
    209 	if ( ! isset( $from_api->updates ) || ! is_array( $from_api->updates ) ) {
    210 		return false;
    211 	}
    212 
    213 	$updates = $from_api->updates;
    214 	foreach ( $updates as $update ) {
    215 		if ( $update->current == $version && $update->locale == $locale ) {
    216 			return $update;
    217 		}
    218 	}
    219 	return false;
    220 }
    221 
    222 /**
    223  * @since 2.3.0
    224  *
    225  * @param string $msg
    226  * @return string
    227  */
    228 function core_update_footer( $msg = '' ) {
    229 	if ( ! current_user_can( 'update_core' ) ) {
    230 		/* translators: %s: WordPress version. */
    231 		return sprintf( __( 'Version %s' ), get_bloginfo( 'version', 'display' ) );
    232 	}
    233 
    234 	$cur = get_preferred_from_update_core();
    235 	if ( ! is_object( $cur ) ) {
    236 		$cur = new stdClass;
    237 	}
    238 
    239 	if ( ! isset( $cur->current ) ) {
    240 		$cur->current = '';
    241 	}
    242 
    243 	if ( ! isset( $cur->response ) ) {
    244 		$cur->response = '';
    245 	}
    246 
    247 	// Include an unmodified $wp_version.
    248 	require ABSPATH . WPINC . '/version.php';
    249 
    250 	$is_development_version = preg_match( '/alpha|beta|RC/', $wp_version );
    251 
    252 	if ( $is_development_version ) {
    253 		return sprintf(
    254 			/* translators: 1: WordPress version number, 2: URL to WordPress Updates screen. */
    255 			__( 'You are using a development version (%1$s). Cool! Please <a href="%2$s">stay updated</a>.' ),
    256 			get_bloginfo( 'version', 'display' ),
    257 			network_admin_url( 'update-core.php' )
    258 		);
    259 	}
    260 
    261 	switch ( $cur->response ) {
    262 		case 'upgrade':
    263 			return sprintf(
    264 				'<strong><a href="%s">%s</a></strong>',
    265 				network_admin_url( 'update-core.php' ),
    266 				/* translators: %s: WordPress version. */
    267 				sprintf( __( 'Get Version %s' ), $cur->current )
    268 			);
    269 
    270 		case 'latest':
    271 		default:
    272 			/* translators: %s: WordPress version. */
    273 			return sprintf( __( 'Version %s' ), get_bloginfo( 'version', 'display' ) );
    274 	}
    275 }
    276 
    277 /**
    278  * @since 2.3.0
    279  *
    280  * @global string $pagenow
    281  * @return void|false
    282  */
    283 function update_nag() {
    284 	if ( is_multisite() && ! current_user_can( 'update_core' ) ) {
    285 		return false;
    286 	}
    287 
    288 	global $pagenow;
    289 
    290 	if ( 'update-core.php' === $pagenow ) {
    291 		return;
    292 	}
    293 
    294 	$cur = get_preferred_from_update_core();
    295 
    296 	if ( ! isset( $cur->response ) || 'upgrade' !== $cur->response ) {
    297 		return false;
    298 	}
    299 
    300 	$version_url = sprintf(
    301 		/* translators: %s: WordPress version. */
    302 		esc_url( __( 'https://wordpress.org/support/wordpress-version/version-%s/' ) ),
    303 		sanitize_title( $cur->current )
    304 	);
    305 
    306 	if ( current_user_can( 'update_core' ) ) {
    307 		$msg = sprintf(
    308 			/* translators: 1: URL to WordPress release notes, 2: New WordPress version, 3: URL to network admin, 4: Accessibility text. */
    309 			__( '<a href="%1$s">WordPress %2$s</a> is available! <a href="%3$s" aria-label="%4$s">Please update now</a>.' ),
    310 			$version_url,
    311 			$cur->current,
    312 			network_admin_url( 'update-core.php' ),
    313 			esc_attr__( 'Please update WordPress now' )
    314 		);
    315 	} else {
    316 		$msg = sprintf(
    317 			/* translators: 1: URL to WordPress release notes, 2: New WordPress version. */
    318 			__( '<a href="%1$s">WordPress %2$s</a> is available! Please notify the site administrator.' ),
    319 			$version_url,
    320 			$cur->current
    321 		);
    322 	}
    323 
    324 	echo "<div class='update-nag notice notice-warning inline'>$msg</div>";
    325 }
    326 
    327 /**
    328  * Displays WordPress version and active theme in the 'At a Glance' dashboard widget.
    329  *
    330  * @since 2.5.0
    331  */
    332 function update_right_now_message() {
    333 	$theme_name = wp_get_theme();
    334 	if ( current_user_can( 'switch_themes' ) ) {
    335 		$theme_name = sprintf( '<a href="themes.php">%1$s</a>', $theme_name );
    336 	}
    337 
    338 	$msg = '';
    339 
    340 	if ( current_user_can( 'update_core' ) ) {
    341 		$cur = get_preferred_from_update_core();
    342 
    343 		if ( isset( $cur->response ) && 'upgrade' === $cur->response ) {
    344 			$msg .= sprintf(
    345 				'<a href="%s" class="button" aria-describedby="wp-version">%s</a> ',
    346 				network_admin_url( 'update-core.php' ),
    347 				/* translators: %s: WordPress version number, or 'Latest' string. */
    348 				sprintf( __( 'Update to %s' ), $cur->current ? $cur->current : __( 'Latest' ) )
    349 			);
    350 		}
    351 	}
    352 
    353 	/* translators: 1: Version number, 2: Theme name. */
    354 	$content = __( 'WordPress %1$s running %2$s theme.' );
    355 
    356 	/**
    357 	 * Filters the text displayed in the 'At a Glance' dashboard widget.
    358 	 *
    359 	 * Prior to 3.8.0, the widget was named 'Right Now'.
    360 	 *
    361 	 * @since 4.4.0
    362 	 *
    363 	 * @param string $content Default text.
    364 	 */
    365 	$content = apply_filters( 'update_right_now_text', $content );
    366 
    367 	$msg .= sprintf( '<span id="wp-version">' . $content . '</span>', get_bloginfo( 'version', 'display' ), $theme_name );
    368 
    369 	echo "<p id='wp-version-message'>$msg</p>";
    370 }
    371 
    372 /**
    373  * @since 2.9.0
    374  *
    375  * @return array
    376  */
    377 function get_plugin_updates() {
    378 	$all_plugins     = get_plugins();
    379 	$upgrade_plugins = array();
    380 	$current         = get_site_transient( 'update_plugins' );
    381 	foreach ( (array) $all_plugins as $plugin_file => $plugin_data ) {
    382 		if ( isset( $current->response[ $plugin_file ] ) ) {
    383 			$upgrade_plugins[ $plugin_file ]         = (object) $plugin_data;
    384 			$upgrade_plugins[ $plugin_file ]->update = $current->response[ $plugin_file ];
    385 		}
    386 	}
    387 
    388 	return $upgrade_plugins;
    389 }
    390 
    391 /**
    392  * @since 2.9.0
    393  */
    394 function wp_plugin_update_rows() {
    395 	if ( ! current_user_can( 'update_plugins' ) ) {
    396 		return;
    397 	}
    398 
    399 	$plugins = get_site_transient( 'update_plugins' );
    400 	if ( isset( $plugins->response ) && is_array( $plugins->response ) ) {
    401 		$plugins = array_keys( $plugins->response );
    402 		foreach ( $plugins as $plugin_file ) {
    403 			add_action( "after_plugin_row_{$plugin_file}", 'wp_plugin_update_row', 10, 2 );
    404 		}
    405 	}
    406 }
    407 
    408 /**
    409  * Displays update information for a plugin.
    410  *
    411  * @since 2.3.0
    412  *
    413  * @param string $file        Plugin basename.
    414  * @param array  $plugin_data Plugin information.
    415  * @return void|false
    416  */
    417 function wp_plugin_update_row( $file, $plugin_data ) {
    418 	$current = get_site_transient( 'update_plugins' );
    419 	if ( ! isset( $current->response[ $file ] ) ) {
    420 		return false;
    421 	}
    422 
    423 	$response = $current->response[ $file ];
    424 
    425 	$plugins_allowedtags = array(
    426 		'a'       => array(
    427 			'href'  => array(),
    428 			'title' => array(),
    429 		),
    430 		'abbr'    => array( 'title' => array() ),
    431 		'acronym' => array( 'title' => array() ),
    432 		'code'    => array(),
    433 		'em'      => array(),
    434 		'strong'  => array(),
    435 	);
    436 
    437 	$plugin_name = wp_kses( $plugin_data['Name'], $plugins_allowedtags );
    438 	$plugin_slug = isset( $response->slug ) ? $response->slug : $response->id;
    439 
    440 	if ( isset( $response->slug ) ) {
    441 		$details_url = self_admin_url( 'plugin-install.php?tab=plugin-information&plugin=' . $plugin_slug . '&section=changelog' );
    442 	} elseif ( isset( $response->url ) ) {
    443 		$details_url = $response->url;
    444 	} else {
    445 		$details_url = $plugin_data['PluginURI'];
    446 	}
    447 
    448 	$details_url = add_query_arg(
    449 		array(
    450 			'TB_iframe' => 'true',
    451 			'width'     => 600,
    452 			'height'    => 800,
    453 		),
    454 		$details_url
    455 	);
    456 
    457 	/** @var WP_Plugins_List_Table $wp_list_table */
    458 	$wp_list_table = _get_list_table(
    459 		'WP_Plugins_List_Table',
    460 		array(
    461 			'screen' => get_current_screen(),
    462 		)
    463 	);
    464 
    465 	if ( is_network_admin() || ! is_multisite() ) {
    466 		if ( is_network_admin() ) {
    467 			$active_class = is_plugin_active_for_network( $file ) ? ' active' : '';
    468 		} else {
    469 			$active_class = is_plugin_active( $file ) ? ' active' : '';
    470 		}
    471 
    472 		$requires_php   = isset( $response->requires_php ) ? $response->requires_php : null;
    473 		$compatible_php = is_php_version_compatible( $requires_php );
    474 		$notice_type    = $compatible_php ? 'notice-warning' : 'notice-error';
    475 
    476 		printf(
    477 			'<tr class="plugin-update-tr%s" id="%s" data-slug="%s" data-plugin="%s">' .
    478 			'<td colspan="%s" class="plugin-update colspanchange">' .
    479 			'<div class="update-message notice inline %s notice-alt"><p>',
    480 			$active_class,
    481 			esc_attr( $plugin_slug . '-update' ),
    482 			esc_attr( $plugin_slug ),
    483 			esc_attr( $file ),
    484 			esc_attr( $wp_list_table->get_column_count() ),
    485 			$notice_type
    486 		);
    487 
    488 		if ( ! current_user_can( 'update_plugins' ) ) {
    489 			printf(
    490 				/* translators: 1: Plugin name, 2: Details URL, 3: Additional link attributes, 4: Version number. */
    491 				__( 'There is a new version of %1$s available. <a href="%2$s" %3$s>View version %4$s details</a>.' ),
    492 				$plugin_name,
    493 				esc_url( $details_url ),
    494 				sprintf(
    495 					'class="thickbox open-plugin-details-modal" aria-label="%s"',
    496 					/* translators: 1: Plugin name, 2: Version number. */
    497 					esc_attr( sprintf( __( 'View %1$s version %2$s details' ), $plugin_name, $response->new_version ) )
    498 				),
    499 				esc_attr( $response->new_version )
    500 			);
    501 		} elseif ( empty( $response->package ) ) {
    502 			printf(
    503 				/* translators: 1: Plugin name, 2: Details URL, 3: Additional link attributes, 4: Version number. */
    504 				__( 'There is a new version of %1$s available. <a href="%2$s" %3$s>View version %4$s details</a>. <em>Automatic update is unavailable for this plugin.</em>' ),
    505 				$plugin_name,
    506 				esc_url( $details_url ),
    507 				sprintf(
    508 					'class="thickbox open-plugin-details-modal" aria-label="%s"',
    509 					/* translators: 1: Plugin name, 2: Version number. */
    510 					esc_attr( sprintf( __( 'View %1$s version %2$s details' ), $plugin_name, $response->new_version ) )
    511 				),
    512 				esc_attr( $response->new_version )
    513 			);
    514 		} else {
    515 			if ( $compatible_php ) {
    516 				printf(
    517 					/* translators: 1: Plugin name, 2: Details URL, 3: Additional link attributes, 4: Version number, 5: Update URL, 6: Additional link attributes. */
    518 					__( 'There is a new version of %1$s available. <a href="%2$s" %3$s>View version %4$s details</a> or <a href="%5$s" %6$s>update now</a>.' ),
    519 					$plugin_name,
    520 					esc_url( $details_url ),
    521 					sprintf(
    522 						'class="thickbox open-plugin-details-modal" aria-label="%s"',
    523 						/* translators: 1: Plugin name, 2: Version number. */
    524 						esc_attr( sprintf( __( 'View %1$s version %2$s details' ), $plugin_name, $response->new_version ) )
    525 					),
    526 					esc_attr( $response->new_version ),
    527 					wp_nonce_url( self_admin_url( 'update.php?action=upgrade-plugin&plugin=' ) . $file, 'upgrade-plugin_' . $file ),
    528 					sprintf(
    529 						'class="update-link" aria-label="%s"',
    530 						/* translators: %s: Plugin name. */
    531 						esc_attr( sprintf( _x( 'Update %s now', 'plugin' ), $plugin_name ) )
    532 					)
    533 				);
    534 			} else {
    535 				printf(
    536 					/* translators: 1: Plugin name, 2: Details URL, 3: Additional link attributes, 4: Version number 5: URL to Update PHP page. */
    537 					__( 'There is a new version of %1$s available, but it doesn&#8217;t work with your version of PHP. <a href="%2$s" %3$s>View version %4$s details</a> or <a href="%5$s">learn more about updating PHP</a>.' ),
    538 					$plugin_name,
    539 					esc_url( $details_url ),
    540 					sprintf(
    541 						'class="thickbox open-plugin-details-modal" aria-label="%s"',
    542 						/* translators: 1: Plugin name, 2: Version number. */
    543 						esc_attr( sprintf( __( 'View %1$s version %2$s details' ), $plugin_name, $response->new_version ) )
    544 					),
    545 					esc_attr( $response->new_version ),
    546 					esc_url( wp_get_update_php_url() )
    547 				);
    548 				wp_update_php_annotation( '<br><em>', '</em>' );
    549 			}
    550 		}
    551 
    552 		/**
    553 		 * Fires at the end of the update message container in each
    554 		 * row of the plugins list table.
    555 		 *
    556 		 * The dynamic portion of the hook name, `$file`, refers to the path
    557 		 * of the plugin's primary file relative to the plugins directory.
    558 		 *
    559 		 * @since 2.8.0
    560 		 *
    561 		 * @param array $plugin_data {
    562 		 *     An array of plugin metadata.
    563 		 *
    564 		 *     @type string $name        The human-readable name of the plugin.
    565 		 *     @type string $plugin_uri  Plugin URI.
    566 		 *     @type string $version     Plugin version.
    567 		 *     @type string $description Plugin description.
    568 		 *     @type string $author      Plugin author.
    569 		 *     @type string $author_uri  Plugin author URI.
    570 		 *     @type string $text_domain Plugin text domain.
    571 		 *     @type string $domain_path Relative path to the plugin's .mo file(s).
    572 		 *     @type bool   $network     Whether the plugin can only be activated network wide.
    573 		 *     @type string $title       The human-readable title of the plugin.
    574 		 *     @type string $author_name Plugin author's name.
    575 		 *     @type bool   $update      Whether there's an available update. Default null.
    576 		 * }
    577 		 * @param array $response {
    578 		 *     An array of metadata about the available plugin update.
    579 		 *
    580 		 *     @type int    $id          Plugin ID.
    581 		 *     @type string $slug        Plugin slug.
    582 		 *     @type string $new_version New plugin version.
    583 		 *     @type string $url         Plugin URL.
    584 		 *     @type string $package     Plugin update package URL.
    585 		 * }
    586 		 */
    587 		do_action( "in_plugin_update_message-{$file}", $plugin_data, $response ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
    588 
    589 		echo '</p></div></td></tr>';
    590 	}
    591 }
    592 
    593 /**
    594  * @since 2.9.0
    595  *
    596  * @return array
    597  */
    598 function get_theme_updates() {
    599 	$current = get_site_transient( 'update_themes' );
    600 
    601 	if ( ! isset( $current->response ) ) {
    602 		return array();
    603 	}
    604 
    605 	$update_themes = array();
    606 	foreach ( $current->response as $stylesheet => $data ) {
    607 		$update_themes[ $stylesheet ]         = wp_get_theme( $stylesheet );
    608 		$update_themes[ $stylesheet ]->update = $data;
    609 	}
    610 
    611 	return $update_themes;
    612 }
    613 
    614 /**
    615  * @since 3.1.0
    616  */
    617 function wp_theme_update_rows() {
    618 	if ( ! current_user_can( 'update_themes' ) ) {
    619 		return;
    620 	}
    621 
    622 	$themes = get_site_transient( 'update_themes' );
    623 	if ( isset( $themes->response ) && is_array( $themes->response ) ) {
    624 		$themes = array_keys( $themes->response );
    625 
    626 		foreach ( $themes as $theme ) {
    627 			add_action( "after_theme_row_{$theme}", 'wp_theme_update_row', 10, 2 );
    628 		}
    629 	}
    630 }
    631 
    632 /**
    633  * Displays update information for a theme.
    634  *
    635  * @since 3.1.0
    636  *
    637  * @param string   $theme_key Theme stylesheet.
    638  * @param WP_Theme $theme     Theme object.
    639  * @return void|false
    640  */
    641 function wp_theme_update_row( $theme_key, $theme ) {
    642 	$current = get_site_transient( 'update_themes' );
    643 
    644 	if ( ! isset( $current->response[ $theme_key ] ) ) {
    645 		return false;
    646 	}
    647 
    648 	$response = $current->response[ $theme_key ];
    649 
    650 	$details_url = add_query_arg(
    651 		array(
    652 			'TB_iframe' => 'true',
    653 			'width'     => 1024,
    654 			'height'    => 800,
    655 		),
    656 		$current->response[ $theme_key ]['url']
    657 	);
    658 
    659 	/** @var WP_MS_Themes_List_Table $wp_list_table */
    660 	$wp_list_table = _get_list_table( 'WP_MS_Themes_List_Table' );
    661 
    662 	$active = $theme->is_allowed( 'network' ) ? ' active' : '';
    663 
    664 	$requires_wp  = isset( $response['requires'] ) ? $response['requires'] : null;
    665 	$requires_php = isset( $response['requires_php'] ) ? $response['requires_php'] : null;
    666 
    667 	$compatible_wp  = is_wp_version_compatible( $requires_wp );
    668 	$compatible_php = is_php_version_compatible( $requires_php );
    669 
    670 	printf(
    671 		'<tr class="plugin-update-tr%s" id="%s" data-slug="%s">' .
    672 		'<td colspan="%s" class="plugin-update colspanchange">' .
    673 		'<div class="update-message notice inline notice-warning notice-alt"><p>',
    674 		$active,
    675 		esc_attr( $theme->get_stylesheet() . '-update' ),
    676 		esc_attr( $theme->get_stylesheet() ),
    677 		$wp_list_table->get_column_count()
    678 	);
    679 
    680 	if ( $compatible_wp && $compatible_php ) {
    681 		if ( ! current_user_can( 'update_themes' ) ) {
    682 			printf(
    683 				/* translators: 1: Theme name, 2: Details URL, 3: Additional link attributes, 4: Version number. */
    684 				__( 'There is a new version of %1$s available. <a href="%2$s" %3$s>View version %4$s details</a>.' ),
    685 				$theme['Name'],
    686 				esc_url( $details_url ),
    687 				sprintf(
    688 					'class="thickbox open-plugin-details-modal" aria-label="%s"',
    689 					/* translators: 1: Theme name, 2: Version number. */
    690 					esc_attr( sprintf( __( 'View %1$s version %2$s details' ), $theme['Name'], $response['new_version'] ) )
    691 				),
    692 				$response['new_version']
    693 			);
    694 		} elseif ( empty( $response['package'] ) ) {
    695 			printf(
    696 				/* translators: 1: Theme name, 2: Details URL, 3: Additional link attributes, 4: Version number. */
    697 				__( 'There is a new version of %1$s available. <a href="%2$s" %3$s>View version %4$s details</a>. <em>Automatic update is unavailable for this theme.</em>' ),
    698 				$theme['Name'],
    699 				esc_url( $details_url ),
    700 				sprintf(
    701 					'class="thickbox open-plugin-details-modal" aria-label="%s"',
    702 					/* translators: 1: Theme name, 2: Version number. */
    703 					esc_attr( sprintf( __( 'View %1$s version %2$s details' ), $theme['Name'], $response['new_version'] ) )
    704 				),
    705 				$response['new_version']
    706 			);
    707 		} else {
    708 			printf(
    709 				/* translators: 1: Theme name, 2: Details URL, 3: Additional link attributes, 4: Version number, 5: Update URL, 6: Additional link attributes. */
    710 				__( 'There is a new version of %1$s available. <a href="%2$s" %3$s>View version %4$s details</a> or <a href="%5$s" %6$s>update now</a>.' ),
    711 				$theme['Name'],
    712 				esc_url( $details_url ),
    713 				sprintf(
    714 					'class="thickbox open-plugin-details-modal" aria-label="%s"',
    715 					/* translators: 1: Theme name, 2: Version number. */
    716 					esc_attr( sprintf( __( 'View %1$s version %2$s details' ), $theme['Name'], $response['new_version'] ) )
    717 				),
    718 				$response['new_version'],
    719 				wp_nonce_url( self_admin_url( 'update.php?action=upgrade-theme&theme=' ) . $theme_key, 'upgrade-theme_' . $theme_key ),
    720 				sprintf(
    721 					'class="update-link" aria-label="%s"',
    722 					/* translators: %s: Theme name. */
    723 					esc_attr( sprintf( _x( 'Update %s now', 'theme' ), $theme['Name'] ) )
    724 				)
    725 			);
    726 		}
    727 	} else {
    728 		if ( ! $compatible_wp && ! $compatible_php ) {
    729 			printf(
    730 				/* translators: %s: Theme name. */
    731 				__( 'There is a new version of %s available, but it doesn&#8217;t work with your versions of WordPress and PHP.' ),
    732 				$theme['Name']
    733 			);
    734 			if ( current_user_can( 'update_core' ) && current_user_can( 'update_php' ) ) {
    735 				printf(
    736 					/* translators: 1: URL to WordPress Updates screen, 2: URL to Update PHP page. */
    737 					' ' . __( '<a href="%1$s">Please update WordPress</a>, and then <a href="%2$s">learn more about updating PHP</a>.' ),
    738 					self_admin_url( 'update-core.php' ),
    739 					esc_url( wp_get_update_php_url() )
    740 				);
    741 				wp_update_php_annotation( '</p><p><em>', '</em>' );
    742 			} elseif ( current_user_can( 'update_core' ) ) {
    743 				printf(
    744 					/* translators: %s: URL to WordPress Updates screen. */
    745 					' ' . __( '<a href="%s">Please update WordPress</a>.' ),
    746 					self_admin_url( 'update-core.php' )
    747 				);
    748 			} elseif ( current_user_can( 'update_php' ) ) {
    749 				printf(
    750 					/* translators: %s: URL to Update PHP page. */
    751 					' ' . __( '<a href="%s">Learn more about updating PHP</a>.' ),
    752 					esc_url( wp_get_update_php_url() )
    753 				);
    754 				wp_update_php_annotation( '</p><p><em>', '</em>' );
    755 			}
    756 		} elseif ( ! $compatible_wp ) {
    757 			printf(
    758 				/* translators: %s: Theme name. */
    759 				__( 'There is a new version of %s available, but it doesn&#8217;t work with your version of WordPress.' ),
    760 				$theme['Name']
    761 			);
    762 			if ( current_user_can( 'update_core' ) ) {
    763 				printf(
    764 					/* translators: %s: URL to WordPress Updates screen. */
    765 					' ' . __( '<a href="%s">Please update WordPress</a>.' ),
    766 					self_admin_url( 'update-core.php' )
    767 				);
    768 			}
    769 		} elseif ( ! $compatible_php ) {
    770 			printf(
    771 				/* translators: %s: Theme name. */
    772 				__( 'There is a new version of %s available, but it doesn&#8217;t work with your version of PHP.' ),
    773 				$theme['Name']
    774 			);
    775 			if ( current_user_can( 'update_php' ) ) {
    776 				printf(
    777 					/* translators: %s: URL to Update PHP page. */
    778 					' ' . __( '<a href="%s">Learn more about updating PHP</a>.' ),
    779 					esc_url( wp_get_update_php_url() )
    780 				);
    781 				wp_update_php_annotation( '</p><p><em>', '</em>' );
    782 			}
    783 		}
    784 	}
    785 
    786 	/**
    787 	 * Fires at the end of the update message container in each
    788 	 * row of the themes list table.
    789 	 *
    790 	 * The dynamic portion of the hook name, `$theme_key`, refers to
    791 	 * the theme slug as found in the WordPress.org themes repository.
    792 	 *
    793 	 * @since 3.1.0
    794 	 *
    795 	 * @param WP_Theme $theme    The WP_Theme object.
    796 	 * @param array    $response {
    797 	 *     An array of metadata about the available theme update.
    798 	 *
    799 	 *     @type string $new_version New theme version.
    800 	 *     @type string $url         Theme URL.
    801 	 *     @type string $package     Theme update package URL.
    802 	 * }
    803 	 */
    804 	do_action( "in_theme_update_message-{$theme_key}", $theme, $response ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
    805 
    806 	echo '</p></div></td></tr>';
    807 }
    808 
    809 /**
    810  * @since 2.7.0
    811  *
    812  * @global int $upgrading
    813  * @return void|false
    814  */
    815 function maintenance_nag() {
    816 	// Include an unmodified $wp_version.
    817 	require ABSPATH . WPINC . '/version.php';
    818 	global $upgrading;
    819 	$nag = isset( $upgrading );
    820 	if ( ! $nag ) {
    821 		$failed = get_site_option( 'auto_core_update_failed' );
    822 		/*
    823 		 * If an update failed critically, we may have copied over version.php but not other files.
    824 		 * In that case, if the installation claims we're running the version we attempted, nag.
    825 		 * This is serious enough to err on the side of nagging.
    826 		 *
    827 		 * If we simply failed to update before we tried to copy any files, then assume things are
    828 		 * OK if they are now running the latest.
    829 		 *
    830 		 * This flag is cleared whenever a successful update occurs using Core_Upgrader.
    831 		 */
    832 		$comparison = ! empty( $failed['critical'] ) ? '>=' : '>';
    833 		if ( isset( $failed['attempted'] ) && version_compare( $failed['attempted'], $wp_version, $comparison ) ) {
    834 			$nag = true;
    835 		}
    836 	}
    837 
    838 	if ( ! $nag ) {
    839 		return false;
    840 	}
    841 
    842 	if ( current_user_can( 'update_core' ) ) {
    843 		$msg = sprintf(
    844 			/* translators: %s: URL to WordPress Updates screen. */
    845 			__( 'An automated WordPress update has failed to complete - <a href="%s">please attempt the update again now</a>.' ),
    846 			'update-core.php'
    847 		);
    848 	} else {
    849 		$msg = __( 'An automated WordPress update has failed to complete! Please notify the site administrator.' );
    850 	}
    851 
    852 	echo "<div class='update-nag notice notice-warning inline'>$msg</div>";
    853 }
    854 
    855 /**
    856  * Prints the JavaScript templates for update admin notices.
    857  *
    858  * Template takes one argument with four values:
    859  *
    860  *     param {object} data {
    861  *         Arguments for admin notice.
    862  *
    863  *         @type string id        ID of the notice.
    864  *         @type string className Class names for the notice.
    865  *         @type string message   The notice's message.
    866  *         @type string type      The type of update the notice is for. Either 'plugin' or 'theme'.
    867  *     }
    868  *
    869  * @since 4.6.0
    870  */
    871 function wp_print_admin_notice_templates() {
    872 	?>
    873 	<script id="tmpl-wp-updates-admin-notice" type="text/html">
    874 		<div <# if ( data.id ) { #>id="{{ data.id }}"<# } #> class="notice {{ data.className }}"><p>{{{ data.message }}}</p></div>
    875 	</script>
    876 	<script id="tmpl-wp-bulk-updates-admin-notice" type="text/html">
    877 		<div id="{{ data.id }}" class="{{ data.className }} notice <# if ( data.errors ) { #>notice-error<# } else { #>notice-success<# } #>">
    878 			<p>
    879 				<# if ( data.successes ) { #>
    880 					<# if ( 1 === data.successes ) { #>
    881 						<# if ( 'plugin' === data.type ) { #>
    882 							<?php
    883 							/* translators: %s: Number of plugins. */
    884 							printf( __( '%s plugin successfully updated.' ), '{{ data.successes }}' );
    885 							?>
    886 						<# } else { #>
    887 							<?php
    888 							/* translators: %s: Number of themes. */
    889 							printf( __( '%s theme successfully updated.' ), '{{ data.successes }}' );
    890 							?>
    891 						<# } #>
    892 					<# } else { #>
    893 						<# if ( 'plugin' === data.type ) { #>
    894 							<?php
    895 							/* translators: %s: Number of plugins. */
    896 							printf( __( '%s plugins successfully updated.' ), '{{ data.successes }}' );
    897 							?>
    898 						<# } else { #>
    899 							<?php
    900 							/* translators: %s: Number of themes. */
    901 							printf( __( '%s themes successfully updated.' ), '{{ data.successes }}' );
    902 							?>
    903 						<# } #>
    904 					<# } #>
    905 				<# } #>
    906 				<# if ( data.errors ) { #>
    907 					<button class="button-link bulk-action-errors-collapsed" aria-expanded="false">
    908 						<# if ( 1 === data.errors ) { #>
    909 							<?php
    910 							/* translators: %s: Number of failed updates. */
    911 							printf( __( '%s update failed.' ), '{{ data.errors }}' );
    912 							?>
    913 						<# } else { #>
    914 							<?php
    915 							/* translators: %s: Number of failed updates. */
    916 							printf( __( '%s updates failed.' ), '{{ data.errors }}' );
    917 							?>
    918 						<# } #>
    919 						<span class="screen-reader-text"><?php _e( 'Show more details' ); ?></span>
    920 						<span class="toggle-indicator" aria-hidden="true"></span>
    921 					</button>
    922 				<# } #>
    923 			</p>
    924 			<# if ( data.errors ) { #>
    925 				<ul class="bulk-action-errors hidden">
    926 					<# _.each( data.errorMessages, function( errorMessage ) { #>
    927 						<li>{{ errorMessage }}</li>
    928 					<# } ); #>
    929 				</ul>
    930 			<# } #>
    931 		</div>
    932 	</script>
    933 	<?php
    934 }
    935 
    936 /**
    937  * Prints the JavaScript templates for update and deletion rows in list tables.
    938  *
    939  * The update template takes one argument with four values:
    940  *
    941  *     param {object} data {
    942  *         Arguments for the update row
    943  *
    944  *         @type string slug    Plugin slug.
    945  *         @type string plugin  Plugin base name.
    946  *         @type string colspan The number of table columns this row spans.
    947  *         @type string content The row content.
    948  *     }
    949  *
    950  * The delete template takes one argument with four values:
    951  *
    952  *     param {object} data {
    953  *         Arguments for the update row
    954  *
    955  *         @type string slug    Plugin slug.
    956  *         @type string plugin  Plugin base name.
    957  *         @type string name    Plugin name.
    958  *         @type string colspan The number of table columns this row spans.
    959  *     }
    960  *
    961  * @since 4.6.0
    962  */
    963 function wp_print_update_row_templates() {
    964 	?>
    965 	<script id="tmpl-item-update-row" type="text/template">
    966 		<tr class="plugin-update-tr update" id="{{ data.slug }}-update" data-slug="{{ data.slug }}" <# if ( data.plugin ) { #>data-plugin="{{ data.plugin }}"<# } #>>
    967 			<td colspan="{{ data.colspan }}" class="plugin-update colspanchange">
    968 				{{{ data.content }}}
    969 			</td>
    970 		</tr>
    971 	</script>
    972 	<script id="tmpl-item-deleted-row" type="text/template">
    973 		<tr class="plugin-deleted-tr inactive deleted" id="{{ data.slug }}-deleted" data-slug="{{ data.slug }}" <# if ( data.plugin ) { #>data-plugin="{{ data.plugin }}"<# } #>>
    974 			<td colspan="{{ data.colspan }}" class="plugin-update colspanchange">
    975 				<# if ( data.plugin ) { #>
    976 					<?php
    977 					printf(
    978 						/* translators: %s: Plugin name. */
    979 						_x( '%s was successfully deleted.', 'plugin' ),
    980 						'<strong>{{{ data.name }}}</strong>'
    981 					);
    982 					?>
    983 				<# } else { #>
    984 					<?php
    985 					printf(
    986 						/* translators: %s: Theme name. */
    987 						_x( '%s was successfully deleted.', 'theme' ),
    988 						'<strong>{{{ data.name }}}</strong>'
    989 					);
    990 					?>
    991 				<# } #>
    992 			</td>
    993 		</tr>
    994 	</script>
    995 	<?php
    996 }
    997 
    998 /**
    999  * Displays a notice when the user is in recovery mode.
   1000  *
   1001  * @since 5.2.0
   1002  */
   1003 function wp_recovery_mode_nag() {
   1004 	if ( ! wp_is_recovery_mode() ) {
   1005 		return;
   1006 	}
   1007 
   1008 	$url = wp_login_url();
   1009 	$url = add_query_arg( 'action', WP_Recovery_Mode::EXIT_ACTION, $url );
   1010 	$url = wp_nonce_url( $url, WP_Recovery_Mode::EXIT_ACTION );
   1011 
   1012 	?>
   1013 	<div class="notice notice-info">
   1014 		<p>
   1015 			<?php
   1016 			printf(
   1017 				/* translators: %s: Recovery Mode exit link. */
   1018 				__( 'You are in recovery mode. This means there may be an error with a theme or plugin. To exit recovery mode, log out or use the Exit button. <a href="%s">Exit Recovery Mode</a>' ),
   1019 				esc_url( $url )
   1020 			);
   1021 			?>
   1022 		</p>
   1023 	</div>
   1024 	<?php
   1025 }
   1026 
   1027 /**
   1028  * Checks whether auto-updates are enabled.
   1029  *
   1030  * @since 5.5.0
   1031  *
   1032  * @param string $type The type of update being checked: 'theme' or 'plugin'.
   1033  * @return bool True if auto-updates are enabled for `$type`, false otherwise.
   1034  */
   1035 function wp_is_auto_update_enabled_for_type( $type ) {
   1036 	if ( ! class_exists( 'WP_Automatic_Updater' ) ) {
   1037 		require_once ABSPATH . 'wp-admin/includes/class-wp-automatic-updater.php';
   1038 	}
   1039 
   1040 	$updater = new WP_Automatic_Updater();
   1041 	$enabled = ! $updater->is_disabled();
   1042 
   1043 	switch ( $type ) {
   1044 		case 'plugin':
   1045 			/**
   1046 			 * Filters whether plugins auto-update is enabled.
   1047 			 *
   1048 			 * @since 5.5.0
   1049 			 *
   1050 			 * @param bool $enabled True if plugins auto-update is enabled, false otherwise.
   1051 			 */
   1052 			return apply_filters( 'plugins_auto_update_enabled', $enabled );
   1053 		case 'theme':
   1054 			/**
   1055 			 * Filters whether themes auto-update is enabled.
   1056 			 *
   1057 			 * @since 5.5.0
   1058 			 *
   1059 			 * @param bool $enabled True if themes auto-update is enabled, false otherwise.
   1060 			 */
   1061 			return apply_filters( 'themes_auto_update_enabled', $enabled );
   1062 	}
   1063 
   1064 	return false;
   1065 }
   1066 
   1067 /**
   1068  * Checks whether auto-updates are forced for an item.
   1069  *
   1070  * @since 5.6.0
   1071  *
   1072  * @param string    $type   The type of update being checked: 'theme' or 'plugin'.
   1073  * @param bool|null $update Whether to update. The value of null is internally used
   1074  *                          to detect whether nothing has hooked into this filter.
   1075  * @param object    $item   The update offer.
   1076  * @return bool True if auto-updates are forced for `$item`, false otherwise.
   1077  */
   1078 function wp_is_auto_update_forced_for_item( $type, $update, $item ) {
   1079 	/** This filter is documented in wp-admin/includes/class-wp-automatic-updater.php */
   1080 	return apply_filters( "auto_update_{$type}", $update, $item );
   1081 }
   1082 
   1083 /**
   1084  * Determines the appropriate auto-update message to be displayed.
   1085  *
   1086  * @since 5.5.0
   1087  *
   1088  * @return string The update message to be shown.
   1089  */
   1090 function wp_get_auto_update_message() {
   1091 	$next_update_time = wp_next_scheduled( 'wp_version_check' );
   1092 
   1093 	// Check if the event exists.
   1094 	if ( false === $next_update_time ) {
   1095 		$message = __( 'Automatic update not scheduled. There may be a problem with WP-Cron.' );
   1096 	} else {
   1097 		$time_to_next_update = human_time_diff( (int) $next_update_time );
   1098 
   1099 		// See if cron is overdue.
   1100 		$overdue = ( time() - $next_update_time ) > 0;
   1101 
   1102 		if ( $overdue ) {
   1103 			$message = sprintf(
   1104 				/* translators: %s: Duration that WP-Cron has been overdue. */
   1105 				__( 'Automatic update overdue by %s. There may be a problem with WP-Cron.' ),
   1106 				$time_to_next_update
   1107 			);
   1108 		} else {
   1109 			$message = sprintf(
   1110 				/* translators: %s: Time until the next update. */
   1111 				__( 'Automatic update scheduled in %s.' ),
   1112 				$time_to_next_update
   1113 			);
   1114 		}
   1115 	}
   1116 
   1117 	return $message;
   1118 }