diff --git a/app/Controllers/AdminController.php b/app/Controllers/AdminController.php index 371404e..263ab15 100644 --- a/app/Controllers/AdminController.php +++ b/app/Controllers/AdminController.php @@ -34,9 +34,10 @@ class AdminController extends Controller $totalSize += $filesystem->getSize($media->storage_path); } - $registerEnabled = $this->database->query('SELECT `value` FROM `settings` WHERE `key` = \'register_enabled\'')->fetch()->value; - $hideByDefault = $this->database->query('SELECT `value` FROM `settings` WHERE `key` = \'hide_by_default\'')->fetch()->value; - $copyUrl = $this->database->query('SELECT `value` FROM `settings` WHERE `key` = \'copy_url_behavior\'')->fetch()->value; + $registerEnabled = $this->database->query('SELECT `value` FROM `settings` WHERE `key` = \'register_enabled\'')->fetch()->value ?? 'off'; + $hideByDefault = $this->database->query('SELECT `value` FROM `settings` WHERE `key` = \'hide_by_default\'')->fetch()->value ?? 'off'; + $copyUrl = $this->database->query('SELECT `value` FROM `settings` WHERE `key` = \'copy_url_behavior\'')->fetch()->value ?? 'off'; + $defaultUserQuota = $this->database->query('SELECT `value` FROM `settings` WHERE `key` = \'default_user_quota\'')->fetch()->value ?? '1G'; return view()->render($response, 'dashboard/system.twig', [ 'usersCount' => $usersCount, @@ -52,6 +53,7 @@ class AdminController extends Controller 'register_enabled' => $registerEnabled, 'hide_by_default' => $hideByDefault, 'copy_url_behavior' => $copyUrl, + 'default_user_quota' => $defaultUserQuota, ]); } diff --git a/app/Controllers/LoginController.php b/app/Controllers/Auth/LoginController.php similarity index 84% rename from app/Controllers/LoginController.php rename to app/Controllers/Auth/LoginController.php index db17443..46fb47a 100644 --- a/app/Controllers/LoginController.php +++ b/app/Controllers/Auth/LoginController.php @@ -1,20 +1,21 @@ render($response, 'auth/login.twig'); + $registerEnabled = $this->database->query('SELECT `value` FROM `settings` WHERE `key` = \'register_enabled\'')->fetch()->value ?? 'off'; + + return view()->render($response, 'auth/login.twig', [ + 'register_enabled' => $registerEnabled, + ]); } /** - * @param Request $request - * @param Response $response - * - * @throws \Exception + * @param Request $request + * @param Response $response * * @return Response + * @throws \Exception + * */ public function login(Request $request, Response $response): Response { @@ -74,8 +79,8 @@ class LoginController extends Controller } /** - * @param Request $request - * @param Response $response + * @param Request $request + * @param Response $response * * @return Response */ diff --git a/app/Controllers/Auth/PasswordRecoveryController.php b/app/Controllers/Auth/PasswordRecoveryController.php new file mode 100644 index 0000000..b1a0065 --- /dev/null +++ b/app/Controllers/Auth/PasswordRecoveryController.php @@ -0,0 +1,12 @@ +session->get('logged', false)) { + return redirect($response, route('home')); + } + + $registerEnabled = $this->database->query('SELECT `value` FROM `settings` WHERE `key` = \'register_enabled\'')->fetch()->value ?? 'off'; + if ($registerEnabled === 'off') { + throw new HttpNotFoundException($request); + } + + return view()->render($response, 'auth/register.twig'); + } + + /** + * @param Request $request + * @param Response $response + * @return Response + * @throws HttpNotFoundException + * @throws \Exception + */ + public function register(Request $request, Response $response): Response + { + if ($this->session->get('logged', false)) { + return redirect($response, route('home')); + } + + $registerEnabled = $this->database->query('SELECT `value` FROM `settings` WHERE `key` = \'register_enabled\'')->fetch()->value ?? 'off'; + if ($registerEnabled === 'off') { + throw new HttpNotFoundException($request); + } + + if (param($request, 'email') === null && !filter_var(param($request, 'email'), FILTER_VALIDATE_EMAIL)) { + $this->session->alert(lang('email_required'), 'danger'); + + return redirect($response, route('register.show')); + } + + if ($this->database->query('SELECT COUNT(*) AS `count` FROM `users` WHERE `email` = ?', param($request, 'email'))->fetch()->count > 0) { + $this->session->alert(lang('email_taken'), 'danger'); + + return redirect($response, route('register.show')); + } + + if (param($request, 'username') === null) { + $this->session->alert(lang('username_required'), 'danger'); + + return redirect($response, route('register.show')); + } + + if (param($request, 'password') === null) { + $this->session->alert(lang('password_required'), 'danger'); + + return redirect($response, route('register.show')); + } + + if ($this->database->query('SELECT COUNT(*) AS `count` FROM `users` WHERE `username` = ?', param($request, 'username'))->fetch()->count > 0) { + $this->session->alert(lang('username_taken'), 'danger'); + + return redirect($response, route('register.show')); + } + + do { + $userCode = humanRandomString(5); + } while ($this->database->query('SELECT COUNT(*) AS `count` FROM `users` WHERE `user_code` = ?', $userCode)->fetch()->count > 0); + + $token = $this->generateUserUploadToken(); + $activateToken = bin2hex(random_bytes(16)); + + $this->database->query('INSERT INTO `users`(`email`, `username`, `password`, `is_admin`, `active`, `user_code`, `token`, `activate_token`) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', [ + param($request, 'email'), + param($request, 'username'), + password_hash(param($request, 'password'), PASSWORD_DEFAULT), + 0, + 0, + $userCode, + $token, + $activateToken, + ]); + + $this->session->alert(lang('register_success', [param($request, 'username')]), 'success'); + $this->logger->info('New user registered.', [array_diff_key($request->getParsedBody(), array_flip(['password']))]); + + return redirect($response, route('login.show')); + } + + /** + * @param Request $request + * @param Response $response + * @param string $activateToken + * @return Response + */ + public function activateUser(Request $request, Response $response, string $activateToken): Response + { + + } +} \ No newline at end of file diff --git a/app/Controllers/Controller.php b/app/Controllers/Controller.php index 15baf91..f6031b6 100644 --- a/app/Controllers/Controller.php +++ b/app/Controllers/Controller.php @@ -126,4 +126,17 @@ abstract class Controller ]); } } + + + /** + * @return string + */ + protected function generateUserUploadToken(): string + { + do { + $token = 'token_'.md5(uniqid('', true)); + } while ($this->database->query('SELECT COUNT(*) AS `count` FROM `users` WHERE `token` = ?', $token)->fetch()->count > 0); + + return $token; + } } diff --git a/app/Controllers/DashboardController.php b/app/Controllers/DashboardController.php index b280152..2ebbbae 100644 --- a/app/Controllers/DashboardController.php +++ b/app/Controllers/DashboardController.php @@ -58,7 +58,7 @@ class DashboardController extends Controller ->search(param($request, 'search', null)) ->run($page); - $copyUrl = $this->database->query('SELECT `value` FROM `settings` WHERE `key` = \'copy_url_behavior\'')->fetch()->value; + $copyUrl = $this->database->query('SELECT `value` FROM `settings` WHERE `key` = \'copy_url_behavior\'')->fetch()->value ?? 'off'; return view()->render( $response, diff --git a/app/Controllers/MediaController.php b/app/Controllers/MediaController.php index 9f09e01..f4697b8 100644 --- a/app/Controllers/MediaController.php +++ b/app/Controllers/MediaController.php @@ -66,7 +66,7 @@ class MediaController extends Controller throw new HttpNotFoundException($request); } - $copyUrl = $this->database->query('SELECT `value` FROM `settings` WHERE `key` = \'copy_url_behavior\'')->fetch()->value; + $copyUrl = $this->database->query('SELECT `value` FROM `settings` WHERE `key` = \'copy_url_behavior\'')->fetch()->value ?? 'off'; return view()->render($response, 'upload/public.twig', [ 'delete_token' => $token, diff --git a/app/Controllers/SettingController.php b/app/Controllers/SettingController.php index 02d9fa9..245d72b 100644 --- a/app/Controllers/SettingController.php +++ b/app/Controllers/SettingController.php @@ -16,8 +16,14 @@ class SettingController extends Controller */ public function saveSettings(Request $request, Response $response): Response { + if (!preg_match('/[0-9]+[K|M|G|T]/i', param($request, 'default_user_quota', '1G'))) { + $this->session->alert(lang('invalid_quota', 'danger')); + return redirect($response, route('system')); + } + $this->updateSetting('register_enabled', param($request, 'register_enabled', 'off')); $this->updateSetting('hide_by_default', param($request, 'hide_by_default', 'off')); + $this->updateSetting('default_user_quota', param($request, 'default_user_quota', '1G')); $this->updateSetting('copy_url_behavior', param($request, 'copy_url_behavior') === null ? 'default' : 'raw'); $this->applyTheme($request); diff --git a/app/Controllers/UploadController.php b/app/Controllers/UploadController.php index baef51a..1e9c6dc 100644 --- a/app/Controllers/UploadController.php +++ b/app/Controllers/UploadController.php @@ -101,7 +101,7 @@ class UploadController extends Controller } while ($this->database->query('SELECT COUNT(*) AS `count` FROM `uploads` WHERE `code` = ?', $code)->fetch()->count > 0); $published = 1; - if ($this->database->query('SELECT `value` FROM `settings` WHERE `key` = \'hide_by_default\'')->fetch()->value === 'on') { + if (($this->database->query('SELECT `value` FROM `settings` WHERE `key` = \'hide_by_default\'')->fetch()->value ?? 'off') === 'on') { $published = 0; } diff --git a/app/Controllers/UserController.php b/app/Controllers/UserController.php index b9f21f4..9ff7405 100644 --- a/app/Controllers/UserController.php +++ b/app/Controllers/UserController.php @@ -62,7 +62,7 @@ class UserController extends Controller */ public function store(Request $request, Response $response): Response { - if (param($request, 'email') === null) { + if (param($request, 'email') === null && !filter_var(param($request, 'email'), FILTER_VALIDATE_EMAIL)) { $this->session->alert(lang('email_required'), 'danger'); return redirect($response, route('user.create')); @@ -96,7 +96,7 @@ class UserController extends Controller $userCode = humanRandomString(5); } while ($this->database->query('SELECT COUNT(*) AS `count` FROM `users` WHERE `user_code` = ?', $userCode)->fetch()->count > 0); - $token = $this->generateNewToken(); + $token = $this->generateUserUploadToken(); $this->database->query('INSERT INTO `users`(`email`, `username`, `password`, `is_admin`, `active`, `user_code`, `token`) VALUES (?, ?, ?, ?, ?, ?, ?)', [ param($request, 'email'), @@ -151,7 +151,7 @@ class UserController extends Controller { $user = $this->getUser($request, $id, false); - if (param($request, 'email') === null) { + if (param($request, 'email') === null && !filter_var(param($request, 'email'), FILTER_VALIDATE_EMAIL)) { $this->session->alert(lang('email_required'), 'danger'); return redirect($response, route('user.edit', ['id' => $id])); @@ -251,7 +251,7 @@ class UserController extends Controller { $user = $this->getUser($request, $id, true); - $token = $this->generateNewToken(); + $token = $this->generateUserUploadToken(); $this->database->query('UPDATE `users` SET `token`=? WHERE `id` = ?', [ $token, @@ -264,16 +264,4 @@ class UserController extends Controller return $response; } - - /** - * @return string - */ - protected function generateNewToken(): string - { - do { - $token = 'token_'.md5(uniqid('', true)); - } while ($this->database->query('SELECT COUNT(*) AS `count` FROM `users` WHERE `token` = ?', $token)->fetch()->count > 0); - - return $token; - } } diff --git a/app/helpers.php b/app/helpers.php index 97db80a..6e5d732 100644 --- a/app/helpers.php +++ b/app/helpers.php @@ -22,7 +22,7 @@ if (!function_exists('humanFileSize')) { for ($i = 0; ($size / 1024) > 0.9; $i++, $size /= 1024) { } - return round($size, $precision).' '.['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'][$i]; + return round($size, $precision).' '.['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'][$i]; } } @@ -88,6 +88,8 @@ if (!function_exists('stringToBytes')) { $val = (float) $val; switch ($last) { + case 't': + $val *= 1024; case 'g': $val *= 1024; case 'm': diff --git a/app/routes.php b/app/routes.php index 8b2bcfc..52e8bfa 100644 --- a/app/routes.php +++ b/app/routes.php @@ -2,14 +2,14 @@ // Auth routes use App\Controllers\AdminController; +use App\Controllers\Auth\RegisterController; use App\Controllers\ClientController; use App\Controllers\DashboardController; use App\Controllers\ExportController; -use App\Controllers\LoginController; +use App\Controllers\Auth\LoginController; use App\Controllers\MediaController; use App\Controllers\ProfileController; use App\Controllers\SettingController; -use App\Controllers\ThemeController; use App\Controllers\UpgradeController; use App\Controllers\UploadController; use App\Controllers\UserController; @@ -63,6 +63,8 @@ $app->group('', function (RouteCollectorProxy $group) { })->add(App\Middleware\CheckForMaintenanceMiddleware::class)->add(AuthMiddleware::class); $app->get('/', [DashboardController::class, 'redirects'])->setName('root'); +$app->get('/register', [RegisterController::class, 'registerForm'])->setName('register.show'); +$app->post('/register', [RegisterController::class, 'register'])->setName('register'); $app->get('/login', [LoginController::class, 'show'])->setName('login.show'); $app->post('/login', [LoginController::class, 'login'])->setName('login'); $app->map(['GET', 'POST'], '/logout', [LoginController::class, 'logout'])->setName('logout'); diff --git a/resources/lang/en.lang.php b/resources/lang/en.lang.php index 18999bd..e7a8a24 100644 --- a/resources/lang/en.lang.php +++ b/resources/lang/en.lang.php @@ -1,119 +1,124 @@ 'English', - 'enforce_language' => 'Enforce language', - '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', - 'upload' => 'Upload', - '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', - 'php_info' => 'PHP Informations', - 'system_settings' => 'System Settings', - '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.', - 'cannot_write_file' => 'The destination path is not writable.', - 'deleted_orphans' => 'Successfully deleted %d orphaned files.', - 'switch_to' => 'Switch to', - 'gallery' => 'Gallery', - 'table' => 'Table', - 'dotted_search' => 'Search...', - 'order_by' => 'Order by...', - 'time' => 'Time', - 'name' => 'Name', - 'maintenance' => 'Maintenance', - 'clean_orphaned_uploads' => 'Clean Orphaned Uploads', - 'path_not_writable' => 'The output path is not writable.', - 'already_latest_version' => 'You already have the latest version.', - 'new_version_available' => 'New version %s available!', - 'cannot_retrieve_file' => 'Cannot retrieve the file.', - 'file_size_no_match' => 'The downloaded file doesn\'t match the correct file size.', - 'check_for_updates' => 'Check for updates', - 'upgrade' => 'Upgrade', - 'updates' => 'Updates', + 'lang' => 'English', + 'enforce_language' => 'Enforce language', + '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', + 'upload' => 'Upload', + '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', + 'php_info' => 'PHP Informations', + 'system_settings' => 'System Settings', + '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.', + 'cannot_write_file' => 'The destination path is not writable.', + 'deleted_orphans' => 'Successfully deleted %d orphaned files.', + 'switch_to' => 'Switch to', + 'gallery' => 'Gallery', + 'table' => 'Table', + 'dotted_search' => 'Search...', + 'order_by' => 'Order by...', + 'time' => 'Time', + 'name' => 'Name', + 'maintenance' => 'Maintenance', + 'clean_orphaned_uploads' => 'Clean Orphaned Uploads', + 'path_not_writable' => 'The output path is not writable.', + 'already_latest_version' => 'You already have the latest version.', + 'new_version_available' => 'New version %s available!', + 'cannot_retrieve_file' => 'Cannot retrieve the file.', + 'file_size_no_match' => 'The downloaded file doesn\'t match the correct file size.', + 'check_for_updates' => 'Check for updates', + 'upgrade' => 'Upgrade', + 'updates' => 'Updates', 'maintenance_in_progress' => 'Platform under maintenance, try again later...', - 'cancel' => 'Cancel', - 'auto_set' => 'Set automatically', - 'default_lang_behavior' => 'XBackBone will try to match the browser language by default (the fallback is English).', - 'prerelease_channel' => 'Prerelease Channel', - 'no_upload_token' => 'You don\'t have a personal upload token. (Generate one and try again.)', - 'drop_to_upload' => 'Click or drop your files here to upload.', - 'donation' => 'Donation', - 'donate_text' => 'If you like XBackBone, consider a donation to support development!', - 'custom_head_html' => 'Custom HTML Head content', - 'custom_head_html_hint' => 'This content will be added at the tag on every page.', - 'custom_head_set' => 'Custom HTML head applied.', - 'remember_me' => 'Remember me', - 'please_wait' => 'Please wait…', - 'dont_close' => 'Do not close this tab until completion.', - 'register_enabled' => 'Registration Enabled', - 'hide_by_default' => 'Hide uploads by default', - 'copy_url_behavior' => 'Copy URL mode', - 'settings_saved' => 'System settings saved!', - 'export_data' => 'Export Data' + 'cancel' => 'Cancel', + 'auto_set' => 'Set automatically', + 'default_lang_behavior' => 'XBackBone will try to match the browser language by default (the fallback is English).', + 'prerelease_channel' => 'Prerelease Channel', + 'no_upload_token' => 'You don\'t have a personal upload token. (Generate one and try again.)', + 'drop_to_upload' => 'Click or drop your files here to upload.', + 'donation' => 'Donation', + 'donate_text' => 'If you like XBackBone, consider a donation to support development!', + 'custom_head_html' => 'Custom HTML Head content', + 'custom_head_html_hint' => 'This content will be added at the tag on every page.', + 'custom_head_set' => 'Custom HTML head applied.', + 'remember_me' => 'Remember me', + 'please_wait' => 'Please wait…', + 'dont_close' => 'Do not close this tab until completion.', + 'register_enabled' => 'Registrations enabled', + 'hide_by_default' => 'Hide media by default', + 'copy_url_behavior' => 'Copy URL mode', + 'settings_saved' => 'System settings saved!', + 'export_data' => 'Export Data', + 'password_recovery' => 'Recover password', + 'no_account' => 'Don\'t have an account?', + 'register' => 'Register', + 'default_user_quota' => 'Default User Quota', + 'invalid_quota' => 'Invalid values as default user quota.', ]; diff --git a/resources/schemas/mysql/mysql.5.sql b/resources/schemas/mysql/mysql.5.sql new file mode 100644 index 0000000..16c1e83 --- /dev/null +++ b/resources/schemas/mysql/mysql.5.sql @@ -0,0 +1,7 @@ +ALTER TABLE `users` + ADD COLUMN `activate_token` VARCHAR(32) DEFAULT NULL, + ADD COLUMN `reset_token` VARCHAR(32) DEFAULT NULL, + ADD COLUMN `disk_quota` BIGINT(20) NOT NULL DEFAULT -1; + +ALTER TABLE `users` ADD INDEX (`activate_token`); +ALTER TABLE `users` ADD INDEX (`reset_token`); \ No newline at end of file diff --git a/resources/schemas/sqlite/sqlite.5.sql b/resources/schemas/sqlite/sqlite.5.sql new file mode 100644 index 0000000..c116f1b --- /dev/null +++ b/resources/schemas/sqlite/sqlite.5.sql @@ -0,0 +1,10 @@ +ALTER TABLE `users` ADD COLUMN `activate_token` VARCHAR(32); +ALTER TABLE `users` ADD COLUMN `reset_token` VARCHAR(32); +ALTER TABLE `users` ADD COLUMN `disk_quota` BIGINT NOT NULL DEFAULT -1; + +CREATE INDEX IF NOT EXISTS `activate_token_index` + ON `users` (`activate_token`); + +CREATE INDEX IF NOT EXISTS `reset_token_index` + ON `users` (`reset_token`); + diff --git a/resources/templates/auth/login.twig b/resources/templates/auth/login.twig index f6fb078..e21419f 100644 --- a/resources/templates/auth/login.twig +++ b/resources/templates/auth/login.twig @@ -35,19 +35,28 @@
- + -
- - +
+
+ + +
+ {{ lang('password_recovery') }}
+
+ {% if register_enabled == 'on' %} +
+ {{ lang('no_account') }} {{ lang('register') }}. +
+ {% endif %}
diff --git a/resources/templates/auth/register.twig b/resources/templates/auth/register.twig new file mode 100644 index 0000000..0b21836 --- /dev/null +++ b/resources/templates/auth/register.twig @@ -0,0 +1,56 @@ +{% extends 'base.twig' %} + +{% block title %}{{ lang('login') }}{% endblock %} + +{% block head %} + +{% endblock %} + +{% block content %} +
+ +
+{% endblock %} \ No newline at end of file diff --git a/resources/templates/comp/navbar.twig b/resources/templates/comp/navbar.twig index 5b34560..21abcbe 100644 --- a/resources/templates/comp/navbar.twig +++ b/resources/templates/comp/navbar.twig @@ -37,7 +37,7 @@
+
+ +
+ +
+
+
diff --git a/src/css/app.css b/src/css/app.css index c0f9168..8078da8 100644 --- a/src/css/app.css +++ b/src/css/app.css @@ -34,6 +34,11 @@ body { border-bottom-left-radius: 0; } +.form-signin input[type="email"] { + margin-bottom: -1px; + border-radius: 0; +} + .form-signin input[type="password"] { margin-bottom: .50rem; border-top-left-radius: 0; diff --git a/src/js/app.js b/src/js/app.js index 023531f..2ce2010 100644 --- a/src/js/app.js +++ b/src/js/app.js @@ -106,7 +106,7 @@ var app = { window.open($('#telegram-share-button').data('url') + $('#telegram-share-text').val(), '_blank'); }, checkForUpdates: function () { - $('#checkForUpdatesMessage').empty().html(''); + $('#checkForUpdatesMessage').empty().html(''); $('#doUpgradeButton').prop('disabled', true); $.get(window.AppConfig.base_url + '/system/checkForUpdates?prerelease=' + $(this).data('prerelease'), function (data) { $('#checkForUpdatesMessage').empty().text(data.message);