瀏覽代碼

Adding installer step 2: database schema

ohartl 9 年之前
父節點
當前提交
fce56b3273
共有 2 個文件被更改,包括 503 次插入0 次删除
  1. 203 0
      installer/step2.php
  2. 300 0
      installer/step3.php

+ 203 - 0
installer/step2.php

@@ -0,0 +1,203 @@
+<?php
+
+$thisStep = 2;
+
+/*-----------------------------------------------------------------------------*/
+
+$exampleConfigValues = require_once 'config/config.php.example';
+
+$tablesInDatabase = array();
+try{
+	Database::init($_SESSION['installer']['config']['mysql']);
+
+	$tablesResult = Database::getInstance()->query("SELECT table_name FROM information_schema.tables WHERE table_schema='".$_SESSION['installer']['config']['mysql']['database']."';");
+	foreach($tablesResult->fetch_all() as $row){
+		$tablesInDatabase[] = $row[0];
+	}
+}
+catch(Exception $e){
+}
+
+/*-----------------------------------------------------------------------------*/
+
+$databaseSchema = array(
+	'domains' => "CREATE TABLE IF NOT EXISTS ___database___.___table___ (___id___ INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, ___domain___ VARCHAR(128) NOT NULL, PRIMARY KEY (___domain___), UNIQUE KEY ___id___ (___id___)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;",
+	'users' => "CREATE TABLE IF NOT EXISTS ___database___.___table___ (___id___ INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, ___username___ VARCHAR(128) NOT NULL DEFAULT '', ___domain___ VARCHAR(128) NOT NULL DEFAULT '', ___password___ VARCHAR(128) NOT NULL DEFAULT '', ___mailbox_limit___ INT(10) NOT NULL DEFAULT '128', ___max_user_redirects___ INT(10) NOT NULL DEFAULT '0', PRIMARY KEY (___username___,___domain___), UNIQUE KEY ___id___ (___id___)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;",
+	'aliases' => "CREATE TABLE IF NOT EXISTS ___database___.___table___ (___id___ INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, ___source___ VARCHAR(128) NOT NULL, ___destination___ TEXT NOT NULL, ___multi_source___ VARCHAR(32) DEFAULT NULL, ___is_created_by_user___ INT(1) NOT NULL DEFAULT '0', PRIMARY KEY (___source___), UNIQUE KEY ___id___ (___id___)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;",
+);
+
+/**
+ * @param string $stmt
+ * @param string $database
+ * @param string $table
+ * @param array $attributes
+ *
+ * @return string
+ */
+function prepareSchemaTableStmt($stmt, $database, $table, $attributes)
+{
+	$attributes['database'] = $database;
+	$attributes['table'] = $table;
+
+	foreach($attributes as $search => $replace){
+		$stmt = str_replace('___'.$search.'___', '`'.Database::getInstance()->escape($replace).'`', $stmt);
+	}
+
+	return $stmt;
+}
+
+$preparedSchemaStmt = '';
+$allTablesFromSchemaExist = true;
+
+foreach($databaseSchema as $table => $stmt){
+	$preparedSchemaStmt .= prepareSchemaTableStmt(
+			$stmt,
+			$_SESSION['installer']['config']['mysql']['database'],
+			$exampleConfigValues['schema']['tables'][$table],
+			$exampleConfigValues['schema']['attributes'][$table]
+		).PHP_EOL;
+
+	// check if tables exist, should be enough for now
+	if(!in_array($exampleConfigValues['schema']['tables'][$table], $tablesInDatabase)){
+		$allTablesFromSchemaExist = false;
+	}
+}
+
+$commandDenied = false;
+
+/*-----------------------------------------------------------------------------*/
+
+if(isset($_GET['go'])){
+	if($_GET['go'] == 'next' && $_SERVER['REQUEST_METHOD'] == 'POST'){
+		if(isset($_POST['manual'])){
+			if($_POST['manual'] == 1){
+				// display SQL
+			}
+			elseif($_POST['manual'] == 2){
+				// check if schema was created
+				if($allTablesFromSchemaExist){
+					// saving information
+					$_SESSION['installer']['config']['schema'] = $exampleConfigValues['schema'];
+
+					installer_message('Database schema was manually created.');
+
+					installer_next($thisStep, 2);
+				}
+				else{
+					$_POST['manual'] = 1;
+				}
+			}
+		}
+		else{
+			if(!$allTablesFromSchemaExist){
+				try{
+					foreach(explode(PHP_EOL, $preparedSchemaStmt) as $stmt){
+						Database::getInstance()->query($stmt);
+					}
+
+					// saving information
+					$_SESSION['installer']['config']['schema'] = $exampleConfigValues['schema'];
+
+					installer_message('Database schema was automatically created.');
+
+					installer_next($thisStep, 2);
+				}
+				catch(Exception $e){
+					if(strpos($e->getMessage(), 'command denied') !== false){
+						$commandDenied = true;
+					}
+					else{
+						throw $e;
+					}
+				}
+			}
+		}
+	}
+	elseif($_GET['go'] == 'prev'){
+		// reset
+		unset($_SESSION['installer']['config']['schema']);
+
+		installer_prev($thisStep);
+	}
+}
+?>
+<?php echo installer_message(); ?>
+
+	<h2>Step 2: Create database schema.</h2>
+
+<?php if($allTablesFromSchemaExist): ?>
+	<div class="notification notification-fail">
+		The schema already exists in database "<?php echo $_SESSION['installer']['config']['mysql']['database']; ?>".
+	</div>
+
+	<div>
+		Your next possible steps:
+		<ul>
+			<li>Either <strong>delete</strong> the existing schema.</li>
+			<li>Go Back and <strong>change</strong> the used database.</li>
+			<li>Go Back and <strong>start mapping</strong> the existing database schema.</li>
+		</ul>
+	</div>
+
+	<div class="buttons">
+		<a class="button" href="/?step=<?php echo $thisStep; ?>&go=prev">Back</a>
+		<a class="button" href="/?step=<?php echo $thisStep; ?>">Retry</a>
+	</div>
+<?php else: ?>
+	<form class="form" action="/?step=<?php echo $thisStep; ?>&go=next" method="post">
+		<?php if(isset($_POST['manual']) && $_POST['manual'] == 1): ?>
+			<textarea readonly style="width: 100%; height: 170px"><?php echo $preparedSchemaStmt; ?></textarea>
+
+			<div class="notification notification-warning">
+				Copy the SQL-Code above and import it into your database "<?php echo $_SESSION['installer']['config']['mysql']['database']; ?>".
+			</div>
+
+			<hr class="invisible">
+
+			<p>Once you have imported the schema, you can continue by clicking on the Continue button.</p>
+
+			<div class="buttons">
+				<a class="button" href="/?step=<?php echo $thisStep; ?>">Back</a>
+				<button class="button button-primary" name="manual" value="2" type="submit">Continue</button>
+			</div>
+		<?php else: ?>
+			<div class="notification notification-warning">
+				The following database schema will be created in
+				<strong>database "<?php echo $_SESSION['installer']['config']['mysql']['database']; ?>"</strong>.
+				<br><strong>Please make sure that "<?php echo $_SESSION['installer']['config']['mysql']['database']; ?>" is clean / empty database!</strong>
+			</div>
+
+			<?php if($commandDenied): ?>
+				<div class="notification notification-fail">The
+					<strong>user "<?php echo $_SESSION['installer']['config']['mysql']['user']; ?>" is missing the permission</strong> to execute MySQL "CREATE" commands.
+				</div>
+			<?php else: ?>
+				<div class="notification notification-warning">
+					Also <strong>make sure</strong> that the database
+					<strong>user "<?php echo $_SESSION['installer']['config']['mysql']['user']; ?>" has the privileges to create</strong> the schema.
+				</div>
+			<?php endif; ?>
+
+			<?php foreach($exampleConfigValues['schema']['tables'] as $table => $mappedTable): ?>
+				<div>
+					<strong>Table "<?php echo $table; ?>"</strong>
+					<ul>
+						<?php foreach($exampleConfigValues['schema']['attributes'][$table] as $attribute => $mappedAttribute): ?>
+							<li><?php echo $attribute; ?></li>
+						<?php endforeach; ?>
+					</ul>
+				</div>
+			<?php endforeach; ?>
+
+			<hr class="invisible">
+
+			<p>Click on the Continue button to try creating the schema automatically.</p>
+
+			<div class="buttons">
+				<a class="button" href="/?step=<?php echo $thisStep; ?>&go=prev">Back</a>
+				<button class="button" name="manual" value="1" type="submit">Import schema manually</button>
+				<button class="button button-primary" type="submit">Continue</button>
+			</div>
+		<?php endif; ?>
+	</form>
+<?php endif; ?>

+ 300 - 0
installer/step3.php

@@ -0,0 +1,300 @@
+<?php
+
+$thisStep = 3;
+
+if($_SESSION['installer']['lastStep'] > $thisStep){
+	$_SESSION['installer']['subStep'] = 1;
+}
+elseif($_SESSION['installer']['lastStep'] < $thisStep || !isset($_SESSION['installer']['subStep'])){
+	$_SESSION['installer']['subStep'] = 0;
+}
+
+$error = null;
+
+/*-----------------------------------------------------------------------------*/
+
+$exampleConfigValues = require_once 'config/config.php.example';
+
+$tablesInDatabase = array();
+try{
+	Database::init($_SESSION['installer']['config']['mysql']);
+
+	$db = Database::getInstance();
+	$tablesResult = $db->query(
+		"SELECT TABLE_NAME FROM information_schema.tables "
+		."WHERE TABLE_SCHEMA='".$db->escape($_SESSION['installer']['config']['mysql']['database'])."';"
+	);
+
+	foreach($tablesResult->fetch_all() as $row){
+		$tablesInDatabase[] = $row[0];
+	}
+}
+catch(Exception $e){
+}
+
+function getTableAttributes($table)
+{
+	global $_SESSION;
+	$attributes = array();
+
+	if(Database::isInitialized()){
+		try{
+			$db = Database::getInstance();
+			$tablesResult = $db->query(
+				"SELECT COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, COLUMN_DEFAULT, COLUMN_KEY, EXTRA FROM information_schema.columns "
+				."WHERE TABLE_SCHEMA = '".$db->escape($_SESSION['installer']['config']['mysql']['database'])."' "
+				."AND TABLE_NAME = '".$db->escape($table)."' "
+				."ORDER BY TABLE_NAME,ORDINAL_POSITION;"
+			);
+
+			foreach($tablesResult->fetch_all() as $row){
+				$s = $row[0];
+
+				if(!empty($row[1])){
+					$s .= ' : '.$row[1];
+				}
+
+				if($row[2] == 'NO'){
+					$s .= ', NOT NULL';
+				}
+
+				if(!is_null($row[3])){
+					$s .= ', DEFAULT \''.$row[3].'\'';
+				}
+
+				if(!empty($row[4])){
+					if(strpos($row[4], 'PR') !== false){
+						$s .= ', PRIMARY KEY';
+					}
+					if(strpos($row[4], 'UN') !== false){
+						$s .= ', UNIQUE KEY';
+					}
+				}
+
+				if(!empty($row[5]) && strpos($row[5], 'auto_inc') !== false){
+					$s .= ', AUTO_INCREMENT';
+				}
+
+				$attributes[$row[0]] = $s;
+			}
+		}
+		catch(Exception $e){
+		}
+	}
+
+	return $attributes;
+}
+
+$optionalAttributes = array(
+	'users' => array('mailbox_limit', 'max_user_redirects'),
+	'aliases' => array('multi_source', 'is_created_by_user'),
+);
+
+define('ATTR_SEP', '---');
+
+function getAttr($name, $default = null)
+{
+	global $_SESSION, $_POST;
+	
+	if(isset($_POST[$name])){
+		return strip_tags($_POST[$name]);
+	}
+	elseif(strpos($name, ATTR_SEP) !== false){
+		list($table, $attribute) = explode(ATTR_SEP, $name);
+
+		if(isset($_SESSION['installer']['config']['schema']['attributes'][$table][$attribute])){
+			return $_SESSION['installer']['config']['schema']['attributes'][$table][$attribute];
+		}
+	}
+	elseif(isset($_SESSION['installer']['config']['schema']['tables'][$name])){
+		return $_SESSION['installer']['config']['schema']['tables'][$name];
+	}
+
+	return $default;
+}
+
+/*-----------------------------------------------------------------------------*/
+
+if(isset($_GET['go'])){
+	if($_GET['go'] == 'next' && $_SERVER['REQUEST_METHOD'] == 'POST'){
+		try{
+			if($_SESSION['installer']['subStep'] === 0){
+
+				$tables = array();
+				foreach($exampleConfigValues['schema']['tables'] as $table => $mappedTable){
+					if(!isset($_POST[$table])
+						|| !in_array($_POST[$table], $tablesInDatabase)
+					){
+						throw new InvalidArgumentException('Missing mapping for table "'.$table.'".');
+					}
+
+					if(in_array($_POST[$table], array_values($tables))){
+						throw new Exception('You cannot map table "'.$_POST[$table].'" twice.');
+					}
+
+					$tables[$table] = $_POST[$table];
+				}
+
+				// saving information
+				$_SESSION['installer']['config']['schema'] = array();
+				$_SESSION['installer']['config']['schema']['tables'] = $tables;
+
+				installer_message('Database tables were successfully mapped.');
+
+				$_SESSION['installer']['subStep'] = 1;
+				installer_next($thisStep, 0);
+			}
+			elseif($_SESSION['installer']['subStep'] === 1){
+
+				$attributes = array();
+				foreach($_SESSION['installer']['config']['schema']['tables'] as $table => $mappedTable){
+
+					$attributes[$table] = array();
+
+					$attributesInDatabase = getTableAttributes($table);
+
+					foreach($exampleConfigValues['schema']['attributes'][$table] as $attribute => $mappedAttribute){
+						$key = $table.'---'.$attribute;
+
+						if(isset($optionalAttributes[$table])
+							&& in_array($attribute, $optionalAttributes[$table])
+							&& !isset($attributesInDatabase[$_POST[$key]])
+						){
+							$attributes[$table][$attribute] = '';
+						}
+						else{
+							if(!isset($_POST[$key]) || !isset($attributesInDatabase[$_POST[$key]])){
+								throw new InvalidArgumentException('Missing mapping for attribute "'.$attribute.'" on table "'.$table.'".');
+							}
+
+							if(in_array($_POST[$key], $attributes[$table])){
+								throw new Exception('You cannot map attribute "'.$_POST[$key].'" twice on table "'.$table.'".');
+							}
+
+							$attributes[$table][$attribute] = $_POST[$key];
+						}
+					}
+				}
+
+				// saving information
+				$_SESSION['installer']['config']['schema']['attributes'] = $attributes;
+
+				installer_message('Database attributes were successfully mapped.');
+
+				unset($_SESSION['installer']['subStep']);
+				installer_next($thisStep);
+			}
+		}
+		catch(Exception $e){
+			$error = $e->getMessage();
+		}
+	}
+	elseif($_GET['go'] == 'prev'){
+
+		// reset
+		if(isset($_SESSION['installer']['config']['schema']['tables'])){
+			if($_SESSION['installer']['subStep'] === 0){
+				unset($_SESSION['installer']['config']['schema']);
+			}
+			elseif($_SESSION['installer']['subStep'] === 1){
+				unset($_SESSION['installer']['config']['schema']['attributes']);
+			}
+		}
+
+		if($_SESSION['installer']['subStep'] === 0){
+			unset($_SESSION['installer']['subStep']);
+			installer_prev($thisStep, ($_SESSION['installer']['type'] === INSTALLER_TYPE_MAP) ? 2 : 1);
+		}
+		else{
+			$_SESSION['installer']['subStep'] = 0;
+			installer_prev($thisStep, 0);
+		}
+	}
+}
+?>
+
+<?php echo installer_message(); ?>
+
+<h2>
+	Step 2:
+	<?php if($_SESSION['installer']['subStep'] === 0): ?>
+		Database - table mapping.
+	<?php elseif($_SESSION['installer']['subStep'] === 1): ?>
+		Database - attribute mapping.
+	<?php else: ?>
+		Wrong turn
+	<?php endif; ?>
+</h2>
+
+<?php if(!empty($error)): ?>
+	<div class="notification notification-fail"><?php echo $error; ?></div>
+<?php endif; ?>
+
+<?php if($_SESSION['installer']['subStep'] === 0): ?>
+	<form class="form" action="/?step=<?php echo $thisStep; ?>&go=next" method="post">
+		<?php foreach($exampleConfigValues['schema']['tables'] as $table => $mappedTable): ?>
+			<div class="input-group">
+				<label for="<?php echo $table; ?>">Table "<?php echo $table; ?>"</label>
+				<div class="input">
+					<select name="<?php echo $table; ?>">
+						<option value="">-- Not mapped --</option>
+						<?php foreach($tablesInDatabase as $t): ?>
+							<option value="<?php echo $t; ?>" <?php echo getAttr($table, $mappedTable) == $t ? 'selected' : ''; ?>><?php echo $t; ?></option>
+						<?php endforeach; ?>
+					</select>
+				</div>
+			</div>
+		<?php endforeach; ?>
+
+		<hr class="invisible">
+
+		<div class="buttons">
+			<a class="button" href="/?step=<?php echo $thisStep; ?>&go=prev">Back</a>
+			<button class="button button-primary" type="submit">Continue</button>
+		</div>
+	</form>
+<?php elseif($_SESSION['installer']['subStep'] === 1): ?>
+	<form class="form" action="/?step=<?php echo $thisStep; ?>&go=next" method="post">
+		<?php
+		$lastTable = array_keys($_SESSION['installer']['config']['schema']['tables']);
+		$lastTable = $lastTable[count($lastTable) - 1];
+		foreach($_SESSION['installer']['config']['schema']['tables'] as $table => $mappedTable):
+			$attributesInDatabase = getTableAttributes($mappedTable);
+			?>
+			<h3>
+				Table "<?php echo $table; ?>"
+				<div class="sub-header">Has been mapped to table "<?php echo $mappedTable; ?>".</div>
+			</h3>
+			<div style="margin-left: 25px;">
+				<?php foreach($exampleConfigValues['schema']['attributes'][$table] as $attribute => $mappedAttribute): ?>
+					<div class="input-group">
+						<label for="<?php echo $table.ATTR_SEP.$attribute; ?>">Attribute "<?php echo $attribute; ?>"</label>
+						<?php if(isset($optionalAttributes[$table]) && in_array($attribute, $optionalAttributes[$table])): ?>
+							<div class="input-info">This attribute is optional (used by optional features) and doesn't need to be mapped.</div>
+						<?php endif; ?>
+						<div class="input">
+							<select name="<?php echo $table.ATTR_SEP.$attribute; ?>">
+								<option value="">-- Not mapped --</option>
+								<?php foreach($attributesInDatabase as $dbAttr => $dbAttrText): ?>
+									<option value="<?php echo $dbAttr; ?>" <?php echo getAttr($table.ATTR_SEP.$attribute, $mappedAttribute) == $dbAttr ? 'selected' : ''; ?>><?php echo $dbAttrText; ?></option>
+								<?php endforeach; ?>
+							</select>
+						</div>
+					</div>
+				<?php endforeach; ?>
+			</div>
+			<?php if($table != $lastTable): ?>
+			<hr>
+		<?php endif; ?>
+		<?php endforeach; ?>
+
+		<hr class="invisible">
+
+		<div class="buttons">
+			<a class="button" href="/?step=<?php echo $thisStep; ?>&go=prev">Back</a>
+			<button class="button button-primary" type="submit">Continue</button>
+		</div>
+	</form>
+<?php else: ?>
+	<div class="notification notification-fail">You took the wrong turn, <a href="/">restart installation</a>.</div>
+<?php endif; ?>