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 */ 16function 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 */ 36function 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 */ 87function 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 */ 122function 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’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 */ 170function 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 */ 185function 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 */ 206function 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 */ 228function 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 */ 283function 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 */ 332function 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 */ 377function 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 */ 394function 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 */ 417function 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 . '§ion=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’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 */ 598function 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 */ 617function 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 */ 641function 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’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’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’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 */ 815function 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 */ 871function 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 */ 963function 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 */ 1003function 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 */ 1035function 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 */ 1078function 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 */ 1090function 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} 1119