Add Post Type Archive Links to Your Menu

A common request, particularly for those who have created custom post types like 'News' or 'Events', is to add a link to their post type's archive page on their navigation menu. Currently, however, this can only be done by manually entering the post type archive URL. Apart from being fairly inelegant, this solution has a few drawbacks: it doesn't always appear as 'current', if you change your permalink structure it could break the link, manually adding the URLs is tedious and the link does not appear as 'current' when on a post of that post type.

In this tutorial I will show you how to produce a plugin that creates a meta-box on your Appearance -> Menu page which allows you to add post type archive links. These links don't suffer from the drawbacks mentioned above.


Step 1 Creating a Plugin

This plugin will be called 'My Post Type Archive Links', and to that end first create a folder called my-post-type-archive-links under your /wp-content/plugins/ folder, and inside that create a file my-post-type-archive-links.php. This file is the main plugin file. We're going to wrap it in a class – this is simply so we don't have to worry about our function names clashing with WordPress or other plugins: we simply need to make sure that our class name is unique. Add the following to my-post-type-archive-links.php

Everything in this tutorial will sit inside that class.


Step 2 Loading the Plugin

When the plugin file is loaded, it will fire the class method load(). This method will be responsible for adding actions and filters onto various WordPress hooks. We'll go through each of them in the subsequent steps, but it also provides a useful summary. Add the following method to our class:

Let's summarise what each of these parts are doing:

  1. Add a meta-box – Fairly self explanatory. The hooked function will be responsible for adding our meta box.
  2. Enqueue JavaScript – We use the admin_enqueue_scripts hook to enqueue our JavaScript file. Our JavaScript, when 'add to menu' is clicked, will trigger an AJAX request.
  3. AJAX callback – This function is responsible for handling the above AJAX request. It will create the menu items and add them onto the menu.
  4. Menu item set up – This ensures that when the archive link appears on your menu it correctly points to the post type's archive.
  5. Maybe make current – Whenever a menu appears its items are passed through a filter, we will ensure that the class 'current-menu-item' is added to the appropriate post type link.

Step 3 Adding the Metabox

First we define our add_meta_box method, which simply calls the WordPress function add_meta_box(). The details of this function have been covered many times before, but if you are unsure you can read up on it in the Codex pages.

Next we define the meta-box callback function which is responsible for displaying the metabox's insides:

This method simply gets all public custom post types with get_post_types() and then loops through them to create a list of checkboxes. Each checkbox has the name of the post type as its value. In the next step we add some javascript that will be triggered when a user clicks the 'Add to Menu' button.


Step 4 The JavaScript

We only want to enqueue our JavaScript on the Appearance -> Menu admin page. We've used the admin_enqueue_scripts hook which fires only on admin pages and passes the page's hook as an argument. The hook for the Appearance -> Menu page is nav-menus.php. After enqueueing our script we use wp_localize_script to make the nonce available in our JavaScript. We include it in the AJAX request to help verify that the action was intended.

In the previous step the 'Add to Menu' button was given the ID submit-post-type-archives. We now use jQuery to target that button and, when clicked, send an AJAX request to create the menu item and append it to the menu. The following is the only part of this tutorial that lives outside our class. It should go in a file called metabox.js, inside our plug-in folder.

Notice the URL we are sending the request to: ajaxurl. We haven't defined it anywhere. It's a global variable set by WordPress on the admin side only which points to the page that WordPress uses to handle AJAX requests. When the submit button is clicked, the names of the checked post types, a unique action and nonce are all sent to this URL. When WordPress receives the request it triggers the wp_ajax_my-add-post-type-archive-links hook. The nonce is a security precaution to help verify that the action was intended.


Step 5 The AJAX Callback

We now define the AJAX callback function ajax_add_post_type.

Let's go through this callback a bit at a time. First we check the user's permissions, verify the nonce and load the nav-menu.php page (we need some of the functions).

We then create a menu item for each selected post type. We first check the post type we received exists by checking the value returned by get_post_type_object(). We can obtain the archive link with the function get_post_type_archive_link()

Menu items are in fact posts of post type 'nav_menu_item' with built-in post meta, including fields relating to 'url', 'type' and 'object'. The item's 'type' is normally 'custom', 'post_type' or 'taxonomy' – but we shall set its value to 'post_type_archive'. The item's 'object' meta value is normally only used for items of 'post_type' or 'taxonomy' type and refers to post type or taxonomy the link refers to. We'll use this to store the post type of the archive link.

Next we simply generate the HTML which will be added to the menu. We use the $item_ids array to get an array of menu items and pass this to a WordPress walker class to do the hard work for us.


Step 6 The Menu Item

Unfortunately, because of a bug with WordPress, if your item's type is not 'taxonomy', 'custom' or 'post_type' the URL gets removed. To counter this, when a 'post_type_archive' link is used in a menu, we manually re-add the URL. This also ensures the archive link is 'up to date' (in case your permalink structure has been changed).


Step 7 Making the Link Current

Finally we need to make the item 'current' when we are on the appropriate page. I want the post type archive link to be highlighted as current if we are on that archive page, or viewing a single post of that type. To do this I check:

To make the item current, we simply need to add current-menu-item to the item's classes which are stored in $item->classes. We then have to loop through its parents in the menu and add the classes current_item_parent and current_item_ancestor. Let's look at each bit individually:

We loop through each of the items in the menu:

If the item is not of 'post_type_archive' or if it is, but we don't want it to make it 'current', we simply skip on to the next item. Recall that for our archive links, the post type is stored as the item's object. So inside the foreach loop:

If we do want to make it current we give it the appropriate class and then grab its parents in the menu. A menu item's parent are stored as post meta with meta key _menu_item_menu_item_parent.

We then loop through the menu items and give the 'current' item's parents and ancestors the appropriate classes.

Putting that function together:

All that remains is to go to your plugins admin page and activate the plugin.


Conclusion

There is always room for improvement. For instance, with a bit of jQuery you could add a 'Select All' link underneath the checkboxes or display a 'loading' symbol while the AJAX is processing. Now this plugin isn't the simplest solution – but it does work well and avoids the pitfalls of simply adding a custom link. The above plugin in it's entirety can be found on my GitHub. If you have any comments or suggestions, feel free to leave a comment or contact me via Twitter.

Tags:

Comments

Related Articles