Tutorial: Writing a Wordpress to Joomla Conversion script. Part 1
The first thing when creating the converter was to decide on whether or not I would use any part of my current libraries to help expedite the process of writing the converter. Generally there is one class that I will ALWAYS use when writing code, applications or database work. The class I use is ezSQL I wont dive into how to use the class in full here as this is best saved for another tutorial. In general it simplifies the process of connecting to and interacting with your databases and also offers connections across multiple different database platforms, it’s a great class and you really should check it out.
Since this script will largely be a backend script requiring very little to no front-end GUI then we don’t really need to worry about any template classes and can just stick with some basic PHP and HTML.
We are now ready to start the planning stage for the script itself, ideally this is the first thing you do, but I tend to gather the tools I know I will need first of all and then begin planning and mapping the work itself, this is just a personal habit I should probably break, but it saves time in the long run, when you can just dive straight into a project.
Putting together the plan
Ok so you have gathered your basic tools, and you are now ready to start planning out your new script.
The first thing you will need to put on the checklist is Backups. Since you will be doing wholesale inserts into a database, if this is a fresh database, the backup is not as important, however if this is a live and active site, you are going to need a safe keeping copy before we start dumping data into it.
Next it’s time to think long and hard about what data you are going to need, and what data you are going to lose, the script we are going to put together in this tutorial will be written from the perspective of a fresh, Vanilla Joomla install, which means we will be able to fairly easily just run deletes and we can pretty much import whatever we want.
In this case I have decided that I am going to import the following into the Joomla install.
- All the wordpress Users
- Wordpress Categories
- Content assigned to the correct categories
I will not at this stage be converting pages, simply because the project I am writing this tutorial with does not have any individual pages. As such I won’t spend too much time importing them. The different structures of how Wordpress and Joomla handle both pages and content makes this slightly more tricky, but it may be worth someone picking this up as a feature to add for their own experience or use.
Starting the Code
At this stage we have a basic plan of what we are going to be doing, there was no need to dive into too much details because this all comes in the coding stage. If this were a more advanced project, then I would have spent some more time in planning it out, looking at schema’s and putting a full project plan on paper. However because we are only running a few select and insert queries then we should be able to read the schema’s and code “ad-hoc”.
Since we are going to start by importing the wordpress users, we can either delete the current user from Joomla, however it’s usually best to leave this in place, in particular if this is a live Joomla install. The only time that I do not follow this rule is when writing conversions for Forums, in this case, because of the large amount of posts and posters, it’s easier to do a one for one insert into the user table so you have their “ID’s” the way around this of course is to create a transition database and store the new ID for the user to reference during the conversion, which is what we will do in this conversion script, and clean it up afterwards.
This script is a conversion for Joomla 1.0.x and wordpress 2.6, I will follow up, if required with the changes needed to import into Joomla 1.5.x
In your local test environment (see this post for details on how to set one up) create a new folder called “wp2joomla”, within this folder create another folder called “db” (this is purely for organizational purposes). Assuming that you have mySQL databases for both your installs place the “ez_sql_core.php” and “mysql/ez_sql_mysql.php” files into the “db” folder.
Now lets start writing the code, we can do everything from a single file, though I like to create a file for each part of the import, i.e. users, content, categories, this just makes for fast referencing later. However this means it will be a good idea to have a config file that we can include into each page.
Creating the Config.php and more
Lets create a file and name it config.php and place this in the root of the wp2joomla folder.
Open up the file and put in the following code.
<?php // // Please fill in the information below information for your two databases // // Include the ezSQL class files require('db/ez_sql_core.php'); require('db/ez_sql_mysql.php'); // // wordpress Database information // $wp_db_user = 'wp db username'; $wp_db_host = 'wp db host'; $wp_db = 'wp db name'; $wp_db_pass = 'wp db password'; $wp_prefix = "wp_"; // // Joomla Database information // $j_db_user = 'Joomla db user'; $j_db_host = 'Joomla host'; $j_db = 'Joomla DB Name'; $j_db_pass = 'Joomla db password'; $j_prefix = "jos_"; // The version of Joomla, currently accepts "1.5" or 1.0 $j_version = "1.0"; ?> |
I think the code above is pretty self explanatory it simply includes in the ezSQL files and then specifies the connection information for the databases, if you wanted to write something a little easier for the user, you could wrap the config creation into a GUI or, simply ask them to specify the path to the Wordpress and Joomla configuration files and leverage the data already contained in there, however once again that is beyond the scope of this script and tutorial.
Now that we have the database connections and config file sorted out (you should put the necesary information in the config file above). We can now open up phpMyAdmin or whatever tool you use to look at mySQL database and start examining whe user fields that you are going to use, I find it easier to either extract the column headers or print them out so you can reference them as you code.
Luckily, I have done this for you in a manner of speaking and chosen the fields we are going to map for the users, you may want to add or change them, but at this point I have created a 1 for 1 of required fields.
Looking at the user tables
The first thing we need to do is look at the Joomla user table, we do this to determine which fields are required for a user to be active and correct in Joomla, the target system. When looking at the Joomla table however you need to be conscious that it is not just the “jos_user” table that handles the users information within Joomla and you will need to also import relevant data into the ACL tables in order for an account to be valid.
One hard decision to make is the user levels you are going to import everyone into Joomla with. The simple way would be to import everyone as an author or another base level, but that then means that you will need to spend time running through the relevant levels in the Administrator GUI of Joomla in order to re-map the users to the correct group.
Unfortunately the ACL of Joomla and Wordpress are not the same so there has to be a level of creative mapping and decisions made by you for this part of the script. I have mapped things out as follows.
- Wordpress: Subscriber = Joomla: Registered
- Wordpress: Author = Joomla: Author
- Wordpress: Administrator = Joomla: Super Administrator
- Wordpress: Editor = Joomla: Editor
- Wordpress: Contributor = Joomla: Publisher
The list is not perfect but at least it gives us an idea, by mapping these out it makes the code a bit more complicated as we need to do some data manipulation on each pass to ascertain the status and then import accordingly into the ACL tables in Joomla.
Writing the user conversion
Right now we have that out of the way we can start writing the conversion, rather than go through each field we are importing I will comment in the code and then explain afterwards. So here is the code you will need.
<?php error_reporting(E_ALL); // // Include The configuration file // include('config.php'); // // Create a new connection to the Wordpress Database // $db = new ezSQL_mysql($wp_db_user, $wp_db_pass, $wp_db, $wp_db_host); echo "Initialised WP DB<br/>"; // // Create a connection to the Joomla Database // $jb = new ezSQL_mysql($j_db_user, $j_db_pass, $j_db, $j_db_host); echo "Initialised JOS DB<br />"; // // This function simply checks user permissions and then returns the correct one to the Joomla // At some point we are going to need to get the ARO group id's as well but we can do that with another function // function check_user_perms($perm_string) { //I am using an strpos search, which isn't ideal, we should use a regular expression but that is for another tutorial if(strpos($perm_string, "subscriber")!==false) { //we have a subscriber $perm = "Registered"; } else if(strpos($perm_string, "author")!==false) { //we have an author $perm = "Author"; } else if(strpos($perm_string, "administrator")!==false) { $perm = "Super Administrator"; } else if(strpos($perm_string, "editor")!==false) { $perm = "Editor"; } else if(strpos($perm_string, "contributor")!==false) { $perm = "Publisher"; } return $perm; } // // This function just maps the group the user is in with the ARO group ID // function aro_acl_group_id($group) { switch ($group) { case "Registered": $g_id = "18"; break; case "Author": $g_id = "19"; break; case "Super Administrator": $g_id = "25"; break; case "Editor": $g_id = "20"; break; case "Publisher"; $g_id = "21"; } return $g_id; } // // Create a new table in your Joomla database to store the new // User ID's so that we can map posts and articles to them. We will destroy this table as one of the final steps // $jb->query( "CREATE TABLE `jos_user_map` (`wp_id` INT NOT NULL ,`jos_id` INT NOT NULL) ENGINE = MYISAM "); echo "Created User Map table<br />"; // // Select the users and their related meta details from Wordpress // The query looks complicated, but all it does is applies some easy names to each column selected for handling later, it also join the usermeta table and grabs the "capabilities" field // which tells us what permissions the user has $users = $db->get_results("select u.ID as wpID, u.user_login as username, u.user_pass as pwd, u.user_email as email, u.user_registered as reg_date, u.display_name as display_name, um.meta_key as mkey, um.meta_value as u_perm from ".$wp_prefix."users u join ".$wp_prefix."usermeta um on um.user_id = u.ID Where um.meta_key='wp_capabilities' order by u.ID"); echo "Selected WP Users and beginning insert loop"; // // Declare the $i integer simple for a count later // $i=0; // // Loop through the results array and work our magic // foreach($users as $user) { // // It's sometimes a good idea when throwing in usernames to run some checks and remove any invalid characters, or at least invalid for the new system // Luckily Joomla and Wordpress were both written with a great deal fo security in mind and as such, we don't need to write any RegExp to check // and remove anything, but if you were coming from a system like e107 it is VITAL that you remove characters like ' from usernames as not only // will this cause problems in Joomla, such characters can open you up to SQL injections. But once again we don't need it here. // // // First things first, lets see what user they are and then do the mapping. // $user_perms = check_user_perms($user->u_perm); // // Get ARO Group ID for Joomla // $aro_group = aro_acl_group_id($user_perms); // // we are ready to start importing users // Insert the basic user Profile information into the Joomla User table (you may want to change the "display name" to "wp: nice_name" both in the qp query and here $jb->query("Insert into ".$j_prefix."users (name, username, email, password, usertype, sendemail, gid) VALUES ('".$user->display_name."', '".$user->username."', '".$user->email."', '".$user->pwd."', '".$user_perms."', 0, ".$aro_group.")"); // Get the ID and store it $new_user_id = $jb->insert_id; // Use the new User ID to insert into our conversion table so that we can match ID's for the articles $jb->query("insert into jos_user_map (wp_id, jos_id) VALUES (".$user->wpID.", ".$new_user_id.")"); // Insert into the first ACL table, Core ACL $jb->query("Insert into ".$j_prefix."core_acl_aro (section_value, value, name) VALUES ('users', ".$new_user_id.", '".$user->username."')"); // get the aro_id $aro_id = $jb->insert_id; // Insert into the ACL Groups table $jb->query("insert into ".$j_prefix."core_acl_groups_aro_map (group_id, aro_id) VALUES (".$aro_group.", ".$aro_id.")"); $i++; } echo $i." records inserted<br /><br /><br />"; echo "User Import Complete"; // // TO DO: Add the link to import the next step, categories // ?> |
Before you can test whether any of the login information that was transferred is correct you will need to make some changes to your Joomla installation.
Joomla 1.0.x:
Open “includes/joomla.php”
On line: 1073 look for
if (!$valid_remember) { // Conversion to new type if ((strpos($row->password, ':') === false) && $row->password == md5($passwd)) { |
Then replace it with
if (!$valid_remember) { // Conversion to new type require_once( 'path to wp-includes/class-phppass.php'); CheckPassword($passwd, $row->password); if ((strpos($row->password, ':') === false) && $row->password == md5($passwd) || $wpcheck == true) { |
All this does is uses the Wordpress password hashing class, and checks whether the password matches when passed through, if it does, then we update the password to the new Joomla hash and log the user in. This means eventually after a set length of time you can just drop this code as all user passwords will be updated once they login. However be wary that Joomla upgrades in 1.0.x will likely remove this code and you will need to re-add it, as of now it is current with Joomla 1.0.15.
Joomla 1.5
Open plugins/authentication/joomla.php
Look for the following on line 86.
$testcrypt = JUserHelper::getCryptedPassword($credentials['password'], $salt); |
Add this after (remember to change the path to the correct path)
require_once( 'path to wp-includes/class-phppass.php'); $wp_hasher = new PasswordHash(8, TRUE); $wpcheck = $wp_hasher->CheckPassword($credentials['password'], $crypt); |
Search for (Now on line 95)
$response->status = JAUTHENTICATE_STATUS_SUCCESS; $response->error_message = ''; |
and replace with the following
$response->status = JAUTHENTICATE_STATUS_SUCCESS; $response->error_message = ''; } else if($wpcheck==true) { $email = JUser::getInstance($result->id); // Bring this in line with the rest of the system $response->email = $email->email; $response->status = JAUTHENTICATE_STATUS_SUCCESS; $response->error_message = 'Output because we are here, user '.$result->id; //Generate the new encrypted password and salt then update the database $nsalt = JUserHelper::genRandomPassword(32); $ncrypt = JUserHelper::getCryptedPassword($credentials['password'], $nsalt); $npassword = $ncrypt.':'.$nsalt; $query = 'UPDATE #__users' . ' SET password = '.$db->Quote($npassword) . ' , activation = ""' . ' WHERE id = '.$result->id; $db->setQuery($query); // Save the password if (!$db->query()) { $this->setError(JText::_('DATABASE_ERROR')); return false; } //wp update complete |
I purposely have not provided those scripts for download, for the simple fact I want you to examine the code, this is the authentication of your users and therefore a vulnerable place. Please make sure you are happy with the code above and make the changes you need before changing the Joomla files. If there are enough requests and enough people verify the above code then I will provide the files for download.
You may want to wrap a GUI interface around that, I have never seen the point when it is a simple 1 run import script that will not be seen by anyone but system administrators. When we wrap the entire code up at the end of this set of tutorials I will release the full version with a basic GUI around it for sanity sake.
In the mean time have a read and a play with that, and I will start on the next stage and tutorial, importing categories.
| 2.0 (1 person) |


Dan



Currently working as a Technical Support Manager, I have some 5+ years experience as a PHP coder, and am here to share my experiences and ideas with you all. Whilst I no longer code full time, I often write snippets to help with day to day work, that I am sure you will find useful.


Paul (Who am I?)
1 year ago
Very Interesting Dan.