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..e29e0d6dc2ce4 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 @@ -1124,6 +1124,33 @@ public function prepare_item_for_response( $item, $request ) { $data['type'] = get_comment_type( $comment->comment_ID ); } + if ( in_array( 'post_details', $fields, true ) ) { + $post = get_post( $comment->comment_post_ID ); + + if ( $post ) { + $data['post_details'] = array( + 'id' => (int) $post->ID, + 'title' => (string) get_the_title( $post->ID ), + 'type' => $post->post_type, + 'link' => (string) rest_url( rest_get_route_for_post( $post ) ), + ); + } + } + + if ( in_array( 'i_replied', $fields, true ) ) { + $data['i_replied'] = (bool) get_comments( + array( + 'user_id' => get_current_user_id(), + 'parent' => $comment->comment_ID, + 'count' => true, + ) + ); + } + + if ( in_array( 'can_moderate', $fields, true ) ) { + $data['can_moderate'] = (bool) current_user_can( 'edit_comment', $comment->comment_ID ); + } + if ( in_array( 'author_avatar_urls', $fields, true ) ) { $data['author_avatar_urls'] = rest_get_avatar_urls( $comment ); } @@ -1521,6 +1548,24 @@ public function get_item_schema() { 'context' => array( 'view', 'edit', 'embed' ), 'readonly' => true, ), + 'post_details' => array( + 'description' => __( 'An array of post details.' ), + 'type' => 'array', + 'context' => array( 'view', 'edit' ), + 'default' => null, + ), + 'i_replied' => array( + 'description' => __( 'Whether the authenticated user has replied to the comment.' ), + 'type' => 'boolean', + 'context' => array( 'view', 'edit' ), + 'readonly' => true, + ), + 'can_moderate' => array( + 'description' => __( 'Whether the authenticated can moderate the comment.' ), + 'type' => 'boolean', + 'context' => array( 'view', 'edit' ), + 'readonly' => true, + ), ), ); diff --git a/tests/phpunit/tests/rest-api/rest-comments-controller.php b/tests/phpunit/tests/rest-api/rest-comments-controller.php index 0bfe4e778d870..97572580fa5fe 100644 --- a/tests/phpunit/tests/rest-api/rest-comments-controller.php +++ b/tests/phpunit/tests/rest-api/rest-comments-controller.php @@ -447,6 +447,63 @@ public function test_get_items_no_permission_for_no_post( $method ) { $this->assertErrorResponse( 'rest_cannot_read', $response, 401 ); } + public function test_can_moderate_is_true_with_edit_post_permission() { + wp_set_current_user( self::$admin_id ); + + $request = new WP_REST_Request( 'GET', '/wp/v2/comments/' . self::$approved_id ); + + $response = rest_get_server()->dispatch( $request ); + $this->assertSame( 200, $response->get_status() ); + + $comment_data = $response->get_data(); + $this->assertTrue( $comment_data['can_moderate'] ); + } + + public function test_can_moderate_is_false_without_edit_post_permission() { + wp_set_current_user( 0 ); + + $request = new WP_REST_Request( 'GET', '/wp/v2/comments/' . self::$approved_id ); + + $response = rest_get_server()->dispatch( $request ); + $this->assertSame( 200, $response->get_status() ); + + $comment_data = $response->get_data(); + $this->assertFalse( $comment_data['can_moderate'] ); + } + + public function test_i_replied_is_true_if_authenticated_user_created_reply() { + wp_set_current_user( self::$editor_id ); + + $args = array( + 'comment_approved' => 1, + 'comment_post_ID' => self::$post_id, + 'comment_parent' => self::$approved_id, + 'user_id' => self::$editor_id, + ); + + self::factory()->comment->create( $args ); + + $request = new WP_REST_Request( 'GET', '/wp/v2/comments/' . self::$approved_id ); + + $response = rest_get_server()->dispatch( $request ); + $this->assertSame( 200, $response->get_status() ); + + $comment_data = $response->get_data(); + $this->assertTrue( $comment_data['i_replied'] ); + } + + public function test_i_replied_is_false_if_authenticated_user_did_not_create_reply() { + wp_set_current_user( self::$editor_id ); + + $request = new WP_REST_Request( 'GET', '/wp/v2/comments/' . self::$approved_id ); + + $response = rest_get_server()->dispatch( $request ); + $this->assertSame( 200, $response->get_status() ); + + $comment_data = $response->get_data(); + $this->assertFalse( $comment_data['i_replied'] ); + } + /** * Data provider intended to provide HTTP method names for testing GET and HEAD requests. * @@ -3265,7 +3322,7 @@ public function test_get_item_schema() { $response = rest_get_server()->dispatch( $request ); $data = $response->get_data(); $properties = $data['schema']['properties']; - $this->assertCount( 17, $properties ); + $this->assertCount( 20, $properties ); $this->assertArrayHasKey( 'id', $properties ); $this->assertArrayHasKey( 'author', $properties ); $this->assertArrayHasKey( 'author_avatar_urls', $properties ); @@ -3283,6 +3340,9 @@ public function test_get_item_schema() { $this->assertArrayHasKey( 'post', $properties ); $this->assertArrayHasKey( 'status', $properties ); $this->assertArrayHasKey( 'type', $properties ); + $this->assertArrayHasKey( 'post_details', $properties ); + $this->assertArrayHasKey( 'can_moderate', $properties ); + $this->assertArrayHasKey( 'i_replied', $properties ); $this->assertSame( 0, $properties['parent']['default'] ); $this->assertSame( 0, $properties['post']['default'] ); @@ -3434,7 +3494,12 @@ protected function check_comment_data( $data, $context, $links ) { ); if ( $comment->comment_post_ID ) { + $post = get_post( $comment->comment_post_ID ); + $this->assertSame( rest_url( '/wp/v2/posts/' . $comment->comment_post_ID ), $links['up'][0]['href'] ); + $this->assertSame( $post->ID, $data['post_details']['id'] ); + $this->assertSame( $post->post_title, $data['post_details']['title'] ); + $this->assertSame( rest_url( '/wp/v2/posts/' . $comment->comment_post_ID ), $data['post_details']['link'] ); } if ( 'edit' === $context ) { diff --git a/tests/phpunit/tests/rest-api/rest-schema-setup.php b/tests/phpunit/tests/rest-api/rest-schema-setup.php index 7f8de5f0dd83d..e651708fda3f0 100644 --- a/tests/phpunit/tests/rest-api/rest-schema-setup.php +++ b/tests/phpunit/tests/rest-api/rest-schema-setup.php @@ -748,9 +748,13 @@ public function test_build_wp_api_client_fixtures() { 'CommentsCollection.0._links.self.0.href' => 'http://example.org/index.php?rest_route=/wp/v2/comments/2', 'CommentsCollection.0._links.collection.0.href' => 'http://example.org/index.php?rest_route=/wp/v2/comments', 'CommentsCollection.0._links.up.0.href' => 'http://example.org/index.php?rest_route=/wp/v2/posts/4', + 'CommentsCollection.0.post_details.id' => 4, + 'CommentsCollection.0.post_details.link' => 'http://example.org/index.php?rest_route=/wp/v2/posts/4', 'CommentModel.id' => 2, 'CommentModel.post' => 4, 'CommentModel.link' => 'http://example.org/?p=4#comment-2', + 'CommentModel.post_details.id' => 4, + 'CommentModel.post_details.link' => 'http://example.org/index.php?rest_route=/wp/v2/posts/4', 'settings.title' => 'Test Blog', 'settings.url' => 'http://example.org', 'settings.email' => 'admin@example.org', diff --git a/tests/qunit/fixtures/wp-api-generated.js b/tests/qunit/fixtures/wp-api-generated.js index 6626758a8a9dc..b9dbbd2eeb69b 100644 --- a/tests/qunit/fixtures/wp-api-generated.js +++ b/tests/qunit/fixtures/wp-api-generated.js @@ -10442,6 +10442,11 @@ mockedApiResponse.Schema = { "type": "string", "required": false }, + "post_details": { + "description": "An array of post details.", + "type": "array", + "required": false + }, "meta": { "description": "Meta fields.", "type": "object", @@ -10589,6 +10594,11 @@ mockedApiResponse.Schema = { "type": "string", "required": false }, + "post_details": { + "description": "An array of post details.", + "type": "array", + "required": false + }, "meta": { "description": "Meta fields.", "type": "object", @@ -14042,6 +14052,14 @@ mockedApiResponse.CommentsCollection = [ "link": "http://example.org/?p=4#comment-2", "status": "approved", "type": "comment", + "post_details": { + "id": 4, + "title": "REST API Client Fixture: Post", + "type": "post", + "link": "http://example.org/index.php?rest_route=/wp/v2/posts/4" + }, + "i_replied": false, + "can_moderate": true, "author_avatar_urls": { "24": "https://secure.gravatar.com/avatar/9ca51ced0b389ffbeba3d269c6d824be664c84fa1b35503282abdd302e1f417c?s=24&d=mm&r=g", "48": "https://secure.gravatar.com/avatar/9ca51ced0b389ffbeba3d269c6d824be664c84fa1b35503282abdd302e1f417c?s=48&d=mm&r=g", @@ -14096,6 +14114,14 @@ mockedApiResponse.CommentModel = { "link": "http://example.org/?p=4#comment-2", "status": "approved", "type": "comment", + "post_details": { + "id": 4, + "title": "REST API Client Fixture: Post", + "type": "post", + "link": "http://example.org/index.php?rest_route=/wp/v2/posts/4" + }, + "i_replied": false, + "can_moderate": true, "author_avatar_urls": { "24": "https://secure.gravatar.com/avatar/9ca51ced0b389ffbeba3d269c6d824be664c84fa1b35503282abdd302e1f417c?s=24&d=mm&r=g", "48": "https://secure.gravatar.com/avatar/9ca51ced0b389ffbeba3d269c6d824be664c84fa1b35503282abdd302e1f417c?s=48&d=mm&r=g",