From 102b5bc59a48258e6b1b04eb1ea27bf948686ede Mon Sep 17 00:00:00 2001 From: Brandon Kraft Date: Fri, 30 Jan 2026 14:56:43 -0600 Subject: [PATCH 1/9] Taxonomy: Add UI to Category page to indicate default category. Shows the default category first in the list and adds a "Default" label next to its name in the categories admin screen. See https://core.trac.wordpress.org/ticket/26268 --- .../includes/class-wp-terms-list-table.php | 42 ++++++++++++++++++- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/src/wp-admin/includes/class-wp-terms-list-table.php b/src/wp-admin/includes/class-wp-terms-list-table.php index 7537a499974d5..71c207454cd23 100644 --- a/src/wp-admin/includes/class-wp-terms-list-table.php +++ b/src/wp-admin/includes/class-wp-terms-list-table.php @@ -242,6 +242,15 @@ public function display_rows_or_placeholder() { return; } + // Handle custom display of default category by showing it first in the list. + $default_category = false; + if ( 'category' === $taxonomy ) { + $default_category = get_term( get_option( 'default_category' ), 'category' ); + if ( ! $default_category || is_wp_error( $default_category ) ) { + $default_category = false; + } + } + if ( is_taxonomy_hierarchical( $taxonomy ) && ! isset( $this->callback_args['orderby'] ) ) { if ( ! empty( $this->callback_args['search'] ) ) {// Ignore children on searches. $children = array(); @@ -249,13 +258,24 @@ public function display_rows_or_placeholder() { $children = _get_term_hierarchy( $taxonomy ); } + if ( $default_category ) { + $this->single_row( $default_category ); + } + /* * Some funky recursion to get the job done (paging & parents mainly) is contained within. * Skip it for non-hierarchical taxonomies for performance sake. */ $this->_rows( $taxonomy, $this->items, $children, $offset, $number, $count ); } else { + if ( $default_category ) { + $this->single_row( $default_category ); + } + foreach ( $this->items as $term ) { + if ( $default_category && $default_category->term_id === $term->term_id ) { + continue; + } $this->single_row( $term ); } } @@ -275,6 +295,12 @@ private function _rows( $taxonomy, $terms, &$children, $start, $per_page, &$coun $end = $start + $per_page; + // Handle display of default category by showing it first in the list, capture default category id. + $default_category_id = false; + if ( 'category' === $taxonomy ) { + $default_category_id = (int) get_option( 'default_category' ); + } + foreach ( $terms as $key => $term ) { if ( $count >= $end ) { @@ -285,6 +311,11 @@ private function _rows( $taxonomy, $terms, &$children, $start, $per_page, &$coun continue; } + // Skip duplicating display of default category. + if ( $default_category_id && $default_category_id === $term->term_id ) { + continue; + } + // If the page starts in a subtree, print the parents. if ( $count === $start && $term->parent > 0 && empty( $_REQUEST['s'] ) ) { $my_parents = array(); @@ -383,6 +414,12 @@ public function column_cb( $item ) { public function column_name( $tag ) { $taxonomy = $this->screen->taxonomy; + $default_term = get_option( 'default_' . $taxonomy ); + $default_term_label = ''; + if ( $tag->term_id == $default_term ) { + $default_term_label = ' — ' . __( 'Default' ) . ''; + } + $pad = str_repeat( '— ', max( 0, $this->level ) ); /** @@ -422,8 +459,9 @@ public function column_name( $tag ) { } $output = sprintf( - '%s
', - $name + '%s%s
', + $name, + $default_term_label ); /** This filter is documented in wp-admin/includes/class-wp-terms-list-table.php */ From 4dc573efaa532aea6023f7bb6bca5a271e0c02af Mon Sep 17 00:00:00 2001 From: Brandon Kraft Date: Mon, 2 Feb 2026 12:12:38 -0600 Subject: [PATCH 2/9] Taxonomy: Fix pagination and optimize default category handling. - Only display pinned default category on first page to maintain correct per-page count - Pass default_category_id as parameter to _rows() to avoid repeated get_option() calls during recursion --- .../includes/class-wp-terms-list-table.php | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/wp-admin/includes/class-wp-terms-list-table.php b/src/wp-admin/includes/class-wp-terms-list-table.php index 71c207454cd23..a5f3e6daaf4af 100644 --- a/src/wp-admin/includes/class-wp-terms-list-table.php +++ b/src/wp-admin/includes/class-wp-terms-list-table.php @@ -243,11 +243,14 @@ public function display_rows_or_placeholder() { } // Handle custom display of default category by showing it first in the list. - $default_category = false; + $default_category = false; + $default_category_id = 0; if ( 'category' === $taxonomy ) { - $default_category = get_term( get_option( 'default_category' ), 'category' ); + $default_category_id = (int) get_option( 'default_category' ); + $default_category = get_term( $default_category_id, 'category' ); if ( ! $default_category || is_wp_error( $default_category ) ) { - $default_category = false; + $default_category = false; + $default_category_id = 0; } } @@ -258,7 +261,8 @@ public function display_rows_or_placeholder() { $children = _get_term_hierarchy( $taxonomy ); } - if ( $default_category ) { + // Only show pinned default category on the first page. + if ( $default_category && 0 === $offset ) { $this->single_row( $default_category ); } @@ -266,14 +270,15 @@ public function display_rows_or_placeholder() { * Some funky recursion to get the job done (paging & parents mainly) is contained within. * Skip it for non-hierarchical taxonomies for performance sake. */ - $this->_rows( $taxonomy, $this->items, $children, $offset, $number, $count ); + $this->_rows( $taxonomy, $this->items, $children, $offset, $number, $count, 0, 0, $default_category_id ); } else { - if ( $default_category ) { + // Only show pinned default category on the first page. + if ( $default_category && 0 === $offset ) { $this->single_row( $default_category ); } foreach ( $this->items as $term ) { - if ( $default_category && $default_category->term_id === $term->term_id ) { + if ( $default_category_id && $default_category_id === $term->term_id ) { continue; } $this->single_row( $term ); @@ -290,17 +295,12 @@ public function display_rows_or_placeholder() { * @param int $count * @param int $parent_term * @param int $level + * @param int $default_category_id */ - private function _rows( $taxonomy, $terms, &$children, $start, $per_page, &$count, $parent_term = 0, $level = 0 ) { + private function _rows( $taxonomy, $terms, &$children, $start, $per_page, &$count, $parent_term = 0, $level = 0, $default_category_id = 0 ) { $end = $start + $per_page; - // Handle display of default category by showing it first in the list, capture default category id. - $default_category_id = false; - if ( 'category' === $taxonomy ) { - $default_category_id = (int) get_option( 'default_category' ); - } - foreach ( $terms as $key => $term ) { if ( $count >= $end ) { @@ -355,7 +355,7 @@ private function _rows( $taxonomy, $terms, &$children, $start, $per_page, &$coun unset( $terms[ $key ] ); if ( isset( $children[ $term->term_id ] ) && empty( $_REQUEST['s'] ) ) { - $this->_rows( $taxonomy, $terms, $children, $start, $per_page, $count, $term->term_id, $level + 1 ); + $this->_rows( $taxonomy, $terms, $children, $start, $per_page, $count, $term->term_id, $level + 1, $default_category_id ); } } } From b7e0e1a8d0fbd9008239d63839a9efbb18fefadd Mon Sep 17 00:00:00 2001 From: Brandon Kraft Date: Mon, 2 Feb 2026 12:24:01 -0600 Subject: [PATCH 3/9] Taxonomy: Add "Change Default" row action for default category. - Add "Change Default" link to row actions for the default category - Add id attribute to Writing Settings default category row for fragment linking --- src/wp-admin/includes/class-wp-terms-list-table.php | 8 ++++++++ src/wp-admin/options-writing.php | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/wp-admin/includes/class-wp-terms-list-table.php b/src/wp-admin/includes/class-wp-terms-list-table.php index a5f3e6daaf4af..fab353abdd938 100644 --- a/src/wp-admin/includes/class-wp-terms-list-table.php +++ b/src/wp-admin/includes/class-wp-terms-list-table.php @@ -570,6 +570,14 @@ protected function handle_row_actions( $item, $column_name, $primary ) { ); } + if ( 'category' === $taxonomy && $tag->term_id === (int) get_option( 'default_category' ) ) { + $actions['change-default'] = sprintf( + '%s', + admin_url( 'options-writing.php#default-category-row' ), + __( 'Change Default' ) + ); + } + /** * Filters the action links displayed for each term in the Tags list table. * diff --git a/src/wp-admin/options-writing.php b/src/wp-admin/options-writing.php index 6f85b54679327..dd0b64e1c64fe 100644 --- a/src/wp-admin/options-writing.php +++ b/src/wp-admin/options-writing.php @@ -78,7 +78,7 @@ - + Date: Mon, 2 Feb 2026 12:42:27 -0600 Subject: [PATCH 4/9] fix: Taxonomy: Fix Yoda condition in default category check. --- src/wp-admin/includes/class-wp-terms-list-table.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-admin/includes/class-wp-terms-list-table.php b/src/wp-admin/includes/class-wp-terms-list-table.php index fab353abdd938..5a73fb80232e4 100644 --- a/src/wp-admin/includes/class-wp-terms-list-table.php +++ b/src/wp-admin/includes/class-wp-terms-list-table.php @@ -570,7 +570,7 @@ protected function handle_row_actions( $item, $column_name, $primary ) { ); } - if ( 'category' === $taxonomy && $tag->term_id === (int) get_option( 'default_category' ) ) { + if ( 'category' === $taxonomy && (int) get_option( 'default_category' ) === $tag->term_id ) { $actions['change-default'] = sprintf( '%s', admin_url( 'options-writing.php#default-category-row' ), From c985c34a80f09af1045e414b271b254b85ed056f Mon Sep 17 00:00:00 2001 From: Brandon Kraft Date: Mon, 2 Feb 2026 14:11:43 -0600 Subject: [PATCH 5/9] Taxonomy: Add helpful links in default category help text. Update the help text below the Categories list table to include links for renaming the default category and choosing a different default. --- src/wp-admin/edit-tags.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/wp-admin/edit-tags.php b/src/wp-admin/edit-tags.php index 6efdeb9b0527d..6c5b7158c08af 100644 --- a/src/wp-admin/edit-tags.php +++ b/src/wp-admin/edit-tags.php @@ -627,11 +627,14 @@

renamed or you can choose a different default category.' ), /** This filter is documented in wp-includes/category-template.php */ - '' . apply_filters( 'the_category', get_cat_name( get_option( 'default_category' ) ), '', '' ) . '' + '' . apply_filters( 'the_category', get_cat_name( $default_category_id ), '', '' ) . '', + esc_url( get_edit_term_link( $default_category_id, 'category' ) ), + esc_url( admin_url( 'options-writing.php#default-category-row' ) ) ); ?>

From 52e5831c2adf50ad811cd8715b8bc496e3ca515a Mon Sep 17 00:00:00 2001 From: Brandon Kraft Date: Tue, 3 Feb 2026 09:56:16 -0600 Subject: [PATCH 6/9] Apply suggestions from code review Co-authored-by: Weston Ruter --- src/wp-admin/edit-tags.php | 2 +- .../includes/class-wp-terms-list-table.php | 17 +++++++---------- src/wp-admin/options-writing.php | 2 +- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/wp-admin/edit-tags.php b/src/wp-admin/edit-tags.php index 6c5b7158c08af..c8aa12a651021 100644 --- a/src/wp-admin/edit-tags.php +++ b/src/wp-admin/edit-tags.php @@ -634,7 +634,7 @@ /** This filter is documented in wp-includes/category-template.php */ '' . apply_filters( 'the_category', get_cat_name( $default_category_id ), '', '' ) . '', esc_url( get_edit_term_link( $default_category_id, 'category' ) ), - esc_url( admin_url( 'options-writing.php#default-category-row' ) ) + esc_url( admin_url( 'options-writing.php#default_category' ) ) ); ?>

diff --git a/src/wp-admin/includes/class-wp-terms-list-table.php b/src/wp-admin/includes/class-wp-terms-list-table.php index 5a73fb80232e4..f42e11ea76298 100644 --- a/src/wp-admin/includes/class-wp-terms-list-table.php +++ b/src/wp-admin/includes/class-wp-terms-list-table.php @@ -243,14 +243,11 @@ public function display_rows_or_placeholder() { } // Handle custom display of default category by showing it first in the list. - $default_category = false; - $default_category_id = 0; + $default_category = null; if ( 'category' === $taxonomy ) { - $default_category_id = (int) get_option( 'default_category' ); - $default_category = get_term( $default_category_id, 'category' ); - if ( ! $default_category || is_wp_error( $default_category ) ) { - $default_category = false; - $default_category_id = 0; + $default_category = get_term( (int) get_option( 'default_category' ), 'category' ); + if ( ! ( $default_category instanceof WP_Term ) ) { + $default_category = null; } } @@ -270,7 +267,7 @@ public function display_rows_or_placeholder() { * Some funky recursion to get the job done (paging & parents mainly) is contained within. * Skip it for non-hierarchical taxonomies for performance sake. */ - $this->_rows( $taxonomy, $this->items, $children, $offset, $number, $count, 0, 0, $default_category_id ); + $this->_rows( $taxonomy, $this->items, $children, $offset, $number, $count, 0, 0, $default_category->term_id ); } else { // Only show pinned default category on the first page. if ( $default_category && 0 === $offset ) { @@ -278,7 +275,7 @@ public function display_rows_or_placeholder() { } foreach ( $this->items as $term ) { - if ( $default_category_id && $default_category_id === $term->term_id ) { + if ( $default_category && $default_category->term_id === $term->term_id ) { continue; } $this->single_row( $term ); @@ -573,7 +570,7 @@ protected function handle_row_actions( $item, $column_name, $primary ) { if ( 'category' === $taxonomy && (int) get_option( 'default_category' ) === $tag->term_id ) { $actions['change-default'] = sprintf( '%s', - admin_url( 'options-writing.php#default-category-row' ), + admin_url( 'options-writing.php#default_category' ), __( 'Change Default' ) ); } diff --git a/src/wp-admin/options-writing.php b/src/wp-admin/options-writing.php index dd0b64e1c64fe..6f85b54679327 100644 --- a/src/wp-admin/options-writing.php +++ b/src/wp-admin/options-writing.php @@ -78,7 +78,7 @@ - + Date: Tue, 3 Feb 2026 10:09:32 -0600 Subject: [PATCH 7/9] fix: Taxonomy: Fix null dereference for hierarchical taxonomies. --- src/wp-admin/includes/class-wp-terms-list-table.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-admin/includes/class-wp-terms-list-table.php b/src/wp-admin/includes/class-wp-terms-list-table.php index f42e11ea76298..8a2cd638b11f1 100644 --- a/src/wp-admin/includes/class-wp-terms-list-table.php +++ b/src/wp-admin/includes/class-wp-terms-list-table.php @@ -267,7 +267,7 @@ public function display_rows_or_placeholder() { * Some funky recursion to get the job done (paging & parents mainly) is contained within. * Skip it for non-hierarchical taxonomies for performance sake. */ - $this->_rows( $taxonomy, $this->items, $children, $offset, $number, $count, 0, 0, $default_category->term_id ); + $this->_rows( $taxonomy, $this->items, $children, $offset, $number, $count, 0, 0, $default_category ? $default_category->term_id : 0 ); } else { // Only show pinned default category on the first page. if ( $default_category && 0 === $offset ) { From ef7c23a5a8cd1b720554221a084cc9b35583e4b7 Mon Sep 17 00:00:00 2001 From: Brandon Kraft Date: Tue, 3 Feb 2026 16:47:27 -0600 Subject: [PATCH 8/9] Taxonomy: Use strict comparison and improve docblock for default term check. Use strict comparison with integer casting for consistency with other default term checks in the same file. Also add a description to the $default_category_id parameter in the _rows() method docblock. --- src/wp-admin/includes/class-wp-terms-list-table.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wp-admin/includes/class-wp-terms-list-table.php b/src/wp-admin/includes/class-wp-terms-list-table.php index 8a2cd638b11f1..6f0253f579719 100644 --- a/src/wp-admin/includes/class-wp-terms-list-table.php +++ b/src/wp-admin/includes/class-wp-terms-list-table.php @@ -292,7 +292,7 @@ public function display_rows_or_placeholder() { * @param int $count * @param int $parent_term * @param int $level - * @param int $default_category_id + * @param int $default_category_id Optional. Term ID of the default category to skip displaying, or 0 if none. Default 0. */ private function _rows( $taxonomy, $terms, &$children, $start, $per_page, &$count, $parent_term = 0, $level = 0, $default_category_id = 0 ) { @@ -413,7 +413,7 @@ public function column_name( $tag ) { $default_term = get_option( 'default_' . $taxonomy ); $default_term_label = ''; - if ( $tag->term_id == $default_term ) { + if ( $tag->term_id === (int) $default_term ) { $default_term_label = ' — ' . __( 'Default' ) . ''; } From 7b3573f60f630eb842052c1b2d18e644d7b0e626 Mon Sep 17 00:00:00 2001 From: Brandon Kraft Date: Tue, 3 Feb 2026 16:49:22 -0600 Subject: [PATCH 9/9] Taxonomy: Remove pinning of default category to top of list. Keep the default category in its natural position within the hierarchy rather than pinning it to the top. This avoids complexity with child categories appearing detached from their parent when the default category has subcategories. The default category is still clearly indicated via the "Default" label and "Change Default" row action. The explanatory text below the categories table also describes the default category behavior. --- .../includes/class-wp-terms-list-table.php | 34 ++----------------- 1 file changed, 3 insertions(+), 31 deletions(-) diff --git a/src/wp-admin/includes/class-wp-terms-list-table.php b/src/wp-admin/includes/class-wp-terms-list-table.php index 6f0253f579719..3c70221ee3831 100644 --- a/src/wp-admin/includes/class-wp-terms-list-table.php +++ b/src/wp-admin/includes/class-wp-terms-list-table.php @@ -242,15 +242,6 @@ public function display_rows_or_placeholder() { return; } - // Handle custom display of default category by showing it first in the list. - $default_category = null; - if ( 'category' === $taxonomy ) { - $default_category = get_term( (int) get_option( 'default_category' ), 'category' ); - if ( ! ( $default_category instanceof WP_Term ) ) { - $default_category = null; - } - } - if ( is_taxonomy_hierarchical( $taxonomy ) && ! isset( $this->callback_args['orderby'] ) ) { if ( ! empty( $this->callback_args['search'] ) ) {// Ignore children on searches. $children = array(); @@ -258,26 +249,13 @@ public function display_rows_or_placeholder() { $children = _get_term_hierarchy( $taxonomy ); } - // Only show pinned default category on the first page. - if ( $default_category && 0 === $offset ) { - $this->single_row( $default_category ); - } - /* * Some funky recursion to get the job done (paging & parents mainly) is contained within. * Skip it for non-hierarchical taxonomies for performance sake. */ - $this->_rows( $taxonomy, $this->items, $children, $offset, $number, $count, 0, 0, $default_category ? $default_category->term_id : 0 ); + $this->_rows( $taxonomy, $this->items, $children, $offset, $number, $count ); } else { - // Only show pinned default category on the first page. - if ( $default_category && 0 === $offset ) { - $this->single_row( $default_category ); - } - foreach ( $this->items as $term ) { - if ( $default_category && $default_category->term_id === $term->term_id ) { - continue; - } $this->single_row( $term ); } } @@ -292,9 +270,8 @@ public function display_rows_or_placeholder() { * @param int $count * @param int $parent_term * @param int $level - * @param int $default_category_id Optional. Term ID of the default category to skip displaying, or 0 if none. Default 0. */ - private function _rows( $taxonomy, $terms, &$children, $start, $per_page, &$count, $parent_term = 0, $level = 0, $default_category_id = 0 ) { + private function _rows( $taxonomy, $terms, &$children, $start, $per_page, &$count, $parent_term = 0, $level = 0 ) { $end = $start + $per_page; @@ -308,11 +285,6 @@ private function _rows( $taxonomy, $terms, &$children, $start, $per_page, &$coun continue; } - // Skip duplicating display of default category. - if ( $default_category_id && $default_category_id === $term->term_id ) { - continue; - } - // If the page starts in a subtree, print the parents. if ( $count === $start && $term->parent > 0 && empty( $_REQUEST['s'] ) ) { $my_parents = array(); @@ -352,7 +324,7 @@ private function _rows( $taxonomy, $terms, &$children, $start, $per_page, &$coun unset( $terms[ $key ] ); if ( isset( $children[ $term->term_id ] ) && empty( $_REQUEST['s'] ) ) { - $this->_rows( $taxonomy, $terms, $children, $start, $per_page, $count, $term->term_id, $level + 1, $default_category_id ); + $this->_rows( $taxonomy, $terms, $children, $start, $per_page, $count, $term->term_id, $level + 1 ); } } }