From e66f459435eb8ee2bad083a9e8703a5151fdc0b3 Mon Sep 17 00:00:00 2001 From: Timothy Jacobs Date: Sat, 24 Oct 2020 16:02:34 +0000 Subject: [PATCH] REST API: Don't validate status if it hasn't changed. In particular, this allows for sending `status=inherit` to an attachment if it's current status is `inherit`. This status would be rejected because it is an "internal" post status which isn't exposed. As a general rule, a developer should always be able to PUT back a GET response without error. Props dfenton, pputzer, TimothyBlynJacobs. Fixes #40399. git-svn-id: https://develop.svn.wordpress.org/trunk@49302 602fd350-edb4-49c9-b593-d223f7449a82 --- .../class-wp-rest-posts-controller.php | 39 ++++++++++++++- .../rest-api/rest-attachments-controller.php | 47 +++++++++++++++++++ 2 files changed, 84 insertions(+), 2 deletions(-) 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 e4a8bcba86..c168d8993c 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 @@ -1052,7 +1052,8 @@ class WP_REST_Posts_Controller extends WP_REST_Controller { * @return stdClass|WP_Error Post object or WP_Error. */ protected function prepare_item_for_database( $request ) { - $prepared_post = new stdClass(); + $prepared_post = new stdClass(); + $current_status = ''; // Post ID. if ( isset( $request['id'] ) ) { @@ -1062,6 +1063,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller { } $prepared_post->ID = $existing_post->ID; + $current_status = $existing_post->post_status; } $schema = $this->get_item_schema(); @@ -1105,7 +1107,11 @@ class WP_REST_Posts_Controller extends WP_REST_Controller { $post_type = get_post_type_object( $prepared_post->post_type ); // Post status. - if ( ! empty( $schema['properties']['status'] ) && isset( $request['status'] ) ) { + if ( + ! empty( $schema['properties']['status'] ) && + isset( $request['status'] ) && + ( ! $current_status || $current_status !== $request['status'] ) + ) { $status = $this->handle_status_param( $request['status'], $post_type ); if ( is_wp_error( $status ) ) { @@ -1255,6 +1261,32 @@ class WP_REST_Posts_Controller extends WP_REST_Controller { } + /** + * Checks whether the status is valid for the given post. + * + * Allows for sending an update request with the current status, even if that status would not be acceptable. + * + * @since 5.6.0 + * + * @param string $status The provided status. + * @param WP_REST_Request $request The request object. + * @param string $param The parameter name. + * @return true|WP_Error True if the status is valid, or WP_Error if not. + */ + public function check_status( $status, $request, $param ) { + if ( $request['id'] ) { + $post = $this->get_post( $request['id'] ); + + if ( ! is_wp_error( $post ) && $post->post_status === $status ) { + return true; + } + } + + $args = $request->get_attributes()['args'][ $param ]; + + return rest_validate_value_from_schema( $status, $args, $param ); + } + /** * Determines validity and normalizes the given status parameter. * @@ -2115,6 +2147,9 @@ class WP_REST_Posts_Controller extends WP_REST_Controller { 'type' => 'string', 'enum' => array_keys( get_post_stati( array( 'internal' => false ) ) ), 'context' => array( 'view', 'edit' ), + 'arg_options' => array( + 'validate_callback' => array( $this, 'check_status' ), + ), ), 'type' => array( 'description' => __( 'Type of Post for the object.' ), diff --git a/tests/phpunit/tests/rest-api/rest-attachments-controller.php b/tests/phpunit/tests/rest-api/rest-attachments-controller.php index b70663788b..3e163dc320 100644 --- a/tests/phpunit/tests/rest-api/rest-attachments-controller.php +++ b/tests/phpunit/tests/rest-api/rest-attachments-controller.php @@ -1007,6 +1007,53 @@ class WP_Test_REST_Attachments_Controller extends WP_Test_REST_Post_Type_Control $this->assertErrorResponse( 'rest_invalid_param', $response, 400 ); } + /** + * @ticket 40399 + */ + public function test_update_item_with_existing_inherit_status() { + wp_set_current_user( self::$editor_id ); + $parent_id = self::factory()->post->create( array() ); + $attachment_id = self::factory()->attachment->create_object( + $this->test_file, + $parent_id, + array( + 'post_mime_type' => 'image/jpeg', + 'post_excerpt' => 'A sample caption', + 'post_author' => self::$editor_id, + ) + ); + + $request = new WP_REST_Request( 'POST', '/wp/v2/media/' . $attachment_id ); + $request->set_param( 'status', 'inherit' ); + $response = rest_get_server()->dispatch( $request ); + + $this->assertNotWPError( $response->as_error() ); + $this->assertEquals( 'inherit', $response->get_data()['status'] ); + } + + /** + * @ticket 40399 + */ + public function test_update_item_with_new_inherit_status() { + wp_set_current_user( self::$editor_id ); + $attachment_id = self::factory()->attachment->create_object( + $this->test_file, + 0, + array( + 'post_mime_type' => 'image/jpeg', + 'post_excerpt' => 'A sample caption', + 'post_author' => self::$editor_id, + 'post_status' => 'private', + ) + ); + + $request = new WP_REST_Request( 'POST', '/wp/v2/media/' . $attachment_id ); + $request->set_param( 'status', 'inherit' ); + $response = rest_get_server()->dispatch( $request ); + + $this->assertErrorResponse( 'rest_invalid_param', $response, 400 ); + } + public function verify_attachment_roundtrip( $input = array(), $expected_output = array() ) { // Create the post. $request = new WP_REST_Request( 'POST', '/wp/v2/media' );