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. 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
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
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') ."http://net.tutsplus.cdn.plus.org/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",
and opening a new section.
"type" => "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!
- Follow us on Twitter, or subscribe to the Nettuts+ RSS Feed for the best web development tutorials on the web.