Adding installer step 2: database schema

This commit is contained in:
ohartl 2016-05-19 02:51:14 +02:00
parent e054c8627b
commit fce56b3273
2 changed files with 503 additions and 0 deletions

203
installer/step2.php Normal file
View file

@ -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
installer/step3.php Normal file
View file

@ -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; ?>