From cd234e8641c6f727b3883ebc9039127b4a4bc105 Mon Sep 17 00:00:00 2001 From: Ramon Date: Thu, 15 Jun 2023 16:41:19 +1000 Subject: [PATCH] Templates API: return post modified datetime in response (#51362) * All these file changes are so that we can add `modified` to the template and template part objects in the rest response for WP_REST_Templates_Controller For theme templates, it will add the modified value when we have a post id (which is once the template is first save after user change) * Modified property is returned when updated. * Modified property is returned when created. * _wp_build_title_and_description_for_single_post_type_block_template and _wp_build_title_and_description_for_taxonomy_block_template have to be ported across too because they expect WP_Block_Template, not Gutenberg_Block_Template * Revert changes that required pulling all files across and using hooks to add the field to the response. * Now adding the value of `modified` to the response using the get_callback method on the register_rest_field filter, which is the way I should have done it in the first place :) * Formatting date according post_modified rules found in https://developer.wordpress.org/reference/classes/wp_rest_posts_controller/prepare_item_for_response/ * Whoops, forgot to wrap date expectations in mysql_to_rfc3339 * Update lib/compat/wordpress-6.3/rest-api.php Co-authored-by: Andrew Serong <14988353+andrewserong@users.noreply.github.com> --------- Co-authored-by: Andrew Serong <14988353+andrewserong@users.noreply.github.com> --- ...utenberg-rest-templates-controller-6-3.php | 4 +- lib/compat/wordpress-6.3/rest-api.php | 30 +++ ...tenberg-rest-templates-controller-test.php | 192 ++++++++++++++++-- 3 files changed, 211 insertions(+), 15 deletions(-) diff --git a/lib/compat/wordpress-6.3/class-gutenberg-rest-templates-controller-6-3.php b/lib/compat/wordpress-6.3/class-gutenberg-rest-templates-controller-6-3.php index cbe9b5242a248e..7d59ccc9c71fc1 100644 --- a/lib/compat/wordpress-6.3/class-gutenberg-rest-templates-controller-6-3.php +++ b/lib/compat/wordpress-6.3/class-gutenberg-rest-templates-controller-6-3.php @@ -69,6 +69,8 @@ public function get_template_fallback( $request ) { /** * Add revisions to the response. * + * @since 6.3.0 Added prepare_revision_links() method to get revision links. + * * @param WP_Block_Template $item Template instance. * @param WP_REST_Request $request Request object. * @return WP_REST_Response Response object. @@ -98,7 +100,7 @@ public function prepare_item_for_response( $item, $request ) { // phpcs:ignore V /** * Adds revisions to links. * - * @since 6.2.0 + * @since 6.3.0 * * @param WP_Block_Template $template Template instance. * @return array Links for the given post. diff --git a/lib/compat/wordpress-6.3/rest-api.php b/lib/compat/wordpress-6.3/rest-api.php index d571e56dae7628..9dfecc5ec2c530 100644 --- a/lib/compat/wordpress-6.3/rest-api.php +++ b/lib/compat/wordpress-6.3/rest-api.php @@ -53,3 +53,33 @@ function gutenberg_register_global_styles_endpoints() { } add_action( 'rest_api_init', 'gutenberg_register_global_styles_endpoints' ); +/** + * Add the `modified` value to the `wp_template` schema. + * + * @since 6.3.0 Added 'modified' property and response value. + */ +function add_modified_wp_template_schema() { + register_rest_field( + array( 'wp_template', 'wp_template_part' ), + 'modified', + array( + 'schema' => array( + 'description' => __( "The date the template was last modified, in the site's timezone.", 'gutenberg' ), + 'type' => 'string', + 'format' => 'date-time', + 'context' => array( 'view', 'edit' ), + 'readonly' => true, + ), + 'get_callback' => function( $object ) { + if ( ! empty( $object['wp_id'] ) ) { + $post = get_post( $object['wp_id'] ); + if ( $post && isset( $post->post_modified ) ) { + return mysql_to_rfc3339( $post->post_modified ); + } + } + return null; + }, + ) + ); +} +add_filter( 'rest_api_init', 'add_modified_wp_template_schema' ); diff --git a/phpunit/class-gutenberg-rest-templates-controller-test.php b/phpunit/class-gutenberg-rest-templates-controller-test.php index e86f9db5d848e1..0992399464c5c8 100644 --- a/phpunit/class-gutenberg-rest-templates-controller-test.php +++ b/phpunit/class-gutenberg-rest-templates-controller-test.php @@ -5,10 +5,22 @@ class Gutenberg_REST_Templates_Controller_Test extends WP_Test_REST_Controller_T * @var int */ protected static $admin_id; + private static $post; + + protected function find_and_normalize_template_by_id( $templates, $id ) { + foreach ( $templates as $template ) { + if ( $template['id'] === $id ) { + unset( $template['content'] ); + unset( $template['_links'] ); + return $template; + } + } + + return null; + } public function set_up() { parent::set_up(); - switch_theme( 'emptytheme' ); } /** @@ -17,11 +29,28 @@ public function set_up() { * @param WP_UnitTest_Factory $factory Helper that lets us create fake data. */ public static function wpSetupBeforeClass( $factory ) { + switch_theme( 'emptytheme' ); self::$admin_id = $factory->user->create( array( 'role' => 'administrator', ) ); + + // Set up template post. + $args = array( + 'post_type' => 'wp_template', + 'post_name' => 'my_template', + 'post_title' => 'My Template', + 'post_content' => 'Content', + 'post_excerpt' => 'Description of my template.', + 'tax_input' => array( + 'wp_theme' => array( + get_stylesheet(), + ), + ), + ); + self::$post = self::factory()->post->create_and_get( $args ); + wp_set_post_terms( self::$post->ID, get_stylesheet(), 'wp_theme' ); } public function test_register_routes() { @@ -63,42 +92,177 @@ public function test_get_template_fallback() { } /** - * @doesNotPerformAssertions + * @covers WP_REST_Templates_Controller::get_item_schema */ - public function test_context_param() {} + public function test_get_item_schema() { + $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/templates' ); + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + $properties = $data['schema']['properties']; + $this->assertCount( 15, $properties ); + $this->assertArrayHasKey( 'id', $properties ); + $this->assertArrayHasKey( 'description', $properties ); + $this->assertArrayHasKey( 'slug', $properties ); + $this->assertArrayHasKey( 'theme', $properties ); + $this->assertArrayHasKey( 'type', $properties ); + $this->assertArrayHasKey( 'source', $properties ); + $this->assertArrayHasKey( 'origin', $properties ); + $this->assertArrayHasKey( 'content', $properties ); + $this->assertArrayHasKey( 'title', $properties ); + $this->assertArrayHasKey( 'description', $properties ); + $this->assertArrayHasKey( 'status', $properties ); + $this->assertArrayHasKey( 'wp_id', $properties ); + $this->assertArrayHasKey( 'has_theme_file', $properties ); + $this->assertArrayHasKey( 'is_custom', $properties ); + $this->assertArrayHasKey( 'author', $properties ); + $this->assertArrayHasKey( 'modified', $properties ); + } /** - * @doesNotPerformAssertions + * @covers WP_REST_Templates_Controller::get_item */ - public function test_get_items() {} + public function test_get_item() { + wp_set_current_user( self::$admin_id ); + $request = new WP_REST_Request( 'GET', '/wp/v2/templates/emptytheme//my_template' ); + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + unset( $data['content'] ); + unset( $data['_links'] ); + + $this->assertSame( + array( + 'id' => 'emptytheme//my_template', + 'theme' => 'emptytheme', + 'slug' => 'my_template', + 'source' => 'custom', + 'origin' => null, + 'type' => 'wp_template', + 'description' => 'Description of my template.', + 'title' => array( + 'raw' => 'My Template', + 'rendered' => 'My Template', + ), + 'status' => 'publish', + 'wp_id' => self::$post->ID, + 'has_theme_file' => false, + 'is_custom' => true, + 'author' => 0, + 'modified' => mysql_to_rfc3339( self::$post->post_modified ), + ), + $data + ); + } /** - * @doesNotPerformAssertions + * @covers WP_REST_Templates_Controller::get_items */ - public function test_get_item() {} + public function test_get_items() { + wp_set_current_user( self::$admin_id ); + $request = new WP_REST_Request( 'GET', '/wp/v2/templates' ); + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + + $this->assertSame( + array( + 'id' => 'emptytheme//my_template', + 'theme' => 'emptytheme', + 'slug' => 'my_template', + 'source' => 'custom', + 'origin' => null, + 'type' => 'wp_template', + 'description' => 'Description of my template.', + 'title' => array( + 'raw' => 'My Template', + 'rendered' => 'My Template', + ), + 'status' => 'publish', + 'wp_id' => self::$post->ID, + 'has_theme_file' => false, + 'is_custom' => true, + 'author' => 0, + 'modified' => mysql_to_rfc3339( self::$post->post_modified ), + ), + $this->find_and_normalize_template_by_id( $data, 'emptytheme//my_template' ) + ); + } /** - * @doesNotPerformAssertions + * @covers WP_REST_Templates_Controller::update_item */ - public function test_create_item() {} + public function test_update_item() { + wp_set_current_user( self::$admin_id ); + $request = new WP_REST_Request( 'PUT', '/wp/v2/templates/emptytheme//my_template' ); + $request->set_body_params( + array( + 'title' => 'My new Index Title', + ) + ); + + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + $this->assertSame( 'My new Index Title', $data['title']['raw'] ); + $this->assertSame( 'custom', $data['source'] ); + $this->assertIsString( $data['modified'] ); + } /** - * @doesNotPerformAssertions + * @covers WP_REST_Templates_Controller::create_item */ - public function test_update_item() {} + public function test_create_item() { + wp_set_current_user( self::$admin_id ); + $request = new WP_REST_Request( 'POST', '/wp/v2/templates' ); + $request->set_body_params( + array( + 'slug' => 'my_custom_template', + 'description' => 'Just a description', + 'title' => 'My Template', + 'content' => 'Content', + 'author' => self::$admin_id, + ) + ); + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + unset( $data['_links'] ); + unset( $data['wp_id'] ); + + $this->assertSame( + array( + 'id' => 'emptytheme//my_custom_template', + 'theme' => 'emptytheme', + 'content' => array( + 'raw' => 'Content', + ), + 'slug' => 'my_custom_template', + 'source' => 'custom', + 'origin' => null, + 'type' => 'wp_template', + 'description' => 'Just a description', + 'title' => array( + 'raw' => 'My Template', + 'rendered' => 'My Template', + ), + 'status' => 'publish', + 'has_theme_file' => false, + 'is_custom' => true, + 'author' => self::$admin_id, + 'modified' => $data['modified'], + ), + $data + ); + } /** * @doesNotPerformAssertions */ - public function test_delete_item() {} + public function test_context_param() {} /** * @doesNotPerformAssertions */ - public function test_prepare_item() {} + public function test_delete_item() {} /** * @doesNotPerformAssertions */ - public function test_get_item_schema() {} + public function test_prepare_item() {} }