Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

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
}
?>

minimal working example.

share|improve this question
2  
What do the other settings look like? And what does your validate function look like? –  helgatheviking Oct 18 '13 at 9:55
1  
@helgatheviking No other settings. One setting (one 'register_setting' line), two sections (two 'add_settings_section' lines), several fields ('add_settings_field') in each. Validation function is just a placeholder at the moment. I wanted it to check for blank fields and fill them with default values (and write "leave blank for default" on the settings page), but this problem turns my plan upside down. –  Eques Oct 18 '13 at 10:30
2  
Can you paste your full code? That way I'll be able to try it out on my own server. –  helgatheviking Oct 18 '13 at 19:28
1  
Sure. Here it is pastebin.com/6NJrDLhq –  Eques Oct 20 '13 at 15:12
3  
"Validation function is just a placeholder at the moment." - and that is your problem. The validation callback is where you would selectively update your (properly whitelisted) options. –  Chip Bennett Oct 20 '13 at 15:14

3 Answers 3

up vote 2 down vote accepted

First kick in your validation callback by changing the register_setting() to

register_setting( 'pluginname_options', 'pluginname-settings', 'pluginname_validate' );

And 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.

function pluginname_validate($input)
    {
    $options_array = get_option('pluginname-settings');

    if( isset( $input['optionname-one'] ) )
         $options_array['optionname-one'] = sanitize_text_field( $input['optionname-one'] );

    if( isset( $input['optionname-two'] ) )
         $options_array['optionname-two'] = sanitize_text_field( $input['optionname-two'] );

    if( isset( $input['optionname-three'] ) )
         $options_array['optionname-three'] = sanitize_text_field( $input['optionname-three'] );

    if( isset( $input['optionname-four'] ) )
         $options_array['optionname-four'] = sanitize_text_field( $input['optionname-four'] );

    return $options_array;
    }

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.

share|improve this answer

There are two ways:

  1. Pass the tab ID as hidden field with your form and update only the fields which belong to that tab.

  2. Separate the field names for each tab, and combine them in your save callback.

share|improve this answer
    
Thank you very much! –  Eques Jan 7 at 4:59

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:

'display_social_icons' => array(
    'name' => 'display_social_icons',
    'title' => __( 'Display Social Icons', 'oenology' ),
    'type' => 'checkbox',
    'description' => __( 'Display social icons in sidebar', 'oenology' ),
    'section' => 'social',
    'tab' => 'general',
    'since' => '1.2',
    'default' => true
),

Then, when you output your settings page, you determine the current tab:

$currenttab = oenology_get_current_tab();
$settings_section = 'oenology_' . $currenttab . '_tab';
// Defining $currenttab left out for brevity

...then output each tab separately:

do_settings_sections( $settings_section );

Then, you use tab-specific submit/reset buttons:

<?php submit_button( __( 'Save Settings', 'oenology' ), 'primary', 'theme_oenology_options[submit-' . $currenttab . ']', false ); ?>
<?php submit_button( __( 'Reset Defaults', 'oenology' ), 'secondary', 'theme_oenology_options[reset-' . $currenttab . ']', false ); ?>

Now, inside your validation callback, you can determine which submit/reset tab is selected:

    $tabs = oenology_get_settings_page_tabs();

    // Determine what type of submit was input
    $submittype = 'submit';        
    foreach ( $tabs as $tab ) {
            $resetname = 'reset-' . $tab['name'];
            if ( ! empty( $input[$resetname] ) ) {
                    $submittype = 'reset';
            }
    }

    // Determine what tab was input
    $submittab = 'varietals';        
    foreach ( $tabs as $tab ) {
            $submitname = 'submit-' . $tab['name'];
            $resetname = 'reset-' . $tab['name'];
            if ( ! empty( $input[$submitname] ) || ! empty($input[$resetname] ) ) {
                    $submittab = $tab['name'];
            }
    }

...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.

share|improve this answer
    
Thank you very much! –  Eques Jan 7 at 4:58

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.