Here is my way:
// get some info from the stylesheet (we need theme name & version)
$theme = get_theme_data(get_stylesheet_uri());
define('THE_THEME', strip_tags($theme['Name'])); // this is used as option name
define('THEME_VERSION', trim($theme['Version']));
// default theme settings, the reason for a function is to avoid using a global array :)
function default_theme_options(){
return array(
'version' => '1.4.5', // we use this to sync options between different theme versions
'my_textfield' => 'A simple text field',
'my_checkbox' => false,
'my_select' => 2,
'my_texarea' => 'Just a text area bla blah',
'my_radio' => '3rd',
'my_multicheck' => '1,3',
);
}
// make the theme settings are properly set up
add_action('after_setup_theme', 'check_theme_options');
function check_theme_options(){
// get current settings
$options = get_option(THE_THEME);
// get default settings
$defaults = default_theme_options();
// make sure we have the theme options stored in the db, if not store the default ones
if(!$options) update_option(THE_THEME, $defaults);
// new theme version? synchronize options then
if(version_compare($options['version'], THEME_VERSION, '!=')):
// check for new options
foreach($defaults as $option => $value)
if(!array_key_exists($option, $options)) $options[$option] = $defaults[$option];
// remove deprecated options if any
foreach($options as $option => $value)
if(!array_key_exists($option, $defaults)) unset($options[$option]);
// update
update_option(THE_THEME, $options);
endif;
}
// create the menu entry and link it to the form
add_action('admin_menu', 'add_options_menu');
function add_options_menu(){
add_theme_page(
__('Theme settings'), // page title
'My Theme Name', // menu title
'edit_theme_options', // show the theme settings to all users who have this capability
'my_theme_options_page', // menu slug
'the_theme_options_form' // the fields, function below
);
}
// trigger when the form is submitted
add_action('admin_post_save_my_options', 'save_my_options');
function save_my_options() {
// check permissions and referer
check_admin_referer('theme-settings');
if(!current_user_can('edit_theme_options')) wp_die(__('You are not authorised to perform this operation.'));
$options = get_option(THE_THEME); // get the current settings
// change options
foreach (default_theme_options() as $key => $value)
if(isset($_POST[$key]))
if(is_array($_POST[$key])) $options[$key] = strip_tags(implode(',', $_POST[$key]));
else $options[$key] = current_user_can('unfiltered_html') ? stripslashes((string)$_POST[$key]) : stripslashes(wp_filter_post_kses((string)($_POST[$key])));
// update settings with the post data
update_option(THE_THEME, $options);
// reload options page
wp_redirect(admin_url('admin.php?page=my_theme_options_page&updated=true'));
}
// the theme settings form
function the_theme_options_form() {
if(!current_user_can('edit_theme_options')) wp_die(__('You are not authorised to perform this operation.'));
?>
<div class="wrap">
<?php screen_icon('themes'); ?><h2><?php echo get_admin_page_title(); ?></h2>
<form id="theme-settings-form" action="<?php echo admin_url('admin-post.php?action=save_my_options'); ?>" method="post" enctype="multipart/form-data">
<?php if (isset($_GET['updated'])): // just updated? ?>
<div class="updated fade below-h2">
<p><?php _e('Settings saved.'); ?></p>
</div>
<?php endif; ?>
<table class="form-table">
<tr>
<th scope="row">
<label for="my_textfield"><?php _e("A simple textfield"); ?></label>
</th>
<td>
<input id="my_textfield" size="20" name="my_textfield" type="text" value="<?php echo get_my_option('my_textfield'); ?>" />
</td>
</tr>
<tr>
<th scope="row">
<label for="my_checkbox"><?php _e("Checkbox test"); ?></label>
</th>
<td>
<input type="hidden" name="my_checkbox" value="0">
<input id="my_checkbox" name="my_checkbox" type="checkbox" value="1" <?php checked('1', get_my_option('my_checkbox')) ?> />
</td>
</tr>
<tr valign="top">
<th scope="row">
<label for="my_select"><?php _e("Multiple select field"); ?></label>
</th>
<td>
<select name="my_select" id="my_select">
<option value="1" <?php selected('1', get_my_option('my_select')) ?>><?php _e("option 1"); ?></option>
<option value="2" <?php selected('2', get_my_option('my_select')) ?>><?php _e("option 2"); ?></option>
<option value="3" <?php selected('3', get_my_option('my_select')) ?>><?php _e("option 3"); ?></option>
</select>
</td>
</tr>
<tr valign="top">
<th scope="row">
<label for="my_texarea"><?php _e("A textarea"); ?></label>
</th>
<td>
<textarea rows="6" cols="40" name="my_textarea" id="my_textarea" class="code"><?php echo esc_html(get_my_option('my_textarea')); ?></textarea>
</td>
</tr>
<tr valign="top">
<th scope="row">
<?php _e("A radio group:"); ?>
</th>
<td>
<label for="my_radio_yes">
<input name="my_radio" id="my_radio_yes" type="radio" value="yes" <?php checked('yes', get_my_option('my_radio')); ?> />
<?php _e("Yes"); ?>
</label>
<label for="my_radio_no">
<input name="my_radio" id="my_radio_no" type="radio" value="no" <?php checked('no', get_my_option('my_radio')); ?> />
<?php _e("No"); ?>
</label>
<label for="my_radio_whatever">
<input name="my_radio" id="my_radio_whatever" type="radio" value="whatever" <?php checked('whatever', get_my_option('my_radio')); ?> />
<?php _e("Whatever"); ?>
</label>
</td>
</tr>
<tr valign="top">
<th scope="row">
<?php _e("Multiple checkbox option (eg. a list of categories):"); ?>
</th>
<td>
<?php foreach(get_terms('category') as $term): // hidden input not needed here because we only send checked options ?>
<label for="my_multicheck_<?php echo $term->term_id; ?>">
<input id="my_multicheck_<?php echo $term->term_id; ?>" name="my_multicheck[]" type="checkbox" value="<?php echo $term->term_id; ?>" <?php checked(in_array($term->term_id, explode(',', get_my_option('my_multicheck')))); ?> />
<?php echo $term->name; ?>
</label>
<br />
<?php endforeach; ?>
</td>
</tr>
<tr valign="top">
<th scope="row">
</th>
<td>
<input type="submit" class="button-secondary" name="submit" value="<?php _e("Save Changes"); ?>" />
</td>
</tr>
</table>
<?php wp_nonce_field('theme-settings'); ?>
</form>
</div>
<?php
}
// apis -- eg. echo get_my_option('my_textfield');
function get_my_option($what){
// check if it's a theme option or a normal wp option
if($what && !array_key_exists((string)$what, default_theme_options())) return get_option($what);
// it's a theme option
$options = get_option(THE_THEME);
return $options[$what];
}
function update_my_option($what, $value){
// check if it's a theme option or a normal wp option
if($what && !array_key_exists((string)$what, default_theme_options())) return update_option($what, $value);
// it's a theme option
$options = get_option(THE_THEME);
$options[$what] = $value;
return update_option(THE_THEME, $options);
}
This is just a short adaptation from the theme settings I implemented in my theme, which is much more complex than this.
Note that hidden input with 0 value - this is a trick to force the form to send $_POST['my_checkbox'] even if the field is unchecked.
