Skip to content

Commit 87e656a

Browse files
committed
Notes: trash (or delete) child notes when parent is deleted.
Ensure that when a top level note is trashed (or deleted), all of its replies (children) are also trashed or deleted. If EMPTY_TRASH_DAYS is 0, notes are deleted immediately; otherwise they are marked as trash for later cleanup. Reviewed by wildworks. Merges [61248] to the 6.9 branch. Props adamsilverstein, desrosj, wildworks, mamaduka, karthickmurugan, jeffpaul, shailu25. See #64240. git-svn-id: https://develop.svn.wordpress.org/branches/6.9@61274 602fd350-edb4-49c9-b593-d223f7449a82
1 parent ad58843 commit 87e656a

File tree

2 files changed

+269
-1
lines changed

2 files changed

+269
-1
lines changed

src/wp-includes/comment.php

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1574,13 +1574,38 @@ function wp_delete_comment( $comment_id, $force_delete = false ) {
15741574
* If Trash is disabled, comment is permanently deleted.
15751575
*
15761576
* @since 2.9.0
1577+
* @since 6.9.0 Any child notes are deleted when deleting a note.
15771578
*
15781579
* @param int|WP_Comment $comment_id Comment ID or WP_Comment object.
15791580
* @return bool True on success, false on failure.
15801581
*/
15811582
function wp_trash_comment( $comment_id ) {
15821583
if ( ! EMPTY_TRASH_DAYS ) {
1583-
return wp_delete_comment( $comment_id, true );
1584+
$comment = get_comment( $comment_id );
1585+
$success = wp_delete_comment( $comment_id, true );
1586+
1587+
if ( ! $success ) {
1588+
return false;
1589+
}
1590+
1591+
// Also delete children of top level 'note' type comments.
1592+
if ( $comment && 'note' === $comment->comment_type && 0 === (int) $comment->comment_parent ) {
1593+
$children = $comment->get_children(
1594+
array(
1595+
'fields' => 'ids',
1596+
'status' => 'all',
1597+
'type' => 'note',
1598+
)
1599+
);
1600+
1601+
foreach ( $children as $child_id ) {
1602+
if ( ! wp_delete_comment( $child_id, true ) ) {
1603+
$success = false;
1604+
}
1605+
}
1606+
}
1607+
1608+
return $success;
15841609
}
15851610

15861611
$comment = get_comment( $comment_id );
@@ -1616,6 +1641,25 @@ function wp_trash_comment( $comment_id ) {
16161641
*/
16171642
do_action( 'trashed_comment', $comment->comment_ID, $comment );
16181643

1644+
// For top level 'note' type comments, also trash children.
1645+
if ( 'note' === $comment->comment_type && 0 === (int) $comment->comment_parent ) {
1646+
$children = $comment->get_children(
1647+
array(
1648+
'fields' => 'ids',
1649+
'status' => 'all',
1650+
'type' => 'note',
1651+
)
1652+
);
1653+
1654+
$success = true;
1655+
foreach ( $children as $child_id ) {
1656+
if ( ! wp_trash_comment( $child_id ) ) {
1657+
$success = false;
1658+
}
1659+
}
1660+
return $success;
1661+
}
1662+
16191663
return true;
16201664
}
16211665

tests/phpunit/tests/comment.php

Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1672,4 +1672,228 @@ public function test_unspam_should_invalidate_comment_cache() {
16721672

16731673
$this->assertSame( '1', $comment->comment_approved );
16741674
}
1675+
1676+
/**
1677+
* Tests that trashing a top-level note also trashes all direct child notes.
1678+
*
1679+
* @ticket 64240
1680+
* @covers ::wp_trash_comment
1681+
* @dataProvider data_comment_approved_statuses
1682+
*/
1683+
public function test_wp_trash_comment_trashes_child_notes( $approved_status ) {
1684+
// Create a parent note (top-level, comment_parent=0).
1685+
$parent_note = self::factory()->comment->create(
1686+
array(
1687+
'comment_post_ID' => self::$post_id,
1688+
'comment_type' => 'note',
1689+
'comment_parent' => 0,
1690+
'comment_approved' => $approved_status,
1691+
)
1692+
);
1693+
1694+
// Create child notes under the parent.
1695+
$child_note_1 = self::factory()->comment->create(
1696+
array(
1697+
'comment_post_ID' => self::$post_id,
1698+
'comment_type' => 'note',
1699+
'comment_parent' => $parent_note,
1700+
'comment_approved' => $approved_status,
1701+
)
1702+
);
1703+
1704+
$child_note_2 = self::factory()->comment->create(
1705+
array(
1706+
'comment_post_ID' => self::$post_id,
1707+
'comment_type' => 'note',
1708+
'comment_parent' => $parent_note,
1709+
'comment_approved' => $approved_status,
1710+
)
1711+
);
1712+
1713+
$child_note_3 = self::factory()->comment->create(
1714+
array(
1715+
'comment_post_ID' => self::$post_id,
1716+
'comment_type' => 'note',
1717+
'comment_parent' => $parent_note,
1718+
'comment_approved' => $approved_status,
1719+
)
1720+
);
1721+
1722+
// Trash the parent note.
1723+
wp_trash_comment( $parent_note );
1724+
1725+
// Verify parent note is trashed.
1726+
$this->assertSame( 'trash', get_comment( $parent_note )->comment_approved );
1727+
1728+
// Verify all child notes are also trashed.
1729+
$this->assertSame( 'trash', get_comment( $child_note_1 )->comment_approved );
1730+
$this->assertSame( 'trash', get_comment( $child_note_2 )->comment_approved );
1731+
$this->assertSame( 'trash', get_comment( $child_note_3 )->comment_approved );
1732+
}
1733+
1734+
/**
1735+
* Data provider for test_wp_trash_comment_trashes_child_notes.
1736+
*/
1737+
public function data_comment_approved_statuses() {
1738+
return array(
1739+
array( '1' ),
1740+
array( '0' ),
1741+
);
1742+
}
1743+
1744+
/**
1745+
* Tests that trashing a regular comment does NOT trash its children.
1746+
*
1747+
* @ticket 64240
1748+
* @covers ::wp_trash_comment
1749+
*/
1750+
public function test_wp_trash_comment_does_not_trash_child_comments() {
1751+
// Create a parent comment (default type='comment').
1752+
$parent_comment = self::factory()->comment->create(
1753+
array(
1754+
'comment_post_ID' => self::$post_id,
1755+
'comment_type' => 'comment',
1756+
'comment_parent' => 0,
1757+
'comment_approved' => '1',
1758+
)
1759+
);
1760+
1761+
// Create child comments under the parent.
1762+
$child_comment_1 = self::factory()->comment->create(
1763+
array(
1764+
'comment_post_ID' => self::$post_id,
1765+
'comment_type' => 'comment',
1766+
'comment_parent' => $parent_comment,
1767+
'comment_approved' => '1',
1768+
)
1769+
);
1770+
1771+
$child_comment_2 = self::factory()->comment->create(
1772+
array(
1773+
'comment_post_ID' => self::$post_id,
1774+
'comment_type' => 'comment',
1775+
'comment_parent' => $parent_comment,
1776+
'comment_approved' => '1',
1777+
)
1778+
);
1779+
1780+
// Trash the parent comment.
1781+
wp_trash_comment( $parent_comment );
1782+
1783+
// Verify parent comment is trashed.
1784+
$this->assertSame( 'trash', get_comment( $parent_comment )->comment_approved );
1785+
1786+
// Verify child comments are NOT trashed (maintaining existing behavior).
1787+
$this->assertSame( '1', get_comment( $child_comment_1 )->comment_approved );
1788+
$this->assertSame( '1', get_comment( $child_comment_2 )->comment_approved );
1789+
}
1790+
1791+
/**
1792+
* Tests that trashing a child note does not affect parent or siblings.
1793+
*
1794+
* @ticket 64240
1795+
* @covers ::wp_trash_comment
1796+
*/
1797+
public function test_wp_trash_comment_child_note_does_not_affect_parent_or_siblings() {
1798+
// Create a parent note.
1799+
$parent_note = self::factory()->comment->create(
1800+
array(
1801+
'comment_post_ID' => self::$post_id,
1802+
'comment_type' => 'note',
1803+
'comment_parent' => 0,
1804+
'comment_approved' => '1',
1805+
)
1806+
);
1807+
1808+
// Create multiple child notes.
1809+
$child_note_1 = self::factory()->comment->create(
1810+
array(
1811+
'comment_post_ID' => self::$post_id,
1812+
'comment_type' => 'note',
1813+
'comment_parent' => $parent_note,
1814+
'comment_approved' => '1',
1815+
)
1816+
);
1817+
1818+
$child_note_2 = self::factory()->comment->create(
1819+
array(
1820+
'comment_post_ID' => self::$post_id,
1821+
'comment_type' => 'note',
1822+
'comment_parent' => $parent_note,
1823+
'comment_approved' => '1',
1824+
)
1825+
);
1826+
1827+
$child_note_3 = self::factory()->comment->create(
1828+
array(
1829+
'comment_post_ID' => self::$post_id,
1830+
'comment_type' => 'note',
1831+
'comment_parent' => $parent_note,
1832+
'comment_approved' => '1',
1833+
)
1834+
);
1835+
1836+
// Trash only one child note.
1837+
wp_trash_comment( $child_note_2 );
1838+
1839+
// Verify the parent note is still approved.
1840+
$this->assertSame( '1', get_comment( $parent_note )->comment_approved );
1841+
1842+
// Verify the trashed child is trashed.
1843+
$this->assertSame( 'trash', get_comment( $child_note_2 )->comment_approved );
1844+
1845+
// Verify sibling notes are still approved.
1846+
$this->assertSame( '1', get_comment( $child_note_1 )->comment_approved );
1847+
$this->assertSame( '1', get_comment( $child_note_3 )->comment_approved );
1848+
}
1849+
1850+
/**
1851+
* Tests that only top-level notes trigger child deletion.
1852+
*
1853+
* @ticket 64240
1854+
* @covers ::wp_trash_comment
1855+
*/
1856+
public function test_wp_trash_comment_only_top_level_notes_trigger_child_deletion() {
1857+
// Create a parent note.
1858+
$parent_note = self::factory()->comment->create(
1859+
array(
1860+
'comment_post_ID' => self::$post_id,
1861+
'comment_type' => 'note',
1862+
'comment_parent' => 0,
1863+
'comment_approved' => '1',
1864+
)
1865+
);
1866+
1867+
// Create a child note (not top-level, has comment_parent > 0).
1868+
$child_note = self::factory()->comment->create(
1869+
array(
1870+
'comment_post_ID' => self::$post_id,
1871+
'comment_type' => 'note',
1872+
'comment_parent' => $parent_note,
1873+
'comment_approved' => '1',
1874+
)
1875+
);
1876+
1877+
// Create a sibling note (also not top-level).
1878+
$sibling_note = self::factory()->comment->create(
1879+
array(
1880+
'comment_post_ID' => self::$post_id,
1881+
'comment_type' => 'note',
1882+
'comment_parent' => $parent_note,
1883+
'comment_approved' => '1',
1884+
)
1885+
);
1886+
1887+
// Trash the child note (which has comment_parent > 0).
1888+
wp_trash_comment( $child_note );
1889+
1890+
// Verify the child note is trashed.
1891+
$this->assertSame( 'trash', get_comment( $child_note )->comment_approved );
1892+
1893+
// Verify the parent note is NOT trashed.
1894+
$this->assertSame( '1', get_comment( $parent_note )->comment_approved );
1895+
1896+
// Verify the sibling note is NOT trashed (no cascade since child is not top-level).
1897+
$this->assertSame( '1', get_comment( $sibling_note )->comment_approved );
1898+
}
16751899
}

0 commit comments

Comments
 (0)