[ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
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'] ) ? $request['response-format'] : ''; 29 30 if ( ! $response_format || ! in_array( $response_format, array( 'json', 'markup' ), true ) ) { 31 $response_format = 'json'; 32 } 33 34 if ( 'markup' === $response_format ) { 35 $args['walker'] = new Walker_Nav_Menu_Checklist(); 36 } 37 38 if ( 'get-post-item' === $type ) { 39 if ( post_type_exists( $object_type ) ) { 40 if ( isset( $request['ID'] ) ) { 41 $object_id = (int) $request['ID']; 42 43 if ( 'markup' === $response_format ) { 44 echo walk_nav_menu_tree( 45 array_map( 'wp_setup_nav_menu_item', array( get_post( $object_id ) ) ), 46 0, 47 (object) $args 48 ); 49 } elseif ( 'json' === $response_format ) { 50 echo wp_json_encode( 51 array( 52 'ID' => $object_id, 53 'post_title' => get_the_title( $object_id ), 54 'post_type' => get_post_type( $object_id ), 55 ) 56 ); 57 echo "\n"; 58 } 59 } 60 } elseif ( taxonomy_exists( $object_type ) ) { 61 if ( isset( $request['ID'] ) ) { 62 $object_id = (int) $request['ID']; 63 64 if ( 'markup' === $response_format ) { 65 echo walk_nav_menu_tree( 66 array_map( 'wp_setup_nav_menu_item', array( get_term( $object_id, $object_type ) ) ), 67 0, 68 (object) $args 69 ); 70 } elseif ( 'json' === $response_format ) { 71 $post_obj = get_term( $object_id, $object_type ); 72 echo wp_json_encode( 73 array( 74 'ID' => $object_id, 75 'post_title' => $post_obj->name, 76 'post_type' => $object_type, 77 ) 78 ); 79 echo "\n"; 80 } 81 } 82 } 83 } elseif ( preg_match( '/quick-search-(posttype|taxonomy)-([a-zA-Z0-9_-]*\b)/', $type, $matches ) ) { 84 if ( 'posttype' === $matches[1] && get_post_type_object( $matches[2] ) ) { 85 $post_type_obj = _wp_nav_menu_meta_box_object( get_post_type_object( $matches[2] ) ); 86 $query_args = array( 87 'no_found_rows' => true, 88 'update_post_meta_cache' => false, 89 'update_post_term_cache' => false, 90 'posts_per_page' => 10, 91 'post_type' => $matches[2], 92 's' => $query, 93 'search_columns' => array( 'post_title' ), 94 ); 95 /** 96 * Filter the menu quick search arguments. 97 * 98 * @since 6.9.0 99 * 100 * @param array $args { 101 * Menu quick search arguments. 102 * 103 * @type boolean $no_found_rows Whether to return found rows data. Default true. 104 * @type boolean $update_post_meta_cache Whether to update post meta cache. Default false. 105 * @type boolean $update_post_term_cache Whether to update post term cache. Default false. 106 * @type int $posts_per_page Number of posts to return. Default 10. 107 * @type string $post_type Type of post to return. 108 * @type string $s Search query. 109 * @type array $search_columns Which post table columns to query. 110 * } 111 */ 112 $query_args = apply_filters( 'wp_ajax_menu_quick_search_args', $query_args ); 113 $args = array_merge( $args, $query_args ); 114 115 if ( isset( $post_type_obj->_default_query ) ) { 116 $args = array_merge( $args, (array) $post_type_obj->_default_query ); 117 } 118 119 $search_results_query = new WP_Query( $args ); 120 if ( ! $search_results_query->have_posts() ) { 121 return; 122 } 123 124 while ( $search_results_query->have_posts() ) { 125 $post = $search_results_query->next_post(); 126 127 if ( 'markup' === $response_format ) { 128 $var_by_ref = $post->ID; 129 echo walk_nav_menu_tree( 130 array_map( 'wp_setup_nav_menu_item', array( get_post( $var_by_ref ) ) ), 131 0, 132 (object) $args 133 ); 134 } elseif ( 'json' === $response_format ) { 135 echo wp_json_encode( 136 array( 137 'ID' => $post->ID, 138 'post_title' => get_the_title( $post->ID ), 139 'post_type' => $matches[2], 140 ) 141 ); 142 echo "\n"; 143 } 144 } 145 } elseif ( 'taxonomy' === $matches[1] ) { 146 $terms = get_terms( 147 array( 148 'taxonomy' => $matches[2], 149 'name__like' => $query, 150 'number' => 10, 151 'hide_empty' => false, 152 ) 153 ); 154 155 if ( empty( $terms ) || is_wp_error( $terms ) ) { 156 return; 157 } 158 159 foreach ( (array) $terms as $term ) { 160 if ( 'markup' === $response_format ) { 161 echo walk_nav_menu_tree( 162 array_map( 'wp_setup_nav_menu_item', array( $term ) ), 163 0, 164 (object) $args 165 ); 166 } elseif ( 'json' === $response_format ) { 167 echo wp_json_encode( 168 array( 169 'ID' => $term->term_id, 170 'post_title' => $term->name, 171 'post_type' => $matches[2], 172 ) 173 ); 174 echo "\n"; 175 } 176 } 177 } 178 } 179 } 180 181 /** 182 * Register nav menu meta boxes and advanced menu items. 183 * 184 * @since 3.0.0 185 */ 186 function wp_nav_menu_setup() { 187 // Register meta boxes. 188 wp_nav_menu_post_type_meta_boxes(); 189 add_meta_box( 190 'add-custom-links', 191 __( 'Custom Links' ), 192 'wp_nav_menu_item_link_meta_box', 193 'nav-menus', 194 'side', 195 'default' 196 ); 197 wp_nav_menu_taxonomy_meta_boxes(); 198 199 // Register advanced menu items (columns). 200 add_filter( 'manage_nav-menus_columns', 'wp_nav_menu_manage_columns' ); 201 202 // If first time editing, disable advanced items by default. 203 if ( false === get_user_option( 'managenav-menuscolumnshidden' ) ) { 204 $user = wp_get_current_user(); 205 update_user_meta( 206 $user->ID, 207 'managenav-menuscolumnshidden', 208 array( 209 0 => 'link-target', 210 1 => 'css-classes', 211 2 => 'xfn', 212 3 => 'description', 213 4 => 'title-attribute', 214 ) 215 ); 216 } 217 } 218 219 /** 220 * Limit the amount of meta boxes to pages, posts, links, and categories for first time users. 221 * 222 * @since 3.0.0 223 * 224 * @global array $wp_meta_boxes Global meta box state. 225 */ 226 function wp_initial_nav_menu_meta_boxes() { 227 global $wp_meta_boxes; 228 229 if ( get_user_option( 'metaboxhidden_nav-menus' ) !== false || ! is_array( $wp_meta_boxes ) ) { 230 return; 231 } 232 233 $initial_meta_boxes = array( 'add-post-type-page', 'add-post-type-post', 'add-custom-links', 'add-category' ); 234 $hidden_meta_boxes = array(); 235 236 foreach ( array_keys( $wp_meta_boxes['nav-menus'] ) as $context ) { 237 foreach ( array_keys( $wp_meta_boxes['nav-menus'][ $context ] ) as $priority ) { 238 foreach ( $wp_meta_boxes['nav-menus'][ $context ][ $priority ] as $box ) { 239 if ( in_array( $box['id'], $initial_meta_boxes, true ) ) { 240 unset( $box['id'] ); 241 } else { 242 $hidden_meta_boxes[] = $box['id']; 243 } 244 } 245 } 246 } 247 248 $user = wp_get_current_user(); 249 update_user_meta( $user->ID, 'metaboxhidden_nav-menus', $hidden_meta_boxes ); 250 } 251 252 /** 253 * Creates meta boxes for any post type menu item.. 254 * 255 * @since 3.0.0 256 */ 257 function wp_nav_menu_post_type_meta_boxes() { 258 $post_types = get_post_types( array( 'show_in_nav_menus' => true ), 'object' ); 259 260 if ( ! $post_types ) { 261 return; 262 } 263 264 foreach ( $post_types as $post_type ) { 265 /** 266 * Filters whether a menu items meta box will be added for the current 267 * object type. 268 * 269 * If a falsey value is returned instead of an object, the menu items 270 * meta box for the current meta box object will not be added. 271 * 272 * @since 3.0.0 273 * 274 * @param WP_Post_Type|false $post_type The current object to add a menu items 275 * meta box for. 276 */ 277 $post_type = apply_filters( 'nav_menu_meta_box_object', $post_type ); 278 279 if ( $post_type ) { 280 $id = $post_type->name; 281 // Give pages a higher priority. 282 $priority = ( 'page' === $post_type->name ? 'core' : 'default' ); 283 add_meta_box( 284 "add-post-type-{$id}", 285 $post_type->labels->name, 286 'wp_nav_menu_item_post_type_meta_box', 287 'nav-menus', 288 'side', 289 $priority, 290 $post_type 291 ); 292 } 293 } 294 } 295 296 /** 297 * Creates meta boxes for any taxonomy menu item. 298 * 299 * @since 3.0.0 300 */ 301 function wp_nav_menu_taxonomy_meta_boxes() { 302 $taxonomies = get_taxonomies( array( 'show_in_nav_menus' => true ), 'object' ); 303 304 if ( ! $taxonomies ) { 305 return; 306 } 307 308 foreach ( $taxonomies as $tax ) { 309 /** This filter is documented in wp-admin/includes/nav-menu.php */ 310 $tax = apply_filters( 'nav_menu_meta_box_object', $tax ); 311 312 if ( $tax ) { 313 $id = $tax->name; 314 add_meta_box( 315 "add-{$id}", 316 $tax->labels->name, 317 'wp_nav_menu_item_taxonomy_meta_box', 318 'nav-menus', 319 'side', 320 'default', 321 $tax 322 ); 323 } 324 } 325 } 326 327 /** 328 * Check whether to disable the Menu Locations meta box submit button and inputs. 329 * 330 * @since 3.6.0 331 * @since 5.3.1 The `$display` parameter was added. 332 * 333 * @global bool $one_theme_location_no_menus to determine if no menus exist 334 * 335 * @param int|string $nav_menu_selected_id ID, name, or slug of the currently selected menu. 336 * @param bool $display Whether to display or just return the string. 337 * @return string|false Disabled attribute if at least one menu exists, false if not. 338 */ 339 function wp_nav_menu_disabled_check( $nav_menu_selected_id, $display = true ) { 340 global $one_theme_location_no_menus; 341 342 if ( $one_theme_location_no_menus ) { 343 return false; 344 } 345 346 return disabled( $nav_menu_selected_id, 0, $display ); 347 } 348 349 /** 350 * Displays a meta box for the custom links menu item. 351 * 352 * @since 3.0.0 353 * 354 * @global int $_nav_menu_placeholder 355 * @global int|string $nav_menu_selected_id 356 */ 357 function wp_nav_menu_item_link_meta_box() { 358 global $_nav_menu_placeholder, $nav_menu_selected_id; 359 360 $_nav_menu_placeholder = 0 > $_nav_menu_placeholder ? $_nav_menu_placeholder - 1 : -1; 361 362 ?> 363 <div class="customlinkdiv" id="customlinkdiv"> 364 <input type="hidden" value="custom" name="menu-item[<?php echo $_nav_menu_placeholder; ?>][menu-item-type]" /> 365 <p id="menu-item-url-wrap" class="wp-clearfix"> 366 <label class="howto" for="custom-menu-item-url"><?php _e( 'URL' ); ?></label> 367 <input id="custom-menu-item-url" name="menu-item[<?php echo $_nav_menu_placeholder; ?>][menu-item-url]" 368 type="text"<?php wp_nav_menu_disabled_check( $nav_menu_selected_id ); ?> 369 class="code menu-item-textbox form-required" placeholder="https://" 370 /> 371 <span id="custom-url-error" class="error-message" style="display: none;"><?php _e( 'Please provide a valid link.' ); ?></span> 372 </p> 373 374 <p id="menu-item-name-wrap" class="wp-clearfix"> 375 <label class="howto" for="custom-menu-item-name"><?php _e( 'Link Text' ); ?></label> 376 <input id="custom-menu-item-name" name="menu-item[<?php echo $_nav_menu_placeholder; ?>][menu-item-title]" 377 type="text"<?php wp_nav_menu_disabled_check( $nav_menu_selected_id ); ?> 378 class="regular-text menu-item-textbox" 379 /> 380 </p> 381 382 <p class="button-controls wp-clearfix"> 383 <span class="add-to-menu"> 384 <input id="submit-customlinkdiv" name="add-custom-menu-item" 385 type="submit"<?php wp_nav_menu_disabled_check( $nav_menu_selected_id ); ?> 386 class="button submit-add-to-menu right" value="<?php esc_attr_e( 'Add to Menu' ); ?>" 387 /> 388 <span class="spinner"></span> 389 </span> 390 </p> 391 392 </div><!-- /.customlinkdiv --> 393 <?php 394 } 395 396 /** 397 * Displays a meta box for a post type menu item. 398 * 399 * @since 3.0.0 400 * 401 * @global int $_nav_menu_placeholder 402 * @global int|string $nav_menu_selected_id 403 * 404 * @param string $data_object Not used. 405 * @param array $box { 406 * Post type menu item meta box arguments. 407 * 408 * @type string $id Meta box 'id' attribute. 409 * @type string $title Meta box title. 410 * @type callable $callback Meta box display callback. 411 * @type WP_Post_Type $args Extra meta box arguments (the post type object for this meta box). 412 * } 413 */ 414 function wp_nav_menu_item_post_type_meta_box( $data_object, $box ) { 415 global $_nav_menu_placeholder, $nav_menu_selected_id; 416 417 $post_type_name = $box['args']->name; 418 $post_type = get_post_type_object( $post_type_name ); 419 $tab_name = $post_type_name . '-tab'; 420 421 // Paginate browsing for large numbers of post objects. 422 $per_page = 50; 423 $pagenum = isset( $_REQUEST[ $tab_name ] ) && isset( $_REQUEST['paged'] ) ? absint( $_REQUEST['paged'] ) : 1; 424 $offset = 0 < $pagenum ? $per_page * ( $pagenum - 1 ) : 0; 425 426 $args = array( 427 'offset' => $offset, 428 'order' => 'ASC', 429 'orderby' => 'title', 430 'posts_per_page' => $per_page, 431 'post_type' => $post_type_name, 432 'suppress_filters' => true, 433 'update_post_term_cache' => false, 434 'update_post_meta_cache' => false, 435 ); 436 437 if ( isset( $box['args']->_default_query ) ) { 438 $args = array_merge( $args, (array) $box['args']->_default_query ); 439 } 440 441 /* 442 * If we're dealing with pages, let's prioritize the Front Page, 443 * Posts Page and Privacy Policy Page at the top of the list. 444 */ 445 $important_pages = array(); 446 if ( 'page' === $post_type_name ) { 447 $suppress_page_ids = array(); 448 449 // Insert Front Page or custom Home link. 450 $front_page = 'page' === get_option( 'show_on_front' ) ? (int) get_option( 'page_on_front' ) : 0; 451 452 $front_page_obj = null; 453 454 if ( ! empty( $front_page ) ) { 455 $front_page_obj = get_post( $front_page ); 456 } 457 458 if ( $front_page_obj ) { 459 $front_page_obj->front_or_home = true; 460 461 $important_pages[] = $front_page_obj; 462 $suppress_page_ids[] = $front_page_obj->ID; 463 } else { 464 $_nav_menu_placeholder = ( 0 > $_nav_menu_placeholder ) ? (int) $_nav_menu_placeholder - 1 : -1; 465 $front_page_obj = (object) array( 466 'front_or_home' => true, 467 'ID' => 0, 468 'object_id' => $_nav_menu_placeholder, 469 'post_content' => '', 470 'post_excerpt' => '', 471 'post_parent' => '', 472 'post_title' => _x( 'Home', 'nav menu home label' ), 473 'post_type' => 'nav_menu_item', 474 'type' => 'custom', 475 'url' => home_url( '/' ), 476 ); 477 478 $important_pages[] = $front_page_obj; 479 } 480 481 // Insert Posts Page. 482 $posts_page = 'page' === get_option( 'show_on_front' ) ? (int) get_option( 'page_for_posts' ) : 0; 483 484 if ( ! empty( $posts_page ) ) { 485 $posts_page_obj = get_post( $posts_page ); 486 487 if ( $posts_page_obj ) { 488 $front_page_obj->posts_page = true; 489 490 $important_pages[] = $posts_page_obj; 491 $suppress_page_ids[] = $posts_page_obj->ID; 492 } 493 } 494 495 // Insert Privacy Policy Page. 496 $privacy_policy_page_id = (int) get_option( 'wp_page_for_privacy_policy' ); 497 498 if ( ! empty( $privacy_policy_page_id ) ) { 499 $privacy_policy_page = get_post( $privacy_policy_page_id ); 500 501 if ( $privacy_policy_page instanceof WP_Post && 'publish' === $privacy_policy_page->post_status ) { 502 $privacy_policy_page->privacy_policy_page = true; 503 504 $important_pages[] = $privacy_policy_page; 505 $suppress_page_ids[] = $privacy_policy_page->ID; 506 } 507 } 508 509 // Add suppression array to arguments for WP_Query. 510 if ( ! empty( $suppress_page_ids ) ) { 511 $args['post__not_in'] = $suppress_page_ids; 512 } 513 } 514 515 $get_posts = new WP_Query(); 516 $posts = $get_posts->query( $args ); 517 518 // Only suppress and insert when more than just suppression pages available. 519 if ( ! $get_posts->post_count ) { 520 if ( ! empty( $suppress_page_ids ) ) { 521 unset( $args['post__not_in'] ); 522 $get_posts = new WP_Query(); 523 $posts = $get_posts->query( $args ); 524 } else { 525 echo '<p>' . __( 'No items.' ) . '</p>'; 526 return; 527 } 528 } elseif ( ! empty( $important_pages ) ) { 529 $posts = array_merge( $important_pages, $posts ); 530 } 531 532 $num_pages = $get_posts->max_num_pages; 533 534 $page_links = paginate_links( 535 array( 536 'base' => add_query_arg( 537 array( 538 $tab_name => 'all', 539 'paged' => '%#%', 540 'item-type' => 'post_type', 541 'item-object' => $post_type_name, 542 ) 543 ), 544 'format' => '', 545 'prev_text' => '<span aria-label="' . esc_attr__( 'Previous page' ) . '">' . __( '«' ) . '</span>', 546 'next_text' => '<span aria-label="' . esc_attr__( 'Next page' ) . '">' . __( '»' ) . '</span>', 547 /* translators: Hidden accessibility text. */ 548 'before_page_number' => '<span class="screen-reader-text">' . __( 'Page' ) . '</span> ', 549 'total' => $num_pages, 550 'current' => $pagenum, 551 ) 552 ); 553 554 $db_fields = false; 555 if ( is_post_type_hierarchical( $post_type_name ) ) { 556 $db_fields = array( 557 'parent' => 'post_parent', 558 'id' => 'ID', 559 ); 560 } 561 562 $walker = new Walker_Nav_Menu_Checklist( $db_fields ); 563 564 $current_tab = 'most-recent'; 565 566 if ( isset( $_REQUEST[ $tab_name ] ) && in_array( $_REQUEST[ $tab_name ], array( 'all', 'search' ), true ) ) { 567 $current_tab = $_REQUEST[ $tab_name ]; 568 } 569 570 if ( ! empty( $_REQUEST[ "quick-search-posttype-{$post_type_name}" ] ) ) { 571 $current_tab = 'search'; 572 } 573 574 $removed_args = array( 575 'action', 576 'customlink-tab', 577 'edit-menu-item', 578 'menu-item', 579 'page-tab', 580 '_wpnonce', 581 ); 582 583 $most_recent_url = ''; 584 $view_all_url = ''; 585 $search_url = ''; 586 587 if ( $nav_menu_selected_id ) { 588 $most_recent_url = add_query_arg( $tab_name, 'most-recent', remove_query_arg( $removed_args ) ); 589 $view_all_url = add_query_arg( $tab_name, 'all', remove_query_arg( $removed_args ) ); 590 $search_url = add_query_arg( $tab_name, 'search', remove_query_arg( $removed_args ) ); 591 } 592 ?> 593 <div id="<?php echo esc_attr( "posttype-{$post_type_name}" ); ?>" class="posttypediv"> 594 <ul id="<?php echo esc_attr( "posttype-{$post_type_name}-tabs" ); ?>" class="posttype-tabs add-menu-item-tabs"> 595 <li <?php echo ( 'most-recent' === $current_tab ? ' class="tabs"' : '' ); ?>> 596 <a class="nav-tab-link" 597 data-type="<?php echo esc_attr( "tabs-panel-posttype-{$post_type_name}-most-recent" ); ?>" 598 href="<?php echo esc_url( $most_recent_url . "#tabs-panel-posttype-{$post_type_name}-most-recent" ); ?>" 599 > 600 <?php _e( 'Most Recent' ); ?> 601 </a> 602 </li> 603 <li <?php echo ( 'all' === $current_tab ? ' class="tabs"' : '' ); ?>> 604 <a class="nav-tab-link" 605 data-type="<?php echo esc_attr( "{$post_type_name}-all" ); ?>" 606 href="<?php echo esc_url( $view_all_url . "#{$post_type_name}-all" ); ?>" 607 > 608 <?php _e( 'View All' ); ?> 609 </a> 610 </li> 611 <li <?php echo ( 'search' === $current_tab ? ' class="tabs"' : '' ); ?>> 612 <a class="nav-tab-link" 613 data-type="<?php echo esc_attr( "tabs-panel-posttype-{$post_type_name}-search" ); ?>" 614 href="<?php echo esc_url( $search_url . "#tabs-panel-posttype-{$post_type_name}-search" ); ?>" 615 > 616 <?php _e( 'Search' ); ?> 617 </a> 618 </li> 619 </ul><!-- .posttype-tabs --> 620 621 <div id="<?php echo esc_attr( "tabs-panel-posttype-{$post_type_name}-most-recent" ); ?>" 622 class="tabs-panel <?php echo ( 'most-recent' === $current_tab ? 'tabs-panel-active' : 'tabs-panel-inactive' ); ?>" 623 role="region" aria-label="<?php esc_attr_e( 'Most Recent' ); ?>" tabindex="0" 624 > 625 <ul id="<?php echo esc_attr( "{$post_type_name}checklist-most-recent" ); ?>" 626 class="categorychecklist form-no-clear" 627 > 628 <?php 629 $recent_args = array_merge( 630 $args, 631 array( 632 'orderby' => 'post_date', 633 'order' => 'DESC', 634 'posts_per_page' => 15, 635 ) 636 ); 637 $most_recent = $get_posts->query( $recent_args ); 638 639 $args['walker'] = $walker; 640 641 /** 642 * Filters the posts displayed in the 'Most Recent' tab of the current 643 * post type's menu items meta box. 644 * 645 * The dynamic portion of the hook name, `$post_type_name`, refers to the post type name. 646 * 647 * Possible hook names include: 648 * 649 * - `nav_menu_items_post_recent` 650 * - `nav_menu_items_page_recent` 651 * 652 * @since 4.3.0 653 * @since 4.9.0 Added the `$recent_args` parameter. 654 * 655 * @param WP_Post[] $most_recent An array of post objects being listed. 656 * @param array $args An array of `WP_Query` arguments for the meta box. 657 * @param array $box Arguments passed to `wp_nav_menu_item_post_type_meta_box()`. 658 * @param array $recent_args An array of `WP_Query` arguments for 'Most Recent' tab. 659 */ 660 $most_recent = apply_filters( 661 "nav_menu_items_{$post_type_name}_recent", 662 $most_recent, 663 $args, 664 $box, 665 $recent_args 666 ); 667 668 echo walk_nav_menu_tree( 669 array_map( 'wp_setup_nav_menu_item', $most_recent ), 670 0, 671 (object) $args 672 ); 673 ?> 674 </ul> 675 </div><!-- /.tabs-panel --> 676 677 <div id="<?php echo esc_attr( "tabs-panel-posttype-{$post_type_name}-search" ); ?>" 678 class="tabs-panel <?php echo ( 'search' === $current_tab ? 'tabs-panel-active' : 'tabs-panel-inactive' ); ?>" 679 role="region" aria-label="<?php echo esc_attr( $post_type->labels->search_items ); ?>" tabindex="0" 680 > 681 <?php 682 if ( isset( $_REQUEST[ "quick-search-posttype-{$post_type_name}" ] ) ) { 683 $searched = esc_attr( $_REQUEST[ "quick-search-posttype-{$post_type_name}" ] ); 684 $search_results = get_posts( 685 array( 686 's' => $searched, 687 'post_type' => $post_type_name, 688 'fields' => 'all', 689 'order' => 'DESC', 690 ) 691 ); 692 } else { 693 $searched = ''; 694 $search_results = array(); 695 } 696 ?> 697 <p class="quick-search-wrap"> 698 <label for="<?php echo esc_attr( "quick-search-posttype-{$post_type_name}" ); ?>" class="screen-reader-text"> 699 <?php 700 /* translators: Hidden accessibility text. */ 701 _e( 'Search' ); 702 ?> 703 </label> 704 <input type="search"<?php wp_nav_menu_disabled_check( $nav_menu_selected_id ); ?> 705 class="quick-search" value="<?php echo $searched; ?>" 706 name="<?php echo esc_attr( "quick-search-posttype-{$post_type_name}" ); ?>" 707 id="<?php echo esc_attr( "quick-search-posttype-{$post_type_name}" ); ?>" 708 /> 709 <span class="spinner"></span> 710 <?php 711 submit_button( 712 __( 'Search' ), 713 'small quick-search-submit hide-if-js', 714 'submit', 715 false, 716 array( 'id' => "submit-quick-search-posttype-{$post_type_name}" ) 717 ); 718 ?> 719 </p> 720 721 <ul id="<?php echo esc_attr( "{$post_type_name}-search-checklist" ); ?>" 722 data-wp-lists="<?php echo esc_attr( "list:{$post_type_name}" ); ?>" 723 class="categorychecklist form-no-clear" 724 > 725 <?php if ( ! empty( $search_results ) && ! is_wp_error( $search_results ) ) : ?> 726 <?php 727 $args['walker'] = $walker; 728 echo walk_nav_menu_tree( 729 array_map( 'wp_setup_nav_menu_item', $search_results ), 730 0, 731 (object) $args 732 ); 733 ?> 734 <?php elseif ( is_wp_error( $search_results ) ) : ?> 735 <li><?php echo $search_results->get_error_message(); ?></li> 736 <?php elseif ( ! empty( $searched ) ) : ?> 737 <li><?php _e( 'No results found.' ); ?></li> 738 <?php endif; ?> 739 </ul> 740 </div><!-- /.tabs-panel --> 741 742 <div id="<?php echo esc_attr( "{$post_type_name}-all" ); ?>" 743 class="tabs-panel tabs-panel-view-all <?php echo ( 'all' === $current_tab ? 'tabs-panel-active' : 'tabs-panel-inactive' ); ?>" 744 role="region" aria-label="<?php echo esc_attr( $post_type->labels->all_items ); ?>" tabindex="0" 745 > 746 <?php if ( ! empty( $page_links ) ) : ?> 747 <div class="add-menu-item-pagelinks"> 748 <?php echo $page_links; ?> 749 </div> 750 <?php endif; ?> 751 752 <ul id="<?php echo esc_attr( "{$post_type_name}checklist" ); ?>" 753 data-wp-lists="<?php echo esc_attr( "list:{$post_type_name}" ); ?>" 754 class="categorychecklist form-no-clear" 755 > 756 <?php 757 $args['walker'] = $walker; 758 759 if ( $post_type->has_archive ) { 760 $_nav_menu_placeholder = ( 0 > $_nav_menu_placeholder ) ? (int) $_nav_menu_placeholder - 1 : -1; 761 array_unshift( 762 $posts, 763 (object) array( 764 'ID' => 0, 765 'object_id' => $_nav_menu_placeholder, 766 'object' => $post_type_name, 767 'post_content' => '', 768 'post_excerpt' => '', 769 'post_title' => $post_type->labels->archives, 770 'post_type' => 'nav_menu_item', 771 'type' => 'post_type_archive', 772 'url' => get_post_type_archive_link( $post_type_name ), 773 ) 774 ); 775 } 776 777 /** 778 * Filters the posts displayed in the 'View All' tab of the current 779 * post type's menu items meta box. 780 * 781 * The dynamic portion of the hook name, `$post_type_name`, refers 782 * to the slug of the current post type. 783 * 784 * Possible hook names include: 785 * 786 * - `nav_menu_items_post` 787 * - `nav_menu_items_page` 788 * 789 * @since 3.2.0 790 * @since 4.6.0 Converted the `$post_type` parameter to accept a WP_Post_Type object. 791 * 792 * @see WP_Query::query() 793 * 794 * @param object[] $posts The posts for the current post type. Mostly `WP_Post` objects, but 795 * can also contain "fake" post objects to represent other menu items. 796 * @param array $args An array of `WP_Query` arguments. 797 * @param WP_Post_Type $post_type The current post type object for this menu item meta box. 798 */ 799 $posts = apply_filters( 800 "nav_menu_items_{$post_type_name}", 801 $posts, 802 $args, 803 $post_type 804 ); 805 806 $checkbox_items = walk_nav_menu_tree( 807 array_map( 'wp_setup_nav_menu_item', $posts ), 808 0, 809 (object) $args 810 ); 811 812 echo $checkbox_items; 813 ?> 814 </ul> 815 816 <?php if ( ! empty( $page_links ) ) : ?> 817 <div class="add-menu-item-pagelinks"> 818 <?php echo $page_links; ?> 819 </div> 820 <?php endif; ?> 821 </div><!-- /.tabs-panel --> 822 823 <p class="button-controls wp-clearfix" data-items-type="<?php echo esc_attr( "posttype-{$post_type_name}" ); ?>"> 824 <span class="list-controls hide-if-no-js"> 825 <input type="checkbox"<?php wp_nav_menu_disabled_check( $nav_menu_selected_id ); ?> 826 id="<?php echo esc_attr( $tab_name ); ?>" class="select-all" 827 /> 828 <label for="<?php echo esc_attr( $tab_name ); ?>"><?php _e( 'Select All' ); ?></label> 829 </span> 830 831 <span class="add-to-menu"> 832 <input type="submit"<?php wp_nav_menu_disabled_check( $nav_menu_selected_id ); ?> 833 class="button submit-add-to-menu right" value="<?php esc_attr_e( 'Add to Menu' ); ?>" 834 name="add-post-type-menu-item" id="<?php echo esc_attr( "submit-posttype-{$post_type_name}" ); ?>" 835 /> 836 <span class="spinner"></span> 837 </span> 838 </p> 839 840 </div><!-- /.posttypediv --> 841 <?php 842 } 843 844 /** 845 * Displays a meta box for a taxonomy menu item. 846 * 847 * @since 3.0.0 848 * 849 * @global int|string $nav_menu_selected_id 850 * 851 * @param string $data_object Not used. 852 * @param array $box { 853 * Taxonomy menu item meta box arguments. 854 * 855 * @type string $id Meta box 'id' attribute. 856 * @type string $title Meta box title. 857 * @type callable $callback Meta box display callback. 858 * @type object $args Extra meta box arguments (the taxonomy object for this meta box). 859 * } 860 */ 861 function wp_nav_menu_item_taxonomy_meta_box( $data_object, $box ) { 862 global $nav_menu_selected_id; 863 864 $taxonomy_name = $box['args']->name; 865 $taxonomy = get_taxonomy( $taxonomy_name ); 866 $tab_name = $taxonomy_name . '-tab'; 867 868 // Paginate browsing for large numbers of objects. 869 $per_page = 50; 870 $pagenum = isset( $_REQUEST[ $tab_name ] ) && isset( $_REQUEST['paged'] ) ? absint( $_REQUEST['paged'] ) : 1; 871 $offset = 0 < $pagenum ? $per_page * ( $pagenum - 1 ) : 0; 872 873 $args = array( 874 'taxonomy' => $taxonomy_name, 875 'child_of' => 0, 876 'exclude' => '', 877 'hide_empty' => false, 878 'hierarchical' => 1, 879 'include' => '', 880 'number' => $per_page, 881 'offset' => $offset, 882 'order' => 'ASC', 883 'orderby' => 'name', 884 'pad_counts' => false, 885 ); 886 887 $terms = get_terms( $args ); 888 889 if ( ! $terms || is_wp_error( $terms ) ) { 890 echo '<p>' . __( 'No items.' ) . '</p>'; 891 return; 892 } 893 894 $num_pages = (int) ceil( 895 (int) wp_count_terms( 896 array_merge( 897 $args, 898 array( 899 'number' => '', 900 'offset' => '', 901 ) 902 ) 903 ) / $per_page 904 ); 905 906 $page_links = paginate_links( 907 array( 908 'base' => add_query_arg( 909 array( 910 $tab_name => 'all', 911 'paged' => '%#%', 912 'item-type' => 'taxonomy', 913 'item-object' => $taxonomy_name, 914 ) 915 ), 916 'format' => '', 917 'prev_text' => '<span aria-label="' . esc_attr__( 'Previous page' ) . '">' . __( '«' ) . '</span>', 918 'next_text' => '<span aria-label="' . esc_attr__( 'Next page' ) . '">' . __( '»' ) . '</span>', 919 /* translators: Hidden accessibility text. */ 920 'before_page_number' => '<span class="screen-reader-text">' . __( 'Page' ) . '</span> ', 921 'total' => $num_pages, 922 'current' => $pagenum, 923 ) 924 ); 925 926 $db_fields = false; 927 if ( is_taxonomy_hierarchical( $taxonomy_name ) ) { 928 $db_fields = array( 929 'parent' => 'parent', 930 'id' => 'term_id', 931 ); 932 } 933 934 $walker = new Walker_Nav_Menu_Checklist( $db_fields ); 935 936 $current_tab = 'most-used'; 937 938 if ( isset( $_REQUEST[ $tab_name ] ) && in_array( $_REQUEST[ $tab_name ], array( 'all', 'most-used', 'search' ), true ) ) { 939 $current_tab = $_REQUEST[ $tab_name ]; 940 } 941 942 if ( ! empty( $_REQUEST[ "quick-search-taxonomy-{$taxonomy_name}" ] ) ) { 943 $current_tab = 'search'; 944 } 945 946 $removed_args = array( 947 'action', 948 'customlink-tab', 949 'edit-menu-item', 950 'menu-item', 951 'page-tab', 952 '_wpnonce', 953 ); 954 955 $most_used_url = ''; 956 $view_all_url = ''; 957 $search_url = ''; 958 959 if ( $nav_menu_selected_id ) { 960 $most_used_url = add_query_arg( $tab_name, 'most-used', remove_query_arg( $removed_args ) ); 961 $view_all_url = add_query_arg( $tab_name, 'all', remove_query_arg( $removed_args ) ); 962 $search_url = add_query_arg( $tab_name, 'search', remove_query_arg( $removed_args ) ); 963 } 964 ?> 965 <div id="<?php echo esc_attr( "taxonomy-{$taxonomy_name}" ); ?>" class="taxonomydiv"> 966 <ul id="<?php echo esc_attr( "taxonomy-{$taxonomy_name}-tabs" ); ?>" class="taxonomy-tabs add-menu-item-tabs"> 967 <li <?php echo ( 'most-used' === $current_tab ? ' class="tabs"' : '' ); ?>> 968 <a class="nav-tab-link" 969 data-type="<?php echo esc_attr( "tabs-panel-{$taxonomy_name}-pop" ); ?>" 970 href="<?php echo esc_url( $most_used_url . "#tabs-panel-{$taxonomy_name}-pop" ); ?>" 971 > 972 <?php echo esc_html( $taxonomy->labels->most_used ); ?> 973 </a> 974 </li> 975 <li <?php echo ( 'all' === $current_tab ? ' class="tabs"' : '' ); ?>> 976 <a class="nav-tab-link" 977 data-type="<?php echo esc_attr( "tabs-panel-{$taxonomy_name}-all" ); ?>" 978 href="<?php echo esc_url( $view_all_url . "#tabs-panel-{$taxonomy_name}-all" ); ?>" 979 > 980 <?php _e( 'View All' ); ?> 981 </a> 982 </li> 983 <li <?php echo ( 'search' === $current_tab ? ' class="tabs"' : '' ); ?>> 984 <a class="nav-tab-link" 985 data-type="<?php echo esc_attr( "tabs-panel-search-taxonomy-{$taxonomy_name}" ); ?>" 986 href="<?php echo esc_url( $search_url . "#tabs-panel-search-taxonomy-{$taxonomy_name}" ); ?>" 987 > 988 <?php _e( 'Search' ); ?> 989 </a> 990 </li> 991 </ul><!-- .taxonomy-tabs --> 992 993 <div id="<?php echo esc_attr( "tabs-panel-{$taxonomy_name}-pop" ); ?>" 994 class="tabs-panel <?php echo ( 'most-used' === $current_tab ? 'tabs-panel-active' : 'tabs-panel-inactive' ); ?>" 995 role="region" aria-label="<?php echo esc_attr( $taxonomy->labels->most_used ); ?>" tabindex="0" 996 > 997 <ul id="<?php echo esc_attr( "{$taxonomy_name}checklist-pop" ); ?>" 998 class="categorychecklist form-no-clear" 999 > 1000 <?php 1001 $popular_terms = get_terms( 1002 array( 1003 'taxonomy' => $taxonomy_name, 1004 'orderby' => 'count', 1005 'order' => 'DESC', 1006 'number' => 10, 1007 'hierarchical' => false, 1008 ) 1009 ); 1010 1011 $args['walker'] = $walker; 1012 echo walk_nav_menu_tree( 1013 array_map( 'wp_setup_nav_menu_item', $popular_terms ), 1014 0, 1015 (object) $args 1016 ); 1017 ?> 1018 </ul> 1019 </div><!-- /.tabs-panel --> 1020 1021 <div id="<?php echo esc_attr( "tabs-panel-{$taxonomy_name}-all" ); ?>" 1022 class="tabs-panel tabs-panel-view-all <?php echo ( 'all' === $current_tab ? 'tabs-panel-active' : 'tabs-panel-inactive' ); ?>" 1023 role="region" aria-label="<?php echo esc_attr( $taxonomy->labels->all_items ); ?>" tabindex="0" 1024 > 1025 <?php if ( ! empty( $page_links ) ) : ?> 1026 <div class="add-menu-item-pagelinks"> 1027 <?php echo $page_links; ?> 1028 </div> 1029 <?php endif; ?> 1030 1031 <ul id="<?php echo esc_attr( "{$taxonomy_name}checklist" ); ?>" 1032 data-wp-lists="<?php echo esc_attr( "list:{$taxonomy_name}" ); ?>" 1033 class="categorychecklist form-no-clear" 1034 > 1035 <?php 1036 $args['walker'] = $walker; 1037 echo walk_nav_menu_tree( 1038 array_map( 'wp_setup_nav_menu_item', $terms ), 1039 0, 1040 (object) $args 1041 ); 1042 ?> 1043 </ul> 1044 1045 <?php if ( ! empty( $page_links ) ) : ?> 1046 <div class="add-menu-item-pagelinks"> 1047 <?php echo $page_links; ?> 1048 </div> 1049 <?php endif; ?> 1050 </div><!-- /.tabs-panel --> 1051 1052 <div id="<?php echo esc_attr( "tabs-panel-search-taxonomy-{$taxonomy_name}" ); ?>" 1053 class="tabs-panel <?php echo ( 'search' === $current_tab ? 'tabs-panel-active' : 'tabs-panel-inactive' ); ?>" 1054 role="region" aria-label="<?php echo esc_attr( $taxonomy->labels->search_items ); ?>" tabindex="0"> 1055 <?php 1056 if ( isset( $_REQUEST[ "quick-search-taxonomy-{$taxonomy_name}" ] ) ) { 1057 $searched = esc_attr( $_REQUEST[ "quick-search-taxonomy-{$taxonomy_name}" ] ); 1058 $search_results = get_terms( 1059 array( 1060 'taxonomy' => $taxonomy_name, 1061 'name__like' => $searched, 1062 'fields' => 'all', 1063 'orderby' => 'count', 1064 'order' => 'DESC', 1065 'hierarchical' => false, 1066 ) 1067 ); 1068 } else { 1069 $searched = ''; 1070 $search_results = array(); 1071 } 1072 ?> 1073 <p class="quick-search-wrap"> 1074 <label for="<?php echo esc_attr( "quick-search-taxonomy-{$taxonomy_name}" ); ?>" class="screen-reader-text"> 1075 <?php 1076 /* translators: Hidden accessibility text. */ 1077 _e( 'Search' ); 1078 ?> 1079 </label> 1080 <input type="search" 1081 class="quick-search" value="<?php echo $searched; ?>" 1082 name="<?php echo esc_attr( "quick-search-taxonomy-{$taxonomy_name}" ); ?>" 1083 id="<?php echo esc_attr( "quick-search-taxonomy-{$taxonomy_name}" ); ?>" 1084 /> 1085 <span class="spinner"></span> 1086 <?php 1087 submit_button( 1088 __( 'Search' ), 1089 'small quick-search-submit hide-if-js', 1090 'submit', 1091 false, 1092 array( 'id' => "submit-quick-search-taxonomy-{$taxonomy_name}" ) 1093 ); 1094 ?> 1095 </p> 1096 1097 <ul id="<?php echo esc_attr( "{$taxonomy_name}-search-checklist" ); ?>" 1098 data-wp-lists="<?php echo esc_attr( "list:{$taxonomy_name}" ); ?>" 1099 class="categorychecklist form-no-clear" 1100 > 1101 <?php if ( ! empty( $search_results ) && ! is_wp_error( $search_results ) ) : ?> 1102 <?php 1103 $args['walker'] = $walker; 1104 echo walk_nav_menu_tree( 1105 array_map( 'wp_setup_nav_menu_item', $search_results ), 1106 0, 1107 (object) $args 1108 ); 1109 ?> 1110 <?php elseif ( is_wp_error( $search_results ) ) : ?> 1111 <li><?php echo $search_results->get_error_message(); ?></li> 1112 <?php elseif ( ! empty( $searched ) ) : ?> 1113 <li><?php _e( 'No results found.' ); ?></li> 1114 <?php endif; ?> 1115 </ul> 1116 </div><!-- /.tabs-panel --> 1117 1118 <p class="button-controls wp-clearfix" data-items-type="<?php echo esc_attr( "taxonomy-{$taxonomy_name}" ); ?>"> 1119 <span class="list-controls hide-if-no-js"> 1120 <input type="checkbox"<?php wp_nav_menu_disabled_check( $nav_menu_selected_id ); ?> 1121 id="<?php echo esc_attr( $tab_name ); ?>" class="select-all" 1122 /> 1123 <label for="<?php echo esc_attr( $tab_name ); ?>"><?php _e( 'Select All' ); ?></label> 1124 </span> 1125 1126 <span class="add-to-menu"> 1127 <input type="submit"<?php wp_nav_menu_disabled_check( $nav_menu_selected_id ); ?> 1128 class="button submit-add-to-menu right" value="<?php esc_attr_e( 'Add to Menu' ); ?>" 1129 name="add-taxonomy-menu-item" id="<?php echo esc_attr( "submit-taxonomy-{$taxonomy_name}" ); ?>" 1130 /> 1131 <span class="spinner"></span> 1132 </span> 1133 </p> 1134 1135 </div><!-- /.taxonomydiv --> 1136 <?php 1137 } 1138 1139 /** 1140 * Save posted nav menu item data. 1141 * 1142 * @since 3.0.0 1143 * 1144 * @param int $menu_id The menu ID for which to save this item. Value of 0 makes a draft, orphaned menu item. Default 0. 1145 * @param array[] $menu_data The unsanitized POSTed menu item data. 1146 * @return int[] The database IDs of the items saved 1147 */ 1148 function wp_save_nav_menu_items( $menu_id = 0, $menu_data = array() ) { 1149 $menu_id = (int) $menu_id; 1150 $items_saved = array(); 1151 1152 if ( 0 === $menu_id || is_nav_menu( $menu_id ) ) { 1153 1154 // Loop through all the menu items' POST values. 1155 foreach ( (array) $menu_data as $_possible_db_id => $_item_object_data ) { 1156 if ( 1157 // Checkbox is not checked. 1158 empty( $_item_object_data['menu-item-object-id'] ) && 1159 ( 1160 // And item type either isn't set. 1161 ! isset( $_item_object_data['menu-item-type'] ) || 1162 // Or URL is the default. 1163 in_array( $_item_object_data['menu-item-url'], array( 'https://', 'http://', '' ), true ) || 1164 // Or it's not a custom menu item (but not the custom home page). 1165 ! ( 'custom' === $_item_object_data['menu-item-type'] && ! isset( $_item_object_data['menu-item-db-id'] ) ) || 1166 // Or it *is* a custom menu item that already exists. 1167 ! empty( $_item_object_data['menu-item-db-id'] ) 1168 ) 1169 ) { 1170 // Then this potential menu item is not getting added to this menu. 1171 continue; 1172 } 1173 1174 // If this possible menu item doesn't actually have a menu database ID yet. 1175 if ( 1176 empty( $_item_object_data['menu-item-db-id'] ) || 1177 ( 0 > $_possible_db_id ) || 1178 $_possible_db_id !== (int) $_item_object_data['menu-item-db-id'] 1179 ) { 1180 $_actual_db_id = 0; 1181 } else { 1182 $_actual_db_id = (int) $_item_object_data['menu-item-db-id']; 1183 } 1184 1185 $args = array( 1186 'menu-item-db-id' => ( isset( $_item_object_data['menu-item-db-id'] ) ? $_item_object_data['menu-item-db-id'] : '' ), 1187 'menu-item-object-id' => ( isset( $_item_object_data['menu-item-object-id'] ) ? $_item_object_data['menu-item-object-id'] : '' ), 1188 'menu-item-object' => ( isset( $_item_object_data['menu-item-object'] ) ? $_item_object_data['menu-item-object'] : '' ), 1189 'menu-item-parent-id' => ( isset( $_item_object_data['menu-item-parent-id'] ) ? $_item_object_data['menu-item-parent-id'] : '' ), 1190 'menu-item-position' => ( isset( $_item_object_data['menu-item-position'] ) ? $_item_object_data['menu-item-position'] : '' ), 1191 'menu-item-type' => ( isset( $_item_object_data['menu-item-type'] ) ? $_item_object_data['menu-item-type'] : '' ), 1192 'menu-item-title' => ( isset( $_item_object_data['menu-item-title'] ) ? $_item_object_data['menu-item-title'] : '' ), 1193 'menu-item-url' => ( isset( $_item_object_data['menu-item-url'] ) ? $_item_object_data['menu-item-url'] : '' ), 1194 'menu-item-description' => ( isset( $_item_object_data['menu-item-description'] ) ? $_item_object_data['menu-item-description'] : '' ), 1195 'menu-item-attr-title' => ( isset( $_item_object_data['menu-item-attr-title'] ) ? $_item_object_data['menu-item-attr-title'] : '' ), 1196 'menu-item-target' => ( isset( $_item_object_data['menu-item-target'] ) ? $_item_object_data['menu-item-target'] : '' ), 1197 'menu-item-classes' => ( isset( $_item_object_data['menu-item-classes'] ) ? $_item_object_data['menu-item-classes'] : '' ), 1198 'menu-item-xfn' => ( isset( $_item_object_data['menu-item-xfn'] ) ? $_item_object_data['menu-item-xfn'] : '' ), 1199 ); 1200 1201 $items_saved[] = wp_update_nav_menu_item( $menu_id, $_actual_db_id, $args ); 1202 1203 } 1204 } 1205 1206 return $items_saved; 1207 } 1208 1209 /** 1210 * Adds custom arguments to some of the meta box object types. 1211 * 1212 * @since 3.0.0 1213 * 1214 * @access private 1215 * 1216 * @param object $data_object The post type or taxonomy meta-object. 1217 * @return object The post type or taxonomy object. 1218 */ 1219 function _wp_nav_menu_meta_box_object( $data_object = null ) { 1220 if ( isset( $data_object->name ) ) { 1221 1222 if ( 'page' === $data_object->name ) { 1223 $data_object->_default_query = array( 1224 'orderby' => 'menu_order title', 1225 'post_status' => 'publish', 1226 ); 1227 1228 // Posts should show only published items. 1229 } elseif ( 'post' === $data_object->name ) { 1230 $data_object->_default_query = array( 1231 'post_status' => 'publish', 1232 ); 1233 1234 // Categories should be in reverse chronological order. 1235 } elseif ( 'category' === $data_object->name ) { 1236 $data_object->_default_query = array( 1237 'orderby' => 'id', 1238 'order' => 'DESC', 1239 ); 1240 1241 // Custom post types should show only published items. 1242 } else { 1243 $data_object->_default_query = array( 1244 'post_status' => 'publish', 1245 ); 1246 } 1247 } 1248 1249 return $data_object; 1250 } 1251 1252 /** 1253 * Returns the menu formatted to edit. 1254 * 1255 * @since 3.0.0 1256 * 1257 * @param int $menu_id Optional. The ID of the menu to format. Default 0. 1258 * @return string|WP_Error|null The menu formatted to edit or error object on failure. 1259 * Null if the `$menu_id` parameter is not supplied or the term does not exist. 1260 */ 1261 function wp_get_nav_menu_to_edit( $menu_id = 0 ) { 1262 $menu = wp_get_nav_menu_object( $menu_id ); 1263 1264 // If the menu exists, get its items. 1265 if ( is_nav_menu( $menu ) ) { 1266 $menu_items = wp_get_nav_menu_items( $menu->term_id, array( 'post_status' => 'any' ) ); 1267 $result = '<div id="menu-instructions" class="post-body-plain'; 1268 $result .= ( ! empty( $menu_items ) ) ? ' menu-instructions-inactive">' : '">'; 1269 $result .= '<p>' . __( 'Add menu items from the column on the left.' ) . '</p>'; 1270 $result .= '</div>'; 1271 1272 if ( empty( $menu_items ) ) { 1273 return $result . ' <ul class="menu" id="menu-to-edit"> </ul>'; 1274 } 1275 1276 /** 1277 * Filters the Walker class used when adding nav menu items. 1278 * 1279 * @since 3.0.0 1280 * 1281 * @param string $class The walker class to use. Default 'Walker_Nav_Menu_Edit'. 1282 * @param int $menu_id ID of the menu being rendered. 1283 */ 1284 $walker_class_name = apply_filters( 'wp_edit_nav_menu_walker', 'Walker_Nav_Menu_Edit', $menu_id ); 1285 1286 if ( class_exists( $walker_class_name ) ) { 1287 $walker = new $walker_class_name(); 1288 } else { 1289 return new WP_Error( 1290 'menu_walker_not_exist', 1291 sprintf( 1292 /* translators: %s: Walker class name. */ 1293 __( 'The Walker class named %s does not exist.' ), 1294 '<strong>' . $walker_class_name . '</strong>' 1295 ) 1296 ); 1297 } 1298 1299 $some_pending_menu_items = false; 1300 $some_invalid_menu_items = false; 1301 1302 foreach ( (array) $menu_items as $menu_item ) { 1303 if ( isset( $menu_item->post_status ) && 'draft' === $menu_item->post_status ) { 1304 $some_pending_menu_items = true; 1305 } 1306 if ( ! empty( $menu_item->_invalid ) ) { 1307 $some_invalid_menu_items = true; 1308 } 1309 } 1310 1311 if ( $some_pending_menu_items ) { 1312 $message = __( 'Click Save Menu to make pending menu items public.' ); 1313 $notice_args = array( 1314 'type' => 'info', 1315 'additional_classes' => array( 'notice-alt', 'inline' ), 1316 ); 1317 $result .= wp_get_admin_notice( $message, $notice_args ); 1318 } 1319 1320 if ( $some_invalid_menu_items ) { 1321 $message = __( 'There are some invalid menu items. Please check or delete them.' ); 1322 $notice_args = array( 1323 'type' => 'error', 1324 'additional_classes' => array( 'notice-alt', 'inline' ), 1325 ); 1326 $result .= wp_get_admin_notice( $message, $notice_args ); 1327 } 1328 1329 $result .= '<ul class="menu" id="menu-to-edit"> '; 1330 $result .= walk_nav_menu_tree( 1331 array_map( 'wp_setup_nav_menu_item', $menu_items ), 1332 0, 1333 (object) array( 'walker' => $walker ) 1334 ); 1335 $result .= ' </ul> '; 1336 1337 return $result; 1338 } elseif ( is_wp_error( $menu ) ) { 1339 return $menu; 1340 } 1341 1342 return null; 1343 } 1344 1345 /** 1346 * Returns the columns for the nav menus page. 1347 * 1348 * @since 3.0.0 1349 * 1350 * @return string[] Array of column titles keyed by their column name. 1351 */ 1352 function wp_nav_menu_manage_columns() { 1353 return array( 1354 '_title' => __( 'Show advanced menu properties' ), 1355 'cb' => '<input type="checkbox" />', 1356 'link-target' => __( 'Link Target' ), 1357 'title-attribute' => __( 'Title Attribute' ), 1358 'css-classes' => __( 'CSS Classes' ), 1359 'xfn' => __( 'Link Relationship (XFN)' ), 1360 'description' => __( 'Description' ), 1361 ); 1362 } 1363 1364 /** 1365 * Deletes orphaned draft menu items 1366 * 1367 * @access private 1368 * @since 3.0.0 1369 * 1370 * @global wpdb $wpdb WordPress database abstraction object. 1371 */ 1372 function _wp_delete_orphaned_draft_menu_items() { 1373 global $wpdb; 1374 1375 $delete_timestamp = time() - ( DAY_IN_SECONDS * EMPTY_TRASH_DAYS ); 1376 1377 // Delete orphaned draft menu items. 1378 $menu_items_to_delete = $wpdb->get_col( 1379 $wpdb->prepare( 1380 "SELECT ID FROM $wpdb->posts AS p 1381 LEFT JOIN $wpdb->postmeta AS m ON p.ID = m.post_id 1382 WHERE post_type = 'nav_menu_item' AND post_status = 'draft' 1383 AND meta_key = '_menu_item_orphaned' AND meta_value < %d", 1384 $delete_timestamp 1385 ) 1386 ); 1387 1388 foreach ( (array) $menu_items_to_delete as $menu_item_id ) { 1389 wp_delete_post( $menu_item_id, true ); 1390 } 1391 } 1392 1393 /** 1394 * Saves nav menu items. 1395 * 1396 * @since 3.6.0 1397 * 1398 * @param int|string $nav_menu_selected_id ID, slug, or name of the currently-selected menu. 1399 * @param string $nav_menu_selected_title Title of the currently-selected menu. 1400 * @return string[] The menu updated messages. 1401 */ 1402 function wp_nav_menu_update_menu_items( $nav_menu_selected_id, $nav_menu_selected_title ) { 1403 $unsorted_menu_items = wp_get_nav_menu_items( 1404 $nav_menu_selected_id, 1405 array( 1406 'orderby' => 'ID', 1407 'output' => ARRAY_A, 1408 'output_key' => 'ID', 1409 'post_status' => 'draft,publish', 1410 ) 1411 ); 1412 1413 $messages = array(); 1414 $menu_items = array(); 1415 1416 // Index menu items by DB ID. 1417 foreach ( $unsorted_menu_items as $_item ) { 1418 $menu_items[ $_item->db_id ] = $_item; 1419 } 1420 1421 $post_fields = array( 1422 'menu-item-db-id', 1423 'menu-item-object-id', 1424 'menu-item-object', 1425 'menu-item-parent-id', 1426 'menu-item-position', 1427 'menu-item-type', 1428 'menu-item-title', 1429 'menu-item-url', 1430 'menu-item-description', 1431 'menu-item-attr-title', 1432 'menu-item-target', 1433 'menu-item-classes', 1434 'menu-item-xfn', 1435 ); 1436 1437 wp_defer_term_counting( true ); 1438 1439 // Loop through all the menu items' POST variables. 1440 if ( ! empty( $_POST['menu-item-db-id'] ) ) { 1441 foreach ( (array) $_POST['menu-item-db-id'] as $_key => $k ) { 1442 1443 // Menu item title can't be blank. 1444 if ( ! isset( $_POST['menu-item-title'][ $_key ] ) || '' === $_POST['menu-item-title'][ $_key ] ) { 1445 continue; 1446 } 1447 1448 $args = array(); 1449 foreach ( $post_fields as $field ) { 1450 $args[ $field ] = isset( $_POST[ $field ][ $_key ] ) ? $_POST[ $field ][ $_key ] : ''; 1451 } 1452 1453 $menu_item_db_id = wp_update_nav_menu_item( 1454 $nav_menu_selected_id, 1455 ( (int) $_POST['menu-item-db-id'][ $_key ] !== $_key ? 0 : $_key ), 1456 $args 1457 ); 1458 1459 if ( is_wp_error( $menu_item_db_id ) ) { 1460 $messages[] = wp_get_admin_notice( 1461 $menu_item_db_id->get_error_message(), 1462 array( 1463 'id' => 'message', 1464 'additional_classes' => array( 'error' ), 1465 ) 1466 ); 1467 } else { 1468 unset( $menu_items[ $menu_item_db_id ] ); 1469 } 1470 } 1471 } 1472 1473 // Remove menu items from the menu that weren't in $_POST. 1474 if ( ! empty( $menu_items ) ) { 1475 foreach ( array_keys( $menu_items ) as $menu_item_id ) { 1476 if ( is_nav_menu_item( $menu_item_id ) ) { 1477 wp_delete_post( $menu_item_id ); 1478 } 1479 } 1480 } 1481 1482 // Store 'auto-add' pages. 1483 $auto_add = ! empty( $_POST['auto-add-pages'] ); 1484 $nav_menu_option = (array) get_option( 'nav_menu_options' ); 1485 1486 if ( ! isset( $nav_menu_option['auto_add'] ) ) { 1487 $nav_menu_option['auto_add'] = array(); 1488 } 1489 1490 if ( $auto_add ) { 1491 if ( ! in_array( $nav_menu_selected_id, $nav_menu_option['auto_add'], true ) ) { 1492 $nav_menu_option['auto_add'][] = $nav_menu_selected_id; 1493 } 1494 } else { 1495 $key = array_search( $nav_menu_selected_id, $nav_menu_option['auto_add'], true ); 1496 if ( false !== $key ) { 1497 unset( $nav_menu_option['auto_add'][ $key ] ); 1498 } 1499 } 1500 1501 // Remove non-existent/deleted menus. 1502 $nav_menu_option['auto_add'] = array_intersect( 1503 $nav_menu_option['auto_add'], 1504 wp_get_nav_menus( array( 'fields' => 'ids' ) ) 1505 ); 1506 1507 update_option( 'nav_menu_options', $nav_menu_option, false ); 1508 1509 wp_defer_term_counting( false ); 1510 1511 /** This action is documented in wp-includes/nav-menu.php */ 1512 do_action( 'wp_update_nav_menu', $nav_menu_selected_id ); 1513 1514 /* translators: %s: Nav menu title. */ 1515 $message = sprintf( __( '%s has been updated.' ), '<strong>' . $nav_menu_selected_title . '</strong>' ); 1516 $notice_args = array( 1517 'id' => 'message', 1518 'dismissible' => true, 1519 'additional_classes' => array( 'updated' ), 1520 ); 1521 1522 $messages[] = wp_get_admin_notice( $message, $notice_args ); 1523 1524 unset( $menu_items, $unsorted_menu_items ); 1525 1526 return $messages; 1527 } 1528 1529 /** 1530 * If a JSON blob of navigation menu data is in POST data, expand it and inject 1531 * it into `$_POST` to avoid PHP `max_input_vars` limitations. See #14134. 1532 * 1533 * @ignore 1534 * @since 4.5.3 1535 * @access private 1536 */ 1537 function _wp_expand_nav_menu_post_data() { 1538 if ( ! isset( $_POST['nav-menu-data'] ) ) { 1539 return; 1540 } 1541 1542 $data = json_decode( stripslashes( $_POST['nav-menu-data'] ) ); 1543 1544 if ( ! is_null( $data ) && $data ) { 1545 foreach ( $data as $post_input_data ) { 1546 /* 1547 * For input names that are arrays (e.g. `menu-item-db-id[3][4][5]`), 1548 * derive the array path keys via regex and set the value in $_POST. 1549 */ 1550 preg_match( '#([^\[]*)(\[(.+)\])?#', $post_input_data->name, $matches ); 1551 1552 $array_bits = array( $matches[1] ); 1553 1554 if ( isset( $matches[3] ) ) { 1555 $array_bits = array_merge( $array_bits, explode( '][', $matches[3] ) ); 1556 } 1557 1558 $new_post_data = array(); 1559 1560 // Build the new array value from leaf to trunk. 1561 for ( $i = count( $array_bits ) - 1; $i >= 0; $i-- ) { 1562 if ( count( $array_bits ) - 1 === $i ) { 1563 $new_post_data[ $array_bits[ $i ] ] = wp_slash( $post_input_data->value ); 1564 } else { 1565 $new_post_data = array( $array_bits[ $i ] => $new_post_data ); 1566 } 1567 } 1568 1569 $_POST = array_replace_recursive( $_POST, $new_post_data ); 1570 } 1571 } 1572 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated : Wed Oct 22 08:20:04 2025 | Cross-referenced by PHPXref |