The biggest problem with documentation is that if you don’t think the same way the doc was written, the examples will frustrate you to the point where you want to cry. If you’re not techy at all, directions to move WordPress to a subfolder will piss you off. If you’re a basic webmaster, who can edit posts and maybe FTP, they’re scary but doable. It’s all a measure of leveling up and seeing things in a way you understand.
Recently I was banging around with the Settings API in WordPress to fix some debug errors in a plugin. It took me about 5 hours to figure out what it was I was doing, and how to fix it. Actual fixing? About half an hour, including the time it took to make fresh coffee.
What was my problem? I was looking at code from a different angle. If you start ‘right’ it’s really easy to follow tutorials, but when you start from an already functioning plugin and want to correct it, it’s a nightmare. Reverse engineering things isn’t easy. You’re used to looking at things in one way, and changing that is a headache. What I had was working, up until you turned on debug, which is why I got away with it for so long, and why I hated having to change it.
But I did. I had a plugin admin page that let you enter two text strings, an access key and a secret key, which the rest of the code used to do it’s magic. Because of that, I couldn’t just be lazy and use a basic register_setting()
call like this:
register_setting( 'myplugin-retain-settings', 'myplugin-retain');
That’s easy, you put it in, you call it with settings_fields('myplugin-retain-settings');
, and you go to the races. But I have two strings, and if you call the settings fields twice, you’ll find all sorts of fun errors.
To explain, let me show you what I did wrong, and what right is.
Doing It Wrong
<form method="post" action="options.php"> <input type="hidden" name="action" value="update" /> <?php wp_nonce_field('update-options'); ?> <input type="hidden" name="page_options" value="myplugin-accesskey,myplugin-secretkey" /> <table class="form-table"> <tbody> <tr valign="top"><th colspan="2"><h3><?php _e('MyPlugin Settings', myplugin); ?></h3></th></tr> <tr valign="top"> <th scope="row"><label for="myplugin-accesskey"><?php _e('Access Key', myplugin); ?></label></th> <td><input type="text" name="myplugin-accesskey" value="<?php echo get_option('myplugin-accesskey'); ?>" class="regular-text"/></td> </tr> <tr valign="top"> <th scope="row"><label for="myplugin-secretkey"><?php _e('Secret Key', myplugin); ?></label></th> <td><input type="text" name="myplugin-secretkey" value="<?php echo get_option('myplugin-secretkey'); ?>" class="regular-text"/></td> </tr> </tbody> </table> <p class="submit"><input class='button-primary' type='Submit' name='update' value='<?php _e("Update Options", dreamobjects); ?>' id='submitbutton' /></p> </form>
Doing It Right
<form method="post" action="options.php"> <?php settings_fields( 'myplugin-keypair-settings' ); do_settings_sections( 'myplugin-keypair_page' ); submit_button(); ?> </form>
Making Wrong Right
As you can tell, there’s a huge difference between the two, and one is a lot easier to look at than the other.
First you have to set up registering settings. Since I already had an admin action for my settings page, I wanted to just add to that:
function add_settings_page() { load_plugin_textdomain(myplugin, MYPLUG::getPath() . 'i18n', 'i18n'); add_menu_page(__('MyPlugin Settings'), __('MyPlugin'), 'manage_options', 'myplugin-menu', array('MYPLUG', 'settings_page'), plugins_url('myplugin/images/myplugin-color.png')); }
I added this in(I’m using an array to isolate my plugin functions, so I don’t have to worry as much about namespace clases.):
add_action('admin_init', array('MYPLUG', 'add_register_settings'));
That was the easy part. The hard part is converting all my table information into settings. Making another new function, I want to add a setting section, just as you would for a one-off. But in this case I need to make a group. Since I named my settings field myplugin-keypair-settings
, and my section myplugin-keypair_page
, that’s the first important information I need. But backwards.
First I have to add my section using add_settings_section()
:
add_settings_section( 'myplugin-keypair_id', 'MyPlugin Settings', 'myplugkeypair_callback', 'myplugin-keypair_page' );
Once you do that, you want to register the setting, just like normal:
register_setting( 'myplugin-keypair-settings','myplugin-key');
The complete function
function add_register_settings() { // Keypair settings add_settings_section( 'myplugin-keypair_id', 'MyPlugin Settings', 'myplugkeypair_callback', 'myplugin-keypair_page' ); register_setting( 'myplugin-keypair-settings','myplugin-key'); add_settings_field( 'myplugin-key_id', 'Access Key', 'myplugkey_callback', 'myplugin-keypair_page', 'myplugin-keypair_id' ); register_setting( 'myplugin-keypair-settings','myplugin-secretkey'); add_settings_field( 'myplugin-secretkey_id', 'Secret Key', 'myplugsecretkey_callback', 'myplugin-keypair_page', 'myplugin-keypair_id' ); function myplugkeypair_callback() { echo '<p>'. _e("Once you've configured your keypair here, you'll be able to use the features of this plugin.", MyPlugins).'</p>'; } function myplugkey_callback() { echo '<input type="text" name="myplugin-key" value="'. get_option('myplugin-key') .'" class="regular-text"/>'; } function myplugsecretkey_callback() { echo '<input type="text" name="myplugin-secretkey" value="'. get_option('myplugin-secretkey') .'" class="regular-text"/>'; } }
When I got around to the massive amounts of design I put into things, I ended up making a file called lib/settings.php where I stored everything related to the settings. For me, that’s easier to manage than one massive file with lots of different calls. Easier to debug and edit too, without panicing that I broke something.
I don’t know if it was so much I think backwards, or the API was backwards, but whatever it was, I had a massive brain-block about all this for about a day. I really have to thank Kailey and Pippin for pointing me at examples that ‘clicked’ in a way that I suddenly could figure it all out.