Browse Source

User registration procedure

Sergio Brighenti 5 years ago
parent
commit
0269eaa6f6

+ 3 - 1
app/Controllers/AdminController.php

@@ -37,6 +37,7 @@ class AdminController extends Controller
         $registerEnabled = $this->database->query('SELECT `value` FROM `settings` WHERE `key` = \'register_enabled\'')->fetch()->value ?? 'off';
         $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';
         $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';
         $copyUrl = $this->database->query('SELECT `value` FROM `settings` WHERE `key` = \'copy_url_behavior\'')->fetch()->value ?? 'off';
+        $quotaEnabled = $this->database->query('SELECT `value` FROM `settings` WHERE `key` = \'quota_enabled\'')->fetch()->value ?? 'off';
         $defaultUserQuota = $this->database->query('SELECT `value` FROM `settings` WHERE `key` = \'default_user_quota\'')->fetch()->value ?? '1G';
         $defaultUserQuota = $this->database->query('SELECT `value` FROM `settings` WHERE `key` = \'default_user_quota\'')->fetch()->value ?? '1G';
 
 
         return view()->render($response, 'dashboard/system.twig', [
         return view()->render($response, 'dashboard/system.twig', [
@@ -53,7 +54,8 @@ class AdminController extends Controller
             'register_enabled' => $registerEnabled,
             'register_enabled' => $registerEnabled,
             'hide_by_default' => $hideByDefault,
             'hide_by_default' => $hideByDefault,
             'copy_url_behavior' => $copyUrl,
             'copy_url_behavior' => $copyUrl,
-            'default_user_quota' => $defaultUserQuota,
+            'quota_enabled' => $quotaEnabled,
+            'default_user_quota' => humanFileSize($defaultUserQuota, 0, true),
         ]);
         ]);
     }
     }
 
 

+ 32 - 0
app/Controllers/Auth/RegisterController.php

@@ -4,6 +4,7 @@
 namespace App\Controllers\Auth;
 namespace App\Controllers\Auth;
 
 
 use App\Controllers\Controller;
 use App\Controllers\Controller;
+use App\Web\Mail;
 use Psr\Http\Message\ResponseInterface as Response;
 use Psr\Http\Message\ResponseInterface as Response;
 use Psr\Http\Message\ServerRequestInterface as Request;
 use Psr\Http\Message\ServerRequestInterface as Request;
 use Slim\Exception\HttpNotFoundException;
 use Slim\Exception\HttpNotFoundException;
@@ -100,6 +101,18 @@ class RegisterController extends Controller
             $activateToken,
             $activateToken,
         ]);
         ]);
 
 
+        Mail::make()
+            ->from('no-reply@'.str_ireplace('www.', '', parse_url($this->config['base_url'], PHP_URL_HOST)), $this->config['app_name'])
+            ->to(param($request, 'email'))
+            ->subject(lang('mail.activate_account', [$this->config['app_name']]))
+            ->message(lang('mail.activate_text', [
+                param($request, 'username'),
+                $this->config['app_name'],
+                $this->config['base_url'],
+                route('activate', ['activateToken' => $activateToken]),
+            ]))
+            ->send();
+
         $this->session->alert(lang('register_success', [param($request, 'username')]), 'success');
         $this->session->alert(lang('register_success', [param($request, 'username')]), 'success');
         $this->logger->info('New user registered.', [array_diff_key($request->getParsedBody(), array_flip(['password']))]);
         $this->logger->info('New user registered.', [array_diff_key($request->getParsedBody(), array_flip(['password']))]);
 
 
@@ -114,5 +127,24 @@ class RegisterController extends Controller
      */
      */
     public function activateUser(Request $request, Response $response, string $activateToken): Response
     public function activateUser(Request $request, Response $response, string $activateToken): Response
     {
     {
+        if ($this->session->get('logged', false)) {
+            return redirect($response, route('home'));
+        }
+
+        $userId = $this->database->query('SELECT `id` FROM `users` WHERE `activate_token` = ? LIMIT 1', $activateToken)->fetch()->id ?? null;
+
+        if ($userId === null) {
+            $this->session->alert(lang('account_not_found'), 'warning');
+            return redirect($response, route('login.show'));
+        }
+
+        $this->database->query('UPDATE `users` SET `activate_token`=?, `active`=? WHERE `id` = ?', [
+            null,
+            1,
+            $userId,
+        ]);
+
+        $this->session->alert(lang('account_activated'), 'success');
+        return redirect($response, route('login.show'));
     }
     }
 }
 }

+ 2 - 1
app/Controllers/SettingController.php

@@ -23,7 +23,8 @@ class SettingController extends Controller
 
 
         $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('hide_by_default', param($request, 'hide_by_default', 'off'));
-        $this->updateSetting('default_user_quota', param($request, 'default_user_quota', '1G'));
+        $this->updateSetting('quota_enabled', param($request, 'quota_enabled', 'off'));
+        $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->updateSetting('copy_url_behavior', param($request, 'copy_url_behavior') === null ? 'default' : 'raw');
 
 
         $this->applyTheme($request);
         $this->applyTheme($request);

+ 127 - 0
app/Web/Mail.php

@@ -0,0 +1,127 @@
+<?php
+
+
+namespace App\Web;
+
+
+use InvalidArgumentException;
+
+class Mail
+{
+    protected $fromMail = 'no-reply@example.com';
+    protected $fromName;
+
+    protected $to;
+
+    protected $subject;
+    protected $message;
+
+    protected $additionalHeaders = '';
+    protected $headers = '';
+
+    /**
+     * @return Mail
+     */
+    public static function make()
+    {
+        return new self();
+    }
+
+    /**
+     * @param $mail
+     * @param $name
+     * @return $this
+     */
+    public function from(string $mail, string $name)
+    {
+        $this->fromMail = $mail;
+        $this->fromName = $name;
+        return $this;
+    }
+
+    /**
+     * @param $mail
+     * @return $this
+     */
+    public function to(string $mail)
+    {
+        if (!filter_var($mail, FILTER_VALIDATE_EMAIL)) {
+            throw new InvalidArgumentException('Mail not valid.');
+        }
+        $this->to = $mail;
+        return $this;
+    }
+
+
+    /**
+     * @param $text
+     * @return $this
+     */
+    public function subject(string $text)
+    {
+        $this->subject = htmlentities($text);
+        return $this;
+    }
+
+    /**
+     * @param $text
+     * @return $this
+     */
+    public function message(string $text)
+    {
+        $this->message = htmlentities($text);
+        return $this;
+    }
+
+    /**
+     * @param $header
+     * @return $this
+     */
+    public function addHeader(string $header)
+    {
+        $this->additionalHeaders .= "$header\r\n";
+        return $this;
+    }
+
+    /**
+     * @param $header
+     * @return $this
+     */
+    protected function addRequiredHeader(string $header)
+    {
+        $this->headers .= "$header\r\n";
+        return $this;
+    }
+
+    /**
+     * @return int
+     */
+    public function send()
+    {
+        if ($this->to === null) {
+            throw new InvalidArgumentException('Target email cannot be null.');
+        }
+
+        if ($this->subject === null) {
+            throw new InvalidArgumentException('Subject cannot be null.');
+        }
+
+        if ($this->message === null) {
+            throw new InvalidArgumentException('Message cannot be null.');
+        }
+
+        if ($this->fromName === null) {
+            $this->addRequiredHeader("From: $this->fromMail");
+        } else {
+            $this->addRequiredHeader("From: $this->fromName <$this->fromMail>");
+        }
+
+        $this->addRequiredHeader('X-Mailer: PHP/'.phpversion());
+        $this->addRequiredHeader('MIME-Version: 1.0');
+        $this->addRequiredHeader('Content-Type: text/html; charset=iso-8859-1');
+
+        $this->headers .= $this->additionalHeaders;
+
+        return (int) mail($this->to, $this->subject, $this->message, $this->headers);
+    }
+}

+ 53 - 48
app/helpers.php

@@ -13,22 +13,27 @@ if (!function_exists('humanFileSize')) {
      * Generate a human readable file size.
      * Generate a human readable file size.
      *
      *
      * @param $size
      * @param $size
-     * @param int $precision
+     * @param  int  $precision
      *
      *
+     * @param  bool  $iniMode
      * @return string
      * @return string
      */
      */
-    function humanFileSize($size, $precision = 2): string
+    function humanFileSize($size, $precision = 2, $iniMode = false): string
     {
     {
         for ($i = 0; ($size / 1024) > 0.9; $i++, $size /= 1024) {
         for ($i = 0; ($size / 1024) > 0.9; $i++, $size /= 1024) {
         }
         }
 
 
+        if ($iniMode) {
+            return round($size, $precision).['B', 'K', 'M', 'G', 'T'][$i];
+        }
+
         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];
     }
     }
 }
 }
 
 
 if (!function_exists('humanRandomString')) {
 if (!function_exists('humanRandomString')) {
     /**
     /**
-     * @param int $length
+     * @param  int  $length
      *
      *
      * @return string
      * @return string
      */
      */
@@ -49,7 +54,7 @@ if (!function_exists('humanRandomString')) {
 
 
 if (!function_exists('isDisplayableImage')) {
 if (!function_exists('isDisplayableImage')) {
     /**
     /**
-     * @param string $mime
+     * @param  string  $mime
      *
      *
      * @return bool
      * @return bool
      */
      */
@@ -140,7 +145,7 @@ if (!function_exists('resolve')) {
     /**
     /**
      * Resolve a service from de DI container.
      * Resolve a service from de DI container.
      *
      *
-     * @param string $service
+     * @param  string  $service
      *
      *
      * @return mixed
      * @return mixed
      */
      */
@@ -168,9 +173,9 @@ if (!function_exists('redirect')) {
     /**
     /**
      * Set the redirect response.
      * Set the redirect response.
      *
      *
-     * @param Response $response
-     * @param string   $url
-     * @param int      $status
+     * @param  Response  $response
+     * @param  string  $url
+     * @param  int  $status
      *
      *
      * @return Response
      * @return Response
      */
      */
@@ -186,7 +191,7 @@ if (!function_exists('asset')) {
     /**
     /**
      * Get the asset link with timestamp.
      * Get the asset link with timestamp.
      *
      *
-     * @param string $path
+     * @param  string  $path
      *
      *
      * @return string
      * @return string
      */
      */
@@ -200,8 +205,8 @@ if (!function_exists('urlFor')) {
     /**
     /**
      * Generate the app url given a path.
      * Generate the app url given a path.
      *
      *
-     * @param string $path
-     * @param string $append
+     * @param  string  $path
+     * @param  string  $append
      *
      *
      * @return string
      * @return string
      */
      */
@@ -217,9 +222,9 @@ if (!function_exists('route')) {
     /**
     /**
      * Generate the app url given a path.
      * Generate the app url given a path.
      *
      *
-     * @param string $path
-     * @param array  $args
-     * @param string $append
+     * @param  string  $path
+     * @param  array  $args
+     * @param  string  $append
      *
      *
      * @return string
      * @return string
      */
      */
@@ -236,9 +241,9 @@ if (!function_exists('param')) {
     /**
     /**
      * Get a parameter from the request.
      * Get a parameter from the request.
      *
      *
-     * @param Request $request
-     * @param string  $name
-     * @param null    $default
+     * @param  Request  $request
+     * @param  string  $name
+     * @param  null  $default
      *
      *
      * @return string
      * @return string
      */
      */
@@ -262,10 +267,10 @@ if (!function_exists('json')) {
     /**
     /**
      * Return a json response.
      * Return a json response.
      *
      *
-     * @param Response $response
+     * @param  Response  $response
      * @param $data
      * @param $data
-     * @param int $status
-     * @param int $options
+     * @param  int  $status
+     * @param  int  $options
      *
      *
      * @return Response
      * @return Response
      */
      */
@@ -281,8 +286,8 @@ if (!function_exists('json')) {
 
 
 if (!function_exists('lang')) {
 if (!function_exists('lang')) {
     /**
     /**
-     * @param string $key
-     * @param array  $args
+     * @param  string  $key
+     * @param  array  $args
      *
      *
      * @return string
      * @return string
      */
      */
@@ -294,7 +299,7 @@ if (!function_exists('lang')) {
 
 
 if (!function_exists('isBot')) {
 if (!function_exists('isBot')) {
     /**
     /**
-     * @param string $userAgent
+     * @param  string  $userAgent
      *
      *
      * @return bool
      * @return bool
      */
      */
@@ -331,27 +336,27 @@ if (!function_exists('mime2font')) {
     function mime2font($mime)
     function mime2font($mime)
     {
     {
         $classes = [
         $classes = [
-            'image'                                                          => 'fa-file-image',
-            'audio'                                                          => 'fa-file-audio',
-            'video'                                                          => 'fa-file-video',
-            'application/pdf'                                                => 'fa-file-pdf',
-            'application/msword'                                             => 'fa-file-word',
-            'application/vnd.ms-word'                                        => 'fa-file-word',
-            'application/vnd.oasis.opendocument.text'                        => 'fa-file-word',
+            'image' => 'fa-file-image',
+            'audio' => 'fa-file-audio',
+            'video' => 'fa-file-video',
+            'application/pdf' => 'fa-file-pdf',
+            'application/msword' => 'fa-file-word',
+            'application/vnd.ms-word' => 'fa-file-word',
+            'application/vnd.oasis.opendocument.text' => 'fa-file-word',
             'application/vnd.openxmlformats-officedocument.wordprocessingml' => 'fa-file-word',
             'application/vnd.openxmlformats-officedocument.wordprocessingml' => 'fa-file-word',
-            'application/vnd.ms-excel'                                       => 'fa-file-excel',
-            'application/vnd.openxmlformats-officedocument.spreadsheetml'    => 'fa-file-excel',
-            'application/vnd.oasis.opendocument.spreadsheet'                 => 'fa-file-excel',
-            'application/vnd.ms-powerpoint'                                  => 'fa-file-powerpoint',
-            'application/vnd.openxmlformats-officedocument.presentationml'   => 'fa-file-powerpoint',
-            'application/vnd.oasis.opendocument.presentation'                => 'fa-file-powerpoint',
-            'text/plain'                                                     => 'fa-file-alt',
-            'text/html'                                                      => 'fa-file-code',
-            'text/x-php'                                                     => 'fa-file-code',
-            'application/json'                                               => 'fa-file-code',
-            'application/gzip'                                               => 'fa-file-archive',
-            'application/zip'                                                => 'fa-file-archive',
-            'application/octet-stream'                                       => 'fa-file-alt',
+            'application/vnd.ms-excel' => 'fa-file-excel',
+            'application/vnd.openxmlformats-officedocument.spreadsheetml' => 'fa-file-excel',
+            'application/vnd.oasis.opendocument.spreadsheet' => 'fa-file-excel',
+            'application/vnd.ms-powerpoint' => 'fa-file-powerpoint',
+            'application/vnd.openxmlformats-officedocument.presentationml' => 'fa-file-powerpoint',
+            'application/vnd.oasis.opendocument.presentation' => 'fa-file-powerpoint',
+            'text/plain' => 'fa-file-alt',
+            'text/html' => 'fa-file-code',
+            'text/x-php' => 'fa-file-code',
+            'application/json' => 'fa-file-code',
+            'application/gzip' => 'fa-file-archive',
+            'application/zip' => 'fa-file-archive',
+            'application/octet-stream' => 'fa-file-alt',
         ];
         ];
 
 
         foreach ($classes as $fullMime => $class) {
         foreach ($classes as $fullMime => $class) {
@@ -383,7 +388,7 @@ if (!function_exists('queryParams')) {
     /**
     /**
      * Get the query parameters of the current request.
      * Get the query parameters of the current request.
      *
      *
-     * @param array $replace
+     * @param  array  $replace
      *
      *
      * @return string
      * @return string
      */
      */
@@ -401,8 +406,8 @@ if (!function_exists('inPath')) {
     /**
     /**
      * Check if uri start with a path.
      * Check if uri start with a path.
      *
      *
-     * @param string $uri
-     * @param string $path
+     * @param  string  $uri
+     * @param  string  $path
      *
      *
      * @return bool
      * @return bool
      */
      */
@@ -410,7 +415,7 @@ if (!function_exists('inPath')) {
     {
     {
         $path = parse_url(urlFor($path), PHP_URL_PATH);
         $path = parse_url(urlFor($path), PHP_URL_PATH);
 
 
-        return substr($uri, 0, strlen($uri)) === $path;
+        return substr($uri, 0, strlen($path)) === $path;
     }
     }
 }
 }
 
 
@@ -419,7 +424,7 @@ if (!function_exists('glob_recursive')) {
      * Does not support flag GLOB_BRACE.
      * Does not support flag GLOB_BRACE.
      *
      *
      * @param $pattern
      * @param $pattern
-     * @param int $flags
+     * @param  int  $flags
      *
      *
      * @return array|false
      * @return array|false
      */
      */

+ 1 - 0
app/routes.php

@@ -65,6 +65,7 @@ $app->group('', function (RouteCollectorProxy $group) {
 $app->get('/', [DashboardController::class, 'redirects'])->setName('root');
 $app->get('/', [DashboardController::class, 'redirects'])->setName('root');
 $app->get('/register', [RegisterController::class, 'registerForm'])->setName('register.show');
 $app->get('/register', [RegisterController::class, 'registerForm'])->setName('register.show');
 $app->post('/register', [RegisterController::class, 'register'])->setName('register');
 $app->post('/register', [RegisterController::class, 'register'])->setName('register');
+$app->get('/activate/{activateToken}', [RegisterController::class, 'activateUser'])->setName('activate');
 $app->get('/login', [LoginController::class, 'show'])->setName('login.show');
 $app->get('/login', [LoginController::class, 'show'])->setName('login.show');
 $app->post('/login', [LoginController::class, 'login'])->setName('login');
 $app->post('/login', [LoginController::class, 'login'])->setName('login');
 $app->map(['GET', 'POST'], '/logout', [LoginController::class, 'logout'])->setName('logout');
 $app->map(['GET', 'POST'], '/logout', [LoginController::class, 'logout'])->setName('logout');

+ 1 - 0
resources/lang/en.lang.php

@@ -121,4 +121,5 @@ return [
     'register' => 'Register',
     'register' => 'Register',
     'default_user_quota' => 'Default User Quota',
     'default_user_quota' => 'Default User Quota',
     'invalid_quota' => 'Invalid values as default user quota.',
     'invalid_quota' => 'Invalid values as default user quota.',
+    'mail.activate_text' => "Hi %s!\nthank you for creating your account on %s (%s), click on the following link to activate it:\n\n%s"
 ];
 ];

+ 2 - 1
resources/schemas/mysql/mysql.5.sql

@@ -1,7 +1,8 @@
 ALTER TABLE `users`
 ALTER TABLE `users`
     ADD COLUMN `activate_token` VARCHAR(32) DEFAULT NULL,
     ADD COLUMN `activate_token` VARCHAR(32) DEFAULT NULL,
     ADD COLUMN `reset_token` VARCHAR(32) DEFAULT NULL,
     ADD COLUMN `reset_token` VARCHAR(32) DEFAULT NULL,
-    ADD COLUMN `disk_quota` BIGINT(20) NOT NULL DEFAULT -1;
+    ADD COLUMN `current_disk_quota` BIGINT(20) NOT NULL DEFAULT 0,
+    ADD COLUMN `max_disk_quota` BIGINT(20) NOT NULL DEFAULT -1;
 
 
 ALTER TABLE `users` ADD INDEX (`activate_token`);
 ALTER TABLE `users` ADD INDEX (`activate_token`);
 ALTER TABLE `users` ADD INDEX (`reset_token`);
 ALTER TABLE `users` ADD INDEX (`reset_token`);

+ 2 - 1
resources/schemas/sqlite/sqlite.5.sql

@@ -1,6 +1,7 @@
 ALTER TABLE `users` ADD COLUMN `activate_token` VARCHAR(32);
 ALTER TABLE `users` ADD COLUMN `activate_token` VARCHAR(32);
 ALTER TABLE `users` ADD COLUMN `reset_token` VARCHAR(32);
 ALTER TABLE `users` ADD COLUMN `reset_token` VARCHAR(32);
-ALTER TABLE `users` ADD COLUMN `disk_quota` BIGINT NOT NULL DEFAULT -1;
+ALTER TABLE `users` ADD COLUMN `current_disk_quota` BIGINT NOT NULL DEFAULT 0;
+ALTER TABLE `users` ADD COLUMN `max_disk_quota` BIGINT NOT NULL DEFAULT -1;
 
 
 CREATE INDEX IF NOT EXISTS `activate_token_index`
 CREATE INDEX IF NOT EXISTS `activate_token_index`
   ON `users` (`activate_token`);
   ON `users` (`activate_token`);

+ 1 - 1
resources/templates/auth/register.twig

@@ -38,7 +38,7 @@
                     <label for="username" class="sr-only">{{ lang('username') }}</label>
                     <label for="username" class="sr-only">{{ lang('username') }}</label>
                     <input type="text" id="username" class="form-control" placeholder="{{ lang('username') }}" name="username" required autofocus>
                     <input type="text" id="username" class="form-control" placeholder="{{ lang('username') }}" name="username" required autofocus>
                     <label for="email" class="sr-only">E-Mail</label>
                     <label for="email" class="sr-only">E-Mail</label>
-                    <input type="email" id="email" class="form-control" placeholder="mail@example.com" name="password" required>
+                    <input type="email" id="email" class="form-control" placeholder="mail@example.com" name="email" required>
                     <label for="password" class="sr-only">{{ lang('password') }}</label>
                     <label for="password" class="sr-only">{{ lang('password') }}</label>
                     <input type="password" id="password" class="form-control" placeholder="{{ lang('password') }}" name="password" required>
                     <input type="password" id="password" class="form-control" placeholder="{{ lang('password') }}" name="password" required>
                 </div>
                 </div>

+ 1 - 1
resources/templates/comp/navbar.twig

@@ -18,7 +18,7 @@
                 </li>
                 </li>
                 {% if session.get('admin') %}
                 {% if session.get('admin') %}
                     <li class="nav-item">
                     <li class="nav-item">
-                        <a href="{{ route('user.index') }}" class="nav-link {{ inPath(request.uri.path, '/users') ? 'active' }}"><i class="fas fa-fw fa-users"></i>
+                        <a href="{{ route('user.index') }}" class="nav-link {{ inPath(request.uri.path, '/user') ? 'active' }}"><i class="fas fa-fw fa-users"></i>
                             {{ lang('users') }}
                             {{ lang('users') }}
                         </a>
                         </a>
                     </li>
                     </li>

+ 12 - 5
resources/templates/dashboard/system.twig

@@ -61,28 +61,28 @@
                         <form method="post" action="{{ route('settings.save') }}">
                         <form method="post" action="{{ route('settings.save') }}">
 
 
                             <div class="form-group row">
                             <div class="form-group row">
-                                <label for="custom_head" class="col-sm-4 col-form-label">{{ lang('register_enabled') }}</label>
+                                <label for="register_enabled" class="col-sm-4 col-form-label">{{ lang('register_enabled') }}</label>
                                 <div class="col-sm-8">
                                 <div class="col-sm-8">
                                     <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">
                             <div class="form-group row">
-                                <label for="custom_head" class="col-sm-4 col-form-label">{{ lang('hide_by_default') }}</label>
+                                <label for="hide_by_default" class="col-sm-4 col-form-label">{{ lang('hide_by_default') }}</label>
                                 <div class="col-sm-8">
                                 <div class="col-sm-8">
                                     <input type="checkbox" name="hide_by_default" data-toggle="toggle" {{ hide_by_default == 'on' ? 'checked' }}>
                                     <input type="checkbox" name="hide_by_default" data-toggle="toggle" {{ hide_by_default == 'on' ? 'checked' }}>
                                 </div>
                                 </div>
                             </div>
                             </div>
 
 
                             <div class="form-group row">
                             <div class="form-group row">
-                                <label for="custom_head" class="col-sm-4 col-form-label">{{ lang('copy_url_behavior') }}</label>
+                                <label for="copy_url_behavior" class="col-sm-4 col-form-label">{{ lang('copy_url_behavior') }}</label>
                                 <div class="col-sm-8">
                                 <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' }}>
                                     <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>
                             </div>
 
 
                             <div class="form-group row">
                             <div class="form-group row">
-                                <label for="custom_head" 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">
                                     <select class="form-control" id="themes" name="css">
                                     <select class="form-control" id="themes" name="css">
                                         <option id="theme-load" selected disabled hidden>{{ lang('click_to_load') }}</option>
                                         <option id="theme-load" selected disabled hidden>{{ lang('click_to_load') }}</option>
@@ -90,6 +90,13 @@
                                 </div>
                                 </div>
                             </div>
                             </div>
 
 
+                            <div class="form-group row">
+                                <label for="quota_enabled" class="col-sm-4 col-form-label">{{ lang('quota_enabled') }}</label>
+                                <div class="col-sm-8">
+                                    <input type="checkbox" name="quota_enabled" data-toggle="toggle" {{ quota_enabled == 'on' ? 'checked' }}>
+                                </div>
+                            </div>
+
                             <div class="form-group row">
                             <div class="form-group row">
                                 <label for="default_user_quota" class="col-sm-4 col-form-label">{{ lang('default_user_quota') }}</label>
                                 <label for="default_user_quota" class="col-sm-4 col-form-label">{{ lang('default_user_quota') }}</label>
                                 <div class="col-sm-8">
                                 <div class="col-sm-8">
@@ -98,7 +105,7 @@
                             </div>
                             </div>
 
 
                             <div class="form-group row">
                             <div class="form-group row">
-                                <label for="custom_head" class="col-sm-4 col-form-label">{{ lang('enforce_language') }}</label>
+                                <label for="lang" class="col-sm-4 col-form-label">{{ lang('enforce_language') }}</label>
                                 <div class="col-sm-8">
                                 <div class="col-sm-8">
                                     <select class="form-control" id="lang" name="lang">
                                     <select class="form-control" id="lang" name="lang">
                                         <option value="auto">({{ lang('auto_set') }})</option>
                                         <option value="auto">({{ lang('auto_set') }})</option>

+ 5 - 15
resources/templates/user/create.twig

@@ -7,7 +7,7 @@
     <div class="container">
     <div class="container">
         {% include 'comp/alert.twig' %}
         {% include 'comp/alert.twig' %}
         <div class="row justify-content-center">
         <div class="row justify-content-center">
-            <div class="col-md-8">
+            <div class="col-md-10">
                 <div class="card shadow-sm">
                 <div class="card shadow-sm">
                     <div class="card-header">{{ lang('user.create') }}</div>
                     <div class="card-header">{{ lang('user.create') }}</div>
                     <div class="card-body">
                     <div class="card-body">
@@ -31,25 +31,15 @@
                                 </div>
                                 </div>
                             </div>
                             </div>
                             <div class="form-group row">
                             <div class="form-group row">
-                                <div class="col-sm-2"></div>
+                                <label for="is_admin" class="col-sm-2 col-form-label">{{ lang('is_admin') }}</label>
                                 <div class="col-sm-10">
                                 <div class="col-sm-10">
-                                    <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">
-                                            {{ lang('is_admin') }}
-                                        </label>
-                                    </div>
+                                    <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">
-                                <div class="col-sm-2"></div>
+                                <label for="is_active" class="col-sm-2 col-form-label">{{ lang('is_active') }}</label>
                                 <div class="col-sm-10">
                                 <div class="col-sm-10">
-                                    <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">
-                                            {{ lang('is_active') }}
-                                        </label>
-                                    </div>
+                                    <input type="checkbox" name="is_active" data-toggle="toggle" data-off="{{ lang('no') }}" data-on="{{ lang('yes') }}" checked>
                                 </div>
                                 </div>
                             </div>
                             </div>
                             <div class="form-group row justify-content-md-end">
                             <div class="form-group row justify-content-md-end">

+ 5 - 15
resources/templates/user/edit.twig

@@ -7,7 +7,7 @@
     <div class="container">
     <div class="container">
         {% include 'comp/alert.twig' %}
         {% include 'comp/alert.twig' %}
         <div class="row justify-content-center">
         <div class="row justify-content-center">
-            <div class="col-md-8">
+            <div class="col-md-10">
                 <div class="card shadow-sm">
                 <div class="card shadow-sm">
                     {% if not profile %}
                     {% if not profile %}
                         <div class="card-header">{{ lang('user.edit') }}</div>
                         <div class="card-header">{{ lang('user.edit') }}</div>
@@ -67,25 +67,15 @@
                             </div>
                             </div>
                             {% if not profile %}
                             {% if not profile %}
                                 <div class="form-group row">
                                 <div class="form-group row">
-                                    <div class="col-sm-2"></div>
+                                    <label for="is_admin" class="col-sm-2 col-form-label">{{ lang('is_admin') }}</label>
                                     <div class="col-sm-10">
                                     <div class="col-sm-10">
-                                        <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">
-                                                {{ lang('is_admin') }}
-                                            </label>
-                                        </div>
+                                        <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">
-                                    <div class="col-sm-2"></div>
+                                    <label for="is_active" class="col-sm-2 col-form-label">{{ lang('is_active') }}</label>
                                     <div class="col-sm-10">
                                     <div class="col-sm-10">
-                                        <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">
-                                                {{ lang('is_active') }}
-                                            </label>
-                                        </div>
+                                        <input type="checkbox" name="is_active" data-toggle="toggle" data-off="{{ lang('no') }}" data-on="{{ lang('yes') }}" {{ user.active ? 'checked' }}>
                                     </div>
                                     </div>
                                 </div>
                                 </div>
                             {% endif %}
                             {% endif %}