By

Settings API Explained

Update: Plugin Options Starter Kit

I have now written a Plugin options starter kit for people who want to learn how to put together a Plugin options form using the WordPress Settings API. You can see more information about the Plugin and download the latest version here.

Have fun, and be sure to let me know what other features you want added in future versions!

In this post I discuss the WordPress Settings API and how you can leverage them for use in your own plugins. Firstly there is coverage of the API in general and the various functions available together with their usage. After that there is a tutorial section with an example walk through of how to add options to a new sub menu page under the Settings Menu.

The Settings API was first added in WordPress 2.7 to make things more efficient when it came to adding options pages for your plugins (and themes) to the WordPress admin area. Until recently I was still doing things the ‘old’ way which involved doing all the form creation and submission handling yourself, checking for $_POST variables, manual security checks etc. Whilst this worked fine it was also cumbersome and time consuming to implement, and making changes was awkward.

Adding new options to an existing plugin was not the breeze it should have been and you certainly thought twice before committing to a plugin options revision! Also, all of the security implementation had to be done manually too. Now, with the Settings API all the security checks are handled automatically which is a huge bonus! You don’t even need to create the options form table yourself if you don’t want to, this is taken care of for you too. Basically, all you have to do is to register your options with the Settings API and specify the input fields you wish to add to the options page, and away you go!

The motivation behind writing this post is to bring together the information on how the Settings API can really make your life easier when developing plugins. Now, there is a clean way you can construct your options pages quickly leaving you with more time to focus on the core plugin functionality. Also making amendments later on is no trouble at all now.

Settings API Functions

There are six general functions that you will be using to get the most out of the Settings API. For quick reference, all the functions you need to get started are listed below, with full parameter specification as defined in the core WordPress files. The coloured function parameters (red, green, blue) indicate how particular parameter values overlap between the various Settings API functions. Any coloured parameters should hold identical values between functions to make everything work properly. All the other parameters not coloured are allowed to have different values between functions (e.g. all the $callback parameters should contain different values as they refer to separate function calls).

  • settings_fields($option_group);
  • register_setting($option_group, $option_name, $sanitize_callback=“”);
  • unregister_setting($option_group, $option_name, $sanitize_callback=“”);
  • add_settings_section($id, $title, $callback, $page);
  • add_settings_field($id, $title, $callback, $page, $section, $args = array());
  • do_settings_sections($page)

The parameters and what they do are listed next:

  • $option_group – unique group name for option set
  • $option_name – name of each option (more than one option in the same register_settings() function requires an array of options)
  • $sanitize_callback=“” – section/field callback function to validate option data
  • $id – unique ID for the section/field
  • $title – the title of the section/field (displayed on options page)
  • $callback – callback function to be executed
  • $page – options page name (use __FILE__ if creating new options page)
  • $section – ID of the settings section (needs to be the same as $id in add_settings_section)
  • $args = array() – additional arguments

Let’s now go through each of these Settings API functions one by one. Firstly, is the settings_fields($option_group) function which renders the hidden input fields and handles the security aspects (nonce etc.).

This function has only one parameter, $option_group, used for the options group name which must be a unique string. This string is also used as the first parameter of register_setting() and unregister_setting() functions (discussed below) and should be identical in all three functions.

Next we have the register_settings() function. It job is to register the option(s) being defined with the Settings API, and accepts three parameters. The first one is the group options name which is the same as in the settings_fields() function. You can define different sets of group options with this option should you choose to but most often you will likely use only one option group.

The second parameter is the name of the option(s) you are registering which is the name used to store the options in the options db table. The option name can be a reference to a single option or an array of options. It is considered good practice to group all of your options into one option variable (i.e. all your individual options are stored in an array) which reduces the queries to the database.

The unregister_settings() function is very similar to the register_settings() function except that it unregisters (not surprisingly!) the option(s) in question from the Settings API.

Moving on, next we have the add_settings_section($id, $title, $callback, $page) function. This creates the ‘section’ for the options you will be adding. You can have more than one section on an options page but usually for most pages you will probably only need one. This time we have four parameters which are quite straightforward.

First is the section ID, which must be unique. Then we have the title of the section that is displayed on the options page. This is followed by the callback function that displays the actual HTML of the section (such as introductory information, perhaps donation links etc.). Finally we have the page parameter which needs to be the same string as specified in the add_settings_field(), and do_settings_sections() functions (covered below).

Next we have add_settings_field($id, $title, $callback, $page, $section, $args = array()), which is closely relate to the add_settings_section() function. This function adds the option field to the newly created section, and you will need to place a call to it in your code for every new option you wish to add. So, if you have three textboxes and two checkboxes on your options page you will need a total of five calls to the add_settings_field() function. Also it is worth mentioning that the order in which you place the calls to add_settings_field() will determine how the options are rendered on the options page. To re-order the options simply swap the function calls around! How easy is that?

The first four parameters are almost identical to the previous function where you specify a unique ID for each field added to the Settings API, give it a field title that is displayed on the page, a callback function that actually renders the field, and a page – which must be the same one specified in the add_settings_section() function. The fifth parameter specifies the ID of the settings section. This needs to be identical to ID you entered for the first parameter of add_settings_section() previously. Finally, the last parameter allows you to add additional arguments.

I think it is worth mentioning here that the ID used in the first parameter of the add_settings_field() function does NOT have to match the ID for the input field HTML tag specified in the callback function. Most of the time it is advisable to do so to keep things simple but, as you will see later on for radio buttons, the ID’s for the input field HTML tags can be different.

I initially thought that they had to be identical for everything to work properly but after experimentation this does not appear to be the case at all. This only seems to be useful when adding radio button options to your plugin – as it makes no logical sense to have multiple HTML input tags with identical ID’s. For other controls I would strongly suggest (for simplicity) that you match the add_settings_field() ID to the callback function HTML input field tag ID.

Finally we have the do_settings_sections($page) function. This function handles the rendering loop which displays all of your option input fields to the form on the options page. It has only one parameter, $page, which must be the same as specified in the last two functions: add_settings_section(), and add_settings_field().

OK, so that is all the major Settings API functions covered. We will now discuss the various input field types that can be added to options pages and how you can use these with the settings API. There are various input field types that you can use on your options pages (checkbox, radio button, dropdown box, textbox, password box, and textarea). To add each one requires the correct integration with the Settings API. Basically the callback function you specified in the add_settings_field() function is where you place HTML code to render the options. Let’s now go through all the input field types and see an example callback function for each option control type.

Drop Down Box

The code to add a drop down combo style box is:

1
2
3
4
5
6
7
8
9
10
function  setting_dropdown_fn() {
	$options = get_option('plugin_options');
	$items = array("Red", "Green", "Blue", "Orange", "White", "Violet", "Yellow");
	echo "<select id='drop_down1' name='plugin_options[dropdown1]'>";
	foreach($items as $item) {
		$selected = ($options['dropdown1']==$item) ? 'selected="selected"' : '';
		echo "<option value='$item' $selected>$item</option>";
	}
	echo "</select>";
}

Here, we basically get the current state of the option from the db, then loop through the available color options. If we find the one that is selected then we render it as the selected item otherwise it is added to the list of available options (but not selected). This good thing about this method is that it is nice and scalable for any number of options.

Textarea

The code to add a textarea control is:

1
2
3
4
function setting_textarea_fn() {
	$options = get_option('plugin_options');
	echo "<textarea id='plugin_textarea_string' name='plugin_options[text_area]' rows='7' cols='50' type='textarea'>{$options['text_area']}</textarea>";
}

Nice and easy this one. Just get the current value (if any), and add it to the textarea!

Textbox (single line)

The code to add a textbox control is:

1
2
3
4
function setting_string_fn() {
	$options = get_option('plugin_options');
	echo "<input id='plugin_text_string' name='plugin_options[text_string]' size='40' type='text' value='{$options['text_string']}' />";
}

Another easy this one. Again, just get the current value (if any), and add it to the textbox!

Password Textbox (single line)

The code to add a password textbox control is:

1
2
3
4
function setting_pass_fn() {
	$options = get_option('plugin_options');
	echo "<input id='plugin_text_pass' name='plugin_options[pass_string]' size='40' type='password' value='{$options['pass_string']}' />";
}

This one is pretty much identical to the textbox except the input is masked.

Checkbox

The code to add a checkbox control is:

1
2
3
4
5
function setting_chk1_fn() {
	$options = get_option('plugin_options');
	if($options['chkbox1']) { $checked = ' checked="checked" '; }
	echo "<input ".$checked." id='plugin_chk1' name='plugin_options[chkbox1]' type='checkbox' />";
}

To add a checkbox we need to toggle the setting each time it is selected (and if the Save Changes option button has been clicked). For each checkbox we check the db option setting to see if it has been selected/deselected, and if so then the control needs to be updated.

Radio Buttons

The code to add radio button controls is:

1
2
3
4
5
6
7
8
function setting_radio_fn() {
	$options = get_option('plugin_options');
	$items = array("Square", "Triangle", "Circle");
	foreach($items as $item) {
		$checked = ($options['option_set1']==$item) ? ' checked="checked" ' : '';
		echo "<label><input ".$checked." value='$item' name='plugin_options[option_set1]' type='radio' /> $item</label><br />";
	}
}

To add radio buttons you obviously need more than one, or there isn’t much point to using them! So, you need to define a ‘set’ of buttons. Here I have created an example of a set of shapes. The current db option is obtained as usual and stored in $options. Similar to the drop down box, each possible option is looped through until the db selected option is encountered. The db item is rendered to the screen as selected, and the other options are just displayed unchecked.

OK, enough theory.. Now for the REALLY good stuff – the tutorial! The following section covers a complete walk through on how to add options to a new page under the Settings Menu. The full code is given at the end for you to copy/paste into your own plugins as a starting template.

Tutorial – Adding a New Options Page

OK, let’s build a complete options page using the new Settings API armed with all the theory we have learned so far. First though, a couple of notes. All the callback functions have been added with a suffix of '_fn' to denote it refers to a callback function. There are a lot of parameters flying around so I think it is useful to know just at a glance what is a callback function and what is an ID etc. Also, the tutorial uses the method of storing all page options in a single options db table entry, which is a cleaner way to add your options to the WordPress db.

You will see the PHP ‘magic constant’ __FILE__ pop up a fair bit. All this fancy looking variable does is use the plugin file name as a constant in a place where a unique string is needed. __FILE__ is considered the correct constant to use in these circumstances as (hopefully!) your plugin name should be pretty much unique compared to the other plugins under your WordPress installation. Also, it has the added advantage that it saves us from having to come up with unique page names.

The first thing we need to do is create our sub menu page under the top level Settings Menu. This is done by using the add_options_page('Options Example Page', 'Options Example', 'administrator', __FILE__, 'options_page_fn') function. Here the parameters (in order) are: page title (displayed in the browser title bar), options menu title (displayed as a link in the Settings Menu), access level (who can access the options page), unique page name, and callback function to display the options form. The code to add the new menu page plus action hook is:

1
2
3
4
5
add_action('admin_menu', 'sampleoptions_add_page_fn');
// Add sub page to the Settings Menu
function sampleoptions_add_page_fn() {
	add_options_page('Options Example Page', 'Options Example', 'administrator', __FILE__, 'options_page_fn');
}

While we are at it let’s define the options_page_fn() callback function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function options_page_fn() {
?>
	<div class="wrap">
		<div class="icon32" id="icon-options-general"><br></div>
		<h2>My Example Options Page</h2>
		Some optional text here explaining the overall purpose of the options and what they relate to etc.
		<form action="options.php" method="post">
		<?php settings_fields('plugin_options'); ?>
		<?php do_settings_sections(__FILE__); ?>
		<p class="submit">
			<input name="Submit" type="submit" class="button-primary" value="<?php esc_attr_e('Save Changes'); ?>" />
		</p>
		</form>
	</div>
<?php
}

OK, so what is going on here? Well the nice thing about the Settings API is that it handles a lot of the tedious set up and rendering of your options form. The form is pretty standard apart from the two function calls inside the opening/closing form tags. Firstly is the settings_fields('plugin_options'); function which basically outputs all the hidden fields automatically (including all the security stuff – nonces etc.). Next is the do_settings_sections(__FILE__) function which handles the rendering of the form input field HTML tags (it works by cycling through all the defined add_settings_field() functions and executing each callback function in turn to output the HTML).

To complete the setup (yes it is nearly done!) all you need to do is register your settings, and define the fields you wish to add to your options page plus the callback functions that define the HTML rendering of the option fields. So let’s do that now.

We place all the code to register settings in a callback function that runs when the ‘admin_init’ hook fires. The code looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
add_action('admin_init', 'sampleoptions_init_fn' );
// Register our settings. Add the settings section, and settings fields
function sampleoptions_init_fn(){
	register_setting('plugin_options', 'plugin_options', 'plugin_options_validate' );
	add_settings_section('main_section', 'Main Settings', 'section_text_fn', __FILE__);
	add_settings_field('plugin_text_string', 'Text Input', 'setting_string_fn', __FILE__, 'main_section');
	add_settings_field('plugin_text_pass', 'Password Text Input', 'setting_pass_fn', __FILE__, 'main_section');
	add_settings_field('plugin_textarea_string', 'Large Textbox!', 'setting_textarea_fn', __FILE__, 'main_section');
	add_settings_field('plugin_chk2', 'A Checkbox', 'setting_chk2_fn', __FILE__, 'main_section');
	add_settings_field('radio_buttons', 'Select Shape', 'setting_radio_fn', __FILE__, 'main_section');
	add_settings_field('drop_down1', 'Select Color', 'setting_dropdown_fn', __FILE__, 'main_section');
	add_settings_field('plugin_chk1', 'Restore Defaults Upon Reactivation?', 'setting_chk1_fn', __FILE__, 'main_section');
}

Here we need to register our settings with the Settings API, and we do this with the register_setting('plugin_options', 'plugin_options', 'plugin_options_validate' ) function. The first parameter is the option group name that identifies all of the options in the set. The second parameter is the options name that is stored in the options db. Finally the third function defines an optional validation function that can be used to validate user input (more on this later).

Next we define our section that will contain all our option fields: add_settings_section('main_section', 'Main Settings', 'section_text_fn', __FILE__). The parameters in order are: ID for the section, title (to be displayed on the options page), callback function (that displays section information), and finally – page (defined as the constant __FILE__).

After the section definition we now define the individual fields themselves. Each option on your form needs to have a call made to a add_settings_field('plugin_text_string', 'Text Input', 'setting_string_fn', __FILE__, 'main_section') function. The parameters are similar to the add_settings_section('main_section', 'Main Settings', 'section_text_fn', __FILE__) function. They are specified as: field ID, title of field (displayed on the left hand side of the options page), callback function (to render the input field), page name (__FILE__ constant again), and finally a reference to the section name (same as the first parameter in the add_settings_section function).

Each callback function in add_settings_field() needs to be defined. We showed all the possible types earlier and so for completeness lets just show the callback function here for the field defined above:

1
2
3
4
function setting_string_fn() {
	$options = get_option('plugin_options');
	echo "<input id='plugin_text_string' name='plugin_options[text_string]' size='40' type='text' value='{$options['text_string']}' />";
}

Now we know how to structure the options page setup lets look at another couple of important points. Firstly, how do we specify default options? Also, how do we use the validate callback function defined in register_settings(). Firstly the default options…

To set these up we need to specify a hook that runs when the plugin is activated on the plugins page. This will trigger a callback function to be executed (which we specified in the hook definition). The callback function will then add the required defaults to the options db, as follows:

1
2
3
4
5
6
register_activation_hook(__FILE__, 'add_defaults_fn');
// Define default option settings
function add_defaults_fn() {
    $arr = array("dropdown1"=>"Orange", "text_area" => "Space to put a lot of information here!", "text_string" => "Some sample text", "pass_string" => "123456", "chkbox1" => "", "chkbox2" => "on", "option_set1" => "Triangle");
    update_option('plugin_options', $arr);
}

Taking this one step further, it would be nice if we could specify one of our options as a switch to determine if we ALWAYS want the options db entries reset every time the plugin is first activated (or whenever the plugin is deactivated/reactivated), or whether to leave them as they were as sometimes you will want to temporarily want to deactivate a plugin but not delete/reset the options. This way, the next time you activate the plugin the options will still be there. Anyway, it is a nice feature to give to your users. The following modified add_defaults_fn() function handles this:

1
2
3
4
5
6
7
function add_defaults_fn() {
	$tmp = get_option('plugin_options');
    if(($tmp['chkbox1']=='on')||(!is_array($tmp))) {
		$arr = array("dropdown1"=>"Orange", "text_area" => "Space to put a lot of information here!", "text_string" => "Some sample text", "pass_string" => "123456", "chkbox1" => "", "chkbox2" => "on", "option_set1" => "Triangle");
		update_option('plugin_options', $arr);
	}
}

Now the options db entries only ever get overwritten if the checkbox option to restore the defaults is checked. Also, the very first time the plugin is activated then there will be no current options to overwrite and this is where the second test in the line if(($tmp['chkbox1']=='on')||(!is_array($tmp))) comes in. Basically, if there is no options in the db then it creates them with default settings.

OK, deep breath, nearly done. Finally, it is worth showing you how to use the validation feature built in to the Settings API. If you remember this is specified as a callback function in the third parameter of the register_settings() function.

Note: Remember that if you define this callback function in register_settings() then you must define this function in code otherwise you may get unexpected results (as WordPress will not be able to find a function that it is expecting). The first time I specified a validation function I forgot to implement it and it took me a while to realise what I had done wrong, and why the options form was not displaying properly!

The validation feature is a really nice touch of the Settings API and is worth using, it can be very powerful too. As a simple example, for the textbox we defined, how would you like a super easy way to validate input so that no HTML tags were allowed. If any are found then they are simply striped out! Nice eh? The code to do this is as follows:

1
2
3
4
5
function plugin_options_validate($input) {
	// Check our textbox option field contains no HTML tags - if so strip them out
	$input['text_string'] =  wp_filter_nohtml_kses($input['text_string']);	
	return $input; // return validated input
}

This is especially nice as we can test some/all of our options and validate them as we see fit. In the above example we are only testing the text field but we can easily extend this and add whatever validation code we like!

OK, let’s now add the rest of our input field options as we defined earlier in the post. Putting everything we have learned together, the finished options page looks something like the image below.

The full code to render the complete options page can be found here. To test this in your own WordPress installation simply copy/paste the code into a new text file, save it and upload as a new plugin. You can then begin to experiment and build your own options pages from this example template. Enjoy..! :)

In Summary

In this post we looked at the Settings API available in WordPress which makes it much(!) easier to add/maintain options for your plugins. The tutorial showed an example of adding a new sub page to the Settings Menu. Adding new options to existing menu pages as well as adding new sub pages to other top level menus (such as the Tools Menu, and Appearance Menu) are fairly straight forward. These will be covered in another post on the Settings API.

I hope you have found this post helpful, if so I would love to hear your comments…

You can also follow me on twitter.

Below are some helpful links I came across whilst compiling this post: