[ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * WordPress Theme Administration API 4 * 5 * @package WordPress 6 * @subpackage Administration 7 */ 8 9 /** 10 * Remove a theme 11 * 12 * @since 2.8.0 13 * 14 * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. 15 * 16 * @param string $stylesheet Stylesheet of the theme to delete. 17 * @param string $redirect Redirect to page when complete. 18 * @return bool|null|WP_Error True on success, false if `$stylesheet` is empty, WP_Error on failure. 19 * Null if filesystem credentials are required to proceed. 20 */ 21 function delete_theme( $stylesheet, $redirect = '' ) { 22 global $wp_filesystem; 23 24 if ( empty( $stylesheet ) ) { 25 return false; 26 } 27 28 if ( empty( $redirect ) ) { 29 $redirect = wp_nonce_url( 'themes.php?action=delete&stylesheet=' . urlencode( $stylesheet ), 'delete-theme_' . $stylesheet ); 30 } 31 32 ob_start(); 33 $credentials = request_filesystem_credentials( $redirect ); 34 $data = ob_get_clean(); 35 36 if ( false === $credentials ) { 37 if ( ! empty( $data ) ) { 38 include_once ( ABSPATH . 'wp-admin/admin-header.php' ); 39 echo $data; 40 include ( ABSPATH . 'wp-admin/admin-footer.php' ); 41 exit; 42 } 43 return; 44 } 45 46 if ( ! WP_Filesystem( $credentials ) ) { 47 ob_start(); 48 request_filesystem_credentials( $redirect, '', true ); // Failed to connect, Error and request again. 49 $data = ob_get_clean(); 50 51 if ( ! empty( $data ) ) { 52 include_once ( ABSPATH . 'wp-admin/admin-header.php' ); 53 echo $data; 54 include ( ABSPATH . 'wp-admin/admin-footer.php' ); 55 exit; 56 } 57 return; 58 } 59 60 if ( ! is_object( $wp_filesystem ) ) { 61 return new WP_Error( 'fs_unavailable', __( 'Could not access filesystem.' ) ); 62 } 63 64 if ( is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->has_errors() ) { 65 return new WP_Error( 'fs_error', __( 'Filesystem error.' ), $wp_filesystem->errors ); 66 } 67 68 // Get the base plugin folder. 69 $themes_dir = $wp_filesystem->wp_themes_dir(); 70 if ( empty( $themes_dir ) ) { 71 return new WP_Error( 'fs_no_themes_dir', __( 'Unable to locate WordPress theme directory.' ) ); 72 } 73 74 $themes_dir = trailingslashit( $themes_dir ); 75 $theme_dir = trailingslashit( $themes_dir . $stylesheet ); 76 $deleted = $wp_filesystem->delete( $theme_dir, true ); 77 78 if ( ! $deleted ) { 79 return new WP_Error( 80 'could_not_remove_theme', 81 /* translators: %s: Theme name. */ 82 sprintf( __( 'Could not fully remove the theme %s.' ), $stylesheet ) 83 ); 84 } 85 86 $theme_translations = wp_get_installed_translations( 'themes' ); 87 88 // Remove language files, silently. 89 if ( ! empty( $theme_translations[ $stylesheet ] ) ) { 90 $translations = $theme_translations[ $stylesheet ]; 91 92 foreach ( $translations as $translation => $data ) { 93 $wp_filesystem->delete( WP_LANG_DIR . '/themes/' . $stylesheet . '-' . $translation . '.po' ); 94 $wp_filesystem->delete( WP_LANG_DIR . '/themes/' . $stylesheet . '-' . $translation . '.mo' ); 95 96 $json_translation_files = glob( WP_LANG_DIR . '/themes/' . $stylesheet . '-' . $translation . '-*.json' ); 97 if ( $json_translation_files ) { 98 array_map( array( $wp_filesystem, 'delete' ), $json_translation_files ); 99 } 100 } 101 } 102 103 // Remove the theme from allowed themes on the network. 104 if ( is_multisite() ) { 105 WP_Theme::network_disable_theme( $stylesheet ); 106 } 107 108 // Force refresh of theme update information. 109 delete_site_transient( 'update_themes' ); 110 111 return true; 112 } 113 114 /** 115 * Gets the page templates available in this theme. 116 * 117 * @since 1.5.0 118 * @since 4.7.0 Added the `$post_type` parameter. 119 * 120 * @param WP_Post|null $post Optional. The post being edited, provided for context. 121 * @param string $post_type Optional. Post type to get the templates for. Default 'page'. 122 * @return string[] Array of template file names keyed by the template header name. 123 */ 124 function get_page_templates( $post = null, $post_type = 'page' ) { 125 return array_flip( wp_get_theme()->get_page_templates( $post, $post_type ) ); 126 } 127 128 /** 129 * Tidies a filename for url display by the theme editor. 130 * 131 * @since 2.9.0 132 * @access private 133 * 134 * @param string $fullpath Full path to the theme file 135 * @param string $containingfolder Path of the theme parent folder 136 * @return string 137 */ 138 function _get_template_edit_filename( $fullpath, $containingfolder ) { 139 return str_replace( dirname( dirname( $containingfolder ) ), '', $fullpath ); 140 } 141 142 /** 143 * Check if there is an update for a theme available. 144 * 145 * Will display link, if there is an update available. 146 * 147 * @since 2.7.0 148 * @see get_theme_update_available() 149 * 150 * @param WP_Theme $theme Theme data object. 151 */ 152 function theme_update_available( $theme ) { 153 echo get_theme_update_available( $theme ); 154 } 155 156 /** 157 * Retrieve the update link if there is a theme update available. 158 * 159 * Will return a link if there is an update available. 160 * 161 * @since 3.8.0 162 * 163 * @staticvar object $themes_update 164 * 165 * @param WP_Theme $theme WP_Theme object. 166 * @return false|string HTML for the update link, or false if invalid info was passed. 167 */ 168 function get_theme_update_available( $theme ) { 169 static $themes_update = null; 170 171 if ( ! current_user_can( 'update_themes' ) ) { 172 return false; 173 } 174 175 if ( ! isset( $themes_update ) ) { 176 $themes_update = get_site_transient( 'update_themes' ); 177 } 178 179 if ( ! ( $theme instanceof WP_Theme ) ) { 180 return false; 181 } 182 183 $stylesheet = $theme->get_stylesheet(); 184 185 $html = ''; 186 187 if ( isset( $themes_update->response[ $stylesheet ] ) ) { 188 $update = $themes_update->response[ $stylesheet ]; 189 $theme_name = $theme->display( 'Name' ); 190 $details_url = add_query_arg( 191 array( 192 'TB_iframe' => 'true', 193 'width' => 1024, 194 'height' => 800, 195 ), 196 $update['url'] 197 ); //Theme browser inside WP? replace this, Also, theme preview JS will override this on the available list. 198 $update_url = wp_nonce_url( admin_url( 'update.php?action=upgrade-theme&theme=' . urlencode( $stylesheet ) ), 'upgrade-theme_' . $stylesheet ); 199 200 if ( ! is_multisite() ) { 201 if ( ! current_user_can( 'update_themes' ) ) { 202 $html = sprintf( 203 /* translators: 1: Theme name, 2: Theme details URL, 3: Additional link attributes, 4: Version number. */ 204 '<p><strong>' . __( 'There is a new version of %1$s available. <a href="%2$s" %3$s>View version %4$s details</a>.' ) . '</strong></p>', 205 $theme_name, 206 esc_url( $details_url ), 207 sprintf( 208 'class="thickbox open-plugin-details-modal" aria-label="%s"', 209 /* translators: 1: Theme name, 2: Version number. */ 210 esc_attr( sprintf( __( 'View %1$s version %2$s details' ), $theme_name, $update['new_version'] ) ) 211 ), 212 $update['new_version'] 213 ); 214 } elseif ( empty( $update['package'] ) ) { 215 $html = sprintf( 216 /* translators: 1: Theme name, 2: Theme details URL, 3: Additional link attributes, 4: Version number. */ 217 '<p><strong>' . __( '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>' ) . '</strong></p>', 218 $theme_name, 219 esc_url( $details_url ), 220 sprintf( 221 'class="thickbox open-plugin-details-modal" aria-label="%s"', 222 /* translators: 1: Theme name, 2: Version number. */ 223 esc_attr( sprintf( __( 'View %1$s version %2$s details' ), $theme_name, $update['new_version'] ) ) 224 ), 225 $update['new_version'] 226 ); 227 } else { 228 $html = sprintf( 229 /* translators: 1: Theme name, 2: Theme details URL, 3: Additional link attributes, 4: Version number, 5: Update URL, 6: Additional link attributes. */ 230 '<p><strong>' . __( '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>.' ) . '</strong></p>', 231 $theme_name, 232 esc_url( $details_url ), 233 sprintf( 234 'class="thickbox open-plugin-details-modal" aria-label="%s"', 235 /* translators: 1: Theme name, 2: Version number. */ 236 esc_attr( sprintf( __( 'View %1$s version %2$s details' ), $theme_name, $update['new_version'] ) ) 237 ), 238 $update['new_version'], 239 $update_url, 240 sprintf( 241 'aria-label="%s" id="update-theme" data-slug="%s"', 242 /* translators: %s: Theme name. */ 243 esc_attr( sprintf( __( 'Update %s now' ), $theme_name ) ), 244 $stylesheet 245 ) 246 ); 247 } 248 } 249 } 250 251 return $html; 252 } 253 254 /** 255 * Retrieve list of WordPress theme features (aka theme tags). 256 * 257 * @since 3.1.0 258 * 259 * @param bool $api Optional. Whether try to fetch tags from the WordPress.org API. Defaults to true. 260 * @return array Array of features keyed by category with translations keyed by slug. 261 */ 262 function get_theme_feature_list( $api = true ) { 263 // Hard-coded list is used if api not accessible. 264 $features = array( 265 266 __( 'Subject' ) => array( 267 'blog' => __( 'Blog' ), 268 'e-commerce' => __( 'E-Commerce' ), 269 'education' => __( 'Education' ), 270 'entertainment' => __( 'Entertainment' ), 271 'food-and-drink' => __( 'Food & Drink' ), 272 'holiday' => __( 'Holiday' ), 273 'news' => __( 'News' ), 274 'photography' => __( 'Photography' ), 275 'portfolio' => __( 'Portfolio' ), 276 ), 277 278 __( 'Features' ) => array( 279 'accessibility-ready' => __( 'Accessibility Ready' ), 280 'custom-background' => __( 'Custom Background' ), 281 'custom-colors' => __( 'Custom Colors' ), 282 'custom-header' => __( 'Custom Header' ), 283 'custom-logo' => __( 'Custom Logo' ), 284 'editor-style' => __( 'Editor Style' ), 285 'featured-image-header' => __( 'Featured Image Header' ), 286 'featured-images' => __( 'Featured Images' ), 287 'footer-widgets' => __( 'Footer Widgets' ), 288 'full-width-template' => __( 'Full Width Template' ), 289 'post-formats' => __( 'Post Formats' ), 290 'sticky-post' => __( 'Sticky Post' ), 291 'theme-options' => __( 'Theme Options' ), 292 ), 293 294 __( 'Layout' ) => array( 295 'grid-layout' => __( 'Grid Layout' ), 296 'one-column' => __( 'One Column' ), 297 'two-columns' => __( 'Two Columns' ), 298 'three-columns' => __( 'Three Columns' ), 299 'four-columns' => __( 'Four Columns' ), 300 'left-sidebar' => __( 'Left Sidebar' ), 301 'right-sidebar' => __( 'Right Sidebar' ), 302 ), 303 304 ); 305 306 if ( ! $api || ! current_user_can( 'install_themes' ) ) { 307 return $features; 308 } 309 310 $feature_list = get_site_transient( 'wporg_theme_feature_list' ); 311 if ( ! $feature_list ) { 312 set_site_transient( 'wporg_theme_feature_list', array(), 3 * HOUR_IN_SECONDS ); 313 } 314 315 if ( ! $feature_list ) { 316 $feature_list = themes_api( 'feature_list', array() ); 317 if ( is_wp_error( $feature_list ) ) { 318 return $features; 319 } 320 } 321 322 if ( ! $feature_list ) { 323 return $features; 324 } 325 326 set_site_transient( 'wporg_theme_feature_list', $feature_list, 3 * HOUR_IN_SECONDS ); 327 328 $category_translations = array( 329 'Layout' => __( 'Layout' ), 330 'Features' => __( 'Features' ), 331 'Subject' => __( 'Subject' ), 332 ); 333 334 // Loop over the wporg canonical list and apply translations 335 $wporg_features = array(); 336 foreach ( (array) $feature_list as $feature_category => $feature_items ) { 337 if ( isset( $category_translations[ $feature_category ] ) ) { 338 $feature_category = $category_translations[ $feature_category ]; 339 } 340 $wporg_features[ $feature_category ] = array(); 341 342 foreach ( $feature_items as $feature ) { 343 if ( isset( $features[ $feature_category ][ $feature ] ) ) { 344 $wporg_features[ $feature_category ][ $feature ] = $features[ $feature_category ][ $feature ]; 345 } else { 346 $wporg_features[ $feature_category ][ $feature ] = $feature; 347 } 348 } 349 } 350 351 return $wporg_features; 352 } 353 354 /** 355 * Retrieves theme installer pages from the WordPress.org Themes API. 356 * 357 * It is possible for a theme to override the Themes API result with three 358 * filters. Assume this is for themes, which can extend on the Theme Info to 359 * offer more choices. This is very powerful and must be used with care, when 360 * overriding the filters. 361 * 362 * The first filter, {@see 'themes_api_args'}, is for the args and gives the action 363 * as the second parameter. The hook for {@see 'themes_api_args'} must ensure that 364 * an object is returned. 365 * 366 * The second filter, {@see 'themes_api'}, allows a plugin to override the WordPress.org 367 * Theme API entirely. If `$action` is 'query_themes', 'theme_information', or 'feature_list', 368 * an object MUST be passed. If `$action` is 'hot_tags', an array should be passed. 369 * 370 * Finally, the third filter, {@see 'themes_api_result'}, makes it possible to filter the 371 * response object or array, depending on the `$action` type. 372 * 373 * Supported arguments per action: 374 * 375 * | Argument Name | 'query_themes' | 'theme_information' | 'hot_tags' | 'feature_list' | 376 * | -------------------| :------------: | :-----------------: | :--------: | :--------------: | 377 * | `$slug` | No | Yes | No | No | 378 * | `$per_page` | Yes | No | No | No | 379 * | `$page` | Yes | No | No | No | 380 * | `$number` | No | No | Yes | No | 381 * | `$search` | Yes | No | No | No | 382 * | `$tag` | Yes | No | No | No | 383 * | `$author` | Yes | No | No | No | 384 * | `$user` | Yes | No | No | No | 385 * | `$browse` | Yes | No | No | No | 386 * | `$locale` | Yes | Yes | No | No | 387 * | `$fields` | Yes | Yes | No | No | 388 * 389 * @since 2.8.0 390 * 391 * @param string $action API action to perform: 'query_themes', 'theme_information', 392 * 'hot_tags' or 'feature_list'. 393 * @param array|object $args { 394 * Optional. Array or object of arguments to serialize for the Themes API. 395 * 396 * @type string $slug The theme slug. Default empty. 397 * @type int $per_page Number of themes per page. Default 24. 398 * @type int $page Number of current page. Default 1. 399 * @type int $number Number of tags to be queried. 400 * @type string $search A search term. Default empty. 401 * @type string $tag Tag to filter themes. Default empty. 402 * @type string $author Username of an author to filter themes. Default empty. 403 * @type string $user Username to query for their favorites. Default empty. 404 * @type string $browse Browse view: 'featured', 'popular', 'updated', 'favorites'. 405 * @type string $locale Locale to provide context-sensitive results. Default is the value of get_locale(). 406 * @type array $fields { 407 * Array of fields which should or should not be returned. 408 * 409 * @type bool $description Whether to return the theme full description. Default false. 410 * @type bool $sections Whether to return the theme readme sections: description, installation, 411 * FAQ, screenshots, other notes, and changelog. Default false. 412 * @type bool $rating Whether to return the rating in percent and total number of ratings. 413 * Default false. 414 * @type bool $ratings Whether to return the number of rating for each star (1-5). Default false. 415 * @type bool $downloaded Whether to return the download count. Default false. 416 * @type bool $downloadlink Whether to return the download link for the package. Default false. 417 * @type bool $last_updated Whether to return the date of the last update. Default false. 418 * @type bool $tags Whether to return the assigned tags. Default false. 419 * @type bool $homepage Whether to return the theme homepage link. Default false. 420 * @type bool $screenshots Whether to return the screenshots. Default false. 421 * @type int $screenshot_count Number of screenshots to return. Default 1. 422 * @type bool $screenshot_url Whether to return the URL of the first screenshot. Default false. 423 * @type bool $photon_screenshots Whether to return the screenshots via Photon. Default false. 424 * @type bool $template Whether to return the slug of the parent theme. Default false. 425 * @type bool $parent Whether to return the slug, name and homepage of the parent theme. Default false. 426 * @type bool $versions Whether to return the list of all available versions. Default false. 427 * @type bool $theme_url Whether to return theme's URL. Default false. 428 * @type bool $extended_author Whether to return nicename or nicename and display name. Default false. 429 * } 430 * } 431 * @return object|array|WP_Error Response object or array on success, WP_Error on failure. See the 432 * {@link https://developer.wordpress.org/reference/functions/themes_api/ function reference article} 433 * for more information on the make-up of possible return objects depending on the value of `$action`. 434 */ 435 function themes_api( $action, $args = array() ) { 436 // include an unmodified $wp_version 437 include ( ABSPATH . WPINC . '/version.php' ); 438 439 if ( is_array( $args ) ) { 440 $args = (object) $args; 441 } 442 443 if ( 'query_themes' == $action ) { 444 if ( ! isset( $args->per_page ) ) { 445 $args->per_page = 24; 446 } 447 } 448 449 if ( ! isset( $args->locale ) ) { 450 $args->locale = get_user_locale(); 451 } 452 453 if ( ! isset( $args->wp_version ) ) { 454 $args->wp_version = substr( $wp_version, 0, 3 ); // X.y 455 } 456 457 /** 458 * Filters arguments used to query for installer pages from the WordPress.org Themes API. 459 * 460 * Important: An object MUST be returned to this filter. 461 * 462 * @since 2.8.0 463 * 464 * @param object $args Arguments used to query for installer pages from the WordPress.org Themes API. 465 * @param string $action Requested action. Likely values are 'theme_information', 466 * 'feature_list', or 'query_themes'. 467 */ 468 $args = apply_filters( 'themes_api_args', $args, $action ); 469 470 /** 471 * Filters whether to override the WordPress.org Themes API. 472 * 473 * Passing a non-false value will effectively short-circuit the WordPress.org API request. 474 * 475 * If `$action` is 'query_themes', 'theme_information', or 'feature_list', an object MUST 476 * be passed. If `$action` is 'hot_tags', an array should be passed. 477 * 478 * @since 2.8.0 479 * 480 * @param false|object|array $override Whether to override the WordPress.org Themes API. Default false. 481 * @param string $action Requested action. Likely values are 'theme_information', 482 * 'feature_list', or 'query_themes'. 483 * @param object $args Arguments used to query for installer pages from the Themes API. 484 */ 485 $res = apply_filters( 'themes_api', false, $action, $args ); 486 487 if ( ! $res ) { 488 $url = 'http://api.wordpress.org/themes/info/1.2/'; 489 $url = add_query_arg( 490 array( 491 'action' => $action, 492 'request' => $args, 493 ), 494 $url 495 ); 496 497 $http_url = $url; 498 $ssl = wp_http_supports( array( 'ssl' ) ); 499 if ( $ssl ) { 500 $url = set_url_scheme( $url, 'https' ); 501 } 502 503 $http_args = array( 504 'user-agent' => 'WordPress/' . $wp_version . '; ' . home_url( '/' ), 505 ); 506 $request = wp_remote_get( $url, $http_args ); 507 508 if ( $ssl && is_wp_error( $request ) ) { 509 if ( ! wp_doing_ajax() ) { 510 trigger_error( 511 sprintf( 512 /* translators: %s: Support forums URL. */ 513 __( '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>.' ), 514 __( 'https://wordpress.org/support/forums/' ) 515 ) . ' ' . __( '(WordPress could not establish a secure connection to WordPress.org. Please contact your server administrator.)' ), 516 headers_sent() || WP_DEBUG ? E_USER_WARNING : E_USER_NOTICE 517 ); 518 } 519 $request = wp_remote_get( $http_url, $http_args ); 520 } 521 522 if ( is_wp_error( $request ) ) { 523 $res = new WP_Error( 524 'themes_api_failed', 525 sprintf( 526 /* translators: %s: Support forums URL. */ 527 __( '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>.' ), 528 __( 'https://wordpress.org/support/forums/' ) 529 ), 530 $request->get_error_message() 531 ); 532 } else { 533 $res = json_decode( wp_remote_retrieve_body( $request ), true ); 534 if ( is_array( $res ) ) { 535 // Object casting is required in order to match the info/1.0 format. 536 $res = (object) $res; 537 } elseif ( null === $res ) { 538 $res = new WP_Error( 539 'themes_api_failed', 540 sprintf( 541 /* translators: %s: Support forums URL. */ 542 __( '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>.' ), 543 __( 'https://wordpress.org/support/forums/' ) 544 ), 545 wp_remote_retrieve_body( $request ) 546 ); 547 } 548 549 if ( isset( $res->error ) ) { 550 $res = new WP_Error( 'themes_api_failed', $res->error ); 551 } 552 } 553 554 // Back-compat for info/1.2 API, upgrade the theme objects in query_themes to objects. 555 if ( 'query_themes' == $action ) { 556 foreach ( $res->themes as $i => $theme ) { 557 $res->themes[ $i ] = (object) $theme; 558 } 559 } 560 // Back-compat for info/1.2 API, downgrade the feature_list result back to an array. 561 if ( 'feature_list' == $action ) { 562 $res = (array) $res; 563 } 564 } 565 566 /** 567 * Filters the returned WordPress.org Themes API response. 568 * 569 * @since 2.8.0 570 * 571 * @param array|object|WP_Error $res WordPress.org Themes API response. 572 * @param string $action Requested action. Likely values are 'theme_information', 573 * 'feature_list', or 'query_themes'. 574 * @param object $args Arguments used to query for installer pages from the WordPress.org Themes API. 575 */ 576 return apply_filters( 'themes_api_result', $res, $action, $args ); 577 } 578 579 /** 580 * Prepare themes for JavaScript. 581 * 582 * @since 3.8.0 583 * 584 * @param WP_Theme[] $themes Optional. Array of theme objects to prepare. 585 * Defaults to all allowed themes. 586 * 587 * @return array An associative array of theme data, sorted by name. 588 */ 589 function wp_prepare_themes_for_js( $themes = null ) { 590 $current_theme = get_stylesheet(); 591 592 /** 593 * Filters theme data before it is prepared for JavaScript. 594 * 595 * Passing a non-empty array will result in wp_prepare_themes_for_js() returning 596 * early with that value instead. 597 * 598 * @since 4.2.0 599 * 600 * @param array $prepared_themes An associative array of theme data. Default empty array. 601 * @param WP_Theme[]|null $themes An array of theme objects to prepare, if any. 602 * @param string $current_theme The current theme slug. 603 */ 604 $prepared_themes = (array) apply_filters( 'pre_prepare_themes_for_js', array(), $themes, $current_theme ); 605 606 if ( ! empty( $prepared_themes ) ) { 607 return $prepared_themes; 608 } 609 610 // Make sure the current theme is listed first. 611 $prepared_themes[ $current_theme ] = array(); 612 613 if ( null === $themes ) { 614 $themes = wp_get_themes( array( 'allowed' => true ) ); 615 if ( ! isset( $themes[ $current_theme ] ) ) { 616 $themes[ $current_theme ] = wp_get_theme(); 617 } 618 } 619 620 $updates = array(); 621 if ( current_user_can( 'update_themes' ) ) { 622 $updates_transient = get_site_transient( 'update_themes' ); 623 if ( isset( $updates_transient->response ) ) { 624 $updates = $updates_transient->response; 625 } 626 } 627 628 WP_Theme::sort_by_name( $themes ); 629 630 $parents = array(); 631 632 foreach ( $themes as $theme ) { 633 $slug = $theme->get_stylesheet(); 634 $encoded_slug = urlencode( $slug ); 635 636 $parent = false; 637 if ( $theme->parent() ) { 638 $parent = $theme->parent(); 639 $parents[ $slug ] = $parent->get_stylesheet(); 640 $parent = $parent->display( 'Name' ); 641 } 642 643 $customize_action = null; 644 if ( current_user_can( 'edit_theme_options' ) && current_user_can( 'customize' ) ) { 645 $customize_action = esc_url( 646 add_query_arg( 647 array( 648 'return' => urlencode( esc_url_raw( remove_query_arg( wp_removable_query_args(), wp_unslash( $_SERVER['REQUEST_URI'] ) ) ) ), 649 ), 650 wp_customize_url( $slug ) 651 ) 652 ); 653 } 654 655 $prepared_themes[ $slug ] = array( 656 'id' => $slug, 657 'name' => $theme->display( 'Name' ), 658 'screenshot' => array( $theme->get_screenshot() ), // @todo multiple 659 'description' => $theme->display( 'Description' ), 660 'author' => $theme->display( 'Author', false, true ), 661 'authorAndUri' => $theme->display( 'Author' ), 662 'version' => $theme->display( 'Version' ), 663 'tags' => $theme->display( 'Tags' ), 664 'parent' => $parent, 665 'active' => $slug === $current_theme, 666 'hasUpdate' => isset( $updates[ $slug ] ), 667 'hasPackage' => isset( $updates[ $slug ] ) && ! empty( $updates[ $slug ]['package'] ), 668 'update' => get_theme_update_available( $theme ), 669 'actions' => array( 670 'activate' => current_user_can( 'switch_themes' ) ? wp_nonce_url( admin_url( 'themes.php?action=activate&stylesheet=' . $encoded_slug ), 'switch-theme_' . $slug ) : null, 671 'customize' => $customize_action, 672 'delete' => current_user_can( 'delete_themes' ) ? wp_nonce_url( admin_url( 'themes.php?action=delete&stylesheet=' . $encoded_slug ), 'delete-theme_' . $slug ) : null, 673 ), 674 ); 675 } 676 677 // Remove 'delete' action if theme has an active child 678 if ( ! empty( $parents ) && array_key_exists( $current_theme, $parents ) ) { 679 unset( $prepared_themes[ $parents[ $current_theme ] ]['actions']['delete'] ); 680 } 681 682 /** 683 * Filters the themes prepared for JavaScript, for themes.php. 684 * 685 * Could be useful for changing the order, which is by name by default. 686 * 687 * @since 3.8.0 688 * 689 * @param array $prepared_themes Array of theme data. 690 */ 691 $prepared_themes = apply_filters( 'wp_prepare_themes_for_js', $prepared_themes ); 692 $prepared_themes = array_values( $prepared_themes ); 693 return array_filter( $prepared_themes ); 694 } 695 696 /** 697 * Print JS templates for the theme-browsing UI in the Customizer. 698 * 699 * @since 4.2.0 700 */ 701 function customize_themes_print_templates() { 702 ?> 703 <script type="text/html" id="tmpl-customize-themes-details-view"> 704 <div class="theme-backdrop"></div> 705 <div class="theme-wrap wp-clearfix" role="document"> 706 <div class="theme-header"> 707 <button type="button" class="left dashicons dashicons-no"><span class="screen-reader-text"><?php _e( 'Show previous theme' ); ?></span></button> 708 <button type="button" class="right dashicons dashicons-no"><span class="screen-reader-text"><?php _e( 'Show next theme' ); ?></span></button> 709 <button type="button" class="close dashicons dashicons-no"><span class="screen-reader-text"><?php _e( 'Close details dialog' ); ?></span></button> 710 </div> 711 <div class="theme-about wp-clearfix"> 712 <div class="theme-screenshots"> 713 <# if ( data.screenshot && data.screenshot[0] ) { #> 714 <div class="screenshot"><img src="{{ data.screenshot[0] }}" alt="" /></div> 715 <# } else { #> 716 <div class="screenshot blank"></div> 717 <# } #> 718 </div> 719 720 <div class="theme-info"> 721 <# if ( data.active ) { #> 722 <span class="current-label"><?php _e( 'Current Theme' ); ?></span> 723 <# } #> 724 <h2 class="theme-name">{{{ data.name }}}<span class="theme-version"> 725 <?php 726 /* translators: %s: Theme version. */ 727 printf( __( 'Version: %s' ), '{{ data.version }}' ); 728 ?> 729 </span></h2> 730 <h3 class="theme-author"> 731 <?php 732 /* translators: %s: Theme author link. */ 733 printf( __( 'By %s' ), '{{{ data.authorAndUri }}}' ); 734 ?> 735 </h3> 736 737 <# if ( data.stars && 0 != data.num_ratings ) { #> 738 <div class="theme-rating"> 739 {{{ data.stars }}} 740 <a class="num-ratings" target="_blank" href="{{ data.reviews_url }}"> 741 <?php 742 printf( 743 '%1$s <span class="screen-reader-text">%2$s</span>', 744 /* translators: %s: Number of ratings. */ 745 sprintf( __( '(%s ratings)' ), '{{ data.num_ratings }}' ), 746 /* translators: Accessibility text. */ 747 __( '(opens in a new tab)' ) 748 ); 749 ?> 750 </a> 751 </div> 752 <# } #> 753 754 <# if ( data.hasUpdate ) { #> 755 <div class="notice notice-warning notice-alt notice-large" data-slug="{{ data.id }}"> 756 <h3 class="notice-title"><?php _e( 'Update Available' ); ?></h3> 757 {{{ data.update }}} 758 </div> 759 <# } #> 760 761 <# if ( data.parent ) { #> 762 <p class="parent-theme"> 763 <?php 764 printf( 765 /* translators: %s: Theme name. */ 766 __( 'This is a child theme of %s.' ), 767 '<strong>{{{ data.parent }}}</strong>' 768 ); 769 ?> 770 </p> 771 <# } #> 772 773 <p class="theme-description">{{{ data.description }}}</p> 774 775 <# if ( data.tags ) { #> 776 <p class="theme-tags"><span><?php _e( 'Tags:' ); ?></span> {{{ data.tags }}}</p> 777 <# } #> 778 </div> 779 </div> 780 781 <div class="theme-actions"> 782 <# if ( data.active ) { #> 783 <button type="button" class="button button-primary customize-theme"><?php _e( 'Customize' ); ?></button> 784 <# } else if ( 'installed' === data.type ) { #> 785 <?php if ( current_user_can( 'delete_themes' ) ) { ?> 786 <# if ( data.actions && data.actions['delete'] ) { #> 787 <a href="{{{ data.actions['delete'] }}}" data-slug="{{ data.id }}" class="button button-secondary delete-theme"><?php _e( 'Delete' ); ?></a> 788 <# } #> 789 <?php } ?> 790 <button type="button" class="button button-primary preview-theme" data-slug="{{ data.id }}"><?php _e( 'Live Preview' ); ?></button> 791 <# } else { #> 792 <button type="button" class="button theme-install" data-slug="{{ data.id }}"><?php _e( 'Install' ); ?></button> 793 <button type="button" class="button button-primary theme-install preview" data-slug="{{ data.id }}"><?php _e( 'Install & Preview' ); ?></button> 794 <# } #> 795 </div> 796 </div> 797 </script> 798 <?php 799 } 800 801 /** 802 * Determines whether a theme is technically active but was paused while 803 * loading. 804 * 805 * For more information on this and similar theme functions, check out 806 * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/ 807 * Conditional Tags} article in the Theme Developer Handbook. 808 * 809 * @since 5.2.0 810 * 811 * @param string $theme Path to the theme directory relative to the themes directory. 812 * @return bool True, if in the list of paused themes. False, not in the list. 813 */ 814 function is_theme_paused( $theme ) { 815 if ( ! isset( $GLOBALS['_paused_themes'] ) ) { 816 return false; 817 } 818 819 if ( get_stylesheet() !== $theme && get_template() !== $theme ) { 820 return false; 821 } 822 823 return array_key_exists( $theme, $GLOBALS['_paused_themes'] ); 824 } 825 826 /** 827 * Gets the error that was recorded for a paused theme. 828 * 829 * @since 5.2.0 830 * 831 * @param string $theme Path to the theme directory relative to the themes 832 * directory. 833 * @return array|false Array of error information as it was returned by 834 * `error_get_last()`, or false if none was recorded. 835 */ 836 function wp_get_theme_error( $theme ) { 837 if ( ! isset( $GLOBALS['_paused_themes'] ) ) { 838 return false; 839 } 840 841 if ( ! array_key_exists( $theme, $GLOBALS['_paused_themes'] ) ) { 842 return false; 843 } 844 845 return $GLOBALS['_paused_themes'][ $theme ]; 846 } 847 848 /** 849 * Tries to resume a single theme. 850 * 851 * If a redirect was provided and a functions.php file was found, we first ensure that 852 * functions.php file does not throw fatal errors anymore. 853 * 854 * The way it works is by setting the redirection to the error before trying to 855 * include the file. If the theme fails, then the redirection will not be overwritten 856 * with the success message and the theme will not be resumed. 857 * 858 * @since 5.2.0 859 * 860 * @param string $theme Single theme to resume. 861 * @param string $redirect Optional. URL to redirect to. Default empty string. 862 * @return bool|WP_Error True on success, false if `$theme` was not paused, 863 * `WP_Error` on failure. 864 */ 865 function resume_theme( $theme, $redirect = '' ) { 866 list( $extension ) = explode( '/', $theme ); 867 868 /* 869 * We'll override this later if the theme could be resumed without 870 * creating a fatal error. 871 */ 872 if ( ! empty( $redirect ) ) { 873 $functions_path = ''; 874 if ( strpos( STYLESHEETPATH, $extension ) ) { 875 $functions_path = STYLESHEETPATH . '/functions.php'; 876 } elseif ( strpos( TEMPLATEPATH, $extension ) ) { 877 $functions_path = TEMPLATEPATH . '/functions.php'; 878 } 879 880 if ( ! empty( $functions_path ) ) { 881 wp_redirect( 882 add_query_arg( 883 '_error_nonce', 884 wp_create_nonce( 'theme-resume-error_' . $theme ), 885 $redirect 886 ) 887 ); 888 889 // Load the theme's functions.php to test whether it throws a fatal error. 890 ob_start(); 891 if ( ! defined( 'WP_SANDBOX_SCRAPING' ) ) { 892 define( 'WP_SANDBOX_SCRAPING', true ); 893 } 894 include $functions_path; 895 ob_clean(); 896 } 897 } 898 899 $result = wp_paused_themes()->delete( $extension ); 900 901 if ( ! $result ) { 902 return new WP_Error( 903 'could_not_resume_theme', 904 __( 'Could not resume the theme.' ) 905 ); 906 } 907 908 return true; 909 } 910 911 /** 912 * Renders an admin notice in case some themes have been paused due to errors. 913 * 914 * @since 5.2.0 915 */ 916 function paused_themes_notice() { 917 if ( 'themes.php' === $GLOBALS['pagenow'] ) { 918 return; 919 } 920 921 if ( ! current_user_can( 'resume_themes' ) ) { 922 return; 923 } 924 925 if ( ! isset( $GLOBALS['_paused_themes'] ) || empty( $GLOBALS['_paused_themes'] ) ) { 926 return; 927 } 928 929 printf( 930 '<div class="notice notice-error"><p><strong>%s</strong><br>%s</p><p><a href="%s">%s</a></p></div>', 931 __( 'One or more themes failed to load properly.' ), 932 __( 'You can find more details and make changes on the Themes screen.' ), 933 esc_url( admin_url( 'themes.php' ) ), 934 __( 'Go to the Themes screen' ) 935 ); 936 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Sat Nov 23 20:47:33 2019 | Cross-referenced by PHPXref 0.7 |