WordPress How To: Easily make an admin panel for a plugin

April 19, 2007 by aaron

I find the Admin panel the most tedious part of plugin development–even the slightest changes to the plugin requires major changes to the Admin panel, and it can be hard to remember every option you have in your plugin. However by following a few simple rules, your next Admin panel will be a breeze. Once you have the basic parts down, new options are just a cut and past away. One thing to note is that his Admin panel uses a few features that are best if repeated inside your plugin itself.

Please note that these same general techniques work outside of WordPress also; the only thing that you would need to do is to change the way options are actually set. Also, I assume you already know HTML; this is not a basic HTML tutorial, but rather is a way to do a specific task. Some basics are explained, but if you don’t already know the difference between a checkbox and radiobox this isn’t the best place for you to start.

There are rules for the easiest Admin panel.

  1. Always name your variables the same ways in the form and the database– there is absolutely no reason to have to change the name’s assign to variables individually. Especially seeing as this tutorial shows you the easiest ways to set all variables, and it requires the names to be the same.
  2. Always use arrays for your variables in both the form and database–you don’t want to spend a lot of time in your code pulling individual items from the database and assigning them to names, nor do you want to fill the users database up with a lot of individual options. You can, however, assign individual options to some variable such as $background inside the functions, but it is easiest just to leave all the options in a single array ($my_plugin[‘background’]) that doesn’t change from function to function.
  3. Always make your variable names sensible. If you can’t figure out what it does just by looking at the variable name it is useless. EX. If I want to set an option for the background color any of the following would be good names: background_color, back_color, bkgrnd_clr, backgroundcolor. backcolor etc. These names all make sense however if you were to use back, color, bc, monkeys, x, gqyjkl it is difficult to understand or remmeber exactly what it does. Furthermore, obvious meanings in your options make it easier to update the plugin later with more options. For example, if I set the background color with the option named color what will happen when I decide to give a new option for foreground color, border color, and text color? Not only can you net tell immediately which option color sets, but in CSS color means text color not background color. Why not ‘bc’ you may ask, well it is nice to save the shortest abbreviations for other things (as demonstrated in the radio and checkboxes settings), or for inside the actual code.
  4. Never use words as the values for individual checkboxes- individual checkboxes are useful for on or off statements, so it is perfectly simple to use the values 1 and 0 for checked and unchecked respectively.
  5. Try to use numbers as values for radio boxes whenever possible– sometimes it is too confusing to remember what the difference between 4 and 2 is when it comes to the actual code, so when you have to use strings as the values make them short (use the rules above) but descriptive (ex ‘student’,’teacher’,’admin’,’gen_staff’).
  6. Text boxes are for text — don’t expect your user to only input the options you suggest. If you require that one of a few option is selected use a checkbox, radiobox, list or dropdown list, otherwise you will have to perform a check every single time a user inputs a value.
  7. Administrative panels are for administrators; because of this, you don’t have to worry about abuse usually. If you give a radio box with the options 1, 2, and 3, you don’t have to check to make sure that only one of those options was selected. However, when using any sort of checks always check for all but one of the possible values (see radio boxes section) and then just use an else statement to default to one.
  8. Unless the option with the value 0 is meant as false, don’t start radio boxes from 0– doing this doesn’t make any sense.
  9. If your Administration panel is more than a few options long, separate it from the rest of the plugin. Otherwise, the entire thing has to be read into memory every time a page is loaded.

There are a few basic rules that we use in this Admin panel (and our plugin).

  1. Globals are our friend. By using global variables we make it very easy to access information from any part of WordPress, and can even allow other plugins to interact with our variables. Globals also allow us to get the information only a single time from the database and then reuse it over and over

    again. Just as a reminder globals should be named uniquely. It may make sense for your plugin to use the global $post, but if you do so neither WordPress or your plugin will work as expected.

  2. This Admin panel uses one global array to store all of the options ($mypluginall) and one array for for the options when submitted (myplugin).

Of course we need to tell the plugin that there is an Admin panel. The following is just an example, if you would like to learn what it all means, then please visit the WordPress Codex page dedicated to it.

add_action('admin_menu', 'myplguin_menu');

function myplugin_menu() {
    // Add a submenu to the Dashboard:
    add_submenu_page('post.php', 'My Plugin Managment', 'My Plugin Managment', 8, 'myplugin/myplugin_admin.php');

Before we create any options we should create the function that sets them (this way we can test it as we go along). The following function I developed makes it very easy to update the options. There really isn’t much to explain, but by taking advantage of the rules above, it takes an array of values and their names (keys), and translates it into the options for the plugin. We could have this as part of the admin panel, but ti is much clearner if we seperate it. I’m not going to go to in depth into how it actually works, but it goes throug hall of the values ($options) we passed to it (the new options) finds the name of the option ($option) and its corresponding value ($value). it then adds these same names and values to our global array of variables. This is the reason why we have the same names in our form and our database. If we didn’t, we would have to specifically set map each form option to the database option (cat to dog, background_color to back_color etc). The entire array is a global so it automatically updates the options before they go into the database.

function myplugin_update_options($options){
global $mypluginall;
//This is the section where we add individual rules to single options (see checkbox part.)

// End that section.
    while (list($option, $value) = each($options)) {
// this line here just fixes individual server bugs.
// If our user has magic quotes turned on and then wordpress tries to add slashes to it we will have everything double slashed.
        if( get_magic_quotes_gpc() ) {
        $value = stripslashes($value);
        $mypluginall[$option] =$value;
return $mypluginall;

This part goes at the top of our administration page. It checks if we just set some options (line 1), then passes the new options on to our function above for processing (line 2), and finally updates the options in the database. Because we were smart and used a global array for our values, we can continue loading the form without pulling the new values out of the database again.

if ($_POST["action"] == "saveconfiguration") {
            $mypluginall = myplugin_update_options($_REQUEST['myplugin']);
            $message .= &#039;My Plugin Options have Been Updated.<br/>&#039;;

        //$mypluginall doesn&#039;t need to be updated because it has the new values added to it immediately

    echo &#039;<div class="updated"><p><strong> Updated <br/> &#039;.$message;
    echo &#039;</strong></p></div>&#039;;

Now for some things we will need to set default values, or process the values in some way before the form sees them. In these cases we would add them before the form, but after the update option function.

This section is the panel itself. It doesn’t require much explanation because it is just a basic form, but this is where we will put the actual inputs.

echo <<<block
    echo &#039;<div class="wrap"><fieldset class="manage" style="width:100%; text-align:center;">&#039;;
    echo &#039;<form method="post">&#039;;
    echo &#039;<table width="90%">&#039;;
        <td colspan="2"><strong>Edit My Plugin Configutarion</strong></td>

        <td colspan="2"><p><strong>Post Options</strong></p></td>

// Inside this block add all of your options.
echo <<<block


    echo &#039; </table>
            <input type="hidden" name="action" value="saveconfiguration">
            <input type="submit" value="Save">

Setting Default Options

Code to use for checkboxes, radioboxes, textareas, selectboxes and text boxes.

To add a TextBox:

  1. The text box is the easiest of all the HTML inputs: (this one is named ‘title_text’)

        <td>My plugins title text:</td>
        <td><input type="text" value="$mypluginall[title_text]" name="myplugin[title_text]"></td>
  2. Because a user might use a quote (‘) or double quote(“) we need to add the following text to the ‘set options’ portion of the admin panel. This code makes whatever the default value be valid HTMl that won’t break our plugin by encoding HTML special characters like &, <, $gt; etc.

    $mypluginall['title_text'] = stripslashes(htmlspecialchars($mypluginall['title_text'],ENT_QUOTES));

To add a single checkbox: (best for on or off options)

  1. We first add a checkbox to the admin panel table with the name “say_yes”.

            <td>Would you like me to say yes?:</td>
            <td><input type="checkbox" value="1" $sy name="myplugin[say_yes]"></td>

    Did you notice the $sy inside the input? In basic HTML to check a checkbox we have to add the attribute checked=’checked’, but if we don’t want it checked we can’t add this input. If we were to leave the attribute checked=”checked” out the cjkebox would always be blank no matter what option was selected, but this is bad for usability. So to set the default option we do the following:.

  2. Because the initial value of the checkbox isn’t as simple to set a textbox, we then add the following in the ‘set options’ section of the admin panel. Notice that we set the variable $sy to be ‘checked=”checked”‘. As said above this is how we tell the browser that this option should be selected by default. The best thing about this is if the option isn’t checked (does not evaluate to 1) the $sy is just empty which means the checkbox isn’t checked . The name $sy does not mean anything it could just as easily be named $the_monkey, but t makes it a lot easier because it is just an abbreviation of the option name “say_yes’.

    if($mypluginall[&#039;say_yes&#039;] == 1){ $sy = &#039;checked="checked"&#039;; }
  3. The checkbox is unique because of the way it sends its data. If the checkbox is selected it sends data, but if it isn’t selected it doesn’t send anything. This can cause a problem when we are updating the options if you don’t check if the option is set first. To fix this behavior we just add the following to the update options function.

    if(!$options[&#039;say_yes&#039;]){$options[&#039;say_yes&#039;] = 0; }

To add multiple radioboxes:

  1. We first add the radioboxes to the admin panel table with the name “fav_num”.

            <td>Which of these is your favorite Number </td>
    <label>My Favorite Number is one :<input type="radio" value="1" $fn1 name="inap[fav_num]"/></label><br/>
    <label>My Favorite Number is two :<input type="radio" value ="2" $fn2 name="inap[fav_num]"/></label> <br/>
    <label>My Favorite Number is three :<input type="radio" value ="3" $fn3 name="inapfav_num]"/></label>

    Did you notice the $fn1,$fn2, $fn3 inside the input? This is done for the same reason as $sy is done above. As with checkboxes the In basic HTML the attribute checked=’checked’ means that that raadio box is the defaut. So to set the default option we do the following:.

  2. Because there is are more than one possible initial value for this radiobox (1, 2 or 3) , we have to use a larger statement to decide which one is set. As above we then assign the attribute checked=”checked” to one of the three variables and leave the other two blank.

    if($mypluginall[&#039;fav_num&#039;] == &#039;3&#039;){
            $fn3 = &#039;checked="checked"&#039;;
        }elseif($mypluginall[&#039;fav_num&#039;] == &#039;2&#039;){
            $fn2 = &#039;checked="checked"&#039;;
            $fn = &#039;checked="checked"&#039;;

    You will notice two things about the above example: the numbers count down and option one is the default. Both are by personal taste. I always have option one be the default so it makes more sense to look for the others first (and coujnting 3,2,1 makes more sense then 2,3,1), so that way if the other values are not found it defaults to one automatically. However, it doesn’t matter which order you do it it in as long as the default value comes last and is not specifically checked for.

  3. Because we always set a default value there is always some value sent with a radio box (unless the user specifically tries not to), so we don’t need to add a check as we did in step three of the checkbox.

The Text Area

  1. The text are is basically the same as the textbox, but it allows multiple lines of data (this one is named list)

        <td>List of my favorite things:</td>
        <td><textarea  name="myplugin[favthings]>$mypluginall[favthings]</textarea></td>

    Notice that the textarea uses a different format for its input. Specifically, it does not start with input and its default value goes between two tags rather than in a attribute named value.

  2. For the same reasons as the textbox we add the following in the set options section of the Admin panel.

    $mypluginall['title_text'] = stripslashes(htmlspecialchars($mypluginall['title_text'],ENT_QUOTES));

The Select Box

  1. The select or drop down box is the single best argument for using descriptive names. This is because we can either just add an extra option to it to set the current value, or we can do a loop, and check each option individually before we set them. The latter is a complete waste of coding time and memory and you will have to define an array of possible values etc etc, so I prefer to just add a default option. This is named ‘fav_color’

        <td>What is your Favorite Color:</td>
        <td><select name="myplugin[fav_color]">
            <option value="red">Red</option>
            <option value="orange_red">Orange Red</option>
            <option value="orange">Orange</option>

    as you can see the values are fairly attractive, with a little manipulation the values can also be used as the displayed text. $fav_color will be our default value.

  2. You get the drill, the following is added to the set options section of the Admin panel. However, we do a few manipulations to the name to make it look good.

    if($mypluginall[&#039;fav_color&#039;] != &#039;&#039;){
        $fc = &#039;<option selected="selected" value="&#039;.$mypluginall[&#039;fav_color&#039;].&#039;">&#039;.ucwords(str_replace(&#039;-&#039;,&#039; &#039;,$mypluginall[fav_color])).&#039;</option>&#039;;

    No matter what value is set we a re going to have a duplicate value, so the least we can do is make it look nice, but this part is optional. The str_replace removes any underscores in the values and males it a space and then the ucwords makes the first letter of each word uppercase to make it match the preset values–we could also use ucfirst to make only the first letter an uppercase.

That is pretty much it. As a final note this administration panel isn’t perfect, but ti will make your life a lot easier. Before developing this system it was extremely irritating to add even a single option. I wouldn’t do it unless I had no other choice, but now it literally takes a few seconds to add 3 or 4 new options by using the same templates and just changing their names and descriptions.

Categorized as:
comments powered by Disqus