You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

1135 lines
41 KiB

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