├── LICENSE.txt ├── README.txt ├── data.inc.php ├── display_step00.database-settings.inc.php ├── display_step01.analysis_results.inc.php ├── display_step02.set-options.inc.php ├── display_step03.migrate.inc.php ├── drupaltowordpress-custom.sql ├── drupaltowordpress.php ├── fix_duplicate_aliases.php ├── fix_duplicate_terms.php ├── fix_terms_charlength.php ├── functions_database.php ├── functions_display.php ├── functions_utility.php ├── license.html └── style.css /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2013 Another Cup of Coffee Limited 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | Drupal to WordPress database migration tool 2 | by Another Cup of Coffee Limited 3 | 4 | This tool performs the bulk of the migration from Drupal 6 to WordPress 3.5. 5 | It works pretty well with our own installations but you will likely need 6 | to make some tweaks, either to the code or to the migrated database. If you're 7 | not sure how to make the appropriate changes, Another Cup of Coffee Limited 8 | will be happy to do the work. Contact us at http://anothercoffee.net 9 | 10 | 11 | Installation and use: 12 | Copy folder to your web server and run the drupaltowordpress.php script on your browser. Follow the on-screen instructions. 13 | 14 | 15 | CAUTION: 16 | Make a backup of both your Drupal and WordPress databases before running this 17 | tool. USE IS ENTIRELY AT YOUR OWN RISK. 18 | 19 | First released 2013-05-26 by Anthony Lopez-Vito of Another Cup of Coffee Limited 20 | http://anothercoffee.net 21 | 22 | All code is released under The MIT License. 23 | Please see LICENSE.txt. 24 | 25 | 26 | Credits: 27 | 28 | * Scott Anderson of Room 34 Creative Services. 29 | The queries for migrating from Drupal to WordPress are based on a post 30 | by Room 34 in http://blog.room34.com/archives/4530 31 | 32 | * David Coveney of Interconnect IT Ltd (UK). 33 | I used UI elements of Interconnect IT's WordPress Search and Replace Tool 34 | as a starting point to create the in-house scripts on which this tool 35 | is based. 36 | http://interconnectit.com/products/search-and-replace-for-wordpress-databases/ 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /data.inc.php: -------------------------------------------------------------------------------- 1 | In data.inc

'; 14 | print "
";
 15 | print_r($_POST);
 16 | print '
'; 17 | print '
'; 18 | */ 19 | 20 | define("D2W_VERSION", "0.4"); 21 | 22 | /*** DB details ***/ 23 | // Wordpress 24 | $wp_settings_array['host'] = isset( $_POST[ 'wp_host' ] ) ? stripcslashes( $_POST[ 'wp_host' ] ) : 'localhost'; // normally localhost, but not necessarily. 25 | $wp_settings_array['user'] = isset( $_POST[ 'wp_user' ] ) ? stripcslashes( $_POST[ 'wp_user' ] ) : 'username'; // your db userid 26 | $wp_settings_array['pass'] = isset( $_POST[ 'wp_pass' ] ) ? stripcslashes( $_POST[ 'wp_pass' ] ) : 'password'; // your db password 27 | $wp_settings_array['data'] = isset( $_POST[ 'wp_data' ] ) ? stripcslashes( $_POST[ 'wp_data' ] ) : 'wordpress'; // your database 28 | $wp_settings_array['char'] = isset( $_POST[ 'wp_char' ] ) ? stripcslashes( $_POST[ 'wp_char' ] ) : ''; // your db charset 29 | // Drupal 30 | $d_settings_array['host'] = $wp_settings_array['host']; // normally localhost, but not necessarily. 31 | $d_settings_array['user'] = $wp_settings_array['user']; // your db userid 32 | $d_settings_array['pass'] = $wp_settings_array['pass']; // your db password 33 | $d_settings_array['data'] = isset( $_POST[ 'd_data' ] ) ? stripcslashes( $_POST[ 'd_data' ] ) : 'drupal'; // your database 34 | $d_settings_array['char'] = isset( $_POST[ 'd_char' ] ) ? stripcslashes( $_POST[ 'd_char' ] ) : ''; // your db charset 35 | /* 36 | $d_settings_array['host'] = isset( $_POST[ 'd_host' ] ) ? stripcslashes( $_POST[ 'd_host' ] ) : ''; // normally localhost, but not necessarily. 37 | $d_settings_array['data'] = isset( $_POST[ 'd_data' ] ) ? stripcslashes( $_POST[ 'd_data' ] ) : ''; // your database 38 | $d_settings_array['user'] = isset( $_POST[ 'd_user' ] ) ? stripcslashes( $_POST[ 'd_user' ] ) : ''; // your db userid 39 | $d_settings_array['pass'] = isset( $_POST[ 'd_pass' ] ) ? stripcslashes( $_POST[ 'd_pass' ] ) : ''; // your db password 40 | $d_settings_array['char'] = isset( $_POST[ 'd_char' ] ) ? stripcslashes( $_POST[ 'd_char' ] ) : ''; // your db charset 41 | */ 42 | 43 | /********************************************************************** 44 | * START: room34.com queries for migrating FROM Drupal TO WordPress 45 | * 46 | */ 47 | 48 | /* room34.com: Empty previous content from WordPress database. */ 49 | define("QUERY_LIST_POSTS", "SELECT * FROM ".$wp_settings_array['data'].".wp_posts;"); 50 | 51 | /* room34.com: Empty previous content from WordPress database. */ 52 | define("QUERY_TRUNCATE_WP_TABLES", "TRUNCATE TABLE ".$wp_settings_array['data'].".wp_comments;". 53 | "TRUNCATE TABLE ".$wp_settings_array['data'].".wp_links;". 54 | "TRUNCATE TABLE ".$wp_settings_array['data'].".wp_postmeta;". 55 | "TRUNCATE TABLE ".$wp_settings_array['data'].".wp_posts;". 56 | "TRUNCATE TABLE ".$wp_settings_array['data'].".wp_term_relationships;". 57 | "TRUNCATE TABLE ".$wp_settings_array['data'].".wp_term_taxonomy;". 58 | "TRUNCATE TABLE ".$wp_settings_array['data'].".wp_terms;"); 59 | 60 | /* 61 | * room34.com: If you're not bringing over multiple Drupal authors, comment out these lines and the other 62 | * author-related queries near the bottom of the script. 63 | * This assumes you're keeping the default admin user (user_id = 1) created during installation. 64 | */ 65 | define("QUERY_DELETE_WP_AUTHORS", "DELETE FROM ". 66 | $wp_settings_array['data']. 67 | ".wp_users WHERE ID > 1; DELETE FROM ". 68 | $wp_settings_array['data']. 69 | ".wp_usermeta WHERE user_id > 1;"); 70 | 71 | 72 | /* 73 | * room34.com: TAGS 74 | * Using REPLACE prevents script from breaking if Drupal contains duplicate terms. 75 | */ 76 | define("QUERY_CREATE_WP_TAGS", "REPLACE INTO ".$wp_settings_array['data'].".wp_terms ". 77 | "(term_id, name, slug, term_group) ". 78 | "SELECT DISTINCT ". 79 | "d.tid, d.name, REPLACE(LOWER(d.name), ' ', '_'), 0 ". 80 | "FROM ".$d_settings_array['data'].".term_data d ". 81 | "WHERE (1);"); // This helps eliminate spam tags from import; uncomment if necessary. 82 | // AND LENGTH(d.name) < 50 83 | 84 | /* room34.com: POST/TAG RELATIONSHIPS */ 85 | define("QUERY_SET_TERM_RELATIONSHIPS", "INSERT INTO ".$wp_settings_array['data'].".wp_term_relationships (object_id, term_taxonomy_id) ". 86 | "SELECT DISTINCT nid, tid FROM ".$d_settings_array['data'].".term_node;"); 87 | 88 | /* room34.com: Update tag counts */ 89 | define("QUERY_UPDATE_TAG_COUNTS", "UPDATE ".$wp_settings_array['data'].".wp_term_taxonomy tt ". 90 | "SET count = ( ". 91 | "SELECT COUNT(tr.object_id) ". 92 | "FROM ".$wp_settings_array['data'].".wp_term_relationships tr ". 93 | "WHERE tr.term_taxonomy_id = tt.term_taxonomy_id);"); 94 | 95 | /* 96 | * room34.com: COMMENTS 97 | * Keeps unapproved comments hidden. 98 | * Incorporates change noted here: http://www.mikesmullin.com/development/migrate-convert-import-drupal-5-to-wordpress-27/#comment-32169 99 | * 100 | * ACCL: Amended to truncate Drupal's homepage field (varchar 255) to fit 101 | * Wordpress' comment_author_url field (varchar 200) 102 | * 103 | */ 104 | define("QUERY_HIDE_ADD_COMMENTS", "INSERT INTO ".$wp_settings_array['data'].".wp_comments ". 105 | "(comment_ID, comment_post_ID, comment_date, comment_content, comment_parent, comment_author, ". 106 | "comment_author_email, comment_author_url, comment_approved) ". 107 | "SELECT DISTINCT ". 108 | "cid, nid, FROM_UNIXTIME(timestamp), comment, pid, name, ". 109 | "mail, SUBSTRING(homepage,1,200), ((status + 1) % 2) ". 110 | "FROM ".$d_settings_array['data'].".comments;"); 111 | 112 | /* room34.com: Update comments count on wp_posts table. */ 113 | define("QUERY_UPDATE_COMMENTS_COUNTS", "UPDATE ".$wp_settings_array['data'].".wp_posts ". 114 | "SET comment_count = ( ". 115 | "SELECT COUNT(comment_post_id) ". 116 | "FROM ".$wp_settings_array['data'].".wp_comments ". 117 | "WHERE ".$wp_settings_array['data'].".wp_posts.id = ".$wp_settings_array['data'].".wp_comments.comment_post_id);"); 118 | 119 | /* room34.com: Fix images in post content; uncomment if you're moving files from "files" to "wp-content/uploads". */ 120 | define("QUERY_UPDATE_FILEPATH", "UPDATE ".$wp_settings_array['data'].".wp_posts SET post_content = REPLACE(post_content, '\"/files/', '\"/wp-content/uploads/');"); 121 | 122 | /* room34.com: Fix taxonomy; http://www.mikesmullin.com/development/migrate-convert-import-tindrupal6-5-to-wordpress-27/#comment-27140 */ 123 | define("QUERY_FIX_TAXONOMY", "UPDATE IGNORE ".$wp_settings_array['data'].".wp_term_relationships, ".$wp_settings_array['data'].".wp_term_taxonomy ". 124 | "SET ".$wp_settings_array['data'].".wp_term_relationships.term_taxonomy_id = ".$wp_settings_array['data'].".wp_term_taxonomy.term_taxonomy_id ". 125 | "WHERE ".$wp_settings_array['data'].".wp_term_relationships.term_taxonomy_id = ".$wp_settings_array['data'].".wp_term_taxonomy.term_id;"); 126 | 127 | 128 | /* 129 | * Stuff below needs more testing and customizatio 130 | */ 131 | 132 | /* room34.com: AUTHORS */ 133 | define("QUERY_ADD_AUTHORS", "INSERT IGNORE INTO ".$wp_settings_array['data'].".wp_users ". 134 | "(ID, user_login, user_pass, user_nicename, user_email, ". 135 | "user_registered, user_activation_key, user_status, display_name) ". 136 | "SELECT DISTINCT ". 137 | "u.uid, u.mail, NULL, u.name, u.mail, ". 138 | "FROM_UNIXTIME(created), '', 0, u.name ". 139 | "FROM ".$d_settings_array['data'].".users u ". 140 | "INNER JOIN ".$d_settings_array['data'].".users_roles r ". 141 | "USING (uid) ". 142 | "WHERE (1);"); // Uncomment and enter any email addresses you want to exclude below. 143 | // AND u.mail NOT IN ('test@example.com') 144 | 145 | 146 | /* room34.com: Assign author permissions. 147 | * Sets all authors to "author" by default; next section can selectively promote individual authors 148 | * 149 | * ACCL: Buidling from two separate queries for troubleshooting 150 | */ 151 | define("QUERY_ASSIGN_AUTHOR_PERMISSIONS_A", "INSERT IGNORE INTO ".$wp_settings_array['data'].".wp_usermeta (user_id, meta_key, meta_value) ". 152 | "SELECT DISTINCT ". 153 | "u.uid, 'wp_capabilities', 'a:1:{s:6:\"author\";s:1:\"1\";}' ". 154 | "FROM ".$d_settings_array['data'].".users u ". 155 | "INNER JOIN ".$d_settings_array['data'].".users_roles r ". 156 | "USING (uid) ". 157 | "WHERE (1);"); // Uncomment and enter any email addresses you want to exclude below. 158 | // AND u.mail NOT IN ('test@example.com') 159 | 160 | define("QUERY_ASSIGN_AUTHOR_PERMISSIONS_B", "INSERT IGNORE INTO ".$wp_settings_array['data'].".wp_usermeta (user_id, meta_key, meta_value) ". 161 | "SELECT DISTINCT ". 162 | "u.uid, 'wp_user_level', '2' ". 163 | "FROM ".$d_settings_array['data'].".users u ". 164 | "INNER JOIN ".$d_settings_array['data'].".users_roles r ". 165 | "USING (uid) ". 166 | "WHERE (1);"); // Uncomment and enter any email addresses you want to exclude below. 167 | // AND u.mail NOT IN ('test@example.com') 168 | 169 | define("QUERY_ASSIGN_AUTHOR_PERMISSIONS", QUERY_ASSIGN_AUTHOR_PERMISSIONS_A.QUERY_ASSIGN_AUTHOR_PERMISSIONS_B); 170 | 171 | 172 | /* 173 | * room34.com: Change permissions for admins. 174 | * Add any specific user IDs to IN list to make them administrators. 175 | * User ID values are carried over from Drupal. 176 | */ 177 | define("QUERY_ASSIGN_ADMIN_PERMISSIONS", "UPDATE ".$wp_settings_array['data'].".wp_usermeta ". 178 | "SET meta_value = 'a:1:{s:13:\"administrator\";s:1:\"1\";}' ". 179 | "WHERE user_id IN (1) AND meta_key = 'wp_capabilities'; ". 180 | "UPDATE ".$wp_settings_array['data'].".wp_usermeta ". 181 | "SET meta_value = '10' ". 182 | "WHERE user_id IN (1) AND meta_key = 'wp_user_level';"); 183 | 184 | /* room34.com: Reassign post authorship. */ 185 | define("QUERY_ASSIGN_POST_AUTHORSHIP", "UPDATE ".$wp_settings_array['data'].".wp_posts ". 186 | "SET post_author = NULL ". 187 | "WHERE post_author NOT IN (SELECT DISTINCT ID FROM ".$wp_settings_array['data'].".wp_users);"); 188 | 189 | /* 190 | * room34.com: VIDEO - READ BELOW AND COMMENT OUT IF NOT APPLICABLE TO YOUR SITE 191 | * If your Drupal site uses the content_field_video table to store links to YouTube videos, 192 | * this query will insert the video URLs at the end of all relevant posts. 193 | * WordPress will automatically convert the video URLs to YouTube embed code. 194 | */ 195 | /* 196 | define("QUERY_ADD_VIDEO_URLS", "UPDATE IGNORE ".$wp_settings_array['data'].".wp_posts p, ".$d_settings_array['data'].".content_field_video v ". 197 | "SET p.post_content = CONCAT_WS('\n',post_content,v.field_video_embed) ". 198 | "WHERE p.ID = v.nid;"); 199 | */ 200 | 201 | /* 202 | * room34.com: IMAGES - READ BELOW AND COMMENT OUT IF NOT APPLICABLE TO YOUR SITE 203 | * If your Drupal site uses the content_field_image table to store images associated with posts, 204 | * but not actually referenced in the content of the posts themselves, this query 205 | * will insert the images at the top of the post. 206 | * HTML/CSS NOTE: The code applies a "".$d_settings_array['data']."_image" class to the image and places it inside a
207 | * with the "".$d_settings_array['data']."_image_wrapper" class. Add CSS to your WordPress theme as appropriate to 208 | * handle styling of these elements. The tag as written assumes you'll be copying the 209 | * Drupal "files" directory into the root level of WordPress, NOT placing it inside the 210 | * "wp-content/uploads" directory. It also relies on a properly formatted tag. 211 | * Make changes as necessary before running this script! 212 | */ 213 | /* 214 | define("QUERY_ADD_IMAGES", "UPDATE IGNORE ".$wp_settings_array['data'].".wp_posts p, ".$d_settings_array['data'].".content_field_image i, ".$d_settings_array['data'].".files f ". 215 | "SET p.post_content = ". 216 | "CONCAT( ". 217 | "CONCAT( ". 218 | "'
' ". 221 | "), ". 222 | "p.post_content ". 223 | ") ". 224 | "WHERE p.ID = i.nid ". 225 | "AND i.field_image_fid = f.fid ". 226 | "AND ( ". 227 | "f.filename LIKE '%.jpg' ". 228 | "OR f.filename LIKE '%.jpeg' ". 229 | "OR f.filename LIKE '%.png' ". 230 | "OR f.filename LIKE '%.gif');"); 231 | */ 232 | 233 | /* 234 | * room34.com: Fix post_name to remove paths. 235 | * If applicable; Drupal allows paths (i.e. slashes) in the dst field, but this breaks 236 | * WordPress URLs. If you have mod_rewrite turned on, stripping out the portion before 237 | * the final slash will allow old site links to work properly, even if the path before 238 | * the slash is different! 239 | */ 240 | /* 241 | define("QUERY_FIX_POST_NAME", "UPDATE ".$wp_settings_array['data'].".wp_posts ". 242 | "SET post_name = ". 243 | "REVERSE(SUBSTRING(REVERSE(post_name),1,LOCATE('/',REVERSE(post_name))-1));"); 244 | */ 245 | 246 | /* 247 | * room34.com: Miscellaneous clean-up. 248 | * There may be some extraneous blank spaces in your Drupal posts; use these queries 249 | * or other similar ones to strip out the undesirable tags. 250 | */ 251 | /* 252 | define("QUERY_STRIP_NBSP", "UPDATE ".$wp_settings_array['data'].".wp_posts SET post_content = REPLACE(post_content,'

 

','');"); 253 | define("QUERY_STRIP_TAGS_01", "UPDATE ".$wp_settings_array['data'].".wp_posts SET post_content = REPLACE(post_content,'

 

','');"); 254 | */ 255 | 256 | /* 257 | * room34.com: NEW PAGES - READ BELOW AND COMMENT OUT IF NOT APPLICABLE TO YOUR SITE 258 | * MUST COME LAST IN THE SCRIPT AFTER ALL OTHER QUERIES! 259 | * If your site will contain new pages, you can set up the basic structure for them here. 260 | * Once the import is complete, go into the WordPress admin and copy content from the Drupal 261 | * pages (which are set to "pending" in a query above) into the appropriate new pages. 262 | */ 263 | /* 264 | define("QUERY_SETUP_NEW_PAGE_STRUCTURE", "INSERT INTO ".$wp_settings_array['data'].".wp_posts ". 265 | "('post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_title', ". 266 | "'post_excerpt', 'post_status', 'comment_status', 'ping_status', 'post_password', ". 267 | "'post_name', 'to_ping', 'pinged', 'post_modified', 'post_modified_gmt', ". 268 | "'post_content_filtered', 'post_parent', 'guid', 'menu_order', 'post_type', ". 269 | "'post_mime_type', 'comment_count') ". 270 | "VALUES ". 271 | "(1, NOW(), NOW(), 'Page content goes here, or leave this value empty.', 'Page Title', ". 272 | "'', 'publish', 'closed', 'closed', '', ". 273 | "'slug-goes-here', '', '', NOW(), NOW(), ". 274 | "'', 0, 'http://full.url.to.page.goes.here', 1, 'page', '', 0);"); 275 | */ 276 | 277 | /* room34.com: Disable or enable comments */ 278 | /* 279 | UPDATE ".$wp_settings_array['data'].".wp_posts p SET comment_status = 'closed', ping_status = 'closed' WHERE comment_status = 'open'; 280 | UPDATE ".$wp_settings_array['data'].".wp_posts p SET comment_status = 'open', ping_status = 'open' WHERE comment_status = 'closed'; 281 | */ 282 | 283 | /* 284 | * END: room34.com queries for migrating FROM Drupal TO WordPress 285 | * 286 | **********************************************************************/ 287 | 288 | 289 | /********************************************************************** 290 | * Additional work and research by ACCL 291 | * 292 | */ 293 | 294 | /* Set site name, description and admin email */ 295 | define("QUERY_SET_SITE_NAME", "UPDATE ".$wp_settings_array['data'].".wp_options ". 296 | "SET option_value = ( SELECT value FROM ".$d_settings_array['data'].".variable WHERE name='site_name') ". 297 | "WHERE option_name = 'blogname';"); 298 | 299 | define("QUERY_SET_SITE_DESC", "UPDATE ".$wp_settings_array['data'].".wp_options ". 300 | "SET option_value = ( SELECT value FROM ".$d_settings_array['data'].".variable WHERE name='site_slogan') ". 301 | "WHERE option_name = 'blogdescription';"); 302 | 303 | define("QUERY_SET_SITE_EMAIL", "UPDATE ".$wp_settings_array['data'].".wp_options ". 304 | "SET option_value = ( SELECT value FROM ".$d_settings_array['data'].".variable WHERE name='site_mail') ". 305 | "WHERE option_name = 'admin_email';"); 306 | 307 | /****************************** 308 | * Querying Drupal 309 | * 310 | */ 311 | define("QUERY_DRUPAL_GET_POSTS", "SELECT DISTINCT ". 312 | "nid, FROM_UNIXTIME(created) post_date, title, type ". 313 | "FROM ".$d_settings_array['data'].".node;"); // Add more Drupal content types below if applicable. 314 | 315 | define("QUERY_DRUPAL_GET_NODE_TYPES", "SELECT DISTINCT type, name, description FROM ".$d_settings_array['data'].".node_type n "); // Add more Drupal content types below if applicable. 316 | 317 | define("QUERY_DRUPAL_GET_TERMS", "SELECT DISTINCT tid, name, REPLACE(LOWER(name), ' ', '_') slug, 0 ". 318 | "FROM ".$d_settings_array['data'].".term_data WHERE (1);"); 319 | 320 | /****************************** 321 | * Checks for common problems 322 | * 323 | */ 324 | 325 | /* 326 | * Can't import duplicate terms into the WordPress wp_terms table 327 | */ 328 | define("QUERY_DRUPAL_GET_DUPLICATE_TERMS", "SELECT tid, name, COUNT(*) c FROM ".$d_settings_array['data'].".term_data GROUP BY name HAVING c > 1;"); 329 | 330 | /* 331 | * WordPress term name field is set 200 chars but Drupal's is term name is 255 chars 332 | */ 333 | define("QUERY_DRUPAL_TERMS_CHARLENGTH", "SELECT tid, name FROM ".$d_settings_array['data'].".term_data WHERE CHAR_LENGTH(name) > 200;"); 334 | 335 | /* 336 | * The node ID in Drupal's node table is used to create the post ID in WordPress' wp_posts table. 337 | * 338 | * The post name in WordPress' wp_posts table is created using either 339 | * (a) the url alias (dst field) in Drupal's url_alias table OR 340 | * (b) the node id (nid) in Drupal's node table IF there is no url alias 341 | * 342 | * If there are multiple Drupal aliases with the same node ID, we will end up trying to create multiple entries 343 | * into the WordPress wp_posts table with the same wp_posts ID. This will cause integrity constraint violation 344 | * errors since wp_posts ID is a unique primary key. 345 | * 346 | * To avoid this error, we need to check for duplicate aliases 347 | * 348 | * See buildQueryCreateWPPosts() 349 | */ 350 | define("QUERY_DRUPAL_GET_DUPLICATE_ALIAS", "SELECT pid, src, COUNT(*) c FROM ".$d_settings_array['data'].".url_alias GROUP BY src HAVING c > 1;"); 351 | 352 | ?> 353 | 354 | -------------------------------------------------------------------------------- /display_step00.database-settings.inc.php: -------------------------------------------------------------------------------- 1 | 10 |

This tool performs the bulk of the migration from Drupal 6 to WordPress 3.5. It works pretty well with our own Drupal installations but you may need to make some tweaks to get things right for you, either to the code or to the migrated WordPress database. If you're not sure how to make the appropriate changes or would simply like someone else to do the work, please contact Another Cup of Coffee Limited and we'll be happy to provide a quotation.

11 | 12 |

CAUTION: Make a backup of both your Drupal and WordPress databases before running this tool. USE IS ENTIRELY AT YOUR OWN RISK.

13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 |
Database connection details
Server Name:
Username:
Password:
WordPress Database Name:
WordPressCharset:
Drupal Database Name:
Drupal Charset:
50 | 51 |

First we will analyze your Drupal database. No changes will be made.

52 | 53 |
54 | 55 | -------------------------------------------------------------------------------- /display_step01.analysis_results.inc.php: -------------------------------------------------------------------------------- 1 | 0) || 32 | ($terms_charlength_exceeded_count > 0) || 33 | ($duplicate_aliases_count > 0 )) { 34 | $abort = true; 35 | } 36 | 37 | // Show the database analysis results 38 | showHTMLHeader("Drupal database analysis", $errors); 39 | 40 | echo ""; 41 | echo ""; 42 | 43 | echo ""; 44 | echo ""; 46 | echo ""; 47 | echo "
Drupal properties
PropertyFound in Drupal
Terms$terms_count terms
Node types$node_types_count node types (". 45 | $node_types_params_list.")
Entries$posts_count entries
"; 48 | 49 | // Any potential problems? Don't show migration options 50 | if($abort) 51 | { 52 | echo ""; 53 | echo ""; 54 | 55 | // Duplicate terms? 56 | if($duplicate_terms_count > 0) { 57 | showDuplicateTermsRow($result_dup_terms, $duplicate_terms_count, $d_settings_array); 58 | } 59 | // Exceeded terms character length? 60 | if($terms_charlength_exceeded_count > 0) { 61 | showCharLengthExceededRow($result_terms_charlength_exceeded, $terms_charlength_exceeded_count, $d_settings_array); 62 | } 63 | // Duplicate aliases? 64 | if($duplicate_aliases_count > 0) { 65 | showDuplicateAliasesRow($result_dup_aliases, $duplicate_aliases_count, $d_settings_array); 66 | } 67 | echo "
Possible problems
ProblemDescription
"; 68 | } ?> 69 |
70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 |
82 | 83 |
84 |

85 |
86 | 87 | Duplicate terms

$duplicate_terms_count duplicate terms. We can't import duplicate terms into WordPress. The migration will fail if these are to be included. Clicking Fix will solve this by appending the term's tid to create a unique term.

"; 98 | ?> 99 |
100 | 101 |
102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | $row_val) { 112 | echo ""; 113 | } 114 | ?> 115 |
Duplicate terms
namecount
".$row_val['name']."".$row_val['c']."
116 |
117 | 118 | 124 | 125 | 126 | 154 | 155 | 156 | Term character length exceeded

$terms_charlength_exceeded_count terms exceed WordPress' 200 character length. The migration will fail if these are to be included. Clicking Fix will solve this by truncating the column to 200 characters. Warning: this will cause some data loss on the truncated columns.

"; 167 | ?> 168 |
169 | 170 |
171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | $row_val) { 181 | echo ""; 182 | } 183 | ?> 184 |
Term character length exceeded
tidname
".$row_val['tid']."".$row_val['name']."
185 |
186 | 187 | 193 | 194 | 222 | 223 | Duplicate aliases

$duplicate_aliases_count duplicate aliases found. Due to the way we build the WordPress post data, Drupal nodes with multiple url aliases will cause errors. More

"; 233 | ?> 234 | 235 |
236 |

The node ID in Drupal's node table is used to create the post ID in WordPress' wp_posts table.

237 |

The post name in WordPress' wp_posts table is created using either 238 |

    239 |
  • (a) the url alias (dst field) in Drupal's url_alias table OR
  • 240 |
  • (b) the node id (nid) in Drupal's node table IF there is no url alias
  • 241 |
242 |

If there are multiple Drupal aliases with the same node ID, we will end up trying to create multiple entries into the WordPress wp_posts table with the same wp_posts ID. This will cause integrity constraint violation errors since the ID field in wp_posts is a unique primary key.

243 |

To avoid this error, we need to check for duplicate aliases.

244 |
245 | 246 | 252 | 253 |
254 | 255 |
256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | $row_val) { 265 | echo ""; 266 | } 267 | ?> 268 |
Duplicate aliases
srccount
".$row_val['src']."".$row_val['c']."
269 |
270 | 271 | 277 | 278 | 306 | 307 | -------------------------------------------------------------------------------- /display_step02.set-options.inc.php: -------------------------------------------------------------------------------- 1 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 75 | 76 | 77 | 78 | 87 | 88 | 89 | 90 | 91 |
Migration options
DescriptionSetting
Delete additional authors in WordPress? (Default admin user created during installation will not be deleted.)
Drupal file directory
Permalink structure
Please select which Drupal terms will be used as WordPress categories. The remaining terms will be converted into WordPress post tags. It's best not to create too many categories.
Please select your WordPress default category.
92 | 93 |
94 | 95 | 96 | 124 | 125 | Please select which Drupal content types will be converted into WordPress posts. The unselected types will be converted in to pages."; 138 | 139 | if ($node_types_count) { 140 | foreach ($node_types_result as $row_key => $row_val) { 141 | echo "".$row_val['type']."
"; 142 | } 143 | } 144 | echo ""; 145 | } 146 | ?> -------------------------------------------------------------------------------- /display_step03.migrate.inc.php: -------------------------------------------------------------------------------- 1 | Done

"; 9 | showHTMLFooter(); 10 | } 11 | ?> 12 | -------------------------------------------------------------------------------- /drupaltowordpress-custom.sql: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Drupal to WordPress database migration tool 3 | * by Another Cup of Coffee Limited 4 | * 5 | * Version 0.2 6 | * 7 | * This script is based on the Drupal to WordPress Migration tool 8 | * drupaltowordpress-d6w35 version 3 by Another Cup of Coffee Limited. 9 | * It allows you to run a Drupal to WordPress migration without the user interface. 10 | * 11 | * This was a custom script written for the specific needs of a client but may be 12 | * useful for other migrations. I've stripped out any identifying information but 13 | * left some generic data to provide an example. 14 | * 15 | * Migration options are set directly in this script. 16 | * 17 | * 18 | * CAUTION: 19 | * Make a backup of both your Drupal and WordPress databases before running this 20 | * tool. USE IS ENTIRELY AT YOUR OWN RISK. 21 | * 22 | * First released by Anthony Lopez-Vito of Another Cup of Coffee Limited 23 | * http://anothercoffee.net 24 | * 25 | * All code is released under The MIT License. 26 | * Please see LICENSE.txt. 27 | * 28 | * Credits: Please see README.txt for credits 29 | * 30 | *******************************************************************************/ 31 | 32 | 33 | /******************** 34 | * Clear out WP tables. 35 | */ 36 | TRUNCATE TABLE wordpressdb.wp_comments; 37 | TRUNCATE TABLE wordpressdb.wp_links; 38 | TRUNCATE TABLE wordpressdb.wp_postmeta; 39 | TRUNCATE TABLE wordpressdb.wp_posts; 40 | TRUNCATE TABLE wordpressdb.wp_term_relationships; 41 | TRUNCATE TABLE wordpressdb.wp_term_taxonomy; 42 | TRUNCATE TABLE wordpressdb.wp_terms; 43 | TRUNCATE TABLE wordpressdb.wp_users; 44 | /* 45 | * For some installations, we make changes to the wp_usermeta table 46 | TRUNCATE TABLE wordpressdb.wp_usermeta; 47 | */ 48 | 49 | /******************** 50 | * Clear out working tables. 51 | * 52 | * These may have been created during a previous run of the migration queries. 53 | */ 54 | DROP TABLE IF EXISTS drupaldb.acc_duplicates; 55 | DROP TABLE IF EXISTS drupaldb.acc_news_terms; 56 | DROP TABLE IF EXISTS drupaldb.acc_tags_terms; 57 | DROP TABLE IF EXISTS drupaldb.acc_wp_tags; 58 | DROP TABLE IF EXISTS drupaldb.acc_users_post_count; 59 | DROP TABLE IF EXISTS drupaldb.acc_users_comment_count; 60 | DROP TABLE IF EXISTS drupaldb.acc_users_with_content; 61 | DROP TABLE IF EXISTS drupaldb.acc_users_post_count; 62 | 63 | 64 | /******************** 65 | * Delete unwanted vocabularies and their associated terms. 66 | */ 67 | 68 | /* Delete unwanted vocabularies */ 69 | DELETE FROM drupaldb.vocabulary WHERE vid IN (5, 7, 8, 38, 40); 70 | 71 | /* Delete terms associated with unwanted vocabularies; keep 38. 72 | * Sometimes you might want to keep some terms of unwated 73 | * vocabularies to convert into WordPress tags 74 | */ 75 | DELETE FROM drupaldb.term_data WHERE vid IN (5, 7, 8, 40); 76 | 77 | 78 | /******************** 79 | * Merge terms. 80 | * 81 | * You may want to merge terms. In this case, we are merging vid 38 82 | * to the tag vocabulary terms (vid 2). 83 | * 84 | * We will need to deal with duplicates. For example, in the Drupal 85 | * installation, 'term_a' could appear in both vid 2 and vid 38. This 86 | * will cause a problem when exporting to WordPress since we can't have 87 | * duplicate terms. 88 | */ 89 | 90 | /* Create working tables for tables both term groups. 91 | * In this case, vid 38 is a vocabulary called 'News' and 92 | * vid 2 is a vocabulary called 'Tags'. 93 | */ 94 | CREATE TABLE drupaldb.acc_news_terms AS SELECT tid, vid, name FROM drupaldb.term_data WHERE vid=38; 95 | CREATE TABLE drupaldb.acc_tags_terms AS SELECT tid, vid, name FROM drupaldb.term_data WHERE vid=2; 96 | 97 | /* Create table from duplicates */ 98 | CREATE TABLE drupaldb.acc_duplicates AS 99 | SELECT t.tid tag_tid, 100 | n.tid news_tid, 101 | t.vid tag_vid, 102 | n.vid news_vid, 103 | t.name 104 | FROM drupaldb.acc_tags_terms AS t 105 | INNER JOIN (drupaldb.acc_news_terms AS n) 106 | ON n.name=t.name; 107 | 108 | /* Append string to News terms duplicates so they won't clash during migration. 109 | * Here we used a fixed string but this won't work if you have more than two 110 | * terms with the same name. Better to generate a unique number. For example, using 111 | * the tid would make it unique since these are unique primary keys. 112 | */ 113 | UPDATE drupaldb.term_data 114 | SET name=CONCAT(name, '_01') 115 | WHERE tid IN (SELECT news_tid FROM drupaldb.acc_duplicates); 116 | 117 | /* Convert News terms to Tags */ 118 | UPDATE drupaldb.term_data SET vid=2 WHERE vid=38; 119 | 120 | 121 | /******************** 122 | * Create a table of tags. 123 | * 124 | * Exclude terms from vocabularies that we might later 125 | * convert into categories. See stage below where we create categories 126 | * and sub-categories. 127 | */ 128 | CREATE TABLE drupaldb.acc_wp_tags AS 129 | SELECT 130 | tid, 131 | vid, 132 | name 133 | FROM drupaldb.term_data 134 | WHERE vid NOT IN (37, 36, 35); 135 | 136 | 137 | /******************** 138 | * Create the tags in the WordPress database 139 | */ 140 | 141 | /* Add the tags to WordPress. 142 | * 143 | * A clean WordPress database will have term_id=1 for Uncategorized. 144 | * Use REPLACE as this may conflict with a Drupal tid 145 | */ 146 | 147 | /* ASSUMPTION: 148 | * Assuming that this point the Drupal term_data table 149 | * has been cleaned of any duplicate names as any 150 | * duplicates will be lost. 151 | */ 152 | REPLACE INTO wordpressdb.wp_terms (term_id, name, slug, term_group) 153 | SELECT 154 | d.tid, 155 | d.name, 156 | REPLACE(LOWER(d.name), ' ', '_'), 157 | d.vid 158 | FROM drupaldb.term_data d WHERE d.tid IN ( 159 | SELECT t.tid FROM drupaldb.acc_wp_tags t 160 | ); 161 | 162 | /* Convert these Drupal terms into tags */ 163 | REPLACE INTO wordpressdb.wp_term_taxonomy ( 164 | term_taxonomy_id, 165 | term_id, 166 | taxonomy, 167 | description, 168 | parent) 169 | SELECT DISTINCT 170 | d.tid, 171 | d.tid 'term_id', 172 | 'post_tag', /* This string makes them WordPress tags */ 173 | d.description 'description', 174 | 0 /* In this case, we don't give tags a parent */ 175 | FROM drupaldb.term_data d 176 | WHERE d.tid IN (SELECT t.tid FROM drupaldb.acc_wp_tags t); 177 | 178 | 179 | /******************** 180 | * Create the categories and sub-categories in the WordPress database. 181 | * 182 | * This may be unnecessary depending on your setup. 183 | */ 184 | 185 | /* Add terms associated with a Drupal vocabulary into WordPress. 186 | * 187 | * Note that in this case, these are the same vids that we 188 | * excluded from the tag table above. 189 | */ 190 | REPLACE INTO wordpressdb.wp_terms (term_id, name, slug, term_group) 191 | SELECT DISTINCT 192 | d.tid, 193 | d.name, 194 | REPLACE(LOWER(d.name), ' ', '_'), 195 | d.vid 196 | FROM drupaldb.term_data d 197 | WHERE d.vid IN (37, 36, 35); 198 | 199 | /* Convert these Drupal terms into sub-categories by setting parent */ 200 | REPLACE INTO wordpressdb.wp_term_taxonomy ( 201 | term_taxonomy_id, 202 | term_id, 203 | taxonomy, 204 | description, 205 | parent) 206 | SELECT DISTINCT 207 | d.tid, 208 | d.tid 'term_id', 209 | 'category', 210 | d.description 'description', 211 | d.vid 212 | FROM drupaldb.term_data d 213 | WHERE d.vid IN (37, 36, 35); 214 | 215 | /* Add vocabularies to the WordPress terms table. 216 | * 217 | * No need to set term_id as vocabilaries are not 218 | * directly associated with posts 219 | */ 220 | INSERT INTO wordpressdb.wp_terms (name, slug, term_group) 221 | SELECT DISTINCT 222 | v.name, 223 | REPLACE(LOWER(v.name), ' ', '_'), 224 | v.vid 225 | FROM drupaldb.vocabulary v 226 | WHERE vid IN (37, 36, 35); 227 | 228 | /* Insert Drupal vocabularies as WordPress categories */ 229 | INSERT INTO wordpressdb.wp_term_taxonomy ( 230 | term_id, 231 | taxonomy, 232 | description, 233 | parent, 234 | count) 235 | SELECT DISTINCT 236 | v.vid, 237 | 'category', /* This string makes them WordPress categories */ 238 | v.description, 239 | v.vid, 240 | 0 241 | FROM drupaldb.vocabulary v 242 | WHERE vid IN (37, 36, 35); 243 | 244 | /* Update term groups and parents. 245 | * 246 | * Before continuing with this step, we need to manually inspect the table for the 247 | * term_id for the parents inserted above. In this case, vids 37, 36, 35 were inserted 248 | * as into the wp_term_taxonomy table as term_ids 7517, 7518 and 7519. We will use them 249 | * as the parents for their respective terms. i.e. terms that formerly belonged 250 | * to the Drupal vocabulary ID 37 would now belong to the WordPress parent category 7519. 251 | */ 252 | UPDATE wordpressdb.wp_terms SET term_group=7519 WHERE term_group=37; 253 | UPDATE wordpressdb.wp_terms SET term_group=7518 WHERE term_group=36; 254 | UPDATE wordpressdb.wp_terms SET term_group=7517 WHERE term_group=35; 255 | 256 | UPDATE wordpressdb.wp_term_taxonomy SET parent=7519 WHERE parent=37; 257 | UPDATE wordpressdb.wp_term_taxonomy SET parent=7518 WHERE parent=36; 258 | UPDATE wordpressdb.wp_term_taxonomy SET parent=7517 WHERE parent=35; 259 | 260 | UPDATE wordpressdb.wp_term_taxonomy SET term_id=7519 WHERE term_taxonomy_id=7519; 261 | UPDATE wordpressdb.wp_term_taxonomy SET term_id=7518 WHERE term_taxonomy_id=7518; 262 | UPDATE wordpressdb.wp_term_taxonomy SET term_id=7517 WHERE term_taxonomy_id=7517; 263 | 264 | 265 | /******************** 266 | * Re-insert the Uncategorized term replaced previously. 267 | * 268 | * We may have replaced or deleted the Uncategorized category 269 | * during an earlier query. Re-insert it if you want an 270 | * Uncategorized category. 271 | */ 272 | INSERT INTO wordpressdb.wp_terms (name, slug, term_group) 273 | VALUES ('Uncategorized', 'uncategorized', 0); 274 | INSERT INTO wordpressdb.wp_term_taxonomy ( 275 | term_taxonomy_id, 276 | term_id, 277 | taxonomy, 278 | description, 279 | parent, 280 | count) 281 | SELECT DISTINCT 282 | t.term_id, 283 | t.term_id, 284 | 'category', 285 | t.name, 286 | 0, 287 | 0 288 | FROM wordpressdb.wp_terms t 289 | WHERE t.slug='uncategorized'; 290 | 291 | 292 | /******************** 293 | * Create WP Posts from Drupal nodes 294 | */ 295 | REPLACE INTO wordpressdb.wp_posts ( 296 | id, 297 | post_author, 298 | post_date, 299 | post_content, 300 | post_title, 301 | post_excerpt, 302 | post_name, 303 | post_modified, 304 | post_type, 305 | post_status, 306 | to_ping, 307 | pinged, 308 | post_content_filtered) 309 | SELECT DISTINCT 310 | n.nid 'id', 311 | n.uid 'post_author', 312 | DATE_ADD(FROM_UNIXTIME(0), interval n.created second) 'post_date', 313 | r.body 'post_content', 314 | n.title 'post_title', 315 | r.teaser 'post_excerpt', 316 | IF(a.dst IS NULL,n.nid, SUBSTRING_INDEX(a.dst, '/', -1)) 'post_name', 317 | DATE_ADD(FROM_UNIXTIME(0), interval n.changed second) 'post_modified', 318 | n.type 'post_type', 319 | IF(n.status = 1, 'publish', 'private') 'post_status', 320 | ' ', 321 | ' ', 322 | ' ' 323 | FROM drupaldb.node n 324 | INNER JOIN drupaldb.node_revisions r USING(vid) 325 | LEFT OUTER JOIN drupaldb.url_alias a 326 | ON a.src = CONCAT('node/', n.nid) 327 | WHERE n.type IN ( 328 | /* List the content types you want to migrate */ 329 | 'page', 330 | 'story', 331 | 'blog', 332 | 'video1', 333 | 'forum', 334 | 'comment'); 335 | 336 | /* Set the content types that should be converted into 'posts' */ 337 | UPDATE wordpressdb.wp_posts SET post_type = 'post' 338 | WHERE post_type IN ( 339 | 'page', 340 | 'story', 341 | 'blog', 342 | 'video1', 343 | 'forum', 344 | 'comment'); 345 | 346 | /* The rest of the content types are converted into pages */ 347 | UPDATE wordpressdb.wp_posts SET post_type = 'page' WHERE post_type NOT IN ('post'); 348 | 349 | 350 | /******************** 351 | * Housekeeping queries for terms 352 | */ 353 | 354 | /* Associate posts with terms */ 355 | INSERT INTO wordpressdb.wp_term_relationships ( 356 | object_id, 357 | term_taxonomy_id) 358 | SELECT DISTINCT nid, tid FROM drupaldb.term_node; 359 | 360 | /* Update tag counts */ 361 | UPDATE wordpressdb.wp_term_taxonomy tt 362 | SET count = ( SELECT COUNT(tr.object_id) 363 | FROM wordpressdb.wp_term_relationships tr 364 | WHERE tr.term_taxonomy_id = tt.term_taxonomy_id); 365 | 366 | /* Fix taxonomy 367 | * Found in room34.com queries: Fix taxonomy 368 | * http://www.mikesmullin.com/development/migrate-convert-import-drupal-5-to-wordpress-27/#comment-27140 369 | * 370 | * IS THIS NECESSARY? 371 | 372 | UPDATE IGNORE wordpressdb.wp_term_relationships, wordpressdb.wp_term_taxonomy 373 | SET wordpressdb.wp_term_relationships.term_taxonomy_id = wordpressdb.wp_term_taxonomy.term_taxonomy_id 374 | WHERE wordpressdb.wp_term_relationships.term_taxonomy_id = wordpressdb.wp_term_taxonomy.term_id; 375 | */ 376 | 377 | /* Set default category. 378 | * 379 | * Manually look in the database for the term_id of the category you want to set as 380 | * the default category. 381 | */ 382 | UPDATE wordpressdb.wp_options SET option_value='7520' WHERE option_name='default_category'; 383 | UPDATE wordpressdb.wp_term_taxonomy SET taxonomy='category' WHERE term_id=7520; 384 | 385 | 386 | /******************** 387 | * Migrate comments 388 | */ 389 | REPLACE INTO wordpressdb.wp_comments ( 390 | comment_ID, 391 | comment_post_ID, 392 | comment_date, 393 | comment_content, 394 | comment_parent, 395 | comment_author, 396 | comment_author_email, 397 | comment_author_url, 398 | comment_approved) 399 | SELECT DISTINCT 400 | cid, 401 | nid, 402 | FROM_UNIXTIME(timestamp), 403 | comment, 404 | pid, 405 | name, 406 | mail, 407 | SUBSTRING(homepage,1,200), 408 | ((status + 1) % 2) FROM drupaldb.comments; 409 | 410 | /* Update comment counts */ 411 | UPDATE wordpressdb.wp_posts 412 | SET comment_count = ( SELECT COUNT(comment_post_id) 413 | FROM wordpressdb.wp_comments 414 | WHERE wordpressdb.wp_posts.id = wordpressdb.wp_comments.comment_post_id); 415 | 416 | 417 | /******************** 418 | * Migrate Drupal Authors into WordPress 419 | * 420 | * In this case we are migrating only users who have created a post. 421 | */ 422 | 423 | /* Delete all WP Authors except for admin */ 424 | DELETE FROM wordpressdb.wp_users WHERE ID > 1; 425 | DELETE FROM wordpressdb.wp_usermeta WHERE user_id > 1; 426 | 427 | /* Set Drupal's admin password to a known value. 428 | * 429 | * This avoids hassles with trying to reset the password on 430 | * the new WordPress installation. 431 | * 432 | * UPDATE drupaldb.users set pass=md5('password') where uid = 1; 433 | * 434 | */ 435 | 436 | /* Create table of users who have created a post */ 437 | CREATE TABLE drupaldb.acc_users_post_count AS 438 | SELECT 439 | u.uid, 440 | u.name, 441 | u.mail, 442 | count(n.uid) node_count 443 | FROM drupaldb.node n 444 | INNER JOIN drupaldb.users u on n.uid = u.uid 445 | WHERE n.type IN ( 446 | /* List the post types we migrated earlier */ 447 | 'page', 448 | 'story', 449 | 'blog', 450 | 'video1', 451 | 'forum', 452 | 'comment') 453 | GROUP BY u.uid 454 | ORDER BY node_count; 455 | 456 | /* Add authors using table of users who have created a post */ 457 | INSERT IGNORE INTO wordpressdb.wp_users ( 458 | ID, 459 | user_login, 460 | user_pass, 461 | user_nicename, 462 | user_email, 463 | user_registered, 464 | user_activation_key, 465 | user_status, 466 | display_name) 467 | SELECT DISTINCT 468 | u.uid, 469 | REPLACE(LOWER(u.name), ' ', '_'), 470 | u.pass, 471 | u.name, 472 | u.mail, 473 | FROM_UNIXTIME(created), 474 | '', 475 | 0, 476 | u.name 477 | FROM drupaldb.users u 478 | WHERE u.uid IN (SELECT uid FROM drupaldb.acc_users_post_count); 479 | 480 | /* Sets all authors to "author" by default; next section can selectively promote individual authors */ 481 | INSERT IGNORE INTO wordpressdb.wp_usermeta ( 482 | user_id, 483 | meta_key, 484 | meta_value) 485 | SELECT DISTINCT 486 | u.uid, 487 | 'wp_capabilities', 488 | 'a:1:{s:6:"author";s:1:"1";}' 489 | FROM drupaldb.users u 490 | WHERE u.uid IN (SELECT uid FROM drupaldb.acc_users_post_count); 491 | 492 | INSERT IGNORE INTO wordpressdb.wp_usermeta ( 493 | user_id, 494 | meta_key, 495 | meta_value) 496 | SELECT DISTINCT 497 | u.uid, 498 | 'wp_user_level', 499 | '2' 500 | FROM drupaldb.users u 501 | WHERE u.uid IN (SELECT uid FROM drupaldb.acc_users_post_count); 502 | 503 | /* Reassign post authorship to admin for posts have no author */ 504 | UPDATE wordpressdb.wp_posts 505 | SET post_author = 1 506 | WHERE post_author NOT IN (SELECT DISTINCT ID FROM wordpressdb.wp_users); 507 | 508 | 509 | /******************** 510 | * Housekeeping for WordPress options 511 | */ 512 | 513 | /* Update filepath */ 514 | UPDATE wordpressdb.wp_posts SET post_content = REPLACE(post_content, '"/files/', '"/wp-content/uploads/'); 515 | 516 | /* Set site name */ 517 | UPDATE wordpressdb.wp_options SET option_value = ( SELECT value FROM drupaldb.variable WHERE name='site_name') WHERE option_name = 'blogname'; 518 | 519 | /* Set site description */ 520 | UPDATE wordpressdb.wp_options SET option_value = ( SELECT value FROM drupaldb.variable WHERE name='site_slogan') WHERE option_name = 'blogdescription'; 521 | 522 | /* Set site email */ 523 | UPDATE wordpressdb.wp_options SET option_value = ( SELECT value FROM drupaldb.variable WHERE name='site_mail') WHERE option_name = 'admin_email'; 524 | 525 | /* Set permalink structure */ 526 | UPDATE wordpressdb.wp_options SET option_value = '/%postname%/' WHERE option_name = 'permalink_structure'; 527 | 528 | 529 | 530 | /******************** 531 | * Create URL redirects table 532 | * 533 | * This table will not be used for the migration but may be useful if 534 | * you need to manually create redirects from Drupal aliases 535 | */ 536 | 537 | DROP TABLE IF EXISTS drupaldb.acc_redirects; 538 | CREATE TABLE drupaldb.acc_redirects AS 539 | SELECT 540 | CONCAT('drupaldb/', 541 | IF(a.dst IS NULL, 542 | CONCAT('node/', n.nid), 543 | a.dst 544 | ) 545 | ) 'old_url', 546 | IF(a.dst IS NULL,n.nid, SUBSTRING_INDEX(a.dst, '/', -1)) 'new_url', 547 | '301' redirect_code 548 | FROM drupaldb.node n 549 | INNER JOIN drupaldb.node_revisions r USING(vid) 550 | LEFT OUTER JOIN drupaldb.url_alias a 551 | ON a.src = CONCAT('node/', n.nid) 552 | WHERE n.type IN ( 553 | /* List the post types we migrated earlier */ 554 | 'page', 555 | 'story', 556 | 'blog', 557 | 'video1', 558 | 'forum', 559 | 'comment'); 560 | 561 | 562 | 563 | /******************** 564 | * Run additional query to import users who have commented 565 | * but haven't created any of the selected content types 566 | * 567 | * Running this will throw errors if you haven't manually 568 | * copied over required tables and renamed copies 569 | * to the tables names below. 570 | * 571 | * Tables requred for these queries: 572 | * acc_users_with_comments: empty copy of wp_users 573 | * acc_users_add_commenters: empty copy of wp_users 574 | * acc_wp_users: copy of wp_users from wordpress database containing users 575 | * 576 | */ 577 | 578 | /* Create table of users who have created a comment */ 579 | CREATE TABLE drupaldb.acc_users_comment_count AS 580 | SELECT 581 | u.uid, 582 | u.name, 583 | count(c.uid) comment_count 584 | FROM drupaldb.comments c 585 | INNER JOIN drupaldb.users u on c.uid = u.uid 586 | GROUP BY u.uid; 587 | 588 | INSERT IGNORE INTO drupaldb.acc_users_with_comments ( 589 | ID, 590 | user_login, 591 | user_pass, 592 | user_nicename, 593 | user_email, 594 | user_registered, 595 | user_activation_key, 596 | user_status, 597 | display_name) 598 | SELECT DISTINCT 599 | u.uid, 600 | REPLACE(LOWER(u.name), ' ', '_'), 601 | u.pass, 602 | u.name, 603 | u.mail, 604 | FROM_UNIXTIME(created), 605 | '', 606 | 0, 607 | u.name 608 | FROM drupaldb.users u 609 | WHERE u.uid IN (SELECT uid FROM drupaldb.acc_users_comment_count); 610 | 611 | /* Build a table of users who have commented but 612 | * not already added to WordPress' wp_users */ 613 | INSERT IGNORE INTO drupaldb.acc_users_add_commenters ( 614 | ID, 615 | user_login, 616 | user_pass, 617 | user_nicename, 618 | user_email, 619 | user_registered, 620 | user_activation_key, 621 | user_status, 622 | display_name) 623 | SELECT DISTINCT 624 | u.ID, 625 | u.user_login, 626 | u.user_pass, 627 | u.user_nicename, 628 | u.user_email, 629 | u.user_registered, 630 | '', 631 | 0, 632 | u.display_name 633 | FROM drupaldb.acc_users_with_comments u 634 | WHERE u.ID NOT IN (SELECT ID FROM drupaldb.acc_wp_users); 635 | 636 | /* Combine the tables 637 | * Remember to copy wp_users back into wordpress database 638 | */ 639 | INSERT IGNORE 640 | INTO drupaldb.acc_wp_users 641 | SELECT * 642 | FROM drupaldb.acc_users_add_commenters; 643 | 644 | 645 | /******************** 646 | * Additional customisations 647 | * 648 | * --- ERROR: "You do not have sufficient permissions to access this page" --- 649 | * 650 | * If you receive this error after logging in to your new WordPress installation, it's possible that the 651 | * database prefix on your new WordPress site is not set correctly. This may happen if, for example, you used 652 | * a local WordPress installation to run the migration before setting up on your live WordPress installation. 653 | * 654 | * Try running one of the queries below. 655 | * 656 | * Sources: 657 | * (1) http://wordpress.org/support/topic/you-do-not-have-sufficient-permissions-to-access-this-page-98 658 | * (2) http://stackoverflow.com/questions/13815461/you-do-not-have-sufficient-permissions-to-access-this-page-without-any-change 659 | * 660 | * OPTION 1 661 | * UPDATE wp_new_usermeta SET meta_key = REPLACE(meta_key,'oldprefix_','newprefix_'); 662 | * UPDATE wp_new_options SET option_name = REPLACE(option_name,'oldprefix_','newprefix_'); 663 | * 664 | * OPTION 2 665 | * update wp_new_usermeta set meta_key = 'newprefix_usermeta' where meta_key = 'wp_capabilities'; 666 | * update wp_new_usermeta set meta_key = 'newprefix_user_level' where meta_key = 'wp_user_level'; 667 | * update wp_new_usermeta set meta_key = 'newprefix_autosave_draft_ids' where meta_key = 'wp_autosave_draft_ids'; 668 | * update wp_new_options set option_name = 'newprefix_user_roles' where option_name = 'wp_user_roles'; 669 | * 670 | * 671 | * --- Incorrect domain in link URLs --- 672 | * 673 | * WordPress stores the domains in the database. If you performed the migration on a local or development server, 674 | * there's a good chance that the links will be incorrect after migrating to your live server. Use the Interconnect IT 675 | * utility to run a search and replace on your database. This will also correct changed database prefixes. 676 | * 677 | * https://interconnectit.com/products/search-and-replace-for-wordpress-databases/ 678 | * 679 | * 680 | * END 681 | * 682 | ********************/ -------------------------------------------------------------------------------- /drupaltowordpress.php: -------------------------------------------------------------------------------- 1 | 0 ) { 34 | if( !testDatabaseConnection($wp_settings_array, $errors) ) { 35 | $step = 4; 36 | } 37 | } 38 | 39 | switch ( $step ) { 40 | case 0: 41 | include "display_step00.database-settings.inc.php"; 42 | displayConnectionSettingsPage($wp_settings_array, $d_settings_array); 43 | break; 44 | case 1: 45 | include "display_step01.analysis_results.inc.php"; 46 | displayAnalysisResultsPage($wp_settings_array, $d_settings_array); 47 | break; 48 | case 2: 49 | include "display_step02.set-options.inc.php"; 50 | displaySetOptionsPage($wp_settings_array, $d_settings_array); 51 | break; 52 | case 3: 53 | $options_array = array(); 54 | if(isset($_POST['formDeleteAuthors']) && 55 | $_POST['formDeleteAuthors'] == 'Yes') { 56 | $options_array['deleteAuthors'] = $_POST['formDeleteAuthors']; 57 | } 58 | if(isset($_POST['formFilePath'])) { 59 | $options_array['filePath'] = $_POST['formFilePath']; 60 | } 61 | if(isset($_POST['formContentTypes'])) { 62 | $options_array['formContentTypes']=$_POST['formContentTypes']; 63 | } 64 | if(isset($_POST['formTerms'])) { 65 | $options_array['formTerms']=$_POST['formTerms']; 66 | } 67 | if(isset($_POST['formDefaultCategory'])) { 68 | $options_array['formDefaultCategory']=$_POST['formDefaultCategory']; 69 | } 70 | if(isset($_POST['formPermalinkStructure'])) { 71 | $options_array['formPermalinkStructure']=$_POST['formPermalinkStructure']; 72 | } 73 | 74 | $errors = migrate($wp_settings_array, $d_settings_array, $options_array); 75 | include "display_step03.migrate.inc.php"; 76 | displayResultsPage($wp_settings_array, $d_settings_array, $errors); 77 | break; 78 | case 4: 79 | default: 80 | /* 81 | print "
";
82 | 	print_r($_POST);
83 | 	print '
'; 84 | print '
'; 85 | print "
";
86 | 	print_r($options_array);
87 | 	print '
'; 88 | print '
'; 89 | */ 90 | showErrorPage($errors); 91 | break; 92 | } 93 | 94 | ?> 95 | 96 | -------------------------------------------------------------------------------- /fix_duplicate_aliases.php: -------------------------------------------------------------------------------- 1 | $errors, 'html_output' => $html_output)); 33 | ?> -------------------------------------------------------------------------------- /fix_duplicate_terms.php: -------------------------------------------------------------------------------- 1 | 1 ) temp ON term_data.name= temp.name;"; 19 | 20 | $result_dup_terms = runFetchFromDatabase($database_settings_array, $query_get_duplicate_terms, $errors); 21 | $duplicate_terms_count = count($result_dup_terms); 22 | 23 | 24 | if($duplicate_terms_count > 0) { 25 | foreach ($result_dup_terms as $row_key => $row_val) { 26 | $update_query = "UPDATE ".$database_settings_array['data'].".term_data ". 27 | "SET term_data.name = '".$row_val['name']."_".$row_val['tid']."' ". 28 | "WHERE tid=".$row_val['tid'].";"; 29 | 30 | $html_output = runAlterDatabase($database_settings_array, $update_query, $errors); 31 | } 32 | } 33 | 34 | print json_encode(array('result' => $errors, 'html_output' => $html_output)); 35 | ?> -------------------------------------------------------------------------------- /fix_terms_charlength.php: -------------------------------------------------------------------------------- 1 | $errors, 'html_output' => $html_output)); 23 | ?> -------------------------------------------------------------------------------- /functions_database.php: -------------------------------------------------------------------------------- 1 | $row_val) { 19 | $node_types_params_list=$node_types_params_list."'".$row_val."'"; 20 | 21 | if ($counter < $node_types_count-1) { 22 | $node_types_params_list = $node_types_params_list.", "; 23 | $counter++; 24 | } 25 | } 26 | } 27 | 28 | // Selected node types are converted to 'post' 29 | $query = "UPDATE ".$wp_settings_array['data'].".wp_posts ". 30 | "SET post_type = 'post' ". 31 | "WHERE post_type IN (".$node_types_params_list.");"; 32 | // remaining are set to 'page' 33 | $query = $query." UPDATE ".$wp_settings_array['data'].".wp_posts ". 34 | "SET post_type = 'page' ". 35 | "WHERE post_type NOT IN ('post');"; 36 | return $query; 37 | } 38 | 39 | /* 40 | * Gets nodes from Drupal and inserts them into WordPress 41 | */ 42 | function buildQueryCreateWPPosts($wp_settings_array, $d_settings_array) { 43 | 44 | // Get all available Drupal node types 45 | $result = runFetchFromDatabase($d_settings_array, QUERY_DRUPAL_GET_NODE_TYPES, $errors); 46 | $node_types_count = count($result); 47 | $node_types_params_list = buildNodeTypesParamsList($result); 48 | 49 | // Insert associated nodes as WordPress posts 50 | $query = "INSERT INTO ".$wp_settings_array['data'].".wp_posts ". 51 | "(id, post_author, post_date, post_content, post_title, post_excerpt, ". 52 | "post_name, post_modified, post_type, post_status, to_ping, pinged, post_content_filtered) ". 53 | "SELECT DISTINCT ". 54 | "n.nid 'id', ". 55 | "n.uid 'post_author', ". 56 | /* ACCL: 57 | * Original query had "FROM_UNIXTIME(n.created) 'post_date', ". 58 | * FROM_UNIXTIME cannot handle dates prior to 1970. If a post date is somehow set to a date 59 | * before 1970, this returns a NULL and will cause the query to fail. DATE_ADD solves this. 60 | */ 61 | "DATE_ADD(FROM_UNIXTIME(0), interval n.created second) 'post_date', ". 62 | "r.body 'post_content', ". 63 | "n.title 'post_title', ". 64 | "r.teaser 'post_excerpt', ". 65 | //No url alias? Use node ID. Strip directory path 66 | "IF(a.dst IS NULL, n.nid, SUBSTRING_INDEX(a.dst, '/', -1)) 'post_name', ". 67 | "DATE_ADD(FROM_UNIXTIME(0), interval n.changed second) 'post_modified', ". 68 | "n.type 'post_type', ". 69 | "IF(n.status = 1, 'publish', 'private') 'post_status', ". 70 | "' ', ". 71 | "' ', ". 72 | "' ' ". 73 | "FROM ".$d_settings_array['data'].".node n ". 74 | "INNER JOIN ".$d_settings_array['data'].".node_revisions r ". 75 | "USING(vid) ". 76 | "LEFT OUTER JOIN ".$d_settings_array['data'].".url_alias a ". 77 | "ON a.src = CONCAT('node/', n.nid) ". 78 | "WHERE n.type IN (".$node_types_params_list.");"; 79 | 80 | return $query; 81 | } 82 | 83 | 84 | /* 85 | * room34.com: Fix post type; http://www.mikesmullin.com/development/migrate-convert-import-tindrupal6-5-to-wordpress-27/#comment-17826 86 | * Add more Drupal content types below if applicable. 87 | */ 88 | function buildQuerySetWPPostType($node_types, $wp_settings_array, $d_settings_array) { 89 | $query = "UPDATE ".$wp_settings_array['data'].".wp_posts ". 90 | "SET post_type = 'post' ". 91 | "WHERE post_type IN (".$node_types.");"; 92 | return $query; 93 | } 94 | 95 | /* 96 | * room34.com: Fix images in post content; uncomment if you're moving files from "files" to "wp-content/uploads". 97 | */ 98 | function buildQueryUpdateFilepath($filepath, $wp_settings_array) { 99 | $query = "UPDATE ".$wp_settings_array['data']. 100 | ".wp_posts SET post_content = REPLACE(post_content, '\"".$filepath."', '\"/wp-content/uploads/');"; 101 | return $query; 102 | } 103 | 104 | /* 105 | * 106 | */ 107 | function buildQuerySetPermalinkStructure($wp_settings_array, $permalink_structure ) { 108 | $query = "UPDATE ".$wp_settings_array['data'].".wp_options ". 109 | "SET option_value = '$permalink_structure' ". 110 | "WHERE option_name = 'permalink_structure';"; 111 | 112 | return $query; 113 | } 114 | 115 | /* 116 | * Builds node types in comma separated list for use in queries 117 | */ 118 | function buildNodeTypesParamsList($params_array) { 119 | 120 | // Build node types list 121 | $node_types_count = count($params_array); 122 | $types_list=""; 123 | $counter = 0; 124 | 125 | if ($node_types_count) { 126 | foreach ($params_array as $row_key => $row_val) { 127 | $types_list=$types_list."'".$row_val['type']."'"; 128 | if ($counter < $node_types_count-1) { 129 | $types_list = $types_list.", "; 130 | $counter++; 131 | } 132 | } 133 | } 134 | return $types_list; 135 | } 136 | 137 | /* 138 | * room34.com: Auto-assign posts to category. 139 | * You'll need to work out your own logic to determine strings/terms to match. 140 | * Repeat this block as needed for each category you're creating. 141 | */ 142 | function buildQueryAssignPostsToCategory($wp_settings_array) { 143 | $query = "INSERT IGNORE INTO ".$wp_settings_array['data'].".wp_term_relationships (object_id, term_taxonomy_id) ". 144 | "SELECT DISTINCT p.ID AS object_id, ". 145 | "(SELECT tt.term_taxonomy_id ". 146 | "FROM ".$wp_settings_array['data'].".wp_term_taxonomy tt ". 147 | "INNER JOIN ".$wp_settings_array['data'].".wp_terms t USING (term_id) ". 148 | "WHERE t.slug = 'enter-category-slug-here' ". 149 | "AND tt.taxonomy = 'category') AS term_taxonomy_id ". 150 | "FROM ".$wp_settings_array['data'].".wp_posts p ". 151 | "WHERE p.post_content LIKE '%enter string to match here%' ". 152 | "OR p.ID IN ( ". 153 | "SELECT tr.object_id ". 154 | "FROM ".$wp_settings_array['data'].".wp_term_taxonomy tt ". 155 | "INNER JOIN ".$wp_settings_array['data'].".wp_terms t USING (term_id) ". 156 | "INNER JOIN ".$wp_settings_array['data'].".wp_term_relationships tr USING (term_taxonomy_id) ". 157 | "WHERE t.slug IN ('enter','terms','to','match','here') ". 158 | "AND tt.taxonomy = 'post_tag');"; 159 | return $query; 160 | } 161 | 162 | 163 | /* 164 | * Turns all Drupal terms into Drupal post tags 165 | * 166 | * Under WordPress, tags would be more numerous than categories. 167 | * It's more efficient to blanket convert all terms into tags, 168 | * then offer the choice to convert selected tags into categories. 169 | * 170 | * room34.com's similar query didn't work for me as it didn't correctly match 171 | * Drupal's term ID for use with WordPress. WordPress associates a post's object_id 172 | * with a tag or category via term_taxonomy_id in table wp_term_relationships. 173 | 174 | * term_taxonomy_id is the primary key of table wp_term_taxonomy. 175 | * Therefore, when their query ran, new term_taxonomy_id primary keys are created in 176 | * wp_term_taxonomy as term_ids are inserted. Thus, the term_taxonomy_id no longer 177 | * corresponds to Drupal's tid 178 | * 179 | * I fixed this by inserting Drupal's tid into term_taxonomy_id and term_taxonomy_id 180 | * 181 | */ 182 | function buildQueryConvertDrupalTermsToWPTags($wp_settings_array, $d_settings_array) { 183 | $query = "INSERT INTO ".$wp_settings_array['data'].".wp_term_taxonomy ". 184 | "(term_taxonomy_id, term_id, taxonomy, description, parent) ". 185 | "SELECT DISTINCT ". 186 | "d.tid, ". 187 | "d.tid 'term_id', ". 188 | "'post_tag', ". 189 | "d.description 'description', ". 190 | "h.parent 'parent' ". 191 | "FROM ".$d_settings_array['data'].".term_data d ". 192 | "INNER JOIN ".$d_settings_array['data'].".term_hierarchy h ". 193 | "USING(tid) ". 194 | "INNER JOIN ".$d_settings_array['data'].".term_node n ". 195 | "USING(tid) WHERE (1); "; 196 | 197 | return $query; 198 | } 199 | 200 | /* 201 | * 202 | */ 203 | function buildQuerySetCategories($wp_settings_array, $term_id_array) { 204 | $term_id_count = count($term_id_array); 205 | $term_id_list=""; 206 | $counter = 0; 207 | if ($term_id_count) { 208 | foreach ($term_id_array as $row_key => $row_val) { 209 | $term_id_list=$term_id_list."'".$row_val."'"; 210 | if ($counter < $term_id_count-1) { 211 | $term_id_list = $term_id_list.", "; 212 | $counter++; 213 | } 214 | } 215 | } 216 | 217 | $query = "UPDATE ".$wp_settings_array['data'].".wp_term_taxonomy ". 218 | "SET taxonomy='category' WHERE term_id IN (".$term_id_list.");"; 219 | 220 | // Update category counts. 221 | $query = $query." UPDATE ".$wp_settings_array['data'].".wp_term_taxonomy tt ". 222 | "SET count = ( ". 223 | "SELECT COUNT(tr.object_id) ". 224 | "FROM ".$wp_settings_array['data'].".wp_term_relationships tr ". 225 | "WHERE tr.term_taxonomy_id = tt.term_taxonomy_id);"; 226 | 227 | return $query; 228 | } 229 | 230 | /* 231 | * 232 | */ 233 | function buildQuerySetDefaultCategory($wp_settings_array, $term_id) { 234 | $query = "UPDATE ".$wp_settings_array['data'].".wp_options SET option_value='$term_id' WHERE option_name='default_category';"; 235 | 236 | // Make sure the selection is set as a category 237 | // It might already have been done by buildQuerySetCategories but do it anyway 238 | $query = $query." UPDATE ".$wp_settings_array['data'].".wp_term_taxonomy SET taxonomy='category' WHERE term_id=$term_id;"; 239 | 240 | return $query; 241 | } 242 | 243 | 244 | /* 245 | "QUERY_MIGRATE_POST_NAME", "UPDATE ".$wp_settings_array['data'].".wp_posts ". 246 | "SET post_name = ". 247 | "REVERSE(SUBSTRING(REVERSE(post_name),1,LOCATE('/',REVERSE(post_name))-1));" 248 | */ 249 | 250 | 251 | // Queries that get data from the database 252 | function runFetchFromDatabase($database_settings_array, $query, &$errors) { 253 | $result = array(); 254 | try { 255 | $dsn = "mysql:host=".$database_settings_array['host'].";dbname=".$database_settings_array['data']; 256 | $conn = new PDO($dsn, $database_settings_array['user'], $database_settings_array['pass']); 257 | $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 258 | $stmt = $conn->prepare($query); 259 | $stmt->execute(); 260 | $result = $stmt->fetchAll(); 261 | 262 | if ( count($result) ==0 ) { 263 | //$errors.= "No rows returned."; 264 | error_log($query, 0); 265 | error_log("No rows returned", 0); 266 | } 267 | } catch(PDOException $e) { 268 | $errors = $errors."
". 269 | "Query: ".$query."
". 270 | "Error: ". $e->getMessage()."
"; 271 | } 272 | return $result; 273 | } 274 | 275 | // Queries that alter the database but don't need result 276 | function runAlterDatabase($database_settings_array, $query, &$errors) { 277 | $row_count = 0; 278 | 279 | try { 280 | $dsn = "mysql:host=".$database_settings_array['host'].";dbname=".$database_settings_array['data']; 281 | $conn = new PDO($dsn, $database_settings_array['user'], $database_settings_array['pass']); 282 | $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 283 | $stmt = $conn->prepare($query); 284 | $stmt->execute(); 285 | $row_count = $stmt->rowCount(); 286 | } catch(PDOException $e) { 287 | $errors = $errors."
". 288 | "Query: ".$query."
". 289 | "Error: ". $e->getMessage()."
"; 290 | 291 | error_log("runAlterDatabase(): dsn: $dsn", 0); 292 | error_log("runAlterDatabase(): query: $query", 0); 293 | error_log("runAlterDatabase(): Error: $errors", 0); 294 | } 295 | 296 | return $row_count; 297 | } 298 | 299 | 300 | // Tests a database connection 301 | function testDatabaseConnection($database_settings_array, &$errors) { 302 | $success = false; 303 | try{ 304 | $conn = new pdo( "mysql:host=".$database_settings_array['host'].";dbname=".$database_settings_array['data'], 305 | $database_settings_array['user'], 306 | $database_settings_array['pass'], 307 | array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION)); 308 | if ($conn) { 309 | $success = true; 310 | } 311 | } 312 | catch(PDOException $e){ 313 | $errors = $errors."Error: ". $e->getMessage(); 314 | } 315 | return $success; 316 | } 317 | ?> 318 | -------------------------------------------------------------------------------- /functions_display.php: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | 15 | Drupal to WordPress migration tool 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
'; 26 | echo "

Sorry, there were errors

"; 27 | echo '

' . $errors . '

'; 28 | echo '
'; 29 | } 30 | echo "

Drupal 6 to WordPress 3.5 database migration

"; 31 | echo ""; 32 | 33 | /* 34 | 40 | */ 41 | 42 | if ($heading ) echo "

".$heading."

"; 43 | } 44 | 45 | function showHTMLFooter() 46 | { 47 | ?> 48 | 49 | 52 |
53 | 54 | 55 | 63 | 64 | 65 | 66 | Wordpress to Drupal migration tool 67 | 68 | 69 | 70 |
71 |

Drupal 6 to WordPress 3.5 database migration

72 | 73 |

Error

74 |

Sorry there was a problem. The error message is shown below.

75 | '; 78 | echo '

' . $errors . '

'; 79 | echo '
'; 80 | } 81 | 82 | showHTMLFooter(); 83 | } 84 | ?> -------------------------------------------------------------------------------- /functions_utility.php: -------------------------------------------------------------------------------- 1 | 31 | /> 92 | -------------------------------------------------------------------------------- /license.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Drupal to WordPress migration tool 5 | 6 | 7 | 8 |

Drupal 6 to WordPress 3.5 database migration

9 | 10 |

The MIT License (MIT)

11 |

Copyright (c) 2013 Another Cup of Coffee Limited

12 | 13 |

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

14 | 15 |

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

16 | 17 |

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

18 | 19 | 21 |
22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Verdana,Arial,"Bitstream Vera Sans",Arial,Helvetica; 3 | font-size: 13px; 4 | background-color: #6DBDDB; 5 | color: #444; 6 | } 7 | 8 | p { 9 | line-height: 18px; 10 | } 11 | 12 | h1, h2, h3, h4 { 13 | color: #2763A5; 14 | } 15 | 16 | h1 { 17 | font-size: 25px; 18 | } 19 | 20 | h2, h3, h4 { 21 | font-family: "Trebuchet MS","Bitstream Vera Sans",Verdana,Arial,Helvetica,sans-serif; 22 | color: #F29676; 23 | font-size: 20px; 24 | font-style: italic; 25 | font-weight: normal; 26 | } 27 | 28 | 29 | a { 30 | color: #2763A5; 31 | } 32 | 33 | #container { 34 | display:block; 35 | width: 800px; 36 | padding: 10px; 37 | margin: 0px auto; 38 | background-color: #ffffff; 39 | } 40 | 41 | #footer { 42 | clear: both; 43 | font-size: 10px; 44 | height: 15px; 45 | padding-top: 20px; 46 | margin: 40px 0 5px 0; 47 | text-align: center; 48 | border-top: 4px solid #AAD370; 49 | } 50 | 51 | #navigation { 52 | text-align: left; 53 | margin: 0 0 34px 0; 54 | border-bottom: 4px solid #AAD370; 55 | padding: 0; 56 | z-index: 30; 57 | } 58 | 59 | #navigation a { 60 | text-transform: uppercase; 61 | color: #444; 62 | } 63 | 64 | #navigation li { 65 | position: relative; 66 | margin: 0; 67 | padding: 0; 68 | list-style: none; 69 | display:inline-block; 70 | text-transform: uppercase; 71 | font-size: 12px; 72 | color: #444444; 73 | } 74 | 75 | #navigation li a { 76 | display: block; 77 | margin: 0; 78 | padding: 0px 15px; 79 | font-weight: normal; 80 | color: #444444; 81 | } 82 | 83 | #navigation li a:hover { 84 | border: none; 85 | } 86 | 87 | fieldset { 88 | border: 0 none; 89 | } 90 | 91 | .error { 92 | border: solid 1px #c00; 93 | padding: 5px; 94 | background-color: #FFEBE8; 95 | text-align: left; 96 | margin-bottom: 10px; 97 | } 98 | 99 | label { 100 | display:block; 101 | line-height: 18px; 102 | cursor: pointer; 103 | } 104 | 105 | select.multi, 106 | input.text { 107 | width: 100%; 108 | height: 100%; 109 | background-color: #fff; 110 | vertical-align: center; 111 | display:block; 112 | } 113 | 114 | select.multi { 115 | height: 144px; 116 | } 117 | 118 | input.button { 119 | } 120 | 121 | div.help { 122 | border-top: 1px dashed #999999; 123 | margin-top: 9px; 124 | } 125 | 126 | table { 127 | border-collapse: collapse; 128 | width:100%; 129 | margin: 1.5em 0.5em 0.5em 0.5em; 130 | } 131 | 132 | caption { 133 | color: #2763A5; 134 | font-weight: bold; 135 | text-align: left; 136 | margin-bottom: 0.5em; 137 | } 138 | 139 | th { 140 | text-align: left; 141 | } 142 | 143 | th, td { 144 | padding: .5em; 145 | border: 1px solid #999999; 146 | width: 100px; 147 | } 148 | 149 | table.settings_table { 150 | } 151 | 152 | th.setting, 153 | td.setting { 154 | padding: .5em; 155 | width: 35%; 156 | } 157 | 158 | th.description, 159 | td.description { 160 | padding: .5em; 161 | width: 65%; 162 | } 163 | 164 | table.analysis_table { 165 | } 166 | 167 | .analysis_table td { 168 | } 169 | 170 | th.property, 171 | td.property { 172 | padding: .5em; 173 | width: 25%; 174 | } 175 | 176 | th.problem_found, 177 | td.problem_found { 178 | padding: .5em; 179 | width: 75%; 180 | } 181 | 182 | table.problems_table { 183 | } 184 | 185 | caption.problems { 186 | color: #f00; 187 | } 188 | 189 | .problems_table td { 190 | border: 1px solid #f00; 191 | } 192 | .problems_table select { 193 | margin: 10px 0 10px 0; 194 | } 195 | 196 | th.problem_property, 197 | td.problem_property { 198 | padding: .5em; 199 | border: 1px solid #f00; 200 | width: 25%; 201 | } 202 | 203 | th.problem_found, 204 | td.problem_found { 205 | padding: .5em; 206 | border: 1px solid #f00; 207 | width: 75%; 208 | } --------------------------------------------------------------------------------