Quick Tip: Add Archive Pages to the WordPress Menu Builder

By Simon Codrington

When you create new content types with add_post_type you can specify if you want your individual post items to be accessible via the WordPress menu builder, giving you quick access to link to your single post.

It’s a pretty useful feature, and all you need to do to take advantage of it is to specify that the add_to_menu property is set to true. What’s not so easy is if you wanted to link directly to your post types archive page.

archive page plugin

Creating a simple interface for your archives.

By default, WordPress doesn’t provide an easy way to to link to your Post Type archives. This makes sense, as internally WordPress handles the post content type as your blog (controlled by your site’s reading settings) and the page content type doesn’t require an archive page. However, if we have custom post types it would be great to have them as menu items so it’s easy to link to them (without having to rely on using the custom menu elements).

Creating a Simple Plugin

The first step is creating a basic plugin to hold our code. There’s heaps of great plugin tutorials out there if you’re new to WordPress plugin development, including these previous articles on SitePoint:

We’re not going to do anything overly complex in this article, however it’s good practice to wrap up functionality in a plugin so it can be redeployed across different sites.

Firstly, we need to create a new folder and add a new PHP file. Inside that file, let’s create our main plugin class and add the basic structure we need.

/*
Plugin Name: Add Archive Pages to Menu
Plugin URI: https://elevate360.com.au/plugins/archive-page
Description: Adds a new metabox item to WordPress's menu builder. Allows quick access to add your post types archive pages instead of having to rely on custom links 
Version: 1.0.0
Author: Simon Codrington
Author URI: http://simoncodrington.com.au
Text Domain: archive-pages-to-menu
Domain Path: /languages
*/

//main plugin class
class el_archive_pages_menu{

    public function __construct(){
        add_action('admin_init', array($this, 'add_meta_box'));
    }
    //add metabox to the navmenu builder
    public function add_meta_box(){
    }
    //output for the metabox
    public function display_meta_box(){

    }

}
$el_archive_pages_menu = new el_archive_pages_menu();

Nothing overly special here. Feel free to change up the plugin header information if you plan on re-distributing the plugin.

Adding a Custom Meta Box to the Menu Builder

We want to mimic the way WordPress already handles adding elements to the menu builder, with a left hand side content selector that’s simple to use.

We hook into the add_meta_boxes action to add a custom meta box. The secret is using the correct context so it outputs in the right spot. Add the following:

//register our meta box for our links
public function add_meta_box(){
        add_meta_box(
            'el_archive_page_menu_metabox',
            __('Archive Pages', 'archive-pages-to-menu'),
            array($this, 'display_meta_box'),
            'nav-menus',
            'side',
            'low'
        );
    }

Customising Our Meta Box

Here’s where all the magic happens. The bulk of the functionality is based on how WordPress adds menu items using the ‘custom’ link type elements on the left, we just repurpose it for our needs.

Copy the following into the display_meta_box function we defined earlier. To get the meta box looking right you need to define your markup as I’ve done below. WordPress uses a combination of class names and ID’s to make the whole ‘add to menu’ functionality work. In short, I would get the following markup to work and then change it up if you need to do something fancy.

//displays a metabox that will let users link directly to post type archives
public function display_meta_box(){

    ?>
    <div id="posttype-archive-pages" class="posttypediv">
        <div id="tabs-panel-archive-pages" class="tabs-panel tabs-panel-active">
            <p>These will link your users directly to your post type archives (if the post type allows)</p>
            <ul id="archive-pages" class="categorychecklist form-no-clear">
                <!--Custom -->
                <?php
                //loop through all registered content types that have 'has-archive' enabled 
                $post_types = get_post_types(array('has_archive' => true));
                if($post_types){
                    $counter = -1;
                    foreach($post_types as $post_type){
                        $post_type_obj = get_post_type_object($post_type);
                        $post_type_archive_url = get_post_type_archive_link($post_type);
                        $post_type_name = $post_type_obj->labels->singular_name;
                        ?>
                        <li>
                            <label class="menu-item-title">
                                <input type="checkbox" class="menu-item-checkbox" name="menu-item[<?php echo $counter; ?>][menu-item-object-id]" value="-1"/>Archive Page: <?php echo $post_type_name; ?>
                            </label>
                            <input type="hidden" class="menu-item-type" name="menu-item[<?php echo $counter; ?>][menu-item-type]" value="custom"/>
                            <input type="hidden" class="menu-item-title" name="menu-item[<?php echo $counter; ?>][menu-item-title]" value="<?php echo $post_type_name; ?>"/>
                            <input type="hidden" class="menu-item-url" name="menu-item[<?php echo $counter; ?>][menu-item-url]" value="<?php echo $post_type_archive_url; ?>"/>
                            <input type="hidden" class="menu-item-classes" name="menu-item[<?php echo $counter; ?>][menu-item-classes]"/>
                        </li>
                        <?php
                        $counter--;
                    }
                }?>     
            </ul>
        </div>
        <p class="button-controls">
            <span class="list-controls">
                <a href="<?php echo admin_url('nav-menus.php?page-tab=all&selectall=1#posttype-archive-pages'); ?>" class="select-all"> <?php _e('Select All', 'archive-pages-to-menu' ); ?></a>
            </span>
            <span class="add-to-menu">
                <input type="submit" class="button-secondary submit-add-to-menu right" value="<?php _e('Add to Menu', 'archive-pages-to-menu') ?>" name="add-post-type-menu-item" id="submit-posttype-archive-pages">
                <span class="spinner"></span>
            </span>
        </p>
    </div>
    <?php
}

The functionality is split into two sections; the top section that displays all of our options and the lower section that contains our Select All and Add button.

Important Notes

There’s a few things to focus on here as they affect the way the functionality works:

  1. The top level div’s ID has to relate to the submit button used to add the items to the menu. In our example the div’s ID is posttype-archive-pages and the submit button’s ID is submit-posttype-archive-pages. In addition, inside the list-controls element there a link that’s used as a select all button. You need to ensure your ID is added here too e.g admin_url('nav-menus.php?page-tab=all&selectall=1#posttype-archive-pages');
  2. To get our functionality to work we query all post types that have their archive enabled. We then loop through each and get their archive page link. Only Post Types that have implicitly registered with an archive will appear here.
  3. When looping through our Post Types to display, we use a negative counter variable in the name of the inputs, this is intentional as WordPress expects these to be negative for some reason.
  4. The way the system works is based off the hidden input values attached to each li item. These are important as they tell the menu builder what type of element this is. For our example, we leverage the custom type and pass our values.

There are a few moving pieces here, but we mimic what WordPress does and alter it for our own purposes. At the end of the process you should see our custom selector.

Wrapping It All Up

That’s all there is to it. You should now have a simple to use meta box that lets you quickly add your archive pages to your WordPress menu. You don’t need to manually type in the URL to your archive pages, the plugin will do that automatically. Another benefit is that if you ever change your rewrite rules (pretty permalinks) the URL will automatically update.

Using this plugin and the ideas we looked at today, you could expand on this and create additional meta boxes to add any custom links and data to your WordPress menu builder.

  • Abegail Louise Acosta

    Nice tip! I am a web designer and not much of a developer. So, this really help me a lot.
    #Professional Website Development Company

    • simon codrington

      Glad it helped you out Abegail :)

  • C-3PO

    This is actually built into WordPress for custom post types as the first option under “view all”. Seems taxonomy archives are still missing this tho.

    • simon codrington

      Yep exactly. It seems to be either an oversight or something core thinks people can develop for themselves. Re-leveraging existing code like this should make it easier than using the ‘custom’ option (as if the permalinks / name every changes it can ruin the link)

      Thanks for reading my article :)

  • Love the 2nd lesson on this, in how easy it is to incapsulate the solution into a class.

    • simon codrington

      Thanks Hensley. Glad you found the article useful. Cheers

  • Jason Sully

    WebhostFive offers premium hosting for only $3.96 per month, can’t beat it and save yourself the hassles of dealing with a terrible company.

    www. webhostfive .com

Recommended
Sponsors
Get the latest in WordPress, once a week, for free.