Skip to content

Simplifying the Settings API for WordPress

They suckered me into helping redesign the website for my daughter's school (don't worry—if you're looking at it before July 27, 2012 it's not my design), and they wanted to go with a WordPress site so lots of people could edit it easily. It seemed like a good idea, but it took 4 weeks of tweaking to get it to the point that everyone liked it.

One thing I wanted to do was have a single settings page for all the relevant options (secretary's email, reCaptcha info for forms), etc. While there are tutorials out there, nothing really hung together for me until I started playing with it. To make it simple, I created a single function that would implement all the settings. It uses PHP 5.3, with namespaces and closures (both of which make life much easier by not polluting the global namespace).

The key is to put all the plugin's code under a single unique namespace, and use that namespace as the index for all the options.

Calling it is simple:

namespace MyUniqueName;
$title="Title for the Plugins Page (short enough to fit in the settings menu)";
plugin_settings($title, $defaults);

$defaults is an array of 'key' => $array, where the $array is array('default value', 'descriptive text', 'callback function'), where 'callback function' is the callable used to display the input element for that option. It can be a string representing the name of the function (prepended with the namespace!) or an anonymous function itself. It is passed a single argument, array('key' => $key), where $key is the key used in the original array.

Use the options as $options = get_option(__NAMESPACE__); $my_option = $options[$key];.

A simple callback function for a text box is:

function text_callback($args){
	$name = $args['key'];
	$ns = __NAMESPACE__; // note that __NAMESPACE__ is used as the index for the options in the database
	$options = get_option($ns);
	echo "<input name='{$ns}[$name]' value='{$options[$name]}' />";		
};

And to make it simpler, I created a function generator to do exactly this: textbox($size=40) returns a callback function to output a text box of $size characters.

So a sample $defaults would be:

$defaults = array(
  'email' => array('info@example.org', 'Main Email Address', textbox()),
  'principal' => array('Ed Rooney', 'Principal\'s Full name', textbox(80))
);

The code for the function is (note that it has to go in the main plugin file, since it uses __FILE__) (unfortunately it mixes HTML and PHP and confuses the syntax highighter terribly):

function plugin_settings ($title, $defaults){

	register_activation_hook(__FILE__, function() use ($defaults) {
		$options = get_option(__NAMESPACE__);
		if (empty($options)) $options = array();
		foreach ($defaults as $key => $option){
			if (!isset($options[$key])) $options[$key] = $option[0];
		}
		update_option (__NAMESPACE__, $options);
	});

	// this gets put in the database, so it can't use a closure
	register_uninstall_hook(__FILE__, create_function('', 'delete_option('.__NAMESPACE__.');'));

	add_action('admin_menu', function () use ($title, $defaults) {
		if (wp_verify_nonce($_POST['_wpnonce'], __NAMESPACE__.'-reset')){
			$options = get_option(__NAMESPACE__);
			foreach ($defaults as $key=>$option){
				$options[$key] = $option[0];
			}
			update_option(__NAMESPACE__, $options);
		}
		add_options_page($title, $title, 'manage_options', __NAMESPACE__, function() use ($title){
			?><h2><?=$title?></h2>
			<form method=POST action="options.php">
				<? settings_fields(__NAMESPACE__); ?>
				<? do_settings_sections(__NAMESPACE__); ?>
				<input name="Submit" type="submit" value="<? esc_attr_e('Save Changes'); ?>" />
			</form>
			<form method=POST>
				<? wp_nonce_field(__NAMESPACE__.'-reset'); ?>
				<input name="Reset" type="submit" value="<? esc_attr_e('Revert to Defaults'); ?>" />
			</form><?
		});
	});

	add_action('admin_init', function () use ($defaults) {
		$ns = __NAMESPACE__; // to save typing and allow in-string replacement
		register_setting($ns, $ns);
		add_settings_section('', '', function(){}, $ns);
		foreach ($defaults as $key => $option){
			if (isset($option[2])){
				add_settings_field("$ns_$key", $option[1], $option[2], $ns, '', array('label_for'=>"$ns_$key", 'key'=>$key));
			}
		}
	});
}

function textbox($size=40){
	// returns a textbox generator
	return function($args) use ($size) {
		$name = $args['key'];
		$ns = __NAMESPACE__;
		$options = get_option($ns);
		echo "<input name='{$ns}[$name]' value='{$options[$name]}' size=$size />";		
	};
}

Post a Comment

Your email is never published nor shared. Required fields are marked *