How I migrated a client’s blog from Drupal to WordPress 3.0

I just finished migrating a client’s blog from Drupal to WordPress 3.0. When I took on the project, I assumed there was a straightforward way to migrate content from Drupal to WordPress, but I was mistaken. Fortunately, there is a way, but it involves directly mucking about with the Drupal and WordPress databases. Never fear! There are some tools to help.

After a bunch of research, I found the most comprehensive and up-to-date solution on Mike Smullin’s blog. It wasn’t that up-to-date, though, having been written over a year ago and targeted at WordPress 2.7. Some helpful comments on that post offered a few refinements, and I added a few of my own to accommodate the particular quirks of this client’s site.

Since Mike Smullin (and D’Arcy Norman and Dave Dash before him) was so kind to share his work, I thought I should do the same. And so, I present my further refined SQL script for migrating Drupal data to a WordPress 3.0 site.

# DRUPAL-TO-WORDPRESS CONVERSION SCRIPT

# Changelog

# 07.29.2010 – Updated by Scott Anderson / Room 34 Creative Services http://blog.room34.com/archives/4530
# 02.06.2009 – Updated by Mike Smullin http://www.mikesmullin.com/development/migrate-convert-import-drupal-5-to-wordpress-27/
# 05.15.2007 – Updated by D’Arcy Norman http://www.darcynorman.net/2007/05/15/how-to-migrate-from-drupal-5-to-wordpress-2/
# 05.19.2006 – Created by Dave Dash http://spindrop.us/2006/05/19/migrating-from-drupal-47-to-wordpress/

# This assumes that WordPress and Drupal are in separate databases, named 'wordpress' and 'drupal'.
# If your database names differ, adjust these accordingly.

# Empty previous content from WordPress database.
TRUNCATE TABLE wordpress.wp_comments;
TRUNCATE TABLE wordpress.wp_links;
TRUNCATE TABLE wordpress.wp_postmeta;
TRUNCATE TABLE wordpress.wp_posts;
TRUNCATE TABLE wordpress.wp_term_relationships;
TRUNCATE TABLE wordpress.wp_term_taxonomy;
TRUNCATE TABLE wordpress.wp_terms;

# If you're not bringing over multiple Drupal authors, comment out these lines and the other
# author-related queries near the bottom of the script.
# This assumes you're keeping the default admin user (user_id = 1) created during installation.
DELETE FROM wordpress.wp_users WHERE ID > 1;
DELETE FROM wordpress.wp_usermeta WHERE user_id > 1;

# TAGS
# Using REPLACE prevents script from breaking if Drupal contains duplicate terms.
REPLACE INTO wordpress.wp_terms
    (term_id, `name`, slug, term_group)
    SELECT DISTINCT
        d.tid, d.name, REPLACE(LOWER(d.name), ' ', '_'), 0
    FROM drupal.term_data d
    INNER JOIN drupal.term_hierarchy h
        USING(tid)
    INNER JOIN drupal.term_node n
        USING(tid)
    WHERE (1
         # This helps eliminate spam tags from import; uncomment if necessary.
         # AND LENGTH(d.name) < 50
    )
;

INSERT INTO wordpress.wp_term_taxonomy
    (term_id, taxonomy, description, parent)
    SELECT DISTINCT
        d.tid `term_id`,
        'post_tag' `taxonomy`,
        d.description `description`,
        h.parent `parent`
    FROM drupal.term_data d
    INNER JOIN drupal.term_hierarchy h
        USING(tid)
    INNER JOIN drupal.term_node n
        USING(tid)
    WHERE (1
         # This helps eliminate spam tags from import; uncomment if necessary.
         # AND LENGTH(d.name) < 50
    )
;

# POSTS
# Keeps private posts hidden.
INSERT INTO wordpress.wp_posts
    (id, post_author, post_date, post_content, post_title, post_excerpt,
    post_name, post_modified, post_type, `post_status`)
    SELECT DISTINCT
        n.nid `id`,
        n.uid `post_author`,
        FROM_UNIXTIME(n.created) `post_date`,
        r.body `post_content`,
        n.title `post_title`,
        r.teaser `post_excerpt`,
        IF(SUBSTR(a.dst, 11, 1) = '/', SUBSTR(a.dst, 12), a.dst) `post_name`,
        FROM_UNIXTIME(n.changed) `post_modified`,
        n.type `post_type`,
        IF(n.status = 1, 'publish', 'private') `post_status`
    FROM drupal.node n
    INNER JOIN drupal.node_revisions r
        USING(vid)
    LEFT OUTER JOIN drupal.url_alias a
        ON a.src = CONCAT('node/', n.nid)
    # Add more Drupal content types below if applicable.
    WHERE n.type IN ('post', 'page', 'blog')
;

# Fix post type; http://www.mikesmullin.com/development/migrate-convert-import-drupal-5-to-wordpress-27/#comment-17826
# Add more Drupal content types below if applicable.
UPDATE wordpress.wp_posts
    SET post_type = 'post'
    WHERE post_type IN ('blog')
;

# Set all pages to "pending".
# If you're keeping the same page structure from Drupal, comment out this query
# and the new page INSERT at the end of this script.
UPDATE wordpress.wp_posts SET post_status = 'pending' WHERE post_type = 'page';

# POST/TAG RELATIONSHIPS
INSERT INTO wordpress.wp_term_relationships (object_id, term_taxonomy_id)
    SELECT DISTINCT nid, tid FROM drupal.term_node
;

# Update tag counts.
UPDATE wp_term_taxonomy tt
    SET `count` = (
        SELECT COUNT(tr.object_id)
        FROM wp_term_relationships tr
        WHERE tr.term_taxonomy_id = tt.term_taxonomy_id
    )
;

# COMMENTS
# Keeps unapproved comments hidden.
# Incorporates change noted here: http://www.mikesmullin.com/development/migrate-convert-import-drupal-5-to-wordpress-27/#comment-32169
INSERT INTO wordpress.wp_comments
    (comment_post_ID, comment_date, comment_content, comment_parent, comment_author,
    comment_author_email, comment_author_url, comment_approved)
    SELECT DISTINCT
        nid, FROM_UNIXTIME(timestamp), comment, thread, name,
        mail, homepage, ((status + 1) % 2)
    FROM drupal.comments
;

# Update comments count on wp_posts table.
UPDATE wordpress.wp_posts
    SET `comment_count` = (
        SELECT COUNT(`comment_post_id`)
        FROM wordpress.wp_comments
        WHERE wordpress.wp_posts.`id` = wordpress.wp_comments.`comment_post_id`
    )
;

# Fix images in post content; uncomment if you're moving files from "files" to "wp-content/uploads".
# UPDATE wordpress.wp_posts SET post_content = REPLACE(post_content, '"/files/', '"/wp-content/uploads/');

# Fix taxonomy; http://www.mikesmullin.com/development/migrate-convert-import-drupal-5-to-wordpress-27/#comment-27140
UPDATE IGNORE wordpress.wp_term_relationships, wordpress.wp_term_taxonomy
    SET wordpress.wp_term_relationships.term_taxonomy_id = wordpress.wp_term_taxonomy.term_taxonomy_id
    WHERE wordpress.wp_term_relationships.term_taxonomy_id = wordpress.wp_term_taxonomy.term_id
;

# OPTIONAL ADDITIONS — REMOVE ALL BELOW IF NOT APPLICABLE TO YOUR CONFIGURATION

# CATEGORIES
# These are NEW categories, not in Drupal. Add as many sets as needed.
INSERT IGNORE INTO wordpress.wp_terms (name, slug)
    VALUES
    ('First Category', 'first-category'),
    ('Second Category', 'second-category'),
    ('Third Category', 'third-category')
;

# Set category names to title case (in case term already exists [as a tag] in lowercase).
UPDATE wordpress.wp_terms SET name = 'First Category' WHERE name = 'first category';
UPDATE wordpress.wp_terms SET name = 'Second Category' WHERE name = 'second category';
UPDATE wordpress.wp_terms SET name = 'Third Category' WHERE name = 'third category';

# Add categories to taxonomy.
INSERT INTO wordpress.wp_term_taxonomy (term_id, taxonomy)
    VALUES
    ((SELECT term_id FROM wp_terms WHERE slug = 'first-category'), 'category'),
    ((SELECT term_id FROM wp_terms WHERE slug = 'second-category'), 'category'),
    ((SELECT term_id FROM wp_terms WHERE slug = 'third-category'), 'category')
;

# Auto-assign posts to category.
# You'll need to work out your own logic to determine strings/terms to match.
# Repeat this block as needed for each category you're creating.
INSERT IGNORE INTO wordpress.wp_term_relationships (object_id, term_taxonomy_id)
    SELECT DISTINCT p.ID AS object_id,
        (SELECT tt.term_taxonomy_id
        FROM wordpress.wp_term_taxonomy tt
        INNER JOIN wordpress.wp_terms t USING (term_id)
        WHERE t.slug = 'enter-category-slug-here'
        AND tt.taxonomy = 'category') AS term_taxonomy_id
    FROM wordpress.wp_posts p
    WHERE p.post_content LIKE '%enter string to match here%'
    OR p.ID IN (
        SELECT tr.object_id
        FROM wordpress.wp_term_taxonomy tt
        INNER JOIN wordpress.wp_terms t USING (term_id)
        INNER JOIN wordpress.wp_term_relationships tr USING (term_taxonomy_id)
        WHERE t.slug IN ('enter','terms','to','match','here')
        AND tt.taxonomy = 'post_tag'
    )
;

# Update category counts.
UPDATE wp_term_taxonomy tt
    SET `count` = (
        SELECT COUNT(tr.object_id)
        FROM wp_term_relationships tr
        WHERE tr.term_taxonomy_id = tt.term_taxonomy_id
    )
;

# AUTHORS
INSERT IGNORE INTO wordpress.wp_users
    (ID, user_login, user_pass, user_nicename, user_email,
    user_registered, user_activation_key, user_status, display_name)
    SELECT DISTINCT
        u.uid, u.mail, NULL, u.name, u.mail,
        FROM_UNIXTIME(created), '', 0, u.name
    FROM drupal.users u
    INNER JOIN drupal.users_roles r
        USING (uid)
    WHERE (1
        # Uncomment and enter any email addresses you want to exclude below.
        # AND u.mail NOT IN ('test@example.com')
    )
;

# Assign author permissions.
# Sets all authors to "author" by default; next section can selectively promote individual authors
INSERT IGNORE INTO wordpress.wp_usermeta (user_id, meta_key, meta_value)
    SELECT DISTINCT
        u.uid, 'wp_capabilities', 'a:1:{s:6:"author";s:1:"1";}'
    FROM drupal.users u
    INNER JOIN drupal.users_roles r
        USING (uid)
    WHERE (1
        # Uncomment and enter any email addresses you want to exclude below.
        # AND u.mail NOT IN ('test@example.com')
    )
;
INSERT IGNORE INTO wordpress.wp_usermeta (user_id, meta_key, meta_value)
    SELECT DISTINCT
        u.uid, 'wp_user_level', '2'
    FROM drupal.users u
    INNER JOIN drupal.users_roles r
        USING (uid)
    WHERE (1
        # Uncomment and enter any email addresses you want to exclude below.
        # AND u.mail NOT IN ('test@example.com')
    )
;

# Change permissions for admins.
# Add any specific user IDs to IN list to make them administrators.
# User ID values are carried over from Drupal.
UPDATE wordpress.wp_usermeta
    SET meta_value = 'a:1:{s:13:"administrator";s:1:"1";}'
    WHERE user_id IN (1) AND meta_key = 'wp_capabilities'
;
UPDATE wordpress.wp_usermeta
    SET meta_value = '10'
    WHERE user_id IN (1) AND meta_key = 'wp_user_level'
;

# Reassign post authorship.
UPDATE wordpress.wp_posts
    SET post_author = NULL
    WHERE post_author NOT IN (SELECT DISTINCT ID FROM wordpress.wp_users)
;

# VIDEO – READ BELOW AND COMMENT OUT IF NOT APPLICABLE TO YOUR SITE
# If your Drupal site uses the content_field_video table to store links to YouTube videos,
# this query will insert the video URLs at the end of all relevant posts.
# WordPress will automatically convert the video URLs to YouTube embed code.
UPDATE IGNORE wordpress.wp_posts p, drupal.content_field_video v
    SET p.post_content = CONCAT_WS('\n',post_content,v.field_video_embed)
    WHERE p.ID = v.nid
;

# IMAGES – READ BELOW AND COMMENT OUT IF NOT APPLICABLE TO YOUR SITE
# If your Drupal site uses the content_field_image table to store images associated with posts,
# but not actually referenced in the content of the posts themselves, this query
# will insert the images at the top of the post.
# HTML/CSS NOTE: The code applies a "drupal_image" class to the image and places it inside a <div>
# with the "drupal_image_wrapper" class. Add CSS to your WordPress theme as appropriate to
# handle styling of these elements. The <img> tag as written assumes you'll be copying the
# Drupal "files" directory into the root level of WordPress, NOT placing it inside the
# "wp-content/uploads" directory. It also relies on a properly formatted <base href="" /> tag.
# Make changes as necessary before running this script!
UPDATE IGNORE wordpress.wp_posts p, drupal.content_field_image i, drupal.files f
    SET p.post_content =
        CONCAT(
            CONCAT(
                '<div class="drupal_image_wrapper"><img src="files/',
                f.filename,
                '" class="drupal_image" /></div>'
            ),
            p.post_content
        )
    WHERE p.ID = i.nid
    AND i.field_image_fid = f.fid
    AND (
        f.filename LIKE '%.jpg'
        OR f.filename LIKE '%.jpeg'
        OR f.filename LIKE '%.png'
        OR f.filename LIKE '%.gif'
    )
;

# Fix post_name to remove paths.
# If applicable; Drupal allows paths (i.e. slashes) in the dst field, but this breaks
# WordPress URLs. If you have mod_rewrite turned on, stripping out the portion before
# the final slash will allow old site links to work properly, even if the path before
# the slash is different!
UPDATE wordpress.wp_posts
    SET post_name =
    REVERSE(SUBSTRING(REVERSE(post_name),1,LOCATE('/',REVERSE(post_name))-1))
;

# Miscellaneous clean-up.
# There may be some extraneous blank spaces in your Drupal posts; use these queries
# or other similar ones to strip out the undesirable tags.
UPDATE wordpress.wp_posts
    SET post_content = REPLACE(post_content,'<p> </p>','')
;
UPDATE wordpress.wp_posts
    SET post_content = REPLACE(post_content,'<p class="italic"> </p>','')
;

# NEW PAGES – READ BELOW AND COMMENT OUT IF NOT APPLICABLE TO YOUR SITE
# MUST COME LAST IN THE SCRIPT AFTER ALL OTHER QUERIES!
# If your site will contain new pages, you can set up the basic structure for them here.
# Once the import is complete, go into the WordPress admin and copy content from the Drupal
# pages (which are set to "pending" in a query above) into the appropriate new pages.
INSERT INTO wordpress.wp_posts
    (`post_author`, `post_date`, `post_date_gmt`, `post_content`, `post_title`,
    `post_excerpt`, `post_status`, `comment_status`, `ping_status`, `post_password`,
    `post_name`, `to_ping`, `pinged`, `post_modified`, `post_modified_gmt`,
    `post_content_filtered`, `post_parent`, `guid`, `menu_order`, `post_type`,
    `post_mime_type`, `comment_count`)
    VALUES
    (1, NOW(), NOW(), 'Page content goes here, or leave this value empty.', 'Page Title',
    '', 'publish', 'closed', 'closed', '',
    'slug-goes-here', '', '', NOW(), NOW(),
    '', 0, 'http://full.url.to.page.goes.here', 1, 'page', '', 0)
;

A few notes and suggestions:

  • Use at your own risk. I’m just sharing the code I used. In fact, it’s not even exactly the code I used, since I had to generalize a few things for mass consumption. I haven’t even tested this modified version. I make no guarantees as to its reliability or applicability to your particular implementation.
  • Download the script below; don’t just copy-paste the code from the block above. I had to apply some HTML formatting to make it look right, and this might result in bad characters that would break the script if run as-is above.
  • Always work with a test copy of your database before altering live site content. In theory this script can be run over and over again cleanly, but it will erase whatever data was already in the WordPress database, and there will be no way to get it back unless you have a backup.
  • Read through the code carefully, especially all comments before running the script. Most likely you will need to make some changes to the code.
  • Good luck! If you find bugs or develop further improvements, please comment below!

Download the Script

Download zip file... Drupal-to-WordPress Migration Script
drupal-to-wordpress.sql.zip • 4.8 KB

60 thoughts on “How I migrated a client’s blog from Drupal to WordPress 3.0

  1. Scott, this looks like what I need, but do you know if this will work for Drupal 6 with Content Creation Kit content types?

  2. Well, like I said in the post, I only tried it in my particular situation. I don’t even know what version of Drupal the site was running. (I don’t know much about Drupal, other than that when I tried it on my site a couple years ago, I hated it and was drowning in spam comments.)

    No matter what, you’re probably going to need to tweak a few things in the script. Try it on a test copy of your database and you should be fine. The cool thing about the script is that, since it doesn’t alter any of the data in the Drupal data tables, and it clears out the WordPress tables for a “blank slate” each time it runs, you can actually run it over and over again, tweaking it as necessary until you get what you want.

    So… run it on a test copy of your database; tweak as needed, then when it’s ready to go, make a backup copy of your live database (just in case something goes wrong) and then go for it!

    Good luck!

  3. But—speaking as a designer—why would you want to do that? I mean WordPress is easier, but it results in such bloglike sites… 

  4. Not to start a flame war, but in my own experience Drupal seems to encourage cookie-cutter, cluttered site designs a lot more than WordPress does. It’s pretty easy to make a WordPress site look any way you want, and it’s increasingly useful as a general-purpose CMS, not just for blogs.

  5. I don’t know enough about Drupal to know if the old site was using that module or not. I do know that I had to write some additional SQL to bring over some images that were stored in Drupal tables called content_field_image and files.

    I decided to just pull those images over and insert them at the beginning of the relevant posts. I included a special wrapper CSS class to allow me to handle these images in a consistent way.

    Here’s the SQL. It should come at the end of your script (or at least after everything from the original above):

    UPDATE IGNORE wp_posts p, content_field_image i, files f
    SET p.post_content = CONCAT(CONCAT('<div class="drupal_image_wrapper"><img src="files/',f.filename,'" class="drupal_image" /></div>'), p.post_content)
    WHERE p.ID = i.nid
    AND i.field_image_fid = f.fid
    AND (
    f.filename LIKE '%.jpg'
    OR f.filename LIKE '%.jpeg'
    OR f.filename LIKE '%.png'
    OR f.filename LIKE '%.gif'
    );

  6. Pingback: Conversion from Drupal to Wordpress | ex ipso et per ipsum et in ipso omnia

  7. Thanks. With the exception of the Video section, simply because I didn’t have videos in Drupal, this works great.

    I migrated from Drupal 6 to WordPress 3, well am in the testing process and everything is going good.

  8. Pingback: Migrate from Drupal 6 to Wordpress 3

  9. I’m migrating the data of my Drupal site http://hottopicsonline.info/ to WordPress site http://zigg.in/

    But, I’m getting this message in after running SQL query:

    Error
    SQL query:

    # This assumes that WordPress and Drupal are copied in one databases, named ‘wordpress’.
    # The Drupal tables has the prefix ishk_
    # The WordPress tables do not have a prefix
    # If your database name or prefixes differ, adjust these accordingly.
    # I had to comment out the VIDEO and IMAGE parts of the script. You can find it simply by
    # searching VIDEO or IMAGE
    # Empty previous content from WordPress database.
    TRUNCATE TABLE wordpress.comments;

    MySQL said:

    #1142 – DELETE command denied to user ‘ziggin’@'localhost’ for table ‘comments’

  10. Looks like your user account doesn’t have DELETE privileges on the WordPress database. You’ll need to contact a server administrator (i.e. your hosting provider) to change that.

  11. Thanks for the comment room34.com !

    I’ve run the SQL Query successfully, but still my data not showing up. Can u tell me something about this ?

  12. Hi,

    just wondering if you could help – I using a mac and I’ve no idea how to run a script.

    I’m moving from porting from Drupal 5 (Drigg) to WordPress 3.

    It’s just Posts and Tags that I’m really looking to move so quite basic stuff I hope…

    Any help would be appreciated.

    Thanks,
    Pete

  13. You are da’ BOMB!!!! Whoo-hoo!!!

    I never-ever-never could have done this without your help especially on the last little bit about image files.

    Now I just need to CSS the WordPress site and I am ready to hand it back over to the artist.

    Making sure to save these commands for future reference.

    Thanks and keep up the good work.
    Pilar

  14. Hi, first of all, thanks for this. I have been contemplating in migrating to WordPress for quite a while, and this post definitely pushed me further to actually do it.

    Just to add something about comments, to preserve the thread-reply structure, I keep drupal.comments.cid and use drupal.comments.pid instead of drupal.comments.thread as wordpress.wp_comments.comment_parent. I modified your comments query to:


    INSERT INTO wordpress.wp_comments
    (comment_ID, comment_post_ID, comment_date, comment_content, comment_parent, comment_author,
    comment_author_email, comment_author_url, comment_approved)
    SELECT DISTINCT
    cid, nid, FROM_UNIXTIME(timestamp), comment, pid, name,
    mail, homepage, ((status + 1) % 2)
    FROM drupal.comments
    ;

    Probably this helps someone.

    Now the daunting task left is to migrate the forum. Just have to decide first which forum plugin I’ll use. :)

    Again, thanks!

  15. I ran the script and got the error [ERROR in query 12] Duplicate entry '14' for key 'PRIMARY', so I made this change and the script succeeded:

    @@ -63,10 +63,9 @@
    # POSTS
    # Keeps private posts hidden.
    INSERT INTO newschools_migrated.wp_posts
    - (id, post_author, post_date, post_content, post_title, post_excerpt,
    + (post_author, post_date, post_content, post_title, post_excerpt,
    post_name, post_modified, post_type, `post_status`)
    SELECT DISTINCT
    - n.nid `id`,
    n.uid `post_author`,
    FROM_UNIXTIME(n.created) `post_date`,
    r.body `post_content`,

  16. Pingback: Migrating Drupal 6 to Wordpress 3 | Modeling Languages

  17. You rock! Thank you SO much for this post. I procrastinated on migrating the database for one site for over a year, and finally hackers made it an immediate necessity. I did need to spend some time understanding the database structures and customizing the script (for example, I used the ‘story’ node type in Drupal), but in the end, this led to success. I am very very grateful!

  18. Worked a treat. Thanks for sharing this.

    Going back to Rocky and his delete error.

    #1142 – DELETE command denied to user ‘ziggin’@’localhost’ for table

    I got the same thing before realising that you need to input the full database name and for most people on cpanel hosting accounts it would be something like.

    useraccount_wordpress and useraccount_drupal.

  19. Pingback: Migracja z drupala na wordpress « harce

  20. The downloaded script turned up several “No database selected” errors (maybe 3)

    This is because there are several insert statements where wordpress tables missing “wordpress.” prefix.

    I also encountered the Primary Key issue in the first statement. Removed the id from the select and insert as indicated… et voila!

    Thanks for this… and nice choices on the @font-face in your css.

  21. Hi,

    my site is in D7.8,slow loading, another problem is mysql database increasing, caches not clearing. Content types Basic, Book pages (health,career,beauty etc), youtube videos, image gallery (gallery formatter). Can u give me a code to migrate to WP 1.3 ?

  22. I think Drupal 6 was the version this client was running. The script doesn’t touch the original Drupal database though, so there’s no danger in trying it out, even if it doesn’t work.

  23. Ran the script and the posts are in the database – but they do not show up in wordpress. Any ideas? Also comments and categories are not migrating. Help!

  24. Hmm… it’s been a while now since I looked at this, but my first thought is that maybe the post_status field on the posts isn’t set correctly. Check the wp_posts table and make sure that on the posts you want active, that post_status is set to publish.

  25. Thanks for the scripting!

    Wasn’t prefect but got it working. One thing people might want to note is that Drupal (my version at least) uses the type “story” for posts. So you need to add that to the list in the “INSERT INTO wordpress.wp_posts…” section. In mine I just changed “blog” to “story”. But that didn’t solve everything.

    I also had to add a line to update the wordpress.wp_posts from “story” to “post”, like this:

    UPDATE wordpress.wp_posts SET post_type = ‘post’ WHERE post_type = ‘story’;

    This is the only way I could get my drupal “story” to show up in wordpress.

    LOVE IT! Thanks for all the help.

  26. Thanks a lot!
    I’m looking forward to migrate just the users (8000 so far!) with their passwords. Do you think that it is possible?

  27. I haven’t actually tried that, but I think it should work. Just remove the queries that work on tables you don’t want to migrate. Experiment and, as I suggested in the main post, be sure to work on a copy of the database first so you can start over if things go wrong.

  28. Got “ERROR 1062 (23000) at line 65: Duplicate entry ’319′ for key ‘PRIMARY’”

  29. Thanks for this, but this doesn’t allow to migrate all custom fields, featured images. Also I had a lot of problems with Category parent-child and Category-Posts relationships. However I just found gConverter from the Twitter, I need convert all components  listed here:
    http://gconverter.com/drupal-to-wordpress/ . Also it would be fine to have any solution for converting Drupal Forum to WordPress forum plugins, such as Simple:Press or bbPress.

  30. it s not working. so i went to my blog and did a new blog entry and checked in phpMyAdmin.
    entries made by script didnt had the guid field and i think it s causing the problem .
    how do i fix it .

  31. Sorry… I only know as much about Drupal as it took to make this work for myself (and, now, even less, since I haven’t touched Drupal in a long enough time to have forgotten most of what I did briefly know). Back to “use at your own risk.” If this script is helpful to you, great! If not, sorry!

  32. it s working for me now . i ran this command in sql after running the wp_posts copy code snippet .

    UPDATE `wp_posts` SET `post_type`=`post`

  33. Hi,

    Thanks for the nice script.
    I used it to migrate from drupal 6 to wordpress 3.3

    Great job.
    Dont know hoe to thank for saving loads of hours of work.

  34. Hi, im using your script to move from drupal 5 to wordpress, but no success, the url of the posts are all wrong and use the title, i’m using Pathauto in drupl so my urls are friendly one and clean.  Do you know how i may do it in to get the urls from pathauto.

    Thank you very much for your help.

    Antonio.
    error500.com.es

  35. Thanks for the script, I migrated 2 websites with no errors. I never thought that transition will be that easy!
    Good Luck

  36. Thank you for this script. It worked for me with a some tweaking, as suggested in the script notes and was a lot easier than attempting to cut and paste a whole site manually. I will likely use this again.

  37. Hi. I would like to pay someone to do this for me. I basically have a drupal site with a lot of posts that I want to convert to a wordpress site on a different server. I don’t need the side bar content or even the pages really, as I can manually replace those. But I need to get the blog posts into wordpress. There are just too many to do manually.

    How much would you (anyone who has done this successfully already and is confident) charge to do this for me?

    Thanks.
    Emanuel

  38. @Emanuel: Well, assuming that you’ve already set up the WordPress site, and we wouldn’t need to develop a WordPress theme, just do the database migration itself, it would probably take us somewhere between 2-4 hours, depending on how much customization the script would require.

    Unfortunately at the moment we don’t have the capacity to take on tasks of this scale outside of a larger project, but I am sure there are plenty of developers out there who could help you out. (Perhaps even some who have stumbled their way onto this post…?)

  39. Hi. There are probably still people out there who, like me, need to migrate an old Drupal 6 site over to WordPress.

    In case it helps, I’ve built upon Scott’s work to put together a utility that runs the various SQL scripts to migrate Drupal 6 nodes, comments and terms over into WordPress 3.5. It uses the SQL on this page with a few modifications to get it working for my own Drupal installations.

    You can grab it from here: http://anothercoffee.net/the-drupal-to-wordpress-migration-tool/

    It’s pretty basic at the moment but should get the bulk of the work done for fairly simple Drupal sites.

    HTH.

  40. This totally helped me! Thanks for sharing this, it saved me hours!

    On a related note, I had to do the images for the site I was working on, 25k images, 5.5g — not something I would do by hand. I rsynced the images off the server with -r -t to recursivley get the files/images folder to my new server and then did this:

    I ended up doing this as a one-time hack, not fit to be a plugin. Script is a new file in document_root, called by commend line, “php file.php”

    [Code sample removed by site editor -- WordPress auto-formatting would have made the code unusable as entered.]

  41. Thanks for posting this, an awesome well explained guide that’s just helped me make the transition from Drupal to WordPress. One addition that might be worth adding is with the tags.

    Bit of background. My old site had a taxonomy of tags that were essentially used as categories. On the old site, a post would have a category and a number of tags. Using the script, I get WordPress posts that have the correct categories but those categories are also listed as tags on the posts, so they’re duplicated.

    Below is an SQL snippet that _should_ delete any tags that are already listed as categories. As always, please make sure there’s backups before testing this out. Hope it’s useful.

    # Remove any tags that are duplicated as categories.
    delete from wordpress.wp_term_relationships where term_taxonomy_id in (
    SELECT tt.term_taxonomy_id
    FROM wordpress.wp_term_taxonomy tt
    INNER JOIN wordpress.wp_terms t USING (term_id)
    WHERE tt.taxonomy = ‘post_tag’
    and t.slug IN (
    select t.slug from wp_terms t
    inner join wp_term_taxonomy tt using (term_id)
    where tt.taxonomy = ‘category’
    )
    )

  42. I ran in to another problem when migrating my Drupal install. We used a complicated path which had a number of directories e.g.

    /blog/2012/08/blog-name

    the existing SQL takes care of one of the slashes but not enough, so the redirect still breaks in WordPress. I used an additional update to postname for the migrated Drupal nodes:

    UPDATE tp_wordpress.wp_posts
    SET post_name = REPLACE(post_name, ‘/’, ‘-’);

    might be worth adding that statement after the existing one as a safeguard. Hope that’s useful.

  43. I’m not having any luck installing new gallery skins.
    Everything seems to proceed with out error but I never get the option to activate the skin.

    When I do a refresh on set up skins, then I get
    multiple move errors but the skin (chocolate.zip) isn’t put in.
    Here are an example on the errors I get after doing the refresh: Failed to move file /chroot/home/website/html/wp-content/plugins/flash-album-gallery/skins/default_old to
    /chroot/home/website/html/wp-content/plugins/flagallery-skins/default_old

  44. Thank you for this very useful post! I am testing it to convert a Drupal 6.28 site to a WordPress 3.7 site and the only thing that I cannot accomplish is getting the images that are in the Drupal posts to the wordpress posts (also the featured_image in WordPress). The Drupal site is setup using content_type_[type_of_post] f.i. “content_type_publication” or “content_type_website” and so on and uses the table “files”. With a SELECT query I can gather the images and paths (this works) but I do’nt have any clue how to incorporate this in the main query.

    My SELECT query (in this example for the content_type_publication) also provides some more info about writer, and user (not important):

    SELECT t1.nid, t1.vid, t1.uid, t1.type AS category, FROM_UNIXTIME(t1.created, ‘%Y-%m-%d %T’) AS post_date, t1.title AS title, t2.field_publicationtext02_value AS post, t2.field_bron_freetext_value AS source, t3.filepath, t3.filename, t3.filemime, t3.origname, t4.field_write_value, t5.name, t5.mail, t5.picture FROM node t1
    join content_type_publication t2 on t1.vid= t2.vid
    join files t3 on t2.field_foto_publication_fid = t3.fid
    join content_field_write t4 on t1.nid = t4.nid
    join users t5 on t1.uid = t5.uid

    Can anybody set me on the right track?

  45. I’m sure there are some better tools out there now; this post was written nearly four years ago. On the other hand, I do think there’s a lot of value in knowing the underlying data structures and being aware of exactly what is being copied and where.

  46. I just want to take the time to say thanks for this script! After a few edits, I’ve been able to migrate from Drupal 6.x to WordPress 3.8.1. My users however did not migrate for some reason, but I’m in the process of figuring that out. I’m just glad to have gotten this far after no success and not wanting to take the route of a paid option.

    Thanks again! Four years later and still helpful!

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>