WordPress.org

Make WordPress Core

Ticket #30335: 30335.8.patch

File 30335.8.patch, 15.6 KB (added by mboynes, 4 years ago)

Update menu items when terms split

  • src/wp-includes/category-template.php

     
    402402                        $output .= "\t<option value='" . esc_attr( $option_none_value ) . "'$selected>$show_option_none</option>\n";
    403403                }
    404404
     405                if ( $r['selected'] ) {
     406                        // Check if the 'selected' term was split
     407                        $selected_term = get_term( $r['selected'], $r['taxonomy'] );
     408                        if ( $selected_term && ! is_wp_error( $selected_term ) && intval( $r['selected'] ) != $selected_term->term_id ) {
     409                                $r['selected'] = $selected_term->term_id;
     410                        }
     411                }
     412
    405413                if ( $r['hierarchical'] ) {
    406414                        $depth = $r['depth'];  // Walk the full depth.
    407415                } else {
  • src/wp-includes/default-filters.php

     
    306306add_filter( 'determine_current_user', 'wp_validate_auth_cookie'          );
    307307add_filter( 'determine_current_user', 'wp_validate_logged_in_cookie', 20 );
    308308
     309// Split term updates
     310add_filter( 'split_shared_term', '_wp_check_split_default_terms',  10, 4 );
     311add_filter( 'split_shared_term', '_wp_check_split_terms_in_menus', 10, 4 );
     312
    309313unset($filter, $action);
  • src/wp-includes/taxonomy.php

     
    12331233                                " );
    12341234                                break;
    12351235                        default:
    1236                                 $terms = implode( ',', array_map( 'intval', $query['terms'] ) );
     1236                                $terms = $query['terms'];
     1237
     1238                                // If any passed term_ids were previously split, substitute the correct term_id.
     1239                                foreach ( $terms as $term_index => $term_id ) {
     1240                                        if ( $split_term = wp_get_split_term( $term_id, $query['taxonomy'] ) ) {
     1241                                                $terms[ $term_index ] = $split_term;
     1242                                        }
     1243                                }
     1244
     1245                                $terms = implode( ',', array_map( 'intval', $terms ) );
     1246
    12371247                                $terms = $wpdb->get_col( "
    12381248                                        SELECT $resulting_field
    12391249                                        FROM $wpdb->term_taxonomy
     
    13151325                        return null;
    13161326                if ( ! $_term = wp_cache_get( $term, $taxonomy . ':terms:' . $incrementor ) ) {
    13171327                        $_term = $wpdb->get_row( $wpdb->prepare( "SELECT t.*, tt.* FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id WHERE tt.taxonomy = %s AND t.term_id = %d LIMIT 1", $taxonomy, $term) );
     1328
     1329                        // If no term is found, check whether this was a previously split term.
     1330                        if ( ! $_term && $old_term_id = wp_get_split_term( $term, $taxonomy ) ) {
     1331                                return get_term( $old_term_id, $taxonomy, $output, $filter );
     1332                        }
     1333
    13181334                        if ( ! $_term )
    13191335                                return null;
    13201336                        wp_cache_add( $term, $_term, $taxonomy . ':terms:' . $incrementor );
     
    16751691         */
    16761692        $args = apply_filters( 'get_terms_args', $args, $taxonomies );
    16771693
     1694        /*
     1695         * For parameters that accept term_ids, check to see whether any of the IDs passed to the parameter correspond
     1696         * to previously split taxonomy terms. If so, swap out the old term_id for the new one, so the query matches.
     1697         */
     1698        $term_id_params = array( 'exclude', 'exclude_tree', 'include', 'parent', 'child_of' );
     1699        foreach ( $term_id_params as $term_id_param ) {
     1700                if ( empty( $args[ $term_id_param ] ) ) {
     1701                        continue;
     1702                }
     1703
     1704                $param_is_array = is_array( $args[ $term_id_param ] );
     1705
     1706                $passed_term_ids = (array) $args[ $term_id_param ];
     1707                foreach ( $passed_term_ids as $passed_term_index => $passed_term_id ) {
     1708                        foreach ( $taxonomies as $taxonomy ) {
     1709                                if ( $split_term = wp_get_split_term( $passed_term_id, $taxonomy ) ) {
     1710                                        $passed_term_ids[ $passed_term_index ] = $split_term;
     1711                                        continue 2;
     1712                                }
     1713                        }
     1714                }
     1715
     1716                if ( $param_is_array ) {
     1717                        $args[ $term_id_param ] = $passed_term_ids;
     1718                } else {
     1719                        $args[ $term_id_param ] = $passed_term_ids[0];
     1720                }
     1721        }
     1722
    16781723        $child_of = $args['child_of'];
    16791724        if ( $child_of ) {
    16801725                $hierarchy = _get_term_hierarchy( reset( $taxonomies ) );
     
    39033948}
    39043949
    39053950/**
     3951 * Look up a previously split taxonomy term by its old term_id.
     3952 *
     3953 * Terms that have been split by _split_shared_term() are stored, so that
     3954 * attempts to retrieve terms by the previous term_id still work.
     3955 *
     3956 * @since 4.1.0
     3957 *
     3958 * @param int    $old_term_id Old, pre-split term_id.
     3959 * @param string $taxonomy    Taxonomy name.
     3960 */
     3961function wp_get_split_term( $old_term_id, $taxonomy ) {
     3962        $split_terms = get_option( '_split_terms_' . $taxonomy );
     3963
     3964        return ! empty( $split_terms[ $old_term_id ] ) ? $split_terms[ $old_term_id ] : null;
     3965}
     3966
     3967/**
    39063968 * Add count of children to parent count.
    39073969 *
    39083970 * Recalculates term counts by including items from child terms. Assumes all
     
    41154177                clean_term_cache( $term_id, $shared_term_taxonomy );
    41164178        }
    41174179
     4180        // Keep a record of term_ids that have been split, keyed by old term_id.
     4181        $split_term_data = get_option( '_split_terms_' . $term_taxonomy->taxonomy, array() );
     4182        $split_term_data[ $term_id ] = $new_term_id;
     4183        update_option( '_split_terms_' . $term_taxonomy->taxonomy, $split_term_data );
     4184
    41184185        /**
    41194186         * Fires after a previously shared taxonomy term is split into two separate terms.
    41204187         *
    41214188         * @since 4.1.0
    41224189         *
    4123          * @param int $term_id          ID of the formerly shared term.
    4124          * @param int $new_term_id      ID of the new term created for the $term_taxonomy_id.
    4125          * @param int $term_taxonomy_id ID for the term_taxonomy row affected by the split.
     4190         * @param int    $term_id          ID of the formerly shared term.
     4191         * @param int    $new_term_id      ID of the new term created for the $term_taxonomy_id.
     4192         * @param int    $term_taxonomy_id ID for the term_taxonomy row affected by the split.
     4193         * @param string $taxonomy         Taxonomy for the split term.
    41264194         */
    4127         do_action( 'split_shared_term', $term_id, $new_term_id, $term_taxonomy_id );
     4195        do_action( 'split_shared_term', $term_id, $new_term_id, $term_taxonomy_id, $term_taxonomy->taxonomy );
    41284196
    41294197        return $new_term_id;
    41304198}
    41314199
    41324200/**
     4201 * Check default categories when a term gets split to see if any of them need
     4202 * to be updated.
     4203 *
     4204 * @since 4.1.0
     4205 * @access private
     4206 *
     4207 * @param int    $term_id          ID of the formerly shared term.
     4208 * @param int    $new_term_id      ID of the new term created for the $term_taxonomy_id.
     4209 * @param int    $term_taxonomy_id ID for the term_taxonomy row affected by the split.
     4210 * @param string $taxonomy         Taxonomy for the split term.
     4211 */
     4212function _wp_check_split_default_terms( $term_id, $new_term_id, $term_taxonomy_id, $taxonomy ) {
     4213        if ( 'category' == $taxonomy ) {
     4214                foreach ( array( 'default_category', 'default_link_category', 'default_email_category' ) as $option ) {
     4215                        if ( $term_id == get_option( $option, -1 ) ) {
     4216                                update_option( $option, $new_term_id );
     4217                        }
     4218                }
     4219        }
     4220}
     4221
     4222/**
     4223 * Check menu items when a term gets split to see if any of them need to be
     4224 * updated.
     4225 *
     4226 * @since 4.1.0
     4227 * @access private
     4228 *
     4229 * @param int    $term_id          ID of the formerly shared term.
     4230 * @param int    $new_term_id      ID of the new term created for the $term_taxonomy_id.
     4231 * @param int    $term_taxonomy_id ID for the term_taxonomy row affected by the split.
     4232 * @param string $taxonomy         Taxonomy for the split term.
     4233 */
     4234function _wp_check_split_terms_in_menus( $term_id, $new_term_id, $term_taxonomy_id, $taxonomy ) {
     4235        global $wpdb;
     4236        $post_ids = $wpdb->get_col( $wpdb->prepare(
     4237                "SELECT m1.post_id
     4238                FROM {$wpdb->postmeta} AS m1
     4239                        INNER JOIN {$wpdb->postmeta} AS m2 ON m2.post_id=m1.post_id
     4240                        INNER JOIN {$wpdb->postmeta} AS m3 ON m3.post_id=m1.post_id
     4241                WHERE ( m1.meta_key = '_menu_item_type' AND m1.meta_value = 'taxonomy' )
     4242                        AND ( m2.meta_key = '_menu_item_object' AND m2.meta_value = '%s' )
     4243                        AND ( m3.meta_key = '_menu_item_object_id' AND m3.meta_value = %d )",
     4244                $taxonomy,
     4245                $term_id
     4246        ) );
     4247
     4248        if ( $post_ids ) {
     4249                foreach ( $post_ids as $post_id ) {
     4250                        update_post_meta( $post_id, '_menu_item_object_id', $new_term_id, $term_id );
     4251                }
     4252        }
     4253}
     4254
     4255/**
    41334256 * Generate a permalink for a taxonomy term archive.
    41344257 *
    41354258 * @since 2.5.0
  • tests/phpunit/tests/term/splitSharedTerm.php

     
    44 * @group taxonomy
    55 */
    66class Tests_Term_SplitSharedTerm extends WP_UnitTestCase {
    7         protected $tt_ids = array();
     7        protected $terms = array();
    88
    99        public function setUp() {
    1010                global $wpdb;
     
    4040                        'parent' => $t1['term_id'],
    4141                ) );
    4242
    43                 $this->tt_ids = array(
    44                         't1' => $t1['term_taxonomy_id'],
    45                         't2' => $t2['term_taxonomy_id'],
    46                         't3' => $t3['term_taxonomy_id'],
    47                         't2_child' => $t2_child['term_taxonomy_id'],
     43                $this->terms = array(
     44                        't1' => $t1,
     45                        't2' => $t2,
     46                        't3' => $t3,
     47                        't2_child' => $t2_child,
    4848                );
    4949
    5050                _split_shared_term( $t1['term_id'], $t2['term_taxonomy_id'] );
     
    5555         * @ticket 5809
    5656         */
    5757        public function test_should_create_new_term_ids() {
    58                 $t1_term = get_term_by( 'term_taxonomy_id', $this->tt_ids['t1'], 'wptests_tax' );
    59                 $t2_term = get_term_by( 'term_taxonomy_id', $this->tt_ids['t2'], 'wptests_tax_2' );
    60                 $t3_term = get_term_by( 'term_taxonomy_id', $this->tt_ids['t3'], 'wptests_tax_3' );
     58                $t1_term = get_term_by( 'term_taxonomy_id', $this->terms['t1']['term_taxonomy_id'], 'wptests_tax' );
     59                $t2_term = get_term_by( 'term_taxonomy_id', $this->terms['t2']['term_taxonomy_id'], 'wptests_tax_2' );
     60                $t3_term = get_term_by( 'term_taxonomy_id', $this->terms['t3']['term_taxonomy_id'], 'wptests_tax_3' );
    6161
    6262                $this->assertNotEquals( $t1_term->term_id, $t2_term->term_id );
    6363                $this->assertNotEquals( $t1_term->term_id, $t3_term->term_id );
     
    6868         * @ticket 5809
    6969         */
    7070        public function test_should_retain_child_terms_when_using_get_terms_parent() {
    71                 $t2_term = get_term_by( 'term_taxonomy_id', $this->tt_ids['t2'], 'wptests_tax_2' );
     71                $t2_term = get_term_by( 'term_taxonomy_id', $this->terms['t2']['term_taxonomy_id'], 'wptests_tax_2' );
    7272                $children = get_terms( 'wptests_tax_2', array(
    7373                        'parent' => $t2_term->term_id,
    7474                        'hide_empty' => false,
    7575                ) );
    7676
    77                 $this->assertEquals( $this->tt_ids['t2_child'], $children[0]->term_taxonomy_id );
     77                $this->assertEquals( $this->terms['t2_child']['term_taxonomy_id'], $children[0]->term_taxonomy_id );
    7878        }
    7979
    8080        /**
    8181         * @ticket 5809
    8282         */
    8383        public function test_should_retain_child_terms_when_using_get_terms_child_of() {
    84                 $t2_term = get_term_by( 'term_taxonomy_id', $this->tt_ids['t2'], 'wptests_tax_2' );
     84                $t2_term = get_term_by( 'term_taxonomy_id', $this->terms['t2']['term_taxonomy_id'], 'wptests_tax_2' );
    8585                $children = get_terms( 'wptests_tax_2', array(
    8686                        'child_of' => $t2_term->term_id,
    8787                        'hide_empty' => false,
    8888                ) );
    8989
    90                 $this->assertEquals( $this->tt_ids['t2_child'], $children[0]->term_taxonomy_id );
     90                $this->assertEquals( $this->terms['t2_child']['term_taxonomy_id'], $children[0]->term_taxonomy_id );
    9191        }
    9292
    9393        /**
     
    121121                $t2_children = get_term_children( $t2['term_id'], 'wptests_tax_4' );
    122122                $this->assertEquals( array( $new_term_id ), $t2_children );
    123123        }
     124
     125        /**
     126         * @ticket 30335
     127         */
     128        public function test_should_remain_available_at_old_term_id_using_get_term_by() {
     129                $t2_term = get_term_by( 'term_taxonomy_id', $this->terms['t2']['term_taxonomy_id'], 'wptests_tax_2' );
     130
     131                $this->assertEquals( $t2_term, get_term_by( 'id', $this->terms['t1']['term_id'], 'wptests_tax_2' ) );
     132        }
     133
     134        /**
     135         * @ticket 30335
     136         */
     137        public function test_should_remain_available_at_old_term_id_using_get_term() {
     138                $t2_term = get_term_by( 'term_taxonomy_id', $this->terms['t2']['term_taxonomy_id'], 'wptests_tax_2' );
     139
     140                $this->assertEquals( $t2_term, get_term( intval( $this->terms['t1']['term_id'] ), 'wptests_tax_2' ) );
     141        }
     142
     143        /**
     144         * @ticket 30335
     145         */
     146        public function test_should_remain_available_at_old_term_id_using_get_terms_with_parent_param() {
     147                $found = get_terms( 'wptests_tax_2', array(
     148                        'parent' => $this->terms['t1']['term_id'],
     149                        'hide_empty' => false,
     150                ) );
     151                $this->assertEquals( $this->terms['t2_child']['term_id'], $found[0]->term_id );
     152        }
     153
     154        /**
     155         * @ticket 30335
     156         */
     157        public function test_should_remain_available_at_old_term_id_using_get_terms_with_include_param() {
     158                $t2_term = get_term_by( 'term_taxonomy_id', $this->terms['t2']['term_taxonomy_id'], 'wptests_tax_2' );
     159
     160                $found = get_terms( 'wptests_tax_2', array(
     161                        'include' => array( $this->terms['t1']['term_id'] ),
     162                        'hide_empty' => false,
     163                ) );
     164                $this->assertEquals( $t2_term->term_id, $found[0]->term_id );
     165        }
     166
     167        /**
     168         * @ticket 30335
     169         */
     170        public function test_should_remain_available_at_old_term_id_using_tax_query() {
     171                $t2_term = get_term_by( 'term_taxonomy_id', $this->terms['t2']['term_taxonomy_id'], 'wptests_tax_2' );
     172
     173                $tq = new WP_Tax_Query( array(
     174                        array(
     175                                'field' => 'term_id',
     176                                'taxonomy' => 'wptests_tax_2',
     177                                'terms' => array( $this->terms['t1']['term_id'] ),
     178                        ),
     179                ) );
     180                $tq->transform_query( $tq->queries[0], 'term_taxonomy_id' );
     181
     182                $this->assertEquals( array( $t2_term->term_taxonomy_id ), $tq->queries[0]['terms'] );
     183        }
     184
     185        /**
     186         * @ticket 30335
     187         */
     188        public function test_should_remain_available_at_old_term_id_using_wp_dropdown_categories() {
     189                $t2_term = get_term_by( 'term_taxonomy_id', $this->terms['t2']['term_taxonomy_id'], 'wptests_tax_2' );
     190                $terms = get_terms( 'wptests_tax_2', array( 'hide_empty' => false ) );
     191                $dropdown = wp_dropdown_categories( array(
     192                        'echo'       => false,
     193                        'taxonomy'   => 'wptests_tax_2',
     194                        'hide_empty' => false,
     195                        'selected'   => $this->terms['t1']['term_id'],
     196                ) );
     197
     198                $this->assertRegExp( "/<option[^>]+value=\"{$t2_term->term_id}\" selected=\"selected\">/", $dropdown );
     199        }
     200
     201        /**
     202         * @ticket 30335
     203         */
     204        public function test_should_update_default_category_on_term_split() {
     205                global $wpdb;
     206                $t1 = wp_insert_term( 'Foo Default', 'category' );
     207
     208                update_option( 'default_category', $t1['term_id'] );
     209
     210                register_taxonomy( 'wptests_tax_5', 'post' );
     211                $t2 = wp_insert_term( 'Foo Default', 'wptests_tax_5' );
     212
     213                // Manually modify because split terms shouldn't naturally occur.
     214                $wpdb->update( $wpdb->term_taxonomy,
     215                        array( 'term_id' => $t1['term_id'] ),
     216                        array( 'term_taxonomy_id' => $t2['term_taxonomy_id'] ),
     217                        array( '%d' ),
     218                        array( '%d' )
     219                );
     220
     221                $this->assertEquals( $t1['term_id'], get_option( 'default_category', -1 ) );
     222
     223                $new_term_id = _split_shared_term( $t1['term_id'], $t1['term_taxonomy_id'] );
     224
     225                $this->assertNotEquals( $new_term_id, $t1['term_id'] );
     226                $this->assertEquals( $new_term_id, get_option( 'default_category', -1 ) );
     227        }
     228
     229        /**
     230         * @ticket 30335
     231         */
     232        public function test_should_update_menus_on_term_split() {
     233                global $wpdb;
     234
     235                $t1 = wp_insert_term( 'Foo Menu', 'category' );
     236
     237                register_taxonomy( 'wptests_tax_6', 'post' );
     238                $t2 = wp_insert_term( 'Foo Menu', 'wptests_tax_6' );
     239
     240                // Manually modify because split terms shouldn't naturally occur.
     241                $wpdb->update( $wpdb->term_taxonomy,
     242                        array( 'term_id' => $t1['term_id'] ),
     243                        array( 'term_taxonomy_id' => $t2['term_taxonomy_id'] ),
     244                        array( '%d' ),
     245                        array( '%d' )
     246                );
     247
     248                $menu_id = wp_create_nav_menu( rand_str() );
     249                $cat_menu_item = wp_update_nav_menu_item( $menu_id, 0, array(
     250                        'menu-item-type' => 'taxonomy',
     251                        'menu-item-object' => 'category',
     252                        'menu-item-object-id' => $t1['term_id'],
     253                        'menu-item-status' => 'publish'
     254                ) );
     255                $this->assertEquals( $t1['term_id'], get_post_meta( $cat_menu_item, '_menu_item_object_id', true ) );
     256
     257                $new_term_id = _split_shared_term( $t1['term_id'], $t1['term_taxonomy_id'] );
     258                $this->assertNotEquals( $new_term_id, $t1['term_id'] );
     259                $this->assertEquals( $new_term_id, get_post_meta( $cat_menu_item, '_menu_item_object_id', true ) );
     260        }
    124261}