Advertisement
WordPress

How to Create a Better WordPress Options Panel

by

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.

Related Posts
  • Code
    Creative Coding
    Advanced Use of Attachments in WordPress: Creating Custom Queries for AttachmentsAdvanced use of attachments in wordpress 400
    This tutorial is the second in a four part series in which you'll learn some techniques for working with images in attachments in WordPress which give you advanced options.Read More…
  • Code
    Creative Coding
    Using a Custom Post Type to Create a Home Page BannerUsing a custom post type to create a home page banner
    Sometimes it's useful to have a nice obvious banner on your site's home page—for announcements, snippets that aren't long enough to merit a blog post, or links to new content within the site. But you don't want to be editing your homepage content every time you add a new piece of content, nor do you want to be delving into the code to add content.Read More…
  • Code
    Theme Development
    Custom Controls in the Theme CustomizerTheme customizer custom control 400
    In the last article, we explored the advanced controls available in the Theme Customizer, and how to implement them. We’re going to look at how to create our own custom control, allowing you to choose which Category of Posts are displayed on the home page. To get started, download version 0.6.0 of our Theme Customizer Example.Read More…
  • Code
    Theme Development
    Creating a WordPress Theme from Static HTML: Creating an Archive TemplateCreating wordpress theme from html 400
    If you've been working your way through this series, you now have a functioning theme with two page templates. The steps I've demonstrated to this point are: preparing your markup for WordPress converting your HTML to PHP and splitting your file into template files editing the stylesheet and uploading your theme to WordPress adding a loop to your index file adding meta tags, the wp_head hook and the site title and description to your header file adding a navigation menu adding widget areas to the header and sidebar adding widget areas, a colophon and the wp_footer hook to the footer file creating template files for static pages. Read More…
  • Code
    Theme Development
    Creating a WordPress Theme From Static HTML: Creating Template FilesCreating wordpress theme from html 400
    In the first part of this series, I showed you how to prepare your HTML and CSS files for WordPress, ensuring the structure would work, the code was valid and that the correct classes were being used. In this tutorial you'll learn how to take your index.html file and split it up into a set of template files for use by WordPress.Read More…
  • Code
    Theme Development
    Creating a WordPress Theme From Static HTML: Preparing the MarkupCreating wordpress theme from html 400
    Last year I did a small (and admittedly very un-scientific) survey among other WordPress developers. What I wanted to know was this: When they built their first WordPress theme, how did they do it? Did they hack an existing theme or did they start with their own static HTML and turn it into a theme? The majority of people I spoke to used the second approach - they were all experienced frontend developers who had built sites using HTML and CSS, and found it easiest to take their existing HTML files and convert them to a theme. Two of the people I spoke to were lecturers or teachers, and told me that this is the approach they use with students. So in this series I'm going to show you how to do just that.Read More…