[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/rest-api/endpoints/ -> class-wp-rest-themes-controller.php (source)

   1  <?php
   2  /**
   3   * REST API: WP_REST_Themes_Controller class
   4   *
   5   * @package WordPress
   6   * @subpackage REST_API
   7   * @since 5.0.0
   8   */
   9  
  10  /**
  11   * Core class used to manage themes via the REST API.
  12   *
  13   * @since 5.0.0
  14   *
  15   * @see WP_REST_Controller
  16   */
  17  class WP_REST_Themes_Controller extends WP_REST_Controller {
  18  
  19      /**
  20       * Matches theme's directory: `/themes/<subdirectory>/<theme>/` or `/themes/<theme>/`.
  21       * Excludes invalid directory name characters: `/:<>*?"|`.
  22       *
  23       * @since 5.9.0
  24       */
  25      const PATTERN = '[^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?';
  26  
  27      /**
  28       * Constructor.
  29       *
  30       * @since 5.0.0
  31       */
  32  	public function __construct() {
  33          $this->namespace = 'wp/v2';
  34          $this->rest_base = 'themes';
  35      }
  36  
  37      /**
  38       * Registers the routes for themes.
  39       *
  40       * @since 5.0.0
  41       *
  42       * @see register_rest_route()
  43       */
  44  	public function register_routes() {
  45          register_rest_route(
  46              $this->namespace,
  47              '/' . $this->rest_base,
  48              array(
  49                  array(
  50                      'methods'             => WP_REST_Server::READABLE,
  51                      'callback'            => array( $this, 'get_items' ),
  52                      'permission_callback' => array( $this, 'get_items_permissions_check' ),
  53                      'args'                => $this->get_collection_params(),
  54                  ),
  55                  'schema' => array( $this, 'get_item_schema' ),
  56              )
  57          );
  58  
  59          register_rest_route(
  60              $this->namespace,
  61              sprintf( '/%s/(?P<stylesheet>%s)', $this->rest_base, self::PATTERN ),
  62              array(
  63                  'args'   => array(
  64                      'stylesheet' => array(
  65                          'description'       => __( "The theme's stylesheet. This uniquely identifies the theme." ),
  66                          'type'              => 'string',
  67                          'sanitize_callback' => array( $this, '_sanitize_stylesheet_callback' ),
  68                      ),
  69                  ),
  70                  array(
  71                      'methods'             => WP_REST_Server::READABLE,
  72                      'callback'            => array( $this, 'get_item' ),
  73                      'permission_callback' => array( $this, 'get_item_permissions_check' ),
  74                  ),
  75                  'schema' => array( $this, 'get_public_item_schema' ),
  76              )
  77          );
  78      }
  79  
  80      /**
  81       * Sanitize the stylesheet to decode endpoint.
  82       *
  83       * @since 5.9.0
  84       *
  85       * @param string $stylesheet The stylesheet name.
  86       * @return string Sanitized stylesheet.
  87       */
  88  	public function _sanitize_stylesheet_callback( $stylesheet ) {
  89          return urldecode( $stylesheet );
  90      }
  91  
  92      /**
  93       * Checks if a given request has access to read the theme.
  94       *
  95       * @since 5.0.0
  96       *
  97       * @param WP_REST_Request $request Full details about the request.
  98       * @return true|WP_Error True if the request has read access for the item, otherwise WP_Error object.
  99       */
 100  	public function get_items_permissions_check( $request ) {
 101          if ( current_user_can( 'switch_themes' ) || current_user_can( 'manage_network_themes' ) ) {
 102              return true;
 103          }
 104  
 105          $registered = $this->get_collection_params();
 106          if ( isset( $registered['status'], $request['status'] ) && is_array( $request['status'] ) && array( 'active' ) === $request['status'] ) {
 107              return $this->check_read_active_theme_permission();
 108          }
 109  
 110          return new WP_Error(
 111              'rest_cannot_view_themes',
 112              __( 'Sorry, you are not allowed to view themes.' ),
 113              array( 'status' => rest_authorization_required_code() )
 114          );
 115      }
 116  
 117      /**
 118       * Checks if a given request has access to read the theme.
 119       *
 120       * @since 5.7.0
 121       *
 122       * @param WP_REST_Request $request Full details about the request.
 123       * @return true|WP_Error True if the request has read access for the item, otherwise WP_Error object.
 124       */
 125  	public function get_item_permissions_check( $request ) {
 126          if ( current_user_can( 'switch_themes' ) || current_user_can( 'manage_network_themes' ) ) {
 127              return true;
 128          }
 129  
 130          $wp_theme      = wp_get_theme( $request['stylesheet'] );
 131          $current_theme = wp_get_theme();
 132  
 133          if ( $this->is_same_theme( $wp_theme, $current_theme ) ) {
 134              return $this->check_read_active_theme_permission();
 135          }
 136  
 137          return new WP_Error(
 138              'rest_cannot_view_themes',
 139              __( 'Sorry, you are not allowed to view themes.' ),
 140              array( 'status' => rest_authorization_required_code() )
 141          );
 142      }
 143  
 144      /**
 145       * Checks if a theme can be read.
 146       *
 147       * @since 5.7.0
 148       *
 149       * @return true|WP_Error True if the theme can be read, WP_Error object otherwise.
 150       */
 151  	protected function check_read_active_theme_permission() {
 152          if ( current_user_can( 'edit_posts' ) ) {
 153              return true;
 154          }
 155  
 156          foreach ( get_post_types( array( 'show_in_rest' => true ), 'objects' ) as $post_type ) {
 157              if ( current_user_can( $post_type->cap->edit_posts ) ) {
 158                  return true;
 159              }
 160          }
 161  
 162          return new WP_Error(
 163              'rest_cannot_view_active_theme',
 164              __( 'Sorry, you are not allowed to view the active theme.' ),
 165              array( 'status' => rest_authorization_required_code() )
 166          );
 167      }
 168  
 169      /**
 170       * Retrieves a single theme.
 171       *
 172       * @since 5.7.0
 173       *
 174       * @param WP_REST_Request $request Full details about the request.
 175       * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
 176       */
 177  	public function get_item( $request ) {
 178          $wp_theme = wp_get_theme( $request['stylesheet'] );
 179          if ( ! $wp_theme->exists() ) {
 180              return new WP_Error(
 181                  'rest_theme_not_found',
 182                  __( 'Theme not found.' ),
 183                  array( 'status' => 404 )
 184              );
 185          }
 186          $data = $this->prepare_item_for_response( $wp_theme, $request );
 187  
 188          return rest_ensure_response( $data );
 189      }
 190  
 191      /**
 192       * Retrieves a collection of themes.
 193       *
 194       * @since 5.0.0
 195       *
 196       * @param WP_REST_Request $request Full details about the request.
 197       * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
 198       */
 199  	public function get_items( $request ) {
 200          $themes = array();
 201  
 202          $current_theme = wp_get_theme();
 203          $status        = $request['status'];
 204  
 205          if ( array( 'active' ) === $status ) {
 206              $prepared = $this->prepare_item_for_response( $current_theme, $request );
 207              $themes[] = $this->prepare_response_for_collection( $prepared );
 208          } else {
 209              foreach ( wp_get_themes() as $theme ) {
 210                  $theme_status = ( $this->is_same_theme( $theme, $current_theme ) ) ? 'active' : 'inactive';
 211                  if ( is_array( $status ) && ! in_array( $theme_status, $status, true ) ) {
 212                      continue;
 213                  }
 214  
 215                  $prepared = $this->prepare_item_for_response( $theme, $request );
 216                  $themes[] = $this->prepare_response_for_collection( $prepared );
 217              }
 218          }
 219  
 220          $response = rest_ensure_response( $themes );
 221  
 222          $response->header( 'X-WP-Total', count( $themes ) );
 223          $response->header( 'X-WP-TotalPages', 1 );
 224  
 225          return $response;
 226      }
 227  
 228      /**
 229       * Prepares a single theme output for response.
 230       *
 231       * @since 5.0.0
 232       * @since 5.9.0 Renamed `$theme` to `$item` to match parent class for PHP 8 named parameter support.
 233       * @since 6.6.0 Added `stylesheet_uri` and `template_uri` fields.
 234       *
 235       * @param WP_Theme        $item    Theme object.
 236       * @param WP_REST_Request $request Request object.
 237       * @return WP_REST_Response Response object.
 238       */
 239  	public function prepare_item_for_response( $item, $request ) {
 240          // Restores the more descriptive, specific name for use within this method.
 241          $theme = $item;
 242  
 243          $fields = $this->get_fields_for_response( $request );
 244          $data   = array();
 245  
 246          if ( rest_is_field_included( 'stylesheet', $fields ) ) {
 247              $data['stylesheet'] = $theme->get_stylesheet();
 248          }
 249  
 250          if ( rest_is_field_included( 'template', $fields ) ) {
 251              /**
 252               * Use the get_template() method, not the 'Template' header, for finding the template.
 253               * The 'Template' header is only good for what was written in the style.css, while
 254               * get_template() takes into account where WordPress actually located the theme and
 255               * whether it is actually valid.
 256               */
 257              $data['template'] = $theme->get_template();
 258          }
 259  
 260          $plain_field_mappings = array(
 261              'requires_php' => 'RequiresPHP',
 262              'requires_wp'  => 'RequiresWP',
 263              'textdomain'   => 'TextDomain',
 264              'version'      => 'Version',
 265          );
 266  
 267          foreach ( $plain_field_mappings as $field => $header ) {
 268              if ( rest_is_field_included( $field, $fields ) ) {
 269                  $data[ $field ] = $theme->get( $header );
 270              }
 271          }
 272  
 273          if ( rest_is_field_included( 'screenshot', $fields ) ) {
 274              // Using $theme->get_screenshot() with no args to get absolute URL.
 275              $data['screenshot'] = $theme->get_screenshot() ? $theme->get_screenshot() : '';
 276          }
 277  
 278          $rich_field_mappings = array(
 279              'author'      => 'Author',
 280              'author_uri'  => 'AuthorURI',
 281              'description' => 'Description',
 282              'name'        => 'Name',
 283              'tags'        => 'Tags',
 284              'theme_uri'   => 'ThemeURI',
 285          );
 286  
 287          foreach ( $rich_field_mappings as $field => $header ) {
 288              if ( rest_is_field_included( "{$field}.raw", $fields ) ) {
 289                  $data[ $field ]['raw'] = $theme->display( $header, false, true );
 290              }
 291  
 292              if ( rest_is_field_included( "{$field}.rendered", $fields ) ) {
 293                  $data[ $field ]['rendered'] = $theme->display( $header );
 294              }
 295          }
 296  
 297          $current_theme = wp_get_theme();
 298          if ( rest_is_field_included( 'status', $fields ) ) {
 299              $data['status'] = ( $this->is_same_theme( $theme, $current_theme ) ) ? 'active' : 'inactive';
 300          }
 301  
 302          if ( rest_is_field_included( 'theme_supports', $fields ) && $this->is_same_theme( $theme, $current_theme ) ) {
 303              foreach ( get_registered_theme_features() as $feature => $config ) {
 304                  if ( ! is_array( $config['show_in_rest'] ) ) {
 305                      continue;
 306                  }
 307  
 308                  $name = $config['show_in_rest']['name'];
 309  
 310                  if ( ! rest_is_field_included( "theme_supports.{$name}", $fields ) ) {
 311                      continue;
 312                  }
 313  
 314                  if ( ! current_theme_supports( $feature ) ) {
 315                      $data['theme_supports'][ $name ] = $config['show_in_rest']['schema']['default'];
 316                      continue;
 317                  }
 318  
 319                  $support = get_theme_support( $feature );
 320  
 321                  if ( isset( $config['show_in_rest']['prepare_callback'] ) ) {
 322                      $prepare = $config['show_in_rest']['prepare_callback'];
 323                  } else {
 324                      $prepare = array( $this, 'prepare_theme_support' );
 325                  }
 326  
 327                  $prepared = $prepare( $support, $config, $feature, $request );
 328  
 329                  if ( is_wp_error( $prepared ) ) {
 330                      continue;
 331                  }
 332  
 333                  $data['theme_supports'][ $name ] = $prepared;
 334              }
 335          }
 336  
 337          if ( rest_is_field_included( 'is_block_theme', $fields ) ) {
 338              $data['is_block_theme'] = $theme->is_block_theme();
 339          }
 340  
 341          if ( rest_is_field_included( 'stylesheet_uri', $fields ) ) {
 342              if ( $this->is_same_theme( $theme, $current_theme ) ) {
 343                  $data['stylesheet_uri'] = get_stylesheet_directory_uri();
 344              } else {
 345                  $data['stylesheet_uri'] = $theme->get_stylesheet_directory_uri();
 346              }
 347          }
 348  
 349          if ( rest_is_field_included( 'template_uri', $fields ) ) {
 350              if ( $this->is_same_theme( $theme, $current_theme ) ) {
 351                  $data['template_uri'] = get_template_directory_uri();
 352              } else {
 353                  $data['template_uri'] = $theme->get_template_directory_uri();
 354              }
 355          }
 356  
 357          if ( rest_is_field_included( 'default_template_types', $fields ) && $this->is_same_theme( $theme, $current_theme ) ) {
 358              $default_template_types = array();
 359              foreach ( get_default_block_template_types() as $slug => $template_type ) {
 360                  $template_type['slug']    = (string) $slug;
 361                  $default_template_types[] = $template_type;
 362              }
 363              $data['default_template_types'] = $default_template_types;
 364          }
 365  
 366          if ( rest_is_field_included( 'default_template_part_areas', $fields ) && $this->is_same_theme( $theme, $current_theme ) ) {
 367              $data['default_template_part_areas'] = get_allowed_block_template_part_areas();
 368          }
 369  
 370          $data = $this->add_additional_fields_to_object( $data, $request );
 371  
 372          // Wrap the data in a response object.
 373          $response = rest_ensure_response( $data );
 374  
 375          if ( rest_is_field_included( '_links', $fields ) || rest_is_field_included( '_embedded', $fields ) ) {
 376              $response->add_links( $this->prepare_links( $theme ) );
 377          }
 378  
 379          /**
 380           * Filters theme data returned from the REST API.
 381           *
 382           * @since 5.0.0
 383           *
 384           * @param WP_REST_Response $response The response object.
 385           * @param WP_Theme         $theme    Theme object used to create response.
 386           * @param WP_REST_Request  $request  Request object.
 387           */
 388          return apply_filters( 'rest_prepare_theme', $response, $theme, $request );
 389      }
 390  
 391      /**
 392       * Prepares links for the request.
 393       *
 394       * @since 5.7.0
 395       *
 396       * @param WP_Theme $theme Theme data.
 397       * @return array Links for the given block type.
 398       */
 399  	protected function prepare_links( $theme ) {
 400          $links = array(
 401              'self'       => array(
 402                  'href' => rest_url( sprintf( '%s/%s/%s', $this->namespace, $this->rest_base, $theme->get_stylesheet() ) ),
 403              ),
 404              'collection' => array(
 405                  'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ),
 406              ),
 407          );
 408  
 409          if ( $this->is_same_theme( $theme, wp_get_theme() ) ) {
 410              // This creates a record for the active theme if not existent.
 411              $id = WP_Theme_JSON_Resolver::get_user_global_styles_post_id();
 412          } else {
 413              $user_cpt = WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles( $theme );
 414              $id       = $user_cpt['ID'] ?? null;
 415          }
 416  
 417          if ( $id ) {
 418              $links['https://api.w.org/user-global-styles'] = array(
 419                  'href' => rest_url( 'wp/v2/global-styles/' . $id ),
 420              );
 421          }
 422  
 423          if ( $theme->is_block_theme() && $this->is_same_theme( $theme, wp_get_theme() ) ) {
 424              $links['https://api.w.org/export-theme'] = array(
 425                  'href'        => rest_url( 'wp-block-editor/v1/export' ),
 426                  'targetHints' => array(
 427                      'allow' => current_user_can( 'export' ) ? array( 'GET' ) : array(),
 428                  ),
 429              );
 430          }
 431  
 432          return $links;
 433      }
 434  
 435      /**
 436       * Helper function to compare two themes.
 437       *
 438       * @since 5.7.0
 439       *
 440       * @param WP_Theme $theme_a First theme to compare.
 441       * @param WP_Theme $theme_b Second theme to compare.
 442       * @return bool
 443       */
 444  	protected function is_same_theme( $theme_a, $theme_b ) {
 445          return $theme_a->get_stylesheet() === $theme_b->get_stylesheet();
 446      }
 447  
 448      /**
 449       * Prepares the theme support value for inclusion in the REST API response.
 450       *
 451       * @since 5.5.0
 452       *
 453       * @param mixed           $support The raw value from get_theme_support().
 454       * @param array           $args    The feature's registration args.
 455       * @param string          $feature The feature name.
 456       * @param WP_REST_Request $request The request object.
 457       * @return mixed The prepared support value.
 458       */
 459  	protected function prepare_theme_support( $support, $args, $feature, $request ) {
 460          $schema = $args['show_in_rest']['schema'];
 461  
 462          if ( 'boolean' === $schema['type'] ) {
 463              return true;
 464          }
 465  
 466          if ( is_array( $support ) && ! $args['variadic'] ) {
 467              $support = $support[0];
 468          }
 469  
 470          return rest_sanitize_value_from_schema( $support, $schema );
 471      }
 472  
 473      /**
 474       * Retrieves the theme's schema, conforming to JSON Schema.
 475       *
 476       * @since 5.0.0
 477       *
 478       * @return array Item schema data.
 479       */
 480  	public function get_item_schema() {
 481          if ( $this->schema ) {
 482              return $this->add_additional_fields_schema( $this->schema );
 483          }
 484  
 485          $schema = array(
 486              '$schema'    => 'http://json-schema.org/draft-04/schema#',
 487              'title'      => 'theme',
 488              'type'       => 'object',
 489              'properties' => array(
 490                  'stylesheet'                  => array(
 491                      'description' => __( 'The theme\'s stylesheet. This uniquely identifies the theme.' ),
 492                      'type'        => 'string',
 493                      'readonly'    => true,
 494                  ),
 495                  'stylesheet_uri'              => array(
 496                      'description' => __( 'The uri for the theme\'s stylesheet directory.' ),
 497                      'type'        => 'string',
 498                      'format'      => 'uri',
 499                      'readonly'    => true,
 500                  ),
 501                  'template'                    => array(
 502                      'description' => __( 'The theme\'s template. If this is a child theme, this refers to the parent theme, otherwise this is the same as the theme\'s stylesheet.' ),
 503                      'type'        => 'string',
 504                      'readonly'    => true,
 505                  ),
 506                  'template_uri'                => array(
 507                      'description' => __( 'The uri for the theme\'s template directory. If this is a child theme, this refers to the parent theme, otherwise this is the same as the theme\'s stylesheet directory.' ),
 508                      'type'        => 'string',
 509                      'format'      => 'uri',
 510                      'readonly'    => true,
 511                  ),
 512                  'author'                      => array(
 513                      'description' => __( 'The theme author.' ),
 514                      'type'        => 'object',
 515                      'readonly'    => true,
 516                      'properties'  => array(
 517                          'raw'      => array(
 518                              'description' => __( 'The theme author\'s name, as found in the theme header.' ),
 519                              'type'        => 'string',
 520                          ),
 521                          'rendered' => array(
 522                              'description' => __( 'HTML for the theme author, transformed for display.' ),
 523                              'type'        => 'string',
 524                          ),
 525                      ),
 526                  ),
 527                  'author_uri'                  => array(
 528                      'description' => __( 'The website of the theme author.' ),
 529                      'type'        => 'object',
 530                      'readonly'    => true,
 531                      'properties'  => array(
 532                          'raw'      => array(
 533                              'description' => __( 'The website of the theme author, as found in the theme header.' ),
 534                              'type'        => 'string',
 535                              'format'      => 'uri',
 536                          ),
 537                          'rendered' => array(
 538                              'description' => __( 'The website of the theme author, transformed for display.' ),
 539                              'type'        => 'string',
 540                              'format'      => 'uri',
 541                          ),
 542                      ),
 543                  ),
 544                  'description'                 => array(
 545                      'description' => __( 'A description of the theme.' ),
 546                      'type'        => 'object',
 547                      'readonly'    => true,
 548                      'properties'  => array(
 549                          'raw'      => array(
 550                              'description' => __( 'The theme description, as found in the theme header.' ),
 551                              'type'        => 'string',
 552                          ),
 553                          'rendered' => array(
 554                              'description' => __( 'The theme description, transformed for display.' ),
 555                              'type'        => 'string',
 556                          ),
 557                      ),
 558                  ),
 559                  'is_block_theme'              => array(
 560                      'description' => __( 'Whether the theme is a block-based theme.' ),
 561                      'type'        => 'boolean',
 562                      'readonly'    => true,
 563                  ),
 564                  'name'                        => array(
 565                      'description' => __( 'The name of the theme.' ),
 566                      'type'        => 'object',
 567                      'readonly'    => true,
 568                      'properties'  => array(
 569                          'raw'      => array(
 570                              'description' => __( 'The theme name, as found in the theme header.' ),
 571                              'type'        => 'string',
 572                          ),
 573                          'rendered' => array(
 574                              'description' => __( 'The theme name, transformed for display.' ),
 575                              'type'        => 'string',
 576                          ),
 577                      ),
 578                  ),
 579                  'requires_php'                => array(
 580                      'description' => __( 'The minimum PHP version required for the theme to work.' ),
 581                      'type'        => 'string',
 582                      'readonly'    => true,
 583                  ),
 584                  'requires_wp'                 => array(
 585                      'description' => __( 'The minimum WordPress version required for the theme to work.' ),
 586                      'type'        => 'string',
 587                      'readonly'    => true,
 588                  ),
 589                  'screenshot'                  => array(
 590                      'description' => __( 'The theme\'s screenshot URL.' ),
 591                      'type'        => 'string',
 592                      'format'      => 'uri',
 593                      'readonly'    => true,
 594                  ),
 595                  'tags'                        => array(
 596                      'description' => __( 'Tags indicating styles and features of the theme.' ),
 597                      'type'        => 'object',
 598                      'readonly'    => true,
 599                      'properties'  => array(
 600                          'raw'      => array(
 601                              'description' => __( 'The theme tags, as found in the theme header.' ),
 602                              'type'        => 'array',
 603                              'items'       => array(
 604                                  'type' => 'string',
 605                              ),
 606                          ),
 607                          'rendered' => array(
 608                              'description' => __( 'The theme tags, transformed for display.' ),
 609                              'type'        => 'string',
 610                          ),
 611                      ),
 612                  ),
 613                  'textdomain'                  => array(
 614                      'description' => __( 'The theme\'s text domain.' ),
 615                      'type'        => 'string',
 616                      'readonly'    => true,
 617                  ),
 618                  'theme_supports'              => array(
 619                      'description' => __( 'Features supported by this theme.' ),
 620                      'type'        => 'object',
 621                      'readonly'    => true,
 622                      'properties'  => array(),
 623                  ),
 624                  'theme_uri'                   => array(
 625                      'description' => __( 'The URI of the theme\'s webpage.' ),
 626                      'type'        => 'object',
 627                      'readonly'    => true,
 628                      'properties'  => array(
 629                          'raw'      => array(
 630                              'description' => __( 'The URI of the theme\'s webpage, as found in the theme header.' ),
 631                              'type'        => 'string',
 632                              'format'      => 'uri',
 633                          ),
 634                          'rendered' => array(
 635                              'description' => __( 'The URI of the theme\'s webpage, transformed for display.' ),
 636                              'type'        => 'string',
 637                              'format'      => 'uri',
 638                          ),
 639                      ),
 640                  ),
 641                  'version'                     => array(
 642                      'description' => __( 'The theme\'s current version.' ),
 643                      'type'        => 'string',
 644                      'readonly'    => true,
 645                  ),
 646                  'status'                      => array(
 647                      'description' => __( 'A named status for the theme.' ),
 648                      'type'        => 'string',
 649                      'enum'        => array( 'inactive', 'active' ),
 650                  ),
 651                  'default_template_types'      => array(
 652                      'description' => __( 'A list of default template types.' ),
 653                      'type'        => 'array',
 654                      'readonly'    => true,
 655                      'items'       => array(
 656                          'type'       => 'object',
 657                          'properties' => array(
 658                              'slug'        => array(
 659                                  'type' => 'string',
 660                              ),
 661                              'title'       => array(
 662                                  'type' => 'string',
 663                              ),
 664                              'description' => array(
 665                                  'type' => 'string',
 666                              ),
 667                          ),
 668                      ),
 669                  ),
 670                  'default_template_part_areas' => array(
 671                      'description' => __( 'A list of allowed area values for template parts.' ),
 672                      'type'        => 'array',
 673                      'readonly'    => true,
 674                      'items'       => array(
 675                          'type'       => 'object',
 676                          'properties' => array(
 677                              'area'        => array(
 678                                  'type' => 'string',
 679                              ),
 680                              'label'       => array(
 681                                  'type' => 'string',
 682                              ),
 683                              'description' => array(
 684                                  'type' => 'string',
 685                              ),
 686                              'icon'        => array(
 687                                  'type' => 'string',
 688                              ),
 689                              'area_tag'    => array(
 690                                  'type' => 'string',
 691                              ),
 692                          ),
 693                      ),
 694                  ),
 695              ),
 696          );
 697  
 698          foreach ( get_registered_theme_features() as $feature => $config ) {
 699              if ( ! is_array( $config['show_in_rest'] ) ) {
 700                  continue;
 701              }
 702  
 703              $name = $config['show_in_rest']['name'];
 704  
 705              $schema['properties']['theme_supports']['properties'][ $name ] = $config['show_in_rest']['schema'];
 706          }
 707  
 708          $this->schema = $schema;
 709  
 710          return $this->add_additional_fields_schema( $this->schema );
 711      }
 712  
 713      /**
 714       * Retrieves the search params for the themes collection.
 715       *
 716       * @since 5.0.0
 717       *
 718       * @return array Collection parameters.
 719       */
 720  	public function get_collection_params() {
 721          $query_params = array(
 722              'status' => array(
 723                  'description' => __( 'Limit result set to themes assigned one or more statuses.' ),
 724                  'type'        => 'array',
 725                  'items'       => array(
 726                      'enum' => array( 'active', 'inactive' ),
 727                      'type' => 'string',
 728                  ),
 729              ),
 730          );
 731  
 732          /**
 733           * Filters REST API collection parameters for the themes controller.
 734           *
 735           * @since 5.0.0
 736           *
 737           * @param array $query_params JSON Schema-formatted collection parameters.
 738           */
 739          return apply_filters( 'rest_themes_collection_params', $query_params );
 740      }
 741  
 742      /**
 743       * Sanitizes and validates the list of theme status.
 744       *
 745       * @since 5.0.0
 746       * @deprecated 5.7.0
 747       *
 748       * @param string|array    $statuses  One or more theme statuses.
 749       * @param WP_REST_Request $request   Full details about the request.
 750       * @param string          $parameter Additional parameter to pass to validation.
 751       * @return array|WP_Error A list of valid statuses, otherwise WP_Error object.
 752       */
 753  	public function sanitize_theme_status( $statuses, $request, $parameter ) {
 754          _deprecated_function( __METHOD__, '5.7.0' );
 755  
 756          $statuses = wp_parse_slug_list( $statuses );
 757  
 758          foreach ( $statuses as $status ) {
 759              $result = rest_validate_request_arg( $status, $request, $parameter );
 760  
 761              if ( is_wp_error( $result ) ) {
 762                  return $result;
 763              }
 764          }
 765  
 766          return $statuses;
 767      }
 768  }


Generated : Thu Sep 24 08:20:34 2026 Cross-referenced by PHPXref