I obviously missed my target release date of end of June for the next major release of Connections. It should be only a few weeks longer. Progress? It’s coming along very well but a few things took a little longer than expected. One of them being a Settings API Wrapper Class for WordPress. What is this you ask? Basically, this allows me to quickly and easily create settings pages and manage those settings while seamlessly integrating with WordPress.
This wrapper class, at its core it is powered by two WordPress API/s, the Options API and the Settings API. These two API/s are available to make it easier for developers to create and manage settings pages in the admin. As easy as those API/s make it … I wanted something even easier. Something where I could feed some values and have all the grunt work done for me. Something that I could just drop into any plugin and reuse over and over again.
First, I looked around to see what was available. Why re-invent the wheel, right? There are a few settings frameworks for WordPress … but I wanted simple, straight forward and seamlessly integrates with WordPress. Since I didn’t find anything out there that met my requirements … I rolled up my sleeves and got to work.
Before I go on and explain how to use this API in your own projects I need to make sure to give two others some credit… The first would be Pippin of Pippins Plugins. I follow his work (you should too). He put up a blog post on how he created the settings pages for Easy Digital Downloads. His solution isn’t an API but it did give me a little better direction. The second would be Tarek. He left a comment pointing to his own solution. I nearly trashed what work I did have completed to use his API. In the end I didn’t because I kind of liked my implementation better. I, however, did pickup a little of his code to help my efforts … the callbacks used to actually show the fields on a page. Which reminds me … I need to make sure to put the proper credit in my source.
Are you still with me? Good.
I put up a demo plugin in GitHub. All the source below is pulled from that demo plugin.
And, finally, here’s how to use it yourself. Oh, I’m going to completely skip over creating the plugin base, adding admin page and such as I’m going to assume you already know how to do that if you’re reading this.
Step 1
Include the API and load an instance.
require_once dirname( __FILE__ ) . '/class.settings-api.php'; $this->settings = cnSettingsAPI::getInstance(); |
Step 2
Create three functions. One each for tabs, sections and fields. Each of the following functions simply contain an array that define the properties of each. Each of these functions will actually be used as the callback for some filters added by this API. This will be a large segment of code because this demo will render one of each of the available form fields this API supports. So scroll down to see the next step.
public function tabs( $tabs ) { // Register the core tab banks. $tabs[] = array( 'id' => 'basic' , 'position' => 10 , 'title' => __( 'Basic' , 'connections_settings_api' ) , 'page_hook' => $this->pageHook ); $tabs[] = array( 'id' => 'other' , 'position' => 20 , 'title' => __( 'Other' , 'connections_settings_api' ) , 'page_hook' => $this->pageHook ); $tabs[] = array( 'id' => 'advanced' , 'position' => 30 , 'title' => __( 'Advanced' , 'connections_settings_api' ) , 'page_hook' => $this->pageHook ); return $tabs; } public function sections( $sections ) { $sections[] = array( 'tab' => 'basic' , 'id' => 'basic_one' , 'position' => 10 , 'title' => __( 'Test Section One' , 'connections_settings_api' ) , 'callback' => create_function( '', "_e( 'Test Section One Description.' , 'connections_settings_api' );" ) , 'page_hook' => $this->pageHook ); return $sections; } public function fields( $fields ) { $fields[] = array( 'plugin_id' => 'connections_settings_api', 'id' => 'checkbox_test', 'position' => 5, 'page_hook' => 'settings_page_settings_api_test', 'tab' => 'basic', 'section' => 'basic_one', 'title' => __('Checkbox', 'connections_settings_api'), 'desc' => __('Checkbox Label.', 'connections_settings_api'), 'help' => __(''), 'type' => 'checkbox', 'default' => 1 ); $fields[] = array( 'plugin_id' => 'connections_settings_api', 'id' => 'textarea_test', 'position' => 30, 'page_hook' => 'settings_page_settings_api_test', 'tab' => 'basic', 'section' => 'basic_one', 'title' => __('Rich Text Area', 'connections_settings_api'), 'desc' => __('This is a test of the RTE.', 'connections_settings_api'), 'help' => __(''), 'type' => 'rte', 'size' => 'large', 'default' => '<span style="text-decoration: underline;">Default <strong>text</strong> with <em>style</em>!</span>' ); $fields[] = array( 'plugin_id' => 'connections_settings_api', 'id' => 'textarea_large', 'position' => 29, 'page_hook' => 'settings_page_settings_api_test', 'tab' => 'basic', 'section' => 'basic_one', 'title' => __('Large Text Area', 'connections_settings_api'), 'desc' => __('Text Area', 'connections_settings_api'), 'help' => __(''), 'type' => 'textarea', 'size' => 'large', 'default' => 'LARGE TEXT AREA' ); $fields[] = array( 'plugin_id' => 'connections_settings_api', 'id' => 'text_regular', 'position' => 28, 'page_hook' => 'settings_page_settings_api_test', 'tab' => 'basic', 'section' => 'basic_one', 'title' => __('Regular Text', 'connections_settings_api'), 'desc' => __('Regular Text Label', 'connections_settings_api'), 'help' => __(''), 'type' => 'text', 'size' => 'regular', 'default' => 'Regular' ); $fields[] = array( 'plugin_id' => 'connections_settings_api', 'id' => 'text_small', 'position' => 27, 'page_hook' => 'settings_page_settings_api_test', 'tab' => 'basic', 'section' => 'basic_one', 'title' => __('Small Text', 'connections_settings_api'), 'desc' => __('Small Text Label', 'connections_settings_api'), 'help' => __(''), 'type' => 'text', 'size' => 'small', 'default' => 'SML' ); $fields[] = array( 'plugin_id' => 'connections_settings_api', 'id' => 'text_large', 'position' => 29, 'page_hook' => 'settings_page_settings_api_test', 'tab' => 'basic', 'section' => 'basic_one', 'title' => __('Large Text', 'connections_settings_api'), 'desc' => __('Large Text Label', 'connections_settings_api'), 'help' => __(''), 'type' => 'text', 'size' => 'large', 'default' => 'LARGE' ); $fields[] = array( 'plugin_id' => 'connections_settings_api', 'id' => 'quicktag', 'position' => 29.5, 'page_hook' => 'settings_page_settings_api_test', 'tab' => 'basic', 'section' => 'basic_one', 'title' => __('Quicktag Text', 'connections_settings_api'), 'desc' => __('Quicktag Label', 'connections_settings_api'), 'help' => __(''), 'type' => 'quicktag', 'default' => 'Quicktag Textarea!' ); $fields[] = array( 'plugin_id' => 'connections_settings_api', 'id' => 'multicheck_test', 'position' => 21, 'page_hook' => 'settings_page_settings_api_test', 'tab' => 'basic', 'section' => 'basic_one', 'title' => __('Multi-Checkbox', 'connections_settings_api'), 'desc' => __('Multi-Checkbox Label', 'connections_settings_api'), 'help' => __(''), 'type' => 'multicheckbox', 'options' => array( 'one' => 'One', 'two' => 'Two', 'three' => 'Three', 'four' => 'Four' ), 'default' => array( 'one' , 'three' ) ); $fields[] = array( 'plugin_id' => 'connections_settings_api', 'id' => 'radio_test', 'position' => 22, 'page_hook' => 'settings_page_settings_api_test', 'tab' => 'basic', 'section' => 'basic_one', 'title' => __('Radio', 'connections_settings_api'), 'desc' => __('Radio Label', 'connections_settings_api'), 'help' => __(''), 'type' => 'radio', 'options' => array( 'yes' => 'Yes', 'no' => 'No' ), 'default' => 'yes' ); $fields[] = array( 'plugin_id' => 'connections_settings_api', 'id' => 'select_test', 'position' => 23, 'page_hook' => 'settings_page_settings_api_test', 'tab' => 'basic', 'section' => 'basic_one', 'title' => __('Select', 'connections_settings_api'), 'desc' => __('Select Label', 'connections_settings_api'), 'help' => __(''), 'type' => 'select', 'options' => array( 'one' => 'One', 'two' => 'Two', 'three' => 'Three', 'four' => 'Four' ), 'default' => 'two' ); $fields[] = array( 'plugin_id' => 'connections_settings_api', 'id' => 'multi_select_test', 'position' => 24, 'page_hook' => 'settings_page_settings_api_test', 'tab' => 'basic', 'section' => 'basic_one', 'title' => __('Multi-Select', 'connections_settings_api'), 'desc' => __('Multi-Select Label', 'connections_settings_api'), 'help' => __(''), 'type' => 'multiselect', 'options' => array( 'one' => 'One', 'two' => 'Two', 'three' => 'Three', 'four' => 'Four', 'five' => 'Five', 'six' => 'Six', 'seven' => 'Seven', 'eight' => 'Eight', 'nine' => 'Nine', 'ten' => 'Ten' ), 'default' => array( 'two' , 'four' ) ); return $fields; } |
Step 3
Register the filters.
add_filter( 'cn_register_settings_tabs' , array( &$this , 'tabs' ) ); add_filter( 'cn_register_settings_sections' , array( &$this , 'sections' ) ); add_filter( 'cn_register_settings_fields' , array( &$this , 'fields' ) ); |
Step 4
Initiate the API.
$this->settings->init(); |
Step 5
Add the code to your admin page to render the settings fields.
$args = array( 'page_icon' => '', 'page_title' => 'Connections : Settings API Test Page', 'tab_icon' => 'options-general' ); $this->settings->form( $this->pageHook , $args ); |
Step 6
Read the code source. It is heavily documented.
The Result
As you see from the screen captures the support for various field types is quite extensive. Only two are missing, file upload and the WordPress Media Uploader. I plan on adding these at a later date.
How to retrieve a setting?
This will retrieve the value saved to the small text input.
$value = $this->settings->get('connections_settings_api', 'basic_one', 'text_small'); |
In conclusion I’d like to leave you with a few other tidbits about this API… The API is pretty versatile. For example, if you do not need tabs, you don’t need to add them. If you don’t even need your own settings page and you only want to add a few settings to one of the core settings pages, you can do that too. You can even do a combination all of, if you really wanted to.
Again, the code is available on GitHub and it is heavily documented. If you’re interested in using this API I highly recommend reading thru it.
Enjoy.
Looks great!
Two questions:
1. Are you storing each field as a separate option or is each section a single array that contains all options? Looking briefly through the source it looks like each one is separate.
2. Anything special about retrieve option values outside of the settings page, or is it just with get_option()?
Thanks!
1. Both. If a setting is registered to a section, that setting and any others registered to that section are stored as a single array. And for settings that are not registered to a section, those will be stored as a single value. A quick illustration … for EDD you would register the four sections and all the settings registered to the appropriate section. The settings for those four sections would be stored as four separate arrays in the db. Exactly as you currently do in EDD. The only real difference is that I’m using two arrays one you’re using one large and registering the sections manually. The reason I introduced the ability to store a setting as a single value is so one could register a setting to any of the available WordPress core settings sections as those are saved as single values. You, of course, can still register a new section to any of the core settings pages too.
2. Nope you can certainly still use get_option(). I made sure to strictly adhere to the core API/s. I did add a get method to the API. Maybe a little overkill, but with the get method, you can supply the section ID and the setting ID and it’ll return the value. It does have two nifty tricks though. The first, if you just give it the plugin ID, it’ll return all settings registered to the plugin. Just like the one function you created for EDD. The second, as you know, if a form checkbox is submitted not checked, that index in the array is not saved to the db. What this method will do is check the registered settings and add back in any missing indexes as an empty value. The plus here is that you wouldn’t get those pesky index does not exists PHP notices if you forgot to check for it before attempting to access the value.
To complete my EDD illustration from above … you could use this API to register the sections and the settings using the same ID/s and you would not have to change anything else in the code base.
Hi Steven
This looks great.
Right now I’m testing this on a plugin but when I use a sanitize_callback like esc_url_raw() it’s giving me a notice “Array to string conversion”
Any thoughts about this?
@ Paul
Can you share a link to you plugin? Is it on Github?
You might want to grab the latest version of the API file from Connections as it has fixes I have not yet pushed to Github.
My initial thought on the error…
I use the sanitize_callback callback to write my own custom sanitation functions which in turn use the WP core sanitation functions. I do this because all all the settings I’m creating return arrays which must be looped thru. My guess is that the settings you registered is passing an array. So I suggest creating you own function and loop thru the array sanitizing the URL value and then pass back the whole array to be saved. Make sense?
Thanks Steven for the reply.
I have it on my localhost and am still developing the plugin.
I’ll check the API file from Connections.