WordPress 4.7
[autoinstalls/wordpress.git] / wp-admin / includes / nav-menu.php
1 <?php
2 /**
3  * Core Navigation Menu API
4  *
5  * @package WordPress
6  * @subpackage Nav_Menus
7  * @since 3.0.0
8  */
9
10 /** Walker_Nav_Menu_Edit class */
11 require_once( ABSPATH . 'wp-admin/includes/class-walker-nav-menu-edit.php' );
12
13 /** Walker_Nav_Menu_Checklist class */
14 require_once( ABSPATH . 'wp-admin/includes/class-walker-nav-menu-checklist.php' );
15
16 /**
17  * Prints the appropriate response to a menu quick search.
18  *
19  * @since 3.0.0
20  *
21  * @param array $request The unsanitized request values.
22  */
23 function _wp_ajax_menu_quick_search( $request = array() ) {
24         $args = array();
25         $type = isset( $request['type'] ) ? $request['type'] : '';
26         $object_type = isset( $request['object_type'] ) ? $request['object_type'] : '';
27         $query = isset( $request['q'] ) ? $request['q'] : '';
28         $response_format = isset( $request['response-format'] ) && in_array( $request['response-format'], array( 'json', 'markup' ) ) ? $request['response-format'] : 'json';
29
30         if ( 'markup' == $response_format ) {
31                 $args['walker'] = new Walker_Nav_Menu_Checklist;
32         }
33
34         if ( 'get-post-item' == $type ) {
35                 if ( post_type_exists( $object_type ) ) {
36                         if ( isset( $request['ID'] ) ) {
37                                 $object_id = (int) $request['ID'];
38                                 if ( 'markup' == $response_format ) {
39                                         echo walk_nav_menu_tree( array_map('wp_setup_nav_menu_item', array( get_post( $object_id ) ) ), 0, (object) $args );
40                                 } elseif ( 'json' == $response_format ) {
41                                         echo wp_json_encode(
42                                                 array(
43                                                         'ID' => $object_id,
44                                                         'post_title' => get_the_title( $object_id ),
45                                                         'post_type' => get_post_type( $object_id ),
46                                                 )
47                                         );
48                                         echo "\n";
49                                 }
50                         }
51                 } elseif ( taxonomy_exists( $object_type ) ) {
52                         if ( isset( $request['ID'] ) ) {
53                                 $object_id = (int) $request['ID'];
54                                 if ( 'markup' == $response_format ) {
55                                         echo walk_nav_menu_tree( array_map('wp_setup_nav_menu_item', array( get_term( $object_id, $object_type ) ) ), 0, (object) $args );
56                                 } elseif ( 'json' == $response_format ) {
57                                         $post_obj = get_term( $object_id, $object_type );
58                                         echo wp_json_encode(
59                                                 array(
60                                                         'ID' => $object_id,
61                                                         'post_title' => $post_obj->name,
62                                                         'post_type' => $object_type,
63                                                 )
64                                         );
65                                         echo "\n";
66                                 }
67                         }
68
69                 }
70
71         } elseif ( preg_match('/quick-search-(posttype|taxonomy)-([a-zA-Z_-]*\b)/', $type, $matches) ) {
72                 if ( 'posttype' == $matches[1] && get_post_type_object( $matches[2] ) ) {
73                         $post_type_obj = _wp_nav_menu_meta_box_object( get_post_type_object( $matches[2] ) );
74                         $args = array_merge(
75                                 $args,
76                                 array(
77                                         'no_found_rows'          => true,
78                                         'update_post_meta_cache' => false,
79                                         'update_post_term_cache' => false,
80                                         'posts_per_page'         => 10,
81                                         'post_type'              => $matches[2],
82                                         's'                      => $query,
83                                 )
84                         );
85                         if ( isset( $post_type_obj->_default_query ) ) {
86                                 $args = array_merge( $args, (array) $post_type_obj->_default_query );
87                         }
88                         $search_results_query = new WP_Query( $args );
89                         if ( ! $search_results_query->have_posts() ) {
90                                 return;
91                         }
92                         while ( $search_results_query->have_posts() ) {
93                                 $post = $search_results_query->next_post();
94                                 if ( 'markup' == $response_format ) {
95                                         $var_by_ref = $post->ID;
96                                         echo walk_nav_menu_tree( array_map('wp_setup_nav_menu_item', array( get_post( $var_by_ref ) ) ), 0, (object) $args );
97                                 } elseif ( 'json' == $response_format ) {
98                                         echo wp_json_encode(
99                                                 array(
100                                                         'ID' => $post->ID,
101                                                         'post_title' => get_the_title( $post->ID ),
102                                                         'post_type' => $matches[2],
103                                                 )
104                                         );
105                                         echo "\n";
106                                 }
107                         }
108                 } elseif ( 'taxonomy' == $matches[1] ) {
109                         $terms = get_terms( $matches[2], array(
110                                 'name__like' => $query,
111                                 'number' => 10,
112                         ));
113                         if ( empty( $terms ) || is_wp_error( $terms ) )
114                                 return;
115                         foreach ( (array) $terms as $term ) {
116                                 if ( 'markup' == $response_format ) {
117                                         echo walk_nav_menu_tree( array_map('wp_setup_nav_menu_item', array( $term ) ), 0, (object) $args );
118                                 } elseif ( 'json' == $response_format ) {
119                                         echo wp_json_encode(
120                                                 array(
121                                                         'ID' => $term->term_id,
122                                                         'post_title' => $term->name,
123                                                         'post_type' => $matches[2],
124                                                 )
125                                         );
126                                         echo "\n";
127                                 }
128                         }
129                 }
130         }
131 }
132
133 /**
134  * Register nav menu meta boxes and advanced menu items.
135  *
136  * @since 3.0.0
137  **/
138 function wp_nav_menu_setup() {
139         // Register meta boxes
140         wp_nav_menu_post_type_meta_boxes();
141         add_meta_box( 'add-custom-links', __( 'Custom Links' ), 'wp_nav_menu_item_link_meta_box', 'nav-menus', 'side', 'default' );
142         wp_nav_menu_taxonomy_meta_boxes();
143
144         // Register advanced menu items (columns)
145         add_filter( 'manage_nav-menus_columns', 'wp_nav_menu_manage_columns' );
146
147         // If first time editing, disable advanced items by default.
148         if ( false === get_user_option( 'managenav-menuscolumnshidden' ) ) {
149                 $user = wp_get_current_user();
150                 update_user_option($user->ID, 'managenav-menuscolumnshidden',
151                         array( 0 => 'link-target', 1 => 'css-classes', 2 => 'xfn', 3 => 'description', 4 => 'title-attribute', ),
152                         true);
153         }
154 }
155
156 /**
157  * Limit the amount of meta boxes to pages, posts, links, and categories for first time users.
158  *
159  * @since 3.0.0
160  *
161  * @global array $wp_meta_boxes
162  **/
163 function wp_initial_nav_menu_meta_boxes() {
164         global $wp_meta_boxes;
165
166         if ( get_user_option( 'metaboxhidden_nav-menus' ) !== false || ! is_array($wp_meta_boxes) )
167                 return;
168
169         $initial_meta_boxes = array( 'add-post-type-page', 'add-post-type-post', 'add-custom-links', 'add-category' );
170         $hidden_meta_boxes = array();
171
172         foreach ( array_keys($wp_meta_boxes['nav-menus']) as $context ) {
173                 foreach ( array_keys($wp_meta_boxes['nav-menus'][$context]) as $priority ) {
174                         foreach ( $wp_meta_boxes['nav-menus'][$context][$priority] as $box ) {
175                                 if ( in_array( $box['id'], $initial_meta_boxes ) ) {
176                                         unset( $box['id'] );
177                                 } else {
178                                         $hidden_meta_boxes[] = $box['id'];
179                                 }
180                         }
181                 }
182         }
183
184         $user = wp_get_current_user();
185         update_user_option( $user->ID, 'metaboxhidden_nav-menus', $hidden_meta_boxes, true );
186 }
187
188 /**
189  * Creates meta boxes for any post type menu item..
190  *
191  * @since 3.0.0
192  */
193 function wp_nav_menu_post_type_meta_boxes() {
194         $post_types = get_post_types( array( 'show_in_nav_menus' => true ), 'object' );
195
196         if ( ! $post_types )
197                 return;
198
199         foreach ( $post_types as $post_type ) {
200                 /**
201                  * Filters whether a menu items meta box will be added for the current
202                  * object type.
203                  *
204                  * If a falsey value is returned instead of an object, the menu items
205                  * meta box for the current meta box object will not be added.
206                  *
207                  * @since 3.0.0
208                  *
209                  * @param object $meta_box_object The current object to add a menu items
210                  *                                meta box for.
211                  */
212                 $post_type = apply_filters( 'nav_menu_meta_box_object', $post_type );
213                 if ( $post_type ) {
214                         $id = $post_type->name;
215                         // Give pages a higher priority.
216                         $priority = ( 'page' == $post_type->name ? 'core' : 'default' );
217                         add_meta_box( "add-post-type-{$id}", $post_type->labels->name, 'wp_nav_menu_item_post_type_meta_box', 'nav-menus', 'side', $priority, $post_type );
218                 }
219         }
220 }
221
222 /**
223  * Creates meta boxes for any taxonomy menu item.
224  *
225  * @since 3.0.0
226  */
227 function wp_nav_menu_taxonomy_meta_boxes() {
228         $taxonomies = get_taxonomies( array( 'show_in_nav_menus' => true ), 'object' );
229
230         if ( !$taxonomies )
231                 return;
232
233         foreach ( $taxonomies as $tax ) {
234                 /** This filter is documented in wp-admin/includes/nav-menu.php */
235                 $tax = apply_filters( 'nav_menu_meta_box_object', $tax );
236                 if ( $tax ) {
237                         $id = $tax->name;
238                         add_meta_box( "add-{$id}", $tax->labels->name, 'wp_nav_menu_item_taxonomy_meta_box', 'nav-menus', 'side', 'default', $tax );
239                 }
240         }
241 }
242
243 /**
244  * Check whether to disable the Menu Locations meta box submit button
245  *
246  * @since 3.6.0
247  *
248  * @global bool $one_theme_location_no_menus to determine if no menus exist
249  *
250  * @param int|string $nav_menu_selected_id (id, name or slug) of the currently-selected menu
251  * @return string Disabled attribute if at least one menu exists, false if not
252  */
253 function wp_nav_menu_disabled_check( $nav_menu_selected_id ) {
254         global $one_theme_location_no_menus;
255
256         if ( $one_theme_location_no_menus )
257                 return false;
258
259         return disabled( $nav_menu_selected_id, 0 );
260 }
261
262 /**
263  * Displays a meta box for the custom links menu item.
264  *
265  * @since 3.0.0
266  *
267  * @global int        $_nav_menu_placeholder
268  * @global int|string $nav_menu_selected_id
269  */
270 function wp_nav_menu_item_link_meta_box() {
271         global $_nav_menu_placeholder, $nav_menu_selected_id;
272
273         $_nav_menu_placeholder = 0 > $_nav_menu_placeholder ? $_nav_menu_placeholder - 1 : -1;
274
275         ?>
276         <div class="customlinkdiv" id="customlinkdiv">
277                 <input type="hidden" value="custom" name="menu-item[<?php echo $_nav_menu_placeholder; ?>][menu-item-type]" />
278                 <p id="menu-item-url-wrap" class="wp-clearfix">
279                         <label class="howto" for="custom-menu-item-url"><?php _e( 'URL' ); ?></label>
280                         <input id="custom-menu-item-url" name="menu-item[<?php echo $_nav_menu_placeholder; ?>][menu-item-url]" type="text" class="code menu-item-textbox" value="http://" />
281                 </p>
282
283                 <p id="menu-item-name-wrap" class="wp-clearfix">
284                         <label class="howto" for="custom-menu-item-name"><?php _e( 'Link Text' ); ?></label>
285                         <input id="custom-menu-item-name" name="menu-item[<?php echo $_nav_menu_placeholder; ?>][menu-item-title]" type="text" class="regular-text menu-item-textbox" />
286                 </p>
287
288                 <p class="button-controls wp-clearfix">
289                         <span class="add-to-menu">
290                                 <input type="submit"<?php wp_nav_menu_disabled_check( $nav_menu_selected_id ); ?> class="button submit-add-to-menu right" value="<?php esc_attr_e('Add to Menu'); ?>" name="add-custom-menu-item" id="submit-customlinkdiv" />
291                                 <span class="spinner"></span>
292                         </span>
293                 </p>
294
295         </div><!-- /.customlinkdiv -->
296         <?php
297 }
298
299 /**
300  * Displays a meta box for a post type menu item.
301  *
302  * @since 3.0.0
303  *
304  * @global int        $_nav_menu_placeholder
305  * @global int|string $nav_menu_selected_id
306  *
307  * @param string $object Not used.
308  * @param array  $box {
309  *     Post type menu item meta box arguments.
310  *
311  *     @type string       $id       Meta box 'id' attribute.
312  *     @type string       $title    Meta box title.
313  *     @type string       $callback Meta box display callback.
314  *     @type WP_Post_Type $args     Extra meta box arguments (the post type object for this meta box).
315  * }
316  */
317 function wp_nav_menu_item_post_type_meta_box( $object, $box ) {
318         global $_nav_menu_placeholder, $nav_menu_selected_id;
319
320         $post_type_name = $box['args']->name;
321
322         // Paginate browsing for large numbers of post objects.
323         $per_page = 50;
324         $pagenum = isset( $_REQUEST[$post_type_name . '-tab'] ) && isset( $_REQUEST['paged'] ) ? absint( $_REQUEST['paged'] ) : 1;
325         $offset = 0 < $pagenum ? $per_page * ( $pagenum - 1 ) : 0;
326
327         $args = array(
328                 'offset' => $offset,
329                 'order' => 'ASC',
330                 'orderby' => 'title',
331                 'posts_per_page' => $per_page,
332                 'post_type' => $post_type_name,
333                 'suppress_filters' => true,
334                 'update_post_term_cache' => false,
335                 'update_post_meta_cache' => false
336         );
337
338         if ( isset( $box['args']->_default_query ) )
339                 $args = array_merge($args, (array) $box['args']->_default_query );
340
341         // @todo transient caching of these results with proper invalidation on updating of a post of this type
342         $get_posts = new WP_Query;
343         $posts = $get_posts->query( $args );
344         if ( ! $get_posts->post_count ) {
345                 echo '<p>' . __( 'No items.' ) . '</p>';
346                 return;
347         }
348
349         $num_pages = $get_posts->max_num_pages;
350
351         $page_links = paginate_links( array(
352                 'base' => add_query_arg(
353                         array(
354                                 $post_type_name . '-tab' => 'all',
355                                 'paged' => '%#%',
356                                 'item-type' => 'post_type',
357                                 'item-object' => $post_type_name,
358                         )
359                 ),
360                 'format' => '',
361                 'prev_text'          => '<span aria-label="' . esc_attr__( 'Previous page' ) . '">' . __( '&laquo;' ) . '</span>',
362                 'next_text'          => '<span aria-label="' . esc_attr__( 'Next page' ) . '">' . __( '&raquo;' ) . '</span>',
363                 'before_page_number' => '<span class="screen-reader-text">' . __( 'Page' ) . '</span> ',
364                 'total'   => $num_pages,
365                 'current' => $pagenum
366         ));
367
368         $db_fields = false;
369         if ( is_post_type_hierarchical( $post_type_name ) ) {
370                 $db_fields = array( 'parent' => 'post_parent', 'id' => 'ID' );
371         }
372
373         $walker = new Walker_Nav_Menu_Checklist( $db_fields );
374
375         $current_tab = 'most-recent';
376         if ( isset( $_REQUEST[$post_type_name . '-tab'] ) && in_array( $_REQUEST[$post_type_name . '-tab'], array('all', 'search') ) ) {
377                 $current_tab = $_REQUEST[$post_type_name . '-tab'];
378         }
379
380         if ( ! empty( $_REQUEST['quick-search-posttype-' . $post_type_name] ) ) {
381                 $current_tab = 'search';
382         }
383
384         $removed_args = array(
385                 'action',
386                 'customlink-tab',
387                 'edit-menu-item',
388                 'menu-item',
389                 'page-tab',
390                 '_wpnonce',
391         );
392
393         ?>
394         <div id="posttype-<?php echo $post_type_name; ?>" class="posttypediv">
395                 <ul id="posttype-<?php echo $post_type_name; ?>-tabs" class="posttype-tabs add-menu-item-tabs">
396                         <li <?php echo ( 'most-recent' == $current_tab ? ' class="tabs"' : '' ); ?>>
397                                 <a class="nav-tab-link" data-type="tabs-panel-posttype-<?php echo esc_attr( $post_type_name ); ?>-most-recent" href="<?php if ( $nav_menu_selected_id ) echo esc_url(add_query_arg($post_type_name . '-tab', 'most-recent', remove_query_arg($removed_args))); ?>#tabs-panel-posttype-<?php echo $post_type_name; ?>-most-recent">
398                                         <?php _e( 'Most Recent' ); ?>
399                                 </a>
400                         </li>
401                         <li <?php echo ( 'all' == $current_tab ? ' class="tabs"' : '' ); ?>>
402                                 <a class="nav-tab-link" data-type="<?php echo esc_attr( $post_type_name ); ?>-all" href="<?php if ( $nav_menu_selected_id ) echo esc_url(add_query_arg($post_type_name . '-tab', 'all', remove_query_arg($removed_args))); ?>#<?php echo $post_type_name; ?>-all">
403                                         <?php _e( 'View All' ); ?>
404                                 </a>
405                         </li>
406                         <li <?php echo ( 'search' == $current_tab ? ' class="tabs"' : '' ); ?>>
407                                 <a class="nav-tab-link" data-type="tabs-panel-posttype-<?php echo esc_attr( $post_type_name ); ?>-search" href="<?php if ( $nav_menu_selected_id ) echo esc_url(add_query_arg($post_type_name . '-tab', 'search', remove_query_arg($removed_args))); ?>#tabs-panel-posttype-<?php echo $post_type_name; ?>-search">
408                                         <?php _e( 'Search'); ?>
409                                 </a>
410                         </li>
411                 </ul><!-- .posttype-tabs -->
412
413                 <div id="tabs-panel-posttype-<?php echo $post_type_name; ?>-most-recent" class="tabs-panel <?php
414                         echo ( 'most-recent' == $current_tab ? 'tabs-panel-active' : 'tabs-panel-inactive' );
415                 ?>">
416                         <ul id="<?php echo $post_type_name; ?>checklist-most-recent" class="categorychecklist form-no-clear">
417                                 <?php
418                                 $recent_args = array_merge( $args, array( 'orderby' => 'post_date', 'order' => 'DESC', 'posts_per_page' => 15 ) );
419                                 $most_recent = $get_posts->query( $recent_args );
420                                 $args['walker'] = $walker;
421
422                                 /**
423                                  * Filters the posts displayed in the 'Most Recent' tab of the current
424                                  * post type's menu items meta box.
425                                  *
426                                  * The dynamic portion of the hook name, `$post_type_name`, refers to the post type name.
427                                  *
428                                  * @since 4.3.0
429                                  *
430                                  * @param array $most_recent An array of post objects being listed.
431                                  * @param array $args        An array of WP_Query arguments.
432                                  * @param array $box         Arguments passed to wp_nav_menu_item_post_type_meta_box().
433                                  */
434                                 $most_recent = apply_filters( "nav_menu_items_{$post_type_name}_recent", $most_recent, $args, $box );
435
436                                 echo walk_nav_menu_tree( array_map('wp_setup_nav_menu_item', $most_recent), 0, (object) $args );
437                                 ?>
438                         </ul>
439                 </div><!-- /.tabs-panel -->
440
441                 <div class="tabs-panel <?php
442                         echo ( 'search' == $current_tab ? 'tabs-panel-active' : 'tabs-panel-inactive' );
443                 ?>" id="tabs-panel-posttype-<?php echo $post_type_name; ?>-search">
444                         <?php
445                         if ( isset( $_REQUEST['quick-search-posttype-' . $post_type_name] ) ) {
446                                 $searched = esc_attr( $_REQUEST['quick-search-posttype-' . $post_type_name] );
447                                 $search_results = get_posts( array( 's' => $searched, 'post_type' => $post_type_name, 'fields' => 'all', 'order' => 'DESC', ) );
448                         } else {
449                                 $searched = '';
450                                 $search_results = array();
451                         }
452                         ?>
453                         <p class="quick-search-wrap">
454                                 <label for="quick-search-posttype-<?php echo $post_type_name; ?>" class="screen-reader-text"><?php _e( 'Search' ); ?></label>
455                                 <input type="search" class="quick-search" value="<?php echo $searched; ?>" name="quick-search-posttype-<?php echo $post_type_name; ?>" id="quick-search-posttype-<?php echo $post_type_name; ?>" />
456                                 <span class="spinner"></span>
457                                 <?php submit_button( __( 'Search' ), 'small quick-search-submit hide-if-js', 'submit', false, array( 'id' => 'submit-quick-search-posttype-' . $post_type_name ) ); ?>
458                         </p>
459
460                         <ul id="<?php echo $post_type_name; ?>-search-checklist" data-wp-lists="list:<?php echo $post_type_name?>" class="categorychecklist form-no-clear">
461                         <?php if ( ! empty( $search_results ) && ! is_wp_error( $search_results ) ) : ?>
462                                 <?php
463                                 $args['walker'] = $walker;
464                                 echo walk_nav_menu_tree( array_map('wp_setup_nav_menu_item', $search_results), 0, (object) $args );
465                                 ?>
466                         <?php elseif ( is_wp_error( $search_results ) ) : ?>
467                                 <li><?php echo $search_results->get_error_message(); ?></li>
468                         <?php elseif ( ! empty( $searched ) ) : ?>
469                                 <li><?php _e('No results found.'); ?></li>
470                         <?php endif; ?>
471                         </ul>
472                 </div><!-- /.tabs-panel -->
473
474                 <div id="<?php echo $post_type_name; ?>-all" class="tabs-panel tabs-panel-view-all <?php
475                         echo ( 'all' == $current_tab ? 'tabs-panel-active' : 'tabs-panel-inactive' );
476                 ?>">
477                         <?php if ( ! empty( $page_links ) ) : ?>
478                                 <div class="add-menu-item-pagelinks">
479                                         <?php echo $page_links; ?>
480                                 </div>
481                         <?php endif; ?>
482                         <ul id="<?php echo $post_type_name; ?>checklist" data-wp-lists="list:<?php echo $post_type_name?>" class="categorychecklist form-no-clear">
483                                 <?php
484                                 $args['walker'] = $walker;
485
486                                 /*
487                                  * If we're dealing with pages, let's put a checkbox for the front
488                                  * page at the top of the list.
489                                  */
490                                 if ( 'page' == $post_type_name ) {
491                                         $front_page = 'page' == get_option('show_on_front') ? (int) get_option( 'page_on_front' ) : 0;
492                                         if ( ! empty( $front_page ) ) {
493                                                 $front_page_obj = get_post( $front_page );
494                                                 $front_page_obj->front_or_home = true;
495                                                 array_unshift( $posts, $front_page_obj );
496                                         } else {
497                                                 $_nav_menu_placeholder = ( 0 > $_nav_menu_placeholder ) ? intval($_nav_menu_placeholder) - 1 : -1;
498                                                 array_unshift( $posts, (object) array(
499                                                         'front_or_home' => true,
500                                                         'ID' => 0,
501                                                         'object_id' => $_nav_menu_placeholder,
502                                                         'post_content' => '',
503                                                         'post_excerpt' => '',
504                                                         'post_parent' => '',
505                                                         'post_title' => _x('Home', 'nav menu home label'),
506                                                         'post_type' => 'nav_menu_item',
507                                                         'type' => 'custom',
508                                                         'url' => home_url('/'),
509                                                 ) );
510                                         }
511                                 }
512
513                                 $post_type = get_post_type_object( $post_type_name );
514
515                                 if ( $post_type->has_archive ) {
516                                         $_nav_menu_placeholder = ( 0 > $_nav_menu_placeholder ) ? intval($_nav_menu_placeholder) - 1 : -1;
517                                         array_unshift( $posts, (object) array(
518                                                 'ID' => 0,
519                                                 'object_id' => $_nav_menu_placeholder,
520                                                 'object'     => $post_type_name,
521                                                 'post_content' => '',
522                                                 'post_excerpt' => '',
523                                                 'post_title' => $post_type->labels->archives,
524                                                 'post_type' => 'nav_menu_item',
525                                                 'type' => 'post_type_archive',
526                                                 'url' => get_post_type_archive_link( $post_type_name ),
527                                         ) );
528                                 }
529
530                                 /**
531                                  * Filters the posts displayed in the 'View All' tab of the current
532                                  * post type's menu items meta box.
533                                  *
534                                  * The dynamic portion of the hook name, `$post_type_name`, refers
535                                  * to the slug of the current post type.
536                                  *
537                                  * @since 3.2.0
538                                  * @since 4.6.0 Converted the `$post_type` parameter to accept a WP_Post_Type object.
539                                  *
540                                  * @see WP_Query::query()
541                                  *
542                                  * @param array        $posts     The posts for the current post type.
543                                  * @param array        $args      An array of WP_Query arguments.
544                                  * @param WP_Post_Type $post_type The current post type object for this menu item meta box.
545                                  */
546                                 $posts = apply_filters( "nav_menu_items_{$post_type_name}", $posts, $args, $post_type );
547
548                                 $checkbox_items = walk_nav_menu_tree( array_map('wp_setup_nav_menu_item', $posts), 0, (object) $args );
549
550                                 if ( 'all' == $current_tab && ! empty( $_REQUEST['selectall'] ) ) {
551                                         $checkbox_items = preg_replace('/(type=(.)checkbox(\2))/', '$1 checked=$2checked$2', $checkbox_items);
552
553                                 }
554
555                                 echo $checkbox_items;
556                                 ?>
557                         </ul>
558                         <?php if ( ! empty( $page_links ) ) : ?>
559                                 <div class="add-menu-item-pagelinks">
560                                         <?php echo $page_links; ?>
561                                 </div>
562                         <?php endif; ?>
563                 </div><!-- /.tabs-panel -->
564
565                 <p class="button-controls wp-clearfix">
566                         <span class="list-controls">
567                                 <a href="<?php
568                                         echo esc_url( add_query_arg(
569                                                 array(
570                                                         $post_type_name . '-tab' => 'all',
571                                                         'selectall' => 1,
572                                                 ),
573                                                 remove_query_arg( $removed_args )
574                                         ));
575                                 ?>#posttype-<?php echo $post_type_name; ?>" class="select-all aria-button-if-js"><?php _e( 'Select All' ); ?></a>
576                         </span>
577
578                         <span class="add-to-menu">
579                                 <input type="submit"<?php wp_nav_menu_disabled_check( $nav_menu_selected_id ); ?> class="button submit-add-to-menu right" value="<?php esc_attr_e( 'Add to Menu' ); ?>" name="add-post-type-menu-item" id="<?php echo esc_attr( 'submit-posttype-' . $post_type_name ); ?>" />
580                                 <span class="spinner"></span>
581                         </span>
582                 </p>
583
584         </div><!-- /.posttypediv -->
585         <?php
586 }
587
588 /**
589  * Displays a meta box for a taxonomy menu item.
590  *
591  * @since 3.0.0
592  *
593  * @global int|string $nav_menu_selected_id
594  *
595  * @param string $object Not used.
596  * @param array  $box {
597  *     Taxonomy menu item meta box arguments.
598  *
599  *     @type string $id       Meta box 'id' attribute.
600  *     @type string $title    Meta box title.
601  *     @type string $callback Meta box display callback.
602  *     @type object $args     Extra meta box arguments (the taxonomy object for this meta box).
603  * }
604  */
605 function wp_nav_menu_item_taxonomy_meta_box( $object, $box ) {
606         global $nav_menu_selected_id;
607         $taxonomy_name = $box['args']->name;
608
609         // Paginate browsing for large numbers of objects.
610         $per_page = 50;
611         $pagenum = isset( $_REQUEST[$taxonomy_name . '-tab'] ) && isset( $_REQUEST['paged'] ) ? absint( $_REQUEST['paged'] ) : 1;
612         $offset = 0 < $pagenum ? $per_page * ( $pagenum - 1 ) : 0;
613
614         $args = array(
615                 'child_of' => 0,
616                 'exclude' => '',
617                 'hide_empty' => false,
618                 'hierarchical' => 1,
619                 'include' => '',
620                 'number' => $per_page,
621                 'offset' => $offset,
622                 'order' => 'ASC',
623                 'orderby' => 'name',
624                 'pad_counts' => false,
625         );
626
627         $terms = get_terms( $taxonomy_name, $args );
628
629         if ( ! $terms || is_wp_error($terms) ) {
630                 echo '<p>' . __( 'No items.' ) . '</p>';
631                 return;
632         }
633
634         $num_pages = ceil( wp_count_terms( $taxonomy_name , array_merge( $args, array('number' => '', 'offset' => '') ) ) / $per_page );
635
636         $page_links = paginate_links( array(
637                 'base' => add_query_arg(
638                         array(
639                                 $taxonomy_name . '-tab' => 'all',
640                                 'paged' => '%#%',
641                                 'item-type' => 'taxonomy',
642                                 'item-object' => $taxonomy_name,
643                         )
644                 ),
645                 'format' => '',
646                 'prev_text'          => '<span aria-label="' . esc_attr__( 'Previous page' ) . '">' . __( '&laquo;' ) . '</span>',
647                 'next_text'          => '<span aria-label="' . esc_attr__( 'Next page' ) . '">' . __( '&raquo;' ) . '</span>',
648                 'before_page_number' => '<span class="screen-reader-text">' . __( 'Page' ) . '</span> ',
649                 'total'   => $num_pages,
650                 'current' => $pagenum
651         ));
652
653         $db_fields = false;
654         if ( is_taxonomy_hierarchical( $taxonomy_name ) ) {
655                 $db_fields = array( 'parent' => 'parent', 'id' => 'term_id' );
656         }
657
658         $walker = new Walker_Nav_Menu_Checklist( $db_fields );
659
660         $current_tab = 'most-used';
661         if ( isset( $_REQUEST[$taxonomy_name . '-tab'] ) && in_array( $_REQUEST[$taxonomy_name . '-tab'], array('all', 'most-used', 'search') ) ) {
662                 $current_tab = $_REQUEST[$taxonomy_name . '-tab'];
663         }
664
665         if ( ! empty( $_REQUEST['quick-search-taxonomy-' . $taxonomy_name] ) ) {
666                 $current_tab = 'search';
667         }
668
669         $removed_args = array(
670                 'action',
671                 'customlink-tab',
672                 'edit-menu-item',
673                 'menu-item',
674                 'page-tab',
675                 '_wpnonce',
676         );
677
678         ?>
679         <div id="taxonomy-<?php echo $taxonomy_name; ?>" class="taxonomydiv">
680                 <ul id="taxonomy-<?php echo $taxonomy_name; ?>-tabs" class="taxonomy-tabs add-menu-item-tabs">
681                         <li <?php echo ( 'most-used' == $current_tab ? ' class="tabs"' : '' ); ?>>
682                                 <a class="nav-tab-link" data-type="tabs-panel-<?php echo esc_attr( $taxonomy_name ); ?>-pop" href="<?php if ( $nav_menu_selected_id ) echo esc_url(add_query_arg($taxonomy_name . '-tab', 'most-used', remove_query_arg($removed_args))); ?>#tabs-panel-<?php echo $taxonomy_name; ?>-pop">
683                                         <?php _e( 'Most Used' ); ?>
684                                 </a>
685                         </li>
686                         <li <?php echo ( 'all' == $current_tab ? ' class="tabs"' : '' ); ?>>
687                                 <a class="nav-tab-link" data-type="tabs-panel-<?php echo esc_attr( $taxonomy_name ); ?>-all" href="<?php if ( $nav_menu_selected_id ) echo esc_url(add_query_arg($taxonomy_name . '-tab', 'all', remove_query_arg($removed_args))); ?>#tabs-panel-<?php echo $taxonomy_name; ?>-all">
688                                         <?php _e( 'View All' ); ?>
689                                 </a>
690                         </li>
691                         <li <?php echo ( 'search' == $current_tab ? ' class="tabs"' : '' ); ?>>
692                                 <a class="nav-tab-link" data-type="tabs-panel-search-taxonomy-<?php echo esc_attr( $taxonomy_name ); ?>" href="<?php if ( $nav_menu_selected_id ) echo esc_url(add_query_arg($taxonomy_name . '-tab', 'search', remove_query_arg($removed_args))); ?>#tabs-panel-search-taxonomy-<?php echo $taxonomy_name; ?>">
693                                         <?php _e( 'Search' ); ?>
694                                 </a>
695                         </li>
696                 </ul><!-- .taxonomy-tabs -->
697
698                 <div id="tabs-panel-<?php echo $taxonomy_name; ?>-pop" class="tabs-panel <?php
699                         echo ( 'most-used' == $current_tab ? 'tabs-panel-active' : 'tabs-panel-inactive' );
700                 ?>">
701                         <ul id="<?php echo $taxonomy_name; ?>checklist-pop" class="categorychecklist form-no-clear" >
702                                 <?php
703                                 $popular_terms = get_terms( $taxonomy_name, array( 'orderby' => 'count', 'order' => 'DESC', 'number' => 10, 'hierarchical' => false ) );
704                                 $args['walker'] = $walker;
705                                 echo walk_nav_menu_tree( array_map('wp_setup_nav_menu_item', $popular_terms), 0, (object) $args );
706                                 ?>
707                         </ul>
708                 </div><!-- /.tabs-panel -->
709
710                 <div id="tabs-panel-<?php echo $taxonomy_name; ?>-all" class="tabs-panel tabs-panel-view-all <?php
711                         echo ( 'all' == $current_tab ? 'tabs-panel-active' : 'tabs-panel-inactive' );
712                 ?>">
713                         <?php if ( ! empty( $page_links ) ) : ?>
714                                 <div class="add-menu-item-pagelinks">
715                                         <?php echo $page_links; ?>
716                                 </div>
717                         <?php endif; ?>
718                         <ul id="<?php echo $taxonomy_name; ?>checklist" data-wp-lists="list:<?php echo $taxonomy_name?>" class="categorychecklist form-no-clear">
719                                 <?php
720                                 $args['walker'] = $walker;
721                                 echo walk_nav_menu_tree( array_map('wp_setup_nav_menu_item', $terms), 0, (object) $args );
722                                 ?>
723                         </ul>
724                         <?php if ( ! empty( $page_links ) ) : ?>
725                                 <div class="add-menu-item-pagelinks">
726                                         <?php echo $page_links; ?>
727                                 </div>
728                         <?php endif; ?>
729                 </div><!-- /.tabs-panel -->
730
731                 <div class="tabs-panel <?php
732                         echo ( 'search' == $current_tab ? 'tabs-panel-active' : 'tabs-panel-inactive' );
733                 ?>" id="tabs-panel-search-taxonomy-<?php echo $taxonomy_name; ?>">
734                         <?php
735                         if ( isset( $_REQUEST['quick-search-taxonomy-' . $taxonomy_name] ) ) {
736                                 $searched = esc_attr( $_REQUEST['quick-search-taxonomy-' . $taxonomy_name] );
737                                 $search_results = get_terms( $taxonomy_name, array( 'name__like' => $searched, 'fields' => 'all', 'orderby' => 'count', 'order' => 'DESC', 'hierarchical' => false ) );
738                         } else {
739                                 $searched = '';
740                                 $search_results = array();
741                         }
742                         ?>
743                         <p class="quick-search-wrap">
744                                 <label for="quick-search-taxonomy-<?php echo $taxonomy_name; ?>" class="screen-reader-text"><?php _e( 'Search' ); ?></label>
745                                 <input type="search" class="quick-search" value="<?php echo $searched; ?>" name="quick-search-taxonomy-<?php echo $taxonomy_name; ?>" id="quick-search-taxonomy-<?php echo $taxonomy_name; ?>" />
746                                 <span class="spinner"></span>
747                                 <?php submit_button( __( 'Search' ), 'small quick-search-submit hide-if-js', 'submit', false, array( 'id' => 'submit-quick-search-taxonomy-' . $taxonomy_name ) ); ?>
748                         </p>
749
750                         <ul id="<?php echo $taxonomy_name; ?>-search-checklist" data-wp-lists="list:<?php echo $taxonomy_name?>" class="categorychecklist form-no-clear">
751                         <?php if ( ! empty( $search_results ) && ! is_wp_error( $search_results ) ) : ?>
752                                 <?php
753                                 $args['walker'] = $walker;
754                                 echo walk_nav_menu_tree( array_map('wp_setup_nav_menu_item', $search_results), 0, (object) $args );
755                                 ?>
756                         <?php elseif ( is_wp_error( $search_results ) ) : ?>
757                                 <li><?php echo $search_results->get_error_message(); ?></li>
758                         <?php elseif ( ! empty( $searched ) ) : ?>
759                                 <li><?php _e('No results found.'); ?></li>
760                         <?php endif; ?>
761                         </ul>
762                 </div><!-- /.tabs-panel -->
763
764                 <p class="button-controls wp-clearfix">
765                         <span class="list-controls">
766                                 <a href="<?php
767                                         echo esc_url(add_query_arg(
768                                                 array(
769                                                         $taxonomy_name . '-tab' => 'all',
770                                                         'selectall' => 1,
771                                                 ),
772                                                 remove_query_arg($removed_args)
773                                         ));
774                                 ?>#taxonomy-<?php echo $taxonomy_name; ?>" class="select-all aria-button-if-js"><?php _e( 'Select All' ); ?></a>
775                         </span>
776
777                         <span class="add-to-menu">
778                                 <input type="submit"<?php wp_nav_menu_disabled_check( $nav_menu_selected_id ); ?> class="button submit-add-to-menu right" value="<?php esc_attr_e( 'Add to Menu' ); ?>" name="add-taxonomy-menu-item" id="<?php echo esc_attr( 'submit-taxonomy-' . $taxonomy_name ); ?>" />
779                                 <span class="spinner"></span>
780                         </span>
781                 </p>
782
783         </div><!-- /.taxonomydiv -->
784         <?php
785 }
786
787 /**
788  * Save posted nav menu item data.
789  *
790  * @since 3.0.0
791  *
792  * @param int $menu_id The menu ID for which to save this item. $menu_id of 0 makes a draft, orphaned menu item.
793  * @param array $menu_data The unsanitized posted menu item data.
794  * @return array The database IDs of the items saved
795  */
796 function wp_save_nav_menu_items( $menu_id = 0, $menu_data = array() ) {
797         $menu_id = (int) $menu_id;
798         $items_saved = array();
799
800         if ( 0 == $menu_id || is_nav_menu( $menu_id ) ) {
801
802                 // Loop through all the menu items' POST values.
803                 foreach ( (array) $menu_data as $_possible_db_id => $_item_object_data ) {
804                         if (
805                                 // Checkbox is not checked.
806                                 empty( $_item_object_data['menu-item-object-id'] ) &&
807                                 (
808                                         // And item type either isn't set.
809                                         ! isset( $_item_object_data['menu-item-type'] ) ||
810                                         // Or URL is the default.
811                                         in_array( $_item_object_data['menu-item-url'], array( 'http://', '' ) ) ||
812                                         ! ( 'custom' == $_item_object_data['menu-item-type'] && ! isset( $_item_object_data['menu-item-db-id'] ) ) || // or it's not a custom menu item (but not the custom home page)
813                                         // Or it *is* a custom menu item that already exists.
814                                         ! empty( $_item_object_data['menu-item-db-id'] )
815                                 )
816                         ) {
817                                 // Then this potential menu item is not getting added to this menu.
818                                 continue;
819                         }
820
821                         // If this possible menu item doesn't actually have a menu database ID yet.
822                         if (
823                                 empty( $_item_object_data['menu-item-db-id'] ) ||
824                                 ( 0 > $_possible_db_id ) ||
825                                 $_possible_db_id != $_item_object_data['menu-item-db-id']
826                         ) {
827                                 $_actual_db_id = 0;
828                         } else {
829                                 $_actual_db_id = (int) $_item_object_data['menu-item-db-id'];
830                         }
831
832                         $args = array(
833                                 'menu-item-db-id' => ( isset( $_item_object_data['menu-item-db-id'] ) ? $_item_object_data['menu-item-db-id'] : '' ),
834                                 'menu-item-object-id' => ( isset( $_item_object_data['menu-item-object-id'] ) ? $_item_object_data['menu-item-object-id'] : '' ),
835                                 'menu-item-object' => ( isset( $_item_object_data['menu-item-object'] ) ? $_item_object_data['menu-item-object'] : '' ),
836                                 'menu-item-parent-id' => ( isset( $_item_object_data['menu-item-parent-id'] ) ? $_item_object_data['menu-item-parent-id'] : '' ),
837                                 'menu-item-position' => ( isset( $_item_object_data['menu-item-position'] ) ? $_item_object_data['menu-item-position'] : '' ),
838                                 'menu-item-type' => ( isset( $_item_object_data['menu-item-type'] ) ? $_item_object_data['menu-item-type'] : '' ),
839                                 'menu-item-title' => ( isset( $_item_object_data['menu-item-title'] ) ? $_item_object_data['menu-item-title'] : '' ),
840                                 'menu-item-url' => ( isset( $_item_object_data['menu-item-url'] ) ? $_item_object_data['menu-item-url'] : '' ),
841                                 'menu-item-description' => ( isset( $_item_object_data['menu-item-description'] ) ? $_item_object_data['menu-item-description'] : '' ),
842                                 'menu-item-attr-title' => ( isset( $_item_object_data['menu-item-attr-title'] ) ? $_item_object_data['menu-item-attr-title'] : '' ),
843                                 'menu-item-target' => ( isset( $_item_object_data['menu-item-target'] ) ? $_item_object_data['menu-item-target'] : '' ),
844                                 'menu-item-classes' => ( isset( $_item_object_data['menu-item-classes'] ) ? $_item_object_data['menu-item-classes'] : '' ),
845                                 'menu-item-xfn' => ( isset( $_item_object_data['menu-item-xfn'] ) ? $_item_object_data['menu-item-xfn'] : '' ),
846                         );
847
848                         $items_saved[] = wp_update_nav_menu_item( $menu_id, $_actual_db_id, $args );
849
850                 }
851         }
852         return $items_saved;
853 }
854
855 /**
856  * Adds custom arguments to some of the meta box object types.
857  *
858  * @since 3.0.0
859  *
860  * @access private
861  *
862  * @param object $object The post type or taxonomy meta-object.
863  * @return object The post type of taxonomy object.
864  */
865 function _wp_nav_menu_meta_box_object( $object = null ) {
866         if ( isset( $object->name ) ) {
867
868                 if ( 'page' == $object->name ) {
869                         $object->_default_query = array(
870                                 'orderby' => 'menu_order title',
871                                 'post_status' => 'publish',
872                         );
873
874                 // Posts should show only published items.
875                 } elseif ( 'post' == $object->name ) {
876                         $object->_default_query = array(
877                                 'post_status' => 'publish',
878                         );
879
880                 // Categories should be in reverse chronological order.
881                 } elseif ( 'category' == $object->name ) {
882                         $object->_default_query = array(
883                                 'orderby' => 'id',
884                                 'order' => 'DESC',
885                         );
886
887                 // Custom post types should show only published items.
888                 } else {
889                         $object->_default_query = array(
890                                 'post_status' => 'publish',
891                         );
892                 }
893         }
894
895         return $object;
896 }
897
898 /**
899  * Returns the menu formatted to edit.
900  *
901  * @since 3.0.0
902  *
903  * @param int $menu_id Optional. The ID of the menu to format. Default 0.
904  * @return string|WP_Error $output The menu formatted to edit or error object on failure.
905  */
906 function wp_get_nav_menu_to_edit( $menu_id = 0 ) {
907         $menu = wp_get_nav_menu_object( $menu_id );
908
909         // If the menu exists, get its items.
910         if ( is_nav_menu( $menu ) ) {
911                 $menu_items = wp_get_nav_menu_items( $menu->term_id, array('post_status' => 'any') );
912                 $result = '<div id="menu-instructions" class="post-body-plain';
913                 $result .= ( ! empty($menu_items) ) ? ' menu-instructions-inactive">' : '">';
914                 $result .= '<p>' . __( 'Add menu items from the column on the left.' ) . '</p>';
915                 $result .= '</div>';
916
917                 if ( empty($menu_items) )
918                         return $result . ' <ul class="menu" id="menu-to-edit"> </ul>';
919
920                 /**
921                  * Filters the Walker class used when adding nav menu items.
922                  *
923                  * @since 3.0.0
924                  *
925                  * @param string $class   The walker class to use. Default 'Walker_Nav_Menu_Edit'.
926                  * @param int    $menu_id ID of the menu being rendered.
927                  */
928                 $walker_class_name = apply_filters( 'wp_edit_nav_menu_walker', 'Walker_Nav_Menu_Edit', $menu_id );
929
930                 if ( class_exists( $walker_class_name ) ) {
931                         $walker = new $walker_class_name;
932                 } else {
933                         return new WP_Error( 'menu_walker_not_exist',
934                                 /* translators: %s: walker class name */
935                                 sprintf( __( 'The Walker class named %s does not exist.' ),
936                                         '<strong>' . $walker_class_name . '</strong>'
937                                 )
938                         );
939                 }
940
941                 $some_pending_menu_items = $some_invalid_menu_items = false;
942                 foreach ( (array) $menu_items as $menu_item ) {
943                         if ( isset( $menu_item->post_status ) && 'draft' == $menu_item->post_status )
944                                 $some_pending_menu_items = true;
945                         if ( ! empty( $menu_item->_invalid ) )
946                                 $some_invalid_menu_items = true;
947                 }
948
949                 if ( $some_pending_menu_items ) {
950                         $result .= '<div class="notice notice-info notice-alt inline"><p>' . __( 'Click Save Menu to make pending menu items public.' ) . '</p></div>';
951                 }
952
953                 if ( $some_invalid_menu_items ) {
954                         $result .= '<div class="notice notice-error notice-alt inline"><p>' . __( 'There are some invalid menu items. Please check or delete them.' ) . '</p></div>';
955                 }
956
957                 $result .= '<ul class="menu" id="menu-to-edit"> ';
958                 $result .= walk_nav_menu_tree( array_map('wp_setup_nav_menu_item', $menu_items), 0, (object) array('walker' => $walker ) );
959                 $result .= ' </ul> ';
960                 return $result;
961         } elseif ( is_wp_error( $menu ) ) {
962                 return $menu;
963         }
964
965 }
966
967 /**
968  * Returns the columns for the nav menus page.
969  *
970  * @since 3.0.0
971  *
972  * @return array Columns.
973  */
974 function wp_nav_menu_manage_columns() {
975         return array(
976                 '_title'          => __( 'Show advanced menu properties' ),
977                 'cb'              => '<input type="checkbox" />',
978                 'link-target'     => __( 'Link Target' ),
979                 'title-attribute' => __( 'Title Attribute' ),
980                 'css-classes'     => __( 'CSS Classes' ),
981                 'xfn'             => __( 'Link Relationship (XFN)' ),
982                 'description'     => __( 'Description' ),
983         );
984 }
985
986 /**
987  * Deletes orphaned draft menu items
988  *
989  * @access private
990  * @since 3.0.0
991  *
992  * @global wpdb $wpdb WordPress database abstraction object.
993  */
994 function _wp_delete_orphaned_draft_menu_items() {
995         global $wpdb;
996         $delete_timestamp = time() - ( DAY_IN_SECONDS * EMPTY_TRASH_DAYS );
997
998         // Delete orphaned draft menu items.
999         $menu_items_to_delete = $wpdb->get_col($wpdb->prepare("SELECT ID FROM $wpdb->posts AS p LEFT JOIN $wpdb->postmeta AS m ON p.ID = m.post_id WHERE post_type = 'nav_menu_item' AND post_status = 'draft' AND meta_key = '_menu_item_orphaned' AND meta_value < '%d'", $delete_timestamp ) );
1000
1001         foreach ( (array) $menu_items_to_delete as $menu_item_id )
1002                 wp_delete_post( $menu_item_id, true );
1003 }
1004
1005 /**
1006  * Saves nav menu items
1007  *
1008  * @since 3.6.0
1009  *
1010  * @param int|string $nav_menu_selected_id (id, slug, or name ) of the currently-selected menu
1011  * @param string $nav_menu_selected_title Title of the currently-selected menu
1012  * @return array $messages The menu updated message
1013  */
1014 function wp_nav_menu_update_menu_items ( $nav_menu_selected_id, $nav_menu_selected_title ) {
1015         $unsorted_menu_items = wp_get_nav_menu_items( $nav_menu_selected_id, array( 'orderby' => 'ID', 'output' => ARRAY_A, 'output_key' => 'ID', 'post_status' => 'draft,publish' ) );
1016         $messages = array();
1017         $menu_items = array();
1018         // Index menu items by db ID
1019         foreach ( $unsorted_menu_items as $_item )
1020                 $menu_items[$_item->db_id] = $_item;
1021
1022         $post_fields = array(
1023                 'menu-item-db-id', 'menu-item-object-id', 'menu-item-object',
1024                 'menu-item-parent-id', 'menu-item-position', 'menu-item-type',
1025                 'menu-item-title', 'menu-item-url', 'menu-item-description',
1026                 'menu-item-attr-title', 'menu-item-target', 'menu-item-classes', 'menu-item-xfn'
1027         );
1028
1029         wp_defer_term_counting( true );
1030         // Loop through all the menu items' POST variables
1031         if ( ! empty( $_POST['menu-item-db-id'] ) ) {
1032                 foreach ( (array) $_POST['menu-item-db-id'] as $_key => $k ) {
1033
1034                         // Menu item title can't be blank
1035                         if ( ! isset( $_POST['menu-item-title'][ $_key ] ) || '' == $_POST['menu-item-title'][ $_key ] )
1036                                 continue;
1037
1038                         $args = array();
1039                         foreach ( $post_fields as $field )
1040                                 $args[$field] = isset( $_POST[$field][$_key] ) ? $_POST[$field][$_key] : '';
1041
1042                         $menu_item_db_id = wp_update_nav_menu_item( $nav_menu_selected_id, ( $_POST['menu-item-db-id'][$_key] != $_key ? 0 : $_key ), $args );
1043
1044                         if ( is_wp_error( $menu_item_db_id ) ) {
1045                                 $messages[] = '<div id="message" class="error"><p>' . $menu_item_db_id->get_error_message() . '</p></div>';
1046                         } else {
1047                                 unset( $menu_items[ $menu_item_db_id ] );
1048                         }
1049                 }
1050         }
1051
1052         // Remove menu items from the menu that weren't in $_POST
1053         if ( ! empty( $menu_items ) ) {
1054                 foreach ( array_keys( $menu_items ) as $menu_item_id ) {
1055                         if ( is_nav_menu_item( $menu_item_id ) ) {
1056                                 wp_delete_post( $menu_item_id );
1057                         }
1058                 }
1059         }
1060
1061         // Store 'auto-add' pages.
1062         $auto_add = ! empty( $_POST['auto-add-pages'] );
1063         $nav_menu_option = (array) get_option( 'nav_menu_options' );
1064         if ( ! isset( $nav_menu_option['auto_add'] ) )
1065                 $nav_menu_option['auto_add'] = array();
1066         if ( $auto_add ) {
1067                 if ( ! in_array( $nav_menu_selected_id, $nav_menu_option['auto_add'] ) )
1068                         $nav_menu_option['auto_add'][] = $nav_menu_selected_id;
1069         } else {
1070                 if ( false !== ( $key = array_search( $nav_menu_selected_id, $nav_menu_option['auto_add'] ) ) )
1071                         unset( $nav_menu_option['auto_add'][$key] );
1072         }
1073         // Remove nonexistent/deleted menus
1074         $nav_menu_option['auto_add'] = array_intersect( $nav_menu_option['auto_add'], wp_get_nav_menus( array( 'fields' => 'ids' ) ) );
1075         update_option( 'nav_menu_options', $nav_menu_option );
1076
1077         wp_defer_term_counting( false );
1078
1079         /** This action is documented in wp-includes/nav-menu.php */
1080         do_action( 'wp_update_nav_menu', $nav_menu_selected_id );
1081
1082         $messages[] = '<div id="message" class="updated notice is-dismissible"><p>' .
1083                 /* translators: %s: nav menu title */
1084                 sprintf( __( '%s has been updated.' ),
1085                         '<strong>' . $nav_menu_selected_title . '</strong>'
1086                 ) . '</p></div>';
1087
1088         unset( $menu_items, $unsorted_menu_items );
1089
1090         return $messages;
1091 }
1092
1093 /**
1094  * If a JSON blob of navigation menu data is in POST data, expand it and inject
1095  * it into `$_POST` to avoid PHP `max_input_vars` limitations. See #14134.
1096  *
1097  * @ignore
1098  * @since 4.5.3
1099  * @access private
1100  */
1101 function _wp_expand_nav_menu_post_data() {
1102         if ( ! isset( $_POST['nav-menu-data'] ) ) {
1103                 return;
1104         }
1105
1106         $data = json_decode( stripslashes( $_POST['nav-menu-data'] ) );
1107
1108         if ( ! is_null( $data ) && $data ) {
1109                 foreach ( $data as $post_input_data ) {
1110                         // For input names that are arrays (e.g. `menu-item-db-id[3][4][5]`),
1111                         // derive the array path keys via regex and set the value in $_POST.
1112                         preg_match( '#([^\[]*)(\[(.+)\])?#', $post_input_data->name, $matches );
1113
1114                         $array_bits = array( $matches[1] );
1115
1116                         if ( isset( $matches[3] ) ) {
1117                                 $array_bits = array_merge( $array_bits, explode( '][', $matches[3] ) );
1118                         }
1119
1120                         $new_post_data = array();
1121
1122                         // Build the new array value from leaf to trunk.
1123                         for ( $i = count( $array_bits ) - 1; $i >= 0; $i -- ) {
1124                                 if ( $i == count( $array_bits ) - 1 ) {
1125                                         $new_post_data[ $array_bits[ $i ] ] = wp_slash( $post_input_data->value );
1126                                 } else {
1127                                         $new_post_data = array( $array_bits[ $i ] => $new_post_data );
1128                                 }
1129                         }
1130
1131                         $_POST = array_replace_recursive( $_POST, $new_post_data );
1132                 }
1133         }
1134 }