Series links
Building A Theme Options Panel – Part One
Building A Theme Options Panel – Part Two
Building A Theme Options Panel – Part Three
Building A Theme Options Panel – Part Four coming November 16th
Table of contents
- Introduction
- Define the Options (wps-panel-options.php)
- Get and Output the HTML (wps-panel.php)
- Generate the HTML (wps-panel-generator.php)
Introduction
In this part we’ll replace the static HTML with a dynamic system using PHP to automatically generate the HTML for our options.
Not Using The Settings API
WordPress has a Settings API which can be used for plugin and theme options page. It handles quite a lot of stuff and makes developing a theme options page easier if you don’t have a framework but i am not using it because i don’t feel it gives me enough freedom.
I build commercial(premium) WordPress themes and having an options panel that stands out is quite important.
As far as i know if you build a free theme you plan to submit to WordPress.org you have to use the Settings API.
Here are some resources you can check out if you want to learn more about it:
- Settings API – WordPress Codex
- Incorporating the Settings API in WordPress Themes
- Creating Custom Options Pages in WordPress (video)
Define the Options (wps-panel-options.php)
First we’ll set a few “dummy” options which we’ll use to develop the system that’ll generate the fields and pass them over to the HTML output.
<?php
/* ---------------------------------------------------------------------------------------------------
File: wps-panel-options.php
Here we will set the options we are going to have in the theme options panel.
--------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------
Declare vars
--------------------------------------------------------------------------------------------------- */
$shortname = 'wps';
$options = array();
/* ---------------------------------------------------------------------------------------------------
First Section
--------------------------------------------------------------------------------------------------- */
$options[] = array( 'title' => 'First Section',
'type' => 'open',
'desc' => 'Here goes the description for this section');
$options[] = array( 'title' => 'Text Field',
'desc' => 'Here goes the description for this field.',
'id' => $shortname.'_text_field',
'std' => 'default value',
'type' => 'text' );
$options[] = array( 'title' => 'Textarea Field',
'desc' => 'Here goes the description for this field.',
'id' => $shortname.'_textarea_field',
'std' => '',
'type' => 'textarea' );
$options[] = array( 'title' => 'Select Field',
'desc' => 'Here goes the description for this field.',
'id' => $shortname.'_select_field',
'opts' => array( 'Option Title 1' => 'option_value_1', 'Option Title 2' => 'option_value_2' ),
'std' => 'option_value_1',
'type' => 'select' );
$options[] = array( 'title' => 'Radio Field',
'desc' => 'Here goes the description for this field.',
'id' => $shortname.'_radio_field',
'opts' => array( 'Option Title 1' => 'option_value_1', 'Option Title 2' => 'option_value_2' ),
'std' => 'option_value_1',
'type' => 'radio' );
$options[] = array( 'title' => 'Checkbox Field',
'desc' => 'Here goes the description for this field.',
'id' => $shortname.'_radio_field',
'std' => '',
'type' => 'checkbox' );
$options[] = array( 'type' => 'close' );
/* ---------------------------------------------------------------------------------------------------
Second Section
--------------------------------------------------------------------------------------------------- */
$options[] = array( 'title' => 'Second Section',
'type' => 'open',
'desc' => 'Here goes the description for this section');
$options[] = array( 'title' => 'Text Field 2',
'desc' => 'Here goes the description for this field.',
'id' => $shortname.'_text_field_2',
'std' => '',
'type' => 'text' );
$options[] = array( 'title' => 'Textarea Field 2',
'desc' => 'Here goes the description for this field.',
'id' => $shortname.'_textarea_field_2',
'std' => '',
'type' => 'textarea' );
$options[] = array( 'title' => 'Select Field 2',
'desc' => 'Here goes the description for this field.',
'id' => $shortname.'_select_field_2',
'opts' => array( 'Option Title 1' => 'option_value_1', 'Option Title 2' => 'option_value_2' ),
'std' => 'option_value_2',
'type' => 'select' );
$options[] = array( 'type' => 'close' );
It’s a big chunk of code but it’s really simple, mostly the same code over and over again.
As you can see all options go inside of $options array.
Option sections are separated by defining where a section starts and where it ends using the open and close type. Every other type except those 2 will represent an option field.
Here is what each parameter represents:
- title – The title of the option, will be shown as the label of the field.
- desc – The description of the option.
- id – This one is important, it’s the id of the option, has to be unique.
- std – the default value for the option. Can be blank. The least descriptive parameter name, i used it the first time when i made a theme options panel and i just kept using it, it’s meaningful for me.
- opts – Array with field options. Not used by every option type, at the moment just by the select and radio types.
Get and Output the HTML (wps-panel.php)
As you remember the function used to output the HTML to the page is wps_panel_output. Time to change all that HTML we have in there so it gets the options declared in wps-panel-options.php and output the fields.
/* ---------------------------------------------------------------------------------------------------
wpsPanel HTML OUTPUT
--------------------------------------------------------------------------------------------------- */
function wps_panel_output(){
/* Declare some vars */
$sidebar_html = '';
$fields_html = '';
/* Include the options from wps-panel-options.php */
include TEMPLATEPATH.'/wps-panel/wps-panel-options.php';
/* Go through all the options to populate the 2 vars we declared above */
include TEMPLATEPATH.'/wps-panel/wps-panel-generator.php';
?>
<!-- Form to reset options to default values -->
<form method="post" enctype="multipart/form-data">
<input type="hidden" name="action" value="reset" />
<p>
Want to <input type="submit" class="button-secondary" value="reset all options" /> to default values?
</p>
</form>
<!-- Form with all the options -->
<form method="post" enctype="multipart/form-data">
<div id="wps-panel">
<div id="wps-panel-sidebar">
<ul>
<?php echo $sidebar_html; ?>
</ul>
</div><!-- #wps-panel-sidebar -->
<div id="wps-panel-content">
<?php echo $fields_html; ?>
<div id="wps-panel-actions">
<input type="hidden" name="action" value="save" />
<input type="submit" class="button-primary" value="Save Options" />
</div><!-- #wps-panel-actions -->
</div><!-- #wps-panel-content -->
</div><!-- #wps_panel -->
</form>
<?php
}/* wps_panel_output() */
You see the second “include”? We didn’t make that file, didn’t we? Well i decided that it’ll be cleaner and more organized to have the part where the 2 vars declared at the top will be generated in a separate PHP file (it’s a big chunk of code).
Generate the HTML (wps-panel-generator.php)
Prepare to scroll, it’s a big one, but do not fear, it’s easy to understand.
<?php
/* ---------------------------------------------------------------------------------------------------
File: wps-panel-generator.php
--------------------------------------------------------------------------------------------------- */
foreach($options as $option){
/* Get the value of the field (nothing for types open and close). */
if($option['type'] != 'open' && $option['type'] != 'close'){
/* Variable that will hold the value of this option */
$real_value = '';
/* Get default value */
$default_value = $option['std'];
/* Get the value if user has set it */
$user_defined_value = get_option($option['id']);
/* Set the $real_value */
if($user_defined_value == '') { $real_value = $default_value; }else{ $real_value = $user_defined_value; }
}/* end if */
/* Populate $sidebar_html and $content_html according to the option type */
switch ($option['type']) {
/* open: Opens a new section */
case 'open':
/* Generate the id which will be used as the id of the section (for the tabs system purposes) */
$tab_id = str_replace(' ', '-', strtolower($option['title']));
/* Add the new link in the sidebar for this section */
$sidebar_html .= '<li><a href="#wps-panel-'.$tab_id.'">'.$option['title'].'</a></li>';
/* Open the new section */
$fields_html .= '<div class="wps-panel-section" id="wps-panel-'.$tab_id.'">';
break;
/* close: Closes the current section */
case 'close':
/* Close the current section */
$fields_html .= '</div><!-- .wps-panel-section -->';
break;
/* text: Simple textual input field */
case 'text':
/* Field container open */
$fields_html .= '<div class="wps-panel-field">';
/* Label */
$fields_html .= '<label for="'.$option['id'].'">'.$option['title'].'</label>';
/* Description */
if(isset($option['desc'])){
$fields_html .= '<div class="wps-panel-description">';
$fields_html .= $option['desc'];
$fields_html .= '</div><!-- .jw-field-description -->';
}
/* The Field */
$fields_html .= '<input type="text" name="'.$option['id'].'" id="'.$option['id'].'" value="'.$real_value.'" />';
/* Field container close */
$fields_html .= '</div><!-- .wps-panel-field -->';
break;
/* textarea: Text area field */
case 'textarea':
/* Field container open */
$fields_html .= '<div class="wps-panel-field">';
/* Label */
$fields_html .= '<label for="'.$option['id'].'">'.$option['title'].'</label>';
/* Description */
if(isset($option['desc'])){
$fields_html .= '<div class="wps-panel-description">';
$fields_html .= $option['desc'];
$fields_html .= '</div><!-- .jw-field-description -->';
}
/* The Field */
$fields_html .= '<textarea name="'.$option['id'].'" id="'.$option['id'].'">'.$real_value.'</textarea>';
/* Field container close */
$fields_html .= '</div><!-- .wps-panel-field -->';
break;
/* select: Select field */
case 'select':
/* Field container open */
$fields_html .= '<div class="wps-panel-field">';
/* Label */
$fields_html .= '<label for="'.$option['id'].'">'.$option['title'].'</label>';
/* Description */
if(isset($option['desc'])){
$fields_html .= '<div class="wps-panel-description">';
$fields_html .= $option['desc'];
$fields_html .= '</div><!-- .jw-field-description -->';
}
/* The Field */
$fields_html .= '<select name="'.$option['id'].'" id="'.$option['id'].'">';
/* Loop options */
foreach($option['opts'] as $key => $value){
/* Which options should be selected */
if($value == $real_value){
$active_attr = 'selected';
}else{
$active_attr = '';
}
/* Option */
$fields_html .= '<option value="'.$value.'" '.$active_attr.'>'.$key.'</option>';
}
$fields_html .= '</select>';
/* Field container close */
$fields_html .= '</div><!-- .wps-panel-field -->';
break;
/* radio: radio field */
case 'radio':
/* Field container open */
$fields_html .= '<div class="wps-panel-field">';
/* Label */
$fields_html .= '<label for="'.$option['id'].'">'.$option['title'].'</label>';
/* Description */
if(isset($option['desc'])){
$fields_html .= '<div class="wps-panel-description">';
$fields_html .= $option['desc'];
$fields_html .= '</div><!-- .jw-field-description -->';
}
/* The Field */
foreach($option['opts'] as $key => $value){
/* Which options should be selected */
if($value == $real_value){
$active_attr = 'checked';
}else{
$active_attr = '';
}
/* Field */
$fields_html .= '<p><input type="radio" name="'.$option['id'].'" value="'.$value.'" '.$active_attr.'>'.$key.'</p>';
}
/* Field container close */
$fields_html .= '</div><!-- .wps-panel-field -->';
break;
/* checkbox: checkbox field */
case 'checkbox':
/* Field container open */
$fields_html .= '<div class="wps-panel-field">';
/* Label */
$fields_html .= '<label for="'.$option['id'].'">'.$option['title'].'</label>';
/* Description */
if(isset($option['desc'])){
$fields_html .= '<div class="wps-panel-description">';
$fields_html .= $option['desc'];
$fields_html .= '</div><!-- .jw-field-description -->';
}
/* The Field */
if($value == $real_value){
$active_attr = 'checked';
}else{
$active_attr = '';
}
/* Field */
$fields_html .= '<p><input type="checkbox" name="'.$option['id'].'" value="'.$value.'" '.$active_attr.'>'.$key.'</p>';
/* Field container close */
$fields_html .= '</div><!-- .wps-panel-field -->';
break;
}/* end switch */
}/* end foreach */
Told you it’s a big one. But quite a lot of lines are repeating over and over again for every “case”“. I commented everything in the code so you should be able to understand what’s what, but let me give some explanation here also.
We’re looping all the options we have set in wps-panel-options.php. Inside of that loop we first get the current value of the option (if user didn’t set one we get the default which you defined). After that we’re using a switch statement which determines the option type and adds the option HTML to the $sidebar_html and $fields_html variables. That’s all.
You’ll notice the first two “cases” are the open and close types, which aren’t real options but we’re using them to determine where a section starts, where it ends and to add the link to the section in the sidebar.
Also, as you can see, all the fields use the id of the option for the name attribute.
I think everything else is quite clear, if you need more info feel free to ask in the comments.
Submit&Reset Buttons
Maybe i should have added these in the second part when we made the HTML template but nevermind, we’ll handle it now, it’s not a big change. Of course it goes in the wps_panel_output function.
<!-- Form to reset options to default values --> <form method="post" enctype="multipart/form-data"> <input type="hidden" name="action" value="reset" /> <p> Want to <input type="submit" class="button-secondary" value="reset all options" /> to default values? </p> </form> <!-- Form with all the options --> <form method="post" enctype="multipart/form-data"> <div id="wps-panel"> <div id="wps-panel-sidebar"> <ul> <?php echo $sidebar_html; ?> </ul> </div><!-- #wps-panel-sidebar --> <div id="wps-panel-content"> <?php echo $fields_html; ?> <div id="wps-panel-actions"> <input type="hidden" name="action" value="save" /> <input type="submit" class="button-primary" value="Save Options" /> </div><!-- #wps-panel-actions --> </div><!-- #wps-panel-content --> </div><!-- #wps_panel --> </form>
As you can see we wrapped what we had previously with a form and added another form on top of it(gonna be used to reset the options). Also, we used WordPress styles for the buttons (button-primary and button-secondary).
And you’ll notice both forms have an input hidden field with the name attribute of “action” and value of “save” and “reset”. That’s how we’ll know what to do in the next section of code.
Final Words
That’s all for the third part of the series. In the next part we’ll cover saving the options, retrieving them for usage in the front-end, handling security and add a few more option types.
You can download the files the files for this tutorial, the download link is located on top by the thumbnail. There’s a tweet button too, feel free to tweet about this tutorial.









Thanks a lot! Very nice tutorial series!
You’re welcome. Any suggestions for tutorials after this series ends?
Awesome tuts boba !!
How about tutorial for adding more feature in the control panel, like colorpicker, file uploader, fonts, etc.
Well, It’s just my suggestion..
Thanks bro..
Well i guess we’ll have ourselves a part four then
Man thanks a lot! I can’t wait for the next part.
Poland salutes
You’re welcome. Just 2 more days
Awesome Page. Can we expect the next page soon. I am sure you got caught up in development like most developers do but is it something we should still look forward to?
The fourth part is available. http://wpscientist.com/tutorial/building-a-theme-options-panel-part-four/
What about sanitization and validation?
Planned a special part for that. Should be up in January.
Sounds Great. Thanks for sharing;) i will test a drive
Hey great set of tutorials, really learnt a lot from them. Working on this one the panel-options.php you have both the radio and checkbox in the first section with the same ID which causes conflicts with saving.