@@ -124,14 +124,6 @@ public function __construct( Plugin $plugin ) {
124124 add_action ( 'admin_bar_menu ' , array ( $ this , 'customize_menu ' ), 41 );
125125 add_action ( 'customize_controls_print_footer_scripts ' , array ( $ this , 'render_templates ' ) );
126126
127- add_filter ( 'theme_mod_nav_menu_locations ' , array ( $ this , 'filter_theme_mod_nav_menu_locations ' ) );
128- add_filter ( 'wp_get_nav_menus ' , array ( $ this , 'filter_wp_get_nav_menus ' ) );
129- add_filter ( 'wp_get_nav_menu_items ' , array ( $ this , 'filter_wp_get_nav_menu_items ' ), 10 , 3 );
130- add_filter ( 'wp_get_nav_menu_object ' , array ( $ this , 'filter_wp_get_nav_menu_object ' ), 10 , 2 );
131-
132- // Needs priority 12 since it has to happen after the default nav menus are registered.
133- add_action ( 'customize_register ' , array ( $ this , 'customize_register_nav_menus ' ), 12 );
134-
135127 /*
136128 * Add WP_Customize_Widget component hooks which were short-circuited in 4.5 (r36611 for #35895).
137129 * See https://core.trac.wordpress.org/ticket/35895
@@ -163,8 +155,46 @@ public function __construct( Plugin $plugin ) {
163155 }
164156 }
165157
158+ /*
159+ * Add WP_Customize_Nav_Menu component hooks which were short-circuited in 4.5 (r36611 for #35895).
160+ * See https://core.trac.wordpress.org/ticket/35895
161+ */
162+ if ( isset ( $ this ->customize_manager ->nav_menus ) && ! current_user_can ( 'edit_theme_options ' ) ) {
163+ $ hooks = array (
164+ 'customize_register ' => array (
165+ 'callback ' => array ( $ this ->customize_manager ->nav_menus , 'customize_register ' ),
166+ 'priority ' => 11 ,
167+ ),
168+ 'customize_dynamic_setting_args ' => array (
169+ 'callback ' => array ( $ this ->customize_manager ->nav_menus , 'filter_dynamic_setting_args ' ),
170+ 'priority ' => 10 ,
171+ ),
172+ 'customize_dynamic_setting_class ' => array (
173+ 'callback ' => array ( $ this ->customize_manager ->nav_menus , 'filter_dynamic_setting_class ' ),
174+ 'priority ' => 10 ,
175+ ),
176+ 'wp_nav_menu_args ' => array (
177+ 'callback ' => array ( $ this ->customize_manager ->nav_menus , 'filter_wp_nav_menu_args ' ),
178+ 'priority ' => 1000 ,
179+ ),
180+ 'wp_nav_menu ' => array (
181+ 'callback ' => array ( $ this ->customize_manager ->nav_menus , 'filter_wp_nav_menu ' ),
182+ 'priority ' => 10 ,
183+ ),
184+ );
185+ foreach ( $ hooks as $ hook_name => $ hook_args ) {
186+ // Note that add_action()/has_action() are just aliases for add_filter()/has_filter().
187+ if ( ! has_filter ( $ hook_name , $ hook_args ['callback ' ] ) ) {
188+ add_filter ( $ hook_name , $ hook_args ['callback ' ], $ hook_args ['priority ' ], PHP_INT_MAX );
189+ }
190+ }
191+ }
192+
166193 // Preview a Snapshot.
167194 add_action ( 'after_setup_theme ' , array ( $ this , 'set_post_values ' ), 1 );
195+ if ( isset ( $ this ->customize_manager ->nav_menus ) ) {
196+ add_action ( 'customize_register ' , array ( $ this , 'preview_early_nav_menus_in_customizer ' ), 9 );
197+ }
168198 add_action ( 'wp_loaded ' , array ( $ this , 'preview ' ) );
169199
170200 /*
@@ -388,235 +418,6 @@ function suspend_kses_for_snapshot_revision_restore() {
388418 } );
389419 }
390420
391- /**
392- * Filter for displaying the Snapshot menu location values.
393- *
394- * @param array $menu_locations Default menu locations.
395- * @return array Modified menu locations.
396- */
397- public function filter_theme_mod_nav_menu_locations ( $ menu_locations ) {
398- if ( false === $ this ->snapshot ->is_preview () ) {
399- return $ menu_locations ;
400- }
401-
402- $ values = $ this ->snapshot ->values ();
403- $ locations = get_registered_nav_menus ();
404-
405- foreach ( $ locations as $ location => $ name ) {
406- if ( isset ( $ values [ 'nav_menu_locations[ ' . $ location . '] ' ] ) ) {
407- $ menu_locations [ $ location ] = $ values [ 'nav_menu_locations[ ' . $ location . '] ' ];
408- }
409- }
410-
411- return $ menu_locations ;
412- }
413-
414- /**
415- * Filter wp_get_nav_menus() to load Snapshot values.
416- *
417- * @see wp_get_nav_menus()
418- *
419- * @param array $menus Array of menus.
420- * @return array Modified array of menus.
421- */
422- public function filter_wp_get_nav_menus ( $ menus ) {
423- if ( false === $ this ->snapshot ->is_preview () ) {
424- return $ menus ;
425- }
426-
427- $ values = $ this ->snapshot ->values ();
428- $ removed = array ();
429-
430- foreach ( $ values as $ setting_id => $ value ) {
431- if ( preg_match ( '/^nav_menu\[(?P<id>-?\d+)\]$/ ' , $ setting_id , $ matches ) ) {
432- if ( false !== $ value ) {
433- $ menus [] = $ this ->get_nav_menu_object ( $ matches ['id ' ], $ value );
434- } else {
435- $ removed [] = intval ( $ matches ['id ' ] );
436- }
437- }
438- }
439-
440- if ( ! empty ( $ removed ) && ! empty ( $ menus ) ) {
441- foreach ( $ menus as $ key => $ term ) {
442- if ( in_array ( $ term ->term_id , $ removed , true ) ) {
443- unset( $ menus [ $ key ] );
444- }
445- }
446- }
447-
448- return array_values ( $ menus );
449- }
450-
451- /**
452- * Filter wp_get_nav_menu_items() to load Snapshot values.
453- *
454- * @see wp_get_nav_menu_items()
455- *
456- * @param array $items An array of menu item post objects.
457- * @param object $menu The menu object.
458- * @param array $args An array of arguments used to retrieve menu item objects.
459- * @return array Array of menu items.
460- */
461- function filter_wp_get_nav_menu_items ( $ items , $ menu , $ args ) {
462- if ( false === $ this ->snapshot ->is_preview () ) {
463- return $ items ;
464- }
465-
466- $ values = $ this ->snapshot ->values ();
467- $ removed = array ();
468-
469- foreach ( $ values as $ setting_id => $ value ) {
470- if ( preg_match ( '/^nav_menu_item\[(?P<id>-?\d+)\]$/ ' , $ setting_id , $ matches ) && false === $ value ) {
471- $ removed [] = intval ( $ matches ['id ' ] );
472- }
473- }
474-
475- if ( ! empty ( $ removed ) && ! empty ( $ items ) ) {
476- foreach ( $ items as $ key => $ post ) {
477- if ( in_array ( $ post ->ID , $ removed , true ) ) {
478- unset( $ items [ $ key ] );
479- }
480- }
481- }
482-
483- foreach ( $ values as $ setting_id => $ item ) {
484- if ( preg_match ( \WP_Customize_Nav_Menu_Item_Setting::ID_PATTERN , $ setting_id , $ matches ) ) {
485- if ( (int ) $ menu ->term_id === (int ) $ item ['nav_menu_term_id ' ] ) {
486- $ item ['post_id ' ] = intval ( $ matches ['id ' ] );
487- $ items [] = $ this ->value_as_wp_post_nav_menu_item ( (object ) $ item );
488- }
489- }
490- }
491-
492- return array_values ( $ items );
493- }
494-
495- /**
496- * Filter wp_get_nav_menu_object() to load Snapshot values.
497- *
498- * @see wp_get_nav_menu_object()
499- *
500- * @param object|null $menu_obj Object returned by wp_get_nav_menu_object().
501- * @param string $menu_id ID of the nav_menu term. Requests by slug or name will be ignored.
502- * @return object|null New menu object or null.
503- */
504- function filter_wp_get_nav_menu_object ( $ menu_obj , $ menu_id ) {
505- if ( false === $ this ->snapshot ->is_preview () ) {
506- return $ menu_obj ;
507- }
508-
509- if ( false === $ menu_obj && $ menu_id < 0 ) {
510- $ values = $ this ->snapshot ->values ();
511- if ( isset ( $ values [ 'nav_menu[ ' . $ menu_id . '] ' ] ) ) {
512- $ menu_obj = $ this ->get_nav_menu_object ( $ menu_id , $ values [ 'nav_menu[ ' . $ menu_id . '] ' ] );
513- }
514- }
515-
516- return $ menu_obj ;
517- }
518-
519- /**
520- * Build a nav menu object from a Snapshot value.
521- *
522- * @param int $menu_id Menu ID.
523- * @param array $value Menu value.
524- * @return \WP_Term
525- */
526- public function get_nav_menu_object ( $ menu_id , $ value ) {
527- $ menu_obj = new \WP_Term ( (object ) $ value );
528- $ menu_obj ->term_id = $ menu_obj ->term_taxonomy_id = $ menu_id ;
529- $ menu_obj ->taxonomy = 'nav_menu ' ;
530- $ menu_obj ->slug = sanitize_title ( $ menu_obj ->name );
531-
532- return $ menu_obj ;
533- }
534-
535- /**
536- * Get the value emulated into a WP_Post and set up as a nav_menu_item.
537- *
538- * @param object $item Snapshot nav menu item.
539- * @return WP_Post With wp_setup_nav_menu_item() applied.
540- */
541- public function value_as_wp_post_nav_menu_item ( $ item ) {
542- unset( $ item ->nav_menu_term_id );
543-
544- $ item ->post_status = $ item ->status ;
545- unset( $ item ->status );
546-
547- $ item ->post_type = 'nav_menu_item ' ;
548- $ item ->menu_order = $ item ->position ;
549- unset( $ item ->position );
550-
551- if ( $ item ->title ) {
552- $ item ->post_title = $ item ->title ;
553- }
554-
555- $ item ->ID = $ item ->post_id ;
556- $ item ->db_id = $ item ->post_id ;
557- $ post = new \WP_Post ( (object ) $ item );
558-
559- if ( empty ( $ post ->post_author ) ) {
560- $ post ->post_author = get_current_user_id ();
561- }
562-
563- if ( ! isset ( $ post ->type_label ) ) {
564- if ( 'post_type ' === $ post ->type ) {
565- $ object = get_post_type_object ( $ post ->object );
566- if ( $ object ) {
567- $ post ->type_label = $ object ->labels ->singular_name ;
568- } else {
569- $ post ->type_label = $ post ->object ;
570- }
571- } elseif ( 'taxonomy ' == $ post ->type ) {
572- $ object = get_taxonomy ( $ post ->object );
573- if ( $ object ) {
574- $ post ->type_label = $ object ->labels ->singular_name ;
575- } else {
576- $ post ->type_label = $ post ->object ;
577- }
578- } else {
579- $ post ->type_label = __ ( 'Custom Link ' , 'customize-snapshots ' );
580- }
581- }
582-
583- /** This filter is documented in wp-includes/nav-menu.php */
584- $ post ->attr_title = apply_filters ( 'nav_menu_attr_title ' , $ post ->attr_title );
585-
586- /** This filter is documented in wp-includes/nav-menu.php */
587- $ post ->description = apply_filters ( 'nav_menu_description ' , wp_trim_words ( $ post ->description , 200 ) );
588-
589- /** This filter is documented in wp-includes/nav-menu.php */
590- $ post = apply_filters ( 'wp_setup_nav_menu_item ' , $ post );
591-
592- return $ post ;
593- }
594-
595- /**
596- * Register nav menus found in a Snapshot.
597- */
598- public function customize_register_nav_menus () {
599- if ( false === $ this ->snapshot ->is_preview () ) {
600- return ;
601- }
602-
603- $ menus = wp_get_nav_menus ();
604-
605- foreach ( $ menus as $ menu ) {
606- if ( $ menu ->term_id < 0 ) {
607-
608- // Create a section for each menu.
609- $ section_id = 'nav_menu[ ' . $ menu ->term_id . '] ' ;
610- $ this ->customize_manager ->remove_section ( $ section_id );
611- $ this ->customize_manager ->add_section ( new Customize_Snapshot_Nav_Menu_Section ( $ this ->customize_manager , $ section_id , array (
612- 'title ' => html_entity_decode ( $ menu ->name , ENT_QUOTES , get_bloginfo ( 'charset ' ) ),
613- 'priority ' => 10 ,
614- 'panel ' => 'nav_menus ' ,
615- ) ) );
616- }
617- }
618- }
619-
620421 /**
621422 * Remove edit bulk action for snapshots.
622423 *
@@ -1154,4 +955,33 @@ public function preview() {
1154955 }
1155956 }
1156957 }
958+
959+ /**
960+ * Preview nav menu settings early so that the sections and controls for snapshot values will be added properly.
961+ *
962+ * This must happen at `customize_register` priority prior to 11 which is when `WP_Customize_Nav_Menus::customize_register()` runs.
963+ * This is only relevant when accessing the Customizer app (customize.php), as this is where sections/controls matter.
964+ *
965+ * @see \WP_Customize_Nav_Menus::customize_register()
966+ */
967+ public function preview_early_nav_menus_in_customizer () {
968+ if ( ! is_admin () ) {
969+ return ;
970+ }
971+
972+ $ this ->customize_manager ->add_dynamic_settings ( array_keys ( $ this ->snapshot ()->data () ) );
973+ foreach ( $ this ->snapshot ->settings () as $ setting ) {
974+ $ is_nav_menu_setting = (
975+ $ setting instanceof \WP_Customize_Nav_Menu_Setting
976+ ||
977+ $ setting instanceof \WP_Customize_Nav_Menu_Item_Setting
978+ ||
979+ preg_match ( '/^nav_menu_locations\[/ ' , $ setting ->id )
980+ );
981+ if ( $ is_nav_menu_setting ) {
982+ $ setting ->preview ();
983+ $ setting ->dirty = true ;
984+ }
985+ }
986+ }
1157987}
0 commit comments