Posted by tarek on January 22, 2006 at 10:39am
Hey all!
I've been trying to convert a very complex site from EE to Drupal for some time now. Finally, I gave up and wrote my own conversion script. Special thanks to |Gatsby| for his support and love. I will try to put this into the handbook tomorrow morning. This originally started out as an attempt to refine the WordPress -> Drupal engine available in the handbook..
Advantages:
Disadvantages:
Please refine mercilessly.
tarek : )
It says that the code can't be posted because it's suspicious. You may find it at ExpressionEngine_to_Drupal_migration_program on the Free ZNet project wiki.
Comments
Works also with EE 1.4.1 and Drupal 4.7 final with one issue
Hi,
I just tried your script with EE 1.4.1 and Drupal 4.7 final and it works after a small change (although I don't know if it depended on the EE version):
In EE 1.4.1 the "summary" (teaser in Drupal) field has got field_id_1, the "body" has field_id_2. So you only have to change these in the script.
However, when I run the script the summary is converted to the "teaser" field of Drupal and the body is converted into the "body" field of Drupal. Even when I'm changing the the length of the teaser in the script to unlimited and the same in the Drupal settings I don't bring them together.
I would just like to have both field_id_1 AND field_id_2 in the body field of Drupal. Since I can barely read PHP code not to mention that I can't code it at all I would need some help here.
And, well, the issue is (but it works fine anyway): for every imported entry I'm getting the following error:
Invalid argument supplied for foreach() in /home/xxx/xxx/xxx/migration.php on line 208 (migration.php is how I called your script).
neat...
I'm giving this script a spin, just to investigate the feasibility of migrating two EE installs to Drupal.
Some early findings - my apologies if this write-up lacks coherency:
The script is not aware of EE running multiple sites off a single install, like Drupal's multisites. My inclination is to give each site a database of its own, which means that the converter should be modified to selectively import articles and comments of a specific weblog id. It seems like the simplest way to do it is to add a configuration parameter like
$ee[weblog] = 1;and tack on a clause like
"WHERE weblog_id = " . $ee[weblog]to the pertinent SQL queries. This should always work for EE installs running but a single site and require one pass per site otherwise.
Another issue is the mapping between EE member groups and Drupal roles. It's probably best to add another step. EE groups 1 to 5 seem standard and code can be added to the script that adds these roles and suitable default permissions. Banned EE users should be blocked. Groups 6 and up can be migrated, but it's probably easier to manually assign access rights to these migrates roles after the fact.
I don't have good answer for user accounts shared between multiple EE sites. A workable solution would be to migrate users into a "main" drupal site, enabling the drupal module on the others, and pointing it at the main site. The other sites shouldn't migrate users other than the EE Super Admins.
If Drupal blogs can be individually themed, importing weblog_id other than 1 into blogs might be an option.
When migrating users, the Drupal account name should be set to EE's screenname instead of the username.
As a matter of preference, EE articles should be migrated to Drupal stories instead of blog entries, but see above.
The script doesn't seem to import article bodies, which is probably a trivial fix.
Finally, comment subjects, if enabled on the Drupal side, may need some attention.
getting there...
See
http://fallacio.us/system/files?file=ee-to-drupal.php_.txt
for an updated version of the script. It worked well enough to import a site with 1.400 users, 4.000 posts, and 40.000 comments.
The script has some poorly documented assumptions; e.g.:
a) It should start from a pristine database, with no roles added.
b) A single category should be added before or after the import.
There are also major and minor caveats:
If EE uses SHA1 password hashes, all passwords must be reset. In this case, it's also necessary to reset the password of the Drupal admin using the tool of your choice to do something like
UPDATE users SET pass = md5('ADMIN PASSWORD') WHERE uid = 0;The script does not deal with nested EE categories at all and instead creates a flat hierarchy. Term name collisions are possible, but the term names can be disambiguated by prefixing or appended EE's cat_id, a simple modification to the code. Regardless, for the time being nested categories would have to be manually recreated in Drupal - hopefully that's not too much work.
Finally, depending on the version of EE the import of articles might require code changes, too. Apparently EE 1.3.1 stored the teaser in field_id_1, a table column that doesn't exist in EE 1.4.2.
By and large, migrating users and content is but one step in switching software packages. I don't know that it's possible to create a script that accepts an EE site as input and outputs an equivalent Drupal site, so I fully expect that anybody actually embarking on a migration of a production site would customize the script and repeatedly run it until satisfied.
I'd appreciate bug reports, enhancements, and the like...
Anything new on this front?
I'm looking at moving from EE to Drupal. The links for the scripts no longer work, and there have been some version changes since. EE is now at 1.6.x and Drupal has jumped to 6.x.
Anyone done this or have pointers?
Resurrected the code
I suggest importing to 4.7 and then upgrading to Drupal 5 and then to Drupal 6. Should work. Someone above says it still works with version 1.4 of EE.
The code referenced above no longer exists. The following code was recovered from the Internet Archive.
<?php
/**
* Copyright (c) 2006 Tarek Lubani <tarek@tarek.2y.net>
* This code is hereby licensed for public consumption under the
* GNU GPL v2.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/* $Id$ */
/**
* ExpressionEngine 1.0 -> Drupal Migration
* See ee_migrate.php for more information
*/
$version = "1.0";
// First, configuration options
// ExpressionEngine
$ee[user] = ''; // ExpressionEngine database user
$ee[pass] = ''; // ExpressionEngine database password
$ee[host] = ''; // ExpressionEngine database host
$ee[dbname] = ''; // ExpressionEngine database name
$ee[prefix] = 'exp_'; // ExpressionEngine database table prefix
// Drupal nodes to create from EE posts
$ArticleType = 'blog';
$StaticArticleType = 'page';
$ArticleFilter = 3; // 1 = filtered HTML, 2 = PHP, 3 = raw HTML
$NodeTeaserLength = 100; // 0 = unlimited teaser length
// Start the engine!
// Include Drupal bootstrap
include_once "includes/bootstrap.inc";
include "includes/common.inc";
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
// If not in 'safe mode', increase the maximum execution time:
if (!ini_get('safe_mode')) {
set_time_limit(240);
}
$step1 = "Step 1: Import Users";
$step2 = "Step 2: Import Categories";
$step3 = "Step 3: Import Posts";
// First page
if ($_GET[step] == 1) {
echo "
<html>
<head>
<title>$step1</title>
</head>
<body>
<h2>$step1</h2>";
ConvertUsers($ee);
echo "<p>Next: <b><a href=\"?step=2\">$step2</a></b></p>
</body>
</html>";
exit;
}
elseif ($_GET[step] == 2) {
echo "
<html>
<head>
<title>$step2</title>
</head>
<body>
<h2>$step2</h2>";
ConvertCategories($ee);
echo "<p>Next: <b><a href=\"?step=3\">$step3</a></b></p>
</body>
</html>";
exit;
}
elseif ($_GET[step] == 3) {
echo "
<html>
<head>
<title>$step3</title>
</head>
<body>
<h2>$step3</h2>";
ConvertPosts($ee);
echo "<p>Done! Hopefully it all worked!</p>
</body>
</html>";
exit;
}
else {
echo "
<html>
<head>
<title>EE -> Drupal conversion $version</title>
</head>
<body>
<p>This script will convert EE to Drupal. It has only been tested on EE version 1.0 and Drupal 4.7.0</p>
<ul>
<li><a href=\"?step=1\">$step1</a></li>
<li><a href=\"?step=2\">$step2</a></li>
<li><a href=\"?step=3\">$step3</a></li>
</ul>";
exit;
}
//
// The purpose of this function is to convert users from EE and convert them to drupal
//
function ConvertUsers($ee) {
// Load all users from EE
$sql = "SELECT username,password,email,url FROM ".$ee[prefix]."members WHERE group_id=1 OR group_id=6";
$eeUsers = mysqlQuery($ee,$sql);
foreach($eeUsers as $eeUser) {
$drupalUser = user_load(array('name' => $eeUser['username']));
if (!$drupalUser) {
$user_array = array("name" => $eeUser['username'], "pass" => $eeUser['password'], "mail" => $eeUser['email'], "status" => 1);
user_save($account, $user_array);
echo "Added <i>$eeUser[username] <br />";
}
else{
echo "$eeUser[username] already exists <br />";
}
}
}
//
// The purpose of this function is to convert categories from EE to drupal
//
function ConvertCategories($ee) {
$sql = "SELECT cat_id,parent_id,cat_name from ".$ee[prefix]."categories";
$eeCategories = mysqlQuery($ee,$sql);
foreach ($eeCategories as $eeCategory) {
$taxonomy = taxonomy_get_term_by_name($eeCategory['cat_name']);
if (!$taxonomy) {
$term['name'] = "$eeCategory[cat_name]";
$term['vid'] = "1";
$status = taxonomy_save_term($term);
unset($term);
echo "Imported category <b>$eeCategory[cat_name]</b><br />";
}
else {
//echo $taxonomy->tid;
echo "Category <b>$eeCategory[cat_name]</b> already exists as ".$taxonomy[0]->tid."<br />";
}
}
}
//
// The purpose of this function is to convert posts from EE and convert them to drupal
//
function ConvertPosts($ee) {
$ArticleType = $GLOBALS['ArticleType'];
$ArticleFilter = $GLOBALS['ArticleFilter'];
// Load all weblog titles from EE
$sql = "SELECT entry_id,author_id,title,url_title,entry_date,edit_date FROM ".$ee[prefix]."weblog_titles ORDER BY entry_date";
$ee_Post_titles = mysqlQuery($ee,$sql);
foreach($ee_Post_titles as $ee_Post_title) {
// Load all weblog content from EE
$sql = "SELECT field_id_2,field_id_3 FROM ".$ee[prefix]."weblog_data WHERE entry_id=$ee_Post_title[entry_id]";
$ee_Post_content = mysqlQuery($ee,$sql);
foreach ($ee_Post_content as $ee_Post_content) { $ee_Post_content = $ee_Post_content;}
// Load associated username
$sql = "SELECT username FROM ".$ee[prefix]."members WHERE member_id=$ee_Post_title[author_id]";
$eeUser = mysqlQuery($ee,$sql);
foreach ($eeUser as $eeUser) { $eeUser = $eeUser; }
$drUser = user_load(array('name' => $eeUser['username']));
$node->uid = $drUser->uid;
$node->type = $ArticleType;
$node->title = $ee_Post_title['title'];
$node->body = $ee_Post_content['field_id_3'];
$node->teaser = $ee_Post_content['field_id_2'];
$node->filter = $ArticleFilter;
$node->status = 1;
// $node->revision = ;
$node->promote = 0;
$node->comment = 2;
$node->created = $ee_Post_title['entry_date'];
$node->changed = $ee_Post_title['edit_date'];
// Save the node:
node_save($node);
echo "Saved <b>".$node->title."</b> by <i>".$drUser->name."</i><br />";
$numComments = ImportComments($ee,$node->nid,$ee_Post_title['entry_id']);
MatchCategories($ee,$node->nid,$ee_Post_title['entry_id']);
if (!$numComments) { $numComments = 0;}
echo "Imported $numComments comments for article.<br /><br />";
unset($node);
}
}
function ImportComments($ee,$nid,$eeid) {
$sql = "SELECT `name`,`email`,`url`,`comment_date`,`comment` FROM `".$ee[prefix]."comments` WHERE `entry_id`=$eeid";
$Comments = mysqlQuery($ee,$sql);
foreach ($Comments as $Comments) {
$comment->nid = $nid;
$comment->comment = $Comments['comment'];
$comment->timestamp= $Comments['comment_date'];
$comment->homepage= $Comments['url'];
$comment->mail = $Comments['email'];
$comment->name = $Comments['name'];
comment_save_modified($comment);
$numComments = $numComments + 1;
}
return ($numComments);
}
function MatchCategories($ee,$nid,$eeid) {
$sql = "SELECT cat_id FROM ".$ee[prefix]."category_posts WHERE entry_id=$eeid";
$Category_numbers = mysqlQuery($ee,$sql);
foreach ($Category_numbers as $Category_number) {
$sql = "SELECT cat_name FROM ".$ee[prefix]."categories WHERE cat_id=$Category_number[cat_id]";
$terms = mysqlQuery($ee,$sql);
foreach ($terms as $term) {
$matches = taxonomy_get_term_by_name($term['cat_name']);
foreach ($matches as $match) {
taxonomy_node_save_modified($nid, array($match->tid));
echo "Entry <i>$nid</i> is categorized as <i>$term[cat_name]</i><br />";
}
}
}
}
function mysqlQuery($db, $query_record) {
$mysql_database = $db['dbname'];
$mysql_hostname = $db['host'];
$mysql_username = $db['user'];
$mysql_password = $db['pass'];
$mysql_all = mysql_pconnect($mysql_hostname, $mysql_username, $mysql_password) or die(mysql_error());
mysql_select_db($mysql_database, $mysql_all);
$Recordset1 = mysql_query($query_record, $mysql_all) or die(mysql_error());
while ($a = mysql_fetch_array($Recordset1)) { $resultset[] = $a; }
return ($resultset);
}
function comment_save_modified($edit) {
// Import the comment in the database.
db_query("INSERT INTO {comments} (nid, comment, timestamp, name, mail, homepage) VALUES (%d, '%s', %d, '%s', '%s', '%s')", $edit->nid, $edit->comment,$edit->timestamp, $edit->name, $edit->mail, $edit->homepage);
_comment_update_node_statistics($edit->nid);
// Tell the other modules a new comment has been submitted.
comment_invoke_comment($edit, 'insert');
// Clear the cache so an anonymous user can see his comment being added.
cache_clear_all();
}
function taxonomy_node_save_modified($nid, $terms) {
if ($terms) {
foreach ($terms as $term) {
if ($term) {
db_query("INSERT INTO {term_node} (nid, tid) VALUES (%d, %d)", $nid, $term);
}
}
}
}
?>
I couldn't get this to work.
I couldn't get this to work. When I copy and pasted the text into a file as ee_to_drupal.php and ftp'd it to my public_html directory, the script ran through fine but when I went my website and viewed the index.php, the following error is inside a red box near the top of the screen:
warning: Invalid argument supplied for foreach() in /home/trunksy/public_html/ee_to_drupal.php on line 208.
warning: Invalid argument supplied for foreach() in /home/trunksy/public_html/ee_to_drupal.php on line 225.
warning: Invalid argument supplied for foreach() in /home/trunksy/public_html/ee_to_drupal.php on line 225.
When I log into my website and go to into the admin panel to see if I could edit the content, I get:
warning: call_user_func_array() [function.call-user-func-array]: First argumented is expected to be a valid callback, 'blog_node_form' was given in /home/trunksy/public_html/includes/form.inc on line 377.
I'm migrating from EE 1.6.8 to Drupal 6.22. Does the script need to be tweaked?
Nevermind, I just needed to
Nevermind, I just needed to turn on the blog module. Ha ha. This was a new Drupal installation with no content yet.
Script
Hi, can you please email me the script, as the links for the script does not work? annali.vanzyl@gmail.com, Thanks!