Enhancing WordPress Custom Menus for Navigation

  • Category : WordPress
  • Posted on : Sep 24, 2018
  • Views : 1,606
  • By : Radcliff S.

Custom navigation menus are an amazing WordPress feature that allows admin users to add any list of links anywhere into the site. They can be links to internal resources, like posts, pages and archives, as well as links to external resources, like your social profiles pages. WordPress reserves a specific admin page to custom navigation menus. In this page the admin user can create menus, arrange items and set properties which affect the way they appear in the front-end.

That being said, the main questions behind this post is: “Can we extend the built-in functionalities of the Menus Screen?”
Of course we can. So I will dive deep into the topic, and I will dissect sections and objects that allow to add and manage menus and their items. But it won’t be just a description of the screen. I will show you how to add custom items to the accordion menu placed in the left sidebar.

These custom boxes will show additional lists of links to choose from when building custom navigation menus. More specifically, I will show you how to add a box containing links to the site authors’ archives.
Let’s start diving into building a WordPress custom menu.

The Appearance Menus Screen

The Menus editing page allows users to create and manage custom menus thanks to an intuitive interface. The Screen Options button in the upper right corner unveils the first section of the page which contains two groups of checkboxes. The Boxes group allows the user to hide and show menu meta boxes in the left sidebar, while the Show advanced menu properties group hides and shows properties like link targets, descriptions and CSS classes for list items.

Once the first menu has been created, the Menus Screen looks a bit different from its default appearance. New sections appear in the Edit Menus tab.

  • The top section provides a select menu to choose from and a create a new menu link;
  • The left column holds an accordion menu containing the available menu item meta boxes. These meta boxes contain items selected from pages, posts, categories, tags, taxonomies, post types and post formats (shortly, custom post types and taxonomies).
  • The Menu Structure section holds the menu item modules from which the user can arrange links and set their attributes and properties.
  • The Menu Settings section provides a list of check boxes that link menus to registered theme locations.

Meta Boxes and Modules

The left sidebar of Menus Screen holds an accordion menu whose elements are named menu item meta boxes. These blocks provide a number of form fields and controls that allow the user to search the site content and add links to navigation menus.
By default WordPress provides the following menu item meta boxes:

  • pages
  • posts
  • custom links
  • categories
  • tags
  • post formats
  • custom post types
  • custom taxonomies

When the user adds a link to a menu, a new item module is appended to the Menu Structure section of the screen. WordPress provides four typologies of menu item modules:

  • Post Type module (posts, pages and custom post types);
  • Post Type Archive module;
  • Taxonomy module (for categories, tags, custom taxonomies and post formats);
  • Custom Links module.

We are going to to extend the built-in structure of the Appearance Menus Screen with custom meta boxes that would allow the admin user to select pieces of content not available by default, like author archives.
Unfortunately, the Core Navigation Menu API is not documented and to achieve our goal it was necessary to dissect line by line two core files: /wp-admin/includes/nav-menu.php for menu meta boxes, and /wp-includes/nav-menu.php for menu item modules.
Let’s start coding.

Create a WordPress Custom Menu Item Meta Box

Create a new plugin and add the following code to the main file:

/**
 * Add menu meta box
 *
 * @param object $object The meta box object
 * @link https://developer.wordpress.org/reference/functions/add_meta_box/
 */
function custom_add_menu_meta_box( $object ) {
add_meta_box( 'custom-menu-metabox', __( 'Authors' ), 'custom_menu_meta_box', 'nav-menus', 'side', 'default' );
return $object;
}
add_filter( 'nav_menu_meta_box_object', 'custom_add_menu_meta_box', 10, 1);

The WordPress add_meta_box function registers meta boxes in admin pages. It keeps the following arguments:

Argument Type Description
$id string (required) the meta box ID
$callback callable(required) the function that echoes the meta box content
$screen string|array|WP_Screen(optional) the screen on which to show the meta box
$context string (optional) the context within the screen where the meta box shloud display (possible values are 'normal''side''advanced', defaults to 'advanced')
$priority string (optional) possible values 'high''low''default'
$callback_args array (optional) an array of arguments to be passed to the callback

Maybe you would have expected the add_meta_box function would be hooked to an action like add_meta_boxes, which is triggered in post editing pages. Unfortunately, we don’t have an action in nav-menus.php we can hook the function to, so we’re forced to use the nav_menu_meta_box_object filter. This hook determines whether a menu item meta box will be added for an object type. When the filter runs, add_meta_box registers the custom meta box.
Now we can define the callback function which will produce the HTML content for the meta box.

/**
 * Displays a metabox for an author menu item.
 *
 * @global int|string $nav_menu_selected_id (id, name or slug) of the currently-selected menu
 */
function custom_menu_meta_box(){
global $nav_menu_selected_id;
$walker = new Walker_Nav_Menu_Checklist();
...
}

The global variable keeps memory of the current menu ID, while $walker stores a new instance of Walker_Nav_Menu_Checklist object, which builds the HTML list of menu items.
Then, we have to set the value of $current_tab variable which will determine the active tab in the meta box. To keep things easy, I will provide just two tabs:

$current_tab = 'all';
if ( isset( $_REQUEST['authorarchive-tab'] ) && 'admins' == $_REQUEST['authorarchive-tab'] ) {
$current_tab = 'admins';
}elseif ( isset( $_REQUEST['authorarchive-tab'] ) && 'all' == $_REQUEST['authorarchive-tab'] ) {
$current_tab = 'all';
}

Following, we will get all users with write privileges, adding a number of properties to the $authors object.

$authors = get_users( array( 'orderby' => 'nicename', 'order' => 'ASC', 'who' => 'authors' ) );
$admins = array();
/* set values to required item properties */
foreach ( $authors as &$author ) {
$author->classes = array();
$author->type = 'custom';
$author->object_id = $author->nickname;
$author->title = $author->nickname . ' - ' . implode(', ', $author->roles);
$author->object = 'custom';
$author->url = get_author_posts_url( $author->ID ); 
	$author->attr_title = $author->displayname;
if( $author->has_cap( 'edit_users' ) ){
$admins[] = $author;
}
}
$removed_args = array( 'action', 'customlink-tab', 'edit-menu-item', 'menu-item', 'page-tab', '_wpnonce' );
?>

get_users returns an array of $user objects selected by the specified parameters. The who param forces WordPress to query the database just for users with writing privileges.
$admins array will store an array of authors, while the latest variable, $removed_args, will store a list of query vars to be removed.
When we’re done with data, we can print the meta box mark-up. First we’ll build tab labels and links:

<div id="authorarchive" class="categorydiv">
<ul id="authorarchive-tabs" class="authorarchive-tabs add-menu-item-tabs">
<li <?php echo ( 'all' == $current_tab ? ' class="tabs"' : '' ); ?>>
<a class="nav-tab-link" data-type="tabs-panel-authorarchive-all" href="<?php if ( $nav_menu_selected_id ) echo esc_url( add_query_arg( 'authorarchive-tab', 'all', remove_query_arg( $removed_args ) ) ); ?>#tabs-panel-authorarchive-all">
<?php _e( 'View All' ); ?>
</a>
</li><!-- /.tabs -->
<li <?php echo ( 'admins' == $current_tab ? ' class="tabs"' : '' ); ?>>
<a class="nav-tab-link" data-type="tabs-panel-authorarchive-admins" href="<?php if ( $nav_menu_selected_id ) echo esc_url( add_query_arg( 'authorarchive-tab', 'admins', remove_query_arg( $removed_args ) ) ); ?>#tabs-panel-authorarchive-admins">
<?php _e( 'Admins' ); ?>
</a>
</li><!-- /.tabs -->
</ul>

We have to be careful and assign the correct class names, IDs and data attributes to the elements of the meta box. In case of error, the accordion menu won’t work properly.
We used add_query_arg and remove_query_arg functions to set tab specific values for authorarchive-tab query var and remove unnecessary query variables.
The image below shows the resulting tab labels.

Next, we will build the HTML content of the tabs (again, pay attention to class names and IDs):

<div id="tabs-panel-authorarchive-all" class="tabs-panel tabs-panel-view-all <?php echo ( 'all' == $current_tab ? 'tabs-panel-active' : 'tabs-panel-inactive' ); ?>">
<ul id="authorarchive-checklist-all" class="categorychecklist form-no-clear">
<?php
echo walk_nav_menu_tree( array_map('wp_setup_nav_menu_item', $authors), 0, (object) array( 'walker' => $walker) );
?>
</ul>
</div><!-- /.tabs-panel -->
<div id="tabs-panel-authorarchive-admins" class="tabs-panel tabs-panel-view-admins <?php echo ( 'admins' == $current_tab ? 'tabs-panel-active' : 'tabs-panel-inactive' ); ?>">
<ul id="authorarchive-checklist-admins" class="categorychecklist form-no-clear">
<?php
echo walk_nav_menu_tree( array_map('wp_setup_nav_menu_item', $admins), 0, (object) array( 'walker' => $walker) );
?>
</ul>
</div><!-- /.tabs-panel -->

Each tab contains a list of checkboxes. This list is printed by the walk_nav_menu_treefunction, which keeps the following three arguments:

Argument Type Description
$items array (required) An array of menu items
$depth int (required) How many levels of the hierarchy are to be included
$r object (required) Instance of a custom Walker_Nav_Menu_Checklist class

$items stores the array of admin users. The array_map function applies the wp_setup_nav_menu_item function to $admins, adding menu item properties to the array elements.
The following image shows the resulting checklist of authors.

Now we can add the submit button and a spinner.

<p class="button-controls wp-clearfix">
<span class="list-controls">
<a href="<?php echo esc_url( add_query_arg( array( 'authorarchive-tab' => 'all', 'selectall' => 1, ), remove_query_arg( $removed_args ) )); ?>#authorarchive" class="select-all"><?php _e('Select All'); ?></a>
</span>
<span class="add-to-menu">
		<input type="submit"<?php wp_nav_menu_disabled_check( $nav_menu_selected_id ); ?> class="button-secondary submit-add-to-menu right" value="<?php esc_attr_e('Add to Menu'); ?>" name="add-authorarchive-menu-item" id="submit-authorarchive" />
		<span class="spinner"></span>
</span>
</p>
</div><!-- /.categorydiv -->
<?php
}

The link in the first span element allows to select all check boxes in the current list, thanks to the query argument selectall. The second span contains a submit button which will be disabled by wp_nav_menu_disabled_check function if no items are checked.
Finally, the span.spinner element adds a spinner icon.
The Authors Meta Box is complete, and we can appreciate the result of our hard work in the following image.

Summary

WordPress Core Navigation Menu API is a set of core functions which create sections and objects in Appearance Menus Screen. Unfortunately, this API is not documented in the Codex, and the only way to learn how to change the Menus Screen functionalities is an in-depth dissection of /wp-admin/includes/nav-menu.php file.
Same problem with menu item modules. How are they structured? How many typologies are available? Can we create new typologies? No answers to these questions in the Codex. At the time of writing, the only way to better understand item modules is to analyze /wp-admin/includes/nav-menu.php core file, and the wp_setup_nav_menu_item function (line 730 in WordPress 4.5).
For these reasons, this post can be considered a first approach to this topic, and I hope you readers would leave your suggestions and ideas for discussion in the comments below.

 

Subscribe Now

10,000 successful online businessmen like to have our content directly delivered to their inbox. Subscribe to our newsletter!

Archive Calendar

Sat Sun Mon Tue Wed Thu Fri
 1
2345678
9101112131415
16171819202122
23242526272829
30  

Over 20000 Satisfied Customers!

From 24/7 support that acts as your extended team to incredibly fast website performance

Zelt staff were fantastic, I had a concern with a domain and they got back to me very quickly and they helped me to resolve the issue!

author
Technician, Diageo PLC

I'm using Zelt for my portfolio since 2006. The transition was seamless, the support was immediate, and everything works perfectly.

author
Photographer, Allister Freeman

Very easy to understand & use even though I am not very technologically minded. No complications whatsoever & I wouldn't hesitate to recommend it to all.

author
Actor, A&J Artists

Zelt support team have been amazingly responsive and helpful to any of my queries, thank you so much to the Zelt have been amazingly responsive and helpful to any of my queries 👍👍👍

author
Technician, Diageo PLC

Anytime I've had a problem I can't solve, I've found Zelt to be diligent and persistent. They simply won't let an issue go until the client is happy.

author
Doctor, SmartClinics

Zelt support team have been amazingly responsive and helpful to any of my queries, thank you so much to the Zelt have been amazingly responsive and helpful to any of my queries 👍👍👍

author
Freelancer, Fiverr

24/7 World-Class Support

Ran into trouble? Contact our Customer Success team any time via live chat or email.

  • Receive professional WordPress support
  • Our specialists are available round
Get Support