WordPress.org

Make WordPress Core

Ticket #5809: 5809.patch

File 5809.patch, 16.8 KB (added by boonebgorges, 7 years ago)
  • src/wp-includes/taxonomy.php

    diff --git src/wp-includes/taxonomy.php src/wp-includes/taxonomy.php
    index b9b8042..9aa66ee 100644
    function wp_insert_term( $term, $taxonomy, $args = array() ) { 
    28242824                }
    28252825        }
    28262826
    2827         if ( $term_id = term_exists($slug) ) {
    2828                 $existing_term = $wpdb->get_row( $wpdb->prepare( "SELECT name FROM $wpdb->terms WHERE term_id = %d", $term_id), ARRAY_A );
    2829                 // We've got an existing term in the same taxonomy, which matches the name of the new term:
    2830                 if ( is_taxonomy_hierarchical($taxonomy) && $existing_term['name'] == $name && $exists = term_exists( (int) $term_id, $taxonomy ) ) {
    2831                         // Hierarchical, and it matches an existing term, Do not allow same "name" in the same level.
    2832                         $siblings = get_terms($taxonomy, array('fields' => 'names', 'get' => 'all', 'parent' => $parent ) );
    2833                         if ( in_array($name, $siblings) ) {
    2834                                 if ( $slug_provided ) {
    2835                                         return new WP_Error( 'term_exists', __( 'A term with the name and slug provided already exists with this parent.' ), $exists['term_id'] );
    2836                                 } else {
    2837                                         return new WP_Error( 'term_exists', __( 'A term with the name provided already exists with this parent.' ), $exists['term_id'] );
     2827        // Terms with duplicate names are not allowed at the same level of a taxonomy hierarchy.
     2828        if ( $exists = term_exists( $slug, $taxonomy ) ) {
     2829                $existing_term = get_term( $exists['term_id'], $taxonomy );
     2830
     2831                if ( $name === $existing_term->name ) {
     2832
     2833                        if ( is_taxonomy_hierarchical( $taxonomy ) ) {
     2834                                $siblings = get_terms( $taxonomy, array( 'fields' => 'names', 'get' => 'all', 'parent' => $parent ) );
     2835                                if ( in_array( $name, $siblings ) ) {
     2836                                        return new WP_Error( 'term_exists', __( 'A term with the name and slug already exists with this parent.' ), $exists['term_id'] );
    28382837                                }
     2838
    28392839                        } else {
    2840                                 $slug = wp_unique_term_slug($slug, (object) $args);
    2841                                 if ( false === $wpdb->insert( $wpdb->terms, compact( 'name', 'slug', 'term_group' ) ) ) {
    2842                                         return new WP_Error('db_insert_error', __('Could not insert term into the database'), $wpdb->last_error);
    2843                                 }
    2844                                 $term_id = (int) $wpdb->insert_id;
     2840                                return new WP_Error( 'term_exists', __( 'A term with the name and slug already exists in this taxonomy.' ), $exists['term_id'] );
    28452841                        }
    2846                 } elseif ( $existing_term['name'] != $name ) {
    2847                         // We've got an existing term, with a different name, Create the new term.
    2848                         $slug = wp_unique_term_slug($slug, (object) $args);
    2849                         if ( false === $wpdb->insert( $wpdb->terms, compact( 'name', 'slug', 'term_group' ) ) ) {
    2850                                 return new WP_Error('db_insert_error', __('Could not insert term into the database'), $wpdb->last_error);
    2851                         }
    2852                         $term_id = (int) $wpdb->insert_id;
    2853                 } elseif ( $exists = term_exists( (int) $term_id, $taxonomy ) )  {
    2854                         // Same name, same slug.
    2855                         return new WP_Error( 'term_exists', __( 'A term with the name and slug provided already exists.' ), $exists['term_id'] );
    2856                 }
    2857         } else {
    2858                 // This term does not exist at all in the database, Create it.
    2859                 $slug = wp_unique_term_slug($slug, (object) $args);
    2860                 if ( false === $wpdb->insert( $wpdb->terms, compact( 'name', 'slug', 'term_group' ) ) ) {
    2861                         return new WP_Error('db_insert_error', __('Could not insert term into the database'), $wpdb->last_error);
    28622842                }
    2863                 $term_id = (int) $wpdb->insert_id;
    28642843        }
    28652844
     2845        $slug = wp_unique_term_slug( $slug, (object) $args );
     2846        if ( false === $wpdb->insert( $wpdb->terms, compact( 'name', 'slug', 'term_group' ) ) ) {
     2847                return new WP_Error( 'db_insert_error', __( 'Could not insert term into the database' ), $wpdb->last_error );
     2848        }
     2849
     2850        $term_id = (int) $wpdb->insert_id;
     2851
    28662852        // Seems unreachable, However, Is used in the case that a term name is provided, which sanitizes to an empty string.
    28672853        if ( empty($slug) ) {
    28682854                $slug = sanitize_title($slug, $term_id);
    function wp_unique_term_slug($slug, $term) { 
    31893175        if ( ! term_exists( $slug ) )
    31903176                return $slug;
    31913177
     3178        // As of 4.1, duplicate slugs are allowed as long as they're in different taxonomies.
     3179        if ( get_option( 'db_version' ) >= 30056 && ! get_term_by( 'slug', $slug, $term->taxonomy ) ) {
     3180                return $slug;
     3181        }
     3182
    31923183        // If the taxonomy supports hierarchy and the term has a parent, make the slug unique
    31933184        // by incorporating parent slugs.
    31943185        if ( is_taxonomy_hierarchical($term->taxonomy) && !empty($term->parent) ) {
    function wp_update_term( $term_id, $taxonomy, $args = array() ) { 
    33523343                        return new WP_Error('duplicate_term_slug', sprintf(__('The slug “%s” is already in use by another term'), $slug));
    33533344        }
    33543345
     3346        $tt_id = $wpdb->get_var( $wpdb->prepare( "SELECT tt.term_taxonomy_id FROM $wpdb->term_taxonomy AS tt INNER JOIN $wpdb->terms AS t ON tt.term_id = t.term_id WHERE tt.taxonomy = %s AND t.term_id = %d", $taxonomy, $term_id) );
     3347
     3348        // Check to see if this is a shared terms that needs splitting.
     3349        $_term_id = _split_shared_term( $term_id, $tt_id );
     3350        if ( ! is_wp_error( $_term_id ) ) {
     3351                $term_id = $_term_id;
     3352        }
     3353
    33553354        /**
    33563355         * Fires immediately before the given terms are edited.
    33573356         *
    function wp_update_term( $term_id, $taxonomy, $args = array() ) { 
    33773376         */
    33783377        do_action( 'edited_terms', $term_id, $taxonomy );
    33793378
    3380         $tt_id = $wpdb->get_var( $wpdb->prepare( "SELECT tt.term_taxonomy_id FROM $wpdb->term_taxonomy AS tt INNER JOIN $wpdb->terms AS t ON tt.term_id = t.term_id WHERE tt.taxonomy = %s AND t.term_id = %d", $taxonomy, $term_id) );
    3381 
    33823379        /**
    33833380         * Fires immediate before a term-taxonomy relationship is updated.
    33843381         *
    function _update_generic_term_count( $terms, $taxonomy ) { 
    40034000}
    40044001
    40054002/**
     4003 * Create a new term for a term_taxonomy item that currently shares its term.
     4004 *
     4005 * @param int   $term_id          ID of the shared term.
     4006 * @param int   $term_taxonomy_id ID of the term taxonomy item to receive a new term.
     4007 * @param array $shared_tts       Sibling term taxonomies, used for busting caches.
     4008 * @return int  Term ID.
     4009 */
     4010function _split_shared_term( $term_id, $term_taxonomy_id ) {
     4011        global $wpdb;
     4012
     4013        // Don't try to split terms if database schema does not support shared slugs.
     4014        $current_db_version = get_option( 'db_version' );
     4015        if ( $current_db_version < 30056 ) {
     4016                return $term_id;
     4017        }
     4018
     4019        // If there are no shared term_taxonomy rows, there's nothing to do here.
     4020        $shared_tt_count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_taxonomy WHERE term_id = %d AND term_taxonomy_id != %d", $term_id, $term_taxonomy_id ) );
     4021        if ( ! $shared_tt_count ) {
     4022                return $term_id;
     4023        }
     4024
     4025        // Pull up data about the currently shared slug, which we'll use to populate the new one.
     4026        $shared_term = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->terms WHERE term_id = %d", $term_id ) );
     4027
     4028        $new_term_data = array(
     4029                'name' => $shared_term->name,
     4030                'slug' => $shared_term->slug,
     4031                'term_group' => $shared_term->term_group,
     4032        );
     4033
     4034        if ( false === $wpdb->insert( $wpdb->terms, $new_term_data ) ) {
     4035                return new WP_Error( 'db_insert_error', __( 'Could not split shared term.' ), $wpdb->last_error );
     4036        }
     4037
     4038        $new_term_id = (int) $wpdb->insert_id;
     4039
     4040        // Update the existing term_taxonomy to point to the newly created term.
     4041        $wpdb->update( $wpdb->term_taxonomy,
     4042                array( 'term_id' => $new_term_id ),
     4043                array( 'term_taxonomy_id' => $term_taxonomy_id ),
     4044                array( '%d' ),
     4045                array( '%d' )
     4046        );
     4047
     4048        // Clean the formerly shared term's cache.
     4049        clean_term_cache( $term_id, $shared_term->taxonomy );
     4050
     4051        return $new_term_id;
     4052}
     4053
     4054/**
    40064055 * Generate a permalink for a taxonomy term archive.
    40074056 *
    40084057 * @since 2.5.0
  • tests/phpunit/tests/term.php

    diff --git tests/phpunit/tests/term.php tests/phpunit/tests/term.php
    index b6fe994..1258c4b 100644
    class Tests_Term extends WP_UnitTestCase { 
    226226                $this->assertFalse( is_wp_error( $term20 ) );
    227227        }
    228228
     229        /**
     230         * @ticket 5809
     231         */
     232        public function test_wp_insert_term_duplicate_slug_same_taxonomy() {
     233                register_taxonomy( 'wptests_tax', 'post' );
     234                $t = $this->factory->term->create( array(
     235                        'name' => 'Foo',
     236                        'slug' => 'foo',
     237                        'taxonomy' => 'wptests_tax',
     238                ) );
     239
     240                $term = get_term( $t, 'wptests_tax' );
     241
     242                $created = wp_insert_term( 'Foo 2', 'wptests_tax', array(
     243                        'slug' => 'foo',
     244                ) );
     245
     246                $created_term = get_term( $created['term_id'], 'wptests_tax' );
     247                $this->assertSame( 'foo-2', $created_term->slug );
     248
     249                _unregister_taxonomy( 'wptests_tax', 'post' );
     250        }
     251
     252        /**
     253         * @ticket 5809
     254         */
     255        public function test_wp_insert_term_duplicate_slug_different_taxonomy() {
     256                register_taxonomy( 'wptests_tax', 'post' );
     257                register_taxonomy( 'wptests_tax_2', 'post' );
     258                $t = $this->factory->term->create( array(
     259                        'name' => 'Foo',
     260                        'slug' => 'foo',
     261                        'taxonomy' => 'wptests_tax',
     262                ) );
     263
     264                $term = get_term( $t, 'wptests_tax' );
     265
     266                $created = wp_insert_term( 'Foo 2', 'wptests_tax_2', array(
     267                        'slug' => 'foo',
     268                ) );
     269
     270                $this->assertFalse( is_wp_error( $created ) );
     271
     272                $new_term = get_term( $created['term_id'], 'wptests_tax_2' );
     273
     274                $this->assertSame( 'foo', $new_term->slug );
     275
     276                _unregister_taxonomy( 'wptests_tax', 'post' );
     277        }
     278
     279        /**
     280         * @ticket 5809
     281         */
     282        public function test_wp_insert_term_duplicate_slug_different_taxonomy_before_410_schema_change() {
     283
     284                $db_version = get_option( 'db_version' );
     285                update_option( 'db_version', 30055 );
     286
     287                register_taxonomy( 'wptests_tax', 'post' );
     288                register_taxonomy( 'wptests_tax_2', 'post' );
     289                $t = $this->factory->term->create( array(
     290                        'name' => 'Foo',
     291                        'slug' => 'foo',
     292                        'taxonomy' => 'wptests_tax',
     293                ) );
     294
     295                $term = get_term( $t, 'wptests_tax' );
     296
     297                $created = wp_insert_term( 'Foo 2', 'wptests_tax_2', array(
     298                        'slug' => 'foo',
     299                ) );
     300
     301                $this->assertFalse( is_wp_error( $created ) );
     302
     303                $new_term = get_term( $created['term_id'], 'wptests_tax_2' );
     304
     305                /*
     306                 * As of 4.1, we no longer create a shared term, but we also do not
     307                 * allow for duplicate slugs.
     308                 */
     309                $this->assertSame( 'foo-2', $new_term->slug );
     310                $this->assertNotEquals( $new_term->term_id, $term->term_id );
     311
     312                _unregister_taxonomy( 'wptests_tax', 'post' );
     313                update_option( 'db_version', $db_version );
     314        }
     315
    229316        public function test_wp_insert_term_alias_of_no_term_group() {
    230317                register_taxonomy( 'wptests_tax', 'post' );
    231318                $t1 = $this->factory->term->create( array(
    class Tests_Term extends WP_UnitTestCase { 
    353440                $this->assertEquals( $existing_term, $found->get_error_data() );
    354441        }
    355442
     443        /**
     444         * @ticket 5809
     445         */
     446        public function test_wp_insert_term_should_not_create_shared_term() {
     447                register_taxonomy( 'wptests_tax', 'post' );
     448                register_taxonomy( 'wptests_tax_2', 'post' );
     449
     450                $t1 = wp_insert_term( 'Foo', 'wptests_tax' );
     451                $t2 = wp_insert_term( 'Foo', 'wptests_tax_2' );
     452
     453                $this->assertNotEquals( $t1['term_id'], $t2['term_id'] );
     454        }
     455
    356456        public function test_wp_insert_term_should_return_term_id_and_term_taxonomy_id() {
    357457                register_taxonomy( 'wptests_tax', 'post' );
    358458                $found = wp_insert_term( 'foo', 'wptests_tax' );
    class Tests_Term extends WP_UnitTestCase { 
    537637                _unregister_taxonomy( 'wptests_tax' );
    538638        }
    539639
     640        /**
     641         * @ticket 5809
     642         */
     643        public function test_wp_update_term_duplicate_slug_same_taxonomy() {
     644                register_taxonomy( 'wptests_tax', 'post' );
     645
     646                $t1 = $this->factory->term->create( array(
     647                        'name' => 'Foo',
     648                        'slug' => 'foo',
     649                        'taxonomy' => 'wptests_tax',
     650                ) );
     651
     652                $t2 = $this->factory->term->create( array(
     653                        'name' => 'Foo',
     654                        'slug' => 'bar',
     655                        'taxonomy' => 'wptests_tax',
     656                ) );
     657
     658                $updated = wp_update_term( $t2, 'wptests_tax', array(
     659                        'slug' => 'foo',
     660                ) );
     661
     662                $this->assertWPError( $updated );
     663                $this->assertSame( 'duplicate_term_slug', $updated->get_error_code() );
     664        }
     665
     666        /**
     667         * @ticket 5809
     668         */
     669        public function test_wp_update_term_duplicate_slug_different_taxonomy() {
     670                register_taxonomy( 'wptests_tax', 'post' );
     671                register_taxonomy( 'wptests_tax_2', 'post' );
     672
     673                $t1 = $this->factory->term->create( array(
     674                        'name' => 'Foo',
     675                        'slug' => 'foo',
     676                        'taxonomy' => 'wptests_tax',
     677                ) );
     678
     679                $t2 = $this->factory->term->create( array(
     680                        'name' => 'Foo',
     681                        'slug' => 'bar',
     682                        'taxonomy' => 'wptests_tax_2',
     683                ) );
     684
     685                $updated = wp_update_term( $t2, 'wptests_tax_2', array(
     686                        'slug' => 'foo',
     687                ) );
     688
     689                $this->assertWPError( $updated );
     690                $this->assertSame( 'duplicate_term_slug', $updated->get_error_code() );
     691        }
     692
     693        /**
     694         * @ticket 5809
     695         */
     696        public function test_wp_update_term_should_split_shared_term() {
     697                global $wpdb;
     698
     699                register_taxonomy( 'wptests_tax', 'post' );
     700                register_taxonomy( 'wptests_tax_2', 'post' );
     701
     702                $t1 = wp_insert_term( 'Foo', 'wptests_tax' );
     703                $t2 = wp_insert_term( 'Foo', 'wptests_tax_2' );
     704
     705                // Manually modify because split terms shouldn't naturally occur.
     706                $wpdb->update( $wpdb->term_taxonomy,
     707                        array( 'term_id' => $t1['term_id'] ),
     708                        array( 'term_taxonomy_id' => $t2['term_taxonomy_id'] ),
     709                        array( '%d' ),
     710                        array( '%d' )
     711                );
     712
     713                $posts = $this->factory->post->create_many( 2 );
     714                wp_set_object_terms( $posts[0], array( 'Foo' ), 'wptests_tax' );
     715                wp_set_object_terms( $posts[1], array( 'Foo' ), 'wptests_tax_2' );
     716
     717                // Verify that the terms are shared.
     718                $t1_terms = wp_get_object_terms( $posts[0], 'wptests_tax' );
     719                $t2_terms = wp_get_object_terms( $posts[1], 'wptests_tax_2' );
     720                $this->assertSame( $t1_terms[0]->term_id, $t2_terms[0]->term_id );
     721
     722                wp_update_term( $t2_terms[0]->term_id, 'wptests_tax_2', array(
     723                        'name' => 'New Foo',
     724                ) );
     725
     726                $t1_terms = wp_get_object_terms( $posts[0], 'wptests_tax' );
     727                $t2_terms = wp_get_object_terms( $posts[1], 'wptests_tax_2' );
     728                $this->assertNotEquals( $t1_terms[0]->term_id, $t2_terms[0]->term_id );
     729        }
     730
     731        /**
     732         * @ticket 5809
     733         */
     734        public function test_wp_update_term_should_not_split_shared_term_before_410_schema_change() {
     735                global $wpdb;
     736
     737                $db_version = get_option( 'db_version' );
     738                update_option( 'db_version', 30055 );
     739
     740                register_taxonomy( 'wptests_tax', 'post' );
     741                register_taxonomy( 'wptests_tax_2', 'post' );
     742
     743                $t1 = wp_insert_term( 'Foo', 'wptests_tax' );
     744                $t2 = wp_insert_term( 'Foo', 'wptests_tax_2' );
     745
     746                // Manually modify because split terms shouldn't naturally occur.
     747                $wpdb->update( $wpdb->term_taxonomy,
     748                        array( 'term_id' => $t1['term_id'] ),
     749                        array( 'term_taxonomy_id' => $t2['term_taxonomy_id'] ),
     750                        array( '%d' ),
     751                        array( '%d' )
     752                );
     753
     754                $posts = $this->factory->post->create_many( 2 );
     755                wp_set_object_terms( $posts[0], array( 'Foo' ), 'wptests_tax' );
     756                wp_set_object_terms( $posts[1], array( 'Foo' ), 'wptests_tax_2' );
     757
     758                // Verify that the term is shared.
     759                $t1_terms = wp_get_object_terms( $posts[0], 'wptests_tax' );
     760                $t2_terms = wp_get_object_terms( $posts[1], 'wptests_tax_2' );
     761                $this->assertSame( $t1_terms[0]->term_id, $t2_terms[0]->term_id );
     762
     763                wp_update_term( $t2_terms[0]->term_id, 'wptests_tax_2', array(
     764                        'name' => 'New Foo',
     765                ) );
     766
     767                // Term should still be shared.
     768                $t1_terms = wp_get_object_terms( $posts[0], 'wptests_tax' );
     769                $t2_terms = wp_get_object_terms( $posts[1], 'wptests_tax_2' );
     770                $this->assertSame( $t1_terms[0]->term_id, $t2_terms[0]->term_id );
     771
     772                update_option( 'db_version', $db_version );
     773        }
     774
    540775        public function test_wp_update_term_alias_of_no_term_group() {
    541776                register_taxonomy( 'wptests_tax', 'post' );
    542777                $t1 = $this->factory->term->create( array(
    class Tests_Term extends WP_UnitTestCase { 
    13661601        }
    13671602
    13681603        /**
    1369          * @ticket 5809
    1370          */
    1371         function test_update_shared_term() {
    1372                 $random_tax = __FUNCTION__;
    1373 
    1374                 register_taxonomy( $random_tax, 'post' );
    1375 
    1376                 $post_id = $this->factory->post->create();
    1377 
    1378                 $old_name = 'Initial';
    1379 
    1380                 $t1 = wp_insert_term( $old_name, 'category' );
    1381                 $t2 = wp_insert_term( $old_name, 'post_tag' );
    1382 
    1383                 $this->assertEquals( $t1['term_id'], $t2['term_id'] );
    1384 
    1385                 wp_set_post_categories( $post_id, array( $t1['term_id'] ) );
    1386                 wp_set_post_tags( $post_id, array( (int) $t2['term_id'] ) );
    1387 
    1388                 $new_name = 'Updated';
    1389 
    1390                 // create the term in a third taxonomy, just to keep things interesting
    1391                 $t3 = wp_insert_term( $old_name, $random_tax );
    1392                 wp_set_post_terms( $post_id, array( (int) $t3['term_id'] ), $random_tax );
    1393                 $this->assertPostHasTerms( $post_id, array( $t3['term_id'] ), $random_tax );
    1394 
    1395                 $t2_updated = wp_update_term( $t2['term_id'], 'post_tag', array(
    1396                         'name' => $new_name
    1397                 ) );
    1398 
    1399                 $this->assertNotEquals( $t2_updated['term_id'], $t3['term_id'] );
    1400 
    1401                 // make sure the terms have split
    1402                 $this->assertEquals( $old_name, get_term_field( 'name', $t1['term_id'], 'category' ) );
    1403                 $this->assertEquals( $new_name, get_term_field( 'name', $t2_updated['term_id'], 'post_tag' ) );
    1404 
    1405                 // and that they are still assigned to the correct post
    1406                 $this->assertPostHasTerms( $post_id, array( $t1['term_id'] ), 'category' );
    1407                 $this->assertPostHasTerms( $post_id, array( $t2_updated['term_id'] ), 'post_tag' );
    1408                 $this->assertPostHasTerms( $post_id, array( $t3['term_id'] ), $random_tax );
    1409 
    1410                 // clean up
    1411                 unset( $GLOBALS['wp_taxonomies'][ $random_tax ] );
    1412         }
    1413 
    1414         /**
    14151604         * @ticket 25852
    14161605         */
    14171606        function test_sanitize_term_field() {