6

I'm using Settings API and I can't get checkboxes to work, if they're set to "false" they're not even being $_POST and that's the point.

Stephen Harris & Chip Bennett explained it, but I believe their way is not simple enough (especially for me, not PHP-guy at all). Do I REALLY need a few custom PHP functions and custom-named submit buttons for every form just to get this simple thing working?

My settings look much like:

register_setting('XX_theme_settings', 'XX_theme_settings', 'setting_validate' );

add_settings_section('theme_options', 'Theme Options', 'theme_options_generate', 'page1' ); 
add_settings_field( 'XX_Option1', 'Option 1', 'checkbox', 'page1', 'theme_options', 'XX_Option1', $args = array('id' => 'checkbox1', 'type' => 'checkbox') );
add_settings_field( 'XX_Option2', 'Option 2', 'checkbox', 'page1', 'theme_options', 'XX_Option2', $args = array('id' => 'checkbox2', 'type' => 'checkbox') ); 

Then I generate checkboxes with this callback function:

function checkbox($args) {
    $options = get_option('XX_theme_settings');
    echo '<input type="checkbox" name="'. $args['id'] .'" value="true"'; if($options[$args['id']]==true) { echo 'checked="checked'; }; echo '/>';
    echo $args['type'];  
}

Now, everything I need is a good validate callback:

function setting_validate($input) {
   // Firstly it should find all fields of type == "checkbox" 
   // (I'm not sure why should I check which field was just being sent, 
   // is updating all the checkboxes from different sections a big no-no?
   // it is much easier indeed.
   // Now set the ones missing to false/0 and the rest to true/1.
   // Then merge everything like below:

   $options = get_option('XX_theme_settings');
   $merged = array_merge($options, $input);  
   return $merged;  
}

So the question is: how to list all Settings Api checkboxes and then set the right ones to true?

I guess it'd look something like that:

$options = array(
        array(
            'id' => 'checkbox1',
            'type' => 'checkbox',
            'page' => 'page1',
         ),
        array(
            'id' => 'checkbox2',
            'type' => 'checkbox',
            'page' => 'page1',
         ),
);

function setting_validate($input) {
   foreach($options as $option) {
     if($option['type'] == "checkbox") { 
        //set it to 1 if sent
     }
   }

   $options = get_option('XX_theme_settings');
   $merged = array_merge($options, $input);  
   return $merged;  
}

Thanks :)

Here's how Chip Bennett does it exactly, but even with all these great comments I can't get how it exactly works.

1 Answer 1

4

If the setting in question is $setting, and is a checkbox type, you validate it like so:

<?php
$valid_input[$setting] = ( isset( $input[$setting] ) && true == $input[$setting] ? true : false );
?>

In this case, $input is the form data passed to the validation callback, where $input is an array-of-arrays. The validation method is known as whitelisting, where the current settings array is called (in this case, as $valid_input), and each setting in the settings array is only updated if the value in $input[$setting] passes the validation.

With checkboxes, if the checkbox isn't set, then the setting itself doesn't get populated in the $input array. So, the first step to validate it is to determine if it's been set in $input. The second step is to ensure that the value that is set is true (or 'true', or 'on', or whatever it is to which you set the checkbox value in the settings form). If both those conditions are met, return true; otherwise, return false.

See: it's quite simple, really. :)

Edit

Regarding this:

function setting_validate($input) {
   foreach($options as $option) {
     if($option['type'] == "checkbox") { 
        //set it to 1 if sent
     }
   }

   $options = get_option('XX_theme_settings');
   $merged = array_merge($options, $input);  
   return $merged;  
}

...that's not exactly the way you want to do it. You want to limit the return value to valid options; that is, you want to return, explicitly, the settings that you know you've defined. If, somehow, someone can populate $input with additional data - i.e. malicious code - your method would simply pass that additional data right into the database.

Don't do an array_merge(). Update each $valid_options[$setting] separately/individually, and only ever return $valid_options.

Here's the process:

  1. Define the currently valid data:

    $valid_input = get_option( 'XX_theme_settings' );
    
  2. Update the currently valid data with $input only if $input passes your validation/sanitization.
  3. Then return the updated, currently valid data.

If the fields are being generated dynamically, then you should also step through them dynamically, in your validation callback. You have this correct, with foreach( $options as $option ).

Putting it all together might look like this:

<?php
function setting_validate( $input ) {
    // Get current setting values
    $valid_options = get_option( 'XX_theme_options' );
    // Get option parameters array
    // Note: XX_theme_get_option_parameters() function
    // should be defined to return the array you've 
    // defined for $options
    $options = XX_theme_get_option_parameters();
    // Now step through the option parameters,
    // and validate each $input[$option]
    foreach( $options as $option ) {
         // If $option['type] is a checkbox
         if( 'checkbox' == $option['type'] ) { 
             // verify that $input[$option] is set
             // and is set with a valid value;
             // if so, set $valid_input[$option] to 1
             $valid_input[$option] = ( isset( $input[$option] && true == $input[$option] ? 1 : 0 );
         }
     }
     // Return the updated $valid_input array
     return $valid_input;
}
?>
12
  • Chip Bennett, it's all becoming clearer now, I'm a bit scared when it comes to editing validation callbacks (see wordpress.stackexchange.com/questions/43749/broken-settings-api ), so I'll ask before doing - is this code valid: pastebin.com/5fmqWkDA ? Of course fields are being generated dynamically based on $options arrays. Thanks a lot, you're really patient ;) Feb 28, 2012 at 2:14
  • 1
    See updated answer. I've tried to step through your code, rearranging as necessary. Feb 28, 2012 at 2:38
  • 1
    @ChipBennett You are the Settings API boss for true. Feb 28, 2012 at 3:10
  • It still doesn't save checkboxes, XX_theme_get_option_parameters() returns $options array, but nothing happes when I submit data. When I tried adding another if statement, like: "else if ('textarea' == $option['type']) { $valid_input[$option] = wp_filter_nohtml_kses( $input[$option] ); } I'm getting tons of ": Illegal offset type" errors. Feb 28, 2012 at 12:57
  • The next thing to look at, then, is your setting callback. What are the contents of $args passed to the checkbox field callback? Feb 28, 2012 at 13:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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