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
- 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...
- Read the collection of options from WordPress
- Access the individual option from the collection
- 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:
- settings_fields takes care of rendering several security measures for the options form.
- 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:
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:
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.
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.
Comments