Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
b7cb7fc
Comments: Add filter for comment types excluded from queries by default
adamsilverstein Jun 24, 2026
4b57b1a
Apply suggestion from @adamsilverstein
adamsilverstein Jun 24, 2026
9e9fb0b
Apply suggestion from @adamsilverstein
adamsilverstein Jun 24, 2026
6e67226
Apply suggestion from @adamsilverstein
adamsilverstein Jun 24, 2026
237d916
Apply suggestion from @adamsilverstein
adamsilverstein Jun 24, 2026
bb238e9
Comments: Clarify the default_excluded_comment_types filter is not ac…
adamsilverstein Jun 25, 2026
1a4bf12
Comments: Feed default_excluded_comment_types into the comment counter.
adamsilverstein Jun 25, 2026
2f75e55
Comments: Honor default_excluded_comment_types in pending counts.
adamsilverstein Jun 25, 2026
e656cbd
Comments: Extract wp_get_default_excluded_comment_types().
adamsilverstein Jul 11, 2026
9355bd4
Merge branch 'trunk' into feature/65537-excluded-comment-types-filter
adamsilverstein Jul 17, 2026
e0d3cf9
Comments: Treat aliased type requests as explicit against excluded ty…
adamsilverstein Jul 17, 2026
4f69762
Comments: Keep a comment type named '0' in the excluded types list.
adamsilverstein Jul 17, 2026
0ded145
Comments: Drop the unsupported "feeds" claim from the filter docblock.
adamsilverstein Jul 17, 2026
c082ed3
Comments: Apply the excluded types filter in the comments list table.
adamsilverstein Jul 17, 2026
bebcf94
Merge branch 'trunk' into feature/65537-excluded-comment-types-filter
adamsilverstein Jul 23, 2026
91e2876
Merge branch 'trunk' into feature/65537-excluded-comment-types-filter
adamsilverstein Jul 24, 2026
b3fc3ef
Comments: Drop non-scalar values from the excluded comment types filter.
adamsilverstein Jul 24, 2026
e658376
Potential fix for pull request finding
adamsilverstein Jul 24, 2026
3e371d0
Merge branch 'trunk' into feature/65537-excluded-comment-types-filter
adamsilverstein Jul 24, 2026
3d507a5
Merge branch 'trunk' into feature/65537-excluded-comment-types-filter
adamsilverstein Jul 24, 2026
29af50b
Merge branch 'trunk' into feature/65537-excluded-comment-types-filter
adamsilverstein Jul 25, 2026
e0e1c5e
Comments: Let the list table show an explicitly requested excluded type.
adamsilverstein Jul 25, 2026
106def5
Merge branch 'trunk' into feature/65537-excluded-comment-types-filter
adamsilverstein Jul 26, 2026
0a788e4
Merge remote-tracking branch 'origin/trunk' into feature/65537-exclud…
adamsilverstein Aug 12, 2026
50e826e
Comments: Include the excluded comment types in the comment query cac…
adamsilverstein Aug 12, 2026
ef244ab
Comments: Exclude the default-excluded comment types from the comment…
adamsilverstein Aug 12, 2026
f1bc386
Comments: Default the excluded comment types to the registered intern…
adamsilverstein Aug 12, 2026
5aec019
Comments: Pin the visibility boundaries of the excluded comment types.
adamsilverstein Aug 12, 2026
05d3739
Comments: Filter empty excluded comment types with a boolean callback.
adamsilverstein Aug 12, 2026
eee634d
Comments: Apply the default comment type exclusions to comment page m…
adamsilverstein Aug 12, 2026
00576f0
Comments: Stamp the excluded-types filter work for 7.2.0.
adamsilverstein Aug 23, 2026
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
38 changes: 37 additions & 1 deletion src/wp-admin/includes/class-wp-comments-list-table.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,48 @@ public function prepare_items() {
$comment_status = 'all';
}

/*
* Notes carry their own visibility rules, so they are never listed here and a
* request for them is dropped outright. This is deliberately narrower than the
* treatment of the other default-excluded types below, which can be listed when
* explicitly requested. Emptying the excluded set does surface notes in the
* untyped views, but not as a type the table can be filtered to.
*/
$comment_type = '';

if ( ! empty( $_REQUEST['comment_type'] ) && 'note' !== $_REQUEST['comment_type'] ) {
$comment_type = $_REQUEST['comment_type'];
}

/*
* WP_Comment_Query drops the default exclusions when 'all' types are
* requested, so they are also passed as 'type__not_in' to keep excluded
* types out of the list table in that case.
*
* The requested type is removed from that list, so a plugin that adds
* its own default-excluded type to the type dropdown via
* 'admin_comment_types_dropdown' can still list it. Type aliases are
* expanded first, matching how WP_Comment_Query resolves them.
*/
switch ( $comment_type ) {
// Kept for symmetry with WP_Comment_Query; the accessor strips these tokens
// from the excluded set, so this branch can never subtract anything.
case 'comment':
case 'comments':
$requested_types = array( '', 'comment' );
break;

case 'pings':
$requested_types = array( 'pingback', 'trackback' );
break;

default:
$requested_types = array( $comment_type );
break;
}

$excluded_types = array_values( array_diff( wp_get_default_excluded_comment_types(), $requested_types ) );

$search = $_REQUEST['s'] ?? '';

$post_type = ( isset( $_REQUEST['post_type'] ) ) ? sanitize_key( $_REQUEST['post_type'] ) : '';
Expand Down Expand Up @@ -155,7 +191,7 @@ public function prepare_items() {
'number' => $number,
'post_id' => $post_id,
'type' => $comment_type,
'type__not_in' => array( 'note' ),
'type__not_in' => $excluded_types,
'orderby' => $orderby,
'order' => $order,
Comment on lines 192 to 196

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good catch, fixed in e0e1c5e.

I didn't drop type__not_in entirely though - the list table still has to force the exclusions for a type=all request. WP_Comment_Query skips its default exclusions when 'all' is requested, and there is an existing test (test_comments_list_table_does_not_show_note_comment_type, added for #64198/#64474) asserting notes stay hidden on edit-comments.php?comment_type=all. Removing the arg outright regresses that.

Instead the requested type is now subtracted from the exclusion list before the query runs, with the 'comment'/'comments'/'pings' aliases expanded the same way WP_Comment_Query expands them:

$excluded_types = array_values( array_diff( wp_get_default_excluded_comment_types(), $requested_types ) );

The 'note' type is still stripped from the request a few lines above, so that one remains unlistable in the admin regardless.

Added two tests in tests/phpunit/tests/admin/wpCommentsListTable.php: a filtered-in 'private' type stays hidden for both an empty and an all request, and it is listed when explicitly selected. The second one fails against the old code (0 items) and passes now.

'post_type' => $post_type,
Expand Down
8 changes: 7 additions & 1 deletion src/wp-admin/includes/comment.php
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,8 @@ function get_comment_to_edit( $id ) {
*
* @since 2.3.0
* @since 6.9.0 Exclude the 'note' comment type from the count.
* @since 7.2.0 The excluded comment types are derived from the
* {@see 'default_excluded_comment_types'} filter.
*
* @global wpdb $wpdb WordPress database abstraction object.
*
Expand All @@ -242,7 +244,11 @@ 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 );
$type_not_in = _wp_get_excluded_comment_types_clause();

// $post_id_in is built from integers and $type_not_in is prepared above.
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
$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'$type_not_in GROUP BY comment_post_ID", ARRAY_A );

if ( $single ) {
if ( empty( $pending ) ) {
Expand Down
70 changes: 63 additions & 7 deletions src/wp-includes/class-wp-comment-query.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,17 @@ class WP_Comment_Query {
*/
protected $filtered_where_clause;

/**
* Comment types excluded from the results by default.
*
* Resolved once per query in get_comments(), so that the set folded into the cache
* key is the same one get_comment_ids() builds the SQL from. Null until resolved.
*
* @since 7.2.0
* @var string[]|null
*/
protected $default_excluded_comment_types = null;

/**
* Date query container
*
Expand Down Expand Up @@ -455,6 +466,18 @@ public function get_comments() {
$_args = wp_array_slice_assoc( $this->query_vars, array_keys( $this->query_var_defaults ) );
unset( $_args['fields'], $_args['update_comment_meta_cache'], $_args['update_comment_post_cache'] );

/*
* The default-excluded types are not query vars, but they do change the results,
* so they belong in the key. Without them a persistent object cache would keep
* serving entries built before a plugin changed the excluded set, since changing
* it does not touch the comment 'last_changed' value the key is salted with.
*
* The resolved set is reused in get_comment_ids() so the filter runs once per query.
*/
$this->default_excluded_comment_types = wp_get_default_excluded_comment_types( $this );

$_args['default_excluded_comment_types'] = $this->default_excluded_comment_types;

$key = md5( serialize( $_args ) );
$last_changed = wp_cache_get_last_changed( 'comment' );

Expand Down Expand Up @@ -544,6 +567,7 @@ public function get_comments() {
*
* @since 4.4.0
* @since 6.9.0 Excludes the 'note' comment type, unless 'all' or the 'note' types are requested.
* @since 7.2.0 The default-excluded comment types are filterable via {@see 'default_excluded_comment_types'}.
*
* @global wpdb $wpdb WordPress database abstraction object.
*
Expand Down Expand Up @@ -779,13 +803,45 @@ protected function get_comment_ids() {
'NOT IN' => (array) $this->query_vars['type__not_in'],
);

// Exclude the 'note' comment type, unless 'all' types or the 'note' type explicitly are requested.
if (
! in_array( 'all', $raw_types['IN'], true ) &&
! in_array( 'note', $raw_types['IN'], true ) &&
! in_array( 'note', $raw_types['NOT IN'], true )
) {
$raw_types['NOT IN'][] = 'note';
// Resolved in get_comments() when the cache key is built; resolve here for direct calls.
if ( null === $this->default_excluded_comment_types ) {
$this->default_excluded_comment_types = wp_get_default_excluded_comment_types( $this );
}

$excluded_types = $this->default_excluded_comment_types;

// Unless all types are requested, exclude each default-excluded type
// that the query does not explicitly request. The special type tokens
// in the request ('comment', 'comments', 'pings') are first expanded to
// the literal comment_type values they represent, so a type requested
// via an alias (for example 'pings' for 'pingback' and 'trackback') is
// still treated as explicitly requested and is not excluded.
if ( ! in_array( 'all', $raw_types['IN'], true ) ) {
$requested_types = array();
foreach ( $raw_types['IN'] as $requested_type ) {
switch ( $requested_type ) {
case 'comment':
case 'comments':
$requested_types[] = '';
$requested_types[] = 'comment';
break;

case 'pings':
$requested_types[] = 'pingback';
$requested_types[] = 'trackback';
break;

default:
$requested_types[] = $requested_type;
break;
}
}

foreach ( $excluded_types as $excluded_type ) {
if ( ! in_array( $excluded_type, $requested_types, true ) ) {
$raw_types['NOT IN'][] = $excluded_type;
}
}
}

$comment_types = array();
Expand Down
10 changes: 7 additions & 3 deletions src/wp-includes/class-wp-query.php
Original file line number Diff line number Diff line change
Expand Up @@ -2825,13 +2825,15 @@ public function get_posts() {

// Comments feeds.
if ( $this->is_comment_feed && ! $this->is_singular ) {
$ctype_not_in = _wp_get_excluded_comment_types_clause( "{$wpdb->comments}.comment_type" );

if ( $this->is_archive || $this->is_search ) {
$cjoin = "JOIN {$wpdb->posts} ON ( {$wpdb->comments}.comment_post_ID = {$wpdb->posts}.ID ) $join ";
$cwhere = "WHERE comment_approved = '1' AND {$wpdb->comments}.comment_type != 'note' $where";
$cwhere = "WHERE comment_approved = '1'$ctype_not_in $where";
$cgroupby = "{$wpdb->comments}.comment_id";
} else { // Other non-singular, e.g. front.
$cjoin = "JOIN {$wpdb->posts} ON ( {$wpdb->comments}.comment_post_ID = {$wpdb->posts}.ID )";
$cwhere = "WHERE ( post_status = 'publish' OR ( post_status = 'inherit' AND post_type = 'attachment' ) ) AND comment_approved = '1' AND {$wpdb->comments}.comment_type != 'note'";
$cwhere = "WHERE ( post_status = 'publish' OR ( post_status = 'inherit' AND post_type = 'attachment' ) ) AND comment_approved = '1'$ctype_not_in";
$cgroupby = '';
}

Expand Down Expand Up @@ -3485,11 +3487,13 @@ public function get_posts() {
}

if ( ! empty( $this->posts ) && $this->is_comment_feed && $this->is_singular ) {
$ctype_not_in = _wp_get_excluded_comment_types_clause( "{$wpdb->comments}.comment_type" );

/** This filter is documented in wp-includes/class-wp-query.php */
$cjoin = apply_filters_ref_array( 'comment_feed_join', array( '', &$this ) );

/** This filter is documented in wp-includes/class-wp-query.php */
$cwhere = apply_filters_ref_array( 'comment_feed_where', array( "WHERE comment_post_ID = '{$this->posts[0]->ID}' AND comment_approved = '1' AND {$wpdb->comments}.comment_type != 'note'", &$this ) );
$cwhere = apply_filters_ref_array( 'comment_feed_where', array( "WHERE comment_post_ID = '{$this->posts[0]->ID}' AND comment_approved = '1'$ctype_not_in", &$this ) );

/** This filter is documented in wp-includes/class-wp-query.php */
$cgroupby = apply_filters_ref_array( 'comment_feed_groupby', array( '', &$this ) );
Expand Down
3 changes: 2 additions & 1 deletion src/wp-includes/comment-template.php
Original file line number Diff line number Diff line change
Expand Up @@ -755,6 +755,7 @@ function comment_ID() { // phpcs:ignore WordPress.NamingConventions.ValidFunctio
*
* @since 1.5.0
* @since 4.4.0 Added the ability for `$comment` to also accept a WP_Comment object. Added `$cpage` argument.
* @since 7.2.0 The default 'type' changed from 'all' to '', matching get_page_of_comment().
*
* @see get_page_of_comment()
*
Expand Down Expand Up @@ -786,7 +787,7 @@ function get_comment_link( $comment = null, $args = array() ) {
}

$defaults = array(
'type' => 'all',
'type' => '',
'page' => '',
'per_page' => '',
'max_depth' => '',
Expand Down
Loading
Loading