Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/wp-admin/includes/class-wp-comments-list-table.php
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ public function prepare_items() {

$comment_type = '';

if ( ! empty( $_REQUEST['comment_type'] ) && 'note' !== $_REQUEST['comment_type'] ) {
if ( ! empty( $_REQUEST['comment_type'] ) && ! in_array( $_REQUEST['comment_type'], array( 'note', 'reaction' ), true ) ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like perhaps there should be some centralized place for this list of non-comment comment types which can be re-used. This would avoid having to manually and tediously update all these instances whenever there is a new such type introduced. Maybe like a wp_get_internal_comment_types().

Then this could be:

Suggested change
if ( ! empty( $_REQUEST['comment_type'] ) && ! in_array( $_REQUEST['comment_type'], array( 'note', 'reaction' ), true ) ) {
if ( ! empty( $_REQUEST['comment_type'] ) && ! in_array( $_REQUEST['comment_type'], wp_get_internal_comment_types(), true ) ) {

$comment_type = $_REQUEST['comment_type'];
}

Expand Down Expand Up @@ -155,7 +155,7 @@ public function prepare_items() {
'number' => $number,
'post_id' => $post_id,
'type' => $comment_type,
'type__not_in' => array( 'note' ),
'type__not_in' => array( 'note', 'reaction' ),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the above suggestion:

Suggested change
'type__not_in' => array( 'note', 'reaction' ),
'type__not_in' => wp_get_internal_comment_types(),

'orderby' => $orderby,
'order' => $order,
'post_type' => $post_type,
Expand Down
2 changes: 1 addition & 1 deletion src/wp-admin/includes/comment.php
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ function get_pending_comments_num( $post_id ) {
$post_id_array = array_map( 'intval', $post_id_array );
$post_id_in = "'" . implode( "', '", $post_id_array ) . "'";

$pending = $wpdb->get_results( "SELECT comment_post_ID, COUNT(comment_ID) as num_comments FROM $wpdb->comments WHERE comment_post_ID IN ( $post_id_in ) AND comment_approved = '0' AND comment_type != 'note' GROUP BY comment_post_ID", ARRAY_A );
$pending = $wpdb->get_results( "SELECT comment_post_ID, COUNT(comment_ID) as num_comments FROM $wpdb->comments WHERE comment_post_ID IN ( $post_id_in ) AND comment_approved = '0' AND comment_type != 'note' AND comment_type != 'reaction' GROUP BY comment_post_ID", ARRAY_A );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
$pending = $wpdb->get_results( "SELECT comment_post_ID, COUNT(comment_ID) as num_comments FROM $wpdb->comments WHERE comment_post_ID IN ( $post_id_in ) AND comment_approved = '0' AND comment_type != 'note' AND comment_type != 'reaction' GROUP BY comment_post_ID", ARRAY_A );
$comment_type_not = implode(
'',
array_map(
fn( $comment_type ) => sprintf( ' AND comment_type != "%s"' ), // TODO: Prepare!???
wp_get_internal_comment_types()
)
);
$pending = $wpdb->get_results( "SELECT comment_post_ID, COUNT(comment_ID) as num_comments FROM $wpdb->comments WHERE comment_post_ID IN ( $post_id_in ) AND comment_approved = '0' $comment_type_not GROUP BY comment_post_ID", ARRAY_A );


if ( $single ) {
if ( empty( $pending ) ) {
Expand Down
2 changes: 1 addition & 1 deletion src/wp-includes/comment.php
Original file line number Diff line number Diff line change
Expand Up @@ -2875,7 +2875,7 @@ function wp_update_comment_count_now( $post_id ) {
$new = apply_filters( 'pre_wp_update_comment_count_now', null, $old, $post_id );

if ( is_null( $new ) ) {
$new = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_approved = '1' AND comment_type != 'note'", $post_id ) );
$new = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_approved = '1' AND comment_type != 'note' AND comment_type != 'reaction'", $post_id ) );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto above with using something like wp_get_internal_comment_types().

} else {
$new = (int) $new;
}
Expand Down
5 changes: 3 additions & 2 deletions src/wp-includes/link-template.php
Original file line number Diff line number Diff line change
Expand Up @@ -4349,10 +4349,11 @@ function is_avatar_comment_type( $comment_type ) {
* @since 3.0.0
*
* @since 6.9.0 The 'note' comment type was added.
* @since 7.0.0 The 'reaction' comment type was added.
*
* @param array $types An array of content types. Default contains 'comment' and 'note'.
* @param array $types An array of content types. Default contains 'comment', 'note', and 'reaction'.
*/
$allowed_comment_types = apply_filters( 'get_avatar_comment_types', array( 'comment', 'note' ) );
$allowed_comment_types = apply_filters( 'get_avatar_comment_types', array( 'comment', 'note', 'reaction' ) );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
$allowed_comment_types = apply_filters( 'get_avatar_comment_types', array( 'comment', 'note', 'reaction' ) );
$allowed_comment_types = apply_filters( 'get_avatar_comment_types', array_merge( array( 'comment' ), wp_get_internal_comment_types() ) );


return in_array( $comment_type, (array) $allowed_comment_types, true );
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ public function register_routes() {
* @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_note = in_array( $request['type'], array( 'note', 'reaction' ), true );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
$is_note = in_array( $request['type'], array( 'note', 'reaction' ), true );
$is_note = in_array( $request['type'], wp_get_internal_comment_types(), true );

$is_edit_context = 'edit' === $request['context'];
$protected_params = array( 'author', 'author_exclude', 'author_email', 'type', 'status' );
$forbidden_params = array();
Expand Down Expand Up @@ -437,8 +437,8 @@ public function get_item_permissions_check( $request ) {
return $comment;
}

// Re-map edit context capabilities when requesting `note` type.
$edit_cap = 'note' === $comment->comment_type ? array( 'edit_comment', $comment->comment_ID ) : array( 'moderate_comments' );
// Re-map edit context capabilities when requesting `note` or `reaction` type.
$edit_cap = in_array( $comment->comment_type, array( 'note', 'reaction' ), true ) ? array( 'edit_comment', $comment->comment_ID ) : array( 'moderate_comments' );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
$edit_cap = in_array( $comment->comment_type, array( 'note', 'reaction' ), true ) ? array( 'edit_comment', $comment->comment_ID ) : array( 'moderate_comments' );
$edit_cap = in_array( $comment->comment_type, wp_get_internal_comment_types(), true ) ? 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',
Expand Down Expand Up @@ -497,7 +497,7 @@ public function get_item( $request ) {
* @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'];
$is_note = ! empty( $request['type'] ) && in_array( $request['type'], array( 'note', 'reaction' ), true );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
$is_note = ! empty( $request['type'] ) && in_array( $request['type'], array( 'note', 'reaction' ), true );
$is_note = ! empty( $request['type'] ) && in_array( $request['type'], wp_get_internal_comment_types(), true );


if ( ! is_user_logged_in() && $is_note ) {
return new WP_Error(
Expand Down Expand Up @@ -649,14 +649,65 @@ public function create_item( $request ) {
}

// Do not allow comments to be created with a non-core type.
if ( ! empty( $request['type'] ) && ! in_array( $request['type'], array( 'comment', 'note' ), true ) ) {
if ( ! empty( $request['type'] ) && ! in_array( $request['type'], array( 'comment', 'note', 'reaction' ), true ) ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if ( ! empty( $request['type'] ) && ! in_array( $request['type'], array( 'comment', 'note', 'reaction' ), true ) ) {
if ( ! empty( $request['type'] ) && ! in_array( $request['type'], array_merge( array( 'comment' ), wp_get_internal_comment_types() ), true ) ) {

return new WP_Error(
'rest_invalid_comment_type',
__( 'Cannot create a comment with that type.' ),
array( 'status' => 400 )
);
}

// Validate reaction-specific constraints.
if ( ! empty( $request['type'] ) && 'reaction' === $request['type'] ) {
$valid_emojis = array( 'heart', 'celebration', 'smile', 'eyes', 'rocket' );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No 💩? 😜


// Reaction content must be a valid emoji slug.
if ( empty( $request['content'] ) || ! in_array( $request['content'], $valid_emojis, true ) ) {
return new WP_Error(
'rest_reaction_invalid_emoji',
__( 'Reaction content must be a valid emoji slug.' ),
array( 'status' => 400 )
);
}

// Reaction parent must exist and be a note.
if ( empty( $request['parent'] ) ) {
return new WP_Error(
'rest_reaction_parent_required',
__( 'Reactions must have a parent note.' ),
array( 'status' => 400 )
);
}

$parent_comment = get_comment( $request['parent'] );
if ( ! $parent_comment || 'note' !== $parent_comment->comment_type ) {
return new WP_Error(
'rest_reaction_invalid_parent',
__( 'Reactions can only be added to notes.' ),
array( 'status' => 400 )
);
}

// Enforce uniqueness: one emoji per user per note.
Comment on lines +682 to +691
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the future, perhaps reactions could be enabled for non-note comments as well?

$existing = get_comments(
array(
'comment_type' => 'reaction',
'comment_parent' => $request['parent'],
'user_id' => get_current_user_id(),
'search' => $request['content'],
'count' => true,
)
);

if ( $existing > 0 ) {
return new WP_Error(
'rest_reaction_duplicate',
__( 'You have already added this reaction.' ),
array( 'status' => 409 )
);
}
}

$prepared_comment = $this->prepare_item_for_database( $request );
if ( is_wp_error( $prepared_comment ) ) {
return $prepared_comment;
Expand Down Expand Up @@ -735,9 +786,9 @@ public function create_item( $request ) {
);
}

// Don't check for duplicates or flooding for notes.
// Don't check for duplicates or flooding for notes or reactions.
$prepared_comment['comment_approved'] =
'note' === $prepared_comment['comment_type'] ?
in_array( $prepared_comment['comment_type'], array( 'note', 'reaction' ), true ) ?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
in_array( $prepared_comment['comment_type'], array( 'note', 'reaction' ), true ) ?
in_array( $prepared_comment['comment_type'], wp_get_internal_comment_types(), true ) ?

'1' :
wp_allow_comment( $prepared_comment, true );

Expand Down Expand Up @@ -1297,7 +1348,7 @@ protected function prepare_links( $comment ) {
}

// Embedding children for notes requires `type` and `status` inheritance.
if ( isset( $links['children'] ) && 'note' === $comment->comment_type ) {
if ( isset( $links['children'] ) && in_array( $comment->comment_type, array( 'note', 'reaction' ), true ) ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if ( isset( $links['children'] ) && in_array( $comment->comment_type, array( 'note', 'reaction' ), true ) ) {
if ( isset( $links['children'] ) && in_array( $comment->comment_type, wp_get_internal_comment_types(), true ) ) {

$args = array(
'parent' => $comment->comment_ID,
'type' => $comment->comment_type,
Expand Down Expand Up @@ -1911,7 +1962,7 @@ protected function check_read_post_permission( $post, $request ) {
* @return bool Whether the comment can be read.
*/
protected function check_read_permission( $comment, $request ) {
if ( 'note' !== $comment->comment_type && ! empty( $comment->comment_post_ID ) ) {
if ( ! in_array( $comment->comment_type, array( 'note', 'reaction' ), true ) && ! empty( $comment->comment_post_ID ) ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if ( ! in_array( $comment->comment_type, array( 'note', 'reaction' ), true ) && ! empty( $comment->comment_post_ID ) ) {
if ( ! in_array( $comment->comment_type, wp_get_internal_comment_types(), true ) && ! 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 ) {
Expand Down Expand Up @@ -2026,6 +2077,11 @@ protected function check_is_comment_content_allowed( $prepared_comment ) {
return true;
}

// Reactions always have content (the emoji slug), so allow them.
if ( isset( $check['comment_type'] ) && 'reaction' === $check['comment_type'] ) {
return true;
}

/*
* Do not allow a comment to be created with missing or empty
* comment_content. See wp_handle_comment_submission().
Expand Down
14 changes: 14 additions & 0 deletions tests/phpunit/tests/comment/wpUpdateCommentCountNow.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,20 @@ public function test_only_approved_regular_comments_are_counted() {
'comment_approved' => 1,
)
);
self::factory()->comment->create(
array(
'comment_post_ID' => $post_id,
'comment_type' => 'reaction',
'comment_approved' => 0,
)
);
self::factory()->comment->create(
array(
'comment_post_ID' => $post_id,
'comment_type' => 'reaction',
'comment_approved' => 1,
)
);

$this->assertTrue( wp_update_comment_count_now( $post_id ) );
$this->assertSame( '1', get_comments_number( $post_id ) );
Expand Down
Loading
Loading