I write a wordpress plugin. My plugin has options. All options are in one group, ‘pluginname-settings’. All option names are uniform and look like “pluginname-settings[‘option-name’]”. Some code to illustrate my case:
register_setting( 'pluginname_options', 'pluginname-settings', 'pluginname_validate' );
add_settings_section( 'section-one', 'Name of Section One', 'section_one_callback', 'pluginname_options-sectionname' );
add_settings_field( 'optionname', 'First Option', 'my_text_input', 'pluginname_options-sectionname', 'section-button', array(
'name' => 'pluginname[optionname]',
'value' => '42')
)
For blog admins to tweak them, I created a settings page and grouped options in tabs. Each tab shows a subset of options (one section) and an “Update button”, as follows:
echo "<form action="options.php" method="POST">";
settings_fields( 'pluginname_options' );
do_settings_sections( 'pluginname_options-sectionname' );
submit_button('Update', 'primary', 'submit-form', false);
echo "</form>";
The problem is that when I press “Update” on one tab, the options from other tabs get cleared.
To my understanding, this is because all options represent an array of one setting.
Question: is it possible to save one section without clearing others under this setup? What is a good practice in such cases — register separate settings, use validation function to fill in the blanks or something else? Or is it just me doing it all wrong?
Thank you in advance.
P.S.: Per request in comments, here is a minimal working example:
<?php
/*
Plugin Name: Plugin Name
Plugin URI:
Description: Description
Version: 0.0.1
Author: Author Name
Author URI:
License: GPL3+
*/
add_action( 'admin_menu', 'pluginname_menu' );
function pluginname_menu()
{
add_options_page( 'PluginName Options', 'pluginname', 'manage_options', 'pluginname_options_page', 'pluginname_options_page' );
}
add_action( 'admin_init', 'pluginname_admin_init' );
function pluginname_admin_init()
{
register_setting( 'pluginname_options', 'pluginname-settings' );
add_settings_section( 'section-one', 'Section One', 'pluginname_section_callback', 'pluginname_options-section-one' );
$options_array = get_option('pluginname-settings');
add_settings_field( 'optionname-one', 'First Option', 'pluginname_field_callback', 'pluginname_options-section-one', 'section-one', array(
'name' => 'pluginname-settings[optionname-one]',
'value' => isset($options_array['optionname-one']) ? $options_array['optionname-one'] : '42')
);
add_settings_field( 'optionname-two', 'Second Option', 'pluginname_field_callback', 'pluginname_options-section-one', 'section-one', array(
'name' => 'pluginname-settings[optionname-two]',
'value' => isset($options_array['optionname-two']) ? $options_array['optionname-two'] : '42')
);
add_settings_section( 'section-two', 'Section Two', 'pluginname_section_callback', 'pluginname_options-section-two' );
add_settings_field( 'optionname-three', 'Third Option', 'pluginname_field_callback', 'pluginname_options-section-two', 'section-two', array(
'name' => 'pluginname-settings[optionname-three]',
'value' => isset($options_array['optionname-three']) ? $options_array['optionname-three'] : '42')
);
add_settings_field( 'optionname-four', 'Fourth Option', 'pluginname_field_callback', 'pluginname_options-section-two', 'section-two', array(
'name' => 'pluginname-settings[optionname-four]',
'value' => isset($options_array['optionname-four']) ? $options_array['optionname-four'] : '42')
);
}
function pluginname_validate($input)
{
return $input;
}
function pluginname_section_callback()
{
echo 'Feel free to change parameters below.';
}
function pluginname_field_callback( $args )
{
$name = esc_attr( $args['name'] );
$value = esc_attr( $args['value'] );
echo "<input type='text' name='$name' value='$value' /> ";
}
function pluginname_tab1()
{
echo "<form action="options.php" method="POST">";
settings_fields( 'pluginname_options' );
do_settings_sections( 'pluginname_options-section-one' );
submit_button('Update', 'primary', 'submit-form', false);
echo "</form>";
}
function pluginname_tab2()
{
echo "<form action="options.php" method="POST">";
settings_fields( 'pluginname_options' );
do_settings_sections( 'pluginname_options-section-two' );
submit_button('Update', 'primary', 'submit-form', false);
echo "</form>";
}
function pluginname_options_page()
{
?>
<div class="wrap">
<h2>Options</h2>
<h2 class="nav-tab-wrapper">
<a href="?page=pluginname_options_page&tab=1" class="nav-tab <? if ( @( $_GET['tab'] == '1' ) || !isset($_GET['tab'])) echo "nav-tab-active"; ?>">Section One Options</a>
<a href="?page=pluginname_options_page&tab=2" class="nav-tab <? if ( $_GET['tab'] == '2' ) echo "nav-tab-active"; ?>">Section Two Options</a>
</h2>
<?php $current_tab = $_GET['tab'];
switch ($current_tab)
{
case '2' : pluginname_tab2(); break;
default: pluginname_tab1();
} ?>
</div>
<?php
}
?>
First kick in your validation callback by changing the
register_setting()
toAnd then update your validation function to actually do something. Below it gets the current state of the options, and then only updates the pieces of the array that are submitted. When you are on a tab and click “Update” only the info in the tab is posted. Therefore the array (as written) only has 2 keys, and the info from the other tab is purged.
I threw in a little sanitization for fun, the type of data santization depends on what your real options are, but you should always do some kind of sanitization.
There are two ways:
Pass the tab ID as hidden field with your form and update only the fields which belong to that tab.
Separate the field names for each tab, and combine them in your save callback.
So, let’s assume that you’ve defined a parameter to add each option to its own tab. Here’s how I do it, for example:
Then, when you output your settings page, you determine the current tab:
…then output each tab separately:
Then, you use tab-specific submit/reset buttons:
Now, inside your validation callback, you can determine which submit/reset tab is selected:
…and then only act on that subset of options, as you step through your whitelist.
(I’ve left a lot of code out here, as this is just proof-of-concept. If you would like to see a working example, check here.