Using the Settings API – Part 2: Create A Top Level Admin Menu

Using the Settings API – Part 2: Create A Top Level Admin Menu

Tutorial Details
  • Program: WordPress
  • Version (if applicable): 3.0+
  • Difficulty: Intermediate
  • Estimated Completion Time: 45 minutes+
This entry is part 2 of 2 in the series Using the Settings API to Create a Theme Options Page

This is Part Two of “Create Theme Settings Pages that use the Settings API” tutorial. In this part, we will be looking into how we can create a top level admin menu with more than one settings pages as well as how we can add tabs when we need them. If you have not yet had the chance to read Part One, you may want to do that first before you continue. This tutorial builds on the code shown in the first part of this tutorial.


A look at what we will be creating

Just like we did in Part One lets take a look at how the finished result looks like. We will be using the twentyeleven WordPress 3.2 default theme again however feel free to use your own theme if you prefer.

  1. Download the Source files and unzip
  2. Find the Part Two/source_files/lib folder and upload it inside the twentyeleven theme folder so that it is on the same level as the twentyeleven/js folder you see.
  3. Then, open the Part Two/source_files/functions.php in a code editor and copy the require_once code line.
  4. Next, open the twentyeleven/functions.php in your code editor.
    Find the twentyeleven_setup() function around line 74 and paste the line you copied earlier (point 3) inside the function as you see shown below.
    This will replace the previous require_once() line of code you added in Part One.
function twentyeleven_setup() {

	//require only in admin!
	if(is_admin()){	
		require_once('lib/wptuts-theme-settings-advanced.php');
	}

Upon completing this step you should be able to see the Wptuts Settings top-level menu in your Admin area. You will notice the two menu links: Options Page One and Options Page Two. Options Page One is exactly the same settings page you created in Part One so go directly to Options Page Two Following the Wptuts Settings Page Two title you see four tabs with their individual settings fields. To keep things simple and help you concentrate on the code required to create multiple pages and tabs you will notice that the settings are the same as those you see on Options Page One.

Now that you see what the finished result looks like, let’s learn how we can re-create it step by step.


“The final code described in this step is found in the Part Two/source_ files/step1 folder”

Step 1 Registering the administration pages

This step is about creating a top level admin menu and registering two settings pages.

We will be using the same my-theme-settings.php document you were working with previously in Part One so go ahead and open that file in your code editor.

Register the top level menu

We will edit the existing wptuts_add_menu() function by adding a call to the add_menu_page(). We will add this right after we collect our contextual help like you see done below.


/*
 * The admin menu pages
 */
function wptuts_add_menu(){
	
	$settings_output 		= wptuts_get_settings();
	// collect our contextual help text
	$wptuts_contextual_help = $settings_output['wptuts_contextual_help'];
	
	// As a "top level" menu
	// add_menu_page( $page_title, $menu_title, $capability, $menu_slug, $function, $icon_url, $position );
	add_menu_page( __('Wptuts Settings'), __('Wptuts Settings','wptuts_textdomain'), 'manage_options', WPTUTS_PAGE_BASENAME, 'wptuts_settings_page_fn');
	
	// old code follows below
}

Understand the add_menu_page() parameters

Take note of the add_menu_page() function parameters so you can later on customize the function call to your needs. The information is taken from the Codex page:

  • $page_title – The text to be displayed in the title tag of the page when the menu is selected
  • $menu_title – The text to be used for the menu
  • $capability – The capability required for this menu to be displayed to the user.
  • $menu_slug – The slug name to refer to this menu by (should be unique for this menu). (Note the use of our WPTUTS_PAGE_BASENAME constant!)
  • $function – The callback function to output the content for this page.
  • $icon_url – The url to the icon to be used for this menu. This parameter is optional. (We did not include this paramenter)
  • $position – The position in the menu order this menu should appear. (We did not include this paramenter)

Register the pages

Next, we will replace the existing add_theme_page() call with the add_submenu_page() function to register our first settings page. Then, we will register a second page as you see shown below.

/*
 * The admin menu pages
 */
function wptuts_add_menu(){
	
	$settings_output 		= wptuts_get_settings();
	// collect our contextual help text
	$wptuts_contextual_help = $settings_output['wptuts_contextual_help'];
	
	// As a "top level" menu
	// add_menu_page( $page_title, $menu_title, $capability, $menu_slug, $function, $icon_url, $position );
	add_menu_page( __('Wptuts Settings'), __('Wptuts Settings','wptuts_textdomain'), 'manage_options', WPTUTS_PAGE_BASENAME, 'wptuts_settings_page_fn');
	
	// page one
	// add_submenu_page( $parent_slug, $page_title, $menu_title, $capability, $menu_slug, $function );
	$wptuts_settings_page = add_submenu_page(WPTUTS_PAGE_BASENAME, __('Wptuts Settings | Options'), __('Options Page One','wptuts_textdomain'), 'manage_options', WPTUTS_PAGE_BASENAME, 'wptuts_settings_page_fn');
		// contextual help
		if ($wptuts_settings_page) {
			add_contextual_help( $wptuts_settings_page, $wptuts_contextual_help );
		}
		// css & js
		add_action( 'load-'. $wptuts_settings_page, 'wptuts_settings_scripts' );
		
	// page two
	$wptuts_settings_page_two = add_submenu_page(WPTUTS_PAGE_BASENAME, __('Wptuts Settings | Options Page Two', 'wptuts_textdomain'), __('Options Page Two','wptuts_textdomain'), 'manage_options', WPTUTS_PAGE_BASENAME . '-page-two', 'wptuts_settings_page_fn');
		// contextual help
		if ($wptuts_settings_page_two) {
			add_contextual_help( $wptuts_settings_page_two, $wptuts_contextual_help );
		}
		// css & js
		add_action( 'load-'. $wptuts_settings_page_two, 'wptuts_settings_scripts' );
}

Note the variable names used for page two. When you customize this later you may want to use names that are more descriptive of the theme settings they contain. You can repeat the code we used for registering page two as many times as the number of settings pages you want to create. Take care to edit the page titles and variables accordingly. To help you in this, compare the code block for registering page one to the code block used for registering page two. Note which parameters change and which remain constant. Imitate this for any other pages you may want to create.

Understand the add_submenu_page() parameters

Take note of the add_submenu_page() function parameters so you can later on customize the function call to your needs. The information is taken from the Codex page:

  • $page_title – The text to be displayed in the title tag of the page when the menu is selected
  • $menu_title – The text to be used for the menu
  • $capability – The capability required for this menu to be displayed to the user.
  • $menu_slug – The slug name to refer to this menu by (should be unique for this menu). (Note the use of our WPTUTS_PAGE_BASENAME constant!)
  • $function – The callback function to output the content for this page.

Check the result

Do you still have a copy of my-theme-options.php file from Part One in your twentyeleven/lib folder? If you don’t, upload the one (my-theme-options.php) you were working with during the first part of this tutorial or the one you find in Part One/source_files/step7 folder, before you open your WordPress admin in your browser.

If you have followed the above successfully, this is how your settings page should look like at this point.
Note that the page content will be the same for both pages. This is to be expected as we have not yet defined or registered the settings for the second page. This we will do in Steps 2 and 3.


Step 2 Defining the settings for each settings page

“The final code described in this step is found in the Part Two/source_ files/step2 folder”

Now that we have our settings pages in place we need to tell WordPress the settings, sections and fields we want to register and whitelist i.e. sanitize for each individual settings page. You may recall that the wptuts_register_settings() function (located in my-theme-settings.php) does exactly that for one settings page but how do we handle multiple pages?
And what if we want to display a page’s settings in tabs?

The approach we will take is this:
Let WordPress know which page – and when using tabs, which tab – we are currently viewing and based on that register, display and save the settings needed for the current settings page.
Note that when saving the settings, we will save the settings only displayed on the page or tab we currently have open.

Especially those of you familiar with the excellent Incorporating the Settings API in WordPress Themes tutorial by Chip Bennett take extra note of this: The action of saving the settings will require that each page and tab saves it’s settings under it’s own unique option name (as used in a get_option() function call)! This need not necessarily be taken as a negative thing. It is simply a different approach; one that is required because we register, display and save only those settings under the current page or tab. Whether this is the best approach for you to take in a specific project can only be determined by the project at hand and the number of pages/tabs you need to create. So please, do keep an open mind as you read on.

The first thing we need is a couple of new helper functions. One function should be able to return the current settings page and another function the current tab. I’ve decided to keep these in a separate document so let’s create that first.

Prepare a new Document with some helper functions

Create a new document in your code editor and call it wptuts-helper-functions.php. Copy and paste in it the three functions given below then, upload it inside the twentyeleven/lib folder. Each function is explained before the next is given.

The code given below should be written in the wptuts-helper-functions.php file.

/**
 * Helper function: Check for pages and return the current page name
 * 
 * @return string
 */
function wptuts_get_admin_page() {
	global $pagenow;
	
	// read the current page
	$current_page = trim($_GET['page']);
	
	// use a different way to read the current page name when the form submits
	if ($pagenow == 'options.php') {
		// get the page name
		$parts 	= explode('page=', $_POST['_wp_http_referer']); // http://codex.wordpress.org/Function_Reference/wp_referer_field
		$page  	= $parts[1]; 

		// account for the use of tabs (we do not want the tab name to be part of our return value!)
		$t 		= strpos($page,"&");
		
		if($t !== FALSE) {			 
			$page  = substr($parts[1],0,$t); 
		}
		
		$current_page = trim($page);
	}
	
return $current_page;

The first helper function will return the current page name. We depend on the $_GET superglobal.
The variable will return the page name we are currently viewing – e.g. wptuts-settings as shown in the ?page=wptuts-settings part of the page URL.

However, when we save the settings our $_GET variable will not be able to return the settings page name we expect (it will actually return a NULL value).

Why? Well, when we save our settings WordPress will send a POST request to options.php and at that moment we no longer have ?page=wptuts-settings as part of our page URL.
What now? WordPress includes the page name in the $_POST['_wp_http_referer'] variable.
Using the $pagenow WordPress global we can check when options.php is called and return the value stored in $_POST['_wp_http_referer']

/**
 * Helper function: Set default tab
 * 
 * @return string
 */
function wptuts_default_tab() {
	// find current page
	$current_page = wptuts_get_admin_page();
	
	// There may be times when the first tab has a different slug from page to page. Here is where you can override the $default_tab = 'general'; Gives you more control :)
	// if our current page is the 'wptuts-settings-page-two' page then set the default tab to 'text-inputs'
	if ($current_page == 'wptuts-settings-page-two') {
		$default_tab = 'text-inputs';
		
	// if you have more settings pages with a first tab slug other than "general" continue with an 
	//}elseif($current_page == 'your-page-slug'){ 
	//conditional here.
		
	// else fallback to the "general" tab.
	} else {
		$default_tab = 'general';
	}
return $default_tab;
}

This second helper function returns the default tab name. It it useful when your default tab slug is not always called general. To customize the function to your needs later, please take a moment to read through the comments left in the code.

/**
 * Helper function: Check for tabs and return the current tab name
 * 
 * @return string
 */
function wptuts_get_the_tab() {
	global $pagenow;
	
	// set default tab
	$default_tab 	= wptuts_default_tab();
	
	// read the current tab when on our settings page
	$current_tab 	= (isset($_GET['tab']) ? $_GET['tab'] : $default_tab);
	
	//use a different way to read the tab when the form submits
	if ($pagenow == 'options.php') {
		// need to read the tab name so we explode()!
		$parts 	= explode('&tab=', $_POST['_wp_http_referer']); // http://codex.wordpress.org/Function_Reference/wp_referer_field
		// count the "exploded" parts
		$partsNum = count($parts);
		
		// account for "&settings-updated=true" (we do not want that to be part of our return value!)
			// is it "&settings-updated=true" there? - check for the "&"
			$settings_updated = strpos($parts[1],"&");
			
			// filter it out and get the tab name
			$tab_name = ($settings_updated !== FALSE ? substr($parts[1],0,$settings_updated) : $parts[1]);
		
		// use if found, otherwise pass the default tab name
		$current_tab = ($partsNum == 2 ? trim($tab_name) : $default_tab);
	}
	
return $current_tab
} 

The third helper function returns the current tab name.
Similar to what we did in the wptuts_get_admin_page() function we use the $_GET superglobal for reading the current tab name when the settings display and the $_POST['_wp_http_referer'] variable for reading the tab name when the settings save.

Remember to save the three functions shown above in the wptuts-helper-functions.php and upload it inside the twentyeleven/lib folder.

Define those settings

Time to define page tabs, settings sections and fields and contextual help for each settings page. We actually have the settings needed for “Options Page One” already from Part One of this tutorial. The sections, fields and contextual help fuctions are defined in my-theme-options.php and we will leave those as they are.
What we will do now is define settings for “Options Page Two”.

The “Options Page Two” settings page will devide it’s settings into tabs. Create a new document called my-theme-options-two.php then copy and paste in it the functions given below. Each function is explained after the code is given.

Define the page tabs

/**
 * Define page tabs
 * $tabs['tab-slug'] 	= __('Tab Name', 'wpShop');
 */
function wptuts_options_two_page_tabs() {
	
	$tabs = array();
	
	$tabs['text-inputs'] 	= __('Text Inputs', 'wpShop');	
	$tabs['textareas'] 		= __('Textareas', 'wpShop');	
	$tabs['select'] 		= __('Select', 'wpShop');	
	$tabs['checkboxes'] 	= __('Checkboxes', 'wpShop');
	
	return $tabs;
}

The tab array should have as value, the tab name as it will appear on each tab and as key, the tab slug as it will appear as part of the page URL as in &tab=tab-name

Define settings sections for each tab

/**
 * Define our settings sections
 *
 * array key=$id, array value=$title in: add_settings_section( $id, $title, $callback, $page );
 * @return array
 */
function wptuts_options_two_page_sections() {
	// we change the output based on open tab
	
	// get the current tab
	$tab = wptuts_get_the_tab();

	// switch sections array according to tab
	switch ($tab) {
		// Text Inputs
		case 'text-inputs':
			$sections = array();
			$sections['txt_section'] 		= __('Text Form Fields', 'wptuts_textdomain');
		break;
		
		// Textareas
        case 'textareas':
			$sections = array();
			$sections['txtarea_section'] 	= __('Textarea Form Fields', 'wptuts_textdomain');
		break;
		
		// Select
        case 'select':
			$sections = array();
			$sections['select_section'] 	= __('Select Form Fields', 'wptuts_textdomain');
		break;
		
		// Checkboxes
        case 'checkboxes':
			$sections = array();
			$sections['checkbox_section'] 	= __('Checkbox Form Fields', 'wptuts_textdomain');
		break;
	}
	
return $sections;	
}

The sections array is much the same as we defined it for “Options Page One” – my-theme-options.php – in that it returns an associative array where the value, is the section title and the key, the section id. However, the sections array output changes according to the current open tab. Note the tab switch.

// get the current tab
$tab = wptuts_get_the_tab();

// switch sections array according to tab
	switch ($tab) {
	// Text Inputs
	case 'text-inputs':
		
	break;
	
	// Textareas
	case 'textareas':
	
	break;
	
	// Select
	case 'select':
	
	break;
	
	// Checkboxes
	case 'checkboxes':
	
	break;
}

Each case is the tab slug – the array key from our wptuts_options_two_page_tabs() function. When you customize this function later you will need to create a new case for each tab you define.

The wptuts_get_the_tab() function you see called before the switch was defined in our wptuts-helper-functions.php. It helps us “read” the current tab name – the &tab=tab-name part of the page URL and passes that on to the $tab variable.

// get the current tab
$tab = wptuts_get_the_tab();

Define settings fields for each tab

/**
 * Define our form fields (options) 
 *
 * @return array
 */
function wptuts_options_two_page_fields() {
	
	// get the tab using wptuts_get_the_tab() for we need options.php to be able to process the form submission!
	$tab = wptuts_get_the_tab();
	
	// setting fields according to tab
	switch ($tab) {
		// Text Inputs
		case 'text-inputs':
			$options[] = array(
				"section" => "txt_section",
				"id"      => WPTUTS_SHORTNAME . "_txt_input_2",
				"title"   => __( 'Text Input - Some HTML OK!', 'wptuts_textdomain' ),
				"desc"    => __( 'A regular text input field. Some inline HTML (<a>, <b>, <em>, <i>, <strong>) is allowed.', 'wptuts_textdomain' ),
				"type"    => "text",
				"std"     => __('Some default value','wptuts_textdomain')
			);
			
			$options[] = array(
				"section" => "txt_section",
				"id"      => WPTUTS_SHORTNAME . "_nohtml_txt_input_2",
				"title"   => __( 'No HTML!', 'wptuts_textdomain' ),
				"desc"    => __( 'A text input field where no html input is allowed.', 'wptuts_textdomain' ),
				"type"    => "text",
				"std"     => __('Some default value','wptuts_textdomain'),
				"class"   => "nohtml"
			);
			
			$options[] = array(
				"section" => "txt_section",
				"id"      => WPTUTS_SHORTNAME . "_numeric_txt_input_2",
				"title"   => __( 'Numeric Input', 'wptuts_textdomain' ),
				"desc"    => __( 'A text input field where only numeric input is allowed.', 'wptuts_textdomain' ),
				"type"    => "text",
				"std"     => "123",
				"class"   => "numeric"
			);
			
			$options[] = array(
				"section" => "txt_section",
				"id"      => WPTUTS_SHORTNAME . "_multinumeric_txt_input_2",
				"title"   => __( 'Multinumeric Input', 'wptuts_textdomain' ),
				"desc"    => __( 'A text input field where only multible numeric input (i.e. comma separated numeric values) is allowed.', 'wptuts_textdomain' ),
				"type"    => "text",
				"std"     => "123,234,345",
				"class"   => "multinumeric"
			);
			
			$options[] = array(
				"section" => "txt_section",
				"id"      => WPTUTS_SHORTNAME . "_url_txt_input_2",
				"title"   => __( 'URL Input', 'wptuts_textdomain' ),
				"desc"    => __( 'A text input field which can be used for urls.', 'wptuts_textdomain' ),
				"type"    => "text",
				"std"     => "http://wp.tutsplus.com",
				"class"   => "url"
			);
			
			$options[] = array(
				"section" => "txt_section",
				"id"      => WPTUTS_SHORTNAME . "_email_txt_input_2",
				"title"   => __( 'Email Input', 'wptuts_textdomain' ),
				"desc"    => __( 'A text input field which can be used for email input.', 'wptuts_textdomain' ),
				"type"    => "text",
				"std"     => "email@email.com",
				"class"   => "email"
			);
			
			$options[] = array(
				"section" => "txt_section",
				"id"      => WPTUTS_SHORTNAME . "_multi_txt_input_2",
				"title"   => __( 'Multi-Text Inputs', 'wptuts_textdomain' ),
				"desc"    => __( 'A group of text input fields', 'wptuts_textdomain' ),
				"type"    => "multi-text",
				"choices" => array( __('Text input 1','wptuts_textdomain') . "|txt_input1", __('Text input 2','wptuts_textdomain') . "|txt_input2", __('Text input 3','wptuts_textdomain') . "|txt_input3", __('Text input 4','wptuts_textdomain') . "|txt_input4"),
				"std"     => ""
			);
		break;
		
		// Textareas
		case 'textareas':
			$options[] = array(
				"section" => "txtarea_section",
				"id"      => WPTUTS_SHORTNAME . "_txtarea_input_2",
				"title"   => __( 'Textarea - HTML OK!', 'wptuts_textdomain' ),
				"desc"    => __( 'A textarea for a block of text. HTML tags allowed!', 'wptuts_textdomain' ),
				"type"    => "textarea",
				"std"     => __('Some default value','wptuts_textdomain')
			);

			$options[] = array(
				"section" => "txtarea_section",
				"id"      => WPTUTS_SHORTNAME . "_nohtml_txtarea_input_2",
				"title"   => __( 'No HTML!', 'wptuts_textdomain' ),
				"desc"    => __( 'A textarea for a block of text. No HTML!', 'wptuts_textdomain' ),
				"type"    => "textarea",
				"std"     => __('Some default value','wptuts_textdomain'),
				"class"   => "nohtml"
			);
			
			$options[] = array(
				"section" => "txtarea_section",
				"id"      => WPTUTS_SHORTNAME . "_allowlinebreaks_txtarea_input_2",
				"title"   => __( 'No HTML! Line breaks OK!', 'wptuts_textdomain' ),
				"desc"    => __( 'No HTML! Line breaks allowed!', 'wptuts_textdomain' ),
				"type"    => "textarea",
				"std"     => __('Some default value','wptuts_textdomain'),
				"class"   => "allowlinebreaks"
			);

			$options[] = array(
				"section" => "txtarea_section",
				"id"      => WPTUTS_SHORTNAME . "_inlinehtml_txtarea_input_2",
				"title"   => __( 'Some inline HTML ONLY!', 'wptuts_textdomain' ),
				"desc"    => __( 'A textarea for a block of text. 
							Only some inline HTML 
							(<a>, <b>, <em>, <strong>, <abbr>, <acronym>, <blockquote>, <cite>, <code>, <del>, <q>, <strike>)  
							is allowed!', 'wptuts_textdomain' ),
				"type"    => "textarea",
				"std"     => __('Some default value','wptuts_textdomain'),
				"class"   => "inlinehtml"
			);
		break;
		
		// Select
		case 'select':
			$options[] = array(
				"section" => "select_section",
				"id"      => WPTUTS_SHORTNAME . "_select_input_2",
				"title"   => __( 'Select (type one)', 'wptuts_textdomain' ),
				"desc"    => __( 'A regular select form field', 'wptuts_textdomain' ),
				"type"    => "select",
				"std"    => "3",
				"choices" => array( "1", "2", "3")
			);
			
			$options[] = array(
				"section" => "select_section",
				"id"      => WPTUTS_SHORTNAME . "_select2_input_2",
				"title"   => __( 'Select (type two)', 'wptuts_textdomain' ),
				"desc"    => __( 'A select field with a label for the option and a corresponding value.', 'wptuts_textdomain' ),
				"type"    => "select2",
				"std"    => "",
				"choices" => array( __('Option 1','wptuts_textdomain') . "|opt1", __('Option 2','wptuts_textdomain') . "|opt2", __('Option 3','wptuts_textdomain') . "|opt3", __('Option 4','wptuts_textdomain') . "|opt4")
			);
		break;
		
		// Checkboxes
		case 'checkboxes':
			$options[] = array(
				"section" => "checkbox_section",
				"id"      => WPTUTS_SHORTNAME . "_checkbox_input_2",
				"title"   => __( 'Checkbox', 'wptuts_textdomain' ),
				"desc"    => __( 'Some Description', 'wptuts_textdomain' ),
				"type"    => "checkbox",
				"std"     => 1 // 0 for off
			);
			
			$options[] = array(
				"section" => "checkbox_section",
				"id"      => WPTUTS_SHORTNAME . "_multicheckbox_inputs_2",
				"title"   => __( 'Multi-Checkbox', 'wptuts_textdomain' ),
				"desc"    => __( 'Some Description', 'wptuts_textdomain' ),
				"type"    => "multi-checkbox",
				"std"     => 0,
				"choices" => array( __('Checkbox 1','wptuts_textdomain') . "|chckbx1", __('Checkbox 2','wptuts_textdomain') . "|chckbx2", __('Checkbox 3','wptuts_textdomain') . "|chckbx3", __('Checkbox 4','wptuts_textdomain') . "|chckbx4")	
			);
		break;
	}
	
	return $options;	
}

The fields array is much same as we defined it for “Options Page One”. You will notice that I even left the $options[] = array() arguments the same with the option id the only exception (I simply added a _2) The important point to take home here is that the fields array output changes according to the current open tab like we did in our wptuts_options_two_page_sections() function. Note the tab switch.

// get the current tab
$tab = wptuts_get_the_tab();

// setting fields according to tab
	switch ($tab) {
	// Text Inputs
	case 'text-inputs':
		
	break;
	
	// Textareas
	case 'textareas':
	
	break;
	
	// Select
	case 'select':
	
	break;
	
	// Checkboxes
	case 'checkboxes':
	
	break;
}

Each case is the tab slug – the array keys from our wptuts_options_two_page_tabs() function. When you customize this function later you will need to create a new case for each tab you define.

Again, you see the wptuts_get_the_tab() function called before the switch. It helps us “read” the current tab name – the &tab=tab-name part of the page URL and passes that on to the $tab variable.

// get the current tab
$tab = wptuts_get_the_tab();

Define contextual help for each tab

Following the same pattern of switching the array output according to the current open tab, we define each tab’s contextual help.

/**
 * Contextual Help
 */
function wptuts_options_two_page_contextual_help() {
	// get the current tab
	$tab = wptuts_get_the_tab();
	
	$text 	= "<h3>" . __('Wptuts Settings Page Two - Contextual Help','wptuts_textdomain') . "</h3>";
	
	// contextual help according to tab
	switch ($tab) {
		// Text Inputs
		case 'text-inputs':
			$text 	.= "<p>" . __('Contextual help for the "Text Input" settings fields goes here.','wptuts_textdomain') . "</p>";
		break;
		
		// Textareas
		case 'textareas':
			$text 	.= "<p>" . __('Contextual help for the "Textarea" settings fields goes here.','wptuts_textdomain') . "</p>";
		break;
		
		// Select
		case 'select':
			$text 	.= "<p>" . __('Contextual help for the "Select" settings fields goes here.','wptuts_textdomain') . "</p>";
		break;
		
		// Checkboxes
		case 'checkboxes':
			$text 	.= "<p>" . __('Contextual help for the "Checkboxes" settings fields goes here.','wptuts_textdomain') . "</p>";
		break;
	}
	
	// must return text! NOT echo
	return $text;
} 

All the settings code given above should be now written in your my-theme-options-two.php file. Save it and upload it inside the twentyeleven/lib folder


Step 3 Registering the tabs and settings for each settings page

“The final code described in this step is found in the Part Two/source_ files/step3 folder”

Now that our page settings are all nicely defined, let’s tell WordPress to display them.

Include the new Documents we created in Step 2

We want to use our new helper functions and settings in our my-theme-settings.php file so let’s include the new wptuts-helper-functions.php and my-theme-options-two.php files. Find the existing require_once('my-theme-options.php'); code line and adjust as you see below.

The code given below should be written in the my-theme-settings.php file.

/**
 * Include the required files
 */
// helper functions
require_once('wptuts-helper-functions.php');
// page settings sections & fields as well as the contextual help text.
require_once('wptuts-theme-options.php');
require_once('wptuts-theme-options-two.php');

Adjust the wptuts_get_settings() function

Time to adjust the existing wptuts_get_settings() helper function to retrieve our settings based on the page or tab we currently have open.

/**
 * Helper function for defining variables for the current page
 *
 * @return array
 */
function wptuts_get_settings() {
	
	$output = array();
	
	/*PAGES*/
	// get current page
	$page = wptuts_get_admin_page();
	
	/*TABS*/
	// get current tab
	$tab = wptuts_get_the_tab();
	
	/*DEFINE VARS*/
	// define variables according to registered admin menu page
	switch ($page) {
		case WPTUTS_PAGE_BASENAME:
			$wptuts_option_name 		= 'wptuts_options';
			$wptuts_settings_page_title = __( 'Wptuts Settings Page','wptuts_textdomain');	
			$wptuts_page_sections 		= wptuts_options_page_sections();
			$wptuts_page_fields 		= wptuts_options_page_fields();
			$wptuts_contextual_help 	= wptuts_options_page_contextual_help();
			$wptuts_page_tabs			= '';
		break;
	
		case WPTUTS_PAGE_BASENAME . '-page-two':
			$wptuts_option_name 		= 'wptuts_options_two';
			$wptuts_settings_page_title = __( 'Wptuts Settings Page Two','wptuts_textdomain');
			$wptuts_page_sections 		= wptuts_options_two_page_sections();
			$wptuts_page_fields 		= wptuts_options_two_page_fields();
			$wptuts_contextual_help 	= wptuts_options_two_page_contextual_help();
			$wptuts_page_tabs 			= wptuts_options_two_page_tabs();
			
			// define a new option name according to tab
			switch ($tab) {
				// Text Inputs
				case 'text-inputs':
					$wptuts_option_name = $wptuts_option_name . '_text_inputs';
				break;
				
				// Textareas
				case 'textareas':
					$wptuts_option_name = $wptuts_option_name . '_textareas';
				break;
				
				// Select
				case 'select':
					$wptuts_option_name = $wptuts_option_name . '_select';
				break;
				
				// Checkboxes
				case 'checkboxes':
					$wptuts_option_name = $wptuts_option_name . '_checkboxes';
				break;
			}
		break;
	}
	
	// put together the output array 
	$output['wptuts_option_name'] 		= $wptuts_option_name;
	$output['wptuts_page_title'] 		= $wptuts_settings_page_title;
	$output['wptuts_page_tabs']			= $wptuts_page_tabs;
	$output['wptuts_page_sections'] 	= $wptuts_page_sections;
	$output['wptuts_page_fields'] 		= $wptuts_page_fields;
	$output['wptuts_contextual_help'] 	= $wptuts_contextual_help;
	
	
return $output;
}

Breaking-down the code

We fill our $output array with the option name, page title, page tabs, settings sections, settings fields and contextual help. The values of the six array keys change based on the settings page or tab we currently have open.

First we have a page switch

switch ($page) {
	case WPTUTS_PAGE_BASENAME:
		
	break;

	case WPTUTS_PAGE_BASENAME . '-page-two':
		
	break;
}

You will need to create a new case for each settings page you register in the wptuts_add_menu() function. (Note that the case must match the $menu_slug parameter in the add_submenu_page() function call.)

Each case defines the option name and page title with the appropriate string value. The page tabs, settings sections, settings fields and contextual help variables are defined by calling the appropriate functions that give the corresponding output. (You recall that the functions are defined in a separate document – my-theme-options.php for “Options Page One” and my-theme-options-two.php for “Options Page Two”.)

Should a particular settings page not need any tabs then the $wptuts_page_tabs variable is left empty. This is done for example for “Options Page One”

case WPTUTS_PAGE_BASENAME:
	$wptuts_option_name 		= 'wptuts_options';
	$wptuts_settings_page_title = __( 'Wptuts Settings Page','wptuts_textdomain');	
	$wptuts_page_sections 		= wptuts_options_page_sections();
	$wptuts_page_fields 		= wptuts_options_page_fields();
	$wptuts_contextual_help 	= wptuts_options_page_contextual_help();
	$wptuts_page_tabs			= '';
break;

Finally we have a tab switch

// define a new option name according to tab
switch ($tab) {
	// Text Inputs
	case 'text-inputs':
		$wptuts_option_name = $wptuts_option_name . '_text_inputs';
	break;
	
	// Textareas
	case 'textareas':
		$wptuts_option_name = $wptuts_option_name . '_textareas';
	break;
	
	// Select
	case 'select':
		$wptuts_option_name = $wptuts_option_name . '_select';
	break;
	
	// Checkboxes
	case 'checkboxes':
		$wptuts_option_name = $wptuts_option_name . '_checkboxes';
	break;
}

This is only needed on settings pages that we want to use tabs. You will need to create a new case for each tab you register. (Note that the case must match the tab-slug in your tab array key.) Each case defines a new option name with the appropriate string value.

Adjust the page content output function

Finally for this step, we need to make a little adjustment to our wptuts_settings_page_fn()() function so that it displays our tabs when these are defined. Replace the existing function with the following:

/*
 * Admin Settings Page HTML
 * 
 * @return echoes output
 */
function wptuts_settings_page_fn() {
	// get the settings sections array
	$settings_output = wptuts_get_settings();
?>
	<div class="wrap">
		<?php 
		// dislays the page title and tabs (if needed)
		wptuts_settings_page_header(); 
		?>
		
		<form action="options.php" method="post">
			<?php 
			// http://codex.wordpress.org/Function_Reference/settings_fields
			settings_fields($settings_output['wptuts_option_name']); 
			// http://codex.wordpress.org/Function_Reference/do_settings_sections
			do_settings_sections(__FILE__); 
			?>
			
			<p class="submit">
				<input name="Submit" type="submit" class="button-primary" value="<?php esc_attr_e('Save Changes','wptuts_textdomain'); ?>" />
			</p>
			
		</form>
	</div>
<?php }

We call a new function called wptuts_settings_page_header(). Let’s go ahead and define this in our wptuts-helper-functions.php file. Copy and paste the following after the wptuts_get_the_tab() function.

/**
 * Helper function: Creates settings page title and tabs (if needed)
 *
 * @return echos output
 */
function wptuts_settings_page_header() {
	
    // get the tabs
    $settings_output 	= wptuts_get_settings();
	$tabs 				= $settings_output['wptuts_page_tabs'];
	
	// get the current tab
	$current_tab 		= wptuts_get_the_tab();
	
	// display the icon and page title
	echo '<div id="icon-options-general" class="icon32"><br /></div>';
	echo '<h2>' . $settings_output['wptuts_page_title'] . '</h2>';
    
	// check for tabs
	if ($tabs !='') {
		// wrap each in anchor html tags
		$links = array();
		foreach( $tabs as $tab => $name ) {
			// set anchor class
			$class 		= ($tab == $current_tab ? 'nav-tab nav-tab-active' : 'nav-tab');
			$page 		= $_GET['page'];
			// the link
			$links[] 	= "<a class='$class' href='?page=$page&tab=$tab'>$name</a>";
		}
		
		echo '<h3 class="nav-tab-wrapper">';
			foreach ( $links as $link ) {
				echo $link;
			}
		echo '</h3>';
	}  
}

Breaking-down the code

We collect our tabs by calling the wptuts_get_settings() helper function.

// get the tabs
$settings_output 	= wptuts_get_settings();
$tabs 				= $settings_output['wptuts_page_tabs'];

Then, we get the current tab with the wptuts_get_the_tab() helper function.

// get the current tab
$current_tab 		= wptuts_get_the_tab();

We echo the icon and page title.

// display the icon and page title
echo '<div id="icon-options-general" class="icon32"><br /></div>';
echo '<h2>' . $settings_output['wptuts_page_title'] . '</h2>';

If we have tabs defined, we display them too.

// check for tabs
if ($tabs !='') {
	// wrap each in anchor html tags
	$links = array();
	foreach( $tabs as $tab => $name ) {
		// set anchor class
		$class 		= ($tab == $current_tab ? 'nav-tab nav-tab-active' : 'nav-tab');
		$page 		= $_GET['page'];
		// the link
		$links[] 	= "<a class='$class' href='?page=$page&tab=$tab'>$name</a>";
	}
	
	echo '<h3 class="nav-tab-wrapper">';
		foreach ( $links as $link ) {
			echo $link;
		}
	echo '</h3>';
}

The tab code should be familiar to those of you who went through the
Incorporating the Settings API in WordPress Themes tutorial by Chip Bennett

Check the result

If you have followed the above successfully, your “Options Page Two” should display it’s tabs like you see below.


Collecting the theme options together for use in our theme

The last thing we need to do before we conclude Part Two of this tutorial is to collect together the theme options we saved for each settings page and tab in our $wptuts_option variable. To do this we need to adjust the wptuts_get_global_options() function we wrote in twentyeleven/functions.php in Part One

/**
 * Collects our theme options
 *
 * @return array
 */
function wptuts_get_global_options(){
	
	$wptuts_option = array();

	// collect option names as declared in wptuts_get_settings()
	$wptuts_option_names = array (
		'wptuts_options', 
		'wptuts_options_two_text_inputs', 
		'wptuts_options_two_textareas', 
		'wptuts_options_two_select', 
		'wptuts_options_two_checkboxes'	
	);

	// loop for get_option
	foreach ($wptuts_option_names as $wptuts_option_name) {
		if (get_option($wptuts_option_name)!= FALSE) {
			$option 	= get_option($wptuts_option_name);
			
			// now merge in main $wptuts_option array!
			$wptuts_option = array_merge($wptuts_option, $option);
		}
	}	
	
return $wptuts_option;
}

Breaking-down the code

We take each option name we defined per page and tab.

.

// collect option names as declared in wptuts_get_settings()
$wptuts_option_names = array (
	'wptuts_options', 
	'wptuts_options_two_text_inputs', 
	'wptuts_options_two_textareas', 
	'wptuts_options_two_select', 
	'wptuts_options_two_checkboxes'	
);

We then call the get_option() function for each and merge them in one big array which we then output

.

// loop for get_option
	foreach ($wptuts_option_names as $wptuts_option_name) {
		if (get_option($wptuts_option_name)!= FALSE) {
			$option 	= get_option($wptuts_option_name);
			
			// now merge in main $wptuts_option array!
			$wptuts_option = array_merge($wptuts_option, $option);
		}
	}

As mentioned in Part One, echo a particular option in any of your theme templates like this: <?php echo $wptuts_option['wptuts_txt_input']; ?> – the value in the brackets is the id of the option you want to display.

This concludes Part Two of our tutorial. Hope you enjoyed reading it!


Other parts in this series:Using The Settings API: Part 1 – Create A Theme Options Page
Note: Want to add some source code? Type <pre><code> before it and </code></pre> after it. Find out more
  • http://libertyintruth.org Lucas

    Fantastic tutorial! Great update for the newer WP versions!

  • http://danielWesten.com Daniel

    Thank you for the step by step instructions

    • http://www.sarah-neuber.de Sarah
      Author

      You are welcome :)

  • http://profitableintent.com Bobby

    Wow thank you so much for these tutorials. I went through one on Nettuts+ but this one looks more in depth.

  • Jaun

    Will be a part 3?
    If not can you consider making an upload image tutorial for this?
    Thanks!

  • NIKI

    Nice TUT!

    dynamic logo selection with upload would be great ^^

    Thanks

    • http://www.sarah-neuber.de Sarah
      Author

      @ Niki & Jaun, indeed! But making an enitre part 3 tutorial for just an image upload maybe an overkill.

      If you come up with some more suggestions I will definitely think about it :)

      • Peter

        A part for code/script fields would be great! eg. google analytics, adsense…

      • Peter

        In debug-mode WordPress shows “Notice: Undefined index: page in …” on eg. new posting page in admin-area.
        (line in …theme-settings.php” >>> “… = strpos($_GET['page'], …”)

        • Nick

          Hi Peter, Sarash doesn’t seem to provide much support on this tut so I thought I would jump in and explain how to get the script tag working. I have basic to intermediate php skills so I could very well be wrong but it seems to work for me.

          On the validation part of the theme-settings.php (roughly on line 550) you can add to the $allowed_html array…

          Just stick in

          ‘script’ => array(),

          You can take a look at how she structured her array if you want to add divs with classes and ids ect….

        • Daniel

          Hi Peter,

          To get rid of that error message you can replace the following code which can be found around line 591:

          $wptuts_settings_pg = strpos($_GET['page'], WPTUTS_PAGE_BASENAME);

          with the following code if you have the settings page link under the “Appearance” Admin Menu:

          $wptuts_settings_pg = strpos( ‘appearance_page_’ . WPTUTS_PAGE_BASENAME . ‘ ‘, WPTUTS_PAGE_BASENAME);

          or with the following code if you have the settings page link under the “Settings” Admin Menu:

          $wptuts_settings_pg = strpos( ‘settings_page_’ . WPTUTS_PAGE_BASENAME . ‘ ‘, WPTUTS_PAGE_BASENAME);

          or with the following code if you have the settings page link as a Toplevel Admin Menu:

          $wptuts_settings_pg = strpos( ‘toplevel_page_’ . WPTUTS_PAGE_BASENAME . ‘ ‘, WPTUTS_PAGE_BASENAME);

          I hope this helps.

          Daniel

  • Leo

    I love every posts that you guys put up… Help me advance my wp skills a lot. You guys rock…

    Leo

  • Windwalk

    Really nice tutorial!

    Is there going to be part 3? Upload field and color picker meaby? :)

  • Wells

    Oop, I can’t get default option from page two.

  • http://freetemplatez.net/ Free Templates

    Very much overkill… great tut tho…. but all i want to do is add a admin / settings option so that downloaders of my theme can upload a new logo via a settings option.

  • http://ednailor.com Ed Nailor

    For a Part 3, I would suggest expanding this to allow File / Image uploads and Radio buttons. It could be a simple “Couple new items to add” type of post.
    Ed

  • http://www.jacorre.com Josh

    Great tutorial series!

    A suggestion for a continuation of the series would be to provide real life examples of the kinds of fields that would be used on a theme settings page. For example, logo uploads, column settings, featured post with drop down, include/exclude category settings, etc.

  • http://mattabrahams.id.au Matt

    How do you display the info on options page one as tabs?

  • http://claydell.com claydell22

    Great tutorial! Puts me miles ahead in my theming process.

    I don’t want to show an image if optiob field is empty. This works but would like to know if it is good coding. Thanks.

    <?php if ($wptuts_option['wptuts_txt_input']){ ?>
    <li><a href=”<?php echo $wptuts_option['wptuts_txt_input']; ?>” class=”" title=”" target=”new”><img src=”<?php echo get_template_directory_uri(); ?>/images/sample.png” alt=”" /></a></li>
    <?php } ?>

  • http://claydell.com claydell22

    Like Matt… I would like to only use OPTIONS PAGE TWO. Any ideas on how to remove OPTIONS PAGE ONE? Or, How to add tabs to OPTIONS PAGE ONE?

  • ehren

    I cant get this to show in my theme :(

    I have:

    in header.php
    wp_head();
    $wptuts_option = wptuts_get_global_options();

    in index.php
    echo $wptuts_option['wptuts_email'];

    What am I doing wrong?
    Thanks!

  • Rehan

    Great tutorial. Thanks. Can someone please elaborate why echo is not working with header.php , sidebar.php ? It is working with page.php …

    • Rehan

      Plus, it’ll be great to see how to style the admin menu, because it is not possible to style it by adding classes in css file. How to change the color/size of $title.or $disc.etc. ?

  • http://www.parsonsprojects.co.uk/ Alan

    Great Tutorial
    I think showing how an upload form would work within this would be of great help also maybe how to add something like this
    http://marketplace.tutsplus.com/item/create-a-universal-slider-manager-in-wordpress/143854
    into the code would be beneficial.
    One other note, i think that add_contextual_help has been replaced by add_help_tab now which allows for several help tabs per page now.
    Thanks for the tutorials look foward to more. :)

  • http://www.flintandtinder.co.uk Adam

    Does anyone else experience the issue that any data in input into the fields on my local database doesn’t transfer over to the live site when exported?

    If so, is there anything I can do to solve it? Having to re-input the data in the options fields each time I update the database is an incredible pain in the…

    Thanks.

  • http://www.flintandtinder.co.uk Adam

    One other thing…

    I need one of the options textareas to handle google maps code, but when I paste it in, it seems to strip out the … part of the code. Does anyone know why this is and is there anything I can do to solve it?

  • http://www.flintandtinder.co.uk Adam

    Oops keep forgetting this site strips out code. Section in post above should read:

    strip out the <iframe>etc.</iframe>

  • John

    Sarah you’ve done a Great tutorial! priceless, Ive been working for a month now just doing this kind of options and i stumble to this page. Is it possible if i could use your code and modify it in my theme projects?

    Thanks
    John

  • Barbara

    Does anyone know if this can be modified to create a page that updates Existing settings in wp-options?

    Working on a “client-friendly” admin modification, and I’m trying to create a panel where the client can update blogname and the timezone setting, without having to go into general settings – as I plan to hide that menu / submenu page.

  • Alvaro

    How could I insert ‘std’ for the Multi-text Field? If I insert a value it gives that value to every field. Is it possible to insert a (different) value for each field? Thanks

  • Narre

    Great article!! But your code generates a lot of errors, do not declare variables inside a switch statement.

  • Narre

    Here are some fixes for debug mode:

    1. For wptuts_get_admin_page function inside wptuts-helper-function.php. On line 10, change:

    // read the current page
    $current_page = trim($_GET['page']);

    with:

    if(isset($_GET['page']))
    { $current_page = trim($_GET['page']); }
    else
    { $current_page = false; }

    2. Add these lines into your wptuts_get_settings function (file: wptuts-theme-settings-advanced.php) right after the open “{”

    $wptuts_option_name =”";
    $wptuts_settings_page_title = “”;
    $wptuts_page_tabs = “”;
    $wptuts_page_sections = “”;
    $wptuts_page_fields = “”;
    $wptuts_contextual_help = “”;

    so you’ll get your function like this:

    function wptuts_get_settings() {
    $wptuts_option_name =”";
    $wptuts_settings_page_title = “”;
    $wptuts_page_tabs = “”;
    $wptuts_page_sections = “”;
    $wptuts_page_fields = “”;
    $wptuts_contextual_help = “”;

  • Narre

    3. Line 688, wptuts_admin_msgs function (file: wptuts-theme-settings-advanced.php). Change:

    $wptuts_settings_pg = strpos($_GET['page'], WPTUTS_PAGE_BASENAME);

    with:

    if(isset($_GET['page']))
    { $wptuts_settings_pg = strpos($_GET['page'], WPTUTS_PAGE_BASENAME); }
    else
    { $wptuts_settings_pg = false; }

  • mohamed elkebir

    hi sarah! thank you for the big great tutorial, i looove it!

    i just want to know how to delete the h3 tag and its content i.e “Text Form Fields” & the p tag “Setting for this section” i hope to get the words from you & thank you for all

  • Knikky224

    Awesome tutorial!! :D I am waiting for the part 3 with upload field (single and multiple images) and color picker as well. Thanks again for showing it in a great detail! :)

  • Debu

    Awesome tutorial! Thanks for such great help. I am facing little problem.

    After implementing changes everything is working fine. But if you click “save setting” option under “Settings” (General, Writing, Reading… etc), the success message (“Settings saved.”) is not displaying. Earlier it was displaying. Same message is displaying correctly for “Wptuts Settings”. Please help me!

  • Debu

    Nice Sarah. You saved my days..:)

    How to I access multidimensional elements ?

    For example to access wptuts_checkbox_input I am using

    How to access value of “chckbx3″ of “wptuts_multicheckbox_inputs”?

    ["wptuts_checkbox_input"]=> int(1)
    ["wptuts_multicheckbox_inputs"]=> array(4) {
    ["chckbx1"]=> string(5) “false”
    ["chckbx2"]=> string(5) “false”
    ["chckbx3"]=> string(5) “false”
    ["chckbx4"]=> string(5) “false” }

    Regards,
    Debu

  • http://rusevdimitar.com/ Dimitar

    As of WordPress 3.3 ‘add_contextual_help( $screen, $content )’ is deprecated. To get rid of the Debugger warnings you need to update the code. In ‘wptuts_add_menu()’ function replace the code bellow:

    // page one
    // add_submenu_page( $parent_slug, $page_title, $menu_title, $capability, $menu_slug, $function );
    $wptuts_settings_page = add_submenu_page(WPTUTS_PAGE_BASENAME, __(‘Wptuts Settings | Options’), __(‘Options Page One’,'wptuts_textdomain’), ‘manage_options’, WPTUTS_PAGE_BASENAME, ‘wptuts_settings_page_fn’);
    // contextual help
    if ($wptuts_settings_page) {
    add_contextual_help( $wptuts_settings_page, $wptuts_contextual_help );
    }
    // css & js
    add_action( ‘load-’. $wptuts_settings_page, ‘wptuts_settings_scripts’ );
    // page two
    $wptuts_settings_page_two = add_submenu_page(WPTUTS_PAGE_BASENAME, __(‘Wptuts Settings | Options Page Two’, ‘wptuts_textdomain’), __(‘Options Page Two’,'wptuts_textdomain’), ‘manage_options’, WPTUTS_PAGE_BASENAME . ‘-page-two’, ‘wptuts_settings_page_fn’);
    // contextual help
    if ($wptuts_settings_page_two) {
    add_contextual_help( $wptuts_settings_page_two, $wptuts_contextual_help );
    }
    // css & js
    add_action( ‘load-’. $wptuts_settings_page_two, ‘wptuts_settings_scripts’ );

    With the following:

    // page one
    // add_submenu_page( $parent_slug, $page_title, $menu_title, $capability, $menu_slug, $function );
    $wptuts_settings_page = add_submenu_page(WPTUTS_PAGE_BASENAME, __(‘Wptuts Settings | Options’), __(‘Options Page One’,'wptuts_textdomain’), ‘manage_options’, WPTUTS_PAGE_BASENAME, ‘wptuts_settings_page_fn’);
    // contextual help on settings page 1
    if ($wptuts_settings_page) {
    add_action( ‘load-’ . $wptuts_settings_page, ‘wptuts_add_help_tabs_to_theme_page’ );
    }
    function wptuts_add_help_tabs_to_theme_page() {
    $screen = get_current_screen();
    $screen->add_help_tab( array(
    ‘id’ => ‘additional-help-page-one’, // This should be unique for the screen.
    ‘title’ => ‘Your Tab Title’,
    // retrieve the function output and set it as tab content
    ‘content’ => wptuts_options_page_contextual_help()
    ) );
    }
    // css & js
    add_action( ‘load-’. $wptuts_settings_page, ‘wptuts_settings_scripts’ );

    // page two
    $wptuts_settings_page_two = add_submenu_page(WPTUTS_PAGE_BASENAME, __(‘Wptuts Settings | Options Page Two’, ‘wptuts_textdomain’), __(‘Options Page Two’,'wptuts_textdomain’), ‘manage_options’, WPTUTS_PAGE_BASENAME . ‘-page-two’, ‘wptuts_settings_page_fn’);
    // contextual help on settings page 2
    if ($wptuts_settings_page_two) {
    add_action( ‘load-’ . $wptuts_settings_page_two, ‘wptuts_add_help_tabs_to_theme_page_two’ );
    }
    function wptuts_add_help_tabs_to_theme_page_two() {
    $screen = get_current_screen();
    $screen->add_help_tab( array(
    ‘id’ => ‘additional-help-page-two’, // This should be unique for the screen.
    ‘title’ => ‘Your Tab Title Two’,
    // retrieve the function output and set it as tab content
    ‘content’ => wptuts_options_two_page_contextual_help()
    ) );
    }
    // css & js
    add_action( ‘load-’. $wptuts_settings_page_two, ‘wptuts_settings_scripts’ );

    You might want to check this link for more info on these API changes -> http://make.wordpress.org/core/2011/12/06/help-and-screen-api-changes-in-3-3/

    Hope this helps.

  • http://twitter.com/thatfire That Fire

    Any potential for a bit about adding an image upload field to be included. I have attempted to do this myself without very much luck. Everytime I add a file input type to the update process it clears the database table. I can’t seem to figure out the proper way to enter a field type into validate so that it returns the right values to the database.

  • Stjepan Tafra

    Great tutorial! How do you make it work for non admins? When registered as Editor, I always get ‘Cheatin’ uh?’ messages? Thanks!

  • sapling

    Finished watching the entire tutorial, good very complete, studied the thank
    you!!!!!!!!!!!