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

  1. Introduction
  2. Define the Options (wps-panel-options.php)
  3. Get and Output the HTML (wps-panel.php)
  4. 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:

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

  • authorSlobodan Kustrimovic
  • date25th July 2011
  • views7227
  • comments12 comments

About Author

Slobodan Kustrimovic

I'm a web developer based in a little country called Serbia. Developing WordPress themes is what i like the most and i try to learn more about it every day. I'm the developer behind our WordPress themes and the writer of the Web Development tutorials.

12 Comments

  1. Niels De Craecker on November 12, 2011 Reply

    Thanks a lot! Very nice tutorial series!

    • boba on November 13, 2011 Reply

      You’re welcome. Any suggestions for tutorials after this series ends?

      • yulius on November 15, 2011 Reply

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

        • boba on November 15, 2011 Reply

          Well i guess we’ll have ourselves a part four then :)

  2. Zdzichu on November 14, 2011 Reply

    Man thanks a lot! I can’t wait for the next part.

    Poland salutes :)

    • boba on November 14, 2011 Reply

      You’re welcome. Just 2 more days :)

  3. David Bartolone on December 21, 2011 Reply

    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?

  4. Simon on December 22, 2011 Reply

    What about sanitization and validation?

    • boba on December 22, 2011 Reply

      Planned a special part for that. Should be up in January.

  5. Mj on May 3, 2012 Reply

    Sounds Great. Thanks for sharing;) i will test a drive :)

  6. Jack Whiting on August 4, 2012 Reply

    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.

Leave a Comment

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>