Create New Item
Item Type
File
Folder
Item Name
Search file in folder and subfolders...
Are you sure want to rename?
relanceing
/
wp-includes
/
rest-api
/
endpoints
:
class-wp-rest-view-config-controller.php
Advanced Search
Upload
New Item
Settings
Back
Back Up
Advanced Editor
Save
<?php /** * REST API: WP_REST_View_Config_Controller class * * @package WordPress * @subpackage REST_API * @since 7.1.0 */ /** * Controller which provides a REST endpoint for retrieving the default * view configuration for a given entity type. * * @since 7.1.0 * * @see WP_REST_Controller */ class WP_REST_View_Config_Controller extends WP_REST_Controller { /** * Constructor. * * @since 7.1.0 */ public function __construct() { $this->namespace = 'wp/v2'; $this->rest_base = 'view-config'; } /** * Registers the routes for the controller. * * @since 7.1.0 */ public function register_routes() { register_rest_route( $this->namespace, '/' . $this->rest_base, array( array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get_items' ), 'permission_callback' => array( $this, 'get_items_permissions_check' ), 'args' => array( 'kind' => array( 'description' => __( 'Entity kind.' ), 'type' => 'string', 'required' => true, ), 'name' => array( 'description' => __( 'Entity name.' ), 'type' => 'string', 'required' => true, ), ), ), 'schema' => array( $this, 'get_public_item_schema' ), ) ); } /** * Checks if a given request has access to read view config. * * @since 7.1.0 * * @param WP_REST_Request $request Full details about the request. * @return true|WP_Error True if the request has read access, WP_Error object otherwise. */ public function get_items_permissions_check( $request ) { $kind = $request->get_param( 'kind' ); $name = $request->get_param( 'name' ); $capability = $this->get_required_capability( $kind, $name ); if ( null === $capability ) { return new WP_Error( 'rest_view_config_invalid_entity', __( 'Invalid entity kind or name.' ), array( 'status' => 404 ) ); } if ( ! current_user_can( $capability ) ) { return new WP_Error( 'rest_cannot_read', __( 'Sorry, you are not allowed to read view config.' ), array( 'status' => rest_authorization_required_code() ) ); } return true; } /** * Resolves the capability required to read the view config for an entity. * * Known kinds map to the capability that gates managing that entity's list: * post types use their own `edit_posts` capability (which honors custom * `capability_type` registrations), taxonomies use `manage_terms`, and * root-level entities use `manage_options`. A post type or taxonomy that is * not registered, or not exposed to the REST API, resolves to `null` so the * request is treated as referencing an unknown entity. * * Any other kind falls back to `edit_posts`. This keeps entities registered * through the `get_entity_view_config_{$kind}_{$name}` filter readable behind * a baseline capability. * * @since 7.1.0 * * @param string $kind The entity kind (e.g. `postType`). * @param string $name The entity name (e.g. `page`). * @return string|null Capability required to read the config, or null if the * entity is not registered. */ protected function get_required_capability( $kind, $name ) { switch ( $kind ) { case 'postType': $post_type = get_post_type_object( $name ); if ( $post_type && $post_type->show_in_rest ) { return $post_type->cap->edit_posts; } return null; case 'taxonomy': $taxonomy = get_taxonomy( $name ); if ( $taxonomy && $taxonomy->show_in_rest ) { return $taxonomy->cap->manage_terms; } return null; case 'root': return 'manage_options'; } return 'edit_posts'; } /** * Returns the default view configuration for the given entity type. * * @since 7.1.0 * * @param WP_REST_Request $request Full details about the request. * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. */ public function get_items( $request ) { $kind = $request->get_param( 'kind' ); $name = $request->get_param( 'name' ); $config = wp_get_entity_view_config( $kind, $name ); $schema = $this->get_item_schema(); $response = array( 'kind' => $kind, 'name' => $name, 'version' => WP_View_Config_Data::LATEST_VERSION, 'default_view' => $this->cast_empty_objects( $config['default_view'], $schema['properties']['default_view'] ), 'default_layouts' => $this->cast_empty_objects( $config['default_layouts'], $schema['properties']['default_layouts'] ), 'view_list' => $this->cast_empty_objects( $config['view_list'], $schema['properties']['view_list'] ), 'form' => $this->cast_empty_objects( $config['form'], $schema['properties']['form'] ), ); return rest_ensure_response( $response ); } /** * Recursively casts empty arrays to objects where the schema types them as * objects. * * PHP cannot distinguish an empty associative array from an empty list, so * `json_encode()` always serializes `array()` as a JSON array (`[]`). The * REST schema, however, types several values as objects, which must encode * as `{}`. This walks the value against its schema and casts any empty, * object-typed array to an object. Non-empty associative arrays already * encode as objects, so they are left as arrays and only recursed into to * fix any nested empty objects. * * Union schemas (`oneOf`/`anyOf`) are handled only for the empty-array case: * an empty value is cast to an object when any branch allows an object. Such * values are not recursed into, which is sufficient for the form schema * where they never contain empty nested objects. * * @since 7.1.0 * * @param mixed $value The value to normalize. * @param array $schema The schema node describing the value. * @return mixed The normalized value, with empty object-typed arrays cast to objects. */ protected function cast_empty_objects( $value, $schema ) { if ( ! is_array( $value ) || ! is_array( $schema ) ) { return $value; } if ( isset( $schema['oneOf'] ) || isset( $schema['anyOf'] ) ) { $branches = $schema['oneOf'] ?? $schema['anyOf']; if ( array() === $value ) { foreach ( $branches as $branch ) { if ( is_array( $branch ) && in_array( 'object', (array) ( $branch['type'] ?? array() ), true ) ) { return (object) array(); } } } return $value; } $types = (array) ( $schema['type'] ?? array() ); if ( in_array( 'array', $types, true ) && isset( $schema['items'] ) ) { foreach ( $value as $index => $item ) { $value[ $index ] = $this->cast_empty_objects( $item, $schema['items'] ); } return $value; } if ( in_array( 'object', $types, true ) ) { if ( isset( $schema['properties'] ) ) { foreach ( $schema['properties'] as $property => $property_schema ) { if ( array_key_exists( $property, $value ) ) { $value[ $property ] = $this->cast_empty_objects( $value[ $property ], $property_schema ); } } } if ( isset( $schema['additionalProperties'] ) && is_array( $schema['additionalProperties'] ) ) { foreach ( $value as $key => $item ) { if ( isset( $schema['properties'][ $key ] ) ) { continue; } $value[ $key ] = $this->cast_empty_objects( $item, $schema['additionalProperties'] ); } } // Empty object-typed arrays must serialize as {} to match the schema. if ( array() === $value ) { return (object) array(); } } return $value; } /** * Retrieves the item's schema, conforming to JSON Schema. * * @since 7.1.0 * * @return array Item schema data. */ public function get_item_schema() { if ( $this->schema ) { return $this->add_additional_fields_schema( $this->schema ); } $view_base_properties = $this->get_view_base_schema(); $this->schema = array( '$schema' => 'http://json-schema.org/draft-04/schema#', 'title' => 'view-config', 'type' => 'object', 'properties' => array( 'kind' => array( 'description' => __( 'Entity kind.' ), 'type' => 'string', 'readonly' => true, ), 'name' => array( 'description' => __( 'Entity name.' ), 'type' => 'string', 'readonly' => true, ), 'version' => array( 'description' => __( 'The schema version of the configuration.' ), 'type' => 'integer', 'readonly' => true, ), 'default_view' => array( 'description' => __( 'Default view configuration.' ), 'type' => 'object', 'readonly' => true, 'properties' => array_merge( array( 'type' => array( 'type' => 'string', ), 'layout' => $this->get_combined_layout_schema(), ), $view_base_properties ), ), 'default_layouts' => array( 'description' => __( 'Default layout configurations.' ), 'type' => 'object', 'readonly' => true, 'properties' => array( 'table' => array( 'type' => 'object', 'properties' => array_merge( $view_base_properties, array( 'layout' => $this->get_table_layout_schema(), ) ), ), 'list' => array( 'type' => 'object', 'properties' => array_merge( $view_base_properties, array( 'layout' => $this->get_list_layout_schema(), ) ), ), 'grid' => array( 'type' => 'object', 'properties' => array_merge( $view_base_properties, array( 'layout' => $this->get_grid_layout_schema(), ) ), ), 'activity' => array( 'type' => 'object', 'properties' => array_merge( $view_base_properties, array( 'layout' => $this->get_list_layout_schema(), ) ), ), 'pickerGrid' => array( 'type' => 'object', 'properties' => array_merge( $view_base_properties, array( 'layout' => $this->get_grid_layout_schema(), ) ), ), 'pickerTable' => array( 'type' => 'object', 'properties' => array_merge( $view_base_properties, array( 'layout' => $this->get_table_layout_schema(), ) ), ), ), ), 'view_list' => array( 'description' => __( 'List of default views.' ), 'type' => 'array', 'readonly' => true, 'items' => array( 'type' => 'object', 'properties' => array( 'title' => array( 'type' => 'string', ), 'slug' => array( 'type' => 'string', ), 'view' => array( 'type' => 'object', 'properties' => array_merge( array( 'type' => array( 'type' => 'string', ), 'layout' => $this->get_combined_layout_schema(), ), $view_base_properties ), ), ), ), ), 'form' => array( 'description' => __( 'Default form configuration.' ), 'type' => 'object', 'readonly' => true, 'properties' => $this->get_form_schema(), ), ), ); return $this->add_additional_fields_schema( $this->schema ); } /** * Returns the schema properties shared by all view types (ViewBase), excluding 'type'. * * Note that `search` and `page` are not part of the schema: they are managed * via the URL, which is their only source of truth. * * @since 7.1.0 * * @return array Schema properties for the base view configuration. */ protected function get_view_base_schema() { return array( 'filters' => array( 'type' => 'array', 'items' => array( 'type' => 'object', 'properties' => array( 'field' => array( 'type' => 'string', ), 'operator' => array( 'type' => 'string', 'enum' => array( 'is', 'isNot', 'isAny', 'isNone', 'isAll', 'isNotAll', 'lessThan', 'greaterThan', 'lessThanOrEqual', 'greaterThanOrEqual', 'before', 'after', ), ), 'value' => array(), 'isLocked' => array( 'type' => 'boolean', ), ), ), ), 'sort' => array( 'type' => 'object', 'properties' => array( 'field' => array( 'type' => 'string', ), 'direction' => array( 'type' => 'string', 'enum' => array( 'asc', 'desc' ), ), ), ), 'perPage' => array( 'type' => 'integer', ), 'fields' => array( 'type' => 'array', 'items' => array( 'type' => 'string', ), ), 'titleField' => array( 'type' => 'string', ), 'mediaField' => array( 'type' => 'string', ), 'descriptionField' => array( 'type' => 'string', ), 'showTitle' => array( 'type' => 'boolean', ), 'showMedia' => array( 'type' => 'boolean', ), 'showDescription' => array( 'type' => 'boolean', ), 'showLevels' => array( 'type' => 'boolean', ), 'groupBy' => array( 'type' => 'object', 'properties' => array( 'field' => array( 'type' => 'string', ), 'direction' => array( 'type' => 'string', 'enum' => array( 'asc', 'desc' ), ), 'showLabel' => array( 'type' => 'boolean', 'default' => true, ), ), ), 'infiniteScrollEnabled' => array( 'type' => 'boolean', ), ); } /** * Returns the schema for the ColumnStyle type. * * @since 7.1.0 * * @return array Schema for a column style object. */ protected function get_column_style_schema() { return array( 'type' => 'object', 'properties' => array( 'width' => array( 'type' => array( 'string', 'number' ), ), 'maxWidth' => array( 'type' => array( 'string', 'number' ), ), 'minWidth' => array( 'type' => array( 'string', 'number' ), ), 'align' => array( 'type' => 'string', 'enum' => array( 'start', 'center', 'end' ), ), ), ); } /** * Returns the layout schema for table-type views (ViewTable, ViewPickerTable). * * @since 7.1.0 * * @return array Schema for a table layout object. */ protected function get_table_layout_schema() { return array( 'type' => 'object', 'properties' => array( 'styles' => array( 'type' => 'object', 'additionalProperties' => $this->get_column_style_schema(), ), 'density' => array( 'type' => 'string', 'enum' => array( 'compact', 'balanced', 'comfortable' ), ), 'enableMoving' => array( 'type' => 'boolean', ), ), ); } /** * Returns the layout schema for list-type views (ViewList, ViewActivity). * * @since 7.1.0 * * @return array Schema for a list layout object. */ protected function get_list_layout_schema() { return array( 'type' => 'object', 'properties' => array( 'density' => array( 'type' => 'string', 'enum' => array( 'compact', 'balanced', 'comfortable' ), ), ), ); } /** * Returns a combined layout schema that accepts properties from all view types. * * This is useful for contexts where the view type is not known ahead of time * (e.g. the `view` override in a view list item), so all possible layout * properties must be accepted. * * @since 7.1.0 * * @return array Schema for a combined layout object. */ protected function get_combined_layout_schema() { return array( 'type' => 'object', 'properties' => array_merge( $this->get_table_layout_schema()['properties'], $this->get_grid_layout_schema()['properties'], $this->get_list_layout_schema()['properties'] ), ); } /** * Returns the layout schema for grid-type views (ViewGrid, ViewPickerGrid). * * @since 7.1.0 * * @return array Schema for a grid layout object. */ protected function get_grid_layout_schema() { return array( 'type' => 'object', 'properties' => array( 'badgeFields' => array( 'type' => 'array', 'items' => array( 'type' => 'string', ), ), 'previewSize' => array( 'type' => 'number', ), 'density' => array( 'type' => 'string', 'enum' => array( 'compact', 'balanced', 'comfortable' ), ), ), ); } /** * Returns the schema for a form layout object as a discriminated union. * * Each variant is discriminated by a single-value enum on its `type` property, * matching the TypeScript Layout union in dataviews/src/types/dataform.ts. * * @since 7.1.0 * * @return array Schema for a form layout object. */ protected function get_form_layout_schema() { return array( 'oneOf' => array( // RegularLayout. array( 'type' => 'object', 'properties' => array( 'type' => array( 'type' => 'string', 'enum' => array( 'regular' ), ), 'labelPosition' => array( 'type' => 'string', 'enum' => array( 'top', 'side', 'none' ), ), ), ), // PanelLayout. array( 'type' => 'object', 'properties' => array( 'type' => array( 'type' => 'string', 'enum' => array( 'panel' ), ), 'labelPosition' => array( 'type' => 'string', 'enum' => array( 'top', 'side', 'none' ), ), 'openAs' => array( 'oneOf' => array( array( 'type' => 'string', 'enum' => array( 'dropdown', 'modal' ), ), array( 'type' => 'object', 'properties' => array( 'type' => array( 'type' => 'string', 'enum' => array( 'dropdown', 'modal' ), ), 'applyLabel' => array( 'type' => 'string', ), 'cancelLabel' => array( 'type' => 'string', ), ), ), ), ), 'summary' => array( 'oneOf' => array( array( 'type' => 'string' ), array( 'type' => 'array', 'items' => array( 'type' => 'string', ), ), ), ), 'editVisibility' => array( 'type' => 'string', 'enum' => array( 'always', 'on-hover' ), ), ), ), // CardLayout. array( 'type' => 'object', 'properties' => array( 'type' => array( 'type' => 'string', 'enum' => array( 'card' ), ), 'withHeader' => array( 'type' => 'boolean', ), 'isOpened' => array( 'type' => 'boolean', ), 'isCollapsible' => array( 'type' => 'boolean', ), 'summary' => array( 'oneOf' => array( array( 'type' => 'string' ), array( 'type' => 'array', 'items' => array( 'oneOf' => array( array( 'type' => 'string' ), array( 'type' => 'object', 'properties' => array( 'id' => array( 'type' => 'string', ), 'visibility' => array( 'type' => 'string', 'enum' => array( 'always', 'when-collapsed' ), ), ), ), ), ), ), ), ), ), ), // RowLayout. array( 'type' => 'object', 'properties' => array( 'type' => array( 'type' => 'string', 'enum' => array( 'row' ), ), 'alignment' => array( 'type' => 'string', 'enum' => array( 'start', 'center', 'end' ), ), 'styles' => array( 'type' => 'object', 'additionalProperties' => array( 'type' => 'object', 'properties' => array( 'flex' => array( 'type' => array( 'string', 'number' ), ), ), ), ), ), ), // DetailsLayout. array( 'type' => 'object', 'properties' => array( 'type' => array( 'type' => 'string', 'enum' => array( 'details' ), ), 'summary' => array( 'type' => 'string', ), ), ), ), ); } /** * Returns the schema for a form field item (string or object). * * @since 7.1.0 * * @return array Schema for a form field. */ protected function get_form_field_schema() { return array( 'oneOf' => array( array( 'type' => 'string' ), array( 'type' => 'object', 'properties' => array( 'id' => array( 'type' => 'string', ), 'label' => array( 'type' => 'string', ), 'description' => array( 'type' => 'string', ), 'layout' => $this->get_form_layout_schema(), 'children' => array( 'type' => 'array', 'items' => array( 'oneOf' => array( array( 'type' => 'string' ), // This object can have the shape of a form field itself, // allowing for recursive nesting of form fields. // There's no easy way to codify this recursion via the JSON Schema draft-04 // supported by the REST API. array( 'type' => 'object' ), ), ), ), ), ), ), ); } /** * Returns the schema for the form configuration object. * * @since 7.1.0 * * @return array Schema properties for the form configuration. */ protected function get_form_schema() { return array( 'layout' => $this->get_form_layout_schema(), 'fields' => array( 'type' => 'array', 'items' => $this->get_form_field_schema(), ), ); } }