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


Generated : Tue Jun 16 08:20:09 2026 Cross-referenced by PHPXref