databseRequired = false;
    }
  }
}
namespace Documents\Install {
  class Head extends \Elements\Head {
    public function __construct($document) {
      parent::__construct($document);
    }
    protected function initSources() {
      $this->loadJQuery();
      $this->loadBootstrap();
      $this->loadFontawesome();
      $this->addJS(\Elements\Script::CORE);
      $this->addCSS(\Elements\Link::CORE);
      $this->addJS(\Elements\Script::INSTALL);
    }
    protected function initMetas() {
      return array(
        array('name' => 'viewport', 'content' => 'width=device-width, initial-scale=1.0'),
        array('name' => 'format-detection', 'content' => 'telephone=yes'),
        array('charset' => 'utf-8'),
        array("http-equiv" => 'expires', 'content' => '0'),
        array("name" => 'robots', 'content' => 'noarchive'),
      );
    }
    protected function initRawFields() {
      return array();
    }
    protected function initTitle() {
      return "WebBase - Installation";
    }
  }
  class Body extends \Elements\Body {
    // Status enum
    const NOT_STARTED = 0;
    const PENDING = 1;
    const SUCCESFULL = 2;
    const ERROR = 3;
    // Step enum
    const CHECKING_REQUIRMENTS = 1;
    const DATABASE_CONFIGURATION = 2;
    const CREATE_USER = 3;
    const ADD_MAIL_SERVICE = 4;
    const FINISH_INSTALLATION = 5;
    //
    private $configDirectory;
    private $databaseScript;
    private $errorString;
    function __construct($document) {
      parent::__construct($document);
      // TODO: make better
      $this->configDirectory = getWebRoot() . '/core/Configuration';
      $this->databaseScript  = getWebRoot() . '/core/Configuration/database.sql';
      $this->errorString = "";
    }
    private function getParameter($name) {
      if(isset($_REQUEST[$name]) && is_string($_REQUEST[$name])) {
        return trim($_REQUEST[$name]);
      }
      return NULL;
    }
    private function getCurrentStep() {
      if(!$this->checkRequirements()) {
        return self::CHECKING_REQUIRMENTS;
      }
      $user = $this->getDocument()->getUser();
      $config = $user->getConfiguration();
      // Check if database configuration exists
      if(!$config->getDatabase()) {
        return self::DATABASE_CONFIGURATION;
      }
      $query = "SELECT * FROM User";
      $sql = $user->getSQL();
      if(!is_null($sql) && $sql->isConnected()) {
        $res = $sql->query($query);
        if($res) {
          if($res->num_rows === 0) {
            $step = self::CREATE_USER;
          } else {
            $step = self::ADD_MAIL_SERVICE;
          }
        }
      } else {
        $step = self::DATABASE_CONFIGURATION;
      }
      if($step == self::ADD_MAIL_SERVICE && $config->isFilePresent("Mail")) {
        $step = self::FINISH_INSTALLATION;
        if(!$config->isFilePresent("JWT") && $config->create("JWT", generateRandomString(32))) {
          $this->errorString = "Unable to create jwt file";
        }
      }
      return $step;
    }
    private function checkRequirements() {
      $msg = $this->errorString;
      $success = true;
      $failedRequirements = array();
      if(!is_writeable($this->configDirectory)) {
        $failedRequirements[] = "$this->configDirectory is not writeable. Try running chmod 600";
        $success = false;
      }
      if(!is_readable($this->databaseScript)) {
        $failedRequirements[] = "$this->databaseScript is not readable.";
        $success = false;
      }
      if(version_compare(PHP_VERSION, '7.1', '<')) {
          $failedRequirements[] = "PHP Version >= 7.1 is required. Got: " . PHP_VERSION . "";
          $success = false;
      }
      if(!function_exists('mysqli_connect')) {
        $link = $this->createExternalLink("https://secure.php.net/manual/en/mysqli.setup.php");
        $failedRequirements[] = "mysqli is not enabled yet. See: $link";
        $success = false;
      }
      if(!$success) {
        $msg = "The following requirements failed the check:
" .
          $this->createUnorderedList($failedRequirements);
      }
      return array("success" => $success, "msg" => $msg);
    }
    private function databaseConfiguration() {
      $host = $this->getParameter("host");
      $port = $this->getParameter("port");
      $username = $this->getParameter("username");
      $password = $this->getParameter("password");
      $database = $this->getParameter("database");
      $success = true;
      $missingInputs = array();
      if(is_null($host) || empty($host)) {
        $success = false;
        $missingInputs[] = "Host";
      }
      if(is_null($port) || empty($port)) {
        $success = false;
        $missingInputs[] = "Port";
      }
      if(is_null($username) || empty($username)) {
        $success = false;
        $missingInputs[] = "Username";
      }
      if(is_null($password)) {
        $success = false;
        $missingInputs[] = "Password";
      }
      if(is_null($database) || empty($database)) {
        $success = false;
        $missingInputs[] = "Database";
      }
      if(!$success) {
        $msg = "Please fill out the following inputs:
" .
          $this->createUnorderedList($missingInputs);
      } else if(!is_numeric($port) || ($port = intval($port)) < 1 || $port > 65535) {
        $msg = "Port must be in range of 1-65535.";
        $success = false;
      } else {
        $connectionData = new \Objects\ConnectionData($host, $port, $username, $password);
        $connectionData->setProperty('database', $database);
        $connectionData->setProperty('encoding', 'utf8');
        $sql = new \Driver\SQL($connectionData);
        $success = $sql->connect();
        if(!$success) {
          $msg = "Error connecting to database:
" . $sql->getLastError();
        } else {
          try {
            $msg = "Error loading database script $this->databaseScript";
            $commands = file_get_contents($this->databaseScript);
            $success = $sql->executeMulti($commands);
            if(!$success) {
              $msg = $sql->getLastError();
            } else if(!$this->getDocument()->getUser()->getConfiguration()->create("Database", $connectionData)) {
              $success = false;
              $msg = "Unable to write file";
            } else {
              $msg = "";
            }
          } catch(Exception $e) {
            $success = false;
            $msg .= ": " . $e->getMessage();
          }
          if($sql) {
            $sql->close();
          }
        }
      }
      return array("success" => $success, "msg" => $msg);
    }
    private function createUser() {
      $user = $this->getDocument()->getUser();
      if($this->getParameter("prev") === "true") {
        $success = $user->getConfiguration()->delete("Database");
        $msg = $success ? "" : error_get_last();
        return array("success" => $success, "msg" => $msg);
      }
      $username = $this->getParameter("username");
      $password = $this->getParameter("password");
      $confirmPassword = $this->getParameter("confirmPassword");
      $msg = $this->errorString;
      $success = true;
      $missingInputs = array();
      if(is_null($username) || empty($username)) {
        $success = false;
        $missingInputs[] = "Username";
      }
      if(is_null($password) || empty($password)) {
        $success = false;
        $missingInputs[] = "Password";
      }
      if(is_null($confirmPassword) || empty($confirmPassword)) {
        $success = false;
        $missingInputs[] = "Confirm Password";
      }
      if(!$success) {
        $msg = "Please fill out the following inputs:
" .
          $this->createUnorderedList($missingInputs);
      } else if(strlen($username) < 5 || strlen($username) > 32) {
        $msg = "The username should be between 5 and 32 characters long";
        $success = false;
      } else if(strcmp($password, $confirmPassword) !== 0) {
        $msg = "The given passwords do not match";
        $success = false;
      } else if(strlen($password) < 6) {
        $msg = "The password should be at least 6 characters long";
        $success = false;
      } else {
        $salt = generateRandomString(16);
        $hash = hash('sha256', $password . $salt);
        $query = "INSERT INTO User (name, salt, password) VALUES (?,?,?)";
        $req = new \Api\ExecuteStatement($user);
        $success = $req->execute(array("query" => $query, $username, $salt, $hash));
        $nsg = $req->getLastError();
      }
      return array("msg" => $msg, "success" => $success);
    }
    private function addMailService() {
      $user = $this->getDocument()->getUser();
      if($this->getParameter("prev") === "true") {
        $req = new \Api\ExecuteStatement($user);
        $success = $req->execute(array("query" => "TRUNCATE User"));
        $msg = $req->getLastError();
        return array("success" => $success, "msg" => $msg);
      }
      $success = true;
      $msg = $this->errorString;
      if($this->getParameter("skip") === "true") {
        if(!$user->getConfiguration()->create("Mail", null)) {
          $success = false;
          $msg = "Unable to create file";
        }
      } else {
        $address = $this->getParameter("address");
        $port = $this->getParameter("port");
        $username = $this->getParameter("username");
        $password = $this->getParameter("password");
        $success = true;
        $missingInputs = array();
        if(is_null($address) || empty($address)) {
          $success = false;
          $missingInputs[] = "SMTP Address";
        }
        if(is_null($port) || empty($port)) {
          $success = false;
          $missingInputs[] = "Port";
        }
        if(is_null($username) || empty($username)) {
          $success = false;
          $missingInputs[] = "Username";
        }
        if(is_null($password)) {
          $success = false;
          $missingInputs[] = "Password";
        }
        if(!$success) {
          $msg = "Please fill out the following inputs:
" .
            $this->createUnorderedList($missingInputs);
        } else if(!is_numeric($port) || ($port = intval($port)) < 1 || $port > 65535) {
          $msg = "Port must be in range of 1-65535.";
          $success = false;
        } else {
          $success = false;
          $mail = new \External\PHPMailer\PHPMailer(true);
          $mail->IsSMTP();
          $mail->SMTPAuth = true;
          $mail->Username = $username;
          $mail->Password = $password;
          $mail->Host = $address;
          $mail->Port = $port;
          $mail->SMTPSecure = 'tls';
          $mail->Timeout = 10;
          try {
            $success = $mail->SmtpConnect();
            if(!$success) {
              $error = empty($mail->ErrorInfo) ? "Unknown Error" : $mail->ErrorInfo;
              $msg = "Could not connect to SMTP Server: $error";
            } else {
              $success = true;
              $msg = "";
              $mail->smtpClose();
            }
          } catch(\External\PHPMailer\Exception $error) {
            $msg = "Could not connect to SMTP Server: " . $error->errorMessage();
          }
          if($success) {
            $connectionData = new \Objects\ConnectionData($address, $port, $username, $password);
            if(!$user->getConfiguration()->create("Mail", $connectionData)) {
              $success = false;
              $msg = "Unable to create file";
            }
          }
        }
      }
      return array("success" => $success, "msg" => $msg);
    }
    private function performStep() {
      switch($this->currentStep) {
        case self::CHECKING_REQUIRMENTS:
          return $this->checkRequirements();
        case self::DATABASE_CONFIGURATION:
          return $this->databaseConfiguration();
        case self::CREATE_USER:
          return $this->createUser();
        case self::ADD_MAIL_SERVICE:
          return $this->addMailService();
        default:
          return array(
            "success" => false,
            "msg" => "Invalid step number"
          );
      }
    }
    private function createProgressSidebar() {
      $items = array();
      foreach($this->steps as $num => $step) {
        $title = $step["title"];
        $status = $step["status"];
        $currentStep = ($num == $this->currentStep) ? " id=\"currentStep\"" : "";
        switch($status) {
          case self::PENDING:
            $statusIcon  = '';
            $statusText  = "Loading…";
            $statusColor = "muted";
            break;
          case self::SUCCESFULL:
            $statusIcon  = '';
            $statusText  = "Successfull";
            $statusColor = "success";
            break;
          case self::ERROR:
            $statusIcon  = '';
            $statusText  = "Failed";
            $statusColor = "danger";
            break;
          case self::NOT_STARTED:
          default:
            $statusIcon = '';
            $statusText = "Pending";
            $statusColor = "muted";
            break;
        }
        $items[] = "
          
Process the following steps and fill out the required forms to install your WebBase-Installation.