Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Adds an endpoint that returns revisions for the global styles custom …
…post.

Adds tests
  • Loading branch information
ramonjd committed Jun 26, 2023
commit e421e6fae2cc977abaf61af73e06338e0ebebcc9
24 changes: 13 additions & 11 deletions src/wp-includes/post.php
Original file line number Diff line number Diff line change
Expand Up @@ -465,15 +465,15 @@ 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' => false,
'rewrite' => false,
'capabilities' => array(
'read' => 'edit_theme_options',
'create_posts' => 'edit_theme_options',
'edit_posts' => 'edit_theme_options',
Expand All @@ -482,8 +482,10 @@ function create_initial_post_types() {
'edit_others_posts' => 'edit_theme_options',
'delete_others_posts' => 'edit_theme_options',
),
'map_meta_cap' => true,
'supports' => array(
'rest_base' => 'global-styles',
'rest_controller_class' => 'WP_REST_Global_Styles_Controller',
'map_meta_cap' => true,
'supports' => array(
'title',
'editor',
'revisions',
Expand Down
4 changes: 4 additions & 0 deletions src/wp-includes/rest-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,10 @@ 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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,7 @@ public function prepare_item_for_response( $post, $request ) { // phpcs:ignore V
* Prepares links for the request.
*
* @since 5.9.0
* @since 6.3.0 Adds revisions count and rest URL href to version-history.
*
* @param integer $id ID.
* @return array Links for the given post.
Expand All @@ -433,6 +434,16 @@ protected function prepare_links( $id ) {
),
);

if ( post_type_supports( $this->post_type, 'revisions' ) ) {
$revisions = wp_get_latest_revision_id_and_total_count( $id );
$revisions_count = ! is_wp_error( $revisions ) ? $revisions['count'] : 0;
$revisions_base = sprintf( '/%s/%s/%d/revisions', $this->namespace, $this->rest_base, $id );
$links['version-history'] = array(
'href' => rest_url( $revisions_base ),
'count' => $revisions_count,
);
}

return $links;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,311 @@
<?php
/**
* REST API: WP_REST_Global_Styles_Revisions_Controller class
*
* @package WordPress
* @subpackage REST_API
* @since 6.3.0
*/

/**
* Core class used to access global styles revisions via the REST API.
*
* @since 6.3.0
*
* @see WP_REST_Controller
*/
class WP_REST_Global_Styles_Revisions_Controller extends WP_REST_Controller {
/**
* Parent post type.
*
* @since 6.3.0
* @var string
*/
private $parent_post_type;

/**
* The base of the parent controller's route.
*
* @since 6.3.0
* @var string
*/
private $parent_base;

/**
* Constructor.
*
* @since 6.3.0
*/
public function __construct() {
$this->parent_post_type = 'wp_global_styles';
$this->rest_base = 'revisions';
$this->parent_base = 'global-styles';
$this->namespace = 'wp/v2';
}

/**
* Registers the controllers routes.
*
* @return void
*/
public function register_routes() {
register_rest_route(
$this->namespace,
'/' . $this->parent_base . '/(?P<parent>[\d]+)/' . $this->rest_base,
array(
'args' => array(
'parent' => array(
'description' => __( 'The ID for the parent of the revision.' ),
'type' => 'integer',
),
),
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'get_items' ),
'permission_callback' => array( $this, 'get_item_permissions_check' ),
),
'schema' => array( $this, 'get_public_item_schema' ),
)
);
}

/**
* Returns revisions of the given global styles config custom post type.
*
* @since 6.3.0
*
* @param WP_REST_Request $request The request instance.
*
* @return WP_REST_Response|WP_Error
*/
public function get_items( $request ) {
$parent = $this->get_parent( $request['parent'] );

if ( is_wp_error( $parent ) ) {
return $parent;
}
$response = array();
$raw_config = json_decode( $parent->post_content, true );
$is_global_styles_user_theme_json = isset( $raw_config['isGlobalStylesUserThemeJSON'] ) && true === $raw_config['isGlobalStylesUserThemeJSON'];

if ( $is_global_styles_user_theme_json ) {
$user_theme_revisions = wp_get_post_revisions(
$parent->ID,
array(
'posts_per_page' => 100,
)
);

if ( ! empty( $user_theme_revisions ) ) {
foreach ( $user_theme_revisions as $revision ) {
$revision = $this->prepare_item_for_response( $revision, $request );
$response[] = $this->prepare_response_for_collection( $revision );
}
}
}

return rest_ensure_response( $response );
}

/**
* Prepares the revision for the REST response.
*
* @since 6.3.0
*
* @param WP_Post $item Post revision object.
* @param WP_REST_Request $request Request object.
* @return WP_REST_Response Response object.
*/
public function prepare_item_for_response( $item, $request ) {
$parent = $this->get_parent( $request['parent'] );
// Retrieves global styles config as JSON.
$raw_revision_config = json_decode( $item->post_content, true );
$config = ( new WP_Theme_JSON( $raw_revision_config, 'custom' ) )->get_raw_data();

// Prepares item data.
$data = array();
$fields = $this->get_fields_for_response( $request );

if ( rest_is_field_included( 'author', $fields ) ) {
$data['author'] = (int) $item->post_author;
}

if ( rest_is_field_included( 'date', $fields ) ) {
$data['date'] = $item->post_date;
}

if ( rest_is_field_included( 'date_gmt', $fields ) ) {
$data['date_gmt'] = $item->post_date_gmt;
}

if ( rest_is_field_included( 'id', $fields ) ) {
$data['id'] = (int) $item->ID;
}

if ( rest_is_field_included( 'modified', $fields ) ) {
$data['modified'] = $item->post_modified;
}

if ( rest_is_field_included( 'modified_gmt', $fields ) ) {
$data['modified_gmt'] = $item->post_modified_gmt;
}

if ( rest_is_field_included( 'parent', $fields ) ) {
$data['parent'] = (int) $parent->ID;
}

if ( rest_is_field_included( 'settings', $fields ) ) {
$data['settings'] = ! empty( $config['settings'] ) ? $config['settings'] : new stdClass();
}

if ( rest_is_field_included( 'styles', $fields ) ) {
$data['styles'] = ! empty( $config['styles'] ) ? $config['styles'] : new stdClass();
}

$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
$data = $this->add_additional_fields_to_object( $data, $request );
$data = $this->filter_response_by_context( $data, $context );

return rest_ensure_response( $data );
}

/**
* Retrieves the revision's schema, conforming to JSON Schema.
*
* @since 6.3.0
*
* @return array Item schema data.
*/
public function get_item_schema() {
if ( $this->schema ) {
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(

/*
* 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' ),
),
),
);

$this->schema = $schema;

return $this->add_additional_fields_schema( $this->schema );
}

/**
* Checks if a given request has access to read a single global style.
*
* @since 6.3.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_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;
}

/**
* Get the parent post, if the ID is valid. Copied from WP_REST_Revisions_Controller.
*
* @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;
}
}
1 change: 1 addition & 0 deletions src/wp-settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,7 @@
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';
Expand Down
Loading