Skip to content
Merged
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
1 change: 1 addition & 0 deletions backport-changelog/7.1/12503.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
https://github.com/WordPress/wordpress-develop/pull/12503

* https://github.com/WordPress/gutenberg/pull/79604
* https://github.com/WordPress/gutenberg/pull/80221
17 changes: 14 additions & 3 deletions lib/compat/wordpress-7.1/block-comments.php
Original file line number Diff line number Diff line change
Expand Up @@ -198,9 +198,9 @@ function gutenberg_notes_scope_mention_kses( $commentdata ) {
* Arms the mention markup allowance for REST note writes.
*
* Runs in WP_REST_Comments_Controller::prepare_item_for_database() for both
* creates and updates, before the comment is sanitized. Updates do not carry
* the comment type in the prepared data, so it is resolved from the comment
* being updated.
* creates and updates, before the comment is sanitized. Neither carries the
* comment type in the prepared data, so it is resolved from the comment being
* updated or, on creation, from the request's `type` param.
*
* @param array $prepared_comment Prepared comment data.
* @param WP_REST_Request $request The REST request.
Expand All @@ -213,6 +213,17 @@ function gutenberg_notes_scope_mention_kses_rest( $prepared_comment, $request )
$comment_type = get_comment_type( (int) $request['id'] );
}

/*
* On creation the controller only copies the `type` param into the
* prepared data after this filter has run, and it inserts through
* wp_filter_comment() directly - never through wp_new_comment() and its
* 'preprocess_comment' filter - so this is the only chance to arm and the
* type must be resolved from the request.
*/
if ( '' === $comment_type && isset( $request['type'] ) ) {
$comment_type = $request['type'];
}

if ( 'note' === $comment_type ) {
gutenberg_notes_arm_mention_kses();
}
Expand Down
57 changes: 57 additions & 0 deletions phpunit/tests/notes-mention-kses-test.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,34 @@ public function test_rest_prepare_arms_for_note_update() {
gutenberg_notes_disarm_mention_kses();
}

public function test_rest_prepare_arms_for_note_creation() {
$request = new WP_REST_Request( 'POST', '/wp/v2/comments' );
$request->set_param( 'type', 'note' );

// On creation the prepared data does not carry the comment type either:
// the controller only copies the `type` param in after preparing.
gutenberg_notes_scope_mention_kses_rest( array(), $request );

$this->assertNotFalse(
has_filter( 'wp_kses_allowed_html', 'gutenberg_notes_allow_mention_attributes' ),
'Creating a note should arm the mention allowance.'
);

gutenberg_notes_disarm_mention_kses();
}

public function test_rest_prepare_does_not_arm_for_regular_comment_creation() {
$request = new WP_REST_Request( 'POST', '/wp/v2/comments' );
$request->set_param( 'type', 'comment' );

gutenberg_notes_scope_mention_kses_rest( array(), $request );

$this->assertFalse(
has_filter( 'wp_kses_allowed_html', 'gutenberg_notes_allow_mention_attributes' ),
'Creating a regular comment should not arm the mention allowance.'
);
}

public function test_rest_prepare_does_not_arm_for_regular_comment_update() {
$post_id = self::factory()->post->create();
$comment_id = self::factory()->comment->create( array( 'comment_post_ID' => $post_id ) );
Expand Down Expand Up @@ -134,6 +162,35 @@ public function test_mention_markup_survives_note_insert_end_to_end() {
$this->assertSame( self::MENTION_FILTERED, get_comment( $comment_id )->comment_content );
}

public function test_mention_markup_survives_rest_note_creation_end_to_end() {
$author_id = self::factory()->user->create( array( 'role' => 'author' ) );
$post_id = self::factory()->post->create( array( 'post_author' => $author_id ) );

// Authors lack `unfiltered_html`, so comment kses filters their content.
wp_set_current_user( $author_id );
$this->assertFalse( current_user_can( 'unfiltered_html' ) );

$request = new WP_REST_Request( 'POST', '/wp/v2/comments' );
$request->set_param( 'post', $post_id );
$request->set_param( 'type', 'note' );
$request->set_param( 'content', self::MENTION_CONTENT );

$response = rest_get_server()->dispatch( $request );
$this->assertSame( 201, $response->get_status() );

/*
* REST creation reaches kses through wp_filter_comment() directly, not
* through wp_new_comment() and its 'preprocess_comment' filter, so the
* REST-specific arming must cover it or mentions written by users
* without `unfiltered_html` are silently stripped on save.
*/
$data = $response->get_data();
$this->assertSame(
self::MENTION_FILTERED,
get_comment( (int) $data['id'] )->comment_content
);
}

/**
* Runs commentdata of the given type through the insert-path sanitization.
*
Expand Down
Loading