<?php include_once "includes/bootstrap.inc"; include_once "includes/common.inc"; ?> <html> <head> <title>Movable Type => Drupal Conversion</title> <!-- Based on original code by Tim Allman. --> <!-- Revised by Morbus Iff, 2004-05-26, with --> <!-- path aliasing stolen from Scott Laird. --> <!-- Send comments to morbus@disobey.com. --> </head><body> <?php // import-specific settings. // // there are different types of Drupal content: stories, pages, // forums, blogs, etc. by default, your Movable Type imports // will be classified as new stories within your Drupal // content management system. you can change this over to // "blog" if you'd like, but you'll have to ensure that you've // enabled the "blog" module PRIOR to running this install. $node->type = "story"; // the vocabulary name is used to create a new taxonomy within // your new Drupal installation. taxonomies and vocabularies are // fancy terms for Movable Type categories. you can name the vocab // here; you can also change it through Drupal at a later date. $vocabulary['name'] = "Weblog Categories"; // newly imported authors are given an entirely random password - // for them to login, you can instruct users to "Request new // password" from the login block. If, on the other hand, you'd // like to set a default password for everyone, do so here. $author['pass'] = rand(1,66606660666066606660); // generic settings that probably shouldn't // be changed unless you have a clue bit. $timezone = preg_replace('!:!', '', "<$MTBlogTimezone$>"); $node->promote = 1; // all nodes should have had main page status. $author['status'] = 1; // all author's are enabled within the system. $comment['pid'] = 0; // no comments have been threaded; no parents. // let's begin this bad boy, eh? echo "<h1>Movable Type => Drupal Conversion</h1>"; // /////////////////////////////////////////////////////////////////////////// // define code in functions to minimize generated size of the import file. // // /////////////////////////////////////////////////////////////////////////// function add_taxonomy_term($term) { global $vocabulary; $term['tid'] = NULL; $term['vid'] = $vocabulary['vid']; $term = taxonomy_save_term($term); // should probably do some error checking. echo "<li>Created new term '$term[name]' in '$vocabulary[name]'.</li>"; // path loving. yum. stolen from Scott Laird. $term_link = ereg_replace('^http://[^/]+/', '', $term['permalink']); echo "<li>Linking taxonomy/page/or/$term[tid] to $term_link.</li>"; path_set_alias("taxonomy/page/or/$term[tid]", $term_link); } function check_for_author($author) { global $node; // yum, yum, gimme some. $user = user_load(array(mail => $author['mail'])); $user_uid = $user->uid; if (!$user_uid) { $author['rid'] = array(_user_authenticated_id()); $user = user_save($account, $author); echo "<li>Created new user '$author[name]'.</li>"; } else { echo "<li>Entry has known user '$author[name]'.</li>"; } $node->uid = $user->uid; // node author to uid. } // for the node validation, it's important NOT to use the return // $node from _validate, and only check for $error. since we're // directly adding new entries to the database from previously // unauthorized users (those MT folks we're importing), node_ // validate will fail the "is this user authorized?" check, and // return bum data (notably, the entry's ->date and ->created). function add_node($node) { node_validate($node, $error); // check existence of $error only. if (!$error) { $nid = node_save($node); } else { print_r($error); exit; } echo "<li>Created new entry '". $node->title ."'.</li>"; // path loving. yum. stolen from Scott Laird. $path_link = ereg_replace('^http://[^/]+/', '', $node->permalink); echo "<li>Linking node/view/$nid to $path_link.</li>"; path_set_alias("node/view/$nid", $path_link); return $nid; // all done. keep on truckin'. } function add_term_to_node($term) { global $vocabulary, $nid; $matches = taxonomy_get_term_by_name($term); foreach ($matches as $match) { if ($match->vid == $vocabulary['vid']) { taxonomy_node_save($nid, array($match->tid)); echo "<li>Entry is categorized as '$term'.</li>"; } } } function add_comment_to_node($comment) { global $nid; // wow. I have to pee. huh. that's not cool. $comment['thread'] = "$comment[thread]/"; $comment['cid'] = db_next_id("{comments}_cid"); // woulda liked to use the API, but the timestamp needed to be // the original setting, not the time of the import. some other // issues here and there too (invalid e-mails that had been // accepted, the check for whether comments were enabled, etc.) db_query("INSERT INTO {comments} (cid, nid, pid, uid, subject, comment, hostname, ". "timestamp, status, score, users, thread, name, mail, homepage) VALUES ". "(%d, %d, %d, %d, '%s', '%s', '%s', %d, %d, %d, '%s', '%s', '%s', '%s', '%s')", $comment['cid'], $nid, 0, 0, NULL, $comment['comment'], $comment['remote_addr'], $comment['timestamp'], 0, 0, "a:1:{i:0;i:0;}", $comment['thread'], $comment['name'], $comment['email'], $comment['URL']); } // code stolen from trackback_receive. function add_ping_to_node($ping) { global $nid; // wow. I have to pee. huh. that's not cool. $ping['thread'] = "$ping[thread]/"; $ping['cid'] = db_next_id("{comments}_cid"); $ping['excerpt'] = (strlen($ping['excerpt'] > 255) ? substr($ping['excerpt'], 0, 252) ."..." : $ping['excerpt']); $ping['name'] = ($ping['blog_name']) ? $ping['blog_name'] : $ping['url']; $comment = "<strong>". t("TrackBack from %url", array("%url" => "<a href=\"$ping[url]\">$ping[name]</a>")) .":</strong><br />"; $comment .= "<blockquote>$ping[excerpt]</blockquote>"; // all errors introduced are my own. beeyotch. // woulda liked to use the API, but the timestamp needed to be // the original setting, not time of the import. same with comments. // biggest change here is the addition of name/url as per CVS HEAD. db_query("INSERT INTO {comments} (cid, nid, pid, uid, subject, comment, hostname, ". "timestamp, status, score, users, thread, name, mail, homepage) VALUES ". "(%d, %d, %d, %d, '%s', '%s', '%s', %d, %d, %d, '%s', '%s', '%s', '%s', '%s')", $ping['cid'], $nid, 0, 0, NULL, $comment, $ping['remote_addr'], $ping['timestamp'], 0, 0, "", $ping['thread'], $ping['name'], "", $ping['url']); } // /////////////////////////////////////////////////////////////////////////// // first thing we do is import the categories of Movable Type (the "terms") // // into the Drupal taxonomy ($vocabulary_name). if our vocabulary has been // // added already, we'll move on to entries (assuming multiple import runs). // // /////////////////////////////////////////////////////////////////////////// if (!taxonomy_get_vocabulary_by_name($vocabulary['name'])) { echo "<h2>Importing Categories</h2><ul>"; echo "<li>Created new vocabulary '$vocabulary[name]'.</li>"; $vocabulary = taxonomy_save_vocabulary(array("name"=> $vocabulary['name'], "nodes" => array($node['type']))); <MTCategories show_empty="1"> $term['name'] = "<$MTCategoryLabel$>"; $term['permalink'] = "<$MTCategoryArchiveLink$>"; $term['description'] = <<<CATEGORY_DESCRIPTION <$MTCategoryDescription remove_html="1" $> CATEGORY_DESCRIPTION; add_taxonomy_term($term); </MTCategories> // if we're not creating a vocab, load the ID in. ugly hack, love me anyways. seriously. echo "</ul>"; } else { $v = taxonomy_get_vocabulary_by_name($vocabulary['name']); $vocabulary['vid'] = $v[0]->vid; } // /////////////////////////////////////////////////////////////////////////// // now, let's import the entries. it'd be nice if we could import the // // authors first, but MT has no loopable author equivalent, so instead // // we have to do people when we learn about 'em during entry loop. // // /////////////////////////////////////////////////////////////////////////// echo "<h2>Importing Entries</h2><ul>"; // depending on how you've configured // your movable type entries, you should // tweak these to be better represented. // // if you're running this script multiple // times for large imports, [MTEntries] is // where you'd set "lastn" and "offset". see // your movable type manual for more info. <MTEntries sort_order="ascend" lastn="100"> $node->title = <<<ENTRY_TITLE <$MTEntryTitle remove_html="1"$> ENTRY_TITLE; $node->teaser = <<<ENTRY_TEASER <$MTEntryExcerpt$> ENTRY_TEASER; $node->body = <<<ENTRY_BODY <$MTEntryBody$><$MTEntryMore$> ENTRY_BODY; $author['name'] = "<$MTEntryAuthor$>"; $author['mail'] = '<$MTEntryAuthorEmail$>'; check_for_author($author); // add 'em? $node->comment = 0; // comment status? <MTEntryIfAllowComments>$node->comment = 1; <MTEntryIfCommentsOpen>$node->comment = 2; </MTEntryIfCommentsOpen></MTEntryIfAllowComments> $node->permalink = "<$MTEntryPermalink$>"; $node->status = strtolower('<$MTEntryStatus$>') == "publish" ? 1 : 0; $node->created = strtotime("<$MTEntryDate format="%Y-%m-%d %H:%M:%S"$> $timezone"); $node->changed = strtotime("<$MTEntryModifiedDate format="%Y-%m-%d %H:%M:%S"$> $timezone"); $nid = add_node($node); // does validation. <MTEntryCategories> $term = "<$MTCategoryLabel$>"; add_term_to_node($term); </MTEntryCategories> $comment_count = 1; <MTComments sort_order="ascend"> $comment['comment'] = <<<COMMENT_BODY <$MTCommentBody remove_html="1"$> COMMENT_BODY; $comment['name'] = <<<COMMENT_NAME <$MTCommentAuthor$> COMMENT_NAME; $comment['timestamp'] = strtotime("<$MTCommentDate format="%Y-%m-%d %H:%M:%S"$> $timezone"); $comment['remote_addr'] = "<$MTCommentIP$>"; $comment['email'] = "<$MTCommentEmail$>"; $comment['URL'] = "<$MTCommentURL$>"; $comment['thread'] = $comment_count; add_comment_to_node($comment); $comment_count++; </MTComments> <MTPings sort_order="ascend"> $ping['title'] = <<<PING_TITLE <$MTPingTitle$> PING_TITLE; $ping['excerpt'] = <<<PING_EXCERPT <$MTPingExcerpt$> PING_EXCERPT; $ping['blog_name'] = <<<PING_BLOGNAME <$MTPingBlogName$> PING_BLOGNAME; $ping['url'] = "<$MTPingURL$>"; $ping['timestamp'] = strtotime("<$MTPingDate format="%Y-%m-%d %H:%M:%S"$> $timezone"); $ping['remote_addr'] = "<$MTPingIP$>"; $ping['thread'] = $comment_count; add_ping_to_node($ping); $comment_count++; </MTPings> $comment_count = 0; </MTEntries> echo "</ul>"; ?> </body> </html>