Added ability to set custom html
improved session samesite implementation for older php versions Fixes #82
This commit is contained in:
parent
56c58d50b3
commit
5adb29d700
17 changed files with 187 additions and 71 deletions
|
@ -3,7 +3,10 @@
|
|||
+ Added web upload.
|
||||
+ Raw URL now accept file extensions.
|
||||
+ Improved installer.
|
||||
+ Improved thumbnail generation.
|
||||
+ Replaced videojs player with Plyr.
|
||||
+ Implemented SameSite XSS protection.
|
||||
+ Added ability to add custom HTML in <head> tag.
|
||||
+ Small fixes and improvements.
|
||||
|
||||
## v.2.6.6
|
||||
|
|
|
@ -80,8 +80,8 @@ class AdminController extends Controller
|
|||
{
|
||||
$config = require BASE_DIR.'config.php';
|
||||
|
||||
if (param($request,'lang') !== 'auto') {
|
||||
$config['lang'] = param($request,'lang');
|
||||
if (param($request, 'lang') !== 'auto') {
|
||||
$config['lang'] = param($request, 'lang');
|
||||
} else {
|
||||
unset($config['lang']);
|
||||
}
|
||||
|
@ -92,4 +92,22 @@ class AdminController extends Controller
|
|||
|
||||
return redirect($response, route('system'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param Response $response
|
||||
* @return Response
|
||||
*/
|
||||
public function applyCustomHead(Request $request, Response $response): Response
|
||||
{
|
||||
if ($request->getAttribute('custom_head_key_present')) {
|
||||
$this->database->query('UPDATE `settings` SET `value`=? WHERE `key` = \'custom_head\'', param($request, 'custom_head'));
|
||||
} else {
|
||||
$this->database->query('INSERT INTO `settings`(`key`, `value`) VALUES (\'custom_head\', ?)', param($request, 'custom_head'));
|
||||
}
|
||||
|
||||
$this->session->alert(lang('custom_head_set'));
|
||||
|
||||
return redirect($response, route('system'));
|
||||
}
|
||||
}
|
|
@ -47,7 +47,7 @@ class Migrator
|
|||
$this->db->getPdo()->exec(file_get_contents($this->schemaPath.DIRECTORY_SEPARATOR.'migrations.sql'));
|
||||
}
|
||||
|
||||
$files = glob($this->schemaPath.DIRECTORY_SEPARATOR.$this->db->getCurrentDriver().'/*.sql');
|
||||
$files = glob($this->schemaPath.'/'.$this->db->getCurrentDriver().'/*.sql');
|
||||
|
||||
$names = array_map(function ($path) {
|
||||
return basename($path);
|
||||
|
|
|
@ -24,7 +24,7 @@ class AuthMiddleware extends Middleware
|
|||
}
|
||||
|
||||
if (!$this->database->query('SELECT `id`, `active` FROM `users` WHERE `id` = ? LIMIT 1', [$this->session->get('user_id')])->fetch()->active) {
|
||||
$this->session->alert('Your account is not active anymore.', 'danger');
|
||||
$this->session->alert(lang('account_disabled'), 'danger');
|
||||
$this->session->set('logged', false);
|
||||
return redirect(new Response(), route('login.show'));
|
||||
}
|
||||
|
|
26
app/Middleware/InjectMiddleware.php
Normal file
26
app/Middleware/InjectMiddleware.php
Normal file
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace App\Middleware;
|
||||
|
||||
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
|
||||
|
||||
class InjectMiddleware extends Middleware
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param RequestHandler $handler
|
||||
* @return Response
|
||||
*/
|
||||
public function __invoke(Request $request, RequestHandler $handler)
|
||||
{
|
||||
$head = $this->database->query('SELECT `value` FROM `settings` WHERE `key` = \'custom_head\'')->fetch();
|
||||
$this->view->getTwig()->addGlobal('customHead', $head->value ?? null);
|
||||
|
||||
return $handler->handle($request->withAttribute('custom_head_key_present', isset($head->value)));
|
||||
}
|
||||
}
|
|
@ -6,17 +6,17 @@ namespace App\Middleware;
|
|||
use App\Database\DB;
|
||||
use App\Web\Lang;
|
||||
use App\Web\Session;
|
||||
use App\Web\View;
|
||||
use DI\Container;
|
||||
use League\Flysystem\Filesystem;
|
||||
use Monolog\Logger;
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
|
||||
use Twig\Environment;
|
||||
|
||||
/**
|
||||
* @property Session|null session
|
||||
* @property Environment view
|
||||
* @property View view
|
||||
* @property DB|null database
|
||||
* @property Logger|null logger
|
||||
* @property Filesystem|null storage
|
||||
|
|
|
@ -21,6 +21,18 @@ class Session
|
|||
throw new Exception("The given path '{$path}' is not writable.");
|
||||
}
|
||||
|
||||
// Workaround for php <= 7.3
|
||||
if (PHP_VERSION_ID < 70300) {
|
||||
$params = session_get_cookie_params();
|
||||
session_set_cookie_params(
|
||||
$params['lifetime'],
|
||||
$params['path'].'; SameSite=Lax',
|
||||
$params['domain'],
|
||||
$params['secure'],
|
||||
$params['httponly']
|
||||
);
|
||||
}
|
||||
|
||||
$started = @session_start([
|
||||
'name' => $name,
|
||||
'save_path' => $path,
|
||||
|
@ -29,18 +41,6 @@ class Session
|
|||
'cookie_samesite' => 'Lax' // works only for php >= 7.3
|
||||
]);
|
||||
|
||||
// Workaround for php <= 7.3
|
||||
if (PHP_VERSION_ID < 70300) {
|
||||
$sessionParams = session_get_cookie_params();
|
||||
setcookie(
|
||||
$name,
|
||||
$this->getId(),
|
||||
$sessionParams['filetime'],
|
||||
$sessionParams['path'],
|
||||
$sessionParams['domain'].'; SameSite=Lax'
|
||||
);
|
||||
}
|
||||
|
||||
if (!$started) {
|
||||
throw new Exception("Cannot start the HTTP session. That the session path '{$path}' is writable and your PHP settings.");
|
||||
}
|
||||
|
|
|
@ -60,4 +60,12 @@ class View
|
|||
return $this->twig->render($view, $parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Environment
|
||||
*/
|
||||
public function getTwig(): Environment
|
||||
{
|
||||
return $this->twig;
|
||||
}
|
||||
|
||||
}
|
|
@ -27,6 +27,8 @@ $app->group('', function (RouteCollectorProxy $group) {
|
|||
|
||||
$group->post('/system/lang/apply', [AdminController::class, 'applyLang'])->setName('lang.apply');
|
||||
|
||||
$group->post('/system/customHead', [AdminController::class, 'applyCustomHead'])->setName('customHead.apply');
|
||||
|
||||
$group->post('/system/upgrade', [UpgradeController::class, 'upgrade'])->setName('system.upgrade');
|
||||
$group->get('/system/checkForUpdates', [UpgradeController::class, 'checkForUpdates'])->setName('system.checkForUpdates');
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ use App\Database\DB;
|
|||
use App\Exception\Handlers\AppErrorHandler;
|
||||
use App\Exception\Handlers\Renderers\HtmlErrorRenderer;
|
||||
use App\Factories\ViewFactory;
|
||||
use App\Middleware\InjectMiddleware;
|
||||
use App\Web\Lang;
|
||||
use App\Web\Session;
|
||||
use Aws\S3\S3Client;
|
||||
|
@ -172,8 +173,7 @@ $app->add(function (Request $request, RequestHandler $handler) {
|
|||
return $handler->handle($request);
|
||||
});
|
||||
|
||||
// Load the application routes
|
||||
require BASE_DIR.'app/routes.php';
|
||||
$app->add(InjectMiddleware::class);
|
||||
|
||||
// Configure the error handler
|
||||
$errorHandler = new AppErrorHandler($app->getCallableResolver(), $app->getResponseFactory());
|
||||
|
@ -183,4 +183,7 @@ $errorHandler->registerErrorRenderer('text/html', HtmlErrorRenderer::class);
|
|||
$errorMiddleware = $app->addErrorMiddleware($config['debug'], true, true);
|
||||
$errorMiddleware->setDefaultErrorHandler($errorHandler);
|
||||
|
||||
// Load the application routes
|
||||
require BASE_DIR.'app/routes.php';
|
||||
|
||||
return $app;
|
|
@ -102,4 +102,9 @@ return [
|
|||
'prerelease_channel' => 'Prerelease Channel',
|
||||
'no_upload_token' => 'You don\'t have a personal upload token. (Generate one and try again)',
|
||||
'drop_to_upload' => 'Click or drop your files here to upload.',
|
||||
'donation' => 'Donation',
|
||||
'donate_text' => 'If you like XBackBone, consider a donation to support development!',
|
||||
'custom_head_html' => 'Custom HTML Head content',
|
||||
'custom_head_html_hint' => 'This content will be added at the <head> tag on every page.',
|
||||
'custom_head_set' => 'Custom Head HTML applied successfully.',
|
||||
];
|
||||
|
|
5
resources/schemas/mysql/mysql.3.sql
Normal file
5
resources/schemas/mysql/mysql.3.sql
Normal file
|
@ -0,0 +1,5 @@
|
|||
|
||||
CREATE TABLE IF NOT EXISTS `settings` (
|
||||
`key` VARCHAR(32) PRIMARY KEY,
|
||||
`value` TEXT
|
||||
);
|
4
resources/schemas/sqlite/sqlite.3.sql
Normal file
4
resources/schemas/sqlite/sqlite.3.sql
Normal file
|
@ -0,0 +1,4 @@
|
|||
CREATE TABLE IF NOT EXISTS `settings` (
|
||||
`key` VARCHAR(32) PRIMARY KEY,
|
||||
`value` TEXT
|
||||
);
|
|
@ -27,6 +27,7 @@
|
|||
}
|
||||
</script>
|
||||
{% block head %}{% endblock %}
|
||||
{{ customHead|raw }}
|
||||
</head>
|
||||
<body class="bg-light">
|
||||
{% block content %}{% endblock %}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
<i class="fas fa-users fa-3x"></i>
|
||||
</div>
|
||||
<h6 class="text-uppercase">{{ lang('users') }}</h6>
|
||||
<h1 class="display-4">{{ usersCount }}</h1>
|
||||
<h1 class="display-4 system-tile">{{ usersCount }}</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -25,7 +25,7 @@
|
|||
<i class="fas fa-weight fa-3x"></i>
|
||||
</div>
|
||||
<h6 class="text-uppercase">{{ lang('size') }}</h6>
|
||||
<h1 class="display-4">{{ totalSize }}</h1>
|
||||
<h1 class="display-4 system-tile">{{ totalSize }}</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -36,7 +36,7 @@
|
|||
<i class="fas fa-upload fa-3x"></i>
|
||||
</div>
|
||||
<h6 class="text-uppercase">{{ lang('files') }}</h6>
|
||||
<h1 class="display-4">{{ mediasCount }}</h1>
|
||||
<h1 class="display-4 system-tile">{{ mediasCount }}</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -47,60 +47,66 @@
|
|||
<i class="fas fa-unlink fa-3x"></i>
|
||||
</div>
|
||||
<h6 class="text-uppercase">{{ lang('orphaned_files') }}</h6>
|
||||
<h1 class="display-4">{{ orphanFilesCount }}</h1>
|
||||
<h1 class="display-4 system-tile">{{ orphanFilesCount }}</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-8 mb-3">
|
||||
<div class="card shadow-sm mb-3">
|
||||
<div class="card-header"><i class="fas fa-paint-brush fa-fw"></i> {{ lang('theme') }}</div>
|
||||
<div class="card-body">
|
||||
<form method="post" action="{{ route('theme.apply') }}">
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-12">
|
||||
<select class="form-control" id="themes" name="css">
|
||||
<option id="theme-load" selected disabled hidden>{{ lang('click_to_load') }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-8 mt-3">
|
||||
<div class="row same-height-container">
|
||||
<div class="col-md-6">
|
||||
<div class="card shadow-sm same-height">
|
||||
<div class="card-header"><i class="fas fa-paint-brush fa-fw"></i> {{ lang('theme') }}</div>
|
||||
<div class="card-body">
|
||||
<form method="post" action="{{ route('theme.apply') }}">
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-12">
|
||||
<select class="form-control" id="themes" name="css">
|
||||
<option id="theme-load" selected disabled hidden>{{ lang('click_to_load') }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-12">
|
||||
<button type="submit" class="btn btn-outline-success float-right" id="themes-apply" disabled>
|
||||
<i class="fas fa-save fa-fw"></i> {{ lang('apply') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-12">
|
||||
<button type="submit" class="btn btn-outline-success" id="themes-apply" disabled>
|
||||
<i class="fas fa-save fa-fw"></i> {{ lang('apply') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card shadow-sm same-height">
|
||||
<div class="card-header"><i class="fas fa-language fa-fw"></i> {{ lang('enforce_language') }}</div>
|
||||
<div class="card-body">
|
||||
<form method="post" action="{{ route('lang.apply') }}">
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-12">
|
||||
<select class="form-control" id="lang" name="lang">
|
||||
<option value="auto" selected>({{ lang('auto_set') }})</option>
|
||||
{% for lang, name in installed_lang %}
|
||||
<option value="{{ lang }}">{{ name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<small>{{ lang('default_lang_behavior') }}</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-12">
|
||||
<button type="submit" class="btn btn-outline-success float-right" id="lang-apply">
|
||||
<i class="fas fa-save fa-fw"></i> {{ lang('apply') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card shadow-sm mb-3">
|
||||
<div class="card-header"><i class="fas fa-language fa-fw"></i> {{ lang('enforce_language') }}</div>
|
||||
<div class="card-body">
|
||||
<form method="post" action="{{ route('lang.apply') }}">
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-12">
|
||||
<select class="form-control" id="lang" name="lang">
|
||||
<option value="auto" selected>({{ lang('auto_set') }})</option>
|
||||
{% for lang, name in installed_lang %}
|
||||
<option value="{{ lang }}">{{ name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<small>{{ lang('default_lang_behavior') }}</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-12">
|
||||
<button type="submit" class="btn btn-outline-success" id="lang-apply">
|
||||
<i class="fas fa-save fa-fw"></i> {{ lang('apply') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card shadow-sm">
|
||||
<div class="card shadow-sm mt-3">
|
||||
<div class="card-header"><i class="fas fa-cloud-download-alt fa-fw"></i> {{ lang('updates') }} <span class="float-right">v{{ PLATFORM_VERSION }}</span></div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
|
@ -125,9 +131,21 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card shadow-sm mt-3">
|
||||
<div class="card-header"><i class="fas fa-code fa-fw"></i> {{ lang('custom_head_html') }}</div>
|
||||
<div class="card-body">
|
||||
<form method="post" action="{{ route('customHead.apply') }}">
|
||||
<textarea name="custom_head" class="form-control text-monospace">{{ customHead|raw }}</textarea>
|
||||
<small>{{ lang('custom_head_html_hint') }}</small>
|
||||
<button type="submit" class="btn btn-outline-success float-right mt-3">
|
||||
<i class="fas fa-save fa-fw"></i> {{ lang('apply') }}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<div class="card shadow-sm mb-3">
|
||||
<div class="col-md-4 mt-3">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header"><i class="fas fa-cog fa-fw"></i> {{ lang('system_info') }}</div>
|
||||
<div class="card-body">
|
||||
<strong>Max upload size:</strong>
|
||||
|
@ -137,12 +155,21 @@
|
|||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card shadow-sm mb-3">
|
||||
<div class="card shadow-sm mt-3">
|
||||
<div class="card-header"><i class="fas fa-tools fa-fw"></i> {{ lang('maintenance') }}</div>
|
||||
<div class="card-body">
|
||||
<a href="{{ route('system.deleteOrphanFiles') }}" class="btn btn-outline-dark btn-block"><i class="fas fa-broom fa-fw"></i> {{ lang('clean_orphaned_uploads') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card shadow-sm mt-3">
|
||||
<div class="card-header"><i class="fas fa-donate fa-fw"></i> {{ lang('donation') }}</div>
|
||||
<div class="card-body">
|
||||
<p>{{ lang('donate_text') }}</p>
|
||||
<div class="text-center">
|
||||
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=6RXF8ZGCZBL68&item_name=Support+the+XBackBone+Development¤cy_code=EUR&source=url" target="_blank" class="text-warning"><i class="fab fa-cc-paypal fa-3x"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -111,4 +111,8 @@ body {
|
|||
|
||||
.dropzone .dz-image {
|
||||
border-radius: .25rem !important;
|
||||
}
|
||||
|
||||
.system-tile {
|
||||
font-size: 2.5rem;
|
||||
}
|
|
@ -29,6 +29,16 @@ var app = {
|
|||
|
||||
$('.footer').fadeIn(600);
|
||||
|
||||
$('.same-height-container').each(function () {
|
||||
var highestBox = 0;
|
||||
$('.same-height', this).each(function () {
|
||||
if ($(this).height() > highestBox) {
|
||||
highestBox = $(this).height();
|
||||
}
|
||||
});
|
||||
$('.same-height', this).height(highestBox);
|
||||
});
|
||||
|
||||
console.log('Application is ready.');
|
||||
},
|
||||
modalDelete: function () {
|
||||
|
|
Loading…
Reference in a new issue