Ticket #5809: 5809.4.patch
File 5809.4.patch, 18.5 KB (added by , 10 years ago) |
---|
-
src/wp-includes/taxonomy.php
diff --git src/wp-includes/taxonomy.php src/wp-includes/taxonomy.php index 75e5c6a..92ce286 100644
function wp_insert_term( $term, $taxonomy, $args = array() ) { 2843 2843 } 2844 2844 } 2845 2845 2846 if ( $term_id = term_exists($slug) ) { 2847 $existing_term = $wpdb->get_row( $wpdb->prepare( "SELECT name FROM $wpdb->terms WHERE term_id = %d", $term_id), ARRAY_A ); 2848 // We've got an existing term in the same taxonomy, which matches the name of the new term: 2849 if ( is_taxonomy_hierarchical($taxonomy) && $existing_term['name'] == $name && $exists = term_exists( (int) $term_id, $taxonomy ) ) { 2850 // Hierarchical, and it matches an existing term, Do not allow same "name" in the same level. 2851 $siblings = get_terms($taxonomy, array('fields' => 'names', 'get' => 'all', 'parent' => $parent ) ); 2852 if ( in_array($name, $siblings) ) { 2853 if ( $slug_provided ) { 2854 return new WP_Error( 'term_exists', __( 'A term with the name and slug provided already exists with this parent.' ), $exists['term_id'] ); 2855 } else { 2856 return new WP_Error( 'term_exists', __( 'A term with the name provided already exists with this parent.' ), $exists['term_id'] ); 2846 // Terms with duplicate names are not allowed at the same level of a taxonomy hierarchy. 2847 if ( $exists = term_exists( $slug, $taxonomy ) ) { 2848 $existing_term = get_term( $exists['term_id'], $taxonomy ); 2849 2850 if ( $name === $existing_term->name ) { 2851 2852 if ( is_taxonomy_hierarchical( $taxonomy ) ) { 2853 $siblings = get_terms( $taxonomy, array( 'fields' => 'names', 'get' => 'all', 'parent' => $parent ) ); 2854 if ( in_array( $name, $siblings ) ) { 2855 return new WP_Error( 'term_exists', __( 'A term with the name and slug already exists with this parent.' ), $exists['term_id'] ); 2857 2856 } 2857 2858 2858 } else { 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); 2862 } 2863 $term_id = (int) $wpdb->insert_id; 2864 } 2865 } elseif ( $existing_term['name'] != $name ) { 2866 // We've got an existing term, with a different name, Create the new term. 2867 $slug = wp_unique_term_slug($slug, (object) $args); 2868 if ( false === $wpdb->insert( $wpdb->terms, compact( 'name', 'slug', 'term_group' ) ) ) { 2869 return new WP_Error('db_insert_error', __('Could not insert term into the database'), $wpdb->last_error); 2859 return new WP_Error( 'term_exists', __( 'A term with the name and slug already exists in this taxonomy.' ), $exists['term_id'] ); 2870 2860 } 2871 $term_id = (int) $wpdb->insert_id;2872 } elseif ( $exists = term_exists( (int) $term_id, $taxonomy ) ) {2873 // Same name, same slug.2874 return new WP_Error( 'term_exists', __( 'A term with the name and slug provided already exists.' ), $exists['term_id'] );2875 2861 } 2876 } else {2877 // This term does not exist at all in the database, Create it.2878 $slug = wp_unique_term_slug($slug, (object) $args);2879 if ( false === $wpdb->insert( $wpdb->terms, compact( 'name', 'slug', 'term_group' ) ) ) {2880 return new WP_Error('db_insert_error', __('Could not insert term into the database'), $wpdb->last_error);2881 }2882 $term_id = (int) $wpdb->insert_id;2883 2862 } 2884 2863 2864 $slug = wp_unique_term_slug( $slug, (object) $args ); 2865 2866 if ( false === $wpdb->insert( $wpdb->terms, compact( 'name', 'slug', 'term_group' ) ) ) { 2867 return new WP_Error( 'db_insert_error', __( 'Could not insert term into the database' ), $wpdb->last_error ); 2868 } 2869 2870 $term_id = (int) $wpdb->insert_id; 2871 2885 2872 // Seems unreachable, However, Is used in the case that a term name is provided, which sanitizes to an empty string. 2886 2873 if ( empty($slug) ) { 2887 2874 $slug = sanitize_title($slug, $term_id); … … function wp_insert_term( $term, $taxonomy, $args = array() ) { 2899 2886 if ( !empty($tt_id) ) { 2900 2887 return array('term_id' => $term_id, 'term_taxonomy_id' => $tt_id); 2901 2888 } 2889 2902 2890 $wpdb->insert( $wpdb->term_taxonomy, compact( 'term_id', 'taxonomy', 'description', 'parent') + array( 'count' => 0 ) ); 2903 2891 $tt_id = (int) $wpdb->insert_id; 2904 2892 2893 /* 2894 * Sanity check: if we just created a term with the same parent + taxonomy + slug but a higher term_id than 2895 * an existing term, then we have unwittingly created a duplicate term. Delete the dupe, and use the term_id 2896 * and term_taxonomy_id of the older term instead. Then return out of the function so that the "create" hooks 2897 * are not fired. 2898 */ 2899 $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 ) ); 2900 if ( $duplicate_term ) { 2901 $wpdb->delete( $wpdb->terms, array( 'term_id' => $term_id ) ); 2902 $wpdb->delete( $wpdb->term_taxonomy, array( 'term_taxonomy_id' => $tt_id ) ); 2903 2904 $term_id = (int) $duplicate_term->term_id; 2905 $tt_id = (int) $duplicate_term->term_taxonomy_id; 2906 2907 clean_term_cache( $term_id, $taxonomy ); 2908 return array( 'term_id' => $term_id, 'term_taxonomy_id' => $tt_id ); 2909 } 2910 2905 2911 /** 2906 2912 * Fires immediately after a new term is created, before the term cache is cleaned. 2907 2913 * … … function wp_unique_term_slug($slug, $term) { 3210 3216 if ( ! term_exists( $slug ) ) 3211 3217 return $slug; 3212 3218 3219 // As of 4.1, duplicate slugs are allowed as long as they're in different taxonomies. 3220 if ( get_option( 'db_version' ) >= 30056 && ! get_term_by( 'slug', $slug, $term->taxonomy ) ) { 3221 return $slug; 3222 } 3223 3213 3224 // If the taxonomy supports hierarchy and the term has a parent, make the slug unique 3214 3225 // by incorporating parent slugs. 3215 3226 if ( is_taxonomy_hierarchical($term->taxonomy) && !empty($term->parent) ) { … … function wp_update_term( $term_id, $taxonomy, $args = array() ) { 3373 3384 return new WP_Error('duplicate_term_slug', sprintf(__('The slug “%s” is already in use by another term'), $slug)); 3374 3385 } 3375 3386 3387 $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) ); 3388 3389 // Check to see if this is a shared terms that needs splitting. 3390 $_term_id = _split_shared_term( $term_id, $tt_id ); 3391 if ( ! is_wp_error( $_term_id ) ) { 3392 $term_id = $_term_id; 3393 } 3394 3376 3395 /** 3377 3396 * Fires immediately before the given terms are edited. 3378 3397 * … … function wp_update_term( $term_id, $taxonomy, $args = array() ) { 3398 3417 */ 3399 3418 do_action( 'edited_terms', $term_id, $taxonomy ); 3400 3419 3401 $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) );3402 3403 3420 /** 3404 3421 * Fires immediate before a term-taxonomy relationship is updated. 3405 3422 * … … function _update_generic_term_count( $terms, $taxonomy ) { 4029 4046 } 4030 4047 4031 4048 /** 4049 * Create a new term for a term_taxonomy item that currently shares its term. 4050 * 4051 * @param int $term_id ID of the shared term. 4052 * @param int $term_taxonomy_id ID of the term taxonomy item to receive a new term. 4053 * @param array $shared_tts Sibling term taxonomies, used for busting caches. 4054 * @return int Term ID. 4055 */ 4056 function _split_shared_term( $term_id, $term_taxonomy_id ) { 4057 global $wpdb; 4058 4059 // Don't try to split terms if database schema does not support shared slugs. 4060 $current_db_version = get_option( 'db_version' ); 4061 if ( $current_db_version < 30056 ) { 4062 return $term_id; 4063 } 4064 4065 // If there are no shared term_taxonomy rows, there's nothing to do here. 4066 $shared_tt_count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_taxonomy tt WHERE tt.term_id = %d AND tt.term_taxonomy_id != %d", $term_id, $term_taxonomy_id ) ); 4067 if ( ! $shared_tt_count ) { 4068 return $term_id; 4069 } 4070 4071 // Pull up data about the currently shared slug, which we'll use to populate the new one. 4072 $shared_term = $wpdb->get_row( $wpdb->prepare( "SELECT t.* FROM $wpdb->terms t WHERE t.term_id = %d", $term_id ) ); 4073 4074 $new_term_data = array( 4075 'name' => $shared_term->name, 4076 'slug' => $shared_term->slug, 4077 'term_group' => $shared_term->term_group, 4078 ); 4079 4080 if ( false === $wpdb->insert( $wpdb->terms, $new_term_data ) ) { 4081 return new WP_Error( 'db_insert_error', __( 'Could not split shared term.' ), $wpdb->last_error ); 4082 } 4083 4084 $new_term_id = (int) $wpdb->insert_id; 4085 4086 // Update the existing term_taxonomy to point to the newly created term. 4087 $wpdb->update( $wpdb->term_taxonomy, 4088 array( 'term_id' => $new_term_id ), 4089 array( 'term_taxonomy_id' => $term_taxonomy_id ) 4090 ); 4091 4092 // Clean the cache for term taxonomies formerly shared with the current term. 4093 $shared_term_taxonomies = $wpdb->get_row( $wpdb->prepare( "SELECT taxonomy FROM $wpdb->term_taxonomy WHERE term_id = %d", $term_id ) ); 4094 foreach ( (array) $shared_term_taxonomies as $shared_term_taxonomy ) { 4095 clean_term_cache( $term_id, $shared_term_taxonomy ); 4096 } 4097 4098 return $new_term_id; 4099 } 4100 4101 /** 4032 4102 * Generate a permalink for a taxonomy term archive. 4033 4103 * 4034 4104 * @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 { 226 226 $this->assertFalse( is_wp_error( $term20 ) ); 227 227 } 228 228 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 229 316 public function test_wp_insert_term_alias_of_no_term_group() { 230 317 register_taxonomy( 'wptests_tax', 'post' ); 231 318 $t1 = $this->factory->term->create( array( … … class Tests_Term extends WP_UnitTestCase { 353 440 $this->assertEquals( $existing_term, $found->get_error_data() ); 354 441 } 355 442 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 356 456 public function test_wp_insert_term_should_return_term_id_and_term_taxonomy_id() { 357 457 register_taxonomy( 'wptests_tax', 'post' ); 358 458 $found = wp_insert_term( 'foo', 'wptests_tax' ); … … class Tests_Term extends WP_UnitTestCase { 537 637 _unregister_taxonomy( 'wptests_tax' ); 538 638 } 539 639 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 540 775 public function test_wp_update_term_alias_of_no_term_group() { 541 776 register_taxonomy( 'wptests_tax', 'post' ); 542 777 $t1 = $this->factory->term->create( array( … … class Tests_Term extends WP_UnitTestCase { 1366 1601 } 1367 1602 1368 1603 /** 1369 * @ticket 58091370 */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 interesting1391 $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_name1397 ) );1398 1399 $this->assertNotEquals( $t2_updated['term_id'], $t3['term_id'] );1400 1401 // make sure the terms have split1402 $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 post1406 $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 up1411 unset( $GLOBALS['wp_taxonomies'][ $random_tax ] );1412 }1413 1414 /**1415 1604 * @ticket 25852 1416 1605 */ 1417 1606 function test_sanitize_term_field() {