Upgrading to WordPress 2.5.1

After weeks of putting it off, I’ve upgraded to WordPress 2.5.1. I’ve been putting off the upgrade to WP 2.5.x because my WP install was a bit old (2.1.something, I think), and because I had a nagging suspicion (but no solid recollection) that I had mucked about in the WP source to make something work (bad idea), and I was concerned the upgrade might break it. This morning I decided to bite the bullet. If I did have custom changes to the WP source that I have lost, I haven’t noticed any ill effects. Which is not to say that everything went off without a hitch. Herewith some notes, hints, and suggestions on the upgrade.

First off I wanted to be sure I had good backups, because Murphy and I go way back. I host this site on DreamHost, and used their One-Click Installer option to set up WP originally. Because of this, I was able to upgrade with the One-Click Installer, which offers automatic backup of the entire WP directory tree, including your plugins, themes, etc. The whole tree is copied to a .old directory before any changes are made. Excellent. That only left backing up the database.

WordPress uses MySql , and there are a number of ways to backup a MySql database. Because my hosting account includes shell access (all DreamHost accounts do), I just did a full dump from the command line, which is dead simple and wicked fast:

mysqldump --opt -p -uMySqlUsername -h MySqlServername MyDBName > backup.sql

Note that while many resources will tell you to include you password in the above command by replacing -p with -pMySqlSecretPassword, this is a security risk on any system with more than one user. Don’t do it. Instead, just use -p, and mysqldump will prompt you for your password. The resulting backup.sql is a sql script that you can feed directly to mysql, which will recreate your entire database, data and all. Very nice.

So, precautions properly taken, I asked the One-Click installer to upgrade me. A couple of minutes later, I got an email telling me that my upgrade was successful, and instructing me to follow a link to a special page in my site’s Admin area that would complete the upgrade by updating my database structure. Which is when things took a turn for the worse.

The update database page would not load. The admin login screen would not load. The blog would not load. Half a dimension away, Murphy giggled maniacally as he enforced his Law. Jerk.

The error message indicated that one of my plugins was attempting to redefine a function already defined in one of the core WordPress files. Oops. I’ll just skip ahead here to something I learned after some quality time on The Google: It is recommended that you disable all plugins before upgrading from a pre-2.5 install to 2.5.x. Oops. Annoyingly (but perhaps predictably), the only way to disable a plugin is via the Admin interface. You know, the one that didn’t load. Oops.

Fast-forwards 20 minutes of increasingly irritated Googling, and I had learned that there is an entry in the MySql table wp_options named active_plugins (i.e., where option_name = 'active_plugins'). If you set the option_value field of this row to an empty string, all plugins are disabled. That was enough to let me access the Update Database page, complete the upgrade, and use the Admin interface.

Of course, the blog was still broken, because my theme templates rely on certain functions exposed by various plugins. I proceeded to turn on plugins one at a time, based on the error messages I saw, until things were working. The only problem I ran into was UltimateTagWarrior, which is no longer supported, as of WordPress 2.3. Shutup, Murphy.

The solution was not difficult, although it required a little more Googling than I expected. UTW was discontinued because WP got built-in tagging support in WP 2.3 (I believe). However, anyone who used UTW had theme templates containing calls to UTW-specific functions. In addition, UTW offered a number of tag display options not offered by the core WP install. So the plugin’s author Christine introduced a new plugin, UTW Theme Compatibility Thing, which offers all of the old UTW template functions, but uses WP’s native tagging system as the data source. Once I installed and activated that, everything worked, except that I had no tags on my posts. (Note: If you used icons with your tags, as I do, be sure to check the instructions on the Compatibility Thing wab page or the readme file; there are a couple of extra steps required to make them work.)

This turned out to be an easy fix as well, after more time on The Google. WordPress already includes a tool to convert UTW tags to WP tags. From the Manage tab of the Dashboard, choose Import, and then choose Ultimate Tag Warrior. Five clicks through the wizard later, and you’re all done.

As far as I can tell, everything is working now. If you have any problems using the site, or see anything funny, please drop me an email or a comment.

So far, I really like the new WordPress, and I’m looking forward to trying out the new image uploader and the Gallery. My only complaint so far is that the page preview has been moved out of the Write Post page; you now click to pop open a preview. Do not want. I’ll give it a couple of days to grow on me, and them I’m going to start looking for a plugin to add integrated preview to the Write page.

WordPress Plug-in Sought

I have a WordPress problem, and I’m hoping someone can point me at a plug-in that will help. If not, perhaps I’ll take a crack at it myself.

Like most bloggers, I use comment moderation to help prevent spam (along with Akismet, which does alot of the heavy lifting). I’m also using the option “Comment author must have a previously approved comment”. This option allows comments to bypass moderation if the commenter has a previously approved comment. In general, I prefer this approach – there are certainly some of you who comment here from time to time, and I’d rather your comments go live straightaway rather than wait on me to moderate. On the other hand, I’ve got about 10 comments right now awaiting moderation, because they aren’t really spam, but I don’t want to give the commenter a free pass to comment in the future. For example, my Ubuntu drive mounting tutorial draws a lot of thank-you comments, and many of them are very brief- they could be legit, or they could be someone trying to get me to approve one comment, so they can spam freely.

So what I want – I’d like an option on the Moderation Queue screen to approve a comment without “approving the commenter” so to speak. Anyone know of a plugin to accomplish this?

The Other Canned Meat

Back in June (when this site was still running on Blosxom), I disabled comments because of the amount of comment spam I was receiving. Over the course of several years I built a number of anti-spam measures into my Blosxom install, but without a moderation feature, I could only respond to spam that got past my tests by cleaning it up after the fact. Eventually, it was just too much.

When I switched to WordPress in late August, I reenabled comments, because WordPress offers comment moderation. I even added the following note to my comment form:

Hey Spammers! Comments are Moderated, so please save us both some time and move along. No matter how many you send, they’ll never show up. Really.

To the best of my knowledge, not a single spam has made it through- but not for a lack of trying. On an average day, I probably moderate 50 or so spam attempts. On an average day, I probably get <1 real comment.

Nearly three years ago, Mark Pilgrim posted a warning to those trying to develop anti-spam measures. Some folks criticized him for being too pessimistic, but he was dead on. From that post:

Spammers are smart and determined, and people are numerous and stupid, and spam pays. You can’t make it not pay. Going after their ISPs won’t help; they’ll auto-register somewhere else. (Already happening.) Going after their upstream provider won’t help; they’ll cut deals with the backbone providers and keep going. (Already happening.) Going after them in court won’t help; they’re already living under friendly governments. (Already happening.) You can’t stop them with Turing tests; they’ll hire child workers to read your images and manually register/post/ping/trackback/whatever. (Already happening.) Then they’ll attack you with the power of 100 million owned Windows boxes and knock you off the Internet. (Already happening.) They will keep coming and coming and coming until you give up, go home, cry uncle, take Prozac, get a regular day job to replace the one you quit when being an anti-spammer became your full-time job.

And they have kept coming and coming. I often get several copies of the same spam commented on the same post, each using a different method of encoding links: one with html <a> tags, one with wiki-like [brackets], and one with raw link text. Why bothering to figure out what kind of comment system you’re targeting when your automated spam-bot and cover every contingency, shotgun-style?

A couple of days ago, I got one that almost slipped past me. The following comment was left on this post about mounting an OS X partition from Ubuntu:

heyas all. my 40 gig drive is going to good use now. I have installed UBUNTU and have ordered KUBUNTU. I dont know how to install the driver for my ati radeon 9600xt. Actually i dont know if i am meant to be downloading and installing XFREE86 or the XORG version of the driver. I am downloading them both but i dont know how to do anything in Linux really. I dont know where I am meant to set up my modem or set up a net account. (no INETWIZ.EXE) So yeah, can someone help me out with getting my ATI driver installed? and does anyone know of a good long PDF file i can read and wrap my brain around. I’m still a Windows user, but I want to use Linux as much as possible. Thanks. :)

At first glance, it looks like a typical cry for free tech support, only tangentialy related to the topic at hand, as seen on many a help forum and blog comment feed. If it were legitimate, I’d allow it, even though I expect no one to help. But I’ve learned that not every spam contains a link in the body text, so I always check the link to the commenter’s web site. The site address was http://camera-digital.us, which looks like the kind of hokey “every variation on a theme” URLs that spammers and affiliate sites use. Even more curious, the authors email address was for the digital-camera.com domain- similar but different.

So I tried a google search for a line of text from the comment. The first three matches are blog posts with the exact same comment posted. In a couple cases, they actual comment was showing up in a Recent Comments list on the side of the page, but the original post that drew the comment was actually about Ubuntu.

So what’s the point of this post? None exactly- just like Mark’s post above, I don’t have a solution. I only know that the problem is getting worse.

Counting Posts

Daniel at The Web Design Journal emailed me to ask about the post count block that appears in my sidebar. For example:

There are 2 posts in the last 30 days, and 323 total posts.

There are a number of ways to get the total post count. The last 30 days post count is a feature I wanted to add when I switched to WordPress and designed this theme. After searching for an existing solution, I ended up rolling my own. For reasons I don’t recall, I also wrote my own function for getting the total post count. I had meant to post it here, but had forgotten about it.

Instead of designing this as a WordPress plugin, I added it directly to template. In my sidebar template, I added the following:

<p>There are <?php echo get_post_count_inlast(30) ?> posts in the last 30 days, 
    and <?php echo get_post_count() ?> total posts.  
    <a class="sidelink" href="<?php echo get_bloginfo('wpurl')?>/archive/">Archives &raquo;</a></p>

The special bits here are the calls to get_post_count_inlast() and get_post_count(), which are simple PHP functions I wrote to query the WordPress MySQL database. I added these functions to functions.php in the theme directory for my theme. I’m not sure if all WordPress theme include this file, but the default theme, on which I based my theme, already provided this file. In this file, I added the code for the two new functions:

function get_post_count() {
    global $wpdb;
    return $wpdb->get_var('select count(*) from wp_posts where post_status = "publish"');
}

function get_post_count_inlast($days=30) {
    global $wpdb;
    return $wpdb->get_var("select count(*) from wp_posts where post_status = 'publish' and DATEDIFF(CURRENT_DATE, POST_DATE) <= $days");
}

I’m no PHP expert, I’ve picked up just enough to make the tweaks I wanted to my templates, but I believe you could even include the function definitions directly in the sidebar.php file, as long as they’re inside a <?php ... ?> tag.

Hope someone finds this useful. If you decide to add this to your site, please drop me a comment, and be sure to fill in your site URL.

(The source code in this post is released under the GNU GPL (just like WordPress), and comes with no warranty of any kind, express or implied.)

Notes on Converting from Blosxom to WordPress

When I switched from Blosxom to WordPress, I had intended to write a HOWTO explaining exactly how to accomplish the task. Unfortunately, this proved to be a real challenge, since no two Blosxoms are exactly the same – there are hundreds of plugins that can change the core behavior in ways both subtle and profound. Instead, I decided to write up some notes on what I did (and why), in hopes that it can help others who want to try and make the switch. Note: I’m using WordPress 2.0.4, if you are on an older (especially pre-2.0) WordPress, your mileage may vary.

After getting a basic WordPress install setup on my webserver, I began by looking for a Blosxom import tool for WordPress. I found Eric Davis’ import-blosxom.php, available from Eric’s software page. (Note: Houran Bosci has a modified version of Eric’s script which I only discovered after the fact, I’ve not tested it, but you may want to have a look at the changelog). Eric’s script didn’t come with any docs, so I tried dropping it into my wp-admin/import folder. I then went to the Import section of my WordPress admin page, expecting to find a new option. Instead, the Blosxom import script’s output ( a series of instructions ) were oddly interspersed with the normal Import screen. I eventually found that the script had to be put directly in my wp-admin folder, and I had to go directly to that page with my browser.

Once you load import-blosxom.php in your browser, you get a page full of instructions. These instructions include the text for a Blosxom theme file, which creates a special RSS feed for your Blosxom site. It uses the extension .rss20 instead of .rss, so it shouldn’t conflict with your existing feed. You then fetch a copy of this feed, and the importer reads the feed to load your new WordPress blog with your old content. Sounds simple, right? Yeah, I thought so too.

First, a few notes about setting up the special RSS feed. By default, it needs the theme plugin, which I didn’t use. You can break the theme up into multiple flavour files, but I found it easier to just install the theme plugin. Drop and go, as I recall. The theme plugin requires the interpolate_fancy plugin, which I did use, and the filesystem plugin, which I didn’t, but that was also a quick install.

Now, by default, Blosxom doesn’t show all posts, only the most recent (by default, 10). This limit is applied for any theme/flavour, including feeds. The number can be configured via the $num_entries variable in the Blosxom script. I played around briefly with trying to use the config plugin to allow me to change the number of entries for only this feed- I didn’t want anyone visiting the site or fetching the feed to get all my entries accidently. Unfortunately, config could not do what I wanted (plugins are just loaded too late, I believe), so I cheated and made a copy of my blosxom.cgi, changed $num_entries, and used that one to fetch the special feed. I also had to disable my moreentries plugin, although I can’t remember exactly what it was breaking.

Now, the importer doesn’t fetch your new feed directly, you have to fetch and save a copy (curl or wget are good for this), copy the file to your server, and edit the import script to point to this file. This is for safety, to make sure you only import what you want to import. My first attempt revealed a few deficiencies in the rss feed and the importer.

First of all, I used Markdown to author my posts in Blosxom, and intended to keep doing so with WordPress. However, the RSS feed contains the rendered HTML, which is what gets imported into WordPress. This imports your posts just fine, but I wanted to maintain my Markdown formatting in case of future edits. I ended up disabling Markdown long enough to fetch the feed with the original Markdown formatting intact.

The next hurdle was URIs. In an earlier post I discussed some of the steps I went through to ensure all of my old URIs would work in WordPress, but the very first hurdle was preserving the post slug. In WordPress parlance (and I believe, in publishing in general), the slug is a short name for an article. Specifically for WordPress, the slug is the ‘file name’ of the post URI. The original import-blosxom.php created a slug for each post based on the title, similar to WordPress’s default mechanism for creating slugs. However, in order to keep my old URIs working with a little mod_rewrite magic, I needed the slugs to match the original filenames used to store the Blosxom posts. I hacked this in as follows:

  1. I modified the rss20 theme file to include the original file name, by adding a line to the <item> section:

    <slug>$fn</slug>
    
  2. I re-fetched the feed to pick up the change.

  3. I edited import-blosxom.php to use the slug. I replaced this line:

    $post_name = sanitize_title($title);
    

    with this:

    $slug = ''
    preg_match('|<slug>(.*?)</slug>|is', $post, $slug);
    $post_name = $slug[1];
    

Now, my after import, my WordPress slugs matched my Blosxom post names, making support of old URIs much simpler (see The Permalink Problem for more). But I wasn’t done yet.

Now, this is going to seem petty to some of you, and that’s fine. But it bothered my that the WordPress post numbers of my import posts were backwards. The most recent imported post was post 1, and the oldest imported post was post 300-and-something. Even though you should never see the post number since I use fancy URIs, I bugged me. So, I fixed it. It may also be worth noting that though these iterations, I ended up futzing my WordPress DB by hand to remove prior imports and reset the post numbering. Hopefully, if you’re following along at home on your own import, you’ll get this all right the first time.

The problem is, Blosxom renders posts in reverse chronological order, like every other blog. The import script reads the rss file and imports the posts in the order in which they appear in the file (which is to say, reverse chronological). But I wanted my posts numbers to be chronological. Yes, I’m a geek. Came to terms with it years ago. Anyway, remember earlier that I couldn’t get config to change the number of entries for the feed? I was, however, able to use it to install a custom Blosxom sort method, like so:

  1. Config has to run first, so i renamed the installed config plugin to 000config, the standard Blosxom hack for plugin load ordering.

  2. In my Blosxom content directory, I created config.rss20, the theme-specific config file for the rss20 theme:

    package config;
    
    sub sort {
      return sub {
        my($files_ref) = @_;
        return sort { $files_ref->{$a} <=> $files_ref->{$b} } keys %$files_ref;
      }
    };
    
    1;
    
  3. Another fetch-import cycle, and my posts were numbered chronologically.

Almost there. I had my content (including comments), but the comment and posts-per-category counts were all 0. It seems WordPress stores these numbers in the database instead of calculating them on the fly, and the importer didn’t update them. A couple of quick sql statements set everything right:

UPDATE wp_posts p SET comment_count = ( SELECT count( * )
FROM `wp_comments` c
WHERE c.comment_post_id = p.id ) 

UPDATE wp_categories c SET category_count = ( SELECT count( * )
FROM wp_post2cat p
WHERE p.category_id = c.cat_id ) 

That’s everything I have in my notes. Hopefully, there’s enought here to help others with a similar conversion. If you find other conversion issues or have questions about my process, please leave a comment below and I’ll try to help