The Complete Guide To The WordPress Settings API, Part 4: On Theme Options

The Complete Guide To The WordPress Settings API, Part 4: On Theme Options

Tutorial Details
  • Program: WordPress
  • Version: All
  • Difficulty: Beginner
  • Estimated Completion Time: Varies
This entry is part 4 of 8 in the series The Complete Guide To The WordPress Settings API

In the last article, we took a deep dive into the various types of menus that are supported by the WordPress API. Although they aren’t necessarily part of the Settings API, they play a key part in development especially when working on more advanced plugins and themes.

This article is going to put menus to practical use as we begin building out our Sandbox Theme by refactoring our existing settings and adding several new pages. Note that if you’re just joining us, make sure that you’ve caught up on the previous articles and that you have the latest version of the Sandbox Theme from the repository on GitHub.

Before we get started: This article is code-intensive. In the first half of the article, we’ll be refactoring existing code. In the second half, we’ll be writing some functionality from the ground up. It’s important that you take your time reading each section and its code and making sure you fully understand what’s going on before moving on to the next section. Try to avoid copying and pasting the code below.

Authors Note: This article will walk you through the process of introducing a new section of settings for theme options, but the article doesn’t complete the process – please read more in this comment thread. Know that I’m aware of it, I apologize for not clarifying this in the article, and I’ll have the full working version in the next article later this month!


Some Housekeeping

If you’ve been working through the previous examples, then you should have a number of different menus configured in functions.php. Since we’re going to be taking a more practical approach to our theme, we need to clean up some of the stuff that we’ve written in previous examples. I know that it seems a little cumbersome to write a lot of code only to remove it, but the prior examples were meant to lay a foundation of understanding of what WordPress offers so that we put the functions into practice.

The first thing we want to do is locate the two function calls that are adding the Sandbox options to the Plugin menu:

function sandbox_example_plugin_menu() {

	add_plugins_page(
		'Sandbox Plugin', 			// The title to be displayed in the browser window for this page.
		'Sandbox Plugin',			// The text to be displayed for this menu item
		'administrator',			// Which type of users can see this menu item
		'sandbox_plugin_options',	// The unique ID - that is, the slug - for this menu item
		'sandbox_plugin_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_plugin_menu');

function sandbox_plugin_display() {

	// Create a header in the default WordPress 'wrap' container
	$html = '<div class="wrap">';
		$html .= '<h2>Sandbox Plugin Options</h2>';
		$html .= '<p class="description">There are currently no options. This is just for demo purposes.</p>';
	$html .= '</div>';
	
	// Send the markup to the browser
	echo $html;
	
} // end sandbox_plugin_display

Remove it from functions.php.

Next, we want to remove the options for adding top-level menus. We’ll be revisiting this code later in the article, but it will be a bit different than what we initially created.

function sandbox_create_menu_page() {

	add_menu_page(
		'Sandbox Options',			// The title to be displayed on this menu's corresponding page
		'Sandbox',					// The text to be displayed for this actual menu item
		'administrator',			// Which type of users can see this menu
		'sandbox',					// The unique ID - that is, the slug - for this menu item
		'sandbox_menu_page_display',// The name of the function to call when rendering this menu's page
		''
	);
	
	add_submenu_page(
		'sandbox',					// Register this submenu with the menu defined above
		'Sandbox Options',			// The text to the display in the browser when this menu item is active
		'Options',					// The text for this menu item
		'administrator',			// Which type of users can see this menu
		'sandbox_options',			// The unique ID - the slug - for this menu item
		'sandbox_options_display' 	// The function used to render this menu's page to the screen
	);

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

function sandbox_menu_page_display() {
	
	// Create a header in the default WordPress 'wrap' container
	$html = '<div class="wrap">';
		$html .= '<h2>Sandbox</h2>';
	$html .= '</div>';
	
	// Send the markup to the browser
	echo $html;
	
} // end sandbox_menu_page_display

function sandbox_options_display() {

	// Create a header in the default WordPress 'wrap' container
	$html = '<div class="wrap">';
		$html .= '<h2>Sandbox Options</h2>';
	$html .= '</div>';
	
	// Send the markup to the browser
	echo $html;
	
} // end sandbox_options_display

At this point, you should be left with two specific features in the WordPress administration area that we’ve created:

  • A “Sandbox Theme” menu item under the Appearance menu.
  • A set of three options under the “General” settings under the Settings menu.

If you see these two options, you’re good to go; otherwise, verify that your functions.php looks like the following code:

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');

function sandbox_theme_display() {

	// Create a header in the default WordPress 'wrap' container
	$html = '<div class="wrap">';
		$html .= '<h2>Sandbox Theme Options</h2>';
		$html .= '<p class="description">There are currently no options. This is just for demo purposes.</p>';
	$html .= '</div>';
	
	// Send the markup to the browser
	echo $html;
	
} // end sandbox_theme_display

/* ------------------------------------------------------------------------ *
 * Setting Registration
 * ------------------------------------------------------------------------ */ 
 
function sandbox_initialize_theme_options() {

	// First, we register a section. This is necessary since all future options must belong to a 
	add_settings_section(
		'general_settings_section',			// ID used to identify this section and with which to register options
		'Sandbox Options',					// Title to be displayed on the administration page
		'sandbox_general_options_callback',	// Callback used to render the description of the section
		'general'							// Page on which to add this section of options
	);
	
	// Next, we'll introduce the fields for toggling the visibility of content elements.
	add_settings_field(	
		'show_header',						// ID used to identify the field throughout the theme
		'Header',							// The label to the left of the option interface element
		'sandbox_toggle_header_callback',	// The name of the function responsible for rendering the option interface
		'general',							// The page on which this option will be displayed
		'general_settings_section',			// The name of the section to which this field belongs
		array(								// The array of arguments to pass to the callback. In this case, just a description.
			'Activate this setting to display the header.'
		)
	);
	
	add_settings_field(	
		'show_content',						
		'Content',				
		'sandbox_toggle_content_callback',	
		'general',							
		'general_settings_section',			
		array(								
			'Activate this setting to display the content.'
		)
	);
	
	add_settings_field(	
		'show_footer',						
		'Footer',				
		'sandbox_toggle_footer_callback',	
		'general',							
		'general_settings_section',			
		array(								
			'Activate this setting to display the footer.'
		)
	);
	
	// Finally, we register the fields with WordPress
	
	register_setting(
		'general',
		'show_header'
	);
	
	register_setting(
		'general',
		'show_content'
	);
	
	register_setting(
		'general',
		'show_footer'
	);
	
} // end sandbox_initialize_theme_options
add_action('admin_init', 'sandbox_initialize_theme_options');

/* ------------------------------------------------------------------------ *
 * Section Callbacks
 * ------------------------------------------------------------------------ */ 

function sandbox_general_options_callback() {
	echo '<p>Select which areas of content you wish to display.</p>';
} // end sandbox_general_options_callback

/* ------------------------------------------------------------------------ *
 * Field Callbacks
 * ------------------------------------------------------------------------ */ 

function sandbox_toggle_header_callback($args) {
	
	// Note the ID and the name attribute of the element match that of the ID in the call to add_settings_field
	$html = '<input type="checkbox" id="show_header" name="show_header" value="1" ' . checked(1, get_option('show_header'), false) . '/>'; 
	
	// Here, we'll take the first argument of the array and add it to a label next to the checkbox
	$html .= '<label for="show_header"> '  . $args[0] . '</label>'; 
	
	echo $html;
	
} // end sandbox_toggle_header_callback

function sandbox_toggle_content_callback($args) {

	$html = '<input type="checkbox" id="show_content" name="show_content" value="1" ' . checked(1, get_option('show_content'), false) . '/>'; 
	$html .= '<label for="show_content"> '  . $args[0] . '</label>'; 
	
	echo $html;
	
} // end sandbox_toggle_content_callback

function sandbox_toggle_footer_callback($args) {
	
	$html = '<input type="checkbox" id="show_footer" name="show_footer" value="1" ' . checked(1, get_option('show_footer'), false) . '/>'; 
	$html .= '<label for="show_footer"> '  . $args[0] . '</label>'; 
	
	echo $html;
	
} // end sandbox_toggle_footer_callback

At this point, we’re ready to get started.


Planning Our Options

Before writing any code, it’s important to plan out what we’re going to be doing. At this point, we’ve written some useful functionality – we give the users the ability to toggle the visibility of the header, content, and footer areas. The thing is, the options currently reside on the “General” page of the Settings menu. Since we’re working on a theme, we should be including that in our theme’s options page.

On top of that, those three options aren’t enough for a useful theme. It’s becoming common for users to feature their social networks on their blog, so let’s plan to give users the ability to include a link to their Twitter, Facebook, and Google+ accounts.

With that in mind, we can plan on the following two sections:

  • Display Options
    • Header
    • Content
    • Footer
  • Social Options
    • Twitter
    • Facebook
    • Google+

To take it one step further, we’ll be introducing these options into the Sandbox Theme’s options page which can be accessed under the Appearance menu. Additionally, each section will be accessible via it’s own tab in the page’s navigation.


Creating the Options

Refactoring the Options Page

Our options page is going to get more advanced throughout this article, so it’s a good idea to prepare for that. Reviewing our options page, the callback function looks like this:

function sandbox_theme_display() {

	// Create a header in the default WordPress 'wrap' container
	$html = '<div class="wrap">';
		$html .= '<h2>Sandbox Theme Options</h2>';
		$html .= '<p class="description">There are currently no options. This is just for demo purposes.</p>';
	$html .= '</div>';

	// Send the markup to the browser
	echo $html;

} // end sandbox_theme_display

Since it’s going to get a bit more advanced, we need to refactor it a bit. Specifically, we’ll be doing the following:

  • Give the options page an icon so that the look and feel is more consistent with the WordPress look and feel
  • Remove the page’s description
  • Refactor the PHP string of HTML into an HTML block so that it’s easier to maintain overtime
  • Introduce a function for displaying settings errors
  • Create a form that will be used to house our options

It’s relatively straight forward. In this case, code does a much better job of explaining what we’re doing that trying to break it down line by line. Review the code below paying close attention to the comments, and then make sure your callback looks just like it:

function sandbox_theme_display() {
?>
	<!-- Create a header in the default WordPress 'wrap' container -->
	<div class="wrap">

		<!-- Add the icon to the page -->
		<div id="icon-themes" class="icon32"></div>
		<h2>Sandbox Theme Options</h2>

		<!-- Make a call to the WordPress function for rendering errors when settings are saved. -->
		<?php settings_errors(); ?>

		<!-- Create the form that will be used to render our options -->
		<form method="post" action="options.php">
			<?php settings_fields( 'general' ); ?>
			<?php do_settings_sections( 'general' ); ?>			
			<?php submit_button(); ?>
		</form>

	</div><!-- /.wrap -->
<?php
} // end sandbox_theme_display

Permitting all of your code is correct, your options page should look like this:

Relocating Our Options

Now that the theme’s options page is taking shape, it’s time to move the options we’ve created this far – that is, the display options – onto the theme’s options page. Recall from the previous article that we added each of our new options to the “General” settings page under the label of “Sandbox Options.” Now that we’re moving this to it’s own page, we’re going to need to make a few modifications. We’ll walk through each one detailing exactly what we’re doing.

Step 1 Verify the Options Exist

Since we’re creating our own custom group of options as opposed to adding fields into an existing set, we need to make sure that our collection of options exists in the database. To do this, we’ll make a call to the get_option function. If it returns false, then we’ll add our new set of options using the add_option function.

To do this, add the following block of code as the first line in the sandbox_initialize_theme_options function:

if( false == get_option( 'sandbox_theme_display_options' ) ) {	
	add_option( 'sandbox_theme_display_options' );
} // end if

Step 2 Refactor Our Sections

Locate the call to add_settings_section and note the “title” argument. Right now, it should look like this:

add_settings_section(
	'general_settings_section',			// ID used to identify this section and with which to register options
	'Sandbox Options',					// Title to be displayed on the administration page
	'sandbox_general_options_callback',	// Callback used to render the description of the section
	'general'							// Page on which to add this section of options
);

Because these options will now be living under the “Sandbox Options” page, we don’t want the section to be labeled as such. Instead, let’s change it to read “Display Options.”

add_settings_section(
	'general_settings_section',			// ID used to identify this section and with which to register options
	'Display Options',					// Title to be displayed on the administration page
	'sandbox_general_options_callback',	// Callback used to render the description of the section
	'sandbox_theme_display_options'		// Page on which to add this section of options
);

Secondly, we’re no longer going to be rendering this on the General Settings page, so we need to change the last argument to something that’s unique to our theme. I’m opting to go with sandbox_theme_display_options:

add_settings_section(
	'general_settings_section',			// ID used to identify this section and with which to register options
	'Display Options',					// Title to be displayed on the administration page
	'sandbox_general_options_callback',	// Callback used to render the description of the section
	'sandbox_theme_display_options'		// Page on which to add this section of options
);

Step 3 Update Our Settings Fields

Now that we’ve given our display options a unique page identifier, we need to make sure that our settings properly reference the page so they are rendered correctly. Currently, all of our calls to add_settings_field refer to the General Settings page:

add_settings_field(	
	'show_header',						// ID used to identify the field throughout the theme
	'Header',							// The label to the left of the option interface element
	'sandbox_toggle_header_callback',	// The name of the function responsible for rendering the option interface
	'general',							// The page on which this option will be displayed
	'general_settings_section',			// The name of the section to which this field belongs
	array(								// The array of arguments to pass to the callback. In this case, just a description.
		'Activate this setting to display the header.'
	)
);

We need to change the fourth argument – that is, the page argument – so that it refers to our new display options page and not to the general page. This is a simple matter of changing the string to sandbox_theme_display_options. The three options should now look like the following:

add_settings_field(	
	'show_header',						// ID used to identify the field throughout the theme
	'Header',							// The label to the left of the option interface element
	'sandbox_toggle_header_callback',	// The name of the function responsible for rendering the option interface
	'sandbox_theme_display_options',	// The page on which this option will be displayed
	'general_settings_section',			// The name of the section to which this field belongs
	array(								// The array of arguments to pass to the callback. In this case, just a description.
		'Activate this setting to display the header.'
	)
);

add_settings_field(	
	'show_content',						
	'Content',				
	'sandbox_toggle_content_callback',	
	'sandbox_theme_display_options',					
	'general_settings_section',			
	array(								
		'Activate this setting to display the content.'
	)
);

add_settings_field(	
	'show_footer',						
	'Footer',				
	'sandbox_toggle_footer_callback',	
	'sandbox_theme_display_options',		
	'general_settings_section',			
	array(								
		'Activate this setting to display the footer.'
	)
);

Step 4 Register the New Setting

Up until this point, we registered each option individually with the settings on the general page:

register_setting(
	'general',
	'show_header'
);

register_setting(
	'general',
	'show_content'
);

register_setting(
	'general',
	'show_footer'
);

Since we’ve created our own option page and, consequently, our own section, we can consolidate these three registration calls into a single call that will register the fields for our new section of options:

register_setting(
	'sandbox_theme_display_options',
	'sandbox_theme_display_options'
);

This is perhaps the most challenging idea to grasp. Recall from the second article in the series that settings are a combination of the fields and section to which they belong. Since all of our fields belong to the same section – the display options – we register the section with WordPress.

Step 5 Update the Option Elements

Don’t Miss This: Up to this point, our options were saved as individual settings in the database. That means we could simply look them up by their ID (such as get_option('show_header');, but since we’ve now added them to a custom section, they are part of a collection of options. This means that in order to read our options, we must first access the collection of options, then request the individual option for the collection.

Now we need to update each of our input elements. To do this, locate the callback functions in your functions.php file. The first option should look like this:

function sandbox_toggle_header_callback($args) {
	
	$html = '<input type="checkbox" id="show_header" name="show_header" value="1" ' . checked(1, get_option('show_header'), false) . '/>'; 
	$html .= '<label for="show_header"> '  . $args[0] . '</label>'; 
	
	echo $html;
	
} // end sandbox_toggle_header_callback

As mentioned above, each of our options is now part of a collection so we need to update how we reference them. We need to…

  1. Read the collection of options from WordPress
  2. Access the individual option from the collection
  3. Update the attributes of the input element to properly refer to the options collection

Though it sounds like a lot, the steps are very easy. Review the following code below paying close attention to the comments:

function sandbox_toggle_header_callback($args) {
	
	// First, we read the options collection
	$options = get_option('sandbox_theme_display_options');
	
	// Next, we update the name attribute to access this element's ID in the context of the display options array
	// We also access the show_header element of the options collection in the call to the checked() helper function
	$html = '<input type="checkbox" id="show_header" name="sandbox_theme_display_options[show_header]" value="1" ' . checked(1, $options['show_header'], false) . '/>'; 
	
	// Here, we'll take the first argument of the array and add it to a label next to the checkbox
	$html .= '<label for="show_header"> '  . $args[0] . '</label>'; 
	
	echo $html;
	
} // end sandbox_toggle_header_callback

Easy enough, right? Since we have three elements, we need to make the corresponding changes to each of the elements. Each function will look nearly the same with the exception of the option name. Rather than show_header, it will read show_content or show_footer.

Once done, the final version of the three form elements should look like this:

function sandbox_toggle_header_callback($args) {

	$options = get_option('sandbox_theme_display_options');

	$html = '<input type="checkbox" id="show_header" name="sandbox_theme_display_options[show_header]" value="1" ' . checked(1, $options['show_header'], false) . '/>'; 
	$html .= '<label for="show_header"> '  . $args[0] . '</label>'; 

	echo $html;

} // end sandbox_toggle_header_callback

function sandbox_toggle_content_callback($args) {

	$options = get_option('sandbox_theme_display_options');

	$html = '<input type="checkbox" id="show_content" name="sandbox_theme_display_options[show_content]" value="1" ' . checked(1, $options['show_content'], false) . '/>'; 
	$html .= '<label for="show_content"> '  . $args[0] . '</label>'; 

	echo $html;

} // end sandbox_toggle_content_callback

function sandbox_toggle_footer_callback($args) {

	$options = get_option('sandbox_theme_display_options');

	$html = '<input type="checkbox" id="show_footer" name="sandbox_theme_display_options[show_footer]" value="1" ' . checked(1, $options['show_footer'], false) . '/>'; 
	$html .= '<label for="show_footer"> '  . $args[0] . '</label>'; 

	echo $html;

} // end sandbox_toggle_footer_callback

Step 6 Render the Options

Finally, we are ready to render the options on our new theme options page. This is a simple matter of making a call to two functions in the Settings API:

  1. settings_fields takes care of rendering several security measures for the options form.
  2. do_settings_sections actually renders the options to the page.

Each function takes the ID of the page that we’ll be rendering. In our case, this is sandbox_theme_display_options. To render our options, we update the form to look like this:

	<form method="post" action="options.php">
		<?php settings_fields( 'sandbox_theme_display_options' ); ?>
		<?php do_settings_sections( 'sandbox_theme_display_options' ); ?>			
		<?php submit_button(); ?>
	</form>

At this point, you should be able to refresh your Sandbox Theme options page and see our three options rendered like this:

Sandbox Theme Display Options

The final version of your functions.php should now look like this:

<?php

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' );

/**
 * Renders a simple page to display for the theme menu defined above.
 */
function sandbox_theme_display() {
?>
	<!-- 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(); ?>
		
		<form method="post" action="options.php">
			<?php settings_fields( 'sandbox_theme_display_options' ); ?>
			<?php do_settings_sections( 'sandbox_theme_display_options' ); ?>			
			<?php submit_button(); ?>
		</form>
		
	</div><!-- /.wrap -->
<?php
} // end sandbox_theme_display

function sandbox_initialize_theme_options() {

	// If the theme options don't exist, create them.
	if( false == get_option( 'sandbox_theme_display_options' ) ) {	
		add_option( 'sandbox_theme_display_options' );
	} // end if

	// First, we register a section. This is necessary since all future options must belong to a 
	add_settings_section(
		'general_settings_section',			// ID used to identify this section and with which to register options
		'Display Options',					// Title to be displayed on the administration page
		'sandbox_general_options_callback',	// Callback used to render the description of the section
		'sandbox_theme_display_options'		// Page on which to add this section of options
	);
	
	// Next, we'll introduce the fields for toggling the visibility of content elements.
	add_settings_field(	
		'show_header',						// ID used to identify the field throughout the theme
		'Header',							// The label to the left of the option interface element
		'sandbox_toggle_header_callback',	// The name of the function responsible for rendering the option interface
		'sandbox_theme_display_options',	// The page on which this option will be displayed
		'general_settings_section',			// The name of the section to which this field belongs
		array(								// The array of arguments to pass to the callback. In this case, just a description.
			'Activate this setting to display the header.'
		)
	);
	
	add_settings_field(	
		'show_content',						
		'Content',				
		'sandbox_toggle_content_callback',	
		'sandbox_theme_display_options',					
		'general_settings_section',			
		array(								
			'Activate this setting to display the content.'
		)
	);
	
	add_settings_field(	
		'show_footer',						
		'Footer',				
		'sandbox_toggle_footer_callback',	
		'sandbox_theme_display_options',		
		'general_settings_section',			
		array(								
			'Activate this setting to display the footer.'
		)
	);
	
	// Finally, we register the fields with WordPress
	register_setting(
		'sandbox_theme_display_options',
		'sandbox_theme_display_options'
	);
	
} // end sandbox_initialize_theme_options
add_action('admin_init', 'sandbox_initialize_theme_options');

function sandbox_general_options_callback() {
	echo '<p>Select which areas of content you wish to display.</p>';
} // end sandbox_general_options_callback

function sandbox_toggle_header_callback($args) {
	
	// First, we read the options collection
	$options = get_option('sandbox_theme_display_options');
	
	// Next, we update the name attribute to access this element's ID in the context of the display options array
	// We also access the show_header element of the options collection in the call to the checked() helper function
	$html = '<input type="checkbox" id="show_header" name="sandbox_theme_display_options[show_header]" value="1" ' . checked(1, $options['show_header'], false) . '/>'; 
	
	// Here, we'll take the first argument of the array and add it to a label next to the checkbox
	$html .= '<label for="show_header"> '  . $args[0] . '</label>'; 
	
	echo $html;
	
} // end sandbox_toggle_header_callback

function sandbox_toggle_content_callback($args) {

	$options = get_option('sandbox_theme_display_options');
	
	$html = '<input type="checkbox" id="show_content" name="sandbox_theme_display_options[show_content]" value="1" ' . checked(1, $options['show_content'], false) . '/>'; 
	$html .= '<label for="show_content"> '  . $args[0] . '</label>'; 
	
	echo $html;
	
} // end sandbox_toggle_content_callback

function sandbox_toggle_footer_callback($args) {
	
	$options = get_option('sandbox_theme_display_options');
	
	$html = '<input type="checkbox" id="show_footer" name="sandbox_theme_display_options[show_footer]" value="1" ' . checked(1, $options['show_footer'], false) . '/>'; 
	$html .= '<label for="show_footer"> '  . $args[0] . '</label>'; 
	
	echo $html;
	
} // end sandbox_toggle_footer_callback
?>

Introducing Social Options

This section was a bit exhaustive, I know. We took a lot of code that we’ve previously written and refactored it to fit a completely new page. The Settings API can be challenging! It’s not enough to simply refactor existing code into a new page. So, to make sure that we’ve taken this process of custom options pages and introducing new options from end-to-end, we’ll now introduce a set of brand new options.

In keeping consistent with our usual process, let’s plan out exactly what we’re going to do:

  • We need to create a new section of fields
  • Three fields will be added: one for Facebook, one for Twitter, and one for Google+
  • We need to update our options page to render these new options

Creating Our Sections, Fields, and Settings

The Social Options Section

First, let’s go ahead and setup a new function and add it to the admin_init hook. This function will be specifically for setting up our social options. As with our display options, it will need to perform a check to make sure that the options exist and, if they don’t, it should create them.

Here’s the initial function. Take note of the comments, as well!

/**
 * Initializes the theme's social options by registering the Sections,
 * Fields, and Settings.
 *
 * This function is registered with the 'admin_init' hook.
 */ 
function sandbox_theme_intialize_social_options() {

	// If the social options don't exist, create them.
	if( false == get_option( 'sandbox_theme_social_options' ) ) {	
		add_option( 'sandbox_theme_social_options' );
	} // end if

} // end sandbox_theme_intialize_social_options
add_action( 'admin_init', 'sandbox_theme_intialize_social_options' );

Next, we need to add the new section for the settings. This is done immediately after the code above:

add_settings_section(
	'social_settings_section',			// ID used to identify this section and with which to register options
	'Social Options',					// Title to be displayed on the administration page
	'sandbox_social_options_callback',	// Callback used to render the description of the section
	'sandbox_theme_social_options'		// Page on which to add this section of options
);

But we’re note quite done! Recall that the third option refers to a callback function for this section. Don’t forget to define it:

function sandbox_social_options_callback() {
	echo '<p>Provide the URL to the social networks you\'d like to display.</p>';
} // end sandbox_general_options_callback

At this point, we can do a sanity check to make sure that the section has been properly registered with the Settings API. Hop back into the sandbox_theme_display function and add the following two lines to the form element. They can go directly below their sandbox_theme_display_options counterpart:

<?php settings_fields( 'sandbox_theme_social_options' ); ?>
<?php do_settings_sections( 'sandbox_theme_social_options' ); ?>

Permitting you’ve written your code properly, your options page should now look like this:

Social Options

The Social Options Fields

Now we’re ready to begin adding the social option fields to our page. Let’s begin by adding a field for Twitter. Just below the call to add_settings_section, let’s write the following function:

add_settings_field(	
	'twitter',						
	'Twitter',							
	'sandbox_twitter_callback',	
	'sandbox_theme_social_options',	
	'social_settings_section'
);

Take note – we’ve defined a callback as sandbox_twitter_callback so we’ll need to implement that function, too. Just as we’ve done with our previous options, we’ll need to grab the collection of options, create the HTML element for the option, and properly set its attributes to refer to the option. Take special note of the conditional in the code below – it’s commented for clarity, but is a best practice for making sure we don’t try to read empty options:

function sandbox_twitter_callback() {
	
	// First, we read the social options collection
	$options = get_option( 'sandbox_theme_social_options' );
	
	// Next, we need to make sure the element is defined in the options. If not, we'll set an empty string.
	$url = '';
	if( isset( $options['twitter'] ) ) {
		$url = $options['twitter'];
	} // end if
	
	// Render the output
	echo '<input type="text" id="twitter" name="sandbox_theme_social_options[twitter]" value="' . $options['twitter'] . '" />';
	
} // end sandbox_twitter_callback

Finally, we hop back into the sandbox_theme_intialize_social_options function and register the new setting with WordPress:

	
register_setting(
	'sandbox_theme_social_options',
	'sandbox_theme_social_options',
	'sandbox_theme_sanitize_social_options'
);

There’s an important distinction to make here: Up until this point, we’ve only been providing two arguments for the register_setting function – the option group and the option name – but this time, we’ve supplied a third argument: the name of a callback function. Specifically, this function is called just before the data is written to the database. It allows you to process all the arguments just before saving them. Generally speaking, this is the time that you want to sanitize the data to prevent malicious code or malformed information from being saved.

So let’s go ahead and stub out the function:

function sandbox_theme_sanitize_social_options( $input ) {
} // end sandbox_theme_sanitize_social_options

Notice above that the callback accepts a single argument that we’ve named $input. This argument is the collection of options that exist for the social option sections. Since we’re allowing users to enter raw text into an input field, we need to prevent any malicious code (such as JavaScript or SQL) from being saved.

To do this, we need to do the following:

  • Create an array that we can use to store the processed data
  • Loop through each of the options and properly sanitize the data
  • Return the updated collection of options

Let’s go ahead and write the code to do this. Update the function to look like this:

function sandbox_theme_sanitize_social_options( $input ) {
	
	// Define the array for the updated options
	$output = array();

	// Loop through each of the options sanitizing the data
	foreach( $input as $key => $val ) {
	
		if( isset ( $input[$key] ) ) {
			$output[$key] = esc_url_raw( strip_tags( stripslashes( $input[$key] ) ) );
		} // end if	
	
	} // end foreach
	
	// Return the new collection
	return apply_filters( 'sandbox_theme_sanitize_social_options', $output, $input );

} // end sandbox_theme_sanitize_social_options

Note above that we’re using three functions each of which play a hand in making sure the user-submitted data is clean:

  • stripslashes is a native PHP function that will “unquote a quoted” string
  • strip_tags is another native PHP function that removes HTML and PHP tags from a string
  • esc_url_raw is a WordPress function that will enforce a clean URL

The above process is aggressive, sure, but if you want to make sure that you’re only allowing URLs to be saved to the database, it’s effective.

Note that the last line is actually returning the result of the apply_filters function rather than the array itself. This is a best practice that ensures that other functions attached to this same hook will be called as well. In the context of this theme, there are obviously no other functions; however, it’s a good practice to get into for professional theme development.

At this point, save your work and refresh the browser. Your options page should now not only display the Display Options but should also display the first social option, too. Even more so, you should be able to provide a URL to your Twitter profile and have it save to the database.

Twitter Social Option

Including the Rest of the Options

Introducing Facebook and Google+ will follow the exact same steps as those required for adding support for Twitter. Mainly:

  • Define the settings field
  • Setup the callback

Easy enough, right? Everything else is already taken care of thanks to the Settings API. Of course, we need to be complete – here’s how to include support for the following networks.

First, we’ll define the two settings fields:

add_settings_field(	
	'facebook',						
	'Facebook',							
	'sandbox_facebook_callback',	
	'sandbox_theme_social_options',	
	'social_settings_section'			
);

add_settings_field(	
	'googleplus',						
	'Google+',							
	'sandbox_googleplus_callback',	
	'sandbox_theme_social_options',	
	'social_settings_section'			
);

Next, we’ll define their two callbacks:

function sandbox_facebook_callback() {
	
	$options = get_option( 'sandbox_theme_social_options' );
	
	$url = '';
	if( isset( $options['facebook'] ) ) {
		$url = $options['facebook'];
	} // end if
	
	// Render the output
	echo '<input type="text" id="facebook" name="sandbox_theme_social_options[facebook]" value="' . $options['facebook'] . '" />';
	
} // end sandbox_facebook_callback

function sandbox_googleplus_callback() {
	
	$options = get_option( 'sandbox_theme_social_options' );
	
	$url = '';
	if( isset( $options['googleplus'] ) ) {
		$url = $options['googleplus'];
	} // end if
	
	// Render the output
	echo '<input type="text" id="googleplus" name="sandbox_theme_social_options[googleplus]" value="' . $options['googleplus'] . '" />';
	
} // end sandbox_googleplus_callback

Save your work and, once again, refresh your options page. There should now be two additional fields – one for Facebook and one for Google+. Feel free to try them out, as well.

A note about security: Although this example is functional, there are still a few security measures we should take when saving our options to the database. Later in the series, when we take a look at each of the input fields in more depth, we’ll examine the best practices for making sure we’re sanitizing our data before saving it.


Render the Fields

What good are options if we’re not accessing them on the actual website? Before wrapping up this article, let’s make a minor update to the index template of our theme. Namely, we need to make sure we’re referencing our new options and introduce the ability to display the social networks:

<!DOCTYPE html>
<html>
	<head>	
		<title>The Complete Guide To The Settings API | Sandbox Theme</title>
	</head>
	<body>
	
		<?php $display_options = get_option( 'sandbox_theme_display_options' ); ?>
		<?php $social_options = get_option ( 'sandbox_theme_social_options' ); ?>
	
		<?php if( $display_options[ 'show_header' ] ) { ?>
			<div id="header">
				<h1>Sandbox Header</h1>
			</div><!-- /#header -->
		<?php } // end if ?>
		
		<?php if( $display_options[ 'show_content' ] ) { ?>
			<div id="content">
				<?php echo $social_options['twitter'] ? '<a href="' . $social_options['twitter'] . '">Twitter</a>' : ''; ?>
				<?php echo $social_options['facebook'] ? '<a href="' . $social_options['facebook'] . '">Facebook</a>' : ''; ?>
				<?php echo $social_options['googleplus'] ? '<a href="' . $social_options['googleplus'] . '">Google+</a>' : ''; ?>
			</div><!-- /#content -->
		<?php } // end if ?>
		
		<?php if( $display_options[ 'show_footer' ] ) { ?>
			<div id="footer">
				<p>© <?php echo date('Y'); ?> All Rights Reserved.</p>
			</div><!-- /#footer -->
		<?php } // end if ?>
	
	</body>
</html>

Conclusion

This was arguably the most intense article we’ve had (or will have) in this particular series. If you hung with it, you should be well suited for beginning to do more advanced work the the Settings API. In the next two articles, we’re going to take a look at introducing navigation menus – both tabbed navigation and top-level navigation – to our menus. The articles should be a bit shorter and less code intensive. After that, we’ll head into looking at each of the element types.

In the meantime, experiment with what we’ve covered so far!

Author’s Note:

Thanks a lot to those who took time to comment and share their issues with this particular post. Hopefully the following will clear this up and continue to make it as valuable a series as possible!

At this point in the tutorial, we’ve added a lot of code; however, still don’t have a functioning settings page yet. In the next article, we’re going to be take rounding this out and adding functionality to actually breathe some life into these options so be sure to continue reading.

That said, if you happen to encounter a number of issues with options not saving or with some validation errors. The validation errors have been fixed and the most recent code is available on GitHub, so be sure to check it out. Note that I keep this project up to date so as issues arrive, I work to resolve them as soon as possible.


Related Sources


Other parts in this series:The Complete Guide To The WordPress Settings API, Part 3: All About MenusThe Complete Guide To The WordPress Settings API, Part 5: Tabbed Navigation For Your Settings Page
Note: Want to add some source code? Type <pre><code> before it and </code></pre> after it. Find out more
  • http://www.bdbook24.com/ bokteare

    Nice Post. Just following the tutorial one by one. Carry on. Thanks.

  • http://www.metaphorcreations.com Joe

    Great articles! I am running into one issue with this latest update. Once I add:

    My display options don’t save when I hit “save changes”. I also downloaded your source from GitHub to test (in case I missed something) and I get the same results.

    • http://www.metaphorcreations.com Joe

      Code isn’t showing up…

      • http://www.metaphorcreations.com Joe

        settings_fields( ‘sandbox_theme_social_options’ );
        do_settings_sections( ‘sandbox_theme_social_options’ );

        settings_fields( ‘sandbox_theme_social_options’ );
        do_settings_sections( ‘sandbox_theme_social_options’ );

      • http://tommcfarlin.com Tom McFarlin
        Author

        Joe,

        Thanks so much for this comment – I need to apologize as this article is incomplete. When the article began, it was going to include everything that you see above in addition to tabbed navigation.

        Obviously, the article got a bit long and in an effort to reduce all of the content, I decided to move tabbed navigation into the next article in this series. Unfortunately, that leaves you with a settings page that only saves social options. My fault!

        The reason for the bug: When using the Settings API, WordPress provides a number of different security measures (generally speaking, hidden input fields and associated values) that attempt to make sure nothing gets into the database by accident. When you add multiple settings to admin pages (in our case, display options and social options), two sets of values are generated but only one is honored during the POST request. That’s why the display options aren’t saving.

        I’ll ask Japh to provide an authors note at the top of the this article and I’ll make a note in the source code on GitHub that this is a ‘work-in-progress.’ I’ll have the next article out before the end of the month to handle this.

        Thanks again for the heads up!

        • http://www.metaphorcreations.com Joe

          Sounds good. I’m looking forward to the next article!

        • Esteban

          Hi!

          I’m curious on how to solve this issues. Becaus it seems that the do_settings_sections() creates additional hidden inputs, that conflicts or disables the update/change you try to submit.

          I solved it by adding to different forms:

          Hope that helps someone.

          • Esteban

            Code below:

          • http://wp.envato.com/ Japh Thomson
            Staff

            Hi Esteban, looks like your code snippet got eaten :(

            Don’t forget, in comments, code needs to be wrapped correctly, as follows:

            <pre name=”code” class=”php”>
            // Your code here, with all < replaced with &lt; and > replaced with &gt;
            </pre>

  • http://kovshenin.com Konstantin Kovshenin

    Wow Tom, you’re really nailing it, well done! I have a few tips :) sandbox_toggle_content_callback, header and footer are essentially the same function, you can easily combine them by passing in more than just the label in the args array. In the social part, I see you’re using esc_url_raw when storing the URLs, but you’re not using esc_url when you’re printing them, both in the options pages and on the front end. I might have missed a few others ;)

    Keep up the good work!

    ~ Konstantin

    • http://tommcfarlin.com Tom McFarlin
      Author

      Konstantin,

      You’re awesome – thanks for catching those. By the time the next tutorial drops, I’m aiming to have those issues resolved as well as a few more fixes to honor WordPress coding conventions.

      Always appreciate the suggestions,
      Tom

  • http://www.ellahworks.com Rowela Alzona

    This is very nice tutorial!! This is usefull in my research….

    • http://tom.mcfarl.in Tom McFarlin
      Author

      Sure thing, Rowela!

  • http://www.sanjaykhemlani.com/ sanjay

    Cool post! Tom looking forward to your next article, this is really helpful for WP community! Thanks!

    • http://tom.mcfarl.in Tom McFarlin
      Author

      Of course, Sanjay – should have another one out next week!

  • Pingback: Wordpress - API | Pearltrees

  • Steven

    Awesome post Tom – I have been following along with this post and referencing the codex at the same time. Way to keep things clean and true to WordPress! Looking forward to the next lesson!

    • http://tom.mcfarl.in Tom McFarlin
      Author

      Glad to hear it, Steven!

  • Lion King

    Great tutorials, thanks! Got me hook up and finished 4 parts in a day :D

    I have a few questions though,
    add_settings_section(
    ‘general_settings_section’,
    ‘Display Options’,
    ‘sandbox_general_options_callback’,
    ‘sandbox_theme_display_options’ //page on which to add this section of options
    );

    param#4, so it’s a page/section? or an collection options? or can be either? or they’re the same thing? is it relevant to the theme page’s slug “sandbox_theme_options” in anyway?
    (If it’s viewed as a page, how does WordPress know what it is, cause I don’t see it gets declared anywhere before this?)

    then when we get to this:
    register_setting(
    ‘sandbox_theme_display_options’, //name of the page
    ‘sandbox_theme_display_options’ //id of the field or collection of options
    );

    It looks like they’re 2 different things? I’m bit confused, why the same name?

    This is my first encounter with WordPress API :D
    Cheers!

    • http://tom.mcfarl.in Tom McFarlin
      Author

      The fourth parameter represents the section of options. If you take a look at the query string variable in the browser when you load the page, you’ll notice that you’re actually loading up themes.php and that it’s passing ‘sandbox_theme_options.’ This is what’s defined in the add_theme_page call earlier in the tutorial. It’s essentially registering your page with WordPress.

      In that same function, you’ll see that it’s calling the function ‘sandbox_theme_display’ which is the function that is responsible for rendering the options page. In that function, we’re instructing WordPress to grab the settings sections for both the display options and the social options.

      This is where your second question is relevant: you’ve registered those two sections with WordPress using that function so when you’re displaying the options page, WordPress renders the for elements on the page.

      Finally, the two parameters that register_setting accepts are the name of the group and the name of the option. We did this earlier in the ; however, we’ve not folded multiple fields into a single section. As such, we can pass the ID of the section in to the registe_settings call.

      Hopefully this clarifies it a bit – if not, let me know!

      • Lion King

        Thanks! I went back and looked at Chapter 2 and WordPress docs again, and let me tell you what I understand so far, please correct me if I’m wrong. I guess my problem is that in Chapter 2 –

        register_setting(
        ‘general’,
        ‘show_header’
        );
        you explain parameter 1 as …
        “Option Group – This is actually the name of the page (not the ID of the section) that we specified when creating our settings section. This argument is required.”
        So, I was like “oh, ‘general’ is the slug of the General Settings Page of WordPress”, then in Chapter 4, we add a theme page and name it ‘sandbox_theme_options’. Then when I saw

        register_setting(
        ‘sandbox_theme_display_options’,
        ‘sandbox_theme_display_options’
        );

        I was confused: if the first parameter is the page, why don’t we use ‘sandbox_theme_options’.

        So, after I did some revisions, this is what I think. We group our 3 settings together into an array (right?) that we name “sandbox_theme_display_options”, and that the first param of register_setting() is not the page name, but the option group’s name (‘general’ is also an existing group name that is also a setting page of WordPress, am I right?), so we use the array’s name.

        But what I still don’t grasp is that weather these 2 parameters of register_setting() are the same thing or not, I presume that they are?
        If not, as the first one is group name, while the second is option name, is there any way we can avoid giving them the same name?

        Cheers!

        • http://tommcfarlin.com Tom McFarlin
          Author

          We don’t use Sandbox Theme Options because we’re creating two separate groups of options (Display and Social), but it’s not at all uncommon to use a single group of options for plugins and/or theme options.

          The reason I’ve split it into two options in this series is so that we’re able to showcase a few more things later in the series – namely, different methods of navigation.

          Secondly, you’re right in that it’s the name of the group of options, not the page. That’s a typo on my behalf that I’ll amend earlier in the series. Thanks for pointing that out!

          And finally, the second option is the name of the collection because we’re actually rendering the set of options rather than the individual option. So, in our case, we’re telling WordPress to use the, for example, Display Options group and then we’re passing the ID of that group as the second parameter so that it renders them all out.

          • Lion King

            I must admit I don’t totally understand this yet, but thanks for the help anyway! Can’t wait for next chapter! It might help me understand this a bit more as I go on. :D

          • WP

            Hi,
            Does that mean we can consolidate the fields in the previous tutorial like this:

            register_setting(
            ‘general’,
            ‘general’
            );

  • https://knnktr.com Wayne

    Anyone got any ideas to fix multiple sections on one page?

    I actually really need this on a project and can’t wait for the next post :(

    I’m trying some things out, but no luck so far

    • http://tommcfarlin.com Tom McFarlin
      Author

      The remainder of the article is coming next week and we’ll be using tabbed navigation to resolve this issue, but if you can’t wait that long feel free to follow this thread on GitHub.

      Note that if you follow that, it may ultimately derail the theme from the rest of the series as we’re going to continue building out on this particular setup. Just be sure to make a copy of what you’ve got so far :).

  • Mark

    Tom, thanks for a great series of articles, one bit I’m not sure if i’ve got my head around, where you have the call back functions that create the inputs, for the name value are you referencing this as a key in an array that represents all the values from the inputs within that particular section? I think that’s how it works but would be great if you could confirm.

    • http://tommcfarlin.com Tom McFarlin
      Author

      Yep – what we do is this:

      Create a setup of options (such as sandbox theme social options)
      Add a setting, such as the Twitter URL, to the option where the option is, for example, sandbox_theme_social_options
      Load the option array (by using get_option), then read the setting by access its value in a key – the key being ‘Twitter’) – in the options array

      Look for the following lines in the above article to follow allong:

      add_option( ‘sandbox_theme_social_options’ );
      $options = get_option( ‘sandbox_theme_social_options’ );
      $options['twitter']

      Hope this helps!

      • Mark

        thanks for the reply Tom, in fact the bit i really wanted clarifying was the name value, where you have: sandbox_theme_social_options[twitter], as i believe this is the bit going into the database, I’m thinking that this array comes from the all the values within that section.

        • http://tommcfarlin.com Tom McFarlin
          Author

          The way WordPress serializes your options into the options table is by taking the name of the group that you’ve created, such as ‘sandbox_theme_social_options’ and then serializing the values of the array into a string like this:

          a:3:{s:7:”twitter”;s:30:”http://twitter.com/yourname”;s:8:”facebook”;s:31:”http://facebook.com/yourname”;s:10:”googleplus”;s:27:”http://gplus.to/yourname”;}

          So the value for the name attribute basically follows the form array_name[key_id]. So, in a sense, it is what is written in the database but it’s actually the combination of values rather than having a row like this:

          sandbox_theme_social_options[twitter] = ‘http://twitter.com/yourname’

          Make sense?

  • http://www.xhtml-lab.com Alok Jain

    Awesome articles. I studied and tried all articles in the series, it’s working as expected, except that checkboxes are not working. But you have already mentioned the reason in comments.

    Waiting in haste for the next part of the series. Thanks for all the hard work you have done to prepare these articles.

    • http://tommcfarlin.com Tom McFarlin
      Author

      Next part is due out next week!

  • http://www.hyphenstudio.ca Hyphen Studio

    Tom,

    Great work on this series – it’s been easy to follow and has some great content on building up theme backend options.

    Are you going to tie this all into a theme design? I would like to see how to put this all together with a custom theme of some kind.

    Keep up the great work – can’t wait for next week & Part 5.

    • http://tommcfarlin.com Tom McFarlin
      Author

      Thanks!

      I’ve not yet decided if I’ll be tying it all together in a final project or not. The goal of having source code that goes along with each article is an attempt to have an extremely simple theme. Larger themes can almost be a series in an of themselves, you know? :)

      Right now, the series is slated for at least four more articles. Time permitting, I’ll see what I can do.

  • Pingback: Best of Tuts+ in March 2012

  • Pingback: Best of Tuts+ in March 2012 | SearchPsd Blog

  • Pingback: Best of Tuts+ in March 2012 | Shadowtek Hosting and Design Solutions

  • Pingback: Best of Tuts+ in March 2012 - Milk-Break

  • Pingback: Best of Tuts+ in March 2012 | How to Web

  • Pingback: Best of Tuts+ in March 2012 | cssWOW Showcase | Css Gallery | Css Awards | Design Inspiration Tutorials

  • Pingback: Best of Tuts+ in March 2012 | linuxin.ro

  • Pingback: Best of Tuts+ in March 2012 – blog

  • Pingback: Best of Tuts+ in March 2012 | PHP Developer Resource

  • Pingback: How to Use Custom Sidebars on Posts and Pages | Wptuts+

  • Pingback: How to Use Custom Sidebars on Posts and Pages | Wordpress Webdesigner

  • Pingback: My Stream | How to Use Custom Sidebars on Posts and Pages | My Stream

  • Pingback: Wordpress Leaks » How to Use Custom Sidebars on Posts and Pages

  • Pingback: How to Use Custom Sidebars on Posts and Pages | Shadowtek Hosting and Design Solutions

  • Pingback: How to Use Custom Sidebars on Posts and Pages | How to Web

  • Amanda Dominy

    If you wanted to localise this, I guess you would keep the PHP strings and not convert to a block of HTML code? (From “Refactoring the options page”)

  • Pingback: The Complete Guide To The WordPress Settings API, Part 7: Validation, Sanitisation, and Input I | Wptuts+

  • Pingback: The Complete Guide To The WordPress Settings API, Part 7: Validation, Sanitisation, and Input I | How to Web

  • Pingback: The Complete Guide To The WordPress Settings API, Part 7: Validation, Sanitisation, and Input I | Shadowtek Hosting and Design Solutions

  • Pingback: The Complete Guide To The WordPress Settings API, Part 8: Validation, Sanitisation, and Input II | How to Web

  • http://www.krymsonproductions.com Matthew Caldwell

    This is all really great information! I’m glad I found it because I could tell I was getting myself into bad habits with Theme Options and the other tutorials I found online. I’m taking this one slow and learning. Something weird happened to me in this section and I don’t know why it’s any different from what happened on your screen. In the Refactoring the Options Page section when you really just got the options page ready to go. I followed along and typed all the code myself, looks just like yours. Then you showed a screenshot and your form wasn’t populated at all, except mine was at that section and under the general section where we put it the first time.

    • http://krymsonproductions.com Matthew Caldwell

      Nevermind, I understand why mine was showing up, when you first pasted the settings_fields() and do_settings_sections() functions you passed ‘general’ to them which was the name of the options we had before. So they showed up like they should have. Why didn’t they show up in your screenshot though? Were you already ahead of the article at that point or???

  • Pingback: Как настроить свои сайдбары в Записях и Страницах | Wordpresso

  • http://www.studio88design.com Rob Myrick

    Great tut…….thanks for putting this all into perspective!

    Tom, based on some of the more elaborate themes, I’m finding that wp_list_table is another aspect that I’m sure developers would like info about. And not only the tables themselves, but saving table data to the DB and outputting that info back out onto a separate page for users to edit/change information. Kind of like the “Links” section functionality in the main WP installation.

    Just a thought for your future posts…….

  • Pingback: Как настроить свои сайдбары в Записях и Страницах

  • http://www.greenleaf-net-solutions.com/ Colin Crawford

    Hi Tom, great set of articles and I’m glad I’m behind in this series as you have already got the other articles uploaded so as in part 4 I’ve got the problem with saving the option settings.

    I need to go through it again as there are lots of information to take in. I did get lost when you said insert this block of code just below the other, but it needed to be within the function. I pondered with it for a few minutes and inserted it were I thought you meant and I got it right.

    I want to create an options page for a customer who can then select her own colours for her website and it will be easier doing it this way.

    Well now off to Part 5.

  • Andy Mercer

    Hey Tom, thanks for the great tutorial! I’m having a bit of difficulty though. I just finished with moving everything over to the theme page and refactoring, right before Introducing Social. Everything looks right on the settings page, but the options no longer work. Changing the checkboxes have no effect on whether the three sections of the theme display. After trying to find the problem I finally copied over your final code, but it’s still not working.

    The only thing I can think that might be causing it is that index.php needs to be updated, given that we changed from registering each field to registering the entire section. Is that the problem or am I missing something?

    • Tom McFarlin
      Author

      There may be something small that you’re missing as the version of the code that I have appears to be working fine.

      Do a fresh pull of the theme on GitHub and see if it’s resolved. If not, please let me know!

  • http://thedrawingboardstudio.com Brandon

    What about adding an options page to a custom post type? I have done this. The options page via add_submenu_page works great with the $parent_slug as: edit.php?post_type=my_post_type. Here is where I am running into trouble.
    When I do the do_settings_sections function it asks for: $page – The slug name of the page whose settings sections you want to output. This should match the page name used in add_settings_section().
    Everything I have tried does not seem to work. I cant figure out if I use the $menu_slug from add_submenu_page, or the whole page address including the: edit.php?post_type=my_post_type&page=my_$menu_slug_from_add_submenu_page, or if it is just not possible at all.
    I am stumped and found your posts to be the most helpful so far. Any help on the matter would be great.
    Thanks.

    • Tom McFarlin
      Author

      Historically, I’ve added options to custom post types via meta boxes. Are you looking to add an options page on the custom post type’s actual menu?

  • vhin

    Hi, is it possible to add field without creating a section?
    Thanks

    • vhin

      when the section is present, the field I added is there, but when I try to remove the section the field is missing even when the $section in add_settings_field is already removed. this is my first time on creating a theme settings so I’m having a little difficulties.

      Regards,
      vhin

      • http://twitter.com/tommcfarlin Tom McFarlin

        Hey Vhin – apologies for the delay on the response!

        It *is* possible to create a field without having to define a section, but it requires a little bit more knowledge of the Settings API which is more than I can reasonably offer in this comment.

        To that end, I recommend checking out the codex article here:
        http://codex.wordpress.org/Function_Reference/add_settings_field

        You’ll notice that the `$section` argument is *not* required. That said, the field still has to reside somewhere, right?

        If you don’t provide one, then your option will be written to the default section fo the WordPress settings.

  • Pingback: Integrating With The WordPress Customizer – design / develop

  • http://www.kelleherwebdesign.com Dharma Kelleher

    With Debug turned on in my wp-config file, I’m getting an undefined index error any time any of the display options are unchecked. It goes away when I check the checkbox for that option. It’s as if it’s not storing any value for show_header, etc. if a checkbox is unchecked.

    Any ideas? I’m thinking there may be an error with the checked method in the toggle callback functions. But the error may lie elsewhere.

    • Ravinder Singh Rawat

      Try this

      if(isset($options['show_header'])) checked(1, $options['show_header'], false);

      Actually whenever checkbox is unchecked then wordpress does not save any value releated to that checkbox. But this line

      checked(1, $options['show_header'], false)

      checks for $options['show_header'] which is not present hence showing error. So to solve this just check if $options['show_header'] exists or not through if condition. If exists then use checked(1, $options['show_header'], false);

    • http://twitter.com/tommcfarlin Tom McFarlin

      Dharma – good catch. Apologies on the late reply.

      The short answer is the Ravinder’s reply is correct. For a more detailed response, see below…

      This was an issue that was brought to my attention a little while ago and I resolve the problem in the sample code available on GitHub. I still maintain this project as issues arise in order to make sure this guide is as clear as possible.

      To that end, be sure to check out the project here:
      https://github.com/tommcfarlin/WordPress-Settings-Sandbox

      It’s got the full version of the source code as well as the the various versions that I released during this tutorial.

      I hope this helps!

  • http://stevebondy.ca Steve Bondy

    First off Tom let me say this is a fantastic tutorial – well written, thorough and easy to follow. I was looking for something like this last year and couldn’t find anything that gave me the clarity you have with this series. Glad I started looking again and found it.

    One little issue I had – I followed along through reordering the code, up to just before adding the Social options. At that point I discovered that the code was broken. I would get an error like
    Warning: Illegal string offset ‘show_header’ in …\themes\Sandbox\functions.php
    Warning: Illegal string offset ‘show_content’ in …\themes\Sandbox\functions.php
    Warning: Illegal string offset ‘show_footer’ in …\themes\Sandbox\functions.php

    It turns out that this error was caused by adding ‘sandbox_theme_display_options’ to the options table without giving it a value. If you change sandbox_initialize_theme_options as follows it will create and initialize the options, avoiding the error experienced by myself and others.

    function sandbox_initialize_theme_options() {
    // If the theme options don’t exist, create them.
    if( false == get_option( ‘sandbox_theme_display_options’ ) ) {
    $options = array(“show_header” => TRUE, “show_content” => TRUE, “show_footer” => TRUE);
    add_option( ‘sandbox_theme_display_options’, $options);
    } // end if

    If the old code has been run the empty ‘sandbox_theme_display_options’ value must be deleted from the database first. Alternatively, the following code will also detect this case and correct it.

    function sandbox_initialize_theme_options() {
    // See if the options exist, and initialize them if they don’t
    $options = get_option( ‘sandbox_theme_display_options’ );
    if( false == $options or $options == “” ) {
    $options = array(“show_header” => TRUE, “show_content” => TRUE, “show_footer” => TRUE);
    update_option( ‘sandbox_theme_display_options’, $options);
    } // end if

    This checks for non-existant or empty options values and initializes the options using update_option instead of add_option.

    Thanks again for the great tutorial.

    • Preston Davis

      Thank you, Steve.

      I ran into the same problem. I cannot understand why the author has not updated this article with your fix. Although I see in the code on GitHub that it is addressed, but if one is following along working out the examples (as the author suggests) then -at this point in the tutorial- they will hit this error. It is amazing that 5 months later the code sample is not yet updated.

      It pays to read the comments!

      Again, Great job!

    • http://twitter.com/tommcfarlin Tom McFarlin

      Steve – thanks so much for the kind words. I definitely aimed to make this series as comprehensive as possible while also providing working code.

      Unfortunately, a few issues did slip by.

      Though the Guest commenter below addressed this, the problem has been brought to my attention and I ended up resolving the issue on GitHub.

      To that end, I *do* still maintain this project. You can see it here:

      https://github.com/tommcfarlin/WordPress-Settings-Sandbox

      I try to be diligent in updating the changelog so that other people are capable of tracking what issues have been addressed.

      That said, I apologize for the errors you hit and hope it didn’t cause too much inconvenience! Thanks for the feedback :).

  • GJ

    Unless I missed it, and noticed a couple others experienced this, the tutorials are fairly good up to this #4 one which at the end, the checkboxes work in the backend and save, but the actions for the front of the site do not change. I thought perhaps I missed something but I decided to make sure, I copy and pasted your “this is what your functions should now look like” but this is still not working. So is there a solution to why this is happening?

  • Pingback: Andre's Web Dev: WP Class Blog » Adding Theme Options in the WordPress Admin Dashboard

  • http://twitter.com/P3pitoto Thomas Prinzivalli

    Hi. First of all, thanks a lot.

    I’m finally starting to understand how all of this works.

    I just have a couple of doubts about this article.

    1) looking at the codex, add_option doesn’t do anything if the option already exists. Knowing that, is
    if (false == get_option) really necessary?

    2) In the functions echoing the social inputs, you create a url variable but doesn’t use it. The code shouldn’t be

    echo ”;
    instead of

    echo ”;
    ?

    Once again, thanks a lot for this fantastic serie.

    • http://www.facebook.com/profile.php?id=504803282 Patrick Whitty-Clarke

      I don’t understand why the $url variable was created either. It isn’t referred to anywhere else in the code and the settings still function if you comment out the empty $url and the following if statement.

    • http://twitter.com/tommcfarlin Tom McFarlin

      Hey Thomas – all good points. Hopefully I can address them:

      1. This is a matter of coding style. If you’re comfortable with PHP and its dynamic nature, then this may seem verbose. For me, I tend to me more verbose because I think it makes the code clearer for new programmers (and this site covers a wide audience). So I’d say it’s not necessary, but it’s helpful.

      2. This is simply an overlooked typo. Technically, the code *should* use the `$url` variable because that ensures that we’re checking if the value exists in the option array and then safely echoing it into the value of the input.

      Just an oversight – good catch! Hopefully it didn’t cause too much trouble!

  • http://www.caercam.org/ Charlie Merland

    Brilliant paper! One thing though: you might want to consider adding a word in the conclusion precising that reading part 5 is needed to get the whole piece to work; lost some time figuring that out :)

    • http://twitter.com/tommcfarlin Tom McFarlin

      Thanks a lot, Charlie! Catching up on comments and doing what we can do make the conclusion to this article a bit cleaner. :)

  • Nico

    Un grand merci ! Works fine for me

  • http://www.facebook.com/profile.php?id=504803282 Patrick Whitty-Clarke

    May I suggest including the Authors Note in the article conclusion, just as a reminder that when you follow through the article and use the code exactly as specified the Display Options aren’t supposed to still function. I thought I’d broken something for 20 mins and was comparing code in WinMerge!

    • http://twitter.com/tommcfarlin Tom McFarlin

      Patrick – absolutely. This was my fault. I’ve explained it in a few other comments but that was a problem on my part. I’m looking to rectify the post as soon as possible and see if we can’t get this thing cleared up as it tripped up some others as well.

      Sorry about that!

  • Preston Davis

    The author needs to display the functions.php file at the end as he has done in the previous articles. With so much going into this segment many people are lost. Plus after completing this segment it does not work as certain options don’t save, etc. All this could be solved by simply showing what the functions.php file would look like at this point at the completion of this segment.

    • http://twitter.com/tommcfarlin Tom McFarlin

      Preston – thanks for the heads up. Apologies for the confusion!

      I’m going to have a note added to the end of this article as soon as I can to clear this up.

  • Pingback: WordPress | Annotary