The WordPress Settings API, Part 4: On Theme Options

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:

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.

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:

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:

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:

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:

Step 2 Refactor Our Sections

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

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."

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:

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:

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:

Step 4 Register the New Setting

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

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:

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:

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:

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:

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:

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:


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!

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

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:

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:

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:

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:

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

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:

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:

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:

Next, we'll define their two callbacks:

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:


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

Tags:

Comments

Related Articles