diff --git a/phpBB/install/convertors/convert_phpbb20.php b/phpBB/install/convertors/convert_phpbb20.php
index 132c4fedb4..ef460e3c17 100644
--- a/phpBB/install/convertors/convert_phpbb20.php
+++ b/phpBB/install/convertors/convert_phpbb20.php
@@ -306,7 +306,11 @@ if (!$get_info)
// Instead every file gets copied while processing the corresponding attachment entry.
// if (defined("MOD_ATTACHMENT")) { import_attachment_files(); phpbb_copy_thumbnails(); }
+ // phpBB2 allowed some similar usernames to coexist which would have the same
+ // username_clean in phpBB3 which is not possible, so we'll give the admin a list
+ // of user ids and usernames and let him deicde what he wants to do with them
'execute_first' => '
+ phpbb_check_username_collisions();
import_avatar_gallery();
if (defined("MOD_ATTACHMENT")) phpbb_import_attach_config();
phpbb_insert_forums();
diff --git a/phpBB/install/convertors/functions_phpbb20.php b/phpBB/install/convertors/functions_phpbb20.php
index 93677201be..32f56624e2 100644
--- a/phpBB/install/convertors/functions_phpbb20.php
+++ b/phpBB/install/convertors/functions_phpbb20.php
@@ -1587,4 +1587,194 @@ function phpbb_disallowed_username($username)
return utf8_htmlspecialchars($username);
}
+/**
+* Checks whether there are any usernames on the old board that would map to the same
+* username_clean on phpBB3. Prints out a list if any exist and exits.
+*/
+function phpbb_check_username_collisions()
+{
+ global $db, $src_db, $convert, $table_prefix, $user, $lang;
+
+ $map_dbms = '';
+ switch ($db->sql_layer)
+ {
+ case 'mysql':
+ $map_dbms = 'mysql_40';
+ break;
+
+ case 'mysql4':
+ if (version_compare($db->mysql_version, '4.1.3', '>='))
+ {
+ $map_dbms = 'mysql_41';
+ }
+ else
+ {
+ $map_dbms = 'mysql_40';
+ }
+ break;
+
+ case 'mysqli':
+ $map_dbms = 'mysql_41';
+ break;
+
+ case 'mssql':
+ case 'mssql_odbc':
+ $map_dbms = 'mssql';
+ break;
+
+ default:
+ $map_dbms = $db->sql_layer;
+ break;
+ }
+
+ // create a temporary table in which we store the clean usernames
+ $drop_sql = 'DROP TABLE ' . $table_prefix . 'userconv';
+ switch ($map_dbms)
+ {
+ case 'firebird':
+ $create_sql = 'CREATE TABLE ' . $table_prefix . 'userconv (
+ user_id INTEGER NOT NULL,
+ username_clean VARCHAR(255) CHARACTER SET UTF8 DEFAULT \'\' NOT NULL COLLATE UNICODE
+ )';
+ break;
+
+ case 'mssql':
+ $create_sql = 'CREATE TABLE [' . $table_prefix . 'userconv] (
+ [user_id] [int] NOT NULL ,
+ [username_clean] [varchar] (255) DEFAULT (\'\') NOT NULL
+ )';
+ break;
+
+ case 'mysql_40':
+ $create_sql = 'CREATE TABLE ' . $table_prefix . 'userconv (
+ user_id mediumint(8) UNSIGNED NOT NULL,
+ username_clean blob NOT NULL
+ )';
+ break;
+
+ case 'mysql_41':
+ $create_sql = 'CREATE TABLE ' . $table_prefix . 'userconv (
+ user_id mediumint(8) UNSIGNED NOT NULL,
+ username_clean varchar(255) DEFAULT \'\' NOT NULL
+ ) CHARACTER SET `utf8` COLLATE `utf8_bin`';
+ break;
+
+ case 'oracle':
+ $create_sql = 'CREATE TABLE ' . $table_prefix . 'userconv
+ user_id number(8) NOT NULL,
+ username_clean varchar2(255) DEFAULT \'\'
+ )';
+ break;
+
+ case 'postgres':
+ $create_sql = 'CREATE TABLE ' . $table_prefix . 'userconv (
+ user_id INT4 DEFAULT \'0\',
+ username_clean varchar_ci DEFAULT \'\' NOT NULL
+ )';
+ break;
+
+ case 'sqlite':
+ $create_sql = 'CREATE TABLE ' . $table_prefix . 'userconv (
+ user_id INTEGER NOT NULL DEFAULT \'0\',
+ username_clean varchar(255) NOT NULL DEFAULT \'\',
+ )';
+ break;
+ }
+
+ $db->return_on_error = true;
+ $db->sql_query($drop_sql);
+ $db->sql_query($create_sql);
+ $db->return_on_error = false;
+
+ // now select all user_ids and usernames and then convert the username (this can take quite a while!)
+ $sql = 'SELECT user_id, username
+ FROM ' . $convert->src_table_prefix . 'users';
+ $result = $src_db->sql_query($sql);
+
+ $insert_ary = array();
+ $i = 0;
+ while ($row = $src_db->sql_fetchrow($result))
+ {
+ $clean_name = utf8_clean_string(phpbb_set_default_encoding($row['username']));
+ $insert_ary[] = array('user_id' => $row['user_id'], 'username_clean' => $clean_name);
+
+ if ($i % 1000 == 999)
+ {
+ $db->sql_multi_insert($table_prefix . 'userconv', $insert_ary);
+ $insert_ary = array();
+ }
+ $i++;
+ }
+ $src_db->sql_freeresult($result);
+
+ if (sizeof($insert_ary))
+ {
+ $db->sql_multi_insert($table_prefix . 'userconv', $insert_ary);
+ }
+ unset($insert_ary);
+
+ // now find the clean version of the usernames that collide
+ $sql = 'SELECT username_clean
+ FROM ' . $table_prefix . 'userconv
+ GROUP BY username_clean
+ HAVING COUNT(user_id) > 1';
+ $result = $db->sql_query($sql);
+
+ $colliding_names = array();
+ while ($row = $db->sql_fetchrow($result))
+ {
+ $colliding_names[] = $row['username_clean'];
+ }
+ $db->sql_freeresult($result);
+
+ // there was at least one collision, the admin will have to solve it before conversion can continue
+ if (sizeof($colliding_names))
+ {
+ $sql = 'SELECT user_id, username_clean
+ FROM ' . $table_prefix . 'userconv
+ WHERE ' . $db->sql_in_set('username_clean', $colliding_names);
+ $result = $db->sql_query($sql);
+ unset($colliding_names);
+
+ $colliding_user_ids = array();
+ while ($row = $db->sql_fetchrow($result))
+ {
+ $colliding_user_ids[(int) $row['user_id']] = $row['username_clean'];
+ }
+ $db->sql_freeresult($result);
+
+ $sql = 'SELECT username, user_id, user_posts
+ FROM ' . $convert->src_table_prefix . 'users
+ WHERE ' . $src_db->sql_in_set('user_id', array_keys($colliding_user_ids));
+ $result = $src_db->sql_query($sql);
+
+ $colliding_users = array();
+ while ($row = $db->sql_fetchrow($result))
+ {
+ $row['user_id'] = (int) $row['user_id'];
+ if (isset($colliding_user_ids[$row['user_id']]))
+ {
+ $colliding_users[$colliding_user_ids[$row['user_id']]][] = $row;
+ }
+ }
+ $db->sql_freeresult($result);
+ unset($colliding_user_ids);
+
+ $list = '';
+ foreach ($colliding_users as $username_clean => $users)
+ {
+ $list .= sprintf($user->lang['COLLIDING_CLEAN_USERNAME'], $username_clean) . "
\n";
+ foreach ($users as $i => $row)
+ {
+ $list .= sprintf($user->lang['COLLIDING_USER'], $row['user_id'], phpbb_set_default_encoding($row['username']), $row['user_posts']) . "
\n";
+ }
+ }
+
+ $lang['INST_ERR_FATAL'] = $user->lang['CONV_ERR_FATAL'];
+ $convert->p_master->error('' . $user->lang['COLLIDING_USERNAMES_FOUND'] . '
' . $list . '', __LINE__, __FILE__);
+ }
+
+ $db->sql_query($drop_sql);
+}
+
?>
\ No newline at end of file
diff --git a/phpBB/install/install_convert.php b/phpBB/install/install_convert.php
index 466de0e956..15a98e34ed 100644
--- a/phpBB/install/install_convert.php
+++ b/phpBB/install/install_convert.php
@@ -90,7 +90,7 @@ class install_convert extends module
function main($mode, $sub)
{
- global $lang, $template, $phpbb_root_path, $phpEx, $cache, $config, $language;
+ global $lang, $template, $phpbb_root_path, $phpEx, $cache, $config, $language, $table_prefix;
global $convert;
$this->tpl_name = 'install_convert';
diff --git a/phpBB/language/en/install.php b/phpBB/language/en/install.php
index aa96ce3f1b..cc1e0908bc 100755
--- a/phpBB/language/en/install.php
+++ b/phpBB/language/en/install.php
@@ -57,6 +57,9 @@ $lang = array_merge($lang, array(
'CHANGE' => 'Change',
'CHECK_TABLE_PREFIX' => 'Please check your table prefix and try again.',
'CLEAN_VERIFY' => 'Cleaning up and verifying the final structure',
+ 'COLLIDING_CLEAN_USERNAME' => '%s is the clean username for:',
+ 'COLLIDING_USERNAMES_FOUND' => 'Colliding usernames were found on your old board. In order to complete the conversion please delete or rename these users so that there is only one user on your old board for each clean username.',
+ 'COLLIDING_USER' => 'ยป user id: %d username: %s (%d posts)',
'CONFIG_CONVERT' => 'Converting the configuration',
'CONFIG_FILE_UNABLE_WRITE' => 'It was not possible to write the configuration file. Alternative methods for this file to be created are presented below',
'CONFIG_FILE_WRITTEN' => 'The configuration file has been written. You may now proceed to the next step of the installation',
@@ -75,6 +78,7 @@ $lang = array_merge($lang, array(
'CONVERT_NEW_CONVERSION' => 'New conversion',
'CONVERT_NOT_EXIST' => 'The specified convertor does not exist',
'CONVERT_SETTINGS_VERIFIED' => 'The information you entered has been verified. To start the conversion process, push the button below to begin.',
+ 'CONV_ERR_FATAL' => 'Fatal conversion error',
'CONV_ERROR_ATTACH_FTP_DIR' => 'FTP upload for attachments is enabled at the old board. Please disable the FTP upload option and make sure a valid upload directory is specified, then copy all attachment files to this new web accessible directory. Once you have done this, restart the convertor.',
'CONV_ERROR_CONFIG_EMPTY' => 'There is no configuration information available for the conversion.',