diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php index 495e74b5..493e5a81 100644 --- a/php/class-customize-snapshot-manager.php +++ b/php/class-customize-snapshot-manager.php @@ -124,14 +124,6 @@ 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 @@ -163,8 +155,46 @@ public function __construct( Plugin $plugin ) { } } + /* + * Add WP_Customize_Nav_Menu component hooks which were short-circuited in 4.5 (r36611 for #35895). + * See https://core.trac.wordpress.org/ticket/35895 + */ + if ( isset( $this->customize_manager->nav_menus ) && ! current_user_can( 'edit_theme_options' ) ) { + $hooks = array( + 'customize_register' => array( + 'callback' => array( $this->customize_manager->nav_menus, 'customize_register' ), + 'priority' => 11, + ), + 'customize_dynamic_setting_args' => array( + 'callback' => array( $this->customize_manager->nav_menus, 'filter_dynamic_setting_args' ), + 'priority' => 10, + ), + 'customize_dynamic_setting_class' => array( + 'callback' => array( $this->customize_manager->nav_menus, 'filter_dynamic_setting_class' ), + 'priority' => 10, + ), + 'wp_nav_menu_args' => array( + 'callback' => array( $this->customize_manager->nav_menus, 'filter_wp_nav_menu_args' ), + 'priority' => 1000, + ), + 'wp_nav_menu' => array( + 'callback' => array( $this->customize_manager->nav_menus, 'filter_wp_nav_menu' ), + 'priority' => 10, + ), + ); + foreach ( $hooks as $hook_name => $hook_args ) { + // Note that add_action()/has_action() are just aliases for add_filter()/has_filter(). + if ( ! has_filter( $hook_name, $hook_args['callback'] ) ) { + add_filter( $hook_name, $hook_args['callback'], $hook_args['priority'], PHP_INT_MAX ); + } + } + } + // Preview a Snapshot. add_action( 'after_setup_theme', array( $this, 'set_post_values' ), 1 ); + if ( isset( $this->customize_manager->nav_menus ) ) { + add_action( 'customize_register', array( $this, 'preview_early_nav_menus_in_customizer' ), 9 ); + } add_action( 'wp_loaded', array( $this, 'preview' ) ); /* @@ -388,235 +418,6 @@ 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 ) { - 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-?\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 ) { - 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-?\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 ); - } - } - } - - 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. * @@ -1154,4 +955,33 @@ public function preview() { } } } + + /** + * Preview nav menu settings early so that the sections and controls for snapshot values will be added properly. + * + * This must happen at `customize_register` priority prior to 11 which is when `WP_Customize_Nav_Menus::customize_register()` runs. + * This is only relevant when accessing the Customizer app (customize.php), as this is where sections/controls matter. + * + * @see \WP_Customize_Nav_Menus::customize_register() + */ + public function preview_early_nav_menus_in_customizer() { + if ( ! is_admin() ) { + return; + } + + $this->customize_manager->add_dynamic_settings( array_keys( $this->snapshot()->data() ) ); + foreach ( $this->snapshot->settings() as $setting ) { + $is_nav_menu_setting = ( + $setting instanceof \WP_Customize_Nav_Menu_Setting + || + $setting instanceof \WP_Customize_Nav_Menu_Item_Setting + || + preg_match( '/^nav_menu_locations\[/', $setting->id ) + ); + if ( $is_nav_menu_setting ) { + $setting->preview(); + $setting->dirty = true; + } + } + } } diff --git a/php/class-customize-snapshot-nav-menu-section.php b/php/class-customize-snapshot-nav-menu-section.php deleted file mode 100644 index 5e07e8c2..00000000 --- a/php/class-customize-snapshot-nav-menu-section.php +++ /dev/null @@ -1,39 +0,0 @@ -id ) ); - - return $exported; - } -} diff --git a/readme.md b/readme.md index 9bcca55a..4270e025 100644 --- a/readme.md +++ b/readme.md @@ -6,12 +6,12 @@ Allow Customizer states to be drafted, and previewed with a private URL. **Contributors:** [westonruter](https://profiles.wordpress.org/westonruter), [valendesigns](https://profiles.wordpress.org/valendesigns), [xwp](https://profiles.wordpress.org/xwp), [newscorpau](https://profiles.wordpress.org/newscorpau) **Tags:** [customizer](https://wordpress.org/plugins/tags/customizer), [customize](https://wordpress.org/plugins/tags/customize), [snapshots](https://wordpress.org/plugins/tags/snapshots) -**Requires at least:** 4.5 +**Requires at least:** 4.6-beta2 **Tested up to:** trunk **Stable tag:** 0.4.0 **License:** [GPLv2 or later](http://www.gnu.org/licenses/gpl-2.0.html) -[![Build Status](https://travis-ci.org/xwp/wp-customize-snapshots.svg?branch=master)](https://travis-ci.org/xwp/wp-customize-snapshots) [![Coverage Status](https://coveralls.io/repos/xwp/wp-customize-snapshots/badge.svg?branch=master)](https://coveralls.io/github/xwp/wp-customize-snapshots) [![Built with Grunt](https://cdn.gruntjs.com/builtwith.svg)](http://gruntjs.com) [![devDependency Status](https://david-dm.org/xwp/wp-customize-snapshots/dev-status.svg)](https://david-dm.org/xwp/wp-customize-snapshots#info=devDependencies) +[![Build Status](https://travis-ci.org/xwp/wp-customize-snapshots.svg?branch=master)](https://travis-ci.org/xwp/wp-customize-snapshots) [![Coverage Status](https://coveralls.io/repos/xwp/wp-customize-snapshots/badge.svg?branch=master)](https://coveralls.io/github/xwp/wp-customize-snapshots) [![Built with Grunt](https://cdn.gruntjs.com/builtwith.png)](http://gruntjs.com) [![devDependency Status](https://david-dm.org/xwp/wp-customize-snapshots/dev-status.svg)](https://david-dm.org/xwp/wp-customize-snapshots#info=devDependencies) ## Description ## diff --git a/readme.txt b/readme.txt index b6191db9..97ce5866 100644 --- a/readme.txt +++ b/readme.txt @@ -1,6 +1,6 @@ === Customize Snapshots === Contributors: westonruter, valendesigns, xwp, newscorpau -Requires at least: 4.5 +Requires at least: 4.6-beta2 Tested up to: trunk Stable tag: 0.4.0 License: GPLv2 or later diff --git a/tests/php/test-class-customize-snapshot-manager.php b/tests/php/test-class-customize-snapshot-manager.php index 5e167c73..8b11e84f 100644 --- a/tests/php/test-class-customize-snapshot-manager.php +++ b/tests/php/test-class-customize-snapshot-manager.php @@ -15,12 +15,6 @@ class Test_Customize_Snapshot_Manager extends \WP_UnitTestCase { */ const UUID = '65aee1ff-af47-47df-9e14-9c69b3017cd3'; - /** - * Typical Snapshot menu ID. - * @type string - */ - const MENU_ID = '-1757032258044647400'; - /** * @var \WP_Customize_Manager */ @@ -55,8 +49,6 @@ function setUp() { $this->wp_customize->add_setting( 'foo', array( 'default' => 'foo_default' ) ); $this->wp_customize->add_setting( 'bar', array( 'default' => 'bar_default' ) ); - $this->wp_customize->add_setting( 'nav_menu_locations[foobar]', array( 'default' => '' ) ); - $this->wp_customize->add_setting( 'nav_menu['. self::MENU_ID . ']', array( 'default' => array() ) ); $this->manager = new Customize_Snapshot_Manager( $this->plugin ); $this->user_id = $this->factory->user->create( array( 'role' => 'administrator' ) ); @@ -171,192 +163,15 @@ function test_clean_current_url() { * @see Customize_Snapshot_Manager::create_post_type() */ function test_create_post_type() { - $pobj = get_post_type_object( Customize_Snapshot_Manager::POST_TYPE ); - $this->assertNotNull( $pobj ); - $this->assertEquals( Customize_Snapshot_Manager::POST_TYPE, $pobj->name ); + $post_type_object = get_post_type_object( Customize_Snapshot_Manager::POST_TYPE ); + $this->assertNotNull( $post_type_object ); + $this->assertEquals( Customize_Snapshot_Manager::POST_TYPE, $post_type_object->name ); // Test some defaults. $this->assertFalse( is_post_type_hierarchical( Customize_Snapshot_Manager::POST_TYPE ) ); $this->assertEquals( array(), get_object_taxonomies( Customize_Snapshot_Manager::POST_TYPE ) ); } - /** - * @see Customize_Snapshot_Manager::filter_theme_mod_nav_menu_locations() - */ - function test_filter_theme_mod_nav_menu_locations() { - wp_set_current_user( $this->user_id ); - $_POST = wp_slash( array( - 'nonce' => wp_create_nonce( 'save-customize_' . $this->wp_customize->get_stylesheet() ), - 'snapshot_uuid' => self::UUID, - 'snapshot_customized' => '{"nav_menu_locations[foobar]":{"value":"' . self::MENU_ID . '"}}', - ) ); - add_action( 'after_setup_theme', function() { - register_nav_menu( 'foobar', 'Custom Menu' ); - } ); - $this->do_customize_boot_actions( true ); - - $manager = new Customize_Snapshot_Manager( $this->plugin ); - $manager->set_snapshot_uuid(); - $manager->save_snapshot(); - $manager->snapshot()->is_preview = true; - - $menu_locations = $manager->filter_theme_mod_nav_menu_locations( array( 'foobar' => '' ) ); - $this->assertEquals( self::MENU_ID, $menu_locations['foobar'] ); - } - - /** - * @see Customize_Snapshot_Manager::filter_wp_get_nav_menus() - */ - function test_filter_wp_get_nav_menus() { - - $_POST = wp_slash( array( - 'nonce' => wp_create_nonce( 'save-customize_' . $this->wp_customize->get_stylesheet() ), - 'snapshot_uuid' => self::UUID, - 'snapshot_customized' => '{"nav_menu[' . self::MENU_ID . ']":{"value":{}}}', - ) ); - - wp_set_current_user( $this->user_id ); - $this->do_customize_boot_actions( true ); - - $manager = new Customize_Snapshot_Manager( $this->plugin ); - $manager->set_snapshot_uuid(); - $manager->save_snapshot(); - $manager->snapshot()->is_preview = true; - - $menus = array(); - $menus[] = $this->manager->get_nav_menu_object( self::MENU_ID, array() ); - $this->assertEquals( array_values( $menus ), $manager->filter_wp_get_nav_menus( array() ) ); - } - - /** - * @see Customize_Snapshot_Manager::filter_wp_get_nav_menu_items() - */ - function test_filter_wp_get_nav_menu_items() { - - $this->wp_customize->add_setting( 'nav_menu_item[' . self::MENU_ID . ']'); - - $_POST = wp_slash( array( - 'nonce' => wp_create_nonce( 'save-customize_' . $this->wp_customize->get_stylesheet() ), - 'snapshot_uuid' => self::UUID, - 'snapshot_customized' => '{"nav_menu_item[' . self::MENU_ID . ']":{"value":{"nav_menu_term_id":"' . - self::MENU_ID . '","status":"publish","position":"1","title":"","post_id":"' . self::MENU_ID . '"}}}', - ) ); - - wp_set_current_user( $this->user_id ); - $this->do_customize_boot_actions( true ); - - $manager = new Customize_Snapshot_Manager( $this->plugin ); - $manager->set_snapshot_uuid(); - $manager->save_snapshot(); - $manager->snapshot()->is_preview = true; - - $menu_items = array(); - $menu_items[] = $this->manager->value_as_wp_post_nav_menu_item( - (object) array( - 'post_id' => self::MENU_ID, - 'nav_menu_term_id' => self::MENU_ID, - 'status' => 'publish', - 'position' => 1, - 'title' => '', - ) - ); - - $menu_object = $this->manager->get_nav_menu_object( self::MENU_ID, array() ); - - $this->assertEquals( array_values( $menu_items ), $manager->filter_wp_get_nav_menu_items( array(), $menu_object, array() ) ); - - } - - /** - * @see Customize_Snapshot_Manager::filter_wp_get_nav_menu_object() - */ - function test_filter_wp_get_nav_menu_object() { - - $_POST = wp_slash( array( - 'nonce' => wp_create_nonce( 'save-customize_' . $this->wp_customize->get_stylesheet() ), - 'snapshot_uuid' => self::UUID, - 'snapshot_customized' => '{"nav_menu[' . self::MENU_ID . ']":{"value":{"name":"Custom Menu"}}}', - ) ); - - wp_set_current_user( $this->user_id ); - $this->do_customize_boot_actions( true ); - - $manager = new Customize_Snapshot_Manager( $this->plugin ); - $manager->set_snapshot_uuid(); - $manager->save_snapshot(); - $manager->snapshot()->is_preview = true; - - $menu_object = $this->manager->get_nav_menu_object( self::MENU_ID, array( 'name' => 'Custom Menu' ) ); - - $this->assertEquals( $menu_object, $manager->filter_wp_get_nav_menu_object( false, self::MENU_ID ) ); - } - - /** - * @see Customize_Snapshot_Manager::get_nav_menu_object() - */ - function test_get_nav_menu_object() { - - $values = array( - 'name' => 'Custom Menu', - 'term_id' => self::MENU_ID, - 'taxonomy' => 'nav_menu', - 'slug' => 'custom-menu', - ); - $menu_obj = new \WP_Term( (object) array_merge( $values, array( 'term_taxonomy_id' => self::MENU_ID ) ) ); - - $this->assertEquals( $menu_obj, $this->manager->get_nav_menu_object( self::MENU_ID, $values ) ); - } - - /** - * @see Customize_Snapshot_Manager::value_as_wp_post_nav_menu_item() - */ - function test_value_as_wp_post_nav_menu_item() { - $menu_item_values = array( - 'ID' => self::MENU_ID, - 'menu_order' => 1, - 'post_type' => 'nav_menu_item', - 'post_id' => self::MENU_ID, - 'title' => '', - 'db_id' => self::MENU_ID, - 'type_label' => 'Custom Link', - 'attr_title' => '', - 'description' => '', - ); - $post = new \WP_Post( (object) $menu_item_values ); - - $this->assertEquals( $post, $this->manager->value_as_wp_post_nav_menu_item( (object) array( - 'post_id' => self::MENU_ID, - 'nav_menu_term_id' => self::MENU_ID, - 'status' => 'publish', - 'position' => 1, - 'title' => '', - ) ) ); - } - - /** - * @see Customize_Snapshot_Manager::customize_register_nav_menus() - */ - function test_customize_register_nav_menus() { - $_POST = wp_slash( array( - 'nonce' => wp_create_nonce( 'save-customize_' . $this->wp_customize->get_stylesheet() ), - 'snapshot_uuid' => self::UUID, - 'snapshot_customized' => '{"nav_menu[' . self::MENU_ID . ']":{"value":{}}}', - ) ); - - wp_set_current_user( $this->user_id ); - $this->do_customize_boot_actions( true ); - - $manager = new Customize_Snapshot_Manager( $this->plugin ); - $manager->set_snapshot_uuid(); - $manager->save_snapshot(); - $manager->snapshot()->is_preview = true; - - $manager->customize_register_nav_menus(); - - $this->assertTrue( is_a( $this->wp_customize->get_section( 'nav_menu[' . self::MENU_ID . ']' ), 'CustomizeSnapshots\Customize_Snapshot_Nav_Menu_Section' ) ); - - } - /** * @see Customize_Snapshot_Manager::filter_bulk_actions() */