From 817da8e61136e18f1618cf2fe9df8d601a91dc21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Maneiro?= <583546+oandregal@users.noreply.github.com> Date: Tue, 17 Mar 2026 12:24:06 +0100 Subject: [PATCH] Bootstrap entity config: view config endpoint --- src/wp-includes/rest-api.php | 4 + .../class-wp-rest-view-config-controller.php | 395 ++++++++++++++++++ src/wp-settings.php | 1 + 3 files changed, 400 insertions(+) create mode 100644 src/wp-includes/rest-api/endpoints/class-wp-rest-view-config-controller.php diff --git a/src/wp-includes/rest-api.php b/src/wp-includes/rest-api.php index df7f262d3aa58..57d3d99efd412 100644 --- a/src/wp-includes/rest-api.php +++ b/src/wp-includes/rest-api.php @@ -429,6 +429,10 @@ function create_initial_rest_routes() { $icons_controller = new WP_REST_Icons_Controller(); $icons_controller->register_routes(); + // View Config. + $view_config_controller = new WP_REST_View_Config_Controller(); + $view_config_controller->register_routes(); + // Collaboration. if ( get_option( 'wp_enable_real_time_collaboration' ) ) { $sync_storage = new WP_Sync_Post_Meta_Storage(); diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-view-config-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-view-config-controller.php new file mode 100644 index 0000000000000..705a927946098 --- /dev/null +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-view-config-controller.php @@ -0,0 +1,395 @@ +namespace = 'wp/v2'; + $this->rest_base = 'view-config'; + } + + /** + * Registers the routes for the controller. + */ + 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 ) { + if ( ! current_user_can( 'edit_posts' ) ) { + return new WP_Error( + 'rest_cannot_read', + __( 'Sorry, you are not allowed to read view config.' ), + array( 'status' => rest_authorization_required_code() ) + ); + } + + return true; + } + + /** + * 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' ); + + // TODO: this data will come from a registry of view configs per entity. + $default_view = array( + 'type' => 'table', + 'filters' => array(), + 'perPage' => 20, + 'sort' => array( + 'field' => 'title', + 'direction' => 'asc', + ), + 'titleField' => 'title', + 'fields' => array( 'author', 'status' ), + ); + $default_layouts = array( + 'table' => array(), + 'grid' => array(), + 'list' => array(), + ); + $all_items_title = __( 'All items' ); + if ( 'postType' === $kind ) { + $post_type_object = get_post_type_object( $name ); + if ( $post_type_object && ! empty( $post_type_object->labels->all_items ) ) { + $all_items_title = $post_type_object->labels->all_items; + } + } + $view_list = array( + array( + 'title' => $all_items_title, + 'slug' => 'all', + ), + ); + if ( 'postType' === $kind && 'page' === $name ) { + $default_view = array( + 'type' => 'list', + 'filters' => array(), + 'perPage' => 20, + 'sort' => array( + 'field' => 'title', + 'direction' => 'asc', + ), + 'showLevels' => true, + 'titleField' => 'title', + 'mediaField' => 'featured_media', + 'fields' => array( 'author', 'status' ), + ); + $default_layouts = array( + 'table' => array( + 'layout' => array( + 'styles' => array( + 'author' => array( + 'align' => 'start', + ), + ), + ), + ), + 'grid' => array(), + 'list' => array(), + ); + $view_list = array( + array( + 'title' => $all_items_title, + 'slug' => 'all', + ), + array( + 'title' => __( 'Published' ), + 'slug' => 'published', + 'view' => array( + 'filters' => array( + array( + 'field' => 'status', + 'operator' => 'isAny', + 'value' => 'publish', + 'isLocked' => true, + ), + ), + ), + ), + array( + 'title' => __( 'Scheduled' ), + 'slug' => 'future', + 'view' => array( + 'filters' => array( + array( + 'field' => 'status', + 'operator' => 'isAny', + 'value' => 'future', + 'isLocked' => true, + ), + ), + ), + ), + array( + 'title' => __( 'Drafts' ), + 'slug' => 'drafts', + 'view' => array( + 'filters' => array( + array( + 'field' => 'status', + 'operator' => 'isAny', + 'value' => 'draft', + 'isLocked' => true, + ), + ), + ), + ), + array( + 'title' => __( 'Pending' ), + 'slug' => 'pending', + 'view' => array( + 'filters' => array( + array( + 'field' => 'status', + 'operator' => 'isAny', + 'value' => 'pending', + 'isLocked' => true, + ), + ), + ), + ), + array( + 'title' => __( 'Private' ), + 'slug' => 'private', + 'view' => array( + 'filters' => array( + array( + 'field' => 'status', + 'operator' => 'isAny', + 'value' => 'private', + 'isLocked' => true, + ), + ), + ), + ), + array( + 'title' => __( 'Trash' ), + 'slug' => 'trash', + 'view' => array( + 'type' => 'table', + 'layout' => isset( $default_layouts['table']['layout'] ) ? $default_layouts['table']['layout'] : array(), + 'filters' => array( + array( + 'field' => 'status', + 'operator' => 'isAny', + 'value' => 'trash', + 'isLocked' => true, + ), + ), + ), + ), + ); + } + + $response = array( + 'kind' => $kind, + 'name' => $name, + 'default_view' => $default_view, + 'default_layouts' => $default_layouts, + 'view_list' => $view_list, + ); + + return rest_ensure_response( $response ); + } + + /** + * 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 ); + } + + $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, + ), + 'default_view' => array( + 'description' => __( 'Default view configuration.' ), + 'type' => 'object', + 'readonly' => true, + 'properties' => array( + 'type' => array( + 'type' => 'string', + ), + 'search' => array( + 'type' => 'string', + ), + 'filters' => array( + 'type' => 'array', + 'items' => array( + 'type' => 'object', + ), + ), + 'sort' => array( + 'type' => 'object', + 'properties' => array( + 'field' => array( + 'type' => 'string', + ), + 'direction' => array( + 'type' => 'string', + 'enum' => array( 'asc', 'desc' ), + ), + ), + ), + 'page' => array( + 'type' => 'integer', + ), + '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', + ), + ), + ), + 'default_layouts' => array( + 'description' => __( 'Default layout configurations.' ), + 'type' => 'object', + 'readonly' => true, + 'additionalProperties' => array( + 'type' => 'object', + ), + ), + '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', + ), + ), + ), + ), + ), + ); + + return $this->add_additional_fields_schema( $this->schema ); + } +} diff --git a/src/wp-settings.php b/src/wp-settings.php index dab1d8fd4c0de..7f32efc320111 100644 --- a/src/wp-settings.php +++ b/src/wp-settings.php @@ -360,6 +360,7 @@ require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-font-faces-controller.php'; require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-font-collections-controller.php'; require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-icons-controller.php'; +require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-view-config-controller.php'; require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-abilities-v1-categories-controller.php'; require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-abilities-v1-list-controller.php'; require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-abilities-v1-run-controller.php';