Nessuna descrizione

update.php 34KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113
  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' ), '', '&' );
  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 The filename of the current screen.
  244. * @return void|false
  245. */
  246. function update_nag() {
  247. global $pagenow;
  248. if ( is_multisite() && ! current_user_can( 'update_core' ) ) {
  249. return false;
  250. }
  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 does not 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 An array of plugin metadata. See get_plugin_data()
  494. * and the {@see 'plugin_row_meta'} filter for the list
  495. * of possible values.
  496. * @param object $response {
  497. * An object of metadata about the available plugin update.
  498. *
  499. * @type string $id Plugin ID, e.g. `w.org/plugins/[plugin-name]`.
  500. * @type string $slug Plugin slug.
  501. * @type string $plugin Plugin basename.
  502. * @type string $new_version New plugin version.
  503. * @type string $url Plugin URL.
  504. * @type string $package Plugin update package URL.
  505. * @type string[] $icons An array of plugin icon URLs.
  506. * @type string[] $banners An array of plugin banner URLs.
  507. * @type string[] $banners_rtl An array of plugin RTL banner URLs.
  508. * @type string $requires The version of WordPress which the plugin requires.
  509. * @type string $tested The version of WordPress the plugin is tested against.
  510. * @type string $requires_php The version of PHP which the plugin requires.
  511. * }
  512. */
  513. do_action( "in_plugin_update_message-{$file}", $plugin_data, $response ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
  514. echo '</p></div></td></tr>';
  515. }
  516. }
  517. /**
  518. * @since 2.9.0
  519. *
  520. * @return array
  521. */
  522. function get_theme_updates() {
  523. $current = get_site_transient( 'update_themes' );
  524. if ( ! isset( $current->response ) ) {
  525. return array();
  526. }
  527. $update_themes = array();
  528. foreach ( $current->response as $stylesheet => $data ) {
  529. $update_themes[ $stylesheet ] = wp_get_theme( $stylesheet );
  530. $update_themes[ $stylesheet ]->update = $data;
  531. }
  532. return $update_themes;
  533. }
  534. /**
  535. * @since 3.1.0
  536. */
  537. function wp_theme_update_rows() {
  538. if ( ! current_user_can( 'update_themes' ) ) {
  539. return;
  540. }
  541. $themes = get_site_transient( 'update_themes' );
  542. if ( isset( $themes->response ) && is_array( $themes->response ) ) {
  543. $themes = array_keys( $themes->response );
  544. foreach ( $themes as $theme ) {
  545. add_action( "after_theme_row_{$theme}", 'wp_theme_update_row', 10, 2 );
  546. }
  547. }
  548. }
  549. /**
  550. * Displays update information for a theme.
  551. *
  552. * @since 3.1.0
  553. *
  554. * @param string $theme_key Theme stylesheet.
  555. * @param WP_Theme $theme Theme object.
  556. * @return void|false
  557. */
  558. function wp_theme_update_row( $theme_key, $theme ) {
  559. $current = get_site_transient( 'update_themes' );
  560. if ( ! isset( $current->response[ $theme_key ] ) ) {
  561. return false;
  562. }
  563. $response = $current->response[ $theme_key ];
  564. $details_url = add_query_arg(
  565. array(
  566. 'TB_iframe' => 'true',
  567. 'width' => 1024,
  568. 'height' => 800,
  569. ),
  570. $current->response[ $theme_key ]['url']
  571. );
  572. /** @var WP_MS_Themes_List_Table $wp_list_table */
  573. $wp_list_table = _get_list_table( 'WP_MS_Themes_List_Table' );
  574. $active = $theme->is_allowed( 'network' ) ? ' active' : '';
  575. $requires_wp = isset( $response['requires'] ) ? $response['requires'] : null;
  576. $requires_php = isset( $response['requires_php'] ) ? $response['requires_php'] : null;
  577. $compatible_wp = is_wp_version_compatible( $requires_wp );
  578. $compatible_php = is_php_version_compatible( $requires_php );
  579. printf(
  580. '<tr class="plugin-update-tr%s" id="%s" data-slug="%s">' .
  581. '<td colspan="%s" class="plugin-update colspanchange">' .
  582. '<div class="update-message notice inline notice-warning notice-alt"><p>',
  583. $active,
  584. esc_attr( $theme->get_stylesheet() . '-update' ),
  585. esc_attr( $theme->get_stylesheet() ),
  586. $wp_list_table->get_column_count()
  587. );
  588. if ( $compatible_wp && $compatible_php ) {
  589. if ( ! current_user_can( 'update_themes' ) ) {
  590. printf(
  591. /* translators: 1: Theme name, 2: Details URL, 3: Additional link attributes, 4: Version number. */
  592. __( 'There is a new version of %1$s available. <a href="%2$s" %3$s>View version %4$s details</a>.' ),
  593. $theme['Name'],
  594. esc_url( $details_url ),
  595. sprintf(
  596. 'class="thickbox open-plugin-details-modal" aria-label="%s"',
  597. /* translators: 1: Theme name, 2: Version number. */
  598. esc_attr( sprintf( __( 'View %1$s version %2$s details' ), $theme['Name'], $response['new_version'] ) )
  599. ),
  600. $response['new_version']
  601. );
  602. } elseif ( empty( $response['package'] ) ) {
  603. printf(
  604. /* translators: 1: Theme name, 2: Details URL, 3: Additional link attributes, 4: Version number. */
  605. __( '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>' ),
  606. $theme['Name'],
  607. esc_url( $details_url ),
  608. sprintf(
  609. 'class="thickbox open-plugin-details-modal" aria-label="%s"',
  610. /* translators: 1: Theme name, 2: Version number. */
  611. esc_attr( sprintf( __( 'View %1$s version %2$s details' ), $theme['Name'], $response['new_version'] ) )
  612. ),
  613. $response['new_version']
  614. );
  615. } else {
  616. printf(
  617. /* translators: 1: Theme name, 2: Details URL, 3: Additional link attributes, 4: Version number, 5: Update URL, 6: Additional link attributes. */
  618. __( '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>.' ),
  619. $theme['Name'],
  620. esc_url( $details_url ),
  621. sprintf(
  622. 'class="thickbox open-plugin-details-modal" aria-label="%s"',
  623. /* translators: 1: Theme name, 2: Version number. */
  624. esc_attr( sprintf( __( 'View %1$s version %2$s details' ), $theme['Name'], $response['new_version'] ) )
  625. ),
  626. $response['new_version'],
  627. wp_nonce_url( self_admin_url( 'update.php?action=upgrade-theme&theme=' ) . $theme_key, 'upgrade-theme_' . $theme_key ),
  628. sprintf(
  629. 'class="update-link" aria-label="%s"',
  630. /* translators: %s: Theme name. */
  631. esc_attr( sprintf( _x( 'Update %s now', 'theme' ), $theme['Name'] ) )
  632. )
  633. );
  634. }
  635. } else {
  636. if ( ! $compatible_wp && ! $compatible_php ) {
  637. printf(
  638. /* translators: %s: Theme name. */
  639. __( 'There is a new version of %s available, but it does not work with your versions of WordPress and PHP.' ),
  640. $theme['Name']
  641. );
  642. if ( current_user_can( 'update_core' ) && current_user_can( 'update_php' ) ) {
  643. printf(
  644. /* translators: 1: URL to WordPress Updates screen, 2: URL to Update PHP page. */
  645. ' ' . __( '<a href="%1$s">Please update WordPress</a>, and then <a href="%2$s">learn more about updating PHP</a>.' ),
  646. self_admin_url( 'update-core.php' ),
  647. esc_url( wp_get_update_php_url() )
  648. );
  649. wp_update_php_annotation( '</p><p><em>', '</em>' );
  650. } elseif ( current_user_can( 'update_core' ) ) {
  651. printf(
  652. /* translators: %s: URL to WordPress Updates screen. */
  653. ' ' . __( '<a href="%s">Please update WordPress</a>.' ),
  654. self_admin_url( 'update-core.php' )
  655. );
  656. } elseif ( current_user_can( 'update_php' ) ) {
  657. printf(
  658. /* translators: %s: URL to Update PHP page. */
  659. ' ' . __( '<a href="%s">Learn more about updating PHP</a>.' ),
  660. esc_url( wp_get_update_php_url() )
  661. );
  662. wp_update_php_annotation( '</p><p><em>', '</em>' );
  663. }
  664. } elseif ( ! $compatible_wp ) {
  665. printf(
  666. /* translators: %s: Theme name. */
  667. __( 'There is a new version of %s available, but it does not work with your version of WordPress.' ),
  668. $theme['Name']
  669. );
  670. if ( current_user_can( 'update_core' ) ) {
  671. printf(
  672. /* translators: %s: URL to WordPress Updates screen. */
  673. ' ' . __( '<a href="%s">Please update WordPress</a>.' ),
  674. self_admin_url( 'update-core.php' )
  675. );
  676. }
  677. } elseif ( ! $compatible_php ) {
  678. printf(
  679. /* translators: %s: Theme name. */
  680. __( 'There is a new version of %s available, but it does not work with your version of PHP.' ),
  681. $theme['Name']
  682. );
  683. if ( current_user_can( 'update_php' ) ) {
  684. printf(
  685. /* translators: %s: URL to Update PHP page. */
  686. ' ' . __( '<a href="%s">Learn more about updating PHP</a>.' ),
  687. esc_url( wp_get_update_php_url() )
  688. );
  689. wp_update_php_annotation( '</p><p><em>', '</em>' );
  690. }
  691. }
  692. }
  693. /**
  694. * Fires at the end of the update message container in each
  695. * row of the themes list table.
  696. *
  697. * The dynamic portion of the hook name, `$theme_key`, refers to
  698. * the theme slug as found in the WordPress.org themes repository.
  699. *
  700. * @since 3.1.0
  701. *
  702. * @param WP_Theme $theme The WP_Theme object.
  703. * @param array $response {
  704. * An array of metadata about the available theme update.
  705. *
  706. * @type string $new_version New theme version.
  707. * @type string $url Theme URL.
  708. * @type string $package Theme update package URL.
  709. * }
  710. */
  711. do_action( "in_theme_update_message-{$theme_key}", $theme, $response ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
  712. echo '</p></div></td></tr>';
  713. }
  714. /**
  715. * @since 2.7.0
  716. *
  717. * @global int $upgrading
  718. * @return void|false
  719. */
  720. function maintenance_nag() {
  721. // Include an unmodified $wp_version.
  722. require ABSPATH . WPINC . '/version.php';
  723. global $upgrading;
  724. $nag = isset( $upgrading );
  725. if ( ! $nag ) {
  726. $failed = get_site_option( 'auto_core_update_failed' );
  727. /*
  728. * If an update failed critically, we may have copied over version.php but not other files.
  729. * In that case, if the installation claims we're running the version we attempted, nag.
  730. * This is serious enough to err on the side of nagging.
  731. *
  732. * If we simply failed to update before we tried to copy any files, then assume things are
  733. * OK if they are now running the latest.
  734. *
  735. * This flag is cleared whenever a successful update occurs using Core_Upgrader.
  736. */
  737. $comparison = ! empty( $failed['critical'] ) ? '>=' : '>';
  738. if ( isset( $failed['attempted'] ) && version_compare( $failed['attempted'], $wp_version, $comparison ) ) {
  739. $nag = true;
  740. }
  741. }
  742. if ( ! $nag ) {
  743. return false;
  744. }
  745. if ( current_user_can( 'update_core' ) ) {
  746. $msg = sprintf(
  747. /* translators: %s: URL to WordPress Updates screen. */
  748. __( 'An automated WordPress update has failed to complete - <a href="%s">please attempt the update again now</a>.' ),
  749. 'update-core.php'
  750. );
  751. } else {
  752. $msg = __( 'An automated WordPress update has failed to complete! Please notify the site administrator.' );
  753. }
  754. echo "<div class='update-nag notice notice-warning inline'>$msg</div>";
  755. }
  756. /**
  757. * Prints the JavaScript templates for update admin notices.
  758. *
  759. * Template takes one argument with four values:
  760. *
  761. * param {object} data {
  762. * Arguments for admin notice.
  763. *
  764. * @type string id ID of the notice.
  765. * @type string className Class names for the notice.
  766. * @type string message The notice's message.
  767. * @type string type The type of update the notice is for. Either 'plugin' or 'theme'.
  768. * }
  769. *
  770. * @since 4.6.0
  771. */
  772. function wp_print_admin_notice_templates() {
  773. ?>
  774. <script id="tmpl-wp-updates-admin-notice" type="text/html">
  775. <div <# if ( data.id ) { #>id="{{ data.id }}"<# } #> class="notice {{ data.className }}"><p>{{{ data.message }}}</p></div>
  776. </script>
  777. <script id="tmpl-wp-bulk-updates-admin-notice" type="text/html">
  778. <div id="{{ data.id }}" class="{{ data.className }} notice <# if ( data.errors ) { #>notice-error<# } else { #>notice-success<# } #>">
  779. <p>
  780. <# if ( data.successes ) { #>
  781. <# if ( 1 === data.successes ) { #>
  782. <# if ( 'plugin' === data.type ) { #>
  783. <?php
  784. /* translators: %s: Number of plugins. */
  785. printf( __( '%s plugin successfully updated.' ), '{{ data.successes }}' );
  786. ?>
  787. <# } else { #>
  788. <?php
  789. /* translators: %s: Number of themes. */
  790. printf( __( '%s theme successfully updated.' ), '{{ data.successes }}' );
  791. ?>
  792. <# } #>
  793. <# } else { #>
  794. <# if ( 'plugin' === data.type ) { #>
  795. <?php
  796. /* translators: %s: Number of plugins. */
  797. printf( __( '%s plugins successfully updated.' ), '{{ data.successes }}' );
  798. ?>
  799. <# } else { #>
  800. <?php
  801. /* translators: %s: Number of themes. */
  802. printf( __( '%s themes successfully updated.' ), '{{ data.successes }}' );
  803. ?>
  804. <# } #>
  805. <# } #>
  806. <# } #>
  807. <# if ( data.errors ) { #>
  808. <button class="button-link bulk-action-errors-collapsed" aria-expanded="false">
  809. <# if ( 1 === data.errors ) { #>
  810. <?php
  811. /* translators: %s: Number of failed updates. */
  812. printf( __( '%s update failed.' ), '{{ data.errors }}' );
  813. ?>
  814. <# } else { #>
  815. <?php
  816. /* translators: %s: Number of failed updates. */
  817. printf( __( '%s updates failed.' ), '{{ data.errors }}' );
  818. ?>
  819. <# } #>
  820. <span class="screen-reader-text"><?php _e( 'Show more details' ); ?></span>
  821. <span class="toggle-indicator" aria-hidden="true"></span>
  822. </button>
  823. <# } #>
  824. </p>
  825. <# if ( data.errors ) { #>
  826. <ul class="bulk-action-errors hidden">
  827. <# _.each( data.errorMessages, function( errorMessage ) { #>
  828. <li>{{ errorMessage }}</li>
  829. <# } ); #>
  830. </ul>
  831. <# } #>
  832. </div>
  833. </script>
  834. <?php
  835. }
  836. /**
  837. * Prints the JavaScript templates for update and deletion rows in list tables.
  838. *
  839. * The update template takes one argument with four values:
  840. *
  841. * param {object} data {
  842. * Arguments for the update row
  843. *
  844. * @type string slug Plugin slug.
  845. * @type string plugin Plugin base name.
  846. * @type string colspan The number of table columns this row spans.
  847. * @type string content The row content.
  848. * }
  849. *
  850. * The delete template takes one argument with four values:
  851. *
  852. * param {object} data {
  853. * Arguments for the update row
  854. *
  855. * @type string slug Plugin slug.
  856. * @type string plugin Plugin base name.
  857. * @type string name Plugin name.
  858. * @type string colspan The number of table columns this row spans.
  859. * }
  860. *
  861. * @since 4.6.0
  862. */
  863. function wp_print_update_row_templates() {
  864. ?>
  865. <script id="tmpl-item-update-row" type="text/template">
  866. <tr class="plugin-update-tr update" id="{{ data.slug }}-update" data-slug="{{ data.slug }}" <# if ( data.plugin ) { #>data-plugin="{{ data.plugin }}"<# } #>>
  867. <td colspan="{{ data.colspan }}" class="plugin-update colspanchange">
  868. {{{ data.content }}}
  869. </td>
  870. </tr>
  871. </script>
  872. <script id="tmpl-item-deleted-row" type="text/template">
  873. <tr class="plugin-deleted-tr inactive deleted" id="{{ data.slug }}-deleted" data-slug="{{ data.slug }}" <# if ( data.plugin ) { #>data-plugin="{{ data.plugin }}"<# } #>>
  874. <td colspan="{{ data.colspan }}" class="plugin-update colspanchange">
  875. <# if ( data.plugin ) { #>
  876. <?php
  877. printf(
  878. /* translators: %s: Plugin name. */
  879. _x( '%s was successfully deleted.', 'plugin' ),
  880. '<strong>{{{ data.name }}}</strong>'
  881. );
  882. ?>
  883. <# } else { #>
  884. <?php
  885. printf(
  886. /* translators: %s: Theme name. */
  887. _x( '%s was successfully deleted.', 'theme' ),
  888. '<strong>{{{ data.name }}}</strong>'
  889. );
  890. ?>
  891. <# } #>
  892. </td>
  893. </tr>
  894. </script>
  895. <?php
  896. }
  897. /**
  898. * Displays a notice when the user is in recovery mode.
  899. *
  900. * @since 5.2.0
  901. */
  902. function wp_recovery_mode_nag() {
  903. if ( ! wp_is_recovery_mode() ) {
  904. return;
  905. }
  906. $url = wp_login_url();
  907. $url = add_query_arg( 'action', WP_Recovery_Mode::EXIT_ACTION, $url );
  908. $url = wp_nonce_url( $url, WP_Recovery_Mode::EXIT_ACTION );
  909. ?>
  910. <div class="notice notice-info">
  911. <p>
  912. <?php
  913. printf(
  914. /* translators: %s: Recovery Mode exit link. */
  915. __( '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>' ),
  916. esc_url( $url )
  917. );
  918. ?>
  919. </p>
  920. </div>
  921. <?php
  922. }
  923. /**
  924. * Checks whether auto-updates are enabled.
  925. *
  926. * @since 5.5.0
  927. *
  928. * @param string $type The type of update being checked: 'theme' or 'plugin'.
  929. * @return bool True if auto-updates are enabled for `$type`, false otherwise.
  930. */
  931. function wp_is_auto_update_enabled_for_type( $type ) {
  932. if ( ! class_exists( 'WP_Automatic_Updater' ) ) {
  933. require_once ABSPATH . 'wp-admin/includes/class-wp-automatic-updater.php';
  934. }
  935. $updater = new WP_Automatic_Updater();
  936. $enabled = ! $updater->is_disabled();
  937. switch ( $type ) {
  938. case 'plugin':
  939. /**
  940. * Filters whether plugins auto-update is enabled.
  941. *
  942. * @since 5.5.0
  943. *
  944. * @param bool $enabled True if plugins auto-update is enabled, false otherwise.
  945. */
  946. return apply_filters( 'plugins_auto_update_enabled', $enabled );
  947. case 'theme':
  948. /**
  949. * Filters whether themes auto-update is enabled.
  950. *
  951. * @since 5.5.0
  952. *
  953. * @param bool $enabled True if themes auto-update is enabled, false otherwise.
  954. */
  955. return apply_filters( 'themes_auto_update_enabled', $enabled );
  956. }
  957. return false;
  958. }
  959. /**
  960. * Checks whether auto-updates are forced for an item.
  961. *
  962. * @since 5.6.0
  963. *
  964. * @param string $type The type of update being checked: 'theme' or 'plugin'.
  965. * @param bool|null $update Whether to update. The value of null is internally used
  966. * to detect whether nothing has hooked into this filter.
  967. * @param object $item The update offer.
  968. * @return bool True if auto-updates are forced for `$item`, false otherwise.
  969. */
  970. function wp_is_auto_update_forced_for_item( $type, $update, $item ) {
  971. /** This filter is documented in wp-admin/includes/class-wp-automatic-updater.php */
  972. return apply_filters( "auto_update_{$type}", $update, $item );
  973. }
  974. /**
  975. * Determines the appropriate auto-update message to be displayed.
  976. *
  977. * @since 5.5.0
  978. *
  979. * @return string The update message to be shown.
  980. */
  981. function wp_get_auto_update_message() {
  982. $next_update_time = wp_next_scheduled( 'wp_version_check' );
  983. // Check if the event exists.
  984. if ( false === $next_update_time ) {
  985. $message = __( 'Automatic update not scheduled. There may be a problem with WP-Cron.' );
  986. } else {
  987. $time_to_next_update = human_time_diff( (int) $next_update_time );
  988. // See if cron is overdue.
  989. $overdue = ( time() - $next_update_time ) > 0;
  990. if ( $overdue ) {
  991. $message = sprintf(
  992. /* translators: %s: Duration that WP-Cron has been overdue. */
  993. __( 'Automatic update overdue by %s. There may be a problem with WP-Cron.' ),
  994. $time_to_next_update
  995. );
  996. } else {
  997. $message = sprintf(
  998. /* translators: %s: Time until the next update. */
  999. __( 'Automatic update scheduled in %s.' ),
  1000. $time_to_next_update
  1001. );
  1002. }
  1003. }
  1004. return $message;
  1005. }