Skip to content

Commit

Permalink
Global Styles: Update REST controller override method and backport ch…
Browse files Browse the repository at this point in the history
…anges from Core (#65259)

Global Styles: Update REST controller override method: Update unit tests; Update class with Core's WP_REST_Global_Styles_Controller; Inherit WP_REST_Posts_Controller, make methods public et. al; Add 6.6+ post args to register post filter; Overwrite Core endpoints with $override when registering routes.
---------

Co-authored-by: Mamaduka <mamaduka@git.wordpress.org>
Co-authored-by: ramonjd <ramonopoly@git.wordpress.org>
Co-authored-by: aaronrobertshaw <aaronrobertshaw@git.wordpress.org>
Co-authored-by: spacedmonkey <spacedmonkey@git.wordpress.org>
  • Loading branch information
5 people authored Sep 17, 2024
1 parent 788bb9e commit 46d898e
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 114 deletions.
163 changes: 57 additions & 106 deletions lib/class-wp-rest-global-styles-controller-gutenberg.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,30 @@
/**
* Base Global Styles REST API Controller.
*/
class WP_REST_Global_Styles_Controller_Gutenberg extends WP_REST_Controller {
class WP_REST_Global_Styles_Controller_Gutenberg extends WP_REST_Posts_Controller {

/**
* Post type.
* Whether the controller supports batching.
*
* @since 5.9.0
* @var string
* @since 6.6.0
* @var array
*/
protected $post_type;
protected $allow_batch = array( 'v1' => false );

/**
* Constructor.
*
* @since 5.9.0
*/
public function __construct() {
$this->namespace = 'wp/v2';
$this->rest_base = 'global-styles';
$this->post_type = 'wp_global_styles';
/**
* Constructor.
*
* @since 6.6.0
*
* @param string $post_type Post type.
*/
public function __construct( $post_type = 'wp_global_styles' ) {
parent::__construct( $post_type );
}

/**
Expand All @@ -54,8 +59,14 @@ public function register_routes() {
'type' => 'string',
),
),
'allow_batch' => $this->allow_batch,
),
)
),
/*
* $override is set to true to avoid conflicts with the core endpoint.
* Do not sync to WordPress core.
*/
true
);

// List themes global styles.
Expand All @@ -65,8 +76,10 @@ public function register_routes() {
sprintf(
'/%s/themes/(?P<stylesheet>%s)',
$this->rest_base,
// Matches theme's directory: `/themes/<subdirectory>/<theme>/` or `/themes/<theme>/`.
// Excludes invalid directory name characters: `/:<>*?"|`.
/*
* Matches theme's directory: `/themes/<subdirectory>/<theme>/` or `/themes/<theme>/`.
* Excludes invalid directory name characters: `/:<>*?"|`.
*/
'[^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?'
),
array(
Expand All @@ -81,8 +94,14 @@ public function register_routes() {
'sanitize_callback' => array( $this, '_sanitize_global_styles_callback' ),
),
),
'allow_batch' => $this->allow_batch,
),
)
),
/*
* $override is set to true to avoid conflicts with the core endpoint.
* Do not sync to WordPress core.
*/
true
);

// Lists/updates a single global style variation based on the given id.
Expand All @@ -108,8 +127,14 @@ public function register_routes() {
'permission_callback' => array( $this, 'update_item_permissions_check' ),
'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
),
'schema' => array( $this, 'get_public_item_schema' ),
)
'schema' => array( $this, 'get_public_item_schema' ),
'allow_batch' => $this->allow_batch,
),
/*
* $override is set to true to avoid conflicts with the core endpoint.
* Do not sync to WordPress core.
*/
true
);
}

Expand Down Expand Up @@ -196,28 +221,10 @@ public function get_item_permissions_check( $request ) {
* @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.
*
Expand All @@ -243,61 +250,12 @@ public function update_item_permissions_check( $request ) {
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
* @since 6.2.0 Added validation of styles.css property.
*
* @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.
*
* @since 5.9.0
* @since 6.2.0 Added validation of styles.css property.
* @since 6.6.0 Added registration of block style variations from theme.json sources (theme.json, user theme.json, partials).
*
* @param WP_REST_Request $request Request object.
* @return stdClass|WP_Error Prepared item on success. WP_Error on when the custom CSS is not valid.
Expand Down Expand Up @@ -394,10 +352,12 @@ public function prepare_item_for_response( $post, $request ) { // phpcs:ignore V
}
if ( rest_is_field_included( 'title.rendered', $fields ) ) {
add_filter( 'protected_title_format', array( $this, 'protected_title_format' ) );
add_filter( 'private_title_format', array( $this, 'protected_title_format' ) );

$data['title']['rendered'] = get_the_title( $post->ID );

remove_filter( 'protected_title_format', array( $this, 'protected_title_format' ) );
remove_filter( 'private_title_format', array( $this, 'protected_title_format' ) );
}

if ( rest_is_field_included( 'settings', $fields ) ) {
Expand Down Expand Up @@ -426,7 +386,7 @@ public function prepare_item_for_response( $post, $request ) { // phpcs:ignore V
}
$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 );
Expand All @@ -450,9 +410,12 @@ protected function prepare_links( $id ) {
$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' ) ) {
Expand All @@ -473,13 +436,16 @@ protected function prepare_links( $id ) {
*
* @since 5.9.0
* @since 6.2.0 Added 'edit-css' action.
* @since 6.6.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 ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
$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';
}
Expand All @@ -491,21 +457,6 @@ protected function get_available_actions() {
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.
*
Expand Down Expand Up @@ -589,7 +540,7 @@ public function get_theme_item_permissions_check( $request ) { // phpcs:ignore V

/*
* Verify if the current user has edit_theme_options capability.
* This capability is required to edit/view/delete templates.
* This capability is required to edit/view/delete global styles.
*/
if ( ! current_user_can( 'edit_theme_options' ) ) {
return new WP_Error(
Expand Down Expand Up @@ -623,8 +574,8 @@ public function get_theme_item( $request ) {
}

$theme = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data( 'theme' );
$data = array();
$fields = $this->get_fields_for_response( $request );
$data = array();

if ( rest_is_field_included( 'settings', $fields ) ) {
$data['settings'] = $theme->get_settings();
Expand Down Expand Up @@ -669,7 +620,7 @@ public function get_theme_items_permissions_check( $request ) { // phpcs:ignore

/*
* Verify if the current user has edit_theme_options capability.
* This capability is required to edit/view/delete templates.
* This capability is required to edit/view/delete global styles.
*/
if ( ! current_user_can( 'edit_theme_options' ) ) {
return new WP_Error(
Expand Down
22 changes: 16 additions & 6 deletions lib/rest-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,24 @@
}

/**
* Registers the Global Styles REST API routes.
* Overrides the REST controller for the `wp_global_styles` post type.
*
* @param array $args Array of arguments for registering a post type.
* See the register_post_type() function for accepted arguments.
* @param string $post_type Post type key.
*
* @return array Array of arguments for registering a post type.
*/
function gutenberg_register_global_styles_endpoints() {
$global_styles_controller = new WP_REST_Global_Styles_Controller_Gutenberg();
$global_styles_controller->register_routes();
}
add_action( 'rest_api_init', 'gutenberg_register_global_styles_endpoints' );
function gutenberg_override_global_styles_endpoint( array $args ): array {
$args['rest_controller_class'] = 'WP_REST_Global_Styles_Controller_Gutenberg';
$args['revisions_rest_controller_class'] = 'Gutenberg_REST_Global_Styles_Revisions_Controller_6_6';
$args['late_route_registration'] = true;
$args['show_in_rest'] = true;
$args['rest_base'] = 'global-styles';

return $args;
}
add_filter( 'register_wp_global_styles_post_type_args', 'gutenberg_override_global_styles_endpoint', 10, 2 );

/**
* Registers the Edit Site Export REST API routes.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,14 +79,18 @@ public static function wpTearDownAfterClass() {
* @covers WP_REST_Global_Styles_Controller_Gutenberg::register_routes
*/
public function test_register_routes() {
// Register routes so that they overwrite identical Core routes.
$global_styles_controller = new WP_REST_Global_Styles_Controller_Gutenberg();
$global_styles_controller->register_routes();

$routes = rest_get_server()->get_routes();
$this->assertArrayHasKey(
'/wp/v2/global-styles/(?P<id>[\/\w-]+)',
$routes,
'Single global style based on the given ID route does not exist'
);
$this->assertCount(
4, // Double core because both sets get registered in the plugin.
2,
$routes['/wp/v2/global-styles/(?P<id>[\/\w-]+)'],
'Single global style based on the given ID route does not have exactly two elements'
);
Expand All @@ -96,7 +100,7 @@ public function test_register_routes() {
'Theme global styles route does not exist'
);
$this->assertCount(
2, // Double core because both sets get registered in the plugin.
1,
$routes['/wp/v2/global-styles/themes/(?P<stylesheet>[^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)'],
'Theme global styles route does not have exactly one element'
);
Expand Down

0 comments on commit 46d898e

Please sign in to comment.