How to Create a Better WordPress Options Panel

How to Create a Better WordPress Options Panel

Today, we’ll go through the entire process of creating an admin options panel for a WordPress theme, using the excellent WooFramework as an example. Then, we’ll take things a step further, as we implement jQuery to improve some of the functionality.

Tutorial Details

  • Program: WordPress
  • Version: 2.7, 2.8, 2.9 onwards
  • Difficulty: Intermediate
  • Estimated Completion Time: 1.5 hours
Final Product

WordPress is one of the most popular Content Management Software (CMS) systems out there. Whether it be for a client project or for selling themes on ThemeForest, WordPress is fast emerging as a CMS of choice for many web developers. It’s relatively easy to use, but can be made even simpler when you include an administration panel for users. Rather than having to open up the PHP template files and fiddling with the code, users can directly use the options panel to interact with your WordPress theme.

For example – if your theme has red, blue and green color schemes, and each has a corresponding CSS file, it would be far easier for a user to select their preferred color from a dropdown list. So today, let me guide you through the entire process of creating and enhancing a WordPress admin panel page inspired by Woo.

Step 1

Before we begin creating the admin panel, we need to have a theme, right? So download the source files provided with the tutorial. I’ve slightly modified the Classic WordPress theme. Place the ‘nettuts’ folder (I’ve named the theme ‘Nettuts’) in your wp-content/themes folder. You should see the following files:

  • functions.php (blank)
  • index.php
  • comments.php
  • footer.php
  • header.php
  • rtl.php
  • sidebar.php
  • style.css
  • screenshot.png
  • An images folder with two files

Most of our work is going to be done within functions.php file.

A theme can optionally use a functions file, which resides in the theme subdirectory and is named functions.php. This file basically acts like a plugin, and if it is present in the theme you are using, it is automatically loaded during WordPress initialization (both for admin pages and external pages).

Suggested uses for this file:

  • Define functions used in several template files of your theme
  • Set up an admin screen, giving users options for colors, styles, and other aspects of your theme

(From the WP Codex)

Step 2

Now that we’ve got our WordPress theme set up, go to Appearance>Themes, and activate the nettuts theme.

Activated? Ok, great. Now we have to think of a layout for our admin panel page. Here’s the structure I’ve decided upon:

<div class="wrap rm_wrap">
	<div class="rm_opts">
		<form method="post">
			<div class="rm_section">
				<div class="rm_title> 
					<h3>Title</h3>
					<submit button>
				</div>
				<div class="rm_input rm_<select/textarea/text etc>">
					<input area>
					<description>
				</div>
			</div>
			
			/*Repeat the inputs for as many options as required. */
			/* use <div class="rm_section"> for each new section of inputs eg General, Home Page etc */
		</form>
	</div>			
</div>			

Let me explain all of that to you. The options set is going to be wrapped up into a div named “rm_wrap” and then “rm_opts” for the options. Then we start a form, with all the inputs within it. Each section of options(general Settings, Homepage settings, Blog settings etc) has a separate div with a class of “rm_section”. This div has a title (for the name) as well as several input divs within it. By using classes like <div class=”rm_input rm_select”>, we can style dropdowns, text inputs, and textareas differently.

Now, the most important thing is that the coding of this shouldn’t be done manually – we should use PHP’s flexibility as much as possible. That means efficiency: don’t code manually when you have loops for you!

Step 3

Begin by opening up functions.php in your favorite code editor (I use Notepad++). Enter the following code:

<?php

$themename = "Nettuts";
$shortname = "nt";

The two PHP variables hold the name of your theme (Nettuts in our case), and a shortname which you’ve defined (nt in our case). The shortname is used to prefix all our theme options names, and is usually unique to a particular theme. Moving on, we’ll write some code to automatically generate a list of WordPress categories, rather than having users type in ID numbers. Enter the code below under the already typed code:

$categories = get_categories('hide_empty=0&orderby=name');
$wp_cats = array();
foreach ($categories as $category_list ) {
       $wp_cats[$category_list->cat_ID] = $category_list->cat_name;
}
array_unshift($wp_cats, "Choose a category"); 

This snippet uses WordPress’ built-in get_categories function to fetch all categories, and then uses a foreach loop to store them in the variable $wp_cats. The options “Choose a category” is then added to the top of the array

Step 4

Now we start entering a list of options for the theme. See below, and paste it into your functions.php file:


$options = array (
 
array( "name" => $themename." Options",
	"type" => "title"),
 

array( "name" => "General",
	"type" => "section"),
array( "type" => "open"),
 
array( "name" => "Colour Scheme",
	"desc" => "Select the colour scheme for the theme",
	"id" => $shortname."_color_scheme",
	"type" => "select",
	"options" => array("blue", "red", "green"),
	"std" => "blue"),
	
array( "name" => "Logo URL",
	"desc" => "Enter the link to your logo image",
	"id" => $shortname."_logo",
	"type" => "text",
	"std" => ""),
	
	
array( "name" => "Custom CSS",
	"desc" => "Want to add any custom CSS code? Put in here, and the rest is taken care of. This overrides any other stylesheets. eg: a.button{color:green}",
	"id" => $shortname."_custom_css",
	"type" => "textarea",
	"std" => ""),		
	
array( "type" => "close"),
array( "name" => "Homepage",
	"type" => "section"),
array( "type" => "open"),

array( "name" => "Homepage header image",
	"desc" => "Enter the link to an image used for the homepage header.",
	"id" => $shortname."_header_img",
	"type" => "text",
	"std" => ""),
	
array( "name" => "Homepage featured category",
	"desc" => "Choose a category from which featured posts are drawn",
	"id" => $shortname."_feat_cat",
	"type" => "select",
	"options" => $wp_cats,
	"std" => "Choose a category"),
	

array( "type" => "close"),
array( "name" => "Footer",
	"type" => "section"),
array( "type" => "open"),
	
array( "name" => "Footer copyright text",
	"desc" => "Enter text used in the right side of the footer. It can be HTML",
	"id" => $shortname."_footer_text",
	"type" => "text",
	"std" => ""),
	
array( "name" => "Google Analytics Code",
	"desc" => "You can paste your Google Analytics or other tracking code in this box. This will be automatically added to the footer.",
	"id" => $shortname."_ga_code",
	"type" => "textarea",
	"std" => ""),	
	
array( "name" => "Custom Favicon",
	"desc" => "A favicon is a 16x16 pixel icon that represents your site; paste the URL to a .ico image that you want to use as the image",
	"id" => $shortname."_favicon",
	"type" => "text",
	"std" => get_bloginfo('url') ."/favicon.ico"),	
	
array( "name" => "Feedburner URL",
	"desc" => "Feedburner is a Google service that takes care of your RSS feed. Paste your Feedburner URL here to let readers see it in your website",
	"id" => $shortname."_feedburner",
	"type" => "text",
	"std" => get_bloginfo('rss2_url')),

 
array( "type" => "close")
 
);

That was a large chunk of code, which surely warrants some explanation. So here we go:

  • The PHP variable $options stores the entire list of options for the theme.
  • It is composed of a number of arrays, each with a “type” key to signify how it will be displayed, and what it does.
  • We start with a “type” => “title” array – this will be used to show the themename and a title at the top of the page
  • Each section (General, Homepage and Footer) has a separate list of options.
  • We start a new section by closing any previous sections, declaring a new section using array( "name" => "Footer",
    "type" => "section")
    and opening a new section.
  • Each option can have the options specified below:
    name: The name of the input field.
    desc: A short description explaining what it is to the user.
    id: the id of the field, prefixed by the shortname. It will be used to store as well as access the options.
    type: the input type – select, text or textarea
    options: used to declare an array of options for a select type input.
    std: the default input value, used if no other input is given.

Step 5

Try navigating to WordPress. You will see that there’s no option anywhere to actually view the admin panel page; so how can we view it? Add the following pieces of code to the functions.php file:

function mytheme_add_admin() {
 
global $themename, $shortname, $options;
 
if ( $_GET['page'] == basename(__FILE__) ) {
 
	if ( 'save' == $_REQUEST['action'] ) {
 
		foreach ($options as $value) {
		update_option( $value['id'], $_REQUEST[ $value['id'] ] ); }
 
foreach ($options as $value) {
	if( isset( $_REQUEST[ $value['id'] ] ) ) { update_option( $value['id'], $_REQUEST[ $value['id'] ]  ); } else { delete_option( $value['id'] ); } }
 
	header("Location: admin.php?page=functions.php&saved=true");
die;
 
} 
else if( 'reset' == $_REQUEST['action'] ) {
 
	foreach ($options as $value) {
		delete_option( $value['id'] ); }
 
	header("Location: admin.php?page=functions.php&reset=true");
die;
 
}
}
 
add_menu_page($themename, $themename, 'administrator', basename(__FILE__), 'mytheme_admin');
}

function mytheme_add_init() {

}

This function is meant for updating options, as well as adding a menu page. If the options are being saved (indicated by a hidden variable save), then all the options are updated with their new values. If the options are being reset (indicated by another hidden variable with a value reset), then all of the options are deleted. The last line adds a menu page – the parameters are respectively, name and title, the user authorization level required to view the page, the save page and the function used for display/saving (called mytheme_admin in our case). See the mytheme_add_init, a blanbk function? Let that be, we’ll come to it later.

Step 6

Still no theme options page, right? Well, remember the mytheme_admim function we had talked about a few lines ago? We still haven’t written that function. So use the code from steps 6,7 and 8 to write that function. Starting off:

function mytheme_admin() {
 
global $themename, $shortname, $options;
$i=0;
 
if ( $_REQUEST['saved'] ) echo '<div id="message" class="updated fade"><p><strong>'.$themename.' settings saved.</strong></p></div>';
if ( $_REQUEST['reset'] ) echo '<div id="message" class="updated fade"><p><strong>'.$themename.' settings reset.</strong></p></div>';
 
?>
<div class="wrap rm_wrap">
<h2><?php echo $themename; ?> Settings</h2>
 
<div class="rm_opts">
<form method="post">

Pretty simple, right? If the options have been saved, write a message indicating so. Likewise for resets. You’ll notice a class=”updated fade” – WordPress will automatically fade this out in a few sections. Nifty, right? Moving on, we then start the “rm_wrap” div.

Step 7

Carrying on from above, paste in the following code:

<?php foreach ($options as $value) {
switch ( $value['type'] ) {
 
case "open":
?>
 
<?php break;
 
case "close":
?>
 
</div>
</div>
<br />

 
<?php break;
 
case "title":
?>
<p>To easily use the <?php echo $themename;?> theme, you can use the menu below.</p>

 
<?php break;
 
case 'text':
?>

<div class="rm_input rm_text">
	<label for="<?php echo $value['id']; ?>"><?php echo $value['name']; ?></label>
 	<input name="<?php echo $value['id']; ?>" id="<?php echo $value['id']; ?>" type="<?php echo $value['type']; ?>" value="<?php if ( get_settings( $value['id'] ) != "") { echo stripslashes(get_settings( $value['id'])  ); } else { echo $value['std']; } ?>" />
 <small><?php echo $value['desc']; ?></small><div class="clearfix"></div>
 
 </div>
<?php
break;
 
case 'textarea':
?>

<div class="rm_input rm_textarea">
	<label for="<?php echo $value['id']; ?>"><?php echo $value['name']; ?></label>
 	<textarea name="<?php echo $value['id']; ?>" type="<?php echo $value['type']; ?>" cols="" rows=""><?php if ( get_settings( $value['id'] ) != "") { echo stripslashes(get_settings( $value['id']) ); } else { echo $value['std']; } ?></textarea>
 <small><?php echo $value['desc']; ?></small><div class="clearfix"></div>
 
 </div>
  
<?php
break;
 
case 'select':
?>

<div class="rm_input rm_select">
	<label for="<?php echo $value['id']; ?>"><?php echo $value['name']; ?></label>
	
<select name="<?php echo $value['id']; ?>" id="<?php echo $value['id']; ?>">
<?php foreach ($value['options'] as $option) { ?>
		<option <?php if (get_settings( $value['id'] ) == $option) { echo 'selected="selected"'; } ?>><?php echo $option; ?></option><?php } ?>
</select>

	<small><?php echo $value['desc']; ?></small><div class="clearfix"></div>
</div>
<?php
break;
 
case "checkbox":
?>

<div class="rm_input rm_checkbox">
	<label for="<?php echo $value['id']; ?>"><?php echo $value['name']; ?></label>
	
<?php if(get_option($value['id'])){ $checked = "checked=\"checked\""; }else{ $checked = "";} ?>
<input type="checkbox" name="<?php echo $value['id']; ?>" id="<?php echo $value['id']; ?>" value="true" <?php echo $checked; ?> />
	<small><?php echo $value['desc']; ?></small><div class="clearfix"></div>
 </div>
<?php break; 

That is one large piece of code! Explanation – using a php foreach loop, each option type is evaluated on a case-by-case basis. We use a switch-case technique for this. The switch variable is the options – the cases are matched and evaluated. Notice the ‘break’ statement after each case? This is to prevent something known as the ‘fall-through’ property. When a case is matched, all successive cases are also executed. This means that if we match case 3, cases 4,5 etc. are also executed. We don’t wan’t that, right? So use a break to stop the switch-case.

If there is an “open” type option – nothing is done. If there is a “close” type options, two divs are closed. The “title” option is only used once – it is an introduction to the theme options. For each of the types “text” (input type=”text”), “select” (dropdowns), “checkbox” and “textarea” (its obvious what those mean), the corresponding input is displayed. Notice the <div class=”clearfix”> – it’s used for clearing floats, which we will do later.

Step 8

We’re coming to the end of that rather massive function. Paste in the code below:


case "section":

$i++;

?>

<div class="rm_section">
<div class="rm_title"><h3><img src="<?php bloginfo('template_directory')?>/functions/images/trans.gif" class="inactive" alt="""><?php echo $value['name']; ?></h3><span class="submit"><input name="save<?php echo $i; ?>" type="submit" value="Save changes" />
</span><div class="clearfix"></div></div>
<div class="rm_options">

 
<?php break;
 
}
}
?>
 
<input type="hidden" name="action" value="save" />
</form>
<form method="post">
<p class="submit">
<input name="reset" type="submit" value="Reset" />
<input type="hidden" name="action" value="reset" />
</p>
</form>
<div style="font-size:9px; margin-bottom:10px;">Icons: <a href="http://www.woothemes.com/2009/09/woofunction/">WooFunction</a></div>
 </div> 
 

<?php
}

?>

For a “section” type option, I’ve used a counter variable $i. This keeps track of the sections number and conactenates it to the name of the submit button, to have unique submit buttons. There is also a last form at the end for resetting all options. The image used is going to be a transparent image used in our jQuery-fication. Use this very last piece of code to bring our functions into play:

<?php
add_action('admin_init', 'mytheme_add_init');
add_action('admin_menu', 'mytheme_add_admin');
?>

That tells WordPress to add the admin menu.

Step 9

And voila! We have our own awesome admin panel page with a separate menu position for itself. So let’s check it out – click the link. And yuck. That has got to be the most ugly admin panel page ever. So let’s call upon our good friend, CSS! Create a new folder in the nettuts/ directory and name it “functions”. Create a new CSS file there – functions.css. Paste in the following code:

.rm_wrap{
	width:740px;
}
.rm_section{
	border:1px solid #ddd;
	border-bottom:0;
	background:#f9f9f9;
}
.rm_opts label{
	font-size:12px;
	font-weight:700;
	width:200px;
	display:block;
	float:left;	
}
.rm_input {
	padding:30px 10px;
	border-bottom:1px solid #ddd;
	border-top:1px solid #fff;
}
.rm_opts small{
	display:block;
	float:right;
	width:200px;
	color:#999;
}
.rm_opts input[type="text"], .rm_opts select{
	width:280px;
	font-size:12px;
	padding:4px;
	color:#333;
	line-height:1em;
	background:#f3f3f3;
}
.rm_input input:focus, .rm_input textarea:focus{
		background:#fff;
}
.rm_input textarea{
	width:280px;
	height:175px;
	font-size:12px;
	padding:4px;
	color:#333;
	line-height:1.5em;
	background:#f3f3f3;
}
.rm_title h3 {
	cursor:pointer;
	font-size:1em;
	text-transform: uppercase;
	margin:0;
	font-weight:bold;
	color:#232323;
	float:left;
	width:80%;
	padding:14px 4px;
}
.rm_title{
	cursor:pointer;
	border-bottom:1px solid #ddd;
	background:#eee;
	padding:0;
}
.rm_title h3 img.inactive{
	margin:-8px 10px 0 2px;
	width:32px;
	height:32px;	
	background:url('images/pointer.png') no-repeat 0 0;
	float:left;
	-moz-border-radius:6px;
	border:1px solid #ccc;
}
.rm_title h3 img.active{
	margin:-8px 10px 0 2px;
	width:32px;
	height:32px;	
	background:url('images/pointer.png') no-repeat  0 -32px;
	float:left;
	-moz-border-radius:6px;
	-webkit-border-radius:6px;
	border:1px solid #ccc;
}		
.rm_title h3:hover img{
	border:1px solid #999;
}
.rm_title span.submit{
	display:block;
	float:right;
	margin:0;
	padding:0;
	width:15%;
	padding:14px 0;
}
.clearfix{
	clear:both;
}
.rm_table th, .rm_table td{
	border:1px solid #bbb;
	padding:10px;
	text-align:center;
}
.rm_table th, .rm_table td.feature{
	border-color:#888;
	}

I won’t explain anything here; it’s pretty clear what each CSS declaration does, and you’re free to customize the layout for your own theme.

Step 10

So now we have a nice CSS file. But how do we add it to the page? After all, we don’t have direct access to the <head> of the document. Remember that blank mytheme_add_init() function we wrote in Step 4? That will come in handy. Change it to this:

function mytheme_add_init() {
$file_dir=get_bloginfo('template_directory');
wp_enqueue_style("functions", $file_dir."/functions/functions.css", false, "1.0", "all");
}

That adds the functions.css file to the head. The location of the file is determined by the template directory.

Step 11

Go look at the page now. Pretty nice looking, isn’t it? But then, you ask, whats the ‘+’ icon for? Well, thats where jQuery comes in!. Create a new file rm_script.js in the nettuts/functions/ folder. Paste in the following code:

jQuery(document).ready(function(){
		jQuery('.rm_options').slideUp();
		
		jQuery('.rm_section h3').click(function(){		
			if(jQuery(this).parent().next('.rm_options').css('display')==='none')
				{	jQuery(this).removeClass('inactive').addClass('active').children('img').removeClass('inactive').addClass('active');
					
				}
			else
				{	jQuery(this).removeClass('active').addClass('inactive').children('img').removeClass('active').addClass('inactive');
				}
				
			jQuery(this).parent().next('.rm_options').slideToggle('slow');	
		});
});

What this does is – once the DOM loads, all the rm_options slide up. When the ‘+’ icon is clicked, the inactive class is removed from the image and the active class added – making it a ‘-’ icon. The reverse is done when the ‘-’ icon is clicked. The rm_options is then slid up or down(determined by the current CSS state) using the slideToggle function – pretty simple. To add this script, the same mytheme_add_init() function is used. Change it to:

function mytheme_add_init() {
$file_dir=get_bloginfo('template_directory');
wp_enqueue_style("functions", $file_dir."/functions/functions.css", false, "1.0", "all");
wp_enqueue_script("rm_script", $file_dir."/functions/rm_script.js", false, "1.0");
}

The jQuery script will now be active. Gp check it out. Personally, I think its beautiful!

Step 12

Now that we have our theme options page all set up, I’ll just run you through using the options. The code to use the options is as follows:

$var = get_option('nt_colur_scheme');

That will fetch the nt_color_scheme options. See the examples below:

/* To change the CSS stylesheet depending on the chosen color */
<link rel="stylesheet" type="text/css"  href="<?php bloginfo('template_directory'); ?>/<?php echo get_option('nt_color_scheme'); ?>.css" /> 

/*To echo some footer copyright text, with HTML */
<p><?php echo stripslashes(get_option('bl_footer_text')); ?></p>

The variety of uses is limited only by your imagination.

Conclusion

I hope you’ve learned something in this tutorial. This isn’t your standard options panel. This one doesn’t use tables, it’s jQuery enhanced, uses awesome CSS, and is extremely easy to use. The point of this tutorial is to learn – you could always replace collapsible panels with tabs, for example, or even something more advanced. Use your creativity! Feel free to discuss or ask questions in the comments!

WooThemes has since released version two of their framework. You can review the details here.

Tags: Wordpress
Note: Want to add some source code? Type <pre><code> before it and </code></pre> after it. Find out more
  • http://xandesigns.com Alex McCabe

    Well this is perfect for a piece of coursework that I doing. Thank you very much =]

  • Chris

    Awesome. This is really helpful.

  • http://themeforest.net/user/unisphere/portfolio?ref=unisphere UniSphere

    Great tutorial Rohan, enhancing my theme’s options page has been on my list of TODOs for a while. I’m sure this will come in handy.

    Thanks again, UniSphere

  • http://www.BryanWatson.ca Bryan Watson

    At Step 11, any reason you specifically use ‘jQuery’ instead of ‘$’ in the syntax?

    • http://www.weblimner.com Ali Sipahioglu

      I believe just $ won’t work in jQuery because WordPress uses jQuery in the safe mode.

    • http://zreedee.com Luis // zreedee.com

      This is used only when you are working with different javascript libraries at the same time.. normally to avoid conflicts between the js libraries.

      Here you have more info… http://docs.jquery.com/Using_jQuery_with_Other_Libraries

      Cheers!

    • http://mfields.org/ Michael Fields

      Could also be written like:

      jQuery(document).ready(function($){  
          $('.rm_options').slideUp();
          $('.rm_section h3').click(function(){
          // etc....
      
    • Rohan Mehta
      Author

      Everyone above is correct. It’s to avoid conflicts with other libraries.

      (And Michael’s method is very useful, too)

  • http://cosydale.com slaFFik

    I saw exactly this options panel in one a commercial theme on ThemeForest (Quadro: http://themeforest.net/item/quadro-wordpress-version/79966).
    Does Simon Bouchard know about this post?

    • Rohan Mehta
      Author

      No, he doesn’t, but that doesn’t matter. I ported that theme to WordPress :-)

      • http://cosydale.com slaFFik

        But that theme is already for WordPress – how could you port it – again?

    • Rohan Mehta
      Author

      It was in HTML. I converted it to WordPress for him.

      • Boyd

        Owned.

  • http://cosydale.com slaFFik

    I saw exactly this options panel in one of commercial themes on ThemeForest (Quadro: http://themeforest.net/item/quadro-wordpress-version/79966).
    Does Simon Bouchard know about this post?

  • http://www.joshcanhelp.com Josh C

    Fantastic, thanks for this! I pieced one together from a few different posts a week ago and it was much harder than it should be.

    Regarding the “$” as “jquery,” Ali is correct, the “$” won’t work in WordPress unfortunately.

  • http://wpshout.com Alex Denning

    I think ‘inspired by Woo’ is a bit of an understatement. It looks more or less the whole original WooFramework options page. Fair enough it’s GPL, but it’s hardly encouraging innovation showing off the market leader’s options page for all and sundry to copy.

    • http://net.tutspus.com Jeffrey way

      I’ll look into it. If it’s indeed a verbatim copy, we’ll remove this article. Thanks, guys!

      • http://www.nunomedia.com Nuno Marques

        I do not think you guys should remove… Make it better. It is interesting!

      • http://wpart.net Matthew Jones

        I’ve customized woothemes admin pages a lot for my themes and I can tell you that the whole code is woothemes. Not just the user-interface, but the way they came up with doing the arrays for the theme options is truly unique. I don’t think you should have to remove the tutorial, but I think they should be credited for this article. I credit them for the theme options page that I have used in my themes (which are exactly like this one by the way) in both my documentation and my video tutorials.

      • http://crispytech.com Saad Bassi

        @Jeffery Way
        All the code in php files of woo themes in GPL licensed and everyone is free to use it for any purpose. You can read GPL v2 for more info. There is no copyright violation.

    • Rohan Mehta
      Author

      Alex – the admin page I’ve created is different from the Woo page is multiple ways:
      -The HTML structure is different.
      -The array of options is different
      -The script is different
      -The way of displaying is different
      -There are different save buttons – no floating button.
      -And of course, the new WooThemes don’t have that interface anymore.

      I’ll be the first to admit, the CSS is quite similar, but that’s pretty much the only thing. WooThemes’ admin panels (when the did look like this) had a very, very different backend.

      • http://literalbarrage.org/blog/ Doug Stewart

        To those complaining that Woo has been “ripped off”;

        I (http://literalbarrage.org/blog/archives/2007/05/03/a-theme-tip-for-wordpress-theme-authors/) and The Undersigned came up with the basis for the original Woo options pages. Adii contracted with me to do some additional work back before he started Woo and my original tutorial served as the basis for their back-end.

        My code was GPL. Woo’s was/is GPL. There’s no copyright issues at play here.

        (I just wish folk would credit my old tutorial every once in a while. *grin*)

  • http://azadcreative.com Saddam Azad

    Excellent tutorial. Time to give my Themeforest theme an upgrade.

  • http://fan-tas-tic.com Tschai

    Simply fan-tas-tic!

    I was wondering whether I should invest time and energy in an Admin/Options Panel…but this makes the decision rather easy, I think.

    Thanks a lot…

  • http://www.thomascraigconsulting.com Thomas Craig Consulting

    Very nice, I am just in the processing of dabbling with WordPress theme development, although it has been pretty straight forward thus far, with the abundance of tutorials and guidance available from the WordPress community. This is definitely going into the bookmarks for future reference.

  • http://www.elime.be brechtvhb

    I can’t understand how wordpress extensions are so popular. Don’t get me wrong, WordPress by itself is a great blogging platform. At the moment I’m working on a wordpress site for a customer (who’s previous webmaster abandonned the project) and I feel like It’s been years I’ve performed so poor. WordPress is lacking the base priciples of good programming (like separation of concerns and solid OOP for instance) or I’m missing something.

    • http://www.nomad-one.com @nomadone

      the main positive of wordpress is it;s ease of use, especially for the non-developer who does not code in php. Even someone with very limited front end dev skills and put together quite an advanced site in a short space of time.

      WordPress has made this easy for the masses, why code functionality from scratch when it’s already there to plug and play.

  • http://www.saadibrahim.com Saad Ibrahim

    woah… i was looking for a tutorial about it since quite a few days… thankyou guys!

  • http://www.aboubaker.info/ Aboubaker Nour

    Thank you so much for this tutorial…
    I need it right now,

  • http://m13online.com DogBoy455

    Not a very well written article.

    heh, thanks though.

  • http://benj3.com Benje

    Great tutorial! Thanks, this helped me a lot.

  • http://jayj.dk Jay

    It’s better to use register_setting

    Here’s a post about it

    http://planetozh.com/blog/2009/05/handling-plugins-options-in-wordpress-28-with-register_setting/

    The post is for plugins, but it’s pretty much the same way for themes.

  • John

    Jeffrey, if this tutorial gets removed, does that mean we can’t use the code in Themeforest themes?

    • Carlos Caniguante

      Nice question

  • http://sideradesign.com paul

    this is great. I printed it just in case…
    Seriously, even if it’s copied, remember the saying:
    “Good Designers Copy, Great Designers Steal”

    • http://tinygiantstudios.co.uk Tiny Giant Studios

      Hah… I’m sure Themeforest will be inspecting your code closely :)

  • http://www.jsxtech.com Jaspal Singh

    Great tutorial.
    Thanks for sharing.

  • http://www.jennieroutley.co.uk Jen

    Nice tutorial, but I feel a bit weird about it as it seems to be a straight woo themes custom admin.

    I prefer to use Theme Toolkit, it is way easier for making custom admin pages so long as you only want basic options available to your end user/client – http://planetozh.com/blog/my-projects/wordpress-theme-toolkit-admin-menu/

    • http://www.krio.me Kevin

      thanks for the link! very useful.

    • http://tinygiantstudios.co.uk Tiny Giant Studios

      Interestingly, Theme Toolkit is also proposed by Collis (The main man of envato) in his Rockstar WordPress Designer book (Available at http://rockablepress.com/books/rockstar-wordpress-designer/) … I’m actually working through it now.

    • DaveKingsnorth

      Thanks for the link, looks promising. wefunction wrote a good tutorial on custom write panels for wordpress as well

  • http://watchwhitecollarfree.com James

    thanks for tut interetesting

  • http://bloggerzbible.blogspot.com/ Bloggerzbible

    Thanks very nice tutorial

  • http://askewmedia.net Nissa Askew

    This is perfect for what I am doing. Great tutorial. Thanks!

  • http://junkiee.net Nina

    I was always wondering how to make this happen.
    Thanks a lot for sharing, very helpful post indeed :D

  • sandeep

    nice tutorial thanks…

  • Het Gelaat

    Thanks!!

  • http://tinygiantstudios.co.uk Tiny Giant Studios

    Admittedly, and rather ashamedly, I’ve only recently stumbled across tuts+ and I have to say I’m now a fervent convert. I’m spreading the gospel of “+” everywhere I go… Awesome article – this will definitely up the ante in commercial WordPress theme design!

  • http://www.damianherrington.co.uk Damian Herrington

    Excellent! This will be very handy to have, I always wondered how to do these. Thanks.

  • http://spotdex.com/ David Moreen

    I haven’t touched WordPress in a year and a lot of things changed!

  • http://www.jordanwalker.net Jordan Walker

    I sure like these tutorials, they are very helpful.

  • http://twitter.com/z0r z0r

    Thanks for the time spent writing this. A very useful article.

  • http://www.rafdesign.com Raf

    I think there is some problem with the source code provided. The folder containing the final result. Inside images directory there aren’t any image file with gif file extension and i see only png where as in the functions.php it is written to point to a gif file.

  • Kyle King

    Source theme no longer works. It says that the stylesheet is missing.

  • http://www.allenmoorecreative.com Allen Moore

    Thanks for this article. I have some projects that I could use this with.

  • http://www.tutsbot.com TutsBot

    Thanks for this tutorials , it is very helpful :)

  • http://fwebde.com/ Eric B.

    Are there supposed to be images here? Because they’re not loading for me…

  • http://www.dextech.info Dextech

    Nice post. Ill try it myself

  • http://www.furtzdesigns.com Ryan

    I have been trying to use this for a new theme im working on but I cant see how the $category_list is of any use! I need it to output category ID’s rather than the names in text.

    Is there a way for the user to choose a category, for example ‘Featured’ and then from $shortname.”_feat_cat” i get a numerical value of that category’s ID?

    • http://sixonefive.net Mike McAlister

      Same here! The category name isn’t useful when running queries as far as I know. I was able to bring in the ID’s but then the user is looking at ID’s rather than the name. It would be nice to have the name in the drop down and have the ID be the actual value.

    • http://sixonefive.net Mike McAlister

      Does anyone know how to grab the pages in an array similar to how the categories are being brought in?

      • Rohan Mehta
      • http://frankiejarrett.com Frankie Jarrett

        I’m using this, but just like the the category example in this tut it returns the post_title the select value instead of the post_name (slug) :/

        $pages = get_pages();
        $wp_pages = array();
        foreach ($pages as $pagg) {
        $wp_pages[$pagg->ID] = $pagg->post_title;
        }
        array_unshift($wp_pages, “Choose a page”);

        Then use it in your option array:
        “options” => $wp_pages,

        I’m using the var $pagg since using “$page” creates some havoc :)

    • Rohan Mehta
      Author

      Yes, there is. Like this:

      <?php get_cat_ID( $cat_name ) ?>

      Just substitute $cat_name with the category name.

    • http://www.furtzdesigns.com Ryan

      Thanks Rohan! I managed to get it working by doing this:

      have_posts()) : $my_query->the_post();
      $do_not_duplicate = $post->ID; ?>

      Hope it helps!

      • http://www.furtzdesigns.com Ryan

        Ok that didnt work, heres the snippet: http://pastie.org/834080

      • Rohan Mehta
        Author

        That’s great Ryan :-)

  • Ronny

    When trying to update the settings values, I get the following message on a blank page:

    “You do not have sufficient permissions to access this page.”

    I am indeed logged in as the admin user. The strange thing is that the settings are actually updated, but I am still getting an error. Strange.

    • Ronny

      Never mind. I found the answer :P

      • MarkB

        what is the answer? I have the same problem…

      • http://www.bretglassett.com Bret Glassett

        If you changed the name of functions.php or moved the code out of there (by including or requiring) and did not change the header locations (lines 111 and 120) you will get that error.

  • http://www.owenw.co.uk owenW

    Can’t wait to give this a go

  • http://www.owenw.co.uk owenW

    Great tutorial!

  • http://chris@atlchris.com ATLChris

    Thanks for the great tutorial. I especially appreciate the instructions on how to make your Options Panel stand apart from all the other admin panels.

    Thanks, my next ThemeForest.net theme is going to have a beefed up admin panel with tons of controls!

  • http://www.driftwoodcms.com Ethan

    Thanks for the top-notch tutorial. As someone who is new to WordPress as a developer it has really helped me grasp the concept of theme options.

    Just a side note: The source theme works if you use the folder from “Final”.

    Thank you again!

  • Rune

    Invaluable. seems like it is coming in WP 3.0 though but I guess this is great for custom work.

  • http://www.vagrantradio.com Jason

    Very nice, this will definitely come in handy for the next Themeforest WordPress theme.

    Thanks, Rohan.

  • http://H Tom Walters

    That’s amazing, I’ve been looking everywhere for a high quality tutorial on this everywhere and I’m glad I finally found it on such a great site. Thanks very much for this!

  • JesperA

    Ok so i am using some checkboxen for different uses but i would much rather use an if statement if the checkbox is checked, is this possible?

    Like if the checkbox is checked, show whatever is inside the if statement, if it is unchecked, show the stuff in the else statement!

  • gabriel

    wow, thanks
    i tried this customization on my own , but always had an jquery conflict.

    thank you

  • http://www.jamesmorrisondesign.com/ James Morrison

    Awesome tutorial.

    Yes the options panel shares some of the code from Woo but if you take the code and use it as a template on which to build and improve I can’t see any issue with it.

    Most of the Woo functions aren’t included anyway.