Custom Write Panels for WordPress 3.0
June 18, 2010NOTE: October 5, 2010
Just letting you know that for some reason, some of this custom post type info does not transfer well. I used this one a site recently, and through SQL exports, I’ve discovered that the information within *some* of these fields does not get retained in an SQL export, and when you move your site, you lose some fo the info. Now, I don’t know if that’s just something in MY theme files for the project I was doing, or if it’s something in these examples that causes information to go missing, but I’m just making you aware of the possibility.Most likely it’s the former, since this doesn’t happen to ALL of the custom fields, but only a few.I just wanted to make you aware so if it happens to you, you’ll already know I know, and I’m trying to figure it out.
As promised (from earlier today) I’m going to supply a small tutorial. Over the last few weeks, I’ve actually been bombed with requests on how to do this, and I’ve been happily passing out my code to anyone who’s asked for it. (I think it’s because of this comment I left on Liam McKay’s site. I guess it piqued a lot of interest.)
I will also say that, earlier today, I mentioned that the tuorial he provides was tossing up errors for me – which it did – but as it turned out (in my checking of the code I’m about to give you) that it was *my fault* – not his. So I apologize for saying that before. His tutorial – as it stands, works just fine in WordPress 3.0. it was my fudging with it to make it work with different field types what was munging it.
That being said, I won’t rehash Liam’s entire tutorial. He covers the basics of what’s going on just fine. I don’t feel it’s necessary to reinvent the wheel there, so I’m not going to rehash all of that here.
However, I’ve had several projects where these panels come in very handy. But I wanted to use something other than text input to retain the values. In some cases, using a checkbox or radio button would work much better. Makes it a lot easier for the client, you know? In. 2.9.x, I had this working just fine*, but the shiny, new updated version made it slightly more difficult to pull off. but I did it, because i needed it. So I’m going to tell you how I did it so it’ll help you.
You know, because I’m good like that.
If you’d like to download the basic plugin that shows the whole shebang, you can click here and take a gander: Add Post Meta Files
So the point of this is to expand upon the genius that is the Custom Write Panels Tutorial. A couple of things I will note is when I’ve used this in the past, and I wanted to do one set of stuff for posts, and a different set for Pages… it just makes everything a lot easier to just use this as a plugin instead of plunked into your functions.php file. For me, anyway, trying that just made my functions.php file immensely huge, and I was always fighting with conflicts. So I’ve found it easier to manage to just make ‘em plugins. But feel free to do what you like!
Now I’m thinking I could just use the array values to decide what types of Pages or posts these shoudl go on… hmmm… I feel an experiment coming on…
So the changes I’ve made to Liam’s tutorial is fairly simple, really. It’s basically adding a little bit more to your arrays in the beginning, and adding some conditional statements and foreach loops. At the very beginning, we set up a “key” value (which is the meta_value that’s placed in the database so we can find it and use the information later), and then we begin our arrays for the fields. The original tutorial only sets up arrays for the name, title, description and other assorted stuff. What we are going to do is also set up the type of field we want for each item, and a couple of “aesthetic” array values so it doesn’t look weird.
$key = "key";
$meta_boxes = array(
"input" => array(
"name" => "input",
"title" => "Input Field",
"description" => "This is an example for a regular input field.",
"type" => "text",
"rows" => "",
"width" => "100%",
"options" => ""
),
"textarea" => array(
"name" => "textarea",
"title" => "Textarea Field",
"description" => "This is an example for a textarea field.",
"type" => "textarea",
"rows" => "6",
"width" => "100%",
"options" => ""
),
"checkbox" => array(
"name" => "checkbox",
"title" => "Checkbox Field",
"description" => "This is an example of a checkbox field.",
"type" => "checkbox",
"rows" => "",
"width" => "",
"options" => array("1" => "checkbox 1",
"2" => "checkbox 2",
"3" => "checkbox 3",
"4" => "checkbox 4",
"5" => "checkbox 5",
"6" => "checkbox 6")
),
"radio" => array(
"name" => "radio",
"title" => "Radio Field",
"description" => "This is an example of a radio field.",
"type" => "radio",
"rows" => "",
"width" => "",
"options" => array("1" => "radio 1",
"2" => "radio 2",
"3" => "radio 3",
"4" => "radio 4")
),
"dropdown" => array(
"name" => "dropdown",
"title" => "Dropdown Selection",
"description" => "This is an example of a dropdown field.",
"type" => "dropdown",
"rows" => "",
"width" => "",
"options" => array("1" => "option 1",
"2" => "option 2",
"3" => "option 3")
),
"checkbox2" => array(
"name" => "checkbox2",
"title" => "Checkbox Field",
"description" => "This is an example of a secondary checkbox field.",
"type" => "checkbox",
"rows" => "",
"width" => "",
"options" => array("1" => "yes")
)
);
You’ll notice I have two “checkbox” types here. That’s because I wanted to show you that you can re-use these arrays, and vary how they will be used. In the first one, we’ve got a bunch of checkboxes – but the last one would be more of a “yes/no” type deal. (Check for “yes”, unchecked for “no”.) but that one’s just used as an example – any of these fields can be reused.
Now, the only other area we need to change is in the display_meta_box() function. Namely in the “foreach” loop contained within. We’re changing Liam’s basic function from this:
<?php
function display_meta_box() {
global $post, $meta_boxes, $key;
?>
<div class="form-wrap">
<?php
wp_nonce_field( plugin_basename( __FILE__ ), $key . '_wpnonce', false, true );
foreach($meta_boxes as $meta_box) {
$data = get_post_meta($post->ID, $key, true);
?>
<div class="form-field form-required">
<label for="<?php echo $meta_box[ 'name' ]; ?>"><?php echo $meta_box[ 'title' ]; ?></label>
<input type="text" name="<?php echo $meta_box[ 'name' ]; ?>" value="<?php echo htmlspecialchars( $data[ $meta_box[ 'name' ] ] ); ?>" />
<p><?php echo $meta_box[ 'description' ]; ?></p>
</div>
<?php } ?>
</div>
<?php
}
?>
To an extremely huge and expanded form that makes me wish I’d get to it and install some sort of code-formatting plugin. In all honesty, the beginning of the code above stays the same – you only need to change right around the foreach loop – just after the wp_nonce_field call:
<?php
function display_meta_box() {
global $post, $meta_boxes, $key;
?>
<div class="form-wrap">
<?php
wp_nonce_field( plugin_basename( __FILE__ ), $key . '_wpnonce', false, true );
$output = '';
foreach($meta_boxes as $meta_box) {
$data = get_post_meta($post->ID, $key, true);
$output .= '<p style="font-size:1.1em; font-style:normal;">' . $meta_box['title'] . '<br />' . "\n";
$output .= $meta_box['description'] . '<br />' . "\n";
if($meta_box['type'] == 'text') { // plain text input
$output .= '<input type="text" name="' . $meta_box['name'] . '" value="' . $data[$meta_box['name']] . '" style="width:' . $meta_box['width'] . ';" />';
}
else if($meta_box['type'] == 'textarea') { // textarea box
$output .= '<textarea name="' . $meta_box['name'] . '" style="width:' . $meta_box['width'] . '; height:100px;">' . $data[$meta_box['name']] . '</textarea>';
}
else if(($meta_box['type'] == 'checkbox') && (!empty($meta_box['options']))) { // checkboxes
foreach($meta_box['options'] as $checkbox_value) {
if($data[$meta_box['name']] != "") { // if array is empty, warnings will be issued, this circumvents it
$output .= '<input type="checkbox" name="' . $meta_box['name'] . '[]" value="' . $checkbox_value . '" ' . (isset($data[$meta_box['name']]) && (in_array($checkbox_value, $data[$meta_box['name']])) ? ' checked="checked"' : '') . '/> ' . $checkbox_value . ' ' . "\n";
} else {
$output .= '<input type="checkbox" name="' . $meta_box['name'] . '[]" value="' . $checkbox_value . '"/> ' . $checkbox_value . ' ' . "\n";
}
}
}
else if(($meta_box['type'] == 'radio') && (!empty($meta_box['options']))) { // radio buttons
foreach($meta_box['options'] as $radio_value) {
$output .= '<input type="radio" name="' . $meta_box['name'] . '" value="' . $radio_value . '" ' . (isset($data[$meta_box['name']]) && ($data[$meta_box['name']] == $radio_value) ? ' checked="checked"' : '') . '/> ' . $radio_value . ' ' . "\n";
}
}
else if(($meta_box['type'] == 'dropdown') && (!empty($meta_box['options']))) { // dropdown lists
$output .= '<select name="' . $meta_box['name'] . '">' . "\n";
if (isset($data[$meta_box['name']])) {
$output .= '<option selected>'. $data[$meta_box['name']] .'</option>' . "\n";
}
$output .= '<option value="">----------------</option>' . "\n";
foreach($meta_box['options'] as $dropdown_key => $dropdown_value) {
$output .= '<option value="' . $dropdown_value . '">' . $dropdown_value . '</option>' . "\n";
}
$output .= '</select>' . "\n";
}
$output .= "</p>\n\n";
}
echo '<div>' . "\n" . $output . "\n" . '</div>' . "\n\n";
} ?>
That’s really pretty much all there is to it. I’ll explain the pertinent information in bits, just to break it down into a more palatable chunk of information (I know that looks like a lot up there!) We’ll start with the array information.
Setting up the arrays
Basically, this is the only section you’ll really need to edit. Since the “checkbox” field has the most information, I’ll use it as an example to explain what’s going on.
"checkbox" => array(
Obviously, this is the beginning. It’s basically just setting up your field with a name that will be associated with the values. Makes sense, right? The array is the stuff that makes up the field, so you can manipulate what’s going on.
"name" => "checkbox",
The “name” of the field within the array. It does *not* have to match the one above, but I find it helps. This one is required. Gotta have it!
"title" => "Checkbox Field",
Optional. This is the text that will appear in the custom panel, the label of the field.
"description" => "This is an example of a checkbox field.",
This is the optional description. If there’s some ‘splaining that needs to be done for this particular field, I’ll use the description for that purpose. You can leave it blank if you like.
"type" => "checkbox",
Required. Here’s where get into the meat of it. This field here tells the code what kind of input type you want. Standard names apply.
"rows" => "",
Optional. For use only when using the “textarea” type of input.
"width" => "",
Optional. This is if you so desire to apply a width value to the textarea and text input fields. I usually so desire, so it’s there.
"options" => array("1" => "checkbox 1",
"2" => "checkbox 2",
"3" => "checkbox 3",
"4" => "checkbox 4",
"5" => "checkbox 5",
"6" => "checkbox 6")
),
This is your secondary array for your options. This pupply is pretty useless for textareas and text fields, but for checkboxes, dropdowns and radio buttons,it’s really handy. You can change the numbers to more associative type stuff – that’s your preference. But the second part (“checkbox 1″, “checkbox 2″, etc) are what your values and text for the fields are (same text applies to both).
Really, that’s all you need to know. If you go messing with the other part, I won’t be held responsible.
But you can if you really feel the need to – I won’t come banging on your door to arrest you if you do!
Just so you know, you can assign where you want this stuff to show up. As it stands, it’ll show up in the “Write Post” area – which (as explained in the original tutorial) – is set in the create_meta_box() function (which is under the above-described array) in this line:
add_meta_box( 'new-meta-boxes', ucfirst( $key ) . ' Custom Post Options', 'display_meta_box', 'post', 'normal', 'high' );
You can have it assigned to Pages by changing it to this:
add_meta_box( 'new-meta-boxes', ucfirst( $key ) . ' Custom Post Options', 'display_meta_box', 'page', 'normal', 'high' );
or even – with the lovely new 3.0 feature of custom post types, assign it only to your custom post type. (in this example, we’ll say your custom post type is called “pictures”):
add_meta_box( 'new-meta-boxes', ucfirst( $key ) . ' Custom Post Options', 'display_meta_box', 'pictures', 'normal', 'high' );
Hopefully that helps you in your next project!
How to pull out the information
Silly me! i went and published this without telling you how to actually pull out this information and use it! That’s not very handy.
Unfortunately, I have to quickly make dinner (the kidlets are starving, apparently) so I will return shortly to tell you exactly how to do that.
Okay, so getting the info out and using it elsewhere is actually pretty simple. In the old version, you had to call every single item (because it wasn’t placed in a nice, neat array like it is now) via get_post_meta. If you had a lot of meat_key/meta_value pairs, that made for a lot of calls. But now, it’s one call (per key, as set in the top of the array), and you just pull them out with brackets.
So, within the Loop, you just need the single call:
<?php $fields = get_post_meta($post->ID, 'key', true); ?> (not “key” is hte same name set in the $key value at the very top of the array in the plugin/function). Now it’s a matter of simple calls to get the rest. Just want to grab what’s in the input and textarea fields?
Input field: <?php echo $fields['input']; ?>
Textarea field: <?php echo $fields['textarea']; ?>
note “input” and “textarea” within the brackets are the initial array names.
Works the same with the “Radio” field. Since radio buttons are a one-shot deal (you can choose one item only), it works in the same manner:
Radio field: <?php echo $fields['radio']; ?>
Dropdown is the same way as well – as long as only one option is chosen. If you allow multiple options to be selected, then you’ll want to deal with it the same way as you will your checkbox values. So, checkbox1 is a choice of 6 options. To obtain the checked values, we do a simple bit of code (using extra brackets, because it’s an array within an array) to do a foreach loop and yank out every single one of them:
Checkbox field: <?php if($fields['checkbox'] != '') {
foreach ($fields['checkbox'] as $value) echo $value . ' ';
} ?>
Checkbox 2 is a “yes/no” type option – so we can do a quick little test for that as well:
Checkbox 2 field: <?php if($fields['checkbox2'] != '')
echo 'yes'; // if the box is checked
else
echo 'no'; // if it's left unchecked
?>
And that’s all she wrote!









P.O. Box 46
How would you set query_posts to display posts from the Checkbox 2 field with a value of yes?
Hi Keith,
I actually covered that at the end. it’s the very last piece of code at the end of the article
Thanks for the reply Shelly.
Could you have a looksie over the following code and see what I am doing wrong?
<?php $featured_loop = new WP_Query(“meta_key=key&meta_value=yes&showposts=5″); ?>
Well, the loop you’re using would work with the old method, because the old method placed an individual meta key and meta value for each entry. The new method, however, sets a single meta key, and the values are arrays. So this query you’re using won’t work, because your value is set to “yes,” but the value within the database is an array that has every value set in it – so it won’t find it. You’ll want to change it to something more like this:
<?php $featured_loop = new WP_Query("meta_key=key&showposts=5");
$fields = get_post_meta($post->ID, 'key', true); ?>
what that will do is pull the posts that have the “key” set with some value – any value – and pull the most 5 recent posts that do not have an empty “key.” To pull the posts that have “yes” set within the value, you then have to do a secondary conditional:
if($featured_loop->have_posts()) :
if(in_array('yes', $fields['checkbox2']) {
while($featured_loop->have_posts()) : $featured_loop->the_post(); ?>
layout here
<?php endwhile; } endif; ?>
Now, that will only pull the first 5 posts that have “key” set with values, and if only 3 of those posts have “yes” in them, then it’ll only show three posts. What you’re wanting to do can be pulled off in a couple of ways, but using your basic loop only won’t generally cut it.
There was a promising bit of code submitted to WordPress trac not too long ago, that allowed for the meta_compare call to use “in” (to check within an array) but it’s not a part of core… yet. But you may be able to download it and manually add the patch and make it work. Otherwise, you’ll have to use a custom query to pull it out, and make it bend to your will.
Hi Shelly
To take this one step further…
If I want different post types to have different custom write panels…how would I achieve that?
I had a go at it, but there’s a problem with a second usage of add_meta_box.
Any ideas?
Great article though…many thanks.
Hi Dyrk, actually you have to create a second instance of everything.. You can’t reuse functions. So, for example, you would create an initial array of “$key2″ with it’s own distinct values. you would also have to rename anything that has the same name so there’s no conflicts. To make it show up in a different custom post type section, you define what page these options show up in in the “add_meta_box” area, which I describe just before the “How to pull out the information” section.
Hope that helps!
Hi Shelly. Thumbs up. Great job with the article.
Thanks to you I now have an alternative to PODS (which I still love due to their simplicity).
Hey, i have read your tutorial and others on the same subject and just cannot get it to work, The backend bits show up fine but it will not display on my page, i have checked my code against others and it is exactly the same so i do not understand where i am going wrong for it not to display.
Any suggestions would be great.
HI Luke,
If the “back end bits” are showing up fine (meaning, you’re seeing the fields you’ve crated, and the stuff you put into them is saved like it’s supposed to be) then you’ve done it correctly. The code doesn’t *display* the stuff, it only creates the extra fields and saves the information. You have to edit your theme to make it show the items you’ve put into it.
So without knowing what you’ve put into your theme files, i have no way of troubleshooting the issue. Sounds like it’s working like it’s supposed to, but you’ve just either used the wrong code in your theme files to display the information, or you haven’t done it yet at all.
At the end of the post above is a little section on how to pull the info out and display it, see how that compares to what you’ve got.
Hi, great article
How I can I move the meta box to the middle panel, below the editor, for instance?
HI Mario,
the tutorial above already places the custom meta box below the editor. If you want it in the sidebar, you’d change “normal” to “side” in the “add_meta_box” section.
You can read more about it here:
Function reference: add_meta_box
Hey thanks nice writeup and very helpful the way you’ve explained some of the finer details.
I’m wondering though haven’t found any references anywhere to this is can you set these write panels to show up for only specific pages or specific posts based on something within the page or post like whether the page has a specific custom field already assigned or whether it’s a child of a specific page etc.
Anyone heard of this being done? Possible?
Nomad – why would you want to do that? Just as a general thing, it doesn’t really make sense. but perhaps if you supplied some details as to what you’re trying to accomplish, there would be a solution for you.
Hi Shelly,
First off: Thank you very much for going to the trouble of writing this guide; I’ve found it incredibly handy!
I am only having one problem: I’m attempting to incorporate a second custom write panel – as Dyrk mentioned earlier – and am having great difficulty. My knowledge of PHP is quite limited to say the least, and my attempts to implement a second panel has resulted in numerous problems.
Might I humbly request an additional section added to your article detailing the necessary steps to include a second panel please? I’ve renamed each variable and function and ensured that they are behaving as they should do, but I’m at my wits-end.
If you might be able to include the code that we could easily copy/paste then that would be absolutely superb. I do hope this is no trouble. I’m tearing my hair out over here!
Thank you for reading my response and I look forward to hearing back from you.
Steve
Hi Steve,
Well, there’s really not much else I could tell you. if I did a write-up of how to add a second panel, it would basically look like this one, almost exactly.
You just have to rename your variables and functions so they aren’t duplicated. usually, I just add a number next to everything. So if my first variable is “$key”, then the second section will be “$key1″. All references to “$key” in my copy should be changed to “$key1″. Same for the functions, and calls to those functions. If I have “function display_meta_box()”, then in my copy of the whole thing, I change it to “function display_meta_box1()”, and I have to make sure the function calls reflect that change. (i.e. in the “add_meta_box” thing, you have to change “display_meta_box” to “display_meta_box1″so it pulls in the correct fields). You also have to call them in at the end to “activate” them – the “add_action” areas. You have the “add_action(‘admin_menu’, ‘create_meta_box’)” AND “add_action(‘admin_menu’, ‘create_meta_box1′)” .
other than just making sure you’re adding numbers (or some other form of defining what you’re doing) and making sure you’re calling them in by changing the information, its pretty much exactly the same as above.
greetings shelly,
thanks so much for the tutorial. i have a feeling i’ll be implementing it regularly.
looks to me like it’s missing a closing div tag? when i used the code as provided, i ended up with some of the write panels INSIDE the new custom panel.
http://cl.ly/4ab5e34964f661977de2
when i added another closing div tag just after the final $output variable, it looks better. http://cl.ly/b12dd66bcd1ed8c99eff
was it an oversight? or maybe i implemented the code incorrectly in the first place.
thx again for your work. much appreciated.
Hi Shelly, great tutorial, but I’m just having some trouble and I’m sure it’s obvious, but going right over my head… what i’m looking to do is use a custom checkbox field called “new” in my custom type called portfolio.
everything works so far, i have the custom write panel showing up in the right place and the code echo properly, but still cant figure out how to use it to command two different post templates.
could you take a look at what im trying to do please?
http://www.pastie.org/1197659
thanks a lot your help is much appreciated!