namespace, '/posts/(?P\d+)/comments', array( array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get_items' ), 'permission_callback' => array( $this, 'permission_community' ), 'args' => $this->paging_args( 50 ), ), array( 'methods' => WP_REST_Server::CREATABLE, 'callback' => array( $this, 'create_item' ), 'permission_callback' => array( $this, 'permission_logged_in' ), 'args' => array( 'message' => array( 'type' => 'string', 'required' => true, ), 'parent_id' => array( 'type' => 'integer', 'default' => 0, ), ), ), ) ); register_rest_route( $this->namespace, '/comments/(?P\d+)', array( array( 'methods' => WP_REST_Server::EDITABLE, 'callback' => array( $this, 'update_item' ), 'permission_callback' => array( $this, 'permission_logged_in' ), 'args' => array( 'message' => array( 'type' => 'string', 'required' => true, ), ), ), array( 'methods' => WP_REST_Server::DELETABLE, 'callback' => array( $this, 'delete_item' ), 'permission_callback' => array( $this, 'permission_logged_in' ), ), ) ); register_rest_route( $this->namespace, '/comments/(?P\d+)/like', array( array( 'methods' => WP_REST_Server::CREATABLE, 'callback' => array( $this, 'toggle_like' ), 'permission_callback' => array( $this, 'permission_logged_in' ), 'args' => array( 'on' => array( 'type' => 'boolean', 'required' => true, ), ), ), ) ); } /** * Lists comments of a post. * * @param WP_REST_Request $request Request. * @return WP_REST_Response|WP_Error */ public function get_items( $request ) { $found = $this->readable_post( $request['id'] ); if ( is_wp_error( $found ) ) { return $found; } $result = Comments::for_post( $found['post']['id'], $request['page'], $request['per_page'] ); return $this->paged_response( Formatter::comments( $result['items'], $found['space'], get_current_user_id() ), $result['total'], $request['per_page'] ); } /** * Adds a comment or reply. * * @param WP_REST_Request $request Request. * @return WP_REST_Response|WP_Error */ public function create_item( $request ) { $found = $this->readable_post( $request['id'] ); if ( is_wp_error( $found ) ) { return $found; } $comment = Comments::create( $found['post'], $found['space'], array( 'message' => $request['message'], 'parent_id' => $request['parent_id'], ), get_current_user_id() ); if ( is_wp_error( $comment ) ) { return $comment; } $items = Formatter::comments( array( $comment ), $found['space'], get_current_user_id() ); $response = rest_ensure_response( $items[0] ); $response->set_status( 201 ); return $response; } /** * Edits a comment. * * @param WP_REST_Request $request Request. * @return WP_REST_Response|WP_Error */ public function update_item( $request ) { $found = $this->changeable_comment( $request['id'] ); if ( is_wp_error( $found ) ) { return $found; } $comment = Comments::update( $found['comment'], $request['message'], $found['space'], get_current_user_id() ); if ( is_wp_error( $comment ) ) { return $comment; } $items = Formatter::comments( array( $comment ), $found['space'], get_current_user_id() ); return rest_ensure_response( $items[0] ); } /** * Deletes a comment and its replies. * * @param WP_REST_Request $request Request. * @return WP_REST_Response|WP_Error */ public function delete_item( $request ) { $found = $this->changeable_comment( $request['id'] ); if ( is_wp_error( $found ) ) { return $found; } $keep = ! SpaceAccess::can_moderate( $found['space'], get_current_user_id() ); $result = Comments::delete( $found['comment'], $keep ); return rest_ensure_response( array( 'deleted' => true, 'id' => $found['comment']['id'], 'replies_kept' => $keep, 'parent_id' => $result['parent_id'], 'promoted_ids' => $result['promoted_ids'], 'deleted_ids' => $result['ids'], ) ); } /** * Likes or unlikes a comment. * * @param WP_REST_Request $request Request. * @return WP_REST_Response|WP_Error */ public function toggle_like( $request ) { $limited = \SpaceBay\Support\RateLimit::hit( 'react', get_current_user_id(), 60 ); if ( is_wp_error( $limited ) ) { return $limited; } $found = $this->readable_comment( $request['id'] ); if ( is_wp_error( $found ) ) { return $found; } $user_id = get_current_user_id(); if ( ! PostAccess::can_comment( $found['space'], $user_id ) ) { return new WP_Error( 'spacebay_cannot_react', __( 'Join this space to like comments.', 'spacebay' ), array( 'status' => 403 ) ); } Reactions::set( 'comment', $found['comment']['id'], $user_id, 'like', (bool) $request['on'] ); $items = Formatter::comments( array( Comments::find( $found['comment']['id'] ) ), $found['space'], $user_id ); return rest_ensure_response( $items[0] ); } /** * Returns a readable post and its space. * * @param int $id Post ID. * @return array{post: array, space: array}|WP_Error */ private function readable_post( $id ) { $post = Posts::find( $id ); $space = $post ? SpaceRepository::find( $post['space_id'] ) : null; if ( ! $post || ! $space || 'published' !== $post['status'] || ! SpaceAccess::can_read( $space, get_current_user_id() ) ) { return new WP_Error( 'spacebay_post_not_found', __( 'Post not found.', 'spacebay' ), array( 'status' => 404 ) ); } return array( 'post' => $post, 'space' => $space, ); } /** * Returns a readable comment with its post and space. * * @param int $id Comment ID. * @return array{comment: array, post: array, space: array}|WP_Error */ private function readable_comment( $id ) { $comment = Comments::find( $id ); if ( ! $comment || 'published' !== $comment['status'] ) { return new WP_Error( 'spacebay_comment_not_found', __( 'Comment not found.', 'spacebay' ), array( 'status' => 404 ) ); } $found = $this->readable_post( $comment['post_id'] ); if ( is_wp_error( $found ) ) { return new WP_Error( 'spacebay_comment_not_found', __( 'Comment not found.', 'spacebay' ), array( 'status' => 404 ) ); } $found['comment'] = $comment; return $found; } /** * Returns a comment the viewer may change. * * @param int $id Comment ID. * @return array|WP_Error */ private function changeable_comment( $id ) { $found = $this->readable_comment( $id ); if ( is_wp_error( $found ) ) { return $found; } if ( ! PostAccess::can_change( $found['space'], $found['comment']['user_id'], get_current_user_id() ) ) { return $this->forbidden(); } return $found; } }