Няма описание

update.php 34KB

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