Advertisement
Theme Development

The Complete Guide To The WordPress Settings API, Part 6: Menu Pages

by

In Part 3 of this series, we surveyed the various menu functions that the WordPress API provides. If you've been following along, then you know that we've already setup a settings page for our theme by using the add_theme_page function. Although introducing menus and submenus aren't explicitly part of the Settings API, they play a role in building custom functionality, plugins, and/or themes.

In this article, we're going to introduce a new menu to the WordPress dashboard that will make our theme options available elsewhere other than just under the "Appearance" options.

Before we get started: This article assumes that you're familiar with the Settings API and theme options. If you're a beginner or even intermediate WordPress developer, I highly recommend catching up on the rest of the series before diving into this post.


A Look at the API

Because we've already looked at each of the menu functions, we don't need to rehash each of the functions that WordPress has available. Instead, we'll take a look at the ones we're going to use and then work through a practical example of how to use them in our own work.

Before we look at each function, let's detail what we're planning to accomplish in the next phase of this series:

  • Introduce a top-level menu for our theme options
  • Add a submenu item that will link to the "Display Options" tab
  • Add a submenu item that will link to the "Social Options" tab

Relatively simple, right? In order to do this, we'll be taking advantage of the following two functions:

  • add_menu_page which is used for introducing top-level menu items
  • add_submenu_page which is used to introduce sub-menu items to top-level menus.

We'll take a look at each function's parameters and usage as we implement them in our theme.

Note that the remainder of this article builds on this version of the WordPress Settings Sandbox. If you're following along with the repository, make sure you check it out.


The Top-Level Menu

The first thing that we want to do is introduce a top-level menu. This menu will appear directly below the "Settings" menu in the WordPress dashboard and will serve two purposes. The menu should:

  1. Expose the theme's options to the WordPress dashboard
  2. Display a default options page for the theme options

The function takes the following seven arguments, the first five are required, the last two are not:

  1. page_title is the text that will be rendered in the browser's title bar
  2. menu_title is the text that will be displayed for the menu item
  3. capability refers to the role that the user must have in order to access this menu
  4. menu_slug is a unique value that identifies this menu. It's also how submenus register themselves with this menu.
  5. function_name that is called when the menu is clicked for displaying the options page.
  6. icon_url is the path to the icon that you want to display next to your menu item.
  7. position is where the menu should be added in relation to the other menus in the WordPress Dashboard.

In our work, we'll be focused only on the first five parameters. I discuss menu positioning in the conclusion of this article.

To get started, we're going to need to introduce a call to the add_menu_page function. According to the WordPress Codex, administration menus can be added using the admin_menu hook. Earlier in this series, we wrote a function that adds our theme options to the "Appearance" menu. Specifically, we wrote sandbox_example_theme_menu:

function sandbox_example_theme_menu() {

	add_theme_page(
		'Sandbox Theme', 			// The title to be displayed in the browser window for this page.
		'Sandbox Theme',			// The text to be displayed for this menu item
		'administrator',			// Which type of users can see this menu item
		'sandbox_theme_options',	// The unique ID - that is, the slug - for this menu item
		'sandbox_theme_display'		// The name of the function to call when rendering this menu's page
	);

} // end sandbox_example_theme_menu
add_action( 'admin_menu', 'sandbox_example_theme_menu' );

Note in the code above that this function was registered with the admin_menu hook, as well. You should always strive to keep your functions logically consistent. Since we already have a function that registers a menu, that is registered with the appropriate hook, and since we're introducing similar functionality, we'll be adding our new menu functions to this function.

Add the following call to add_menu_page directly under the call above:

add_menu_page(
	'Sandbox Theme',		// The value used to populate the browser's title bar when the menu page is active
	'Sandbox Theme',		// The text of the menu in the administrator's sidebar
	'administrator',		// What roles are able to access the menu
	'sandbox_theme_menu',	// The ID used to bind submenu items to this menu 
	'sandbox_theme_display'	// The callback function used to render this menu
);

As you can see, we're registering a menu that will display "Sandbox Theme" in both the browser's title bar and in the menu item. We're exposing the menu only to administrators and we've given the menu the unique ID of "sandbox_theme_parent_menu". We'll be re-using this parameter in the next section.

There's one important thing that we need to clarify: Notice that we've passed 'sandbox_theme_display' as the function to be called when this menu item is clicked. Recall that in Part 3 we introduced this function (and we refined it in Part 5). Specifically, it is responsible for rendering our tabbed theme options page.

By passing this existing function name to the add_menu_page function, we're taking advantage of code that we've already written and we're rendering a default options page for the menu item.

At this point, we're ready to begin adding submenus but before moving forward, make sure that your function looks exactly like this:

function sandbox_example_theme_menu() {

	add_theme_page(
		'Sandbox Theme',
		'Sandbox Theme',
		'administrator',
		'sandbox_theme_options',
		'sandbox_theme_display'
	);
	
	add_menu_page(
		'Sandbox Theme',
		'Sandbox Theme',
		'administrator',
		'sandbox_theme_menu',
		'sandbox_theme_display'
	);

} // end sandbox_example_theme_menu

Add the Submenus

Submenus are very similar to menus except that they "belong" to an existing menu. The API for registering submenus is relatively similar, too. The function accepts six arguments, the first five being required, the last one being optional:

  1. parent_slug refers to the unique ID of the parent menu item. In our case, "sandbox_theme_menu".
  2. page_title is the text to be rendered in the browser's toolbar when this submenu item is active
  3. menu_title is the text for this actual submenu item in the dashboard
  4. capability is the role that a user must have to access this menu item
  5. menu_slug is the unique ID for this particular menu item
  6. function_name that is called when the menu is clicked in order for displaying the options page

The function is straightforward. We have two menu items to introduce – one for "Display Options" and one for "Social Options."

Display Options

First, let's introduce a submenu item for display options. Add the following block of code directly under the add_menu_page call that we defined above:

add_submenu_page(
	'sandbox_theme_menu',				// The ID of the top-level menu page to which this submenu item belongs
	'Display Options',					// The value used to populate the browser's title bar when the menu page is active
	'Display Options',					// The label of this submenu item displayed in the menu
	'administrator',					// What roles are able to access this submenu item
	'sandbox_theme_display_options',	// The ID used to represent this submenu item
	'sandbox_theme_display'				// The callback function used to render the options for this submenu item
);

Each of the above parameters should be clear with the exception of the function name that we passed as the final argument. Notice that it's the same function name that we provided in the add_menu_page call. But this makes sense, right? After all, the "Display Options" is the default tab that is displayed when the theme options are selected so it would make sense that it should be rendered when the top-level menu is selected and when the "Display Options" menu item is selected.

At this point, there's an important feature of WordPress to highlight: Note that once you've added your first submenu item, WordPress will actually render two submenu items to the top-level menu – one item that duplicates the function of the top-level menu and one item that corresponds to the submenu item that you just defined. I bring this up because, in my experience, I've seen developers get confused as to how (and why) this happens. The short of it is that WordPress is doing this – it's nothing wrong with your code.

Social Options

Adding a menu item for the social options is almost exactly like adding a menu item for the display options. Of course, we just want to change the values for the title bar, menu item, and the page that is displayed whenever the menu is selected. First, let's setup our call to the add_submenu_page function. It should look like this:

add_submenu_page(
	'sandbox_theme',
	'Social Options',
	'Social Options',
	'administrator',
	'sandbox_theme_social_options',
	'sandbox_theme_display'
);

Save your code, refresh your dashboard, and you should see the "Social Options" menu item now available under the "Sandbox Theme" menu; however, notice that clicking on the new submenu item only renders the "Display Options." Since we've passed the "sandbox_theme_display" as the function name, that's what we should expect, right? So now we're faced with a little bit of a challenge: How do we select the "Social Options" tab when clicking on the submenu item?

Refactoring Our Tab Functionality

There are a couple of different options that we have for binding the new submenu item to the proper tab on the theme options' page:

  • We can define a new function that renders out the social options. This would require that we do some additional work in introducing a new function, setting up tabbing functionality so that we don't break the experience of the existing page, and duplicating a little bit of code.
  • We can refactor the existing sandbox_theme_display function to accept an optional parameter and then use an anonymous function in the add_submenu_page call to pass a parameter to it.

Ultimately, either of these options are up to you; however, I'd rather refactor my existing function than duplicate code so that's what I'll be doing throughout the remainder of this article.

First, let's begin refactoring our sandbox_theme_display function. Let's have it accept an optional argument that will be used to indicate which tab we want to select. Locate the following signature in your functions.php file:

function sandbox_theme_display() {
	/* Consolidated for this part of the article. */
} // end sandbox_theme_display

Update the signature so that it accepts a single argument and sets it to null when it's not defined:

function sandbox_theme_display( $active_tab = null ) {
	/* Consolidated for this part of the article. */
} // end sandbox_theme_display

If you're new to PHP, you can read about default arguments on this page.

Remember from the last article that our display function is actually looking for a query string value to be set. We still want to maintain that functionality, but we need to account for the fact that the parameter may be passed into the function, as well. To perform this refactoring, first locate the following line of code in the sandbox_theme_display function:

$active_tab = isset( $_GET[ 'tab' ] ) ? $_GET[ 'tab' ] : 'display_options';

Notice that this particular line of code is only concerned with the query string parameters. To account for the new optional parameter, we need to introduce logic that first checks if the query string parameter is checked, if not, it will check to see if the function's argument is set to display the social options, and, if not, then it will default to the display options. Replace the line of code above with the following conditional:

if( isset( $_GET[ 'tab' ] ) ) {
	$active_tab = $_GET[ 'tab' ];
} else if( $active_tab == 'social_options' ) {
	$active_tab = 'social_options';
} else {
	$active_tab = 'display_options';
} // end if/else

The final version of the function should look like this:

function sandbox_theme_display( $active_tab = '' ) {
?>
	<!-- Create a header in the default WordPress 'wrap' container -->
	<div class="wrap">
	
		<div id="icon-themes" class="icon32"></div>
		<h2>Sandbox Theme Options</h2>
		<?php settings_errors(); ?>
		
		<?php if( isset( $_GET[ 'tab' ] ) ) {
			$active_tab = $_GET[ 'tab' ];
		} else if( $active_tab == 'social_options' ) {
			$active_tab = 'social_options';
		} else {
			$active_tab = 'display_options';
		} // end if/else ?>
		
		<h2 class="nav-tab-wrapper">
			<a href="?page=sandbox_theme_options&tab=display_options" class="nav-tab <?php echo $active_tab == 'display_options' ? 'nav-tab-active' : ''; ?>">Display Options</a>
			<a href="?page=sandbox_theme_options&tab=social_options" class="nav-tab <?php echo $active_tab == 'social_options' ? 'nav-tab-active' : ''; ?>">Social Options</a>
		</h2>
		
		<form method="post" action="options.php">
			<?php
			
				if( $active_tab == 'display_options' ) {
					settings_fields( 'sandbox_theme_display_options' );
					do_settings_sections( 'sandbox_theme_display_options' );
				} else {
					settings_fields( 'sandbox_theme_social_options' );
					do_settings_sections( 'sandbox_theme_social_options' );
				} // end if/else
				
				submit_button();
			
			?>
		</form>
		
	</div><!-- /.wrap -->
<?php
} // end sandbox_theme_display

We're not quite done yet. Though we've done the necessary work to display the social options if the proper parameter has been passed, we haven't actually called the function using a parameter. To do this, we need to refactor the add_submenu_page function from above. Currently, the function call looks like this:

add_submenu_page(
	'sandbox_theme',
	'Social Options',
	'Social Options',
	'administrator',
	'sandbox_theme_social_options',
	'sandbox_theme_display'
);

We need to update the final parameter so that it calls the display function and passes the proper value for rendering the social options. To do that, we'll create an anonymous function:

add_submenu_page(
	'sandbox_theme_menu',
	'Social Options',
	'Social Options',
	'administrator',
	'sandbox_theme_social_options',
	create_function( null, 'sandbox_theme_display( "social_options" );' )
);

If you're new to PHP, be sure to read up on the create_function feature and anonymous functions. Though they are outside the scope of this article, they can be powerful (and useful!) when used in the proper context.

The final version of the sandbox_example_theme_menu function should be as follows:

function sandbox_example_theme_menu() {

	add_theme_page(
		'Sandbox Theme', 					// The title to be displayed in the browser window for this page.
		'Sandbox Theme',					// The text to be displayed for this menu item
		'administrator',					// Which type of users can see this menu item
		'sandbox_theme_options',			// The unique ID - that is, the slug - for this menu item
		'sandbox_theme_display'				// The name of the function to call when rendering this menu's page
	);
	
	add_menu_page(
		'Sandbox Theme',					// The value used to populate the browser's title bar when the menu page is active
		'Sandbox Theme',					// The text of the menu in the administrator's sidebar
		'administrator',					// What roles are able to access the menu
		'sandbox_theme_menu',				// The ID used to bind submenu items to this menu 
		'sandbox_theme_display'				// The callback function used to render this menu
	);
	
	add_submenu_page(
		'sandbox_theme_menu',				// The ID of the top-level menu page to which this submenu item belongs
		'Display Options',					// The value used to populate the browser's title bar when the menu page is active
		'Display Options',					// The label of this submenu item displayed in the menu
		'administrator',					// What roles are able to access this submenu item
		'sandbox_theme_display_options',	// The ID used to represent this submenu item
		'sandbox_theme_display'				// The callback function used to render the options for this submenu item
	);
	
	add_submenu_page(
		'sandbox_theme_menu',
		'Social Options',
		'Social Options',
		'administrator',
		'sandbox_theme_social_options',
		create_function( null, 'sandbox_theme_display( "social_options" );' )
	);

} // end sandbox_example_theme_menu
add_action( 'admin_menu', 'sandbox_example_theme_menu' );

Conclusion

At this point, our theme now has its own top-level menu item with each of the settings tabs accessible via submenu items. Though this is useful, I believe it's important to note that there are some mixed opinions on introducing menus into the WordPress Dashboard. Although they can make your work more prominent and accessible, they may also interfere with existing WordPress menus or other third-party work especially if you attempt to place your menus somewhere using the position parameter. Though there is no absolute right or absolute wrong when it comes to introducing menus, think carefully about where you expose your menus. If an existing WordPress menu makes sense, register your work as a submenu.

In the next post, we'll begin taking a look at the various input elements that we can use to introduce options into our WordPress theme as well as how to validate and sanitize the data before serializing them.


Related Sources

Related Posts
  • Web Design
    eCommerce
    Taking Shopify Theme Development FurtherShopify thumb
    In this final part of our Shopify series we will look at how a deeper knowledge of Liquid will enable you to make your themes even more flexible. This will help you deliver richer and more imaginative store designs.Read More…
  • Code
    Theme Development
    Custom Controls in the Theme CustomizerTheme customizer custom control 400
    In the last article, we explored the advanced controls available in the Theme Customizer, and how to implement them. We’re going to look at how to create our own custom control, allowing you to choose which Category of Posts are displayed on the home page. To get started, download version 0.6.0 of our Theme Customizer Example.Read More…
  • Code
    Theme Development
    Creating a WordPress Theme from Static HTML: Creating an Archive TemplateCreating wordpress theme from html 400
    If you've been working your way through this series, you now have a functioning theme with two page templates. The steps I've demonstrated to this point are: preparing your markup for WordPress converting your HTML to PHP and splitting your file into template files editing the stylesheet and uploading your theme to WordPress adding a loop to your index file adding meta tags, the wp_head hook and the site title and description to your header file adding a navigation menu adding widget areas to the header and sidebar adding widget areas, a colophon and the wp_footer hook to the footer file creating template files for static pages. Read More…
  • Code
    Theme Development
    Creating a WordPress Theme from Static HTML: Adding NavigationCreating wordpress theme from html 400
    If you've been working through this series, you now have a working theme with template files which you've uploaded to WordPress. In this tutorial, you'll continue to work on the header.php file that you created in Part 2. You'll learn how to add a navigation menu which can be edited via the WordPress Menus admin screen. To do this you'll also need to create a new file for your theme: the functions file.Read More…
  • Code
    Plugins
    Using HighCharts in WP-AdminHighcharts 400
    Charts are a great way to present data. They make data more digestible by making it visually appealing. In WordPress, there is no built-in method for getting posts and pages data in a graphical form. Although, there are certain plugins available which integrate Google Analytics with WordPress, but they are overkill if you want to get only a portion of that data. Also, nothing should keep you from learning new techniques and to dive straight into the subject is the best way to learn.Read More…
  • Code
    Theme Development
    The Complete Guide To The WordPress Settings API, Part 3: All About MenusWp api part3
    At this point, we've learned why the Settings API matters, we've learned all about sections, fields, and settings, and we've even setup a Sandbox Theme that we're using to develop functionality as we explore the API. In this article, we're going to take a look at how we can interface with the WordPress menu system. It's important to note that this isn't actually a part of the Settings API, but it's so closely related that we should familiarize ourselves with how to use it. As our themes and/or plugins become more complex, we need to know all of the various ways that we can include them into the WordPress Dashboard. In this article, we're going to be taking a look at the various WordPress menu functions, when to use them, when to avoid them, and the situations to which each function lends itself best.Read More…