WordPress.org

Make WordPress Core

Ticket #39692: 39692.7.diff

File 39692.7.diff, 14.0 KB (added by westonruter, 10 months ago)

Δ https://github.com/xwp/wordpress-develop/pull/245/files/48a67c0..b2d5c67

  • src/wp-includes/class-wp-customize-nav-menus.php

    diff --git src/wp-includes/class-wp-customize-nav-menus.php src/wp-includes/class-wp-customize-nav-menus.php
    index 3d7b1f0deb..3b9ca487fd 100644
    final class WP_Customize_Nav_Menus { 
    2727        public $manager;
    2828
    2929        /**
    30          * Previewed Menus.
     30         * Original nav menu locations before the theme was switched.
    3131         *
    32          * @since 4.3.0
     32         * @since 4.9.0
    3333         * @var array
    3434         */
    35         public $previewed_menus;
     35        protected $original_nav_menu_locations;
    3636
    3737        /**
    3838         * Constructor.
    final class WP_Customize_Nav_Menus { 
    4242         * @param object $manager An instance of the WP_Customize_Manager class.
    4343         */
    4444        public function __construct( $manager ) {
    45                 $this->previewed_menus = array();
    46                 $this->manager         = $manager;
     45                $this->manager = $manager;
     46                $this->original_nav_menu_locations = get_nav_menu_locations();
    4747
    4848                // See https://github.com/xwp/wp-customize-snapshots/blob/962586659688a5b1fd9ae93618b7ce2d4e7a421c/php/class-customize-snapshot-manager.php#L469-L499
    4949                add_action( 'customize_register', array( $this, 'customize_register' ), 11 );
    final class WP_Customize_Nav_Menus { 
    582582                        $choices[ $menu->term_id ] = wp_html_excerpt( $menu->name, 40, '…' );
    583583                }
    584584
     585                // Attempt to re-map the nav menu location assignments when previewing a theme switch.
     586                $mapped_nav_menu_locations = array();
     587                if ( ! $this->manager->is_theme_active() ) {
     588                        $mapped_nav_menu_locations = wp_map_nav_menu_locations( get_nav_menu_locations(), $this->original_nav_menu_locations );
     589                }
     590
    585591                foreach ( $locations as $location => $description ) {
    586592                        $setting_id = "nav_menu_locations[{$location}]";
    587593
    final class WP_Customize_Nav_Menus { 
    600606                                ) );
    601607                        }
    602608
     609                        // Override the assigned nav menu location if mapped during previewed theme switch.
     610                        if ( isset( $mapped_nav_menu_locations[ $location ] ) ) {
     611                                $this->manager->set_post_value( $setting_id, $mapped_nav_menu_locations[ $location ] );
     612                        }
     613
    603614                        $this->manager->add_control( new WP_Customize_Nav_Menu_Location_Control( $this->manager, $setting_id, array(
    604615                                'label'       => $description,
    605616                                'location_id' => $location,
  • src/wp-includes/default-filters.php

    diff --git src/wp-includes/default-filters.php src/wp-includes/default-filters.php
    index 9212e42865..8a8b6a9072 100644
    add_action( 'wp_footer', 'wp_print_footer_scripts', 20 ); 
    262262add_action( 'template_redirect',   'wp_shortlink_header',             11, 0 );
    263263add_action( 'wp_print_footer_scripts', '_wp_footer_scripts'                 );
    264264add_action( 'init',                'check_theme_switched',            99    );
     265add_action( 'after_switch_theme',  '_wp_menus_changed'                      );
    265266add_action( 'after_switch_theme',  '_wp_sidebars_changed'                   );
    266267add_action( 'wp_print_styles',     'print_emoji_styles'                     );
    267268
  • src/wp-includes/nav-menu.php

    diff --git src/wp-includes/nav-menu.php src/wp-includes/nav-menu.php
    index 1585c62f3d..882aee6287 100644
    function _wp_delete_customize_changeset_dependent_auto_drafts( $post_id ) { 
    10261026        }
    10271027        add_action( 'delete_post', '_wp_delete_customize_changeset_dependent_auto_drafts' );
    10281028}
     1029
     1030/**
     1031 * Handle menu config after theme change.
     1032 *
     1033 * @access private
     1034 * @since 4.9.0
     1035 */
     1036function _wp_menus_changed() {
     1037        $old_nav_menu_locations = get_option( 'theme_switch_menu_locations', array() );
     1038        $new_nav_menu_locations = get_nav_menu_locations();
     1039        $mapped_nav_menu_locations = wp_map_nav_menu_locations( $new_nav_menu_locations, $old_nav_menu_locations );
     1040        set_theme_mod( 'nav_menu_locations', $mapped_nav_menu_locations );
     1041        delete_option( 'theme_switch_menu_locations' );
     1042}
     1043
     1044/**
     1045 * Re-maps nav menu location assignments according to previous location assignments (in a different theme).
     1046 *
     1047 * @since 4.9.0
     1048 *
     1049 * @param array $new_nav_menu_locations New nav menu locations assignments.
     1050 * @param array $old_nav_menu_locations Old nav menu locations assignments.
     1051 * @return array Nav menus mapped to new nav menu locations.
     1052 */
     1053function wp_map_nav_menu_locations( $new_nav_menu_locations, $old_nav_menu_locations ) {
     1054        $registered_nav_menus = get_registered_nav_menus();
     1055
     1056        // Short-circuit if there are no old nav menu location assignments to map.
     1057        if ( empty( $old_nav_menu_locations ) ) {
     1058                return $new_nav_menu_locations;
     1059        }
     1060
     1061        // If old and new theme have just one location, map it and we're done.
     1062        if ( 1 === count( $old_nav_menu_locations ) && 1 === count( $registered_nav_menus ) ) {
     1063                $new_nav_menu_locations[ key( $registered_nav_menus ) ] = array_pop( $old_nav_menu_locations );
     1064                return $new_nav_menu_locations;
     1065        }
     1066
     1067        $old_locations = array_keys( $old_nav_menu_locations );
     1068
     1069        // Map locations with the same slug.
     1070        foreach ( $registered_nav_menus as $location => $name ) {
     1071                if ( in_array( $location, $old_locations, true ) ) {
     1072                        $new_nav_menu_locations[ $location ] = $old_nav_menu_locations[ $location ];
     1073                        unset( $old_nav_menu_locations[ $location ] );
     1074                }
     1075        }
     1076
     1077        // If there are no old nav menu locations left, then we're done.
     1078        if ( empty( $old_nav_menu_locations ) ) {
     1079                return $new_nav_menu_locations;
     1080        }
     1081
     1082        /*
     1083         * If old and new theme both have locations that contain phrases
     1084         * from within the same group, make an educated guess and map it.
     1085         */
     1086        $common_slug_groups = array(
     1087                array( 'header', 'main', 'navigation', 'primary', 'top' ),
     1088                array( 'bottom', 'footer', 'secondary', 'subsidiary' ),
     1089                array( 'social' ),
     1090                // TODO: Find a second slug or remove, since locations with same slug are already mapped.
     1091        );
     1092
     1093        // Go through each group...
     1094        foreach ( $common_slug_groups as $slug_group ) {
     1095
     1096                // ...and see if any of these slugs...
     1097                foreach ( $slug_group as $slug ) {
     1098
     1099                        // ...and any of the new menu locations...
     1100                        foreach ( $registered_nav_menus as $new_location => $name ) {
     1101
     1102                                // ...actually match!
     1103                                if ( false === stripos( $new_location, $slug ) && false === stripos( $slug, $new_location ) ) {
     1104                                        continue;
     1105                                }
     1106
     1107                                // Then see if any of the old locations...
     1108                                foreach ( $old_nav_menu_locations as $location => $menu_id ) {
     1109
     1110                                        // ...match a slug in the same group.
     1111                                        foreach ( $slug_group as $slug ) {
     1112
     1113                                                // But skip if the location and slug don't match.
     1114                                                if ( false === stripos( $location, $slug ) && false === stripos( $slug, $location ) ) {
     1115                                                        continue;
     1116                                                }
     1117
     1118                                                // Make sure this location wasn't mapped and removed previously.
     1119                                                if ( ! empty( $old_nav_menu_locations[ $location ] ) ) {
     1120
     1121                                                        // We have a match that can be mapped!
     1122                                                        $new_nav_menu_locations[ $new_location ] = $old_nav_menu_locations[ $location ];
     1123
     1124                                                        // Remove the mapped location so it can't be mapped again.
     1125                                                        unset( $old_nav_menu_locations[ $location ] );
     1126
     1127                                                        // Go back and check the next new menu location.
     1128                                                        continue 3;
     1129                                                }
     1130                                        } // endforeach ( $slug_group as $slug )
     1131                                } // endforeach ( $old_nav_menu_locations as $location => $menu_id )
     1132                        } // endforeach foreach ( $registered_nav_menus as $new_location => $name )
     1133                } // endforeach ( $slug_group as $slug )
     1134        } // endforeach ( $common_slug_groups as $slug_group )
     1135
     1136        return $new_nav_menu_locations;
     1137}
  • src/wp-includes/theme.php

    diff --git src/wp-includes/theme.php src/wp-includes/theme.php
    index 2b2fbf2403..1d55f4f27c 100644
    function switch_theme( $stylesheet ) { 
    691691        }
    692692
    693693        $nav_menu_locations = get_theme_mod( 'nav_menu_locations' );
     694        add_option( 'theme_switch_menu_locations', $nav_menu_locations );
    694695
    695696        if ( func_num_args() > 1 ) {
    696697                $stylesheet = func_get_arg( 1 );
    function switch_theme( $stylesheet ) { 
    731732                if ( 'wp_ajax_customize_save' === current_action() ) {
    732733                        remove_theme_mod( 'sidebars_widgets' );
    733734                }
    734 
    735                 if ( ! empty( $nav_menu_locations ) ) {
    736                         $nav_mods = get_theme_mod( 'nav_menu_locations' );
    737                         if ( empty( $nav_mods ) ) {
    738                                 set_theme_mod( 'nav_menu_locations', $nav_menu_locations );
    739                         }
    740                 }
    741735        }
    742736
    743737        update_option( 'theme_switched', $old_theme->get_stylesheet() );
  • new file tests/phpunit/tests/menu/nav-menu.php

    diff --git tests/phpunit/tests/menu/nav-menu.php tests/phpunit/tests/menu/nav-menu.php
    new file mode 100644
    index 0000000000..ec346da53b
    - +  
     1<?php
     2
     3/**
     4 * @group navmenus
     5 */
     6class Tests_Nav_Menu_Theme_Change extends WP_UnitTestCase {
     7
     8        /**
     9         * Set up.
     10         */
     11        function setUp() {
     12                parent::setUp();
     13
     14                // Unregister all nav menu locations.
     15                foreach ( array_keys( get_registered_nav_menus() ) as $location ) {
     16                        unregister_nav_menu( $location );
     17                }
     18        }
     19
     20        /**
     21         * Register nav menu locations.
     22         *
     23         * @param array $locations Location slugs.
     24         */
     25        function register_nav_menu_locations( $locations ) {
     26                foreach ( $locations as $location ) {
     27                        register_nav_menu( $location, ucfirst( $location ) );
     28                }
     29        }
     30
     31        /**
     32         * Two themes with one location each should just map, switching to a theme not previously-active.
     33         *
     34         * @covers wp_map_nav_menu_locations()
     35         */
     36        function test_one_location_each() {
     37                $this->register_nav_menu_locations( array( 'primary' ) );
     38                $prev_theme_nav_menu_locations = array(
     39                        'unique-slug' => 1,
     40                );
     41                $old_next_theme_nav_menu_locations = array(); // It was not active before.
     42                $new_next_theme_nav_menu_locations = wp_map_nav_menu_locations( $old_next_theme_nav_menu_locations, $prev_theme_nav_menu_locations );
     43
     44                $expected_nav_menu_locations = array(
     45                        'primary' => 1,
     46                );
     47                $this->assertEquals( $expected_nav_menu_locations, $new_next_theme_nav_menu_locations );
     48        }
     49
     50        /**
     51         * Locations with the same name should map, switching to a theme not previously-active.
     52         *
     53         * @covers wp_map_nav_menu_locations()
     54         */
     55        function test_locations_with_same_slug() {
     56                $this->register_nav_menu_locations( array( 'primary', 'secondary' ) );
     57                $prev_theme_nav_menu_locations = array(
     58                        'primary' => 1,
     59                        'secondary' => 2,
     60                );
     61
     62                $old_next_theme_nav_menu_locations = array(); // It was not active before.
     63                $new_next_theme_nav_menu_locations = wp_map_nav_menu_locations( $old_next_theme_nav_menu_locations, $prev_theme_nav_menu_locations );
     64
     65                $expected_nav_menu_locations = $prev_theme_nav_menu_locations;
     66                $this->assertEquals( $expected_nav_menu_locations, $new_next_theme_nav_menu_locations );
     67        }
     68
     69        /**
     70         * If the new theme was previously active, we should honor any changes to nav menu mapping done when the other theme was active.
     71         *
     72         * @covers wp_map_nav_menu_locations()
     73         */
     74        function test_new_theme_previously_active() {
     75                $this->register_nav_menu_locations( array( 'primary' ) );
     76
     77                $prev_theme_nav_menu_locations = array(
     78                        'primary' => 1,
     79                        'secondary' => 2,
     80                );
     81
     82                // Nav menu location assignments that were set on the next theme when it was previously active.
     83                $old_next_theme_nav_menu_locations = array(
     84                        'primary' => 3,
     85                );
     86
     87                $new_next_theme_nav_menu_locations = wp_map_nav_menu_locations( $old_next_theme_nav_menu_locations, $prev_theme_nav_menu_locations );
     88
     89                $expected_nav_menu_locations = wp_array_slice_assoc( $prev_theme_nav_menu_locations, array_keys( get_registered_nav_menus() ) );
     90                $this->assertEquals( $expected_nav_menu_locations, $new_next_theme_nav_menu_locations );
     91        }
     92
     93        /**
     94         * Make educated guesses on theme locations.
     95         *
     96         * @covers wp_map_nav_menu_locations()
     97         */
     98        function test_location_guessing() {
     99                $this->register_nav_menu_locations( array( 'primary', 'secondary' ) );
     100
     101                $prev_theme_nav_menu_locations = array(
     102                        'header' => 1,
     103                        'footer' => 2,
     104                );
     105
     106                $old_next_theme_nav_menu_locations = array();
     107                $new_next_theme_nav_menu_locations = wp_map_nav_menu_locations( $old_next_theme_nav_menu_locations, $prev_theme_nav_menu_locations );
     108
     109                $expected_nav_menu_locations = array(
     110                        'primary' => 1,
     111                        'secondary' => 2,
     112                );
     113                $this->assertEquals( $expected_nav_menu_locations, $new_next_theme_nav_menu_locations );
     114        }
     115
     116        /**
     117         * Make sure two locations that fall in the same group don't get the same menu assigned.
     118         *
     119         * @covers wp_map_nav_menu_locations()
     120         */
     121        function test_location_guessing_one_menu_per_group() {
     122                $this->register_nav_menu_locations( array( 'primary' ) );
     123                $prev_theme_nav_menu_locations = array(
     124                        'top-menu' => 1,
     125                        'secondary' => 2,
     126                );
     127
     128                $old_next_theme_nav_menu_locations = array();
     129                $new_next_theme_nav_menu_locations = wp_map_nav_menu_locations( $old_next_theme_nav_menu_locations, $prev_theme_nav_menu_locations );
     130
     131                $expected_nav_menu_locations = array(
     132                        'main' => 1,
     133                );
     134                $this->assertEqualSets( $expected_nav_menu_locations, $new_next_theme_nav_menu_locations );
     135        }
     136
     137        /**
     138         * Make sure two locations that fall in the same group get menus assigned from the same group.
     139         *
     140         * @covers wp_map_nav_menu_locations()
     141         */
     142        function test_location_guessing_one_menu_per_location() {
     143                $this->register_nav_menu_locations( array( 'primary', 'main' ) );
     144
     145                $prev_theme_nav_menu_locations = array(
     146                        'navigation-menu' => 1,
     147                        'top-menu' => 2,
     148                );
     149
     150                $old_next_theme_nav_menu_locations = array();
     151                $new_next_theme_nav_menu_locations = wp_map_nav_menu_locations( $old_next_theme_nav_menu_locations, $prev_theme_nav_menu_locations );
     152
     153                $expected_nav_menu_locations = array(
     154                        'main' => 1,
     155                        'primary' => 2,
     156                );
     157                $this->assertEquals( $expected_nav_menu_locations, $new_next_theme_nav_menu_locations );
     158        }
     159
     160        /**
     161         * Technically possible to register menu locations numerically.
     162         *
     163         * @covers wp_map_nav_menu_locations()
     164         */
     165        function test_numerical_locations() {
     166                $this->register_nav_menu_locations( array( 'primary', 1 ) );
     167
     168                $prev_theme_nav_menu_locations = array(
     169                        'main' => 1,
     170                        'secondary' => 2,
     171                        'tertiary' => 3,
     172                );
     173
     174                $old_next_theme_nav_menu_locations = array();
     175                $new_next_theme_nav_menu_locations = wp_map_nav_menu_locations( $old_next_theme_nav_menu_locations, $prev_theme_nav_menu_locations );
     176
     177                $expected_nav_menu_locations = array(
     178                        'primary' => 1,
     179                );
     180                $this->assertEqualSets( $expected_nav_menu_locations, $new_next_theme_nav_menu_locations );
     181        }
     182}