WordPress.org

Make WordPress Core

Ticket #5809: 5809.2.patch

File 5809.2.patch, 18.0 KB (added by boonebgorges, 6 years ago)
  • src/wp-includes/taxonomy.php

    diff --git src/wp-includes/taxonomy.php src/wp-includes/taxonomy.php
    index 44307fa..359b90a 100644
    function wp_insert_term( $term, $taxonomy, $args = array() ) { 
    28252825                }
    28262826        }
    28272827
    2828         if ( $term_id = term_exists($slug) ) {
    2829                 $existing_term = $wpdb->get_row( $wpdb->prepare( "SELECT name FROM $wpdb->terms WHERE term_id = %d", $term_id), ARRAY_A );
    2830                 // We've got an existing term in the same taxonomy, which matches the name of the new term:
    2831                 if ( is_taxonomy_hierarchical($taxonomy) && $existing_term['name'] == $name && $exists = term_exists( (int) $term_id, $taxonomy ) ) {
    2832                         // Hierarchical, and it matches an existing term, Do not allow same "name" in the same level.
    2833                         $siblings = get_terms($taxonomy, array('fields' => 'names', 'get' => 'all', 'parent' => $parent ) );
    2834                         if ( in_array($name, $siblings) ) {
    2835                                 if ( $slug_provided ) {
    2836                                         return new WP_Error( 'term_exists', __( 'A term with the name and slug provided already exists with this parent.' ), $exists['term_id'] );
    2837                                 } else {
    2838                                         return new WP_Error( 'term_exists', __( 'A term with the name provided already exists with this parent.' ), $exists['term_id'] );
     2828        // Terms with duplicate names are not allowed at the same level of a taxonomy hierarchy.
     2829        if ( $exists = term_exists( $slug, $taxonomy ) ) {
     2830                $existing_term = get_term( $exists['term_id'], $taxonomy );
     2831
     2832                if ( $name === $existing_term->name ) {
     2833
     2834                        if ( is_taxonomy_hierarchical( $taxonomy ) ) {
     2835                                $siblings = get_terms( $taxonomy, array( 'fields' => 'names', 'get' => 'all', 'parent' => $parent ) );
     2836                                if ( in_array( $name, $siblings ) ) {
     2837                                        return new WP_Error( 'term_exists', __( 'A term with the name and slug already exists with this parent.' ), $exists['term_id'] );
    28392838                                }
     2839
    28402840                        } else {
    2841                                 $slug = wp_unique_term_slug($slug, (object) $args);
    2842                                 if ( false === $wpdb->insert( $wpdb->terms, compact( 'name', 'slug', 'term_group' ) ) ) {
    2843                                         return new WP_Error('db_insert_error', __('Could not insert term into the database'), $wpdb->last_error);
    2844                                 }
    2845                                 $term_id = (int) $wpdb->insert_id;
    2846                         }
    2847                 } elseif ( $existing_term['name'] != $name ) {
    2848                         // We've got an existing term, with a different name, Create the new term.
    2849                         $slug = wp_unique_term_slug($slug, (object) $args);
    2850                         if ( false === $wpdb->insert( $wpdb->terms, compact( 'name', 'slug', 'term_group' ) ) ) {
    2851                                 return new WP_Error('db_insert_error', __('Could not insert term into the database'), $wpdb->last_error);
     2841                                return new WP_Error( 'term_exists', __( 'A term with the name and slug already exists in this taxonomy.' ), $exists['term_id'] );
    28522842                        }
    2853                         $term_id = (int) $wpdb->insert_id;
    2854                 } elseif ( $exists = term_exists( (int) $term_id, $taxonomy ) )  {
    2855                         // Same name, same slug.
    2856                         return new WP_Error( 'term_exists', __( 'A term with the name and slug provided already exists.' ), $exists['term_id'] );
    2857                 }
    2858         } else {
    2859                 // This term does not exist at all in the database, Create it.
    2860                 $slug = wp_unique_term_slug($slug, (object) $args);
    2861                 if ( false === $wpdb->insert( $wpdb->terms, compact( 'name', 'slug', 'term_group' ) ) ) {
    2862                         return new WP_Error('db_insert_error', __('Could not insert term into the database'), $wpdb->last_error);
    28632843                }
    2864                 $term_id = (int) $wpdb->insert_id;
    28652844        }
    28662845
     2846        $slug = wp_unique_term_slug( $slug, (object) $args );
     2847
     2848        if ( false === $wpdb->insert( $wpdb->terms, compact( 'name', 'slug', 'term_group' ) ) ) {
     2849                return new WP_Error( 'db_insert_error', __( 'Could not insert term into the database' ), $wpdb->last_error );
     2850        }
     2851
     2852        $term_id = (int) $wpdb->insert_id;
     2853
    28672854        // Seems unreachable, However, Is used in the case that a term name is provided, which sanitizes to an empty string.
    28682855        if ( empty($slug) ) {
    28692856                $slug = sanitize_title($slug, $term_id);
    function wp_insert_term( $term, $taxonomy, $args = array() ) { 
    28812868        if ( !empty($tt_id) ) {
    28822869                return array('term_id' => $term_id, 'term_taxonomy_id' => $tt_id);
    28832870        }
     2871
    28842872        $wpdb->insert( $wpdb->term_taxonomy, compact( 'term_id', 'taxonomy', 'description', 'parent') + array( 'count' => 0 ) );
    28852873        $tt_id = (int) $wpdb->insert_id;
    28862874
     2875        /*
     2876         * Sanity check: if we just created a duplicate term, use the original instead,
     2877         * and delete the duplicate.
     2878         */
     2879        $duplicate_term = $wpdb->get_row( $wpdb->prepare( "SELECT t.term_id, tt.term_taxonomy_id FROM $wpdb->terms t INNER JOIN $wpdb->term_taxonomy tt ON ( tt.term_id = t.term_id ) WHERE t.slug = %s AND tt.parent = %d AND tt.taxonomy = %s AND t.term_id != %d AND tt.term_taxonomy_id != %d", $slug, $parent, $taxonomy, $term_id, $tt_id ) );
     2880        if ( $duplicate_term ) {
     2881                $wpdb->delete( $wpdb->terms, array( 'term_id' => $term_id ), array( '%d' ) );
     2882                $wpdb->delete( $wpdb->term_taxonomy, array( 'term_taxonomy_id' => $tt_id ), array( '%d' ) );
     2883
     2884                $term_id = (int) $duplicate_term->term_id;
     2885                $tt_id   = (int) $duplicate_term->term_taxonomy_id;
     2886        }
     2887
    28872888        /**
    28882889         * Fires immediately after a new term is created, before the term cache is cleaned.
    28892890         *
    function wp_unique_term_slug($slug, $term) { 
    31923193        if ( ! term_exists( $slug ) )
    31933194                return $slug;
    31943195
     3196        // As of 4.1, duplicate slugs are allowed as long as they're in different taxonomies.
     3197        if ( get_option( 'db_version' ) >= 30056 && ! get_term_by( 'slug', $slug, $term->taxonomy ) ) {
     3198                return $slug;
     3199        }
     3200
    31953201        // If the taxonomy supports hierarchy and the term has a parent, make the slug unique
    31963202        // by incorporating parent slugs.
    31973203        if ( is_taxonomy_hierarchical($term->taxonomy) && !empty($term->parent) ) {
    function wp_update_term( $term_id, $taxonomy, $args = array() ) { 
    33553361                        return new WP_Error('duplicate_term_slug', sprintf(__('The slug “%s” is already in use by another term'), $slug));
    33563362        }
    33573363
     3364        $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) );
     3365
     3366        // Check to see if this is a shared terms that needs splitting.
     3367        $_term_id = _split_shared_term( $term_id, $tt_id );
     3368        if ( ! is_wp_error( $_term_id ) ) {
     3369                $term_id = $_term_id;
     3370        }
     3371
    33583372        /**
    33593373         * Fires immediately before the given terms are edited.
    33603374         *
    function wp_update_term( $term_id, $taxonomy, $args = array() ) { 
    33803394         */
    33813395        do_action( 'edited_terms', $term_id, $taxonomy );
    33823396
    3383         $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) );
    3384 
    33853397        /**
    33863398         * Fires immediate before a term-taxonomy relationship is updated.
    33873399         *
    function _update_generic_term_count( $terms, $taxonomy ) { 
    40114023}
    40124024
    40134025/**
     4026 * Create a new term for a term_taxonomy item that currently shares its term.
     4027 *
     4028 * @param int   $term_id          ID of the shared term.
     4029 * @param int   $term_taxonomy_id ID of the term taxonomy item to receive a new term.
     4030 * @param array $shared_tts       Sibling term taxonomies, used for busting caches.
     4031 * @return int  Term ID.
     4032 */
     4033function _split_shared_term( $term_id, $term_taxonomy_id ) {
     4034        global $wpdb;
     4035
     4036        // Don't try to split terms if database schema does not support shared slugs.
     4037        $current_db_version = get_option( 'db_version' );
     4038        if ( $current_db_version < 30056 ) {
     4039                return $term_id;
     4040        }
     4041
     4042        // If there are no shared term_taxonomy rows, there's nothing to do here.
     4043        $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 ) );
     4044        if ( ! $shared_tt_count ) {
     4045                return $term_id;
     4046        }
     4047
     4048        // Pull up data about the currently shared slug, which we'll use to populate the new one.
     4049        $shared_term = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->terms WHERE term_id = %d", $term_id ) );
     4050
     4051        $new_term_data = array(
     4052                'name' => $shared_term->name,
     4053                'slug' => $shared_term->slug,
     4054                'term_group' => $shared_term->term_group,
     4055        );
     4056
     4057        if ( false === $wpdb->insert( $wpdb->terms, $new_term_data ) ) {
     4058                return new WP_Error( 'db_insert_error', __( 'Could not split shared term.' ), $wpdb->last_error );
     4059        }
     4060
     4061        $new_term_id = (int) $wpdb->insert_id;
     4062
     4063        // Update the existing term_taxonomy to point to the newly created term.
     4064        $wpdb->update( $wpdb->term_taxonomy,
     4065                array( 'term_id' => $new_term_id ),
     4066                array( 'term_taxonomy_id' => $term_taxonomy_id ),
     4067                array( '%d' ),
     4068                array( '%d' )
     4069        );
     4070
     4071        // Clean the formerly shared term's cache.
     4072        clean_term_cache( $term_id, $shared_term->taxonomy );
     4073
     4074        return $new_term_id;
     4075}
     4076
     4077/**
    40144078 * Generate a permalink for a taxonomy term archive.
    40154079 *
    40164080 * @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() {