From 8f87db53e5a76cb9326e6a620fcb9f34199456bc Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 24 Nov 2021 15:37:51 +0800 Subject: [PATCH 01/10] Add support to post types --- src/wp-includes/post.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php index a51d616416e03..f7fe8d74516e6 100644 --- a/src/wp-includes/post.php +++ b/src/wp-includes/post.php @@ -384,6 +384,7 @@ function create_initial_post_types() { 'excerpt', 'editor', 'revisions', + 'author', ), ) ); @@ -442,6 +443,7 @@ function create_initial_post_types() { 'excerpt', 'editor', 'revisions', + 'author', ), ) ); From 77a5e680bfa0088411c1cddf036b831ef559ca3c Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 24 Nov 2021 16:09:03 +0800 Subject: [PATCH 02/10] Add author support to templates REST API controller --- src/wp-includes/block-template-utils.php | 1 + src/wp-includes/class-wp-block-template.php | 9 +++++++ .../class-wp-rest-templates-controller.php | 27 +++++++++++++++++++ 3 files changed, 37 insertions(+) diff --git a/src/wp-includes/block-template-utils.php b/src/wp-includes/block-template-utils.php index 9b248c1453945..7cf41c37ddc75 100644 --- a/src/wp-includes/block-template-utils.php +++ b/src/wp-includes/block-template-utils.php @@ -516,6 +516,7 @@ function _build_block_template_result_from_post( $post ) { $template->status = $post->post_status; $template->has_theme_file = $has_theme_file; $template->is_custom = true; + $template->post_author = $post->post_author; if ( 'wp_template' === $post->post_type && isset( $default_template_types[ $template->slug ] ) ) { $template->is_custom = false; diff --git a/src/wp-includes/class-wp-block-template.php b/src/wp-includes/class-wp-block-template.php index 3ea090830f51f..9a54b4d97d597 100644 --- a/src/wp-includes/class-wp-block-template.php +++ b/src/wp-includes/class-wp-block-template.php @@ -69,6 +69,7 @@ class WP_Block_Template { */ public $description = ''; + /** * Source of the content. `theme` and `custom` is used for now. * @@ -109,4 +110,12 @@ class WP_Block_Template { * @var bool */ public $is_custom = true; + + /** + * Author. + * + * @since 5.9.0 + * @var int + */ + public $post_author = 0; } diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-templates-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-templates-controller.php index 951c8232dde90..e75273188c232 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-templates-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-templates-controller.php @@ -461,6 +461,24 @@ protected function prepare_item_for_database( $request ) { } } + if ( ! empty( $request['author'] ) ) { + $post_author = (int) $request['author']; + + if ( get_current_user_id() !== $post_author ) { + $user_obj = get_userdata( $post_author ); + + if ( ! $user_obj ) { + return new WP_Error( + 'rest_invalid_author', + __( 'Invalid author ID.' ), + array( 'status' => 400 ) + ); + } + } + + $changes->post_author = $post_author; + } + return $changes; } @@ -547,6 +565,10 @@ public function prepare_item_for_response( $item, $request ) { // phpcs:ignore V $data['has_theme_file'] = (bool) $template->has_theme_file; } + if ( rest_is_field_included( 'author', $fields ) ) { + $data['author'] = (int) $template->post_author; + } + if ( rest_is_field_included( 'area', $fields ) && 'wp_template_part' === $template->type ) { $data['area'] = $template->area; } @@ -758,6 +780,11 @@ public function get_item_schema() { 'context' => array( 'embed', 'view', 'edit' ), 'readonly' => true, ), + 'author' => array ( + 'description' => __( 'The ID for the author of the template.' ), + 'type' => 'integer', + 'context' => array( 'view', 'edit', 'embed' ), + ), ), ); From bdb04b39d7a8b5d56b879d404be2de2d63e94f86 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 24 Nov 2021 17:11:39 +0800 Subject: [PATCH 03/10] Update unit tests --- .../rest-api/wpRestTemplatesController.php | 25 +++++++++++++++- tests/qunit/fixtures/wp-api-generated.js | 30 +++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/tests/phpunit/tests/rest-api/wpRestTemplatesController.php b/tests/phpunit/tests/rest-api/wpRestTemplatesController.php index 3d1699e16f4f8..d082d6a809509 100644 --- a/tests/phpunit/tests/rest-api/wpRestTemplatesController.php +++ b/tests/phpunit/tests/rest-api/wpRestTemplatesController.php @@ -102,6 +102,7 @@ public function test_get_items() { 'status' => 'publish', 'wp_id' => self::$post->ID, 'has_theme_file' => false, + 'author' => 0, ), $this->find_and_normalize_template_by_id( $data, 'default//my_template' ) ); @@ -143,6 +144,7 @@ public function test_get_item() { 'status' => 'publish', 'wp_id' => self::$post->ID, 'has_theme_file' => false, + 'author' => 0, ), $data ); @@ -161,6 +163,7 @@ public function test_create_item() { 'description' => 'Just a description', 'title' => 'My Template', 'content' => 'Content', + 'author' => self::$admin_id, ) ); $response = rest_get_server()->dispatch( $request ); @@ -185,6 +188,7 @@ public function test_create_item() { ), 'status' => 'publish', 'has_theme_file' => false, + 'author' => self::$admin_id, ), $data ); @@ -207,6 +211,7 @@ public function test_create_item_raw() { 'content' => array( 'raw' => 'Content', ), + 'author' => self::$admin_id, ) ); $response = rest_get_server()->dispatch( $request ); @@ -231,11 +236,28 @@ public function test_create_item_raw() { ), 'status' => 'publish', 'has_theme_file' => false, + 'author' => self::$admin_id, ), $data ); } + public function test_create_item_invalid_author() { + 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_invalid_author', + 'description' => 'Just a description', + 'title' => 'My Template', + 'content' => 'Content', + 'author' => -999, + ) + ); + $response = rest_get_server()->dispatch( $request ); + $this->assertErrorResponse( 'rest_invalid_author', $response, 400 ); + } + /** * @covers WP_REST_Templates_Controller::update_item */ @@ -370,7 +392,7 @@ public function test_get_item_schema() { $response = rest_get_server()->dispatch( $request ); $data = $response->get_data(); $properties = $data['schema']['properties']; - $this->assertCount( 11, $properties ); + $this->assertCount( 12, $properties ); $this->assertArrayHasKey( 'id', $properties ); $this->assertArrayHasKey( 'description', $properties ); $this->assertArrayHasKey( 'slug', $properties ); @@ -383,6 +405,7 @@ public function test_get_item_schema() { $this->assertArrayHasKey( 'status', $properties ); $this->assertArrayHasKey( 'wp_id', $properties ); $this->assertArrayHasKey( 'has_theme_file', $properties ); + $this->assertArrayHasKey( 'author', $properties ); } protected function find_and_normalize_template_by_id( $templates, $id ) { diff --git a/tests/qunit/fixtures/wp-api-generated.js b/tests/qunit/fixtures/wp-api-generated.js index 3c6f4906af686..5d36b184abca2 100644 --- a/tests/qunit/fixtures/wp-api-generated.js +++ b/tests/qunit/fixtures/wp-api-generated.js @@ -5117,6 +5117,11 @@ mockedApiResponse.Schema = { "private" ], "required": false + }, + "author": { + "description": "The ID for the author of the template.", + "type": "integer", + "required": false } } } @@ -5262,6 +5267,11 @@ mockedApiResponse.Schema = { "private" ], "required": false + }, + "author": { + "description": "The ID for the author of the template.", + "type": "integer", + "required": false } } }, @@ -5571,6 +5581,11 @@ mockedApiResponse.Schema = { "private" ], "required": false + }, + "author": { + "description": "The ID for the author of the template.", + "type": "integer", + "required": false } } } @@ -5750,6 +5765,11 @@ mockedApiResponse.Schema = { ], "required": false }, + "author": { + "description": "The ID for the author of the template.", + "type": "integer", + "required": false + }, "area": { "description": "Where the template part is intended for use (header, footer, etc.)", "type": "string", @@ -5900,6 +5920,11 @@ mockedApiResponse.Schema = { ], "required": false }, + "author": { + "description": "The ID for the author of the template.", + "type": "integer", + "required": false + }, "area": { "description": "Where the template part is intended for use (header, footer, etc.)", "type": "string", @@ -6214,6 +6239,11 @@ mockedApiResponse.Schema = { ], "required": false }, + "author": { + "description": "The ID for the author of the template.", + "type": "integer", + "required": false + }, "area": { "description": "Where the template part is intended for use (header, footer, etc.)", "type": "string", From 0095e94fcf3867e644ea887628963a60480aa215 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Thu, 25 Nov 2021 10:57:46 +0800 Subject: [PATCH 04/10] Fix linting issue --- .../rest-api/endpoints/class-wp-rest-templates-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-templates-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-templates-controller.php index e75273188c232..8a27e12fa0e90 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-templates-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-templates-controller.php @@ -780,7 +780,7 @@ public function get_item_schema() { 'context' => array( 'embed', 'view', 'edit' ), 'readonly' => true, ), - 'author' => array ( + 'author' => array( 'description' => __( 'The ID for the author of the template.' ), 'type' => 'integer', 'context' => array( 'view', 'edit', 'embed' ), From 39e5f9181012442973b80000e9cfadd3818f0311 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Thu, 25 Nov 2021 16:02:00 +0800 Subject: [PATCH 05/10] Add origin field to templates --- src/wp-includes/block-template-utils.php | 4 ++++ src/wp-includes/class-wp-block-template.php | 11 +++++++++++ .../class-wp-rest-templates-controller.php | 13 +++++++++++++ 3 files changed, 28 insertions(+) diff --git a/src/wp-includes/block-template-utils.php b/src/wp-includes/block-template-utils.php index 7cf41c37ddc75..83d392899ce5c 100644 --- a/src/wp-includes/block-template-utils.php +++ b/src/wp-includes/block-template-utils.php @@ -503,6 +503,9 @@ function _build_block_template_result_from_post( $post ) { $has_theme_file = wp_get_theme()->get_stylesheet() === $theme && null !== _get_block_template_file( $post->post_type, $post->post_name ); + + $origin = get_post_meta( $post->ID, 'origin', true ); + $template = new WP_Block_Template(); $template->wp_id = $post->ID; $template->id = $theme . '//' . $post->post_name; @@ -510,6 +513,7 @@ function _build_block_template_result_from_post( $post ) { $template->content = $post->post_content; $template->slug = $post->post_name; $template->source = 'custom'; + $template->origin = ! empty( $origin ) ? $origin : null; $template->type = $post->post_type; $template->description = $post->post_excerpt; $template->title = $post->post_title; diff --git a/src/wp-includes/class-wp-block-template.php b/src/wp-includes/class-wp-block-template.php index 9a54b4d97d597..d2f3c331aec95 100644 --- a/src/wp-includes/class-wp-block-template.php +++ b/src/wp-includes/class-wp-block-template.php @@ -78,6 +78,17 @@ class WP_Block_Template { */ public $source = 'theme'; + + /** + * Origin of the content when the content has been customized. + * When customized, origin takes on the value of source and source becomes + * 'custom'. + * + * @since 5.9.0 + * @var string + */ + public $origin; + /** * Post Id. * diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-templates-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-templates-controller.php index 8a27e12fa0e90..97604512a9734 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-templates-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-templates-controller.php @@ -422,6 +422,9 @@ protected function prepare_item_for_database( $request ) { $changes->tax_input = array( 'wp_theme' => $template->theme, ); + $changes->meta_input = array( + 'origin' => $template->source + ); } else { $changes->post_name = $template->slug; $changes->ID = $template->wp_id; @@ -528,6 +531,10 @@ public function prepare_item_for_response( $item, $request ) { // phpcs:ignore V $data['source'] = $template->source; } + if ( rest_is_field_included( 'origin', $fields ) ) { + $data['origin'] = $template->origin; + } + if ( rest_is_field_included( 'type', $fields ) ) { $data['type'] = $template->type; } @@ -717,6 +724,12 @@ public function get_item_schema() { 'context' => array( 'embed', 'view', 'edit' ), 'readonly' => true, ), + 'origin' => array( + 'description' => __( 'Source of a customized template' ), + 'type' => 'string', + 'context' => array( 'embed', 'view', 'edit' ), + 'readonly' => true, + ), 'content' => array( 'description' => __( 'Content of template.' ), 'type' => array( 'object', 'string' ), From 11e1a10496402d18c6aefaebe997a60806c1e809 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Thu, 25 Nov 2021 16:46:33 +0800 Subject: [PATCH 06/10] Handle errors from `prepare_item_for_database` in REST templates controller --- .../endpoints/class-wp-rest-templates-controller.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-templates-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-templates-controller.php index 97604512a9734..9dbae2c658116 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-templates-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-templates-controller.php @@ -244,6 +244,10 @@ public function update_item( $request ) { $changes = $this->prepare_item_for_database( $request ); + if ( is_wp_error( $changes ) ) { + return $changes; + } + if ( 'custom' === $template->source ) { $result = wp_update_post( wp_slash( (array) $changes ), true ); } else { @@ -294,6 +298,11 @@ public function create_item_permissions_check( $request ) { */ public function create_item( $request ) { $prepared_post = $this->prepare_item_for_database( $request ); + + if ( is_wp_error( $prepared_post ) ) { + return $prepared_post; + } + $prepared_post->post_name = $request['slug']; $post_id = wp_insert_post( wp_slash( (array) $prepared_post ), true ); if ( is_wp_error( $post_id ) ) { From 8888852836dcfedaa01a9c3cea6cc9c0f70afbfc Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Thu, 25 Nov 2021 16:46:52 +0800 Subject: [PATCH 07/10] Update tests to support origin --- .../tests/rest-api/wpRestTemplatesController.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/phpunit/tests/rest-api/wpRestTemplatesController.php b/tests/phpunit/tests/rest-api/wpRestTemplatesController.php index d082d6a809509..9ea7aa01ded26 100644 --- a/tests/phpunit/tests/rest-api/wpRestTemplatesController.php +++ b/tests/phpunit/tests/rest-api/wpRestTemplatesController.php @@ -53,7 +53,6 @@ public static function wpTearDownAfterClass() { wp_delete_post( self::$post->ID ); } - public function test_register_routes() { $routes = rest_get_server()->get_routes(); $this->assertArrayHasKey( '/wp/v2/templates', $routes ); @@ -93,6 +92,7 @@ public function test_get_items() { 'theme' => 'default', 'slug' => 'my_template', 'source' => 'custom', + 'origin' => null, 'type' => 'wp_template', 'description' => 'Description of my template.', 'title' => array( @@ -135,6 +135,7 @@ public function test_get_item() { 'theme' => 'default', 'slug' => 'my_template', 'source' => 'custom', + 'origin' => null, 'type' => 'wp_template', 'description' => 'Description of my template.', 'title' => array( @@ -180,6 +181,7 @@ public function test_create_item() { ), 'slug' => 'my_custom_template', 'source' => 'custom', + 'origin' => null, 'type' => 'wp_template', 'description' => 'Just a description', 'title' => array( @@ -228,6 +230,7 @@ public function test_create_item_raw() { ), 'slug' => 'my_custom_template_raw', 'source' => 'custom', + 'origin' => null, 'type' => 'wp_template', 'description' => 'Just a description', 'title' => array( @@ -251,7 +254,7 @@ public function test_create_item_invalid_author() { 'description' => 'Just a description', 'title' => 'My Template', 'content' => 'Content', - 'author' => -999, + 'author' => 99999, ) ); $response = rest_get_server()->dispatch( $request ); @@ -392,13 +395,14 @@ public function test_get_item_schema() { $response = rest_get_server()->dispatch( $request ); $data = $response->get_data(); $properties = $data['schema']['properties']; - $this->assertCount( 12, $properties ); + $this->assertCount( 13, $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 ); From 63d008a72cff9a0ccc918733fa5dd96b7975900e Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Thu, 25 Nov 2021 17:28:55 +0800 Subject: [PATCH 08/10] Address review feedback --- src/wp-includes/block-template-utils.php | 3 +-- src/wp-includes/class-wp-block-template.php | 4 +++- .../rest-api/endpoints/class-wp-rest-templates-controller.php | 2 +- tests/phpunit/tests/rest-api/wpRestTemplatesController.php | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/wp-includes/block-template-utils.php b/src/wp-includes/block-template-utils.php index 83d392899ce5c..923b46b740de8 100644 --- a/src/wp-includes/block-template-utils.php +++ b/src/wp-includes/block-template-utils.php @@ -503,7 +503,6 @@ function _build_block_template_result_from_post( $post ) { $has_theme_file = wp_get_theme()->get_stylesheet() === $theme && null !== _get_block_template_file( $post->post_type, $post->post_name ); - $origin = get_post_meta( $post->ID, 'origin', true ); $template = new WP_Block_Template(); @@ -520,7 +519,7 @@ function _build_block_template_result_from_post( $post ) { $template->status = $post->post_status; $template->has_theme_file = $has_theme_file; $template->is_custom = true; - $template->post_author = $post->post_author; + $template->author = $post->post_author; if ( 'wp_template' === $post->post_type && isset( $default_template_types[ $template->slug ] ) ) { $template->is_custom = false; diff --git a/src/wp-includes/class-wp-block-template.php b/src/wp-includes/class-wp-block-template.php index d2f3c331aec95..fab386b04419e 100644 --- a/src/wp-includes/class-wp-block-template.php +++ b/src/wp-includes/class-wp-block-template.php @@ -125,8 +125,10 @@ class WP_Block_Template { /** * Author. * + * A value of 0 means no author. + * * @since 5.9.0 * @var int */ - public $post_author = 0; + public $author; } diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-templates-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-templates-controller.php index 9dbae2c658116..aef97067d9158 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-templates-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-templates-controller.php @@ -582,7 +582,7 @@ public function prepare_item_for_response( $item, $request ) { // phpcs:ignore V } if ( rest_is_field_included( 'author', $fields ) ) { - $data['author'] = (int) $template->post_author; + $data['author'] = (int) $template->author; } if ( rest_is_field_included( 'area', $fields ) && 'wp_template_part' === $template->type ) { diff --git a/tests/phpunit/tests/rest-api/wpRestTemplatesController.php b/tests/phpunit/tests/rest-api/wpRestTemplatesController.php index 9ea7aa01ded26..734d7a45216ea 100644 --- a/tests/phpunit/tests/rest-api/wpRestTemplatesController.php +++ b/tests/phpunit/tests/rest-api/wpRestTemplatesController.php @@ -254,7 +254,7 @@ public function test_create_item_invalid_author() { 'description' => 'Just a description', 'title' => 'My Template', 'content' => 'Content', - 'author' => 99999, + 'author' => -1, ) ); $response = rest_get_server()->dispatch( $request ); From 51c768e43bfab0da9676583e97471af2aefbb891 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Thu, 25 Nov 2021 18:53:03 +0800 Subject: [PATCH 09/10] Linting fix --- .../rest-api/endpoints/class-wp-rest-templates-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-templates-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-templates-controller.php index aef97067d9158..35434920036ea 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-templates-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-templates-controller.php @@ -432,7 +432,7 @@ protected function prepare_item_for_database( $request ) { 'wp_theme' => $template->theme, ); $changes->meta_input = array( - 'origin' => $template->source + 'origin' => $template->source, ); } else { $changes->post_name = $template->slug; From 89ea62cb027a913b263a504b56c224c188f4e071 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Mon, 29 Nov 2021 09:26:42 +0800 Subject: [PATCH 10/10] Remove empty lines --- src/wp-includes/class-wp-block-template.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/wp-includes/class-wp-block-template.php b/src/wp-includes/class-wp-block-template.php index fab386b04419e..1d73d281fe762 100644 --- a/src/wp-includes/class-wp-block-template.php +++ b/src/wp-includes/class-wp-block-template.php @@ -69,7 +69,6 @@ class WP_Block_Template { */ public $description = ''; - /** * Source of the content. `theme` and `custom` is used for now. * @@ -78,7 +77,6 @@ class WP_Block_Template { */ public $source = 'theme'; - /** * Origin of the content when the content has been customized. * When customized, origin takes on the value of source and source becomes