--- /dev/null
+{
+ "setup": {
+ "database": {
+ "auto": true,
+ "host": "localhost",
+ "password": "root",
+ "username": "root",
+ "dbNumber": "2"
+ },
+ "useDefaultInstallPath": true
+ },
+ "configuration": {
+ "option": {
+ "captcha_type": "",
+ "module_cookie_policy_page": "0"
+ }
+ },
+ "user": [
+ {
+ "username": "test",
+ "password": "test",
+ "email": "test@example.com"
+ },
+ {
+ "username": "test2",
+ "password": "test",
+ "email": "test2@example.com"
+ }
+ ]
+}
use wcf\data\application\Application;
use wcf\data\package\installation\queue\PackageInstallationQueue;
use wcf\system\cache\CacheHandler;
+use wcf\system\devtools\DevtoolsSetup;
use wcf\system\exception\IllegalLinkException;
use wcf\system\package\PackageInstallationDispatcher;
use wcf\system\search\SearchIndexManager;
use wcf\system\database\exception\DatabaseException;
use wcf\system\database\util\SQLParser;
use wcf\system\database\MySQLDatabase;
+use wcf\system\devtools\DevtoolsSetup;
use wcf\system\exception\SystemException;
use wcf\system\exception\UserInputException;
use wcf\system\io\File;
}
$documentRoot = FileUtil::unifyDirSeparator(realpath($_SERVER['DOCUMENT_ROOT']));
- if (self::$developerMode && isset($_ENV['WCFSETUP_USEDEFAULTWCFDIR'])) {
+ if (self::$developerMode && (isset($_ENV['WCFSETUP_USEDEFAULTWCFDIR']) || DevtoolsSetup::getInstance()->useDefaultInstallPath())) {
// resolve path relative to document root
$relativePath = FileUtil::getRelativePath($documentRoot, INSTALL_SCRIPT_DIR);
foreach ($packages as $application => $packageData) {
* Shows the page for configuring the database connection.
*/
protected function configureDB() {
+ $attemptConnection = isset($_POST['send']);
+
if (self::$developerMode && isset($_ENV['WCFSETUP_DBHOST'])) {
$dbHost = $_ENV['WCFSETUP_DBHOST'];
$dbUser = $_ENV['WCFSETUP_DBUSER'];
$dbPassword = $_ENV['WCFSETUP_DBPASSWORD'];
$dbName = $_ENV['WCFSETUP_DBNAME'];
$dbNumber = 1;
+
+ $attemptConnection = true;
+ }
+ else if (self::$developerMode && ($config = DevtoolsSetup::getInstance()->getDatabaseConfig()) !== null) {
+ $dbHost = $config['host'];
+ $dbUser = $config['username'];
+ $dbPassword = $config['password'];
+ $dbName = $config['dbName'];
+ $dbNumber = $config['dbNumber'];
+
+ if ($config['auto']) $attemptConnection = true;
}
else {
$dbHost = 'localhost';
$dbNumber = 1;
}
- if (isset($_POST['send']) || (self::$developerMode && isset($_ENV['WCFSETUP_DBHOST']))) {
+ if ($attemptConnection) {
if (isset($_POST['dbHost'])) $dbHost = $_POST['dbHost'];
if (isset($_POST['dbUser'])) $dbUser = $_POST['dbUser'];
if (isset($_POST['dbPassword'])) $dbPassword = $_POST['dbPassword'];
// check connection data
/** @var \wcf\system\database\Database $db */
try {
- $db = new MySQLDatabase($dbHost, $dbUser, $dbPassword, $dbName, $dbPort, true);
+ $db = new MySQLDatabase($dbHost, $dbUser, $dbPassword, $dbName, $dbPort, true, !!(self::$developerMode));
}
catch (DatabaseException $e) {
// work-around for older MySQL versions that don't know utf8mb4
*/
protected $activeTransactions = 0;
+ /**
+ * attempts to create the database after the connection has been established
+ * @var boolean
+ */
+ protected $tryToCreateDatabase = false;
+
/**
* Creates a Database Object.
*
* @param string $database SQL database server database name
* @param integer $port SQL database server port
* @param boolean $failsafeTest
+ * @param boolean $tryToCreateDatabase
*/
- public function __construct($host, $user, $password, $database, $port, $failsafeTest = false) {
+ public function __construct($host, $user, $password, $database, $port, $failsafeTest = false, $tryToCreateDatabase = false) {
$this->host = $host;
$this->port = $port;
$this->user = $user;
$this->password = $password;
$this->database = $database;
$this->failsafeTest = $failsafeTest;
+ $this->tryToCreateDatabase = $tryToCreateDatabase;
// connect database
$this->connect();
// throw PDOException instead of dumb false return values
$driverOptions[\PDO::ATTR_ERRMODE] = \PDO::ERRMODE_EXCEPTION;
- $this->pdo = new \PDO('mysql:host='.$this->host.';port='.$this->port.';dbname='.$this->database, $this->user, $this->password, $driverOptions);
+ $dsn = 'mysql:host='.$this->host.';port='.$this->port;
+ if (!$this->tryToCreateDatabase) $dsn .= ';dbname='.$this->database;
+
+ $this->pdo = new \PDO($dsn, $this->user, $this->password, $driverOptions);
$this->setAttributes();
+
+ if ($this->tryToCreateDatabase) {
+ try {
+ $this->pdo->exec("USE ".$this->database);
+ }
+ catch (\PDOException $e) {
+ // 1049 = Unknown database
+ if ($this->pdo->errorInfo()[1] == 1049) {
+ try {
+ $this->pdo->exec("CREATE DATABASE " . $this->database);
+ $this->pdo->exec("USE " . $this->database);
+ }
+ catch (\PDOException $e) {
+ wcfDebug($e);
+ }
+ }
+ else {
+ throw $e;
+ }
+ }
+ }
}
catch (\PDOException $e) {
throw new GenericDatabaseException("Connecting to MySQL server '".$this->host."' failed", $e);
--- /dev/null
+<?php
+namespace wcf\system\devtools;
+use wcf\system\SingletonFactory;
+use wcf\util\FileUtil;
+use wcf\util\JSON;
+
+/**
+ * Enables the rapid deployment of new installations using a central configuration file
+ * in the document root. Requires the developer mode to work.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2017 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Devtools\Package
+ * @since 3.1
+ */
+class DevtoolsSetup extends SingletonFactory {
+ /**
+ * configuration file in the server's document root
+ * @var string
+ */
+ const CONFIGURATION_FILE = 'wsc-dev-config-31.json';
+
+ /**
+ * configuration data
+ * @var array
+ */
+ protected $configuration = [];
+
+ /**
+ * @inheritDoc
+ */
+ protected function init() {
+ if (empty($_SERVER['DOCUMENT_ROOT'])) return;
+
+ $docRoot = FileUtil::addTrailingSlash(FileUtil::unifyDirSeparator($_SERVER['DOCUMENT_ROOT']));
+ if (!file_exists($docRoot . self::CONFIGURATION_FILE)) return;
+
+ $contents = file_get_contents($docRoot . self::CONFIGURATION_FILE);
+
+ // allow the exception to go rampage
+ $this->configuration = JSON::decode($contents);
+ }
+
+ /**
+ * Returns the database configuration.
+ *
+ * @return array|null
+ */
+ public function getDatabaseConfig() {
+ if (!isset($this->configuration['setup']) || !isset($this->configuration['setup']['database'])) return null;
+
+ // dirname return a single backslash on Windows if there are no parent directories
+ $dir = dirname($_SERVER['SCRIPT_NAME']);
+ $dir = ($dir === '\\') ? '/' : FileUtil::addTrailingSlash($dir);
+ if ($dir === '/') throw new \RuntimeException("Refusing to install in the document root.");
+
+ $dir = FileUtil::removeLeadingSlash(FileUtil::removeTrailingSlash($dir));
+ $dbName = implode('_', explode('/', $dir));
+
+ $dbConfig = $this->configuration['setup']['database'];
+ return [
+ 'auto' => $dbConfig['auto'],
+ 'host' => $dbConfig['host'],
+ 'password' => $dbConfig['password'],
+ 'username' => $dbConfig['username'],
+ 'dbName' => $dbName,
+ 'dbNumber' => $dbConfig['dbNumber']
+ ];
+ }
+
+ /**
+ * Returns true if the suggested default paths for the Core and, if exists,
+ * the bundled app should be used.
+ *
+ * @return boolean
+ */
+ public function useDefaultInstallPath() {
+ return (isset($this->configuration['setup']) && isset($this->configuration['setup']['useDefaultInstallPath']) && $this->configuration['setup']['useDefaultInstallPath'] === true);
+ }
+
+ /**
+ * List of option values that will be set after the setup has completed.
+ *
+ * @return string[]
+ */
+ public function getOptionOverrides() {
+ if (!isset($this->configuration['configuration']) || empty($this->configuration['configuration']['option'])) return [];
+
+ return $this->configuration['configuration']['option'];
+ }
+
+ /**
+ * Returns a list of users that should be automatically created during setup.
+ *
+ * @return array|\Generator
+ */
+ public function getUsers() {
+ if (empty($this->configuration['user'])) return [];
+
+ foreach ($this->configuration['user'] as $user) {
+ if ($user['username'] === 'root') throw new \LogicException("The 'root' user is automatically created.");
+
+ yield [
+ 'username' => $user['username'],
+ 'password' => $user['password'],
+ 'email' => $user['email']
+ ];
+ }
+ }
+
+ /**
+ * Returns the raw configuration data.
+ *
+ * @return array
+ */
+ public function getRawConfiguration() {
+ return $this->configuration;
+ }
+}
\ No newline at end of file
use wcf\data\package\Package;
use wcf\data\package\PackageEditor;
use wcf\data\user\User;
+use wcf\data\user\UserAction;
use wcf\system\application\ApplicationHandler;
use wcf\system\cache\builder\TemplateListenerCodeCacheBuilder;
use wcf\system\cache\CacheHandler;
use wcf\system\database\statement\PreparedStatement;
use wcf\system\database\util\PreparedStatementConditionBuilder;
+use wcf\system\devtools\DevtoolsSetup;
use wcf\system\event\EventHandler;
use wcf\system\exception\ImplementationException;
use wcf\system\exception\SystemException;
1,
'enable_developer_tools'
]);
+
+ foreach (DevtoolsSetup::getInstance()->getOptionOverrides() as $optionName => $optionValue) {
+ $statement->execute([
+ $optionValue,
+ $optionName
+ ]);
+ }
+
+ foreach (DevtoolsSetup::getInstance()->getUsers() as $newUser) {
+ (new UserAction([], 'create', [
+ 'data' => [
+ 'email' => $newUser['email'],
+ 'password' => $newUser['password'],
+ 'username' => $newUser['username']
+ ],
+ 'groups' => [
+ 1,
+ 3
+ ]
+ ]))->executeAction();
+ }
}
// update options.inc.php