Skip to content
Closed
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
5 changes: 4 additions & 1 deletion src/wp-admin/edit-form-comment.php
Original file line number Diff line number Diff line change
Expand Up @@ -221,12 +221,15 @@
if ( $comment_threading_enabled ) {
$max_thread_depth = (int) get_option( 'thread_comments_depth' );

// Pending comments may be nested under pending parents, but approved comments require publicly visible parents.
$parent_statuses = '1' === $comment->comment_approved ? 'approve' : array( 'approve', 'hold' );

// Limit the number of comments to keep memory usage and the size of the dropdown reasonable on busy posts.
$post_comments = get_comments(
array(
'post_id' => $comment->comment_post_ID,
'type' => 'comment',
'status' => array( 'approve', 'hold' ),
'status' => $parent_statuses,
'orderby' => 'comment_date_gmt',
'order' => 'DESC',
'number' => 100,
Expand Down
10 changes: 7 additions & 3 deletions src/wp-admin/includes/comment.php
Original file line number Diff line number Diff line change
Expand Up @@ -92,14 +92,18 @@ function edit_comment() {
return new WP_Error( 'comment_parent_invalid', __( 'A comment cannot be a reply to itself.' ) );
}

$parent = get_comment( $comment_parent );
$parent = get_comment( $comment_parent );
$parent_status = $parent ? wp_get_comment_status( $parent ) : false;
$comment_status = $_POST['comment_approved'] ?? $comment->comment_approved;
$comment_is_approved = in_array( $comment_status, array( 1, '1', 'approve' ), true );

Comment thread
tyxla marked this conversation as resolved.
// The parent must be a comment of the same type, on the same post, and not in the Trash or marked as spam.
// The parent must be a comment of the same type, on the same post, and publicly visible if the comment is approved.
if (
! $parent
|| (int) $parent->comment_post_ID !== (int) $comment->comment_post_ID
|| $parent->comment_type !== $comment->comment_type
|| in_array( wp_get_comment_status( $parent ), array( 'spam', 'trash' ), true )
|| in_array( $parent_status, array( 'spam', 'trash' ), true )
|| ( $comment_is_approved && 'approved' !== $parent_status )
) {
return new WP_Error( 'comment_parent_invalid', __( 'Invalid parent comment.' ) );
}
Expand Down
29 changes: 29 additions & 0 deletions tests/phpunit/tests/admin/EditFormComment_Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,12 @@ public function test_should_list_valid_parents_and_exclude_invalid_ones() {
'comment_approved' => 'trash',
)
);
$pending_id = self::factory()->comment->create(
array(
'comment_post_ID' => self::$post_id,
'comment_approved' => '0',
)
);
$comment_id = self::factory()->comment->create( array( 'comment_post_ID' => self::$post_id ) );
$child_id = self::factory()->comment->create(
array(
Expand All @@ -113,6 +119,29 @@ public function test_should_list_valid_parents_and_exclude_invalid_ones() {
$this->assertStringNotContainsString( "value='{$child_id}'", $dropdown, 'A reply to the comment being edited should not be listed.' );
$this->assertStringNotContainsString( "value='{$spam_id}'", $dropdown, 'A spam comment should not be listed.' );
$this->assertStringNotContainsString( "value='{$trash_id}'", $dropdown, 'A trashed comment should not be listed.' );
$this->assertStringNotContainsString( "value='{$pending_id}'", $dropdown, 'A pending comment should not be listed for an approved comment.' );
}

/**
* @ticket 65688
*/
public function test_should_list_pending_parent_for_pending_comment() {
$parent_id = self::factory()->comment->create(
array(
'comment_post_ID' => self::$post_id,
'comment_approved' => '0',
)
);
$comment_id = self::factory()->comment->create(
array(
'comment_post_ID' => self::$post_id,
'comment_approved' => '0',
)
);

$dropdown = $this->get_parent_dropdown( $comment_id );

$this->assertStringContainsString( "value='{$parent_id}'", $dropdown, 'A pending comment should be listed for another pending comment.' );
}

/**
Expand Down
74 changes: 71 additions & 3 deletions tests/phpunit/tests/admin/includes/comment/EditComment_Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,21 @@ public function tear_down() {
/**
* Calls edit_comment() with a comment ID and a new parent, as submitted from the Edit Comment screen.
*
* @param int $comment_id Comment ID.
* @param int $comment_parent New parent comment ID.
* @param int $comment_id Comment ID.
* @param int $comment_parent New parent comment ID.
* @param string|null $comment_status Optional. New comment status.
* @return int|WP_Error The edit_comment() return value.
*/
private function update_comment_parent( $comment_id, $comment_parent ) {
private function update_comment_parent( $comment_id, $comment_parent, $comment_status = null ) {
$_POST = array(
'comment_ID' => $comment_id,
'comment_parent' => $comment_parent,
);

if ( null !== $comment_status ) {
$_POST['comment_status'] = $comment_status;
}

return edit_comment();
}

Expand All @@ -75,6 +80,69 @@ public function test_should_update_comment_parent() {
$this->assertSame( (string) $parent_id, get_comment( $comment_id )->comment_parent );
}

/**
* @ticket 65688
*/
public function test_should_reject_unapproved_parent_for_approved_comment() {
$parent_id = self::factory()->comment->create(
array(
'comment_post_ID' => self::$post_id,
'comment_approved' => '0',
)
);
$comment_id = self::factory()->comment->create( array( 'comment_post_ID' => self::$post_id ) );

$result = $this->update_comment_parent( $comment_id, $parent_id );

$this->assertWPError( $result );
$this->assertSame( 'comment_parent_invalid', $result->get_error_code() );
$this->assertSame( '0', get_comment( $comment_id )->comment_parent );
}

/**
* @ticket 65688
*/
public function test_should_reject_unapproved_parent_when_comment_is_approved_in_same_update() {
$parent_id = self::factory()->comment->create(
array(
'comment_post_ID' => self::$post_id,
'comment_approved' => '0',
)
);
$comment_id = self::factory()->comment->create(
array(
'comment_post_ID' => self::$post_id,
'comment_approved' => '0',
)
);

$result = $this->update_comment_parent( $comment_id, $parent_id, '1' );

$this->assertWPError( $result );
$this->assertSame( 'comment_parent_invalid', $result->get_error_code() );
$this->assertSame( '0', get_comment( $comment_id )->comment_approved );
$this->assertSame( '0', get_comment( $comment_id )->comment_parent );
}

Comment thread
tyxla marked this conversation as resolved.
/**
* @ticket 65688
*/
public function test_should_allow_unapproved_parent_when_comment_is_unapproved_in_same_update() {
$parent_id = self::factory()->comment->create(
array(
'comment_post_ID' => self::$post_id,
'comment_approved' => '0',
)
);
$comment_id = self::factory()->comment->create( array( 'comment_post_ID' => self::$post_id ) );

$result = $this->update_comment_parent( $comment_id, $parent_id, '0' );

$this->assertSame( 1, $result );
$this->assertSame( '0', get_comment( $comment_id )->comment_approved );
$this->assertSame( (string) $parent_id, get_comment( $comment_id )->comment_parent );
}

/**
* @ticket 65570
*/
Expand Down
Loading