Map nav menu locations on theme switch

This will send nav menu locations through three levels of mapping:
1. If both themes have only one location, that gets mapped.
2. If both themes have locations with the same slug, they get mapped.
3. Locations that (even partially) match slugs from a similar kind of menu location will get mapped.

Menu locations are mapped for Live Previews in the Customizer and during theme switches.

Props westonruter, obenland, welcher, melchoyce.
Fixes #39692.


Built from https://develop.svn.wordpress.org/trunk@41237


git-svn-id: http://core.svn.wordpress.org/trunk@41077 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
Konstantin Obenland 2017-08-09 21:04:47 +00:00
parent 5fe8d31118
commit e183d59acb
5 changed files with 127 additions and 13 deletions

View File

@ -27,12 +27,12 @@ final class WP_Customize_Nav_Menus {
public $manager;
/**
* Previewed Menus.
* Original nav menu locations before the theme was switched.
*
* @since 4.3.0
* @since 4.9.0
* @var array
*/
public $previewed_menus;
protected $original_nav_menu_locations;
/**
* Constructor.
@ -42,8 +42,8 @@ final class WP_Customize_Nav_Menus {
* @param object $manager An instance of the WP_Customize_Manager class.
*/
public function __construct( $manager ) {
$this->previewed_menus = array();
$this->manager = $manager;
$this->manager = $manager;
$this->original_nav_menu_locations = get_nav_menu_locations();
// See https://github.com/xwp/wp-customize-snapshots/blob/962586659688a5b1fd9ae93618b7ce2d4e7a421c/php/class-customize-snapshot-manager.php#L469-L499
add_action( 'customize_register', array( $this, 'customize_register' ), 11 );
@ -582,6 +582,12 @@ final class WP_Customize_Nav_Menus {
$choices[ $menu->term_id ] = wp_html_excerpt( $menu->name, 40, '…' );
}
// Attempt to re-map the nav menu location assignments when previewing a theme switch.
$mapped_nav_menu_locations = array();
if ( ! $this->manager->is_theme_active() ) {
$mapped_nav_menu_locations = wp_map_nav_menu_locations( get_nav_menu_locations(), $this->original_nav_menu_locations );
}
foreach ( $locations as $location => $description ) {
$setting_id = "nav_menu_locations[{$location}]";
@ -600,6 +606,11 @@ final class WP_Customize_Nav_Menus {
) );
}
// Override the assigned nav menu location if mapped during previewed theme switch.
if ( isset( $mapped_nav_menu_locations[ $location ] ) ) {
$this->manager->set_post_value( $setting_id, $mapped_nav_menu_locations[ $location ] );
}
$this->manager->add_control( new WP_Customize_Nav_Menu_Location_Control( $this->manager, $setting_id, array(
'label' => $description,
'location_id' => $location,

View File

@ -262,6 +262,7 @@ add_action( 'wp_footer', 'wp_print_footer_scripts', 20 );
add_action( 'template_redirect', 'wp_shortlink_header', 11, 0 );
add_action( 'wp_print_footer_scripts', '_wp_footer_scripts' );
add_action( 'init', 'check_theme_switched', 99 );
add_action( 'after_switch_theme', '_wp_menus_changed' );
add_action( 'after_switch_theme', '_wp_sidebars_changed' );
add_action( 'wp_print_styles', 'print_emoji_styles' );

View File

@ -1026,3 +1026,111 @@ function _wp_delete_customize_changeset_dependent_auto_drafts( $post_id ) {
}
add_action( 'delete_post', '_wp_delete_customize_changeset_dependent_auto_drafts' );
}
/**
* Handle menu config after theme change.
*
* @access private
* @since 4.9.0
*/
function _wp_menus_changed() {
$old_nav_menu_locations = get_option( 'theme_switch_menu_locations', array() );
$new_nav_menu_locations = get_nav_menu_locations();
$mapped_nav_menu_locations = wp_map_nav_menu_locations( $new_nav_menu_locations, $old_nav_menu_locations );
set_theme_mod( 'nav_menu_locations', $mapped_nav_menu_locations );
delete_option( 'theme_switch_menu_locations' );
}
/**
* Maps nav menu locations according to assignments in previously active theme.
*
* @since 4.9.0
*
* @param array $new_nav_menu_locations New nav menu locations assignments.
* @param array $old_nav_menu_locations Old nav menu locations assignments.
* @return array Nav menus mapped to new nav menu locations.
*/
function wp_map_nav_menu_locations( $new_nav_menu_locations, $old_nav_menu_locations ) {
$registered_nav_menus = get_registered_nav_menus();
// Short-circuit if there are no old nav menu location assignments to map.
if ( empty( $old_nav_menu_locations ) ) {
return $new_nav_menu_locations;
}
// If old and new theme have just one location, map it and we're done.
if ( 1 === count( $old_nav_menu_locations ) && 1 === count( $registered_nav_menus ) ) {
$new_nav_menu_locations[ key( $registered_nav_menus ) ] = array_pop( $old_nav_menu_locations );
return $new_nav_menu_locations;
}
$old_locations = array_keys( $old_nav_menu_locations );
// Map locations with the same slug.
foreach ( $registered_nav_menus as $location => $name ) {
if ( in_array( $location, $old_locations, true ) ) {
$new_nav_menu_locations[ $location ] = $old_nav_menu_locations[ $location ];
unset( $old_nav_menu_locations[ $location ] );
}
}
// If there are no old nav menu locations left, then we're done.
if ( empty( $old_nav_menu_locations ) ) {
return $new_nav_menu_locations;
}
/*
* If old and new theme both have locations that contain phrases
* from within the same group, make an educated guess and map it.
*/
$common_slug_groups = array(
array( 'header', 'main', 'navigation', 'primary', 'top' ),
array( 'bottom', 'footer', 'secondary', 'subsidiary' ),
);
// Go through each group...
foreach ( $common_slug_groups as $slug_group ) {
// ...and see if any of these slugs...
foreach ( $slug_group as $slug ) {
// ...and any of the new menu locations...
foreach ( $registered_nav_menus as $new_location => $name ) {
// ...actually match!
if ( false === stripos( $new_location, $slug ) && false === stripos( $slug, $new_location ) ) {
continue;
}
// Then see if any of the old locations...
foreach ( $old_nav_menu_locations as $location => $menu_id ) {
// ...and any slug in the same group...
foreach ( $slug_group as $slug ) {
// ... have a match as well.
if ( false === stripos( $location, $slug ) && false === stripos( $slug, $location ) ) {
continue;
}
// Make sure this location wasn't mapped and removed previously.
if ( ! empty( $old_nav_menu_locations[ $location ] ) ) {
// We have a match that can be mapped!
$new_nav_menu_locations[ $new_location ] = $old_nav_menu_locations[ $location ];
// Remove the mapped location so it can't be mapped again.
unset( $old_nav_menu_locations[ $location ] );
// Go back and check the next new menu location.
continue 3;
}
} // endforeach ( $slug_group as $slug )
} // endforeach ( $old_nav_menu_locations as $location => $menu_id )
} // endforeach foreach ( $registered_nav_menus as $new_location => $name )
} // endforeach ( $slug_group as $slug )
} // endforeach ( $common_slug_groups as $slug_group )
return $new_nav_menu_locations;
}

View File

@ -691,6 +691,7 @@ function switch_theme( $stylesheet ) {
}
$nav_menu_locations = get_theme_mod( 'nav_menu_locations' );
add_option( 'theme_switch_menu_locations', $nav_menu_locations );
if ( func_num_args() > 1 ) {
$stylesheet = func_get_arg( 1 );
@ -731,13 +732,6 @@ function switch_theme( $stylesheet ) {
if ( 'wp_ajax_customize_save' === current_action() ) {
remove_theme_mod( 'sidebars_widgets' );
}
if ( ! empty( $nav_menu_locations ) ) {
$nav_mods = get_theme_mod( 'nav_menu_locations' );
if ( empty( $nav_mods ) ) {
set_theme_mod( 'nav_menu_locations', $nav_menu_locations );
}
}
}
update_option( 'theme_switched', $old_theme->get_stylesheet() );

View File

@ -4,7 +4,7 @@
*
* @global string $wp_version
*/
$wp_version = '4.9-alpha-41236';
$wp_version = '4.9-alpha-41237';
/**
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.