-
Notifications
You must be signed in to change notification settings - Fork 3.3k
Taxonomy: Add UI to Category page to indicate default category #10831
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: trunk
Are you sure you want to change the base?
Conversation
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the Core Committers: Use this line as a base for the props when committing in SVN: To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
Test using WordPress PlaygroundThe changes in this pull request can previewed and tested using a WordPress Playground instance. WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser. Some things to be aware of
For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation. |
|
Test failures appear unrelated — CI tooling tripping. |
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
829cb2c to
102b5bc
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR updates the Terms list table in the admin to visually surface the default term, particularly for post categories, by pinning it to the top of the list and labeling it as “Default.”
Changes:
- In
display_rows_or_placeholder(), resolves the default category for thecategorytaxonomy and renders it before the rest of the terms, while skipping it later in the non-hierarchical loop. - In
_rows(), introduces logic to detect the default category ID and skip it during hierarchical recursion so it is not duplicated when already rendered first. - In
column_name(), adds a “Default” label (with ataxonomy-default-labelspan) next to the term name when the row’s term matches the configured default term option.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| 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 ); |
Copilot
AI
Feb 1, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prepending the default category with $this->single_row( $default_category ); here without updating $count or otherwise integrating it into the paging logic means the "per page" limit is effectively exceeded: hierarchical lists will show number rows from _rows() plus this extra row, and in the non‑hierarchical branch the default term can appear on every page even when it is not part of the current slice of $this->items. This changes the semantics of pagination (screen options say N items per page but the table often renders N+1), and duplicates the default term across pages. If the goal is just to surface the default category at the top, consider only rendering it when $offset is 0 (first page) and/or adjusting the paging counters so the extra row is accounted for instead of being "outside" the pagination logic.
| // 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' ); |
Copilot
AI
Feb 1, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_rows() re-fetches get_option( 'default_category' ) on every (possibly recursive) invocation and re-derives the default term ID, even though display_rows_or_placeholder() already resolves the default term object just above. To keep the logic DRY and avoid repeated option lookups during deep hierarchies, consider computing the default term ID once (e.g., in display_rows_or_placeholder() and passing it into _rows(), or caching it on the instance) and using that in both places where you skip the default term.
| $default_category_id = (int) get_option( 'default_category' ); | |
| static $cached_default_category_id = null; | |
| if ( null === $cached_default_category_id ) { | |
| $cached_default_category_id = (int) get_option( 'default_category' ); | |
| } | |
| $default_category_id = $cached_default_category_id; |
| $default_term = get_option( 'default_' . $taxonomy ); | ||
| $default_term_label = ''; | ||
| if ( $tag->term_id == $default_term ) { | ||
| $default_term_label = ' — <span class="taxonomy-default-label">' . __( 'Default' ) . '</span>'; | ||
| } |
Copilot
AI
Feb 1, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using get_option( 'default_' . $taxonomy ) here will work for core taxonomies like category and link_category, but it will not pick up default terms registered via register_taxonomy( ... 'default_term' => ... ), which are stored under default_term_{$taxonomy} (see taxonomy.php:537-557 and post.php:5333-5337). If the intent is for the "Default" label to apply to all taxonomies that support default terms (not only categories/link categories), consider mirroring the pattern in map_meta_cap() by checking both default_{$taxonomy} and default_term_{$taxonomy} so the UI stays consistent with how default terms are modeled elsewhere.
|
One thing seems to be missing in the user interface and that is discovery for how to change the default category. Should there be a link to the Writing Settings screen, linking to the specific field on that screen? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.
I'm open to that. The main intent was to simply explain versus the user needing to infer why that category is acting differently in the list. |
- 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
- Add "Change Default" link to row actions for the default category - Add id attribute to Writing Settings default category row for fragment linking
| if ( 'category' === $taxonomy && $tag->term_id === (int) get_option( 'default_category' ) ) { | ||
| $actions['change-default'] = sprintf( | ||
| '<a href="%s">%s</a>', | ||
| admin_url( 'options-writing.php#default-category-row' ), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is slightly better because it puts keyboard focus on the select element:
| admin_url( 'options-writing.php#default-category-row' ), | |
| admin_url( 'options-writing.php#default_category' ), |
|
Oh, I like that. I don't think it would be redundant. "The default category can be renamed or changed, but not deleted" or something like that with "renamed" going to the edit category page for that term and "changed" going to the settings page seems lightweight and direct. Going to try that. I'll leave the row action too. Just like us, I thought about the category in the table and you thought about the text at the bottom (which I didn't think about at all), this is an area that I'd rather just help people find what they need no matter where their focus is, especially if it can be done in a relatively "invisible" way if you're thinking about other things. |
Update the help text below the Categories list table to include links for renaming the default category and choosing a different default.
|
Used language similar to elsewhere in Core (src/wp-admin/user-edit.php:63 - |
westonruter
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor point, but I think the $default_category_id variable can be eliminated in favor of just $default_category. And I think it would be slightly better to use null when it isn't WP_Term (but just a personal preference).
| 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 ) ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would reduce the conditions and it would handle the case of someone erroneously filtering the return value of get_term() to be something that isn't falsey:
| if ( ! $default_category || is_wp_error( $default_category ) ) { | |
| if ( ! ( $default_category instanceof WP_Term ) ) { |
| } | ||
|
|
||
| foreach ( $this->items as $term ) { | ||
| if ( $default_category_id && $default_category_id === $term->term_id ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| if ( $default_category_id && $default_category_id === $term->term_id ) { | |
| if ( $default_category && $default_category->term_id === $term->term_id ) { |
| * 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 ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| $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 ); |
| $default_category_id = (int) get_option( 'default_category' ); | ||
| $default_category = get_term( $default_category_id, 'category' ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| $default_category_id = (int) get_option( 'default_category' ); | |
| $default_category = get_term( $default_category_id, 'category' ); | |
| $default_category = get_term( (int) get_option( 'default_category' ), 'category' ); |
| $default_category = false; | ||
| $default_category_id = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| $default_category = false; | |
| $default_category_id = 0; | |
| $default_category = null; |
| $default_category = false; | ||
| $default_category_id = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| $default_category = false; | |
| $default_category_id = 0; | |
| $default_category = null; |
| /* translators: %s: Default category. */ | ||
| __( 'Deleting a category does not delete the posts in that category. Instead, posts that were only assigned to the deleted category are set to the default category %s. The default category cannot be deleted.' ), | ||
| /* translators: 1: Default category name, 2: URL to edit the default category, 3: URL to Writing Settings. */ | ||
| __( 'Deleting a category does not delete the posts in that category. Instead, posts that were only assigned to the deleted category are set to the default category %1$s. The default category cannot be deleted, but it can be <a href="%2$s">renamed</a> or you can choose a <a href="%3$s">different default category</a>.' ), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this a lot.
| '<strong>' . apply_filters( 'the_category', get_cat_name( get_option( 'default_category' ) ), '', '' ) . '</strong>' | ||
| '<strong>' . apply_filters( 'the_category', get_cat_name( $default_category_id ), '', '' ) . '</strong>', | ||
| esc_url( get_edit_term_link( $default_category_id, 'category' ) ), | ||
| esc_url( admin_url( 'options-writing.php#default-category-row' ) ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Per above.
| esc_url( admin_url( 'options-writing.php#default-category-row' ) ) | |
| esc_url( admin_url( 'options-writing.php#default_category' ) ) |
| </tr> | ||
| <?php endif; ?> | ||
| <tr> | ||
| <tr id="default-category-row"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This wouldn't be needed if we link to the field directy.
| <tr id="default-category-row"> | |
| <tr> |


This patch adds UI to the Categories admin page to clearly indicate which category is the default:
This helps users quickly identify and understand the default category behavior, as discussed in the Trac ticket.
Trac ticket: https://core.trac.wordpress.org/ticket/26268
Before:

After:

This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See GitHub Pull Requests for Code Review in the Core Handbook for more details.