Close

Menu Snippets

How to Add a Login/Logout Link to WordPress Menus

In my last post, I talked about how to add a custom meta box with a custom link to WordPress navigation menus. Several times, I mentioned how I needed to add a specific class to that link for WishList Login 2.0, so that I could find that link later and do stuff with it.

This is the part where we “do stuff” with that link. Specifically, we’re going to hook into the navigation menu before it displays, find our link and change its display based on the current user’s login status. Here’s what it looks like:

Adding a Login/Logout Link

So, if they’re logged in, we’re going to change it to a logout link. If they’re not logged, then we’ll leave it alone. Here’s the code to do that:

if ( !class_exists( 'HijackMe' ) ) {
class HijackMe {
public function hijack_menu($objects) {
/**
* If user isn't logged in, we return the link as normal
*/
if ( !is_user_logged_in() ) {
return $objects;
}
/**
* If they are logged in, we search through the objects for items with the
* class wl-login-pop and we change the text and url into a logout link
*/
foreach ( $objects as $k=>$object ) {
if ( in_array( 'wl-login-pop', $object->classes ) ) {
$objects[$k]->title = 'Logout';
$objects[$k]->url = wp_logout_url();
$remove_key = array_search( 'wl-login-pop', $object->classes );
unset($objects[$k]->classes[$remove_key]);
}
}

return $objects;
}
}
}

$hijackme = new HijackMe;

add_filter('wp_nav_menu_objects', array($hijackme, 'hijack_menu'), 10, 2);

Here’s a run-down of what’s happening:

  1. We’re hooking in using the wp_nav_menu_objects filter.
  2. If the user is logged out, we just return the link as it is since by default it’s a login link.
  3. If the user is logged in, we then loop through the items and search the “classes” array element for our class.
  4. If the class exists in an item object, then we alter the “title” and “url” of that link and we unset our target class from the object.
  5. Then, we simply return the new objects array.

There’s definitely some customization and abstraction you could do with this:

1. Edit only the menu for a certain theme location. You’ll notice in the hook, I have 2 arguments being sent to the callback function, but I’m only actually using the first one. That’s to show you that there are actually 2 arguments available. The second one is the $args array which will contain information about that menu… including the theme location.

You would simply run a check on that array to see if the current theme location is the one you want to edit. This is especially important if you don’t want to touch widget menus at all… since, the Custom Menu widget uses the same back-end functionality as regular menus.

In our case, we wanted to hijack every instance of a link that contained our special class… even in a widget… so, we didn’t run any such check.

2. Abstract the target class. In our function, the target class we search for is hard-coded. In this instance, it’s fine because there’s no reason to have an option to change that class. You may have a scenario where it does make sense to allow users to change that class. In this case, you’d want to abstract that out by creating an admin option. You could then use get_option() to retrieve the target class and alter your menu accordingly.

So, that’s it. It’s a pretty straight-forward way to hook in and alter navigation menus how you’d like. And, IMHO, a hell of a lot easier than dealing with wp_nav_menu_items() and futzing with parsing HTML, and so on.

March 26, 2013

How to Add a Fully Functional Custom Meta Box to WordPress Navigation Menus

When I created WishList Login 2.0, I wanted to an easy way for users to add a dynamic login/logout link to their navigation menus. As simple as it sounds, it’s not something you can do natively in WordPress and we can requests for this kind of thing all the time.

In WishList Login 1.0, I had added an entire admin interface in the plugin settings that had all the necessary options for creating the link, editing its text, setting its position, and so on.

Of course, this was before WordPress added menus, so I didn’t have much choice… but, now WITH WordPress’ menus, it seemed silly to create a redundant interface… especially when the WordPress menus handled all the things need to added a menu link in a much clear way than my original interface did.

All that led me to figure out how to add my own meta box to the WordPress navigation menu interface with (important) the ability to add a custom link that contained certain parameters I needed in order to hijack the link’s display on the front-end.

Here’s what it looks like in the admin:

WordPress Custom Meta Box

Notice the CSS class. This was really the most important part because it’s what allows me to identify this link later and change it to a login link if the user is logged out and a logout link if the user is logged in.

Here’s a look at the code to make this happen:

if ( !class_exists('JMO_Custom_Nav')) {
class JMO_Custom_Nav {
public function add_nav_menu_meta_boxes() {
add_meta_box(
'wl_login_nav_link',
__('WishList Login'),
array( $this, 'nav_menu_link'),
'nav-menus',
'side',
'low'
);
}

public function nav_menu_link() {?>
<div id="posttype-wl-login" class="posttypediv">
<div id="tabs-panel-wishlist-login" class="tabs-panel tabs-panel-active">
<ul id ="wishlist-login-checklist" class="categorychecklist form-no-clear">
<li>
<label class="menu-item-title">
<input type="checkbox" class="menu-item-checkbox" name="menu-item[-1][menu-item-object-id]" value="-1"> Login/Logout Link
</label>
<input type="hidden" class="menu-item-type" name="menu-item[-1][menu-item-type]" value="custom">
<input type="hidden" class="menu-item-title" name="menu-item[-1][menu-item-title]" value="Login">
<input type="hidden" class="menu-item-url" name="menu-item[-1][menu-item-url]" value="<?php bloginfo('wpurl'); ?>/wp-login.php">
<input type="hidden" class="menu-item-classes" name="menu-item[-1][menu-item-classes]" value="wl-login-pop">
</li>
</ul>
</div>
<p class="button-controls">
<span class="list-controls">
<a href="/wordpress/wp-admin/nav-menus.php?page-tab=all&amp;selectall=1#posttype-page" class="select-all">Select All</a>
</span>
<span class="add-to-menu">
<input type="submit" class="button-secondary submit-add-to-menu right" value="Add to Menu" name="add-post-type-menu-item" id="submit-posttype-wl-login">
<span class="spinner"></span>
</span>
</p>
</div>
<?php }
}
}

$custom_nav = new JMO_Custom_Nav;

add_action('admin_init', array($custom_nav, 'add_nav_menu_meta_boxes'));

Most of this is pretty straight-forward.

  1. You hook into WordPress using the “add_nav_menu_meta_boxes” hook.
  2. You call¬†add_meta_box() in your callback function setting “nav-menus” as the $post_type parameter.
  3. Finally, in your callback function for add_meta_box() you lay out your meta box HTML.

It’s that last part that can get tricky in order to make the adding of your custom link item to the nav menu… so, we’ll look at this more in-depth.

1. First, you need to make sure your HTML is set up correctly. The JavaScript that actually processes the adding of the link to the menu selects your link item in a specific way… so altering the HTML can break it. The above works. I’d recommend copying it and then just altering the small bits you need to.

Or, you can do what I did which is to copy the Category meta box native in WordPress and change what I needed.

2. Next, you need to adjust the main container div ID and the submit button ID so they match. This is part of how the jQuery works. You need to change these so they are unique and you need to make them match.

In the example code, you’ll notice the name of the container div ID is “posttype-wl-login” and the name of the submit button ID is “submit-posttype-wl-login”. This is the kind of relationship these two items need to have.

3. Finally, you’ll edit the inputs in the un-ordered list. These are the bare minimum I needed for everything to work. You can alter the values of these to what you’d like to display be default. You can also check other native meta boxes to see what inputs are available. The important one in our case was the “menu-item-classes”. This sets the CSS classes the link item will have by default and is what I used to hijack the menu later on.

And, that’s it. Once you have that all set up. Your users will be able to add your custom link item from your custom meta box to any of their menus and it’ll have the information pre-loaded in it that you may need later on when displaying menus.

Later, I’ll write up a tutorial on how to hijack menu items when you display the menus, so you can alter your custom link as you need.

March 22, 2013