Since this blog started in 2005, we’ve steadily grown in readership and the number of blogs hosted at the Walker. We had 10 unique blogs, which was a hassle to maintain and upgrade. Our blogs all shared a central installation of WordPress, but used different databases. This meant that the users between blogs were unique: [...]
Since this blog started in 2005, we’ve steadily grown in readership and the number of blogs hosted at the Walker. We had 10 unique blogs, which was a hassle to maintain and upgrade. Our blogs all shared a central installation of WordPress, but used different databases. This meant that the users between blogs were unique: I could have different passwords and profiles on each blog, keeping them in sync was time consuming.
Faced with adding another blog (more on that in a couple weeks), we knew that we couldn’t continue to grow our haphazard system any more. We bit the bullet and switched to WordPress MU (WPMU), which solves many of the problems over standalone WordPress. I’ll enumerate the benefits:
- Shared users between all blogs
- Centralized location for files
- Can activate plug-ins across all blogs
- Easy to add new blogs
- Allows us to set up caching
In the past, we had resisted going to WPMU because it wasn’t keeping pace with standalone WordPress in terms of updates. At the same time, we hadn’t updated in a while, and were still using WordPress 2.3.3, when 2.5 and 2.6 added quite a bit, especially better image management. Around the 2.5.2 release of WordPress, the MU trunk was finally in close sync with standalone. No more excuses.
How MU Works
WPMU is very similar to regular WordPress. In fact, there are really only a handful of files that are any different than regular WordPress. The functions that WPMU adds are mostly to deal with the mutiple blog IDs, creating and delting blogs, and dealing with the more complicated permissions. Two of the more handy function it provides are switch_to_blog( $new_blog ) and restore_current_blog(), which let you switch to a different blog, use the proper Loop queries, and then go back to the original blog. This makes it a lot easier to deal with getting information from multiple blogs, an otherwise complicated procedure.
If you look at the database model, WPMU keeps centralized tables for users and blog meta information. Each new blog created gets a set of it’s own tables.
Upgrading to WPMU is not a simple process. In fact, there’s no specific procedure or easy script for it. The main problem is that if you have multiple blogs, your user IDs and post IDs are going to collide. That is, on the New Media blog, my user ID might be 7, but on OffCenter, it is 23, and user ID 7 is someone else. This prevents us from doing a straight SQL import. We’d need a script that could interact with all our existing databases, figure out the conflicts, and then create a new centralized list of users (more on this in a minute).
Additionally, importing posts wouldn’t work either, because with a new users table, all the user IDs are likely to be different. Thankfully, WordPress does provide functionality to export posts on a per-user basis, and then when you import them, assign them to new users. I did a little testing and this worked well enough, but I hacked wordpress and wrote a small script to automate this and dump all the WordPress Export files (WXR) to the filesystem. I was able to run this script and grab all the export files.
As for importing the users, I dumped the wp_users and wp_usermeta table from each database, renamed them, and brought them all into a single database. I wrote a script that iterated through all the databases, matching users based on their login name. Thankfully, we only had two users that shared the same login names, so resolving that conflict was easy. I would collect all the information about a user spread across all the blogs, and then add it correctly to a new wp_users and wp_usermeta table. The trick was to know ahead of time what the new blog IDs in WPMU would be, then re-map the existing wp_user_level and wp_capabilities info to wp_3_user_level, and wp_capabilities, for instance.
Cursed Text Encoding
Another problem we ran into was incompatabilites with text encoding. Many of our entries had invalid characters in them. For compatability reasons with AxKit (which powers most of the Walker site) we’ve been using ISO-8859-1 encoding, which has a fairly limited range of characters. WordPress doesn’t do such a good job of forcing non-standard characters to be transformed into HTML entities, so anytime there was a proper apostrophe, curly quote or emdash, a freaky character would make it’s debut. Despite this, the posts continuted to work, the database threw a couple of warnings, but it continued to work.
The problem came with importing the exported WXR files. WordPress’ importer is very strict, and it would hang on any of these invalid characters. After trying multiple things to filter them out in the export or the database, I ended up using a Text Factory in BBEdit to replace them. Additionally, BBEdit has a wonderful command to “Zap Gremlins”, or take out weird characters in text. A few minutes of batch processing later, and the import files were clean.
Importing the cleaned WXR files (267 of them) was a tedious process that had to be done by hand, but allowed us to catch any errors and make sure the author assignment worked correctly.
VHOST vs. Sub-directory
WPMU can work in two different URL configurations. One is sub-domain based: joe.mydomain.com, steve.mydomain.com, etc. The other is sub-directory based: blogs.mydomain.com/steve, blogs.mydomain.com/joe. You have to decide what configuration to use when you install WPMU. We use a sub-directory based system for our blogs, but a couple other sites, teens.walkerart.org and air.walkerart.org, are also blogs, but use a sub-domain.
This would seem to leave us at somethinig of an impasse. All of my research confirmed there was no way to have both a sub-domain and sub-directory based URL structure. Several people on the WPMU forums were looking for a solution, but none was to be found.
However, there is a way, and it’s a thorougly good hack. Rather than try to force WPMU to work with both, we just let WordPress deal with sub-directory based URLs. Instead, we use a mod_rewrite to proxy the site to the sub-domain URL (your server must have mod_rewrite AND mod_proxy installed). The theme for the site has to be hacked to output all the links as the sub-domain URLs, even though it’s being served out of a sub-directory configuration. The teens site is a good example. If you visit blogs.walkerart.org/teens/, the site works just fine. Here’s the mod_rewrite that would go in the httpd.conf or .htaccess:
RewriteRule ^(.*)$ http://blogs.walkerart.org/teens$1 [P]
All of the links will take you to a teens.walkerart.org URL. To re-write the URLs, you must replace all the get_permalink() function calls with a custom function that returns the sub-domain URL. And if you use any other functions, such as wp_get_pages or wp_list_cats() to output any links, you must again replace the sub-directory URL with the sub-domain URL. Here’s an example of the useful code:
define('BASE_URL','blogs.walkerart.org/teens/'); //original sub-directory URL
define('REWRITE_URL',"teens.walkerart.org/"); // new sub-domain URL
echo str_replace(BASE_URL,REWRITE_URL, wp_list_pages('hierarchical=0&title_li=&echo=0') );
//takes a given post or page ID and returns the permalink for a teens.walkerart.org permalink
//this is used for the teens site that's run on wordpress mu in a subdirectory configuration,
//but uses mod_rewrite and mod_proxy to relay URLs to a different domain
$perma = get_permalink($id);
Other useful bits
Aside from the sub-directory/sub-domain problems, there were not any other huge issues with our upgrade. A few little things in our themes had to be changed. Author pages needed to be made WPMU aware. Styles needed to be added for the galleries and captions that WP 2.5 and 2.6 added.
We also replaced blogs.walkerart.org with a page powered by WPMU, rather than the old axkit-based aggregation that was there previously. This is a custom theme that queries all the blogs and displays the current aggregation. The aggregated RSS feeds we offer was something we also needed to keep, but WPMU doesn’t offer a built-in way to do that. Thankfully, there is a plugin, WPMU-Sitewide-Feed, that takes care of it. The plugin is no longer maintained, but there is a version available that I was able to get to work with some slight modifications.
Another plug-in we’re now using is Comment Timeout. This plug-in is pretty simple. It closes comments on old and inactive posts to cut down on spam. It is a little smart, in that it won’t close comments on a “popular” post that has recent activity. In our old setup, we had been using the wonderful Spam Karma plugin to take care of spam. It worked really well, but it’s no longer under development and is not WPMU compatible. Instead, we’ve switched to using Defenseio, which is a more free and potentially smarter competitor to Akismet. Our filter is still being trained, but I have high hopes.