diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-comments-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-comments-controller.php index a0b68759f9942..41eefb96484c8 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-comments-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-comments-controller.php @@ -1680,8 +1680,8 @@ public function get_collection_params() { $query_params['status'] = array( 'default' => 'approve', 'description' => __( 'Limit result set to comments assigned a specific status. Requires authorization.' ), - 'sanitize_callback' => 'sanitize_key', - 'type' => 'string', + 'sanitize_callback' => array( $this, 'sanitize_comment_statuses' ), + 'type' => 'array', 'validate_callback' => 'rest_validate_request_arg', ); @@ -1928,4 +1928,17 @@ protected function check_is_comment_content_allowed( $prepared_comment ) { */ return '' !== $check['comment_content']; } + + /** + * Sanitize a single comment status or a list of comment statuses with `sanitize_key`. + * + * @since 6.9.0 + * + * @param string|array $statuses Comment status or array of comment statuses. + * @return array Sanitized array of comment statuses. + */ + public function sanitize_comment_statuses( $statuses ) { + $statuses = wp_parse_list( $statuses ); + return array_unique( array_map( 'sanitize_key', $statuses ) ); + } } diff --git a/tests/phpunit/tests/rest-api/rest-comments-controller.php b/tests/phpunit/tests/rest-api/rest-comments-controller.php index 0bfe4e778d870..9572327c34fbd 100644 --- a/tests/phpunit/tests/rest-api/rest-comments-controller.php +++ b/tests/phpunit/tests/rest-api/rest-comments-controller.php @@ -119,6 +119,7 @@ public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) { array( 'comment_content' => "Comment {$i}", 'comment_post_ID' => self::$post_id, + 'status' => ( 0 === $i % 2 ) ? 'approve' : 'hold', ) ); } @@ -224,6 +225,151 @@ public function test_get_items() { $this->assertCount( self::$total_comments, $comments ); } + /** + * Test getting items of a specific status. + * + * @ticket 63982 + */ + public function test_get_items_by_status() { + wp_set_current_user( self::$admin_id ); + + $request = new WP_REST_Request( 'GET', '/wp/v2/comments' ); + $request->set_param( 'status', 'approve' ); + $request->set_param( 'per_page', self::$per_page ); + $response = rest_get_server()->dispatch( $request ); + + $this->assertSame( 200, $response->get_status() ); + + $q = new WP_Comment_Query(); + $found = $q->query( + array( + 'status' => 'approve', + 'count' => true, + ) + ); + + $comments = $response->get_data(); + + $this->assertCount( $found, $comments ); + } + + /** + * Test getting comments of all statuses. + * + * @ticket 63982 + */ + public function test_get_items_by_all_status() { + wp_set_current_user( self::$admin_id ); + + $request = new WP_REST_Request( 'GET', '/wp/v2/comments' ); + $request->set_param( 'status', 'all' ); + $request->set_param( 'per_page', self::$per_page ); + $response = rest_get_server()->dispatch( $request ); + + $this->assertSame( 200, $response->get_status() ); + + $q = new WP_Comment_Query(); + $found = $q->query( + array( + 'status' => 'all', + 'count' => true, + ) + ); + + $comments = $response->get_data(); + $this->assertCount( $found, $comments ); + } + + /** + * Test getting items of multiple statuses. + * + * @ticket 63982 + */ + public function test_get_items_by_multiple_status() { + wp_set_current_user( self::$admin_id ); + + $request = new WP_REST_Request( 'GET', '/wp/v2/comments' ); + $request->set_param( 'status', array( 'approve', 'hold' ) ); + $request->set_param( 'per_page', self::$per_page ); + + $response = rest_get_server()->dispatch( $request ); + $this->assertSame( 200, $response->get_status() ); + + $q = new WP_Comment_Query(); + $found = $q->query( + array( + 'status' => array( 'approve', 'hold' ), + 'count' => true, + ) + ); + + $comments = $response->get_data(); + $this->assertCount( $found, $comments ); + } + + /** + * Test sanization of the status parameter. + * + * @ticket 63982 + * + * @dataProvider data_get_items_by_status_sanitize + */ + public function test_get_items_by_status_sanitize( $key, $expected ) { + wp_set_current_user( self::$admin_id ); + + // Create a post with the test status. + $params = array( + 'post' => self::$post_id, + 'author_name' => 'Comic Book Guy', + 'author_email' => 'cbg@androidsdungeon.com', + 'author_url' => 'http://androidsdungeon.com', + 'content' => 'Worst Comment Ever!', + 'status' => $key, + ); + + $request = new WP_REST_Request( 'POST', '/wp/v2/comments' ); + $request->add_header( 'Content-Type', 'application/json' ); + $request->set_body( wp_json_encode( $params ) ); + + $response = rest_get_server()->dispatch( $request ); + $this->assertSame( 201, $response->get_status() ); + + $comment = $response->get_data(); + + $this->assertEquals( $expected, $comment['status'] ); + } + + /** + * Data provider. + * + * @return array + */ + public function data_get_items_by_status_sanitize() { + return array( + 'an empty string key' => array( + 'key' => '', + 'expected' => 'hold', + ), + 'a lowercase key with commas' => array( + 'key' => 'howdy,admin', + 'expected' => 'hold', + ), + 'a lowercase key with commas' => array( + 'key' => 'HOWDY,ADMIN', + 'expected' => 'hold', + ), + 'a mixed case key with commas' => array( + 'key' => 'HoWdY,aDmIn', + 'expected' => 'hold', + ), + 'a string with unicode' => array( + 'key' => array( 'howdy admin', 'another-value' ), + 'expected' => 'hold', + ), + ); + } + + /** * @ticket 38692 */ diff --git a/tests/qunit/fixtures/wp-api-generated.js b/tests/qunit/fixtures/wp-api-generated.js index 6626758a8a9dc..bc5048a232504 100644 --- a/tests/qunit/fixtures/wp-api-generated.js +++ b/tests/qunit/fixtures/wp-api-generated.js @@ -10335,7 +10335,10 @@ mockedApiResponse.Schema = { "status": { "default": "approve", "description": "Limit result set to comments assigned a specific status. Requires authorization.", - "type": "string", + "type": [ + "string", + "array" + ], "required": false }, "type": {