When creating terms, avoid false dupe checks due to accented characters.

`wp_insert_term()` doesn't allow the creation of a term when the term `name`
is the same as another term in the same hierarchy level of the same taxonomy.
Previously, this duplicate check used `get_term_by( 'name' )`, which uses the
database collation to determine sameness. But common collations do not
distinguish between accented and non-accented versions of a character. As a
result, it was impossible to create a term 'Foo' if a sibling term with an
accented character existed.

We address this problem by using `get_terms()` to do the duplicate check. This
query returns all potentially matching terms. We then do a stricter check
for equivalence in PHP, before determining whether one of the matches is
indeed a duplicate.

Props boonebgorges, tyxla, geza.miklo, mehulkaklotar.
Fixes #33864.

git-svn-id: https://develop.svn.wordpress.org/trunk@34809 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
Boone Gorges 2015-10-03 20:24:09 +00:00
parent 8b8dcb2ae4
commit eebe549408
2 changed files with 45 additions and 1 deletions

View File

@ -2516,7 +2516,26 @@ function wp_insert_term( $term, $taxonomy, $args = array() ) {
* Prevent the creation of terms with duplicate names at the same level of a taxonomy hierarchy, * Prevent the creation of terms with duplicate names at the same level of a taxonomy hierarchy,
* unless a unique slug has been explicitly provided. * unless a unique slug has been explicitly provided.
*/ */
if ( $name_match = get_term_by( 'name', $name, $taxonomy ) ) { $name_matches = get_terms( $taxonomy, array(
'name' => $name,
'hide_empty' => false,
) );
/*
* The `name` match in `get_terms()` doesn't differentiate accented characters,
* so we do a stricter comparison here.
*/
$name_match = null;
if ( $name_matches ) {
foreach ( $name_matches as $_match ) {
if ( strtolower( $name ) === strtolower( $_match->name ) ) {
$name_match = $_match;
break;
}
}
}
if ( $name_match ) {
$slug_match = get_term_by( 'slug', $slug, $taxonomy ); $slug_match = get_term_by( 'slug', $slug, $taxonomy );
if ( ! $slug_provided || $name_match->slug === $slug || $slug_match ) { if ( ! $slug_provided || $name_match->slug === $slug || $slug_match ) {
if ( is_taxonomy_hierarchical( $taxonomy ) ) { if ( is_taxonomy_hierarchical( $taxonomy ) ) {

View File

@ -628,6 +628,31 @@ class Tests_Term_WpInsertTerm extends WP_UnitTestCase {
$this->assertTrue( in_array( $found['term_id'], $cached_children[ $t ] ) ); $this->assertTrue( in_array( $found['term_id'], $cached_children[ $t ] ) );
} }
/**
* @ticket 33864
*/
public function test_wp_insert_term_with_and_without_accents() {
register_taxonomy( 'wptests_tax', 'post' );
$t1 = $this->factory->term->create( array(
'name' => 'Foó',
'taxonomy' => 'wptests_tax',
) );
$t2 = $this->factory->term->create( array(
'name' => 'Foo',
'taxonomy' => 'wptests_tax',
) );
$this->assertInternalType( 'int', $t1 );
$this->assertInternalType( 'int', $t2 );
$this->assertNotEquals( $t1, $t2 );
$term_2 = get_term( $t2, 'wptests_tax' );
$this->assertSame( $t2, $term_2->term_id );
$this->assertSame( 'Foo', $term_2->name );
}
/** Helpers **********************************************************/ /** Helpers **********************************************************/
public function deleted_term_cb( $term, $tt_id, $taxonomy, $deleted_term ) { public function deleted_term_cb( $term, $tt_id, $taxonomy, $deleted_term ) {