update installer class and ui

This commit is contained in:
envoyr
2022-04-30 11:59:38 +02:00
parent 10313d9058
commit 4ea31c7124
9 changed files with 287 additions and 442 deletions

View File

@@ -25,30 +25,118 @@
namespace Froxlor\Install;
use Exception;
use Froxlor\UI\Panel\UI;
use Froxlor\UI\Request;
class Install
{
public $step = 0;
public $currentStep;
public $maxSteps;
public $phpVersion;
public $formfield;
public string $requiredVersion = '7.4.0';
public array $requiredExtensions = ['libxml', 'zip'];
public array $suggestedExtensions = ['curl'];
public array $suggestions = [];
public array $criticals = [];
public array $loadedExtensions;
public array $supportedOS = [
'focal' => 'Ubuntu 20.04 LTS (Focal Fossa)'
];
public function __construct()
{
$this->step = Request::get('step');
// set formfield, so we can get the fields and steps etc.
$this->formfield = require dirname(__DIR__, 3) . '/lib/formfields/install/formfield.install.php';
// set actual step
$this->currentStep = Request::get('step', 0);
$this->maxSteps = count($this->formfield['install']['sections']);
// set actual php version and extensions
$this->phpVersion = phpversion();
$this->loadedExtensions = get_loaded_extensions();
$this->checkExtensions();
// init twig
UI::initTwig(true);
UI::sendHeaders();
// set global variables
UI::twig()->addGlobal('install_mode', true);
UI::twig()->addGlobal('basehref', '../');
// unset session if user goes back to step 0
if (isset($_SESSION['installation']) && $this->currentStep == 0) {
unset($_SESSION['installation']);
}
}
public function checkExtensions()
/**
* @return void
* @throws Exception
*/
public function handle(): void
{
// handle form data
if (!is_null(Request::get('submit')) && $this->currentStep) {
try {
$this->handleFormData($this->formfield['install']);
} catch (Exception $e) {
$error = $e->getMessage();
}
}
// load template
UI::twigBuffer('/install/index.html.twig', [
'setup' => [
'step' => $this->currentStep,
],
'preflight' => $this->checkExtensions(),
'page' => [
'title' => 'Database',
'description' => 'Test',
],
'section' => $this->formfield['install']['sections']['step' . $this->currentStep] ?? [],
'error' => $error ?? null,
]);
// output view
UI::twigOutputBuffer();
}
/**
* @throws Exception
*/
private function handleFormData(array $formfield): void
{
// Validate user data
$validatedData = $this->validateRequest($formfield['sections']['step' . $this->currentStep]['fields']);
// handle current step
if ($this->currentStep <= $this->maxSteps) {
// Check database connection (
if ($this->currentStep == 1) {
$this->checkDatabase($validatedData);
}
// Store validated data for later use
$_SESSION['installation'] = array_merge($_SESSION['installation'] ?? [], $validatedData);
}
// also handle completion of installation if it's the last step
if ($this->currentStep == $this->maxSteps) {
$this->startInstallation($validatedData);
header('Location: ../');
return;
}
header('Location: ?step=' . ($this->currentStep + 1));
}
/**
* @return array
*/
private function checkExtensions(): array
{
// check for required extensions
foreach ($this->requiredExtensions as $requiredExtension) {
@@ -65,42 +153,18 @@ class Install
}
$this->suggestions['missing_extensions'][] = $suggestedExtension;
}
return [
'text' => $this->getInformationText(),
'suggestions' => $this->suggestions,
'criticals' => $this->criticals,
];
}
public function handle()
{
$formfield = require dirname(__DIR__, 3) . '/lib/formfields/install/formfield.install.php';
// init twig
UI::initTwig(true);
UI::sendHeaders();
// set global variables
UI::twig()->addGlobal('install_mode', true);
UI::twig()->addGlobal('basehref', '../');
// load template
UI::twigBuffer('/install/index.html.twig', [
'setup' => [
'step' => $this->step,
],
'preflight' => [
'text' => $this->getPreflightText(),
'suggestions' => $this->suggestions,
'criticals' => $this->criticals,
],
'page' => [
'title' => 'Database',
'description' => 'Test',
],
'section' => $formfield['install']['sections'][sprintf('step%s', $this->step)] ?? [],
]);
// output view
UI::twigOutputBuffer();
}
public function getPreflightText(): string
/**
* @return string
*/
private function getInformationText(): string
{
if (version_compare($this->requiredVersion, PHP_VERSION, "<")) {
$text = 'Your system is running with PHP ' . $this->phpVersion;
@@ -108,7 +172,93 @@ class Install
$text = 'Your system is running a lower version than PHP ' . $this->requiredVersion;
$this->criticals[] = 'Update your current PHP Version from ' . $this->phpVersion . ' to ' . $this->requiredVersion . ' or higher';
}
return $text;
}
/**
* @throws Exception
*/
private function validateRequest(array $fields): array
{
$attributes = [];
foreach ($fields as $name => $field) {
$attributes[$name] = $this->validateAttribute(Request::get($name), $field);
if (isset($field['next_to'])) {
$attributes = array_merge($attributes, $this->validateRequest($field['next_to']));
}
}
return $attributes;
}
/**
* @return mixed
* @throws Exception
*/
private function validateAttribute($attribute, array $field)
{
// TODO: do validations
if (isset($field['mandatory']) && $field['mandatory'] && empty($attribute)) {
throw new Exception('Mandatory field is not set!');
}
return $attribute;
}
/**
* @throws Exception
*/
private function checkDatabase(array $validatedData): void
{
$dsn = sprintf('mysql:host=%s;charset=utf8mb4', $validatedData['sql_hostname']);
$pdo = new \PDO($dsn, $validatedData['sql_root_username'], $validatedData['sql_root_password']);
// check if the database already exist
$stmt = $pdo->prepare('SHOW DATABASES LIKE ?');
$stmt->execute([
$validatedData['sql_database']
]);
$hasDatabase = $stmt->fetch();
if ($hasDatabase && !$validatedData['sql_override_database']) {
throw new Exception('Database already exist, please set override option to rebuild!');
}
// check if we can create a new database
$testDatabase = uniqid('froxlor_tmp_');
if ($pdo->exec('CREATE DATABASE IF NOT EXISTS ' . $testDatabase . ';') === false) {
throw new Exception('cant create test db');
}
if ($pdo->exec('DROP DATABASE IF EXISTS ' . $testDatabase . ';') === false) {
throw new Exception('Cant drop test db');
}
// FIXME: seems not to work
// check if the user already exist
$stmt = $pdo->prepare("SELECT `user` FROM `mysql.user` WHERE `user` = '?'");
if ($stmt->rowCount()) {
throw new Exception('Username already exist, please use another username!');
}
// check if we can create a new user
$testUser = uniqid('froxlor_tmp_');
$stmt = $pdo->prepare('CREATE USER ?@? IDENTIFIED BY ?');
if ($stmt->execute([$testUser, $validatedData['sql_hostname'], uniqid()]) === false) {
throw new Exception('cant create test user');
}
$stmt = $pdo->prepare('DROP USER ?@?');
if ($stmt->execute([$testUser, $validatedData['sql_hostname']]) === false) {
throw new Exception('cant create test user');
}
if ($pdo->prepare('FLUSH PRIVILEGES')->execute() === false) {
throw new Exception('Cant flush privileges');
}
}
/**
* @param array $validatedData
* @return void
* @throws Exception
*/
private function startInstallation(array $validatedData): void
{
// TODO: do the installation (maybe in an own class?)
}
}

View File

@@ -30,153 +30,111 @@ return [
'self_overview' => ['section' => 'admins', 'page' => 'admins'],
'sections' => [
'step1' => [
'title' => lng('install.dabatase'),
'title' => lng('install.database.title'),
'fields' => [
'sql_hostname' => [
'label' => lng('sql_hostname'),
'type' => 'text',
'mandatory' => true,
'value' => 'localhost'
'value' => old('sql_hostname', 'localhost', 'installation')
],
'sql_root_username' => [
'label' => lng('sql_root_username'),
'type' => 'password',
'type' => 'text',
'mandatory' => true,
'value' => old('sql_root_username', 'froxroot', 'installation'),
'next_to' => [
'sql_root_password' => [
'label' => lng('sql_root_password'),
'type' => 'password',
'mandatory' => true
'mandatory' => true,
'value' => old('sql_root_password', null, 'installation'),
],
]
],
'sql_username' => [
'label' => lng('sql_username'),
'type' => 'password',
'type' => 'text',
'mandatory' => true,
'value' => old('sql_username', 'froxlor', 'installation'),
'next_to' => [
'sql_password' => [
'label' => lng('sql_password'),
'type' => 'password',
'mandatory' => true
'mandatory' => true,
'value' => old('sql_password', null, 'installation'),
],
]
],
'sql_database' => [
'label' => lng('sql_database'),
'type' => 'text',
'mandatory' => true,
'value' => old('sql_database', 'froxlor', 'installation'),
],
'sql_override_database' => [
'label' => lng('sql_override_database'),
'type' => 'checkbox',
'value' => '1',
'checked' => old('sql_override_database', '0', 'installation')
],
]
],
'step2' => [
'title' => lng('admin.contactdata'),
'image' => 'icons/user_add.png',
'title' => lng('install.admin.title'),
'fields' => [
'name' => [
'label' => lng('name'),
'type' => 'text',
'mandatory' => true
'mandatory' => true,
'value' => old('name', 'Administrator', 'installation'),
],
'username' => [
'label' => lng('username'),
'type' => 'text',
'mandatory' => true
'mandatory' => true,
'value' => old('username', 'admin', 'installation'),
],
'password' => [
'label' => lng('password'),
'type' => 'password',
'mandatory' => true
'mandatory' => true,
'value' => old('password', null, 'installation'),
],
'email' => [
'label' => lng('email'),
'type' => 'text',
'mandatory' => true
'mandatory' => true,
'value' => old('email', null, 'installation'),
],
]
],
'step3' => [
'title' => lng('admin.servicedata'),
'image' => 'icons/user_add.png',
'title' => lng('install.system.title'),
'fields' => [
'ipaddress' => [
'label' => lng('serversettings.ipaddress.title'),
'type' => 'select'
'system' => [
'label' => lng('install.system.system'),
'type' => 'select',
'select_var' => $this->supportedOS,
],
'change_serversettings' => [
'label' => lng('admin.change_serversettings'),
'test' => [
'label' => lng('install.system.test'),
'type' => 'checkbox',
'value' => '1',
'checked' => false
],
'customers' => [
'label' => lng('admin.customers'),
'type' => 'textul',
'value' => 0,
'maxlength' => 9,
'mandatory' => true
]
],
'step4' => [
'title' => lng('install.system.title'),
'fields' => [
'system' => [
'label' => lng('install.system.system'),
'type' => 'textarea',
'value' => '/var/www/html/froxlor/bin/froxlor-cli cron --force',
'readonly' => true,
'rows' => 1
],
'customers_see_all' => [
'label' => lng('admin.customers_see_all'),
'type' => 'checkbox',
'value' => '1',
'checked' => false
],
'domains' => [
'label' => lng('admin.domains'),
'type' => 'textul',
'value' => 0,
'maxlength' => 9,
'mandatory' => true
],
'domains_see_all' => [
'label' => lng('admin.domains_see_all'),
'type' => 'checkbox',
'value' => '1',
'checked' => false
],
'caneditphpsettings' => [
'label' => lng('admin.caneditphpsettings'),
'type' => 'checkbox',
'value' => '1',
'checked' => false
],
'subdomains' => [
'label' => lng('customer.subdomains'),
'type' => 'textul',
'value' => 0,
'maxlength' => 9,
'mandatory' => true
],
'emails' => [
'label' => lng('customer.emails'),
'type' => 'textul',
'value' => 0,
'maxlength' => 9,
'mandatory' => true
],
'email_accounts' => [
'label' => lng('customer.accounts'),
'type' => 'textul',
'value' => 0,
'maxlength' => 9,
'mandatory' => true
],
'email_forwarders' => [
'label' => lng('customer.forwarders'),
'type' => 'textul',
'value' => 0,
'maxlength' => 9,
'mandatory' => true
],
'ftps' => [
'label' => lng('customer.ftps'),
'type' => 'textul',
'value' => 0,
'maxlength' => 9
],
'mysqls' => [
'label' => lng('customer.mysqls'),
'type' => 'textul',
'value' => 0,
'maxlength' => 9,
'mandatory' => true
]
]
]
]

View File

@@ -24,6 +24,7 @@
*/
use Froxlor\Language;
use Froxlor\UI\Request;
function view($template, $attributes)
{
@@ -36,3 +37,11 @@ function lng(string $identifier, array $arguments = [])
{
return Language::getTranslation($identifier, $arguments);
}
function old(string $identifier, string $default = null, string $session = null)
{
if ($session && isset($_SESSION[$session])) {
return $_SESSION[$session][$identifier] ?? $default;
}
return Request::get($identifier, $default);
}