Adding Settings to an Existing Page Using the Settings API


When it comes to saving options and settings in WordPress, you all probably know and use the Options API: add_option(), update_option(), get_option() and delete_option(). These functions are the first layer of another, broader and powerful, yet under-used, API: the Settings API.

Goal of this Tutorial

Some time ago I wrote a quick tutorial on how to use the Settings API to create your plugins' option pages, and then Otto went deeper in it with a more detailed WordPress Settings API Tutorial.

Most tutorials you'll find out there will show you how to create an option page for your plugin. In this tutorial we'll take a different approach and will use the Settings API to add fields to an existing page.

Scenario: you're making a neat site & theme for a client. The client's Boss wants to receive an email every time a new post is published, so you'll add an option in the Writing Settings page where the blog admin will enter the Boss' email address. The end result will be something like the following:

Throughout this tutorial we'll use 'ozhwpe' (for 'Ozh WP Engineer') as a prefix for functions and everything. I'll try to make code snippets as self explanatory as possible with comments for quick cut-and-paste, and then add more explanations for the first time understanding.

Enough chit-chat, it's time to write actual code!

Register and Define the New Setting

The function register_setting() tells WordPress that you'll need the Settings API and whitelists new options, and then add_settings_field() defines the input fields to be used.

// Register and define the settings
add_action('admin_init', 'ozhwpe_admin_init');

function ozhwpe_admin_init(){
	register_setting(
		'writing',                 // settings page
		'ozhwpe_options',          // option name
		'ozhwpe_validate_options'  // validation callback
	);
	
	add_settings_field(
		'ozhwpe_notify_boss',      // id
		'Boss Email',              // setting title
		'ozhwpe_setting_input',    // display callback
		'writing',                 // settings page
		'default'                  // settings section
	);

}

Let's dissect this a bit.

The three string parameters for register_setting() are:

  1. $option_group: the setting group name, or its "page". It's either created by you (for instance on a standalone plugin option page) or, in this case, an existing group. We'll target the 'writing' group, which as its name suggest belongs to the "Writing Settings" page.
  2. $option_name, here 'ozhwpe_options': the option name, as you would use it in a get_option()
  3. $sanitize_callback: an optional callback function used to sanitize the input. We'll define that function later

The function add_settings_field() is passed five parameters, which are:

  1. $id: the field id. Make it a unique string
  2. $title: the title of the setting, printed right next to the field
  3. $callback, here 'ozhwpe_setting_input': the callback function that will echo the form field itself
  4. $page: the settings page on which to show the field, here 'writing'
  5. $section: the section of the settings page in which to add the field, with a value of 'default' if omitted. The section is either an existing section in a page, or a whole new section defined using add_settings_section(). Here we'll add our field in the 'default' section of the 'writing' page.

Core Settings Pages and Sections Names

We've decided to target the 'default' section of the 'writing' Settings page, but of course it's possible to add form fields to all other existing page. Here is a comprehensive list of all these places:

Settings Page Settings Name Sections
General Settings 'general' 'default'
Writing Settings 'writing' 'default'

'remote_plublishing'

'post_via_email'
Reading Settings 'reading' 'default'
Discussion Settings 'discussion' 'default'
'avatars'
Media Settings 'discussion' 'default'
'embeds'
'uploads'
Privacy Settings 'privacy' 'default'
Permalink Settings 'permalink' 'optional'

First Callback: Display the Field

In the first code block, we've referred to a function callback 'ozhwpe_setting_input' that would display the field itself. Let's define that function now:

// Display and fill the form field
function ozhwpe_setting_input() {
	// get option 'boss_email' value from the database
	$options = get_option( 'ozhwpe_options' );
	$value = $options['boss_email'];
	
	// echo the field
	?>
<input id='boss_email' name='ozhwpe_options[boss_email]'
 type='text' value='<?php echo esc_attr( $value ); ?>' /> Boss wants to get a mail when a post is published
	<?php
}

Nothing hardcore here: we read any existing value of the field using get_option() and display an simple HTML text field.

Still, note the fine "array trick": options are stored in a new wp_options table entry named 'ozhwpe_options', which will contain an array of every values you'll have to save (see the HTML name of the input field). Remember: every time you save multiple options each in their own DB entry, God kills a narwhal. Please, all in one array.

While we're at it, also notice that $value is escaped before printing, using esc_attr() in order to kill any XSS joke you might want to attempt. But, again, that's nothing new to you, right? Right?

Second Callback: Validation Function

The first function call mentions 'ozhwpe_validate_options' as a callback that will validate the input. You don't want bogus stuff to be stored in your DB, do you?

// Validate user input and return validated data
function ozhwpe_validate_options( $input ) {
	$valid = array();
	$valid['boss_email'] = sanitize_email( $input['boss_email'] );
	return $valid;
}

To validate the user input, we're using here sanitize_email(), a built-in WordPress function that makes sure a string looks like an email.

Best Practice Alert™: when you want to validate or sanitize an array, pick and validate only selected parts of it and return a 100% safe new array. Naming it $valid and initializing it as an empty array is a good hint for code readers that you're doing it right and paying attention to security.

The opposite and sucky way would be here to validate $input['boss_email'] and return $input, which is sucky because this array may also contain $input['evilstring'].

Second Callback v2.0: Validation Function And Error Feedback

While we're at it, let's improve a bit things: using add_settings_error() we will display a nice error message if input data does not pass the validation test, so the user knows that we know that he is a dummy.

// Validate user input
function ozhwpe_validate_options( $input ) {
	$valid = array();
	$valid['boss_email'] = sanitize_email( $input['boss_email'] );
	
	// Something dirty entered? Warn user.
	if( $valid['boss_email'] != $input['boss_email'] ) {
		add_settings_error(
			'ozhwpe_boss_email',           // setting title
			'ozhwpe_texterror',            // error ID
			'Invalid email, please fix',   // error message
			'error'                        // type of message
		);		
	}
	
	return $valid;
}

The function add_settings_error() takes up to 4 parameters:

  1. $setting: the slug title of the setting that triggered the error
  2. $code: an error ID, that will be used in the HTML ID of the printed <div>
  3. $message: the error message itself, that will wrapped inside a <div> and <p>.
  4. $type: this optional parameter with a default value of 'error' controls the HTML class name for the error <div>. Core styles are 'error' or 'updated'.

Now, if Clueless Joe enters something that does not look like a valid email, he'll get an error message as shown here:

All Done

Let's review the whole entire plugin:

<?php
/*
Plugin Name: Settings API Example
Plugin URI: http://wpengineer.com/
Description: Complete and practical example of use of the Settings API to add fields on the Writing option page
Author: Ozh
Author URI: http://ozh.org/
*/

// We'll use ozhwpe (Ozh / WP Engineer) as a prefix throughout the plugin

// Register and define the settings
add_action('admin_init', 'ozhwpe_admin_init');
function ozhwpe_admin_init(){
	register_setting(
		'writing',                 // settings page
		'ozhwpe_options',          // option name
		'ozhwpe_validate_options'  // validation callback
	);
	
	add_settings_field(
		'ozhwpe_notify_boss',      // id
		'Boss Email',              // setting title
		'ozhwpe_setting_input',    // display callback
		'writing',                 // settings page
		'default'                  // settings section
	);

}

// Display and fill the form field
function ozhwpe_setting_input() {
	// get option 'boss_email' value from the database
	$options = get_option( 'ozhwpe_options' );
	$value = $options['boss_email'];
	
	// echo the field
	?>
<input id='boss_email' name='ozhwpe_options[boss_email]'
 type='text' value='<?php echo esc_attr( $value ); ?>' /> Boss wants to get a mail when a post is published
	<?php
}

// Validate user input
function ozhwpe_validate_options( $input ) {
	$valid = array();
	$valid['boss_email'] = sanitize_email( $input['boss_email'] );
	
	// Something dirty entered? Warn user.
	if( $valid['boss_email'] != $input['boss_email'] ) {
		add_settings_error(
			'ozhwpe_boss_email',           // setting title
			'ozhwpe_texterror',            // error ID
			'Invalid email, please fix',   // error message
			'error'                        // type of message
		);		
	}
	
	return $valid;
}

And that's it for the user interface part. Of course, the plugin now needs to have a few add_action() to trigger the email sending part when a new post is published, but this is outside of the scope of this tutorial.

We're done with the coding, but here are a couple of design considerations now.

Why Use The Settings API

The benefits of using the Settings API over manually coding yourself your whole plugin page and checking $_POST for data submission are pretty chunky: you focus just on defining what you need, and let WordPress manage the repetitive parts, which are:

  • output most of the HTML (<table> rows and stuff).
  • monitor form submission and take care of $_POST data
  • create and update options in the database if necessary
  • include all the anti-XSS and anti-CSRF stuff (that is, nonces and referrer checks)

I find the first point alone enough as a reason why freelancers and web agencies should use the Settings API: future proofness. When the interface of WordPress will change in 10 years (new styles, new colors, new CSS class names), your plugin interface will still flawlessly integrate because you did not hardcode any styled HTML.

And, obviously, the 4th point alone is enough as a reason why everybody should use this API: implement all the security measure without even thinking of them.

Adding Fields to an Existing Page or Making a New Page?

If you're making a plugin that will be downloaded by users, I think it's often best to make a standalone new option page. WordPress users are mostly used to installing plugins and then look for a new menu where they'll configure it, and it's certainly more convenient than forcing them to check every Settings page for new fields that may have appeared.

On the contrary, if you're delivering a key-in-hand CMS solution for a client, it would not make sense to split settings that relate over different pages. For such a client, what you're giving them is a consistant whole and there is no such thing as core features vs plugin feature, so adding fields to appropriate existing Settings pages makes much more sense.

Guest Post

This post was written byOzh, he has been using and hacking with WordPress since may 2004 on version 1.0.1, and released his first WordPress plugin more than 6 years ago. His passion for plugins recently shaped up in writing a dedicated book with Brad Williams and Justin Tadlock: Professional WordPress Plugin Development will hit the shelves in March 2011. If you like this article, you will love the book!

Control WordPress Nav Menu via Custom Fields


WordPress 3.0 also includes the menus, in addition to a number of other new features. Normally you generate a menu automatically by creating pages, but under Appereance / Menus you can create your own menu and assign a place where it replaces the normal menu.

But whoever currently uses WordPress as a CMS and blog (like me) may now have a problem: If you look at the blog you should be able to see the normal pages in the navigation, but if you then select a page with many subpages it would be nice if the subpages were listed in the menu bar as well as listing only one entry "Blog" which brings you back to the blog page.

Conveniently, WordPress already includes everything you need to put together this idea. They are on the one hand of course the aforementioned menus, and on the other side there are Custom Fields. If you have not hidden, you can find at every post and every page an editor where you can enter meta data. Via this meta data and with the help of a small code snippet you can control what is displayed in the menu.

You can put the code snippet in your functions.php of your Theme or in a Plugin.

//Filter the arguments for the wp_nav_menu_function to include a custom menu on pages.
function nr_2010_wp_nav_menu_args($args = '')
{
	if ( is_page() ) //custom menus only on sites
	{
		global $post;

		$menu_name = get_post_meta($post->ID, 'menu_name', true);

		if( !empty($menu_name) && is_nav_menu($menu_name) )
		{
			$args['menu'] = $menu_name;
		}
	}
	return $args;
}
add_filter( 'wp_nav_menu_args', 'nr_2010_wp_nav_menu_args' );

This code puts a filter on wp_nav_menu_args, these are the parameters of the function wp_nav_menu, which the Theme uses to output the menus.

As long as we are on a page (is_page()) the custom field menu_name will be used. If it's not empty and it has a menu with this name, the paramaters of this call will be modified, so it shows the right menu.

Only one thing left: Provide the correct meta data to the pages and create the corresponding menus. The menu editor is very intuitive, it shouldn't be a problem. Just a hint: It doesn't save automatically. ;)

Guest Post

This post was written by Niklas Rother - niklas-rother.de and is a post in our Advent Calendar Series on WP Engineer about WordPress.
Thank you very much from my part to Niklas.
If you also like to have your interesting post published on our website, please let us know on our contact page. Of course we will appreciate your contribution!

wp-exif-bsp

The WordPress Exif-Meta-Datas


WordPress stores when uploading files, some data in the meta data - the metadata. If there are pictures involved and these files contain EXIF data, then it outputs some EXIF data which can be used. For example, as additional information about the picture in a photo blog.

No matter what, and how you want to do that, here is a small function that reads the Exif data.
Continue reading …

Create Users Automatically In WordPress

WordPress is a web application by many - often you have to create a bridge to other systems. For example, to give users a single sign-on via the web applications. Therefore, automated creating users might be necessary. Depending on which system is the leader, the data must be synchronized. From past experience, the users are usually created in WordPress, as this is not the leading system for user administration. WordPress has the function wp_insert_user() for the automated creation of users.
Continue reading …

Remove WordPress Multisite Feature – ToDo List!

You can activate the Multisite functionality of WordPress relatively quick and it is well explaind in the backend how to do it. But not always you like to keep it as a multi site installation and therefore you have to do some measures to remove the Network function again.

Not easy and probably not an every day task - so that's why I created a small todo list in order not to forget a step to restore to the default installation.
Continue reading …