This commit is contained in:
Hanson.xyz Dev
2026-01-04 17:50:08 -06:00
parent 7e45ce0756
commit acc8ac87a0
4131 changed files with 232562 additions and 250244 deletions
@@ -123,6 +123,10 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
* @return true|WP_Error True if the request has read access, error object otherwise.
*/
public function get_items_permissions_check( $request ) {
$is_note = 'note' === $request['type'];
$is_edit_context = 'edit' === $request['context'];
$protected_params = array( 'author', 'author_exclude', 'author_email', 'type', 'status' );
$forbidden_params = array();
if ( ! empty( $request['post'] ) ) {
foreach ( (array) $request['post'] as $post_id ) {
@@ -141,10 +145,51 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
array( 'status' => rest_authorization_required_code() )
);
}
if ( $post && $is_note && ! $this->check_post_type_supports_notes( $post->post_type ) ) {
if ( current_user_can( 'edit_post', $post->ID ) ) {
return new WP_Error(
'rest_comment_not_supported_post_type',
__( 'Sorry, this post type does not support notes.' ),
array( 'status' => 403 )
);
}
foreach ( $protected_params as $param ) {
if ( 'status' === $param ) {
if ( 'approve' !== $request[ $param ] ) {
$forbidden_params[] = $param;
}
} elseif ( 'type' === $param ) {
if ( 'comment' !== $request[ $param ] ) {
$forbidden_params[] = $param;
}
} elseif ( ! empty( $request[ $param ] ) ) {
$forbidden_params[] = $param;
}
}
return new WP_Error(
'rest_forbidden_param',
/* translators: %s: List of forbidden parameters. */
sprintf( __( 'Query parameter not permitted: %s' ), implode( ', ', $forbidden_params ) ),
array( 'status' => rest_authorization_required_code() )
);
}
}
}
if ( ! empty( $request['context'] ) && 'edit' === $request['context'] && ! current_user_can( 'moderate_comments' ) ) {
// Re-map edit context capabilities when requesting `note` for a post.
if ( $is_edit_context && $is_note && ! empty( $request['post'] ) ) {
foreach ( (array) $request['post'] as $post_id ) {
if ( ! current_user_can( 'edit_post', $post_id ) ) {
return new WP_Error(
'rest_forbidden_context',
__( 'Sorry, you are not allowed to edit comments.' ),
array( 'status' => rest_authorization_required_code() )
);
}
}
} elseif ( $is_edit_context && ! current_user_can( 'moderate_comments' ) ) {
return new WP_Error(
'rest_forbidden_context',
__( 'Sorry, you are not allowed to edit comments.' ),
@@ -153,9 +198,6 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
}
if ( ! current_user_can( 'edit_posts' ) ) {
$protected_params = array( 'author', 'author_exclude', 'author_email', 'type', 'status' );
$forbidden_params = array();
foreach ( $protected_params as $param ) {
if ( 'status' === $param ) {
if ( 'approve' !== $request[ $param ] ) {
@@ -302,12 +344,13 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
$max_pages = (int) $query->max_num_pages;
if ( $total_comments < 1 ) {
// Out-of-bounds, run the query again without LIMIT for total count.
// Out-of-bounds, run the query without pagination/offset to get the total count.
unset( $prepared_args['number'], $prepared_args['offset'] );
$query = new WP_Comment_Query();
$prepared_args['count'] = true;
$prepared_args['orderby'] = 'none';
$query = new WP_Comment_Query();
$prepared_args['count'] = true;
$prepared_args['orderby'] = 'none';
$prepared_args['update_comment_meta_cache'] = false;
$total_comments = $query->query( $prepared_args );
$max_pages = (int) ceil( $total_comments / $request['per_page'] );
@@ -394,7 +437,9 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
return $comment;
}
if ( ! empty( $request['context'] ) && 'edit' === $request['context'] && ! current_user_can( 'moderate_comments' ) ) {
// Re-map edit context capabilities when requesting `note` type.
$edit_cap = 'note' === $comment->comment_type ? array( 'edit_comment', $comment->comment_ID ) : array( 'moderate_comments' );
if ( ! empty( $request['context'] ) && 'edit' === $request['context'] && ! current_user_can( ...$edit_cap ) ) {
return new WP_Error(
'rest_forbidden_context',
__( 'Sorry, you are not allowed to edit comments.' ),
@@ -452,6 +497,16 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
* @return true|WP_Error True if the request has access to create items, error object otherwise.
*/
public function create_item_permissions_check( $request ) {
$is_note = ! empty( $request['type'] ) && 'note' === $request['type'];
if ( ! is_user_logged_in() && $is_note ) {
return new WP_Error(
'rest_comment_login_required',
__( 'Sorry, you must be logged in to comment.' ),
array( 'status' => 401 )
);
}
if ( ! is_user_logged_in() ) {
if ( get_option( 'comment_registration' ) ) {
return new WP_Error(
@@ -505,7 +560,8 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
}
}
if ( isset( $request['status'] ) && ! current_user_can( 'moderate_comments' ) ) {
$edit_cap = $is_note ? array( 'edit_post', (int) $request['post'] ) : array( 'moderate_comments' );
if ( isset( $request['status'] ) && ! current_user_can( ...$edit_cap ) ) {
return new WP_Error(
'rest_comment_invalid_status',
/* translators: %s: Request parameter. */
@@ -532,7 +588,15 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
);
}
if ( 'draft' === $post->post_status ) {
if ( $is_note && ! $this->check_post_type_supports_notes( $post->post_type ) ) {
return new WP_Error(
'rest_comment_not_supported_post_type',
__( 'Sorry, this post type does not support notes.' ),
array( 'status' => 403 )
);
}
if ( 'draft' === $post->post_status && ! $is_note ) {
return new WP_Error(
'rest_comment_draft_post',
__( 'Sorry, you are not allowed to create a comment on this post.' ),
@@ -556,7 +620,7 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
);
}
if ( ! comments_open( $post->ID ) ) {
if ( ! comments_open( $post->ID ) && ! $is_note ) {
return new WP_Error(
'rest_comment_closed',
__( 'Sorry, comments are closed for this item.' ),
@@ -584,8 +648,8 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
);
}
// Do not allow comments to be created with a non-default type.
if ( ! empty( $request['type'] ) && 'comment' !== $request['type'] ) {
// Do not allow comments to be created with a non-core type.
if ( ! empty( $request['type'] ) && ! in_array( $request['type'], array( 'comment', 'note' ), true ) ) {
return new WP_Error(
'rest_invalid_comment_type',
__( 'Cannot create a comment with that type.' ),
@@ -598,12 +662,17 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
return $prepared_comment;
}
$prepared_comment['comment_type'] = 'comment';
$prepared_comment['comment_type'] = $request['type'];
if ( ! isset( $prepared_comment['comment_content'] ) ) {
$prepared_comment['comment_content'] = '';
}
// Include note metadata into check_is_comment_content_allowed.
if ( isset( $request['meta']['_wp_note_status'] ) ) {
$prepared_comment['meta']['_wp_note_status'] = $request['meta']['_wp_note_status'];
}
if ( ! $this->check_is_comment_content_allowed( $prepared_comment ) ) {
return new WP_Error(
'rest_comment_content_invalid',
@@ -666,7 +735,11 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
);
}
$prepared_comment['comment_approved'] = wp_allow_comment( $prepared_comment, true );
// Don't check for duplicates or flooding for notes.
$prepared_comment['comment_approved'] =
'note' === $prepared_comment['comment_type'] ?
'1' :
wp_allow_comment( $prepared_comment, true );
if ( is_wp_error( $prepared_comment['comment_approved'] ) ) {
$error_code = $prepared_comment['comment_approved']->get_error_code();
@@ -859,8 +932,7 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
if ( is_wp_error( $prepared_args ) ) {
return $prepared_args;
}
if ( isset( $prepared_args['comment_content'] ) && empty( $prepared_args['comment_content'] ) ) {
if ( ! $this->check_is_comment_content_allowed( $prepared_args ) ) {
return new WP_Error(
'rest_comment_content_invalid',
__( 'Invalid comment content.' ),
@@ -1207,6 +1279,7 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
array(
'count' => true,
'orderby' => 'none',
'type' => 'all',
)
);
@@ -1223,6 +1296,22 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
);
}
// Embedding children for notes requires `type` and `status` inheritance.
if ( isset( $links['children'] ) && 'note' === $comment->comment_type ) {
$args = array(
'parent' => $comment->comment_ID,
'type' => $comment->comment_type,
'status' => 'all',
);
$rest_url = add_query_arg( $args, rest_url( $this->namespace . '/' . $this->rest_base ) );
$links['children'] = array(
'href' => $rest_url,
'embeddable' => true,
);
}
return $links;
}
@@ -1520,6 +1609,7 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
'type' => 'string',
'context' => array( 'view', 'edit', 'embed' ),
'readonly' => true,
'default' => 'comment',
),
),
);
@@ -1821,7 +1911,7 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
* @return bool Whether the comment can be read.
*/
protected function check_read_permission( $comment, $request ) {
if ( ! empty( $comment->comment_post_ID ) ) {
if ( 'note' !== $comment->comment_type && ! empty( $comment->comment_post_ID ) ) {
$post = get_post( $comment->comment_post_ID );
if ( $post ) {
if ( $this->check_read_post_permission( $post, $request ) && 1 === (int) $comment->comment_approved ) {
@@ -1903,6 +1993,10 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
* @return bool True if the content is allowed, false otherwise.
*/
protected function check_is_comment_content_allowed( $prepared_comment ) {
if ( ! isset( $prepared_comment['comment_content'] ) ) {
return true;
}
$check = wp_parse_args(
$prepared_comment,
array(
@@ -1922,10 +2016,42 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
return true;
}
// Allow empty notes only when resolution metadata is valid.
if (
isset( $check['comment_type'] ) &&
'note' === $check['comment_type'] &&
isset( $check['meta']['_wp_note_status'] ) &&
in_array( $check['meta']['_wp_note_status'], array( 'resolved', 'reopen' ), true )
) {
return true;
}
/*
* Do not allow a comment to be created with missing or empty
* comment_content. See wp_handle_comment_submission().
*/
return '' !== $check['comment_content'];
}
/**
* Check if post type supports notes.
*
* @param string $post_type Post type name.
* @return bool True if post type supports notes, false otherwise.
*/
private function check_post_type_supports_notes( $post_type ) {
$supports = get_all_post_type_supports( $post_type );
if ( ! isset( $supports['editor'] ) ) {
return false;
}
if ( ! is_array( $supports['editor'] ) ) {
return false;
}
foreach ( $supports['editor'] as $item ) {
if ( ! empty( $item['notes'] ) ) {
return true;
}
}
return false;
}
}