Diff: STRATO-apps/wordpress_03/app/wp-includes/rest-api/endpoints/class-wp-rest-themes-controller.php

Keine Baseline-Datei – Diff nur gegen leer.
Zur Liste
1 -
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 + $active_themes = wp_get_themes();
201 + $current_theme = wp_get_theme();
202 + $status = $request['status'];
203 +
204 + foreach ( $active_themes as $theme ) {
205 + $theme_status = ( $this->is_same_theme( $theme, $current_theme ) ) ? 'active' : 'inactive';
206 + if ( is_array( $status ) && ! in_array( $theme_status, $status, true ) ) {
207 + continue;
208 + }
209 +
210 + $prepared = $this->prepare_item_for_response( $theme, $request );
211 + $themes[] = $this->prepare_response_for_collection( $prepared );
212 + }
213 +
214 + $response = rest_ensure_response( $themes );
215 +
216 + $response->header( 'X-WP-Total', count( $themes ) );
217 + $response->header( 'X-WP-TotalPages', 1 );
218 +
219 + return $response;
220 + }
221 +
222 + /**
223 + * Prepares a single theme output for response.
224 + *
225 + * @since 5.0.0
226 + * @since 5.9.0 Renamed `$theme` to `$item` to match parent class for PHP 8 named parameter support.
227 + * @since 6.6.0 Added `stylesheet_uri` and `template_uri` fields.
228 + *
229 + * @param WP_Theme $item Theme object.
230 + * @param WP_REST_Request $request Request object.
231 + * @return WP_REST_Response Response object.
232 + */
233 + public function prepare_item_for_response( $item, $request ) {
234 + // Restores the more descriptive, specific name for use within this method.
235 + $theme = $item;
236 +
237 + $fields = $this->get_fields_for_response( $request );
238 + $data = array();
239 +
240 + if ( rest_is_field_included( 'stylesheet', $fields ) ) {
241 + $data['stylesheet'] = $theme->get_stylesheet();
242 + }
243 +
244 + if ( rest_is_field_included( 'template', $fields ) ) {
245 + /**
246 + * Use the get_template() method, not the 'Template' header, for finding the template.
247 + * The 'Template' header is only good for what was written in the style.css, while
248 + * get_template() takes into account where WordPress actually located the theme and
249 + * whether it is actually valid.
250 + */
251 + $data['template'] = $theme->get_template();
252 + }
253 +
254 + $plain_field_mappings = array(
255 + 'requires_php' => 'RequiresPHP',
256 + 'requires_wp' => 'RequiresWP',
257 + 'textdomain' => 'TextDomain',
258 + 'version' => 'Version',
259 + );
260 +
261 + foreach ( $plain_field_mappings as $field => $header ) {
262 + if ( rest_is_field_included( $field, $fields ) ) {
263 + $data[ $field ] = $theme->get( $header );
264 + }
265 + }
266 +
267 + if ( rest_is_field_included( 'screenshot', $fields ) ) {
268 + // Using $theme->get_screenshot() with no args to get absolute URL.
269 + $data['screenshot'] = $theme->get_screenshot() ? $theme->get_screenshot() : '';
270 + }
271 +
272 + $rich_field_mappings = array(
273 + 'author' => 'Author',
274 + 'author_uri' => 'AuthorURI',
275 + 'description' => 'Description',
276 + 'name' => 'Name',
277 + 'tags' => 'Tags',
278 + 'theme_uri' => 'ThemeURI',
279 + );
280 +
281 + foreach ( $rich_field_mappings as $field => $header ) {
282 + if ( rest_is_field_included( "{$field}.raw", $fields ) ) {
283 + $data[ $field ]['raw'] = $theme->display( $header, false, true );
284 + }
285 +
286 + if ( rest_is_field_included( "{$field}.rendered", $fields ) ) {
287 + $data[ $field ]['rendered'] = $theme->display( $header );
288 + }
289 + }
290 +
291 + $current_theme = wp_get_theme();
292 + if ( rest_is_field_included( 'status', $fields ) ) {
293 + $data['status'] = ( $this->is_same_theme( $theme, $current_theme ) ) ? 'active' : 'inactive';
294 + }
295 +
296 + if ( rest_is_field_included( 'theme_supports', $fields ) && $this->is_same_theme( $theme, $current_theme ) ) {
297 + foreach ( get_registered_theme_features() as $feature => $config ) {
298 + if ( ! is_array( $config['show_in_rest'] ) ) {
299 + continue;
300 + }
301 +
302 + $name = $config['show_in_rest']['name'];
303 +
304 + if ( ! rest_is_field_included( "theme_supports.{$name}", $fields ) ) {
305 + continue;
306 + }
307 +
308 + if ( ! current_theme_supports( $feature ) ) {
309 + $data['theme_supports'][ $name ] = $config['show_in_rest']['schema']['default'];
310 + continue;
311 + }
312 +
313 + $support = get_theme_support( $feature );
314 +
315 + if ( isset( $config['show_in_rest']['prepare_callback'] ) ) {
316 + $prepare = $config['show_in_rest']['prepare_callback'];
317 + } else {
318 + $prepare = array( $this, 'prepare_theme_support' );
319 + }
320 +
321 + $prepared = $prepare( $support, $config, $feature, $request );
322 +
323 + if ( is_wp_error( $prepared ) ) {
324 + continue;
325 + }
326 +
327 + $data['theme_supports'][ $name ] = $prepared;
328 + }
329 + }
330 +
331 + if ( rest_is_field_included( 'is_block_theme', $fields ) ) {
332 + $data['is_block_theme'] = $theme->is_block_theme();
333 + }
334 +
335 + if ( rest_is_field_included( 'stylesheet_uri', $fields ) ) {
336 + if ( $this->is_same_theme( $theme, $current_theme ) ) {
337 + $data['stylesheet_uri'] = get_stylesheet_directory_uri();
338 + } else {
339 + $data['stylesheet_uri'] = $theme->get_stylesheet_directory_uri();
340 + }
341 + }
342 +
343 + if ( rest_is_field_included( 'template_uri', $fields ) ) {
344 + if ( $this->is_same_theme( $theme, $current_theme ) ) {
345 + $data['template_uri'] = get_template_directory_uri();
346 + } else {
347 + $data['template_uri'] = $theme->get_template_directory_uri();
348 + }
349 + }
350 +
351 + if ( rest_is_field_included( 'default_template_types', $fields ) && $this->is_same_theme( $theme, $current_theme ) ) {
352 + $default_template_types = array();
353 + foreach ( get_default_block_template_types() as $slug => $template_type ) {
354 + $template_type['slug'] = (string) $slug;
355 + $default_template_types[] = $template_type;
356 + }
357 + $data['default_template_types'] = $default_template_types;
358 + }
359 +
360 + if ( rest_is_field_included( 'default_template_part_areas', $fields ) && $this->is_same_theme( $theme, $current_theme ) ) {
361 + $data['default_template_part_areas'] = get_allowed_block_template_part_areas();
362 + }
363 +
364 + $data = $this->add_additional_fields_to_object( $data, $request );
365 +
366 + // Wrap the data in a response object.
367 + $response = rest_ensure_response( $data );
368 +
369 + if ( rest_is_field_included( '_links', $fields ) || rest_is_field_included( '_embedded', $fields ) ) {
370 + $response->add_links( $this->prepare_links( $theme ) );
371 + }
372 +
373 + /**
374 + * Filters theme data returned from the REST API.
375 + *
376 + * @since 5.0.0
377 + *
378 + * @param WP_REST_Response $response The response object.
379 + * @param WP_Theme $theme Theme object used to create response.
380 + * @param WP_REST_Request $request Request object.
381 + */
382 + return apply_filters( 'rest_prepare_theme', $response, $theme, $request );
383 + }
384 +
385 + /**
386 + * Prepares links for the request.
387 + *
388 + * @since 5.7.0
389 + *
390 + * @param WP_Theme $theme Theme data.
391 + * @return array Links for the given block type.
392 + */
393 + protected function prepare_links( $theme ) {
394 + $links = array(
395 + 'self' => array(
396 + 'href' => rest_url( sprintf( '%s/%s/%s', $this->namespace, $this->rest_base, $theme->get_stylesheet() ) ),
397 + ),
398 + 'collection' => array(
399 + 'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ),
400 + ),
401 + );
402 +
403 + if ( $this->is_same_theme( $theme, wp_get_theme() ) ) {
404 + // This creates a record for the active theme if not existent.
405 + $id = WP_Theme_JSON_Resolver::get_user_global_styles_post_id();
406 + } else {
407 + $user_cpt = WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles( $theme );
408 + $id = isset( $user_cpt['ID'] ) ? $user_cpt['ID'] : null;
409 + }
410 +
411 + if ( $id ) {
412 + $links['https://api.w.org/user-global-styles'] = array(
413 + 'href' => rest_url( 'wp/v2/global-styles/' . $id ),
414 + );
415 + }
416 +
417 + if ( $theme->is_block_theme() && $this->is_same_theme( $theme, wp_get_theme() ) ) {
418 + $links['https://api.w.org/export-theme'] = array(
419 + 'href' => rest_url( 'wp-block-editor/v1/export' ),
420 + 'targetHints' => array(
421 + 'allow' => current_user_can( 'export' ) ? array( 'GET' ) : array(),
422 + ),
423 + );
424 + }
425 +
426 + return $links;
427 + }
428 +
429 + /**
430 + * Helper function to compare two themes.
431 + *
432 + * @since 5.7.0
433 + *
434 + * @param WP_Theme $theme_a First theme to compare.
435 + * @param WP_Theme $theme_b Second theme to compare.
436 + * @return bool
437 + */
438 + protected function is_same_theme( $theme_a, $theme_b ) {
439 + return $theme_a->get_stylesheet() === $theme_b->get_stylesheet();
440 + }
441 +
442 + /**
443 + * Prepares the theme support value for inclusion in the REST API response.
444 + *
445 + * @since 5.5.0
446 + *
447 + * @param mixed $support The raw value from get_theme_support().
448 + * @param array $args The feature's registration args.
449 + * @param string $feature The feature name.
450 + * @param WP_REST_Request $request The request object.
451 + * @return mixed The prepared support value.
452 + */
453 + protected function prepare_theme_support( $support, $args, $feature, $request ) {
454 + $schema = $args['show_in_rest']['schema'];
455 +
456 + if ( 'boolean' === $schema['type'] ) {
457 + return true;
458 + }
459 +
460 + if ( is_array( $support ) && ! $args['variadic'] ) {
461 + $support = $support[0];
462 + }
463 +
464 + return rest_sanitize_value_from_schema( $support, $schema );
465 + }
466 +
467 + /**
468 + * Retrieves the theme's schema, conforming to JSON Schema.
469 + *
470 + * @since 5.0.0
471 + *
472 + * @return array Item schema data.
473 + */
474 + public function get_item_schema() {
475 + if ( $this->schema ) {
476 + return $this->add_additional_fields_schema( $this->schema );
477 + }
478 +
479 + $schema = array(
480 + '$schema' => 'http://json-schema.org/draft-04/schema#',
481 + 'title' => 'theme',
482 + 'type' => 'object',
483 + 'properties' => array(
484 + 'stylesheet' => array(
485 + 'description' => __( 'The theme\'s stylesheet. This uniquely identifies the theme.' ),
486 + 'type' => 'string',
487 + 'readonly' => true,
488 + ),
489 + 'stylesheet_uri' => array(
490 + 'description' => __( 'The uri for the theme\'s stylesheet directory.' ),
491 + 'type' => 'string',
492 + 'format' => 'uri',
493 + 'readonly' => true,
494 + ),
495 + 'template' => array(
496 + '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.' ),
497 + 'type' => 'string',
498 + 'readonly' => true,
499 + ),
500 + 'template_uri' => array(
501 + '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.' ),
502 + 'type' => 'string',
503 + 'format' => 'uri',
504 + 'readonly' => true,
505 + ),
506 + 'author' => array(
507 + 'description' => __( 'The theme author.' ),
508 + 'type' => 'object',
509 + 'readonly' => true,
510 + 'properties' => array(
511 + 'raw' => array(
512 + 'description' => __( 'The theme author\'s name, as found in the theme header.' ),
513 + 'type' => 'string',
514 + ),
515 + 'rendered' => array(
516 + 'description' => __( 'HTML for the theme author, transformed for display.' ),
517 + 'type' => 'string',
518 + ),
519 + ),
520 + ),
521 + 'author_uri' => array(
522 + 'description' => __( 'The website of the theme author.' ),
523 + 'type' => 'object',
524 + 'readonly' => true,
525 + 'properties' => array(
526 + 'raw' => array(
527 + 'description' => __( 'The website of the theme author, as found in the theme header.' ),
528 + 'type' => 'string',
529 + 'format' => 'uri',
530 + ),
531 + 'rendered' => array(
532 + 'description' => __( 'The website of the theme author, transformed for display.' ),
533 + 'type' => 'string',
534 + 'format' => 'uri',
535 + ),
536 + ),
537 + ),
538 + 'description' => array(
539 + 'description' => __( 'A description of the theme.' ),
540 + 'type' => 'object',
541 + 'readonly' => true,
542 + 'properties' => array(
543 + 'raw' => array(
544 + 'description' => __( 'The theme description, as found in the theme header.' ),
545 + 'type' => 'string',
546 + ),
547 + 'rendered' => array(
548 + 'description' => __( 'The theme description, transformed for display.' ),
549 + 'type' => 'string',
550 + ),
551 + ),
552 + ),
553 + 'is_block_theme' => array(
554 + 'description' => __( 'Whether the theme is a block-based theme.' ),
555 + 'type' => 'boolean',
556 + 'readonly' => true,
557 + ),
558 + 'name' => array(
559 + 'description' => __( 'The name of the theme.' ),
560 + 'type' => 'object',
561 + 'readonly' => true,
562 + 'properties' => array(
563 + 'raw' => array(
564 + 'description' => __( 'The theme name, as found in the theme header.' ),
565 + 'type' => 'string',
566 + ),
567 + 'rendered' => array(
568 + 'description' => __( 'The theme name, transformed for display.' ),
569 + 'type' => 'string',
570 + ),
571 + ),
572 + ),
573 + 'requires_php' => array(
574 + 'description' => __( 'The minimum PHP version required for the theme to work.' ),
575 + 'type' => 'string',
576 + 'readonly' => true,
577 + ),
578 + 'requires_wp' => array(
579 + 'description' => __( 'The minimum WordPress version required for the theme to work.' ),
580 + 'type' => 'string',
581 + 'readonly' => true,
582 + ),
583 + 'screenshot' => array(
584 + 'description' => __( 'The theme\'s screenshot URL.' ),
585 + 'type' => 'string',
586 + 'format' => 'uri',
587 + 'readonly' => true,
588 + ),
589 + 'tags' => array(
590 + 'description' => __( 'Tags indicating styles and features of the theme.' ),
591 + 'type' => 'object',
592 + 'readonly' => true,
593 + 'properties' => array(
594 + 'raw' => array(
595 + 'description' => __( 'The theme tags, as found in the theme header.' ),
596 + 'type' => 'array',
597 + 'items' => array(
598 + 'type' => 'string',
599 + ),
600 + ),
601 + 'rendered' => array(
602 + 'description' => __( 'The theme tags, transformed for display.' ),
603 + 'type' => 'string',
604 + ),
605 + ),
606 + ),
607 + 'textdomain' => array(
608 + 'description' => __( 'The theme\'s text domain.' ),
609 + 'type' => 'string',
610 + 'readonly' => true,
611 + ),
612 + 'theme_supports' => array(
613 + 'description' => __( 'Features supported by this theme.' ),
614 + 'type' => 'object',
615 + 'readonly' => true,
616 + 'properties' => array(),
617 + ),
618 + 'theme_uri' => array(
619 + 'description' => __( 'The URI of the theme\'s webpage.' ),
620 + 'type' => 'object',
621 + 'readonly' => true,
622 + 'properties' => array(
623 + 'raw' => array(
624 + 'description' => __( 'The URI of the theme\'s webpage, as found in the theme header.' ),
625 + 'type' => 'string',
626 + 'format' => 'uri',
627 + ),
628 + 'rendered' => array(
629 + 'description' => __( 'The URI of the theme\'s webpage, transformed for display.' ),
630 + 'type' => 'string',
631 + 'format' => 'uri',
632 + ),
633 + ),
634 + ),
635 + 'version' => array(
636 + 'description' => __( 'The theme\'s current version.' ),
637 + 'type' => 'string',
638 + 'readonly' => true,
639 + ),
640 + 'status' => array(
641 + 'description' => __( 'A named status for the theme.' ),
642 + 'type' => 'string',
643 + 'enum' => array( 'inactive', 'active' ),
644 + ),
645 + 'default_template_types' => array(
646 + 'description' => __( 'A list of default template types.' ),
647 + 'type' => 'array',
648 + 'readonly' => true,
649 + 'items' => array(
650 + 'type' => 'object',
651 + 'properties' => array(
652 + 'slug' => array(
653 + 'type' => 'string',
654 + ),
655 + 'title' => array(
656 + 'type' => 'string',
657 + ),
658 + 'description' => array(
659 + 'type' => 'string',
660 + ),
661 + ),
662 + ),
663 + ),
664 + 'default_template_part_areas' => array(
665 + 'description' => __( 'A list of allowed area values for template parts.' ),
666 + 'type' => 'array',
667 + 'readonly' => true,
668 + 'items' => array(
669 + 'type' => 'object',
670 + 'properties' => array(
671 + 'area' => array(
672 + 'type' => 'string',
673 + ),
674 + 'label' => array(
675 + 'type' => 'string',
676 + ),
677 + 'description' => array(
678 + 'type' => 'string',
679 + ),
680 + 'icon' => array(
681 + 'type' => 'string',
682 + ),
683 + 'area_tag' => array(
684 + 'type' => 'string',
685 + ),
686 + ),
687 + ),
688 + ),
689 + ),
690 + );
691 +
692 + foreach ( get_registered_theme_features() as $feature => $config ) {
693 + if ( ! is_array( $config['show_in_rest'] ) ) {
694 + continue;
695 + }
696 +
697 + $name = $config['show_in_rest']['name'];
698 +
699 + $schema['properties']['theme_supports']['properties'][ $name ] = $config['show_in_rest']['schema'];
700 + }
701 +
702 + $this->schema = $schema;
703 +
704 + return $this->add_additional_fields_schema( $this->schema );
705 + }
706 +
707 + /**
708 + * Retrieves the search params for the themes collection.
709 + *
710 + * @since 5.0.0
711 + *
712 + * @return array Collection parameters.
713 + */
714 + public function get_collection_params() {
715 + $query_params = array(
716 + 'status' => array(
717 + 'description' => __( 'Limit result set to themes assigned one or more statuses.' ),
718 + 'type' => 'array',
719 + 'items' => array(
720 + 'enum' => array( 'active', 'inactive' ),
721 + 'type' => 'string',
722 + ),
723 + ),
724 + );
725 +
726 + /**
727 + * Filters REST API collection parameters for the themes controller.
728 + *
729 + * @since 5.0.0
730 + *
731 + * @param array $query_params JSON Schema-formatted collection parameters.
732 + */
733 + return apply_filters( 'rest_themes_collection_params', $query_params );
734 + }
735 +
736 + /**
737 + * Sanitizes and validates the list of theme status.
738 + *
739 + * @since 5.0.0
740 + * @deprecated 5.7.0
741 + *
742 + * @param string|array $statuses One or more theme statuses.
743 + * @param WP_REST_Request $request Full details about the request.
744 + * @param string $parameter Additional parameter to pass to validation.
745 + * @return array|WP_Error A list of valid statuses, otherwise WP_Error object.
746 + */
747 + public function sanitize_theme_status( $statuses, $request, $parameter ) {
748 + _deprecated_function( __METHOD__, '5.7.0' );
749 +
750 + $statuses = wp_parse_slug_list( $statuses );
751 +
752 + foreach ( $statuses as $status ) {
753 + $result = rest_validate_request_arg( $status, $request, $parameter );
754 +
755 + if ( is_wp_error( $result ) ) {
756 + return $result;
757 + }
758 + }
759 +
760 + return $statuses;
761 + }
762 + }
763 +