Opened 7 years ago
Closed 6 years ago
#46431 closed defect (bug) (fixed)
wp_unique_term_slug() doesn't guarantee unique slugs for child terms
| Reported by: |
|
Owned by: |
|
|---|---|---|---|
| Milestone: | 5.3 | Priority: | normal |
| Severity: | normal | Version: | 4.3 |
| Component: | Taxonomy | Keywords: | has-patch has-unit-tests |
| Focuses: | Cc: |
Description
Steps to reproduce:
- Create a fresh site with WP 5.1 and Twenty Nineteen
- Create a category "Animal" with slug "animal"
- Create a category "Dog" with slug "dog"
- Create a child category for "Animal" named "Cat" with slug "dog-animal"
- Create a top-level category "Giraffe" with slug "giraffe"
- Edit the "Giraffe" category so that in a single update, the parent changes from "None" to "Animal" and the slug becomes "dog".
What I expected?
The slug of category "Giraffe" should have become "dog-animal-2".
What did I get?
Category "Giraffe" now has identical slug with "Cat", both having the exact same slug "dog-animal".
---
I tracked this down to wp_unique_term_slug(). It seems that if a parent suffix is added to a term, no further checks are made to ensure its uniqueness. The whole section where a number is appended is skipped.
Why those exact steps are needed to reproduce this has to do with differences in wp_insert_term() and wp_update_term() I won't go into detail on that as it's a completely separate issue and seemingly a huge mess...
Attachments (1)
Change History (7)
#2
@
6 years ago
I encountered this bug when I was trying to auto assign custom taxonomy term slugs by using pre_{$taxonomy}_{$field} filter hook.
I found out in the last part of wp_unique_term_slug() in taxonomy.php, when it tries to append sequel numbers in case $slug is not unique yet, it checks for $parent_suffix ('-parent-slug') and if it exists, the function ignores appending a number and returns $slug.$parent_suffix :
if ( apply_filters( 'wp_unique_term_slug_is_bad_slug', $needs_suffix, $slug, $term ) ) {
if ( $parent_suffix ) {
$slug .= $parent_suffix;
} else {
if ( ! empty( $term->term_id ) ) {
$query = $wpdb->prepare( "SELECT slug FROM $wpdb->terms WHERE slug = %s AND term_id != %d", $slug, $term->term_id );
} else {
$query = $wpdb->prepare( "SELECT slug FROM $wpdb->terms WHERE slug = %s", $slug );
}
if ( $wpdb->get_var( $query ) ) {
$num = 2;
do {
$alt_slug = $slug . "-$num";
$num++;
$slug_check = $wpdb->get_var( $wpdb->prepare( "SELECT slug FROM $wpdb->terms WHERE slug = %s", $alt_slug ) );
} while ( $slug_check );
$slug = $alt_slug;
}
}
}
Possible Solution
I modified this code as below and it seems that this solves the problem and now the number appends to both $slug.$parent_suffix and $slug in case there is a existing term with that slug:
if ( apply_filters( 'wp_unique_term_slug_is_bad_slug', $needs_suffix, $slug, $term ) ) {
/*
* MODIFIED: $parent_suffix conditional if block is closed right after appending $parent_suffix to $slug.
* Else block content for this condition moved to the outside of conditional block.
*/
if ( $parent_suffix ) {
$slug .= $parent_suffix;
}
if ( ! empty( $term->term_id ) ) {
$query = $wpdb->prepare( "SELECT slug FROM $wpdb->terms WHERE slug = %s AND term_id != %d", $slug, $term->term_id );
} else {
$query = $wpdb->prepare( "SELECT slug FROM $wpdb->terms WHERE slug = %s", $slug );
}
if ( $wpdb->get_var( $query ) ) {
$num = 2;
do {
$alt_slug = $slug . "-$num";
$num++;
$slug_check = $wpdb->get_var( $wpdb->prepare( "SELECT slug FROM $wpdb->terms WHERE slug = %s", $alt_slug ) );
} while ( $slug_check );
$slug = $alt_slug;
}
}
#3
@
6 years ago
- Keywords has-patch has-unit-tests added; needs-patch needs-unit-tests removed
- Milestone changed from Awaiting Review to 5.3
Thanks all for the detailed report.
I've written a test that confirms what @saskak describes above (minus the additional complication of wp_update_term()), and the test confirms that the suggested fix from @yashar_hv works. Could I get another set of eyes on 46431.diff ? @dlh since you dropped by recently? :)
#4
@
6 years ago
46431.diff looks good to me, with a couple potential copy-edits:
- Switch the test annotation from
@groupto@ticket. - Drop
be_from the test name:test_duplicate_parent_suffixed_slug_should_get_numeric_suffix.
Hi @saskak, and welcome to WordPress Trac!
It looks to me like this behavior was introduced in [32837] and that the cause is as you describe.