Ticket #5809: 5809.2.patch
File 5809.2.patch, 18.0 KB (added by , 10 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() ) { 2825 2825 } 2826 2826 } 2827 2827 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'] ); 2839 2838 } 2839 2840 2840 } 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'] ); 2852 2842 } 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);2863 2843 } 2864 $term_id = (int) $wpdb->insert_id;2865 2844 } 2866 2845 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 2867 2854 // Seems unreachable, However, Is used in the case that a term name is provided, which sanitizes to an empty string. 2868 2855 if ( empty($slug) ) { 2869 2856 $slug = sanitize_title($slug, $term_id); … … function wp_insert_term( $term, $taxonomy, $args = array() ) { 2881 2868 if ( !empty($tt_id) ) { 2882 2869 return array('term_id' => $term_id, 'term_taxonomy_id' => $tt_id); 2883 2870 } 2871 2884 2872 $wpdb->insert( $wpdb->term_taxonomy, compact( 'term_id', 'taxonomy', 'description', 'parent') + array( 'count' => 0 ) ); 2885 2873 $tt_id = (int) $wpdb->insert_id; 2886 2874 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 2887 2888 /** 2888 2889 * Fires immediately after a new term is created, before the term cache is cleaned. 2889 2890 * … … function wp_unique_term_slug($slug, $term) { 3192 3193 if ( ! term_exists( $slug ) ) 3193 3194 return $slug; 3194 3195 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 3195 3201 // If the taxonomy supports hierarchy and the term has a parent, make the slug unique 3196 3202 // by incorporating parent slugs. 3197 3203 if ( is_taxonomy_hierarchical($term->taxonomy) && !empty($term->parent) ) { … … function wp_update_term( $term_id, $taxonomy, $args = array() ) { 3355 3361 return new WP_Error('duplicate_term_slug', sprintf(__('The slug “%s” is already in use by another term'), $slug)); 3356 3362 } 3357 3363 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 3358 3372 /** 3359 3373 * Fires immediately before the given terms are edited. 3360 3374 * … … function wp_update_term( $term_id, $taxonomy, $args = array() ) { 3380 3394 */ 3381 3395 do_action( 'edited_terms', $term_id, $taxonomy ); 3382 3396 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 3385 3397 /** 3386 3398 * Fires immediate before a term-taxonomy relationship is updated. 3387 3399 * … … function _update_generic_term_count( $terms, $taxonomy ) { 4011 4023 } 4012 4024 4013 4025 /** 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 */ 4033 function _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 /** 4014 4078 * Generate a permalink for a taxonomy term archive. 4015 4079 * 4016 4080 * @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() {