浏览代码

Converted installer

Sergio Brighenti 5 年之前
父节点
当前提交
b5784abb52

+ 1 - 1
app/Controllers/UploadController.php

@@ -84,7 +84,7 @@ class UploadController extends Controller
         $json['message'] = 'OK.';
         $json['message'] = 'OK.';
         $json['url'] = urlFor("/$user->user_code/$code.$fileInfo[extension]");
         $json['url'] = urlFor("/$user->user_code/$code.$fileInfo[extension]");
 
 
-        $this->logger->info("User $user->username uploaded new media.", [$this->database->raw()->lastInsertId()]);
+        $this->logger->info("User $user->username uploaded new media.", [$this->database->getPdo()->lastInsertId()]);
 
 
         return json($response, $json, 201);
         return json($response, $json, 201);
     }
     }

+ 0 - 65
app/Database/DB.php

@@ -11,25 +11,14 @@ class DB
 	/** @var  DB */
 	/** @var  DB */
 	protected static $instance;
 	protected static $instance;
 
 
-	/** @var string */
-	private static $password;
-
-	/** @var string */
-	private static $username;
-
 	/** @var PDO */
 	/** @var PDO */
 	protected $pdo;
 	protected $pdo;
 
 
-	/** @var string */
-	protected static $dsn = 'sqlite:database.db';
-
 	/** @var string */
 	/** @var string */
 	protected $currentDriver;
 	protected $currentDriver;
 
 
 	public function __construct(string $dsn, string $username = null, string $password = null)
 	public function __construct(string $dsn, string $username = null, string $password = null)
 	{
 	{
-		self::setDsn($dsn, $username, $password);
-
 		$this->pdo = new PDO($dsn, $username, $password);
 		$this->pdo = new PDO($dsn, $username, $password);
 		$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
 		$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
 		$this->pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);
 		$this->pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);
@@ -72,58 +61,4 @@ class DB
 	{
 	{
 		return $this->currentDriver;
 		return $this->currentDriver;
 	}
 	}
-
-	public static function getInstance(): DB
-	{
-		if (self::$instance === null) {
-			self::$instance = new self(self::$dsn, self::$username, self::$password);
-		}
-
-		return self::$instance;
-	}
-
-	/**
-	 * Perform a query
-	 * @param string $query
-	 * @param array $parameters
-	 * @return bool|\PDOStatement|string
-	 */
-	public static function doQuery(string $query, $parameters = [])
-	{
-		return self::getInstance()->query($query, $parameters);
-	}
-
-	/**
-	 * Static method to get the current driver name
-	 * @return string
-	 */
-	public static function driver(): string
-	{
-
-		return self::getInstance()->getCurrentDriver();
-	}
-
-	/**
-	 * Get directly the PDO instance
-	 * @return PDO
-	 */
-	public static function raw(): PDO
-	{
-
-		return self::getInstance()->getPdo();
-	}
-
-	/**
-	 * Set the PDO connection string
-	 * @param string $dsn
-	 * @param string|null $username
-	 * @param string|null $password
-	 */
-	public static function setDsn(string $dsn, string $username = null, string $password = null)
-	{
-		self::$dsn = $dsn;
-		self::$username = $username;
-		self::$password = $password;
-	}
-
 }
 }

+ 98 - 0
app/Database/Migrator.php

@@ -0,0 +1,98 @@
+<?php
+
+
+namespace App\Database;
+
+
+use PDOException;
+
+class Migrator
+{
+    /**
+     * @var DB
+     */
+    private $db;
+    /**
+     * @var string
+     */
+    private $schemaPath;
+    /**
+     * @var bool
+     */
+    private $firstMigrate;
+
+
+    /**
+     * Migrator constructor.
+     * @param  DB  $db
+     * @param  string  $schemaPath
+     * @param  bool  $firstMigrate
+     */
+    public function __construct(DB $db, string $schemaPath, bool $firstMigrate = false)
+    {
+        $this->db = $db;
+        $this->schemaPath = $schemaPath;
+        $this->firstMigrate = $firstMigrate;
+    }
+
+    public function migrate()
+    {
+        try {
+            $this->db->query('SELECT 1 FROM `migrations` LIMIT 1');
+        } catch (PDOException $exception) {
+            $this->firstMigrate = true;
+        }
+
+        if ($this->firstMigrate) {
+            $this->db->getPdo()->exec(file_get_contents($this->schemaPath.DIRECTORY_SEPARATOR.'migrations.sql'));
+        }
+
+        $files = glob($this->schemaPath.DIRECTORY_SEPARATOR.$this->db->getCurrentDriver().'/*.sql');
+
+        $names = array_map(function ($path) {
+            return basename($path);
+        }, $files);
+
+        $in = str_repeat('?, ', count($names) - 1).'?';
+
+        $inMigrationsTable = $this->db->query("SELECT * FROM `migrations` WHERE `name` IN ($in)", $names)->fetchAll();
+
+
+        foreach ($files as $file) {
+
+            $continue = false;
+            $exists = false;
+
+            foreach ($inMigrationsTable as $migration) {
+                if (basename($file) === $migration->name && $migration->migrated) {
+                    $continue = true;
+                    break;
+                } else {
+                    if (basename($file) === $migration->name && !$migration->migrated) {
+                        $exists = true;
+                        break;
+                    }
+                }
+            }
+            if ($continue) {
+                continue;
+            }
+
+            $sql = file_get_contents($file);
+            try {
+                $this->db->getPdo()->exec($sql);
+                if (!$exists) {
+                    $this->db->query('INSERT INTO `migrations` VALUES (?,?)', [basename($file), 1]);
+                } else {
+                    $this->db->query('UPDATE `migrations` SET `migrated`=? WHERE `name`=?', [1, basename($file)]);
+                }
+            } catch (PDOException $exception) {
+                if (!$exists) {
+                    $this->db->query('INSERT INTO `migrations` VALUES (?,?)', [basename($file), 0]);
+                }
+                throw $exception;
+            }
+        }
+    }
+
+}

+ 71 - 0
app/Factories/ViewFactory.php

@@ -0,0 +1,71 @@
+<?php
+
+
+namespace App\Factories;
+
+
+use App\Web\View;
+use Psr\Container\ContainerInterface as Container;
+use Slim\Factory\ServerRequestCreatorFactory;
+use Twig\Environment;
+use Twig\Loader\FilesystemLoader;
+use Twig\TwigFunction;
+
+class ViewFactory
+{
+
+    public static function createAppInstance(Container $container)
+    {
+        $config = $container->get('config');
+        $loader = new FilesystemLoader(BASE_DIR.'resources/templates');
+
+        $twig = new Environment($loader, [
+            'cache' => BASE_DIR.'resources/cache/twig',
+            'autoescape' => 'html',
+            'debug' => $config['debug'],
+            'auto_reload' => $config['debug'],
+        ]);
+
+        $request = ServerRequestCreatorFactory::create()->createServerRequestFromGlobals();
+
+        $twig->addGlobal('config', $config);
+        $twig->addGlobal('request', $request);
+        $twig->addGlobal('alerts', $container->get('session')->getAlert());
+        $twig->addGlobal('session', $container->get('session')->all());
+        $twig->addGlobal('current_lang', $container->get('lang')->getLang());
+        $twig->addGlobal('PLATFORM_VERSION', PLATFORM_VERSION);
+
+        $twig->addFunction(new TwigFunction('route', 'route'));
+        $twig->addFunction(new TwigFunction('lang', 'lang'));
+        $twig->addFunction(new TwigFunction('urlFor', 'urlFor'));
+        $twig->addFunction(new TwigFunction('asset', 'asset'));
+        $twig->addFunction(new TwigFunction('mime2font', 'mime2font'));
+        $twig->addFunction(new TwigFunction('queryParams', 'queryParams'));
+        $twig->addFunction(new TwigFunction('isDisplayableImage', 'isDisplayableImage'));
+
+        return new View($twig);
+    }
+
+    public static function createInstallerInstance(Container $container) {
+        $config = $container->get('config');
+        $loader = new FilesystemLoader([BASE_DIR . 'install/templates', BASE_DIR.'resources/templates']);
+
+        $twig = new Environment($loader, [
+            'cache' => false,
+            'autoescape' => 'html',
+            'debug' => $config['debug'],
+            'auto_reload' => $config['debug'],
+        ]);
+
+        $request = ServerRequestCreatorFactory::create()->createServerRequestFromGlobals();
+
+        $twig->addGlobal('config', $config);
+        $twig->addGlobal('request', $request);
+        $twig->addGlobal('alerts', $container->get('session')->getAlert());
+        $twig->addGlobal('session', $container->get('session')->all());
+        $twig->addGlobal('PLATFORM_VERSION', PLATFORM_VERSION);
+
+        return new View($twig);
+    }
+
+}

+ 2 - 3
app/Middleware/CheckForMaintenanceMiddleware.php

@@ -17,11 +17,10 @@ class CheckForMaintenanceMiddleware extends Middleware
      */
      */
     public function __invoke(Request $request, RequestHandler $handler): Response
     public function __invoke(Request $request, RequestHandler $handler): Response
     {
     {
-        if (isset($this->config['maintenance']) && $this->settings['maintenance'] && !$this->database->query('SELECT `id`, `is_admin` FROM `users` WHERE `id` = ? LIMIT 1', [$this->session->get('user_id')])->fetch()->is_admin) {
+        if (isset($this->config['maintenance']) && $this->config['maintenance'] && !$this->database->query('SELECT `id`, `is_admin` FROM `users` WHERE `id` = ? LIMIT 1', [$this->session->get('user_id')])->fetch()->is_admin) {
             throw new UnderMaintenanceException($request);
             throw new UnderMaintenanceException($request);
         }
         }
 
 
-        $response = $handler->handle($request);
-        return $response;
+        return $handler->handle($request);
     }
     }
 }
 }

+ 15 - 0
app/Middleware/Middleware.php

@@ -3,11 +3,26 @@
 namespace App\Middleware;
 namespace App\Middleware;
 
 
 
 
+use App\Database\DB;
+use App\Web\Lang;
+use App\Web\Session;
 use DI\Container;
 use DI\Container;
+use League\Flysystem\Filesystem;
+use Monolog\Logger;
 use Psr\Http\Message\ResponseInterface as Response;
 use Psr\Http\Message\ResponseInterface as Response;
 use Psr\Http\Message\ServerRequestInterface as Request;
 use Psr\Http\Message\ServerRequestInterface as Request;
 use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
 use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
+use Twig\Environment;
 
 
+/**
+ * @property Session|null session
+ * @property Environment view
+ * @property DB|null database
+ * @property Logger|null logger
+ * @property Filesystem|null storage
+ * @property Lang lang
+ * @property array config
+ */
 abstract class Middleware
 abstract class Middleware
 {
 {
     /** @var Container */
     /** @var Container */

+ 134 - 130
app/Web/Lang.php

@@ -6,134 +6,138 @@ namespace App\Web;
 class Lang
 class Lang
 {
 {
 
 
-	const DEFAULT_LANG = 'en';
-	const LANG_PATH = __DIR__ . '../../resources/lang/';
-
-	/** @var  string */
-	protected static $langPath = self::LANG_PATH;
-
-	/** @var  string */
-	protected static $lang;
-
-	/** @var  Lang */
-	protected static $instance;
-
-	/** @var  array */
-	protected $cache = [];
-
-
-	/**
-	 * @return Lang
-	 */
-	public static function getInstance(): Lang
-	{
-		if (self::$instance === null) {
-			self::$instance = new self();
-		}
-
-		return self::$instance;
-	}
-
-	/**
-	 * @param string $lang
-	 * @param string $langPath
-	 * @return Lang
-	 */
-	public static function build($lang = self::DEFAULT_LANG, $langPath = null): Lang
-	{
-		self::$lang = $lang;
-
-		if ($langPath !== null) {
-			self::$langPath = $langPath;
-		}
-
-		self::$instance = new self();
-
-		return self::$instance;
-	}
-
-	/**
-	 * Recognize the current language from the request.
-	 * @return bool|string
-	 */
-	public static function recognize()
-	{
-		if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
-			return locale_accept_from_http($_SERVER['HTTP_ACCEPT_LANGUAGE']);
-		}
-		return self::DEFAULT_LANG;
-	}
-
-	/**
-	 * @return string
-	 */
-	public static function getLang(): string
-	{
-		return self::$lang;
-	}
-
-	/**
-	 * @return array
-	 */
-	public static function getList()
-	{
-		$languages = [];
-
-		$default = count(include self::$langPath . self::DEFAULT_LANG . '.lang.php') - 1;
-
-		foreach (glob(self::$langPath . '*.lang.php') as $file) {
-			$dict = include $file;
-
-			$count = count($dict) - 1;
-			$prepend = "[{$count}/{$default}] ";
-
-			$languages[str_replace('.lang.php', '', basename($file))] = $prepend . $dict['lang'];
-		}
-
-		return $languages;
-	}
-
-
-	/**
-	 * @param $key
-	 * @param array $args
-	 * @return string
-	 */
-	public function get($key, $args = []): string
-	{
-		return $this->getString($key, self::$lang, $args);
-	}
-
-	/**
-	 * @param $key
-	 * @param $lang
-	 * @param $args
-	 * @return string
-	 */
-	private function getString($key, $lang, $args): string
-	{
-		$redLang = strtolower(substr($lang, 0, 2));
-
-		if (array_key_exists($lang, $this->cache)) {
-			$transDict = $this->cache[$lang];
-		} else if (file_exists(self::$langPath . $lang . '.lang.php')) {
-			$transDict = include self::$langPath . $lang . '.lang.php';
-			$this->cache[$lang] = $transDict;
-		} else if (file_exists(self::$langPath . $redLang . '.lang.php')) {
-			$transDict = include self::$langPath . $redLang . '.lang.php';
-			$this->cache[$lang] = $transDict;
-		} else {
-			$transDict = [];
-		}
-
-		if (array_key_exists($key, $transDict)) {
-			return vsprintf($transDict[$key], $args);
-		}
-
-		if ($lang !== self::DEFAULT_LANG) {
-			return $this->getString($key, self::DEFAULT_LANG, $args);
-		}
-
-		return $key;
-	}
+    const DEFAULT_LANG = 'en';
+    const LANG_PATH = __DIR__.'../../resources/lang/';
+
+    /** @var  string */
+    protected static $langPath = self::LANG_PATH;
+
+    /** @var  string */
+    protected static $lang;
+
+    /** @var  Lang */
+    protected static $instance;
+
+    /** @var  array */
+    protected $cache = [];
+
+
+    /**
+     * @return Lang
+     */
+    public static function getInstance(): Lang
+    {
+        if (self::$instance === null) {
+            self::$instance = new self();
+        }
+
+        return self::$instance;
+    }
+
+    /**
+     * @param  string  $lang
+     * @param  string  $langPath
+     * @return Lang
+     */
+    public static function build($lang = self::DEFAULT_LANG, $langPath = null): Lang
+    {
+        self::$lang = $lang;
+
+        if ($langPath !== null) {
+            self::$langPath = $langPath;
+        }
+
+        self::$instance = new self();
+
+        return self::$instance;
+    }
+
+    /**
+     * Recognize the current language from the request.
+     * @return bool|string
+     */
+    public static function recognize()
+    {
+        if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
+            return locale_accept_from_http($_SERVER['HTTP_ACCEPT_LANGUAGE']);
+        }
+        return self::DEFAULT_LANG;
+    }
+
+    /**
+     * @return string
+     */
+    public static function getLang(): string
+    {
+        return self::$lang;
+    }
+
+    /**
+     * @return array
+     */
+    public static function getList()
+    {
+        $languages = [];
+
+        $default = count(include self::$langPath.self::DEFAULT_LANG.'.lang.php') - 1;
+
+        foreach (glob(self::$langPath.'*.lang.php') as $file) {
+            $dict = include $file;
+
+            $count = count($dict) - 1;
+            $percent = round(($count / $default) * 100);
+
+            $languages[str_replace('.lang.php', '', basename($file))] = "[{$percent}%] ".$dict['lang'];
+        }
+
+        return $languages;
+    }
+
+
+    /**
+     * @param $key
+     * @param  array  $args
+     * @return string
+     */
+    public function get($key, $args = []): string
+    {
+        return $this->getString($key, self::$lang, $args);
+    }
+
+    /**
+     * @param $key
+     * @param $lang
+     * @param $args
+     * @return string
+     */
+    private function getString($key, $lang, $args): string
+    {
+        $redLang = strtolower(substr($lang, 0, 2));
+
+        if (array_key_exists($lang, $this->cache)) {
+            $transDict = $this->cache[$lang];
+        } else {
+            if (file_exists(self::$langPath.$lang.'.lang.php')) {
+                $transDict = include self::$langPath.$lang.'.lang.php';
+                $this->cache[$lang] = $transDict;
+            } else {
+                if (file_exists(self::$langPath.$redLang.'.lang.php')) {
+                    $transDict = include self::$langPath.$redLang.'.lang.php';
+                    $this->cache[$lang] = $transDict;
+                } else {
+                    $transDict = [];
+                }
+            }
+        }
+
+        if (array_key_exists($key, $transDict)) {
+            return vsprintf($transDict[$key], $args);
+        }
+
+        if ($lang !== self::DEFAULT_LANG) {
+            return $this->getString($key, self::DEFAULT_LANG, $args);
+        }
+
+        return $key;
+    }
 }
 }

+ 2 - 32
app/Web/View.php

@@ -24,40 +24,10 @@ class View
 
 
     /**
     /**
      * View constructor.
      * View constructor.
-     * @param  Container  $container
-     * @throws \DI\DependencyException
-     * @throws \DI\NotFoundException
+     * @param  Environment  $twig
      */
      */
-    public function __construct(Container $container)
+    public function __construct(Environment $twig)
     {
     {
-        $config = $container->get('config');
-        $loader = new FilesystemLoader(BASE_DIR.'resources/templates');
-
-        $twig = new Environment($loader, [
-            'cache' => BASE_DIR.'resources/cache/twig',
-            'autoescape' => 'html',
-            'debug' => $config['debug'],
-            'auto_reload' => $config['debug'],
-        ]);
-
-        $serverRequestCreator = ServerRequestCreatorFactory::create();
-        $request = $serverRequestCreator->createServerRequestFromGlobals();
-
-        $twig->addGlobal('config', $config);
-        $twig->addGlobal('request', $request);
-        $twig->addGlobal('alerts', $container->get('session')->getAlert());
-        $twig->addGlobal('session', $container->get('session')->all());
-        $twig->addGlobal('current_lang', $container->get('lang')->getLang());
-        $twig->addGlobal('PLATFORM_VERSION', PLATFORM_VERSION);
-
-        $twig->addFunction(new TwigFunction('route', 'route'));
-        $twig->addFunction(new TwigFunction('lang', 'lang'));
-        $twig->addFunction(new TwigFunction('urlFor', 'urlFor'));
-        $twig->addFunction(new TwigFunction('asset', 'asset'));
-        $twig->addFunction(new TwigFunction('mime2font', 'mime2font'));
-        $twig->addFunction(new TwigFunction('queryParams', 'queryParams'));
-        $twig->addFunction(new TwigFunction('isDisplayableImage', 'isDisplayableImage'));
-
         $this->twig = $twig;
         $this->twig = $twig;
     }
     }
 
 

+ 3 - 3
bootstrap/app.php

@@ -3,9 +3,9 @@
 use App\Database\DB;
 use App\Database\DB;
 use App\Exception\Handlers\AppErrorHandler;
 use App\Exception\Handlers\AppErrorHandler;
 use App\Exception\Handlers\Renderers\HtmlErrorRenderer;
 use App\Exception\Handlers\Renderers\HtmlErrorRenderer;
+use App\Factories\ViewFactory;
 use App\Web\Lang;
 use App\Web\Lang;
 use App\Web\Session;
 use App\Web\Session;
-use App\Web\View;
 use Aws\S3\S3Client;
 use Aws\S3\S3Client;
 use DI\Bridge\Slim\Bridge;
 use DI\Bridge\Slim\Bridge;
 use DI\ContainerBuilder;
 use DI\ContainerBuilder;
@@ -137,8 +137,8 @@ $builder->addDefinitions([
         return Lang::build(Lang::recognize(), BASE_DIR.'resources/lang/');
         return Lang::build(Lang::recognize(), BASE_DIR.'resources/lang/');
     }),
     }),
 
 
-    'view' => factory(function ($container) {
-        return new View($container);
+    'view' => factory(function (Container $container) {
+        return ViewFactory::createAppInstance($container);
     }),
     }),
 ]);
 ]);
 
 

+ 24 - 24
composer.lock

@@ -4,20 +4,20 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
         "This file is @generated automatically"
     ],
     ],
-    "content-hash": "e7ad26b98d3ded901c5c663673f3bfff",
+    "content-hash": "a5d4341b89b81518c0e488cd3bc47127",
     "packages": [
     "packages": [
         {
         {
             "name": "aws/aws-sdk-php",
             "name": "aws/aws-sdk-php",
-            "version": "3.115.2",
+            "version": "3.116.0",
             "source": {
             "source": {
                 "type": "git",
                 "type": "git",
                 "url": "https://github.com/aws/aws-sdk-php.git",
                 "url": "https://github.com/aws/aws-sdk-php.git",
-                "reference": "15792196be1b3b1b5663bca7b6cd021d005e2265"
+                "reference": "b0669936681365a6c5201a6d28bfa76553052912"
             },
             },
             "dist": {
             "dist": {
                 "type": "zip",
                 "type": "zip",
-                "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/15792196be1b3b1b5663bca7b6cd021d005e2265",
-                "reference": "15792196be1b3b1b5663bca7b6cd021d005e2265",
+                "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/b0669936681365a6c5201a6d28bfa76553052912",
+                "reference": "b0669936681365a6c5201a6d28bfa76553052912",
                 "shasum": ""
                 "shasum": ""
             },
             },
             "require": {
             "require": {
@@ -87,7 +87,7 @@
                 "s3",
                 "s3",
                 "sdk"
                 "sdk"
             ],
             ],
-            "time": "2019-11-11T19:22:34+00:00"
+            "time": "2019-11-12T19:15:09+00:00"
         },
         },
         {
         {
             "name": "firebase/php-jwt",
             "name": "firebase/php-jwt",
@@ -249,16 +249,16 @@
         },
         },
         {
         {
             "name": "google/cloud-storage",
             "name": "google/cloud-storage",
-            "version": "v1.15.0",
+            "version": "v1.16.0",
             "source": {
             "source": {
                 "type": "git",
                 "type": "git",
                 "url": "https://github.com/googleapis/google-cloud-php-storage.git",
                 "url": "https://github.com/googleapis/google-cloud-php-storage.git",
-                "reference": "003eb1a735d77f8196f816c4a921199d15c4a82c"
+                "reference": "308ad790b257286e02777e3bf1a2c0473a4651a7"
             },
             },
             "dist": {
             "dist": {
                 "type": "zip",
                 "type": "zip",
-                "url": "https://api.github.com/repos/googleapis/google-cloud-php-storage/zipball/003eb1a735d77f8196f816c4a921199d15c4a82c",
-                "reference": "003eb1a735d77f8196f816c4a921199d15c4a82c",
+                "url": "https://api.github.com/repos/googleapis/google-cloud-php-storage/zipball/308ad790b257286e02777e3bf1a2c0473a4651a7",
+                "reference": "308ad790b257286e02777e3bf1a2c0473a4651a7",
                 "shasum": ""
                 "shasum": ""
             },
             },
             "require": {
             "require": {
@@ -296,7 +296,7 @@
                 "Apache-2.0"
                 "Apache-2.0"
             ],
             ],
             "description": "Cloud Storage Client for PHP",
             "description": "Cloud Storage Client for PHP",
-            "time": "2019-10-28T19:05:44+00:00"
+            "time": "2019-11-12T23:35:42+00:00"
         },
         },
         {
         {
             "name": "google/crc32",
             "name": "google/crc32",
@@ -893,16 +893,16 @@
         },
         },
         {
         {
             "name": "monolog/monolog",
             "name": "monolog/monolog",
-            "version": "1.25.1",
+            "version": "1.25.2",
             "source": {
             "source": {
                 "type": "git",
                 "type": "git",
                 "url": "https://github.com/Seldaek/monolog.git",
                 "url": "https://github.com/Seldaek/monolog.git",
-                "reference": "70e65a5470a42cfec1a7da00d30edb6e617e8dcf"
+                "reference": "d5e2fb341cb44f7e2ab639d12a1e5901091ec287"
             },
             },
             "dist": {
             "dist": {
                 "type": "zip",
                 "type": "zip",
-                "url": "https://api.github.com/repos/Seldaek/monolog/zipball/70e65a5470a42cfec1a7da00d30edb6e617e8dcf",
-                "reference": "70e65a5470a42cfec1a7da00d30edb6e617e8dcf",
+                "url": "https://api.github.com/repos/Seldaek/monolog/zipball/d5e2fb341cb44f7e2ab639d12a1e5901091ec287",
+                "reference": "d5e2fb341cb44f7e2ab639d12a1e5901091ec287",
                 "shasum": ""
                 "shasum": ""
             },
             },
             "require": {
             "require": {
@@ -967,7 +967,7 @@
                 "logging",
                 "logging",
                 "psr-3"
                 "psr-3"
             ],
             ],
-            "time": "2019-09-06T13:49:17+00:00"
+            "time": "2019-11-13T10:00:05+00:00"
         },
         },
         {
         {
             "name": "mtdowling/jmespath.php",
             "name": "mtdowling/jmespath.php",
@@ -3073,16 +3073,16 @@
         },
         },
         {
         {
             "name": "symfony/console",
             "name": "symfony/console",
-            "version": "v4.3.7",
+            "version": "v4.3.8",
             "source": {
             "source": {
                 "type": "git",
                 "type": "git",
                 "url": "https://github.com/symfony/console.git",
                 "url": "https://github.com/symfony/console.git",
-                "reference": "d2e39dbddae68560fa6be0c576da6ad4e945b90d"
+                "reference": "831424efae0a1fe6642784bd52aae14ece6538e6"
             },
             },
             "dist": {
             "dist": {
                 "type": "zip",
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/console/zipball/d2e39dbddae68560fa6be0c576da6ad4e945b90d",
-                "reference": "d2e39dbddae68560fa6be0c576da6ad4e945b90d",
+                "url": "https://api.github.com/repos/symfony/console/zipball/831424efae0a1fe6642784bd52aae14ece6538e6",
+                "reference": "831424efae0a1fe6642784bd52aae14ece6538e6",
                 "shasum": ""
                 "shasum": ""
             },
             },
             "require": {
             "require": {
@@ -3144,11 +3144,11 @@
             ],
             ],
             "description": "Symfony Console Component",
             "description": "Symfony Console Component",
             "homepage": "https://symfony.com",
             "homepage": "https://symfony.com",
-            "time": "2019-11-05T15:00:49+00:00"
+            "time": "2019-11-13T07:29:07+00:00"
         },
         },
         {
         {
             "name": "symfony/finder",
             "name": "symfony/finder",
-            "version": "v4.3.7",
+            "version": "v4.3.8",
             "source": {
             "source": {
                 "type": "git",
                 "type": "git",
                 "url": "https://github.com/symfony/finder.git",
                 "url": "https://github.com/symfony/finder.git",
@@ -3313,9 +3313,9 @@
         }
         }
     ],
     ],
     "aliases": [],
     "aliases": [],
-    "minimum-stability": "dev",
+    "minimum-stability": "stable",
     "stability-flags": [],
     "stability-flags": [],
-    "prefer-stable": true,
+    "prefer-stable": false,
     "prefer-lowest": false,
     "prefer-lowest": false,
     "platform": {
     "platform": {
         "php": ">=7.1",
         "php": ">=7.1",

+ 250 - 305
install/index.php

@@ -1,10 +1,15 @@
 <?php
 <?php
 (PHP_MAJOR_VERSION >= 7 && PHP_MINOR_VERSION >= 1) ?: die('Sorry, PHP 7.1 or above is required to run XBackBone.');
 (PHP_MAJOR_VERSION >= 7 && PHP_MINOR_VERSION >= 1) ?: die('Sorry, PHP 7.1 or above is required to run XBackBone.');
-require __DIR__ . '/../vendor/autoload.php';
+require __DIR__.'/../vendor/autoload.php';
 
 
 use App\Database\DB;
 use App\Database\DB;
+use App\Database\Migrator;
+use App\Factories\ViewFactory;
 use App\Web\Session;
 use App\Web\Session;
+use App\Web\View;
 use Aws\S3\S3Client;
 use Aws\S3\S3Client;
+use DI\Bridge\Slim\Bridge;
+use DI\ContainerBuilder;
 use Google\Cloud\Storage\StorageClient;
 use Google\Cloud\Storage\StorageClient;
 use League\Flysystem\Adapter\Local;
 use League\Flysystem\Adapter\Local;
 use League\Flysystem\AwsS3v3\AwsS3Adapter;
 use League\Flysystem\AwsS3v3\AwsS3Adapter;
@@ -12,322 +17,262 @@ use League\Flysystem\Adapter\Ftp as FtpAdapter;
 use League\Flysystem\FileExistsException;
 use League\Flysystem\FileExistsException;
 use Spatie\Dropbox\Client as DropboxClient;
 use Spatie\Dropbox\Client as DropboxClient;
 use League\Flysystem\Filesystem;
 use League\Flysystem\Filesystem;
-use Slim\App;
-use Slim\Container;
-use Slim\Http\Environment;
-use Slim\Http\Request;
-use Slim\Http\Response;
-use Slim\Http\Uri;
-use Slim\Views\Twig;
 use Spatie\FlysystemDropbox\DropboxAdapter;
 use Spatie\FlysystemDropbox\DropboxAdapter;
 use Superbalist\Flysystem\GoogleStorage\GoogleStorageAdapter;
 use Superbalist\Flysystem\GoogleStorage\GoogleStorageAdapter;
+use Psr\Container\ContainerInterface as Container;
+use Psr\Http\Message\ServerRequestInterface as Request;
+use Psr\Http\Message\ResponseInterface as Response;
+use function DI\factory;
+use function DI\get;
+use function DI\value;
 
 
-define('PLATFORM_VERSION', json_decode(file_get_contents(__DIR__ . '/../composer.json'))->version);
+define('PLATFORM_VERSION', json_decode(file_get_contents(__DIR__.'/../composer.json'))->version);
+define('BASE_DIR', realpath(__DIR__.'/../').DIRECTORY_SEPARATOR);
 
 
 // default config
 // default config
 $config = [
 $config = [
-	'base_url' => str_replace('/install/', '', (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http') . "://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]"),
-	'displayErrorDetails' => true,
-	'db' => [
-		'connection' => 'sqlite',
-		'dsn' => realpath(__DIR__ . '/../') . implode(DIRECTORY_SEPARATOR, ['resources', 'database', 'xbackbone.db']),
-		'username' => null,
-		'password' => null,
-	],
-	'storage' => [
-		'driver' => 'local',
-		'path' => realpath(__DIR__ . '/../') . DIRECTORY_SEPARATOR . 'storage',
-	],
+    'base_url' => str_replace('/install/', '', (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http')."://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]"),
+    'debug' => true,
+    'db' => [
+        'connection' => 'sqlite',
+        'dsn' => realpath(__DIR__.'/../').implode(DIRECTORY_SEPARATOR, ['resources', 'database', 'xbackbone.db']),
+        'username' => null,
+        'password' => null,
+    ],
+    'storage' => [
+        'driver' => 'local',
+        'path' => realpath(__DIR__.'/../').DIRECTORY_SEPARATOR.'storage',
+    ],
 ];
 ];
 
 
-if (file_exists(__DIR__ . '/../config.php')) {
-	$config = array_replace_recursive($config, require __DIR__ . '/../config.php');
+if (file_exists(__DIR__.'/../config.php')) {
+    $config = array_replace_recursive($config, require __DIR__.'/../config.php');
 }
 }
 
 
-$container = new Container(['settings' => $config]);
-
-$container['session'] = function ($container) {
-	return new Session('xbackbone_session');
-};
-
-$container['view'] = function ($container) use (&$config) {
-	$view = new Twig([__DIR__ . '/templates', __DIR__ . '/../resources/templates'], [
-		'cache' => false,
-		'autoescape' => 'html',
-		'debug' => $config['displayErrorDetails'],
-		'auto_reload' => $config['displayErrorDetails'],
-	]);
-
-	// Instantiate and add Slim specific extension
-	$router = $container->get('router');
-	$uri = Uri::createFromEnvironment(new Environment($_SERVER));
-	$view->addExtension(new Slim\Views\TwigExtension($router, $uri));
-
-	$view->getEnvironment()->addGlobal('config', $config);
-	$view->getEnvironment()->addGlobal('request', $container->get('request'));
-	$view->getEnvironment()->addGlobal('alerts', $container->get('session')->getAlert());
-	$view->getEnvironment()->addGlobal('session', $container->get('session')->all());
-	$view->getEnvironment()->addGlobal('PLATFORM_VERSION', PLATFORM_VERSION);
-	return $view;
-};
-
-$container['storage'] = function ($container) use (&$config) {
-
-	switch ($config['storage']['driver']) {
-		case 'local':
-			return new Filesystem(new Local($config['storage']['path']));
-		case 's3':
-			$client = new S3Client([
-				'credentials' => [
-					'key' => $config['storage']['key'],
-					'secret' => $config['storage']['secret'],
-				],
-				'region' => $config['storage']['region'],
-				'version' => 'latest',
-			]);
-
-			return new Filesystem(new AwsS3Adapter($client, $config['storage']['bucket'], $config['storage']['path']));
-		case 'dropbox':
-			$client = new DropboxClient($config['storage']['token']);
-			return new Filesystem(new DropboxAdapter($client), ['case_sensitive' => false]);
-		case 'ftp':
-			return new Filesystem(new FtpAdapter([
-				'host' => $config['storage']['host'],
-				'username' => $config['storage']['username'],
-				'password' => $config['storage']['password'],
-				'port' => $config['storage']['port'],
-				'root' => $config['storage']['path'],
-				'passive' => $config['storage']['passive'],
-				'ssl' => $config['storage']['ssl'],
-				'timeout' => 30,
-			]));
-		case 'google-cloud':
-			$client = new StorageClient([
-				'projectId' => $config['storage']['project_id'],
-				'keyFilePath' => $config['storage']['key_path'],
-			]);
-			return new Filesystem(new GoogleStorageAdapter($client, $client->bucket($config['storage']['bucket'])));
-		default:
-			throw new InvalidArgumentException('The driver specified is not supported.');
-	}
-};
-
-function migrate($config) {
-	$firstMigrate = false;
-	if ($config['db']['connection'] === 'sqlite' && !file_exists(__DIR__ . '/../' . $config['db']['dsn'])) {
-		touch(__DIR__ . '/../' . $config['db']['dsn']);
-		$firstMigrate = true;
-	}
-
-	try {
-		DB::doQuery('SELECT 1 FROM `migrations` LIMIT 1');
-	} catch (PDOException $exception) {
-		$firstMigrate = true;
-	}
-
-	if ($firstMigrate) {
-		DB::raw()->exec(file_get_contents(__DIR__ . '/../resources/schemas/migrations.sql'));
-	}
-
-	$files = glob(__DIR__ . '/../resources/schemas/' . DB::driver() . '/*.sql');
-
-	$names = array_map(function ($path) {
-		return basename($path);
-	}, $files);
-
-	$in = str_repeat('?, ', count($names) - 1) . '?';
-
-	$inMigrationsTable = DB::doQuery("SELECT * FROM `migrations` WHERE `name` IN ($in)", $names)->fetchAll();
-
-
-	foreach ($files as $file) {
-
-		$continue = false;
-		$exists = false;
-
-		foreach ($inMigrationsTable as $migration) {
-			if (basename($file) === $migration->name && $migration->migrated) {
-				$continue = true;
-				break;
-			} else if (basename($file) === $migration->name && !$migration->migrated) {
-				$exists = true;
-				break;
-			}
-		}
-		if ($continue) continue;
-
-		$sql = file_get_contents($file);
-		try {
-			DB::raw()->exec($sql);
-			if (!$exists) {
-				DB::doQuery('INSERT INTO `migrations` VALUES (?,?)', [basename($file), 1]);
-			} else {
-				DB::doQuery('UPDATE `migrations` SET `migrated`=? WHERE `name`=?', [1, basename($file)]);
-			}
-		} catch (PDOException $exception) {
-			if (!$exists) {
-				DB::doQuery('INSERT INTO `migrations` VALUES (?,?)', [basename($file), 0]);
-			}
-			throw $exception;
-		}
-	}
-}
-
-$app = new App($container);
-
-$app->get('/', function (Request $request, Response $response) {
-
-	if (!extension_loaded('gd')) {
-		$this->session->alert('The required "gd" extension is not loaded.', 'danger');
-	}
-
-	if (!extension_loaded('intl')) {
-		$this->session->alert('The required "intl" extension is not loaded.', 'danger');
-	}
-
-	if (!extension_loaded('json')) {
-		$this->session->alert('The required "json" extension is not loaded.', 'danger');
-	}
-
-	if (!is_writable(__DIR__ . '/../resources/cache')) {
-		$this->session->alert('The cache folder is not writable (' . __DIR__ . '/../resources/cache' . ')', 'danger');
-	}
-
-	if (!is_writable(__DIR__ . '/../resources/database')) {
-		$this->session->alert('The database folder is not writable (' . __DIR__ . '/../resources/database' . ')', 'danger');
-	}
-
-	if (!is_writable(__DIR__ . '/../resources/sessions')) {
-		$this->session->alert('The sessions folder is not writable (' . __DIR__ . '/../resources/sessions' . ')', 'danger');
-	}
-
-	$installed = file_exists(__DIR__ . '/../config.php');
-
-	return $this->view->render($response, 'install.twig', [
-		'installed' => $installed,
-	]);
+$builder = new ContainerBuilder();
+
+$builder->addDefinitions([
+    'config' => value($config),
+
+    Session::class => factory(function () {
+        return new Session('xbackbone_session');
+    }),
+    'session' => get(Session::class),
+
+    Filesystem::class => factory(function (Container $container) {
+        $config = $container->get('config');
+        switch ($config['storage']['driver']) {
+            case 'local':
+                return new Filesystem(new Local($config['storage']['path']));
+            case 's3':
+                $client = new S3Client([
+                    'credentials' => [
+                        'key' => $config['storage']['key'],
+                        'secret' => $config['storage']['secret'],
+                    ],
+                    'region' => $config['storage']['region'],
+                    'version' => 'latest',
+                ]);
+
+                return new Filesystem(new AwsS3Adapter($client, $config['storage']['bucket'], $config['storage']['path']));
+            case 'dropbox':
+                $client = new DropboxClient($config['storage']['token']);
+                return new Filesystem(new DropboxAdapter($client), ['case_sensitive' => false]);
+            case 'ftp':
+                return new Filesystem(new FtpAdapter([
+                    'host' => $config['storage']['host'],
+                    'username' => $config['storage']['username'],
+                    'password' => $config['storage']['password'],
+                    'port' => $config['storage']['port'],
+                    'root' => $config['storage']['path'],
+                    'passive' => $config['storage']['passive'],
+                    'ssl' => $config['storage']['ssl'],
+                    'timeout' => 30,
+                ]));
+            case 'google-cloud':
+                $client = new StorageClient([
+                    'projectId' => $config['storage']['project_id'],
+                    'keyFilePath' => $config['storage']['key_path'],
+                ]);
+                return new Filesystem(new GoogleStorageAdapter($client, $client->bucket($config['storage']['bucket'])));
+            default:
+                throw new InvalidArgumentException('The driver specified is not supported.');
+        }
+    }),
+
+    View::class => factory(function ($container) {
+        return ViewFactory::createInstallerInstance($container);
+    }),
+]);
+
+$app = Bridge::create($builder->build());
+$app->addRoutingMiddleware();
+$app->setBasePath('/install');
+
+$app->get('/', function (Response $response, View $view, Session $session) {
+
+    if (!extension_loaded('gd')) {
+        $session->alert('The required "gd" extension is not loaded.', 'danger');
+    }
+
+    if (!extension_loaded('intl')) {
+        $session->alert('The required "intl" extension is not loaded.', 'danger');
+    }
+
+    if (!extension_loaded('json')) {
+        $session->alert('The required "json" extension is not loaded.', 'danger');
+    }
+
+    if (!extension_loaded('fileinfo')) {
+        $session->alert('The required "fileinfo" extension is not loaded.', 'danger');
+    }
+
+    if (!is_writable(__DIR__.'/../resources/cache')) {
+        $session->alert('The cache folder is not writable ('.__DIR__.'/../resources/cache'.')', 'danger');
+    }
+
+    if (!is_writable(__DIR__.'/../resources/database')) {
+        $session->alert('The database folder is not writable ('.__DIR__.'/../resources/database'.')', 'danger');
+    }
+
+    if (!is_writable(__DIR__.'/../resources/sessions')) {
+        $session->alert('The sessions folder is not writable ('.__DIR__.'/../resources/sessions'.')', 'danger');
+    }
+
+    $installed = file_exists(__DIR__.'/../config.php');
+
+    return $view->render($response, 'install.twig', [
+        'installed' => $installed,
+    ]);
 });
 });
 
 
-$app->post('/', function (Request $request, Response $response) use (&$config) {
-
-	// Check if there is a previous installation, if not, setup the config file
-	$installed = true;
-	if (!file_exists(__DIR__ . '/../config.php')) {
-		$installed = false;
-
-		// config file setup
-		$config['base_url'] = $request->getParam('base_url');
-		$config['storage']['driver'] = $request->getParam('storage_driver');
-		unset($config['displayErrorDetails']);
-		$config['db']['connection'] = $request->getParam('connection');
-		$config['db']['dsn'] = $request->getParam('dsn');
-		$config['db']['username'] = $request->getParam('db_user');
-		$config['db']['password'] = $request->getParam('db_password');
-
-
-		// setup storage configuration
-		switch ($config['storage']['driver']) {
-			case 's3':
-				$config['storage']['key'] = $request->getParam('storage_key');
-				$config['storage']['secret'] = $request->getParam('storage_secret');
-				$config['storage']['region'] = $request->getParam('storage_region');
-				$config['storage']['bucket'] = $request->getParam('storage_bucket');
-				$config['storage']['path'] = $request->getParam('storage_path');
-				break;
-			case 'dropbox':
-				$config['storage']['token'] = $request->getParam('storage_token');
-				break;
-			case 'ftp':
-				$config['storage']['host'] = $request->getParam('storage_host');
-				$config['storage']['username'] = $request->getParam('storage_username');
-				$config['storage']['password'] = $request->getParam('storage_password');
-				$config['storage']['port'] = $request->getParam('storage_port');
-				$config['storage']['path'] = $request->getParam('storage_path');
-				$config['storage']['passive'] = $request->getParam('storage_passive') === '1';
-				$config['storage']['ssl'] = $request->getParam('storage_ssl') === '1';
-				break;
-			case 'google-cloud':
-				$config['storage']['project_id'] = $request->getParam('storage_project_id');
-				$config['storage']['key_path'] = $request->getParam('storage_key_path');
-				$config['storage']['bucket'] = $request->getParam('storage_bucket');
-				break;
-			case 'local':
-			default:
-				$config['storage']['path'] = $request->getParam('storage_path');
-				break;
-		}
-
-		// check if the storage is valid
-		$storageTestFile = 'storage_test.xbackbone.txt';
-		try {
-			try {
-				$success = $this->storage->write($storageTestFile, 'XBACKBONE_TEST_FILE');
-			} catch (FileExistsException $fileExistsException) {
-				$success = $this->storage->update($storageTestFile, 'XBACKBONE_TEST_FILE');
-			}
-
-			if (!$success) {
-				throw new Exception('The storage is not writable.');
-			}
-			$this->storage->readAndDelete($storageTestFile);
-		} catch (Exception $e) {
-			$this->session->alert("Storage setup error: {$e->getMessage()} [{$e->getCode()}]", 'danger');
-			return redirect($response, '/install');
-		}
-
-		$ret = file_put_contents(__DIR__ . '/../config.php', '<?php' . PHP_EOL . 'return ' . var_export($config, true) . ';');
-		if ($ret === false) {
-			$this->session->alert('The config folder is not writable (' . __DIR__ . '/../config.php' . ')', 'danger');
-			return redirect($response, '/install');
-		}
-	}
-
-	// if from older installations with no support of other than local driver
-	// update the config
-	if ($installed && isset($config['storage_dir'])) {
-		$config['storage']['driver'] = 'local';
-		$config['storage']['path'] = $config['storage_dir'];
-		unset($config['storage_dir']);
-	}
-
-
-	// Build the dns string and run the migrations
-	try {
-
-		$dsn = $config['db']['connection'] === 'sqlite' ? __DIR__ . '/../' . $config['db']['dsn'] : $config['db']['dsn'];
-		DB::setDsn($config['db']['connection'] . ':' . $dsn, $config['db']['username'], $config['db']['password']);
-
-		migrate($config);
-	} catch (PDOException $e) {
-		$this->session->alert("Cannot connect to the database: {$e->getMessage()} [{$e->getCode()}]", 'danger');
-		return redirect($response, '/install');
-	}
-
-	// if not installed, create the default admin account
-	if (!$installed) {
-		DB::doQuery("INSERT INTO `users` (`email`, `username`, `password`, `is_admin`, `user_code`) VALUES (?, 'admin', ?, 1, ?)", [$request->getParam('email'), password_hash($request->getParam('password'), PASSWORD_DEFAULT), humanRandomString(5)]);
-	}
-
-	// post install cleanup
-	cleanDirectory(__DIR__ . '/../resources/cache');
-	cleanDirectory(__DIR__ . '/../resources/sessions');
-
-	removeDirectory(__DIR__ . '/../install');
-
-	// if is upgrading and existing installation, put it out maintenance
-	if ($installed) {
-		unset($config['maintenance']);
-
-		$ret = file_put_contents(__DIR__ . '/../config.php', '<?php' . PHP_EOL . 'return ' . var_export($config, true) . ';');
-		if ($ret === false) {
-			$this->session->alert('The config folder is not writable (' . __DIR__ . '/../config.php' . ')', 'danger');
-			return redirect($response, '/install');
-		}
-	}
-
-	// Installed successfully, destroy the installer session
-	session_destroy();
-	return $response->withRedirect("{$config['base_url']}/?afterInstall=true");
+$app->post('/', function (Request $request, Response $response, Filesystem $storage, Session $session) use (&$config) {
+
+    // Check if there is a previous installation, if not, setup the config file
+    $installed = true;
+    if (!file_exists(__DIR__.'/../config.php')) {
+        $installed = false;
+
+        // config file setup
+        $config['base_url'] = param($request, 'base_url');
+        $config['storage']['driver'] = param($request, 'storage_driver');
+        unset($config['debug']);
+        $config['db']['connection'] = param($request, 'connection');
+        $config['db']['dsn'] = param($request, 'dsn');
+        $config['db']['username'] = param($request, 'db_user');
+        $config['db']['password'] = param($request, 'db_password');
+
+
+        // setup storage configuration
+        switch ($config['storage']['driver']) {
+            case 's3':
+                $config['storage']['key'] = param($request, 'storage_key');
+                $config['storage']['secret'] = param($request, 'storage_secret');
+                $config['storage']['region'] = param($request, 'storage_region');
+                $config['storage']['bucket'] = param($request, 'storage_bucket');
+                $config['storage']['path'] = param($request, 'storage_path');
+                break;
+            case 'dropbox':
+                $config['storage']['token'] = param($request, 'storage_token');
+                break;
+            case 'ftp':
+                $config['storage']['host'] = param($request, 'storage_host');
+                $config['storage']['username'] = param($request, 'storage_username');
+                $config['storage']['password'] = param($request, 'storage_password');
+                $config['storage']['port'] = param($request, 'storage_port');
+                $config['storage']['path'] = param($request, 'storage_path');
+                $config['storage']['passive'] = param($request, 'storage_passive') === '1';
+                $config['storage']['ssl'] = param($request, 'storage_ssl') === '1';
+                break;
+            case 'google-cloud':
+                $config['storage']['project_id'] = param($request, 'storage_project_id');
+                $config['storage']['key_path'] = param($request, 'storage_key_path');
+                $config['storage']['bucket'] = param($request, 'storage_bucket');
+                break;
+            case 'local':
+            default:
+                $config['storage']['path'] = param($request, 'storage_path');
+                break;
+        }
+
+        // check if the storage is valid
+        $storageTestFile = 'storage_test.xbackbone.txt';
+        try {
+            try {
+                $success = $storage->write($storageTestFile, 'XBACKBONE_TEST_FILE');
+            } catch (FileExistsException $fileExistsException) {
+                $success = $storage->update($storageTestFile, 'XBACKBONE_TEST_FILE');
+            }
+
+            if (!$success) {
+                throw new Exception('The storage is not writable.');
+            }
+            $storage->readAndDelete($storageTestFile);
+        } catch (Exception $e) {
+            $session->alert("Storage setup error: {$e->getMessage()} [{$e->getCode()}]", 'danger');
+            return redirect($response, '/install');
+        }
+
+        $ret = file_put_contents(__DIR__.'/../config.php', '<?php'.PHP_EOL.'return '.var_export($config, true).';');
+        if ($ret === false) {
+            $session->alert('The config folder is not writable ('.__DIR__.'/../config.php'.')', 'danger');
+            return redirect($response, '/install');
+        }
+    }
+
+    // if from older installations with no support of other than local driver
+    // update the config
+    if ($installed && isset($config['storage_dir'])) {
+        $config['storage']['driver'] = 'local';
+        $config['storage']['path'] = $config['storage_dir'];
+        unset($config['storage_dir']);
+    }
+
+
+    // Build the dns string and run the migrations
+    try {
+
+        $firstMigrate = false;
+        if ($config['db']['connection'] === 'sqlite' && !file_exists(__DIR__.'/../'.$config['db']['dsn'])) {
+            touch(__DIR__.'/../'.$config['db']['dsn']);
+            $firstMigrate = true;
+        }
+
+        $dsn = $config['db']['connection'] === 'sqlite' ? __DIR__.'/../'.$config['db']['dsn'] : $config['db']['dsn'];
+        $db = new DB($config['db']['connection'].':'.$dsn, $config['db']['username'], $config['db']['password']);
+
+        $migrator = new Migrator($db, __DIR__.'/../resources/schemas', $firstMigrate);
+        $migrator->migrate();
+    } catch (PDOException $e) {
+        $session->alert("Cannot connect to the database: {$e->getMessage()} [{$e->getCode()}]", 'danger');
+        return redirect($response, '/install');
+    }
+
+    // if not installed, create the default admin account
+    if (!$installed) {
+        $db->query("INSERT INTO `users` (`email`, `username`, `password`, `is_admin`, `user_code`) VALUES (?, 'admin', ?, 1, ?)", [param($request, 'email'), password_hash(param($request, 'password'), PASSWORD_DEFAULT), humanRandomString(5)]);
+    }
+
+    // post install cleanup
+    cleanDirectory(__DIR__.'/../resources/cache');
+    cleanDirectory(__DIR__.'/../resources/sessions');
+
+    removeDirectory(__DIR__.'/../install');
+
+    // if is upgrading and existing installation, put it out maintenance
+    if ($installed) {
+        unset($config['maintenance']);
+
+        $ret = file_put_contents(__DIR__.'/../config.php', '<?php'.PHP_EOL.'return '.var_export($config, true).';');
+        if ($ret === false) {
+            $session->alert('The config folder is not writable ('.__DIR__.'/../config.php'.')', 'danger');
+            return redirect($response, '/install');
+        }
+    }
+
+    // Installed successfully, destroy the installer session
+    session_destroy();
+    return redirect($response, "{$config['base_url']}/?afterInstall=true");
 });
 });
 
 
 $app->run();
 $app->run();

+ 1 - 4
resources/lang/en.lang.php

@@ -96,10 +96,7 @@ return [
     'maintenance_in_progress' => 'Platform under maintenance, try again later...',
     'maintenance_in_progress' => 'Platform under maintenance, try again later...',
     'cancel' => 'Cancel',
     'cancel' => 'Cancel',
     'auto_set' => 'Set automatically',
     'auto_set' => 'Set automatically',
-    'translated_strings' => 'translated strings',
-    'total_strings' => 'total strings',
-    'lang_name' => 'language name',
-    'default_lang_behavior' => 'XBackBone will try to match the browser language by default (fallback is English).',
+    'default_lang_behavior' => 'XBackBone will try to match the browser language by default (the fallback is English).',
     'lang_set' => 'System language enforced to "%s"',
     'lang_set' => 'System language enforced to "%s"',
     'prerelease_channel' => 'Prerelease Channel',
     'prerelease_channel' => 'Prerelease Channel',
 ];
 ];

+ 1 - 4
resources/lang/it.lang.php

@@ -96,10 +96,7 @@ return [
     'cancel' => 'Annulla',
     'cancel' => 'Annulla',
     'enforce_language' => 'Imponi lingua',
     'enforce_language' => 'Imponi lingua',
     'auto_set' => 'Imposta automaticamente',
     'auto_set' => 'Imposta automaticamente',
-    'translated_strings' => 'stringhe tradotte',
-    'total_strings' => 'stringhe totali',
-    'lang_name' => 'nome della lingua',
-    'default_lang_behavior' => 'Per impostazione predefinita, XBackbone cercherà di abbinare la lingua del browser (il fallback è l\'inglese).',
+    'default_lang_behavior' => 'Per impostazione predefinita, XBackbone cercherà di abbinare la lingua del browser (il fallback è l\'Inglese).',
     'lang_set' => 'Lingua di sistema applicata a "%s"',
     'lang_set' => 'Lingua di sistema applicata a "%s"',
     'prerelease_channel' => 'Canale prerelease',
     'prerelease_channel' => 'Canale prerelease',
 ];
 ];

+ 1 - 1
resources/templates/dashboard/system.twig

@@ -87,7 +87,7 @@
                                             <option value="{{ lang }}">{{ name }}</option>
                                             <option value="{{ lang }}">{{ name }}</option>
                                         {% endfor %}
                                         {% endfor %}
                                     </select>
                                     </select>
-                                    <small>[{{ lang('translated_strings') }} / {{ lang('total_strings') }}] {{ lang('lang_name') }}. {{ lang('default_lang_behavior') }}</small>
+                                    <small>{{ lang('default_lang_behavior') }}</small>
                                 </div>
                                 </div>
                             </div>
                             </div>
                             <div class="form-group row">
                             <div class="form-group row">