Quick Tip: Displaying the current user’s username in UberMenu

UberMenu allows you to place any shortcode content inside your menu, so adding custom content becomes as simple as writing a shortcode to return that content.

In this example, we’ll grab the current user’s username and display it in the menu.

Write the shortcode

First we want to create a simple shortcode which we will call like this in the menu: [current-username]. We’ll use WordPress’s core wp_get_current_user() function to retrieve the current user object and then return the user’s display name.

add_shortcode( 'current-username' , 'ss_get_current_username' );
function ss_get_current_username(){
	$user = wp_get_current_user();
	return $user->display_name;
}

This PHP can be added to the theme’s functions.php, preferably in a child theme to preserve changes more easily.

For a more detailed explanation of writing custom shortcodes, see How to write a custom shortcode for UberMenu.

Add the shortcode to the menu

You can create the username menu item with whichever type of menu item you prefer. In this example, we’ll create a new custom menu item ( “Link” ) to hold our username menu item. Set the URL to # and the link text to “Username” (temporarily) and add it to the menu.

Next, replace the Navigation Label with our new shortcode, [current-username] and save the menu

Finally, tell the menu to allow shortcodes in Navigation Labels via the UberMenu Control Panel

UberMenu 3

Main UberMenu Configuration > Miscellaneous > Allow Shortcodes in Navigation Label & Description

UberMenu 2

Descriptions, Shortcodes, Widgets > Advanced Content Capabilities > Allow Shortcodes in Navigation Label & Description

Now your menu will display the current user’s username inside the menu

Next Steps

The above strategy will display the username of the currently logged in user. When the user is logged out, the menu item will be blank. You could adjust the shortcode to return something else when logged out. Alternatively, you could use the UberMenu Conditionals Extension to hide the menu item entirely when the user is logged out, and replace it with a different item, perhaps a login link. To do so, you would create two menu items: one for the logged in user (displaying the username), and one for the logged out user (displaying a login link). You would apply the “If user is logged in” condition to the first, and “If user is not logged in” condition to the second.

Learn more about the UberMenu Conditionals Extension

Speeding up the Appearance > Menus Screen in WordPress 3.6 / 3.7 / 3.8 / 3.9 / 4.0 / 4.1

Quick Synopsis

WordPress 3.6 introduced new accessibility functionality in the Appearance > Menus admin screen. This extra javascript processing creates inefficiencies when handling menus with large numbers of menu items, resulting in very slow and sometimes unresponsive interactions when managing menus in WordPress. By deferring this processing using “lazy” techniques, we can eliminate the bottleneck and have the menu management system run much faster for large numbers of menu items. I have provided a plugin which does just that.

Skip to Video Skip to plugin

Update April 2015

The patch submitted has finally been incorporated into the core in WordPress 4.2! See: core trac ticket. The Faster Appearance – Menus plugin should no longer be necessary with this update.

Update October 25, 2013

I have submitted this plugin/solution as a core trac ticket, and the patch is currently awaiting review to be included in a future version of WordPress core. If you’d like to test, head over to the track ticket and report your feedback 🙂

Background

As the author of several WordPress menu plugins which handle large numbers of menu items, I’ve recently been fielding a lot of support requests from concerned customers who have experienced a dramatic slow-down in their Appearance > Menus admin screen since they have upgraded to WordPress 3.6. For one user who was using about 300 menu items (note: definitely not recommended in any event), the Appearance > Menus page completely freezes and crashes. As I was worried that my code might be causing the issue, I began to investigate.

Stripping my installation down to just TwentyTwelve running on WordPress 3.6, without any plugins, I was surprise to find that the Appearance > Menus page “freezes” for about 5 seconds upon loading with a menu of about 100 menu items. That means no scrolling, no clicking – the average user might even think that the browser has crashed, as the only indication that anything is going on is that the progress spinner continues to spin indicating that the page has not completely loaded.

Testing with a much smaller menu, say 5 menu items, the page loads immediately.

When profiling the javascript, it became apparent that the majority of the processing was occurring in wp-admin/js/nav-menu.js (or the minified equivalent). Digging deeper into this file, I found that the bottleneck occurs with the refreshAdvancedAccessibility function. This function is responsible for adding buttons that allow the menu items’ positions to be moved without having to drag and drop.

Searching the issue in trac, I came across this ticket: Give the menus page an accessibility mode option, like the widgets screen, which confirms that this code was added in WordPress 3.6. The core team has done a nice job adding some useful accessibility features to the Appearance > Menus screen, which is great. See the relevant commit.

The code looks like this:

		refreshAdvancedAccessibility : function() {

			// Hide all links by default
			$( '.menu-item-settings .field-move a' ).hide();

			$( '.item-edit' ).each( function() {
				var $this = $(this),
					movement = [],
					availableMovement = '',
					menuItem = $this.parents( 'li.menu-item' ).first(),
					depth = menuItem.menuItemDepth(),
					isPrimaryMenuItem = ( 0 === depth ),
					itemName = $this.parents( '.menu-item-handle' ).find( '.menu-item-title' ).text(),
					position = parseInt( menuItem.index() ),
					prevItemDepth = ( isPrimaryMenuItem ) ? depth : parseInt( depth - 1 ),
					prevItemNameLeft = menuItem.prevAll('.menu-item-depth-' + prevItemDepth).first().find( '.menu-item-title' ).text(),
					prevItemNameRight = menuItem.prevAll('.menu-item-depth-' + depth).first().find( '.menu-item-title' ).text(),
					totalMenuItems = $('#menu-to-edit li').length,
					hasSameDepthSibling = menuItem.nextAll( '.menu-item-depth-' + depth ).length;

				// Where can they move this menu item?
				if ( 0 !== position ) {
					var thisLink = menuItem.find( '.menus-move-up' );
					thisLink.prop( 'title', menus.moveUp ).show();
				}

				if ( 0 !== position && isPrimaryMenuItem ) {
					var thisLink = menuItem.find( '.menus-move-top' );
					thisLink.prop( 'title', menus.moveToTop ).show();
				}

				if ( position + 1 !== totalMenuItems && 0 !== position ) {
					var thisLink = menuItem.find( '.menus-move-down' );
					thisLink.prop( 'title', menus.moveDown ).show();
				}

				if ( 0 === position && 0 !== hasSameDepthSibling ) {
					var thisLink = menuItem.find( '.menus-move-down' );
					thisLink.prop( 'title', menus.moveDown ).show();
				}

				if ( ! isPrimaryMenuItem ) {
					var thisLink = menuItem.find( '.menus-move-left' ),
						thisLinkText = menus.outFrom.replace( '%s', prevItemNameLeft );
					thisLink.prop( 'title', menus.moveOutFrom.replace( '%s', prevItemNameLeft ) ).html( thisLinkText ).show();
				}

				if ( 0 !== position ) {
					if ( menuItem.find( '.menu-item-data-parent-id' ).val() !== menuItem.prev().find( '.menu-item-data-db-id' ).val() ) {
						var thisLink = menuItem.find( '.menus-move-right' ),
							thisLinkText = menus.under.replace( '%s', prevItemNameRight );
						thisLink.prop( 'title', menus.moveUnder.replace( '%s', prevItemNameRight ) ).html( thisLinkText ).show();
					}
				}

				if ( isPrimaryMenuItem ) {
					var primaryItems = $( '.menu-item-depth-0' ),
						itemPosition = primaryItems.index( menuItem ) + 1,
						totalMenuItems = primaryItems.length,

						// String together help text for primary menu items
						title = menus.menuFocus.replace( '%1$s', itemName ).replace( '%2$d', itemPosition ).replace( '%3$d', totalMenuItems );
				} else {
					var parentItem = menuItem.prevAll( '.menu-item-depth-' + parseInt( depth - 1 ) ).first(),
						parentItemId = parentItem.find( '.menu-item-data-db-id' ).val(),
						parentItemName = parentItem.find( '.menu-item-title' ).text(),
						subItems = $( '.menu-item .menu-item-data-parent-id[value="' + parentItemId + '"]' ),
						itemPosition = $( subItems.parents('.menu-item').get().reverse() ).index( menuItem ) + 1;

						// String together help text for sub menu items
						title = menus.subMenuFocus.replace( '%1$s', itemName ).replace( '%2$d', itemPosition ).replace( '%3$s', parentItemName );
				}

				$this.prop('title', title).html( title );
			});
		},

It uses jQuery’s .each() function to loop through every menu item and add in accessibility functionality. This is some relatively intensive processing, but for only a few menu items it’s not a big deal.

Unfortunately, for menus with a large number of menu items, this code creates a significant bottleneck as the amount of processing compounds with each menu item. Every item in the menu needs to be processed at page load, as well as when the menu item order changes. As a result, we get long pauses while the browser executes this javascript over and over. This creates user experience issues as the browser seems to seize up every time a change is made.

Demonstration

Here is a video demonstration of the issue. Best viewed in 720p.

A Potential Solution

The main problem here is that when all the items must be processed at once, this creates a severe bottleneck when working with large numbers of menu items. Processing the items one at a time, only when necessary, would alleviate this issue.

Before this accessibility functionality can be used, the items will need to be either hovered, focused, or touched. Therefore we can defer processing until those events occur on a per-item basis. Here’s what I’ve done:

First, we move all of the processing to a function called refreshAdvancedAccessibilityLazy. This function will be passed an individual menu item for processing, and will only be processed if it hasn’t already been. The rest of the processing is identical, however, we’ll bind this function to certain events as a callback, rather than looping over everything at once.

		/* This function takes all code from refreshAdvancedAccessibility() and executes it only on a single 
			menu item, to be used as a callback */
		refreshAdvancedAccessibilityLazy : function( $itemEdit ){

			//don't reprocess
			if( $itemEdit.data( 'accessibility_refreshed' ) ) return;

			//mark as processed
			$itemEdit.data( 'accessibility_refreshed' , true );

			//do the accessibility processing
			var $this = $itemEdit,
				movement = [],
				availableMovement = '',
				menuItem = $this.parents( 'li.menu-item' ).first(),
				depth = menuItem.menuItemDepth(),
				isPrimaryMenuItem = ( 0 === depth ),
				itemName = $this.parents( '.menu-item-handle' ).find( '.menu-item-title' ).text(),
				position = parseInt( menuItem.index() ),
				prevItemDepth = ( isPrimaryMenuItem ) ? depth : parseInt( depth - 1 ),
				prevItemNameLeft = menuItem.prevAll('.menu-item-depth-' + prevItemDepth).first().find( '.menu-item-title' ).text(),
				prevItemNameRight = menuItem.prevAll('.menu-item-depth-' + depth).first().find( '.menu-item-title' ).text(),
				totalMenuItems = $('#menu-to-edit li').length,
				hasSameDepthSibling = menuItem.nextAll( '.menu-item-depth-' + depth ).length;

			// Where can they move this menu item?
			if ( 0 !== position ) {
				var thisLink = menuItem.find( '.menus-move-up' );
				thisLink.prop( 'title', menus.moveUp ).show();
			}

			if ( 0 !== position && isPrimaryMenuItem ) {
				var thisLink = menuItem.find( '.menus-move-top' );
				thisLink.prop( 'title', menus.moveToTop ).show();
			}

			if ( position + 1 !== totalMenuItems && 0 !== position ) {
				var thisLink = menuItem.find( '.menus-move-down' );
				thisLink.prop( 'title', menus.moveDown ).show();
			}

			if ( 0 === position && 0 !== hasSameDepthSibling ) {
				var thisLink = menuItem.find( '.menus-move-down' );
				thisLink.prop( 'title', menus.moveDown ).show();
			}

			if ( ! isPrimaryMenuItem ) {
				var thisLink = menuItem.find( '.menus-move-left' ),
					thisLinkText = menus.outFrom.replace( '%s', prevItemNameLeft );
				thisLink.prop( 'title', menus.moveOutFrom.replace( '%s', prevItemNameLeft ) ).html( thisLinkText ).show();
			}

			if ( 0 !== position ) {
				if ( menuItem.find( '.menu-item-data-parent-id' ).val() !== menuItem.prev().find( '.menu-item-data-db-id' ).val() ) {
					var thisLink = menuItem.find( '.menus-move-right' ),
						thisLinkText = menus.under.replace( '%s', prevItemNameRight );
					thisLink.prop( 'title', menus.moveUnder.replace( '%s', prevItemNameRight ) ).html( thisLinkText ).show();
				}
			}

			if ( isPrimaryMenuItem ) {
				var primaryItems = $( '.menu-item-depth-0' ),
					itemPosition = primaryItems.index( menuItem ) + 1,
					totalMenuItems = primaryItems.length,

					// String together help text for primary menu items
					title = menus.menuFocus.replace( '%1$s', itemName ).replace( '%2$d', itemPosition ).replace( '%3$d', totalMenuItems );
			} else {
				var parentItem = menuItem.prevAll( '.menu-item-depth-' + parseInt( depth - 1 ) ).first(),
					parentItemId = parentItem.find( '.menu-item-data-db-id' ).val(),
					parentItemName = parentItem.find( '.menu-item-title' ).text(),
					subItems = $( '.menu-item .menu-item-data-parent-id[value="' + parentItemId + '"]' ),
					itemPosition = $( subItems.parents('.menu-item').get().reverse() ).index( menuItem ) + 1;

					// String together help text for sub menu items
					title = menus.subMenuFocus.replace( '%1$s', itemName ).replace( '%2$d', itemPosition ).replace( '%3$s', parentItemName );
			}

			$this.prop('title', title).html( title );

			//console.log( 'refresh ' + title );
		},

Next, we’ll strip everything from the original function. Rather than reprocessing everything every time something changes, we’ll simply reset all states to unprocessed, and then they will be reprocessed next time those items are interacted with

		/* Functionality stripped from this function and deferred to callback 
			function refreshAdvancedAccessibilityLazy() */
		refreshAdvancedAccessibility : function() {

			// Hide all links by default
			$( '.menu-item-settings .field-move a' ).hide();

			//Mark all items as unprocessed
			$( '.item-edit' ).data( 'accessibility_refreshed' , false );

			return;
		},

Finally, we’ll rewrite the accessibility initialization function to trigger our deferred processing on hover/focus/touch, and use on() with event delegation in order to ensure newly added items also work.

		initAccessibility : function() {
			api.refreshKeyboardAccessibility();
			api.refreshAdvancedAccessibility();

			//Setup the refresh on hover/focus/touch event
			$( '#menu-management' ).on( 'mouseenter.refreshAccessibility focus.refreshAccessibility touchstart.refreshAccessibility' , '.menu-item' , function(){
				api.refreshAdvancedAccessibilityLazy( $( this ).find( '.item-edit' ) );
			});

			//Modified events to use on() with event delegation so that newly added menu items work as well
			
			// Events
			$( '#menu-management' ).on( 'click', '.menus-move-up' , function ( e ) {
				api.moveMenuItem( $( this ).parents( 'li.menu-item' ).find( 'a.item-edit' ), 'up' );
				e.preventDefault();
			});
			$( '#menu-management' ).on( 'click', '.menus-move-down', function ( e ) {
				api.moveMenuItem( $( this ).parents( 'li.menu-item' ).find( 'a.item-edit' ), 'down' );
				e.preventDefault();
			});
			$( '#menu-management' ).on( 'click', '.menus-move-top', function ( e ) {
				api.moveMenuItem( $( this ).parents( 'li.menu-item' ).find( 'a.item-edit' ), 'top' );
				e.preventDefault();
			});
			$( '#menu-management' ).on( 'click', '.menus-move-left' , function ( e ) {
				api.moveMenuItem( $( this ).parents( 'li.menu-item' ).find( 'a.item-edit' ), 'left' );
				e.preventDefault();
			});
			$( '#menu-management' ).on( 'click', '.menus-move-right', function ( e ) {
				api.moveMenuItem( $( this ).parents( 'li.menu-item' ).find( 'a.item-edit' ), 'right' );
				e.preventDefault();
			});

		},

The Plugin

In order to make this new code work as a plugin rather than edit the Core, I’ve deregistered the standard nav-menu.js and re-registered this newly edited version from the plugin. Activating the plugin will immediately switch to the revised script. The code and plugin are available for download on GitHub

View Plugin on GitHub Download from WordPress Plugin Repository

How to write a custom shortcode for UberMenu

Please note that this tutorial is no longer necessary, as this functionality is built into UberMenu 3 with Dynamic Posts

One of the really flexible pieces of UberMenu – Responsive WordPress Mega Menu Plugin is the Content Overrides system, which allows users to insert any type of shortcode content into their menu. UberMenu ships with a variety of included shortcodes, but any shortcode can be placed in the menu. This leaves customers with the ability to write their own custom shortcodes as an easy way to add totally custom content to the menu.

In this tutorial, we’ll build a custom shortcode that will allow you to add a grid of recent posts to your submenu.

Skip to Result Skip to Solution View the demo

Writing the Shortcode

Shortcode skeleton

Creating a custom shortcode in WordPress is really very simple. There are two parts to the process:

1. Register the shortcode

First, we tell WordPress that there’s a new shortcode it should be on the lookout for, and we tell it what function to run when it encounters the shortcode.

add_shortcode( 'ubermenu-post-grid' , 'ubermenu_post_grid' );

This means that when WordPress sees this shortcode: [ubermenu-post-grid] it will execute the ubermenu_post_grid() function and print the returned value.

2. Define the shortcode handler function

Next, we need to define a function that will actually produce and return the markup that we want the shortcode to be replaced with. We’ve already created the function name above. The basic function skeleton looks like this:

function ubermenu_post_grid( $atts ){

  $html = '';

  //add markup to $html based on the values in $atts

  return $html;

}

We will add both of these pieces of PHP to our theme’s functions.php, preferably in a child theme in order to best preserve our customizations.

Shortcode attributes

Shortcodes accept specific attributes. We will define the attributes that our shortcode will accept, along with default values, in our shortcode function. For example, if the shortcode instance looks like this

[ubermenu-post-grid num="3" category="kittens"]

Then the $atts array that is passed to ubermenu_post_grid( $atts ) will look like this:

$atts = array(
  'num' => 3,
  'category' => 'kittens'
);

The rest of the attribute values will then be applied as defaults.

WordPress has a handy function called shortcode_atts that automatically merges the default array of attribute values with the values passed by the user in the shortcode instance. In order to define our attributes and their default values, we add this to our function:

//Merge user-provided attributes with default values 
//and extract into individual variables
extract(shortcode_atts(array(

	'num'		=>	4,			//maximum number of posts to retrieve
	'grid'		=> 	4,			//number of columns per row
	'category'	=>	'',			//optional category to retrieve posts from

	'img'		=>	'on',		//'on' or 'off' to display the image or not
	'img_width'	=> 	220,		//image width in pixels
	'img_height'=>	120,		//image height in pixels
		
	'excerpt'	=>	'off',		//'on' or 'off' to display the excerpt
	'default_img' => false,		//URL of a default image to display when no featured image is available
	'offset'	=>	0,			//offset for get_posts() (number of posts to skip)

), $atts));

The extract() function turns each of the array keys into a variable, so now we have variables like $num, $grid, and $img that we can work with in our function.

Query Posts

Next, we want to retrieve the matching posts from the database. To do this, we will set up an array of query arguments and pass them to the get_posts() function. We will also test the category value so that users can pass either a numeric or text value and still have the shortcode work.

//Setup default query arguments
$query_args = array(
	'numberposts'	=>	$num,
	'offset'		=>	$offset,
	'suppress_filters' => false
);

//If the user has provided a category, parse it into the query args
if( !empty( $category ) ){
	//Handle numeric category values
	if(is_numeric($category)){
		$query_args['category'] = $category;
	}
	//Handle category names as well
	else $query_args['category_name'] = $category;		
}

//Retrieve matching posts from the database
$posts = get_posts( $query_args );

Produce the output markup

Now that we have retrieved the appropriate post data from the database, we need build our HTML. We’re going to loop through the array of posts the get_posts() function returned and create an unordered list of our posts, each with a link wrapping the post’s featured image and title. We’ll utilize UberMenu’s image generator function (which will use the same settings as the other images in your menu and can be set in the Control Panel) to make things easy.

//Wrap our grid in an unordered list container
$html = '<ul class="uber-post-grid uber-post-grid-'.$grid.'">';

//Loop through each post and output the image, title, and excerpt
foreach( $posts as $post ){

	//Each post becomes a list item
	$html.= '<li class="uber-post-grid-item post-'.$post->ID.'">';

	//We will wrap the entire item contents with the post's permalink (note anchors can be block level elements in HTML5)
	$html.= '<a href="'.get_permalink( $post->ID ).'">';

	//Get the image at the right size and append it
	$image = '';
	if($img == 'on'){
		$image = $uberMenu->getPostImage($post->ID, $img_width, $img_height, $default_img );
		$html.= $image;
	}
	
	//Add the post title
	$html.= '<h5>'.$post->post_title.'</h5>';

	//Add the excerpt
	if($excerpt == 'on')
		$html.= '<span class="uber-post-grid-excerpt">'.apply_filters( 'get_the_excerpt', $post->post_excerpt ).'</span>';

	$html.= '</a>';

	$html.= '</li>';
}

$html.= '</ul>';

And finally, we just return our markup string, as shortcodes need to return rather than print their output;

//Return the entire list 
return $html;

Here’s how the entire function will look:

function ubermenu_post_grid( $atts ){

	//we're going to need access to the UberMenu object for image functionality
	global $uberMenu;
	
	//Merge user-provided attributes with default values 
	//and extract into individual variables
	extract(shortcode_atts(array(

		'num'		=>	4,			//maximum number of posts to retrieve
		'grid'		=> 	4,			//number of columns per row
		'category'	=>	'',			//optional category to retrieve posts from

		'img'		=>	'on',		//'on' or 'off' to display the image or not
		'img_width'	=> 	220,		//image width in pixels
		'img_height'=>	120,		//image height in pixels
		
		'excerpt'	=>	'off',		//'on' or 'off' to display the excerpt
		'default_img' => false,		//URL of a default image to display when no featured image is available
		'offset'	=>	0,			//offset for get_posts() (number of posts to skip)

	), $atts));
	
	//Setup default query arguments
	$query_args = array(
		'numberposts'	=>	$num,
		'offset'		=>	$offset,
		'suppress_filters' => false
	);

	//If the user has provided a query, parse it into the query args
	if( !empty( $category ) ){
		//Handle numeric category values
		if(is_numeric($category)){
			$query_args['category'] = $category;
		}
		//Handle category names as well
		else $query_args['category_name'] = $category;		
	}

	//Retrieve matching posts from the database
	$posts = get_posts( $query_args );

	//Wrap our grid in an unordered list container
	$html = '<ul class="uber-post-grid uber-post-grid-'.$grid.'">';

	//Loop through each post and output the image, title, and excerpt
	foreach( $posts as $post ){

		//Each post becomes a list item
		$html.= '<li class="uber-post-grid-item post-'.$post->ID.'">';

		//We will wrap the entire item contents with the post's permalink (note anchors can be block level elements in HTML5)
		$html.= '<a href="'.get_permalink( $post->ID ).'">';

		//Get the image at the right size and append it
		$image = '';
		if($img == 'on'){
			$image = $uberMenu->getPostImage($post->ID, $img_width, $img_height, $default_img );
			$html.= $image;
		}
		
		//Add the post title
		$html.= '<h5>'.$post->post_title.'</h5>';

		//Add the excerpt
		if($excerpt == 'on')
			$html.= '<span class="uber-post-grid-excerpt">'.apply_filters( 'get_the_excerpt', $post->post_excerpt ).'</span>';

		$html.= '</a>';

		$html.= '</li>';
	}

	$html.= '</ul>';

	//Return the entire list 
	return $html;
}
add_shortcode( 'ubermenu-post-grid' , 'ubermenu_post_grid' );

Adding Styling (CSS)

At this point, we have our markup, but it’s not styled, and it doesn’t look too pretty.

unstyled grid

Our final goal is to create a simple, flexible and responsive grid system for our menu items.

First, we’re going to use `border-box` box sizing to make things easy.

/* Setup Grid container and items to make sizing easy */
#megaMenu ul.megaMenu .uber-post-grid,
#megaMenu ul.megaMenu .uber-post-grid-item{
	-webkit-box-sizing:border-box;
	-o-box-sizing:border-box;
	-moz-box-sizing:border-box;
	box-sizing:border-box;
}
/* Align grid container properly */
#megaMenu ul.megaMenu .uber-post-grid{
	display:block;
	margin:10px 0 10px 0;
	float:left;
	padding-left:27px;
}

Next, we want to turn our list items into grid boxes, so we’ll give them some padding and margins and float them left. Then we’ll set up a simple grid system to divide the boxes into even columns

/* Space grid items */
#megaMenu ul.megaMenu .uber-post-grid-item{
	float:left;
	padding:0 27px 10px 0;
}
/* Setup simple grid widths */
#megaMenu ul.megaMenu .uber-post-grid-1 .uber-post-grid-item{ width: 100%; }
#megaMenu ul.megaMenu .uber-post-grid-2 .uber-post-grid-item{ width: 50%; }
#megaMenu ul.megaMenu .uber-post-grid-3 .uber-post-grid-item{ width: 33%; }
#megaMenu ul.megaMenu .uber-post-grid-4 .uber-post-grid-item{ width: 25%; }
#megaMenu ul.megaMenu .uber-post-grid-5 .uber-post-grid-item{ width: 20%; }

After that, we’ll style our anchors, titles, and excerpts to look a bit nicer

/* Post titles */
#megaMenu ul.megaMenu .uber-post-grid h5,
#megaMenu ul.megaMenu .uber-post-grid a{
	font-size:12px;
	text-transform:none;
	text-decoration: none
}
#megaMenu ul.megaMenu .uber-post-grid h5{
	font-weight:bold;
	line-height:18px;
}
/* Post Excerpts */
#megaMenu ul.megaMenu .uber-post-grid-excerpt{
	font-size:90%;
}

Finally, we’ll add a special class to adjust the normal submenu item padding in order to properly space out our grid evenly.

#megaMenu ul.megaMenu ul.sub-menu-1 > li.menu-item.ss-override.no-padding-shortcode,
#megaMenu ul.megaMenu ul.sub-menu-1 > li.menu-item.ss-sidebar.menu-item.no-padding-widget > .wpmega-widgetarea > ul.um-sidebar > li.widget{
	padding-left:0;
	padding-right:0;
	width:100%;
}
/* Align nav labels */
#megaMenu ul.megaMenu ul.sub-menu-1 > li.menu-item.ss-override.no-padding-shortcode > a,
#megaMenu ul.megaMenu ul.sub-menu-1 > li.menu-item.ss-override.no-padding-shortcode > span.um-anchoremulator,
#megaMenu ul.megaMenu ul.sub-menu-1 > li.ss-sidebar.no-padding-widget > .wpmega-widgetarea > ul.um-sidebar > li.widget h2.widgettitle{
	margin:0 27px;
}

The special classes no-padding-shortcode and no-padding-widget will be added to second level menu items to remove the padding and allow the grid to be laid out appropriately.

Adding Responsiveness

Finally, we’ll make the grid responsive by implementing two breakpoints. Below 767px, we’ll collapse all grids to two columns, and below 480px we’ll collapse it further to a single column.

First, we’ll make sure all of our images respond to the grid at all sizes

/* Make images responsive */
#megaMenu ul.megaMenu .uber-post-grid .uber-post-grid-item img{
	max-width:100%;
}

Then we’ll force all grids into 1 or two column layouts depending on the current viewport size

/* #Mobile (Landscape) - 480px - 767px
================================================== */
@media only screen and (min-width: 480px) and (max-width: 767px) {
	#megaMenu ul.megaMenu .uber-post-grid-2 .uber-post-grid-item,
	#megaMenu ul.megaMenu .uber-post-grid-3 .uber-post-grid-item,
	#megaMenu ul.megaMenu .uber-post-grid-4 .uber-post-grid-item,
	#megaMenu ul.megaMenu .uber-post-grid-5 .uber-post-grid-item{ width: 50%; }
}
/* #Mobile (Portrait) - < 480px 
================================================== */
@media only screen and (max-width: 479px) {
	#megaMenu ul.megaMenu .uber-post-grid-2 .uber-post-grid-item,
	#megaMenu ul.megaMenu .uber-post-grid-3 .uber-post-grid-item,
	#megaMenu ul.megaMenu .uber-post-grid-4 .uber-post-grid-item,
	#megaMenu ul.megaMenu .uber-post-grid-5 .uber-post-grid-item{ width: 100%; }
}

And we’ll make sure that all our containers are 100% width of the submenus

/* Stretch everything to 100% for no-padding items */
@media only screen and (max-width: 767px) {
	#megaMenu.megaResponsive ul.megaMenu li.menu-item.ss-nav-menu-mega ul.sub-menu.sub-menu-1 > li.menu-item.ss-override.no-padding-shortcode,
	#megaMenu.megaResponsive ul.megaMenu li.menu-item.ss-nav-menu-mega ul.sub-menu.sub-menu-1 > li.menu-item.ss-sidebar.menu-item.no-padding-widget > .wpmega-widgetarea > ul.um-sidebar > li.widget{
		padding:0;
		width:100%;
	}
}

The Result

View the Demo

After all that, we end up with a simple responsive grid that looks like this:

Full width

UberMenu Custom Shortcode Post Grid

< 767px

responsive menu 767px

< 480px

responsive menu 480px

Putting it all together

Step 1: Add the shortcode PHP & CSS to your site

We’ve now completed our two-part process: (1) create a shortcode in PHP, (2) style the shortcode output with CSS. I’ve provided final versions of the code as gists, which can be downloaded from github.

1. Add the shortcode PHP to your theme’s functions.php, preferably in a child theme in order make preserving your changes as straightforward as possible.

2. Add the UberMenu Post Grid CSS to any stylesheet. Not sure where to put it? See Adding Custom CSS

Step 2: Create your menu item

Finally, actually create your menu item. Add a second level menu item, and add a content override using your newly created shortcode!

[ubermenu-post-grid grid="4"]

Then add the special class to your menu item, no-padding-shortcode (if you’re using a widget, use no-padding-widget) to ensure your grid aligns properly.

menu item configuration

Enjoy your new post grid layout!

Like the UberMenu skin used in this tutorial? It’s available in the Flat Skins Pack

How to search a single forum with bbPress

Out of the box, bbPress search will search across all forums. However, there are use cases where this doesn’t make sense, one being running a support forum. For example, if you’re having an issue with Product A and are searching for a solution, you only want to see results from Product A’s forum, not results from Product B’s forum.

A specific case from my support forums: if an UberMenu customer searches the forum for “responsive”, they should get results from the UberMenu – WordPress Mega Menu Plugin forum but not from the Agility Responsive WordPress Theme forum.

Since I couldn’t find a setting or plugin to accomplish this, I made a few customizations to accomplish the following:

  1. Display a search bar on the individual product forums (/forum/ubermenu).
  2. Automatically restrict the search form on each forum to return results only from that forum’s topics.

To implement this we need to make three customizations:

  1. Move the search form to the top of the single forum template
  2. Add an extra field to the search form to identify the current forum
  3. Filter the search query to only search a specific forum

Before proceeding, we’ll be overriding bbPress templates in our theme, so you’ll want to set up a child theme with a /bbpress subfolder to house these templates. bbPress’ default templates are located in the wp-content/plugins/bbpress/templates/defaults/bbpress directory, and we’ll copy the templates we need from here to our wp-content/themes/{mychildtheme}/bbpress directory in order to override them.

Step 1: Move the search form to the individual forums

By default, the bbPress search form will appear at the top of the main /forums page, produced by the content-archive-forum.php template. There is no hook to remove the search form, so we’ll do this by overriding the template. Copy bbPress’ content-archive-forum.php template into your child theme’s /bbpress directory, then remove the search box from the top of the file. Here’s the code you’ll remove:

<div class="bbp-search-form">

	<?php bbp_get_template_part( 'form', 'search' ); ?>

</div>

To move the search form into the content-single-forum.php template, however, we can just use an action hook rather than needing to override the template (which could lead to extra maintenance down the road). Just add this action to your child theme’s functions.php

function my_bbp_search_form(){
	?>
	<div class="bbp-search-form">

		<?php bbp_get_template_part( 'form', 'search' ); ?>

	</div>
	<?php
}
add_action( 'bbp_template_before_single_forum', 'my_bbp_search_form' );

The search form should now appear on your individual forums rather than on the main forums page.

Step 2: Add a field to the search form to identify the current forum

In order to restrict search results to a particular forum, we’ll need that forum’s ID when the form is submitted to alter the query. In this case we need to override form-search.php, so copy bbPress’s template into the child theme’s /bbpress directory, then make the following alterations:

1. Get the forum ID

$forum_id = bbp_get_forum_id();

2. After the search field, add a hidden field to store the forum ID

<?php if( $forum_id ): ?>
	<input class="button" type="hidden" name="bbp_search_forum_id" value="<?php echo $forum_id; ?>" />
<?php endif; ?>

3. (Optional) Edit the placeholder text on the search field to display “Search {Forum Title} Topics”

<input placeholder="Search <?php the_title(); ?> Topics" tabindex="<?php bbp_tab_index(); ?>" type="text" value="<?php echo esc_attr( bbp_get_search_terms() ); ?>" name="bbp_search" id="bbp_search" />

The final file looks like this:

/**
 * Search 
 *
 * @package bbPress
 * @subpackage Theme
 */
$forum_id = bbp_get_forum_id();
?>

<form role="search" method="get" id="bbp-search-form" action="<?php bbp_search_url(); ?>">
	<div>
		<label class="screen-reader-text hidden" for="bbp_search"><?php _e( 'Search for:', 'bbpress' ); ?></label>
		<input placeholder="Search <?php the_title(); ?> Topics" tabindex="<?php bbp_tab_index(); ?>" type="text" value="<?php echo esc_attr( bbp_get_search_terms() ); ?>" name="bbp_search" id="bbp_search" />
		<?php if( $forum_id ): ?>
		<input class="button" type="hidden" name="bbp_search_forum_id" value="<?php echo $forum_id; ?>" />
		<?php endif; ?>
		<input tabindex="<?php bbp_tab_index(); ?>" class="button" type="submit" id="bbp_search_submit" value="<?php esc_attr_e( 'Search', 'bbpress' ); ?>" />
	</div>
</form>

Nothing will change visibly on your site after making these changes, but now the forum ID value will be submitted when the search button is clicked.

Step 3: Filter the search query to only search a specific forum

Here’s where the real meat of the customization occurs. Using the bbp_after_has_search_results_parse_args filter we’ll alter the search query arguments and add a meta query to restrict the results to topics/replies in a specific forum. Just add this code to your functions.php

/*
 * Search only a specific forum
 */
function my_bbp_filter_search_results( $r ){

	//Get the submitted forum ID (from the hidden field added in step 2)
	$forum_id = sanitize_title_for_query( $_GET['bbp_search_forum_id'] );

	//If the forum ID exits, filter the query
	if( $forum_id && is_numeric( $forum_id ) ){

		$r['meta_query'] = array(
			array(
				'key' => '_bbp_forum_id',
				'value' => $forum_id,
				'compare' => '=',
			)
		);
		
	}

	return $r;
}
add_filter( 'bbp_after_has_search_results_parse_args' , 'my_bbp_filter_search_results' );

That’s it! Now when you search from a search form within a specific forum, you’ll only get results back from that forum.

How to change the menu on a custom post type with the Menu Swapper plugin

For those who are unfamiliar, the Menu Swapper plugin allows you to switch the menu you’re using on any individual Post or Page on your site. But what happens if you need to swap the menu on a custom post type?

Out of the box, the Menu Swapper will add a meta box to Posts and Pages (two standard post types) which allow you to choose which menu to display on the single post page. If you want to have the same on a custom post type, like a Portfolio Item for example, you’ll need to add that post type to the list of post types that the Menu Swapper will target.

The Menu Swapper makes this easy with the mswp_post_types filter. You’ll just need to add a few lines of code to your child theme’s functions.php. The main thing you’ll need is the custom post type ID – the easiest way to determine that if you’re not sure is to click on the “All {Post Type}” panel in your WordPress Admin Panel. The URL will contain the string ?post_type=portfolio; in this case the custom post type ID would be portfolio.

For the portfolio example, here’s what the PHP that you’d add to functions.php would look like:

function my_add_mswp_post_type( /* array */ $post_types ){

	$post_types[] = 'portfolio'; // 'portfolio' is the custom post type ID
	//$post_types[] = 'download'; // this is how you would add a second custom post type

	return $post_types;
}
add_filter( 'mswp_post_types', 'my_add_mswp_post_type' );

Add that code to your functions.php and you’ll get the Menu Swapper meta box on your custom post type, allowing you to change out the menu on that post type’s single page.

Enjoy!

The WordPress Menu Item Limit or: Help! Half my menu items just disappeared!

So, you just saved your WordPress 3 menu and found to your horror that you lost menu items – maybe a LOT of menu items. And now you can’t add any more – when you add a new one, the one last drops off the end of the list.

Is this a WordPress bug? Did one of those darn plugins do it?! Nope, it’s a server configuration issue that silently cuts your menu items down before WordPress even knows about them, like wolves picking off sheep in the night. Or you know, the HTTP/PHP equivalent of that.

Shake your fist at the sky and take a deep breath, and I’ll lay it out for you.

High Level Overview

Let’s start at the beginning. There are three important things to understand:

1. To save a WordPress menu, you have to send data from the client (your web browser) to the server, where it is processed and stored by the WordPress Menu System. This form data is submitted to the server via an HTTP POST. PHP parses this data as a nice array that we know and love – $_POST.

2. The WordPress Menu System bases the menu items it saves on the data it receives from the client side via the $_POST array. It assumes that data that is not present should be removed – that is, WordPress deletes menu items not present in the $_POST array upon save.

3. Depending on your server configuration, not all of the POSTed variables sent by the client may be received by the WordPress, as the $_POST array can be truncated. And when that data is lost, we lose menu items. Dang.

Hold on, I’ve had enough of the techno-babble. Skip to the Solution

Here’s how it works

Every menu item on the Appearance > Menus screen has about 11-12 fields associated with it (Title, Classes, Description, ID, etc). There are also a few meta values for the menu overall (like the menu name, menu ID, and a few nonce-related fields for security). All this data gets sent to the server as POST data. Each field is a separate POST variable. So if you have 10 menu items, you’re sending around 120 POST variables (11 per menu item + various menu meta fields). If you have 100 menu items, you’re sending around 1200 POST variables.

Here’s the problem

Unfortunately, PHP’s desire to be secure and WordPress’s desire to be tidy conspire to stab you in the back. Two wrongs might not make a right, but in this case, two rights make a big ol’ headache.

PHP’s Security

For security reasons, PHP likes to limit the maximum numbers of POST vars you can submit. This is to mitigate the effects of DoS attacks. We’ll get into details below. The important bit from the PHP manual:

If there are more input variables than specified by this directive, an E_WARNING is issued, and further input variables are truncated from the request.

This is good, but in practice, if you’re unaware of this setting (and aren’t paying attention to Warnings in the logs – and the average user isn’t), it can produce stress-induced manual hair extraction.

WordPress’s housekeeping

The WordPress Menu System save mechanism works by iterating over the $_POST array, and then discarding any menu items that weren’t present in the array.

//nav-menus.php line 341
// Remove menu items from the menu that weren't in $_POST
if ( ! empty( $menu_items ) ) {
	foreach ( array_keys( $menu_items ) as $menu_item_id ) {
		if ( is_nav_menu_item( $menu_item_id ) ) {
			wp_delete_post( $menu_item_id );
		}
	}
}

That’s good, as it prevents us from having all sorts of orphaned menu items in the posts table. But it also means that if the menu system is sent incomplete data, WordPress assumes the menu items corresponding to that data should be deleted, rather than that they should be ignored.

The Upshot: The road to confounding data loss is paved with good intentions

In the end, if the client submits more menu item variables than the PHP-configured limit, any menu items over the limit will be lost. So if you submit 1200 variables, and the limit is 1000, the last 200 are simply discarded – WordPress never even knows they were sent. These 200 variables will correspond to the last 18 items or so in your menu.

And that is why the menu items are lost. PHP prevents the menu items that you saved on the client side from reaching WordPress’s processing on the server side, so WordPress decides to delete the menu items. Who’s at fault? (I demand blood!) No one, really. It’s just a situation where you need to configure your server properly.

Sidenote: If this happens, it’s all over. We haven’t just lost pointers, we’ve lost the actual data. The only way to recover the menu items would be to restore a backup of the database prior to the menu save. Gotta catch it before it happens to avoid the repercussions.

Menu Item Specifics

Here’s how the POST variables that are submitted on menu save break down, for those who want specifics:

Key

AbbreviationExample Value
n100Number of Menu Items
c10Number of Custom Menu Items
m2Number of Registered Theme Locations

POST Variables (Example values using 100 menu items)

KeyVariable CountExample CountExplanation
menu-name11User-defined Menu Name
save_menu11Submit button
closedpostboxesnonce11nonce
meta-box-order-nonce11nonce
update-nav-menu-nonce11nonce
_wp_http_referer11nonce
action11Form meta
menu11Menu ID
menu-item-urlc10Link URL for custom menu items
menu-item-titlen100Item Title
menu-item-attr-titlen100Item Title Attribute
menu-item-classesn100Item Custom Classes
menu-item-xfnn100Item XFN Attribute (rel=)
menu-item-descriptionn100Item Description
menu-item-db-idn100Menu Item ID
menu-item-object-idn100Linked object ID
menu-item-objectn100Linked object type (post/page/custom)
menu-item-parent-idn100Item Parent ID
menu-item-positionn100Item Position
menu-item-typen100Item Type
menu-locationsm2Theme Location
Total1120

You can estimate about 12 variables per menu item to be safe. So if you want to save a menu with 150 items, you’d want a POST variable limit of no less than 150*12 + 10 = 1810.

Sidenote: the actual number varies because some menu item types have more fields than others. For example, custom menu items have an extra field to set a URL as they are not linked to a post object.

So why does this happen suddenly and without warning?

Sometimes this issue manifests as a user reaching a simple limit: they’ve added 50 menu items. They try to add the 51st, and nothing happens if they’re adding it at the end. Or if they add it in the middle, the last menu item is dropped. The limit is 50, so every time they add X more items, the last X are deleted.

Much more distressing is the case where a user has happily had 120 menu items for the last month. Then, one fateful day, they go to manage their menu, as they have new content they wish to add. Upon adding their 121st menu item and saving, they find that suddenly, to their horror, the last 40 menu items have been deleted. How could that happen? The POST vars limit should have prevented them from adding 120 menu items in the first place, right?

The most likely scenario leading to this massive frustration is that the server’s PHP version has been updated, either by the user or the web host (or possibly added Suhosin, but let’s focus on one thing at a time). And here’s why:

The PHP directive max_input_vars was introduced in PHP 5.3.9 (a relatively recent addition from January 10, 2012). The real issue is that this directive has a default value of 1000. That’s a limit of around 80 menu items. The problem comes from this sequence of events:

  1. User adds 120 menu items while host is at PHP version < 5.3.9 (no menu item limit, because max_post_vars does not exist)
  2. Host upgrades server PHP to 5.3.9+
  3. User adds 121st menu item. Only the first 80 are sent to the server due to new POST variable limit. User loses 40ish menu items
  4. User rips out hair and smashes computer against wall. Vows to quit WordPress once and for all
Sidenote: I don’t actually recommend approaching 100 menu items, even in a mega menu. The general rule of thumb is that having more than 100 links on a page can have a negative impact on SEO. Plus, presenting a user with 100 options is generally overkill, and you should probably be rethinking the navigation on your site to make it more user-friendly

How UberMenu factors in (for those who care)

UPDATE for UberMenu 3

As of UberMenu 3.0, UberMenu’s settings have been extracted from the standard menu system save process and are now saved via AJAX in a single serialized string, so UberMenu’s settings no longer contribute to this limit. The limit will be the same whether or not UberMenu is present.

UberMenu 2 (this section does not apply to UberMenu 3)

I’d like to quickly address the impact my plugin, UberMenu – WordPress Mega Menu Plugin, has on this scenario. While UberMenu does not cause this limit, it does cause the user to reach their menu item limit faster, because it adds one extra variable to each menu item.

Note that UberMenu 2 actually adds around 10 extra settings to each menu item. However, in an effort to minimize this limitation effect, UberMenu serializes all of its settings into a single active form field (via javascript), so that it only contributes one additional POST variable per menu item, rather than 10 per item. This optimization was added in UberMenu 2.0.

So here’s how it breaks down with some quick examples.

max_input_varsNormal Menu Item Limit (approx)UberMenu 2 Item Limit (approx)
10008276
2000165153
3000249230

Adding UberMenu 2 to the mix means the limit decreases by about 6 menu items per 1000 POST vars.

Unfortunately, this has the effect of customers believing UberMenu is the cause of the issue, because with UberMenu enabled they can only add 76 menu items; with UberMenu disabled, they can add 77, 78, 79… and as such it is natural to confuse symptom for cause and conclude that UberMenu is the problem. It’s not; it just makes the problem apparent sooner, unfortunately.

Server Configuration Solution

As with most problems, once you understand the issue, it’s easy to solve it. The server configuration is limiting the number of POST variables the can be submitted to the server; we need to increase that.

There are two ways this limit may be imposed

1. PHP’s max_input_vars

The increasingly common issue is the max_input_vars PHP directive. By default, it is set to 1000. To increase this, simply set it in php.ini. I’d suggest 3000 to be safe, but 2000 should be sufficient for most sites.

max_input_vars = 3000

How do you edit php.ini? That depends on your host. If you have access to the php.ini file, simply add or edit the directive, save, and restart Apache. If you’re with a web host that doesn’t give you access (common with shared hosting), you may have to contact your host and have them make the change for you.

2. Suhosin

Prior to PHP’s max_input_vars, the Suhosin PHP Security module introduced a similar pair of max_vars directives that are identical in purpose. Not all servers run Suhosin, but those that do will need to increase the following directives in php.ini:

suhosin.post.max_vars = 3000
suhosin.request.max_vars = 3000

Again, you may need to contact your host to get them to make these changes if you don’t know how or can’t do it yourself.

Restarting Apache with the new maximum POST variable values should allow you to submit all of your menu item variables and therefore save new menu items. Hoorah!

Keep in mind this won’t actually recover your lost menu items – that data is gone. The only way to get it back would be to restore from a backup copy of your database.

How to edit php.ini on various hosts:

Is there a better solution?

Wouldn’t it be nice if this wasn’t an issue? Theoretically, there may be a few options. In practice, most are more easily said than done.

Option 1: Condense all of the WordPress Menu Item fields into a single input before submitting

This is what I did with UberMenu – just serialize all the settings and decode the string on the server side. It works well, with two caveats. First, we still have the potential to reach a menu item limit eventually. Second, it means we don’t have a non-javascript fallback, and for core WordPress functionality, that’s a no-go. So that solution is probably out.

Option 2: Save menu items individually

We could save menu items via AJAX, individually, so we’d only ever submit data for one menu item at a time. Again, this requires javascript, and I suspect it would also require a major reworking of how the menu system works. It would also have to be implemented carefully in order to be intuitive and convenient; I think there would still need to be a Save-All button that would sequence all of the menu items for processing.

I don’t have enough expertise in the nitty gritty of the menu system to say whether this is truly a viable/practical option.

Option 3: Use a “checksum” to protect from unintended deletions

I’m using the term loosely here, but we could submit an extra form field that acts as a “checksum“, indicating how many menu items are being sent to the server for processing. WordPress’s menu save process could then check this value to make sure the number of menu items that it has received in the $_POST array is equivalent to the number of items it expects to receive. If it finds a discrepancy, it could avoid deleting missing menu items; however, it may be too late to save newly submitted menu items that were previously unknown.

Option 4: Native Nested Menus

Another thought would be to provide a native nesting/import functionality in WordPress. Essentially, each menu item would have an option to nest a separate menu as its submenu. This would allow users to divide up their menu items among multiple menu trees, to be dynamically assembled when the menu is printed. There are some existing plugin solutions out there that already do this, I believe. This would also enhance the reusability of the menu system, as users could potentially use the same menu subtree fragments in multiple menus.

Note that UberMenu 3 has a feature called “Menu Segments” that allows you to do this

Option 5: Provide a Warning

I think this is really the best option, at least in the short term. Simply detect the potential issue and alert the user to it. Providing explanations and solutions (or links to solutions) within the interface is generally the most efficient way to inform a user of what they need to be aware of and resolve.

And that brings us to…

The Menu Item Limit Detector Plugin

As a first step toward this, I’ve written a rudimentary plugin that attempts to detect impending menu item limits by roughly (and conservatively) estimating the remaining POST variables based on the prior save and the current variable limits. It adds an alert in the Appearance > Menus screen when the user approaches the limit, and explains the issue.

Menu Item Limit Warning

Note that I’ve already included a very similar piece of code within my UberMenu plugin to detect this scenario for UberMenu customers. Available in UberMenu 2.3+

The obvious downside to the plugin is that users need to be aware of the issue in order to install it in the first place… and the whole point of the plugin is to provide that awareness. Chicken and egg much? This’d be far more useful in WordPress Core.

Download the Plugin Contribute on Github

Please try out the plugin and let me know if it helps! If it’s useful, I’ll add it to the WordPress plugin repository.

And let me know if you have any creative suggestions for solving this problem! I highly doubt the options I’ve presented are comprehensive, but I hope they start the conversation going in the right direction.

UberMenu 2.3 Released & Important Upgrade Notes

Hit a snag? Head to the Support Forum

UberMenu 2.3.0.2 is now available, and upgrade is recommended.

UberMenu 2.3 was released today, and includes a variety of updates and feature enhancements:

  • Updated Search Box Styling
  • Ability to name custom widget areas
  • Improved localization
  • Added ability to include custom javascript
  • Ability to disable updates
  • Separated basic.css into LESS stylesheets for easier customization
  • Improved compatibility with nested plugin styles
  • Improved Windows 8 Mobile touch compatibility
  • Fixed minor skin bugs

Important upgrade notes

For those upgrading from a previous version of UberMenu, there are a few changes to the plugin to be aware of.

General upgrade

UberMenu 2.3 has a lot of refactoring and refining to the core code. It’s best to delete the previous plugin files and replace them with the new version. Don’t forget to back up any changes!

Style Generator Skins

In the latest version of UberMenu, LIs are now qualified with the .menu-item class in stylesheets in an effort to improve widget and shortcode style compatibility. As a result, you may need to re-save your style generator styles in order to update the skin to be compatible with the latest version.

custom.css Skins

If you are using a custom.css file, you will need to move it into the new /custom folder for it to be picked up by the new version of UberMenu. That is, save your custom.css before upgrading, then move it to the /custom folder.

Also, in the latest version of UberMenu, LIs are now qualified with the .menu-item class in stylesheets in an effort to improve widget and shortcode style compatibility. You will likely need to qualify all of the appropriate LIs in your custom stylesheet with the .menu-item class as well in order to make sure your styles have a high enough specificity to be applied. Generally, a simple find-and-replace from ” li” to ” li.menu-item” will resolve the issue – just be careful not to duplicate the class, and not to replace any LIs that aren’t actually menu items in your style selectors.

You may also need to add the class .megaMenu to any top level ULs in your skins to make sure your styles are specific enough.

Sorry for the inconvenience! But these changes will help improve future compatibility with other content you might like to place in your menu 🙂

Search Bar

UberMenu 2.3 introduces a new search bar style and functionality, and will replace your old UberMenu search short code automatically. If you wish to revert to the old style, please use the short code [ubermenu-search-v1]

Enjoy UberMenu 2.3!

Update April 13, 2013

UberMenu 2.3 bugs that have now been resolved in 2.3.0.1

Unfortunately, a few bugs were introduced in UberMenu 2.3, and have now been resolved. They are noted below for your reference:

Easy Integration Shortcode
Please note, if you are using the Easy Integration shortcode – [uberMenu_easyIntegrate] (normal PHP Easy Integration should not be affected), do not update yet.

TimThumb
I introduced a bug with timthumb in 2.3. If you are using Timthumb, please do not upgrade yet.

I have a fix and have already submitted version 2.3.0.1 (April 12 4PM EDT), and I will update this post when it is approved by CodeCanyon and ready for download. Thanks for your understanding and sorry for the inconvenience!

Update April 13: Version 2.3.0.1 is now available and safe to upgrade – thanks!

Easy WordPress Theme Template Identification

I’ve been developing a new commercial WordPress theme, and one of the main goals I’ve had in mind is to make customizations as easy as possible for customers. One sticking point and a question I often receive in my support forums, is “where do I find XYZ…”. This technique automatically adds HTML comments around the various template code blocks, making it easy to identify the source of any code within the template.

The basic idea is this: Each template is wrapped in HTML comments so that the output on the front end looks something like this (simplified):

<body>
    <header>
      ...
    </header>

    <div class="main">

        <!-- begin archive.php [theme_name] -->
        <div class="primary">

              <!-- begin content.php [theme_name] -->
              <article class="post">

              </article>
              <!-- end content.php -->
        </div>
        <!-- end archive.php -->

        <!-- begin sidebar.php [theme_name] -->
        <div class="sidebar">

        </div>
        <!-- end sidebar.php -->

    </div>
  

    <footer class="site-footer">
      ...
    </footer>

</body>

This gives the developer/customer an immediate reference for where to start looking in the theme files in order to locate the appropriate spot to make changes/override in a child theme. Of course, you could do this with static HTML comments in each template – I’ve done this in my previous theme, and it has definitely helped customers. The downside is that every time you make a new template, if you base it off of an existing template (which I expect is a common scenario), you have to remember to update the comments. That can be a bit tedious, and failure to do so can result in inaccurate comments, and the last thing we want is to send the customer on a wild goose chase. This technique avoids that.

Sidenote: The sidebar template, sidebar.php, the header template, header.php, and the footer template, footer.php, are of course called within the archive template, archive.php – so the HTML comments aren’t technically in the proper locations. The comment locations used here represent the practical divisions within standard WordPress themes in order to provide users with the most useful guidance, rather than the most accurate technical locations.

Of course, sometimes the classes applied to the body tag can be enough to identify the template for advanced users, but the source of a template file can still be ambiguous if a child theme is in use, or when we are using nested content templates. This technique makes it immediately clear which template is producing which code.

We’ll define two functions that dynamically output the template location within the theme. Then we just add these functions to the start and end of each template file to act as bookends for the code. We can copy these functions as many times as we want, and they will always remain accurate within the template they appear (just remember that wherever the __FILE__ magic constant is invoked is the location that will be printed – that’s why __FILE__ must be passed as a parameter). Moreover, if we want to change the structure of the comment, or remove them altogether based on a setting, that becomes trivial to accomplish. Here are the functions, which you should namespace:

function begin_template( $file ){
	?>	<!-- begin <?php echo basename( $file ) . " [".basename( dirname( $file ) ) ."]" ; ?> -->
	<?php
}
function end_template( $file ){
	?><!-- end <?php echo basename( $file ); ?> -->
	<?php
}

Then within the template files, they are called like this:

<?php tinderbox_begin_template( __FILE__ ); ?>
<?php tinderbox_end_template( __FILE__ ); ?>

Each function determines the file name and prints it within an HTML comment. For the beginning comment, we also print out the directory in which the template appears – this makes it easy to determine if the template is from the parent or child theme.

Here’s a basic example of how index.php might look (this is Underscores-based):

get_header(); ?>
		
		<?php begin_template( __FILE__ ); ?>
		<div id="primary" class="content-area span8">
			<div id="content" class="site-content" role="main">

			<?php if ( have_posts() ) : ?>

				<?php tinderbox_content_nav( 'nav-above' ); ?>

				<?php /* Start the Loop */ ?>
				<?php while ( have_posts() ) : the_post(); ?>

					<?php
						/* Include the Post-Format-specific template for the content.
						 * If you want to overload this in a child theme then include a file
						 * called content-___.php (where ___ is the Post Format name) and that will be used instead.
						 */
						get_template_part( 'content', get_post_format() );
					?>

				<?php endwhile; ?>

				<?php tinderbox_content_nav( 'nav-below' ); ?>

			<?php else : ?>

				<?php get_template_part( 'no-results', 'index' ); ?>

			<?php endif; ?>

			</div><!-- #content .site-content -->
		</div><!-- #primary .content-area -->
		<?php begin_template( __FILE__ ); ?>

<?php get_sidebar(); ?>
<?php get_footer(); ?>

The functions bookend the meat of the template, excluding headers, sidebars, and footers. In the content-{type}.php templates, the comment functions would wrap the entire contents of the file.

Now we can make as many copies of this template as we want, and never worry about updating the comments. Nifty.

I think if more developers adopt this type of commenting (in addition to standard code commenting), it’ll go a long way toward customers becoming self-sufficient in sourcing template files and making their own customizations. Plus, I find it makes things much easier for myself during the development process.

How to add links to WordPress image captions

The WordPress media manager allows you to add captions to your images, which can be inserted into your post content wrapped in the caption shortcode.

You can see that I’ve placed anchor tags in the photo caption to link to the photo credit:

Photo credit: <a href="http://www.flickr.com/photos/nattu/2735064420">nattu</a></code>

When you click the “Insert into Post” button, you end up with this markup in your editor:

[ caption id="attachment_1133" align="alignleft" width="300" 
caption="Photo credit: <a href=&quot;http://www.flickr.com/photos/nattu/2735064420&quot;&gt;nattu&lt;/a&gt;"]
<a data-rel="prettyPhoto" title="The Entrance" href="http://localhost/agility/wp-content/uploads/2012/03/TheEntrance.jpg">
<img src="http://localhost/agility/wp-content/uploads/2012/03/TheEntrance-300x199.jpg" alt="" title="The Entrance" width="300" height="199" class="scale-with-grid size-medium wp-image-1133" /></a>[/caption]

Skip to the code solution

The Problem

The issue is that all the HTML entities in the caption attribute (specifically, the anchor tags) are encoded. That is, instead of

Photo credit: <a href="http://www.flickr.com/photos/nattu/2735064420">nattu</a>

we have

Photo credit: &lt;a href=&quot;http://www.flickr.com/photos/nattu/2735064420&quot;&gt;nattu&lt;/a&gt;

As a result, instead of getting the desired output in our caption:

Photo credit: <a href="http://www.flickr.com/photos/nattu/2735064420">nattu</a>

We get the encoded HTML markup instead:

Photo credit: &lt;a href=&quot;http://www.flickr.com/photos/nattu/2735064420&quot;&gt;nattu&lt;/a&gt;

The Solution

Now, the HTML entities have to be escaped in order to safely place them in the caption=”” attribute of the caption shortcode (or at least the double quotes do), and in any event it’s very inconvenient to decode the entities manually. What we’ll need to do is decode them programmatically when the shortcode is processed at run time.

Helpfully, WordPress’s img_caption_shortcode function (wp-includes/media.php) includes a filter that allows us to hook in and override the caption. Here’s the original function:

/**
 * The Caption shortcode.
 *
 * Allows a plugin to replace the content that would otherwise be returned. The
 * filter is 'img_caption_shortcode' and passes an empty string, the attr
 * parameter and the content parameter values.
 *
 * The supported attributes for the shortcode are 'id', 'align', 'width', and
 * 'caption'.
 *
 * @since 2.6.0
 *
 * @param array $attr Attributes attributed to the shortcode.
 * @param string $content Optional. Shortcode content.
 * @return string
 */
function img_caption_shortcode($attr, $content = null) {

	// Allow plugins/themes to override the default caption template.
	$output = apply_filters('img_caption_shortcode', '', $attr, $content);
	if ( $output != '' )
		return $output;

	extract(shortcode_atts(array(
		'id'	=> '',
		'align'	=> 'alignnone',
		'width'	=> '',
		'caption' => ''
	), $attr));

	if ( 1 > (int) $width || empty($caption) )
		return $content;

	if ( $id ) $id = 'id="' . esc_attr($id) . '" ';

	return '<div ' . $id . 'class="wp-caption ' . esc_attr($align) . '" style="width: ' . (10 + (int) $width) . 'px">'
	. do_shortcode( $content ) . '<p class="wp-caption-text">' . $caption . '</p></div>';
}

add_shortcode('gallery', 'gallery_shortcode');

We’re just going to hook in and use the same code, but add a line of our own to decode the HTML tags.

$caption = html_entity_decode( $caption );

The Code

So, our final function, which I’ve placed in functions.php, looks like this:

//Our custom caption shortcode function is based on the WordPress Core version with a small change
function custom_img_caption_shortcode( $a , $attr, $content = null) {

	extract(shortcode_atts(array(
		'id'	=> '',
		'align'	=> 'alignnone',
		'width'	=> '',
		'caption' => ''
	), $attr));

	if ( 1 > (int) $width || empty($caption) )
		return $content;

	$caption = html_entity_decode( $caption );  //Here's our new line to decode the html tags

	if ( $id ) $id = 'id="' . esc_attr($id) . '" ';

	return '<div ' . $id . 'class="wp-caption ' . esc_attr($align) . '" style="width: ' . (10 + (int) $width) . 'px">'
	. do_shortcode( $content ) . '<p class="wp-caption-text">' . $caption . '</p></div>';
}
//Add the filter to override the standard shortcode
add_filter( 'img_caption_shortcode', 'custom_img_caption_shortcode', 10, 3 );

And that’s it! Just pop that code into your functions.php and you should be able to add links to your photo captions. If anyone knows a better way of doing this, let me know in the comments. Enjoy!

How to Add a Custom Class to a WordPress Menu Item

This is a question I get a lot related to UberMenu – WordPress Mega Menu Plugin, though it’s actually baked into WordPress Core. It’s a very powerful feature, as it allows you to target either a single menu item or a group of menu items with a single class. However, the option is hidden from the UI by default. In order to add a class to a menu item, you first have to reveal it.

1. In Appearance > Menus, click the Screen Options tab

Click the Screen Options tab

2. Under Show advanced menu properties, check CSS Classes

Check CSS Classes option

3. Now expand any menu item to reveal the CSS Classes (optional) text input.

CSS Classes text input

4. Enter your class name and save your menu to apply the class to the menu item

The class will be added to the LI (list item) element, the same element that has the menu item ID attached. You can then style your item like this, where .menu is replaced by the class used for your menu:

/* Target the menu item LI */
.menu li.myClass{
  /* Margin might go here */
}
/* Target the menu item Anchor (link) */
.menu li.myClass > a{
  /* Colors, font sizes, would normally go here */
}

/* Target the menu item LI in UberMenu */
#megaMenu ul.megaMenu li.myClass{

}
/* Target the menu item Anchor (link) in UberMenu */
#megaMenu ul.megaMenu li.myClass > a{
  /* Colors, font sizes, would go here */
}

Remember that you may need to increase your selector specificity or use the !important flag in order to ensure that your new styles are applied.

5. Enjoy even more customization control over your menu!

Note that this is a standard WordPress feature and works with or without UberMenu.