Completed multilang

Routes rework
This commit is contained in:
Sergio Brighenti 2018-11-19 19:04:43 +01:00
parent 0409a598d7
commit a950ec9af3
25 changed files with 465 additions and 239 deletions

View file

@ -1,3 +1,9 @@
## 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.

View file

@ -23,11 +23,11 @@ class DashboardController extends Controller
{
if ($request->getParam('afterInstall') !== null && is_dir('install')) {
Session::alert('Installation completed successfully!', 'success');
Session::alert(lang('installed'), 'success');
removeDirectory('install');
}
return redirect($response, '/home');
return redirect($response, 'home');
}
/**
@ -117,6 +117,7 @@ class DashboardController extends Controller
$out = [];
$out['Default - Bootstrap 4 default theme'] = 'https://bootswatch.com/_vendor/bootstrap/dist/css/bootstrap.min.css';
foreach ($apiJson->themes as $theme) {
$out["{$theme->name} - {$theme->description}"] = $theme->cssMin;
}
@ -128,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 redirect($response, '/system')->withAddedHeader('Cache-Control', 'no-cache, must-revalidate');
return redirect($response, 'system')->withAddedHeader('Cache-Control', 'no-cache, must-revalidate');
}
}

View file

@ -19,7 +19,7 @@ class LoginController extends Controller
public function show(Request $request, Response $response): Response
{
if (Session::get('logged', false)) {
return redirect($response, '/home');
return redirect($response, 'home');
}
return $this->view->render($response, 'auth/login.twig');
}
@ -35,13 +35,13 @@ class LoginController extends Controller
$result = DB::query('SELECT `id`, `email`, `username`, `password`,`is_admin`, `active` FROM `users` WHERE `username` = ? OR `email` = ? LIMIT 1', [$request->getParam('username'), $request->getParam('username')])->fetch();
if (!$result || !password_verify($request->getParam('password'), $result->password)) {
Session::alert('Wrong credentials', 'danger');
return redirect($response, '/login');
Session::alert(lang('bad_login'), 'danger');
return redirect($response, 'login');
}
if (!$result->active) {
Session::alert('Your account is disabled.', 'danger');
return redirect($response, '/login');
Session::alert(lang('account_disabled'), 'danger');
return redirect($response, 'login');
}
Session::set('logged', true);
@ -50,14 +50,14 @@ class LoginController extends Controller
Session::set('admin', $result->is_admin);
Session::set('used_space', humanFileSize($this->getUsedSpaceByUser($result->id)));
Session::alert("Welcome, $result->username!", 'info');
Session::alert(lang('welcome', [$result->username]), 'info');
$this->logger->info("User $result->username logged in.");
if (Session::has('redirectTo')) {
return $response->withRedirect(Session::get('redirectTo'));
}
return redirect($response, '/home');
return redirect($response, 'home');
}
/**
@ -69,8 +69,8 @@ class LoginController extends Controller
{
Session::clear();
Session::set('logged', false);
Session::alert('Goodbye!', 'warning');
return redirect($response, '/login');
Session::alert(lang('goodbye'), 'warning');
return redirect($response, 'login.show');
}
}

View file

@ -139,12 +139,12 @@ class UploadController extends Controller
$user = $this->database->query('SELECT `id`, `active` FROM `users` WHERE `token` = ? LIMIT 1', $args['token'])->fetch();
if (!$user) {
Session::alert('Token specified not found.', 'danger');
Session::alert(lang('token_not_found'), 'danger');
return $response->withRedirect($request->getHeaderLine('HTTP_REFERER'));
}
if (!$user->active) {
Session::alert('Account disabled.', 'danger');
Session::alert(lang('account_disabled'), 'danger');
return $response->withRedirect($request->getHeaderLine('HTTP_REFERER'));
}
@ -163,7 +163,7 @@ class UploadController extends Controller
throw new UnauthorizedException();
}
return redirect($response, '/home');
return redirect($response, 'home');
}
/**

View file

@ -57,23 +57,28 @@ class UserController extends Controller
public function store(Request $request, Response $response): Response
{
if ($request->getParam('email') === null) {
Session::alert('The email is required.', 'danger');
return redirect($response, '/user/create');
Session::alert(lang('email_required'), 'danger');
return redirect($response, 'user.create');
}
if ($this->database->query('SELECT COUNT(*) AS `count` FROM `users` WHERE `email` = ?', $request->getParam('email'))->fetch()->count > 0) {
Session::alert(lang('email_taken'), 'danger');
return redirect($response, 'user.create');
}
if ($request->getParam('username') === null) {
Session::alert('The username is required.', 'danger');
return redirect($response, '/user/create');
Session::alert(lang('username_required'), 'danger');
return redirect($response, 'user.create');
}
if ($request->getParam('password') === null) {
Session::alert('The password is required.', 'danger');
return redirect($response, '/user/create');
Session::alert(lang('password_required'), 'danger');
return redirect($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 redirect($response, '/user/create');
Session::alert(lang('username_taken'), 'danger');
return redirect($response, 'user.create');
}
do {
@ -86,16 +91,16 @@ class UserController extends Controller
$request->getParam('email'),
$request->getParam('username'),
password_hash($request->getParam('password'), PASSWORD_DEFAULT),
$request->getParam('is_admin') !== null,
$request->getParam('is_active') !== null,
$request->getParam('is_admin') !== null ? 1 : 0,
$request->getParam('is_active') !== null ? 1 : 0,
$userCode,
$token,
]);
Session::alert("User '{$request->getParam('username')}' created!", 'success');
Session::alert(lang('user_created', [$request->getParam('username')]), 'success');
$this->logger->info('User ' . Session::get('username') . ' created a new user.', [array_diff($request->getParams(), ['password'])]);
return redirect($response, '/users');
return redirect($response, 'user.index');
}
/**
@ -135,23 +140,28 @@ class UserController extends Controller
}
if ($request->getParam('email') === null) {
Session::alert('The email is required.', 'danger');
return redirect($response, '/user/' . $args['id'] . '/edit');
Session::alert(lang('email_required'), 'danger');
return redirect($response, 'user.edit', ['id' => $args['id']]);
}
if ($this->database->query('SELECT COUNT(*) AS `count` FROM `users` WHERE `email` = ? AND `email` <> ?', [$request->getParam('email'), $user->email])->fetch()->count > 0) {
Session::alert(lang('email_taken'), 'danger');
return redirect($response, 'user.edit', ['id' => $args['id']]);
}
if ($request->getParam('username') === null) {
Session::alert('The username is required.', 'danger');
return redirect($response, '/user/' . $args['id'] . '/edit');
Session::alert(lang('username_required'), 'danger');
return redirect($response, 'user.edit', ['id' => $args['id']]);
}
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 redirect($response, '/user/' . $args['id'] . '/edit');
Session::alert(lang('username_taken'), 'danger');
return redirect($response, 'user.edit', ['id' => $args['id']]);
}
if ($user->id === Session::get('user_id') && $request->getParam('is_admin') === null) {
Session::alert('You cannot demote yourself.', 'danger');
return redirect($response, '/user/' . $args['id'] . '/edit');
Session::alert(lang('cannot_demote'), 'danger');
return redirect($response, 'user.edit', ['id' => $args['id']]);
}
if ($request->getParam('password') !== null && !empty($request->getParam('password'))) {
@ -159,24 +169,24 @@ class UserController extends Controller
$request->getParam('email'),
$request->getParam('username'),
password_hash($request->getParam('password'), PASSWORD_DEFAULT),
$request->getParam('is_admin') !== null,
$request->getParam('is_active') !== null,
$request->getParam('is_admin') !== null ? 1 : 0,
$request->getParam('is_active') !== null ? 1 : 0,
$user->id,
]);
} else {
$this->database->query('UPDATE `users` SET `email`=?, `username`=?, `is_admin`=?, `active`=? WHERE `id` = ?', [
$request->getParam('email'),
$request->getParam('username'),
$request->getParam('is_admin') !== null,
$request->getParam('is_active') !== null,
$request->getParam('is_admin') !== null ? 1 : 0,
$request->getParam('is_active') !== null ? 1 : 0,
$user->id,
]);
}
Session::alert("User '{$request->getParam('username')}' updated!", 'success');
Session::alert(lang('user_updated', [$request->getParam('username')]), 'success');
$this->logger->info('User ' . Session::get('username') . " updated $user->id.", [$user, array_diff($request->getParams(), ['password'])]);
return redirect($response, '/users');
return redirect($response, 'user.index');
}
@ -196,16 +206,16 @@ class UserController extends Controller
}
if ($user->id === Session::get('user_id')) {
Session::alert('You cannot delete yourself.', 'danger');
return redirect($response, '/users');
Session::alert(lang('cannot_delete'), 'danger');
return redirect($response, 'user.index');
}
$this->database->query('DELETE FROM `users` WHERE `id` = ?', $user->id);
Session::alert('User deleted.', 'success');
Session::alert(lang('user_deleted'), 'success');
$this->logger->info('User ' . Session::get('username') . " deleted $user->id.");
return redirect($response, '/users');
return redirect($response, 'user.index');
}
/**
@ -254,8 +264,13 @@ class UserController extends Controller
}
if ($request->getParam('email') === null) {
Session::alert('The email is required.', 'danger');
return redirect($response, '/profile');
Session::alert(lang('email_required'), 'danger');
return redirect($response, 'profile');
}
if ($this->database->query('SELECT COUNT(*) AS `count` FROM `users` WHERE `email` = ? AND `email` <> ?', [$request->getParam('email'), $user->email])->fetch()->count > 0) {
Session::alert(lang('email_taken'), 'danger');
return redirect($response, 'profile');
}
if ($request->getParam('password') !== null && !empty($request->getParam('password'))) {
@ -271,10 +286,10 @@ class UserController extends Controller
]);
}
Session::alert('Profile updated successfully!', 'success');
Session::alert(lang('profile_updated'), 'success');
$this->logger->info('User ' . Session::get('username') . " updated profile of $user->id.");
return redirect($response, '/profile');
return redirect($response, 'profile');
}
/**

View file

@ -27,14 +27,14 @@ class AuthMiddleware
{
if (!Session::get('logged', false)) {
Session::set('redirectTo', (isset($_SERVER['HTTPS']) ? 'https' : 'http') . "://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]");
return redirect($response, '/login');
return redirect($response, 'login.show');
}
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 redirect($response, '/login');
return redirect($response, 'login.show');
}
return $next($request, $response);

View file

@ -55,12 +55,19 @@ if (!function_exists('redirect')) {
* Set the redirect response
* @param \Slim\Http\Response $response
* @param string $path
* @param array $args
* @param null $status
* @return \Slim\Http\Response
*/
function redirect(\Slim\Http\Response $response, string $path, $status = null)
function redirect(\Slim\Http\Response $response, string $path, $args = [], $status = null)
{
return $response->withRedirect(urlFor($path), $status);
if ($path === '/' || substr($path, 0, 1) === '/') {
$url = urlFor($path);
} else {
$url = route($path, $args);
}
return $response->withRedirect($url, $status);
}
}
@ -78,3 +85,29 @@ if (!function_exists('urlFor')) {
}
}
if (!function_exists('route')) {
/**
* Generate the app url given a path
* @param string $path
* @param array $args
* @return string
*/
function route(string $path, array $args = [])
{
global $app;
$uri = $app->getContainer()->get('router')->pathFor($path, $args);
return urlFor($uri);
}
}
if (!function_exists('lang')) {
/**
* @param string $key
* @param array $args
* @return string
*/
function lang(string $key, $args = [])
{
return \App\Web\Lang::getInstance()->get($key, $args);
}
}

View file

@ -1,41 +1,41 @@
<?php
// Auth routes
$app->group('', function () {
$this->get('/home[/page/{page}]', \App\Controllers\DashboardController::class . ':home');
$this->get('/system', \App\Controllers\DashboardController::class . ':system')->add(\App\Middleware\AdminMiddleware::class);
$this->get('/system/themes', \App\Controllers\DashboardController::class . ':getThemes')->add(\App\Middleware\AdminMiddleware::class);
$this->post('/system/theme/apply', \App\Controllers\DashboardController::class . ':applyTheme')->add(\App\Middleware\AdminMiddleware::class);
$this->get('/home[/page/{page}]', \App\Controllers\DashboardController::class . ':home')->setName('home');
$this->get('/system', \App\Controllers\DashboardController::class . ':system')->add(\App\Middleware\AdminMiddleware::class)->setName('system');
$this->get('/system/themes', \App\Controllers\DashboardController::class . ':getThemes')->add(\App\Middleware\AdminMiddleware::class)->setName('theme');
$this->post('/system/theme/apply', \App\Controllers\DashboardController::class . ':applyTheme')->add(\App\Middleware\AdminMiddleware::class)->setName('theme.apply');
$this->group('', function () {
$this->get('/users[/page/{page}]', \App\Controllers\UserController::class . ':index');
$this->get('/user/create', \App\Controllers\UserController::class . ':create');
$this->post('/user/create', \App\Controllers\UserController::class . ':store');
$this->get('/user/{id}/edit', \App\Controllers\UserController::class . ':edit');
$this->post('/user/{id}', \App\Controllers\UserController::class . ':update');
$this->get('/user/{id}/delete', \App\Controllers\UserController::class . ':delete');
$this->get('/users[/page/{page}]', \App\Controllers\UserController::class . ':index')->setName('user.index');
$this->get('/user/create', \App\Controllers\UserController::class . ':create')->setName('user.create');
$this->post('/user/create', \App\Controllers\UserController::class . ':store')->setName('user.store');
$this->get('/user/{id}/edit', \App\Controllers\UserController::class . ':edit')->setName('user.edit');
$this->post('/user/{id}', \App\Controllers\UserController::class . ':update')->setName('user.update');
$this->get('/user/{id}/delete', \App\Controllers\UserController::class . ':delete')->setName('user.delete');
})->add(\App\Middleware\AdminMiddleware::class);
$this->get('/profile', \App\Controllers\UserController::class . ':profile');
$this->post('/profile/{id}', \App\Controllers\UserController::class . ':profileEdit');
$this->post('/user/{id}/refreshToken', \App\Controllers\UserController::class . ':refreshToken');
$this->get('/user/{id}/config/sharex', \App\Controllers\UserController::class . ':getShareXconfigFile');
$this->get('/profile', \App\Controllers\UserController::class . ':profile')->setName('profile');
$this->post('/profile/{id}', \App\Controllers\UserController::class . ':profileEdit')->setName('profile.update');
$this->post('/user/{id}/refreshToken', \App\Controllers\UserController::class . ':refreshToken')->setName('refreshToken');
$this->get('/user/{id}/config/sharex', \App\Controllers\UserController::class . ':getShareXconfigFile')->setName('config.sharex');
$this->post('/upload/{id}/publish', \App\Controllers\UploadController::class . ':togglePublish');
$this->post('/upload/{id}/unpublish', \App\Controllers\UploadController::class . ':togglePublish');
$this->get('/upload/{id}/raw', \App\Controllers\UploadController::class . ':getRawById')->add(\App\Middleware\AdminMiddleware::class);
$this->post('/upload/{id}/delete', \App\Controllers\UploadController::class . ':delete');
$this->post('/upload/{id}/publish', \App\Controllers\UploadController::class . ':togglePublish')->setName('upload.publish');
$this->post('/upload/{id}/unpublish', \App\Controllers\UploadController::class . ':togglePublish')->setName('upload.unpublish');
$this->get('/upload/{id}/raw', \App\Controllers\UploadController::class . ':getRawById')->add(\App\Middleware\AdminMiddleware::class)->setName('upload.raw');
$this->post('/upload/{id}/delete', \App\Controllers\UploadController::class . ':delete')->setName('upload.delete');
})->add(\App\Middleware\AuthMiddleware::class);
$app->get('/', \App\Controllers\DashboardController::class . ':redirects');
$app->get('/login', \App\Controllers\LoginController::class . ':show');
$app->post('/login', \App\Controllers\LoginController::class . ':login');
$app->map(['GET', 'POST'], '/logout', \App\Controllers\LoginController::class . ':logout');
$app->get('/', \App\Controllers\DashboardController::class . ':redirects')->setName('root');
$app->get('/login', \App\Controllers\LoginController::class . ':show')->setName('login.show');
$app->post('/login', \App\Controllers\LoginController::class . ':login')->setName('login');
$app->map(['GET', 'POST'], '/logout', \App\Controllers\LoginController::class . ':logout')->setName('logout');
$app->post('/upload', \App\Controllers\UploadController::class . ':upload');
$app->post('/upload', \App\Controllers\UploadController::class . ':upload')->setName('upload');
$app->get('/{userCode}/{mediaCode}', \App\Controllers\UploadController::class . ':show');
$app->get('/{userCode}/{mediaCode}/delete/{token}', \App\Controllers\UploadController::class . ':show');
$app->post('/{userCode}/{mediaCode}/delete/{token}', \App\Controllers\UploadController::class . ':deleteByToken');
$app->get('/{userCode}/{mediaCode}/raw', \App\Controllers\UploadController::class . ':showRaw');
$app->get('/{userCode}/{mediaCode}/download', \App\Controllers\UploadController::class . ':download');
$app->get('/{userCode}/{mediaCode}', \App\Controllers\UploadController::class . ':show')->setName('public');
$app->get('/{userCode}/{mediaCode}/delete/{token}', \App\Controllers\UploadController::class . ':show')->setName('public.delete.show');
$app->post('/{userCode}/{mediaCode}/delete/{token}', \App\Controllers\UploadController::class . ':deleteByToken')->setName('public.delete');
$app->get('/{userCode}/{mediaCode}/raw', \App\Controllers\UploadController::class . ':showRaw')->setName('public.raw');
$app->get('/{userCode}/{mediaCode}/download', \App\Controllers\UploadController::class . ':download')->setName('public.download');

View file

@ -28,7 +28,6 @@ $config = array_replace_recursive([
'username' => null,
'password' => null,
],
'lang' => 'en',
], require __DIR__ . '/../config.php');
if (!$config['displayErrorDetails']) {
@ -59,7 +58,7 @@ $container['database'] = function ($container) use (&$config) {
return DB::getInstance();
};
Lang::build($config['lang'], __DIR__. '/../resources/lang/');
Lang::build(substr(@$_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2), __DIR__. '/../resources/lang/');
$container['lang'] = function ($container) {
return Lang::getInstance();
@ -85,6 +84,10 @@ $container['view'] = function ($container) use (&$config) {
$view->getEnvironment()->addGlobal('session', Session::all());
$view->getEnvironment()->addGlobal('lang', $container->get('lang'));
$view->getEnvironment()->addGlobal('PLATFORM_VERSION', PLATFORM_VERSION);
$view->getEnvironment()->addFunction(new Twig_Function('route', 'route'));
$view->getEnvironment()->addFunction(new Twig_Function('lang', 'lang'));
$view->getEnvironment()->addFunction(new Twig_Function('urlFor', 'urlFor'));
return $view;
};

View file

@ -1,6 +1,6 @@
{
"name": "sergix44/xbackbone",
"version": "2.1",
"version": "2.2",
"description": "A lightweight ShareX PHP backend",
"type": "project",
"require": {

View file

@ -2,4 +2,86 @@
return [
'lang' => 'English',
'yes' => 'Yes',
'no' => 'No',
'send' => 'Send',
'no_media' => 'No media found.',
'login.username' => 'Username or E-Mail',
'password' => 'Password',
'login' => 'Login',
'username' => 'Username',
'home' => 'Home',
'users' => 'Users',
'system' => 'System',
'profile' => 'Profile',
'logout' => 'Logout',
'pager.next' => 'Next',
'pager.previous' => 'Previous',
'copy_link' => 'Copy link',
'public.telegram' => 'Share on Telegram',
'public.delete_text' => 'Are you sure you want to delete this item? It will be gone forever!',
'preview' => 'Preview',
'filename' => 'Filename',
'size' => 'Size',
'public' => 'Public',
'owner' => 'Owner',
'date' => 'Date',
'raw' => 'Show raw',
'download' => 'Download',
'delete' => 'Delete',
'publish' => 'Publish',
'hide' => 'Hide',
'files' => 'Files',
'orphaned_files' => 'Orphaned Files',
'theme' => 'Theme',
'click_to_load' => 'Click to load...',
'apply' => 'Apply',
'save' => 'Save',
'used' => 'Used',
'system_info' => 'System Information',
'user.create' => 'Create User',
'user.edit' => 'Edit User',
'is_active' => 'Is active',
'is_admin' => 'Is administrator',
'your_profile' => 'Your Profile',
'token' => 'Token',
'copy' => 'Copy',
'update' => 'Update',
'edit' => 'Edit',
'client_config' => 'Client Configuration',
'user_code' => 'User Code',
'active' => 'Active',
'admin' => 'Admin',
'reg_date' => 'Registration Date',
'none' => 'None',
'open' => 'Open',
'confirm' => 'Confirmation',
'confirm_string' => 'Are you sure?',
'installed' => 'Installation completed successfully!',
'bad_login' => 'Wrong credentials.',
'account_disabled' => 'Your account is disabled.',
'welcome' => 'Welcome, %s!',
'goodbye' => 'Goodbye!',
'token_not_found' => 'Token specified not found.',
'email_required' => 'The email is required.',
'email_taken' => 'The email is already taken.',
'username_required' => 'The username is required.',
'username_taken' => 'The username is already taken.',
'password_required' => 'The password is required.',
'user_created' => 'User "%s" created!',
'user_updated' => 'User "%s" updated!',
'profile_updated' => 'Profile updated successfully!',
'user_deleted' => 'User deleted.',
'cannot_delete' => 'You cannot delete yourself.',
'cannot_demote' => 'You cannot demote yourself',
];

View file

@ -0,0 +1,87 @@
<?php
return [
'lang' => 'Italian',
'yes' => 'Sì',
'no' => 'No',
'send' => 'Invia',
'no_media' => 'Nessun media trovato.',
'login.username' => 'Username o E-Mail',
'password' => 'Password',
'login' => 'Accedi',
'username' => 'Username',
'home' => 'Home',
'users' => 'Utenti',
'system' => 'Sistema',
'profile' => 'Profilo',
'logout' => 'Esci',
'pager.next' => 'Avanti',
'pager.previous' => 'Indietro',
'copy_link' => 'Copia link',
'public.telegram' => 'Condividi su Telegram',
'public.delete_text' => 'Sei sicuro di voler eliminare questo elemento? Andrà perso per sempre!',
'preview' => 'Anteprima',
'filename' => 'Filename',
'size' => 'Dimensione',
'public' => 'Pubblico',
'owner' => 'Proprietario',
'date' => 'Data',
'raw' => 'Vedi raw',
'download' => 'Download',
'delete' => 'Elimina',
'publish' => 'Pubblica',
'hide' => 'Nascondi',
'files' => 'File',
'orphaned_files' => 'File orfani',
'theme' => 'Tema',
'click_to_load' => 'Clicca per caricare...',
'apply' => 'Applica',
'save' => 'Salva',
'used' => 'Usato',
'system_info' => 'Informazioni di Sistema',
'user.create' => 'Crea Utente',
'user.edit' => 'Modifica Utente',
'is_active' => 'Attivo',
'is_admin' => 'Amministratore',
'your_profile' => 'Il tuo profilo',
'token' => 'Token',
'copy' => 'Copia',
'update' => 'Aggiorna',
'edit' => 'Modifica',
'client_config' => 'Configurazione del Client',
'user_code' => 'Codice Utente',
'active' => 'Attivo',
'admin' => 'Amministratore',
'reg_date' => 'Data Registrazione',
'none' => 'Nessuno',
'open' => 'Apri',
'confirm' => 'Conferma',
'confirm_string' => 'Sei sicuro?',
'installed' => 'Installazione completata!',
'bad_login' => 'Credenziali errate.',
'account_disabled' => 'Il tuo account è disattivato.',
'welcome' => 'Benvenuto, %s!',
'goodbye' => 'Arrivederci!',
'token_not_found' => 'Il token specificato non è stato trovato.',
'email_required' => 'Email obbligatoria.',
'email_taken' => 'Email già in uso.',
'username_required' => 'Username obbligatorio.',
'username_taken' => 'Username già in uso.',
'password_required' => 'Password obbligatoria.',
'user_created' => 'L\'utente "%s" è stato creato!',
'user_updated' => 'L\'utente "%s" è stato aggiornato!',
'profile_updated' => 'Profilo aggiornato con successo!',
'user_deleted' => 'Utente rimosso.',
'cannot_delete' => 'Non puoi eliminare te stesso.',
'cannot_demote' => 'Non puoi degradare te stesso. ',
];

View file

@ -0,0 +1 @@
ALTER TABLE `users` ADD UNIQUE INDEX (`email`);

View file

@ -0,0 +1,2 @@
CREATE UNIQUE INDEX IF NOT EXISTS `username_email`
ON `users` (`email`);

View file

@ -1,6 +1,6 @@
{% extends 'base.twig' %}
{% block title %}Login{% endblock %}
{% block title %}{{ lang('login') }}{% endblock %}
{% block head %}
<style>
@ -27,7 +27,7 @@
{% block content %}
<div class="container-fluid">
<form class="form-signin text-center" method="post" action="login">
<form class="form-signin text-center" method="post" action="{{ route('login') }}">
<div class="row">
<div class="col-md-12">
<h1 class="h3 mb-3 font-weight-normal">{{ config.app_name }}</h1>
@ -36,15 +36,15 @@
</div>
<div class="row">
<div class="col-md-12">
<label for="inputEmail" class="sr-only">Username or E-Mail</label>
<input type="text" id="username" class="form-control" placeholder="Username or E-Mail" name="username" required autofocus>
<label for="password" class="sr-only">Password</label>
<input type="password" id="password" class="form-control" placeholder="Password" name="password" required>
<label for="inputEmail" class="sr-only">{{ lang('login.username') }}</label>
<input type="text" id="username" class="form-control" placeholder="{{ lang('login.username') }}" name="username" required autofocus>
<label for="password" class="sr-only">{{ lang('login.password') }}</label>
<input type="password" id="password" class="form-control" placeholder="{{ lang('password') }}" name="password" required>
</div>
</div>
<div class="row">
<div class="col-md-12">
<button class="btn btn-lg btn-primary btn-block" type="submit">Login</button>
<button class="btn btn-lg btn-primary btn-block" type="submit">{{ lang('login') }}</button>
</div>
</div>
</form>

View file

@ -5,17 +5,17 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="A lightweight PHP backend for ShareX">
<link href="{{ config.base_url }}/static/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<link href="{{ config.base_url }}/static/highlightjs/styles/monokai.css" rel="stylesheet">
<link href="{{ config.base_url }}/static/videojs/video-js.min.css" rel="stylesheet">
<link href="{{ config.base_url }}/static/app/app.css" rel="stylesheet">
<script src="{{ config.base_url }}/static/jquery/jquery.min.js"></script>
<script src="{{ config.base_url }}/static/bootstrap/js/bootstrap.bundle.min.js"></script>
<script src="{{ config.base_url }}/static/fontawesome/js/all.min.js"></script>
<script src="{{ config.base_url }}/static/highlightjs/highlight.pack.min.js"></script>
<script src="{{ config.base_url }}/static/clipboardjs/clipboard.min.js"></script>
<script src="{{ config.base_url }}/static/videojs/video.min.js"></script>
<script src="{{ config.base_url }}/static/app/app.js"></script>
<link href="{{ urlFor('/static/bootstrap/css/bootstrap.min.css') }}" rel="stylesheet">
<link href="{{ urlFor('/static/highlightjs/styles/monokai.css') }}" rel="stylesheet">
<link href="{{ urlFor('/static/videojs/video-js.min.css') }}" rel="stylesheet">
<link href="{{ urlFor('/static/app/app.css') }}" rel="stylesheet">
<script src="{{ urlFor('/static/jquery/jquery.min.js') }}"></script>
<script src="{{ urlFor('/static/bootstrap/js/bootstrap.bundle.min.js') }}"></script>
<script src="{{ urlFor('/static/fontawesome/js/all.min.js') }}"></script>
<script src="{{ urlFor('/static/highlightjs/highlight.pack.min.js') }}"></script>
<script src="{{ urlFor('/static/clipboardjs/clipboard.min.js') }}"></script>
<script src="{{ urlFor('/static/videojs/video.min.js') }}"></script>
<script src="{{ urlFor('/static/app/app.js') }}"></script>
<script>hljs.initHighlightingOnLoad();</script>
<script>window.AppConfig = {'base_url': '{{ config.base_url }}'}</script>
{% block head %}{% endblock %}

View file

@ -1,25 +1,25 @@
<nav class="navbar navbar-dark bg-primary navbar-expand-md mb-4 box-shadow">
<div class="container">
<a class="navbar-brand" href="{{ config.base_url }}">{{ config.app_name }}</a>
<a class="navbar-brand" href="{{ route('root') }}">{{ config.app_name }}</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarCollapse" aria-controls="navbarCollapse" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarCollapse">
<ul class="navbar-nav mr-auto">
<li class="nav-item">
<a href="{{ config.base_url }}/home" class="nav-link {{ request.uri.path starts with '/home' ? 'active' }}"><i class="fas fa-fw fa-home"></i>
Home
<a href="{{ route('home') }}" class="nav-link {{ request.uri.path starts with '/home' ? 'active' }}"><i class="fas fa-fw fa-home"></i>
{{ lang('home') }}
</a>
</li>
{% if session.admin %}
<li class="nav-item">
<a href="{{ config.base_url }}/users" class="nav-link {{ request.uri.path starts with '/user' ? 'active' }}"><i class="fas fa-fw fa-users"></i>
Users
<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') }}
</a>
</li>
<li class="nav-item">
<a href="{{ config.base_url }}/system" class="nav-link {{ request.uri.path starts with '/system' ? 'active' }}"><i class="fas fa-fw fa-cog"></i>
System
<a href="{{ route('system') }}" class="nav-link {{ request.uri.path starts with '/system' ? 'active' }}"><i class="fas fa-fw fa-cog"></i>
{{ lang('system') }}
</a>
</li>
{% endif %}
@ -30,10 +30,10 @@
<i class="fas fa-fw fa-user"></i> {{ session.username }}
</a>
<div class="dropdown-menu" aria-labelledby="userDropdown">
<a class="dropdown-item disabled" href="javascript:void(0)">Used: {{ session.used_space }}</a>
<a class="dropdown-item disabled" href="javascript:void(0)">{{ lang('used') }}: {{ session.used_space }}</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="{{ config.base_url }}/profile"><i class="fas fa-fw fa-user"></i> Profile</a>
<a class="dropdown-item" href="{{ config.base_url }}/logout"><i class="fas fa-fw fa-sign-out-alt"></i> Logout</a>
<a class="dropdown-item" href="{{ route('profile') }}"><i class="fas fa-fw fa-user"></i> {{ lang('profile') }}</a>
<a class="dropdown-item" href="{{ route('logout') }}"><i class="fas fa-fw fa-sign-out-alt"></i> {{ lang('logout') }}</a>
</div>
</li>
</ul>

View file

@ -3,22 +3,19 @@
<ul class="pagination justify-content-center">
{% if previous %}
<li class="page-item">
<a class="page-link" href="{{ config.base_url }}/{{ path }}/page/{{ current_page-1 }}"><i class="fas fa-angle-left fa-fw"></i>
Previous</a>
<a class="page-link" href="{{ urlFor('/' ~ path ~ '/page/' ~ (current_page-1)) }}"><i class="fas fa-angle-left fa-fw"></i> {{ lang('pager.previous') }}</a>
</li>
{% else %}
<li class="page-item disabled">
<a class="page-link" href="#"><i class="fas fa-angle-left fa-fw"></i> Previous</a></li>
<a class="page-link" href="#"><i class="fas fa-angle-left fa-fw"></i> {{ lang('pager.previous') }}</a></li>
{% endif %}
{% if next %}
<li class="page-item">
<a class="page-link" href="{{ config.base_url }}/{{ path }}/page/{{ current_page+1 }}">Next
<i class="fas fa-angle-right fa-fw"></i></a>
<a class="page-link" href="{{ urlFor('/' ~ path ~ '/page/' ~ (current_page+1)) }}">{{ lang('pager.next') }} <i class="fas fa-angle-right fa-fw"></i></a>
</li>
{% else %}
<li class="page-item disabled"><a class="page-link" href="#">Next
<i class="fas fa-angle-right fa-fw"></i></a></li>
<li class="page-item disabled"><a class="page-link" href="#">{{ lang('pager.next') }} <i class="fas fa-angle-right fa-fw"></i></a></li>
{% endif %}
</ul>
</nav>

View file

@ -16,12 +16,12 @@
<table class="table table-hover">
<thead>
<tr>
<th>Preview</th>
<th>Filename</th>
<th>Size</th>
<th>Public</th>
<th>Owner</th>
<th>Date</th>
<th>{{ lang('preview') }}</th>
<th>{{ lang('filename') }}</th>
<th>{{ lang('size') }}</th>
<th>{{ lang('public') }}</th>
<th>{{ lang('owner') }}</th>
<th>{{ lang('date') }}</th>
<th></th>
</tr>
</thead>
@ -31,10 +31,10 @@
<td>
{% if media.mimetype starts with 'image' %}
{% if media.username is not null %}
<img src="{{ config.base_url }}/{{ media.user_code }}/{{ media.code }}.{{ media.extension }}/raw?width=128"
<img src="{{ urlFor('/' ~ media.user_code ~ '/' ~ media.code ~ '.' ~ media.extension ~ '/raw?width=128') }}"
class="img-fluid rounded admin-img">
{% else %}
<img src="{{ config.base_url }}/upload/{{ media.id }}/raw" class="img-fluid rounded admin-img">
<img src="{{ route('upload.raw', {'id': media.id}) }}" class="img-fluid rounded admin-img">
{% endif %}
{% else %}
<i class="far fa-file fa-2x"></i>
@ -54,18 +54,19 @@
<td class="text-right">
<div class="btn-group">
{% if media.username is not null %}
<a href="{{ config.base_url }}/{{ media.user_code }}/{{ media.code }}.{{ media.extension }}" class="btn btn-outline-dark" data-toggle="tooltip" title="Open" target="_blank"><i class="fas fa-external-link-alt"></i></a>
<a href="javascript:void(0)" class="btn btn-outline-success btn-clipboard" data-toggle="tooltip" title="Copy link" data-clipboard-text="{{ config.base_url }}/{{ media.user_code }}/{{ media.code }}.{{ media.extension }}"><i class="fas fa-link"></i></a>
<a href="{{ urlFor('/' ~ media.user_code ~ '/' ~ media.code ~ '.' ~ media.extension) }}" class="btn btn-outline-dark" data-toggle="tooltip" title="{{ lang('open') }}" target="_blank"><i class="fas fa-external-link-alt"></i></a>
<a href="javascript:void(0)" class="btn btn-outline-success btn-clipboard" data-toggle="tooltip" title="{{ lang('copy_link') }}" data-clipboard-text="{{ urlFor('/' ~ media.user_code ~ '/' ~ media.code ~ '.' ~ media.extension) }}"><i
class="fas fa-link"></i></a>
{% else %}
<a href="{{ config.base_url }}/upload/{{ media.id }}/raw" class="btn btn-outline-dark" data-toggle="tooltip" title="Raw" target="_blank"><i class="fas fa-external-link-alt"></i></a>
<a href="{{ route('upload.raw', {'id': media.id}) }}" class="btn btn-outline-dark" data-toggle="tooltip" title="{{ lang('raw') }}" target="_blank"><i class="fas fa-external-link-alt"></i></a>
{% endif %}
{% if media.published %}
<a href="javascript:void(0)" class="btn btn-outline-warning publish-toggle" data-toggle="tooltip" title="Unpublish" data-id="{{ media.id }}" data-published="{{ media.published }}"><i class="fas fa-times-circle"></i></a>
<a href="javascript:void(0)" class="btn btn-outline-warning publish-toggle" data-toggle="tooltip" title="{{ lang('hide') }}" data-id="{{ media.id }}" data-published="{{ media.published }}"><i class="fas fa-times-circle"></i></a>
{% else %}
<a href="javascript:void(0)" class="btn btn-outline-info publish-toggle" data-toggle="tooltip" title="Publish" data-id="{{ media.id }}" data-published="{{ media.published }}"><i class="fas fa-check-circle"></i></a>
<a href="javascript:void(0)" class="btn btn-outline-info publish-toggle" data-toggle="tooltip" title="{{ lang('publish') }}" data-id="{{ media.id }}" data-published="{{ media.published }}"><i class="fas fa-check-circle"></i></a>
{% endif %}
<a href="javascript:void(0)" class="btn btn-outline-danger media-delete" data-link="{{ config.base_url }}/upload/{{ media.id }}/delete" data-id="{{ media.id }}" data-toggle="tooltip" title="Delete"><i class="fas fa-trash"></i></a>
<a href="javascript:void(0)" class="btn btn-outline-danger media-delete" data-link="{{ route('upload.delete', {'id': media.id}) }}" data-id="{{ media.id }}" data-toggle="tooltip" title="{{ lang('delete') }}"><i class="fas fa-trash"></i></a>
</div>
</td>
</tr>
@ -79,7 +80,7 @@
</div>
</div>
{% else %}
<div class="text-center text-muted"><i>No medias found.</i></div>
<div class="text-center text-muted"><i>{{ lang('no_media') }}</i></div>
{% endif %}
</div>
{% include 'comp/footer.twig' %}

View file

@ -1,56 +1,54 @@
{% extends 'base.twig' %}
{% block title %}Home{% endblock %}
{% block title %}{{ lang('home') }}{% endblock %}
{% block content %}
{% include 'comp/navbar.twig' %}
<div class="container">
{% include 'comp/alert.twig' %}
{% if medias|length > 0 %}
<div class="card box-shadow">
<div class="card-body">
{% include 'comp/pager.twig' with {'path': 'home'} %}
<div class="row">
{% for media in medias %}
<div class="col-md-4" id="media_{{ media.id }}">
<div class="card mb-4 box-shadow">
{% if media.mimetype starts with 'image' %}
<a href="{{ config.base_url }}/{{ media.user_code }}/{{ media.code }}.{{ media.extension }}" target="_blank">
<img class="card-img-top user-img" src="{{ config.base_url }}/{{ media.user_code }}/{{ media.code }}.{{ media.extension }}/raw?width=348&height=192" alt="{{ media.filename }}">
</a>
{% else %}
<a href="{{ config.base_url }}/{{ media.user_code }}/{{ media.code }}.{{ media.extension }}" target="_blank">
<div class="card-header text-center"><i class="far fa-file fa-10x"></i></div>
</a>
{% endif %}
<div class="card-body">
<p class="card-text">{{ media.filename }}<small class="float-right">{{ media.size }}</small></p>
<div class="d-flex justify-content-between align-items-center">
<div class="btn-group">
<button type="button" class="btn btn-sm btn-outline-success btn-clipboard" data-toggle="tooltip" title="Copy link" data-clipboard-text="{{ config.base_url }}/{{ media.user_code }}/{{ media.code }}.{{ media.extension }}">
<i class="fas fa-link"></i>
</button>
{% if media.published %}
<a href="javascript:void(0)" class="btn btn-sm btn-outline-warning publish-toggle" data-toggle="tooltip" title="Unpublish" data-id="{{ media.id }}" data-published="{{ media.published }}"><i class="fas fa-times-circle"></i></a>
{% else %}
<a href="javascript:void(0)" class="btn btn-sm btn-outline-info publish-toggle" data-toggle="tooltip" title="Publish" data-id="{{ media.id }}" data-published="{{ media.published }}"><i class="fas fa-check-circle"></i></a>
{% endif %}
<button type="button" class="btn btn-sm btn-outline-danger media-delete" data-link="{{ config.base_url }}/upload/{{ media.id }}/delete" data-id="{{ media.id }}" data-toggle="tooltip" title="Delete">
<i class="fas fa-trash"></i>
</button>
</div>
<small class="text-muted">{{ media.timestamp|date("d/m/Y H:i:s") }}</small>
</div>
{% include 'comp/pager.twig' with {'path': 'home'} %}
<div class="row">
{% for media in medias %}
<div class="col-md-4" id="media_{{ media.id }}">
<div class="card mb-4 box-shadow">
{% if media.mimetype starts with 'image' %}
<a href="{{ urlFor('/' ~ media.user_code ~ '/' ~ media.code ~ '.' ~ media.extension) }}" target="_blank">
<img class="card-img-top user-img" src="{{ urlFor('/' ~ media.user_code ~ '/' ~ media.code ~ '.' ~ media.extension ~ '/raw?width=348&height=192') }}" alt="{{ media.filename }}">
</a>
{% else %}
<a href="{{ urlFor('/' ~ media.user_code ~ '/' ~ media.code ~ '.' ~ media.extension) }}" target="_blank">
<div class="card-header text-center"><i class="far fa-file fa-10x"></i></div>
</a>
{% endif %}
<div class="card-body">
<p class="card-text">{{ media.filename }}
<small class="float-right">{{ media.size }}</small>
</p>
<div class="d-flex justify-content-between align-items-center">
<div class="btn-group">
<button type="button" 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) }}">
<i class="fas fa-link"></i>
</button>
{% if media.published %}
<a href="javascript:void(0)" class="btn btn-sm btn-outline-warning publish-toggle" data-toggle="tooltip" title="{{ lang('hide') }}" data-id="{{ media.id }}" data-published="{{ media.published }}"><i class="fas fa-times-circle"></i></a>
{% else %}
<a href="javascript:void(0)" class="btn btn-sm btn-outline-info publish-toggle" data-toggle="tooltip" title="{{ lang('publish') }}" data-id="{{ media.id }}" data-published="{{ media.published }}"><i class="fas fa-check-circle"></i></a>
{% endif %}
<button type="button" class="btn btn-sm btn-outline-danger media-delete" data-link="{{ route('upload.delete', {'id': media.id}) }}" data-id="{{ media.id }}" data-toggle="tooltip" title="{{ lang('delete') }}">
<i class="fas fa-trash"></i>
</button>
</div>
<small class="text-muted">{{ media.timestamp|date("d/m/Y H:i:s") }}</small>
</div>
</div>
{% endfor %}
</div>
</div>
{% include 'comp/pager.twig' with {'path': 'home'} %}
</div>
{% endfor %}
</div>
{% include 'comp/pager.twig' with {'path': 'home'} %}
{% else %}
<div class="text-center text-muted"><i>No medias found.</i></div>
<div class="text-center text-muted"><i>{{ lang('no_media') }}</i></div>
{% endif %}
</div>
{% include 'comp/footer.twig' %}

View file

@ -1,6 +1,6 @@
{% extends 'base.twig' %}
{% block title %}System{% endblock %}
{% block title %}{{ lang('system') }}{% endblock %}
{% block content %}
{% include 'comp/navbar.twig' %}
@ -12,7 +12,7 @@
<div class="rotate">
<i class="fas fa-users fa-3x"></i>
</div>
<h6 class="text-uppercase">Users</h6>
<h6 class="text-uppercase">{{ lang('users') }}</h6>
<h1 class="display-4">{{ usersCount }}</h1>
</div>
</div>
@ -23,7 +23,7 @@
<div class="rotate">
<i class="fas fa-weight fa-3x"></i>
</div>
<h6 class="text-uppercase">Size</h6>
<h6 class="text-uppercase">{{ lang('size') }}</h6>
<h1 class="display-4">{{ totalSize }}</h1>
</div>
</div>
@ -34,7 +34,7 @@
<div class="rotate">
<i class="fas fa-upload fa-3x"></i>
</div>
<h6 class="text-uppercase">Files</h6>
<h6 class="text-uppercase">{{ lang('files') }}</h6>
<h1 class="display-4">{{ mediasCount }}</h1>
</div>
</div>
@ -45,7 +45,7 @@
<div class="rotate">
<i class="fas fa-unlink fa-3x"></i>
</div>
<h6 class="text-uppercase">Orphaned files</h6>
<h6 class="text-uppercase">{{ lang('orphaned_files') }}</h6>
<h1 class="display-4">{{ orphanFilesCount }}</h1>
</div>
</div>
@ -54,20 +54,20 @@
<div class="row">
<div class="col-md-8">
<div class="card box-shadow">
<div class="card-header"><i class="fas fa-paint-brush fa-fw"></i> Theme</div>
<div class="card-header"><i class="fas fa-paint-brush fa-fw"></i> {{ lang('theme') }}</div>
<div class="card-body">
<form method="post" action="{{ config.base_url }}/system/theme/apply">
<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>Click to load...</option>
<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" id="themes-apply" disabled>
<i class="fas fa-save fa-fw"></i> Apply
<i class="fas fa-save fa-fw"></i> {{ lang('apply') }}
</button>
</div>
</div>
@ -77,7 +77,7 @@
</div>
<div class="col-md-4">
<div class="card box-shadow">
<div class="card-header"><i class="fas fa-cog fa-fw"></i> System Information</div>
<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>
<ul>

View file

@ -5,19 +5,19 @@
{% block content %}
<nav class="navbar navbar-dark bg-primary navbar-expand-md mb-4">
<div class="container-fluid">
<a class="navbar-brand" href="{{ config.base_url }}">{{ config.app_name }}</a>
<a class="navbar-brand" href="{{ route('root') }}">{{ config.app_name }}</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarCollapse" aria-controls="navbarCollapse" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarCollapse">
<div class="ml-auto">
<a href="javascript:void(0)" class="btn btn-success my-2 my-sm-0 btn-clipboard" data-toggle="tooltip" title="Copy link" data-clipboard-text="{{ config.base_url }}/{{ media.user_code }}/{{ media.code }}.{{ extension }}"><i class="fas fa-link fa-lg fa-fw"></i></a>
<a href="javascript:void(0)" class="btn btn-info my-2 my-sm-0" data-html="true" title="Telegram Message" data-toggle="popover" data-placement="bottom"
<a href="javascript:void(0)" class="btn btn-success my-2 my-sm-0 btn-clipboard" data-toggle="tooltip" title="{{ lang('copy_link') }}" data-clipboard-text="{{ urlFor('/' ~ media.user_code ~ '/' ~ media.code ~ '.' ~ extension) }}"><i class="fas fa-link fa-lg fa-fw"></i></a>
<a href="javascript:void(0)" class="btn btn-info my-2 my-sm-0" data-html="true" title="{{ lang('public.telegram') }}" data-toggle="popover" data-placement="bottom"
data-content='<input type="text" class="form-control mb-2" id="telegram-share-text" onclick="this.select()" value="{{ media.filename }}">
<button type="button" class="btn btn-info btn-block" id="telegram-share-button" onclick="app.telegramShare()" data-url="https://telegram.me/share/url?url={{ config.base_url }}/{{ media.user_code }}/{{ media.code }}.{{ extension }}&text="><i class="fab fa-telegram-plane fa-lg fa-fw"></i> Send</button>'><i
<button type="button" class="btn btn-info btn-block" id="telegram-share-button" onclick="app.telegramShare()" data-url="https://telegram.me/share/url?url={{ urlFor('/' ~ media.user_code ~ '/' ~ media.code ~ '.' ~ extension) }}&text="><i class="fab fa-telegram-plane fa-lg fa-fw"></i> {{ lang("send") }}</button>'><i
class="fab fa-telegram-plane fa-lg fa-fw"></i></a>
<a href="{{ config.base_url }}/{{ media.user_code }}/{{ media.code }}.{{ extension }}/raw" class="btn btn-light my-2 my-sm-0" data-toggle="tooltip" title="Show raw"><i class="fas fa-file-alt fa-lg fa-fw"></i></a>
<a href="{{ config.base_url }}/{{ media.user_code }}/{{ media.code }}.{{ extension }}/download" class="btn btn-danger my-2 my-sm-0" data-toggle="tooltip" title="Download"><i class="fas fa-cloud-download-alt fa-lg fa-fw"></i></a>
<a href="{{ urlFor('/' ~ media.user_code ~ '/' ~ media.code ~ '.' ~ extension ~ '/raw') }}" class="btn btn-light my-2 my-sm-0" data-toggle="tooltip" title="{{ lang('raw') }}"><i class="fas fa-file-alt fa-lg fa-fw"></i></a>
<a href="{{ urlFor('/' ~ media.user_code ~ '/' ~ media.code ~ '.' ~ extension ~ '/download') }}" class="btn btn-danger my-2 my-sm-0" data-toggle="tooltip" title="{{ lang('download') }}"><i class="fas fa-cloud-download-alt fa-lg fa-fw"></i></a>
</div>
</div>
</div>
@ -27,12 +27,12 @@
<div class="row ml-auto mr-auto">
<div class="col-md-12 justify-content-center">
{% if delete_token is not null %}
<form method="post" action="{{ config.base_url }}/{{ media.user_code }}/{{ media.code }}.{{ extension }}/delete/{{ delete_token }}">
<form method="post" action="{{ urlFor('/' ~ media.user_code ~ '/' ~ media.code ~ '.' ~ extension ~ '/delete/' ~ delete_token) }}">
<div class="text-center mb-4">
<p>Are you sure you want to delete this item? It will be gone <b>forever</b>!</p>
<p>{{ lang('public.delete_text') }}</p>
<div class="btn-group">
<button type="submit" class="btn btn-danger"><i class="fas fa-trash"></i> Yes</button>
<a href="{{ config.base_url }}/{{ media.user_code }}/{{ media.code }}.{{ extension }}" class="btn btn-secondary">No</a>
<button type="submit" class="btn btn-danger"><i class="fas fa-trash"></i> {{ lang('yes') }}</button>
<a href="{{ urlFor('/' ~ media.user_code ~ '/' ~ media.code ~ '.' ~ extension) }}" class="btn btn-secondary">{{ lang('no') }}</a>
</div>
</div>
</form>
@ -40,7 +40,7 @@
{% if type starts with 'image' %}
<div class="row mb-2">
<div class="col-md-12">
<img src="{{ config.base_url }}/{{ media.user_code }}/{{ media.code }}.{{ extension }}/raw" class="img-thumbnail rounded mx-auto d-block" alt="{{ media.filename }}">
<img src="{{ urlFor('/' ~ media.user_code ~ '/' ~ media.code ~ '.' ~ extension ~ '/raw') }}" class="img-thumbnail rounded mx-auto d-block" alt="{{ media.filename }}">
</div>
</div>
<div class="row">
@ -62,9 +62,9 @@
{% elseif type starts with 'video' %}
<div class="video-content">
<video class="video-js vjs-fluid vjs-big-play-centered" data-setup='{"controls": true, "autoplay": true, "preload": "auto"}'>
<source src="{{ config.base_url }}/{{ media.user_code }}/{{ media.code }}.{{ extension }}/raw" type="{{ type }}">
<source src="{{ urlFor('/' ~ media.user_code ~ '/' ~ media.code ~ '.' ~ extension ~ '/raw') }}" type="{{ type }}">
Your browser does not support HTML5 video.
<a href="{{ config.base_url }}/{{ media.user_code }}/{{ media.code }}.{{ extension }}/download" class="btn btn-dark btn-lg"><i class="fas fa-cloud-download-alt fa-fw"></i> Download</a>
<a href="{{ urlFor('/' ~ media.user_code ~ '/' ~ media.code ~ '.' ~ extension ~ '/download') }}" class="btn btn-dark btn-lg"><i class="fas fa-cloud-download-alt fa-fw"></i> Download</a>
</video>
</div>
{% else %}
@ -81,7 +81,7 @@
</div>
<div class="row">
<div class="col-md-12">
<a href="{{ config.base_url }}/{{ media.user_code }}/{{ media.code }}.{{ extension }}/download" class="btn btn-dark btn-lg"><i class="fas fa-cloud-download-alt fa-fw"></i> Download</a>
<a href="{{ urlFor('/' ~ media.user_code ~ '/' ~ media.code ~ '.' ~ extension ~ '/download') }}" class="btn btn-dark btn-lg"><i class="fas fa-cloud-download-alt fa-fw"></i> Download</a>
</div>
</div>
</div>

View file

@ -1,6 +1,6 @@
{% extends 'base.twig' %}
{% block title %}Create User{% endblock %}
{% block title %}{{ lang('user.create') }}{% endblock %}
{% block content %}
{% include 'comp/navbar.twig' %}
@ -9,25 +9,25 @@
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card box-shadow">
<div class="card-header">Create User</div>
<div class="card-header">{{ lang('user.create') }}</div>
<div class="card-body">
<form method="post" action="{{ config.base_url }}/user/create">
<form method="post" action="{{ route('user.store') }}">
<div class="form-group row">
<label for="email" class="col-sm-2 col-form-label">Email</label>
<label for="email" class="col-sm-2 col-form-label">E-Mail</label>
<div class="col-sm-10">
<input type="email" class="form-control" id="email" placeholder="email@example.com" name="email" autocomplete="off" required>
</div>
</div>
<div class="form-group row">
<label for="email" class="col-sm-2 col-form-label">Username</label>
<label for="email" class="col-sm-2 col-form-label">{{ lang('username') }}</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="username" placeholder="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 class="form-group row">
<label for="password" class="col-sm-2 col-form-label">Password</label>
<label for="password" class="col-sm-2 col-form-label">{{ lang('password') }}</label>
<div class="col-sm-10">
<input type="password" class="form-control" id="password" placeholder="Password" name="password" autocomplete="off" required>
<input type="password" class="form-control" id="password" placeholder="{{ lang('password') }}" name="password" autocomplete="off" required>
</div>
</div>
<div class="form-group row">
@ -36,7 +36,7 @@
<div class="form-check">
<input class="form-check-input" type="checkbox" id="is_admin" name="is_admin">
<label class="form-check-label" for="is_admin">
Is admin
{{ lang('is_admin') }}
</label>
</div>
</div>
@ -47,7 +47,7 @@
<div class="form-check">
<input class="form-check-input" type="checkbox" id="is_active" name="is_active" checked>
<label class="form-check-label" for="is_active">
Is active
{{ lang('is_active') }}
</label>
</div>
</div>
@ -55,7 +55,7 @@
<div class="form-group row justify-content-md-end">
<div class="col-sm-10">
<button type="submit" class="btn btn-outline-success">
<i class="fas fa-save fa-fw"></i> Create
<i class="fas fa-save fa-fw"></i> {{ lang('save') }}
</button>
</div>
</div>

View file

@ -1,6 +1,6 @@
{% extends 'base.twig' %}
{% block title %}{{ profile ? 'Your Profile' : 'Edit User' }}{% endblock %}
{% block title %}{{ profile ? lang('your_profile') : lang('user.edit') }}{% endblock %}
{% block content %}
{% include 'comp/navbar.twig' %}
@ -10,10 +10,10 @@
<div class="col-md-8">
<div class="card box-shadow">
{% if not profile %}
<div class="card-header">Edit User</div>
<div class="card-header">{{ lang('user.edit') }}</div>
{% endif %}
<div class="card-body">
<form method="post" action="{{ config.base_url }}/{{ profile ? 'profile' : 'user' }}/{{ user.id }}">
<form method="post" action="{{ route( (profile ? 'profile.update' : 'user.update'), {'id': user.id}) }}">
<div class="form-group row">
<label for="email" class="col-sm-2 col-form-label">Email</label>
<div class="col-sm-10">
@ -21,37 +21,37 @@
</div>
</div>
<div class="form-group row">
<label for="username" class="col-sm-2 col-form-label">Username</label>
<label for="username" class="col-sm-2 col-form-label">{{ lang('username') }}</label>
<div class="col-sm-10">
{% if profile %}
<input type="text" class="form-control disabled" id="username" value="{{ user.username }}" readonly>
{% else %}
<input type="text" class="form-control" id="username" placeholder="Username" name="username" value="{{ user.username }}" autocomplete="off" required>
<input type="text" class="form-control" id="username" placeholder="{{ lang('username') }}" name="username" value="{{ user.username }}" autocomplete="off" required>
{% endif %}
</div>
</div>
<div class="form-group row">
<label for="password" class="col-sm-2 col-form-label">Password</label>
<label for="password" class="col-sm-2 col-form-label">{{ lang('password') }}</label>
<div class="col-sm-10">
<input type="password" class="form-control" id="password" placeholder="Password" name="password" autocomplete="off">
<input type="password" class="form-control" id="password" placeholder="{{ lang('password') }}" name="password" autocomplete="off">
</div>
</div>
<div class="form-group row">
<label for="token" class="col-sm-2 col-form-label">Token</label>
<label for="token" class="col-sm-2 col-form-label">{{ lang('token') }}</label>
<div class="col-sm-10">
<div class="input-group">
<input type="text" id="token" class="form-control" value="{{ user.token }}" readonly>
<div class="input-group-append">
<button class="btn btn-outline-success btn-clipboard" type="button" data-clipboard-target="#token"><i class="fas fa-fw fa-copy"></i> Copy</button>
<button class="btn btn-outline-primary refresh-token" data-id="{{ user.id }}" type="button"><i class="fas fa-fw fa-sync"></i> Update</button>
<button class="btn btn-outline-success btn-clipboard" type="button" data-clipboard-target="#token"><i class="fas fa-fw fa-copy"></i> {{ lang('copy') }}</button>
<button class="btn btn-outline-primary refresh-token" data-id="{{ user.id }}" type="button"><i class="fas fa-fw fa-sync"></i> {{ lang('update') }}</button>
</div>
</div>
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">Client Config</label>
<label class="col-sm-2 col-form-label">{{ lang('client_config') }}</label>
<div class="col-sm-10">
<a href="{{ config.base_url }}/user/{{ user.id }}/config/sharex" class="btn btn-lg btn-outline-dark"><i class="fas fa-fw fa-download"></i> ShareX Config File</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 File</a>
</div>
</div>
{% if not profile %}
@ -61,7 +61,7 @@
<div class="form-check">
<input class="form-check-input" type="checkbox" id="is_admin" name="is_admin" {{ user.is_admin ? 'checked' }}>
<label class="form-check-label" for="is_admin">
Is admin
{{ lang('is_admin') }}
</label>
</div>
</div>
@ -72,7 +72,7 @@
<div class="form-check">
<input class="form-check-input" type="checkbox" id="is_active" name="is_active" {{ user.active ? 'checked' }}>
<label class="form-check-label" for="is_active">
Is active
{{ lang('is_active') }}
</label>
</div>
</div>
@ -81,7 +81,7 @@
<div class="form-group row justify-content-md-end">
<div class="col-sm-10">
<button type="submit" class="btn btn-outline-info">
<i class="fas fa-save fa-fw"></i> Save
<i class="fas fa-save fa-fw"></i> {{ lang('save') }}
</button>
</div>
</div>

View file

@ -1,6 +1,6 @@
{% extends 'base.twig' %}
{% block title %}Users{% endblock %}
{% block title %}{{ lang('users') }}{% endblock %}
{% block content %}
{% include 'comp/navbar.twig' %}
@ -9,7 +9,7 @@
<div class="card box-shadow">
<div class="card-body">
<div class="text-right">
<a href="{{ config.base_url }}/user/create" class="btn btn-outline-success mb-3"><i class="fas fa-plus"></i> Add User</a>
<a href="{{ route('user.create') }}" class="btn btn-outline-success mb-3"><i class="fas fa-plus"></i> {{ lang('user.create') }}</a>
</div>
<div class="table-responsive">
<table class="table table-hover">
@ -17,12 +17,12 @@
<tr>
<th>ID</th>
<th>Email</th>
<th>Username</th>
<th>User Code</th>
<th>Token</th>
<th>Active</th>
<th>Admin</th>
<th>Registration Date</th>
<th>{{ lang('username') }}</th>
<th>{{ lang('user_code') }}</th>
<th>{{ lang('token') }}</th>
<th>{{ lang('active') }}</th>
<th>{{ lang('admin') }}</th>
<th>{{ lang('reg_date') }}</th>
<th></th>
</tr>
</thead>
@ -33,10 +33,10 @@
<td>{{ user.email }}</td>
<td>{{ user.username }}</td>
<td>
<pre>{{ user.user_code|default('None') }}</pre>
<pre>{{ user.user_code|default(lang('none')) }}</pre>
</td>
<td>
<pre>{{ user.token|default('None') }}</pre>
<pre>{{ user.token|default(lang('none')) }}</pre>
</td>
<td>
{% if user.active %}
@ -57,8 +57,8 @@
</td>
<td class="text-right">
<div class="btn-group">
<a href="{{ config.base_url }}/user/{{ user.id }}/edit" class="btn btn-outline-warning" data-toggle="tooltip" title="Edit"><i class="fas fa-pencil-alt"></i></a>
<a href="#" class="btn btn-outline-danger user-delete" data-link="{{ config.base_url }}/user/{{ user.id }}/delete" data-toggle="tooltip" title="Delete"><i class="fas fa-trash"></i></a>
<a href="{{ route('user.edit', {'id': user.id}) }}" class="btn btn-outline-warning" data-toggle="tooltip" title="{{ lang('edit') }}"><i class="fas fa-pencil-alt"></i></a>
<a href="#" class="btn btn-outline-danger user-delete" data-link="{{ route('user.delete', {'id': user.id}) }}" data-toggle="tooltip" title="{{ lang('delete') }}"><i class="fas fa-trash"></i></a>
</div>
</td>
</tr>
@ -74,17 +74,17 @@
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Confirm</h5>
<h5 class="modal-title">{{ lang('confirm') }}</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<p>Are you sure?</p>
<p>{{ lang('confirm_string') }}</p>
</div>
<div class="modal-footer">
<a href="#" class="btn btn-danger" id="modalDelete-link"><i class="fas fa-trash fa-fw"></i> Delete</a>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<a href="#" class="btn btn-danger" id="modalDelete-link"><i class="fas fa-trash fa-fw"></i> {{ lang('delete') }}</a>
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{ lang('no') }}</button>
</div>
</div>
</div>