diff --git a/app/Controllers/Controller.php b/app/Controllers/Controller.php index 9f07e97..ef024d4 100644 --- a/app/Controllers/Controller.php +++ b/app/Controllers/Controller.php @@ -7,7 +7,6 @@ use League\Flysystem\Adapter\Local; use League\Flysystem\FileNotFoundException; use League\Flysystem\Filesystem; use Slim\Container; -use Slim\Http\Request; use Slim\Http\Response; abstract class Controller @@ -34,20 +33,6 @@ abstract class Controller return null; } - - /** - * Generate a human readable file size - * @param $size - * @param int $precision - * @return string - */ - protected function humanFilesize($size, $precision = 2): string - { - for ($i = 0; ($size / 1024) > 0.9; $i++, $size /= 1024) { - } - return round($size, $precision) . ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'][$i]; - } - /** * Get a filesystem instance * @return Filesystem @@ -57,19 +42,6 @@ abstract class Controller return new Filesystem(new Local($this->settings['storage_dir'])); } - /** - * @param $path - */ - protected function removeDirectory($path) - { - $files = glob($path . '/*'); - foreach ($files as $file) { - is_dir($file) ? $this->removeDirectory($file) : unlink($file); - } - rmdir($path); - return; - } - /** * @param $id * @return int @@ -90,4 +62,14 @@ abstract class Controller return $totalSize; } + + /** + * @param Response $response + * @param string $path + * @return Response + */ + function redirectTo(Response $response, string $path): Response + { + return $response->withRedirect($this->settings['base_url'] . $path); + } } \ No newline at end of file diff --git a/app/Controllers/DashboardController.php b/app/Controllers/DashboardController.php index d9a7cc4..b91631b 100644 --- a/app/Controllers/DashboardController.php +++ b/app/Controllers/DashboardController.php @@ -24,10 +24,10 @@ class DashboardController extends Controller if ($request->getParam('afterInstall') !== null && is_dir('install')) { Session::alert('Installation completed successfully!', 'success'); - $this->removeDirectory('install'); + removeDirectory('install'); } - return $response->withRedirect('/home'); + return $this->redirectTo($response,'/home'); } /** @@ -61,7 +61,7 @@ class DashboardController extends Controller } $media->mimetype = $mime; $media->extension = $extension; - $media->size = $this->humanFilesize($size); + $media->size = humanFileSize($size); } return $this->view->render( @@ -101,7 +101,7 @@ class DashboardController extends Controller 'usersCount' => $usersCount, 'mediasCount' => $mediasCount, 'orphanFilesCount' => $orphanFilesCount, - 'totalSize' => $this->humanFilesize($totalSize), + 'totalSize' => humanFileSize($totalSize), 'post_max_size' => ini_get('post_max_size'), 'upload_max_filesize' => ini_get('upload_max_filesize'), ]); @@ -129,6 +129,6 @@ class DashboardController extends Controller public function applyTheme(Request $request, Response $response): Response { file_put_contents('static/bootstrap/css/bootstrap.min.css', file_get_contents($request->getParam('css'))); - return $response->withRedirect('/system')->withAddedHeader('Cache-Control', 'no-cache, must-revalidate'); + return $this->redirectTo($response,'/system')->withAddedHeader('Cache-Control', 'no-cache, must-revalidate'); } } \ No newline at end of file diff --git a/app/Controllers/LoginController.php b/app/Controllers/LoginController.php index 2234bb6..ac4d4b8 100644 --- a/app/Controllers/LoginController.php +++ b/app/Controllers/LoginController.php @@ -19,7 +19,7 @@ class LoginController extends Controller public function show(Request $request, Response $response): Response { if (Session::get('logged', false)) { - return $response->withRedirect('/home'); + return $this->redirectTo($response, '/home'); } return $this->view->render($response, 'auth/login.twig'); } @@ -36,19 +36,19 @@ class LoginController extends Controller if (!$result || !password_verify($request->getParam('password'), $result->password)) { Session::alert('Wrong credentials', 'danger'); - return $response->withRedirect('/login'); + return $this->redirectTo($response, '/login'); } if (!$result->active) { Session::alert('Your account is disabled.', 'danger'); - return $response->withRedirect('/login'); + return $this->redirectTo($response, '/login'); } Session::set('logged', true); Session::set('user_id', $result->id); Session::set('username', $result->username); Session::set('admin', $result->is_admin); - Session::set('used_space', $this->humanFilesize($this->getUsedSpaceByUser($result->id))); + Session::set('used_space', humanFileSize($this->getUsedSpaceByUser($result->id))); Session::alert("Welcome, $result->username!", 'info'); $this->logger->info("User $result->username logged in."); @@ -57,7 +57,7 @@ class LoginController extends Controller return $response->withRedirect(Session::get('redirectTo')); } - return $response->withRedirect('/home'); + return $this->redirectTo($response,'/home'); } /** @@ -70,7 +70,7 @@ class LoginController extends Controller Session::clear(); Session::set('logged', false); Session::alert('Goodbye!', 'warning'); - return $response->withRedirect('/login'); + return $this->redirectTo($response,'/login'); } } \ No newline at end of file diff --git a/app/Controllers/UploadController.php b/app/Controllers/UploadController.php index f109fc7..bacc5fe 100644 --- a/app/Controllers/UploadController.php +++ b/app/Controllers/UploadController.php @@ -223,7 +223,7 @@ class UploadController extends Controller } finally { $this->database->query('DELETE FROM `uploads` WHERE `id` = ?', $args['id']); $this->logger->info('User ' . Session::get('username') . ' deleted a media.', [$args['id']]); - Session::set('used_space', $this->humanFilesize($this->getUsedSpaceByUser(Session::get('user_id')))); + Session::set('used_space', humanFileSize($this->getUsedSpaceByUser(Session::get('user_id')))); } } else { throw new UnauthorizedException(); diff --git a/app/Controllers/UserController.php b/app/Controllers/UserController.php index 13f1c48..c1a2e68 100644 --- a/app/Controllers/UserController.php +++ b/app/Controllers/UserController.php @@ -58,22 +58,22 @@ class UserController extends Controller { if ($request->getParam('email') === null) { Session::alert('The email is required.', 'danger'); - return $response->withRedirect('/user/create'); + return $this->redirectTo($response,'/user/create'); } if ($request->getParam('username') === null) { Session::alert('The username is required.', 'danger'); - return $response->withRedirect('/user/create'); + return $this->redirectTo($response,'/user/create'); } if ($request->getParam('password') === null) { Session::alert('The password is required.', 'danger'); - return $response->withRedirect('/user/create'); + return $this->redirectTo($response,'/user/create'); } if ($this->database->query('SELECT COUNT(*) AS `count` FROM `users` WHERE `username` = ?', $request->getParam('username'))->fetch()->count > 0) { Session::alert('The username already taken.', 'danger'); - return $response->withRedirect('/user/create'); + return $this->redirectTo($response,'/user/create'); } do { @@ -95,7 +95,7 @@ class UserController extends Controller Session::alert("User '{$request->getParam('username')}' created!", 'success'); $this->logger->info('User ' . Session::get('username') . ' created a new user.', [array_diff($request->getParams(), ['password'])]); - return $response->withRedirect('/users'); + return $this->redirectTo($response,'/users'); } /** @@ -135,22 +135,22 @@ class UserController extends Controller if ($request->getParam('email') === null) { Session::alert('The email is required.', 'danger'); - return $response->withRedirect('/user/' . $args['id'] . '/edit'); + return $this->redirectTo($response,'/user/' . $args['id'] . '/edit'); } if ($request->getParam('username') === null) { Session::alert('The username is required.', 'danger'); - return $response->withRedirect('/user/' . $args['id'] . '/edit'); + return $this->redirectTo($response,'/user/' . $args['id'] . '/edit'); } if ($this->database->query('SELECT COUNT(*) AS `count` FROM `users` WHERE `username` = ? AND `username` <> ?', [$request->getParam('username'), $user->username])->fetch()->count > 0) { Session::alert('The username already taken.', 'danger'); - return $response->withRedirect('/user/' . $args['id'] . '/edit'); + return $this->redirectTo($response,'/user/' . $args['id'] . '/edit'); } if ($user->id === Session::get('user_id') && $request->getParam('is_admin') === null) { Session::alert('You cannot demote yourself.', 'danger'); - return $response->withRedirect('/user/' . $args['id'] . '/edit'); + return $this->redirectTo($response,'/user/' . $args['id'] . '/edit'); } if ($request->getParam('password') !== null && !empty($request->getParam('password'))) { @@ -175,7 +175,7 @@ class UserController extends Controller Session::alert("User '{$request->getParam('username')}' updated!", 'success'); $this->logger->info('User ' . Session::get('username') . " updated $user->id.", [$user, array_diff($request->getParams(), ['password'])]); - return $response->withRedirect('/users'); + return $this->redirectTo($response,'/users'); } @@ -196,7 +196,7 @@ class UserController extends Controller if ($user->id === Session::get('user_id')) { Session::alert('You cannot delete yourself.', 'danger'); - return $response->withRedirect('/users'); + return $this->redirectTo($response,'/users'); } $this->database->query('DELETE FROM `users` WHERE `id` = ?', $user->id); @@ -204,7 +204,7 @@ class UserController extends Controller Session::alert('User deleted.', 'success'); $this->logger->info('User ' . Session::get('username') . " deleted $user->id."); - return $response->withRedirect('/users'); + return $this->redirectTo($response,'/users'); } /** @@ -253,7 +253,7 @@ class UserController extends Controller if ($request->getParam('email') === null) { Session::alert('The email is required.', 'danger'); - return $response->withRedirect('/profile'); + return $this->redirectTo($response,'/profile'); } if ($request->getParam('password') !== null && !empty($request->getParam('password'))) { @@ -272,7 +272,7 @@ class UserController extends Controller Session::alert('Profile updated successfully!', 'success'); $this->logger->info('User ' . Session::get('username') . " updated profile of $user->id."); - return $response->withRedirect('/profile'); + return $this->redirectTo($response,'/profile'); } /** diff --git a/app/Middleware/AuthMiddleware.php b/app/Middleware/AuthMiddleware.php index 5610958..315c0e8 100644 --- a/app/Middleware/AuthMiddleware.php +++ b/app/Middleware/AuthMiddleware.php @@ -28,14 +28,14 @@ class AuthMiddleware { if (!Session::get('logged', false)) { Session::set('redirectTo', (isset($_SERVER['HTTPS']) ? 'https' : 'http') . "://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]"); - return $response->withRedirect('/login'); + return $response->withRedirect($this->container->settings['base_url'] . '/login'); } if (!$this->container->database->query('SELECT `id`, `active` FROM `users` WHERE `id` = ? LIMIT 1', [Session::get('user_id')])->fetch()->active) { Session::alert('Your account is not active anymore.', 'danger'); Session::set('logged', false); Session::set('redirectTo', (isset($_SERVER['HTTPS']) ? 'https' : 'http') . "://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]"); - return $response->withRedirect('/login'); + return $response->withRedirect($this->container->settings['base_url'] . '/login'); } return $next($request, $response); diff --git a/app/helpers.php b/app/helpers.php new file mode 100644 index 0000000..d5c13b5 --- /dev/null +++ b/app/helpers.php @@ -0,0 +1,51 @@ + 0.9; $i++, $size /= 1024) { + } + return round($size, $precision) . ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'][$i]; + } +} + +if (!function_exists('removeDirectory')) { + /** + * Remove a directory and it's content + * @param $path + */ + function removeDirectory($path) + { + $files = glob($path . '/*'); + foreach ($files as $file) { + is_dir($file) ? removeDirectory($file) : unlink($file); + } + rmdir($path); + return; + } +} + +if (!function_exists('cleanDirectory')) { + /** + * Removes all directory contents + * @param $path + */ + function cleanDirectory($path) + { + $directoryIterator = new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS); + $iteratorIterator = new RecursiveIteratorIterator($directoryIterator, RecursiveIteratorIterator::CHILD_FIRST); + foreach ($iteratorIterator as $file) { + if ($file->getFilename() !== '.gitkeep') { + $file->isDir() ? rmdir($file) : unlink($file); + } + } + } +} \ No newline at end of file diff --git a/bin/clean b/bin/clean index 97ed9b8..5724a7a 100644 --- a/bin/clean +++ b/bin/clean @@ -7,29 +7,18 @@ if (php_sapi_name() !== 'cli') { die(); } -function cleanDir($path) -{ - $directoryIterator = new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS); - $iteratorIterator = new RecursiveIteratorIterator($directoryIterator, RecursiveIteratorIterator::CHILD_FIRST); - foreach ($iteratorIterator as $file) { - if ($file->getFilename() !== '.gitkeep') { - $file->isDir() ? rmdir($file) : unlink($file); - } - } -} - $action = isset($argv[1]) ? $argv[1] : 'all'; switch ($action) { case 'cache': - cleanDir(__DIR__ . '/../resources/cache'); + cleanDirectory(__DIR__ . '/../resources/cache'); break; case 'sessions': - cleanDir(__DIR__ . '/../resources/sessions'); + cleanDirectory(__DIR__ . '/../resources/sessions'); break; case 'all': - cleanDir(__DIR__ . '/../resources/cache'); - cleanDir(__DIR__ . '/../resources/sessions'); + cleanDirectory(__DIR__ . '/../resources/cache'); + cleanDirectory(__DIR__ . '/../resources/sessions'); break; case 'help': default: diff --git a/bootstrap/app.php b/bootstrap/app.php index 353acdc..d318644 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -9,7 +9,7 @@ use Slim\App; use Slim\Container; if (!file_exists('config.php') && is_dir('install/')) { - header('Location: /install/'); + header('Location: ./install/'); exit(); } else if (!file_exists('config.php') && !is_dir('install/')) { die('Cannot find the config file.'); @@ -23,11 +23,12 @@ $config = array_replace_recursive([ 'displayErrorDetails' => false, 'db' => [ 'connection' => 'sqlite', - 'dsn' => 'resources/database/xbackbone.db', + 'dsn' => __DIR__ . '/../resources/database/xbackbone.db', 'username' => null, 'password' => null, ], -], require 'config.php'); + 'routerCacheFile' => __DIR__ . '/../resources/cache/routes.cache.php', +], require __DIR__ . '/../config.php'); $container = new Container(['settings' => $config]); @@ -43,10 +44,11 @@ $container['logger'] = function ($container) { }; // Session init -Session::init('xbackbone_session', 'resources/sessions'); +Session::init('xbackbone_session', __DIR__ . '/../resources/sessions'); // Set the database dsn -DB::setDsn($config['db']['connection'] . ':' . $config['db']['dsn'], $config['db']['username'], $config['db']['password']); +$dsn = $config['db']['connection'] === 'sqlite' ? __DIR__ . '/../' . $config['db']['dsn'] : $config['db']['dsn']; +DB::setDsn($config['db']['connection'] . ':' . $dsn, $config['db']['username'], $config['db']['password']); $container['database'] = function ($container) use (&$config) { return DB::getInstance(); @@ -54,8 +56,8 @@ $container['database'] = function ($container) use (&$config) { $container['view'] = function ($container) use (&$config) { - $view = new \Slim\Views\Twig('resources/templates', [ - 'cache' => 'resources/cache', + $view = new \Slim\Views\Twig(__DIR__ . '/../resources/templates', [ + 'cache' => __DIR__ . '/../resources/cache', 'autoescape' => 'html', 'debug' => $config['displayErrorDetails'], 'auto_reload' => $config['displayErrorDetails'], @@ -95,5 +97,23 @@ $container['notFoundHandler'] = function ($container) { $app = new App($container); +// Permanently redirect paths with a trailing slash to their non-trailing counterpart +$app->add(function (\Slim\Http\Request $request, \Slim\Http\Response $response, callable $next) { + $uri = $request->getUri(); + $path = $uri->getPath(); + + if ($path !== '/' && substr($path, -1) === '/') { + $uri = $uri->withPath(substr($path, 0, -1)); + + if ($request->getMethod() === 'GET') { + return $response->withRedirect((string)$uri, 301); + } else { + return $next($request->withUri($uri), $response); + } + } + + return $next($request, $response); +}); + // Load the application routes require 'app/routes.php'; \ No newline at end of file diff --git a/composer.json b/composer.json index 0f1c379..b1997d5 100644 --- a/composer.json +++ b/composer.json @@ -14,6 +14,9 @@ "ext-pdo": "*" }, "autoload": { + "files": [ + "app/helpers.php" + ], "psr-4": { "App\\": "app/" } diff --git a/install/index.php b/install/index.php index 488899f..b6330eb 100644 --- a/install/index.php +++ b/install/index.php @@ -11,7 +11,6 @@ use Slim\Http\Response; define('PLATFORM_VERSION', json_decode(file_get_contents(__DIR__ . '/../composer.json'))->version); $config = [ - 'app_name' => 'XBackBone', 'base_url' => isset($_SERVER['HTTPS']) ? 'https://' . $_SERVER['HTTP_HOST'] : 'http://' . $_SERVER['HTTP_HOST'], 'storage_dir' => 'storage', 'displayErrorDetails' => true, @@ -28,7 +27,7 @@ $container = new Container(['settings' => $config]); Session::init('xbackbone_session'); $container['view'] = function ($container) use (&$config) { - $view = new \Slim\Views\Twig('templates', [ + $view = new \Slim\Views\Twig(__DIR__ . '/templates', [ 'cache' => false, 'autoescape' => 'html', 'debug' => $config['displayErrorDetails'], @@ -51,7 +50,7 @@ $container['view'] = function ($container) use (&$config) { function migrate($config) { $firstMigrate = false; - if (!file_exists(__DIR__ . '/../' . $config['db']['dsn']) && DB::driver() === 'sqlite') { + if ($config['db']['connection'] === 'sqlite' && !file_exists(__DIR__ . '/../' . $config['db']['dsn'])) { touch(__DIR__ . '/../' . $config['db']['dsn']); $firstMigrate = true; } @@ -110,17 +109,6 @@ function migrate($config) } } -function cleanDir($path) -{ - $directoryIterator = new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS); - $iteratorIterator = new RecursiveIteratorIterator($directoryIterator, RecursiveIteratorIterator::CHILD_FIRST); - foreach ($iteratorIterator as $file) { - if ($file->getFilename() !== '.gitkeep') { - $file->isDir() ? rmdir($file) : unlink($file); - } - } -} - $app = new App($container); $app->get('/', function (Request $request, Response $response) { @@ -131,7 +119,10 @@ $app->get('/', function (Request $request, Response $response) { }); $app->post('/', function (Request $request, Response $response) use (&$config) { + $installed = true; if (!file_exists(__DIR__ . '/../config.php')) { + $installed = false; + $config['base_url'] = $request->getParam('base_url'); $config['storage_dir'] = $request->getParam('storage_dir'); $config['displayErrorDetails'] = false; @@ -144,14 +135,18 @@ $app->post('/', function (Request $request, Response $response) use (&$config) { file_put_contents(__DIR__ . '/../config.php', 'getParam('email'), password_hash($request->getParam('password'), PASSWORD_DEFAULT), substr(md5(microtime()), rand(0, 26), 5)]); + if (!$installed) { + DB::query("INSERT INTO `users` (`email`, `username`, `password`, `is_admin`, `user_code`) VALUES (?, 'admin', ?, 1, ?)", [$request->getParam('email'), password_hash($request->getParam('password'), PASSWORD_DEFAULT), substr(md5(microtime()), rand(0, 26), 5)]); + } - cleanDir(__DIR__ . '/../resources/cache'); - cleanDir(__DIR__ . '/../resources/sessions'); + cleanDirectory(__DIR__ . '/../resources/cache'); + cleanDirectory(__DIR__ . '/../resources/sessions'); return $response->withRedirect('../?afterInstall=true'); }); diff --git a/install/templates/install.twig b/install/templates/install.twig index 54c1357..7ff21fc 100644 --- a/install/templates/install.twig +++ b/install/templates/install.twig @@ -6,16 +6,16 @@ - - - + + + - - - - - - + + + + + +