run:R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
23.8 KB
2026-03-11 16:18:51
R W Run
7.8 KB
2026-03-11 16:18:52
R W Run
36.1 KB
2026-03-11 16:18:51
R W Run
11.9 KB
2026-03-11 16:18:52
R W Run
18.94 KB
2026-03-11 16:18:52
R W Run
7.35 KB
2026-03-11 16:18:52
R W Run
28.6 KB
2026-03-11 16:18:51
R W Run
316 By
2026-03-11 16:18:51
R W Run
12.9 KB
2026-03-11 16:18:51
R W Run
61.02 KB
2026-03-11 16:18:52
R W Run
15 KB
2026-03-11 16:18:51
R W Run
112.05 KB
2026-03-11 16:18:51
R W Run
12.47 KB
2026-03-11 16:18:51
R W Run
15.07 KB
2026-03-11 16:18:52
R W Run
9.84 KB
2026-03-11 16:18:52
R W Run
13.17 KB
2026-03-11 16:18:52
R W Run
33.83 KB
2026-03-11 16:18:51
R W Run
42.63 KB
2026-03-11 16:18:51
R W Run
55.71 KB
2026-03-11 16:18:52
R W Run
12.53 KB
2026-03-11 16:18:51
R W Run
2.55 KB
2026-03-11 16:18:52
R W Run
28.92 KB
2026-03-11 16:18:52
R W Run
539 By
2026-03-11 16:18:51
R W Run
367 By
2026-03-11 16:18:52
R W Run
42.65 KB
2026-03-11 16:18:51
R W Run
401 By
2026-03-11 16:18:51
R W Run
6.61 KB
2026-03-11 16:18:51
R W Run
664 By
2026-03-11 16:18:52
R W Run
20.63 KB
2026-03-11 16:18:51
R W Run
2.18 KB
2026-03-11 16:18:52
R W Run
453 By
2026-03-11 16:18:52
R W Run
457 By
2026-03-11 16:18:51
R W Run
36.83 KB
2026-03-11 16:18:52
R W Run
2.41 KB
2026-03-11 16:18:52
R W Run
8.28 KB
2026-03-11 16:18:51
R W Run
13.89 KB
2026-03-11 16:18:51
R W Run
11.76 KB
2026-03-11 16:18:51
R W Run
2.65 KB
2026-03-11 16:18:51
R W Run
7.43 KB
2026-03-11 16:18:51
R W Run
17.46 KB
2026-03-11 16:18:51
R W Run
5.14 KB
2026-03-11 16:18:52
R W Run
16.7 KB
2026-03-11 16:18:51
R W Run
8.28 KB
2026-03-11 16:18:52
R W Run
2.92 KB
2026-03-11 16:18:52
R W Run
1.32 KB
2026-03-11 16:18:51
R W Run
4.6 KB
2026-03-11 16:18:52
R W Run
11.62 KB
2026-03-11 16:18:52
R W Run
2.5 KB
2026-03-11 16:18:51
R W Run
1.97 KB
2026-03-11 16:18:51
R W Run
11.25 KB
2026-03-11 16:18:52
R W Run
5.32 KB
2026-03-11 16:18:51
R W Run
10.99 KB
2026-03-11 16:18:52
R W Run
68.32 KB
2026-03-11 16:18:51
R W Run
6.34 KB
2026-03-11 16:18:51
R W Run
5.49 KB
2026-03-11 16:18:51
R W Run
1.99 KB
2026-03-11 16:18:52
R W Run
7.02 KB
2026-03-11 16:18:51
R W Run
4.91 KB
2026-03-11 16:18:52
R W Run
16.86 KB
2026-03-11 16:18:51
R W Run
24.23 KB
2026-03-11 16:18:51
R W Run
3.97 KB
2026-03-11 16:18:51
R W Run
47.66 KB
2026-03-11 16:18:51
R W Run
9.22 KB
2026-03-11 16:18:51
R W Run
25.51 KB
2026-03-11 16:18:51
R W Run
198.38 KB
2026-03-11 16:18:52
R W Run
56.65 KB
2026-03-11 16:18:51
R W Run
10.46 KB
2026-03-11 16:18:51
R W Run
10.95 KB
2026-03-11 16:18:52
R W Run
29.26 KB
2026-03-11 16:18:51
R W Run
70.91 KB
2026-03-11 16:18:52
R W Run
35.3 KB
2026-03-11 16:18:52
R W Run
16.61 KB
2026-03-11 16:18:52
R W Run
2.57 KB
2026-03-11 16:18:52
R W Run
39.83 KB
2026-03-11 16:18:51
R W Run
70.64 KB
2026-03-11 16:18:51
R W Run
15.56 KB
2026-03-11 16:18:52
R W Run
7.33 KB
2026-03-11 16:18:52
R W Run
253 By
2026-03-11 16:18:51
R W Run
7.96 KB
2026-03-11 16:18:52
R W Run
3.23 KB
2026-03-11 16:18:52
R W Run
969 By
2026-03-11 16:18:52
R W Run
16.28 KB
2026-03-11 16:18:51
R W Run
7.22 KB
2026-03-11 16:18:51
R W Run
12.95 KB
2026-03-11 16:18:51
R W Run
6.53 KB
2026-03-11 16:18:51
R W Run
3.42 KB
2026-03-11 16:18:52
R W Run
5.84 KB
2026-03-11 16:18:51
R W Run
1.97 KB
2026-03-11 16:18:51
R W Run
4.3 KB
2026-03-11 16:18:52
R W Run
2.91 KB
2026-03-11 16:18:51
R W Run
16.46 KB
2026-03-11 16:18:52
R W Run
40.6 KB
2026-03-11 16:18:51
R W Run
20.22 KB
2026-03-11 16:18:51
R W Run
36.11 KB
2026-03-11 16:18:52
R W Run
17.01 KB
2026-03-11 16:18:51
R W Run
7.27 KB
2026-03-11 16:18:52
R W Run
6.62 KB
2026-03-11 16:18:52
R W Run
16.49 KB
2026-03-11 16:18:52
R W Run
1.79 KB
2026-03-11 16:18:52
R W Run
29.82 KB
2026-03-11 16:18:51
R W Run
6.67 KB
2026-03-11 16:18:52
R W Run
8.98 KB
2026-03-11 16:18:52
R W Run
19.42 KB
2026-03-11 16:18:51
R W Run
12.01 KB
2026-03-11 16:18:51
R W Run
17.11 KB
2026-03-11 16:18:51
R W Run
6.74 KB
2026-03-11 16:18:52
R W Run
30.93 KB
2026-03-11 16:18:51
R W Run
4.99 KB
2026-03-11 16:18:51
R W Run
4.25 KB
2026-03-11 16:18:51
R W Run
24.72 KB
2026-03-11 16:18:51
R W Run
29.96 KB
2026-03-11 16:18:52
R W Run
6.41 KB
2026-03-11 16:18:51
R W Run
160 KB
2026-03-11 16:18:51
R W Run
6.72 KB
2026-03-11 16:18:52
R W Run
10.92 KB
2026-03-11 16:18:51
R W Run
4.77 KB
2026-03-11 16:18:51
R W Run
3.38 KB
2026-03-11 16:18:51
R W Run
11.18 KB
2026-03-11 16:18:51
R W Run
62.19 KB
2026-03-11 16:18:51
R W Run
2.46 KB
2026-03-11 16:18:51
R W Run
9.17 KB
2026-03-11 16:18:51
R W Run
32.15 KB
2026-03-11 16:18:51
R W Run
34.05 KB
2026-03-11 16:18:52
R W Run
7.15 KB
2026-03-11 16:18:51
R W Run
3.47 KB
2026-03-11 16:18:52
R W Run
1.87 KB
2026-03-11 16:18:52
R W Run
30.91 KB
2026-03-11 16:18:51
R W Run
7.29 KB
2026-03-11 16:18:52
R W Run
7.35 KB
2026-03-11 16:18:51
R W Run
12.54 KB
2026-03-11 16:18:51
R W Run
19.12 KB
2026-03-11 16:18:51
R W Run
18.12 KB
2026-03-11 16:18:52
R W Run
39.99 KB
2026-03-11 16:18:52
R W Run
5.17 KB
2026-03-11 16:18:52
R W Run
979 By
2026-03-11 16:18:51
R W Run
18.44 KB
2026-03-11 16:18:52
R W Run
10.24 KB
2026-03-11 16:18:51
R W Run
1.77 KB
2026-03-11 16:18:52
R W Run
34.9 KB
2026-03-11 16:18:51
R W Run
7.19 KB
2026-03-11 16:18:52
R W Run
160.5 KB
2026-03-11 16:18:51
R W Run
64.27 KB
2026-03-11 16:18:51
R W Run
27.95 KB
2026-03-11 16:18:51
R W Run
4.69 KB
2026-03-11 16:18:51
R W Run
2.94 KB
2026-03-11 16:18:51
R W Run
43.13 KB
2026-03-11 16:18:52
R W Run
2.25 KB
2026-03-11 16:18:52
R W Run
22.5 KB
2026-03-11 16:18:51
R W Run
13.01 KB
2026-03-11 16:18:52
R W Run
3.27 KB
2026-03-11 16:18:51
R W Run
18 KB
2026-03-11 16:18:51
R W Run
210.4 KB
2026-03-11 16:18:52
R W Run
25.86 KB
2026-03-11 16:18:52
R W Run
115.85 KB
2026-03-11 16:18:51
R W Run
373 By
2026-03-11 16:18:52
R W Run
343 By
2026-03-11 16:18:52
R W Run
338 By
2026-03-11 16:18:51
R W Run
100.73 KB
2026-03-11 16:18:52
R W Run
130.93 KB
2026-03-11 16:18:51
R W Run
19.1 KB
2026-03-11 16:18:51
R W Run
17.41 KB
2026-03-11 16:18:52
R W Run
41.98 KB
2026-03-11 16:18:52
R W Run
400 By
2026-03-11 16:18:52
R W Run
11.1 KB
2026-03-11 16:18:52
R W Run
37.02 KB
2026-03-11 16:18:51
R W Run
2.24 KB
2026-03-11 16:18:51
R W Run
188.13 KB
2026-03-11 16:18:51
R W Run
338 By
2026-03-11 16:18:51
R W Run
38 KB
2026-03-11 16:18:51
R W Run
4.02 KB
2026-03-11 16:18:52
R W Run
5.38 KB
2026-03-11 16:18:51
R W Run
3.05 KB
2026-03-11 16:18:52
R W Run
2.61 KB
2026-03-11 16:18:51
R W Run
1.16 KB
2026-03-11 16:18:52
R W Run
4.04 KB
2026-03-11 16:18:51
R W Run
3.71 KB
2026-03-11 16:18:51
R W Run
24.6 KB
2026-03-11 16:18:51
R W Run
9.56 KB
2026-03-11 16:18:51
R W Run
346.43 KB
2026-03-11 16:18:52
R W Run
281.84 KB
2026-03-11 16:18:52
R W Run
14.95 KB
2026-03-11 16:18:51
R W Run
8.44 KB
2026-03-11 16:18:52
R W Run
168.95 KB
2026-03-11 16:18:52
R W Run
20.71 KB
2026-03-11 16:18:52
R W Run
25.27 KB
2026-03-11 16:18:51
R W Run
5.72 KB
2026-03-11 16:18:51
R W Run
4.63 KB
2026-03-11 16:18:52
R W Run
81.73 KB
2026-03-11 16:18:51
R W Run
67.18 KB
2026-03-11 16:18:51
R W Run
156.36 KB
2026-03-11 16:18:52
R W Run
55.19 KB
2026-03-11 16:18:51
R W Run
162 By
2026-03-11 16:18:51
R W Run
61.72 KB
2026-03-11 16:18:51
R W Run
216.06 KB
2026-03-11 16:18:52
R W Run
65.09 KB
2026-03-11 16:18:51
R W Run
25.24 KB
2026-03-11 16:18:52
R W Run
4.81 KB
2026-03-11 16:18:51
R W Run
6.48 KB
2026-03-11 16:18:52
R W Run
21.25 KB
2026-03-11 16:18:51
R W Run
2.79 KB
2026-03-11 16:18:52
R W Run
89.69 KB
2026-03-11 16:18:52
R W Run
19.42 KB
2026-03-11 16:18:52
R W Run
3.69 KB
2026-03-11 16:18:52
R W Run
4.11 KB
2026-03-11 16:18:51
R W Run
40.74 KB
2026-03-11 16:18:51
R W Run
25.38 KB
2026-03-11 16:18:51
R W Run
43.31 KB
2026-03-11 16:18:52
R W Run
102.57 KB
2026-03-11 16:18:52
R W Run
6.18 KB
2026-03-11 16:18:51
R W Run
124.47 KB
2026-03-11 16:18:52
R W Run
35.65 KB
2026-03-11 16:18:52
R W Run
6.94 KB
2026-03-11 16:18:52
R W Run
67.04 KB
2026-03-11 16:18:52
R W Run
10.62 KB
2026-03-11 16:18:51
R W Run
289.35 KB
2026-03-11 16:18:52
R W Run
36.23 KB
2026-03-11 16:18:51
R W Run
200 By
2026-03-11 16:18:52
R W Run
200 By
2026-03-11 16:18:52
R W Run
98.29 KB
2026-03-11 16:18:52
R W Run
30.02 KB
2026-03-11 16:18:52
R W Run
19.03 KB
2026-03-11 16:18:52
R W Run
5.06 KB
2026-03-11 16:18:52
R W Run
255 By
2026-03-11 16:18:51
R W Run
22.66 KB
2026-03-11 16:18:52
R W Run
154.63 KB
2026-03-11 16:18:51
R W Run
9.68 KB
2026-03-11 16:18:51
R W Run
258 By
2026-03-11 16:18:51
R W Run
23.49 KB
2026-03-11 16:18:51
R W Run
3.16 KB
2026-03-11 16:18:51
R W Run
8.4 KB
2026-03-11 16:18:52
R W Run
441 By
2026-03-11 16:18:51
R W Run
7.39 KB
2026-03-11 16:18:51
R W Run
173 KB
2026-03-11 16:18:52
R W Run
544 By
2026-03-11 16:18:52
R W Run
4.17 KB
2026-03-11 16:18:51
R W Run
35.97 KB
2026-03-11 16:18:52
R W Run
1.69 KB
2026-03-11 16:18:51
R W Run
2.84 KB
2026-03-11 16:18:52
R W Run
6.09 KB
2026-03-11 16:18:51
R W Run
8.71 KB
2026-03-11 16:18:51
R W Run
131.84 KB
2026-03-11 16:18:51
R W Run
37.45 KB
2026-03-11 16:18:51
R W Run
173.89 KB
2026-03-11 16:18:51
R W Run
7.09 KB
2026-03-11 16:18:51
R W Run
6.41 KB
2026-03-11 16:18:51
R W Run
1.08 KB
2026-03-11 16:18:51
R W Run
69.46 KB
2026-03-11 16:18:52
R W Run
445 By
2026-03-11 16:18:51
R W Run
799 By
2026-03-11 16:18:52
R W Run
error_log
📄class-wp-customize-nav-menus.php
1<?php
2/**
3 * WordPress Customize Nav Menus classes
4 *
5 * @package WordPress
6 * @subpackage Customize
7 * @since 4.3.0
8 */
9
10/**
11 * Customize Nav Menus class.
12 *
13 * Implements menu management in the Customizer.
14 *
15 * @since 4.3.0
16 *
17 * @see WP_Customize_Manager
18 */
19#[AllowDynamicProperties]
20final class WP_Customize_Nav_Menus {
21
22 /**
23 * WP_Customize_Manager instance.
24 *
25 * @since 4.3.0
26 * @var WP_Customize_Manager
27 */
28 public $manager;
29
30 /**
31 * Original nav menu locations before the theme was switched.
32 *
33 * @since 4.9.0
34 * @var array
35 */
36 protected $original_nav_menu_locations;
37
38 /**
39 * Constructor.
40 *
41 * @since 4.3.0
42 *
43 * @param WP_Customize_Manager $manager Customizer bootstrap instance.
44 */
45 public function __construct( $manager ) {
46 $this->manager = $manager;
47 $this->original_nav_menu_locations = get_nav_menu_locations();
48
49 // See https://github.com/xwp/wp-customize-snapshots/blob/962586659688a5b1fd9ae93618b7ce2d4e7a421c/php/class-customize-snapshot-manager.php#L469-L499
50 add_action( 'customize_register', array( $this, 'customize_register' ), 11 );
51 add_filter( 'customize_dynamic_setting_args', array( $this, 'filter_dynamic_setting_args' ), 10, 2 );
52 add_filter( 'customize_dynamic_setting_class', array( $this, 'filter_dynamic_setting_class' ), 10, 3 );
53 add_action( 'customize_save_nav_menus_created_posts', array( $this, 'save_nav_menus_created_posts' ) );
54
55 // Skip remaining hooks when the user can't manage nav menus anyway.
56 if ( ! current_user_can( 'edit_theme_options' ) ) {
57 return;
58 }
59
60 add_filter( 'customize_refresh_nonces', array( $this, 'filter_nonces' ) );
61 add_action( 'wp_ajax_load-available-menu-items-customizer', array( $this, 'ajax_load_available_items' ) );
62 add_action( 'wp_ajax_search-available-menu-items-customizer', array( $this, 'ajax_search_available_items' ) );
63 add_action( 'wp_ajax_customize-nav-menus-insert-auto-draft', array( $this, 'ajax_insert_auto_draft_post' ) );
64 add_action( 'customize_controls_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
65 add_action( 'customize_controls_print_footer_scripts', array( $this, 'print_templates' ) );
66 add_action( 'customize_controls_print_footer_scripts', array( $this, 'available_items_template' ) );
67 add_action( 'customize_preview_init', array( $this, 'customize_preview_init' ) );
68 add_action( 'customize_preview_init', array( $this, 'make_auto_draft_status_previewable' ) );
69
70 // Selective Refresh partials.
71 add_filter( 'customize_dynamic_partial_args', array( $this, 'customize_dynamic_partial_args' ), 10, 2 );
72 }
73
74 /**
75 * Adds a nonce for customizing menus.
76 *
77 * @since 4.5.0
78 *
79 * @param string[] $nonces Array of nonces.
80 * @return string[] Modified array of nonces.
81 */
82 public function filter_nonces( $nonces ) {
83 $nonces['customize-menus'] = wp_create_nonce( 'customize-menus' );
84 return $nonces;
85 }
86
87 /**
88 * Ajax handler for loading available menu items.
89 *
90 * @since 4.3.0
91 */
92 public function ajax_load_available_items() {
93 check_ajax_referer( 'customize-menus', 'customize-menus-nonce' );
94
95 if ( ! current_user_can( 'edit_theme_options' ) ) {
96 wp_die( -1 );
97 }
98
99 $all_items = array();
100 $item_types = array();
101 if ( isset( $_POST['item_types'] ) && is_array( $_POST['item_types'] ) ) {
102 $item_types = wp_unslash( $_POST['item_types'] );
103 } elseif ( isset( $_POST['type'] ) && isset( $_POST['object'] ) ) { // Back compat.
104 $item_types[] = array(
105 'type' => wp_unslash( $_POST['type'] ),
106 'object' => wp_unslash( $_POST['object'] ),
107 'page' => empty( $_POST['page'] ) ? 0 : absint( $_POST['page'] ),
108 );
109 } else {
110 wp_send_json_error( 'nav_menus_missing_type_or_object_parameter' );
111 }
112
113 foreach ( $item_types as $item_type ) {
114 if ( empty( $item_type['type'] ) || empty( $item_type['object'] ) ) {
115 wp_send_json_error( 'nav_menus_missing_type_or_object_parameter' );
116 }
117 $type = sanitize_key( $item_type['type'] );
118 $object = sanitize_key( $item_type['object'] );
119 $page = empty( $item_type['page'] ) ? 0 : absint( $item_type['page'] );
120 $items = $this->load_available_items_query( $type, $object, $page );
121 if ( is_wp_error( $items ) ) {
122 wp_send_json_error( $items->get_error_code() );
123 }
124 $all_items[ $item_type['type'] . ':' . $item_type['object'] ] = $items;
125 }
126
127 wp_send_json_success( array( 'items' => $all_items ) );
128 }
129
130 /**
131 * Performs the post_type and taxonomy queries for loading available menu items.
132 *
133 * @since 4.3.0
134 *
135 * @param string $object_type Optional. Accepts any custom object type and has built-in support for
136 * 'post_type' and 'taxonomy'. Default is 'post_type'.
137 * @param string $object_name Optional. Accepts any registered taxonomy or post type name. Default is 'page'.
138 * @param int $page Optional. The page number used to generate the query offset. Default is '0'.
139 * @return array|WP_Error An array of menu items on success, a WP_Error object on failure.
140 */
141 public function load_available_items_query( $object_type = 'post_type', $object_name = 'page', $page = 0 ) {
142 $items = array();
143
144 if ( 'post_type' === $object_type ) {
145 $post_type = get_post_type_object( $object_name );
146 if ( ! $post_type ) {
147 return new WP_Error( 'nav_menus_invalid_post_type' );
148 }
149
150 /*
151 * If we're dealing with pages, let's prioritize the Front Page,
152 * Posts Page and Privacy Policy Page at the top of the list.
153 */
154 $important_pages = array();
155 $suppress_page_ids = array();
156 if ( 0 === $page && 'page' === $object_name ) {
157 // Insert Front Page or custom "Home" link.
158 $front_page = 'page' === get_option( 'show_on_front' ) ? (int) get_option( 'page_on_front' ) : 0;
159 if ( ! empty( $front_page ) ) {
160 $front_page_obj = get_post( $front_page );
161 $important_pages[] = $front_page_obj;
162 $suppress_page_ids[] = $front_page_obj->ID;
163 } else {
164 // Add "Home" link. Treat as a page, but switch to custom on add.
165 $items[] = array(
166 'id' => 'home',
167 'title' => _x( 'Home', 'nav menu home label' ),
168 'type' => 'custom',
169 'type_label' => __( 'Custom Link' ),
170 'object' => '',
171 'url' => home_url(),
172 );
173 }
174
175 // Insert Posts Page.
176 $posts_page = 'page' === get_option( 'show_on_front' ) ? (int) get_option( 'page_for_posts' ) : 0;
177 if ( ! empty( $posts_page ) ) {
178 $posts_page_obj = get_post( $posts_page );
179 $important_pages[] = $posts_page_obj;
180 $suppress_page_ids[] = $posts_page_obj->ID;
181 }
182
183 // Insert Privacy Policy Page.
184 $privacy_policy_page_id = (int) get_option( 'wp_page_for_privacy_policy' );
185 if ( ! empty( $privacy_policy_page_id ) ) {
186 $privacy_policy_page = get_post( $privacy_policy_page_id );
187 if ( $privacy_policy_page instanceof WP_Post && 'publish' === $privacy_policy_page->post_status ) {
188 $important_pages[] = $privacy_policy_page;
189 $suppress_page_ids[] = $privacy_policy_page->ID;
190 }
191 }
192 } elseif ( 'post' !== $object_name && 0 === $page && $post_type->has_archive ) {
193 // Add a post type archive link.
194 $title = $post_type->labels->archives;
195 $items[] = array(
196 'id' => $object_name . '-archive',
197 'title' => $title,
198 'original_title' => $title,
199 'type' => 'post_type_archive',
200 'type_label' => __( 'Post Type Archive' ),
201 'object' => $object_name,
202 'url' => get_post_type_archive_link( $object_name ),
203 );
204 }
205
206 // Prepend posts with nav_menus_created_posts on first page.
207 $posts = array();
208 if ( 0 === $page && $this->manager->get_setting( 'nav_menus_created_posts' ) ) {
209 foreach ( $this->manager->get_setting( 'nav_menus_created_posts' )->value() as $post_id ) {
210 $auto_draft_post = get_post( $post_id );
211 if ( $post_type->name === $auto_draft_post->post_type ) {
212 $posts[] = $auto_draft_post;
213 }
214 }
215 }
216
217 $args = array(
218 'numberposts' => 10,
219 'offset' => 10 * $page,
220 'orderby' => 'date',
221 'order' => 'DESC',
222 'post_type' => $object_name,
223 );
224
225 // Add suppression array to arguments for get_posts.
226 if ( ! empty( $suppress_page_ids ) ) {
227 $args['post__not_in'] = $suppress_page_ids;
228 }
229
230 $posts = array_merge(
231 $posts,
232 $important_pages,
233 get_posts( $args )
234 );
235
236 foreach ( $posts as $post ) {
237 $post_title = $post->post_title;
238 if ( '' === $post_title ) {
239 /* translators: %d: ID of a post. */
240 $post_title = sprintf( __( '#%d (no title)' ), $post->ID );
241 }
242
243 $post_type_label = get_post_type_object( $post->post_type )->labels->singular_name;
244 $post_states = get_post_states( $post );
245 if ( ! empty( $post_states ) ) {
246 $post_type_label = implode( ',', $post_states );
247 }
248
249 $title = html_entity_decode( $post_title, ENT_QUOTES, get_bloginfo( 'charset' ) );
250 $items[] = array(
251 'id' => "post-{$post->ID}",
252 'title' => $title,
253 'original_title' => $title,
254 'type' => 'post_type',
255 'type_label' => $post_type_label,
256 'object' => $post->post_type,
257 'object_id' => (int) $post->ID,
258 'url' => get_permalink( (int) $post->ID ),
259 );
260 }
261 } elseif ( 'taxonomy' === $object_type ) {
262 $terms = get_terms(
263 array(
264 'taxonomy' => $object_name,
265 'child_of' => 0,
266 'exclude' => '',
267 'hide_empty' => false,
268 'hierarchical' => 1,
269 'include' => '',
270 'number' => 10,
271 'offset' => 10 * $page,
272 'order' => 'DESC',
273 'orderby' => 'count',
274 'pad_counts' => false,
275 )
276 );
277
278 if ( is_wp_error( $terms ) ) {
279 return $terms;
280 }
281
282 foreach ( $terms as $term ) {
283 $title = html_entity_decode( $term->name, ENT_QUOTES, get_bloginfo( 'charset' ) );
284 $items[] = array(
285 'id' => "term-{$term->term_id}",
286 'title' => $title,
287 'original_title' => $title,
288 'type' => 'taxonomy',
289 'type_label' => get_taxonomy( $term->taxonomy )->labels->singular_name,
290 'object' => $term->taxonomy,
291 'object_id' => (int) $term->term_id,
292 'url' => get_term_link( (int) $term->term_id, $term->taxonomy ),
293 );
294 }
295 }
296
297 /**
298 * Filters the available menu items.
299 *
300 * @since 4.3.0
301 *
302 * @param array $items The array of menu items.
303 * @param string $object_type The object type.
304 * @param string $object_name The object name.
305 * @param int $page The current page number.
306 */
307 $items = apply_filters( 'customize_nav_menu_available_items', $items, $object_type, $object_name, $page );
308
309 return $items;
310 }
311
312 /**
313 * Ajax handler for searching available menu items.
314 *
315 * @since 4.3.0
316 */
317 public function ajax_search_available_items() {
318 check_ajax_referer( 'customize-menus', 'customize-menus-nonce' );
319
320 if ( ! current_user_can( 'edit_theme_options' ) ) {
321 wp_die( -1 );
322 }
323
324 if ( empty( $_POST['search'] ) ) {
325 wp_send_json_error( 'nav_menus_missing_search_parameter' );
326 }
327
328 $p = isset( $_POST['page'] ) ? absint( $_POST['page'] ) : 0;
329 if ( $p < 1 ) {
330 $p = 1;
331 }
332
333 $s = sanitize_text_field( wp_unslash( $_POST['search'] ) );
334 $items = $this->search_available_items_query(
335 array(
336 'pagenum' => $p,
337 's' => $s,
338 )
339 );
340
341 if ( empty( $items ) ) {
342 wp_send_json_error( array( 'message' => __( 'No results found.' ) ) );
343 } else {
344 wp_send_json_success( array( 'items' => $items ) );
345 }
346 }
347
348 /**
349 * Performs post queries for available-item searching.
350 *
351 * Based on WP_Editor::wp_link_query().
352 *
353 * @since 4.3.0
354 *
355 * @param array $args Optional. Accepts 'pagenum' and 's' (search) arguments.
356 * @return array Menu items.
357 */
358 public function search_available_items_query( $args = array() ) {
359 $items = array();
360
361 $post_type_objects = get_post_types( array( 'show_in_nav_menus' => true ), 'objects' );
362 $query = array(
363 'post_type' => array_keys( $post_type_objects ),
364 'suppress_filters' => true,
365 'update_post_term_cache' => false,
366 'update_post_meta_cache' => false,
367 'post_status' => 'publish',
368 'posts_per_page' => 20,
369 );
370
371 $args['pagenum'] = isset( $args['pagenum'] ) ? absint( $args['pagenum'] ) : 1;
372 $query['offset'] = $args['pagenum'] > 1 ? $query['posts_per_page'] * ( $args['pagenum'] - 1 ) : 0;
373
374 if ( isset( $args['s'] ) ) {
375 $query['s'] = $args['s'];
376 }
377
378 $posts = array();
379
380 // Prepend list of posts with nav_menus_created_posts search results on first page.
381 $nav_menus_created_posts_setting = $this->manager->get_setting( 'nav_menus_created_posts' );
382 if ( 1 === $args['pagenum'] && $nav_menus_created_posts_setting && count( $nav_menus_created_posts_setting->value() ) > 0 ) {
383 $stub_post_query = new WP_Query(
384 array_merge(
385 $query,
386 array(
387 'post_status' => 'auto-draft',
388 'post__in' => $nav_menus_created_posts_setting->value(),
389 'posts_per_page' => -1,
390 )
391 )
392 );
393 $posts = array_merge( $posts, $stub_post_query->posts );
394 }
395
396 // Query posts.
397 $get_posts = new WP_Query( $query );
398 $posts = array_merge( $posts, $get_posts->posts );
399
400 // Create items for posts.
401 foreach ( $posts as $post ) {
402 $post_title = $post->post_title;
403 if ( '' === $post_title ) {
404 /* translators: %d: ID of a post. */
405 $post_title = sprintf( __( '#%d (no title)' ), $post->ID );
406 }
407
408 $post_type_label = $post_type_objects[ $post->post_type ]->labels->singular_name;
409 $post_states = get_post_states( $post );
410 if ( ! empty( $post_states ) ) {
411 $post_type_label = implode( ',', $post_states );
412 }
413
414 $items[] = array(
415 'id' => 'post-' . $post->ID,
416 'title' => html_entity_decode( $post_title, ENT_QUOTES, get_bloginfo( 'charset' ) ),
417 'type' => 'post_type',
418 'type_label' => $post_type_label,
419 'object' => $post->post_type,
420 'object_id' => (int) $post->ID,
421 'url' => get_permalink( (int) $post->ID ),
422 );
423 }
424
425 // Query taxonomy terms.
426 $taxonomies = get_taxonomies( array( 'show_in_nav_menus' => true ), 'names' );
427 $terms = get_terms(
428 array(
429 'taxonomies' => $taxonomies,
430 'name__like' => $args['s'],
431 'number' => 20,
432 'hide_empty' => false,
433 'offset' => 20 * ( $args['pagenum'] - 1 ),
434 )
435 );
436
437 // Check if any taxonomies were found.
438 if ( ! empty( $terms ) ) {
439 foreach ( $terms as $term ) {
440 $items[] = array(
441 'id' => 'term-' . $term->term_id,
442 'title' => html_entity_decode( $term->name, ENT_QUOTES, get_bloginfo( 'charset' ) ),
443 'type' => 'taxonomy',
444 'type_label' => get_taxonomy( $term->taxonomy )->labels->singular_name,
445 'object' => $term->taxonomy,
446 'object_id' => (int) $term->term_id,
447 'url' => get_term_link( (int) $term->term_id, $term->taxonomy ),
448 );
449 }
450 }
451
452 // Add "Home" link if search term matches. Treat as a page, but switch to custom on add.
453 if ( isset( $args['s'] ) ) {
454 // Only insert custom "Home" link if there's no Front Page
455 $front_page = 'page' === get_option( 'show_on_front' ) ? (int) get_option( 'page_on_front' ) : 0;
456 if ( empty( $front_page ) ) {
457 $title = _x( 'Home', 'nav menu home label' );
458 $matches = function_exists( 'mb_stripos' ) ? false !== mb_stripos( $title, $args['s'] ) : false !== stripos( $title, $args['s'] );
459 if ( $matches ) {
460 $items[] = array(
461 'id' => 'home',
462 'title' => $title,
463 'type' => 'custom',
464 'type_label' => __( 'Custom Link' ),
465 'object' => '',
466 'url' => home_url(),
467 );
468 }
469 }
470 }
471
472 /**
473 * Filters the available menu items during a search request.
474 *
475 * @since 4.5.0
476 *
477 * @param array $items The array of menu items.
478 * @param array $args Includes 'pagenum' and 's' (search) arguments.
479 */
480 $items = apply_filters( 'customize_nav_menu_searched_items', $items, $args );
481
482 return $items;
483 }
484
485 /**
486 * Enqueues scripts and styles for Customizer pane.
487 *
488 * @since 4.3.0
489 */
490 public function enqueue_scripts() {
491 wp_enqueue_style( 'customize-nav-menus' );
492 wp_enqueue_script( 'customize-nav-menus' );
493
494 $temp_nav_menu_setting = new WP_Customize_Nav_Menu_Setting( $this->manager, 'nav_menu[-1]' );
495 $temp_nav_menu_item_setting = new WP_Customize_Nav_Menu_Item_Setting( $this->manager, 'nav_menu_item[-1]' );
496
497 $num_locations = count( get_registered_nav_menus() );
498
499 if ( 1 === $num_locations ) {
500 $locations_description = __( 'Your theme can display menus in one location.' );
501 } else {
502 /* translators: %s: Number of menu locations. */
503 $locations_description = sprintf( _n( 'Your theme can display menus in %s location.', 'Your theme can display menus in %s locations.', $num_locations ), number_format_i18n( $num_locations ) );
504 }
505
506 // Pass data to JS.
507 $settings = array(
508 'allMenus' => wp_get_nav_menus(),
509 'itemTypes' => $this->available_item_types(),
510 'l10n' => array(
511 'untitled' => _x( '(no label)', 'missing menu item navigation label' ),
512 'unnamed' => _x( '(unnamed)', 'Missing menu name.' ),
513 'custom_label' => __( 'Custom Link' ),
514 'page_label' => get_post_type_object( 'page' )->labels->singular_name,
515 /* translators: %s: Menu location. */
516 'menuLocation' => _x( '(Currently set to: %s)', 'menu' ),
517 'locationsTitle' => 1 === $num_locations ? __( 'Menu Location' ) : __( 'Menu Locations' ),
518 'locationsDescription' => $locations_description,
519 'menuNameLabel' => __( 'Menu Name' ),
520 'newMenuNameDescription' => __( 'If your theme has multiple menus, giving them clear names will help you manage them.' ),
521 'itemAdded' => __( 'Menu item added' ),
522 'itemDeleted' => __( 'Menu item deleted' ),
523 'menuAdded' => __( 'Menu created' ),
524 'menuDeleted' => __( 'Menu deleted' ),
525 'movedUp' => __( 'Menu item moved up' ),
526 'movedDown' => __( 'Menu item moved down' ),
527 'movedLeft' => __( 'Menu item moved out of submenu' ),
528 'movedRight' => __( 'Menu item is now a sub-item' ),
529 /* translators: &#9656; is the unicode right-pointing triangle. %s: Section title in the Customizer. */
530 'customizingMenus' => sprintf( __( 'Customizing &#9656; %s' ), esc_html( $this->manager->get_panel( 'nav_menus' )->title ) ),
531 /* translators: %s: Title of an invalid menu item. */
532 'invalidTitleTpl' => __( '%s (Invalid)' ),
533 /* translators: %s: Title of a menu item in draft status. */
534 'pendingTitleTpl' => __( '%s (Pending)' ),
535 /* translators: %d: Number of menu items found. */
536 'itemsFound' => __( 'Number of items found: %d' ),
537 /* translators: %d: Number of additional menu items found. */
538 'itemsFoundMore' => __( 'Additional items found: %d' ),
539 'itemsLoadingMore' => __( 'Loading more results... please wait.' ),
540 'reorderModeOn' => __( 'Reorder mode enabled' ),
541 'reorderModeOff' => __( 'Reorder mode closed' ),
542 'reorderLabelOn' => esc_attr__( 'Reorder menu items' ),
543 'reorderLabelOff' => esc_attr__( 'Close reorder mode' ),
544 ),
545 'settingTransport' => 'postMessage',
546 'phpIntMax' => PHP_INT_MAX,
547 'defaultSettingValues' => array(
548 'nav_menu' => $temp_nav_menu_setting->default,
549 'nav_menu_item' => $temp_nav_menu_item_setting->default,
550 ),
551 'locationSlugMappedToName' => get_registered_nav_menus(),
552 );
553
554 $data = sprintf( 'var _wpCustomizeNavMenusSettings = %s;', wp_json_encode( $settings, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ) );
555 wp_scripts()->add_data( 'customize-nav-menus', 'data', $data );
556
557 // This is copied from nav-menus.php, and it has an unfortunate object name of `menus`.
558 $nav_menus_l10n = array(
559 'oneThemeLocationNoMenus' => null,
560 'moveUp' => __( 'Move up one' ),
561 'moveDown' => __( 'Move down one' ),
562 'moveToTop' => __( 'Move to the top' ),
563 /* translators: %s: Previous item name. */
564 'moveUnder' => __( 'Move under %s' ),
565 /* translators: %s: Previous item name. */
566 'moveOutFrom' => __( 'Move out from under %s' ),
567 /* translators: %s: Previous item name. */
568 'under' => __( 'Under %s' ),
569 /* translators: %s: Previous item name. */
570 'outFrom' => __( 'Out from under %s' ),
571 /* translators: 1: Item name, 2: Item type, 3: Item index, 4: Total items. */
572 'menuFocus' => __( 'Edit %1$s (%2$s, %3$d of %4$d)' ),
573 /* translators: 1: Item name, 2: Item type, 3: Item index, 4: Total items, 5: Item parent. */
574 'subMenuFocus' => __( 'Edit %1$s (%2$s, sub-item %3$d of %4$d under %5$s)' ),
575 /* translators: 1: Item name, 2: Item type, 3: Item index, 4: Total items, 5: Item parent, 6: Item depth. */
576 'subMenuMoreDepthFocus' => __( 'Edit %1$s (%2$s, sub-item %3$d of %4$d under %5$s, level %6$d)' ),
577 );
578 wp_localize_script( 'nav-menu', 'menus', $nav_menus_l10n );
579 }
580
581 /**
582 * Filters a dynamic setting's constructor args.
583 *
584 * For a dynamic setting to be registered, this filter must be employed
585 * to override the default false value with an array of args to pass to
586 * the WP_Customize_Setting constructor.
587 *
588 * @since 4.3.0
589 *
590 * @param false|array $setting_args The arguments to the WP_Customize_Setting constructor.
591 * @param string $setting_id ID for dynamic setting, usually coming from `$_POST['customized']`.
592 * @return array|false
593 */
594 public function filter_dynamic_setting_args( $setting_args, $setting_id ) {
595 if ( preg_match( WP_Customize_Nav_Menu_Setting::ID_PATTERN, $setting_id ) ) {
596 $setting_args = array(
597 'type' => WP_Customize_Nav_Menu_Setting::TYPE,
598 'transport' => 'postMessage',
599 );
600 } elseif ( preg_match( WP_Customize_Nav_Menu_Item_Setting::ID_PATTERN, $setting_id ) ) {
601 $setting_args = array(
602 'type' => WP_Customize_Nav_Menu_Item_Setting::TYPE,
603 'transport' => 'postMessage',
604 );
605 }
606 return $setting_args;
607 }
608
609 /**
610 * Allows non-statically created settings to be constructed with custom WP_Customize_Setting subclass.
611 *
612 * @since 4.3.0
613 *
614 * @param string $setting_class WP_Customize_Setting or a subclass.
615 * @param string $setting_id ID for dynamic setting, usually coming from `$_POST['customized']`.
616 * @param array $setting_args WP_Customize_Setting or a subclass.
617 * @return string
618 */
619 public function filter_dynamic_setting_class( $setting_class, $setting_id, $setting_args ) {
620 unset( $setting_id );
621
622 if ( ! empty( $setting_args['type'] ) && WP_Customize_Nav_Menu_Setting::TYPE === $setting_args['type'] ) {
623 $setting_class = 'WP_Customize_Nav_Menu_Setting';
624 } elseif ( ! empty( $setting_args['type'] ) && WP_Customize_Nav_Menu_Item_Setting::TYPE === $setting_args['type'] ) {
625 $setting_class = 'WP_Customize_Nav_Menu_Item_Setting';
626 }
627 return $setting_class;
628 }
629
630 /**
631 * Adds the customizer settings and controls.
632 *
633 * @since 4.3.0
634 */
635 public function customize_register() {
636 $changeset = $this->manager->unsanitized_post_values();
637
638 // Preview settings for nav menus early so that the sections and controls will be added properly.
639 $nav_menus_setting_ids = array();
640 foreach ( array_keys( $changeset ) as $setting_id ) {
641 if ( preg_match( '/^(nav_menu_locations|nav_menu|nav_menu_item)\[/', $setting_id ) ) {
642 $nav_menus_setting_ids[] = $setting_id;
643 }
644 }
645 $settings = $this->manager->add_dynamic_settings( $nav_menus_setting_ids );
646 if ( $this->manager->settings_previewed() ) {
647 foreach ( $settings as $setting ) {
648 $setting->preview();
649 }
650 }
651
652 // Require JS-rendered control types.
653 $this->manager->register_panel_type( 'WP_Customize_Nav_Menus_Panel' );
654 $this->manager->register_control_type( 'WP_Customize_Nav_Menu_Control' );
655 $this->manager->register_control_type( 'WP_Customize_Nav_Menu_Name_Control' );
656 $this->manager->register_control_type( 'WP_Customize_Nav_Menu_Locations_Control' );
657 $this->manager->register_control_type( 'WP_Customize_Nav_Menu_Auto_Add_Control' );
658 $this->manager->register_control_type( 'WP_Customize_Nav_Menu_Item_Control' );
659
660 // Create a panel for Menus.
661 $description = '<p>' . __( 'This panel is used for managing navigation menus for content you have already published on your site. You can create menus and add items for existing content such as pages, posts, categories, tags, formats, or custom links.' ) . '</p>';
662 if ( current_theme_supports( 'widgets' ) ) {
663 $description .= '<p>' . sprintf(
664 /* translators: %s: URL to the Widgets panel of the Customizer. */
665 __( 'Menus can be displayed in locations defined by your theme or in <a href="%s">widget areas</a> by adding a &#8220;Navigation Menu&#8221; widget.' ),
666 "javascript:wp.customize.panel( 'widgets' ).focus();"
667 ) . '</p>';
668 } else {
669 $description .= '<p>' . __( 'Menus can be displayed in locations defined by your theme.' ) . '</p>';
670 }
671
672 /*
673 * Once multiple theme supports are allowed in WP_Customize_Panel,
674 * this panel can be restricted to themes that support menus or widgets.
675 */
676 $this->manager->add_panel(
677 new WP_Customize_Nav_Menus_Panel(
678 $this->manager,
679 'nav_menus',
680 array(
681 'title' => __( 'Menus' ),
682 'description' => $description,
683 'priority' => 100,
684 )
685 )
686 );
687 $menus = wp_get_nav_menus();
688
689 // Menu locations.
690 $locations = get_registered_nav_menus();
691 $num_locations = count( $locations );
692
693 if ( 1 === $num_locations ) {
694 $description = '<p>' . __( 'Your theme can display menus in one location. Select which menu you would like to use.' ) . '</p>';
695 } else {
696 /* translators: %s: Number of menu locations. */
697 $description = '<p>' . sprintf( _n( 'Your theme can display menus in %s location. Select which menu you would like to use.', 'Your theme can display menus in %s locations. Select which menu appears in each location.', $num_locations ), number_format_i18n( $num_locations ) ) . '</p>';
698 }
699
700 if ( current_theme_supports( 'widgets' ) ) {
701 /* translators: URL to the Widgets panel of the Customizer. */
702 $description .= '<p>' . sprintf( __( 'If your theme has widget areas, you can also add menus there. Visit the <a href="%s">Widgets panel</a> and add a &#8220;Navigation Menu widget&#8221; to display a menu in a sidebar or footer.' ), "javascript:wp.customize.panel( 'widgets' ).focus();" ) . '</p>';
703 }
704
705 $this->manager->add_section(
706 'menu_locations',
707 array(
708 'title' => 1 === $num_locations ? _x( 'View Location', 'menu locations' ) : _x( 'View All Locations', 'menu locations' ),
709 'panel' => 'nav_menus',
710 'priority' => 30,
711 'description' => $description,
712 )
713 );
714
715 $choices = array( '0' => __( '&mdash; Select &mdash;' ) );
716 foreach ( $menus as $menu ) {
717 $choices[ $menu->term_id ] = wp_html_excerpt( $menu->name, 40, '&hellip;' );
718 }
719
720 // Attempt to re-map the nav menu location assignments when previewing a theme switch.
721 $mapped_nav_menu_locations = array();
722 if ( ! $this->manager->is_theme_active() ) {
723 $theme_mods = get_option( 'theme_mods_' . $this->manager->get_stylesheet(), array() );
724
725 // If there is no data from a previous activation, start fresh.
726 if ( empty( $theme_mods['nav_menu_locations'] ) ) {
727 $theme_mods['nav_menu_locations'] = array();
728 }
729
730 $mapped_nav_menu_locations = wp_map_nav_menu_locations( $theme_mods['nav_menu_locations'], $this->original_nav_menu_locations );
731 }
732
733 foreach ( $locations as $location => $description ) {
734 $setting_id = "nav_menu_locations[{$location}]";
735
736 $setting = $this->manager->get_setting( $setting_id );
737 if ( $setting ) {
738 $setting->transport = 'postMessage';
739 remove_filter( "customize_sanitize_{$setting_id}", 'absint' );
740 add_filter( "customize_sanitize_{$setting_id}", array( $this, 'intval_base10' ) );
741 } else {
742 $this->manager->add_setting(
743 $setting_id,
744 array(
745 'sanitize_callback' => array( $this, 'intval_base10' ),
746 'theme_supports' => 'menus',
747 'type' => 'theme_mod',
748 'transport' => 'postMessage',
749 'default' => 0,
750 )
751 );
752 }
753
754 // Override the assigned nav menu location if mapped during previewed theme switch.
755 if ( empty( $changeset[ $setting_id ] ) && isset( $mapped_nav_menu_locations[ $location ] ) ) {
756 $this->manager->set_post_value( $setting_id, $mapped_nav_menu_locations[ $location ] );
757 }
758
759 $this->manager->add_control(
760 new WP_Customize_Nav_Menu_Location_Control(
761 $this->manager,
762 $setting_id,
763 array(
764 'label' => $description,
765 'location_id' => $location,
766 'section' => 'menu_locations',
767 'choices' => $choices,
768 )
769 )
770 );
771 }
772
773 // Used to denote post states for special pages.
774 if ( ! function_exists( 'get_post_states' ) ) {
775 require_once ABSPATH . 'wp-admin/includes/template.php';
776 }
777
778 // Register each menu as a Customizer section, and add each menu item to each menu.
779 foreach ( $menus as $menu ) {
780 $menu_id = $menu->term_id;
781
782 // Create a section for each menu.
783 $section_id = 'nav_menu[' . $menu_id . ']';
784 $this->manager->add_section(
785 new WP_Customize_Nav_Menu_Section(
786 $this->manager,
787 $section_id,
788 array(
789 'title' => html_entity_decode( $menu->name, ENT_QUOTES, get_bloginfo( 'charset' ) ),
790 'priority' => 10,
791 'panel' => 'nav_menus',
792 )
793 )
794 );
795
796 $nav_menu_setting_id = 'nav_menu[' . $menu_id . ']';
797 $this->manager->add_setting(
798 new WP_Customize_Nav_Menu_Setting(
799 $this->manager,
800 $nav_menu_setting_id,
801 array(
802 'transport' => 'postMessage',
803 )
804 )
805 );
806
807 // Add the menu contents.
808 $menu_items = (array) wp_get_nav_menu_items( $menu_id );
809
810 foreach ( array_values( $menu_items ) as $i => $item ) {
811
812 // Create a setting for each menu item (which doesn't actually manage data, currently).
813 $menu_item_setting_id = 'nav_menu_item[' . $item->ID . ']';
814
815 $value = (array) $item;
816 if ( empty( $value['post_title'] ) ) {
817 $value['title'] = '';
818 }
819
820 $value['nav_menu_term_id'] = $menu_id;
821 $this->manager->add_setting(
822 new WP_Customize_Nav_Menu_Item_Setting(
823 $this->manager,
824 $menu_item_setting_id,
825 array(
826 'value' => $value,
827 'transport' => 'postMessage',
828 )
829 )
830 );
831
832 // Create a control for each menu item.
833 $this->manager->add_control(
834 new WP_Customize_Nav_Menu_Item_Control(
835 $this->manager,
836 $menu_item_setting_id,
837 array(
838 'label' => $item->title,
839 'section' => $section_id,
840 'priority' => 10 + $i,
841 )
842 )
843 );
844 }
845
846 // Note: other controls inside of this section get added dynamically in JS via the MenuSection.ready() function.
847 }
848
849 // Add the add-new-menu section and controls.
850 $this->manager->add_section(
851 'add_menu',
852 array(
853 'type' => 'new_menu',
854 'title' => __( 'New Menu' ),
855 'panel' => 'nav_menus',
856 'priority' => 20,
857 )
858 );
859
860 $this->manager->add_setting(
861 new WP_Customize_Filter_Setting(
862 $this->manager,
863 'nav_menus_created_posts',
864 array(
865 'transport' => 'postMessage',
866 'type' => 'option', // To prevent theme prefix in changeset.
867 'default' => array(),
868 'sanitize_callback' => array( $this, 'sanitize_nav_menus_created_posts' ),
869 )
870 )
871 );
872 }
873
874 /**
875 * Gets the base10 intval.
876 *
877 * This is used as a setting's sanitize_callback; we can't use just plain
878 * intval because the second argument is not what intval() expects.
879 *
880 * @since 4.3.0
881 *
882 * @param mixed $value Number to convert.
883 * @return int Integer.
884 */
885 public function intval_base10( $value ) {
886 return intval( $value, 10 );
887 }
888
889 /**
890 * Returns an array of all the available item types.
891 *
892 * @since 4.3.0
893 * @since 4.7.0 Each array item now includes a `$type_label` in addition to `$title`, `$type`, and `$object`.
894 *
895 * @return array The available menu item types.
896 */
897 public function available_item_types() {
898 $item_types = array();
899
900 $post_types = get_post_types( array( 'show_in_nav_menus' => true ), 'objects' );
901 if ( $post_types ) {
902 foreach ( $post_types as $slug => $post_type ) {
903 $item_types[] = array(
904 'title' => $post_type->labels->name,
905 'type_label' => $post_type->labels->singular_name,
906 'type' => 'post_type',
907 'object' => $post_type->name,
908 );
909 }
910 }
911
912 $taxonomies = get_taxonomies( array( 'show_in_nav_menus' => true ), 'objects' );
913 if ( $taxonomies ) {
914 foreach ( $taxonomies as $slug => $taxonomy ) {
915 if ( 'post_format' === $taxonomy && ! current_theme_supports( 'post-formats' ) ) {
916 continue;
917 }
918 $item_types[] = array(
919 'title' => $taxonomy->labels->name,
920 'type_label' => $taxonomy->labels->singular_name,
921 'type' => 'taxonomy',
922 'object' => $taxonomy->name,
923 );
924 }
925 }
926
927 /**
928 * Filters the available menu item types.
929 *
930 * @since 4.3.0
931 * @since 4.7.0 Each array item now includes a `$type_label` in addition to `$title`, `$type`, and `$object`.
932 *
933 * @param array $item_types Navigation menu item types.
934 */
935 $item_types = apply_filters( 'customize_nav_menu_available_item_types', $item_types );
936
937 return $item_types;
938 }
939
940 /**
941 * Adds a new `auto-draft` post.
942 *
943 * @since 4.7.0
944 *
945 * @param array $postarr {
946 * Post array. Note that post_status is overridden to be `auto-draft`.
947 *
948 * @type string $post_title Post title. Required.
949 * @type string $post_type Post type. Required.
950 * @type string $post_name Post name.
951 * @type string $post_content Post content.
952 * }
953 * @return WP_Post|WP_Error Inserted auto-draft post object or error.
954 */
955 public function insert_auto_draft_post( $postarr ) {
956 if ( ! isset( $postarr['post_type'] ) ) {
957 return new WP_Error( 'unknown_post_type', __( 'Invalid post type.' ) );
958 }
959 if ( empty( $postarr['post_title'] ) ) {
960 return new WP_Error( 'empty_title', __( 'Empty title.' ) );
961 }
962 if ( ! empty( $postarr['post_status'] ) ) {
963 return new WP_Error( 'status_forbidden', __( 'Status is forbidden.' ) );
964 }
965
966 /*
967 * If the changeset is a draft, this will change to draft the next time the changeset
968 * is updated; otherwise, auto-draft will persist in autosave revisions, until save.
969 */
970 $postarr['post_status'] = 'auto-draft';
971
972 // Auto-drafts are allowed to have empty post_names, so it has to be explicitly set.
973 if ( empty( $postarr['post_name'] ) ) {
974 $postarr['post_name'] = sanitize_title( $postarr['post_title'] );
975 }
976 if ( ! isset( $postarr['meta_input'] ) ) {
977 $postarr['meta_input'] = array();
978 }
979 $postarr['meta_input']['_customize_draft_post_name'] = $postarr['post_name'];
980 $postarr['meta_input']['_customize_changeset_uuid'] = $this->manager->changeset_uuid();
981 unset( $postarr['post_name'] );
982
983 add_filter( 'wp_insert_post_empty_content', '__return_false', 1000 );
984 $r = wp_insert_post( wp_slash( $postarr ), true );
985 remove_filter( 'wp_insert_post_empty_content', '__return_false', 1000 );
986
987 if ( is_wp_error( $r ) ) {
988 return $r;
989 } else {
990 return get_post( $r );
991 }
992 }
993
994 /**
995 * Ajax handler for adding a new auto-draft post.
996 *
997 * @since 4.7.0
998 */
999 public function ajax_insert_auto_draft_post() {
1000 if ( ! check_ajax_referer( 'customize-menus', 'customize-menus-nonce', false ) ) {
1001 wp_send_json_error( 'bad_nonce', 400 );
1002 }
1003
1004 if ( ! current_user_can( 'customize' ) ) {
1005 wp_send_json_error( 'customize_not_allowed', 403 );
1006 }
1007
1008 if ( empty( $_POST['params'] ) || ! is_array( $_POST['params'] ) ) {
1009 wp_send_json_error( 'missing_params', 400 );
1010 }
1011
1012 $params = wp_unslash( $_POST['params'] );
1013 $illegal_params = array_diff( array_keys( $params ), array( 'post_type', 'post_title' ) );
1014 if ( ! empty( $illegal_params ) ) {
1015 wp_send_json_error( 'illegal_params', 400 );
1016 }
1017
1018 $params = array_merge(
1019 array(
1020 'post_type' => '',
1021 'post_title' => '',
1022 ),
1023 $params
1024 );
1025
1026 if ( empty( $params['post_type'] ) || ! post_type_exists( $params['post_type'] ) ) {
1027 status_header( 400 );
1028 wp_send_json_error( 'missing_post_type_param' );
1029 }
1030
1031 $post_type_object = get_post_type_object( $params['post_type'] );
1032 if ( ! current_user_can( $post_type_object->cap->create_posts ) || ! current_user_can( $post_type_object->cap->publish_posts ) ) {
1033 status_header( 403 );
1034 wp_send_json_error( 'insufficient_post_permissions' );
1035 }
1036
1037 $params['post_title'] = trim( $params['post_title'] );
1038 if ( '' === $params['post_title'] ) {
1039 status_header( 400 );
1040 wp_send_json_error( 'missing_post_title' );
1041 }
1042
1043 $r = $this->insert_auto_draft_post( $params );
1044 if ( is_wp_error( $r ) ) {
1045 $error = $r;
1046 if ( ! empty( $post_type_object->labels->singular_name ) ) {
1047 $singular_name = $post_type_object->labels->singular_name;
1048 } else {
1049 $singular_name = __( 'Post' );
1050 }
1051
1052 $data = array(
1053 /* translators: 1: Post type name, 2: Error message. */
1054 'message' => sprintf( __( '%1$s could not be created: %2$s' ), $singular_name, $error->get_error_message() ),
1055 );
1056 wp_send_json_error( $data );
1057 } else {
1058 $post = $r;
1059 $data = array(
1060 'post_id' => $post->ID,
1061 'url' => get_permalink( $post->ID ),
1062 );
1063 wp_send_json_success( $data );
1064 }
1065 }
1066
1067 /**
1068 * Prints the JavaScript templates used to render Menu Customizer components.
1069 *
1070 * Templates are imported into the JS use wp.template.
1071 *
1072 * @since 4.3.0
1073 */
1074 public function print_templates() {
1075 ?>
1076 <script type="text/html" id="tmpl-available-menu-item">
1077 <li id="menu-item-tpl-{{ data.id }}" class="menu-item-tpl" data-menu-item-id="{{ data.id }}">
1078 <div class="menu-item-bar">
1079 <div class="menu-item-handle">
1080 <span class="item-type" aria-hidden="true">{{ data.type_label }}</span>
1081 <span class="item-title" aria-hidden="true">
1082 <span class="menu-item-title<# if ( ! data.title ) { #> no-title<# } #>">{{ data.title || wp.customize.Menus.data.l10n.untitled }}</span>
1083 </span>
1084 <button type="button" class="button-link item-add">
1085 <span class="screen-reader-text">
1086 <?php
1087 /* translators: Hidden accessibility text. 1: Title of a menu item, 2: Type of a menu item. */
1088 printf( __( 'Add to menu: %1$s (%2$s)' ), '{{ data.title || wp.customize.Menus.data.l10n.untitled }}', '{{ data.type_label }}' );
1089 ?>
1090 </span>
1091 </button>
1092 </div>
1093 </div>
1094 </li>
1095 </script>
1096
1097 <script type="text/html" id="tmpl-menu-item-reorder-nav">
1098 <div class="menu-item-reorder-nav">
1099 <?php
1100 printf(
1101 '<button type="button" class="menus-move-up">%1$s</button><button type="button" class="menus-move-down">%2$s</button><button type="button" class="menus-move-left">%3$s</button><button type="button" class="menus-move-right">%4$s</button>',
1102 __( 'Move up' ),
1103 __( 'Move down' ),
1104 __( 'Move one level up' ),
1105 __( 'Move one level down' )
1106 );
1107 ?>
1108 </div>
1109 </script>
1110
1111 <script type="text/html" id="tmpl-nav-menu-delete-button">
1112 <div class="menu-delete-item">
1113 <button type="button" class="button-link button-link-delete">
1114 <?php _e( 'Delete Menu' ); ?>
1115 </button>
1116 </div>
1117 </script>
1118
1119 <script type="text/html" id="tmpl-nav-menu-submit-new-button">
1120 <p id="customize-new-menu-submit-description"><?php _e( 'Click &#8220;Next&#8221; to start adding links to your new menu.' ); ?></p>
1121 <button id="customize-new-menu-submit" type="button" class="button" aria-describedby="customize-new-menu-submit-description"><?php _e( 'Next' ); ?></button>
1122 </script>
1123
1124 <script type="text/html" id="tmpl-nav-menu-locations-header">
1125 <span class="customize-control-title customize-section-title-menu_locations-heading">{{ data.l10n.locationsTitle }}</span>
1126 <p class="customize-control-description customize-section-title-menu_locations-description">{{ data.l10n.locationsDescription }}</p>
1127 </script>
1128
1129 <script type="text/html" id="tmpl-nav-menu-create-menu-section-title">
1130 <p class="add-new-menu-notice">
1131 <?php _e( 'It does not look like your site has any menus yet. Want to build one? Click the button to start.' ); ?>
1132 </p>
1133 <p class="add-new-menu-notice">
1134 <?php _e( 'You&#8217;ll create a menu, assign it a location, and add menu items like links to pages and categories. If your theme has multiple menu areas, you might need to create more than one.' ); ?>
1135 </p>
1136 <h3>
1137 <button type="button" class="button customize-add-menu-button">
1138 <?php _e( 'Create New Menu' ); ?>
1139 </button>
1140 </h3>
1141 </script>
1142 <?php
1143 }
1144
1145 /**
1146 * Prints the HTML template used to render the add-menu-item frame.
1147 *
1148 * @since 4.3.0
1149 */
1150 public function available_items_template() {
1151 ?>
1152 <div id="available-menu-items" class="accordion-container">
1153 <div class="customize-section-title">
1154 <button type="button" class="customize-section-back" tabindex="-1">
1155 <span class="screen-reader-text">
1156 <?php
1157 /* translators: Hidden accessibility text. */
1158 _e( 'Back' );
1159 ?>
1160 </span>
1161 </button>
1162 <h3>
1163 <span class="customize-action">
1164 <?php
1165 /* translators: &#9656; is the unicode right-pointing triangle. %s: Section title in the Customizer. */
1166 printf( __( 'Customizing &#9656; %s' ), esc_html( $this->manager->get_panel( 'nav_menus' )->title ) );
1167 ?>
1168 </span>
1169 <?php _e( 'Add Menu Items' ); ?>
1170 </h3>
1171 </div>
1172 <div id="available-menu-items-search" class="accordion-section cannot-expand">
1173 <div class="accordion-section-title">
1174 <label for="menu-items-search"><?php _e( 'Search Menu Items' ); ?></label>
1175 <input type="text" id="menu-items-search" aria-describedby="menu-items-search-desc" />
1176 <p class="screen-reader-text" id="menu-items-search-desc">
1177 <?php
1178 /* translators: Hidden accessibility text. */
1179 _e( 'The search results will be updated as you type.' );
1180 ?>
1181 </p>
1182 <span class="spinner"></span>
1183 <div class="search-icon" aria-hidden="true"></div>
1184 <button type="button" class="clear-results"><span class="screen-reader-text">
1185 <?php
1186 /* translators: Hidden accessibility text. */
1187 _e( 'Clear Results' );
1188 ?>
1189 </span></button>
1190 </div>
1191 <ul class="accordion-section-content available-menu-items-list" data-type="search"></ul>
1192 </div>
1193 <?php
1194
1195 // Ensure the page post type comes first in the list.
1196 $item_types = $this->available_item_types();
1197 $page_item_type = null;
1198 foreach ( $item_types as $i => $item_type ) {
1199 if ( isset( $item_type['object'] ) && 'page' === $item_type['object'] ) {
1200 $page_item_type = $item_type;
1201 unset( $item_types[ $i ] );
1202 }
1203 }
1204
1205 $this->print_custom_links_available_menu_item();
1206 if ( $page_item_type ) {
1207 $this->print_post_type_container( $page_item_type );
1208 }
1209 // Containers for per-post-type item browsing; items are added with JS.
1210 foreach ( $item_types as $item_type ) {
1211 $this->print_post_type_container( $item_type );
1212 }
1213 ?>
1214 </div><!-- #available-menu-items -->
1215 <?php
1216 }
1217
1218 /**
1219 * Prints the markup for new menu items.
1220 *
1221 * To be used in the template #available-menu-items.
1222 *
1223 * @since 4.7.0
1224 *
1225 * @param array $available_item_type Menu item data to output, including title, type, and label.
1226 */
1227 protected function print_post_type_container( $available_item_type ) {
1228 $id = sprintf( 'available-menu-items-%s-%s', $available_item_type['type'], $available_item_type['object'] );
1229 ?>
1230 <div id="<?php echo esc_attr( $id ); ?>" class="accordion-section">
1231 <h4 class="accordion-section-title">
1232 <button type="button" class="accordion-trigger" aria-expanded="false" aria-controls="<?php echo esc_attr( $id ); ?>-content">
1233 <?php echo esc_html( $available_item_type['title'] ); ?>
1234 <span class="spinner"></span>
1235 <span class="no-items"><?php _e( 'No items' ); ?></span>
1236 <span class="toggle-indicator" aria-hidden="true"></span>
1237 </button>
1238 </h4>
1239 <div class="accordion-section-content" id="<?php echo esc_attr( $id ); ?>-content">
1240 <?php if ( 'post_type' === $available_item_type['type'] ) : ?>
1241 <?php $post_type_obj = get_post_type_object( $available_item_type['object'] ); ?>
1242 <?php if ( current_user_can( $post_type_obj->cap->create_posts ) && current_user_can( $post_type_obj->cap->publish_posts ) ) : ?>
1243 <div class="new-content-item-wrapper">
1244 <label for="<?php echo esc_attr( 'create-item-input-' . $available_item_type['object'] ); ?>"><?php echo esc_html( $post_type_obj->labels->add_new_item ); ?></label>
1245 <div class="new-content-item">
1246 <input type="text" id="<?php echo esc_attr( 'create-item-input-' . $available_item_type['object'] ); ?>" class="create-item-input form-required">
1247 <button type="button" class="button add-content"><?php _e( 'Add' ); ?></button>
1248 </div>
1249 <span id="create-input-<?php echo esc_attr( $available_item_type['object'] ); ?>-error" class="create-item-error error-message" style="display: none;"><?php _e( 'Please enter an item title' ); ?></span>
1250
1251 </div>
1252 <?php endif; ?>
1253 <?php endif; ?>
1254 <ul class="available-menu-items-list" data-type="<?php echo esc_attr( $available_item_type['type'] ); ?>" data-object="<?php echo esc_attr( $available_item_type['object'] ); ?>" data-type_label="<?php echo esc_attr( isset( $available_item_type['type_label'] ) ? $available_item_type['type_label'] : $available_item_type['type'] ); ?>"></ul>
1255 </div>
1256 </div>
1257 <?php
1258 }
1259
1260 /**
1261 * Prints the markup for available menu item custom links.
1262 *
1263 * @since 4.7.0
1264 */
1265 protected function print_custom_links_available_menu_item() {
1266 ?>
1267 <div id="new-custom-menu-item" class="accordion-section">
1268 <h4 class="accordion-section-title">
1269 <button type="button" class="accordion-trigger" aria-expanded="false" aria-controls="new-custom-menu-item-content">
1270 <?php _e( 'Custom Links' ); ?>
1271 <span class="toggle-indicator" aria-hidden="true"></span>
1272 </button>
1273 </h4>
1274 <div class="accordion-section-content customlinkdiv" id="new-custom-menu-item-content">
1275 <input type="hidden" value="custom" id="custom-menu-item-type" name="menu-item[-1][menu-item-type]" />
1276 <p id="menu-item-url-wrap" class="wp-clearfix">
1277 <label class="howto" for="custom-menu-item-url"><?php _e( 'URL' ); ?></label>
1278 <input id="custom-menu-item-url" name="menu-item[-1][menu-item-url]" type="text" class="code menu-item-textbox" placeholder="https://">
1279 <span id="custom-url-error" class="error-message" style="display: none;"><?php _e( 'Please provide a valid link.' ); ?></span>
1280 </p>
1281 <p id="menu-item-name-wrap" class="wp-clearfix">
1282 <label class="howto" for="custom-menu-item-name"><?php _e( 'Link Text' ); ?></label>
1283 <input id="custom-menu-item-name" name="menu-item[-1][menu-item-title]" type="text" class="regular-text menu-item-textbox">
1284 <span id="custom-name-error" class="error-message" style="display: none;"><?php _e( 'The link text cannot be empty.' ); ?></span>
1285 </p>
1286 <p class="button-controls">
1287 <span class="add-to-menu">
1288 <input type="submit" class="button submit-add-to-menu right" value="<?php esc_attr_e( 'Add to Menu' ); ?>" name="add-custom-menu-item" id="custom-menu-item-submit">
1289 <span class="spinner"></span>
1290 </span>
1291 </p>
1292 </div>
1293 </div>
1294 <?php
1295 }
1296
1297 //
1298 // Start functionality specific to partial-refresh of menu changes in Customizer preview.
1299 //
1300
1301 /**
1302 * Nav menu args used for each instance, keyed by the args HMAC.
1303 *
1304 * @since 4.3.0
1305 * @var array
1306 */
1307 public $preview_nav_menu_instance_args = array();
1308
1309 /**
1310 * Filters arguments for dynamic nav_menu selective refresh partials.
1311 *
1312 * @since 4.5.0
1313 *
1314 * @param array|false $partial_args Partial args.
1315 * @param string $partial_id Partial ID.
1316 * @return array Partial args.
1317 */
1318 public function customize_dynamic_partial_args( $partial_args, $partial_id ) {
1319
1320 if ( preg_match( '/^nav_menu_instance\[[0-9a-f]{32}\]$/', $partial_id ) ) {
1321 if ( false === $partial_args ) {
1322 $partial_args = array();
1323 }
1324 $partial_args = array_merge(
1325 $partial_args,
1326 array(
1327 'type' => 'nav_menu_instance',
1328 'render_callback' => array( $this, 'render_nav_menu_partial' ),
1329 'container_inclusive' => true,
1330 'settings' => array(), // Empty because the nav menu instance may relate to a menu or a location.
1331 'capability' => 'edit_theme_options',
1332 )
1333 );
1334 }
1335
1336 return $partial_args;
1337 }
1338
1339 /**
1340 * Adds hooks for the Customizer preview.
1341 *
1342 * @since 4.3.0
1343 */
1344 public function customize_preview_init() {
1345 add_action( 'wp_enqueue_scripts', array( $this, 'customize_preview_enqueue_deps' ) );
1346 add_filter( 'wp_nav_menu_args', array( $this, 'filter_wp_nav_menu_args' ), 1000 );
1347 add_filter( 'wp_nav_menu', array( $this, 'filter_wp_nav_menu' ), 10, 2 );
1348 add_action( 'wp_footer', array( $this, 'export_preview_data' ), 1 );
1349 add_filter( 'customize_render_partials_response', array( $this, 'export_partial_rendered_nav_menu_instances' ) );
1350 }
1351
1352 /**
1353 * Makes the auto-draft status protected so that it can be queried.
1354 *
1355 * @since 4.7.0
1356 *
1357 * @global stdClass[] $wp_post_statuses List of post statuses.
1358 */
1359 public function make_auto_draft_status_previewable() {
1360 global $wp_post_statuses;
1361 $wp_post_statuses['auto-draft']->protected = true;
1362 }
1363
1364 /**
1365 * Sanitizes post IDs for posts created for nav menu items to be published.
1366 *
1367 * @since 4.7.0
1368 *
1369 * @param array $value Post IDs.
1370 * @return array Post IDs.
1371 */
1372 public function sanitize_nav_menus_created_posts( $value ) {
1373 $post_ids = array();
1374 foreach ( wp_parse_id_list( $value ) as $post_id ) {
1375 if ( empty( $post_id ) ) {
1376 continue;
1377 }
1378 $post = get_post( $post_id );
1379 if ( 'auto-draft' !== $post->post_status && 'draft' !== $post->post_status ) {
1380 continue;
1381 }
1382 $post_type_obj = get_post_type_object( $post->post_type );
1383 if ( ! $post_type_obj ) {
1384 continue;
1385 }
1386 if ( ! current_user_can( $post_type_obj->cap->publish_posts ) || ! current_user_can( 'edit_post', $post_id ) ) {
1387 continue;
1388 }
1389 $post_ids[] = $post->ID;
1390 }
1391 return $post_ids;
1392 }
1393
1394 /**
1395 * Publishes the auto-draft posts that were created for nav menu items.
1396 *
1397 * The post IDs will have been sanitized by already by
1398 * `WP_Customize_Nav_Menu_Items::sanitize_nav_menus_created_posts()` to
1399 * remove any post IDs for which the user cannot publish or for which the
1400 * post is not an auto-draft.
1401 *
1402 * @since 4.7.0
1403 *
1404 * @param WP_Customize_Setting $setting Customizer setting object.
1405 */
1406 public function save_nav_menus_created_posts( $setting ) {
1407 $post_ids = $setting->post_value();
1408 if ( ! empty( $post_ids ) ) {
1409 foreach ( $post_ids as $post_id ) {
1410
1411 // Prevent overriding the status that a user may have prematurely updated the post to.
1412 $current_status = get_post_status( $post_id );
1413 if ( 'auto-draft' !== $current_status && 'draft' !== $current_status ) {
1414 continue;
1415 }
1416
1417 $target_status = 'attachment' === get_post_type( $post_id ) ? 'inherit' : 'publish';
1418 $args = array(
1419 'ID' => $post_id,
1420 'post_status' => $target_status,
1421 );
1422 $post_name = get_post_meta( $post_id, '_customize_draft_post_name', true );
1423 if ( $post_name ) {
1424 $args['post_name'] = $post_name;
1425 }
1426
1427 // Note that wp_publish_post() cannot be used because unique slugs need to be assigned.
1428 wp_update_post( wp_slash( $args ) );
1429
1430 delete_post_meta( $post_id, '_customize_draft_post_name' );
1431 }
1432 }
1433 }
1434
1435 /**
1436 * Keeps track of the arguments that are being passed to wp_nav_menu().
1437 *
1438 * @since 4.3.0
1439 *
1440 * @see wp_nav_menu()
1441 * @see WP_Customize_Widgets::filter_dynamic_sidebar_params()
1442 *
1443 * @param array $args An array containing wp_nav_menu() arguments.
1444 * @return array Arguments.
1445 */
1446 public function filter_wp_nav_menu_args( $args ) {
1447 /*
1448 * The following conditions determine whether or not this instance of
1449 * wp_nav_menu() can use selective refreshed. A wp_nav_menu() can be
1450 * selective refreshed if...
1451 */
1452 $can_partial_refresh = (
1453 // ...if wp_nav_menu() is directly echoing out the menu (and thus isn't manipulating the string after generated),
1454 ! empty( $args['echo'] )
1455 &&
1456 // ...and if the fallback_cb can be serialized to JSON, since it will be included in the placement context data,
1457 ( empty( $args['fallback_cb'] ) || is_string( $args['fallback_cb'] ) )
1458 &&
1459 // ...and if the walker can also be serialized to JSON, since it will be included in the placement context data as well,
1460 ( empty( $args['walker'] ) || is_string( $args['walker'] ) )
1461 // ...and if it has a theme location assigned or an assigned menu to display,
1462 && (
1463 ! empty( $args['theme_location'] )
1464 ||
1465 ( ! empty( $args['menu'] ) && ( is_numeric( $args['menu'] ) || is_object( $args['menu'] ) ) )
1466 )
1467 &&
1468 // ...and if the nav menu would be rendered with a wrapper container element (upon which to attach data-* attributes).
1469 (
1470 ! empty( $args['container'] )
1471 ||
1472 ( isset( $args['items_wrap'] ) && str_starts_with( $args['items_wrap'], '<' ) )
1473 )
1474 );
1475 $args['can_partial_refresh'] = $can_partial_refresh;
1476
1477 $exported_args = $args;
1478
1479 // Empty out args which may not be JSON-serializable.
1480 if ( ! $can_partial_refresh ) {
1481 $exported_args['fallback_cb'] = '';
1482 $exported_args['walker'] = '';
1483 }
1484
1485 /*
1486 * Replace object menu arg with a term_id menu arg, as this exports better
1487 * to JS and is easier to compare hashes.
1488 */
1489 if ( ! empty( $exported_args['menu'] ) && is_object( $exported_args['menu'] ) ) {
1490 $exported_args['menu'] = $exported_args['menu']->term_id;
1491 }
1492
1493 ksort( $exported_args );
1494 $exported_args['args_hmac'] = $this->hash_nav_menu_args( $exported_args );
1495
1496 $args['customize_preview_nav_menus_args'] = $exported_args;
1497 $this->preview_nav_menu_instance_args[ $exported_args['args_hmac'] ] = $exported_args;
1498 return $args;
1499 }
1500
1501 /**
1502 * Prepares wp_nav_menu() calls for partial refresh.
1503 *
1504 * Injects attributes into container element.
1505 *
1506 * @since 4.3.0
1507 *
1508 * @see wp_nav_menu()
1509 *
1510 * @param string $nav_menu_content The HTML content for the navigation menu.
1511 * @param object $args An object containing wp_nav_menu() arguments.
1512 * @return string Nav menu HTML with selective refresh attributes added if partial can be refreshed.
1513 */
1514 public function filter_wp_nav_menu( $nav_menu_content, $args ) {
1515 if ( isset( $args->customize_preview_nav_menus_args['can_partial_refresh'] ) && $args->customize_preview_nav_menus_args['can_partial_refresh'] ) {
1516 $attributes = sprintf( ' data-customize-partial-id="%s"', esc_attr( 'nav_menu_instance[' . $args->customize_preview_nav_menus_args['args_hmac'] . ']' ) );
1517 $attributes .= ' data-customize-partial-type="nav_menu_instance"';
1518 $attributes .= sprintf( ' data-customize-partial-placement-context="%s"', esc_attr( wp_json_encode( $args->customize_preview_nav_menus_args ) ) );
1519 $nav_menu_content = preg_replace( '#^(<\w+)#', '$1 ' . str_replace( '\\', '\\\\', $attributes ), $nav_menu_content, 1 );
1520 }
1521 return $nav_menu_content;
1522 }
1523
1524 /**
1525 * Hashes (hmac) the nav menu arguments to ensure they are not tampered with when
1526 * submitted in the Ajax request.
1527 *
1528 * Note that the array is expected to be pre-sorted.
1529 *
1530 * @since 4.3.0
1531 *
1532 * @param array $args The arguments to hash.
1533 * @return string Hashed nav menu arguments.
1534 */
1535 public function hash_nav_menu_args( $args ) {
1536 return wp_hash( serialize( $args ) );
1537 }
1538
1539 /**
1540 * Enqueues scripts for the Customizer preview.
1541 *
1542 * @since 4.3.0
1543 */
1544 public function customize_preview_enqueue_deps() {
1545 wp_enqueue_script( 'customize-preview-nav-menus' ); // Note that we have overridden this.
1546 }
1547
1548 /**
1549 * Exports data from PHP to JS.
1550 *
1551 * @since 4.3.0
1552 */
1553 public function export_preview_data() {
1554
1555 // Why not wp_localize_script? Because we're not localizing, and it forces values into strings.
1556 $exports = array(
1557 'navMenuInstanceArgs' => $this->preview_nav_menu_instance_args,
1558 );
1559 wp_print_inline_script_tag( sprintf( 'var _wpCustomizePreviewNavMenusExports = %s;', wp_json_encode( $exports, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ) ) . "\n//# sourceURL=" . rawurlencode( __METHOD__ ) );
1560 }
1561
1562 /**
1563 * Exports any wp_nav_menu() calls during the rendering of any partials.
1564 *
1565 * @since 4.5.0
1566 *
1567 * @param array $response Response.
1568 * @return array Response.
1569 */
1570 public function export_partial_rendered_nav_menu_instances( $response ) {
1571 $response['nav_menu_instance_args'] = $this->preview_nav_menu_instance_args;
1572 return $response;
1573 }
1574
1575 /**
1576 * Renders a specific menu via wp_nav_menu() using the supplied arguments.
1577 *
1578 * @since 4.3.0
1579 *
1580 * @see wp_nav_menu()
1581 *
1582 * @param WP_Customize_Partial $partial Partial.
1583 * @param array $nav_menu_args Nav menu args supplied as container context.
1584 * @return string|false
1585 */
1586 public function render_nav_menu_partial( $partial, $nav_menu_args ) {
1587 unset( $partial );
1588
1589 if ( ! isset( $nav_menu_args['args_hmac'] ) ) {
1590 // Error: missing_args_hmac.
1591 return false;
1592 }
1593
1594 $nav_menu_args_hmac = $nav_menu_args['args_hmac'];
1595 unset( $nav_menu_args['args_hmac'] );
1596
1597 ksort( $nav_menu_args );
1598 if ( ! hash_equals( $this->hash_nav_menu_args( $nav_menu_args ), $nav_menu_args_hmac ) ) {
1599 // Error: args_hmac_mismatch.
1600 return false;
1601 }
1602
1603 ob_start();
1604 wp_nav_menu( $nav_menu_args );
1605 $content = ob_get_clean();
1606
1607 return $content;
1608 }
1609}
1610
Ui Ux Design – Teachers Night Out https://cardgames4educators.com Wed, 16 Oct 2024 22:24:18 +0000 en-US hourly 1 https://wordpress.org/?v=6.9.4 https://cardgames4educators.com/wp-content/uploads/2024/06/cropped-Card-4-Educators-logo-32x32.png Ui Ux Design – Teachers Night Out https://cardgames4educators.com 32 32 Masters In English How English Speaker https://cardgames4educators.com/masters-in-english-how-english-speaker/ https://cardgames4educators.com/masters-in-english-how-english-speaker/#comments Mon, 27 May 2024 08:54:45 +0000 https://themexriver.com/wp/kadu/?p=1

Erat himenaeos neque id sagittis massa. Hac suscipit pulvinar dignissim platea magnis eu. Don tellus a pharetra inceptos efficitur dui pulvinar. Feugiat facilisis penatibus pulvinar nunc dictumst donec odio platea habitasse. Lacus porta dolor purus elit ante bibendum tortor netus taciti nullam cubilia. Erat per suspendisse placerat morbi egestas pulvinar bibendum sollicitudin nec. Euismod cubilia eleifend velit himenaeos sodales lectus. Leo maximus cras ac porttitor aliquam torquent pulvinar odio volutpat parturient. Quisque risus finibus suspendisse mus purus magnis facilisi condimentum consectetur dui. Curae elit suspendisse cursus vehicula.

Turpis taciti class non vel pretium quis pulvinar tempor lobortis nunc. Libero phasellus parturient sapien volutpat malesuada ornare. Cubilia dignissim sollicitudin rhoncus lacinia maximus. Cras lorem fermentum bibendum pellentesque nisl etiam ligula enim cubilia. Vulputate pede sapien torquent montes tempus malesuada in mattis dis turpis vitae. Porta est tempor ex eget feugiat vulputate ipsum. Justo nec iaculis habitant diam arcu fermentum.

We offer comprehen sive emplo ment services such as assistance wit employer compliance.Our company is your strategic HR partner as instead of HR. john smithson

Cubilia dignissim sollicitudin rhoncus lacinia maximus. Cras lorem fermentum bibendum pellentesque nisl etiam ligula enim cubilia. Vulputate pede sapien torquent montes tempus malesuada in mattis dis turpis vitae.

Exploring Learning Landscapes in Academic

Feugiat facilisis penatibus pulvinar nunc dictumst donec odio platea habitasse. Lacus porta dolor purus elit ante bibendum tortor netus taciti nullam cubilia. Erat per suspendisse placerat morbi egestas pulvinar bibendum sollicitudin nec. Euismod cubilia eleifend velit himenaeos sodales lectus. Leo maximus cras ac porttitor aliquam torquent.

]]>
https://cardgames4educators.com/masters-in-english-how-english-speaker/feed/ 1