1

I created theme options page using Settings API, everything works fine but I can't save options. I tried to figure it out where I missed something but I failed. Any idea what can cause this problem?

Here's code of my options page

<?php
/**
 * This functions registers default options values for Evolutionary theme 
 * settings
 */
function ev_theme_settings_defaults() {
    $options = array(
        'color_scheme' => 'blue',
        'copyright' => '',
        'nav_hide_home' => false,
        'nav_hide_sec' => false,
        'nav_show_lvl' => false,
        'featured_show' => false,
        'featured_pause' => false,
        'featured_speed' => 5,
        'ad1_link' => '',
        'ad1_image' => '',
        'ad2_link' => '',
        'ad2_image' => '',
        'ad3_link' => '',
        'ad3_image' => '',
        'ad4_link' => '',
        'ad4_image' => ''
    );

    return $options;
    // Change to apply_filters( 'ev_theme_settings_defaults', $options );
}

/**
 * Create helper function for theme colors
 */
function ev_get_valid_color_schemes(){
    $color_schemes = array(
        'blue' => __( 'Blue', 'evolutionary' ),
        'green' => __( 'Green', 'evolutionary' ),
        'red' => __( 'Red', 'evolutionary' ),
        'black' => __( 'Black', 'evolutionary')
    );

    return $color_schemes;    
} 

/**
 * Setup initial theme options
 */
function ev_options_init() {
    // Set options equal to defaults
    global $ev_options;
    $ev_options = ev_theme_settings_defaults();
    if ( false === $ev_options ) {
        $ev_options = ev_theme_settings_defaults();
    }
    update_option( 'theme_evolutionary_options', $ev_options );
}

add_action( 'after_setup_theme', 'ev_options_init', 9 );

/**
 * Add theme options page to admin menu
 */
function ev_menu_options() {
    add_theme_page( __( 'Theme Options', 'evolutionary' ), __( 'Theme Options', 'evolutionary' ), 'edit_theme_options', 'evolutionary-settings', 'ev_admin_options_page' );
}

add_action( 'admin_menu', 'ev_menu_options' );

/**
 * Register theme options
 */
function ev_register_options() {
    register_setting( 'theme_evolutionary_options', 'theme_evolutionary_options', 'ev_options_validate' );

/**
 * Defining settings sections and fields
 * fist setting creates sections for general options
 */
add_settings_section( 'ev_settings_general_options', __( 'General Options', 'evolutionary' ), 'ev_settings_general_options', 'evolutionary' );

// Creates section for navigation options
add_settings_section( 'ev_settings_nav_options', __( 'Navigation', 'evolutionary' ), 'ev_settings_nav_options', 'evolutionary' );

// Section for featured post options
add_settings_section( 'ev_settings_featured_options', __( 'Featured Posts', 'evolutionary' ), 'ev_settings_featured_options', 'evolutionary' );

// Section for Advertising options
add_settings_section( 'ev_settings_ad_options', __( 'Advertising', 'evolutionary' ), 'ev_settings_ad_options', 'evolutionary' );

/**
 * Created empty callbacks for setions
 */
function ev_settings_general_options() {
    // Put callback code here
}
function ev_settings_nav_options() {
    // callback
}
function ev_settings_featured_options() {
    // callback
}
function ev_settings_ad_options() {
    // callback
}

/**
 * Registered fields
 */
add_settings_field( 'ev_settings_copyright_field', __( 'Copyright Name', 'evolutionary' ), 'ev_settings_copyright_field', 'evolutionary', 'ev_settings_general_options' );

add_settings_field( 'ev_settings_colors_field', __( 'Theme Color', 'evolutionary' ), 'ev_settings_colors_field', 'evolutionary', 'ev_settings_general_options' );

/**
 * Fields Callbacks
 */
function ev_settings_copyright_field() {
    $ev_options = get_option( 'theme_evolutionary_options' );
    //var_dump( $ev_options );
    ?>
    <input name="theme_evolutionary_options[copyright]" type="text" class="regular-text" value="<?php esc_attr_e( $ev_options['copyright'] ); ?>"  />
<?php }
// Callback for colorscheme
function ev_settings_colors_field() {
    $ev_options = get_option( 'theme_evolutionary_options' );
    $color_schemes = ev_get_valid_color_schemes();

    echo '<select name="theme_evolutionary_options[color_scheme]">';

    foreach( $color_schemes as $value => $caption ) :
?>
        <option value="<?php echo $value; ?>" <?php selected( $value, $ev_options['color_scheme'] ); ?>><?php echo $caption; ?></option>

<?php 
    endforeach;

    echo '</select>';
}

} // ev_register_options
add_action( 'admin_init', 'ev_register_options' );    

/**
 * Creating options page HTML output
 */
function ev_admin_options_page() { ?>

    <div class="wrap">
       <?php if ( isset( $_GET['settings-updated'] ) ) {
                echo "<div class='updated'><p>Theme settings updated successfully.</p></div>";
        } ?>
        <?php screen_icon(); ?>
        <h2><?php _e( 'Theme Options', 'evolutionary' ); ?></h2>
        <form action="options.php" method="post">
        <?php 
        settings_fields( 'theme_evolutionary_options' );
        do_settings_sections( 'evolutionary' );
        ?>

            <input name="submit" type="submit" class="button-primary" value="<?php _e( 'Save Settings', 'evolutionary' ) ?>" />
            <input name="reset" type="submit" class="button-secondary" value="<?php _e( 'Reset Settings', 'evolutionary' ) ?>" />
        </form>
    </div>
<?php }

function ev_options_validate( $input ) {
    $ev_options = get_option( 'theme_evolutionary_options' );
    $valid_input = $ev_options;

    $ev_options['color_scheme'] = array_key_exists( $ev_options['color_scheme'], ev_get_valid_color_schemes() ) ? $ev_options['color_scheme'] : 'blue';
    $ev_options['copytright'] = wp_kses_data( $ev_options['copyright'] );

    return $valid_input;
}
4
  • "everything works find but I can't save options." The only place where I see you trying to save options is when you set up initial defaults. You use update_option() to save the defaults ... so is that where you aren't able to save the options, or is there somewhere else you're trying to save them?
    – EAMann
    Commented Mar 15, 2011 at 14:37
  • @EAMann I tried 'add_options' but result was same
    – Mamaduka
    Commented Mar 15, 2011 at 15:30
  • See TheDeadMedic's answer. You never really clarified anything in response to my question ... I was asking where exactly your issue was. You gave us a huge code block with an ambiguous "it's not saving" without telling us where it wasn't saving.
    – EAMann
    Commented Mar 15, 2011 at 16:20
  • @EAMann Oh sorry for that. I can't save new values for options from Options page. Yeah I think I should follow Press Codes tut.
    – Mamaduka
    Commented Mar 15, 2011 at 18:02

3 Answers 3

2

@Mamaduka:

Here is why your settings aren't updating. Your validation function is wrong. To wit:

function ev_options_validate( $input ) {
    $ev_options = get_option( 'theme_evolutionary_options' );
    $valid_input = $ev_options;

    $ev_options['color_scheme'] = array_key_exists( $ev_options['color_scheme'], ev_get_valid_color_schemes() ) ? $ev_options['color_scheme'] : 'blue';
    $ev_options['copytright'] = wp_kses_data( $ev_options['copyright'] );

    return $valid_input;
}

You're updating $ev_options, but returning $valid_input. You need to be updating $valid_input, not $ev_options.

Try this instead:

function ev_options_validate( $input ) {
    $ev_options = get_option( 'theme_evolutionary_options' );
    $valid_input = $ev_options;

    $valid_input['color_scheme'] = array_key_exists( $ev_options['color_scheme'], ev_get_valid_color_schemes() ) ? $ev_options['color_scheme'] : 'blue';
    $valid_input['copytright'] = wp_kses_data( $ev_options['copyright'] );

    return $valid_input;
}

See if that fixes your problem?

EDITED TO ADD:

Your next problem in the validation function is that you're comparing $ev_options to/against itself, when you need to be evaluating $input against $valid_input.

Instead of this:

function ev_options_validate( $input ) {
    $ev_options = get_option( 'theme_evolutionary_options' );
    $valid_input = $ev_options;

    $valid_input['color_scheme'] = array_key_exists( $ev_options['color_scheme'], ev_get_valid_color_schemes() ) ? $ev_options['color_scheme'] : 'blue';
    $valid_input['copytright'] = wp_kses_data( $ev_options['copyright'] );

    return $valid_input;
}

Try this:

function ev_options_validate( $input ) {
    $ev_options = get_option( 'theme_evolutionary_options' );
    $valid_input = $ev_options;

    $valid_input['color_scheme'] = array_key_exists( $input['color_scheme'], ev_get_valid_color_schemes() ) ? $input['color_scheme'] : 'blue';
    $valid_input['copytright'] = wp_kses_data( $input['copyright'] );

    return $valid_input;
}

Specifically, change this:

$valid_input['color_scheme'] = array_key_exists( $ev_options['color_scheme'], ev_get_valid_color_schemes() ) ? $ev_options['color_scheme'] : 'blue';
$valid_input['copytright'] = wp_kses_data( $ev_options['copyright'] );

To this:

$valid_input['color_scheme'] = array_key_exists( $input['color_scheme'], ev_get_valid_color_schemes() ) ? $input['color_scheme'] : 'blue';
$valid_input['copytright'] = wp_kses_data( $input['copyright'] );

And then, I think, your options should work.

@TheDeadMedic:

The Settings API handles looking at any $POST data. That's not the problem.

Regarding the default-settings updating on init, here's my original, example code:

function oenology_options_init() {
     // set options equal to defaults
     global $oenology_options;
     $oenology_options = get_option( 'theme_oenology_options' );
     if ( false === $oenology_options ) {
          $oenology_options = oenology_get_default_options();
     }
     update_option( 'theme_oenology_options', $oenology_options );
}
// Initialize Theme options
add_action('after_setup_theme','oenology_options_init', 9 );

And here's Mamaduka's code:

function ev_options_init() {
    // Set options equal to defaults
    global $ev_options;
    $ev_options = ev_theme_settings_defaults();
    if ( false === $ev_options ) {
        $ev_options = ev_theme_settings_defaults();
    }
    update_option( 'theme_evolutionary_options', $ev_options );
}

add_action( 'after_setup_theme', 'ev_options_init', 9 );

Do you see the difference? There is a critical change from oenology_options_init() to ev_options_init(), to wit:

This:

global $oenology_options;
$oenology_options = get_option( 'theme_oenology_options' );

Versus this:

global $ev_options;
$ev_options = ev_theme_settings_defaults();

Thus, in ev_options_init(), the function never actually does anything, because of the change.

6
  • ev_options_init() is pointless. On every request, it will update theme_evolutionary_options with the result of ev_theme_settings_defaults(), thus always overriding any custom settings saved via the API. Commented Mar 16, 2011 at 18:49
  • We're talking in circles. Of course, as it is written, ev_options_init() is pointless. But then, that's the point: the function is written incorrectly. Commented Mar 16, 2011 at 19:55
  • I corrected but same result, I think I made some horrible mistake while writing code. No problem I'll rewrite theme options follow PressCoder's guide (that works for). Everything is alright with Chip's code, I download Oenology theme and it works fine. I just wanted to know what I was missing and doing wrong. Thanks for help
    – Mamaduka
    Commented Mar 17, 2011 at 8:50
  • Okay, I've been looking at this some more, and I figured out another problem with your code - again in your validation function. You're comparing against $ev_options, instead of $input. Commented Mar 18, 2011 at 18:24
  • Thank you Chip it was validation function problem. Is there any good reference about settings validation?
    – Mamaduka
    Commented Mar 18, 2011 at 19:21
5

The problem is they are getting saved... it's just you're always saving the defaults!

$ev_options = ev_theme_settings_defaults();
if ( false === $ev_options ) {
    $ev_options = ev_theme_settings_defaults();
}
update_option( 'theme_evolutionary_options', $ev_options );

And that's running on every page load (it's hooked to the action after_theme_setup).

Not to mention the if statement serves no purpose ($ev_options will never be false).

It seems you haven't quite understood the Settings API. The whole point is so that you don't need to save the options yourself, WordPress does it for you!

I'm sure you've checked out the Settings API already, although it is a little lacking. The following go into a little more detail, and also cover how to use the API to store options as an array.

7
  • Thanks for response, I checked all tutorial before I started playing with Settings API. I followed Chip Bennett's (goo.gl/UkeRl) tutorial while creating my theme options, and he uses same method to add default options, I tried 'add_option' too but result is same
    – Mamaduka
    Commented Mar 15, 2011 at 15:27
  • Don't get me wrong, Chip has done a good job, but he's not abiding to best practices. Firstly, he's saving the options manually, which defeats the object of the Settings API, and he's doing it on every request! Follow Otto's or Press Coder's tut instead. Commented Mar 15, 2011 at 16:59
  • @TheDeadMedic Thank you for advice, now I'll try with Press code example
    – Mamaduka
    Commented Mar 15, 2011 at 18:04
  • Actually, as far as I know, I'm not saving settings manually; rather, I'm just adding defaults. If the settings already exist, then the defaults check doesn't do anything. I would love not to run the defaults check on every init, but unfortunately, Themes don't have activation/deactivation hooks. Thus, I can't follow Otto's Plugin model exactly. If you have alternate suggestions, I'd love to hear them, because I am trying to follow best practices. Mine is very much a learning-by-doing example, and I am happy to incorporate any suggested improvements. Edited to add: This is my first Commented Mar 15, 2011 at 20:03
  • Glad to see you here and hope you will find site interesting and useful! On topic of theme activation - it can be emulated to some degree, there was question about that - Theme Activate Hook.
    – Rarst
    Commented Mar 15, 2011 at 20:42
0

I think I see the problem, Mamakuka.

You should be calling get_option(), not your defaults function, here:

    /**
 * Setup initial theme options
 */
function ev_options_init() {
    // Set options equal to defaults
    global $ev_options;
    $ev_options = ev_theme_settings_defaults();
    if ( false === $ev_options ) {
        $ev_options = ev_theme_settings_defaults();
    }
    update_option( 'theme_evolutionary_options', $ev_options );
}

add_action( 'after_setup_theme', 'ev_options_init', 9 );

This:

global $ev_options;
$ev_options = ev_theme_settings_defaults();

Should instead be this:

global $ev_options;
$ev_options = get_option( 'theme_evolutionary_options' );

Otherwise, you're doing as TheDeadMedic indicated: saving the default options on every init.

5
  • Thank you Chip. I changed code but results is same, it simply don't save changes and only uses default values. I think I'm missing something, but couldn't figure it out :(
    – Mamaduka
    Commented Mar 15, 2011 at 21:10
  • That's because it's not listening to $_POST at all. Once the default options are first saved, false === $ev_options will never evaluate true, and so the options are continuously updated with the defaults. Commented Mar 16, 2011 at 0:18
  • No, that's not true. if the "if ( false === $ev_options )" evaluates to FALSE, then the function does nothing. It merely writes the existing values back into the database. It is only if the "if ( false === $ev_options )" evaluates to TRUE that the default values are written. Commented Mar 16, 2011 at 15:48
  • Yah, I'm getting confused here. There's so much code going on I couldn't tell what you were advocating. In other words, we're on the same side of the page now ;) Commented Mar 16, 2011 at 21:24
  • Figured we'd get there eventually ;) Commented Mar 17, 2011 at 2:07

Your Answer

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

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