{"id":2426,"date":"2012-07-25T14:04:04","date_gmt":"2012-07-25T20:04:04","guid":{"rendered":"http:\/\/bililite.com\/blog\/?p=2426"},"modified":"2012-07-31T14:28:57","modified_gmt":"2012-07-31T20:28:57","slug":"simplifying-the-settings-api-for-wordpress","status":"publish","type":"post","link":"https:\/\/bililite.com\/blog\/2012\/07\/25\/simplifying-the-settings-api-for-wordpress\/","title":{"rendered":"Simplifying the Settings API for WordPress"},"content":{"rendered":"<p>They suckered me into helping redesign the website for <a href=\"http:\/\/eha.org\">my daughter's school<\/a> (don't worry&mdash;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.<\/p>\r\n<p>One thing I wanted to do was have a single <a href=\"http:\/\/codex.wordpress.org\/Settings_API\">settings page<\/a> for all the relevant options (secretary's email, <a href=\"https:\/\/developers.google.com\/recaptcha\/\">reCaptcha<\/a> info for forms), etc. While there are <a href=\"http:\/\/ottopress.com\/2009\/wordpress-settings-api-tutorial\/\">tutorials<\/a> 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 <a href=\"http:\/\/lphp.net\/manual\/en\/language.namespaces.php\">namespaces<\/a> and <a href=\"http:\/\/php.net\/manual\/en\/functions.anonymous.php\">closures<\/a> (both of which make life much easier by not polluting the global namespace).<\/p>\r\n<p>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.<\/p>\r\n<p>Calling it is simple:<\/p>\r\n<pre><code class=\"language-php\">namespace MyUniqueName;\r\n$title=\"Title for the Plugins Page (short enough to fit in the settings menu)\";\r\nplugin_settings($title, $defaults);\r\n<\/code><\/pre>\r\n<p><code>$defaults<\/code> is an array of <code>'key' =&gt; $array<\/code>, where the <code>$array<\/code> is <code class=\"language-php\">array('default value', 'descriptive text', 'callback function')<\/code>, where 'callback function' is the <a href=\"http:\/\/www.php.net\/manual\/en\/language.types.callable.php\">callable<\/a> used to <a href=\"http:\/\/codex.wordpress.org\/Function_Reference\/add_settings_field#Parameters\">display the input element<\/a> 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, <code class=\"language-php\">array('key' =&gt; $key)<\/code>, where <code>$key<\/code> is the key used in the original array.<\/p>\r\n<p>Use the options as <code class=\"language-php\">$options = get_option(__NAMESPACE__); $my_option = $options[$key];<\/code>.<\/p>\r\n<p>A simple callback function for a text box is:<\/p>\r\n<pre><code class=\"language-php\">function text_callback($args){\r\n\t$name = $args['key'];\r\n\t$ns = __NAMESPACE__; \/\/ note that __NAMESPACE__ is used as the index for the options in the database\r\n\t$options = get_option($ns);\r\n\techo &quot;&lt;input name='{$ns}[$name]' value='{$options[$name]}' \/&gt;&quot;;\t\t\r\n};<\/code><\/pre>\r\n<p>And to make it simpler, I created a function generator to do exactly this: <code>textbox($size=40)<\/code> returns a callback function to output a text box of <code>$size<\/code> characters.<\/p>\r\n<p>So a sample <code>$defaults<\/code> would be:<\/p>\r\n<pre><code class=\"language-php\">$defaults = array(\r\n  'email' =&gt; array('info@example.org', 'Main Email Address', textbox()),\r\n  'principal' =&gt; array('Ed Rooney', 'Principal\\'s Full name', textbox(80))\r\n);<\/code><\/pre>\r\n<!--more-->\r\n<p>The code for the function is (note that it has to go in the <a href=\"http:\/\/codex.wordpress.org\/Writing_a_Plugin#Plugin_Files\">main plugin file<\/a>, since it uses <code>__FILE__<\/code>) (unfortunately it mixes HTML and PHP and confuses the syntax highighter terribly):<\/p>\r\n<pre><code>function plugin_settings ($title, $defaults){\r\n\r\n\tregister_activation_hook(__FILE__, function() use ($defaults) {\r\n\t\t$options = get_option(__NAMESPACE__);\r\n\t\tif (empty($options)) $options = array();\r\n\t\tforeach ($defaults as $key =&gt; $option){\r\n\t\t\tif (!isset($options[$key])) $options[$key] = $option[0];\r\n\t\t}\r\n\t\tupdate_option (__NAMESPACE__, $options);\r\n\t});\r\n\r\n\t\/\/ this gets put in the database, so it can't use a closure\r\n\tregister_uninstall_hook(__FILE__, create_function('', 'delete_option('.__NAMESPACE__.');'));\r\n\r\n\tadd_action('admin_menu', function () use ($title, $defaults) {\r\n\t\tif (wp_verify_nonce($_POST['_wpnonce'], __NAMESPACE__.'-reset')){\r\n\t\t\t$options = get_option(__NAMESPACE__);\r\n\t\t\tforeach ($defaults as $key=&gt;$option){\r\n\t\t\t\t$options[$key] = $option[0];\r\n\t\t\t}\r\n\t\t\tupdate_option(__NAMESPACE__, $options);\r\n\t\t}\r\n\t\tadd_options_page($title, $title, 'manage_options', __NAMESPACE__, function() use ($title){\r\n\t\t\t?&gt;&lt;h2&gt;&lt;?=$title?&gt;&lt;\/h2&gt;\r\n\t\t\t&lt;form method=POST action=&quot;options.php&quot;&gt;\r\n\t\t\t\t&lt;? settings_fields(__NAMESPACE__); ?&gt;\r\n\t\t\t\t&lt;? do_settings_sections(__NAMESPACE__); ?&gt;\r\n\t\t\t\t&lt;input name=&quot;Submit&quot; type=&quot;submit&quot; value=&quot;&lt;? esc_attr_e('Save Changes'); ?&gt;&quot; \/&gt;\r\n\t\t\t&lt;\/form&gt;\r\n\t\t\t&lt;form method=POST&gt;\r\n\t\t\t\t&lt;? wp_nonce_field(__NAMESPACE__.'-reset'); ?&gt;\r\n\t\t\t\t&lt;input name=&quot;Reset&quot; type=&quot;submit&quot; value=&quot;&lt;? esc_attr_e('Revert to Defaults'); ?&gt;&quot; \/&gt;\r\n\t\t\t&lt;\/form&gt;&lt;?\r\n\t\t});\r\n\t});\r\n\r\n\tadd_action('admin_init', function () use ($defaults) {\r\n\t\t$ns = __NAMESPACE__; \/\/ to save typing and allow in-string replacement\r\n\t\tregister_setting($ns, $ns);\r\n\t\tadd_settings_section('', '', function(){}, $ns);\r\n\t\tforeach ($defaults as $key =&gt; $option){\r\n\t\t\tif (isset($option[2])){\r\n\t\t\t\tadd_settings_field(&quot;$ns_$key&quot;, $option[1], $option[2], $ns, '', array('label_for'=&gt;&quot;$ns_$key&quot;, 'key'=&gt;$key));\r\n\t\t\t}\r\n\t\t}\r\n\t});\r\n}\r\n\r\nfunction textbox($size=40){\r\n\t\/\/ returns a textbox generator\r\n\treturn function($args) use ($size) {\r\n\t\t$name = $args['key'];\r\n\t\t$ns = __NAMESPACE__;\r\n\t\t$options = get_option($ns);\r\n\t\techo &quot;&lt;input name='{$ns}[$name]' value='{$options[$name]}' size=$size \/&gt;&quot;;\t\t\r\n\t};\r\n}\r\n<\/code><\/pre>","protected":false},"excerpt":{"rendered":"They suckered me into helping redesign the website for my daughter's school (don't worry&mdash;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 [&hellip;]","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[9],"tags":[],"_links":{"self":[{"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts\/2426"}],"collection":[{"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/comments?post=2426"}],"version-history":[{"count":16,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts\/2426\/revisions"}],"predecessor-version":[{"id":2444,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts\/2426\/revisions\/2444"}],"wp:attachment":[{"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/media?parent=2426"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/categories?post=2426"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/tags?post=2426"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}