Following years of frustratingly hard-to-reach, disinterested and/or incompetent and needlessly expensive Drupal developers we have set up an affordable commercial service for Small and Medium sized Enterprise (SME) decision-makers who rely on Drupal to support their business, like us. So, if you need help to migrate your Drupal 6 users into a Drupal 7, 8, or 9 System, or any other form of Drupal Wizardry, feel free to contact us.
Our new division, Drupal Wizard provides support for all versions of Drupal.
We offer the following services to serve the unique and evolving needs of your business:
- 24/7 Emergency Support
- Low-Cost Annual Support Packages
- Drupal Module Integration and Development
- Linux and Drupal Scheduled Systems Maintenance
How to Migrate Users from Drupal 6 to Drupal 7 (With Maximum Data Preservation)
Migrating user information across a major Drupal boundary (like 6 to 7) is a major hassle because of the state of the technology. At the time of the release of the development and release of Drupal 7, the message was "Just re-implement your website", obviously a big deal for sites that have hundreds of products and thousands of users like us.So, we just didn't upgrade...for years...
Now, because we want our customers to be able to use their mobile phone, we have decided to upgrade to the next version of Drupal up from our current version in the expectation that after over a half decade of bug-testing and user contributions, we have a good chance to quickly move from Drupal 6 to Drupal 7 - and this has been largely true.
The initial site development took about a week. Loading hundreds of products into the new site, installing tweaks and performing optimizations have taken another two weeks or so of work.
The good news? In less than a month our new site (mobile.holisticpethelp.com) is live and doing daily business with new customers that we are attracting via Social Media advertisements. So far, so delighted.
We plan to run the legacy site in parallel on our internal network for some time (in a container - the subject of another, future post) so we can go ahead and access it for reference purposes, but we do not intend to allow our customers to go back to the site once the mobile site is production ready.
With a newly implemented and modernized Mobile-ready website, we want to bring in our legacy users from our old Drupal 6 site, ideally in such a way that they supply their username and password to the new site and simply see an updated interface with all of their information migrated over.
This problem comes with the following distinct challenges:
A) Migrate as much user information as possible from the Drupal 6 System into the Drupal 7 System, without messing up what is already in there.
B) Migrate as much address information as possible from the Drupal 6 System into the Drupal 7 System, without messing up what is already in there
C) Migrate as much order information as possible from the Drupal 6 System into the Drupal 7 System, without messing up what is already in there
E) Redirect all incoming traffic going to the old website to the new website
Migrating user information is possible between Drupal 6 and Drupal 7, but not something that anyone other than a fairly experienced developer could do successfully.
To accomplish this, the project lead needs to understand how Linux, PHP and SQL works, as well as a fairly intimate understanding of the Drupal System as it evolved over time.
These are definitely not skills that your typical website owner is likely to possess, unless they were a developer first, like we are.
Anyways, after some investigation under the hood, we have discovered that a lot of user information can be carefully migrated (Name, Email, Id) but other information simply cannot be (Password).
Furthermore, we have determined that some information is Critical, some is Important, and some is Optional, with another set of information being Irrelevant.
The TEST
Before going all crazy with migrating Drupal 6 users to Drupal 7 (with data), I thought it would be prudent to move one user across manually, to see if introducing data into Drupal from the back-end would break anything.So, let's experiment with moving one identity between the two sites:
> select name from users where name like "Graham Leach";
+--------------+
| name |
+--------------+
| GL |
+--------------+
OK, we have an identity in Drupal 6 that needs to be migrated to Drupal 7.
Let's try to move this identity into Drupal 7 using SQL and see what happens.
Let's try to move this identity into Drupal 7 using SQL and see what happens.
>select * from drupal_users where name like "GL%";
There's a lot of information in this row, so let's take a shot at decoding it, shall we:
First of all, here's the table schema for the users table in the Drupal 6 System:
+------------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------------+------------------+------+-----+---------+----------------+
| uid | int(10) unsigned | NO | PRI | NULL | auto_increment |
| name | varchar(60) | NO | UNI | | |
| pass | varchar(32) | NO | | | |
| mail | varchar(64) | YES | MUL | | |
| mode | tinyint(4) | NO | | 0 | |
| sort | tinyint(4) | YES | | 0 | |
| threshold | tinyint(4) | YES | | 0 | |
| theme | varchar(255) | NO | | | |
| signature | varchar(255) | NO | | | |
| signature_format | smallint(6) | NO | | 0 | |
| created | int(11) | NO | MUL | 0 | |
| access | int(11) | NO | MUL | 0 | |
| login | int(11) | NO | | 0 | |
| status | tinyint(4) | NO | | 0 | |
| timezone | varchar(8) | YES | | NULL | |
| language | varchar(12) | NO | | | |
| picture | varchar(255) | NO | | | |
| init | varchar(64) | YES | | | |
| data | longtext | YES | | NULL | |
| timezone_name | varchar(50) | NO | | | |
+------------------+------------------+------+-----+---------+----------------+
Here's a field-by-field examination of the Drupal 6 Sytem users table:
uid
Importance: Critical
Meaning: The unique identifier of this identity in the Drupal 6 System
Note: This value cannot clash with any existing uid in the Drupal 7 System
DrupalAPI: Primary Key: Unique user ID
Importance: Critical
Meaning: The common name of the user, what they use to log in as their "Username"
Note: This value cannot clash with any existing name in the Drupal 7 System
DrupalAPI: Unique user name
Importance: Critical
Meaning: The password the User supplies to log into the Drupal System
Note: Unfortunately, this value cannot be migrated due to a password philosophy
change that occurred from Drupal 6 (MD5) to Drupal 7 (SHA512)
DrupalAPI: User's password (md5 hash)
Importance: Critical
Meaning: The current email of the User
Note: This value is implicated in many verification and notification processes
DrupalAPI: User's email address
Importance: Irrelevant
Meaning:
Note:
DrupalAPI: Per-user comment display mode (threaded vs. flat), used
by the {comment} module
by the {comment} module
Importance: Irrelevant
Meaning:
Note:
DrupalAPI: Per-user comment sort order (newest vs. oldest first),
used by the {comment} module
used by the {comment} module
Importance: Irrelevant
Meaning:
Note:
DrupalAPI: Previously used by the {comment} module for per-user
preferences; no longer used.',
preferences; no longer used.',
Importance: Irrelevant
Meaning:
Note:
DrupalAPI: User's default theme
Importance: Irrelevant
Meaning:
Note:
DrupalAPI: User's signature
Importance: irrelevant
Meaning:
Note:
DrupalAPI: The {filter_formats}.format of the signature
Importance: Important
Meaning: The UNIX-formatted creation date of this account
Note:
DrupalAPI: Timestamp for when user was created
Importance: Important
Meaning: The UNIX-formatted last access date of this account
Note:
DrupalAPI: Timestamp for previous time user accessed the site
Importance: Important
Meaning: The UNIX-formatted last login of this account
Note: This seems redundant, because you can infer it from the value of access
DrupalAPI: Timestamp for user's last login
Importance: Critical
Format: Identical
Meaning: Whether or not this account is enabled or disabled
Note:
DrupalAPI: Whether the user is active(1) or blocked(0)
Importance: Important
Meaning:
Note:
DrupalAPI: User's timezone
Importance: Critical
Meaning: The preferred language context of the client
Note: in our case the VAST MAJORITY of our customers prefer Traditional Chinese
(zh-hant).
DrupalAPI: User's default language
Importance: Irrelevant
Meaning:
Note:
DrupalAPI: Path to the user's uploaded picture
Importance: Important
Meaning: This is the original email address used to initially activate the account
Note: This might be used as a "fallback" email address in the event that the
current email address (mail) somehow goes bad. Maybe this is for
fault tolerance, maybe not.
DrupalAPI: Email address used for initial account creation
Importance: Irrelevant
Meaning: This was originally where meta-data about the user was held. Deprecated.
Note: A legacy value that should not be used in the future, it used to hold a bunch
of information about the user (shipping information, for example) that now
should be stored elsewhere.
DrupalAPI: A serialized array of name value pairs that are related
to the user. Any form values posted during user edit are
stored and are loaded into a $user object during
user_load(). Use of this field is discouraged and
it will likely disappear in a future version
of Drupal
to the user. Any form values posted during user edit are
stored and are loaded into a $user object during
user_load(). Use of this field is discouraged and
it will likely disappear in a future version
of Drupal
Importance: Important
Meaning:
Note:
DrupalAPI: NOT PRESENT IN API
Curious, I took a look at timezone_name and found that it was blank throughout the users table. Besides that, I think that it should not be in the users table. This kind of information belongs in a lookup table.
Now that we know what the columns mean, let's map the information provided by the SELECT statement, to see the disposition of the data when mapped onto the schema:
2769 | uid
GL | name
c62d929e7b7e7b6165923a5dfc60cb56 | pass
graham@leach.com | mail
0 | mode
0 | sort
0 | threshold
| theme
| signature
0 | signature_format
1442730945 | created
1493677257 | access
1493677256 | login
1 | status
NULL | timezone
en | language
| picture
graham.r.leach@gmail.com | init
| data
| timezone_name
Password Problems
The first obstacle I hit was the fact that the password scheme has been changed between the Drupal 6 System (MD5) and the Drupal 7 System (SHA512). This means that transporting the password over between the two systems is impossible, so I could never have just pushed data into the Drupal 7 System and then logged in as I would have normally with the pre-existing Drupal 6 System.
This is because of the way passwords work in Drupal:
1. Get username & password
2. Check for username in users table, if found continue, if missing abort
3. Perform SHA512 operation on supplied password ("hash"), compare to stored password
4. If they match, let the user in, if not abort.
Luckily, I know how to spur a user to refresh their password using the One Time Password Module, so this mechanism can be used to reset the missing password from the Drupal 7 System Administrative Interface (People). This process is explained in my post entitled:
Drupal 7 / Ubercart 7 - Bulk Password Refresh with One Time Login Module
So the final challenge was to create a SQL statement that the Drupal 7 System database server would find acceptable, containing as much of the information as possible drawn from the Drupal 6 System database server.
OK, so we now know what matters and what is coming out of the Drupal 6 System users table, but like a lock & key, the information coming in to the Drupal 7 System users table needs to conform to its format, or there will be errors.
Here's the format of the Drupal 7 users table:
INSERT INTO users
(
uid,
name,
pass,
mail,
theme,
signature,
signature_format,
created,
access,
login,
status,
timezone,
language,
picture,
init,
data,
uuid
)
VALUES
(
"2769", /* uid - critical */
"GL", /* name - critical */
"", /* pass - impossible */
"graham@leach.com", /* mail - critical */
"", /* theme - irrelevant */
"", /* signature - irrelevant */
"0", /* signature_format - irrelevant */
"1442730945", /* created - important */
"1493677257", /* access - important */
"1493677256", /* login - important */
"1", /* status - critical */
"NULL", /* timezone - important */
"en", /* language - critical */
"", /* picture - irrelevant */
"graham.r.leach@gmail.com", /* init - important */
"", /* data - irrelevant */
"" /* UUID - important? */
);
Potential trouble ahead there!
This is a new field and, apparently, not part of the official API specification.
Consequently, this field makes me very nervous because I do not know where it comes from, how it works or how it is used in the Drupal 7 System. UUID's are normally utilized to uniquely identify entities in the Drupal 7 System, and used for internal matters in the Drupal System. Curious, I took a look at other entries in the table to see what was already being put in there. Every row had a UUID assigned to it.
Here's an example of a UUID I found for a user created within the Drupal 7 System:
f4539a1f-9fbb-452c-88fd-198442815002
As it turns out, UUID have several version(s), so the first step is to figure out what version of UUID this is, so we can provide a valid UUID to the Drupal system
OK, so we now know what matters and what is coming out of the Drupal 6 System users table, but like a lock & key, the information coming in to the Drupal 7 System users table needs to conform to its format, or there will be errors.
Here's the format of the Drupal 7 users table:
> show columns from users;
+------------------+------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------------+------------------+------+-----+---------+-------+
| uid | int(10) unsigned | NO | PRI | 0 | |
| name | varchar(60) | NO | UNI | | |
| pass | varchar(128) | NO | | | |
| mail | varchar(254) | YES | MUL | | |
| theme | varchar(255) | NO | | | |
| signature | varchar(255) | NO | | | |
| signature_format | varchar(255) | YES | | NULL | |
| created | int(11) | NO | MUL | 0 | |
| access | int(11) | NO | MUL | 0 | |
| login | int(11) | NO | | 0 | |
| status | tinyint(4) | NO | | 0 | |
| timezone | varchar(32) | YES | | NULL | |
| language | varchar(12) | NO | | | |
| picture | int(11) | NO | MUL | 0 | |
| init | varchar(254) | YES | | | |
| data | longblob | YES | | NULL | |
| uuid | char(36) | NO | MUL | | |
+------------------+------------------+------+-----+---------+-------+
Here's a field-by-field examination of the Drupal 7 Sytem users table:
uid
Importance: Critical
Format: Identical
Meaning: The unique identifier of this identity in the Drupal 7 System
Note: This value cannot clash with any incoming uid from the Drupal 6 System
DrupalAPI: Primary Key: Unique user ID
name
Importance: Critical
Format: Identical
Meaning: The common name of the user, what they use to log in as their "Username"
Note: This value cannot clash with any incoming name from the Drupal 6 System
pass
Importance: Critical
Format: DIFFERENT [varchar(32) -> varchar(128)]
Meaning: The password the User supplies to log into the Drupal System
Note: In the Drupal 7 System this is now a SHA512 value
mail
Importance: Critical
Format: DIFFERENT [varchar(64) -> varchar(254)]
Meaning: The current email of the User
Note: This value is implicated in many verification and notification processes
mode
Importance: Irrelevant
Format: NO LONGER PRESENT
Meaning:
Note:
sort
Importance: Irrelevant
Format: NO LONGER PRESENT
Meaning:
Note:
threshold
Importance: Irrelevant
Format: NO LONGER PRESENT
Meaning:
Note:
theme
Importance: Irrelevant
Format: Identical
Meaning:
Note:
signature
Importance: Irrelevant
Format: Identical
Meaning:
Note:
signature_format
Importance: irrelevant
Format: DIFFERENT [smallint(6) -> varchar(255)]
Meaning:
Note:
created
Importance: Important
Format: Identical
Meaning: The UNIX-formatted creation date of this account
Note:
access
Importance: Important
Format: Identical
Meaning: The UNIX-formatted last access date of this account
Note:
login
Importance: Important
Format: Identical
Meaning: The UNIX-formatted last login of this account
Note: This seems redundant, because you can infer it from the value of access
status
Importance: Critical
Meaning: Whether or not this account is enabled or disabled
Note:
timezone
Importance: Important
Format: DIFFERENT [varchar(8) -> varchar(32)]
Meaning:
Note:
language
Importance: Critical
Format: Identical
Meaning: The preferred language context of the client
Note: in our case the VAST MAJORITY of our customers prefer Traditional Chinese
(zh-hant).
picture
Importance: Irrelevant
Format: DIFFERENT [varchar(255) -> int(11)]
Meaning:
Note:
init
Importance: Important
Format: DIFFERENT [varchar(64) -> varchar(254)]
Meaning: This is the original email address used to initially activate the account
Note: This might be used as a "fallback" email address in the event that the
current email address (mail) somehow goes bad. Maybe this is for
fault tolerance, maybe not.
data
Importance: Irrelevant
Format: DIFFERENT [longtext -> longblob]
Meaning: This was originally where meta-data about the user was held. Deprecated.
Note: A legacy value that should not be used in the future, it used to hold a bunch
of information about the user (shipping information, for example) that now
should be stored elsewhere.
DrupalAPI: A serialized array of name value pairs that are related
uuid
Importance: Critical
Format: NEW [char(36)]
Meaning: Universally Unique ID
Note: This is normally a generated value, so we need to look into this more closely.
DrupalAPI: NOT PRESENT IN API
It also looked to me like the Drupal 6 System database had a bunch of extra fields that the Drupal 7 System database either doesn't need, or needs to be stored elsewhere than the users table. This is a sign of the growing pains that Drupal experienced as it scaled up and realized that it needed to segregate certain types of data (longblob, for example) away from being stored in the users table. This story is not over because I see fields in the Drupal 7 System database schema that really shouldn't be there.
| mode | tinyint(4) | NO | | 0 | |
| sort | tinyint(4) | YES | | 0 | |
| threshold | tinyint(4) | YES | | 0 | |
Here's the SQL statement I wrote:+------------------+------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------------+------------------+------+-----+---------+-------+
| uid | int(10) unsigned | NO | PRI | 0 | |
| name | varchar(60) | NO | UNI | | |
| pass | varchar(128) | NO | | | |
| mail | varchar(254) | YES | MUL | | |
| theme | varchar(255) | NO | | | |
| signature | varchar(255) | NO | | | |
| signature_format | varchar(255) | YES | | NULL | |
| created | int(11) | NO | MUL | 0 | |
| access | int(11) | NO | MUL | 0 | |
| login | int(11) | NO | | 0 | |
| status | tinyint(4) | NO | | 0 | |
| timezone | varchar(32) | YES | | NULL | |
| language | varchar(12) | NO | | | |
| picture | int(11) | NO | MUL | 0 | |
| init | varchar(254) | YES | | | |
| data | longblob | YES | | NULL | |
| uuid | char(36) | NO | MUL | | |
+------------------+------------------+------+-----+---------+-------+
Here's a field-by-field examination of the Drupal 7 Sytem users table:
Importance: Critical
Format: Identical
Meaning: The unique identifier of this identity in the Drupal 7 System
Note: This value cannot clash with any incoming uid from the Drupal 6 System
DrupalAPI: Primary Key: Unique user ID
Importance: Critical
Format: Identical
Meaning: The common name of the user, what they use to log in as their "Username"
Note: This value cannot clash with any incoming name from the Drupal 6 System
DrupalAPI: Unique user name
Importance: Critical
Format: DIFFERENT [varchar(32) -> varchar(128)]
Meaning: The password the User supplies to log into the Drupal System
Note: In the Drupal 7 System this is now a SHA512 value
DrupalAPI: User's password (SHA512 hash)
Importance: Critical
Format: DIFFERENT [varchar(64) -> varchar(254)]
Meaning: The current email of the User
Note: This value is implicated in many verification and notification processes
DrupalAPI: User's email address
Importance: Irrelevant
Format: NO LONGER PRESENT
Meaning:
Note:
DrupalAPI: Per-user comment display mode (threaded vs. flat), used
by the {comment} module
by the {comment} module
Importance: Irrelevant
Format: NO LONGER PRESENT
Meaning:
Note:
DrupalAPI: Per-user comment sort order (newest vs. oldest first),
used by the {comment} module
used by the {comment} module
Importance: Irrelevant
Format: NO LONGER PRESENT
Meaning:
Note:
DrupalAPI: Previously used by the {comment} module for per-user
preferences; no longer used.',
preferences; no longer used.',
Importance: Irrelevant
Format: Identical
Meaning:
Note:
DrupalAPI: User's default theme
Importance: Irrelevant
Format: Identical
Meaning:
Note:
DrupalAPI: User's signature
Importance: irrelevant
Format: DIFFERENT [smallint(6) -> varchar(255)]
Meaning:
Note:
DrupalAPI: The {filter_formats}.format of the signature
Importance: Important
Format: Identical
Meaning: The UNIX-formatted creation date of this account
Note:
DrupalAPI: Timestamp for when user was created
Importance: Important
Format: Identical
Meaning: The UNIX-formatted last access date of this account
Note:
DrupalAPI: Timestamp for previous time user accessed the site
Importance: Important
Format: Identical
Meaning: The UNIX-formatted last login of this account
Note: This seems redundant, because you can infer it from the value of access
DrupalAPI: Timestamp for user's last login
Importance: Critical
Meaning: Whether or not this account is enabled or disabled
Note:
DrupalAPI: Whether the user is active(1) or blocked(0)
Importance: Important
Format: DIFFERENT [varchar(8) -> varchar(32)]
Meaning:
Note:
DrupalAPI: User's timezone
Importance: Critical
Format: Identical
Meaning: The preferred language context of the client
Note: in our case the VAST MAJORITY of our customers prefer Traditional Chinese
(zh-hant).
DrupalAPI: User's default language
Importance: Irrelevant
Format: DIFFERENT [varchar(255) -> int(11)]
Meaning:
Note:
DrupalAPI: Path to the user's uploaded picture
Importance: Important
Format: DIFFERENT [varchar(64) -> varchar(254)]
Meaning: This is the original email address used to initially activate the account
Note: This might be used as a "fallback" email address in the event that the
current email address (mail) somehow goes bad. Maybe this is for
fault tolerance, maybe not.
DrupalAPI: Email address used for initial account creation
Importance: Irrelevant
Format: DIFFERENT [longtext -> longblob]
Meaning: This was originally where meta-data about the user was held. Deprecated.
Note: A legacy value that should not be used in the future, it used to hold a bunch
of information about the user (shipping information, for example) that now
should be stored elsewhere.
DrupalAPI: A serialized array of name value pairs that are related
to the user. Any form values posted during user edit are
stored and are loaded into a $user object during
user_load(). Use of this field is discouraged and
it will likely disappear in a future version
of Drupal
uuid
Importance: Critical
Format: NEW [char(36)]
Meaning: Universally Unique ID
Note: This is normally a generated value, so we need to look into this more closely.
DrupalAPI: NOT PRESENT IN API
It also looked to me like the Drupal 6 System database had a bunch of extra fields that the Drupal 7 System database either doesn't need, or needs to be stored elsewhere than the users table. This is a sign of the growing pains that Drupal experienced as it scaled up and realized that it needed to segregate certain types of data (longblob, for example) away from being stored in the users table. This story is not over because I see fields in the Drupal 7 System database schema that really shouldn't be there.
| mode | tinyint(4) | NO | | 0 | |
| sort | tinyint(4) | YES | | 0 | |
| threshold | tinyint(4) | YES | | 0 | |
N.B. For those interested in database design, look into Third Normal Form database design, which I studied in 1995 and which still serves me well, even up to today.
A TEST With Hand Crafted SQL
Using the information I had gathered about the Drupal 6 System users table schma and the Drupal 7 System users table schema, I was able to hand-craft a test SQL statement to migrate one user from the Drupal 6 System into the Drupal 7 System, using the GL identity as my example.INSERT INTO users
(
uid,
name,
pass,
mail,
theme,
signature,
signature_format,
created,
access,
login,
status,
timezone,
language,
picture,
init,
data,
uuid
)
VALUES
(
"2769", /* uid - critical */
"GL", /* name - critical */
"", /* pass - impossible */
"graham@leach.com", /* mail - critical */
"", /* theme - irrelevant */
"", /* signature - irrelevant */
"0", /* signature_format - irrelevant */
"1442730945", /* created - important */
"1493677257", /* access - important */
"1493677256", /* login - important */
"1", /* status - critical */
"NULL", /* timezone - important */
"en", /* language - critical */
"", /* picture - irrelevant */
"graham.r.leach@gmail.com", /* init - important */
"", /* data - irrelevant */
"" /* UUID - important? */
);
UID Clash Avoidance
To prevent problems, it is important that every uid that we manually insert into the Drupal 7 System database be different from any that are already there. This is because, if there are two users with the same uid, major problems will ensue and the Drupal 7 System will almost certainly crash.
Here's the range of uid from the Drupal 6 System users table:
mysql> select max(uid) from users;
+----------+
| max(uid) |
+----------+
| 7990 |
+----------+
Here's the range of uid from the Drupal 7 System users table:
MariaDB [hph]> select uid from users;
+-------+
| uid |
+-------+
| 0 |
| 20806 |
| 20810 |
| 20785 |
| 20691 |
| 20809 |
| 20773 |
| 20813 |
| 20818 |
| 1 |
| 20826 |
+-------+
As we can see, there are two special, low-value uid and then a bunch of uid in the 20,000++ range.
Who Has uid 0?
In the Drupal System, the user with uid 0 is the Anonymous User.
Who Has uid 1?
In the Drupal System, the user with uid 1 is the Administrator
Any migration tool will need to skip those special low-value uid when exporting, so the very same users who are already active in the target system are not interfered with when the migration occurs.
Aside from that small caveat, everything looks OK. The users who will be coming in from the Drupal 6 System user table all have a uid far lower than the lowest Drupal 7 System uid value.
Any migration tool will need to skip those special low-value uid when exporting, so the very same users who are already active in the target system are not interfered with when the migration occurs.
Name Clash Avoidance
Another thing to keep in mind is that the value for name must also be kept unique within the Drupal 7 System database, so when we perform the migration, we must avoid introducing a duplicate value into the database with anything other than a unique value, or risk corrupting or crashing the Drupal 7 System. This can be done programmatically, so we will avoid this problem in code, as opposed to taking action in the database.
This is also a great argument for an intermediate file where an offending name can be manually altered and the implicated user alerted to the change in a graceful way.
This is also a great argument for an intermediate file where an offending name can be manually altered and the implicated user alerted to the change in a graceful way.
UUID
Potential trouble ahead there!
This is a new field and, apparently, not part of the official API specification.
Consequently, this field makes me very nervous because I do not know where it comes from, how it works or how it is used in the Drupal 7 System. UUID's are normally utilized to uniquely identify entities in the Drupal 7 System, and used for internal matters in the Drupal System. Curious, I took a look at other entries in the table to see what was already being put in there. Every row had a UUID assigned to it.
Here's an example of a UUID I found for a user created within the Drupal 7 System:
f4539a1f-9fbb-452c-88fd-198442815002
As it turns out, UUID have several version(s), so the first step is to figure out what version of UUID this is, so we can provide a valid UUID to the Drupal system
Mass Migrating User Information
After performing my research, I began to feel pretty confident in my ability to mass migrate users from the Drupal 6 System to the Drupal 7 System. The only worry was that pesky uuid value, which I couldn't find much information about.
The Plan
Why a file? Because the Drupal 6 System database resided on a different physical computer than the Drupal 7 System database, so using an intermediate file seemed like a smart way to facilitate a neat inter-machine transfer of information. Also, I wanted to be able to examine the output file for errors before trying to read it into the Drupal 7 System database.
Ultimately, it looked to me like a mass migration of user data was eminently possible between the Drupal 6 System and the Drupal 7 System - just as long as I was willing to engage in a little bit of data gymnastics. So here's the general approach I decided to use, which helped me to design the PHP program I used to make the migration happen:
Scenario:
A) Connect to the Drupal 6 database
B) Use SQL statements to pull rows from the users table, one at a time
C) Keep the data needed from each row, and ignore the stuff I didn't need
D) Write an SQL output file that preserved the maximum amount of user information
E) Use MySQL on the target machine to import the Drupal 6 users into Drupal 7
In the end, Export-Drupal-6-Users.php functioned quite well. I was able to preserve the maximum amount of user data from the Drupal 6 System users table. The data was output to a file in a format that was acceptable to the the Drupal 7 System database engine. Any missing data was generated and included so the format of the output file conformed to the requirements of the Drupal 7 System database. Neither system was disrupted as the new data was introduced. Once the process was complete, I disabled the new user function in the Drupal 6 System, directing new users to the Drupal 7 System instead. I also disabled the shopping cart functionality, redirecting users to the Drupal 7 System.
Here's a sample of the output of Export-Drupal-6-Users.php, which (as I mentioned) does a bunch of housekeeping on the Drupal 6 System users table as they are processed and re-formatted for the differently-formatted Drupal 7 System users table:
Here's a sample of the output of Export-Drupal-6-Users.php, which (as I mentioned) does a bunch of housekeeping on the Drupal 6 System users table as they are processed and re-formatted for the differently-formatted Drupal 7 System users table:
How to Perform a Mass Password Reset
So, with the users migrated, the final step was to get them to reset their password. This is because passwords cannot be migrated from Drupal 6 to Drupal 7 due to the due to the MD5 -> SHA512 password strategy evolution between the two versions. I needed to somehow figure out how to do a Selective Mass Password Reset, ideally one that sends all affected users an email with an activation URL that:
A) Re-verifies their current email address
B) Forces forces them to choose a new (and hopefully better) password
Looking around, I found something called the One Time Login Module, which fits those requirements perfectly.
Done!
Related Articles
How To Migrating Order History From Drupal 6 to Drupal 7
How To Migrating Addresses from Drupal 6 to Drupal 7
References
https://api.drupal.org/api/drupal/modules%21user%21user.install/function/user_schema/6.x
https://justmagicdesign.com/blog/201404/upgrading-website-drupal-6-drupal-7-migrating-users
https://www.drupal.org/forum/support/post-installation/2007-01-06/manually-adding-users
No comments:
Post a Comment