Migrating MovableType to WordPress 2.0.2 (or The Good, The Bad and The Ugly)

Over the last weekend and the week before that I spent a lot of time trying to migrate the blog at Pyre to use WordPress instead of the ancient MovableType installation it was using before. Unfortunately it wasn’t a very smooth ride, so I’ll write my conclusions here so that they could be useful for others.

Exporting from MovableType
The first step in the process was to export the post information from MovableType. This wasn’t really a hard step and the WordPress Importing from MovableType tutorial does a very good job of explaining this step. Unfortunately this didn’t quite work for me because MovableType doesn’t export post id #’s into this file. Why would you want that you ask?

Well…

Apparently MovableType hosts multiple blogs in the same database and uses the same tables for all the blogs. It actually goes even further and has global post id numbers so that the sequence of id’s for any one blog is not consecutive, you’ll have breaks in the numbering wherever someone posted to one of the other blogs. Huh! So maybe I should export all of the blogs at once? Well it turns out that you can’t. No single user, including the administrator can export all the blogs, they can only export the ones they “own”, which I guess means they started.

*sigh*

Importing into WordPress
This means that when I import entries into WordPress the post id #s will be completely different and maintaining links will be close to impossible.

At this point I went through the exercise of inserting my foot into my mouth from 10 different angles, after having said that the importing step “should be a breeze”. I tried writing a program to fetch all post numbers and titles from the old blog, trying to compare to how they import into WordPress and then making a file with all the correspoinding ids. In the end it was decided that maybe if we only preserve internal linking it would be OK. At that point I went and wrote a program that would go through the exported MT entries and substitute the correct addresses from my previously created massive file. This had it’s own problems.

In the end everything ended up working out quite nicely. Here’s how.

The Final Solution

First I found a very useful hint on how to export post ids from MT, which worked like a charm.

I then proceeded to to hack up WordPress to import the entries correctly. The hints at the website above gave me a good idea of what needs to be done but it was a bit old, using WordPress 1.0 instead of 2.0. Here is a patch for my WordPress hack. This also inserts print statements that show you that everything is indeed going according to plan. I’m going to explain the main points of the patch below, but these are copy/pasted parts of the patch that won’t apply if you use them by themselves, please use the full patch.

The first part of the patch is for the wp-includes/functions-post.php file, which defines functions for inserting a post into the database. The first chunk of the patch makes WordPress think that it’s creating a new post, instead of updating, by commenting out the update code.

@@ -16,11 +16,13 @@

// Are we updating or creating?
$update = false;
-   if ( !empty($ID) ) {
-       $update = true;
-       $post = & get_post($ID);
-       $previous_status = $post->post_status;
-   }
+   // igorfoox
+   printf(__('In wp_insert_post, ID is %d'), $ID);
+// if ( !empty($ID) ) {
+//     $update = true;
+//     $post = & get_post($ID);
+//     $previous_status = $post->post_status;
+// }

The second chunk makes WordPress always set the post id to the id we extracted from the comment.

@@ -45,8 +47,8 @@
$post_status = 'draft';

// Get the post ID.
-   if ( $update )
-       $post_ID = $ID;
+   //if ( $update )
+   $post_ID = $ID;

The third chunk is just a strategically placed print statement. The fourth chunk adds the post id to the fields that are inserted into the database.

@@ -142,12 +145,14 @@
menu_order = '$menu_order'
WHERE ID = $post_ID");
} else {
+       printf(__(' Running insert query'));
$wpdb->query(
"INSERT IGNORE INTO $wpdb->posts
-           (post_author, post_date, post_date_gmt, post_content, post_content_filtered, post_title, post_excerpt,  post_status, comment_status, ping_status, post_password, post_name, to_ping, pinged, post_modified, post_modified_gmt, post_parent, menu_order, post_mime_type)
+           (post_author, post_date, post_date_gmt, post_content, post_content_filtered, post_title, post_excerpt,  post_status, comment_status, ping_status, post_password, post_name, to_ping, pinged, post_modified, post_modified_gmt, post_parent, menu_order, post_mime_type, ID)
VALUES
-           ('$post_author', '$post_date', '$post_date_gmt', '$post_content', '$post_content_filtered', '$post_title', '$post_excerpt', '$post_status', '$comment_status', '$ping_status', '$post_password', '$post_name', '$to_ping', '$pinged', '$post_date', '$post_date_gmt', '$post_parent', '$menu_order', '$post_mime_type')");
+           ('$post_author', '$post_date', '$post_date_gmt', '$post_content', '$post_content_filtered', '$post_title', '$post_excerpt', '$post_status', '$comment_status', '$ping_status', '$post_password', '$post_name', '$to_ping', '$pinged', '$post_date', '$post_date_gmt', '$post_parent', '$menu_order', '$post_mime_type', '$post_ID')");
$post_ID = $wpdb->insert_id;
+           printf(__('||| end of insertionn with id %d'), $post_id);
}

The second part of the patch makes WordPress extract the post ids from the exported MovableType data. The first chunk ads the ‘ID’ field as a parseable token

@@ -222,6 +222,9 @@
case 'AUTHOR' :
$post_author = $value;
break;
+                       case 'ID':
+                           $ID = $value;
+                           break;
case 'TITLE' :
$post_title = $wpdb->escape($value);
break;

Then we also send this information to the wp_insert_post function in the second chunk

@@ -281,8 +284,9 @@

$post_author = $this->checkauthor($post_author); //just so that if a post already exists, new users are not created by checkauthor

-                   $postdata = compact('post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_title', 'post_excerpt', 'post_status', 'comment_status', 'ping_status', 'post_modified', 'post_modified_gmt');
+                   $postdata = compact('post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_title', 'post_excerpt', 'post_status', 'comment_status', 'ping_status', 'post_modified', 'post_modified_gmt', 'ID');
$post_id = wp_insert_post($postdata);
+                   printf(__('Id was %d, wanted %d'), $post_id, $ID);
// Add categories.
if (0 != count($post_categories)) {
wp_create_categories($post_categories, $post_id);

These things will probably slightly change with new versions of WordPress, but hopefully it stays similar enough so that people can figure out how to apply it.

Finally I also had to make sure that the old blog feed remained available, because WordPress uses a different address for its feed. I accomplished this with the following .htaccess rule

RewriteRule ^index.rdf$ /blog/rdf [R,L]

I know, it’s not so subtle, doing a full redirect to get the feed right, but I couldn’t figure out how to do it in any other way. What seems to be happening is that WordPress’ internal redirect system interferes with this rule unless I do a full redirect. If anybody has a more elegant solution, I’ll be glad to hear about it.

I think this needs to be put up on the WordPress wiki, because the information there is sooo out of date. Unfortunately I spent way too much time working on this, and so I don’t have that much time to do it, but hopefully after writing it all down, I’ll be able to find the time to stick it there as well in the near future.

3 Responses to “Migrating MovableType to WordPress 2.0.2 (or The Good, The Bad and The Ugly)”

  1. [...] Well that was a waste of a night. I edited the source code to both Movabletype and WordPress in an attemptto get my entry IDs to sync up: first, in an attempt to get Movabletype to note the ID for each entry (which worked) and second, an attempt to get WordPress to care (which didn’t.) This would make it easier to forward links pointing to my old entries. [...]

  2. [...] עוד על שימור פרמלינקים ניתן לקרוא כאן. יש גם את הסקריפט של אלכס קינג שעוזר והסברים של החבר’ה מוורדפרס. [...]

  3. [...] עוד על שימור פרמלינקים ניתן לקרוא כאן. יש גם את הסקריפט של אלכס קינג שעוזר והסברים של החבר’ה מוורדפרס. [...]