diff --git a/src/wp-includes/taxonomy.php b/src/wp-includes/taxonomy.php index 3a9b7c985b..8b4e947aab 100644 --- a/src/wp-includes/taxonomy.php +++ b/src/wp-includes/taxonomy.php @@ -2845,45 +2845,32 @@ function wp_insert_term( $term, $taxonomy, $args = array() ) { } } - if ( $term_id = term_exists($slug) ) { - $existing_term = $wpdb->get_row( $wpdb->prepare( "SELECT name FROM $wpdb->terms WHERE term_id = %d", $term_id), ARRAY_A ); - // We've got an existing term in the same taxonomy, which matches the name of the new term: - if ( is_taxonomy_hierarchical($taxonomy) && $existing_term['name'] == $name && $exists = term_exists( (int) $term_id, $taxonomy ) ) { - // Hierarchical, and it matches an existing term, Do not allow same "name" in the same level. - $siblings = get_terms($taxonomy, array('fields' => 'names', 'get' => 'all', 'parent' => $parent ) ); - if ( in_array($name, $siblings) ) { - if ( $slug_provided ) { - return new WP_Error( 'term_exists', __( 'A term with the name and slug provided already exists with this parent.' ), $exists['term_id'] ); - } else { - return new WP_Error( 'term_exists', __( 'A term with the name provided already exists with this parent.' ), $exists['term_id'] ); + // Terms with duplicate names are not allowed at the same level of a taxonomy hierarchy. + if ( $exists = term_exists( $slug, $taxonomy ) ) { + $existing_term = get_term( $exists['term_id'], $taxonomy ); + + if ( $name === $existing_term->name ) { + + if ( is_taxonomy_hierarchical( $taxonomy ) ) { + $siblings = get_terms( $taxonomy, array( 'fields' => 'names', 'get' => 'all', 'parent' => $parent ) ); + if ( in_array( $name, $siblings ) ) { + return new WP_Error( 'term_exists', __( 'A term with the name and slug already exists with this parent.' ), $exists['term_id'] ); } + } else { - $slug = wp_unique_term_slug($slug, (object) $args); - if ( false === $wpdb->insert( $wpdb->terms, compact( 'name', 'slug', 'term_group' ) ) ) { - return new WP_Error('db_insert_error', __('Could not insert term into the database'), $wpdb->last_error); - } - $term_id = (int) $wpdb->insert_id; + return new WP_Error( 'term_exists', __( 'A term with the name and slug already exists in this taxonomy.' ), $exists['term_id'] ); } - } elseif ( $existing_term['name'] != $name ) { - // We've got an existing term, with a different name, Create the new term. - $slug = wp_unique_term_slug($slug, (object) $args); - if ( false === $wpdb->insert( $wpdb->terms, compact( 'name', 'slug', 'term_group' ) ) ) { - return new WP_Error('db_insert_error', __('Could not insert term into the database'), $wpdb->last_error); - } - $term_id = (int) $wpdb->insert_id; - } elseif ( $exists = term_exists( (int) $term_id, $taxonomy ) ) { - // Same name, same slug. - return new WP_Error( 'term_exists', __( 'A term with the name and slug provided already exists.' ), $exists['term_id'] ); } - } else { - // This term does not exist at all in the database, Create it. - $slug = wp_unique_term_slug($slug, (object) $args); - if ( false === $wpdb->insert( $wpdb->terms, compact( 'name', 'slug', 'term_group' ) ) ) { - return new WP_Error('db_insert_error', __('Could not insert term into the database'), $wpdb->last_error); - } - $term_id = (int) $wpdb->insert_id; } + $slug = wp_unique_term_slug( $slug, (object) $args ); + + if ( false === $wpdb->insert( $wpdb->terms, compact( 'name', 'slug', 'term_group' ) ) ) { + return new WP_Error( 'db_insert_error', __( 'Could not insert term into the database' ), $wpdb->last_error ); + } + + $term_id = (int) $wpdb->insert_id; + // Seems unreachable, However, Is used in the case that a term name is provided, which sanitizes to an empty string. if ( empty($slug) ) { $slug = sanitize_title($slug, $term_id); @@ -3230,6 +3217,11 @@ function wp_unique_term_slug($slug, $term) { if ( ! term_exists( $slug ) ) return $slug; + // As of 4.1, duplicate slugs are allowed as long as they're in different taxonomies. + if ( get_option( 'db_version' ) >= 30133 && ! get_term_by( 'slug', $slug, $term->taxonomy ) ) { + return $slug; + } + // If the taxonomy supports hierarchy and the term has a parent, make the slug unique // by incorporating parent slugs. if ( is_taxonomy_hierarchical($term->taxonomy) && !empty($term->parent) ) { diff --git a/tests/phpunit/tests/term.php b/tests/phpunit/tests/term.php index b6fe99488c..35d455c954 100644 --- a/tests/phpunit/tests/term.php +++ b/tests/phpunit/tests/term.php @@ -226,6 +226,93 @@ class Tests_Term extends WP_UnitTestCase { $this->assertFalse( is_wp_error( $term20 ) ); } + /** + * @ticket 5809 + */ + public function test_wp_insert_term_duplicate_slug_same_taxonomy() { + register_taxonomy( 'wptests_tax', 'post' ); + $t = $this->factory->term->create( array( + 'name' => 'Foo', + 'slug' => 'foo', + 'taxonomy' => 'wptests_tax', + ) ); + + $term = get_term( $t, 'wptests_tax' ); + + $created = wp_insert_term( 'Foo 2', 'wptests_tax', array( + 'slug' => 'foo', + ) ); + + $created_term = get_term( $created['term_id'], 'wptests_tax' ); + $this->assertSame( 'foo-2', $created_term->slug ); + + _unregister_taxonomy( 'wptests_tax', 'post' ); + } + + /** + * @ticket 5809 + */ + public function test_wp_insert_term_duplicate_slug_different_taxonomy() { + register_taxonomy( 'wptests_tax', 'post' ); + register_taxonomy( 'wptests_tax_2', 'post' ); + $t = $this->factory->term->create( array( + 'name' => 'Foo', + 'slug' => 'foo', + 'taxonomy' => 'wptests_tax', + ) ); + + $term = get_term( $t, 'wptests_tax' ); + + $created = wp_insert_term( 'Foo 2', 'wptests_tax_2', array( + 'slug' => 'foo', + ) ); + + $this->assertFalse( is_wp_error( $created ) ); + + $new_term = get_term( $created['term_id'], 'wptests_tax_2' ); + + $this->assertSame( 'foo', $new_term->slug ); + + _unregister_taxonomy( 'wptests_tax', 'post' ); + } + + /** + * @ticket 5809 + */ + public function test_wp_insert_term_duplicate_slug_different_taxonomy_before_410_schema_change() { + + $db_version = get_option( 'db_version' ); + update_option( 'db_version', 30055 ); + + register_taxonomy( 'wptests_tax', 'post' ); + register_taxonomy( 'wptests_tax_2', 'post' ); + $t = $this->factory->term->create( array( + 'name' => 'Foo', + 'slug' => 'foo', + 'taxonomy' => 'wptests_tax', + ) ); + + $term = get_term( $t, 'wptests_tax' ); + + $created = wp_insert_term( 'Foo 2', 'wptests_tax_2', array( + 'slug' => 'foo', + ) ); + + $this->assertFalse( is_wp_error( $created ) ); + + $new_term = get_term( $created['term_id'], 'wptests_tax_2' ); + + /* + * As of 4.1, we no longer create a shared term, but we also do not + * allow for duplicate slugs. + */ + $this->assertSame( 'foo-2', $new_term->slug ); + $this->assertNotEquals( $new_term->term_id, $term->term_id ); + + _unregister_taxonomy( 'wptests_tax', 'post' ); + update_option( 'db_version', $db_version ); + } + public function test_wp_insert_term_alias_of_no_term_group() { register_taxonomy( 'wptests_tax', 'post' ); $t1 = $this->factory->term->create( array( @@ -353,6 +440,19 @@ class Tests_Term extends WP_UnitTestCase { $this->assertEquals( $existing_term, $found->get_error_data() ); } + /** + * @ticket 5809 + */ + public function test_wp_insert_term_should_not_create_shared_term() { + register_taxonomy( 'wptests_tax', 'post' ); + register_taxonomy( 'wptests_tax_2', 'post' ); + + $t1 = wp_insert_term( 'Foo', 'wptests_tax' ); + $t2 = wp_insert_term( 'Foo', 'wptests_tax_2' ); + + $this->assertNotEquals( $t1['term_id'], $t2['term_id'] ); + } + public function test_wp_insert_term_should_return_term_id_and_term_taxonomy_id() { register_taxonomy( 'wptests_tax', 'post' ); $found = wp_insert_term( 'foo', 'wptests_tax' );