3 use wcf\data\language\LanguageEditor
;
4 use wcf\data\language\SetupLanguage
;
5 use wcf\data\package\installation\queue\PackageInstallationQueueEditor
;
6 use wcf\data\user\User
;
7 use wcf\data\user\UserAction
;
8 use wcf\system\cache\builder\LanguageCacheBuilder
;
9 use wcf\system\database\util\SQLParser
;
10 use wcf\system\exception\SystemException
;
11 use wcf\system\exception\UserInputException
;
12 use wcf\system\io\File
;
13 use wcf\system\io\Tar
;
14 use wcf\system\language\LanguageFactory
;
15 use wcf\system\package\PackageArchive
;
16 use wcf\system\request\RouteHandler
;
17 use wcf\system\session\ACPSessionFactory
;
18 use wcf\system\session\SessionHandler
;
19 use wcf\system\setup\Installer
;
20 use wcf\system\template\SetupTemplateEngine
;
23 use wcf\util\DirectoryUtil
;
24 use wcf\util\FileUtil
;
25 use wcf\util\StringUtil
;
26 use wcf\util\UserUtil
;
30 define('PACKAGE_ID', '0');
31 define('HTTP_ENABLE_NO_CACHE_HEADERS', 0);
32 define('HTTP_ENABLE_GZIP', 0);
33 define('HTTP_GZIP_LEVEL', 0);
34 define('HTTP_SEND_X_FRAME_OPTIONS', 0);
35 define('CACHE_SOURCE_TYPE', 'disk');
36 define('MODULE_MASTER_PASSWORD', 1);
37 define('ENABLE_DEBUG_MODE', 1);
38 define('ENABLE_BENCHMARK', 0);
41 * Executes the installation of the basic WCF systems.
44 * @copyright 2001-2015 WoltLab GmbH
45 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
46 * @package com.woltlab.wcf
48 * @category Community Framework
50 class WCFSetup
extends WCF
{
52 * list of available languages
55 protected static $availableLanguages = array();
58 * language code of selected installation language
61 protected static $selectedLanguageCode = 'en';
64 * selected languages to be installed
67 protected static $selectedLanguages = array();
70 * directory of the framework
73 protected static $wcfDir = '';
76 * list of installed files
79 protected static $installedFiles = array();
82 * name of installed primary application
85 protected static $setupPackageName = 'WoltLab Community Framework';
88 * indicates if developer mode is used to install
91 protected static $developerMode = 0;
97 protected static $dbClasses = array(
98 'MySQLDatabase' => array('class' => 'wcf\system\database\MySQLDatabase', 'minversion' => '5.1.17')//, // MySQL 5.1.17+
99 //'PostgreSQLDatabase' => array('class' => 'wcf\system\database\PostgreSQLDatabase', 'minversion' => '8.2.0') // PostgreSQL 8.2.0+
103 * Calls all init functions of the WCFSetup class and starts the setup process.
105 public function __construct() {
107 $this->initMagicQuotes();
108 $this->getDeveloperMode();
109 $this->getLanguageSelection();
111 $this->initLanguage();
113 self
::getLanguage()->loadLanguage();
114 $this->getPackageName();
121 * Gets the status of the developer mode.
123 protected static function getDeveloperMode() {
124 if (isset($_GET['dev'])) self
::$developerMode = intval($_GET['dev']);
125 else if (isset($_POST['dev'])) self
::$developerMode = intval($_POST['dev']);
129 * Gets the selected language.
131 protected static function getLanguageSelection() {
132 self
::$availableLanguages = self
::getAvailableLanguages();
134 if (isset($_REQUEST['languageCode']) && isset(self
::$availableLanguages[$_REQUEST['languageCode']])) {
135 self
::$selectedLanguageCode = $_REQUEST['languageCode'];
138 self
::$selectedLanguageCode = LanguageFactory
::getPreferredLanguage(array_keys(self
::$availableLanguages), self
::$selectedLanguageCode);
141 if (isset($_POST['selectedLanguages']) && is_array($_POST['selectedLanguages'])) {
142 self
::$selectedLanguages = $_POST['selectedLanguages'];
147 * Gets the available database classes.
151 protected static function getAvailableDBClasses() {
152 $availableDBClasses = array();
153 foreach (self
::$dbClasses as $class => $data) {
154 if (call_user_func(array($data['class'], 'isSupported'))) {
155 $availableDBClasses[$class] = $data;
159 return $availableDBClasses;
163 * Gets the selected wcf dir from request.
165 protected static function getWCFDir() {
166 if (isset($_REQUEST['wcfDir']) && $_REQUEST['wcfDir'] != '') {
167 self
::$wcfDir = FileUtil
::addTrailingSlash(FileUtil
::unifyDirSeparator($_REQUEST['wcfDir']));
168 if (@file_exists
(self
::$wcfDir)) {
169 define('RELATIVE_WCF_DIR', FileUtil
::getRelativePath(INSTALL_SCRIPT_DIR
, self
::$wcfDir));
173 define('WCF_DIR', self
::$wcfDir);
177 * Initialises the language engine.
179 protected function initLanguage() {
181 mb_internal_encoding('UTF-8');
182 if (function_exists('mb_regex_encoding')) mb_regex_encoding('UTF-8');
185 // init setup language
186 self
::$languageObj = new SetupLanguage(null, array(
187 'languageCode' => self
::$selectedLanguageCode
192 * Initialises the template engine.
194 protected function initTPL() {
195 self
::$tplObj = SetupTemplateEngine
::getInstance();
196 self
::getTPL()->setLanguageID((self
::$selectedLanguageCode == 'en' ?
0 : 1));
197 self
::getTPL()->setCompileDir(TMP_DIR
);
198 self
::getTPL()->addApplication('wcf', PACKAGE_ID
, TMP_DIR
);
199 self
::getTPL()->registerPrefilter(array('lang'));
200 self
::getTPL()->assign(array(
202 'tmpFilePrefix' => TMP_FILE_PREFIX
,
203 'languageCode' => self
::$selectedLanguageCode,
204 'selectedLanguages' => self
::$selectedLanguages,
205 'wcfDir' => self
::$wcfDir,
206 'developerMode' => self
::$developerMode
211 * Returns all languages from WCFSetup.tar.gz.
215 protected static function getAvailableLanguages() {
216 $languages = $match = array();
217 $tar = new Tar(SETUP_FILE
);
218 foreach ($tar->getContentList() as $file) {
219 if (strpos($file['filename'], 'setup/lang/') === 0 && substr($file['filename'], -4) == '.xml') {
221 $xml->load(TMP_DIR
.$file['filename']);
222 $languageCode = LanguageEditor
::readLanguageCodeFromXML($xml);
223 $languageName = LanguageEditor
::readLanguageNameFromXML($xml);
225 $languages[$languageCode] = $languageName;
230 // sort languages by language name
237 * Calculates the current state of the progress bar.
239 * @param integer $currentStep
241 protected function calcProgress($currentStep) {
242 // calculate progress
243 $progress = round((100 / 18) * ++
$currentStep, 0);
244 self
::getTPL()->assign(array('progress' => $progress));
248 * Executes the setup steps.
250 protected function setup() {
252 if (isset($_REQUEST['step'])) $step = $_REQUEST['step'];
253 else $step = 'selectSetupLanguage';
255 // execute current step
257 case 'selectSetupLanguage':
258 if (!self
::$developerMode) {
259 $this->calcProgress(0);
260 $this->selectSetupLanguage();
265 if (!self
::$developerMode) {
266 $this->calcProgress(1);
267 $this->showLicense();
271 case 'showSystemRequirements':
272 if (!self
::$developerMode) {
273 $this->calcProgress(2);
274 $this->showSystemRequirements();
279 $this->calcProgress(3);
280 $this->searchWcfDir();
284 $this->calcProgress(4);
288 case 'selectLanguages':
289 $this->calcProgress(5);
290 $this->selectLanguages();
294 $this->calcProgress(6);
295 $this->configureDB();
300 if (isset($_POST['offset'])) {
301 $currentStep +
= intval($_POST['offset']);
304 $this->calcProgress($currentStep);
309 $this->calcProgress(14);
313 case 'installLanguage':
314 $this->calcProgress(15);
315 $this->installLanguage();
319 $this->calcProgress(16);
323 case 'installPackages':
324 $this->calcProgress(17);
325 $this->installPackages();
331 * Shows the first setup page.
333 protected function selectSetupLanguage() {
334 WCF
::getTPL()->assign(array(
335 'availableLanguages' => self
::$availableLanguages,
336 'nextStep' => 'showLicense'
338 WCF
::getTPL()->display('stepSelectSetupLanguage');
342 * Shows the license agreement.
344 protected function showLicense() {
345 if (isset($_POST['send'])) {
346 if (isset($_POST['accepted'])) {
347 $this->gotoNextStep('showSystemRequirements');
351 WCF
::getTPL()->assign(array('missingAcception' => true));
356 if (file_exists(TMP_DIR
.'setup/license/license_'.self
::$selectedLanguageCode.'.txt')) {
357 $license = file_get_contents(TMP_DIR
.'setup/license/license_'.self
::$selectedLanguageCode.'.txt');
360 $license = file_get_contents(TMP_DIR
.'setup/license/license_en.txt');
363 WCF
::getTPL()->assign(array(
364 'license' => $license,
365 'nextStep' => 'showLicense'
367 WCF
::getTPL()->display('stepShowLicense');
371 * Shows the system requirements.
373 protected function showSystemRequirements() {
377 $system['phpVersion']['value'] = phpversion();
378 $comparePhpVersion = preg_replace('/^(\d+\.\d+\.\d+).*$/', '\\1', $system['phpVersion']['value']);
379 $system['phpVersion']['result'] = (version_compare($comparePhpVersion, '5.3.2') >= 0);
382 $system['sql']['value'] = array_keys(self
::getAvailableDBClasses());
383 $system['sql']['result'] = !empty($system['sql']['value']);
385 // upload_max_filesize
386 $system['uploadMaxFilesize']['value'] = ini_get('upload_max_filesize');
387 $system['uploadMaxFilesize']['result'] = (intval($system['uploadMaxFilesize']['value']) > 0);
390 $system['gdLib']['value'] = '0.0.0';
391 if (function_exists('gd_info')) {
394 if (preg_match('!([0-9]+\.[0-9]+(?:\.[0-9]+)?)!', $temp['GD Version'], $match)) {
395 if (preg_match('/^[0-9]+\.[0-9]+$/', $match[1])) $match[1] .= '.0';
396 $system['gdLib']['value'] = $match[1];
399 $system['gdLib']['result'] = (version_compare($system['gdLib']['value'], '2.0.0') >= 0);
402 $system['memoryLimit']['value'] = ini_get('memory_limit');
403 $system['memoryLimit']['result'] = $this->compareMemoryLimit();
405 WCF
::getTPL()->assign(array(
407 'nextStep' => 'searchWcfDir'
409 WCF
::getTPL()->display('stepShowSystemRequirements');
413 * Returns true if memory_limit is set to at least 128 MB
417 protected function compareMemoryLimit() {
418 $memoryLimit = ini_get('memory_limit');
421 if ($memoryLimit == -1) {
425 // completely numeric, PHP assumes byte
426 if (is_numeric($memoryLimit)) {
427 $memoryLimit = $memoryLimit / 1024 / 1024;
428 return ($memoryLimit >= 128);
431 // PHP supports 'K', 'M' and 'G' shorthand notation
432 if (preg_match('~^(\d+)([KMG])$~', $memoryLimit, $matches)) {
433 switch ($matches[2]) {
435 $memoryLimit = $matches[1] * 1024;
436 return ($memoryLimit >= 128);
440 return ($matches[1] >= 128);
444 return ($matches[1] >= 1);
453 * Searches the wcf dir.
455 protected function searchWcfDir() {
457 $wcfDir = self
::$wcfDir;
460 $wcfDir = FileUtil
::unifyDirSeparator(INSTALL_SCRIPT_DIR
).'wcf/';
463 $invalidDirectory = false;
464 if (@is_file
($wcfDir.'lib/system/WCF.class.php')) {
465 $invalidDirectory = true;
470 if (!empty($_SERVER['SERVER_NAME'])) $domainName = RouteHandler
::getProtocol() . $_SERVER['SERVER_NAME'];
472 if (!empty($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] != 80) $domainName .= ':' . $_SERVER['SERVER_PORT'];
474 $installScriptUrl = '';
475 if (!empty($_SERVER['REQUEST_URI'])) $installScriptUrl = FileUtil
::removeLeadingSlash(FileUtil
::removeTrailingSlash(FileUtil
::unifyDirSeparator(dirname($_SERVER['REQUEST_URI']))));
477 WCF
::getTPL()->assign(array(
478 'nextStep' => 'unzipFiles',
479 'invalidDirectory' => $invalidDirectory,
481 'domainName' => $domainName,
482 'installScriptUrl' => $installScriptUrl,
483 'installScriptDir' => FileUtil
::unifyDirSeparator(INSTALL_SCRIPT_DIR
)
486 WCF
::getTPL()->display('stepSearchWcfDir');
490 * Unzips the files of the wcfsetup tar archive.
492 protected function unzipFiles() {
493 // WCF seems to be installed, abort
494 if (@is_file
(self
::$wcfDir.'lib/system/WCF.class.php')) {
495 throw new SystemException('Target directory seems to be an existing installation of WCF, unable to continue.');
498 // WCF not yet installed, install files first
501 $this->installFiles();
503 catch (\Exception
$e) {
504 WCF
::getTPL()->assign(array('exception' => $e));
505 $this->searchWcfDir();
509 $this->gotoNextStep('selectLanguages');
514 * Shows the page for choosing the installed languages.
516 protected function selectLanguages() {
517 $errorField = $errorType = '';
519 // skip step in developer mode
520 // select all available languages automatically
521 if (self
::$developerMode) {
522 self
::$selectedLanguages = array();
523 foreach (self
::$availableLanguages as $languageCode => $language) {
524 self
::$selectedLanguages[] = $languageCode;
527 self
::getTPL()->assign(array('selectedLanguages' => self
::$selectedLanguages));
528 $this->gotoNextStep('configureDB');
532 // start error handling
533 if (isset($_POST['send'])) {
535 // no languages selected
536 if (empty(self
::$selectedLanguages)) {
537 throw new UserInputException('selectedLanguages');
541 foreach (self
::$selectedLanguages as $language) {
542 if (!isset(self
::$availableLanguages[$language])) {
543 throw new UserInputException('selectedLanguages');
549 $this->gotoNextStep('configureDB');
552 catch (UserInputException
$e) {
553 $errorField = $e->getField();
554 $errorType = $e->getType();
558 self
::$selectedLanguages[] = self
::$selectedLanguageCode;
559 WCF
::getTPL()->assign(array('selectedLanguages' => self
::$selectedLanguages));
562 WCF
::getTPL()->assign(array(
563 'errorField' => $errorField,
564 'errorType' => $errorType,
565 'availableLanguages' => self
::$availableLanguages,
566 'nextStep' => 'selectLanguages'
568 WCF
::getTPL()->display('stepSelectLanguages');
572 * Shows the page for configurating the database connection.
574 protected function configureDB() {
575 $availableDBClasses = self
::getAvailableDBClasses();
576 $dbHost = 'localhost';
582 // set $dbClass to first item in $availableDBClasses
583 foreach ($availableDBClasses as $dbClass) {
584 $dbClass = $dbClass['class'];
588 if (isset($_POST['send'])) {
589 if (isset($_POST['dbHost'])) $dbHost = $_POST['dbHost'];
590 if (isset($_POST['dbUser'])) $dbUser = $_POST['dbUser'];
591 if (isset($_POST['dbPassword'])) $dbPassword = $_POST['dbPassword'];
592 if (isset($_POST['dbName'])) $dbName = $_POST['dbName'];
594 // ensure that $dbNumber is zero or a positive integer
595 if (isset($_POST['dbNumber'])) $dbNumber = max(0, intval($_POST['dbNumber']));
596 if (isset($_POST['dbClass'])) $dbClass = $_POST['dbClass'];
600 if (preg_match('/^(.+?):(\d+)$/', $dbHost, $match)) {
602 $dbPort = intval($match[2]);
609 foreach ($availableDBClasses as $dbData) {
610 if ($dbData['class'] == $dbClass) {
617 throw new SystemException("Database type '".$dbClass."'. is not available on this system.");
620 // check connection data
621 $db = new $dbClass($dbHost, $dbUser, $dbPassword, $dbName, $dbPort, true);
625 if (!empty($availableDBClasses[$dbClass]['minversion'])) {
626 $compareSQLVersion = preg_replace('/^(\d+\.\d+\.\d+).*$/', '\\1', $db->getVersion());
627 if (!(version_compare($compareSQLVersion, $availableDBClasses[$dbClass]['minversion']) >= 0)) {
628 throw new SystemException("Insufficient SQL version '".$compareSQLVersion."'. Version '".$availableDBClasses[$dbClass]['minversion']."' or greater is needed.");
631 // check innodb support
632 if ($dbClass == 'wcf\system\database\MySQLDatabase') {
633 $sql = "SHOW ENGINES";
634 $statement = $db->prepareStatement($sql);
635 $statement->execute();
637 while ($row = $statement->fetchArray()) {
638 if ($row['Engine'] == 'InnoDB' && in_array($row['Support'], array('DEFAULT', 'YES'))) {
645 throw new SystemException("Support for InnoDB is missing.");
649 // check for table conflicts
650 $conflictedTables = $this->getConflictedTables($db, $dbNumber);
653 if (empty($conflictedTables)) {
654 // connection successfully established
655 // write configuration to config.inc.php
656 $file = new File(WCF_DIR
.'config.inc.php');
657 $file->write("<?php\n");
658 $file->write("\$dbHost = '".str_replace("'", "\\'", $dbHost)."';\n");
659 $file->write("\$dbPort = ".$dbPort.";\n");
660 $file->write("\$dbUser = '".str_replace("'", "\\'", $dbUser)."';\n");
661 $file->write("\$dbPassword = '".str_replace("'", "\\'", $dbPassword)."';\n");
662 $file->write("\$dbName = '".str_replace("'", "\\'", $dbName)."';\n");
663 $file->write("\$dbClass = '".str_replace("'", "\\'", $dbClass)."';\n");
664 $file->write("if (!defined('WCF_N')) define('WCF_N', $dbNumber);\n");
668 $this->gotoNextStep('createDB');
671 // show configure template again
673 WCF
::getTPL()->assign(array('conflictedTables' => $conflictedTables));
676 catch (SystemException
$e) {
677 WCF
::getTPL()->assign(array('exception' => $e));
680 WCF
::getTPL()->assign(array(
683 'dbPassword' => $dbPassword,
685 'dbNumber' => $dbNumber,
686 'dbClass' => $dbClass,
687 'availableDBClasses' => $availableDBClasses,
688 'nextStep' => 'configureDB'
690 WCF
::getTPL()->display('stepConfigureDB');
694 * Checks if in the chosen database are tables in conflict with the wcf tables
695 * which will be created in the next step.
697 * @param \wcf\system\database\Database $db
698 * @param integer $dbNumber
700 protected function getConflictedTables($db, $dbNumber) {
701 // get content of the sql structure file
702 $sql = file_get_contents(TMP_DIR
.'setup/db/install.sql');
704 // installation number value 'n' (WCF_N) must be reflected in the executed sql queries
705 $sql = str_replace('wcf1_', 'wcf'.$dbNumber.'_', $sql);
707 // get all tablenames which should be created
708 preg_match_all("%CREATE\s+TABLE\s+(\w+)%", $sql, $matches);
710 // get all installed tables from chosen database
711 $existingTables = $db->getEditor()->getTableNames();
713 // check if existing tables are in conflict with wcf tables
714 $conflictedTables = array();
715 foreach ($existingTables as $existingTableName) {
716 foreach ($matches[1] as $wcfTableName) {
717 if ($existingTableName == $wcfTableName) {
718 $conflictedTables[] = $wcfTableName;
722 return $conflictedTables;
726 * Creates the database structure of the wcf.
728 protected function createDB() {
731 // get content of the sql structure file
732 $sql = file_get_contents(TMP_DIR
.'setup/db/install.sql');
735 $sqlData = explode('/* SQL_PARSER_OFFSET */', $sql);
736 $offset = (isset($_POST['offset'])) ?
intval($_POST['offset']) : 0;
737 if (!isset($sqlData[$offset])) {
738 throw new SystemException("Offset for SQL parser is out of bounds, ".$offset." was requested, but there are only ".count($sqlData)." sections");
740 $sql = $sqlData[$offset];
742 // installation number value 'n' (WCF_N) must be reflected in the executed sql queries
743 $sql = str_replace('wcf1_', 'wcf'.WCF_N
.'_', $sql);
745 // execute sql queries
746 $parser = new SQLParser($sql);
750 preg_match_all("~CREATE\s+TABLE\s+(\w+)~i", $sql, $matches);
752 if (!empty($matches[1])) {
753 $sql = "INSERT INTO wcf".WCF_N
."_package_installation_sql_log
756 $statement = self
::getDB()->prepareStatement($sql);
757 foreach ($matches[1] as $tableName) {
758 $statement->execute(array($tableName));
762 if ($offset < (count($sqlData) - 1)) {
763 WCF
::getTPL()->assign(array(
764 '__additionalParameters' => array(
765 'offset' => $offset +
1
769 $this->gotoNextStep('createDB');
773 * Manually install PIPPackageInstallationPlugin since install.sql content is not escaped resulting
774 * in different behaviour in MySQL and MSSQL. You SHOULD NOT move this into install.sql!
776 $sql = "INSERT INTO wcf".WCF_N
."_package_installation_plugin
777 (pluginName, priority, className)
779 $statement = self
::getDB()->prepareStatement($sql);
780 $statement->execute(array(
781 'packageInstallationPlugin',
783 'wcf\system\package\plugin\PIPPackageInstallationPlugin'
786 $this->gotoNextStep('logFiles');
791 * Logs the unzipped files.
793 protected function logFiles() {
796 $this->getInstalledFiles(WCF_DIR
);
797 $acpTemplateInserts = $fileInserts = array();
798 foreach (self
::$installedFiles as $file) {
800 if (preg_match('!/acp/templates/([^/]+)\.tpl$!', $file, $match)) {
802 $acpTemplateInserts[] = $match[1];
806 $fileInserts[] = str_replace(WCF_DIR
, '', $file);
810 // save acp template log
811 if (!empty($acpTemplateInserts)) {
812 $sql = "INSERT INTO wcf".WCF_N
."_acp_template
813 (templateName, application)
815 $statement = self
::getDB()->prepareStatement($sql);
817 self
::getDB()->beginTransaction();
818 foreach ($acpTemplateInserts as $acpTemplate) {
819 $statement->execute(array($acpTemplate, 'wcf'));
821 self
::getDB()->commitTransaction();
825 if (!empty($fileInserts)) {
826 $sql = "INSERT INTO wcf".WCF_N
."_package_installation_file_log
827 (filename, application)
829 $statement = self
::getDB()->prepareStatement($sql);
831 self
::getDB()->beginTransaction();
832 foreach ($fileInserts as $file) {
833 $statement->execute(array($file, 'wcf'));
835 self
::getDB()->commitTransaction();
838 $this->gotoNextStep('installLanguage');
842 * Scans the given dir for installed files.
846 protected function getInstalledFiles($dir) {
847 if ($files = glob($dir.'*')) {
848 foreach ($files as $file) {
850 $this->getInstalledFiles(FileUtil
::addTrailingSlash($file));
853 self
::$installedFiles[] = FileUtil
::unifyDirSeparator($file);
860 * Installs the selected languages.
862 protected function installLanguage() {
865 foreach (self
::$selectedLanguages as $language) {
866 // get language.xml file name
867 $filename = TMP_DIR
.'install/lang/'.$language.'.xml';
870 if (!file_exists($filename)) {
871 throw new SystemException("unable to find language file '".$filename."'");
876 $xml->load($filename);
879 LanguageEditor
::importFromXML($xml, 0);
882 // set default language
883 $language = LanguageFactory
::getInstance()->getLanguageByCode(in_array(self
::$selectedLanguageCode, self
::$selectedLanguages) ? self
::$selectedLanguageCode : self
::$selectedLanguages[0]);
884 LanguageFactory
::getInstance()->makeDefault($language->languageID
);
886 // rebuild language cache
887 LanguageCacheBuilder
::getInstance()->reset();
890 $this->gotoNextStep('createUser');
894 * Shows the page for creating the admin account.
896 protected function createUser() {
897 $errorType = $errorField = $username = $email = $confirmEmail = $password = $confirmPassword = '';
900 $email = $confirmEmail = '';
901 $password = $confirmPassword = '';
903 if (isset($_POST['send']) || self
::$developerMode) {
904 if (isset($_POST['send'])) {
905 if (isset($_POST['username'])) $username = StringUtil
::trim($_POST['username']);
906 if (isset($_POST['email'])) $email = StringUtil
::trim($_POST['email']);
907 if (isset($_POST['confirmEmail'])) $confirmEmail = StringUtil
::trim($_POST['confirmEmail']);
908 if (isset($_POST['password'])) $password = $_POST['password'];
909 if (isset($_POST['confirmPassword'])) $confirmPassword = $_POST['confirmPassword'];
912 $username = $password = $confirmPassword = 'root';
913 $email = $confirmEmail = 'woltlab@woltlab.com';
919 if (empty($username)) {
920 throw new UserInputException('username');
922 if (!UserUtil
::isValidUsername($username)) {
923 throw new UserInputException('username', 'notValid');
928 throw new UserInputException('email');
930 if (!UserUtil
::isValidEmail($email)) {
931 throw new UserInputException('email', 'notValid');
934 // confirm e-mail address
935 if ($email != $confirmEmail) {
936 throw new UserInputException('confirmEmail', 'notEqual');
940 if (empty($password)) {
941 throw new UserInputException('password');
944 // confirm e-mail address
945 if ($password != $confirmPassword) {
946 throw new UserInputException('confirmPassword', 'notEqual');
950 // init database connection
955 $sql = "SELECT languageID
956 FROM wcf".WCF_N
."_language
957 WHERE languageCode = ?";
958 $statement = self
::getDB()->prepareStatement($sql);
959 $statement->execute(array(self
::$selectedLanguageCode));
960 $row = $statement->fetchArray();
961 if (isset($row['languageID'])) $languageID = $row['languageID'];
964 $languageID = LanguageFactory
::getInstance()->getDefaultLanguageID();
971 'languageID' => $languageID,
972 'password' => $password,
973 'username' => $username
980 'languages' => array(
985 $userAction = new UserAction(array(), 'create', $data);
986 $userAction->executeAction();
989 $this->gotoNextStep('installPackages');
992 catch (UserInputException
$e) {
993 $errorField = $e->getField();
994 $errorType = $e->getType();
998 WCF
::getTPL()->assign(array(
999 'errorField' => $errorField,
1000 'errorType' => $errorType,
1001 'username' => $username,
1003 'confirmEmail' => $confirmEmail,
1004 'password' => $password,
1005 'confirmPassword' => $confirmPassword,
1006 'nextStep' => 'createUser'
1008 WCF
::getTPL()->display('stepCreateUser');
1012 * Registers with wcf setup delivered packages in the package installation queue.
1014 protected function installPackages() {
1015 // init database connection
1018 // get admin account
1019 $admin = new User(1);
1021 // get delivered packages
1022 $wcfPackageFile = '';
1023 $otherPackages = array();
1024 $tar = new Tar(SETUP_FILE
);
1025 foreach ($tar->getContentList() as $file) {
1026 if ($file['type'] != 'folder' && mb_strpos($file['filename'], 'install/packages/') === 0) {
1027 $packageFile = basename($file['filename']);
1029 // ignore any files which aren't an archive
1030 if (preg_match('~\.(tar\.gz|tgz|tar)$~', $packageFile)) {
1031 $packageName = preg_replace('!\.(tar\.gz|tgz|tar)$!', '', $packageFile);
1033 if ($packageName == 'com.woltlab.wcf') {
1034 $wcfPackageFile = $packageFile;
1037 $isStrato = (!empty($_SERVER['DOCUMENT_ROOT']) && (strpos($_SERVER['DOCUMENT_ROOT'], 'strato') !== false));
1038 if (!$isStrato && preg_match('!\.(tar\.gz|tgz)$!', $packageFile)) {
1039 // try to unzip zipped package files
1040 if (FileUtil
::uncompressFile(TMP_DIR
.'install/packages/'.$packageFile, TMP_DIR
.'install/packages/'.$packageName.'.tar')) {
1041 @unlink
(TMP_DIR
.'install/packages/'.$packageFile);
1042 $packageFile = $packageName.'.tar';
1046 $otherPackages[$packageName] = $packageFile;
1053 // register packages in queue
1054 // get new process id
1055 $sql = "SELECT MAX(processNo) AS processNo
1056 FROM wcf".WCF_N
."_package_installation_queue";
1057 $statement = self
::getDB()->prepareStatement($sql);
1058 $statement->execute();
1059 $result = $statement->fetchArray();
1060 $processNo = intval($result['processNo']) +
1;
1062 // search existing wcf package
1063 $sql = "SELECT COUNT(*) AS count
1064 FROM wcf".WCF_N
."_package
1065 WHERE package = 'com.woltlab.wcf'";
1066 $statement = self
::getDB()->prepareStatement($sql);
1067 $statement->execute();
1068 $row = $statement->fetchArray();
1069 if (!$row['count']) {
1070 if (empty($wcfPackageFile)) {
1071 throw new SystemException('the essential package com.woltlab.wcf is missing.');
1074 // register essential wcf package
1075 $queue = PackageInstallationQueueEditor
::create(array(
1076 'processNo' => $processNo,
1077 'userID' => $admin->userID
,
1078 'package' => 'com.woltlab.wcf',
1079 'packageName' => 'WoltLab Community Framework',
1080 'archive' => TMP_DIR
.'install/packages/'.$wcfPackageFile,
1081 'isApplication' => 1
1085 // register all other delivered packages
1086 asort($otherPackages);
1087 foreach ($otherPackages as $packageName => $packageFile) {
1088 // extract packageName from archive's package.xml
1089 $archive = new PackageArchive(TMP_DIR
.'install/packages/'.$packageFile);
1091 $archive->openArchive();
1093 catch (\Exception
$e) {
1094 // we've encountered a broken archive, revert everything and then fail
1095 $sql = "SELECT queueID, parentQueueID
1096 FROM wcf".WCF_N
."_package_installation_queue";
1097 $statement = WCF
::getDB()->prepareStatement($sql);
1098 $statement->execute();
1100 while ($row = $statement->fetchArray()) {
1101 $queues[$row['queueID']] = $row['parentQueueID'];
1104 $queueIDs = array();
1105 $queueID = $queue->queueID
;
1107 $queueIDs[] = $queueID;
1109 $queueID = (isset($queues[$queueID])) ?
$queues[$queueID] : 0;
1112 // remove previously created queues
1113 if (!empty($queueIDs)) {
1114 $sql = "DELETE FROM wcf".WCF_N
."_package_installation_queue
1116 $statement = WCF
::getDB()->prepareStatement($sql);
1117 WCF
::getDB()->beginTransaction();
1118 foreach ($queueIDs as $queueID) {
1119 $statement->execute(array($queueID));
1121 WCF
::getDB()->commitTransaction();
1124 // remove package files
1125 @unlink
(TMP_DIR
.'install/packages/'.$wcfPackageFile);
1126 foreach ($otherPackages as $packageFile) {
1127 @unlink
(TMP_DIR
.'install/packages/'.$packageFile);
1130 // throw exception again
1131 throw new SystemException('', 0, '', $e);
1134 $queue = PackageInstallationQueueEditor
::create(array(
1135 'parentQueueID' => $queue->queueID
,
1136 'processNo' => $processNo,
1137 'userID' => $admin->userID
,
1138 'package' => $packageName,
1139 'packageName' => $archive->getLocalizedPackageInfo('packageName'),
1140 'archive' => TMP_DIR
.'install/packages/'.$packageFile,
1141 'isApplication' => 1
1146 $factory = new ACPSessionFactory();
1149 SessionHandler
::getInstance()->changeUser($admin);
1150 SessionHandler
::getInstance()->register('masterPassword', 1);
1151 SessionHandler
::getInstance()->register('__wcfSetup_developerMode', self
::$developerMode);
1152 SessionHandler
::getInstance()->update();
1154 $installPhpDeleted = @unlink
('./install.php');
1155 @unlink
('./test.php');
1156 $wcfSetupTarDeleted = @unlink
('./WCFSetup.tar.gz');
1159 WCF
::getTPL()->assign(array(
1160 'installPhpDeleted' => $installPhpDeleted,
1161 'wcfSetupTarDeleted' => $wcfSetupTarDeleted
1163 WCF
::getTPL()->display('stepInstallPackages');
1166 $directory = TMP_DIR
.'/';
1167 DirectoryUtil
::getInstance($directory)->removePattern(new Regex('\.tar(\.gz)?$'), true);
1171 * Goes to the next step.
1173 * @param string $nextStep
1175 protected function gotoNextStep($nextStep) {
1176 WCF
::getTPL()->assign(array('nextStep' => $nextStep));
1177 WCF
::getTPL()->display('stepNext');
1181 * Installs the files of the tar archive.
1183 protected static function installFiles() {
1184 new Installer(self
::$wcfDir, SETUP_FILE
, null, 'install/files/');
1188 * Gets the package name of the first application in WCFSetup.tar.gz.
1190 protected static function getPackageName() {
1192 $tar = new Tar(SETUP_FILE
);
1193 foreach ($tar->getContentList() as $file) {
1194 if ($file['type'] != 'folder' && mb_strpos($file['filename'], 'install/packages/') === 0) {
1195 $packageFile = basename($file['filename']);
1196 $packageName = preg_replace('!\.(tar\.gz|tgz|tar)$!', '', $packageFile);
1198 if ($packageName != 'com.woltlab.wcf') {
1200 $archive = new PackageArchive(TMP_DIR
.'install/packages/'.$packageFile);
1201 $archive->openArchive();
1202 self
::$setupPackageName = $archive->getLocalizedPackageInfo('packageName');
1203 $archive->getTar()->close();
1206 catch (SystemException
$e) {}
1212 // assign package name
1213 WCF
::getTPL()->assign(array('setupPackageName' => self
::$setupPackageName));