| [ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * REST API: WP_REST_View_Config_Controller class 4 * 5 * @package WordPress 6 * @subpackage REST_API 7 * @since 7.1.0 8 */ 9 10 /** 11 * Controller which provides a REST endpoint for retrieving the default 12 * view configuration for a given entity type. 13 * 14 * @since 7.1.0 15 * 16 * @see WP_REST_Controller 17 */ 18 class WP_REST_View_Config_Controller extends WP_REST_Controller { 19 20 /** 21 * Constructor. 22 * 23 * @since 7.1.0 24 */ 25 public function __construct() { 26 $this->namespace = 'wp/v2'; 27 $this->rest_base = 'view-config'; 28 } 29 30 /** 31 * Registers the routes for the controller. 32 * 33 * @since 7.1.0 34 */ 35 public function register_routes() { 36 register_rest_route( 37 $this->namespace, 38 '/' . $this->rest_base, 39 array( 40 array( 41 'methods' => WP_REST_Server::READABLE, 42 'callback' => array( $this, 'get_items' ), 43 'permission_callback' => array( $this, 'get_items_permissions_check' ), 44 'args' => array( 45 'kind' => array( 46 'description' => __( 'Entity kind.' ), 47 'type' => 'string', 48 'required' => true, 49 ), 50 'name' => array( 51 'description' => __( 'Entity name.' ), 52 'type' => 'string', 53 'required' => true, 54 ), 55 ), 56 ), 57 'schema' => array( $this, 'get_public_item_schema' ), 58 ) 59 ); 60 } 61 62 /** 63 * Checks if a given request has access to read view config. 64 * 65 * @since 7.1.0 66 * 67 * @param WP_REST_Request $request Full details about the request. 68 * @return true|WP_Error True if the request has read access, WP_Error object otherwise. 69 */ 70 public function get_items_permissions_check( $request ) { 71 $kind = $request->get_param( 'kind' ); 72 $name = $request->get_param( 'name' ); 73 74 $capability = $this->get_required_capability( $kind, $name ); 75 76 if ( null === $capability ) { 77 return new WP_Error( 78 'rest_view_config_invalid_entity', 79 __( 'Invalid entity kind or name.' ), 80 array( 'status' => 404 ) 81 ); 82 } 83 84 if ( ! current_user_can( $capability ) ) { 85 return new WP_Error( 86 'rest_cannot_read', 87 __( 'Sorry, you are not allowed to read view config.' ), 88 array( 'status' => rest_authorization_required_code() ) 89 ); 90 } 91 92 return true; 93 } 94 95 /** 96 * Resolves the capability required to read the view config for an entity. 97 * 98 * Known kinds map to the capability that gates managing that entity's list: 99 * post types use their own `edit_posts` capability (which honors custom 100 * `capability_type` registrations), taxonomies use `manage_terms`, and 101 * root-level entities use `manage_options`. A post type or taxonomy that is 102 * not registered, or not exposed to the REST API, resolves to `null` so the 103 * request is treated as referencing an unknown entity. 104 * 105 * Any other kind falls back to `edit_posts`. This keeps entities registered 106 * through the `get_entity_view_config_{$kind}_{$name}` filter readable behind 107 * a baseline capability. 108 * 109 * @since 7.1.0 110 * 111 * @param string $kind The entity kind (e.g. `postType`). 112 * @param string $name The entity name (e.g. `page`). 113 * @return string|null Capability required to read the config, or null if the 114 * entity is not registered. 115 */ 116 protected function get_required_capability( $kind, $name ) { 117 switch ( $kind ) { 118 case 'postType': 119 $post_type = get_post_type_object( $name ); 120 if ( $post_type && $post_type->show_in_rest ) { 121 return $post_type->cap->edit_posts; 122 } 123 return null; 124 125 case 'taxonomy': 126 $taxonomy = get_taxonomy( $name ); 127 if ( $taxonomy && $taxonomy->show_in_rest ) { 128 return $taxonomy->cap->manage_terms; 129 } 130 return null; 131 132 case 'root': 133 return 'manage_options'; 134 } 135 136 return 'edit_posts'; 137 } 138 139 /** 140 * Returns the default view configuration for the given entity type. 141 * 142 * @since 7.1.0 143 * 144 * @param WP_REST_Request $request Full details about the request. 145 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. 146 */ 147 public function get_items( $request ) { 148 $kind = $request->get_param( 'kind' ); 149 $name = $request->get_param( 'name' ); 150 151 $config = wp_get_entity_view_config( $kind, $name ); 152 $schema = $this->get_item_schema(); 153 154 $response = array( 155 'kind' => $kind, 156 'name' => $name, 157 'version' => WP_View_Config_Data::LATEST_VERSION, 158 'default_view' => wp_prepare_json_value_for_client( $config['default_view'], $schema['properties']['default_view'] ), 159 'default_layouts' => wp_prepare_json_value_for_client( $config['default_layouts'], $schema['properties']['default_layouts'] ), 160 'view_list' => wp_prepare_json_value_for_client( $config['view_list'], $schema['properties']['view_list'] ), 161 'form' => wp_prepare_json_value_for_client( $config['form'], $schema['properties']['form'] ), 162 ); 163 164 return rest_ensure_response( $response ); 165 } 166 167 /** 168 * Recursively casts empty arrays to objects where the schema types them as 169 * objects. 170 * 171 * @since 7.1.0 172 * @since 7.2.0 Uses wp_prepare_json_value_for_client(). 173 * 174 * @see wp_prepare_json_value_for_client() 175 * 176 * @param mixed $value The value to normalize. 177 * @param array $schema The schema node describing the value. 178 * @return mixed The normalized value, with empty object-typed arrays cast to objects. 179 */ 180 protected function cast_empty_objects( $value, $schema ) { 181 return is_array( $schema ) ? wp_prepare_json_value_for_client( $value, $schema ) : $value; 182 } 183 184 /** 185 * Retrieves the item's schema, conforming to JSON Schema. 186 * 187 * @since 7.1.0 188 * 189 * @return array Item schema data. 190 */ 191 public function get_item_schema() { 192 if ( $this->schema ) { 193 return $this->add_additional_fields_schema( $this->schema ); 194 } 195 196 $view_base_properties = $this->get_view_base_schema(); 197 198 $this->schema = array( 199 '$schema' => 'http://json-schema.org/draft-04/schema#', 200 'title' => 'view-config', 201 'type' => 'object', 202 'properties' => array( 203 'kind' => array( 204 'description' => __( 'Entity kind.' ), 205 'type' => 'string', 206 'readonly' => true, 207 ), 208 'name' => array( 209 'description' => __( 'Entity name.' ), 210 'type' => 'string', 211 'readonly' => true, 212 ), 213 'version' => array( 214 'description' => __( 'The schema version of the configuration.' ), 215 'type' => 'integer', 216 'readonly' => true, 217 ), 218 'default_view' => array( 219 'description' => __( 'Default view configuration.' ), 220 'type' => 'object', 221 'readonly' => true, 222 'properties' => array_merge( 223 array( 224 'type' => array( 225 'type' => 'string', 226 ), 227 'layout' => $this->get_combined_layout_schema(), 228 ), 229 $view_base_properties 230 ), 231 ), 232 'default_layouts' => array( 233 'description' => __( 'Default layout configurations.' ), 234 'type' => 'object', 235 'readonly' => true, 236 'properties' => array( 237 'table' => array( 238 'type' => 'object', 239 'properties' => array_merge( 240 $view_base_properties, 241 array( 242 'layout' => $this->get_table_layout_schema(), 243 ) 244 ), 245 ), 246 'list' => array( 247 'type' => 'object', 248 'properties' => array_merge( 249 $view_base_properties, 250 array( 251 'layout' => $this->get_list_layout_schema(), 252 ) 253 ), 254 ), 255 'grid' => array( 256 'type' => 'object', 257 'properties' => array_merge( 258 $view_base_properties, 259 array( 260 'layout' => $this->get_grid_layout_schema(), 261 ) 262 ), 263 ), 264 'activity' => array( 265 'type' => 'object', 266 'properties' => array_merge( 267 $view_base_properties, 268 array( 269 'layout' => $this->get_list_layout_schema(), 270 ) 271 ), 272 ), 273 'pickerGrid' => array( 274 'type' => 'object', 275 'properties' => array_merge( 276 $view_base_properties, 277 array( 278 'layout' => $this->get_grid_layout_schema(), 279 ) 280 ), 281 ), 282 'pickerTable' => array( 283 'type' => 'object', 284 'properties' => array_merge( 285 $view_base_properties, 286 array( 287 'layout' => $this->get_table_layout_schema(), 288 ) 289 ), 290 ), 291 ), 292 ), 293 'view_list' => array( 294 'description' => __( 'List of default views.' ), 295 'type' => 'array', 296 'readonly' => true, 297 'items' => array( 298 'type' => 'object', 299 'properties' => array( 300 'title' => array( 301 'type' => 'string', 302 ), 303 'slug' => array( 304 'type' => 'string', 305 ), 306 'view' => array( 307 'type' => 'object', 308 'properties' => array_merge( 309 array( 310 'type' => array( 311 'type' => 'string', 312 ), 313 'layout' => $this->get_combined_layout_schema(), 314 ), 315 $view_base_properties 316 ), 317 ), 318 ), 319 ), 320 ), 321 'form' => array( 322 'description' => __( 'Default form configuration.' ), 323 'type' => 'object', 324 'readonly' => true, 325 'properties' => $this->get_form_schema(), 326 ), 327 ), 328 ); 329 330 return $this->add_additional_fields_schema( $this->schema ); 331 } 332 333 /** 334 * Returns the schema properties shared by all view types (ViewBase), excluding 'type'. 335 * 336 * Note that `search` and `page` are not part of the schema: they are managed 337 * via the URL, which is their only source of truth. 338 * 339 * @since 7.1.0 340 * 341 * @return array Schema properties for the base view configuration. 342 */ 343 protected function get_view_base_schema() { 344 return array( 345 'filters' => array( 346 'type' => 'array', 347 'items' => array( 348 'type' => 'object', 349 'properties' => array( 350 'field' => array( 351 'type' => 'string', 352 ), 353 'operator' => array( 354 'type' => 'string', 355 'enum' => array( 356 'is', 357 'isNot', 358 'isAny', 359 'isNone', 360 'isAll', 361 'isNotAll', 362 'lessThan', 363 'greaterThan', 364 'lessThanOrEqual', 365 'greaterThanOrEqual', 366 'before', 367 'after', 368 ), 369 ), 370 'value' => array(), 371 'isLocked' => array( 372 'type' => 'boolean', 373 ), 374 ), 375 ), 376 ), 377 'sort' => array( 378 'type' => 'object', 379 'properties' => array( 380 'field' => array( 381 'type' => 'string', 382 ), 383 'direction' => array( 384 'type' => 'string', 385 'enum' => array( 'asc', 'desc' ), 386 ), 387 ), 388 ), 389 'perPage' => array( 390 'type' => 'integer', 391 ), 392 'fields' => array( 393 'type' => 'array', 394 'items' => array( 395 'type' => 'string', 396 ), 397 ), 398 'titleField' => array( 399 'type' => 'string', 400 ), 401 'mediaField' => array( 402 'type' => 'string', 403 ), 404 'descriptionField' => array( 405 'type' => 'string', 406 ), 407 'showTitle' => array( 408 'type' => 'boolean', 409 ), 410 'showMedia' => array( 411 'type' => 'boolean', 412 ), 413 'showDescription' => array( 414 'type' => 'boolean', 415 ), 416 'showLevels' => array( 417 'type' => 'boolean', 418 ), 419 'groupBy' => array( 420 'type' => 'object', 421 'properties' => array( 422 'field' => array( 423 'type' => 'string', 424 ), 425 'direction' => array( 426 'type' => 'string', 427 'enum' => array( 'asc', 'desc' ), 428 ), 429 'showLabel' => array( 430 'type' => 'boolean', 431 'default' => true, 432 ), 433 ), 434 ), 435 'infiniteScrollEnabled' => array( 436 'type' => 'boolean', 437 ), 438 ); 439 } 440 441 /** 442 * Returns the schema for the ColumnStyle type. 443 * 444 * @since 7.1.0 445 * 446 * @return array Schema for a column style object. 447 */ 448 protected function get_column_style_schema() { 449 return array( 450 'type' => 'object', 451 'properties' => array( 452 'width' => array( 453 'description' => __( 'The width of the column.' ), 454 'type' => array( 'string', 'number' ), 455 ), 456 'maxWidth' => array( 457 'description' => __( 'The maximum width of the column.' ), 458 'type' => array( 'string', 'number' ), 459 ), 460 'minWidth' => array( 461 'description' => __( 'The minimum width of the column.' ), 462 'type' => array( 'string', 'number' ), 463 ), 464 'align' => array( 465 'description' => __( 'The horizontal alignment of the column content.' ), 466 'type' => 'string', 467 'enum' => array( 'start', 'center', 'end' ), 468 ), 469 ), 470 ); 471 } 472 473 /** 474 * Returns the layout schema for table-type views (ViewTable, ViewPickerTable). 475 * 476 * @since 7.1.0 477 * 478 * @return array Schema for a table layout object. 479 */ 480 protected function get_table_layout_schema() { 481 return array( 482 'type' => 'object', 483 'properties' => array( 484 'styles' => array( 485 'description' => __( 'Column styles keyed by field id, for the columns listed in the view fields. The primary column (title, media, and description fields) ignores these styles; in the table layout it takes the width left over by the other columns, or the last column does when there is no primary column.' ), 486 'type' => 'object', 487 'additionalProperties' => $this->get_column_style_schema(), 488 ), 489 'density' => array( 490 'type' => 'string', 491 'enum' => array( 'compact', 'balanced', 'comfortable' ), 492 ), 493 'enableMoving' => array( 494 'type' => 'boolean', 495 ), 496 ), 497 ); 498 } 499 500 /** 501 * Returns the layout schema for list-type views (ViewList, ViewActivity). 502 * 503 * @since 7.1.0 504 * 505 * @return array Schema for a list layout object. 506 */ 507 protected function get_list_layout_schema() { 508 return array( 509 'type' => 'object', 510 'properties' => array( 511 'density' => array( 512 'type' => 'string', 513 'enum' => array( 'compact', 'balanced', 'comfortable' ), 514 ), 515 ), 516 ); 517 } 518 519 /** 520 * Returns a combined layout schema that accepts properties from all view types. 521 * 522 * This is useful for contexts where the view type is not known ahead of time 523 * (e.g. the `view` override in a view list item), so all possible layout 524 * properties must be accepted. 525 * 526 * @since 7.1.0 527 * 528 * @return array Schema for a combined layout object. 529 */ 530 protected function get_combined_layout_schema() { 531 return array( 532 'type' => 'object', 533 'properties' => array_merge( 534 $this->get_table_layout_schema()['properties'], 535 $this->get_grid_layout_schema()['properties'], 536 $this->get_list_layout_schema()['properties'] 537 ), 538 ); 539 } 540 541 /** 542 * Returns the layout schema for grid-type views (ViewGrid, ViewPickerGrid). 543 * 544 * @since 7.1.0 545 * 546 * @return array Schema for a grid layout object. 547 */ 548 protected function get_grid_layout_schema() { 549 return array( 550 'type' => 'object', 551 'properties' => array( 552 'badgeFields' => array( 553 'type' => 'array', 554 'items' => array( 555 'type' => 'string', 556 ), 557 ), 558 'previewSize' => array( 559 'type' => 'number', 560 ), 561 'density' => array( 562 'type' => 'string', 563 'enum' => array( 'compact', 'balanced', 'comfortable' ), 564 ), 565 ), 566 ); 567 } 568 569 /** 570 * Returns the schema for a form layout object as a discriminated union. 571 * 572 * Each variant is discriminated by a single-value enum on its `type` property, 573 * matching the TypeScript Layout union in dataviews/src/types/dataform.ts. 574 * 575 * @since 7.1.0 576 * @since 7.2.0 Added the `showPlaceholderIfEmpty` property to the panel layout. 577 * 578 * @return array Schema for a form layout object. 579 */ 580 protected function get_form_layout_schema() { 581 return array( 582 'oneOf' => array( 583 // RegularLayout. 584 array( 585 'type' => 'object', 586 'properties' => array( 587 'type' => array( 588 'type' => 'string', 589 'enum' => array( 'regular' ), 590 ), 591 'labelPosition' => array( 592 'type' => 'string', 593 'enum' => array( 'top', 'side', 'none' ), 594 ), 595 ), 596 ), 597 // PanelLayout. 598 array( 599 'type' => 'object', 600 'properties' => array( 601 'type' => array( 602 'type' => 'string', 603 'enum' => array( 'panel' ), 604 ), 605 'labelPosition' => array( 606 'type' => 'string', 607 'enum' => array( 'top', 'side', 'none' ), 608 ), 609 'openAs' => array( 610 'oneOf' => array( 611 array( 612 'type' => 'string', 613 'enum' => array( 'dropdown', 'modal' ), 614 ), 615 array( 616 'type' => 'object', 617 'properties' => array( 618 'type' => array( 619 'type' => 'string', 620 'enum' => array( 'dropdown', 'modal' ), 621 ), 622 'applyLabel' => array( 623 'type' => 'string', 624 ), 625 'cancelLabel' => array( 626 'type' => 'string', 627 ), 628 ), 629 ), 630 ), 631 ), 632 'summary' => array( 633 'oneOf' => array( 634 array( 'type' => 'string' ), 635 array( 636 'type' => 'array', 637 'items' => array( 638 'type' => 'string', 639 ), 640 ), 641 ), 642 ), 643 'editVisibility' => array( 644 'type' => 'string', 645 'enum' => array( 'always', 'on-hover' ), 646 ), 647 'showPlaceholderIfEmpty' => array( 648 'type' => 'boolean', 649 ), 650 ), 651 ), 652 // CardLayout. 653 array( 654 'type' => 'object', 655 'properties' => array( 656 'type' => array( 657 'type' => 'string', 658 'enum' => array( 'card' ), 659 ), 660 'withHeader' => array( 661 'type' => 'boolean', 662 ), 663 'isOpened' => array( 664 'type' => 'boolean', 665 ), 666 'isCollapsible' => array( 667 'type' => 'boolean', 668 ), 669 'summary' => array( 670 'oneOf' => array( 671 array( 'type' => 'string' ), 672 array( 673 'type' => 'array', 674 'items' => array( 675 'oneOf' => array( 676 array( 'type' => 'string' ), 677 array( 678 'type' => 'object', 679 'properties' => array( 680 'id' => array( 681 'type' => 'string', 682 ), 683 'visibility' => array( 684 'type' => 'string', 685 'enum' => array( 'always', 'when-collapsed' ), 686 ), 687 ), 688 ), 689 ), 690 ), 691 ), 692 ), 693 ), 694 ), 695 ), 696 // RowLayout. 697 array( 698 'type' => 'object', 699 'properties' => array( 700 'type' => array( 701 'type' => 'string', 702 'enum' => array( 'row' ), 703 ), 704 'alignment' => array( 705 'type' => 'string', 706 'enum' => array( 'start', 'center', 'end' ), 707 ), 708 'styles' => array( 709 'type' => 'object', 710 'additionalProperties' => array( 711 'type' => 'object', 712 'properties' => array( 713 'flex' => array( 714 'type' => array( 'string', 'number' ), 715 ), 716 ), 717 ), 718 ), 719 ), 720 ), 721 // DetailsLayout. 722 array( 723 'type' => 'object', 724 'properties' => array( 725 'type' => array( 726 'type' => 'string', 727 'enum' => array( 'details' ), 728 ), 729 'summary' => array( 730 'type' => 'string', 731 ), 732 ), 733 ), 734 ), 735 ); 736 } 737 738 /** 739 * Returns the schema for a form field item (string or object). 740 * 741 * @since 7.1.0 742 * 743 * @return array Schema for a form field. 744 */ 745 protected function get_form_field_schema() { 746 return array( 747 'oneOf' => array( 748 array( 'type' => 'string' ), 749 array( 750 'type' => 'object', 751 'properties' => array( 752 'id' => array( 753 'type' => 'string', 754 ), 755 'label' => array( 756 'type' => 'string', 757 ), 758 'description' => array( 759 'type' => 'string', 760 ), 761 'layout' => $this->get_form_layout_schema(), 762 'children' => array( 763 'type' => 'array', 764 'items' => array( 765 'oneOf' => array( 766 array( 'type' => 'string' ), 767 // This object can have the shape of a form field itself, 768 // allowing for recursive nesting of form fields. 769 // There's no easy way to codify this recursion via the JSON Schema draft-04 770 // supported by the REST API. 771 array( 'type' => 'object' ), 772 ), 773 ), 774 ), 775 ), 776 ), 777 ), 778 ); 779 } 780 781 /** 782 * Returns the schema for the form configuration object. 783 * 784 * @since 7.1.0 785 * 786 * @return array Schema properties for the form configuration. 787 */ 788 protected function get_form_schema() { 789 return array( 790 'layout' => $this->get_form_layout_schema(), 791 'fields' => array( 792 'type' => 'array', 793 'items' => $this->get_form_field_schema(), 794 ), 795 ); 796 } 797 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Sat Oct 24 08:20:34 2026 | Cross-referenced by PHPXref |