From de3ee0fde6f59228096fbeb0f1446cd00f25bf4c Mon Sep 17 00:00:00 2001 From: spacedmonkey Date: Tue, 13 Feb 2024 14:09:08 +0000 Subject: [PATCH] REST API: Refactor global styles endpoints in REST API to register with post type. Updated the global styles endpoints in the REST API to extend from existing posts and revisions controllers. This reduces duplicated code and inconsistencies. The revisions controller is now a subclass of the WP_REST_Revisions_Controller. Related redundant methods were removed and schema generation and collection parameters were adjusted to suit the global styles context. Updated permission checks, constructor, and collection parameters accordingly. This change allows for easy override of these classes using the `register_post_type_args` filter. Props ramonopoly, spacedmonkey, mukesh27. Fixes #60131. Built from https://develop.svn.wordpress.org/trunk@57624 git-svn-id: http://core.svn.wordpress.org/trunk@57125 1a063a9b-81f0-0310-95a4-ce76da25c4cd --- wp-includes/class-wp-post-type.php | 3 +- wp-includes/post.php | 26 +- wp-includes/rest-api.php | 8 - ...class-wp-rest-global-styles-controller.php | 119 ++------ ...est-global-styles-revisions-controller.php | 268 +++--------------- wp-includes/version.php | 2 +- wp-settings.php | 2 +- 7 files changed, 78 insertions(+), 350 deletions(-) diff --git a/wp-includes/class-wp-post-type.php b/wp-includes/class-wp-post-type.php index 7a2769ed88..9d22e2617b 100644 --- a/wp-includes/class-wp-post-type.php +++ b/wp-includes/class-wp-post-type.php @@ -913,6 +913,7 @@ final class WP_Post_Type { * Will only instantiate the controller class once per request. * * @since 6.4.0 + * @since 6.5.0 Prevents autosave class instantiation for wp_global_styles post types. * * @return WP_REST_Controller|null The controller instance, or null if the post type * is set not to show in rest. @@ -922,7 +923,7 @@ final class WP_Post_Type { return null; } - if ( 'attachment' === $this->name ) { + if ( in_array( $this->name, array( 'attachment', 'wp_global_styles' ), true ) ) { return null; } diff --git a/wp-includes/post.php b/wp-includes/post.php index 5fa058363a..c08e0ecb2e 100644 --- a/wp-includes/post.php +++ b/wp-includes/post.php @@ -473,15 +473,19 @@ function create_initial_post_types() { register_post_type( 'wp_global_styles', array( - 'label' => _x( 'Global Styles', 'post type general name' ), - 'description' => __( 'Global styles to include in themes.' ), - 'public' => false, - '_builtin' => true, /* internal use only. don't use this when registering your own post type. */ - '_edit_link' => '/site-editor.php?canvas=edit', /* internal use only. don't use this when registering your own post type. */ - 'show_ui' => false, - 'show_in_rest' => false, - 'rewrite' => false, - 'capabilities' => array( + 'label' => _x( 'Global Styles', 'post type general name' ), + 'description' => __( 'Global styles to include in themes.' ), + 'public' => false, + '_builtin' => true, /* internal use only. don't use this when registering your own post type. */ + '_edit_link' => '/site-editor.php?canvas=edit', /* internal use only. don't use this when registering your own post type. */ + 'show_ui' => false, + 'show_in_rest' => true, + 'rewrite' => false, + 'rest_base' => 'global-styles', + 'rest_controller_class' => 'WP_REST_Global_Styles_Controller', + 'revisions_rest_controller_class' => 'WP_REST_Global_Styles_Revisions_Controller', + 'late_route_registration' => true, + 'capabilities' => array( 'read' => 'edit_theme_options', 'create_posts' => 'edit_theme_options', 'edit_posts' => 'edit_theme_options', @@ -490,8 +494,8 @@ function create_initial_post_types() { 'edit_others_posts' => 'edit_theme_options', 'delete_others_posts' => 'edit_theme_options', ), - 'map_meta_cap' => true, - 'supports' => array( + 'map_meta_cap' => true, + 'supports' => array( 'title', 'editor', 'revisions', diff --git a/wp-includes/rest-api.php b/wp-includes/rest-api.php index b44b205afb..6014e93dbc 100644 --- a/wp-includes/rest-api.php +++ b/wp-includes/rest-api.php @@ -323,14 +323,6 @@ function create_initial_rest_routes() { $controller = new WP_REST_Block_Types_Controller(); $controller->register_routes(); - // Global Styles revisions. - $controller = new WP_REST_Global_Styles_Revisions_Controller(); - $controller->register_routes(); - - // Global Styles. - $controller = new WP_REST_Global_Styles_Controller(); - $controller->register_routes(); - // Settings. $controller = new WP_REST_Settings_Controller(); $controller->register_routes(); diff --git a/wp-includes/rest-api/endpoints/class-wp-rest-global-styles-controller.php b/wp-includes/rest-api/endpoints/class-wp-rest-global-styles-controller.php index 9837f535e2..9a0b687615 100644 --- a/wp-includes/rest-api/endpoints/class-wp-rest-global-styles-controller.php +++ b/wp-includes/rest-api/endpoints/class-wp-rest-global-styles-controller.php @@ -10,25 +10,14 @@ /** * Base Global Styles REST API Controller. */ -class WP_REST_Global_Styles_Controller extends WP_REST_Controller { - +class WP_REST_Global_Styles_Controller extends WP_REST_Posts_Controller { /** - * Post type. + * Whether the controller supports batching. * - * @since 5.9.0 - * @var string + * @since 6.5.0 + * @var array */ - protected $post_type; - - /** - * Constructor. - * @since 5.9.0 - */ - public function __construct() { - $this->namespace = 'wp/v2'; - $this->rest_base = 'global-styles'; - $this->post_type = 'wp_global_styles'; - } + protected $allow_batch = array( 'v1' => false ); /** * Registers the controllers routes. @@ -194,28 +183,10 @@ class WP_REST_Global_Styles_Controller extends WP_REST_Controller { * @param WP_Post $post Post object. * @return bool Whether the post can be read. */ - protected function check_read_permission( $post ) { + public function check_read_permission( $post ) { return current_user_can( 'read_post', $post->ID ); } - /** - * Returns the given global styles config. - * - * @since 5.9.0 - * - * @param WP_REST_Request $request The request instance. - * - * @return WP_REST_Response|WP_Error - */ - public function get_item( $request ) { - $post = $this->get_post( $request['id'] ); - if ( is_wp_error( $post ) ) { - return $post; - } - - return $this->prepare_item_for_response( $post, $request ); - } - /** * Checks if a given request has access to write a single global styles config. * @@ -241,55 +212,6 @@ class WP_REST_Global_Styles_Controller extends WP_REST_Controller { return true; } - /** - * Checks if a global style can be edited. - * - * @since 5.9.0 - * - * @param WP_Post $post Post object. - * @return bool Whether the post can be edited. - */ - protected function check_update_permission( $post ) { - return current_user_can( 'edit_post', $post->ID ); - } - - /** - * Updates a single global style config. - * - * @since 5.9.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 update_item( $request ) { - $post_before = $this->get_post( $request['id'] ); - if ( is_wp_error( $post_before ) ) { - return $post_before; - } - - $changes = $this->prepare_item_for_database( $request ); - if ( is_wp_error( $changes ) ) { - return $changes; - } - - $result = wp_update_post( wp_slash( (array) $changes ), true, false ); - if ( is_wp_error( $result ) ) { - return $result; - } - - $post = get_post( $request['id'] ); - $fields_update = $this->update_additional_fields_for_object( $post, $request ); - if ( is_wp_error( $fields_update ) ) { - return $fields_update; - } - - wp_after_insert_post( $post, true, $post_before ); - - $response = $this->prepare_item_for_response( $post, $request ); - - return rest_ensure_response( $response ); - } - /** * Prepares a single global styles config for update. * @@ -407,7 +329,7 @@ class WP_REST_Global_Styles_Controller extends WP_REST_Controller { $links = $this->prepare_links( $post->ID ); $response->add_links( $links ); if ( ! empty( $links['self']['href'] ) ) { - $actions = $this->get_available_actions(); + $actions = $this->get_available_actions( $post, $request ); $self = $links['self']['href']; foreach ( $actions as $rel ) { $response->add_link( $rel, $self ); @@ -431,9 +353,12 @@ class WP_REST_Global_Styles_Controller extends WP_REST_Controller { $base = sprintf( '%s/%s', $this->namespace, $this->rest_base ); $links = array( - 'self' => array( + 'self' => array( 'href' => rest_url( trailingslashit( $base ) . $id ), ), + 'about' => array( + 'href' => rest_url( 'wp/v2/types/' . $this->post_type ), + ), ); if ( post_type_supports( $this->post_type, 'revisions' ) ) { @@ -454,13 +379,16 @@ class WP_REST_Global_Styles_Controller extends WP_REST_Controller { * * @since 5.9.0 * @since 6.2.0 Added 'edit-css' action. + * @since 6.5.0 Added $post and $request parameters. * + * @param WP_Post $post Post object. + * @param WP_REST_Request $request Request object. * @return array List of link relations. */ - protected function get_available_actions() { + protected function get_available_actions( $post, $request ) { $rels = array(); - $post_type = get_post_type_object( $this->post_type ); + $post_type = get_post_type_object( $post->post_type ); if ( current_user_can( $post_type->cap->publish_posts ) ) { $rels[] = 'https://api.w.org/action-publish'; } @@ -472,21 +400,6 @@ class WP_REST_Global_Styles_Controller extends WP_REST_Controller { return $rels; } - /** - * Overwrites the default protected title format. - * - * By default, WordPress will show password protected posts with a title of - * "Protected: %s", as the REST API communicates the protected status of a post - * in a machine readable format, we remove the "Protected: " prefix. - * - * @since 5.9.0 - * - * @return string Protected title format. - */ - public function protected_title_format() { - return '%s'; - } - /** * Retrieves the query params for the global styles collection. * diff --git a/wp-includes/rest-api/endpoints/class-wp-rest-global-styles-revisions-controller.php b/wp-includes/rest-api/endpoints/class-wp-rest-global-styles-revisions-controller.php index db1be197e5..849f5be5cd 100644 --- a/wp-includes/rest-api/endpoints/class-wp-rest-global-styles-revisions-controller.php +++ b/wp-includes/rest-api/endpoints/class-wp-rest-global-styles-revisions-controller.php @@ -14,14 +14,14 @@ * * @see WP_REST_Controller */ -class WP_REST_Global_Styles_Revisions_Controller extends WP_REST_Controller { +class WP_REST_Global_Styles_Revisions_Controller extends WP_REST_Revisions_Controller { /** - * Parent post type. + * Parent controller. * - * @since 6.3.0 - * @var string + * @since 6.5.0 + * @var WP_REST_Controller */ - protected $parent_post_type; + private $parent_controller; /** * The base of the parent controller's route. @@ -35,12 +35,23 @@ class WP_REST_Global_Styles_Revisions_Controller extends WP_REST_Controller { * Constructor. * * @since 6.3.0 + * @since 6.5.0 Extends class from WP_REST_Revisions_Controller. + * + * @param string $parent_post_type Post type of the parent. */ - public function __construct() { - $this->parent_post_type = 'wp_global_styles'; - $this->rest_base = 'revisions'; - $this->parent_base = 'global-styles'; - $this->namespace = 'wp/v2'; + public function __construct( $parent_post_type ) { + parent::__construct( $parent_post_type ); + $post_type_object = get_post_type_object( $parent_post_type ); + $parent_controller = $post_type_object->get_rest_controller(); + + if ( ! $parent_controller ) { + $parent_controller = new WP_REST_Global_Styles_Controller( $parent_post_type ); + } + + $this->parent_controller = $parent_controller; + $this->rest_base = 'revisions'; + $this->parent_base = ! empty( $post_type_object->rest_base ) ? $post_type_object->rest_base : $post_type_object->name; + $this->namespace = ! empty( $post_type_object->rest_namespace ) ? $post_type_object->rest_namespace : 'wp/v2'; } /** @@ -63,7 +74,7 @@ class WP_REST_Global_Styles_Revisions_Controller extends WP_REST_Controller { array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get_items' ), - 'permission_callback' => array( $this, 'get_item_permissions_check' ), + 'permission_callback' => array( $this, 'get_items_permissions_check' ), 'args' => $this->get_collection_params(), ), 'schema' => array( $this, 'get_public_item_schema' ), @@ -97,29 +108,6 @@ class WP_REST_Global_Styles_Revisions_Controller extends WP_REST_Controller { ); } - /** - * Retrieves the query params for collections. - * - * Inherits from WP_REST_Controller::get_collection_params(), - * also reflects changes to return value WP_REST_Revisions_Controller::get_collection_params(). - * - * @since 6.3.0 - * - * @return array Collection parameters. - */ - public function get_collection_params() { - $collection_params = parent::get_collection_params(); - $collection_params['context']['default'] = 'view'; - $collection_params['offset'] = array( - 'description' => __( 'Offset the result set by a specific number of items.' ), - 'type' => 'integer', - ); - unset( $collection_params['search'] ); - unset( $collection_params['per_page']['default'] ); - - return $collection_params; - } - /** * Returns decoded JSON from post content string, * or a 404 if not found. @@ -268,80 +256,6 @@ class WP_REST_Global_Styles_Revisions_Controller extends WP_REST_Controller { return $response; } - /** - * Retrieves one global styles revision from the collection. - * - * @since 6.5.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_item( $request ) { - $parent = $this->get_parent( $request['parent'] ); - if ( is_wp_error( $parent ) ) { - return $parent; - } - - $revision = $this->get_revision( $request['id'] ); - if ( is_wp_error( $revision ) ) { - return $revision; - } - - $response = $this->prepare_item_for_response( $revision, $request ); - return rest_ensure_response( $response ); - } - - /** - * Gets the global styles revision, if the ID is valid. - * - * @since 6.5.0 - * - * @param int $id Supplied ID. - * @return WP_Post|WP_Error Revision post object if ID is valid, WP_Error otherwise. - */ - protected function get_revision( $id ) { - $error = new WP_Error( - 'rest_post_invalid_id', - __( 'Invalid global styles revision ID.' ), - array( 'status' => 404 ) - ); - - if ( (int) $id <= 0 ) { - return $error; - } - - $revision = get_post( (int) $id ); - if ( empty( $revision ) || empty( $revision->ID ) || 'revision' !== $revision->post_type ) { - return $error; - } - - return $revision; - } - - /** - * Checks the post_date_gmt or modified_gmt and prepare any post or - * modified date for single post output. - * - * Duplicate of WP_REST_Revisions_Controller::prepare_date_response. - * - * @since 6.3.0 - * - * @param string $date_gmt GMT publication time. - * @param string|null $date Optional. Local publication time. Default null. - * @return string|null ISO8601/RFC3339 formatted datetime, otherwise null. - */ - protected function prepare_date_response( $date_gmt, $date = null ) { - if ( '0000-00-00 00:00:00' === $date_gmt ) { - return null; - } - - if ( isset( $date ) ) { - return mysql_to_rfc3339( $date ); - } - - return mysql_to_rfc3339( $date_gmt ); - } - /** * Prepares the revision for the REST response. * @@ -411,6 +325,7 @@ class WP_REST_Global_Styles_Revisions_Controller extends WP_REST_Controller { * Retrieves the revision's schema, conforming to JSON Schema. * * @since 6.3.0 + * @since 6.5.0 Merged parent and parent controller schema data. * * @return array Item schema data. */ @@ -419,70 +334,15 @@ class WP_REST_Global_Styles_Revisions_Controller extends WP_REST_Controller { return $this->add_additional_fields_schema( $this->schema ); } - $schema = array( - '$schema' => 'http://json-schema.org/draft-04/schema#', - 'title' => "{$this->parent_post_type}-revision", - 'type' => 'object', - // Base properties for every revision. - 'properties' => array( + $schema = parent::get_item_schema(); + $parent_schema = $this->parent_controller->get_item_schema(); + $schema['properties'] = array_merge( $schema['properties'], $parent_schema['properties'] ); - /* - * Adds settings and styles from the WP_REST_Revisions_Controller item fields. - * Leaves out GUID as global styles shouldn't be accessible via URL. - */ - 'author' => array( - 'description' => __( 'The ID for the author of the revision.' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit', 'embed' ), - ), - 'date' => array( - 'description' => __( "The date the revision was published, in the site's timezone." ), - 'type' => 'string', - 'format' => 'date-time', - 'context' => array( 'view', 'edit', 'embed' ), - ), - 'date_gmt' => array( - 'description' => __( 'The date the revision was published, as GMT.' ), - 'type' => 'string', - 'format' => 'date-time', - 'context' => array( 'view', 'edit' ), - ), - 'id' => array( - 'description' => __( 'Unique identifier for the revision.' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit', 'embed' ), - ), - 'modified' => array( - 'description' => __( "The date the revision was last modified, in the site's timezone." ), - 'type' => 'string', - 'format' => 'date-time', - 'context' => array( 'view', 'edit' ), - ), - 'modified_gmt' => array( - 'description' => __( 'The date the revision was last modified, as GMT.' ), - 'type' => 'string', - 'format' => 'date-time', - 'context' => array( 'view', 'edit' ), - ), - 'parent' => array( - 'description' => __( 'The ID for the parent of the revision.' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit', 'embed' ), - ), - - // Adds settings and styles from the WP_REST_Global_Styles_Controller parent schema. - 'styles' => array( - 'description' => __( 'Global styles.' ), - 'type' => array( 'object' ), - 'context' => array( 'view', 'edit' ), - ), - 'settings' => array( - 'description' => __( 'Global settings.' ), - 'type' => array( 'object' ), - 'context' => array( 'view', 'edit' ), - ), - ), - ); + unset( $schema['properties']['guid'] ); + unset( $schema['properties']['slug'] ); + unset( $schema['properties']['meta'] ); + unset( $schema['properties']['content'] ); + unset( $schema['properties']['title'] ); $this->schema = $schema; @@ -490,62 +350,20 @@ class WP_REST_Global_Styles_Revisions_Controller extends WP_REST_Controller { } /** - * Checks if a given request has access to read a single global style. + * Retrieves the query params for collections. + * Removes params that are not supported by global styles revisions. * - * @since 6.3.0 + * @since 6.5.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. + * @return array Collection parameters. */ - public function get_item_permissions_check( $request ) { - $post = $this->get_parent( $request['parent'] ); - if ( is_wp_error( $post ) ) { - return $post; - } - - /* - * The same check as WP_REST_Global_Styles_Controller::get_item_permissions_check. - */ - if ( ! current_user_can( 'read_post', $post->ID ) ) { - return new WP_Error( - 'rest_cannot_view', - __( 'Sorry, you are not allowed to view revisions for this global style.' ), - array( 'status' => rest_authorization_required_code() ) - ); - } - - return true; - } - - /** - * Gets the parent post, if the ID is valid. - * - * Duplicate of WP_REST_Revisions_Controller::get_parent. - * - * @since 6.3.0 - * - * @param int $parent_post_id Supplied ID. - * @return WP_Post|WP_Error Post object if ID is valid, WP_Error otherwise. - */ - protected function get_parent( $parent_post_id ) { - $error = new WP_Error( - 'rest_post_invalid_parent', - __( 'Invalid post parent ID.' ), - array( 'status' => 404 ) - ); - - if ( (int) $parent_post_id <= 0 ) { - return $error; - } - - $parent_post = get_post( (int) $parent_post_id ); - - if ( empty( $parent_post ) || empty( $parent_post->ID ) - || $this->parent_post_type !== $parent_post->post_type - ) { - return $error; - } - - return $parent_post; + public function get_collection_params() { + $query_params = parent::get_collection_params(); + unset( $query_params['exclude'] ); + unset( $query_params['include'] ); + unset( $query_params['search'] ); + unset( $query_params['order'] ); + unset( $query_params['orderby'] ); + return $query_params; } } diff --git a/wp-includes/version.php b/wp-includes/version.php index 6581e9cd0a..3fb2180595 100644 --- a/wp-includes/version.php +++ b/wp-includes/version.php @@ -16,7 +16,7 @@ * * @global string $wp_version */ -$wp_version = '6.5-alpha-57623'; +$wp_version = '6.5-alpha-57624'; /** * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema. diff --git a/wp-settings.php b/wp-settings.php index 2772568dee..c3c080e5fe 100644 --- a/wp-settings.php +++ b/wp-settings.php @@ -276,10 +276,10 @@ require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-controller.php'; require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-posts-controller.php'; require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-attachments-controller.php'; require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-global-styles-controller.php'; -require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-global-styles-revisions-controller.php'; require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-post-types-controller.php'; require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-post-statuses-controller.php'; require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-revisions-controller.php'; +require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-global-styles-revisions-controller.php'; require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-template-revisions-controller.php'; require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-autosaves-controller.php'; require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-template-autosaves-controller.php';