This article contains all migration steps and the database conversion script.
Installation of Wordpress
- Download Wordpress and unzip this file on your webserver. Detailed instructions for installing Wordpress can be found here: Installing WordPress, install Wordpress first in the
/wordpress
directory. - First create an empty database for Wordpress, to achieve this phpMyAdmin can be used
- Open the URL to your Wordpress installation and do the initial configuration.
- After the Wordpress installation, remove the post “Hello world” permanently, also remove the “About” demo page.
Now we are ready for the next step
Converting the Drupal 6 tables into Wordpress 3 database tables
With the next steps, we are going to copy the Drupal database into the new Wordpress database
- Login into phpMyAdmin with enough rights
- Click on the old Drupal database in phpMyAdmin and select the tab “Operations“
- In the section “Copy database to:”, fill in the name of the Wordpress database
- Leave the selection on “Structure and data”
- Deselect “CREATE DATABASE before copying”, because we have already the database
- Select “Add DROP TABLE / DROP VIEW“
- Select “Add AUTO_INCREMENT value“
- Leave the other selections off
- Now press on the Go-button, to copy the database
In the next steps we are going to convert the Drupal database tables into the Wordpress tables
The database conversion script is not written by me, I found the base of the script here: http://blog.room34.com/archives/4530, I adapted it a little bit. The original converted directly from the Drupal database. I liked way of copying the database more, because I could remove some problem records from Drupal, without interfering the original database/site. In this way I had the time to complete the migration of Drupal to Wordpress, until everything was working correctly. I also filled in the posts&pages modified dates into the Wordpress database. In some other small adaptations like that.
# 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;
TRUNCATE TABLE wordpress.links;
TRUNCATE TABLE wordpress.postmeta;
TRUNCATE TABLE wordpress.posts;
TRUNCATE TABLE wordpress.term_relationships;
TRUNCATE TABLE wordpress.term_taxonomy;
TRUNCATE TABLE wordpress.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.users WHERE ID > 1;
DELETE FROM wordpress.usermeta WHERE user_id > 1;
# TAGS
# Using REPLACE prevents script from breaking if Drupal contains duplicate terms.
REPLACE INTO wordpress.terms
(term_id, `name`, slug, term_group)
SELECT DISTINCT
d.tid, d.name, REPLACE(LOWER(d.name), ' ', '_'), 0
FROM wordpress.ishk_term_data d
INNER JOIN wordpress.ishk_term_hierarchy h
USING(tid)
INNER JOIN wordpress.ishk_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.term_taxonomy
(term_id, taxonomy, description, parent)
SELECT DISTINCT
d.tid `term_id`,
'post_tag' `taxonomy`,
d.description `description`,
h.parent `parent`
FROM wordpress.ishk_term_data d
INNER JOIN wordpress.ishk_term_hierarchy h
USING(tid)
INNER JOIN wordpress.ishk_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.posts
(id, post_author, post_date, post_date_gmt, post_content, post_title, post_excerpt, post_name, post_modified, post_modified_gmt, post_type, `post_status`)
SELECT DISTINCT
n.nid `id`,
n.uid `post_author`,
FROM_UNIXTIME(n.created) `post_date`,
FROM_UNIXTIME(n.created-3600) `post_date_gmt`,
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`,
FROM_UNIXTIME(n.changed-3600) `post_modified_gmt`,
n.type `post_type`,
IF(n.status = 1, 'publish', 'private') `post_status`
FROM wordpress.ishk_node n
INNER JOIN wordpress.ishk_node_revisions r
USING(vid)
LEFT OUTER JOIN wordpress.ishk_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.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.posts SET post_status = 'pending' WHERE post_type = 'page';
# POST/TAG RELATIONSHIPS
INSERT INTO wordpress.term_relationships (object_id, term_taxonomy_id)
SELECT DISTINCT nid, tid FROM wordpress.ishk_term_node
;
# Update tag counts.
UPDATE term_taxonomy tt
SET `count` = (
SELECT COUNT(tr.object_id)
FROM 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.comments
(comment_post_ID, comment_date, comment_date_gmt, comment_content, comment_parent, comment_author, comment_author_email, comment_author_url, comment_approved)
SELECT DISTINCT
nid, FROM_UNIXTIME(timestamp), FROM_UNIXTIME(timestamp-3600), comment, thread, name,
mail, homepage, ((status + 1) % 2)
FROM wordpress.ishk_comments
;
# Update comments count on posts table.
UPDATE wordpress.posts
SET `comment_count` = (
SELECT COUNT(`comment_post_id`)
FROM wordpress.comments
WHERE wordpress.posts.`id` = wordpress.comments.`comment_post_id`
)
;
# Fix images in post content; uncomment if you're moving files from "files" to "wp-content/uploads".
# UPDATE wordpress.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.term_relationships, wordpress.term_taxonomy
SET wordpress.term_relationships.term_taxonomy_id = wordpress.term_taxonomy.term_taxonomy_id
WHERE wordpress.term_relationships.term_taxonomy_id = wordpress.term_taxonomy.term_id
;
# OPTIONAL ADDITIONS — REMOVE ALL BELOW IF NOT APPLICABLE TO YOUR CONFIGURATION
# CATEGORIES
# These are NEW categories, not in wordpress.ishk_ Add as many sets as needed.
INSERT IGNORE INTO wordpress.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.terms SET name = 'First Category' WHERE name = 'first category';
UPDATE wordpress.terms SET name = 'Second Category' WHERE name = 'second category';
UPDATE wordpress.terms SET name = 'Third Category' WHERE name = 'third category';
# Add categories to taxonomy.
INSERT INTO wordpress.term_taxonomy (term_id, taxonomy)
VALUES
((SELECT term_id FROM terms WHERE slug = 'first-category'), 'category'),
((SELECT term_id FROM terms WHERE slug = 'second-category'), 'category'),
((SELECT term_id FROM 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.term_relationships (object_id, term_taxonomy_id)
SELECT DISTINCT p.ID AS object_id,
(SELECT tt.term_taxonomy_id
FROM wordpress.term_taxonomy tt
INNER JOIN wordpress.terms t USING (term_id)
WHERE t.slug = 'enter-category-slug-here'
AND tt.taxonomy = 'category') AS term_taxonomy_id
FROM wordpress.posts p
WHERE p.post_content LIKE '%enter string to match here%'
OR p.ID IN (
SELECT tr.object_id
FROM wordpress.term_taxonomy tt
INNER JOIN wordpress.terms t USING (term_id)
INNER JOIN wordpress.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 term_taxonomy tt
SET `count` = (
SELECT COUNT(tr.object_id)
FROM term_relationships tr
WHERE tr.term_taxonomy_id = tt.term_taxonomy_id
)
;
# AUTHORS
INSERT IGNORE INTO wordpress.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 wordpress.ishk_users u
INNER JOIN wordpress.ishk_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.usermeta (user_id, meta_key, meta_value)
SELECT DISTINCT
u.uid, 'capabilities', 'a:1:{s:6:"author";s:1:"1";}'
FROM wordpress.ishk_users u
INNER JOIN wordpress.ishk_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.usermeta (user_id, meta_key, meta_value)
SELECT DISTINCT
u.uid, 'user_level', '2'
FROM wordpress.ishk_users u
INNER JOIN wordpress.ishk_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 wordpress.ishk_
UPDATE wordpress.usermeta
SET meta_value = 'a:1:{s:13:"administrator";s:1:"1";}'
WHERE user_id IN (1) AND meta_key = 'capabilities'
;
UPDATE wordpress.usermeta
SET meta_value = '10'
WHERE user_id IN (1) AND meta_key = 'user_level'
;
# Reassign post authorship.
UPDATE wordpress.posts
SET post_author = NULL
WHERE post_author NOT IN (SELECT DISTINCT ID FROM wordpress.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.posts p, wordpress.ishk_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 qb102200_arjans_logboek.posts p, qb102200_arjans_logboek.ishk_content_field_image i, qb102200_arjans_logboek.ishk_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.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.posts
SET post_content = REPLACE(post_content,'
','')
;
UPDATE wordpress.posts
SET post_content = REPLACE(post_content,'
','')
;
# 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.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)
;
To run the conversion script, you need to perform the following steps:
- Login into phpMyAdmin with enough rights
- Click on the new Wordpress database
- Click on the tab “SQL“
- Select the above script and copy it (Ctrl-C)
- Open your favorite editor and paste (Ctrl-V) the script in the editor
- Comment out VIDEO and IMAGE if applicable
- Select the complete script in the editor (Ctrl-A) and past it in the “SQL” tab
- Press the Go-button to start the conversion
- It is possible to re-run the script more than once in case of problems.
I had the following problems with the script:
- 4 of the 200 posts had a key error message while running the script. I simply removed these records, it is possible to re-run the script over and over until it is successfully finished.
- I copied the 4 posts by hand from my old Drupal 6 website into the new Wordpress 3.01 website
- I commented out the VIDEO and IMAGE part
- The script does not support CCK