From b6dcb779eda0c4483fc78e6c2ab12655eee4f0c6 Mon Sep 17 00:00:00 2001 From: "K. Adam White" Date: Tue, 11 Feb 2020 16:26:56 +0000 Subject: [PATCH] REST API: Introduce rest_{$this->post_type}_item_schema filter to enable manipulation of schema values. register_rest_field can be used to add properties to a schema, but no mechanism existed to alter existing properties like "content". Running the schema through this filter lets plugins append additional sub-properties to existing schema definitions. Props luisherranz, TimothyBlynJacobs, swissspidy, westonruter, kadamwhite. Fixes #47779. git-svn-id: https://develop.svn.wordpress.org/trunk@47265 602fd350-edb4-49c9-b593-d223f7449a82 --- .../class-wp-rest-posts-controller.php | 21 ++++++++ .../tests/rest-api/rest-posts-controller.php | 53 +++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php index c251b57dad..d07006f762 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php @@ -2406,6 +2406,27 @@ class WP_REST_Posts_Controller extends WP_REST_Controller { $schema['links'] = $schema_links; } + // Take a snapshot of which fields are in the schema pre-filtering. + $schema_fields = array_keys( $schema['properties'] ); + + /** + * Filter the post's schema. + * + * The dynamic portion of the filter, `$this->post_type`, refers to the + * post type slug for the controller. + * + * @since 5.4.0 + * + * @param array $schema Item schema data. + */ + $schema = apply_filters( "rest_{$this->post_type}_item_schema", $schema ); + + // Emit a _doing_it_wrong warning if user tries to add new properties using this filter. + $new_fields = array_diff( array_keys( $schema['properties'] ), $schema_fields ); + if ( count( $new_fields ) > 0 ) { + _doing_it_wrong( __METHOD__, __( 'Please use register_rest_field to add new schema properties.' ), '5.4.0' ); + } + $this->schema = $schema; return $this->add_additional_fields_schema( $this->schema ); diff --git a/tests/phpunit/tests/rest-api/rest-posts-controller.php b/tests/phpunit/tests/rest-api/rest-posts-controller.php index 3735d1146a..edd9b24f71 100644 --- a/tests/phpunit/tests/rest-api/rest-posts-controller.php +++ b/tests/phpunit/tests/rest-api/rest-posts-controller.php @@ -4841,6 +4841,41 @@ class WP_Test_REST_Posts_Controller extends WP_Test_REST_Post_Type_Controller_Te $this->assertNull( get_post_type_object( 'test' )->get_rest_controller() ); } + /** + * @ticket 47779 + */ + public function test_rest_post_type_item_schema_filter_change_property() { + add_filter( 'rest_post_item_schema', array( $this, 'filter_post_item_schema' ) ); + + // Re-initialize the controller to cache-bust schemas from prior test runs. + $GLOBALS['wp_rest_server']->override_by_default = true; + $controller = new WP_REST_Posts_Controller( 'post' ); + $controller->register_routes(); + $GLOBALS['wp_rest_server']->override_by_default = false; + + $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/posts' ); + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + $properties = $data['schema']['properties']['content']['properties']; + + $this->assertArrayHasKey( 'new_prop', $properties ); + $this->assertEquals( array( 'new_context' ), $properties['new_prop']['context'] ); + } + + /** + * @ticket 47779 + */ + public function test_rest_post_type_item_schema_filter_add_property_triggers_doing_it_wrong() { + add_filter( 'rest_post_item_schema', array( $this, 'filter_post_item_schema_add_property' ) ); + $this->setExpectedIncorrectUsage( 'WP_REST_Posts_Controller::get_item_schema' ); + + // Re-initialize the controller to cache-bust schemas from prior test runs. + $GLOBALS['wp_rest_server']->override_by_default = true; + $controller = new WP_REST_Posts_Controller( 'post' ); + $controller->register_routes(); + $GLOBALS['wp_rest_server']->override_by_default = false; + } + public function tearDown() { _unregister_post_type( 'private-post' ); _unregister_post_type( 'youseeme' ); @@ -4868,4 +4903,22 @@ class WP_Test_REST_Posts_Controller extends WP_Test_REST_Post_Type_Controller_Te 'post-my-test-template.php' => 'My Test Template', ); } + + public function filter_post_item_schema( $schema ) { + $schema['properties']['content']['properties']['new_prop'] = array( + 'description' => __( 'A new prop added with a the rest_post_item_schema filter.' ), + 'type' => 'string', + 'context' => array( 'new_context' ), + ); + return $schema; + } + + public function filter_post_item_schema_add_property( $schema ) { + $schema['properties']['something_entirely_new'] = array( + 'description' => __( 'A new prop added with a the rest_post_item_schema filter.' ), + 'type' => 'string', + 'context' => array( 'new_context' ), + ); + return $schema; + } }