Moved some settings from admin to user side

Updated docs
This commit is contained in:
Sergio Brighenti 2020-04-03 15:59:49 +02:00
parent 50051b3691
commit 5a7ff39ce9
22 changed files with 335 additions and 109 deletions

View file

@ -51,7 +51,7 @@ class LoginController extends Controller
} }
$username = param($request, 'username'); $username = param($request, 'username');
$user = $this->database->query('SELECT `id`, `email`, `username`, `password`,`is_admin`, `active`, `current_disk_quota`, `max_disk_quota`, `ldap` FROM `users` WHERE `username` = ? OR `email` = ? LIMIT 1', [$username, $username])->fetch(); $user = $this->database->query('SELECT `id`, `email`, `username`, `password`,`is_admin`, `active`, `current_disk_quota`, `max_disk_quota`, `ldap`, `copy_raw` FROM `users` WHERE `username` = ? OR `email` = ? LIMIT 1', [$username, $username])->fetch();
if ($this->config['ldap']['enabled'] && ($user->ldap ?? true)) { if ($this->config['ldap']['enabled'] && ($user->ldap ?? true)) {
$user = $this->ldapLogin($request, $username, param($request, 'password'), $user); $user = $this->ldapLogin($request, $username, param($request, 'password'), $user);
@ -80,6 +80,7 @@ class LoginController extends Controller
$this->session->set('user_id', $user->id); $this->session->set('user_id', $user->id);
$this->session->set('username', $user->username); $this->session->set('username', $user->username);
$this->session->set('admin', $user->is_admin); $this->session->set('admin', $user->is_admin);
$this->session->set('copy_raw', $user->copy_raw);
$this->setSessionQuotaInfo($user->current_disk_quota, $user->max_disk_quota); $this->setSessionQuotaInfo($user->current_disk_quota, $user->max_disk_quota);
$this->session->alert(lang('welcome', [$user->username]), 'info'); $this->session->alert(lang('welcome', [$user->username]), 'info');

View file

@ -73,7 +73,7 @@ class DashboardController extends Controller
'next' => $page < floor($query->getPages()), 'next' => $page < floor($query->getPages()),
'previous' => $page >= 1, 'previous' => $page >= 1,
'current_page' => ++$page, 'current_page' => ++$page,
'copy_url_behavior' => $this->getSetting('copy_url_behavior', 'off'), 'copy_raw' => $this->session->get('copy_raw', false),
'tags' => $tags, 'tags' => $tags,
] ]
); );

View file

@ -25,6 +25,8 @@ class ExportController extends Controller
$medias = $this->database->query('SELECT `uploads`.`filename`, `uploads`.`storage_path` FROM `uploads` WHERE `user_id` = ?', $user->id); $medias = $this->database->query('SELECT `uploads`.`filename`, `uploads`.`storage_path` FROM `uploads` WHERE `user_id` = ?', $user->id);
$this->logger->info("User $user->id, $user->name, exporting data...");
set_time_limit(0); set_time_limit(0);
ob_end_clean(); ob_end_clean();

View file

@ -59,14 +59,18 @@ class ProfileController extends Controller
} }
if (param($request, 'password') !== null && !empty(param($request, 'password'))) { if (param($request, 'password') !== null && !empty(param($request, 'password'))) {
$this->database->query('UPDATE `users` SET `email`=?, `password`=? WHERE `id` = ?', [ $this->database->query('UPDATE `users` SET `email`=?, `password`=?, `hide_uploads`=?, `copy_raw`=? WHERE `id` = ?', [
param($request, 'email'), param($request, 'email'),
password_hash(param($request, 'password'), PASSWORD_DEFAULT), password_hash(param($request, 'password'), PASSWORD_DEFAULT),
param($request, 'hide_uploads') !== null ? 1 : 0,
param($request, 'copy_raw') !== null ? 1 : 0,
$user->id, $user->id,
]); ]);
} else { } else {
$this->database->query('UPDATE `users` SET `email`=? WHERE `id` = ?', [ $this->database->query('UPDATE `users` SET `email`=?, `hide_uploads`=?, `copy_raw`=? WHERE `id` = ?', [
param($request, 'email'), param($request, 'email'),
param($request, 'hide_uploads') !== null ? 1 : 0,
param($request, 'copy_raw') !== null ? 1 : 0,
$user->id, $user->id,
]); ]);
} }

View file

@ -27,25 +27,24 @@ class SettingController extends Controller
return redirect($response, route('system')); return redirect($response, route('system'));
} }
// registrations
$this->updateSetting('register_enabled', param($request, 'register_enabled', 'off')); $this->updateSetting('register_enabled', param($request, 'register_enabled', 'off'));
$this->updateSetting('hide_by_default', param($request, 'hide_by_default', 'off'));
$this->updateSetting('quota_enabled', param($request, 'quota_enabled', 'off'));
// quota
$this->updateSetting('quota_enabled', param($request, 'quota_enabled', 'off'));
$this->updateSetting('default_user_quota', stringToBytes(param($request, 'default_user_quota', '1G')));
$user = make(UserQuery::class)->get($request, $this->session->get('user_id')); $user = make(UserQuery::class)->get($request, $this->session->get('user_id'));
$this->setSessionQuotaInfo($user->current_disk_quota, $user->max_disk_quota); $this->setSessionQuotaInfo($user->current_disk_quota, $user->max_disk_quota);
$this->updateSetting('default_user_quota', stringToBytes(param($request, 'default_user_quota', '1G')));
$this->updateSetting('copy_url_behavior', param($request, 'copy_url_behavior') === null ? 'default' : 'raw');
$this->applyTheme($request);
$this->applyLang($request);
$this->updateSetting('custom_head', param($request, 'custom_head')); $this->updateSetting('custom_head', param($request, 'custom_head'));
$this->updateSetting('recaptcha_enabled', param($request, 'recaptcha_enabled', 'off')); $this->updateSetting('recaptcha_enabled', param($request, 'recaptcha_enabled', 'off'));
$this->updateSetting('recaptcha_site_key', param($request, 'recaptcha_site_key')); $this->updateSetting('recaptcha_site_key', param($request, 'recaptcha_site_key'));
$this->updateSetting('recaptcha_secret_key', param($request, 'recaptcha_secret_key')); $this->updateSetting('recaptcha_secret_key', param($request, 'recaptcha_secret_key'));
$this->applyTheme($request);
$this->applyLang($request);
$this->logger->info("User $user->username updated the system settings.");
$this->session->alert(lang('settings_saved')); $this->session->alert(lang('settings_saved'));
return redirect($response, route('system')); return redirect($response, route('system'));

View file

@ -30,6 +30,8 @@ class TagController extends Controller
[$id, $limit] = make(TagQuery::class)->addTag(param($request, 'tag'), param($request, 'mediaId')); [$id, $limit] = make(TagQuery::class)->addTag(param($request, 'tag'), param($request, 'mediaId'));
$this->logger->info("Tag added $id.");
return json($response, [ return json($response, [
'limitReached' => $limit, 'limitReached' => $limit,
'tagId' => $id, 'tagId' => $id,
@ -58,6 +60,8 @@ class TagController extends Controller
throw new HttpNotFoundException($request); throw new HttpNotFoundException($request);
} }
$this->logger->info("Tag removed ".param($request, 'tagId').', from media '.param($request, 'mediaId'));
return $response; return $response;
} }

View file

@ -51,6 +51,7 @@ class UpgradeController extends Controller
return redirect($response, route('system')); return redirect($response, route('system'));
} }
$this->logger->info('System update started.');
$config = require BASE_DIR.'config.php'; $config = require BASE_DIR.'config.php';
$config['maintenance'] = true; $config['maintenance'] = true;
@ -89,6 +90,8 @@ class UpgradeController extends Controller
$updateZip->close(); $updateZip->close();
unlink($tmpFile); unlink($tmpFile);
$this->logger->info('System update completed.');
return redirect($response, urlFor('/install')); return redirect($response, urlFor('/install'));
} }

View file

@ -30,7 +30,7 @@ class UploadController extends Controller
$maxFileSize = min(stringToBytes(ini_get('post_max_size')), stringToBytes(ini_get('upload_max_filesize'))); $maxFileSize = min(stringToBytes(ini_get('post_max_size')), stringToBytes(ini_get('upload_max_filesize')));
return view()->render($response, 'upload/web.twig', [ return view()->render($response, 'upload/web.twig', [
'max_file_size' => humanFileSize($maxFileSize) 'max_file_size' => humanFileSize($maxFileSize),
]); ]);
} }
@ -42,12 +42,6 @@ class UploadController extends Controller
*/ */
public function uploadWeb(Request $request, Response $response): Response public function uploadWeb(Request $request, Response $response): Response
{ {
if ($this->config['maintenance']) {
$this->json['message'] = 'Endpoint under maintenance.';
return json($response, $this->json, 503);
}
try { try {
$file = $this->validateFile($request, $response); $file = $this->validateFile($request, $response);
@ -92,9 +86,6 @@ class UploadController extends Controller
try { try {
$file = $this->validateFile($request, $response); $file = $this->validateFile($request, $response);
} catch (ValidationException $e) {
return $e->response();
}
if (param($request, 'token') === null) { if (param($request, 'token') === null) {
$this->json['message'] = 'Token not specified.'; $this->json['message'] = 'Token not specified.';
@ -104,13 +95,6 @@ class UploadController extends Controller
$user = $this->database->query('SELECT * FROM `users` WHERE `token` = ? LIMIT 1', param($request, 'token'))->fetch(); $user = $this->database->query('SELECT * FROM `users` WHERE `token` = ? LIMIT 1', param($request, 'token'))->fetch();
if (!$user) {
$this->json['message'] = 'Token specified not found.';
return json($response, $this->json, 404);
}
try {
$this->validateUser($request, $response, $file, $user); $this->validateUser($request, $response, $file, $user);
} catch (ValidationException $e) { } catch (ValidationException $e) {
return $e->response(); return $e->response();
@ -174,6 +158,12 @@ class UploadController extends Controller
*/ */
protected function validateUser(Request $request, Response $response, UploadedFileInterface $file, $user) protected function validateUser(Request $request, Response $response, UploadedFileInterface $file, $user)
{ {
if (!$user) {
$this->json['message'] = 'Token specified not found.';
throw new ValidationException(json($response, $this->json, 404));
}
if (!$user->active) { if (!$user->active) {
$this->json['message'] = 'Account disabled.'; $this->json['message'] = 'Account disabled.';
@ -196,11 +186,6 @@ class UploadController extends Controller
$code = humanRandomString(); $code = humanRandomString();
} while ($this->database->query('SELECT COUNT(*) AS `count` FROM `uploads` WHERE `code` = ?', $code)->fetch()->count > 0); } while ($this->database->query('SELECT COUNT(*) AS `count` FROM `uploads` WHERE `code` = ?', $code)->fetch()->count > 0);
$published = 1;
if ($this->getSetting('hide_by_default') === 'on') {
$published = 0;
}
$fileInfo = pathinfo($file->getClientFilename()); $fileInfo = pathinfo($file->getClientFilename());
$storagePath = "$user->user_code/$code.$fileInfo[extension]"; $storagePath = "$user->user_code/$code.$fileInfo[extension]";
@ -211,7 +196,7 @@ class UploadController extends Controller
$code, $code,
$file->getClientFilename(), $file->getClientFilename(),
$storagePath, $storagePath,
$published, $user->hide_uploads == '1' ? 0 : 1,
]); ]);
$mediaId = $this->database->getPdo()->lastInsertId(); $mediaId = $this->database->getPdo()->lastInsertId();

View file

@ -95,7 +95,10 @@ class UserController extends Controller
param($request, 'password'), param($request, 'password'),
param($request, 'is_admin') !== null ? 1 : 0, param($request, 'is_admin') !== null ? 1 : 0,
param($request, 'is_active') !== null ? 1 : 0, param($request, 'is_active') !== null ? 1 : 0,
$maxUserQuota $maxUserQuota,
false,
param($request, 'hide_uploads') !== null ? 1 : 0,
param($request, 'copy_raw') !== null ? 1 : 0
); );
if (param($request, 'send_notification') !== null) { if (param($request, 'send_notification') !== null) {
@ -186,7 +189,9 @@ class UserController extends Controller
param($request, 'is_admin') !== null ? 1 : 0, param($request, 'is_admin') !== null ? 1 : 0,
param($request, 'is_active') !== null ? 1 : 0, param($request, 'is_active') !== null ? 1 : 0,
$user->max_disk_quota, $user->max_disk_quota,
param($request, 'ldap') !== null ? 1 : 0 param($request, 'ldap') !== null ? 1 : 0,
param($request, 'hide_uploads') !== null ? 1 : 0,
param($request, 'copy_raw') !== null ? 1 : 0
); );
if ($user->id === $this->session->get('user_id')) { if ($user->id === $this->session->get('user_id')) {

View file

@ -70,7 +70,6 @@ class UserQuery
return $user; return $user;
} }
/** /**
* @param string $email * @param string $email
* @param string $username * @param string $username
@ -80,9 +79,11 @@ class UserQuery
* @param int $maxUserQuota * @param int $maxUserQuota
* @param string|null $activateToken * @param string|null $activateToken
* @param int $ldap * @param int $ldap
* @param int $hideUploads
* @param int $copyRaw
* @return bool|\PDOStatement|string * @return bool|\PDOStatement|string
*/ */
public function create(string $email, string $username, string $password = null, int $isAdmin = 0, int $isActive = 0, int $maxUserQuota = -1, string $activateToken = null, int $ldap =0) public function create(string $email, string $username, string $password = null, int $isAdmin = 0, int $isActive = 0, int $maxUserQuota = -1, string $activateToken = null, int $ldap = 0, int $hideUploads = 0, int $copyRaw = 0)
{ {
do { do {
$userCode = humanRandomString(5); $userCode = humanRandomString(5);
@ -90,7 +91,7 @@ class UserQuery
$token = $this->generateUserUploadToken(); $token = $this->generateUserUploadToken();
return $this->database->query('INSERT INTO `users`(`email`, `username`, `password`, `is_admin`, `active`, `user_code`, `token`, `max_disk_quota`, `activate_token`, `ldap`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', [ return $this->database->query('INSERT INTO `users`(`email`, `username`, `password`, `is_admin`, `active`, `user_code`, `token`, `max_disk_quota`, `activate_token`, `ldap`, `hide_uploads`, `copy_raw`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', [
$email, $email,
$username, $username,
$password !== null ? password_hash($password, PASSWORD_DEFAULT) : null, $password !== null ? password_hash($password, PASSWORD_DEFAULT) : null,
@ -100,7 +101,9 @@ class UserQuery
$token, $token,
$maxUserQuota, $maxUserQuota,
$activateToken, $activateToken,
$ldap $ldap,
$hideUploads,
$copyRaw,
]); ]);
} }
@ -113,12 +116,14 @@ class UserQuery
* @param int $isActive * @param int $isActive
* @param int $maxUserQuota * @param int $maxUserQuota
* @param int $ldap * @param int $ldap
* @param int $hideUploads
* @param int $copyRaw
* @return bool|\PDOStatement|string * @return bool|\PDOStatement|string
*/ */
public function update($id, string $email, string $username, string $password = null, int $isAdmin = 0, int $isActive = 0, int $maxUserQuota = -1, int $ldap = 0) public function update($id, string $email, string $username, string $password = null, int $isAdmin = 0, int $isActive = 0, int $maxUserQuota = -1, int $ldap = 0, int $hideUploads = 0, int $copyRaw = 0)
{ {
if (!empty($password)) { if (!empty($password)) {
return $this->database->query('UPDATE `users` SET `email`=?, `username`=?, `password`=?, `is_admin`=?, `active`=?, `max_disk_quota`=?, `ldap`=? WHERE `id` = ?', [ return $this->database->query('UPDATE `users` SET `email`=?, `username`=?, `password`=?, `is_admin`=?, `active`=?, `max_disk_quota`=?, `ldap`=?, `hide_uploads`=?, `copy_raw`=? WHERE `id` = ?', [
$email, $email,
$username, $username,
password_hash($password, PASSWORD_DEFAULT), password_hash($password, PASSWORD_DEFAULT),
@ -126,16 +131,20 @@ class UserQuery
$isActive, $isActive,
$maxUserQuota, $maxUserQuota,
$ldap, $ldap,
$hideUploads,
$copyRaw,
$id, $id,
]); ]);
} else { } else {
return $this->database->query('UPDATE `users` SET `email`=?, `username`=?, `is_admin`=?, `active`=?, `max_disk_quota`=?, `ldap`=? WHERE `id` = ?', [ return $this->database->query('UPDATE `users` SET `email`=?, `username`=?, `is_admin`=?, `active`=?, `max_disk_quota`=?, `ldap`=?, `hide_uploads`=?, `copy_raw`=? WHERE `id` = ?', [
$email, $email,
$username, $username,
$isAdmin, $isAdmin,
$isActive, $isActive,
$maxUserQuota, $maxUserQuota,
$ldap, $ldap,
$hideUploads,
$copyRaw,
$id, $id,
]); ]);
} }

View file

@ -16,6 +16,8 @@ Some functions that every user that use XBackBone should know:
+ (`v3.1`+) With the right click on the uploaded media, you can select them, and then remove them in bulk. + (`v3.1`+) With the right click on the uploaded media, you can select them, and then remove them in bulk.
+ (`v3.1`+) You can add additional tag to your uploads, clicking on the **+** button on the media. + (`v3.1`+) You can add additional tag to your uploads, clicking on the **+** button on the media.
+ (`v3.1`+) You can add you can delete tags, by right-click on them. + (`v3.1`+) You can add you can delete tags, by right-click on them.
+ (`v3.1`+) You can choose in your profile options if hide the uploads by default.
+ (`v3.1`+) You can choose in your profile options if always copy the raw url (from the web interface).
## Administrator ## Administrator
In addition, from the system page, and administrator can: In addition, from the system page, and administrator can:

195
docs/changelog.md Normal file
View file

@ -0,0 +1,195 @@
---
layout: default
title: Changelog
nav_order: 8
---
## v.3.1 (WIP)
+ Added tagging system (add, delete, search of tagged files).
+ Added basic media auto-tagging on upload.
+ Added registration system.
+ Added password recovery system.
+ Added ability to export all media of an account.
+ Added ability to choose between default and raw url on copy.
+ Added hide by default option.
+ Added user disk quota.
+ Added reCAPTCHA login protection.
+ Added bulk delete.
+ Added account clean function.
+ Added user disk quota system.
+ Added notification option on account create.
+ Added LDAP authentication.
+ Fixed bug html files raws are rendered in a browser.
+ The theme is now re-applied after every system update.
+ Updated system settings page.
+ Updated translations.
+ Improved grid layout.
+ Fixes and improvements.
## v.3.0.2
+ Fixed error with migrate command.
+ Updated translations.
## v.3.0.1
+ Fixed error with older mysql versions.
+ Fixed config is compiled with the di container.
+ Small installer update.
## v.3.0
+ Upgraded from Slim3 to Slim 4.
+ Added web upload.
+ Added ability to add custom HTML in \<head\> tag.
+ Added ability to show a preview of PDF files.
+ Added remember me functionality.
+ Added delete button on the preview page if the user is logged in.
+ New project icon (by [@SerenaItalia](https://www.deviantart.com/serenaitalia)).
+ Raw URL now accept file extensions.
+ The linux script can be used on headless systems.
+ Improved installer.
+ Improved thumbnail generation.
+ Replaced videojs player with Plyr.
+ Implemented SameSite XSS protection.
+ Small fixes and improvements.
## v.2.6.6
+ Ability to choose between releases and prereleases with the web updater.
+ Updated translations.
## v2.6.5
+ Fixed error after orphaned files removal #74.
+ Fixed update password not correctly removed from log files #74.
+ Changed color to some buttons to address visibility with some themes.
## v2.6.4
+ Filter on displayable images.
+ Fixed during upload error on php compiled for 32 bit.
+ Fixed icons on the installer page.
+ The generated random strings are now more human readable.
## v2.6.3
+ Fixed #67.
+ Fixed bad preload statement.
+ Fixed wrong redirect after install in subdirs.
## v2.6.2
+ Use the font awesome web font for better performances.
+ Changed background default color.
+ Added method for cache busting when updating/change theme.
+ Added russian translation from [Weblate](https://hosted.weblate.org/projects/xbackbone/xbackbone/).
## v2.6.1
+ Fixed bad redirects on the web installer (#62).
+ Fixed login page with dark themes.
+ Improved shell commands.
+ Added alert if required extensions are not loaded.
+ Updated translations.
## v2.6
+ Added support to use AWS S3, Google Cloud Storage, Dropbox and FTP(s) accounts as storage location.
+ Fixed missing icon.
+ Added german and norwegian translations from [Weblate](https://hosted.weblate.org/projects/xbackbone/xbackbone/).
+ Improved lang detection.
+ Added ability to force system language.
## v2.5.3
+ Fixed bad css loading on Firefox (#35).
+ Fixed wrong style for publish/unpublish button.
+ Improved exception stacktrace logging.
## v2.5.2
+ Improved session handling.
+ Fixed telegram share not working.
+ Fix for big text file now are not rendered in the browser.
+ Added preloading for some resources to improve performances.
+ Added check for block execution on EOL and unsupported PHP versions.
+ Other minor improvements.
## v2.5.1
+ Fixed bad redirect if the theme folder is not writable. (#27)
+ Improved HTTP partial content implementation for large files.
## v2.5
+ Updated project license to <a href="https://choosealicense.com/licenses/agpl-3.0/">AGPL v3.0</a> (now releases ships with the new license).
+ **[BETA]** Added self update feature.
+ Added partial content implementation (stream seeking on chromium based browsers).
+ Improved video.js alignment with large videos.
+ Optimized output zip release size.
+ Templates cleanup and optimizations.
+ Improved error handling.
+ Added project favicon.
## v2.4.1
+ Fixed error message when the file is too large. (#15)
+ Fixed button alignment.
## v2.4
+ Added function to remove orphaned files.
+ Switch between tab and gallery mode using an admin account.
+ Multiple uploads sorting methods.
+ Search in uploads.
+ Internal refactoring and improvements
+ Updated js dependencies.
## v2.3.1
+ Fixed en lang.
+ Fixed forced background with dark themes.
+ Added checks during the installation wizard.
+ cURL and Wget can now directly download the file.
## v2.3
+ Improved image scaling in user gallery.
+ Added overlay on user gallery images.
+ Fixed IT translation.
+ Fontawesome icon match the single file mime-type.
+ Enable audio player with video.js.
+ Video and audio now starts with volume at 50%.
+ Added linux script to allow uploads from linux screenshot tools.
+ Minor layout fixes.
## v2.2
+ Added multi-language support.
+ Improved routing.
+ Fixed HTTP/2 push is resetting the current session.
+ Minor improvements and bug fixes.
## v2.1
+ Improved theme style.
+ Improved page redirecting.
+ Allow e-mail login.
+ Support for ShareX deletion URL.
+ Fixed HTTP/2 push preload.
+ Added video.js support.
## v2.0
+ Migrated from Flight to Slim 3 framework.
+ Added install wizard (using the CLI is no longer required).
+ Allow discord bot to display the preview.
+ Theme switcher on the web UI.
+ Added used space indicator per user.
+ MySQL support.
+ Improvements under the hood.
## v1.3
+ Added command to switch between bootswatch.com themes.
+ Added popever to write the telegram message when sharing.
+ Packaging improvements.
+ Updated some dependencies.
+ Allow Facebook bots to display the preview.
## v1.2
+ Previews are now scaled for better page load.
+ Added auto config generator for ShareX.
+ Show upload file size on the dashboard.
+ Fixed insert for admin user (running `php bin\migrate --install`).
+ Removed HTTP2 push from the dashboard to improve loading time.
## v1.1
+ Added logging.
+ Fixed back to top when click delete or publish/unpublish.
+ Improved migrate system.
+ Login redirect back to the requested page.
+ Updated Bootstrap theme.
+ Added share to Telegram.
## v1.0
+ Initial version.

View file

@ -6,11 +6,11 @@ nav_order: 7
# Common Issues # Common Issues
#### Error 404 after installation ### Error 404 after installation
If you have apache, check if it's reading the file `.htaccess` and the `mod_rewrite` is enabled. If you have apache, check if it's reading the file `.htaccess` and the `mod_rewrite` is enabled.
#### [Discord, Telegram, ...] is not showing the image/video preview of the link. ### [Discord, Telegram, ...] is not showing the image/video preview of the link.
If you have Cloudflare enabled, check if it's blocking bots. If this function is enabled the Discord bot, Telebot, etc that fetch the preview will be blocked. If you have Cloudflare enabled, check if it's blocking bots. If this function is enabled the Discord bot, Telebot, etc that fetch the preview will be blocked.
#### How to increase the max file size? ### How to increase the max file size?
Increase the post_max_size and upload_max_filesize in your php.ini Increase the post_max_size and upload_max_filesize in your php.ini

View file

@ -11,11 +11,12 @@ nav_order: 1
XBackBone is a simple and lightweight PHP file manager that support the instant sharing tool ShareX and *NIX systems. It supports uploading and displaying images, GIF, video, code, formatted text, pdf, and file downloading and uploading. Also have a web UI with multi user management, media gallery and search support. XBackBone is a simple and lightweight PHP file manager that support the instant sharing tool ShareX and *NIX systems. It supports uploading and displaying images, GIF, video, code, formatted text, pdf, and file downloading and uploading. Also have a web UI with multi user management, media gallery and search support.
{: .fs-5 .fw-300 } {: .fs-5 .fw-300 }
<p align="center">
[Download](https://github.com/SergiX44/XBackBone/releases/latest){: .btn .btn-green } [Download](https://github.com/SergiX44/XBackBone/releases/latest){: .btn .btn-green }
[GitHub](https://github.com/SergiX44/XBackBone){: .btn .btn-blue } [GitHub](https://github.com/SergiX44/XBackBone){: .btn .btn-blue }
</p>
## Main Features ## Main Features
+ Supports every upload type from ShareX. + Supports every upload type from ShareX.
+ Config generator for ShareX. + Config generator for ShareX.
+ Low memory footprint. + Low memory footprint.

View file

@ -1,7 +1,7 @@
--- ---
layout: default layout: default
title: License & Credits title: License & Credits
nav_order: 8 nav_order: 9
--- ---
# License # License

View file

@ -1 +1,3 @@
ALTER TABLE `users` ADD COLUMN `ldap` BOOLEAN NOT NULL DEFAULT 0; ALTER TABLE `users` ADD COLUMN `ldap` BOOLEAN NOT NULL DEFAULT 0;
ALTER TABLE `users` ADD COLUMN `hide_uploads` BOOLEAN NOT NULL DEFAULT 0;
ALTER TABLE `users` ADD COLUMN `copy_raw` BOOLEAN NOT NULL DEFAULT 0;

View file

@ -1 +1,3 @@
ALTER TABLE `users` ADD COLUMN `ldap` BOOLEAN NOT NULL DEFAULT 0; ALTER TABLE `users` ADD COLUMN `ldap` BOOLEAN NOT NULL DEFAULT 0;
ALTER TABLE `users` ADD COLUMN `hide_uploads` BOOLEAN NOT NULL DEFAULT 0;
ALTER TABLE `users` ADD COLUMN `copy_raw` BOOLEAN NOT NULL DEFAULT 0;

View file

@ -19,7 +19,7 @@
<div class="pl-3 pt-2d5"><span class="badge badge-dark shadow-lg">{{ media.size }}</span></div> <div class="pl-3 pt-2d5"><span class="badge badge-dark shadow-lg">{{ media.size }}</span></div>
<div class="text-right pr-3 pt-2d5"> <div class="text-right pr-3 pt-2d5">
<div class="btn-group shadow-lg"> <div class="btn-group shadow-lg">
<button type="button" class="btn btn-sm btn-success btn-clipboard" data-toggle="tooltip" title="{{ lang('copy_link') }}" data-clipboard-text="{{ urlFor('/' ~ media.user_code ~ '/' ~ media.code ~ '.' ~ media.extension) }}{{ copy_url_behavior == 'raw' ? '/raw' }}"> <button type="button" class="btn btn-sm btn-success btn-clipboard" data-toggle="tooltip" title="{{ lang('copy_link') }}" data-clipboard-text="{{ urlFor('/' ~ media.user_code ~ '/' ~ media.code ~ '.' ~ media.extension) }}{{ copy_raw ? '/raw' }}">
<i class="fas fa-link"></i> <i class="fas fa-link"></i>
</button> </button>
<a href="{{ urlFor('/' ~ media.user_code ~ '/' ~ media.code ~ '.' ~ media.extension ~ '/download') }}" class="btn btn-sm btn-secondary" data-toggle="tooltip" title="{{ lang('download') }}"><i class="fas fa-cloud-download-alt"></i></a> <a href="{{ urlFor('/' ~ media.user_code ~ '/' ~ media.code ~ '.' ~ media.extension ~ '/download') }}" class="btn btn-sm btn-secondary" data-toggle="tooltip" title="{{ lang('download') }}"><i class="fas fa-cloud-download-alt"></i></a>

View file

@ -64,7 +64,7 @@
{% if media.username is not null %} {% if media.username is not null %}
<a href="{{ urlFor('/' ~ media.user_code ~ '/' ~ media.code ~ '.' ~ media.extension) }}" class="btn btn-sm btn-outline-secondary" data-toggle="tooltip" title="{{ lang('open') }}" target="_blank"><i class="fas fa-external-link-alt"></i></a> <a href="{{ urlFor('/' ~ media.user_code ~ '/' ~ media.code ~ '.' ~ media.extension) }}" class="btn btn-sm btn-outline-secondary" data-toggle="tooltip" title="{{ lang('open') }}" target="_blank"><i class="fas fa-external-link-alt"></i></a>
<a href="{{ urlFor('/' ~ media.user_code ~ '/' ~ media.code ~ '.' ~ media.extension ~ '/download') }}" class="btn btn-sm btn-outline-primary" data-toggle="tooltip" title="{{ lang('download') }}"><i class="fas fa-cloud-download-alt"></i></a> <a href="{{ urlFor('/' ~ media.user_code ~ '/' ~ media.code ~ '.' ~ media.extension ~ '/download') }}" class="btn btn-sm btn-outline-primary" data-toggle="tooltip" title="{{ lang('download') }}"><i class="fas fa-cloud-download-alt"></i></a>
<a href="javascript:void(0)" class="btn btn-sm btn-outline-success btn-clipboard" data-toggle="tooltip" title="{{ lang('copy_link') }}" data-clipboard-text="{{ urlFor('/' ~ media.user_code ~ '/' ~ media.code ~ '.' ~ media.extension) }}{{ copy_url_behavior == 'raw' ? '/raw' }}"><i class="fas fa-link"></i></a> <a href="javascript:void(0)" class="btn btn-sm btn-outline-success btn-clipboard" data-toggle="tooltip" title="{{ lang('copy_link') }}" data-clipboard-text="{{ urlFor('/' ~ media.user_code ~ '/' ~ media.code ~ '.' ~ media.extension) }}{{ copy_raw ? '/raw' }}"><i class="fas fa-link"></i></a>
{% else %} {% else %}
<a href="{{ route('upload.raw', {'id': media.id}) }}" class="btn btn-sm btn-outline-dark" data-toggle="tooltip" title="{{ lang('raw') }}" target="_blank"><i class="fas fa-external-link-alt"></i></a> <a href="{{ route('upload.raw', {'id': media.id}) }}" class="btn btn-sm btn-outline-dark" data-toggle="tooltip" title="{{ lang('raw') }}" target="_blank"><i class="fas fa-external-link-alt"></i></a>
{% endif %} {% endif %}

View file

@ -65,18 +65,6 @@
<input type="checkbox" name="register_enabled" data-toggle="toggle" {{ register_enabled == 'on' ? 'checked' }}> <input type="checkbox" name="register_enabled" data-toggle="toggle" {{ register_enabled == 'on' ? 'checked' }}>
</div> </div>
</div> </div>
<div class="form-group row">
<label for="hide_by_default" class="col-sm-4 col-form-label">{{ lang('hide_by_default') }}</label>
<div class="col-sm-8">
<input type="checkbox" name="hide_by_default" data-toggle="toggle" {{ hide_by_default == 'on' ? 'checked' }}>
</div>
</div>
<div class="form-group row">
<label for="copy_url_behavior" class="col-sm-4 col-form-label">{{ lang('copy_url_behavior') }}</label>
<div class="col-sm-8">
<input type="checkbox" name="copy_url_behavior" data-toggle="toggle" data-off="Default URL" data-on="Raw URL" data-onstyle="primary" data-offstyle="secondary" {{ copy_url_behavior == 'raw' ? 'checked' }}>
</div>
</div>
<div class="form-group row"> <div class="form-group row">
<label for="themes" class="col-sm-4 col-form-label">{{ lang('theme') }}</label> <label for="themes" class="col-sm-4 col-form-label">{{ lang('theme') }}</label>
<div class="col-sm-8"> <div class="col-sm-8">

View file

@ -13,54 +13,66 @@
<div class="card-body"> <div class="card-body">
<form method="post" action="{{ route('user.store') }}"> <form method="post" action="{{ route('user.store') }}">
<div class="form-group row"> <div class="form-group row">
<label for="email" class="col-sm-2 col-form-label">E-Mail</label> <label for="email" class="col-sm-3 col-form-label">E-Mail</label>
<div class="col-sm-10"> <div class="col-sm-9">
<input type="email" class="form-control" id="email" placeholder="email@example.com" name="email" autocomplete="off" required> <input type="email" class="form-control" id="email" placeholder="email@example.com" name="email" autocomplete="off" required>
</div> </div>
</div> </div>
<div class="form-group row"> <div class="form-group row">
<label for="email" class="col-sm-2 col-form-label">{{ lang('username') }}</label> <label for="email" class="col-sm-3 col-form-label">{{ lang('username') }}</label>
<div class="col-sm-10"> <div class="col-sm-9">
<input type="text" class="form-control" id="username" placeholder="{{ lang('username') }}" name="username" autocomplete="off" required > <input type="text" class="form-control" id="username" placeholder="{{ lang('username') }}" name="username" autocomplete="off" required >
</div> </div>
</div> </div>
<div class="form-group row"> <div class="form-group row">
<label for="password" class="col-sm-2 col-form-label">{{ lang('password') }}</label> <label for="password" class="col-sm-3 col-form-label">{{ lang('password') }}</label>
<div class="col-sm-10"> <div class="col-sm-9">
<input type="password" class="form-control" id="password" placeholder="{{ lang('password') }}" name="password" autocomplete="off"> <input type="password" class="form-control" id="password" placeholder="{{ lang('password') }}" name="password" autocomplete="off">
<small>{{ lang('user_create_password') }}</small> <small>{{ lang('user_create_password') }}</small>
</div> </div>
</div> </div>
<div class="form-group row">
<label for="hide_uploads" class="col-sm-3 col-form-label">{{ lang('hide_by_default') }}</label>
<div class="col-sm-9">
<input type="checkbox" name="hide_uploads" data-toggle="toggle">
</div>
</div>
<div class="form-group row">
<label for="copy_raw" class="col-sm-3 col-form-label">{{ lang('copy_url_behavior') }}</label>
<div class="col-sm-9">
<input type="checkbox" name="copy_raw" data-toggle="toggle" data-off="Default URL" data-on="Raw URL" data-onstyle="primary" data-offstyle="secondary">
</div>
</div>
{% if quota_enabled == 'on' %} {% if quota_enabled == 'on' %}
<div class="form-group row"> <div class="form-group row">
<label for="max_user_quota" class="col-sm-2 col-form-label">{{ lang('max_user_quota') }}</label> <label for="max_user_quota" class="col-sm-3 col-form-label">{{ lang('max_user_quota') }}</label>
<div class="col-sm-10"> <div class="col-sm-9">
<input type="text" class="form-control" id="max_user_quota" name="max_user_quota" pattern="([0-9]+[K|M|G|T])|(\-1)" title="512M, 2G, 1T, ..." placeholder="1G" value="{{ default_user_quota }}" required> <input type="text" class="form-control" id="max_user_quota" name="max_user_quota" pattern="([0-9]+[K|M|G|T])|(\-1)" title="512M, 2G, 1T, ..." placeholder="1G" value="{{ default_user_quota }}" required>
<small>512M, 2G, 1T, ... (-1=∞)</small> <small>512M, 2G, 1T, ... (-1=∞)</small>
</div> </div>
</div> </div>
{% endif %} {% endif %}
<div class="form-group row"> <div class="form-group row">
<label for="is_admin" class="col-sm-2 col-form-label">{{ lang('is_admin') }}</label> <label for="is_admin" class="col-sm-3 col-form-label">{{ lang('is_admin') }}</label>
<div class="col-sm-10"> <div class="col-sm-9">
<input type="checkbox" name="is_admin" data-toggle="toggle" data-off="{{ lang('no') }}" data-on="{{ lang('yes') }}"> <input type="checkbox" name="is_admin" data-toggle="toggle" data-off="{{ lang('no') }}" data-on="{{ lang('yes') }}">
</div> </div>
</div> </div>
<div class="form-group row"> <div class="form-group row">
<label for="is_active" class="col-sm-2 col-form-label">{{ lang('is_active') }}</label> <label for="is_active" class="col-sm-3 col-form-label">{{ lang('is_active') }}</label>
<div class="col-sm-10"> <div class="col-sm-9">
<input type="checkbox" name="is_active" data-toggle="toggle" data-off="{{ lang('no') }}" data-on="{{ lang('yes') }}" checked> <input type="checkbox" name="is_active" data-toggle="toggle" data-off="{{ lang('no') }}" data-on="{{ lang('yes') }}" checked>
</div> </div>
</div> </div>
<hr> <hr>
<div class="form-group row"> <div class="form-group row">
<label for="send_notification" class="col-sm-2 col-form-label">{{ lang('send_notification') }}</label> <label for="send_notification" class="col-sm-3 col-form-label">{{ lang('send_notification') }}</label>
<div class="col-sm-10"> <div class="col-sm-9">
<input type="checkbox" name="send_notification" data-toggle="toggle" data-onstyle="info" data-off="{{ lang('no') }}" data-on="{{ lang('yes') }}"> <input type="checkbox" name="send_notification" data-toggle="toggle" data-onstyle="info" data-off="{{ lang('no') }}" data-on="{{ lang('yes') }}">
</div> </div>
</div> </div>
<div class="form-group row justify-content-md-end"> <div class="form-group row justify-content-md-end">
<div class="col-sm-10"> <div class="col-sm-9">
<button type="submit" class="btn btn-outline-success"> <button type="submit" class="btn btn-outline-success">
<i class="fas fa-save fa-fw"></i> {{ lang('save') }} <i class="fas fa-save fa-fw"></i> {{ lang('save') }}
</button> </button>

View file

@ -15,14 +15,14 @@
<div class="card-body"> <div class="card-body">
<form method="post" action="{{ route( (profile ? 'profile.update' : 'user.update'), {'id': user.id}) }}"> <form method="post" action="{{ route( (profile ? 'profile.update' : 'user.update'), {'id': user.id}) }}">
<div class="form-group row"> <div class="form-group row">
<label for="email" class="col-sm-2 col-form-label">Email</label> <label for="email" class="col-sm-3 col-form-label">Email</label>
<div class="col-sm-10"> <div class="col-sm-9">
<input type="email" class="form-control" id="email" placeholder="email@example.com" name="email" value="{{ user.email }}" autocomplete="off" required> <input type="email" class="form-control" id="email" placeholder="email@example.com" name="email" value="{{ user.email }}" autocomplete="off" required>
</div> </div>
</div> </div>
<div class="form-group row"> <div class="form-group row">
<label for="username" class="col-sm-2 col-form-label">{{ lang('username') }}</label> <label for="username" class="col-sm-3 col-form-label">{{ lang('username') }}</label>
<div class="col-sm-10"> <div class="col-sm-9">
{% if profile %} {% if profile %}
<input type="text" class="form-control disabled" id="username" value="{{ user.username }}" readonly> <input type="text" class="form-control disabled" id="username" value="{{ user.username }}" readonly>
{% else %} {% else %}
@ -31,14 +31,14 @@
</div> </div>
</div> </div>
<div class="form-group row"> <div class="form-group row">
<label for="password" class="col-sm-2 col-form-label">{{ lang('password') }}</label> <label for="password" class="col-sm-3 col-form-label">{{ lang('password') }}</label>
<div class="col-sm-10"> <div class="col-sm-9">
<input type="password" class="form-control" id="password" placeholder="{{ lang('password') }}" name="password" autocomplete="off"{{ user.ldap ? ' disabled' }}> <input type="password" class="form-control" id="password" placeholder="{{ lang('password') }}" name="password" autocomplete="off"{{ user.ldap ? ' disabled' }}>
</div> </div>
</div> </div>
<div class="form-group row"> <div class="form-group row">
<label for="token" class="col-sm-2 col-form-label">{{ lang('token') }}</label> <label for="token" class="col-sm-3 col-form-label">{{ lang('token') }}</label>
<div class="col-sm-10"> <div class="col-sm-9">
<div class="input-group"> <div class="input-group">
<input type="text" id="token" class="form-control" value="{{ user.token }}" readonly> <input type="text" id="token" class="form-control" value="{{ user.token }}" readonly>
<div class="input-group-append"> <div class="input-group-append">
@ -49,8 +49,20 @@
</div> </div>
</div> </div>
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-2 col-form-label">{{ lang('client_config') }}</label> <label for="hide_uploads" class="col-sm-3 col-form-label">{{ lang('hide_by_default') }}</label>
<div class="col-sm-10"> <div class="col-sm-9">
<input type="checkbox" name="hide_uploads" data-toggle="toggle" {{ user.hide_uploads ? 'checked' }}>
</div>
</div>
<div class="form-group row">
<label for="copy_raw" class="col-sm-3 col-form-label">{{ lang('copy_url_behavior') }}</label>
<div class="col-sm-9">
<input type="checkbox" name="copy_raw" data-toggle="toggle" data-off="Default URL" data-on="Raw URL" data-onstyle="primary" data-offstyle="secondary" {{ user.copy_raw ? 'checked' }}>
</div>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">{{ lang('client_config') }}</label>
<div class="col-sm-9">
<div class="btn-group"> <div class="btn-group">
<a href="{{ route('config.sharex', {'id': user.id}) }}" class="btn btn-lg btn-outline-dark"><i class="fas fa-fw fa-download"></i> ShareX Config</a> <a href="{{ route('config.sharex', {'id': user.id}) }}" class="btn btn-lg btn-outline-dark"><i class="fas fa-fw fa-download"></i> ShareX Config</a>
<a href="{{ route('config.script', {'id': user.id}) }}" class="btn btn-lg btn-outline-danger"><i class="fas fa-fw fa-download"></i> Linux Script</a> <a href="{{ route('config.script', {'id': user.id}) }}" class="btn btn-lg btn-outline-danger"><i class="fas fa-fw fa-download"></i> Linux Script</a>
@ -58,8 +70,8 @@
</div> </div>
</div> </div>
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-2 col-form-label">{{ lang('export_data') }}</label> <label class="col-sm-3 col-form-label">{{ lang('export_data') }}</label>
<div class="col-sm-10"> <div class="col-sm-9">
<div class="btn-group"> <div class="btn-group">
<a href="{{ route('export.data', {'id': user.id}) }}" class="btn btn-lg btn-outline-warning"><i class="fas fa-fw fa-file-archive"></i> {{ lang('download') }}</a> <a href="{{ route('export.data', {'id': user.id}) }}" class="btn btn-lg btn-outline-warning"><i class="fas fa-fw fa-file-archive"></i> {{ lang('download') }}</a>
</div> </div>
@ -70,36 +82,36 @@
<hr> <hr>
{% if quota_enabled == 'on' %} {% if quota_enabled == 'on' %}
<div class="form-group row"> <div class="form-group row">
<label for="max_user_quota" class="col-sm-2 col-form-label">{{ lang('max_user_quota') }}</label> <label for="max_user_quota" class="col-sm-3 col-form-label">{{ lang('max_user_quota') }}</label>
<div class="col-sm-10"> <div class="col-sm-9">
<input type="text" class="form-control" id="max_user_quota" name="max_user_quota" pattern="([0-9]+[K|M|G|T])|(\-1)" title="512M, 2G, 1T, ..." placeholder="1G" value="{{ max_disk_quota }}" required> <input type="text" class="form-control" id="max_user_quota" name="max_user_quota" pattern="([0-9]+[K|M|G|T])|(\-1)" title="512M, 2G, 1T, ..." placeholder="1G" value="{{ max_disk_quota }}" required>
<small>512M, 2G, 1T, ... (-1=∞)</small> <small>512M, 2G, 1T, ... (-1=∞)</small>
</div> </div>
</div> </div>
{% endif %} {% endif %}
<div class="form-group row"> <div class="form-group row">
<label for="is_admin" class="col-sm-2 col-form-label">{{ lang('is_admin') }}</label> <label for="is_admin" class="col-sm-3 col-form-label">{{ lang('is_admin') }}</label>
<div class="col-sm-10"> <div class="col-sm-9">
<input type="checkbox" name="is_admin" data-toggle="toggle" data-off="{{ lang('no') }}" data-on="{{ lang('yes') }}" {{ user.is_admin ? 'checked' }}> <input type="checkbox" name="is_admin" data-toggle="toggle" data-off="{{ lang('no') }}" data-on="{{ lang('yes') }}" {{ user.is_admin ? 'checked' }}>
</div> </div>
</div> </div>
<div class="form-group row"> <div class="form-group row">
<label for="is_active" class="col-sm-2 col-form-label">{{ lang('is_active') }}</label> <label for="is_active" class="col-sm-3 col-form-label">{{ lang('is_active') }}</label>
<div class="col-sm-10"> <div class="col-sm-9">
<input type="checkbox" name="is_active" data-toggle="toggle" data-off="{{ lang('no') }}" data-on="{{ lang('yes') }}" {{ user.active ? 'checked' }}> <input type="checkbox" name="is_active" data-toggle="toggle" data-off="{{ lang('no') }}" data-on="{{ lang('yes') }}" {{ user.active ? 'checked' }}>
</div> </div>
</div> </div>
{% if config.ldap.enabled %} {% if config.ldap.enabled %}
<div class="form-group row"> <div class="form-group row">
<label for="ldap" class="col-sm-2 col-form-label">LDAP Auth</label> <label for="ldap" class="col-sm-3 col-form-label">LDAP Auth</label>
<div class="col-sm-10"> <div class="col-sm-9">
<input type="checkbox" name="ldap" data-toggle="toggle" data-off="{{ lang('no') }}" data-on="{{ lang('yes') }}" {{ user.ldap ? 'checked' }}> <input type="checkbox" name="ldap" data-toggle="toggle" data-off="{{ lang('no') }}" data-on="{{ lang('yes') }}" {{ user.ldap ? 'checked' }}>
</div> </div>
</div> </div>
{% endif %} {% endif %}
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-2 col-form-label">{{ lang('delete_all') }}</label> <label class="col-sm-3 col-form-label">{{ lang('delete_all') }}</label>
<div class="col-sm-10"> <div class="col-sm-9">
<div class="btn-group"> <div class="btn-group">
<a href="{{ route('user.clear', {'id': user.id}) }}" class="btn btn-lg btn-outline-danger"><i class="fas fa-fw fa-recycle"></i> {{ lang('clear_account') }}</a> <a href="{{ route('user.clear', {'id': user.id}) }}" class="btn btn-lg btn-outline-danger"><i class="fas fa-fw fa-recycle"></i> {{ lang('clear_account') }}</a>
</div> </div>
@ -107,7 +119,7 @@
</div> </div>
{% endif %} {% endif %}
<div class="form-group row justify-content-md-end"> <div class="form-group row justify-content-md-end">
<div class="col-sm-10"> <div class="col-sm-9">
<button type="submit" class="btn btn-outline-info"> <button type="submit" class="btn btn-outline-info">
<i class="fas fa-save fa-fw"></i> {{ lang('save') }} <i class="fas fa-save fa-fw"></i> {{ lang('save') }}
</button> </button>