<?php
class XenForo_Importer_SMF extends XenForo_Importer_Abstract
{
/**
* Source database connection.
*
* @var Zend_Db_Adapter_Abstract
*/
protected $_sourceDb;
protected $_prefix;
protected $_charset = 'utf-8';
protected $_groupMap = null;
protected $_userMap = null;
protected $_config;
public static function getName()
{
return 'SMF 2.0';
}
public function configure(XenForo_ControllerAdmin_Abstract $controller, array &$config)
{
if ($config)
{
$errors = $this->validateConfiguration($config, $validatedDirectories);
if ($errors)
{
return $controller->responseError($errors);
}
else if (!isset($config['attachmentPaths']))
{
$attachPaths = $this->_sourceDb->fetchOne('
SELECT value
FROM ' . $this->_prefix . 'settings
WHERE variable = \'attachmentUploadDir\'
');
$path = @unserialize($attachPaths);
if (!$path)
{
if ($attachPaths)
{
$path = array($attachPaths);
}
else
{
$path = array('');
}
}
$viewParams = array(
'attachPaths' => $path ? $path : array(),
'config' => $config
);
return $controller->responseView('XenForo_ViewAdmin_Import_SMF_Config_Attachments', 'import_smf_config_attachments', $viewParams);
}
if ($validatedDirectories)
{
return true;
}
}
else
{
$viewParams = array();
}
return $controller->responseView('XenForo_ViewAdmin_Import_SMF_Config', 'import_smf_config', $viewParams);
}
public function validateConfiguration(array &$config, &$validatedDirectories = false)
{
$errors = array();
$config['db']['prefix'] = preg_replace('/[^a-z0-9_]/i', '', $config['db']['prefix']);
$this->_prefix = $config['db']['prefix'];
try
{
$db = Zend_Db::factory('mysqli',
array(
'host' => $config['db']['host'],
'port' => $config['db']['port'],
'username' => $config['db']['username'],
'password' => $config['db']['password'],
'dbname' => $config['db']['dbname'],
'charset' => str_replace('-', '', $config['db']['charset'])
)
);
$db->getConnection();
}
catch (Zend_Db_Exception $e)
{
$errors[] = new XenForo_Phrase('source_database_connection_details_not_correct_x', array('error' => $e->getMessage()));
}
if ($errors)
{
return $errors;
}
try
{
$db->query('
SELECT id_member
FROM ' . $config['db']['prefix'] . 'members
LIMIT 1
');
}
catch (Zend_Db_Exception $e)
{
if ($config['db']['dbname'] === '')
{
$errors[] = new XenForo_Phrase('please_enter_database_name');
}
else
{
$errors[] = new XenForo_Phrase('table_prefix_or_database_name_is_not_correct');
}
}
$this->_sourceDb = $db;
if (isset($config['attachmentPaths']))
{
if (is_array($config['attachmentPaths']))
{
foreach ($config['attachmentPaths'] AS $key => $path)
{
if (!$path)
{
unset ($config['attachmentPaths'][$key]);
continue;
}
if (!file_exists($path) || !is_dir($path))
{
$errors[] = new XenForo_Phrase('attachments_directory_x_not_found', array('path' => $path));
}
}
}
else
{
$errors[] = new XenForo_Phrase('import_smf_config_no_attachment_paths_entered');
}
}
if (!empty($config['avatarPath']))
{
if (!file_exists($config['avatarPath']) || !is_dir($config['avatarPath']))
{
$errors[] = new XenForo_Phrase('avatars_directory_not_found');
}
}
$validatedDirectories = true;
return $errors;
}
public function getSteps()
{
return array(
'userGroups' => array(
'title' => new XenForo_Phrase('import_user_groups')
),
'users' => array(
'title' => new XenForo_Phrase('import_users'),
'depends' => array('userGroups')
),
'avatars' => array(
'title' => new XenForo_Phrase('import_custom_avatars'),
'depends' => array('users')
),
'privateMessages' => array(
'title' => new XenForo_Phrase('import_private_messages'),
'depends' => array('users')
),
'forums' => array(
'title' => new XenForo_Phrase('import_forums'),
'depends' => array('userGroups')
),
'moderators' => array(
'title' => new XenForo_Phrase('import_forum_moderators'),
'depends' => array('forums', 'users')
),
'threads' => array(
'title' => new XenForo_Phrase('import_threads_and_posts'),
'depends' => array('forums', 'users')
),
'attachments' => array(
'title' => new XenForo_Phrase('import_attached_files'),
'depends' => array('threads')
)
);
}
protected function _bootstrap(array $config)
{
if ($this->_sourceDb)
{
// already run
return;
}
@set_time_limit(0);
$this->_config = $config;
$this->_sourceDb = Zend_Db::factory('mysqli',
array(
'host' => $config['db']['host'],
'port' => $config['db']['port'],
'username' => $config['db']['username'],
'password' => $config['db']['password'],
'dbname' => $config['db']['dbname'],
'charset' => str_replace('-', '', $config['db']['charset'])
)
);
if (empty($config['db']['charset']))
{
$this->_sourceDb->query('SET character_set_results = NULL');
}
$this->_prefix = preg_replace('/[^a-z0-9_]/i', '', $config['db']['prefix']);
$this->_charset = $config['charset'];
}
public function stepUserGroups($start, array $options)
{
$sDb = $this->_sourceDb;
$prefix = $this->_prefix;
/* @var $model XenForo_Model_Import */
$model = $this->_importModel;
$userGroups = $sDb->fetchAll('
SELECT *
FROM ' . $prefix . 'membergroups
ORDER BY id_group
');
$total = 0;
XenForo_Db::beginTransaction();
foreach ($userGroups AS $userGroup)
{
$titlePriority = 5;
switch ($userGroup['id_group'])
{
case 4: // Newbie
$model->logImportData('userGroup', $userGroup['id_group'], XenForo_Model_User::$defaultRegisteredGroupId);
break;
case 1: // Administrator
$model->logImportData('userGroup', $userGroup['id_group'], XenForo_Model_User::$defaultAdminGroupId);
break;
case 3: // Moderator
$model->logImportData('userGroup', $userGroup['id_group'], XenForo_Model_User::$defaultModeratorGroupId);
break;
case 2: // Global Moderator
$titlePriority = 910;
// fall through intentionally
default:
$import = array(
'title' => $this->_convertToUtf8($userGroup['group_name'], true),
'display_style_priority' => $titlePriority,
'permissions' => $this->_convertGlobalPermissionsForGroup($userGroup['id_group'])
);
if ($model->importUserGroup($userGroup['id_group'], $import))
{
$total++;
}
}
}
XenForo_Model::create('XenForo_Model_UserGroup')->rebuildDisplayStyleCache();
XenForo_Db::commit();
$this->_session->incrementStepImportTotal($total);
return true;
}
protected function _convertGlobalPermissionsForGroup($groupId)
{
$smfPerms = $this->_getUserGroupPermsForUserGroup($groupId);
// no equivalents
$perms['general']['view'] = 'allow';
$perms['general']['viewNode'] = 'allow';
$perms['general']['maxTaggedUsers'] = 5;
$perms['general']['report'] = 'allow';
$perms['general']['editSignature'] = 'allow';
$perms['forum']['viewOthers'] = 'allow';
$perms['forum']['viewContent'] = 'allow';
$perms['forum']['like'] = 'allow';
$perms['forum']['votePoll'] = 'allow';
$this->_setXfPermissionBasedOnPermission($perms, $smfPerms, 'general', 'viewMemberList', 'view_mlist');
$this->_setXfPermissionBasedOnPermission($perms, $smfPerms, 'general', 'viewProfile', 'profile_view_any');
$this->_setXfPermissionBasedOnPermission($perms, $smfPerms, 'general', 'search', 'search_posts');
$this->_setXfPermissionBasedOnPermission($perms, $smfPerms, 'general', 'editProfile', 'profile_extra_own');
$this->_setXfPermissionBasedOnPermission($perms, $smfPerms, 'general', 'editCustomTitle', 'profile_title_own');
$this->_setXfPermissionBasedOnPermission($perms, $smfPerms, 'avatar', 'allow', 'profile_upload_avatar');
$this->_setXfPermissionBasedOnPermission($perms, $smfPerms, 'conversation', 'receive', 'pm_read');
$this->_setXfPermissionBasedOnPermission($perms, $smfPerms, 'conversation', 'start', 'pm_send');
if (!empty($perms['general']['viewProfile'])
&& $perms['general']['viewProfile'] == 'allow'
)
{
$perms['profilePost']['view'] = 'allow';
$perms['profilePost']['like'] = 'allow';
$perms['profilePost']['manageOwn'] = 'allow';
$perms['profilePost']['deleteOwn'] = 'allow';
$perms['profilePost']['post'] = 'allow';
$perms['profilePost']['comment'] = 'allow';
$perms['profilePost']['editOwn'] = 'allow';
}
return $perms;
}
protected function _setXfPermissionBasedOnPermission(array &$outputPerms, array $smfPerms, $xfPermGroup, $xfPerm, $smfPerm, $allow = 'allow')
{
if (!isset($smfPerms[$smfPerm]) || $smfPerms[$smfPerm] == -1)
{
return;
}
if ($smfPerms[$smfPerm] == 0)
{
$outputPerms[$xfPermGroup][$xfPerm] = 'deny';
}
else if ($smfPerms[$smfPerm] == 1)
{
$outputPerms[$xfPermGroup][$xfPerm] = $allow;
}
}
protected function _getUserGroupPermsForUserGroup($userGroupId)
{
return $this->_sourceDb->fetchPairs('
SELECT permission, add_deny
FROM ' . $this->_prefix . 'permissions
WHERE id_group = ?
', $userGroupId);
}
public function configStepUsers(array $options)
{
if ($options)
{
return false;
}
return $this->_controller->responseView('XenForo_ViewAdmin_Import_MyBb_ConfigUsers', 'import_config_users');
}
public function stepUsers($start, array $options)
{
$options = array_merge(array(
'limit' => 100,
'max' => false,
// all checkbox options must default to false as they may not be submitted
'mergeEmail' => false,
'mergeName' => false,
'gravatar' => false
), $options);
if ($options['gravatar'])
{
$options['limit'] = max(5, floor($options['limit'] / 2));
}
$sDb = $this->_sourceDb;
$prefix = $this->_prefix;
if ($options['max'] === false)
{
$options['max'] = $sDb->fetchOne('
SELECT MAX(id_member)
FROM ' . $prefix . 'members
');
}
$users = $sDb->fetchAll(
$sDb->limit($this->_getSelectUserSql('members.id_member > ' . $sDb->quote($start)), $options['limit'])
);
if (!$users)
{
return $this->_getNextUserStep();
}
XenForo_Db::beginTransaction();
$next = 0;
$total = 0;
foreach ($users AS $user)
{
$next = $user['id_member'];
$imported = $this->_importOrMergeUser($user, $options);
if ($imported)
{
$total++;
}
}
XenForo_Db::commit();
$this->_session->incrementStepImportTotal($total);
return array($next, $options, $this->_getProgressOutput($next, $options['max']));
}
public function stepUsersMerge($start, array $options)
{
$sDb = $this->_sourceDb;
$manual = $this->_session->getExtraData('userMerge');
if ($manual)
{
$merge = $sDb->fetchAll($this->_getSelectUserSql('members.id_member IN (' . $sDb->quote(array_keys($manual)) . ')'));
$resolve = $this->_controller->getInput()->filterSingle('resolve', XenForo_Input::ARRAY_SIMPLE);
if ($resolve && !empty($options['shownForm']))
{
$this->_session->unsetExtraData('userMerge');
$this->_resolveUserConflicts($merge, $resolve);
}
else
{
// prevents infinite loop if redirected back to step
$options['shownForm'] = true;
$this->_session->setStepInfo(0, $options);
$users = array();
foreach ($merge AS $user)
{
$users[$user['id_member']] = array(
'username' => $this->_convertToUtf8($user['member_name'], true),
'email' => $this->_convertToUtf8($user['email_address'], true),
'message_count' => $user['posts'],
'register_date' => $user['date_registered'],
'conflict' => $manual[$user['id_member']]
);
}
return $this->_controller->responseView(
'XenForo_ViewAdmin_Import_MergeUsers', 'import_merge_users', array('users' => $users)
);
}
}
return $this->_getNextUserStep();
}
public function stepUsersFailed($start, array $options)
{
$sDb = $this->_sourceDb;
$manual = $this->_session->getExtraData('userFailed');
if ($manual)
{
$users = $this->_sourceDb->fetchAll($this->_getSelectUserSql('members.id_member IN (' . $sDb->quote(array_keys($manual)) . ')'));
$resolve = $this->_controller->getInput()->filterSingle('resolve', XenForo_Input::ARRAY_SIMPLE);
if ($resolve && !empty($options['shownForm']))
{
$this->_session->unsetExtraData('userFailed');
$this->_resolveUserConflicts($users, $resolve);
}
else
{
// prevents infinite loop if redirected back to step
$options['shownForm'] = true;
$this->_session->setStepInfo(0, $options);
$failedUsers = array();
foreach ($users AS $user)
{
$failedUsers[$user['id_member']] = array(
'username' => $this->_convertToUtf8($user['member_name'], true),
'email' => $this->_convertToUtf8($user['email_address'], true),
'message_count' => $user['posts'],
'register_date' => $user['date_registered'],
'failure' => $manual[$user['id_member']]
);
}
return $this->_controller->responseView(
'XenForo_ViewAdmin_Import_FailedUsers', 'import_failed_users', array('users' => $failedUsers)
);
}
}
return $this->_getNextUserStep();
}
public function stepUsersFollowing($start, array $options)
{
$s = microtime(true);
$targetRunTime = 10;
$total = 0;
$usersFollowing = $this->_session->getExtraData('userFollowing');
if (!$usersFollowing)
{
$this->_session->unsetExtraData('userFollowing');
return $this->_getNextUserStep();
}
XenForo_Db::beginTransaction();
foreach ($usersFollowing AS $importedUserId => $userFollowing)
{
$total++;
unset ($usersFollowing[$importedUserId]);
$usersFollowingIds = explode(',', $userFollowing);
$usersFollowingIds = $this->_importModel->getImportContentMap('user', $usersFollowingIds);
$this->_importModel->importFollowing($importedUserId, $usersFollowingIds);
if (microtime(true) - $s > $targetRunTime)
{
break;
}
}
XenForo_Db::commit();
$this->_session->setExtraData('userFollowing', $usersFollowing);
$this->_session->incrementStepImportTotal($total, 'users');
return array(1, array(), '');
}
public function stepUsersIgnored($start, array $options)
{
$s = microtime(true);
$targetRunTime = 10;
$total = 0;
$usersIgnored = $this->_session->getExtraData('userIgnored');
if (!$usersIgnored)
{
$this->_session->unsetExtraData('userIgnored');
return $this->_getNextUserStep();
}
XenForo_Db::beginTransaction();
foreach ($usersIgnored AS $importedUserId => $userIgnored)
{
$total++;
unset ($usersIgnored[$importedUserId]);
$usersIgnoredIds = explode(',', $userIgnored);
$usersIgnoredIds = $this->_importModel->getImportContentMap('user', $usersIgnoredIds);
$this->_importModel->importFollowing($importedUserId, $usersIgnoredIds);
if (microtime(true) - $s > $targetRunTime)
{
break;
}
}
XenForo_Db::commit();
$this->_session->setExtraData('userIgnored', $usersIgnored);
$this->_session->incrementStepImportTotal($total, 'users');
return array(1, array(), '');
}
protected function _resolveUserConflicts(array $users, array $resolve)
{
$total = 0;
XenForo_Db::beginTransaction();
foreach ($users AS $user)
{
if (empty($resolve[$user['id_member']]))
{
continue;
}
$info = $resolve[$user['id_member']];
if (empty($info['action']) || $info['action'] == 'change')
{
if (isset($info['email']))
{
$user['email_address'] = $info['email'];
}
if (isset($info['username']))
{
$user['member_name'] = $info['username'];
}
$imported = $this->_importOrMergeUser($user);
if ($imported)
{
$total++;
}
}
else if ($info['action'] == 'merge')
{
$im = $this->_importModel;
if ($match = $im->getUserIdByEmail($this->_convertToUtf8($user['email_address'], true)))
{
$this->_mergeUser($user, $match);
}
else if ($match = $im->getUserIdByUserName($this->_convertToUtf8($user['member_name'], true)))
{
$this->_mergeUser($user, $match);
}
$total++;
}
}
XenForo_Db::commit();
$this->_session->incrementStepImportTotal($total, 'users');
}
protected function _getNextUserStep()
{
if ($this->_session->getExtraData('userMerge'))
{
return 'usersMerge';
}
if ($this->_session->getExtraData('userFailed'))
{
return 'usersFailed';
}
if ($this->_session->getExtraData('userFollowing'))
{
return 'usersFollowing';
}
if ($this->_session->getExtraData('userIgnored'))
{
return 'usersIgnored';
}
return true;
}
protected function _importOrMergeUser(array $user, array $options = array())
{
$im = $this->_importModel;
if ($user['email_address'] && $emailMatch = $im->getUserIdByEmail($this->_convertToUtf8($user['email_address'], true)))
{
if (!empty($options['mergeEmail']))
{
return $this->_mergeUser($user, $emailMatch);
}
else
{
if ($im->getUserIdByUserName($this->_convertToUtf8($user['member_name'], true)))
{
$this->_session->setExtraData('userMerge', $user['id_member'], 'both');
}
else
{
$this->_session->setExtraData('userMerge', $user['id_member'], 'email');
}
return false;
}
}
$name = utf8_substr($this->_convertToUtf8(trim($user['member_name']), true), 0, 50);
if ($nameMatch = $im->getUserIdByUserName($name))
{
if (!empty($options['mergeName']))
{
return $this->_mergeUser($user, $nameMatch);
}
else
{
$this->_session->setExtraData('userMerge', $user['id_member'], 'name');
return false;
}
}
return $this->_importUser($user, $options);
}
protected $_userActivationSetting = array();
protected function _importUser(array $user, array $options)
{
$sDb = $this->_sourceDb;
$prefix = $this->_prefix;
/* @var $model XenForo_Model_Import */
$model = $this->_importModel;
if ($this->_groupMap === null)
{
$this->_groupMap = $this->_importModel->getImportContentMap('userGroup');
}
if ($this->_userActivationSetting === null)
{
$this->_userActivationSetting = $sDb->fetchOne('
SELECT value
FROM ' . $prefix . 'settings
WHERE name = \'registration_method\'
');
}
$secondaryGroupIds = array();
if (!empty($user['additional_groups']))
{
$secondaryGroupIds = explode(',', $user['additional_groups']);
$secondaryGroupIds[] = $user['id_post_group'];
}
$user['ban'] = $this->_getUserBan($user);
// This is pretty much a best effort though should be roughly accurate based on what we know.
$timeZone = $sDb->fetchOne('
SELECT value
FROM ' . $this->_prefix . 'settings
WHERE variable = \'default_timezone\'
');
if ($timeZone)
{
$timeZone = explode('GMT', $timeZone);
if (isset($timeZone[1]))
{
$timeZone = $timeZone[1];
}
else
{
$timeZone = 0;
}
}
else
{
$timeZone = 0;
}
$timeZoneOffset = $timeZone + $user['time_offset'];
$import = array(
'username' => $this->_convertToUtf8($user['member_name'], true),
'email' => $this->_convertToUtf8($user['email_address'], true),
'user_group_id' => XenForo_Model_User::$defaultRegisteredGroupId,
'secondary_group_ids' => $this->_mapLookUpList($this->_groupMap, $secondaryGroupIds, false),
'authentication' => array(
'scheme_class' => 'XenForo_Authentication_SMF',
'data' => array(
'hash' => $user['passwd'],
'username' => strtolower($user['member_name'])
)
),
'last_activity' => $user['last_login'],
'register_date' => $user['date_registered'],
'ip' => $user['member_ip'],
'homepage' => $this->_convertToUtf8($user['website_url'], true),
'message_count' => $user['posts'],
'is_admin' => $user['id_group'] == 1 ? 1 : 0,
'is_banned' => $user['ban'] ? 1 : 0,
'signature' => $this->_sanitizeBbCode($user['signature']),
'timezone' => $this->_importModel->resolveTimeZoneOffset($timeZoneOffset, false),
'visible' => $user['show_online'],
'content_show_signature' => true,
'receive_admin_email' => $user['notify_announcements'],
'allow_send_personal_conversation' => 'everyone',
'email_on_conversation' => $user['pm_email_notify'] ? 1 : 0,
'user_state' => ($user['is_activated'] == 1) ? 'valid' : 'moderated',
'gender' => ($user['gender'] == 1) ? 'male' : (($user['gender'] == 2) ? 'female' : ''),
'custom_title' => $this->_convertToUtf8($user['personal_text'], true)
);
if ($user['birthdate'])
{
$parts = explode('-', $user['birthdate']);
if (count($parts) == 3)
{
// Default birth year.
if (trim($parts[0]) != '0001')
{
$import['dob_day'] = trim($parts[2]);
$import['dob_month'] = trim($parts[1]);
if (trim($parts[0] != '0004'))
{
$import['dob_year'] = trim($parts[0]);
}
}
}
}
if (!empty($options['gravatar']))
{
if (!$user['avatar'] && $user['email_address'] && $user['posts']
&& XenForo_Model_Avatar::gravatarExists($user['email_address'])
)
{
$import['gravatar'] = $import['email'];
}
}
$import['about'] = $this->_convertToUtf8($user['personal_text'], true);
$import['location'] = $this->_convertToUtf8($user['location'], true);
if ($user['website_url'] && Zend_Uri::check($user['website_url']))
{
$import['homepage'] = $user['website_url'];;
}
// custom user fields
$userFieldDefinitions = $this->_importModel->getUserFieldDefinitions();
$identityMap = array(
'icq' => 'icq',
'aim' => 'aim',
'yahoo' => 'yim',
'msn' => 'msn'
);
foreach ($identityMap AS $identityType => $smfField)
{
if (isset($userFieldDefinitions[$identityType]))
{
$import[XenForo_Model_Import::USER_FIELD_KEY][$identityType] = $this->_convertToUtf8($user[$smfField], true);
}
}
if ($import['is_admin'])
{
// give all admin permissions
$adminPerms = XenForo_Model::create('XenForo_Model_Admin')->getAllAdminPermissions();
$import['admin_permissions'] = array_keys($adminPerms);
}
$importedUserId = $this->_importModel->importUser($user['id_member'], $import, $failedKey);
if ($importedUserId)
{
if ($user['ban'])
{
$this->_importModel->importBan(array(
'user_id' => $importedUserId,
'ban_user_id' => 0,
'ban_date' => $user['ban']['ban_time'],
'end_date' => $user['ban']['expire_time'],
'user_reason' => $this->_convertToUtf8($user['ban']['reason'], true)
));
}
if ($user['buddy_list'])
{
$this->_session->setExtraData('userFollowing', $importedUserId, $user['buddy_list']);
}
if ($user['pm_ignore_list'])
{
$this->_session->setExtraData('userIgnored', $importedUserId, $user['pm_ignore_list']);
}
}
else if ($failedKey)
{
$this->_session->setExtraData('userFailed', $user['id_member'], $failedKey);
}
return $importedUserId;
}
protected function _getSelectUserSql($where)
{
return '
SELECT members.*
FROM ' . $this->_prefix . 'members AS members
WHERE ' . $where . '
ORDER BY members.id_member
';
}
protected function _mergeUser(array $user, $targetUserId)
{
$this->_db->query('
UPDATE xf_user SET
message_count = message_count + ?,
register_date = IF(register_date > ?, ?, register_date)
WHERE user_id = ?
', array($user['posts'], $user['date_registered'], $user['date_registered'], $targetUserId));
$this->_importModel->logImportData('user', $user['id_member'], $targetUserId);
return $targetUserId;
}
/**
* SMF bans users on a criteria based system that includes email,
* IP address (or parts thereof), or Username.
*
* Other checks exist but may be somewhat ambiguous
* and may be ported over to other XF tables, e.g. IP and email bans.
*
* @param $user
* @return array
*/
protected function _getUserBan(array $user)
{
$sDb = $this->_sourceDb;
return $sDb->fetchRow($sDb->limit('
SELECT banitems.*, bangroups.*
FROM ' . $this->_prefix . 'ban_items AS banitems
INNER JOIN ' . $this->_prefix . 'ban_groups AS bangroups ON
(banitems.id_ban_group = bangroups.id_ban_group)
WHERE banitems.id_member = ?
OR (banitems.email_address = ? AND banitems.email_address <> \'\')
ORDER BY bangroups.ban_time DESC
', 1), array ($user['id_member'], $user['email_address']));
}
public function stepAvatars($start, array $options)
{
$options = array_merge(array(
'avatarPath' => isset($this->_config['avatarPath']) ? $this->_config['avatarPath'] : '',
'attachmentPaths' => isset($this->_config['attachmentPaths']) ? $this->_config['attachmentPaths'] : '',
'limit' => 50,
'max' => false
), $options);
$sDb = $this->_sourceDb;
$prefix = $this->_prefix;
/* @var $model XenForo_Model_Import */
$model = $this->_importModel;
if ($options['max'] === false)
{
$options['max'] = $sDb->fetchOne('
SELECT MAX(members.id_member)
FROM ' . $prefix . 'members AS members
LEFT JOIN ' . $prefix . 'attachments AS avatar ON
(members.id_member = avatar.id_member
AND avatar.attachment_type IN (0, 1))
WHERE (members.avatar <> \'\' OR avatar.id_attach IS NOT NULL)
AND (avatar.id_msg IS NULL OR avatar.id_msg = 0)
AND (avatar.id_member IS NULL OR avatar.id_member <> 0)
');
}
$avatars = $sDb->fetchAll($sDb->limit(
'
SELECT members.*, avatar.*,
IF (avatar.id_member IS NULL, members.id_member, avatar.id_member) AS id_member
FROM ' . $prefix . 'members AS members
LEFT JOIN ' . $prefix . 'attachments AS avatar ON
(members.id_member = avatar.id_member
AND avatar.attachment_type IN (0, 1))
WHERE members.id_member > ' . $sDb->quote($start) . '
AND (members.avatar <> \'\' OR avatar.id_attach IS NOT NULL)
AND (avatar.id_msg IS NULL OR avatar.id_msg = 0)
AND (avatar.id_member IS NULL OR avatar.id_member <> 0)
ORDER BY members.id_member
', $options['limit']
));
if (!$avatars)
{
return true;
}
$userIdMap = $model->getUserIdsMapFromArray($avatars, 'id_member');
$next = 0;
$total = 0;
foreach ($avatars AS $avatar)
{
$next = $avatar['id_member'];
$newUserId = $this->_mapLookUp($userIdMap, $avatar['id_member']);
if (!$newUserId)
{
continue;
}
$avatarFile = null;
// We have a path to an avatar in the members table, otherwise it's an attachment.
if ($avatar['avatar'])
{
// Only import an avatar if it is a URL, otherwise it's a gallery avatar (skip it).
if (Zend_Uri::check($avatar['avatar']))
{
try
{
$httpClient = XenForo_Helper_Http::getClient($avatar['avatar']);
$response = $httpClient->request('GET');
if ($response->isSuccessful())
{
$avatarFile = tempnam(XenForo_Helper_File::getTempDir(), 'xf');
file_put_contents($avatarFile, $response->getBody());
}
}
catch (Zend_Http_Client_Exception $e) {}
}
}
else
{
$filePath = '';
switch ($avatar['attachment_type'])
{
// Attachment directory
case 0:
if (!isset($options['attachmentPaths'][$avatar['id_folder']]))
{
// Going to have to assume that it was a single attachment directory environment.
$attachmentPath = $options['attachmentPaths'][0];
}
else
{
$attachmentPath = $options['attachmentPaths'][$avatar['id_folder']];
}
if ($avatar['file_hash'] === '')
{
$filePath = "$attachmentPath/$avatar[filename]";
}
else
{
$filePath = "$attachmentPath/$avatar[id_attach]_$avatar[file_hash]";
}
break;
// Custom avatar directory
case 1:
$filePath = "$options[avatarPath]/$avatar[filename]";
break;
default:
break;
}
if (!file_exists($filePath))
{
continue;
}
$avatarFile = tempnam(XenForo_Helper_File::getTempDir(), 'xf');
copy ($filePath, $avatarFile);
}
if ($this->_importModel->importAvatar($avatar['id_member'], $newUserId, $avatarFile))
{
$total++;
}
@unlink($avatarFile);
}
$this->_session->incrementStepImportTotal($total);
return array($next, $options, $this->_getProgressOutput($next, $options['max']));
}
public function stepPrivateMessages($start, array $options)
{
$options = array_merge(array(
'limit' => 300,
'max' => false
), $options);
$sDb = $this->_sourceDb;
$prefix = $this->_prefix;
/* @var $model XenForo_Model_Import */
$model = $this->_importModel;
if ($options['max'] === false)
{
$options['max'] = $sDb->fetchOne('
SELECT MAX(id_pm)
FROM ' . $prefix . 'personal_messages
');
}
$pmTexts = $sDb->fetchAll($sDb->limit(
'
SELECT *
FROM ' . $prefix . 'personal_messages
WHERE id_pm > ' . $sDb->quote($start) . '
ORDER BY id_pm
', $options['limit']
));
if (!$pmTexts)
{
return true;
}
$next = 0;
$total = 0;
XenForo_Db::beginTransaction();
foreach ($pmTexts AS $pmText)
{
$next = $pmText['id_pm'];
$toUsers = $sDb->fetchPairs('
SELECT recip.id_member, member.member_name
FROM ' . $prefix . 'pm_recipients AS recip
INNER JOIN ' . $prefix . 'members AS member ON
(recip.id_member = member.id_member)
WHERE recip.id_pm = ?
', $next);
if (!$toUsers)
{
continue;
}
$users = array(
$pmText['id_member_from'] => $pmText['from_name']
) + $toUsers;
$mapUserIds = $model->getImportContentMap('user', array_keys($users));
$newFromUserId = $this->_mapLookUp($mapUserIds, $pmText['id_member_from']);
if (!$newFromUserId)
{
continue;
}
$unreadState = $sDb->fetchPairs('
SELECT id_member, is_read
FROM ' . $prefix . 'pm_recipients
WHERE id_pm = ' . $sDb->quote($pmText['id_pm']) . '
AND deleted = 0
GROUP BY id_member
');
$recipients = array();
foreach ($users AS $userId => $username)
{
$newUserId = $this->_mapLookUp($mapUserIds, $userId);
if (!$newUserId)
{
continue;
}
if (isset($unreadState[$userId]))
{
$lastReadDate = ($unreadState[$userId] ? 0 : $pmText['msgtime']);
$deleted = false;
$isUnread = $unreadState[$userId] == 0 ? 0 : 1;
}
else
{
$lastReadDate = $pmText['msgtime'];
$deleted = true;
$isUnread = 0;
}
$recipients[$newUserId] = array(
'username' => $this->_convertToUtf8($username, true),
'last_read_date' => $lastReadDate,
'recipient_state' => ($deleted ? 'deleted' : 'active'),
'is_unread' => $isUnread
);
}
$fromUserName = $this->_convertToUtf8($pmText['from_name'], true);
$conversation = array(
'title' => $this->_convertToUtf8($pmText['subject'], true),
'user_id' => $newFromUserId,
'username' => $fromUserName,
'start_date' => $pmText['msgtime'],
'open_invite' => 0,
'conversation_open' => 1
);
$messages = array(
array(
'message_date' => $pmText['msgtime'],
'user_id' => $newFromUserId,
'username' => $fromUserName,
'message' => $this->_sanitizeBbCode($pmText['body'])
)
);
if ($model->importConversation($pmText['id_pm'], $conversation, $recipients, $messages))
{
$total++;
}
}
XenForo_Db::commit();
$this->_session->incrementStepImportTotal($total);
return array($next, $options, $this->_getProgressOutput($next, $options['max']));
}
public function stepForums($start, array $options)
{
$sDb = $this->_sourceDb;
$prefix = $this->_prefix;
/* @var $model XenForo_Model_Import */
$model = $this->_importModel;
if ($start > 0)
{
// after importing everything, rebuild the full permission cache so forums appear
XenForo_Model::create('XenForo_Model_Node')->updateNestedSetInfo();
XenForo_Model::create('XenForo_Model_Permission')->rebuildPermissionCache();
return true;
}
$forums = $sDb->fetchAll('
SELECT *
FROM ' . $prefix . 'boards
ORDER BY id_board
');
$categories = $sDb->fetchAll('
SELECT *
FROM ' . $prefix . 'categories
ORDER BY id_cat
');
$nodes = array();
foreach ($forums AS $key => $forum)
{
$forums[$key] = XenForo_Application::arrayFilterKeys($forum, array(
'id_board', 'id_cat', 'id_parent', 'board_order', 'name', 'member_groups',
'description', 'redirect', 'num_posts', 'num_topics', 'id_profile'
));
$nodes[intval($forum['id_board'])] = $forum['id_board'];
}
// Category / forum IDs can overlap. Map them.
$categoryIdMap = array(0 => 0);
foreach ($categories AS $category)
{
$newId = intval($category['id_cat']);
while (isset($nodes[$newId]))
{
$newId++;
}
$categoryIdMap[intval($category['id_cat'])] = $newId;
$nodes[$newId] = $newId;
$forums[] = array(
'id_board' => $newId,
'id_cat' => 0,
'id_parent' => 0,
'board_order' => $category['cat_order'],
'name' => $category['name'],
'description' => '',
'redirect' => '',
'num_posts' => 0,
'num_topics' => 0,
'id_profile' => 0,
'member_groups' => ''
);
}
if (!$forums)
{
return true;
}
ksort($forums);
$forumTree = array();
$forumPermissions = array();
foreach ($forums AS $forum)
{
$parentId = $categoryIdMap[$forum['id_cat']];
$forumTree[$parentId][$forum['id_board']] = $forum;
$forumPermissionSql = $sDb->query('
SELECT *
FROM ' . $prefix . 'board_permissions
WHERE id_profile = ?
', $forum['id_profile']);
while ($forumPermission = $forumPermissionSql->fetch())
{
$forumPermissions[$forum['id_board']][$forumPermission['id_group']][$forumPermission['permission']] = $forumPermission;
}
}
XenForo_Db::beginTransaction();
$total = $this->_importForumTree(0, $forumTree, $forumPermissions, array(), array_flip($categoryIdMap));
XenForo_Db::commit();
$this->_session->incrementStepImportTotal($total);
return array(1, array(), '');
}
protected function _importForumTree($parentId, array $forumTree, array $forumPermissions, array $forumIdMap = array(), $categoryIdMap = array())
{
if (!isset($forumTree[$parentId]))
{
return 0;
}
$total = 0;
foreach ($forumTree[$parentId] AS $forum)
{
$import = array(
'title' => $this->_convertToUtf8($forum['name'], true),
'description' => $this->_sanitizeBbCode($forum['description'], null, true),
'display_order' => $forum['board_order'],
'parent_node_id' => $this->_mapLookUp($forumIdMap, $forum['id_parent'] ? $forum['id_parent'] : $parentId, 0),
'display_in_list' => 1
);
if ($forum['redirect'])
{
$import['node_type_id'] = 'LinkForum';
$import['link_url'] = $this->_convertToUtf8($forum['redirect']);
$nodeId = $this->_importModel->importLinkForum($forum['id_board'], $import);
}
else if (isset($categoryIdMap[$forum['id_board']]))
{
$import['node_type_id'] = 'Category';
$nodeId = $this->_importModel->importCategory($forum['id_board'], $import);
}
else
{
$import['node_type_id'] = 'Forum';
$import['discussion_count'] = $forum['num_topics'];
$import['message_count'] = $forum['num_posts'];
$import['last_post_date'] = '';
$import['last_post_username'] = '';
$import['last_thread_title'] = '';
$nodeId = $this->_importModel->importForum($forum['id_board'], $import);
}
if ($nodeId)
{
if (!empty($forumPermissions[$forum['id_board']]))
{
$this->_importForumPermissions($nodeId, $forumPermissions[$forum['id_board']], $forum);
}
$forumIdMap[$forum['id_board']] = $nodeId;
$total++;
$total += $this->_importForumTree($forum['id_board'], $forumTree, $forumPermissions, $forumIdMap, $categoryIdMap);
}
}
return $total;
}
protected function _importForumPermissions($nodeId, array $groupPerms, array $forum)
{
if ($this->_groupMap === null)
{
$this->_groupMap = $this->_importModel->getImportContentMap('userGroup');
}
XenForo_Db::beginTransaction();
foreach ($groupPerms AS $oldGroupId => $perms)
{
$newGroupId = $this->_mapLookUp($this->_groupMap, $oldGroupId);
if (!$newGroupId)
{
continue;
}
$groups = array();
if (!empty($forum['member_groups']))
{
$groups = explode(',', $forum['member_groups']);
}
$newPerms = $this->_calculateForumPermissions($perms, in_array($oldGroupId, $groups));
if ($newPerms)
{
$this->_importModel->insertNodePermissionEntries($nodeId, $newGroupId, 0, $newPerms);
}
}
XenForo_Db::commit();
}
protected function _calculateForumPermissions(array $perms, $canView = false)
{
$output = array();
// no equivalents
$output['general']['viewNode'] = $canView ? 'content_allow' : 'reset';
$output['forum']['viewContent'] = $canView ? 'content_allow' : 'reset';
$output['forum']['viewOthers'] = $canView ? 'content_allow' : 'reset';
$output['forum']['postThread'] = (!empty($perms['post_new']['add_deny']) ? 'content_allow' : 'reset');
$output['forum']['postReply'] = (!empty($perms['post_reply_own']['add_deny']) ? 'content_allow' : 'reset');
$output['forum']['editOwnPost'] = (!empty($perms['modify_own']['add_deny']) ? 'content_allow' : 'reset');
$output['forum']['deleteOwnPost'] = (!empty($perms['delete_replies']['add_deny']) ? 'content_allow' : 'reset');
$output['forum']['deleteOwnThread'] = (!empty($perms['delete_own']['add_deny']) ? 'content_allow' : 'reset');
$output['forum']['viewAttachment'] = (!empty($perms['view_attachments']['add_deny']) ? 'content_allow' : 'reset');
$output['forum']['uploadAttachment'] = (!empty($perms['post_attachment']['add_deny']) ? 'content_allow' : 'reset');
$output['forum']['votePoll'] = (!empty($perms['poll_vote']['add_deny']) ? 'content_allow' : 'reset');
return $output;
}
public function stepModerators($start, array $options)
{
$sDb = $this->_sourceDb;
$prefix = $this->_prefix;
/* @var $model XenForo_Model_Import */
$model = $this->_importModel;
$moderators = $sDb->fetchAll('
SELECT members.id_member, members.id_group, members.additional_groups, mods.*,
IF (mods.id_member IS NOT NULL, mods.id_member, members.id_member) AS id_member
FROM ' . $prefix . 'members AS members
LEFT JOIN ' . $prefix . 'moderators AS mods ON
(members.id_member = mods.id_member)
WHERE mods.id_board IS NOT NULL
OR (members.id_group = 2 OR FIND_IN_SET(2, members.additional_groups))
');
if (!$moderators)
{
return true;
}
$contentModerators = array();
$superModerators = array();
$userIds = array();
foreach ($moderators AS $moderator)
{
if ($moderator['id_board'])
{
$contentModerators[$moderator['id_member']][$moderator['id_board']] = $moderator;
}
else
{
$superModerators[$moderator['id_member']] = $moderator;
}
$userIds[] = $moderator['id_member'];
}
$nodeMap = $model->getImportContentMap('node');
$userIdMap = $model->getImportContentMap('user', $userIds);
$total = 0;
$superModPerms = XenForo_Model::create('XenForo_Model_Moderator')->getFullPermissionSet();
XenForo_Db::beginTransaction();
foreach ($superModerators AS $userId => $moderator)
{
$newUserId = $this->_mapLookUp($userIdMap, $userId);
if (!$newUserId)
{
continue;
}
$mod = array(
'user_id' => $newUserId,
'is_super_moderator' => true,
'moderator_permissions' => $superModPerms
);
$model->importGlobalModerator($userId, $mod);
$total++;
}
foreach ($contentModerators AS $userId => $forums)
{
$newUserId = $this->_mapLookUp($userIdMap, $userId);
if (!$newUserId)
{
continue;
}
foreach ($forums AS $forumId => $moderator)
{
$newNodeId = $this->_mapLookUp($nodeMap, $forumId);
if (!$newNodeId)
{
continue;
}
$forum = $sDb->fetchRow('
SELECT id_board, id_profile
FROM ' . $prefix . 'boards
WHERE id_board = ?
', $forumId);
$forumPermissionSql = $sDb->query('
SELECT *
FROM ' . $prefix . 'board_permissions
WHERE id_profile = ?
', $forum['id_profile']);
while ($forumPermission = $forumPermissionSql->fetch())
{
$forumPermissions[$forumPermission['permission']] = $forumPermission;
}
$mod = array(
'user_id' => $newUserId,
'is_super_moderator' => false,
'moderator_permissions' => array()
);
$model->importGlobalModerator($userId, $mod);
$mod = array(
'content_id' => $newNodeId,
'user_id' => $newUserId,
'moderator_permissions' => $this->_convertForumPermissionsForUser($forumPermissions)
);
$model->importNodeModerator($forumId, $userId, $mod);
$total++;
}
}
$this->_session->incrementStepImportTotal($total);
XenForo_Db::commit();
return true;
}
protected function _convertForumPermissionsForUser($perms)
{
$output = array();
$output['forum']['editAny'] = (!empty($perms['modify_any']['add_deny']) ? 'content_allow' : '');
$output['forum']['lockUnlockThread'] = (!empty($perms['lock_any']['add_deny']) ? 'content_allow' : '');
$output['forum']['stickUnstickThread'] = (!empty($perms['make_sticky']['add_deny']) ? 'content_allow' : '');
if (!empty($perms['approve_posts']['add_deny']))
{
$output['forum']['approveUnapprove'] = 'content_allow';
$output['forum']['viewModerated'] = 'content_allow';
}
if (!empty($perms['remove_any']['add_deny']))
{
$output['forum']['deleteAnyPost'] = 'content_allow';
$output['forum']['deleteAnyThread'] = 'content_allow';
$output['forum']['viewDeleted'] = 'content_allow';
$output['forum']['undelete'] = 'content_allow';
}
if (!empty($perms['split_any'])
|| !empty($perms['move_any'])
|| !empty($perms['merge_any'])
)
{
$output['forum']['manageAnyThread'] = 'content_allow';
}
return $output;
}
public function stepThreads($start, array $options)
{
$options = array_merge(array(
'limit' => 100,
'postDateStart' => 0,
'postLimit' => 800,
'max' => false
), $options);
$sDb = $this->_sourceDb;
$prefix = $this->_prefix;
/* @var $model XenForo_Model_Import */
$model = $this->_importModel;
if ($options['max'] === false)
{
$options['max'] = $sDb->fetchOne('
SELECT MAX(id_topic)
FROM ' . $prefix . 'topics
'
);
}
// pull threads from things we actually imported as forums
$threads = $sDb->fetchAll($sDb->limit(
'
SELECT topics.*, fp.subject, fp.poster_time,
lp.poster_time AS last_post_date, lp.poster_name AS last_post_username,
IF (members.member_name IS NOT NULL, members.member_name, fp.poster_name) AS member_name
FROM ' . $prefix . 'topics AS topics FORCE INDEX (PRIMARY)
LEFT JOIN ' . $prefix . 'members AS members ON
(topics.id_member_started = members.id_member)
INNER JOIN ' . $prefix . 'messages AS fp ON
(topics.id_first_msg = fp.id_msg)
INNER JOIN ' . $prefix . 'messages AS lp ON
(topics.id_last_msg = lp.id_msg)
INNER JOIN ' . $prefix . 'boards AS boards ON
(topics.id_board = boards.id_board)
WHERE topics.id_topic >= ' . $sDb->quote($start) . '
AND boards.redirect = \'\'
ORDER BY topics.id_topic
', $options['limit']
));
if (!$threads)
{
return true;
}
$next = 0;
$total = 0;
$totalPosts = 0;
$nodeMap = $model->getImportContentMap('node');
XenForo_Db::beginTransaction();
foreach ($threads AS $thread)
{
if (trim($thread['subject']) === '')
{
continue;
}
$postDateStart = $options['postDateStart'];
$next = $thread['id_topic'] + 1; // uses >=, will be moved back down if need to continue
$options['postDateStart'] = 0;
$maxPosts = $options['postLimit'] - $totalPosts;
$posts = $sDb->fetchAll($sDb->limit(
'
SELECT messages.*,
IF(members.member_name IS NOT NULL, members.member_name, messages.poster_name) AS member_name
FROM ' . $prefix . 'messages AS messages
LEFT JOIN ' . $prefix . 'members AS members ON (messages.id_member = members.id_member)
WHERE messages.id_topic = ' . $sDb->quote($thread['id_topic']) . '
AND messages.poster_time > ' . $sDb->quote($postDateStart) . '
ORDER BY messages.poster_time
', $maxPosts
));
if (!$posts)
{
if ($postDateStart)
{
// continuing thread but it has no more posts
$total++;
}
continue;
}
if ($postDateStart)
{
// continuing thread we already imported
$threadId = $model->mapThreadId($thread['id_topic']);
$position = $this->_db->fetchOne('
SELECT MAX(position)
FROM xf_post
WHERE thread_id = ?
', $threadId);
}
else
{
$forumId = $this->_mapLookUp($nodeMap, $thread['id_board']);
if (!$forumId)
{
continue;
}
if (trim($thread['member_name']) === '')
{
$thread['member_name'] = 'Guest';
}
$import = array(
'title' => $this->_convertToUtf8($thread['subject'], true),
'node_id' => $forumId,
'user_id' => $model->mapUserId($thread['id_member_started'], 0),
'username' => $this->_convertToUtf8($thread['member_name'], true),
'discussion_open' => ($thread['locked'] == 0 ? 1 : 0),
'post_date' => $thread['poster_time'],
'reply_count' => $thread['num_replies'],
'view_count' => $thread['num_views'],
'sticky' => ($thread['is_sticky'] == 1 ? 1 : 0),
'last_post_date' => $thread['last_post_date'],
'last_post_username' => $this->_convertToUtf8($thread['last_post_username'], true),
);
if ($thread['approved'])
{
$import['discussion_state'] = 'visible';
}
else
{
$import['discussion_state'] = 'moderated';
}
$threadId = $model->importThread($thread['id_topic'], $import);
if (!$threadId)
{
continue;
}
if ($thread['id_poll'])
{
$poll = $sDb->fetchRow('
SELECT *
FROM ' . $prefix . 'polls
WHERE id_poll = ?
', $thread['id_poll']);
$responses = $sDb->fetchPairs('
SELECT id_choice, label
FROM ' . $prefix . 'poll_choices
WHERE id_poll = ?
', $thread['id_poll']);
foreach ($responses AS &$value)
{
$value = $this->_sanitizeBbCode($value, null, true);
}
$import = array(
'question' => $this->_sanitizeBbCode($poll['question'], true),
'public_votes' => $poll['hide_results'],
'max_votes' => $poll['max_votes'] ? 1 : 0,
'change_vote' => $poll['change_vote'],
'close_date' => $poll['expire_time']
);
$newPollId = $model->importThreadPoll($thread['id_topic'], $threadId, $import, $responses, $responseIds);
if ($newPollId)
{
$votes = $sDb->fetchAll('
SELECT id_member, id_choice
FROM ' . $prefix . 'log_polls
WHERE id_poll = ' . $sDb->quote($thread['id_poll'])
);
$userIdMap = $model->getUserIdsMapFromArray($votes, 'id_member');
foreach ($votes AS $vote)
{
$userId = $this->_mapLookUp($userIdMap, $vote['id_member']);
if (!$userId)
{
continue;
}
$voteOption = $vote['id_choice'];
if (!isset($responseIds[$voteOption]))
{
continue;
}
$model->importPollVote($newPollId, $userId, $responseIds[$voteOption], 0);
}
}
}
$position = -1;
$subs = $sDb->fetchCol('
SELECT id_member
FROM ' . $prefix . 'log_notify
WHERE id_topic = ' . $sDb->quote($thread['id_topic'])
);
if ($subs)
{
$userIdMap = $model->getImportContentMap('user', array_keys($subs));
foreach ($subs AS $userId => $emailUpdate)
{
$newUserId = $this->_mapLookUp($userIdMap, $userId);
if (!$newUserId)
{
continue;
}
$model->importThreadWatch($newUserId, $threadId, ($emailUpdate ? 1 : 0));
}
}
}
if ($threadId)
{
$quotedPostIds = array();
$threadTitleRegex = '#^(re:\s*)?' . preg_quote($thread['subject'], '#') . '$#i';
$userIdMap = $model->getUserIdsMapFromArray($posts, 'id_member');
foreach ($posts AS $i => $post)
{
if ($post['subject'] !== '' && !preg_match($threadTitleRegex, $post['subject']))
{
$post['body'] = '[b]' . $post['subject'] . "[/b]\n\n" . ltrim($post['body']);
}
if (trim($post['member_name']) === '')
{
$post['member_name'] = 'Guest';
}
$post['body'] = $this->_sanitizeBbCode($post['body']);
$import = array(
'thread_id' => $threadId,
'user_id' => $this->_mapLookUp($userIdMap, $post['id_member'], 0),
'username' => $this->_convertToUtf8($post['member_name'], true),
'post_date' => $post['poster_time'],
'message' => $post['body'],
'attach_count' => 0,
'ip' => $post['poster_ip']
);
if ($post['approved'])
{
$import['message_state'] = 'visible';
$import['position'] = ++$position;
}
else
{
$import['message_state'] = 'moderated';
$import['position'] = $position;
}
$model->importPost($post['id_msg'], $import);
$options['postDateStart'] = $post['poster_time'];
$totalPosts++;
}
if (count($posts) < $maxPosts)
{
// done this thread
$total++;
$options['postDateStart'] = 0;
}
else
{
// not necessarily done the thread; need to pick it up next page
break;
}
}
if (count($posts) < $maxPosts)
{
// done this thread
$total++;
$options['postDateStart'] = 0;
}
else
{
// not necessarily done the thread; need to pick it up next page
break;
}
}
if ($options['postDateStart'])
{
// not done this thread, need to continue with it
$next--;
}
XenForo_Db::commit();
$this->_session->incrementStepImportTotal($total);
return array($next, $options, $this->_getProgressOutput($next - 1, $options['max']));
}
public function stepAttachments($start, array $options)
{
$options = array_merge(array(
'attachmentPaths' => isset($this->_config['attachmentPaths']) ? $this->_config['attachmentPaths'] : '',
'limit' => 50,
'max' => false
), $options);
$sDb = $this->_sourceDb;
$prefix = $this->_prefix;
/* @var $model XenForo_Model_Import */
$model = $this->_importModel;
if ($options['max'] === false)
{
$options['max'] = $sDb->fetchOne('
SELECT MAX(id_attach)
FROM ' . $prefix . 'attachments
');
}
$attachments = $sDb->fetchAll($sDb->limit(
'
SELECT attachments.*, messages.id_member, messages.poster_time
FROM ' . $prefix . 'attachments AS attachments
INNER JOIN ' . $prefix . 'messages AS messages ON
(messages.id_msg = attachments.id_msg)
WHERE attachments.id_attach > ' . $sDb->quote($start) . '
AND attachments.id_msg > 0
AND attachments.id_member = 0
AND attachments.attachment_type = 0
ORDER BY attachments.id_attach
', $options['limit']
));
if (!$attachments)
{
return true;
}
$next = 0;
$total = 0;
$userIdMap = $model->getUserIdsMapFromArray($attachments, 'id_member');
$postIdMap = $model->getPostIdsMapFromArray($attachments, 'id_msg');
$posts = $model->getModelFromCache('XenForo_Model_Post')->getPostsByIds($postIdMap);
foreach ($attachments AS $attachment)
{
$next = $attachment['id_attach'];
$newPostId = $this->_mapLookUp($postIdMap, $attachment['id_msg']);
if (!$newPostId)
{
continue;
}
if (!isset($options['attachmentPaths'][$attachment['id_folder']]))
{
// Single attachment folder environment.
$attachmentPath = $options['attachmentPaths'][0];
}
else
{
$attachmentPath = $options['attachmentPaths'][$attachment['id_folder']];
}
if ($attachment['file_hash'] === '')
{
$filePath = "$attachmentPath/$attachment[filename]";
}
else
{
$filePath = "$attachmentPath/$attachment[id_attach]_$attachment[file_hash]";
}
if (!file_exists($filePath))
{
continue;
}
$attachFile = tempnam(XenForo_Helper_File::getTempDir(), 'xf');
copy($filePath, $attachFile);
$success = $model->importPostAttachment(
$attachment['id_attach'],
$this->_convertToUtf8($attachment['filename'], true),
$attachFile,
$this->_mapLookUp($userIdMap, $attachment['id_member'], 0),
$newPostId,
$attachment['poster_time'],
array('view_count' => $attachment['downloads']),
array($this, 'processAttachmentTags'),
$posts[$newPostId]['message']
);
if ($success)
{
$total++;
}
@unlink($attachFile);
}
$this->_session->incrementStepImportTotal($total);
return array($next, $options, $this->_getProgressOutput($next, $options['max']));
}
public static function processAttachmentTags($oldAttachmentId, $newAttachmentId, $messageText)
{
if (stripos($messageText, '[attach') !== false)
{
$messageText = preg_replace("/\[attach]{$oldAttachmentId}\.SMF[\/attach]/siU", "[ATTACH]{$newAttachmentId}[/ATTACH]", $messageText);
}
return $messageText;
}
protected function _sanitizeBbCode($string, $strip = false)
{
$string = $this->_convertToUtf8($string, true);
// Handles <br /> in message content
$string = preg_replace('#<br\s*/?>#i', "\n", $string);
// Handles quotes
$string = preg_replace('#\[quote\sauthor=(.+)\slink=[^\]]+]#siU', '[quote="$1"]', $string);
// Handles images
$string = preg_replace('#\[img.*](.*)\[/img\]#siU', '[IMG]$1[/IMG]', $string);
// Handles sizes
$string = preg_replace_callback(
'#\[size=([^\]]+)(pt|px|em|)\](.*)\[/size\]#siU',
array($this, '_handleBbCodeSizeCallback'),
$string
);
// Handles list items
$string = str_ireplace('[li]', '[*]', $string);
$string = str_ireplace('[/li]', '', $string);
// Handles FTP tags (converts to URLs)
$string = str_ireplace('[ftp', '[URL', $string);
$string = str_ireplace('[/ftp]', '[/URL]', $string);
// Handles IRUL tags (converts to URLs)
$string = str_ireplace('[iurl', '[URL', $string);
$string = str_ireplace('[/iurl]', '[/URL]', $string);
// Handles non-breaking spaces
$string = str_ireplace(' ', ' ', $string);
// close enough to code
$string = preg_replace('#\[(pre|tt)\](.*)\[/(pre|tt)\]#siU', '[CODE]$2[/CODE]', $string);
// more or less a URL
$string = preg_replace('#\[ftp\](.*)\[/ftp\]#siU', '[URL]$1[/URL]', $string);
// Attachments
$string = preg_replace('#\[attach=(\d+)\]#', '[ATTACH]$1.SMF[/ATTACH]', $string);
// no equivalents, strip
$string = preg_replace('#\[hr\]#siU', '', $string);
$string = preg_replace('#\[move\](.*)\[/move\]#siU', '$1', $string);
$string = preg_replace('#\[sup\](.*)\[/sup\]#siU', '$1', $string);
$string = preg_replace('#\[sub\](.*)\[/sub\]#siU', '$1', $string);
$string = preg_replace('#\[glow=([^\]]+)\](.*)\[/glow\]#siU', '$2', $string);
$string = preg_replace('#\[shadow=([^\]]+)\](.*)\[/shadow\]#siU', '$2', $string);
$string = preg_replace(
'#\[([a-z0-9_\*]+(="[^"]*"|=[^\]]*)?)\]#siU',
($strip ? '' : '[$1]'),
$string
);
$string = preg_replace(
'#\[/([a-z0-9_\*]+)(:[a-z])?\]#siU',
($strip ? '' : '[/$1]'),
$string
);
return $string;
}
protected function _handleBbCodeSizeCallback(array $match)
{
$size = intval($match[1]);
$unit = $match[2];
$text = $match[3];
// Converts pt or em sizes to px (approximately)...
switch ($unit)
{
case 'pt':
$size = round($size * 1.333333);
$unit = 'px';
break;
case 'em':
$size = round($size * 15);
$unit = 'px';
break;
}
return '[SIZE=' . strval($size) . $unit . ']' . $text . '[/SIZE]';
}
}