Fixed installer and app base paths

Fixes #93
This commit is contained in:
Sergio Brighenti 2019-11-17 19:48:37 +01:00
parent 3250fa7093
commit eccd5d5070
14 changed files with 91 additions and 80 deletions

View file

@ -47,11 +47,11 @@ XBackBone require PHP >= `7.1`, with installed the required extensions:
cp config.example.php config.php && nano config.php
```
By default, XBackBone will use Sqlite3 as DB engine, and a `storage` dir in the main directory. You can leave these settings unchanged for a simple personal installation.
You must set the `base_url`, or remove it for get dynamically the url from request (not recommended).
You must set the `base_path`, or remove it for get dynamically the url from request (not recommended).
```php
return [
'base_url' => 'https://example.com', // no trailing slash
'base_path' => '/',
'storage' => [
'driver' => 'local',
'path' => 'storage',

View file

@ -30,8 +30,7 @@ class ViewFactory
$twig->addGlobal('config', $config);
$twig->addGlobal('request', $request);
$twig->addGlobal('alerts', $container->get('session')->getAlert());
$twig->addGlobal('session', $container->get('session')->all());
$twig->addGlobal('session', $container->get('session'));
$twig->addGlobal('current_lang', $container->get('lang')->getLang());
$twig->addGlobal('maxUploadSize', stringToBytes(ini_get('post_max_size')));
$twig->addGlobal('PLATFORM_VERSION', PLATFORM_VERSION);

View file

@ -84,6 +84,10 @@ class Lang
foreach (glob(self::$langPath.'*.lang.php') as $file) {
$dict = include $file;
if (!is_array($dict)) {
continue;
}
$count = count($dict) - 1;
$percent = min(round(($count / $default) * 100), 100);

View file

@ -185,10 +185,10 @@ if (!function_exists('urlFor')) {
* @param string $append
* @return string
*/
function urlFor(string $path, string $append = ''): string
function urlFor(string $path = '', string $append = ''): string
{
$baseUrl = resolve('config')['base_url'];
return $baseUrl.$path.$append;
global $app;
return $app->getBasePath().$path.$append;
}
}

View file

@ -34,9 +34,9 @@ if (isset($argv[1]) && $argv[1] === '--install') {
$db->query("INSERT INTO `users` (`email`, `username`, `password`, `is_admin`, `user_code`) VALUES ('admin@example.com', 'admin', ?, 1, ?)", [password_hash('admin', PASSWORD_DEFAULT), humanRandomString(5)]);
}
//if (file_exists(__DIR__.'/../install')) {
// removeDirectory(__DIR__.'/../install');
//}
if (file_exists(__DIR__.'/../install')) {
removeDirectory(__DIR__.'/../install');
}
echo 'If you are upgrading from a previous version, please run a "php bin\clean".'.PHP_EOL;
echo 'Done.'.PHP_EOL;

View file

@ -41,7 +41,7 @@ if (!file_exists('config.php') && is_dir('install/')) {
// Load the config
$config = array_replace_recursive([
'app_name' => 'XBackBone',
'base_url' => isset($_SERVER['HTTPS']) ? 'https://'.$_SERVER['HTTP_HOST'] : 'http://'.$_SERVER['HTTP_HOST'],
'base_path' => $_SERVER['REQUEST_URI'],
'debug' => false,
'maintenance' => false,
'db' => [
@ -145,6 +145,7 @@ $builder->addDefinitions([
]);
$app = Bridge::create($builder->build());
$app->setBasePath(substr($config['base_path'], 0, -1));
if (!$config['debug']) {
$app->getRouteCollector()->setCacheFile(BASE_DIR.'resources/cache/routes.cache.php');
@ -154,11 +155,11 @@ $app->add(InjectMiddleware::class);
$app->add(RememberMiddleware::class);
// Permanently redirect paths with a trailing slash to their non-trailing counterpart
$app->add(function (Request $request, RequestHandler $handler) {
$app->add(function (Request $request, RequestHandler $handler) use (&$config) {
$uri = $request->getUri();
$path = $uri->getPath();
if ($path !== '/' && substr($path, -1) === '/') {
if ($path !== $config['base_path'] && substr($path, -1) === '/') {
// permanently redirect paths with a trailing slash
// to their non-trailing counterpart
$uri = $uri->withPath(substr($path, 0, -1));

35
composer.lock generated
View file

@ -1,23 +1,23 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "a5d4341b89b81518c0e488cd3bc47127",
"packages": [
{
"name": "aws/aws-sdk-php",
"version": "3.117.1",
"version": "3.117.2",
"source": {
"type": "git",
"url": "https://github.com/aws/aws-sdk-php.git",
"reference": "6951cd08326d939f1fd1097a490fccb9751a59d7"
"reference": "3dc81df70f1cdf2842c85915548bffb870c1e1da"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/6951cd08326d939f1fd1097a490fccb9751a59d7",
"reference": "6951cd08326d939f1fd1097a490fccb9751a59d7",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/3dc81df70f1cdf2842c85915548bffb870c1e1da",
"reference": "3dc81df70f1cdf2842c85915548bffb870c1e1da",
"shasum": ""
},
"require": {
@ -87,7 +87,7 @@
"s3",
"sdk"
],
"time": "2019-11-14T19:27:06+00:00"
"time": "2019-11-15T19:21:02+00:00"
},
{
"name": "firebase/php-jwt",
@ -2901,34 +2901,35 @@
},
{
"name": "ocramius/package-versions",
"version": "1.4.1",
"version": "1.5.1",
"source": {
"type": "git",
"url": "https://github.com/Ocramius/PackageVersions.git",
"reference": "7ca61c24dc301cc9d47d6fb459b3d39f2d54b7e5"
"reference": "1d32342b8c1eb27353c8887c366147b4c2da673c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Ocramius/PackageVersions/zipball/7ca61c24dc301cc9d47d6fb459b3d39f2d54b7e5",
"reference": "7ca61c24dc301cc9d47d6fb459b3d39f2d54b7e5",
"url": "https://api.github.com/repos/Ocramius/PackageVersions/zipball/1d32342b8c1eb27353c8887c366147b4c2da673c",
"reference": "1d32342b8c1eb27353c8887c366147b4c2da673c",
"shasum": ""
},
"require": {
"composer-plugin-api": "^1.0.0",
"php": "^7.1.0"
"php": "^7.3.0"
},
"require-dev": {
"composer/composer": "^1.6.3",
"doctrine/coding-standard": "^5.0.1",
"composer/composer": "^1.8.6",
"doctrine/coding-standard": "^6.0.0",
"ext-zip": "*",
"infection/infection": "^0.7.1",
"phpunit/phpunit": "^7.5.17"
"infection/infection": "^0.13.4",
"phpunit/phpunit": "^8.2.5",
"vimeo/psalm": "^3.4.9"
},
"type": "composer-plugin",
"extra": {
"class": "PackageVersions\\Installer",
"branch-alias": {
"dev-master": "2.0.x-dev"
"dev-master": "1.6.x-dev"
}
},
"autoload": {
@ -2947,7 +2948,7 @@
}
],
"description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)",
"time": "2019-11-14T14:22:47+00:00"
"time": "2019-07-17T15:49:50+00:00"
},
{
"name": "phpstan/phpdoc-parser",

View file

@ -1,6 +1,6 @@
<?php
return [
'base_url' => 'http://localhost',
'base_path' => '/',
'db' => [
'connection' => 'sqlite',
'dsn' => 'resources/database/xbackbone.db',

View file

@ -31,7 +31,7 @@ define('BASE_DIR', realpath(__DIR__.'/../').DIRECTORY_SEPARATOR);
// default config
$config = [
'base_url' => str_replace('/install/', '', (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http')."://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]"),
'base_path' => $_SERVER['REQUEST_URI'],
'debug' => true,
'db' => [
'connection' => 'sqlite',
@ -106,10 +106,10 @@ $builder->addDefinitions([
]);
$app = Bridge::create($builder->build());
$app->setBasePath($_SERVER['REQUEST_URI']);
$app->addRoutingMiddleware();
$app->setBasePath('/install');
$app->get('/', function (Response $response, View $view, Session $session) {
$app->get('', function (Response $response, View $view, Session $session) use (&$config) {
if (!extension_loaded('gd')) {
$session->alert('The required "gd" extension is not loaded.', 'danger');
@ -143,10 +143,11 @@ $app->get('/', function (Response $response, View $view, Session $session) {
return $view->render($response, 'install.twig', [
'installed' => $installed,
'app_path' => str_replace('install/', '', $config['base_path']),
]);
});
})->setName('install');
$app->post('/', function (Request $request, Response $response, Filesystem $storage, Session $session) use (&$config) {
$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;
@ -154,7 +155,7 @@ $app->post('/', function (Request $request, Response $response, Filesystem $stor
$installed = false;
// config file setup
$config['base_url'] = param($request, 'base_url');
$config['base_path'] = param($request, 'base_path');
$config['storage']['driver'] = param($request, 'storage_driver');
unset($config['debug']);
$config['db']['connection'] = param($request, 'connection');
@ -176,6 +177,10 @@ $app->post('/', function (Request $request, Response $response, Filesystem $stor
$config['storage']['token'] = param($request, 'storage_token');
break;
case 'ftp':
if (!extension_loaded('ftp')) {
$session->alert('The "ftp" extension is not loaded.', 'danger');
return redirect($response, urlFor());
}
$config['storage']['host'] = param($request, 'storage_host');
$config['storage']['username'] = param($request, 'storage_username');
$config['storage']['password'] = param($request, 'storage_password');
@ -194,30 +199,24 @@ $app->post('/', function (Request $request, Response $response, Filesystem $stor
$config['storage']['path'] = param($request, 'storage_path');
break;
}
}
// check if the storage is valid
$storageTestFile = 'storage_test.xbackbone.txt';
// check if the storage is valid
$storageTestFile = 'storage_test.xbackbone.txt';
try {
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');
$success = $storage->write($storageTestFile, 'XBACKBONE_TEST_FILE');
} catch (FileExistsException $fileExistsException) {
$success = $storage->update($storageTestFile, 'XBACKBONE_TEST_FILE');
}
$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 (!$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, urlFor());
}
// if from older installations with no support of other than local driver
@ -228,6 +227,13 @@ $app->post('/', function (Request $request, Response $response, Filesystem $stor
unset($config['storage_dir']);
}
// if from 2.x versions
// update the config
if ($installed && isset($config['base_url'])) {
$path = parse_url($config['base_url'], PHP_URL_PATH);
$config['base_path'] = $path.'/';
unset($config['base_url']);
}
// Build the dns string and run the migrations
try {
@ -245,7 +251,7 @@ $app->post('/', function (Request $request, Response $response, Filesystem $stor
$migrator->migrate();
} catch (PDOException $e) {
$session->alert("Cannot connect to the database: {$e->getMessage()} [{$e->getCode()}]", 'danger');
return redirect($response, '/install');
return redirect($response, urlFor());
}
// if not installed, create the default admin account
@ -262,17 +268,18 @@ $app->post('/', function (Request $request, Response $response, Filesystem $stor
// 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');
}
// Finally write the config
$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");
return redirect($response, "{$config['base_path']}?afterInstall=true");
});
$app->run();

View file

@ -27,10 +27,9 @@
<form method="post" action="">
{% if not installed %}
<div class="form-group row">
<label for="base_url" class="col-sm-3 col-form-label">Base URL</label>
<label for="base_path" class="col-sm-3 col-form-label">Base Path</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="base_url" name="base_url" value="{{ config.base_url }}" autocomplete="off" required>
<small>No trailing slash.</small>
<input type="text" class="form-control" id="base_path" name="base_path" value="{{ app_path }}" autocomplete="off" required>
</div>
</div>
<hr>
@ -205,7 +204,7 @@
</div>
</div>
</div>
<div class="modal fade" id="modalLoading" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal" id="modalLoading" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header">

View file

@ -21,7 +21,7 @@
<link href="{{ asset('/static/app/app.css') }}" rel="stylesheet">
<script>
window.AppConfig = {
'base_url': '{{ config.base_url }}',
'base_path': '{{ config.base_path }}',
'max_upload_size': {{ maxUploadSize }},
'lang': {'publish': '{{ lang('publish') }}', 'hide': '{{ lang('hide') }}', 'dropzone': '{{ lang('drop_to_upload') }}'}
}
@ -33,7 +33,7 @@
{% block content %}{% endblock %}
{% block footer %}
<div class="container-fluid footer" style="display: none; font-size: 0.8rem">
<div class="text-muted">Proudly powered by <a href="https://github.com/SergiX44/XBackBone">XBackBone{% if session.admin %} v{{ PLATFORM_VERSION }}{% endif %}</a> — <i class="fas fa-fw fa-balance-scale"></i> AGPL v3.0</div>
<div class="text-muted">Proudly powered by <a href="https://github.com/SergiX44/XBackBone">XBackBone{% if session.get('admin') %} v{{ PLATFORM_VERSION }}{% endif %}</a> — <i class="fas fa-fw fa-balance-scale"></i> AGPL v3.0</div>
</div>
{% endblock %}
<script src="{{ asset('/static/jquery/jquery.min.js') }}"></script>

View file

@ -1,4 +1,4 @@
{% for alert in alerts %}
{% for alert in session.getAlert() %}
{% for type, message in alert %}
<div class="alert alert-{{ type }} alert-dismissible fade show" role="alert">
{{ message }}

View file

@ -16,7 +16,7 @@
{{ lang('upload') }}
</a>
</li>
{% if session.admin %}
{% if session.get('admin') %}
<li class="nav-item">
<a href="{{ route('user.index') }}" class="nav-link {{ request.uri.path starts with '/user' ? 'active' }}"><i class="fas fa-fw fa-users"></i>
{{ lang('users') }}
@ -32,11 +32,11 @@
<ul class="navbar-nav ml-auto">
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="userDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="fas fa-fw fa-user"></i> {{ session.username }}
<i class="fas fa-fw fa-user"></i> {{ session.get('username') }}
</a>
<div class="dropdown-menu shadow-sm" aria-labelledby="userDropdown">
<a class="dropdown-item disabled" href="javascript:void(0)">{{ lang('used') }}: {{ session.used_space }}</a>
{% if session.admin %}
<a class="dropdown-item disabled" href="javascript:void(0)">{{ lang('used') }}: {{ session.get('used_space') }}</a>
{% if session.get('admin') %}
<a class="dropdown-item" href="{{ route('switchView') }}"><i class="fas fa-fw fa-sync"></i> {{ lang('switch_to') }}: {{ session.gallery_view is null or session.gallery_view ? lang('gallery') : lang('table') }}</a>
{% endif %}
<div class="dropdown-divider"></div>

View file

@ -51,7 +51,7 @@ var app = {
var isOutline = false;
if ($(this).data('published')) {
isOutline = $callerButton.hasClass('btn-outline-warning');
$.post(window.AppConfig.base_url + '/upload/' + id + '/unpublish', function () {
$.post(window.AppConfig.base_path + '/upload/' + id + '/unpublish', function () {
$callerButton
.data('published', false)
.tooltip('dispose')
@ -64,7 +64,7 @@ var app = {
});
} else {
isOutline = $callerButton.hasClass('btn-outline-info');
$.post(window.AppConfig.base_url + '/upload/' + id + '/publish', function () {
$.post(window.AppConfig.base_path + '/upload/' + id + '/publish', function () {
$callerButton
.data('published', true)
.tooltip('dispose')
@ -80,7 +80,7 @@ var app = {
mediaDelete: function () {
var id = $(this).data('id');
var $callerButton = $(this);
$.post(window.AppConfig.base_url + '/upload/' + id + '/delete', function () {
$.post(window.AppConfig.base_path + '/upload/' + id + '/delete', function () {
$callerButton.tooltip('dispose');
$('#media_' + id).fadeOut(200, function () {
$(this).remove();
@ -89,14 +89,14 @@ var app = {
},
refreshToken: function () {
var id = $(this).data('id');
$.post(window.AppConfig.base_url + '/user/' + id + '/refreshToken', function (data) {
$.post(window.AppConfig.base_path + '/user/' + id + '/refreshToken', function (data) {
$('#token').val(data);
});
},
loadThemes: function (e) {
e.preventDefault();
var $themes = $('#themes');
$.get(window.AppConfig.base_url + '/system/themes', function (data) {
$.get(window.AppConfig.base_path + '/system/themes', function (data) {
$themes.empty();
$.each(data, function (key, value) {
var opt = document.createElement('option');
@ -114,7 +114,7 @@ var app = {
checkForUpdates: function () {
$('#checkForUpdatesMessage').empty().text('...');
$('#doUpgradeButton').prop('disabled', true);
$.get(window.AppConfig.base_url + '/system/checkForUpdates?prerelease=' + $(this).data('prerelease'), function (data) {
$.get(window.AppConfig.base_path + '/system/checkForUpdates?prerelease=' + $(this).data('prerelease'), function (data) {
$('#checkForUpdatesMessage').empty().text(data.message);
if (data.upgrade) {
$('#doUpgradeButton').prop('disabled', false);