<parent>wcf.acp.menu.link.maintenance</parent>
<permissions>admin.system.canManageApplication</permissions>
</acpmenuitem>
+
+ <acpmenuitem name="wcf.acp.menu.link.maintenance.import">
+ <controller><![CDATA[wcf\acp\form\DataImport]]></controller>
+ <parent>wcf.acp.menu.link.maintenance</parent>
+ <permissions>admin.system.canImportData</permissions>
+ </acpmenuitem>
<!-- /maintenance -->
<!-- log -->
<name>com.woltlab.wcf.label</name>
<definitionname>com.woltlab.wcf.acl</definitionname>
</type>
+
+ <!-- importers -->
+ <type>
+ <name>com.woltlab.wcf.user</name>
+ <definitionname>com.woltlab.wcf.importer</definitionname>
+ <classname><![CDATA[wcf\system\importer\UserImporter]]></classname>
+ </type>
+ <type>
+ <name>com.woltlab.wcf.user.group</name>
+ <definitionname>com.woltlab.wcf.importer</definitionname>
+ <classname><![CDATA[wcf\system\importer\UserGroupImporter]]></classname>
+ </type>
+ <!-- <type>
+ <name>com.woltlab.wcf.user.option</name>
+ <definitionname>com.woltlab.wcf.importer</definitionname>
+ <classname><![CDATA[wcf\system\importer\UserOptionImporter]]></classname>
+ </type>
+ <type>
+ <name>com.woltlab.wcf.user.avatar</name>
+ <definitionname>com.woltlab.wcf.importer</definitionname>
+ <classname><![CDATA[wcf\system\importer\UserAvatarImporter]]></classname>
+ </type>
+ <type>
+ <name>com.woltlab.wcf.user.comment</name>
+ <definitionname>com.woltlab.wcf.importer</definitionname>
+ <classname><![CDATA[wcf\system\importer\UserCommentImporter]]></classname>
+ </type>
+ <type>
+ <name>com.woltlab.wcf.user.comment.response</name>
+ <definitionname>com.woltlab.wcf.importer</definitionname>
+ <classname><![CDATA[wcf\system\importer\UserCommentResponseImporter]]></classname>
+ </type>
+ <type>
+ <name>com.woltlab.wcf.user.follower</name>
+ <definitionname>com.woltlab.wcf.importer</definitionname>
+ <classname><![CDATA[wcf\system\importer\UserFollowerImporter]]></classname>
+ </type>-->
+ <!-- /importers -->
</import>
</data>
\ No newline at end of file
<name>com.woltlab.wcf.poll</name>
<interfacename><![CDATA[wcf\system\poll\IPollHandler]]></interfacename>
</definition>
+
+ <definition>
+ <name>com.woltlab.wcf.importer</name>
+ <interfacename><![CDATA[wcf\system\importer\IImporter]]></interfacename>
+ </definition>
+
+ <definition>
+ <name>com.woltlab.wcf.exporter</name>
+ <interfacename><![CDATA[wcf\system\exporter\IExporter]]></interfacename>
+ </definition>
</import>
</data>
<defaultvalue>0</defaultvalue>
<admindefaultvalue>1</admindefaultvalue>
</option>
+ <option name="admin.system.canImportData">
+ <categoryname>admin.system</categoryname>
+ <optiontype>boolean</optiontype>
+ <defaultvalue>0</defaultvalue>
+ <admindefaultvalue>1</admindefaultvalue>
+ </option>
<option name="admin.system.canManageCronjob">
<categoryname>admin.system</categoryname>
<optiontype>boolean</optiontype>
--- /dev/null
+{include file='header' pageTitle='wcf.acp.dataImport'}
+
+<script type="text/javascript">
+ //<![CDATA[
+ $(function() {
+ {if $queue|isset}
+ WCF.Language.addObject({
+ {implode from=$importers item=importer}'wcf.acp.dataImport.import.{@$importer}': '{lang}wcf.acp.dataImport.import.{@$importer}{/lang}'{/implode}
+ });
+
+ var $queue = [ {implode from=$queue item=item}'{@$item}'{/implode} ];
+ var $queueID = 0;
+
+ function runProcess() {
+ new WCF.ACP.Worker('mail', 'wcf\\system\\worker\\ImportWorker', WCF.Language.get('wcf.acp.dataImport.import.' + $queue[$queueID]), {
+ objectType: $queue[$queueID]
+ }, function(worker) {
+ $queueID++;
+ if ($queueID < $queue.length) {
+ worker._dialog.wcfDialog('close');
+ runProcess();
+ }
+ });
+ }
+
+ runProcess();
+ {/if}
+
+ $('.jsImportSection').change(function(event) {
+ var $section = $(event.currentTarget);
+ window.dtdesign = $section;
+ if ($section.is(':checked')) {
+ $section.parent().next().find('input[type=checkbox]').prop('checked', 'checked');
+ }
+ else {
+ $section.parent().next().find('input[type=checkbox]').prop('checked', false);
+ }
+ });
+
+ $('.jsImportItem').change(function(event) {
+ var $item = $(event.currentTarget);
+ if ($item.is(':checked')) {
+ $item.parents('.jsImportCollection').find('.jsImportSection').prop('checked', 'checked');
+ }
+ else {
+ var $collection = $item.parents('.jsImportCollection');
+ var $checkedItems = $collection.find('.jsImportItem:checked');
+ if (!$checkedItems.length) {
+ $collection.find('.jsImportSection').prop('checked', false);
+ }
+ }
+ });
+ });
+ //]]>
+</script>
+
+<header class="boxHeadline">
+ <h1>{lang}wcf.acp.dataImport{/lang}</h1>
+</header>
+
+{if $errorField}
+ <p class="error">{lang}wcf.global.form.error{/lang} ({$errorField})</p>
+{/if}
+
+<div class="contentNavigation">
+ {hascontent}
+ <nav>
+ <ul>
+ {content}
+ {event name='contentNavigationButtons'}
+ {/content}
+ </ul>
+ </nav>
+ {/hascontent}
+</div>
+
+{if !$exporterName}
+ {if !$availableExporters|count}
+ <p class="info">{lang}wcf.acp.dataImport.selectExporter.noExporters{/lang}</p>
+ {else}
+ <form method="get" action="{link controller='DataImport'}{/link}">
+ <div class="container containerPadding marginTop">
+ <fieldset>
+ <legend>{lang}wcf.acp.dataImport.selectExporter{/lang}</legend>
+
+ <dl{if $errorField == 'exporterName'} class="formError"{/if}>
+ <dt><label for="exporterName">{lang}wcf.acp.dataImport.exporter{/lang}</label></dt>
+ <dd>
+ <select name="exporterName" id="exporterName">
+ {foreach from=$availableExporters key=availableExporterName item=availableExporter}
+ <option value="{@$availableExporterName}">{lang}wcf.acp.dataImport.exporter.{@$availableExporterName}{/lang}</option>
+ {/foreach}
+ </select>
+ {if $errorField == 'exporterName'}
+ <small class="innerError">
+ {if $errorType == 'empty'}
+ {lang}wcf.global.form.error.empty{/lang}
+ {else}
+ {lang}wcf.acp.dataImport.exporterName.error.{@$errorType}{/lang}
+ {/if}
+ </small>
+ {/if}
+ </dd>
+ </dl>
+
+ {event name='selectExporterFields'}
+ </fieldset>
+ </div>
+
+ <div class="formSubmit">
+ {@SID_INPUT_TAG}
+ <input type="submit" value="{lang}wcf.global.button.submit{/lang}" accesskey="s" />
+ </div>
+ </form>
+ {/if}
+{else}
+ <form method="post" action="{link controller='DataImport'}{/link}">
+ <div class="container containerPadding marginTop">
+ <fieldset>
+ <legend>{lang}wcf.acp.dataImport.configure.data{/lang}</legend>
+
+ <small>{lang}wcf.acp.dataImport.configure.data.description{/lang}</small>
+
+ {foreach from=$supportedData key=objectTypeName item=objectTypes}
+ <dl class="wide">
+ <dd class="jsImportCollection">
+ <label><input type="checkbox" name="selectedData[]" value="{@$objectTypeName}" class="jsImportSection"{if $objectTypeName|in_array:$selectedData}checked="checked" {/if}/> {lang}wcf.acp.dataImport.data.{@$objectTypeName}{/lang}</label>
+ <p>
+ {foreach from=$objectTypes item=objectTypeName}
+ <label><input type="checkbox" name="selectedData[]" value="{@$objectTypeName}" class="jsImportItem"{if $objectTypeName|in_array:$selectedData}checked="checked" {/if}/> {lang}wcf.acp.dataImport.data.{@$objectTypeName}{/lang}</label>
+ {/foreach}
+ </p>
+ </dd>
+ </dl>
+ {/foreach}
+ </fieldset>
+
+ <fieldset>
+ <legend>{lang}wcf.acp.dataImport.configure.settings{/lang}</legend>
+
+
+ </fieldset>
+
+ <fieldset>
+ <legend>{lang}wcf.acp.dataImport.configure.database{/lang}</legend>
+
+ <dl>
+ <dt><label for="dbHost">{lang}wcf.acp.dataImport.configure.database.host{/lang}</label></dt>
+ <dd>
+ <input type="text" id="dbHost" name="dbHost" value="{$dbHost}" class="long" />
+ </dd>
+ </dl>
+
+ <dl>
+ <dt><label for="dbUser">{lang}wcf.acp.dataImport.configure.database.user{/lang}</label></dt>
+ <dd>
+ <input type="text" id="dbUser" name="dbUser" value="{$dbUser}" class="medium" />
+ </dd>
+ </dl>
+
+ <dl>
+ <dt><label for="dbPassword">{lang}wcf.acp.dataImport.configure.database.password{/lang}</label></dt>
+ <dd>
+ <input type="password" id="dbPassword" name="dbPassword" value="{$dbPassword}" class="medium" />
+ </dd>
+ </dl>
+
+ <dl>
+ <dt><label for="dbName">{lang}wcf.acp.dataImport.configure.database.name{/lang}</label></dt>
+ <dd>
+ <input type="text" id="dbName" name="dbName" value="{$dbName}" class="medium" />
+ </dd>
+ </dl>
+
+ <dl>
+ <dt><label for="dbPrefix">{lang}wcf.acp.dataImport.configure.database.prefix{/lang}</label></dt>
+ <dd>
+ <input type="text" id="dbPrefix" name="dbPrefix" value="{$dbPrefix}" class="short" />
+ </dd>
+ </dl>
+ </fieldset>
+
+ <fieldset>
+ <legend>{lang}wcf.acp.dataImport.configure.fileSystem{/lang}</legend>
+
+ <dl>
+ <dt><label for="fileSystemPath">{lang}wcf.acp.dataImport.configure.fileSystem.path{/lang}</label></dt>
+ <dd>
+ <input type="text" id="fileSystemPath" name="fileSystemPath" value="{$fileSystemPath}" class="long" />
+ </dd>
+ </dl>
+ </fieldset>
+ </div>
+
+ <div class="formSubmit">
+ <input type="hidden" name="exporterName" value="{$exporterName}" />
+ <input type="submit" value="{lang}wcf.global.button.submit{/lang}" accesskey="s" />
+ </div>
+ </form>
+{/if}
+
+{include file='footer'}
--- /dev/null
+<?php
+namespace wcf\acp\form;
+use wcf\data\object\type\ObjectTypeCache;
+use wcf\form\AbstractForm;
+use wcf\system\exception\IllegalLinkException;
+use wcf\system\exception\UserInputException;
+use wcf\system\WCF;
+use wcf\util\StringUtil;
+
+/**
+ * Provides the data import form.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.user
+ * @subpackage acp.form
+ * @category Community Framework
+ */
+class DataImportForm extends AbstractForm {
+ /**
+ * @see wcf\page\AbstractPage::$activeMenuItem
+ */
+ public $activeMenuItem = 'wcf.acp.menu.link.maintenance.import';
+
+ /**
+ * @see wcf\page\AbstractPage::$neededPermissions
+ */
+ public $neededPermissions = array('admin.system.canImportData');
+
+ /**
+ * list of available exporters
+ * @var array
+ */
+ public $exporters = array();
+
+ /**
+ * exporter name
+ * @var string
+ */
+ public $exporterName = '';
+
+ /**
+ * exporter object
+ * @var wcf\system\exporter\IExporter
+ */
+ public $exporter = null;
+
+ /**
+ * list of available importers
+ * @var array<string>
+ */
+ public $importers = array();
+
+ /**
+ * list of supported data types
+ * @var array
+ */
+ public $supportedData = array();
+
+ /**
+ * selected data types
+ * @var array
+ */
+ public $selectedData = array();
+
+ /**
+ * database host name
+ * @var string
+ */
+ public $dbHost = '';
+
+ /**
+ * database user name
+ * @var string
+ */
+ public $dbUser = '';
+
+ /**
+ * database password
+ * @var string
+ */
+ public $dbPassword = '';
+
+ /**
+ * database name
+ * @var string
+ */
+ public $dbName = '';
+
+ /**
+ * database table prefix
+ * @var string
+ */
+ public $dbPrefix = '';
+
+ /**
+ * file system path
+ * @var string
+ */
+ public $fileSystemPath = '';
+
+ /**
+ * @see wcf\page\IPage::readParameters()
+ */
+ public function readParameters() {
+ parent::readParameters();
+
+ // get available exporters/importers
+ $this->exporters = ObjectTypeCache::getInstance()->getObjectTypes('com.woltlab.wcf.exporter');
+ $this->importers = array_keys(ObjectTypeCache::getInstance()->getObjectTypes('com.woltlab.wcf.importer'));
+
+ if (isset($_REQUEST['exporterName'])) {
+ $this->exporterName = $_REQUEST['exporterName'];
+ if (!isset($this->exporters[$this->exporterName])) {
+ throw new IllegalLinkException();
+ }
+
+ $this->exporter = $this->exporters[$this->exporterName]->getProcessor();
+ $this->supportedData = $this->exporter->getSupportedData();
+
+ // remove unsupported data
+ foreach ($this->supportedData as $key => $subData) {
+ if (!in_array($key, $this->importers)) {
+ unset($this->supportedData[$key]);
+ continue;
+ }
+
+ foreach ($subData as $key2 => $value) {
+ if (!in_array($value, $this->importers)) {
+ unset($this->supportedData[$key][$key2]);
+ }
+ }
+ }
+
+ // get default database prefix
+ if (!count($_POST)) {
+ $this->dbPrefix = $this->exporter->getDefaultDatabasePrefix();
+ }
+ }
+ }
+
+ /**
+ * @see wcf\form\IForm::readFormParameters()
+ */
+ public function readFormParameters() {
+ parent::readFormParameters();
+
+ if (isset($_POST['selectedData']) && is_array($_POST['selectedData'])) $this->selectedData = $_POST['selectedData'];
+
+ if (isset($_POST['dbHost'])) $this->dbHost = StringUtil::trim($_POST['dbHost']);
+ if (isset($_POST['dbUser'])) $this->dbUser = StringUtil::trim($_POST['dbUser']);
+ if (isset($_POST['dbPassword'])) $this->dbPassword = $_POST['dbPassword'];
+ if (isset($_POST['dbName'])) $this->dbName = StringUtil::trim($_POST['dbName']);
+ if (isset($_POST['dbPrefix'])) $this->dbPrefix = StringUtil::trim($_POST['dbPrefix']);
+ if (isset($_POST['fileSystemPath'])) $this->fileSystemPath = StringUtil::trim($_POST['fileSystemPath']);
+ }
+
+ /**
+ * @see wcf\form\IForm::validate()
+ */
+ public function validate() {
+ parent::validate();
+
+ $this->exporter->setData($this->dbHost, $this->dbUser, $this->dbPassword, $this->dbName, $this->dbPrefix, $this->fileSystemPath);
+
+ // validate database Access
+ if (!$this->exporter->validateDatabaseAccess()) {
+ throw new UserInputException('database');
+ }
+
+ // validate selected data
+ if (!$this->exporter->validateSelectedData($this->selectedData)) {
+ throw new UserInputException('selectedData');
+ }
+
+ // validate file access
+ if (!$this->exporter->validateFileAccess()) {
+ throw new UserInputException('fileSystemPath');
+ }
+ }
+
+ /**
+ * @see wcf\form\IForm::save()
+ */
+ public function save() {
+ parent::save();
+
+ // get queue
+ $queue = $this->exporter->getQueue();
+
+ // save import data
+ WCF::getSession()->register('importData', array(
+ 'exporterName' => $this->exporterName,
+ 'dbHost' => $this->dbHost,
+ 'dbUser' => $this->dbUser,
+ 'dbPassword' => $this->dbPassword,
+ 'dbName' => $this->dbName,
+ 'dbPrefix' => $this->dbPrefix,
+ 'fileSystemPath' => $this->fileSystemPath,
+ ));
+
+ WCF::getTPL()->assign('queue', $queue);
+ }
+
+ /**
+ * @see wcf\page\IPage::assignVariables()
+ */
+ public function assignVariables() {
+ parent::assignVariables();
+
+ WCF::getTPL()->assign(array(
+ 'exporter' => $this->exporter,
+ 'importers' => $this->importers,
+ 'exporterName' => $this->exporterName,
+ 'availableExporters' => $this->exporters,
+ 'supportedData' => $this->supportedData,
+ 'selectedData' => $this->selectedData,
+ 'dbHost' => $this->dbHost,
+ 'dbUser' => $this->dbUser,
+ 'dbPassword' => $this->dbPassword,
+ 'dbName' => $this->dbName,
+ 'dbPrefix' => $this->dbPrefix,
+ 'fileSystemPath' => $this->fileSystemPath
+ ));
+ }
+}
public function create() {
$group = parent::create();
- $groupEditor = new UserGroupEditor($group);
- $groupEditor->updateGroupOptions($this->parameters['options']);
+ if (isset($this->parameters['options'])) {
+ $groupEditor = new UserGroupEditor($group);
+ $groupEditor->updateGroupOptions($this->parameters['options']);
+ }
return $group;
}
--- /dev/null
+<?php
+namespace wcf\system\exporter;
+use wcf\system\database\DatabaseException;
+use wcf\system\database\MySQLDatabase;
+use wcf\system\exception\SystemException;
+use wcf\util\FileUtil;
+
+/**
+ * Basic implementation of IExporter.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage system.exporter
+ * @category Community Framework
+ */
+abstract class AbstractExporter implements IExporter {
+ /**
+ * database host name
+ * @var string
+ */
+ protected $databaseHost = '';
+
+ /**
+ * database username
+ * @var string
+ */
+ protected $databaseUser = '';
+
+ /**
+ * database password
+ * @var string
+ */
+ protected $databasePassword = '';
+
+ /**
+ * database name
+ * @var string
+ */
+ protected $databaseName = '';
+
+ /**
+ * table prefix
+ * @var string
+ */
+ protected $databasePrefix = '';
+
+ /**
+ * file system path
+ * @var string
+ */
+ protected $fileSystemPath = '';
+
+ /**
+ * database connection
+ * @var wcf\system\database\Database
+ */
+ protected $database = null;
+
+ /**
+ * object type => method names
+ * @var array
+ */
+ protected $methods = array();
+
+ /**
+ * limits for items per run
+ * @var array<integer>
+ */
+ protected $limits = array();
+
+ /**
+ * default limit for items per run
+ * @var integer
+ */
+ protected $defaultLimit = 1000;
+
+ /**
+ * @see wcf\system\exporter\IExporter::setData()
+ */
+ public function setData($databaseHost, $databaseUser, $databasePassword, $databaseName, $databasePrefix, $fileSystemPath) {
+ $this->databaseHost = $databaseHost;
+ $this->databaseUser = $databaseUser;
+ $this->databasePassword = $databasePassword;
+ $this->databaseName = $databaseName;
+ $this->databasePrefix = $databasePrefix;
+ $this->fileSystemPath = ($fileSystemPath ? FileUtil::addTrailingSlash($fileSystemPath) : '');
+ }
+
+ /**
+ * @see wcf\system\exporter\IExporter::init()
+ */
+ public function init() {
+ $this->database = new MySQLDatabase($this->databaseHost, $this->databaseUser, $this->databasePassword, $this->databaseName, 0);
+ }
+
+ /**
+ * @see wcf\system\exporter\IExporter::validateDatabaseAccess()
+ */
+ public function validateDatabaseAccess() {
+ try {
+ $this->init();
+ }
+ catch (DatabaseException $e) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * @see wcf\system\exporter\IExporter::getDefaultDatabasePrefix()
+ */
+ public function getDefaultDatabasePrefix() {
+ return '';
+ }
+
+ /**
+ * @see wcf\system\exporter\IExporter::countLoops()
+ */
+ public function countLoops($objectType) {
+ if (!isset($this->methods[$objectType]) || !method_exists($this, 'count'.$this->methods[$objectType])) {
+ throw new SystemException("unknown object type '".$objectType."' given");
+ }
+
+ $count = call_user_func(array($this, 'count'.$this->methods[$objectType]));
+ $limit = (isset($this->limits[$objectType]) ? $this->limits[$objectType] : $this->defaultLimit);
+ return ceil($count / $limit);
+ }
+
+ /**
+ * @see wcf\system\exporter\IExporter::exportData()
+ */
+ public function exportData($objectType, $loopCount = 0) {
+ if (!isset($this->methods[$objectType]) || !method_exists($this, 'export'.$this->methods[$objectType])) {
+ throw new SystemException("unknown object type '".$objectType."' given");
+ }
+
+ $limit = (isset($this->limits[$objectType]) ? $this->limits[$objectType] : $this->defaultLimit);
+ call_user_func(array($this, 'export'.$this->methods[$objectType]), $loopCount * $limit, $limit);
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\exporter;
+
+/**
+ * Basic interface for all exporters.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage system.exporter
+ * @category Community Framework
+ */
+interface IExporter {
+ /**
+ * Sets database access data.
+ *
+ * @param string $databaseHost
+ * @param string $databaseUser
+ * @param string $databasePassword
+ * @param string $databaseName
+ * @param string $databasePrefix
+ * @param string $fileSystemPath
+ */
+ public function setData($databaseHost, $databaseUser, $databasePassword, $databaseName, $databasePrefix, $fileSystemPath);
+
+ /**
+ * Initializes this exporter.
+ */
+ public function init();
+
+ /**
+ * Counts the number of required loops for given type.
+ *
+ * @param string $objectType
+ * @return integer
+ */
+ public function countLoops($objectType);
+
+ /**
+ * Runs the data export.
+ *
+ * @param string $objectType
+ * @param integer $loopCount
+ */
+ public function exportData($objectType, $loopCount = 0);
+
+ /**
+ * Validates database access. Returns false on failure.
+ *
+ * @return boolean
+ */
+ public function validateDatabaseAccess();
+
+ /**
+ * Validates given file system path. Returns false on failure.
+ *
+ * @return boolean
+ */
+ public function validateFileAccess();
+
+ /**
+ * Validates the selected data types. Returns false on failure.
+ *
+ * @param array $selectedData
+ * @return boolean
+ */
+ public function validateSelectedData(array $selectedData);
+
+ /**
+ * Returns the import worker queue.
+ *
+ * @return array
+ */
+ public function getQueue();
+
+ /**
+ * Returns the supported data types.
+ *
+ * @return array<string>
+ */
+ public function getSupportedData();
+
+ /**
+ * Returns a default database table prefix.
+ *
+ * @return string
+ */
+ public function getDefaultDatabasePrefix();
+}
--- /dev/null
+<?php
+namespace wcf\system\importer;
+
+/**
+ * Basic interface for all importer.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage system.importer
+ * @category Community Framework
+ */
+interface IImporter {
+ /**
+ * Imports a data set.
+ *
+ * @param mixed $oldID
+ * @param array $data
+ * @return mixed new id
+ */
+ public function import($oldID, array $data);
+}
--- /dev/null
+<?php
+namespace wcf\system\importer;
+use wcf\data\object\type\ObjectTypeCache;
+use wcf\system\exception\SystemException;
+use wcf\system\SingletonFactory;
+use wcf\system\WCF;
+
+/**
+ * Handles data import.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage system.importer
+ * @category Community Framework
+ */
+class ImportHandler extends SingletonFactory {
+ /**
+ * id map cache
+ * @var array
+ */
+ protected $idMappingCache = array();
+
+ /**
+ * list of available importers
+ * @var array
+ */
+ protected $objectTypes = array();
+
+ /**
+ * list of available importer processors
+ * @var array
+ */
+ protected $importers = array();
+
+ /**
+ * @see wcf\system\SingletonFactory::init()
+ */
+ protected function init() {
+ $this->objectTypes = ObjectTypeCache::getInstance()->getObjectTypes('com.woltlab.wcf.importer');
+ }
+
+ /**
+ * Gets a data importer.
+ *
+ * @param string $type
+ * @return wcf\system\importer\IImporter
+ */
+ public function getImporter($type) {
+ if (!isset($this->importers[$type])) {
+ if (!isset($this->objectTypes[$type])) {
+ throw new SystemException("unknown importer '".$type."'");
+ }
+
+ $this->importers[$type] = $this->objectTypes[$type]->getProcessor();
+ }
+
+ return $this->importers[$type];
+ }
+
+ /**
+ * Gets a new id from id mapping.
+ *
+ * @param string $type
+ * @param mixed $oldID
+ * @return integer $newID
+ */
+ public function getNewID($type, $oldID) {
+ $objectTypeID = $this->objectTypes[$type]->objectTypeID;
+
+ if (!isset($this->idMappingCache[$objectTypeID][$oldID])) {
+ $this->idMappingCache[$objectTypeID][$oldID] = 0;
+
+ $sql = "SELECT newID
+ FROM wcf".WCF_N."_import_mapping
+ WHERE objectTypeID = ?
+ AND oldID = ?";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute(array($objectTypeID, $oldID));
+ $row = $statement->fetchArray();
+ if ($row !== false) $this->idMappingCache[$objectTypeID][$oldID] = $row['newID'];
+ }
+
+ return $this->idMappingCache[$objectTypeID][$oldID];
+ }
+
+ /**
+ * Saves an id mapping.
+ *
+ * @param string $type
+ * @param integer $oldID
+ * @param integer $newID
+ */
+ public function saveNewID($type, $oldID, $newID) {
+ $objectTypeID = $this->objectTypes[$type]->objectTypeID;
+
+ $sql = "INSERT IGNORE INTO wcf".WCF_N."_import_mapping
+ (objectTypeID, oldID, newID)
+ VALUES (?, ?, ?)";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute(array($objectTypeID, $oldID, $newID));
+
+ unset($this->idMappingCache[$objectTypeID][$oldID]);
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\importer;
+use wcf\data\user\group\UserGroupAction;
+
+/**
+ * Imports user groups.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage system.importer
+ * @category Community Framework
+ */
+class UserGroupImporter implements IImporter {
+ /**
+ * @see wcf\system\importer::import()
+ */
+ public function import($oldID, array $data) {
+ $action = new UserGroupAction(array(), 'create', array(
+ 'data' => $data
+ ));
+ $returnValues = $action->executeAction();
+ $newGroupID = $returnValues['returnValues']->groupID;
+
+ ImportHandler::getInstance()->saveNewID('com.woltlab.wcf.user.group', $oldID, $newGroupID);
+
+ return $newGroupID;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\importer;
+use wcf\data\user\UserAction;
+use wcf\system\database\DatabaseException;
+use wcf\system\WCF;
+
+/**
+ * Imports users.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage system.importer
+ * @category Community Framework
+ */
+class UserImporter implements IImporter {
+ /**
+ * @see wcf\system\importer::import()
+ */
+ public function import($oldID, array $data) {
+ $sql = "SELECT COUNT(*) AS count
+ FROM wcf".WCF_N."_user
+ WHERE userID = ?";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute(array($oldID));
+ $row = $statement->fetchArray();
+ if (!$row['count']) $data['userID'] = $oldID;
+
+ $action = new UserAction(array(), 'create', array(
+ 'data' => $data
+ ));
+ $returnValues = $action->executeAction();
+
+ $newUserID = $returnValues['returnValues']->groupID;
+ ImportHandler::getInstance()->saveNewID('com.woltlab.wcf.user', $oldID, $newUserID);
+
+ return $newUserID;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\worker;
+use wcf\data\object\type\ObjectTypeCache;
+use wcf\system\exception\SystemException;
+use wcf\system\WCF;
+
+/**
+ * Worker implementation for data import.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage system.worker
+ * @category Community Framework
+ */
+class ImportWorker extends AbstractWorker {
+ /**
+ * import data
+ * @var array
+ */
+ protected $importData = null;
+
+ /**
+ * exporter object
+ * @var wcf\system\exporter\IExporter
+ */
+ protected $exporter = null;
+
+ /**
+ * @see wcf\system\worker\IWorker::validate()
+ */
+ public function validate() {
+ WCF::getSession()->checkPermissions(array('admin.system.canImportData'));
+
+ if (!isset($this->parameters['objectType'])) {
+ throw new SystemException("parameter 'objectType' missing");
+ }
+
+ // get import data
+ $this->importData = WCF::getSession()->getVar('importData');
+ if ($this->importData === null) {
+ throw new SystemException("import data missing");
+ }
+
+ // get exporter
+ $this->exporter = ObjectTypeCache::getInstance()->getObjectTypeByName('com.woltlab.wcf.exporter', $this->importData['exporterName'])->getProcessor();
+
+ // set data
+ $this->exporter->setData($this->importData['dbHost'], $this->importData['dbUser'], $this->importData['dbPassword'], $this->importData['dbName'], $this->importData['dbPrefix'], $this->importData['fileSystemPath']);
+ $this->exporter->init();
+ }
+
+ /**
+ * @see wcf\system\worker\AbstractWorker::countObjects()
+ */
+ protected function countObjects() {
+ $this->count = $this->exporter->countLoops($this->parameters['objectType']);
+ }
+
+ /**
+ * @see wcf\system\worker\IWorker::getProgress()
+ */
+ public function getProgress() {
+ $this->countObjects();
+
+ if (!$this->count) {
+ return 100;
+ }
+
+ $progress = (($this->loopCount + 1) / $this->count) * 100;
+ if ($progress > 100) $progress = 100;
+ return round($progress, 0);
+ }
+
+ /**
+ * @see wcf\system\worker\IWorker::execute()
+ */
+ public function execute() {
+ if (!$this->count) {
+ return;
+ }
+
+ $this->exporter->exportData($this->parameters['objectType'], $this->loopCount);
+ }
+
+ /**
+ * @see wcf\system\worker\IWorker::getProceedURL()
+ */
+ public function getProceedURL() {
+ return '';
+ }
+}
UNIQUE KEY packageID (packageID, environment, eventClassName, eventName, listenerClassName)
);
+DROP TABLE IF EXISTS wcf1_import_mapping;
+CREATE TABLE wcf1_import_mapping (
+ objectTypeID INT(10) NOT NULL,
+ oldID VARCHAR(255) NOT NULL,
+ newID INT(10) NOT NULL,
+ UNIQUE KEY (objectTypeID, oldID)
+);
+
DROP TABLE IF EXISTS wcf1_label;
CREATE TABLE wcf1_label (
labelID INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
ALTER TABLE wcf1_dashboard_option ADD FOREIGN KEY (objectTypeID) REFERENCES wcf1_object_type (objectTypeID) ON DELETE CASCADE;
ALTER TABLE wcf1_dashboard_option ADD FOREIGN KEY (boxID) REFERENCES wcf1_dashboard_box (boxID) ON DELETE CASCADE;
+ALTER TABLE wcf1_import_mapping ADD FOREIGN KEY (objectTypeID) REFERENCES wcf1_object_type (objectTypeID) ON DELETE CASCADE;
+
ALTER TABLE wcf1_tracked_visit ADD FOREIGN KEY (objectTypeID) REFERENCES wcf1_object_type (objectTypeID) ON DELETE CASCADE;
ALTER TABLE wcf1_tracked_visit ADD FOREIGN KEY (userID) REFERENCES wcf1_user (userID) ON DELETE CASCADE;