Skip to content
This repository has been archived by the owner on Dec 27, 2022. It is now read-only.

Support Customize Menus #55

Merged
merged 24 commits into from
Jul 6, 2016
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
bef6bc6
Fix menu locations bug
miina Jun 14, 2016
bfe83dc
Add menu filters. Not complete.
miina Jun 15, 2016
3b033d2
Fix phpcs issues
miina Jun 15, 2016
78affd1
Add isset check.
miina Jun 15, 2016
bf9938b
Fix the issue of negative ID proccesing by overriding the default pro…
miina Jun 16, 2016
9f0b918
Merge branch 'develop' into bugfix/saving-menus-issue_2
valendesigns Jun 28, 2016
4c73e3d
Rework compatiblity with nav menus
valendesigns Jun 29, 2016
390083c
Fix phpcs issues
valendesigns Jun 29, 2016
7d619f1
Fix tests
valendesigns Jun 29, 2016
726e194
Merge branch 'bugfix/saving-menus-issue_2' of https://github.com/xwp/…
valendesigns Jun 29, 2016
8ae0764
Ensure items are added to existing menus
valendesigns Jun 29, 2016
44d8d4a
Add some tests. Not complete.
miina Jun 29, 2016
b7176d3
Add more test coverage. Some still missing.
miina Jun 29, 2016
3488780
Add test coverage. Test for Customize_Snapshot_Nav_Menu_Section missing.
miina Jun 29, 2016
6393ddd
Unit tests for menu changes.
miina Jun 30, 2016
924c872
Remove nav menu code in favor of re-using methods in WP_Customize_Nav…
westonruter Jul 5, 2016
19bbcfa
Fix test_create_post_type for 4.6-beta
westonruter Jul 5, 2016
e42ac1a
Rename MENU_ID to NAV_MENU_ITEM_ID
westonruter Jul 5, 2016
2617a10
Remove now unused Customize_Snapshot_Nav_Menu_Section
westonruter Jul 5, 2016
310b365
Merge branch 'develop' of https://github.com/xwp/wp-customize-snapsho…
westonruter Jul 5, 2016
22504bd
Merge branch 'bugfix/saving-menus-issue_2' into bugfix/saving-menus-i…
westonruter Jul 5, 2016
bce3453
Preview nav menu settings early so that the sections and controls for…
westonruter Jul 6, 2016
adca567
Remove test setup code that is not used
westonruter Jul 6, 2016
d5e0bbc
Merge pull request #56 from xwp/bugfix/saving-menus-issue_3
westonruter Jul 6, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
237 changes: 237 additions & 0 deletions php/class-customize-snapshot-manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,14 @@ public function __construct( Plugin $plugin ) {
add_action( 'admin_bar_menu', array( $this, 'customize_menu' ), 41 );
add_action( 'customize_controls_print_footer_scripts', array( $this, 'render_templates' ) );

add_filter( 'theme_mod_nav_menu_locations', array( $this, 'filter_theme_mod_nav_menu_locations' ) );
add_filter( 'wp_get_nav_menus', array( $this, 'filter_wp_get_nav_menus' ) );
add_filter( 'wp_get_nav_menu_items', array( $this, 'filter_wp_get_nav_menu_items' ), 10, 3 );
add_filter( 'wp_get_nav_menu_object', array( $this, 'filter_wp_get_nav_menu_object' ), 10, 2 );

// Needs priority 12 since it has to happen after the default nav menus are registered.
add_action( 'customize_register', array( $this, 'customize_register_nav_menus' ), 12 );

/*
* Add WP_Customize_Widget component hooks which were short-circuited in 4.5 (r36611 for #35895).
* See https://core.trac.wordpress.org/ticket/35895
Expand Down Expand Up @@ -380,6 +388,235 @@ function suspend_kses_for_snapshot_revision_restore() {
} );
}

/**
* Filter for displaying the Snapshot menu location values.
*
* @param array $menu_locations Default menu locations.
* @return array Modified menu locations.
*/
public function filter_theme_mod_nav_menu_locations( $menu_locations ) {
if ( false === $this->snapshot->is_preview() ) {
return $menu_locations;
}

$values = $this->snapshot->values();
$locations = get_registered_nav_menus();

foreach ( $locations as $location => $name ) {
if ( isset( $values[ 'nav_menu_locations[' . $location . ']' ] ) ) {
$menu_locations[ $location ] = $values[ 'nav_menu_locations[' . $location . ']' ];
}
}

return $menu_locations;
}

/**
* Filter wp_get_nav_menus() to load Snapshot values.
*
* @see wp_get_nav_menus()
*
* @param array $menus Array of menus.
* @return array Modified array of menus.
*/
public function filter_wp_get_nav_menus( $menus ) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder why this is needed as opposed to what WP_Customize_Nav_Menu_Setting::preview() adds? https://github.com/xwp/wordpress-develop/blob/master/src/wp-includes/customize/class-wp-customize-nav-menu-setting.php#L204-L296

if ( false === $this->snapshot->is_preview() ) {
return $menus;
}

$values = $this->snapshot->values();
$removed = array();

foreach ( $values as $setting_id => $value ) {
if ( preg_match( '/^nav_menu\[(?P<id>-?\d+)\]$/', $setting_id, $matches ) ) {
if ( false !== $value ) {
$menus[] = $this->get_nav_menu_object( $matches['id'], $value );
} else {
$removed[] = intval( $matches['id'] );
}
}
}

if ( ! empty( $removed ) && ! empty( $menus ) ) {
foreach ( $menus as $key => $term ) {
if ( in_array( $term->term_id, $removed, true ) ) {
unset( $menus[ $key ] );
}
}
}

return array_values( $menus );
}

/**
* Filter wp_get_nav_menu_items() to load Snapshot values.
*
* @see wp_get_nav_menu_items()
*
* @param array $items An array of menu item post objects.
* @param object $menu The menu object.
* @param array $args An array of arguments used to retrieve menu item objects.
* @return array Array of menu items.
*/
function filter_wp_get_nav_menu_items( $items, $menu, $args ) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder why this is needed as opposed to WP_Customize_Nav_Menu_Item::preview() which adds its own filter handler of the same name? https://github.com/xwp/wordpress-develop/blob/266ffe1f35e577954817d453cd7b2d84778cb57c/src/wp-includes/customize/class-wp-customize-nav-menu-item-setting.php#L368-L480

Copy link
Contributor

@miina miina Jul 4, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue is that these filters are used only settings-specifically but the menus are initially loaded at this moment in WP_Customize_Nav_Menus:
https://github.com/xwp/wordpress-develop/blob/master/src/wp-includes/class-wp-customize-nav-menus.php#L510

Loading the Customize_Nav_Menu_Setting itself to use the filters would require being related to existing setting IDs which are unknown before loading the Snapshot data.

if ( false === $this->snapshot->is_preview() ) {
return $items;
}

$values = $this->snapshot->values();
$removed = array();

foreach ( $values as $setting_id => $value ) {
if ( preg_match( '/^nav_menu_item\[(?P<id>-?\d+)\]$/', $setting_id, $matches ) && false === $value ) {
$removed[] = intval( $matches['id'] );
}
}

if ( ! empty( $removed ) && ! empty( $items ) ) {
foreach ( $items as $key => $post ) {
if ( in_array( $post->ID, $removed, true ) ) {
unset( $items[ $key ] );
}
}
}

foreach ( $values as $setting_id => $item ) {
if ( preg_match( \WP_Customize_Nav_Menu_Item_Setting::ID_PATTERN, $setting_id, $matches ) ) {
if ( (int) $menu->term_id === (int) $item['nav_menu_term_id'] ) {
$item['post_id'] = intval( $matches['id'] );
$items[] = $this->value_as_wp_post_nav_menu_item( (object) $item );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we can re-use WP_Customize_Nav_Menu_Item_Setting::value_as_wp_post_nav_menu_item() here.

if ( preg_match( \WP_Customize_Nav_Menu_Item_Setting::ID_PATTERN, $setting_id, $matches ) ) {
    $wp_customize->register_dynamic_settings( array( $setting_id ) );
    $nav_menu_item_setting = $wp_customize->get_setting( $setting_id );
    if ( $nav_menu_item_setting && (int) $menu->term_id === (int) $item['nav_menu_term_id'] ) {
        $item['post_id'] = intval( $matches['id'] );
        $items[] = $nav_menu_item_setting->value_as_wp_post_nav_menu_item(); // <========
    }
}

}
}
}

return array_values( $items );
}

/**
* Filter wp_get_nav_menu_object() to load Snapshot values.
*
* @see wp_get_nav_menu_object()
*
* @param object|null $menu_obj Object returned by wp_get_nav_menu_object().
* @param string $menu_id ID of the nav_menu term. Requests by slug or name will be ignored.
* @return object|null New menu object or null.
*/
function filter_wp_get_nav_menu_object( $menu_obj, $menu_id ) {
if ( false === $this->snapshot->is_preview() ) {
return $menu_obj;
}

if ( false === $menu_obj && $menu_id < 0 ) {
$values = $this->snapshot->values();
if ( isset( $values[ 'nav_menu[' . $menu_id . ']' ] ) ) {
$menu_obj = $this->get_nav_menu_object( $menu_id, $values[ 'nav_menu[' . $menu_id . ']' ] );
}
}

return $menu_obj;
}

/**
* Build a nav menu object from a Snapshot value.
*
* @param int $menu_id Menu ID.
* @param array $value Menu value.
* @return \WP_Term
*/
public function get_nav_menu_object( $menu_id, $value ) {
$menu_obj = new \WP_Term( (object) $value );
$menu_obj->term_id = $menu_obj->term_taxonomy_id = $menu_id;
$menu_obj->taxonomy = 'nav_menu';
$menu_obj->slug = sanitize_title( $menu_obj->name );

return $menu_obj;
}

/**
* Get the value emulated into a WP_Post and set up as a nav_menu_item.
*
* @param object $item Snapshot nav menu item.
* @return WP_Post With wp_setup_nav_menu_item() applied.
*/
public function value_as_wp_post_nav_menu_item( $item ) {
unset( $item->nav_menu_term_id );

$item->post_status = $item->status;
unset( $item->status );

$item->post_type = 'nav_menu_item';
$item->menu_order = $item->position;
unset( $item->position );

if ( $item->title ) {
$item->post_title = $item->title;
}

$item->ID = $item->post_id;
$item->db_id = $item->post_id;
$post = new \WP_Post( (object) $item );

if ( empty( $post->post_author ) ) {
$post->post_author = get_current_user_id();
}

if ( ! isset( $post->type_label ) ) {
if ( 'post_type' === $post->type ) {
$object = get_post_type_object( $post->object );
if ( $object ) {
$post->type_label = $object->labels->singular_name;
} else {
$post->type_label = $post->object;
}
} elseif ( 'taxonomy' == $post->type ) {
$object = get_taxonomy( $post->object );
if ( $object ) {
$post->type_label = $object->labels->singular_name;
} else {
$post->type_label = $post->object;
}
} else {
$post->type_label = __( 'Custom Link', 'customize-snapshots' );
}
}

/** This filter is documented in wp-includes/nav-menu.php */
$post->attr_title = apply_filters( 'nav_menu_attr_title', $post->attr_title );

/** This filter is documented in wp-includes/nav-menu.php */
$post->description = apply_filters( 'nav_menu_description', wp_trim_words( $post->description, 200 ) );

/** This filter is documented in wp-includes/nav-menu.php */
$post = apply_filters( 'wp_setup_nav_menu_item', $post );

return $post;
}

/**
* Register nav menus found in a Snapshot.
*/
public function customize_register_nav_menus() {
if ( false === $this->snapshot->is_preview() ) {
return;
}

$menus = wp_get_nav_menus();

foreach ( $menus as $menu ) {
if ( $menu->term_id < 0 ) {

// Create a section for each menu.
$section_id = 'nav_menu[' . $menu->term_id . ']';
$this->customize_manager->remove_section( $section_id );
$this->customize_manager->add_section( new Customize_Snapshot_Nav_Menu_Section( $this->customize_manager, $section_id, array(
'title' => html_entity_decode( $menu->name, ENT_QUOTES, get_bloginfo( 'charset' ) ),
'priority' => 10,
'panel' => 'nav_menus',
) ) );
}
}
}

/**
* Remove edit bulk action for snapshots.
*
Expand Down
39 changes: 39 additions & 0 deletions php/class-customize-snapshot-nav-menu-section.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php
/**
* Customize Snapshot Nav Menu Section Class
*
* @package CustomizeSnapshots
*/

namespace CustomizeSnapshots;

/**
* Customize Snapshot Menu Section Class
*
* Custom section only needed in JS.
*
* @see \WP_Customize_Section
*/
class Customize_Snapshot_Nav_Menu_Section extends \WP_Customize_Section {

/**
* Control type.
*
* @access public
* @var string
*/
public $type = 'nav_menu';

/**
* Get section parameters for JS.
*
* @access public
* @return array Exported parameters.
*/
public function json() {
$exported = parent::json();
$exported['menu_id'] = intval( preg_replace( '/^nav_menu\[(-?\d+)\]/', '$1', $this->id ) );

return $exported;
}
}
Loading