Tech from Hel
Helene's ramblings on Wordpress, jQuery and other web technologies.
rss
twitter
  • About
  • Projects

Horizontal Submenus for WordPress Admin Plugins

10 comments
Posted on Mar 25 2009 by helene

How to Add Horizontal Submenus to Your Administration Plugin in WordPress 2.7+

With the launch of version 2.7 in December 2008, WordPress added several new features and changed its user interface (UI). I especially like the new automatic update feature and the new vertical sidebar is sleeker and more modern looking. However, I find navigating the menus on that new sidebar to be very inefficient when administration plugins add more selections to it. [Wordpress screen shot]
Image #1: Plugin with sidebar menu

As an admin plugin author, I want my users to have easy access to my plugin menu and submenus. In WordPress 2.2 to 2.6, the built-in menu functions accomplished this with horizontal menus at the top of the page. In 2.7, however, the same menu functions now place the plugin menu (and submenus) near the bottom of the page, in the sidebar. This makes navigation more cumbersome because plugin menus are positioned outside the initial viewing area of the screen in a typical 1280×800 browser window. The user has to scroll down to select the plugin and, after the plugin is selected, scroll down again to navigate the plugin’s submenus. Worse, it not always obvious that there are submenus available for a plugin. You can see an example of this in image #1 where the green line indicates the end of the window area of a 1280×800 resolution browser window.

To workaround this problem in my own admin plugin, I inserted a new horizontal menu containing the submenu links at the top of each submenu page. plugin menu screen shot
image #2: Plugin with horizontal menu
To keep the look consistent with WordPress' new style, I positioned the menu inline with the “contextual help” button and also adopted it’s “look and feel”. This was a relatively simple task to do, once I located and copied the appropriate styles from WordPress' admin stylesheets. You can see an example of this new horizontal menu in the second image to the right.

Below are step-by-step instructions on how you can do this for your own “top-level” admin plugin with submenus. Please note that the rest of this article assumes prior knowledge of WordPress plugin writing and basic PHP, HTML and CSS skills.

  • Step #1: Create the plugin menu and submenu pages

    Use the WordPress functions, add_menu_page and add_submenu_page, to create your plugin's menu and submenus. This maintains compatibility with earlier versions of WordPress (2.0+) and sets the “page” parameter for your submenu pages. This parameter is used on the admin url to link to your plugin pages and is needed for the submenu links in the horizontal menu in step #2 below.

    Your plugin menu/submenu code should look something like this:

    <?php
    define('DEMOFILE',basename(__FILE__));
    
    //give plugin a top-level WordPress admin menu with submenus
    function demo_menus() {
    	// userlevel=8 restrict users to "Administrators" only
    	add_menu_page('demofromhel', 'Demo from Hel', 8, DEMOFILE, 'demo_content');
    	add_submenu_page(DEMOFILE, 'SubMenu-1 Title', 'SubMenu-1', 8, DEMOFILE, 'demo_content');
    	add_submenu_page(DEMOFILE, 'SubMenu-2 Title', 'SubMenu-2', 8, 'demo-page2', 'demo_content');
    	add_submenu_page(DEMOFILE, 'SubMenu-3 Title', 'SubMenu-3', 8, 'demo-page3', 'demo_content');
    }

    The constant, DEMOFILE, is your plugin filename and also the “page” parameter value for the default submenu (SubMenu-1) page.

  • Step #2: Create a new menu as an “unordered” list of links to each submenu page

    Inside your plugin's content function, create an unordered list (<ul>) containing links to each submenu page. This will be your plugin's horizontal submenu for WordPress 2.7. Insert this list just above the main “wrap” <div> in your document. Give the <ul> tag a unique id and give each <li> tag the same class name. The submenu page links should be listed in the reverse of their intended display order. This is necessary because of the float:right style attribute used to format the menu (see step #3 below). After the terminating </ul> tag, add an empty <div> with the inline style, style="clear:right;". This extra <div> fixes a css bug in IE7. Then, enclose the entire code within a PHP statement that tests for WordPress version 2.7 or higher. This “if” code block excludes the plugin horizontal submenu from earlier versions of WordPress where it would be redundant. This section of code looks like:

    function demo_content() {
    	global $wpdb, $wp_version;
    	if (version_compare($wp_version, '2.7', '>=')) {  ?>
    	<ul id="demo-menu">
    		<li class="demo-menu-link"><a href="?page=demo-page3">SubMenu-3</a></li>
    		<li class="demo-menu-link"><a href="?page=demo-page2">SubMenu-2</a></li>
    		<li class="demo-menu-link"><a href="?page=<?php echo DEMOFILE; ?>">SubMenu-1</a></li>
    	</ul>
    	<div style="clear:right;"></div>
    	<?php
    	} ?>
    	<div class="wrap">
    		<div id="icon-plugins" class="icon32"></div>
    		<h2>Demo from Hel</h2>
    		<?php	//output page content for each plugin submenu page
    		if ($_GET['page'] == "demo-page3") {
    			print '<p>Demo page 3 content</p>';
    		} elseif ($_GET['page'] == "demo-page2" ) {
    			print '<p>Demo page 2 content</p>';
    		} else {	//page 1 is default
    			print '<p>Demo page 1 / default content</p>';
    		} ?>
    		<hr/>
    	</div>
    <?php }

    Of course, your plugin should output content for each submenu “page” of your plugin. As the above example shows, you can read PHP's $_GET['page'] parameter to identify individual pages and customize content accordingly. The content must be enclosed inside a “wrap” division (<div class="wrap">) in keeping with WordPress' admin style standard and for the “ul” menu to be formatted properly. Also, note that the variable, $wp_version is a global WordPress variable and must be declared in the “GLOBAL” section of your content function.

  • Step #3: Style your new menu to display horizontally, like the “help” button

    Next, in your plugin's css stylesheet file, insert the following css code to format the menus so that they are in-line with the “contextual help” button and they use the same background image. If you don’t have a separate css file for your plugin, just put these lines in a <style> tag in your plugin's “head” function or above the <ul> menu code in the “content” function.

    #demo-menu {
    	display: inline;
    	position: relative;
    }
    #demo-menu a, #demo-menu a.link {
    	text-decoration: none;
    	z-index: 1;
    	margin: 0 auto;
    	padding: 0 6px 0 6px;
    	height: 22px;
    	line-height: 22px;
    	font-size: 10px;
    	background-repeat: no-repeat;
    	background-position: right bottom;
    }
    .demo-menu-link {
    	float: right;
    	background: transparent url(images/screen-options-left.gif ) no-repeat 0 0;
    	font-family: "Lucida Grande", Verdana, Arial, "Bitstream Vera Sans", sans-serif;
    	height: 22px;
    	padding: 0;
    	margin: 0 6px 0 0;
    	text-decoration: none;
    	text-align:center;
    }

    You can expand on these styles by adding “a:visited”, “a:hover” and “a:active” css attributes as well.

  • Step #4: Integrate your plugin and menus into WordPress with admin “hooks”

    Lastly, near the bottom of your plugin program, add the appropriate admin hooks to incorporate your plugin functions into WordPress. Use the “admin_menu” hook to integrate the menus and submenu pages into WordPress admin. Use the “admin_head” hook to integrate your plugin stylesheet (and javascripts, if any) into WordPress admin <head>.

    //hook the plugin functions into WordPress admin
    add_action('admin_menu', 'demo_menus');
    add_action('admin_head', 'demo_head');
    

    Note that the “demo_content” function is also “hooked” into WordPress via the “admin_menu” hook as each submenu page calls that function. Also, the “admin_head” hook is not needed unless you created a “demo_head” function containing the <style> tag for your css code or containing the css stylesheet link.

And you are DONE!

Here is the entire code in a single program:

<?php
global $wpdb, $wp_version;
define('DEMOFILE',basename(__FILE__));

//give plugin a top-level WordPress admin menu with submenus
function demo_menus() {
	// userlevel=8 restrict users to "Administrators" only
	add_menu_page('demofromhel', 'Demo from Hel', 8, DEMOFILE, 'demo_content');
	add_submenu_page(DEMOFILE, 'SubMenu-1 Title', 'SubMenu-1', 8, DEMOFILE, 'demo_content');
	add_submenu_page(DEMOFILE, 'SubMenu-2 Title', 'SubMenu-2', 8, 'demo-page2', 'demo_content');
	add_submenu_page(DEMOFILE, 'SubMenu-3 Title', 'SubMenu-3', 8, 'demo-page3', 'demo_content');
}

function demo_head() {
	global $wp_version;
	if (version_compare($wp_version, '2.7', '>=')) {  ?>
<style type="text/css">
#demo-menu {
	display: inline;
	position: relative;
}
#demo-menu a, #demo-menu a.link {
	text-decoration: none;
	z-index: 1;
	margin: 0 auto;
	padding: 0 6px 0 6px;
	height: 22px;
	line-height: 22px;
	font-size: 10px;
	background-repeat: no-repeat;
	background-position: right bottom;
}
.demo-menu-link {
	float: right;
	background: transparent url(images/screen-options-left.gif ) no-repeat 0 0;
	font-family: "Lucida Grande", Verdana, Arial, "Bitstream Vera Sans", sans-serif;
	height: 22px;
	padding: 0;
	margin: 0 6px 0 0;
	text-decoration: none;
	text-align:center;
}
</style>
<?php
	}
}

function demo_content() {
	global $wpdb, $wp_version;
	if (version_compare($wp_version, '2.7', '>=')) {  ?>
	<ul id="demo-menu">
		<li class="demo-menu-link"><a href="?page=demo-page3">SubMenu-3</a></li>
		<li class="demo-menu-link"><a href="?page=demo-page2">SubMenu-2</a></li>
		<li class="demo-menu-link"><a href="?page=<?php echo DEMOFILE; ?>">SubMenu-1</a></li>
	</ul>
	<div style="clear:right;"></div>
<?php	} ?>
	<div class="wrap">
		<div id="icon-plugins" class="icon32"></div>
		<h2>Demo from Hel</h2>
		<?php	//output page content for each plugin submenu page
		if ($_GET['page'] == "demo-page3") {
			print '<p>Demo page 3 content</p>';
		} elseif ($_GET['page'] == "demo-page2" ) {
			print '<p>Demo page 2 content</p>';
		} else {	//page 1 is default
			print '<p>Demo page 1 / default content</p>';
		} ?>
		<hr/>
	</div>
<?php }

//hook the plugin functions into WordPress admin
add_action('admin_menu', 'demo_menus');
add_action('admin_head', 'demo_head');
?>

I put this code into a functioning plugin that you can install, activate, and test for yourself. You can download it here. To install, unzip the archive, copy demo_from_hel.php to your plugin directory, and activate.

This horizontal menu code was tested successfully in IE 6, IE 7, FF 1.5-mac, FF 2, FF 3, Mozilla 1.7, Safari 1.3, Safari 2, Safari 3-Win, Chrome 1.0, and Camino 1.6. Feedback regarding other browsers would be appreciated.

For more information on integrating WordPress’ adminstration menus or submenus into your plugin, see the codex doc: Adding Administration Menus. To learn about writing plugins for WordPress, read the codex doc: Writing_a_Plugin.

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  

  Tags: horizontal menu, wordpress Category: Plugins

10 Comments

  1. Mike Stocks says:
    December 10, 2009 at 9:54 am

    Great plugin. Where exactly would the page content go?
    Would one create a page for each sub menu and link to that.
    How would a page be addressed?

    Reply
    • hellioness says:
      December 10, 2009 at 1:54 pm

      Hi Mike,
      Your answer is in the “add_menu_page” and “add_submenu_page” WordPress functions. The last two parameters for these functions are the page identifier and the function that displays the page content, respectively.

      To “address” an individual plugin page, use the identifier that you created in “add_menu_page/add_submenu_page” (ex: DEMOFILE, demo-page2, demo-page3). That identifier becomes the “page” query argument on the URL for WordPress admin, ex: http://myblog.com/wp-admin/admin.php?page=demo-page3.

      To show your page content when your plugin’s menu is selected, write a function with your page content in it, then enter that function name as the last parameter for “add_submenu_page()”. You can write different functions for each individual page or one function for all the pages like in my example (“demo_content”). For the latter, I read the $_GET['page'] parameter to customize content by page identifier.

      Reply
  2. Mike Stocks says:
    December 16, 2009 at 2:19 pm

    Thanks for coming back.
    I have modified the code slightly to try and get 3 functions for each submenu andI have tried to follow your suggestions,but I get an error ‘cant redeclare author_menus()
    If you have time could you comment on my code please
    Many thanks

    <?php
    /*
    Plugin Name: Authors-1
    Plugin URI:http://www.techfromhel.com/2009/03/horizontal-submenus-wordpress
    Description: A WordPress administration plugin with top-level menu and a horizontal submenu for demonstration only
    Version: 0.1
    Author: Helene D.http://www.techfromhel.com
    Author URI:
    Released under the GNU General Public License (GPL)
    http://www.gnu.org/licenses/gpl.txt
    */
    global $wpdb, $wp_version;
    define('Authors-1',basename(__FILE__));
    // create a top-level menu with submenu pages for wordpress admin
    function author_menus() {
    	// userlevel=8 restrict users to "Administrators" only
    	add_menu_page('Authors-1', 'Authors', 8, Authors-1, 'author_content');
    	add_submenu_page(Authors-1, 'SubMenu-1 Title', 'SubMenu-1', 8, '__FILE__', 'subMenu1');
    	add_submenu_page(Authors-1, 'SubMenu-2 Title', 'SubMenu-2', 8, '__FILE__', 'subMenu2');
    	add_submenu_page(Authors-1, 'SubMenu-3 Title', 'SubMenu-3', 8, '__FILE__', 'subMenu3');
    }
    //output style and other code for document
    function author_head() {
    	global $wp_version;
    	if (version_compare($wp_version, '2.7', '>=')) {  ?>
    
    
#demo-menu {
    	display: inline;
    	position: relative;
    }
    #demo-menu a, #demo-menu a.link {
    	text-decoration: none;
    	z-index: 1;
    	margin: 0 auto;
    	padding: 0 6px 0 6px;
    	height: 22px;
    	line-height: 22px;
    	font-size: 10px;
    	background-repeat: no-repeat;
    	background-position: right bottom;
    }
    .demo-menu-link {
    	float: right;
    	background: transparent url(images/screen-options-left.gif ) no-repeat 0 0;
    	font-family: "Lucida Grande", Verdana, Arial, "Bitstream Vera Sans", sans-serif;
    	height: 22px;
    	padding: 0;
    	margin: 0 6px 0 0;
    	text-decoration: none;
    	text-align:center;
    
    
<?php
    	}
    }
    
    
// output content for document
    function author_content() {
    	global $wpdb, $wp_version;
    	if (version_compare($wp_version, '2.7', '>=')) {  ?>
    		<a>subMenu3
    		<a href="?page=Authors-1.php">subMenu2</a>
    		<a href="?page=Authors-1.php">subMenu1</a>
    
    
    
Authors Additional Information
    		These pages allow you to add additional information to support your book idea or synopsis.
    		Got to your options page if you want publishers to view this additonal information
    Reply
  3. helene says:
    December 19, 2009 at 9:26 pm

    Mike,
    ‘cannot redeclare’ is a common WordPress error that can occur when (a) a function name defined in your plugin is not unique, i.e. when 2 copies/versions of the same plugin is active or when 2 plugins have coincidental duplicate function names,
    or when (b) a plugin is not coded as a php class, especially in WordPress 2.7+
    One work-around for (b) is to move all ‘add_action’ hooks into a single function, then write a new ‘add_action’ to call this function using the ‘plugins_loaded’ hook, for example, in my demo plugin, the last 2 lines of code can be replaced by this:

    function load_demo_plugin() {
        //hook the plugin functions into WordPress admin
        add_action('admin_menu', 'demo_menus');
        add_action('admin_head', 'demo_head');
    }
    add_action('plugins_loaded', 'load_demo_plugin');

    I hope this helps.

    FYI. To add code in comments without WordPress stripping the tags, enclose your code inside <code></code> tags and remove all comment notation (// and /*) and blank lines from the code.

    Reply
  4. Mike says:
    December 31, 2009 at 12:18 pm

    Thanks for your reply. I found the cause of the prolem, but will remeber your solution.
    Unfortunately i still cant get the sub menu to work.
    Any help much appreciated.
    My lataest effort is as follows:-

    <?php
    /*Plugin Name: Writers
    Plugin URI:http://www.techfromhel.com/2009/03/horizontal-submenus-wordpress
    Description: A WordPress administration plugin with top-level menu and a horizontal submenu for demonstration only
    Version: 0.1
    Author: Helene D.http: //www.techfromhel.com
    Author URI:
    Released under the GNU General Public License (GPL)
    http ://www.gnu.org/licenses/gpl.txt
    */
    global $wpdb, $wp_version;
    define('Writers',basename(__FILE__));
    //create a top-level menu with submenu pages for wordpress admin
    function author_menus() {
    	// userlevel=8 restrict users to "Administrators" only
    	add_menu_page('Writers', 'Authors', 8, 'Writers', 'author_content');
    	add_submenu_page(Writers, 'SubMenu-1 Title', 'SubMenu-1', 8, 'subMenu1', 'author_content');
    	add_submenu_page(Writers, 'SubMenu-2 Title', 'SubMenu-2', 8, 'subMenu2', 'author_content');
    	add_submenu_page(Writers, 'SubMenu-3 Title', 'SubMenu-3', 8, 'subMenu3', 'author_content');
    }
    //output style and other code for document
    function author_head() {
    	global $wp_version;
    	if (version_compare($wp_version, '2.7', '>=')) {  ?>
    #demo-menu {
    	display: inline;
    	position: relative;
    }
    #demo-menu a, #demo-menu a.link {
    	text-decoration: none;
    	z-index: 1;
    	margin: 0 auto;
    	padding: 0 6px 0 6px;
    	height: 22px;
    	line-height: 22px;
    	font-size: 10px;
    	background-repeat: no-repeat;
    	background-position: right bottom;
    }
    .demo-menu-link {
    	float: right;
    	background: transparent url(images/screen-options-left.gif ) no-repeat 0 0;
    	font-family: "Lucida Grande", Verdana, Arial, "Bitstream Vera Sans", sans-serif;
    	height: 22px;
    	padding: 0;
    	margin: 0 6px 0 0;
    	text-decoration: none;
    	text-align:center;
    }
    <?php
    	}
    }
    //output content for document
    function author_content() {
    	global $wpdb, $wp_version;
    	if (version_compare($wp_version, '2.7', '>=')) {  ?>
    		<a href="?page=subMenu3">subMenu3</a>
    		<a href="?page=subMenu2">subMenu2</a>
    		<a href="?page=subMenu1">subMenu1</a>
    ...
    		Authors Additional Information
    		These pages allow you to add additional information to support your book idea or synopsis.
    		Got to your options page if you want publishers to view this additonal information
    Reply
    • helene says:
      January 1, 2010 at 7:34 am

      Mike,
      Your code is incomplete. WordPress is still stripping it of tags. You need to describe your problem more specifically than “i still cant get the sub menu to work” and, if possible, do screen shots of your code and all error messages and add links to the images here.

      However, I do see one error in your partial code: ‘add_menu_page’ should not have the 2nd ‘Writers’ quoted. That 4th argument is the constant that you defined in the line above.

      Good luck and happy new year!!

      Reply
  5. Mike says:
    January 1, 2010 at 8:58 am

    Thanks Helene and a very happy new year to you too.
    If I unquote the second writers the page doesnt load.error message=Cannot load Writers.
    The specific problem is that when I select the horizontal tabs the submenus dont display.The functions are just set to echo the menu name at this point.
    When i select any of the other tabs i get submenu 1 not the selected menu.
    I have cut the code down a bit so hopefully you can see the relevant parts.

    global $wpdb, $wp_version;
    define('Writers',basename(__FILE__));
    function author_menus() {
    add_menu_page('Writers', 'Authors', 8, 'Writers', 'author_content');
    add_submenu_page(Writers, 'SubMenu 1', 'SubMenu 1', 8, 'subMenu1', 'author_content');
    add_submenu_page(Writers, 'SubMenu 2', 'SubMenu 2', 8, 'subMenu2', 'author_content');
    add_submenu_page(Writers, 'SubMenu 3', 'SubMenu 3', 8, 'subMenu3', 'author_content');
    }
    function author_head() {
    global $wp_version;
    if (version_compare($wp_version, '2.7', '>=')) { ?>
    =')) { ?>

    subMenu3
    subMenu2
    subMenu1

    Authors Additional Information
    These pages allow you to add additional information to support your book idea or synopsis.
    Got to your options page if you want publishers to view this additonal information

    Reply
  6. Mike says:
    January 1, 2010 at 8:59 am

    Sorry still stripping the code.
    Can I email you with an attachment?

    Reply
    • helene says:
      January 6, 2010 at 2:27 pm

      Mike, I sent you an email that you can reply to with the attachment.

      Reply
  7. Den says:
    April 13, 2010 at 12:39 pm

    Hi Mike! Great tutorial!! Thanks a lot! Tell me please can I use it in commercial plugins or themes?

    Reply

Leave a Reply

Click here to cancel reply.




Categories

  • Blogging
  • javascript
  • Miscellaneous
  • Multimedia
  • MySQL
  • Plugins

Recent Posts

  • WassUp Works Well with WP Widget Cache
  • CTRL+Z Undo: The Keyboard Shortcut You Can’t Do Without
  • A Fine Fix for “get_currentuserinfo undefined”
  • Optimize with MySQL Procedure Analyse
  • Sleeping with Javascript

Popular Posts

  • Horizontal Submenus for Wordpress Admin Plugins
  • Flv Videos in a Thickbox
  • YouTube Videos in a Thickbox
  • Sleeping with Javascript
  • Installing a More Secure Wordpress

Archives

Ads


  • About
  • Projects
Powered by Wordpress  |  Designed by WebTreats