Enhancing WordPress Custom Menus for Navigation
- Category : WordPress
- Posted on : Sep 24, 2018
- Views : 2,483
- 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_tree
function, 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.
Categories
- cPanel Question 47
- cPanel Software Management 29
- cPanel Tutorials 13
- Development 29
- Domain 13
- General 19
- Linux Helpline (Easy Guide) 156
- Marketing 47
- MySQL Question 13
- News 2
- PHP Configuration 14
- SEO 4
- SEO 42
- Server Administration 84
- SSL Installation 54
- Tips and Tricks 24
- VPS 3
- Web Hosting 44
- Website Security 22
- WHM questions 13
- WordPress 148
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 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 |
Recent Articles
-
Posted on : Sep 17
-
Posted on : Sep 10
-
Posted on : Aug 04
-
Posted on : Apr 01
Tags
- ts
- myisam
- vpn
- sql
- process
- kill
- tweak
- server load
- attack
- ddos mitigation
- Knowledge
- layer 7
- ddos
- webmail
- DMARC
- Development
- nginx
- seo vpn
- Hosting Security
- wireguard
- innodb
- exim
- smtp relay
- smtp
- VPS Hosting
- cpulimit
- Plesk
- Comparison
- cpu
- encryption
- WHM
- xampp
- sysstat
- optimize
- cheap vpn
- php-fpm
- mariadb
- apache
- Small Business
- Error
- Networking
- VPS
- SSD Hosting
- Link Building
- centos
- DNS
- optimization
- ubuntu