Archive for the ‘Development’ category

The Right Way to Style WordPress Plugins

From the time I first touched WordPress until a few days ago, I styled plugins wrong. Heck, I put my PHP in the wrong place too, so obviously there’s a lot I was doing poorly. None the less, in the hopes of saving someone some headache, here’s what I believe to be the best way to deal with CSS for any type of plugin today.

Quickly though, here is the basic progression of how I’ve tried to add CSS to plugin output in the past.

  1. Doing it purely inline with <style=" ... ">. (This does have a very small place on the internet — when you truly want to apply a unique style to a single element — but I wasn’t using that.)
  2. Adding bare CSS with an action hook to wp_head. I was just spitting it all in the head of theme making the files bigger, and the source more convoluted.
  3. Appending my own .css file with a script I found somewhere and didn’t really understand.
  4. What I’m about to show you: Appending my own .css file, but allowing the theme to replace it by putting sensibly named alternative in it’s style directory.

Essentially, for that Post Meta Box plugin I mentioned in the aforelinked post, I wanted to be able to modify the basic styling but not affect all the places I’m trying to use it. So, I did what made the most sense to me, copied the master CSS file (postmetabox.css) to my active themes directory and modified it there. And because this script I’m about to give you is so smart, that became the active styling without any problems. So here it is:

(A quick note: I’ve kept all references to “pmb”, my shortcut for making things local to my Post Meta Box plugin. If you’re using this function, I’d do a find and replace with whatever shorthand you use to refer to your plugin. I could have find-and-replaced it to “my-plugin,” but we’re smart people, so I’ll spare us that unnecessary step.)

add_action('wp_print_styles', 'add_pmb_stylesheet');

function add_pmb_stylesheet() {
	$theme_style_url = get_stylesheet_directory_uri() . '/postmetabox.css';
	$theme_style_file = get_stylesheet_directory() . '/postmetabox.css';
	$pmb_style_url = plugins_url() . '/post-meta-box/postmetabox.css';

	if ( file_exists($theme_style_file) ) {
		wp_register_style('themepmb', $theme_style_url);
		wp_enqueue_style( 'themepmb');
	}
	else {
		wp_register_style('pmb', $pmb_style_url);
		wp_enqueue_style( 'pmb');
	}
}

First and foremost, that add_action call is (half of) the proper way to append any stylesheet in WordPress. There are many other things you can do that will function for you (the aforementioned wp_head method is probably the worst of them). Essentially, all it’s doing is calling my function when it gets to any place it typically reaches for style sheets.

So what our function does is hand it the right style sheet. And which stylesheet is right depends on whether or not the user has created one for their current theme, or if they just want to rely on ours. So essentially, we’re just looking to see if the user has created the file to hold their own stylesheet ($theme_style_file), if they have, we use that. If they haven’t, we use our own.

There’s nothing in here that a seasoned PHP and WordPress person should be stunned by, but there’s a lot of little bits that your average tinkerer will not have known about. Like that PHP can easily check if a file exists — that function’s in every version of PHP ever — but does it with local addresses, so you need to know where the style sheet is both locally and internet-wise. And the commands wp_regiester_style and ¬†wp_enqueue_style — which round out the proper way to add a style sheet — were completely unknown to me until I ran across a little tutorial (by I think Justin Tadlock, though after 20 minutes of looking I gave up on definitively finding who it was).

I wondered for some time about the possibility of keeping some sensible defaults for my plugin’s visual output and always using them, but if for some reason the user wants to overwrite what seem to me to be sensible defaults I want to make that easy. And since I think most anyone inspired to modify my styling would take the sensible advice to copy the original file and put it in their theme and modify it there, that’s what we’re allowing for.

It’s entirely possible that another years of dinking around with WordPress will convince me that this method is as foolish, or outdated, or bad, as my previous methods, but the more I think about that the less likely it seems to me.

Don’t Put it in the Theme

If there’s one lesson I can think of that I wish I’d learned sooner in the whole WordPress plugin and theme area it’s this: unless you can make a convincing argument that it belongs in the theme, it doesn’t.

Over the weekend, I finally did some work I’d been meaning to for a while. I moved the “post meta data” — tags, similar posts, last and previous post links, etc — which I used to have in the sidebar of Frozen Toothpaste back to the bottom of the posts. (Every post on the site will show it, but this is a shorter one.) It seems obvious to me (now; there’s a then when it wasn’t) that this is the right place for it to be, there for when someone has finished with one post and can easily get a good “next action” right there, rather than scrolling back to the top and then looking to the sidebar.

All of that’s tangential to the point that when I started to build this new “post meta box” I went to start writing the code for it into my current theme. But I’ve meant to change my theme for nearly a year, and it occurred to me that it would really suck to have to move all this code over next time I change the theme.

So I went with the obvious solution to this problem, and the best answer to the generic question of “Well, then where should I put code that does stuff in my theme?” I made a plugin.

Some of this is simply personal preference and coding style. There’s probably a reasonable argument that could be made for keeping at least some of the code that makes those post meta boxes appear local to the theme. I’m convinced it’s possible that you could convince someone of that argument. But I doubt you could have convinced me that’s what I should do for me. And that’s really all that matters. If you can’t convince yourself it belongs in the theme, it doesn’t. If you’re able to convince yourself, do whatever you like.

To most people who spend a significant amount of time futzing with WordPress plugins and themes, this is probably self-evident. But I struggle to recall a time when I didn’t make at least a few poor decisions because I’d never heard the pretty damn good advice: if it doesn’t obviously belong in your WordPress theme, don’t put it in your WordPress theme.

The Pros and Cons of Moving to WordPress Multisite

It’s not a problem many people have, but for the rare person who suddenly looks down and realizes they’re updating WordPress, its plugins, and themes across many different instances each time they change, the siren song of a multisite WordPress install (previously known as multi-user, or WPMU) is strong. And since they integrated this feature into the WordPress core back in 3.0, that call has only gotten stronger.

I’ve been idly thinking about how good this could be every time a new version of WordPress drops and I’m forced to manually go through each of the five instances of WordPress I maintain online for personal use and update them. Mercifully, this has become massively easier in the last few years, but it’s still an annoying and time consuming process compared with updating everything once.

Since laziness drives many of my technical decisions, I know I’m not likely to take the time to do this without a thorough consideration of the things gained and lost. And since I feel rather confident that this could prove valuable for another person who finds themselves in a similar situation, I’ve decided to weigh the value of the transition in public. So here it is, a list of what I see as the good and bad parts of migrating mulitple disparate WordPress installs to a single WordPress Multisite one.

The Good

  • The aforementioned maintenance benefits. Being able to update WordPress once is unquestionably better than having to do it multiple different times. The same goes for keeping plugins and themes up-to-date. Once is massively better than five times.
  • Plugin propagation. Similarly, when I discover a new awesome plugin¬†with my current setup, I have to manually put it on every blog on which I want to use it. Because with multisite you keep plugins in one place and use them on all blogs, it’s a clear win.
  • New blog creation is easier. Five already seems like too many for me, but I still think about the possibility of creating new sites for various reasons. The ability to just start a new blog without having to reupload WordPress, never mind populate plugins and themes, is huge potential benefit of this process.

The Bad

  • Upfront costs. These are numerous, and non-trivial. Essentially, to get all the long-term maintenance benefits requires a lot of one-time costs in the form of exports, imports, and setting resets. The export/import process to move your blog posts along couldn’t really be simpler. This process, however, doesn’t include the blogroll, plugins and settings, widgets, and your blog’s tagline. And we haven’t even considered the headache of a nearly inevitable period of polluted RSS feeds and unreadable sites.
  • The learning curve. While WordPress Multisite isn’t hugely complicated, there are a number of things that work differently than with standalone blogs. Thus there will inevitably a period of ignorance and learning, even if short it’s not negligible.
  • Embedded HTML. This isn’t one I’d been aware of before investigating, but because Multisite is built for creating a blog network that the general public can use, they made reasonable security concessions in curbing your ability to use <iframe>s and <embed>s which could be a headache if you’ve used them for say, Amazon affilitate panels in posts, embedded YouTube videos, etc. There are solutions to this issue, but it’s definitely a potential problem.
  • Those upfront costs again. I’m not kidding. If you’re not completely comfortable with either server or WordPress administration this is significantly more complex than a standard WordPress install. And if, like I would be, you’re doing it in an existing account at the same host, you’ve got a bit of an acrobatic headache ahead.

I’m not fully decided on the question of whether or not I’ll do it. I do have to say that making the list has already made me think that maybe my reason for wanting to do it is a bit ill-considered. I think for now I’ll personally keep playing with the one I’ve already setup in my development environment and see how I like it. So for me at least, the idea is currently on hold.

The Reasons for Writing Software

Are, in rough order of nobleness:

  1. Because no one else has made anything like this before and I’m sure it’ll be awesome.
  2. Because no one has ever combined these feature sets and the combination will be legen — dramatic pause — dary.
  3. Because this platform needs this type of software.
  4. Because my version will be way better than all the others.
  5. Because building it will teach me something.
  6. Because I can do it too.

Debugging: Random Redirect and WP Super Cache

I think the quip, which I most often see attributed to Thomas Fuller, that “All things are difficult before they are easy” is so clearly borne out by debugging that the truth of it cannot be doubted. You can easily spend minutes, hours, even days bashing your head against a metaphorical wall only to notice that a misplaced colon — which you of course, didn’t realize was misplaced — was the cause of reams of unnecessary pain.

The Problem: Incompatible Rules

During my work on Kaleidoscope, that’s the theme this site is running, I decided that a random post link in the footer would be nice. A quick search yeilded Matt’s Random Redirect Plugin, which was more than up to the job. Without a need to reinvent the wheel, I just borrowed the core functionality of Matt’s plugin function and dropped it into my theme’s functions.php file.

And on my personal test server, it worked well. And when I put it on the Ikiru Demo Blog, there too it worked fine. But I found problems on Ikiru Design. More frustratingly, those problems would sometimes seem to suddenly disappear. (It was only later that I realized that it was whether or not I’d logged in that was the cause of that disparity.)

As you may suspect from what I’ve said so far, Ikiru Design has the WP Super Cache plugin running (though it has rarely needed it). Looking desperately to figure out why my redirect link in the footer worked on the demo blog but not on the main one, I decided to look through their directories.

Solution One: Changing .htaccess

Mercifully, I noticed that the .htaccess files were drastically different sizes. And I remembered that SuperCache depends on your making changes to that file. And indeed, comparing the two showed these lines added to Ikiru’s .htaccess file:

# BEGIN WPSuperCache

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{QUERY_STRING} !.*s=.*
RewriteCond %{HTTP_COOKIE} !^.*(comment_author_|wordpress|wp-postpass_).*$
RewriteCond %{HTTP:Accept-Encoding} gzip
RewriteCond %{DOCUMENT_ROOT}/wp-content/cache/supercache/%{HTTP_HOST}/$1/index.html.gz -f
RewriteRule ^(.*) /wp-content/cache/supercache/%{HTTP_HOST}/$1/index.html.gz [L]

RewriteCond %{QUERY_STRING} !.*s=.*
RewriteCond %{HTTP_COOKIE} !^.*(comment_author_|wordpress|wp-postpass_).*$
RewriteCond %{DOCUMENT_ROOT}/wp-content/cache/supercache/%{HTTP_HOST}/$1/index.html -f
RewriteRule ^(.*) /wp-content/cache/supercache/%{HTTP_HOST}/$1/index.html [L]
</IfModule>

# END WPSuperCache

I now understand what this does, but at first I didn’t. It looked like a mash of random characters. But I know enough about programming conventions to guess that an “!” means not. And I also know enough about this modern world to know to Google things I don’t understand. “RewriteRule” seemed a reasonable phrase to start with.

I’ll spare you the full narrative of the search, but I’ll explain what I learned. RewriteCond provides the conditions that determine whether or not a RewriteRule is used by the server. Essentially, if a user trying to access a given file on the server gets through all the conditions in the RewriteCond stack, they’ll be made to follow the RewriteRule.

In this case, that means that if their query string — that’s the ?s or ?p=187 or ?random that tells the server what dynamic features are wanted — doesn’t start with ?s, and if the user doesn’t have a cookie specifying that they’ve either logged in or commented, and if the proper static file has been generated, send them that static file. (You see essentially the same rules there twice, the top one is for if you’re using compression, the other for if you’re not.) This is exactly how Super Cache is advertised to work.

But it means that the server will ignore the query string ?random that Matt’s Random Redirect function relies on. So the user will get, as I was, the cached version of the index page. And they’ll get it before Matt’s plugin has had a chance to well, redirect them.

Having figured that this was what was probably happening, I felt reasonably hopeful that I could finally fix this week-old problem. After all, the random link was working when I was logged in — thus not making me follow the RewriteRule — but it was failing when I wasn’t.

So I added was this line to the RewriteCond stacks:

RewriteCond %{QUERY_STRING} !.*random*

This adds an exclusion, which says that if the url looks something like “http://www.ikirudesign.com/?random”, exclude the user from the RewriteRule. Uploading the modified file and opening the link from every conceivable page I could find showed that it was finally working.

Solution Two: Changing the Function

But this can’t be packaged into a theme. Fortunately, a usable but imperfect solution can be. The basic problem is that redirects break with WP Super Cache. The solution is to do this same thing without a redirect.

Essentially, all this take is simplifying Matt’s Random Redirect Plugin even further. The result was this function:

function sc_safe_random() {
	global $wpdb;
	$query = "SELECT ID FROM $wpdb->posts WHERE post_type = 'post' AND post_password = '' AND post_status = 'publish' ORDER BY RAND() LIMIT 1";
	$random_id = $wpdb->get_var( $query );
	return get_permalink($random_id);
}

This is essentially the first three lines of Matt’s function — which picks a random post from among those eligible on your blog — and adds a line that just tells it to pass the permalink for that post to the place where the function is called. Then you simply call the function for your random link, like:

<a href="<?php echo sc_safe_random() ?>">a random post</a>

This solution works, but it’s got a problem. If I were to click the redirect version into five new tabs from a single page, I’d receive five tabs each with a different random post in it. If I do the same with this version, I get the same post each time.

The likelihood of someone doing this is obviously questionable, but it’s a feature that I’d rather not lose. For that reason, I’m planning on making sure that I allow a user who doesn’t have WP Super Cache enabled, or who have implemented Solution One, to use the redirect version. Those who have Super Cache enabled, or who don’t mind the limitations of this second solution, would be able to continue to use it.