From f7fa12453509b522b2588aceb02131f9b3df6699 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maurice=20Gr=C3=B6nwoldt?= Date: Fri, 25 Sep 2020 21:33:54 +0200 Subject: [PATCH 01/33] WIP --- composer.json | 3 +- install/db.sql | 13 +++ public/admin/.htaccess | 7 -- public/admin/index.php | 24 ----- public/index.php | 6 +- public/theme/admin/css/test.css | 22 +++++ public/theme/admin/js/test.js | 0 src/Venom/Admin/AdminController.php | 35 ++++++++ src/Venom/Admin/AdminRouterInit.php | 23 +++++ src/Venom/Admin/Routes/LoginRoute.php | 15 ++++ src/Venom/Core/ArgumentHandler.php | 12 +++ src/Venom/Helper/URLHelper.php | 5 ++ src/Venom/Models/User.php | 123 ++++++++++++++++++++++++-- src/Venom/Routing/Router.php | 11 +++ src/Venom/Security/BaseLogin.php | 40 ++++++++- src/Venom/Security/Login.php | 4 - src/Venom/Security/Security.php | 42 ++++++++- src/Venom/Venom.php | 31 ++++++- src/Venom/Views/DataLoader.php | 8 +- src/Venom/Views/VenomRenderer.php | 12 ++- tpl/admin/base.php | 10 +++ 21 files changed, 388 insertions(+), 58 deletions(-) delete mode 100644 public/admin/.htaccess delete mode 100644 public/admin/index.php create mode 100644 public/theme/admin/css/test.css create mode 100644 public/theme/admin/js/test.js create mode 100644 src/Venom/Admin/AdminController.php create mode 100644 src/Venom/Admin/AdminRouterInit.php create mode 100644 src/Venom/Admin/Routes/LoginRoute.php create mode 100644 tpl/admin/base.php diff --git a/composer.json b/composer.json index 8e4f0cb..62992e9 100755 --- a/composer.json +++ b/composer.json @@ -9,7 +9,8 @@ } ], "require": { - "ext-pdo": "*" + "ext-pdo": "*", + "ext-http": "*" }, "autoload": { "psr-4": { diff --git a/install/db.sql b/install/db.sql index 3e89174..3dfd064 100755 --- a/install/db.sql +++ b/install/db.sql @@ -27,3 +27,16 @@ create table if not exists data datatype enum ('content', 'form') ) comment 'DataLoader File'; + +create table if not exists users +( + id int(255) auto_increment not null unique primary key, + username varchar(255) not null unique, + email varchar(255) not null, + password varchar(255) not null, + token varchar(255) not null, + salt varchar(255) not null, + roles text default 'ROLE_GUEST' not null, + isActive tinyint(1) default 1 null +) + comment 'User File'; \ No newline at end of file diff --git a/public/admin/.htaccess b/public/admin/.htaccess deleted file mode 100644 index 4d339de..0000000 --- a/public/admin/.htaccess +++ /dev/null @@ -1,7 +0,0 @@ -RewriteEngine On - -RewriteCond %{REQUEST_FILENAME} !-f -RewriteCond %{REQUEST_FILENAME} !-d - -RewriteCond %{REQUEST_URI} (/[^.]*|\.)$ [NC] -RewriteRule .* index.php [L] \ No newline at end of file diff --git a/public/admin/index.php b/public/admin/index.php deleted file mode 100644 index 7ece959..0000000 --- a/public/admin/index.php +++ /dev/null @@ -1,24 +0,0 @@ -isMaintenance()) { - echo 'Currently not available'; - exit; -} -//if devMode is on show all errors! -if ($config->isDevMode()) { - error_reporting(E_ALL); - ini_set('error_reporting', E_ALL); -} -$venom = new Venom(); -Setup::loadRouters($venom); -Setup::loadModules($venom); -$venom->run(); \ No newline at end of file diff --git a/public/index.php b/public/index.php index e1d99e3..046cb5a 100755 --- a/public/index.php +++ b/public/index.php @@ -2,10 +2,12 @@ use Venom\Core\Config; use Venom\Core\Setup; +use Venom\Helper\URLHelper; use Venom\Venom; require_once '../vendor/autoload.php'; -Setup::loadConfig(false); +session_start(); +Setup::loadConfig(URLHelper::getInstance()->isAdminUrl()); Setup::loadLanguage(); $config = Config::getInstance(); @@ -21,4 +23,4 @@ if ($config->isDevMode()) { $venom = new Venom(); Setup::loadRouters($venom); Setup::loadModules($venom); -$venom->run(); \ No newline at end of file +$venom->inject(); \ No newline at end of file diff --git a/public/theme/admin/css/test.css b/public/theme/admin/css/test.css new file mode 100644 index 0000000..cfed392 --- /dev/null +++ b/public/theme/admin/css/test.css @@ -0,0 +1,22 @@ +* { + box-sizing: border-box; +} + +html, body { + margin: 0; + font-size: 16px; + color: #fff; + background-color: #333; + font-family: sans-serif; + height: 100vh; + width: 100vw; +} + +header { + font-size: 5vw; + cursor: pointer; +} + +header:hover { + color: #ff0323; +} \ No newline at end of file diff --git a/public/theme/admin/js/test.js b/public/theme/admin/js/test.js new file mode 100644 index 0000000..e69de29 diff --git a/src/Venom/Admin/AdminController.php b/src/Venom/Admin/AdminController.php new file mode 100644 index 0000000..60ae195 --- /dev/null +++ b/src/Venom/Admin/AdminController.php @@ -0,0 +1,35 @@ +getUrl() !== '/admin/') { + http_response_code(404); + $this->tpl = 'async'; + } + return true; + } + + public function getTemplate(): string + { + return $this->tpl; + } +} \ No newline at end of file diff --git a/src/Venom/Admin/AdminRouterInit.php b/src/Venom/Admin/AdminRouterInit.php new file mode 100644 index 0000000..3443d10 --- /dev/null +++ b/src/Venom/Admin/AdminRouterInit.php @@ -0,0 +1,23 @@ +addRoutes(self::getRoutes()); + $venom->addRouter(Router::ADMIN_ROUTER, $router); + } + + public static function getRoutes(): array + { + return []; + } +} \ No newline at end of file diff --git a/src/Venom/Admin/Routes/LoginRoute.php b/src/Venom/Admin/Routes/LoginRoute.php new file mode 100644 index 0000000..c706c65 --- /dev/null +++ b/src/Venom/Admin/Routes/LoginRoute.php @@ -0,0 +1,15 @@ + $item) { $this->arguments[htmlspecialchars($key)] = htmlspecialchars($item); + $this->post[htmlspecialchars($key)] = htmlspecialchars($item); } } @@ -41,4 +43,14 @@ class ArgumentHandler { return isset($this->arguments[$key]); } + + public function getPostItem(string $key, $default = null) + { + return $this->post[$key] ?? $default; + } + + public function hasPostItem(string $key): bool + { + return isset($this->post[$key]); + } } \ No newline at end of file diff --git a/src/Venom/Helper/URLHelper.php b/src/Venom/Helper/URLHelper.php index 534632b..9891eaa 100644 --- a/src/Venom/Helper/URLHelper.php +++ b/src/Venom/Helper/URLHelper.php @@ -42,4 +42,9 @@ class URLHelper { return $url; } + + public function isAdminUrl(): bool + { + return strpos($this->parsedUrl, '/admin/') === 0; + } } \ No newline at end of file diff --git a/src/Venom/Models/User.php b/src/Venom/Models/User.php index 43f98d8..7f0b93e 100644 --- a/src/Venom/Models/User.php +++ b/src/Venom/Models/User.php @@ -4,11 +4,124 @@ namespace Venom\Models; +use Venom\Core\DatabaseHandler; + class User { - private string $username; - private string $password; - private string $salt; - private string $token; - private array $roles; + public const ADMIN_ROLE = 'ROLE_ADMIN'; + public const GUEST_ROLE = 'ROLE_GUEST'; + private string $username = 'GUEST'; + private string $email = 'GUEST'; + private string $password = '---'; + private string $salt = '---'; + private string $token = '---'; + private string $id = '-1'; + private array $roles = []; + private bool $isLoaded = false; + + public function hasRole(string $role): bool + { + return in_array($role, $this->roles, true); + } + + public function loadUser(): bool + { + if (isset($_SESSION['userID'])) { + // try to load user from id! + $user = DatabaseHandler::get()->getOne("SELECT * FROM users WHERE id = :id OR username = :name AND isActive = 1", [ + ':id' => $_SESSION['userID'], + ':name' => $this->username + ]); + if ($user) { + $this->username = $user->username ?? ''; + $this->email = $user->email ?? ''; + $this->password = $user->password ?? ''; + $this->token = $user->token ?? ''; + $this->salt = $user->salt ?? ''; + $this->id = $user->id ?? '-1'; + $this->roles = explode(',', $user->roles ?? ''); + $this->isLoaded = true; + return true; + } + } + return false; + } + + public function getUsername(): string + { + return $this->username; + } + + public function setUsername(string $username): void + { + $this->username = $username; + } + + public function getEmail(): string + { + return $this->email; + } + + public function setEmail(string $email): void + { + $this->email = $email; + } + + public function getPassword(): string + { + return $this->password; + } + + public function setPassword(string $password): void + { + $this->password = $password; + } + + public function getSalt(): string + { + return $this->salt; + } + + public function setSalt(string $salt): void + { + $this->salt = $salt; + } + + public function getToken(): string + { + return $this->token; + } + + public function setToken(string $token): void + { + $this->token = $token; + } + + public function getRoles(): array + { + return $this->roles; + } + + public function setRoles(array $roles): void + { + $this->roles = $roles; + } + + public function addRole($value): void + { + if (!in_array($value, $this->roles, true)) { + $this->roles[] = $value; + } + } + + public function isLoaded(): bool + { + return $this->isLoaded; + } + + public function getId(): string + { + return $this->id; + } + } \ No newline at end of file diff --git a/src/Venom/Routing/Router.php b/src/Venom/Routing/Router.php index 091d853..af980ea 100644 --- a/src/Venom/Routing/Router.php +++ b/src/Venom/Routing/Router.php @@ -5,9 +5,14 @@ namespace Venom\Routing; use Exception; +use Venom\Core\Config; +use Venom\Models\User; +use Venom\Security\Security; class Router { + public const DEFAULT_ROUTER = 'defaultRouter'; + public const ADMIN_ROUTER = 'adminRouter'; protected string $id = 'defaultRouter'; protected int $version; protected string $prefix = ''; @@ -66,6 +71,12 @@ class Router $subRouteFound = isset($this->routes[$url]['routes'][$subRoute]); $methodFound = isset($this->routes[$url]['routes'][$subRoute][$method]); if ($routeAvailable && $subRouteFound && $methodFound) { + if ((Config::getInstance()->getSecurity()->useSecurity || $this->id === self::ADMIN_ROUTER) && isset($this->routes[$url]['roles'])) { + $roles = $this->routes[$url]['roles']; + if (!in_array(User::GUEST_ROLE, $roles, true) && !Security::get()->hasRoles($roles)) { + return null; + } + } return [ 'cl' => $this->routes[$url]['cl'], 'fnc' => $this->routes[$url]['routes'][$subRoute][$method], diff --git a/src/Venom/Security/BaseLogin.php b/src/Venom/Security/BaseLogin.php index 1a94a5c..6fc404b 100644 --- a/src/Venom/Security/BaseLogin.php +++ b/src/Venom/Security/BaseLogin.php @@ -4,10 +4,48 @@ namespace Venom\Security; +use Venom\Core\ArgumentHandler; +use Venom\Core\Config; +use Venom\Helper\URLHelper; +use Venom\Models\User; + /** * Class that Login stupid via Password, Username */ -class BaseLogin +class BaseLogin implements Login { + private User $user; + + public function __construct(User $user) + { + $this->user = $user; + } + + public function checkCredentials(): bool + { + $handler = ArgumentHandler::get(); + return $handler->hasPostItem('USERNAME') && $handler->hasPostItem('PASSWORD'); + } + + public function redirect(): void + { + http_redirect(URLHelper::getInstance()->getUrl(), ['redirect' => 'true'], true); + } + + public function login(): bool + { + $sec = Config::getInstance()->getSecurity(); + $this->user->setUsername(ArgumentHandler::get()->getPostItem('USERNAME')); + if (!$this->user->loadUser()) { + return false; + } + $secret = $sec->secret ?? 'venom'; + $hashed = hash($sec->algo ?? 'SHA256', ArgumentHandler::get()->getPostItem('PASSWORD') . $secret . $this->user->getSalt()); + if ($this->user->getPassword() === $hashed) { + $_SESSION['userID'] = $this->user->getId(); + return true; + } + return false; + } } \ No newline at end of file diff --git a/src/Venom/Security/Login.php b/src/Venom/Security/Login.php index 853cffd..51fbd3e 100644 --- a/src/Venom/Security/Login.php +++ b/src/Venom/Security/Login.php @@ -10,10 +10,6 @@ interface Login { public function __construct(User $user); - public function checkUsername(): bool; - - public function checkPassword(): bool; - public function checkCredentials(): bool; public function login(): bool; diff --git a/src/Venom/Security/Security.php b/src/Venom/Security/Security.php index 1585dbd..3d2b971 100644 --- a/src/Venom/Security/Security.php +++ b/src/Venom/Security/Security.php @@ -3,10 +3,20 @@ namespace Venom\Security; +use http\Exception\RuntimeException; +use Venom\Core\Config; +use Venom\Models\User; class Security { private static ?Security $instance = null; + private ?User $user; + + public function __construct() + { + $this->user = new User(); + $this->user->loadData(); + } public static function get(): Security { @@ -16,15 +26,39 @@ class Security return self::$instance; } - /* @todo implement logic */ public function hasRole(string $role): bool { + return $this->user->hasRole($role); + } + + public function hasRoles(array $roles): bool + { + foreach ($roles as $role) { + if (!$this->user->hasRole($role)) { + return false; + } + } return true; } - /* @todo implement logic */ - public function hasRoles(array $roles): bool + public function login(): void { - return true; + if (!$this->user->isLoaded()) { + throw new RuntimeException('Try to re-login!'); + } + $sec = Config::getInstance()->getSecurity(); + $login = new $sec->securityClass; + if ($login instanceof Login) { + if (!$login->checkCredentials() || !$login->login()) { + http_response_code(401); + } + $login->redirect(); + } + } + + public function logout(): void + { + unset($_SESSION['userID']); + $this->user = new User(); } } diff --git a/src/Venom/Venom.php b/src/Venom/Venom.php index d27b073..9dfaa06 100755 --- a/src/Venom/Venom.php +++ b/src/Venom/Venom.php @@ -4,6 +4,8 @@ namespace Venom; +use Venom\Admin\AdminController; +use Venom\Admin\AdminRouterInit; use Venom\Core\ArgumentHandler; use Venom\Core\Config; use Venom\Core\Module; @@ -30,13 +32,22 @@ class Venom Asset::get()->setRenderer($this->renderer); } + public function inject(): void + { + $this->run(); + } + public function run(): void { $arguments = ArgumentHandler::get(); $arguments->setItem(ErrorHandler::ERROR_KEY, false); + $config = Config::getInstance(); + if ($config->isAdmin()) { + $this->initAdmin(); + } // we need to load the current controller and the current start template. // after this we can start the renderer - if (Config::getInstance()->isRouterEnabled()) { + if ($config->isRouterEnabled() || $config->isAdmin()) { $status = $this->useRouter(); if ($status['found']) { if ($status['status']) { @@ -48,18 +59,32 @@ class Venom $registry = Registry::getInstance(); $registry->getLang()->initLang(); // if site is errored then dont load via SEO - if (!$arguments->getItem(ErrorHandler::ERROR_KEY)) { + if (!$config->isAdmin() && !$arguments->getItem(ErrorHandler::ERROR_KEY)) { $registry->getSeo()->loadSite(); } $this->renderer->init($this->findController()); $this->renderer->render(); } + public function initAdmin(): void + { + $this->controllers['adminCtrl'] = AdminController::class; + AdminRouterInit::registerAdminRouters($this); + ArgumentHandler::get()->setItem('cl', 'adminCtrl'); + } + private function useRouter(): array { $url = URLHelper::getInstance()->getUrl(); + $isAdmin = Config::getInstance()->isAdmin(); /** @var Router $router */ - foreach ($this->routers as $router) { + foreach ($this->routers as $key => $router) { + if ($isAdmin && $key !== Router::ADMIN_ROUTER) { + continue; + } + if (!$isAdmin && $key === Router::ADMIN_ROUTER) { + continue; + } $route = $router->findRoute($url, $_SERVER['REQUEST_METHOD']); $status = $router->tryFunctionCall($route); if ($route !== null) { diff --git a/src/Venom/Views/DataLoader.php b/src/Venom/Views/DataLoader.php index 90405b4..aa49d9f 100755 --- a/src/Venom/Views/DataLoader.php +++ b/src/Venom/Views/DataLoader.php @@ -3,7 +3,7 @@ namespace Venom\Views; -use RuntimeException; +use \RuntimeException; use Venom\Core\DatabaseHandler; use Venom\Models\DataModel; @@ -87,8 +87,8 @@ class DataLoader private function generateID(string $id = ''): string { if ($id === '') { - $id = bin2hex(random_bytes(16)); + $id = bin2hex(random_bytes(32)); } - return $id; + return hash('SHA256', $id); } -} +} \ No newline at end of file diff --git a/src/Venom/Views/VenomRenderer.php b/src/Venom/Views/VenomRenderer.php index 5e9c51e..b8bad47 100755 --- a/src/Venom/Views/VenomRenderer.php +++ b/src/Venom/Views/VenomRenderer.php @@ -84,7 +84,7 @@ class VenomRenderer return $this->vars[$name]; } - public function deleteVar($name) + public function deleteVar($name): void { unset($this->vars[$name]); } @@ -93,7 +93,13 @@ class VenomRenderer { $this->controller = $controller; $data = Config::getInstance()->getRenderer(); - $this->baseTemplate = $data->baseFile . '.php' ?? 'base.php'; - $this->templateDir = __DIR__ . '/../../../tpl/' . $data->theme . '/'; + $theme = $data->theme; + $base = $data->baseFile ?? 'base'; + if (Config::getInstance()->isAdmin()) { + $base = 'base'; + $theme = 'admin'; + } + $this->baseTemplate = $base . '.php'; + $this->templateDir = __DIR__ . '/../../../tpl/' . $theme . '/'; } } diff --git a/tpl/admin/base.php b/tpl/admin/base.php new file mode 100644 index 0000000..626f2cf --- /dev/null +++ b/tpl/admin/base.php @@ -0,0 +1,10 @@ +hasRole(User::ADMIN_ROLE)) { + echo 'Login!'; +} else { + echo 'Admin Interface!'; +} From 2db5aa86933c567fe70e77753967a6880cb3de59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maurice=20Gr=C3=B6nwoldt?= Date: Fri, 25 Sep 2020 21:39:05 +0200 Subject: [PATCH 02/33] cherry-picked changes Meta-Generator from engineer-trooper --- composer.json | 3 ++- install/db.sql | 8 ++++++++ src/Venom/Helper/MetaGenerator.php | 29 +++++++++++++++++++++++++++++ src/Venom/Views/VenomRenderer.php | 5 +++++ 4 files changed, 44 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 62992e9..0288075 100755 --- a/composer.json +++ b/composer.json @@ -10,7 +10,8 @@ ], "require": { "ext-pdo": "*", - "ext-http": "*" + "ext-http": "*", + "ext-json": "*" }, "autoload": { "psr-4": { diff --git a/install/db.sql b/install/db.sql index 3dfd064..6e0d81d 100755 --- a/install/db.sql +++ b/install/db.sql @@ -7,6 +7,14 @@ create table if not exists seoData ) comment 'seo url mapping'; +create table if not exists metaTagData +( + id int(255) auto_increment not null unique primary key, + content JSON not null, + isActive tinyint(1) default 1 null +) + comment 'Meta Tag File'; + create table if not exists language ( id int(255) auto_increment not null unique primary key, diff --git a/src/Venom/Helper/MetaGenerator.php b/src/Venom/Helper/MetaGenerator.php index e6dd9dd..81020fb 100644 --- a/src/Venom/Helper/MetaGenerator.php +++ b/src/Venom/Helper/MetaGenerator.php @@ -4,11 +4,40 @@ namespace Venom\Helper; +use Venom\Core\ArgumentHandler; +use Venom\Core\DatabaseHandler; + /** * Class MetaGenerator * @package Venom\Helper */ class MetaGenerator { + private array $container = []; + private string $id; + public function __construct() + { + $this->id = (string)ArgumentHandler::get()->getItem('metaId', '-1'); + } + + public function loadById(): void + { + if ($this->id === '-1') { + return; + } + $db = DatabaseHandler::get(); + $data = $db->getOne('select content from metaTagData where id = :id', [':id' => $this->id]); + if ($data !== null) { + $this->container = json_decode($data->content ?? '', true); + $this->container = array_merge([], $this->container); + } + } + + public function render(): void + { + foreach($this->container as $key => $value) { + echo ''; + } + } } \ No newline at end of file diff --git a/src/Venom/Views/VenomRenderer.php b/src/Venom/Views/VenomRenderer.php index b8bad47..3dfb403 100755 --- a/src/Venom/Views/VenomRenderer.php +++ b/src/Venom/Views/VenomRenderer.php @@ -6,12 +6,14 @@ namespace Venom\Views; use Venom\Core\ArgumentHandler; use Venom\Core\Config; +use Venom\Helper\MetaGenerator; use Venom\Venom; class VenomRenderer { private Venom $venom; private ?RenderController $controller; + private ?MetaGenerator $metaGenerator; private string $templateData = ''; private array $vars = []; private string $baseTemplate = ''; @@ -95,9 +97,12 @@ class VenomRenderer $data = Config::getInstance()->getRenderer(); $theme = $data->theme; $base = $data->baseFile ?? 'base'; + $this->metaGenerator = new MetaGenerator(); if (Config::getInstance()->isAdmin()) { $base = 'base'; $theme = 'admin'; + } else { + $this->metaGenerator->loadById(); } $this->baseTemplate = $base . '.php'; $this->templateDir = __DIR__ . '/../../../tpl/' . $theme . '/'; From f00bdc99ecdbfaa6db4f851aef5cf4cc6e0e9473 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maurice=20Gr=C3=B6nwoldt?= Date: Fri, 25 Sep 2020 21:51:39 +0200 Subject: [PATCH 03/33] fixed security login --- src/Venom/Security/Security.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Venom/Security/Security.php b/src/Venom/Security/Security.php index 3d2b971..ab2742f 100644 --- a/src/Venom/Security/Security.php +++ b/src/Venom/Security/Security.php @@ -15,7 +15,7 @@ class Security public function __construct() { $this->user = new User(); - $this->user->loadData(); + $this->user->loadUser(); } public static function get(): Security From c7984873c00cdc6a6490aee2de31a0d9321e5d62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maurice=20Gr=C3=B6nwoldt?= Date: Fri, 25 Sep 2020 22:33:35 +0200 Subject: [PATCH 04/33] fixed login added example login --- src/Venom/Admin/AdminRouterInit.php | 16 +++++++++++++++- src/Venom/Admin/Routes/LoginRoute.php | 17 ++++++++++++++++- src/Venom/Models/User.php | 4 ++-- src/Venom/Security/BaseLogin.php | 4 +++- src/Venom/Security/Security.php | 6 +++--- tpl/admin/base.php | 9 +++++++++ 6 files changed, 48 insertions(+), 8 deletions(-) diff --git a/src/Venom/Admin/AdminRouterInit.php b/src/Venom/Admin/AdminRouterInit.php index 3443d10..e1643ab 100644 --- a/src/Venom/Admin/AdminRouterInit.php +++ b/src/Venom/Admin/AdminRouterInit.php @@ -4,6 +4,7 @@ namespace Venom\Admin; +use Venom\Admin\Routes\LoginRoute; use Venom\Routing\Router; use Venom\Venom; @@ -18,6 +19,19 @@ class AdminRouterInit public static function getRoutes(): array { - return []; + return [ + '/login' => [ + 'cl' => LoginRoute::class, + 'roles' => ['ROLE_GUEST'], + 'routes' => [ + '*' => [ + "POST" => 'login' + ], + '1' => [ + "GET" => 'handle' + ] + ] + ] + ]; } } \ No newline at end of file diff --git a/src/Venom/Admin/Routes/LoginRoute.php b/src/Venom/Admin/Routes/LoginRoute.php index c706c65..d33680f 100644 --- a/src/Venom/Admin/Routes/LoginRoute.php +++ b/src/Venom/Admin/Routes/LoginRoute.php @@ -4,12 +4,27 @@ namespace Venom\Admin\Routes; +use Venom\Core\ArgumentHandler; use Venom\Routing\Route; +use Venom\Security\Security; class LoginRoute implements Route { - public function getAll(): bool { + public function login(): bool + { + Security::get()->login(); + return true; + } + + public function handle($fnc): bool + { + if ($fnc === 'logout') { + Security::get()->logout(); + $url = ArgumentHandler::get()->getPostItem('REDIRECT_TO', '/admin/'); + header('Location: ' . $url); + die(); + } return true; } } \ No newline at end of file diff --git a/src/Venom/Models/User.php b/src/Venom/Models/User.php index 7f0b93e..27e054c 100644 --- a/src/Venom/Models/User.php +++ b/src/Venom/Models/User.php @@ -26,13 +26,13 @@ class User public function loadUser(): bool { - if (isset($_SESSION['userID'])) { + if (isset($_SESSION['userID']) || $this->username !== 'GUEST') { // try to load user from id! $user = DatabaseHandler::get()->getOne("SELECT * FROM users WHERE id = :id OR username = :name AND isActive = 1", [ ':id' => $_SESSION['userID'], ':name' => $this->username ]); - if ($user) { + if ($user !== null) { $this->username = $user->username ?? ''; $this->email = $user->email ?? ''; $this->password = $user->password ?? ''; diff --git a/src/Venom/Security/BaseLogin.php b/src/Venom/Security/BaseLogin.php index 6fc404b..e4de7e9 100644 --- a/src/Venom/Security/BaseLogin.php +++ b/src/Venom/Security/BaseLogin.php @@ -30,7 +30,9 @@ class BaseLogin implements Login public function redirect(): void { - http_redirect(URLHelper::getInstance()->getUrl(), ['redirect' => 'true'], true); + $url = ArgumentHandler::get()->getPostItem('REDIRECT_TO', URLHelper::getInstance()->getUrl()); + header('Location: ' . $url); + die(); } public function login(): bool diff --git a/src/Venom/Security/Security.php b/src/Venom/Security/Security.php index ab2742f..dcdacab 100644 --- a/src/Venom/Security/Security.php +++ b/src/Venom/Security/Security.php @@ -3,7 +3,7 @@ namespace Venom\Security; -use http\Exception\RuntimeException; +use \RuntimeException; use Venom\Core\Config; use Venom\Models\User; @@ -43,11 +43,11 @@ class Security public function login(): void { - if (!$this->user->isLoaded()) { + if ($this->user->isLoaded()) { throw new RuntimeException('Try to re-login!'); } $sec = Config::getInstance()->getSecurity(); - $login = new $sec->securityClass; + $login = new $sec->securityClass($this->user); if ($login instanceof Login) { if (!$login->checkCredentials() || !$login->login()) { http_response_code(401); diff --git a/tpl/admin/base.php b/tpl/admin/base.php index 626f2cf..d6eedb6 100644 --- a/tpl/admin/base.php +++ b/tpl/admin/base.php @@ -4,7 +4,16 @@ use Venom\Models\User; use \Venom\Security\Security; if (!Security::get()->hasRole(User::ADMIN_ROLE)) { + ?> +
+ + + + +
+ Ausloggen'; } From a2931d93f71464eb988c93a6711d09627f8c1d9d Mon Sep 17 00:00:00 2001 From: engineerTrooper Date: Mon, 5 Oct 2020 20:02:43 +0200 Subject: [PATCH 05/33] VENOM-2 : WIP --- public/theme/admin/css/login.css | 1 + public/theme/admin/css/style.css | 1 + public/theme/admin/css/test.css | 22 --- public/theme/admin/js/scripts.js | 250 ++++++++++++++++++++++++++++ public/theme/admin/js/test.js | 0 src/Venom/Admin/AdminController.php | 12 ++ src/Venom/Views/Asset.php | 7 +- tpl/admin/base.php | 32 ++-- tpl/admin/login.php | 23 +++ 9 files changed, 311 insertions(+), 37 deletions(-) create mode 100644 public/theme/admin/css/login.css create mode 100644 public/theme/admin/css/style.css delete mode 100644 public/theme/admin/css/test.css create mode 100644 public/theme/admin/js/scripts.js delete mode 100644 public/theme/admin/js/test.js create mode 100644 tpl/admin/login.php diff --git a/public/theme/admin/css/login.css b/public/theme/admin/css/login.css new file mode 100644 index 0000000..92d45bb --- /dev/null +++ b/public/theme/admin/css/login.css @@ -0,0 +1 @@ +*{box-sizing:border-box}body{display:flex;align-items:center;justify-content:center;height:100vh;margin:0 auto;width:calc(100% - 20px);background-color:#303030;color:#fff;font-family:sans-serif;font-size:16px}main{display:flex;align-items:center;justify-content:center;position:relative;max-width:540px;width:90%;height:480px;background-color:#424242;color:#fff;border-radius:3px;border-bottom:#007769 solid 20px;box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23)}main:after{z-index:-1;content:'';position:absolute;top:0;left:0;width:calc(100% + 10px);height:calc(100% + 10px);background-color:#3949ab;transform:translate(-5px,-5px) rotate(-5deg);box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23)}p{font-size:1.4rem;text-align:center}a{font-size:.7rem;color:#fff;text-decoration:none;border-bottom:1px solid transparent}a:hover{border-bottom-color:#c51162}form{margin:0 auto;width:80%;min-width:240px}.input-group input{background-color:rgba(0,0,0,.4);border:none;border-bottom:2px solid #3949ab;color:#fff;padding:6px}.input-group input:focus,.input-group.focus input{border-color:#ff0089}.input-group.valid input{border-color:#39ab48}.input-group.invalid input{border-color:#cf1b1b}.input-group{display:flex;flex-direction:column;margin-bottom:3px;position:relative;padding-top:.5rem}.input-group label{font-size:.6rem;position:absolute;top:.5rem;left:6px;height:1rem;vertical-align:middle;transform:translateY(50%);color:#dcdcdc;transition:all .2s ease-out}.input-group input:focus~label,.input-group.focus label{top:0;left:0;transform:translateY(0);font-size:.4rem}.input-group .error{display:none}.input-group.invalid .error{margin-top:2px;display:block;font-size:.5rem;color:#ff3232}.warning{background-color:#c51162} \ No newline at end of file diff --git a/public/theme/admin/css/style.css b/public/theme/admin/css/style.css new file mode 100644 index 0000000..5dd4324 --- /dev/null +++ b/public/theme/admin/css/style.css @@ -0,0 +1 @@ +.btn{border:none;background:#3949ab radial-gradient(circle at 0 0,#3949ab 0,rgba(0,0,0,.2) 100%) no-repeat;color:#fff;padding:5px 15px;margin:10px 0;cursor:pointer;position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;border-radius:4px;box-shadow:0 2px 5px 0 rgba(0,0,0,.26);overflow:hidden;display:flex;justify-content:center;align-items:center;transition:.5s}.btn--outline{background:0 0;border:1px solid #3949ab}.btn:focus{box-shadow:0 3px 8px 1px rgba(0,0,0,.4)}.btn--accent{background:#ff0089 radial-gradient(circle at 0 0,#ff0089 0,rgba(0,0,0,.2) 100%) no-repeat}.btn--warn{background:#f87229 radial-gradient(circle at 0 0,#f87229 0,rgba(0,0,0,.2) 100%) no-repeat}.btn-fab{border-radius:2rem;width:2em;height:2em;padding:5px}.btn-ripple{position:absolute;top:0;left:0;width:100%;height:100%;overflow:hidden;background:0 0}.btn-ripple__effect{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);opacity:1;width:200%;height:0;padding-bottom:200%;border-radius:50%;background:rgba(190,190,190,.3);-webkit-animation:a-ripple .4s ease-in;animation:a-ripple .4s ease-in}.btn-ripple__effect.to-remove{-webkit-animation:remove-ripple .2s linear;animation:remove-ripple .2s linear}.btn--outline .btn-ripple__effect{background:rgba(57,73,171,.5);z-index:-1}.btn__content{z-index:1;font-weight:400;pointer-events:none}@-webkit-keyframes remove-ripple{from{opacity:1}to{opacity:0}}@keyframes remove-ripple{from{opacity:1}to{opacity:0}}@-webkit-keyframes a-ripple{0%{opacity:0;padding-bottom:0;width:0}25%{opacity:1}100%{width:200%;padding-bottom:200%}}@keyframes a-ripple{0%{opacity:0;padding-bottom:0;width:0}25%{opacity:1}100%{width:200%;padding-bottom:200%}} \ No newline at end of file diff --git a/public/theme/admin/css/test.css b/public/theme/admin/css/test.css deleted file mode 100644 index cfed392..0000000 --- a/public/theme/admin/css/test.css +++ /dev/null @@ -1,22 +0,0 @@ -* { - box-sizing: border-box; -} - -html, body { - margin: 0; - font-size: 16px; - color: #fff; - background-color: #333; - font-family: sans-serif; - height: 100vh; - width: 100vw; -} - -header { - font-size: 5vw; - cursor: pointer; -} - -header:hover { - color: #ff0323; -} \ No newline at end of file diff --git a/public/theme/admin/js/scripts.js b/public/theme/admin/js/scripts.js new file mode 100644 index 0000000..79e47cf --- /dev/null +++ b/public/theme/admin/js/scripts.js @@ -0,0 +1,250 @@ +class VUtils { + static makePublic() { + if (VUtils.isUsed) { + return; + } + this.initHandlers(); + VUtils.isUsed = true; + console.log("[VUtils] is now available in the Global Space! no VUtils. anymore needed"); + } + + static initHandlers() { + window.$ = this.$; + window.$$ = this.$$; + window.tryCatch = this.tryCatch; + VUtils.nodePrototypes(); + } + + static $(selector, from) { + from = from || document; + return from.querySelector(selector); + } + + static $$(selector, from) { + from = from || document; + return from.querySelectorAll(selector); + } + + static tryCatch(data, callback, error) { + data = VUtils.wrap(data, []); + error = error || console.error; + callback = callback || console.log; + try { + callback(...data); + } catch (e) { + error(e); + } + } + + static forEach(items, cb, error) { + for (let i = 0; i < items.length; i++) { + VUtils.tryCatch([items[i], i], cb, error); + } + } + + static get(valueOne, value) { + return this.wrap(valueOne, value); + } + + static mergeKeys(root, target) { + root = root || {}; + let keys = Object.keys(root); + for (let key of keys) { + target[key] = root[key]; + } + return target; + } + + static mergeOptions(target, root) { + root = root || {}; + let keys = Object.keys(root); + for (let key of keys) { + target[key] = VUtils.get(root[key], target[key]); + } + return target; + } + + static wrap(valueOne, valueTwo) { + let x = typeof valueTwo; + if (!(valueOne instanceof Array) && valueTwo instanceof Array) { + return [valueOne]; + } + if (x === 'string' && valueOne instanceof Array) { + return valueOne.join("."); + } + return valueOne === undefined ? valueTwo : valueOne; + } + + static nodePrototypes() { + Node.prototype.find = function (selector) { + return this.closest(selector); + }; + Node.prototype.createNew = function (tag, options) { + let el = document.createElement(tag); + el.classList.add(...VUtils.get(options.classes, [])); + el.id = VUtils.get(options.id, ''); + el.innerHTML = VUtils.get(options.content, ""); + VUtils.mergeKeys(options.dataset, el.dataset); + if (VUtils.get(options.append, true) === true) { + this.appendChild(el); + } + + return el; + }; + Node.prototype.addDelegatedEventListener = function (type, aim, callback, err) { + if (!callback || !type || !aim) + return; + this.addMultiListener(type, (event) => { + let target = event.target; + if (event.detail instanceof HTMLElement) { + target = event.detail; + } + if (target instanceof HTMLElement) { + if (target.matches(aim)) { + VUtils.tryCatch([event, target], callback, err); + } else { + const parent = target.find(aim); + if (parent) { + VUtils.tryCatch([event, parent], callback, err); + } + } + } + }); + }; + Node.prototype.addMultiListener = function (listener, cb, options = {}) { + let splits = listener.split(" "); + for (let split of splits) { + this.addEventListener(split, cb, options); + } + }; + } +} +VUtils.makePublic(); + + +class VRipple { + constructor(options = {}) { + if (!VUtils.isUsed) { + throw "VRipply is only with Public VUtils usable!" + } + let self = this; + self.options = JSON.parse('{"classes":["btn-ripple__effect"],"target":"body","selector":".btn-ripple"}'); + VUtils.mergeOptions(self.options, options); + if (self.options.selector.indexOf("#") > -1) { + throw "ID's are not allowed as selector!"; + } + this.instanceCheck(); + this.ripples = []; + requestAnimationFrame(this.initHandler.bind(this)); + } + + instanceCheck() { + let opts = this.options; + const rawKey = [opts.target, opts.selector, opts.classes.join(".")].join(" "); + VRipple.instances = VRipple.instances || {}; + VRipple.instances[rawKey] = this; + } + + initHandler() { + let self = this; + let selector = self.options.selector; + let target = $(self.options.target); + target.addDelegatedEventListener('mousedown touchstart', selector, (e, el) => { + let pos = e.touches ? e.touches[0] : e; + let parent = el.parentNode; + let circle = el.createNew('span', self.options); + let bounds = parent.getBoundingClientRect(); + let x = pos.clientX - bounds.left; + let y = pos.clientY - bounds.top; + circle.style.top = y + 'px'; + circle.style.left = x + 'px'; + circle._mouseDown = true; + circle._animationEnded = false; + self.ripples.push(circle); + }); + document.body.addDelegatedEventListener('animationend', '.' + VUtils.get(self.options.classes, ''), self.rippleEnd.bind(self)) + if (!document.body._vRippleInit) { + document.body.addMultiListener('mouseup touchend mouseleave rippleClose', e => { + let keys = Object.keys(VRipple.instances); + for (let key of keys) { + for (let ripple of VRipple.instances[key].ripples) { + self.rippleEnd.bind(VRipple.instances[key])(e, ripple); + } + } + }) + document.body._vRippleInit = true; + } + } + + rippleEnd(ev, el) { + const parent = el.parentNode; + if (parent) { + if (ev.type === 'animationend') { + el._animationEnded = true; + } else { + el._mouseDown = false; + } + if (!el._mouseDown && el._animationEnded) { + if (el.classList.contains('to-remove')) { + el.parentNode.removeChild(el); + this.ripples.splice(this.ripples.indexOf(el), 1) + } else { + el.classList.add('to-remove'); + } + } + } + } +} +const rippler = new VRipple(); +class FormHandler { + constructor(selector, parent, cb, err) { + this.cb = cb || console.log; + this.err = err || console.err; + $(parent).addDelegatedEventListener('submit', selector, this.handleEvent.bind(this)); + } + + handleEvent(e, el) { + e.preventDefault(); + if (el.checkValidity()) { + const url = el.action ?? ''; + if (url === '') { + console.error("No URL Found on Form", el); + return; + } + fetch(el.action, { + method: el.method.toUpperCase(), + credentials: 'same-origin', + body: new FormData(el), + redirect: 'manual' + }).then(res => { + if(!res.ok) { + throw new Error('Network response errored'); + } + return res.json() + }).then(this.cb).catch(this.err); + } + } +} +(function () { + const body = $('body'); + body.addDelegatedEventListener('change input', 'input', (e, el) => { + let parent = el.parentNode; + if (el.value === "") { + parent.classList.remove('focus') + } else { + parent.classList.add('focus') + } + if (el.checkValidity()) { + parent.classList.add('valid'); + parent.classList.remove('invalid'); + } else { + parent.classList.remove('valid'); + parent.classList.add('invalid'); + } + }) + if($('#login')) { + new FormHandler('form#login', 'body', e => { + console.log(e); + }) + } +})() \ No newline at end of file diff --git a/public/theme/admin/js/test.js b/public/theme/admin/js/test.js deleted file mode 100644 index e69de29..0000000 diff --git a/src/Venom/Admin/AdminController.php b/src/Venom/Admin/AdminController.php index 60ae195..80f37a4 100644 --- a/src/Venom/Admin/AdminController.php +++ b/src/Venom/Admin/AdminController.php @@ -5,8 +5,11 @@ namespace Venom\Admin; use Venom\Helper\URLHelper; +use Venom\Views\Asset; use Venom\Views\RenderController; use Venom\Views\VenomRenderer; +use Venom\Models\User; +use \Venom\Security\Security; class AdminController implements RenderController @@ -25,6 +28,15 @@ class AdminController implements RenderController http_response_code(404); $this->tpl = 'async'; } + + $isLogin = Security::get()->hasRole(User::ADMIN_ROLE); + $renderer->addVar('isLoggedIn', $isLogin); + if (!$isLogin) { + Asset::get()->addCSS('login','login.css'); + } + Asset::get()->addCSS('styles','style.css', 1); + Asset::get()->addJS('scripts', 'scripts.min.js', 1); + return true; } diff --git a/src/Venom/Views/Asset.php b/src/Venom/Views/Asset.php index c4c4e03..7233737 100755 --- a/src/Venom/Views/Asset.php +++ b/src/Venom/Views/Asset.php @@ -62,7 +62,7 @@ class Asset usort($this->jsFiles, function ($a, $b) { return $a['pos'] <=> $b['pos']; }); - $theme = $this->getPath('/theme/' . Config::getInstance()->getRenderer()->assetDir . '/js/'); + $theme = $this->getPath('/js/'); foreach ($this->jsFiles as $key => $file) { echo ''; } @@ -70,7 +70,8 @@ class Asset private function getPath($base): string { - $preDir = $base; + $dir = Config::getInstance()->isAdmin() ? 'admin' : Config::getInstance()->getRenderer()->assetDir; + $preDir = '/theme/' . $dir . $base; $config = Config::getInstance(); $baseUrl = Config::getInstance()->getBaseUrl(); if ($baseUrl !== '' && $config->getRenderer()->useStaticUrl) { @@ -84,7 +85,7 @@ class Asset usort($this->cssFiles, function ($a, $b) { return $a['pos'] <=> $b['pos']; }); - $theme = $this->getPath('/theme/' . Config::getInstance()->getRenderer()->assetDir . '/css/'); + $theme = $this->getPath('/css/'); foreach ($this->cssFiles as $key => $file) { echo ''; } diff --git a/tpl/admin/base.php b/tpl/admin/base.php index d6eedb6..0d7bda0 100644 --- a/tpl/admin/base.php +++ b/tpl/admin/base.php @@ -1,19 +1,27 @@ hasRole(User::ADMIN_ROLE)) { - ?> -
- - - - -
- + + + + + + + Venom Admin Interface + renderCSS(); ?> + + +getVar('isLoggedIn')) { + $this->renderTemplate('login'); } else { echo 'Admin Interface!'; echo 'Ausloggen'; } +Asset::get()->renderJS(); +?> + + diff --git a/tpl/admin/login.php b/tpl/admin/login.php new file mode 100644 index 0000000..50f49a0 --- /dev/null +++ b/tpl/admin/login.php @@ -0,0 +1,23 @@ +
+
+

- Admin Login -
Be Carefully!

+ +
+
+ + + Username is required +
+
+ + + Password is required +
+ +
+ Forgotten Password? [not active!] +
+
From 0baed1a7fc42efbcab68cfc927df2bb7ebe4f0f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maurice=20Gr=C3=B6nwoldt?= Date: Mon, 5 Oct 2020 20:38:36 +0200 Subject: [PATCH 06/33] VENOM-2: Fixed Login, fixed missing scripts.min.js, --- public/theme/admin/css/login.css | 2 +- public/theme/admin/css/style.css | 2 +- public/theme/admin/js/scripts.js | 26 +++++++++++++++++++++----- public/theme/admin/js/scripts.min.js | 1 + src/Venom/Admin/AdminController.php | 6 +++--- src/Venom/Helper/URLHelper.php | 2 +- src/Venom/Security/BaseLogin.php | 6 +++++- tpl/admin/login.php | 9 +++++---- 8 files changed, 38 insertions(+), 16 deletions(-) create mode 100644 public/theme/admin/js/scripts.min.js diff --git a/public/theme/admin/css/login.css b/public/theme/admin/css/login.css index 92d45bb..6b96953 100644 --- a/public/theme/admin/css/login.css +++ b/public/theme/admin/css/login.css @@ -1 +1 @@ -*{box-sizing:border-box}body{display:flex;align-items:center;justify-content:center;height:100vh;margin:0 auto;width:calc(100% - 20px);background-color:#303030;color:#fff;font-family:sans-serif;font-size:16px}main{display:flex;align-items:center;justify-content:center;position:relative;max-width:540px;width:90%;height:480px;background-color:#424242;color:#fff;border-radius:3px;border-bottom:#007769 solid 20px;box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23)}main:after{z-index:-1;content:'';position:absolute;top:0;left:0;width:calc(100% + 10px);height:calc(100% + 10px);background-color:#3949ab;transform:translate(-5px,-5px) rotate(-5deg);box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23)}p{font-size:1.4rem;text-align:center}a{font-size:.7rem;color:#fff;text-decoration:none;border-bottom:1px solid transparent}a:hover{border-bottom-color:#c51162}form{margin:0 auto;width:80%;min-width:240px}.input-group input{background-color:rgba(0,0,0,.4);border:none;border-bottom:2px solid #3949ab;color:#fff;padding:6px}.input-group input:focus,.input-group.focus input{border-color:#ff0089}.input-group.valid input{border-color:#39ab48}.input-group.invalid input{border-color:#cf1b1b}.input-group{display:flex;flex-direction:column;margin-bottom:3px;position:relative;padding-top:.5rem}.input-group label{font-size:.6rem;position:absolute;top:.5rem;left:6px;height:1rem;vertical-align:middle;transform:translateY(50%);color:#dcdcdc;transition:all .2s ease-out}.input-group input:focus~label,.input-group.focus label{top:0;left:0;transform:translateY(0);font-size:.4rem}.input-group .error{display:none}.input-group.invalid .error{margin-top:2px;display:block;font-size:.5rem;color:#ff3232}.warning{background-color:#c51162} \ No newline at end of file +body{display:flex;align-items:center;justify-content:center;height:100vh;width:calc(100% - 20px);font-size:2vw}main{display:flex;align-items:center;justify-content:center;position:relative;max-width:540px;width:90%;height:350px;background-color:#424242;color:#fff;border-radius:3px;border-bottom:#007769 solid 20px;box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23);transform-style:preserve-3d}main:after{content:'';position:absolute;top:0;left:0;width:calc(100% + 10px);height:calc(100% + 10px);background-color:#3949ab;transform:translate3d(-5px,-5px,-1px) rotate(-5deg);box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23)}p{font-size:1.4rem;text-align:center}a{font-size:.7rem;color:#fff;text-decoration:none;border-bottom:1px solid transparent}a:hover{border-bottom-color:#c51162}form{margin:0 auto;width:80%;min-width:240px}.input-group input{background-color:rgba(0,0,0,.4);border:none;border-bottom:2px solid #3949ab;color:#fff;padding:6px}.input-group input:focus,.input-group.focus input{border-color:#ff0089}.input-group.valid input{border-color:#39ab48}.input-group.invalid input{border-color:#cf1b1b}.input-group{display:flex;flex-direction:column;margin-bottom:3px;position:relative;padding-top:.7rem}.input-group label{font-size:.7rem;position:absolute;top:.7rem;left:6px;height:1rem;vertical-align:middle;transform:translateY(50%);color:#dcdcdc;transition:all .2s ease-out}.input-group input:focus~label,.input-group.focus label{top:0;left:0;transform:translateY(0);font-size:.6rem}.input-group .error{display:none}.input-group.invalid .error{margin-top:2px;display:block;font-size:.5rem;color:#ff3232}.error-message{background-color:#c51162;padding:1rem;font-size:.7rem} \ No newline at end of file diff --git a/public/theme/admin/css/style.css b/public/theme/admin/css/style.css index 5dd4324..5b3f6e4 100644 --- a/public/theme/admin/css/style.css +++ b/public/theme/admin/css/style.css @@ -1 +1 @@ -.btn{border:none;background:#3949ab radial-gradient(circle at 0 0,#3949ab 0,rgba(0,0,0,.2) 100%) no-repeat;color:#fff;padding:5px 15px;margin:10px 0;cursor:pointer;position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;border-radius:4px;box-shadow:0 2px 5px 0 rgba(0,0,0,.26);overflow:hidden;display:flex;justify-content:center;align-items:center;transition:.5s}.btn--outline{background:0 0;border:1px solid #3949ab}.btn:focus{box-shadow:0 3px 8px 1px rgba(0,0,0,.4)}.btn--accent{background:#ff0089 radial-gradient(circle at 0 0,#ff0089 0,rgba(0,0,0,.2) 100%) no-repeat}.btn--warn{background:#f87229 radial-gradient(circle at 0 0,#f87229 0,rgba(0,0,0,.2) 100%) no-repeat}.btn-fab{border-radius:2rem;width:2em;height:2em;padding:5px}.btn-ripple{position:absolute;top:0;left:0;width:100%;height:100%;overflow:hidden;background:0 0}.btn-ripple__effect{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);opacity:1;width:200%;height:0;padding-bottom:200%;border-radius:50%;background:rgba(190,190,190,.3);-webkit-animation:a-ripple .4s ease-in;animation:a-ripple .4s ease-in}.btn-ripple__effect.to-remove{-webkit-animation:remove-ripple .2s linear;animation:remove-ripple .2s linear}.btn--outline .btn-ripple__effect{background:rgba(57,73,171,.5);z-index:-1}.btn__content{z-index:1;font-weight:400;pointer-events:none}@-webkit-keyframes remove-ripple{from{opacity:1}to{opacity:0}}@keyframes remove-ripple{from{opacity:1}to{opacity:0}}@-webkit-keyframes a-ripple{0%{opacity:0;padding-bottom:0;width:0}25%{opacity:1}100%{width:200%;padding-bottom:200%}}@keyframes a-ripple{0%{opacity:0;padding-bottom:0;width:0}25%{opacity:1}100%{width:200%;padding-bottom:200%}} \ No newline at end of file +:focus{outline:0}*{box-sizing:border-box}body,html{padding:0;margin:0;background-color:#303030;color:#fff;font-family:sans-serif;font-size:16px}.hide{display:none!important}.btn{border:none;background:#3949ab radial-gradient(circle at 0 0,#3949ab 0,rgba(0,0,0,.2) 100%) no-repeat;color:#fff;padding:5px 15px;margin:10px 0;cursor:pointer;position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;border-radius:4px;box-shadow:0 2px 5px 0 rgba(0,0,0,.26);overflow:hidden;display:flex;justify-content:center;align-items:center;transition:.5s}.btn--outline{background:0 0;border:1px solid #3949ab}.btn:focus{box-shadow:0 3px 8px 1px rgba(0,0,0,.4)}.btn--accent{background:#ff0089 radial-gradient(circle at 0 0,#ff0089 0,rgba(0,0,0,.2) 100%) no-repeat}.btn--warn{background:#f87229 radial-gradient(circle at 0 0,#f87229 0,rgba(0,0,0,.2) 100%) no-repeat}.btn-fab{border-radius:2rem;width:2em;height:2em;padding:5px}.btn-ripple{position:absolute;top:0;left:0;width:100%;height:100%;overflow:hidden;background:0 0}.btn-ripple__effect{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);opacity:1;width:200%;height:0;padding-bottom:200%;border-radius:50%;background:rgba(190,190,190,.3);-webkit-animation:a-ripple .4s ease-in;animation:a-ripple .4s ease-in}.btn-ripple__effect.to-remove{-webkit-animation:remove-ripple .2s linear;animation:remove-ripple .2s linear}.btn--outline .btn-ripple__effect{background:rgba(57,73,171,.5);z-index:-1}.btn__content{z-index:1;font-weight:400;pointer-events:none}@-webkit-keyframes remove-ripple{from{opacity:1}to{opacity:0}}@keyframes remove-ripple{from{opacity:1}to{opacity:0}}@-webkit-keyframes a-ripple{0%{opacity:0;padding-bottom:0;width:0}25%{opacity:1}100%{width:200%;padding-bottom:200%}}@keyframes a-ripple{0%{opacity:0;padding-bottom:0;width:0}25%{opacity:1}100%{width:200%;padding-bottom:200%}} \ No newline at end of file diff --git a/public/theme/admin/js/scripts.js b/public/theme/admin/js/scripts.js index 79e47cf..6aa374b 100644 --- a/public/theme/admin/js/scripts.js +++ b/public/theme/admin/js/scripts.js @@ -217,17 +217,30 @@ class FormHandler { body: new FormData(el), redirect: 'manual' }).then(res => { - if(!res.ok) { + if (!res.ok) { throw new Error('Network response errored'); } return res.json() - }).then(this.cb).catch(this.err); + }).then(ev => this.cb(ev, el)).catch(ev => this.err(ev, el)); + } else { + VUtils.forEach($$('input', el), ele => { + if (!ele.checkValidity()) { + let parent = ele.parentNode; + parent.classList.remove('valid'); + parent.classList.add('invalid'); + } + }); } } } + (function () { const body = $('body'); body.addDelegatedEventListener('change input', 'input', (e, el) => { + let errorMessage = $('.error-message', el.find('form')); + if (errorMessage) { + errorMessage.classList.add('hide') + } let parent = el.parentNode; if (el.value === "") { parent.classList.remove('focus') @@ -242,9 +255,12 @@ class FormHandler { parent.classList.add('invalid'); } }) - if($('#login')) { - new FormHandler('form#login', 'body', e => { - console.log(e); + + if ($('#login')) { + new FormHandler('form#login', 'body', () => { + location.reload(); + }, (e, el) => { + $('.error-message', el).classList.remove('hide'); }) } })() \ No newline at end of file diff --git a/public/theme/admin/js/scripts.min.js b/public/theme/admin/js/scripts.min.js new file mode 100644 index 0000000..47e60ed --- /dev/null +++ b/public/theme/admin/js/scripts.min.js @@ -0,0 +1 @@ +class VUtils{static makePublic(){VUtils.isUsed||(this.initHandlers(),VUtils.isUsed=!0,console.log("[VUtils] is now available in the Global Space! no VUtils. anymore needed"))}static initHandlers(){window.$=this.$,window.$$=this.$$,window.tryCatch=this.tryCatch,VUtils.nodePrototypes()}static $(e,t){return(t=t||document).querySelector(e)}static $$(e,t){return(t=t||document).querySelectorAll(e)}static tryCatch(e,t,i){e=VUtils.wrap(e,[]),i=i||console.error,t=t||console.log;try{t(...e)}catch(e){i(e)}}static forEach(e,t,i){for(let s=0;s{let n=e.target;if(e.detail instanceof HTMLElement&&(n=e.detail),n instanceof HTMLElement)if(n.matches(t))VUtils.tryCatch([e,n],i,s);else{const o=n.find(t);o&&VUtils.tryCatch([e,o],i,s)}})},Node.prototype.addMultiListener=function(e,t,i={}){let s=e.split(" ");for(let e of s)this.addEventListener(e,t,i)}}}VUtils.makePublic();class VRipple{constructor(e={}){if(!VUtils.isUsed)throw"VRipply is only with Public VUtils usable!";let t=this;if(t.options=JSON.parse('{"classes":["btn-ripple__effect"],"target":"body","selector":".btn-ripple"}'),VUtils.mergeOptions(t.options,e),t.options.selector.indexOf("#")>-1)throw"ID's are not allowed as selector!";this.instanceCheck(),this.ripples=[],requestAnimationFrame(this.initHandler.bind(this))}instanceCheck(){let e=this.options;const t=[e.target,e.selector,e.classes.join(".")].join(" ");VRipple.instances=VRipple.instances||{},VRipple.instances[t]=this}initHandler(){let e=this,t=e.options.selector;$(e.options.target).addDelegatedEventListener("mousedown touchstart",t,(t,i)=>{let s=t.touches?t.touches[0]:t,n=i.parentNode,o=i.createNew("span",e.options),l=n.getBoundingClientRect(),r=s.clientX-l.left,a=s.clientY-l.top;o.style.top=a+"px",o.style.left=r+"px",o._mouseDown=!0,o._animationEnded=!1,e.ripples.push(o)}),document.body.addDelegatedEventListener("animationend","."+VUtils.get(e.options.classes,""),e.rippleEnd.bind(e)),document.body._vRippleInit||(document.body.addMultiListener("mouseup touchend mouseleave rippleClose",t=>{let i=Object.keys(VRipple.instances);for(let s of i)for(let i of VRipple.instances[s].ripples)e.rippleEnd.bind(VRipple.instances[s])(t,i)}),document.body._vRippleInit=!0)}rippleEnd(e,t){t.parentNode&&("animationend"===e.type?t._animationEnded=!0:t._mouseDown=!1,!t._mouseDown&&t._animationEnded&&(t.classList.contains("to-remove")?(t.parentNode.removeChild(t),this.ripples.splice(this.ripples.indexOf(t),1)):t.classList.add("to-remove")))}}const rippler=new VRipple;class FormHandler{constructor(e,t,i,s){this.cb=i||console.log,this.err=s||console.err,$(t).addDelegatedEventListener("submit",e,this.handleEvent.bind(this))}handleEvent(e,t){if(e.preventDefault(),t.checkValidity()){if(""===(t.action??""))return void console.error("No URL Found on Form",t);fetch(t.action,{method:t.method.toUpperCase(),credentials:"same-origin",body:new FormData(t),redirect:"manual"}).then(e=>{if(!e.ok)throw new Error("Network response errored");return e.json()}).then(e=>this.cb(e,t)).catch(e=>this.err(e,t))}else VUtils.forEach($$("input",t),e=>{if(!e.checkValidity()){let t=e.parentNode;t.classList.remove("valid"),t.classList.add("invalid")}})}}$("body").addDelegatedEventListener("change input","input",(e,t)=>{let i=$(".error-message",t.find("form"));i&&i.classList.add("hide");let s=t.parentNode;""===t.value?s.classList.remove("focus"):s.classList.add("focus"),t.checkValidity()?(s.classList.add("valid"),s.classList.remove("invalid")):(s.classList.remove("valid"),s.classList.add("invalid"))}),$("#login")&&new FormHandler("form#login","body",()=>{location.reload()},(e,t)=>{$(".error-message",t).classList.remove("hide")}); \ No newline at end of file diff --git a/src/Venom/Admin/AdminController.php b/src/Venom/Admin/AdminController.php index 80f37a4..62d9235 100644 --- a/src/Venom/Admin/AdminController.php +++ b/src/Venom/Admin/AdminController.php @@ -24,7 +24,7 @@ class AdminController implements RenderController public function render(VenomRenderer $renderer): bool { - if (URLHelper::getInstance()->getUrl() !== '/admin/') { + if (!in_array(URLHelper::getInstance()->getUrl(), ['/admin/', '/admin'])) { http_response_code(404); $this->tpl = 'async'; } @@ -32,9 +32,9 @@ class AdminController implements RenderController $isLogin = Security::get()->hasRole(User::ADMIN_ROLE); $renderer->addVar('isLoggedIn', $isLogin); if (!$isLogin) { - Asset::get()->addCSS('login','login.css'); + Asset::get()->addCSS('login', 'login.css'); } - Asset::get()->addCSS('styles','style.css', 1); + Asset::get()->addCSS('styles', 'style.css', 1); Asset::get()->addJS('scripts', 'scripts.min.js', 1); return true; diff --git a/src/Venom/Helper/URLHelper.php b/src/Venom/Helper/URLHelper.php index 9891eaa..697b8fa 100644 --- a/src/Venom/Helper/URLHelper.php +++ b/src/Venom/Helper/URLHelper.php @@ -45,6 +45,6 @@ class URLHelper public function isAdminUrl(): bool { - return strpos($this->parsedUrl, '/admin/') === 0; + return strpos($this->parsedUrl, '/admin') === 0; } } \ No newline at end of file diff --git a/src/Venom/Security/BaseLogin.php b/src/Venom/Security/BaseLogin.php index e4de7e9..5343391 100644 --- a/src/Venom/Security/BaseLogin.php +++ b/src/Venom/Security/BaseLogin.php @@ -31,7 +31,11 @@ class BaseLogin implements Login public function redirect(): void { $url = ArgumentHandler::get()->getPostItem('REDIRECT_TO', URLHelper::getInstance()->getUrl()); - header('Location: ' . $url); + if($url === 'NO') { + echo json_encode(['message' => 'login'], JSON_THROW_ON_ERROR); + } else { + header('Location: ' . $url); + } die(); } diff --git a/tpl/admin/login.php b/tpl/admin/login.php index 50f49a0..62eff0c 100644 --- a/tpl/admin/login.php +++ b/tpl/admin/login.php @@ -1,15 +1,16 @@

- Admin Login -
Be Carefully!

-
+

Login Failed

+
- - + + Username is required
- + Password is required
From 597be3ce2ee907833f72e3418ca2f8fc29935bea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maurice=20Gr=C3=B6nwoldt?= Date: Tue, 6 Oct 2020 16:40:10 +0200 Subject: [PATCH 07/33] added logo (pls move to theme folder!) added svg logo from webinterface --- public/content/logo.svg | 1 + 1 file changed, 1 insertion(+) create mode 100644 public/content/logo.svg diff --git a/public/content/logo.svg b/public/content/logo.svg new file mode 100644 index 0000000..3c3a39a --- /dev/null +++ b/public/content/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file From 8b87c7d2f78dd7048f1abb54438fa1023321048b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maurice=20Gr=C3=B6nwoldt?= Date: Wed, 21 Oct 2020 17:14:08 +0200 Subject: [PATCH 08/33] VENOM-2: do new login <3 --- public/content/logo.svg | 1 - public/theme/admin/css/login.css | 2 +- public/theme/admin/css/style.css | 2 +- public/theme/admin/images/logo.svg | 10 +++++++ tpl/admin/login.php | 45 +++++++++++++++--------------- 5 files changed, 34 insertions(+), 26 deletions(-) delete mode 100644 public/content/logo.svg create mode 100644 public/theme/admin/images/logo.svg diff --git a/public/content/logo.svg b/public/content/logo.svg deleted file mode 100644 index 3c3a39a..0000000 --- a/public/content/logo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/theme/admin/css/login.css b/public/theme/admin/css/login.css index 6b96953..5bdcb6b 100644 --- a/public/theme/admin/css/login.css +++ b/public/theme/admin/css/login.css @@ -1 +1 @@ -body{display:flex;align-items:center;justify-content:center;height:100vh;width:calc(100% - 20px);font-size:2vw}main{display:flex;align-items:center;justify-content:center;position:relative;max-width:540px;width:90%;height:350px;background-color:#424242;color:#fff;border-radius:3px;border-bottom:#007769 solid 20px;box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23);transform-style:preserve-3d}main:after{content:'';position:absolute;top:0;left:0;width:calc(100% + 10px);height:calc(100% + 10px);background-color:#3949ab;transform:translate3d(-5px,-5px,-1px) rotate(-5deg);box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23)}p{font-size:1.4rem;text-align:center}a{font-size:.7rem;color:#fff;text-decoration:none;border-bottom:1px solid transparent}a:hover{border-bottom-color:#c51162}form{margin:0 auto;width:80%;min-width:240px}.input-group input{background-color:rgba(0,0,0,.4);border:none;border-bottom:2px solid #3949ab;color:#fff;padding:6px}.input-group input:focus,.input-group.focus input{border-color:#ff0089}.input-group.valid input{border-color:#39ab48}.input-group.invalid input{border-color:#cf1b1b}.input-group{display:flex;flex-direction:column;margin-bottom:3px;position:relative;padding-top:.7rem}.input-group label{font-size:.7rem;position:absolute;top:.7rem;left:6px;height:1rem;vertical-align:middle;transform:translateY(50%);color:#dcdcdc;transition:all .2s ease-out}.input-group input:focus~label,.input-group.focus label{top:0;left:0;transform:translateY(0);font-size:.6rem}.input-group .error{display:none}.input-group.invalid .error{margin-top:2px;display:block;font-size:.5rem;color:#ff3232}.error-message{background-color:#c51162;padding:1rem;font-size:.7rem} \ No newline at end of file +*{box-sizing:border-box}:focus{outline:0}body,html{width:100vw;height:100vh;overflow:hidden;color:#fff;display:flex;justify-content:center;align-items:center;margin:0;padding:0;background:#1f2857;background:linear-gradient(144deg,#1f2954 25%,#000 50%,#5e002c 80%);background-attachment:fixed}.hide{display:none!important}#login-background{background-color:rgba(31,31,31,.25);-webkit-filter:blur(10px);filter:blur(10px);position:absolute;top:0;left:0;width:100%;height:100%}login{display:flex;position:relative;max-width:860px;width:90%;height:400px;padding:1rem;border-radius:10px 40px;overflow:hidden;box-shadow:0 10px 20px rgba(0,0,0,.19),0 6px 6px rgba(0,0,0,.23)}#login{z-index:1;display:flex;flex-direction:column;align-items:center;width:100%}#login .logo{width:50%;margin-bottom:20px}#login .error-message{background-color:#c51162;padding:1rem;font-size:.7rem;display:block;width:50%}#login .input-group{width:50%}#login a{text-decoration:none;color:#fff}#login a:hover{color:#3949ab} \ No newline at end of file diff --git a/public/theme/admin/css/style.css b/public/theme/admin/css/style.css index 5b3f6e4..aa0e085 100644 --- a/public/theme/admin/css/style.css +++ b/public/theme/admin/css/style.css @@ -1 +1 @@ -:focus{outline:0}*{box-sizing:border-box}body,html{padding:0;margin:0;background-color:#303030;color:#fff;font-family:sans-serif;font-size:16px}.hide{display:none!important}.btn{border:none;background:#3949ab radial-gradient(circle at 0 0,#3949ab 0,rgba(0,0,0,.2) 100%) no-repeat;color:#fff;padding:5px 15px;margin:10px 0;cursor:pointer;position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;border-radius:4px;box-shadow:0 2px 5px 0 rgba(0,0,0,.26);overflow:hidden;display:flex;justify-content:center;align-items:center;transition:.5s}.btn--outline{background:0 0;border:1px solid #3949ab}.btn:focus{box-shadow:0 3px 8px 1px rgba(0,0,0,.4)}.btn--accent{background:#ff0089 radial-gradient(circle at 0 0,#ff0089 0,rgba(0,0,0,.2) 100%) no-repeat}.btn--warn{background:#f87229 radial-gradient(circle at 0 0,#f87229 0,rgba(0,0,0,.2) 100%) no-repeat}.btn-fab{border-radius:2rem;width:2em;height:2em;padding:5px}.btn-ripple{position:absolute;top:0;left:0;width:100%;height:100%;overflow:hidden;background:0 0}.btn-ripple__effect{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);opacity:1;width:200%;height:0;padding-bottom:200%;border-radius:50%;background:rgba(190,190,190,.3);-webkit-animation:a-ripple .4s ease-in;animation:a-ripple .4s ease-in}.btn-ripple__effect.to-remove{-webkit-animation:remove-ripple .2s linear;animation:remove-ripple .2s linear}.btn--outline .btn-ripple__effect{background:rgba(57,73,171,.5);z-index:-1}.btn__content{z-index:1;font-weight:400;pointer-events:none}@-webkit-keyframes remove-ripple{from{opacity:1}to{opacity:0}}@keyframes remove-ripple{from{opacity:1}to{opacity:0}}@-webkit-keyframes a-ripple{0%{opacity:0;padding-bottom:0;width:0}25%{opacity:1}100%{width:200%;padding-bottom:200%}}@keyframes a-ripple{0%{opacity:0;padding-bottom:0;width:0}25%{opacity:1}100%{width:200%;padding-bottom:200%}} \ No newline at end of file +:focus{outline:0}*{box-sizing:border-box}body,html{padding:0;margin:0;background-color:#303030;color:#fff;font-family:sans-serif;font-size:16px}.hide{display:none!important}.btn{border:none;background:#3949ab radial-gradient(circle at 0 0,#3949ab 0,rgba(0,0,0,.2) 100%) no-repeat;color:#fff;padding:5px 15px;margin:10px 0;cursor:pointer;position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;border-radius:4px;box-shadow:0 2px 5px 0 rgba(0,0,0,.26);overflow:hidden;display:flex;justify-content:center;align-items:center;transition:.5s}.btn--outline{background:0 0;border:1px solid #3949ab}.btn:focus{box-shadow:0 3px 8px 1px rgba(0,0,0,.4)}.btn--accent{background:#ff0089 radial-gradient(circle at 0 0,#ff0089 0,rgba(0,0,0,.2) 100%) no-repeat}.btn--warn{background:#f87229 radial-gradient(circle at 0 0,#f87229 0,rgba(0,0,0,.2) 100%) no-repeat}.btn-fab{border-radius:2rem;width:2em;height:2em;padding:5px}.btn-ripple{position:absolute;top:0;left:0;width:100%;height:100%;overflow:hidden;background:0 0}.btn-ripple__effect{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);opacity:1;width:200%;height:0;padding-bottom:200%;border-radius:50%;background:rgba(190,190,190,.3);-webkit-animation:a-ripple .4s ease-in;animation:a-ripple .4s ease-in}.btn-ripple__effect.to-remove{-webkit-animation:remove-ripple .2s linear;animation:remove-ripple .2s linear}.btn--outline .btn-ripple__effect{background:rgba(57,73,171,.5);z-index:-1}.btn__content{z-index:1;font-weight:400;pointer-events:none}@-webkit-keyframes remove-ripple{from{opacity:1}to{opacity:0}}@keyframes remove-ripple{from{opacity:1}to{opacity:0}}@-webkit-keyframes a-ripple{0%{opacity:0;padding-bottom:0;width:0}25%{opacity:1}100%{width:200%;padding-bottom:200%}}@keyframes a-ripple{0%{opacity:0;padding-bottom:0;width:0}25%{opacity:1}100%{width:200%;padding-bottom:200%}}.input-group input{background-color:rgba(0,0,0,.4);border:none;border-bottom:2px solid #3949ab;color:#fff;padding:6px}.input-group input:focus,.input-group.focus input{border-color:#ff0089}.input-group.valid input{border-color:#39ab48}.input-group.invalid input{border-color:#cf1b1b}.input-group{display:flex;flex-direction:column;margin-bottom:3px;position:relative;padding-top:.7rem}.input-group label{font-size:.7rem;position:absolute;top:.7rem;left:6px;height:1rem;vertical-align:middle;transform:translateY(50%);color:#dcdcdc;transition:all .2s ease-out}.input-group input:focus~label,.input-group.focus label{top:0;left:0;transform:translateY(0);font-size:.6rem}.input-group .error{display:none}.input-group.invalid .error{margin-top:2px;display:block;font-size:.5rem;color:#ff3232} \ No newline at end of file diff --git a/public/theme/admin/images/logo.svg b/public/theme/admin/images/logo.svg new file mode 100644 index 0000000..dfd6f7c --- /dev/null +++ b/public/theme/admin/images/logo.svg @@ -0,0 +1,10 @@ + + \ No newline at end of file diff --git a/tpl/admin/login.php b/tpl/admin/login.php index 62eff0c..68035ac 100644 --- a/tpl/admin/login.php +++ b/tpl/admin/login.php @@ -1,24 +1,23 @@ -
-
-

- Admin Login -
Be Carefully!

- -

Login Failed

- -
- - - Username is required -
-
- - - Password is required -
- - + +
+
+ +

Login Failed

+ +
+ + + Username is required +
+
+ + + Password is required +
+ Forgotten Password? [not active!] -
-
+ + From 162cc4b0a46ee99856e87e2c6d241c14f81359ce Mon Sep 17 00:00:00 2001 From: engineerTrooper Date: Sun, 1 Nov 2020 18:59:05 +0100 Subject: [PATCH 09/33] VENOM-10 : added design (first step) --- public/theme/admin/css/admin-panel.css | 1 + public/theme/admin/css/login.css | 2 +- public/theme/admin/css/style.css | 2 +- .../icon/ic_add_circle_outline_24px.svg | 1 + .../admin/images/icon/ic_check_box_24px.svg | 1 + .../theme/admin/images/icon/ic_edit_24px.svg | 1 + .../admin/images/icon/ic_visibility_24px.svg | 1 + src/Venom/Admin/AdminController.php | 2 + tpl/admin/admin-panel.php | 144 ++++++++++++++++++ tpl/admin/base.php | 4 +- 10 files changed, 155 insertions(+), 4 deletions(-) create mode 100644 public/theme/admin/css/admin-panel.css create mode 100644 public/theme/admin/images/icon/ic_add_circle_outline_24px.svg create mode 100644 public/theme/admin/images/icon/ic_check_box_24px.svg create mode 100644 public/theme/admin/images/icon/ic_edit_24px.svg create mode 100644 public/theme/admin/images/icon/ic_visibility_24px.svg create mode 100644 tpl/admin/admin-panel.php diff --git a/public/theme/admin/css/admin-panel.css b/public/theme/admin/css/admin-panel.css new file mode 100644 index 0000000..cc90666 --- /dev/null +++ b/public/theme/admin/css/admin-panel.css @@ -0,0 +1 @@ +:focus{outline:0}*{box-sizing:border-box}body,html{width:100vw;height:100vh;overflow:hidden;padding:0;margin:0;background:#1f2857;background:linear-gradient(144deg,#1f2954 25%,#000 50%,#5e002c 80%);background-attachment:fixed;color:#fff;font-family:sans-serif;font-size:16px}.hide{display:none!important}main{display:flex;height:100vh;overflow:hidden}.menu{width:220px;background-color:#1b1b1b;box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23);flex-shrink:0;display:flex;flex-direction:column}.menu .logo{text-align:center;font-family:monospace}.menu div[data-link]{padding:.75rem .5rem;position:relative}.menu div[data-link]:after{background-color:#3949ab;content:"";position:absolute;left:0;bottom:0;height:3px;width:100%;transform:scaleX(0);transition:transform .4s;transform-origin:left}.menu div[data-link]:hover:after{transform:scaleX(1)}.menu div[data-link]:last-child{margin-top:auto}.menu div[data-link].active{font-weight:700}.content-area{padding:0 1rem;margin:1rem 1.5rem;flex-grow:1;overflow-y:auto;max-height:100%;background:RGBA(27,27,27,.5)}.content-area .max-width{max-width:300px}.content-area hr{border:.5px solid #e9e9e9;box-shadow:0 10px 20px rgba(0,0,0,.19),0 6px 6px rgba(0,0,0,.23)}.content-area .text{display:flex;position:relative;padding:5px 0}.content-area .icon{display:inline-block;width:18px;height:18px;background-size:cover;margin:0 10px 0 auto;fill:#fff}.content-area .icon-add{background-image:url(../images/icon/ic_add_circle_outline_24px.svg)}.content-area .icon-edit{background-image:url(../images/icon/ic_edit_24px.svg)}.content-area .icon-visibility{background-image:url(../images/icon/ic_visibility_24px.svg)}.content-area .add-new-role{display:flex;position:relative;align-items:center;padding:0 0 5px 0} \ No newline at end of file diff --git a/public/theme/admin/css/login.css b/public/theme/admin/css/login.css index 5bdcb6b..92a1cd4 100644 --- a/public/theme/admin/css/login.css +++ b/public/theme/admin/css/login.css @@ -1 +1 @@ -*{box-sizing:border-box}:focus{outline:0}body,html{width:100vw;height:100vh;overflow:hidden;color:#fff;display:flex;justify-content:center;align-items:center;margin:0;padding:0;background:#1f2857;background:linear-gradient(144deg,#1f2954 25%,#000 50%,#5e002c 80%);background-attachment:fixed}.hide{display:none!important}#login-background{background-color:rgba(31,31,31,.25);-webkit-filter:blur(10px);filter:blur(10px);position:absolute;top:0;left:0;width:100%;height:100%}login{display:flex;position:relative;max-width:860px;width:90%;height:400px;padding:1rem;border-radius:10px 40px;overflow:hidden;box-shadow:0 10px 20px rgba(0,0,0,.19),0 6px 6px rgba(0,0,0,.23)}#login{z-index:1;display:flex;flex-direction:column;align-items:center;width:100%}#login .logo{width:50%;margin-bottom:20px}#login .error-message{background-color:#c51162;padding:1rem;font-size:.7rem;display:block;width:50%}#login .input-group{width:50%}#login a{text-decoration:none;color:#fff}#login a:hover{color:#3949ab} \ No newline at end of file +body,html{display:flex;justify-content:center;align-items:center}.hide{display:none!important}#login-background{background-color:rgba(31,31,31,.25);-webkit-filter:blur(10px);filter:blur(10px);position:absolute;top:0;left:0;width:100%;height:100%}login{display:flex;position:relative;max-width:860px;width:90%;height:400px;padding:1rem;border-radius:10px 40px;overflow:hidden;box-shadow:0 10px 20px rgba(0,0,0,.19),0 6px 6px rgba(0,0,0,.23)}#login{z-index:1;display:flex;flex-direction:column;align-items:center;width:100%}#login .logo{width:50%;margin-bottom:20px}#login .error-message{background-color:#c51162;padding:1rem;font-size:.7rem;display:block;width:50%}#login .input-group{width:50%}#login a{text-decoration:none;color:#fff}#login a:hover{color:#3949ab} \ No newline at end of file diff --git a/public/theme/admin/css/style.css b/public/theme/admin/css/style.css index aa0e085..19df622 100644 --- a/public/theme/admin/css/style.css +++ b/public/theme/admin/css/style.css @@ -1 +1 @@ -:focus{outline:0}*{box-sizing:border-box}body,html{padding:0;margin:0;background-color:#303030;color:#fff;font-family:sans-serif;font-size:16px}.hide{display:none!important}.btn{border:none;background:#3949ab radial-gradient(circle at 0 0,#3949ab 0,rgba(0,0,0,.2) 100%) no-repeat;color:#fff;padding:5px 15px;margin:10px 0;cursor:pointer;position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;border-radius:4px;box-shadow:0 2px 5px 0 rgba(0,0,0,.26);overflow:hidden;display:flex;justify-content:center;align-items:center;transition:.5s}.btn--outline{background:0 0;border:1px solid #3949ab}.btn:focus{box-shadow:0 3px 8px 1px rgba(0,0,0,.4)}.btn--accent{background:#ff0089 radial-gradient(circle at 0 0,#ff0089 0,rgba(0,0,0,.2) 100%) no-repeat}.btn--warn{background:#f87229 radial-gradient(circle at 0 0,#f87229 0,rgba(0,0,0,.2) 100%) no-repeat}.btn-fab{border-radius:2rem;width:2em;height:2em;padding:5px}.btn-ripple{position:absolute;top:0;left:0;width:100%;height:100%;overflow:hidden;background:0 0}.btn-ripple__effect{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);opacity:1;width:200%;height:0;padding-bottom:200%;border-radius:50%;background:rgba(190,190,190,.3);-webkit-animation:a-ripple .4s ease-in;animation:a-ripple .4s ease-in}.btn-ripple__effect.to-remove{-webkit-animation:remove-ripple .2s linear;animation:remove-ripple .2s linear}.btn--outline .btn-ripple__effect{background:rgba(57,73,171,.5);z-index:-1}.btn__content{z-index:1;font-weight:400;pointer-events:none}@-webkit-keyframes remove-ripple{from{opacity:1}to{opacity:0}}@keyframes remove-ripple{from{opacity:1}to{opacity:0}}@-webkit-keyframes a-ripple{0%{opacity:0;padding-bottom:0;width:0}25%{opacity:1}100%{width:200%;padding-bottom:200%}}@keyframes a-ripple{0%{opacity:0;padding-bottom:0;width:0}25%{opacity:1}100%{width:200%;padding-bottom:200%}}.input-group input{background-color:rgba(0,0,0,.4);border:none;border-bottom:2px solid #3949ab;color:#fff;padding:6px}.input-group input:focus,.input-group.focus input{border-color:#ff0089}.input-group.valid input{border-color:#39ab48}.input-group.invalid input{border-color:#cf1b1b}.input-group{display:flex;flex-direction:column;margin-bottom:3px;position:relative;padding-top:.7rem}.input-group label{font-size:.7rem;position:absolute;top:.7rem;left:6px;height:1rem;vertical-align:middle;transform:translateY(50%);color:#dcdcdc;transition:all .2s ease-out}.input-group input:focus~label,.input-group.focus label{top:0;left:0;transform:translateY(0);font-size:.6rem}.input-group .error{display:none}.input-group.invalid .error{margin-top:2px;display:block;font-size:.5rem;color:#ff3232} \ No newline at end of file +:focus{outline:0}*{box-sizing:border-box}body,html{width:100vw;height:100vh;overflow:hidden;padding:0;margin:0;background:#1f2857;background:linear-gradient(144deg,#1f2954 25%,#000 50%,#5e002c 80%);background-attachment:fixed;color:#fff;font-family:sans-serif;font-size:16px}.hide{display:none!important}.btn{border:none;background:#3949ab radial-gradient(circle at 0 0,#3949ab 0,rgba(0,0,0,.2) 100%) no-repeat;color:#fff;padding:5px 15px;margin:10px 0;cursor:pointer;position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;border-radius:4px;box-shadow:0 2px 5px 0 rgba(0,0,0,.26);overflow:hidden;display:flex;justify-content:center;align-items:center;transition:.5s}.btn--outline{background:0 0;border:1px solid #3949ab}.btn:focus{box-shadow:0 3px 8px 1px rgba(0,0,0,.4)}.btn--accent{background:#ff0089 radial-gradient(circle at 0 0,#ff0089 0,rgba(0,0,0,.2) 100%) no-repeat}.btn--warn{background:#f87229 radial-gradient(circle at 0 0,#f87229 0,rgba(0,0,0,.2) 100%) no-repeat}.btn-fab{border-radius:2rem;width:2em;height:2em;padding:5px}.btn-ripple{position:absolute;top:0;left:0;width:100%;height:100%;overflow:hidden;background:0 0}.btn-ripple__effect{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);opacity:1;width:200%;height:0;padding-bottom:200%;border-radius:50%;background:rgba(190,190,190,.3);-webkit-animation:a-ripple .4s ease-in;animation:a-ripple .4s ease-in}.btn-ripple__effect.to-remove{-webkit-animation:remove-ripple .2s linear;animation:remove-ripple .2s linear}.btn--outline .btn-ripple__effect{background:rgba(57,73,171,.5);z-index:-1}.btn__content{z-index:1;font-weight:400;pointer-events:none}@-webkit-keyframes remove-ripple{from{opacity:1}to{opacity:0}}@keyframes remove-ripple{from{opacity:1}to{opacity:0}}@-webkit-keyframes a-ripple{0%{opacity:0;padding-bottom:0;width:0}25%{opacity:1}100%{width:200%;padding-bottom:200%}}@keyframes a-ripple{0%{opacity:0;padding-bottom:0;width:0}25%{opacity:1}100%{width:200%;padding-bottom:200%}}.input-group input{background-color:rgba(0,0,0,.4);border:none;border-bottom:2px solid #3949ab;color:#fff;padding:6px}.input-group input:focus,.input-group.focus input{border-color:#ff0089}.input-group.valid input{border-color:#39ab48}.input-group.invalid input{border-color:#cf1b1b}.input-group{display:flex;flex-direction:column;margin-bottom:3px;position:relative;padding-top:.7rem}.input-group.no-space{padding-top:.1rem}.input-group label{font-size:.7rem;position:absolute;top:.7rem;left:6px;height:1rem;vertical-align:middle;transform:translateY(50%);color:#dcdcdc;transition:all .2s ease-out}.input-group input:focus~label,.input-group.focus label{top:0;left:0;transform:translateY(0);font-size:.6rem}.input-group .error{display:none}.input-group.invalid .error{margin-top:2px;display:block;font-size:.7rem;color:#f32f32}.switch{display:flex;flex-direction:row;align-items:center;padding:5px 20px 5px 5px}.switch input{position:absolute;-webkit-appearance:none;-moz-appearance:none;appearance:none;opacity:0}.switch label{display:block;border-radius:10px;width:40px;height:20px;background-color:#e9e9e9;position:relative;cursor:pointer;margin-right:.5rem}.switch label:after{content:'';background-color:#c51162;position:absolute;top:2px;left:2px;height:16px;width:16px;border-radius:10px;transition:.5s}.switch input:checked+label:after{transform:translateX(20px);background-color:#007769} \ No newline at end of file diff --git a/public/theme/admin/images/icon/ic_add_circle_outline_24px.svg b/public/theme/admin/images/icon/ic_add_circle_outline_24px.svg new file mode 100644 index 0000000..10329ce --- /dev/null +++ b/public/theme/admin/images/icon/ic_add_circle_outline_24px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/theme/admin/images/icon/ic_check_box_24px.svg b/public/theme/admin/images/icon/ic_check_box_24px.svg new file mode 100644 index 0000000..037c140 --- /dev/null +++ b/public/theme/admin/images/icon/ic_check_box_24px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/theme/admin/images/icon/ic_edit_24px.svg b/public/theme/admin/images/icon/ic_edit_24px.svg new file mode 100644 index 0000000..0991963 --- /dev/null +++ b/public/theme/admin/images/icon/ic_edit_24px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/theme/admin/images/icon/ic_visibility_24px.svg b/public/theme/admin/images/icon/ic_visibility_24px.svg new file mode 100644 index 0000000..737e63c --- /dev/null +++ b/public/theme/admin/images/icon/ic_visibility_24px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Venom/Admin/AdminController.php b/src/Venom/Admin/AdminController.php index 62d9235..92e71ab 100644 --- a/src/Venom/Admin/AdminController.php +++ b/src/Venom/Admin/AdminController.php @@ -33,6 +33,8 @@ class AdminController implements RenderController $renderer->addVar('isLoggedIn', $isLogin); if (!$isLogin) { Asset::get()->addCSS('login', 'login.css'); + } else { + Asset::get()->addCSS('admin', 'admin-panel.css'); } Asset::get()->addCSS('styles', 'style.css', 1); Asset::get()->addJS('scripts', 'scripts.min.js', 1); diff --git a/tpl/admin/admin-panel.php b/tpl/admin/admin-panel.php new file mode 100644 index 0000000..e129e08 --- /dev/null +++ b/tpl/admin/admin-panel.php @@ -0,0 +1,144 @@ +
+ +
+

Roles

+
+

Overview

+
Admin (secured) +
+
+
Moderator +
+
+
Predator +
+
+

Add new Role

+
+
+ + + New Role Name is required +
+
+
+
+
+
+

Role: Admin

+
+ + + If enabled role is active. +
+
+

Change Name

+
+ + + Role Name is required +
+
+
+

View Permissions

+
+
+ + + Meta-Data +
+ +
+ + + Pages +
+ +
+ + + Roles +
+ +
+ + + SEO-URL +
+ +
+ + + Users +
+ +
+ + + VENOM-Status +
+
+
+
+

View Permissions

+
+
+ + + Meta-Data +
+ +
+ + + Pages +
+ +
+ + + Roles +
+ +
+ + + SEO-URL +
+ +
+ + + Users +
+ +
+ + + VENOM-Status +
+
+
+ + +
+
+
diff --git a/tpl/admin/base.php b/tpl/admin/base.php index 0d7bda0..05c176a 100644 --- a/tpl/admin/base.php +++ b/tpl/admin/base.php @@ -12,14 +12,14 @@ use Venom\Views\Asset; Venom Admin Interface renderCSS(); ?> + getVar('isLoggedIn')) { $this->renderTemplate('login'); } else { - echo 'Admin Interface!'; - echo 'Ausloggen'; + $this->renderTemplate('admin-panel'); } Asset::get()->renderJS(); ?> From 9519236662e845bd2e1efc823b9dede574027320 Mon Sep 17 00:00:00 2001 From: engineerTrooper Date: Sun, 15 Nov 2020 19:19:23 +0100 Subject: [PATCH 10/33] VENOM-10: added, fake tables with display:grid. create v-table as html dom element --- public/theme/admin/css/admin-panel.css | 2 +- public/theme/admin/css/style.css | 2 +- .../admin/images/icon/ic_arrow_back_24px.svg | 1 + .../admin/images/icon/ic_delete_24px.svg | 4 + public/theme/admin/js/scripts.js | 189 ++++++- public/theme/admin/js/scripts.min.js | 2 +- tpl/admin/admin-panel.php | 533 +++++++++++++++--- 7 files changed, 638 insertions(+), 95 deletions(-) create mode 100644 public/theme/admin/images/icon/ic_arrow_back_24px.svg create mode 100644 public/theme/admin/images/icon/ic_delete_24px.svg diff --git a/public/theme/admin/css/admin-panel.css b/public/theme/admin/css/admin-panel.css index cc90666..afdab4e 100644 --- a/public/theme/admin/css/admin-panel.css +++ b/public/theme/admin/css/admin-panel.css @@ -1 +1 @@ -:focus{outline:0}*{box-sizing:border-box}body,html{width:100vw;height:100vh;overflow:hidden;padding:0;margin:0;background:#1f2857;background:linear-gradient(144deg,#1f2954 25%,#000 50%,#5e002c 80%);background-attachment:fixed;color:#fff;font-family:sans-serif;font-size:16px}.hide{display:none!important}main{display:flex;height:100vh;overflow:hidden}.menu{width:220px;background-color:#1b1b1b;box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23);flex-shrink:0;display:flex;flex-direction:column}.menu .logo{text-align:center;font-family:monospace}.menu div[data-link]{padding:.75rem .5rem;position:relative}.menu div[data-link]:after{background-color:#3949ab;content:"";position:absolute;left:0;bottom:0;height:3px;width:100%;transform:scaleX(0);transition:transform .4s;transform-origin:left}.menu div[data-link]:hover:after{transform:scaleX(1)}.menu div[data-link]:last-child{margin-top:auto}.menu div[data-link].active{font-weight:700}.content-area{padding:0 1rem;margin:1rem 1.5rem;flex-grow:1;overflow-y:auto;max-height:100%;background:RGBA(27,27,27,.5)}.content-area .max-width{max-width:300px}.content-area hr{border:.5px solid #e9e9e9;box-shadow:0 10px 20px rgba(0,0,0,.19),0 6px 6px rgba(0,0,0,.23)}.content-area .text{display:flex;position:relative;padding:5px 0}.content-area .icon{display:inline-block;width:18px;height:18px;background-size:cover;margin:0 10px 0 auto;fill:#fff}.content-area .icon-add{background-image:url(../images/icon/ic_add_circle_outline_24px.svg)}.content-area .icon-edit{background-image:url(../images/icon/ic_edit_24px.svg)}.content-area .icon-visibility{background-image:url(../images/icon/ic_visibility_24px.svg)}.content-area .add-new-role{display:flex;position:relative;align-items:center;padding:0 0 5px 0} \ No newline at end of file +:focus{outline:0}*{box-sizing:border-box}body,html{width:100vw;height:100vh;overflow:hidden;padding:0;margin:0;background:#1f2857;background:linear-gradient(144deg,#1f2954 25%,#000 50%,#5e002c 80%);background-attachment:fixed;color:#fff;font-family:sans-serif;font-size:16px}.hide{display:none!important}v-table{display:block}v-table v-table-footer,v-table v-table-header,v-table v-table-row{display:grid;grid-auto-columns:1fr;grid-auto-flow:column;-moz-column-gap:20px;column-gap:20px}v-table v-table-header h2{text-align:center}v-table v-table-row{margin:10px 0}v-table .grid12{grid-template-columns:repeat(12,[col-start] 1fr);-moz-column-gap:5px;column-gap:5px}v-table .grid12 .col-1to4{grid-column:1/3}v-table .grid12 .col-1to6{grid-column:1/6}v-table .grid12 .col-5to6{grid-column:5/6}v-table .grid12 .col-6to11{grid-column:6/11}v-table .grid12 .col-6to9{grid-column:6/9}v-table .grid12 .col-9to10{grid-column:9/10}v-table .grid12 .col-11to13{grid-column:11/13}v-label,v-option,v-options,v-select{display:inline-block;box-sizing:border-box}v-select{display:block;position:relative;margin-bottom:.5rem}v-label{padding:.5rem 1rem;background-color:#3949ab;color:#fff;cursor:pointer;display:block;border-radius:4px}v-options{position:absolute;display:flex;border-radius:4px;flex-direction:column;overflow:hidden;max-height:0;top:.5rem;left:.5rem;background-color:#1b1b1b;z-index:1000;cursor:pointer;color:#fff;box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23)}v-option{padding:.5rem 1rem}v-option:not([disabled]):hover{background-color:rgba(0,0,0,.3)}v-option[selected]{background-color:#3949ab;color:#fff}v-option[disabled]{color:#aaa}main{display:flex;height:100vh;overflow:hidden}.menu{width:220px;background-color:#1b1b1b;box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23);flex-shrink:0;display:flex;flex-direction:column}.menu .logo{text-align:center;font-family:monospace}.menu div[data-link]{padding:.75rem .5rem;position:relative}.menu div[data-link]:after{background-color:#3949ab;content:"";position:absolute;left:0;bottom:0;height:3px;width:100%;transform:scaleX(0);transition:transform .4s;transform-origin:left}.menu div[data-link]:hover:after{transform:scaleX(1)}.menu div[data-link]:last-child{margin-top:auto}.menu div[data-link].active{font-weight:700}.content-area{padding:0 1%;margin:1rem 1.5rem;flex-grow:1;overflow-y:auto;width:100%;max-height:100%;background:rgba(27,27,27,.5)}.content-area .inline{display:inline}.content-area .inline div{display:inline}.content-area .btn-line{margin-top:35px}.content-area .btn-line div{text-align:right}.content-area .btn-line div button{display:inline;margin-left:10px;min-width:100px}.content-area .modules div{padding:6px 20px 6px 0}.content-area .icon-cont div{display:inline-block;vertical-align:middle;margin-top:5px}.content-area .icon-cont .icon{width:18px;height:18px;background-size:cover;margin:0 15px 0 0;fill:#fff}.content-area .icon-cont .icon-add{background-image:url(../images/icon/ic_add_circle_outline_24px.svg)}.content-area .icon-cont .icon-edit{background-image:url(../images/icon/ic_edit_24px.svg)}.content-area .icon-cont .icon-visibility{background-image:url(../images/icon/ic_visibility_24px.svg)}.content-area .icon-cont .icon-delete{background-image:url(../images/icon/ic_delete_24px.svg)}.content-area .icon-cont .icon-arrow-back{background-image:url(../images/icon/ic_arrow_back_24px.svg);width:24px;height:24px}.content-area .description{margin-top:25px}.content-area .fix-pad{padding-top:14px} \ No newline at end of file diff --git a/public/theme/admin/css/style.css b/public/theme/admin/css/style.css index 19df622..a49f290 100644 --- a/public/theme/admin/css/style.css +++ b/public/theme/admin/css/style.css @@ -1 +1 @@ -:focus{outline:0}*{box-sizing:border-box}body,html{width:100vw;height:100vh;overflow:hidden;padding:0;margin:0;background:#1f2857;background:linear-gradient(144deg,#1f2954 25%,#000 50%,#5e002c 80%);background-attachment:fixed;color:#fff;font-family:sans-serif;font-size:16px}.hide{display:none!important}.btn{border:none;background:#3949ab radial-gradient(circle at 0 0,#3949ab 0,rgba(0,0,0,.2) 100%) no-repeat;color:#fff;padding:5px 15px;margin:10px 0;cursor:pointer;position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;border-radius:4px;box-shadow:0 2px 5px 0 rgba(0,0,0,.26);overflow:hidden;display:flex;justify-content:center;align-items:center;transition:.5s}.btn--outline{background:0 0;border:1px solid #3949ab}.btn:focus{box-shadow:0 3px 8px 1px rgba(0,0,0,.4)}.btn--accent{background:#ff0089 radial-gradient(circle at 0 0,#ff0089 0,rgba(0,0,0,.2) 100%) no-repeat}.btn--warn{background:#f87229 radial-gradient(circle at 0 0,#f87229 0,rgba(0,0,0,.2) 100%) no-repeat}.btn-fab{border-radius:2rem;width:2em;height:2em;padding:5px}.btn-ripple{position:absolute;top:0;left:0;width:100%;height:100%;overflow:hidden;background:0 0}.btn-ripple__effect{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);opacity:1;width:200%;height:0;padding-bottom:200%;border-radius:50%;background:rgba(190,190,190,.3);-webkit-animation:a-ripple .4s ease-in;animation:a-ripple .4s ease-in}.btn-ripple__effect.to-remove{-webkit-animation:remove-ripple .2s linear;animation:remove-ripple .2s linear}.btn--outline .btn-ripple__effect{background:rgba(57,73,171,.5);z-index:-1}.btn__content{z-index:1;font-weight:400;pointer-events:none}@-webkit-keyframes remove-ripple{from{opacity:1}to{opacity:0}}@keyframes remove-ripple{from{opacity:1}to{opacity:0}}@-webkit-keyframes a-ripple{0%{opacity:0;padding-bottom:0;width:0}25%{opacity:1}100%{width:200%;padding-bottom:200%}}@keyframes a-ripple{0%{opacity:0;padding-bottom:0;width:0}25%{opacity:1}100%{width:200%;padding-bottom:200%}}.input-group input{background-color:rgba(0,0,0,.4);border:none;border-bottom:2px solid #3949ab;color:#fff;padding:6px}.input-group input:focus,.input-group.focus input{border-color:#ff0089}.input-group.valid input{border-color:#39ab48}.input-group.invalid input{border-color:#cf1b1b}.input-group{display:flex;flex-direction:column;margin-bottom:3px;position:relative;padding-top:.7rem}.input-group.no-space{padding-top:.1rem}.input-group label{font-size:.7rem;position:absolute;top:.7rem;left:6px;height:1rem;vertical-align:middle;transform:translateY(50%);color:#dcdcdc;transition:all .2s ease-out}.input-group input:focus~label,.input-group.focus label{top:0;left:0;transform:translateY(0);font-size:.6rem}.input-group .error{display:none}.input-group.invalid .error{margin-top:2px;display:block;font-size:.7rem;color:#f32f32}.switch{display:flex;flex-direction:row;align-items:center;padding:5px 20px 5px 5px}.switch input{position:absolute;-webkit-appearance:none;-moz-appearance:none;appearance:none;opacity:0}.switch label{display:block;border-radius:10px;width:40px;height:20px;background-color:#e9e9e9;position:relative;cursor:pointer;margin-right:.5rem}.switch label:after{content:'';background-color:#c51162;position:absolute;top:2px;left:2px;height:16px;width:16px;border-radius:10px;transition:.5s}.switch input:checked+label:after{transform:translateX(20px);background-color:#007769} \ No newline at end of file +:focus{outline:0}*{box-sizing:border-box}body,html{width:100vw;height:100vh;overflow:hidden;padding:0;margin:0;background:#1f2857;background:linear-gradient(144deg,#1f2954 25%,#000 50%,#5e002c 80%);background-attachment:fixed;color:#fff;font-family:sans-serif;font-size:16px}.hide{display:none!important}.btn{border:none;background:#3949ab radial-gradient(circle at 0 0,#3949ab 0,rgba(0,0,0,.2) 100%) no-repeat;color:#fff;padding:5px 15px;margin:10px 0;cursor:pointer;position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;border-radius:4px;box-shadow:0 2px 5px 0 rgba(0,0,0,.26);overflow:hidden;display:flex;justify-content:center;align-items:center;transition:.5s}.btn--outline{background:0 0;border:1px solid #3949ab}.btn:focus{box-shadow:0 3px 8px 1px rgba(0,0,0,.4)}.btn--valid{background:#39ab48 radial-gradient(circle at 0 0,#39ab48 0,rgba(0,0,0,.2) 100%) no-repeat}.btn--accent{background:#ff0089 radial-gradient(circle at 0 0,#ff0089 0,rgba(0,0,0,.2) 100%) no-repeat}.btn--warn{background:#f87229 radial-gradient(circle at 0 0,#f87229 0,rgba(0,0,0,.2) 100%) no-repeat}.btn-fab{border-radius:2rem;width:2em;height:2em;padding:5px}.btn-ripple{position:absolute;top:0;left:0;width:100%;height:100%;overflow:hidden;background:0 0}.btn-ripple__effect{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);opacity:1;width:200%;height:0;padding-bottom:200%;border-radius:50%;background:rgba(190,190,190,.3);-webkit-animation:a-ripple .4s ease-in;animation:a-ripple .4s ease-in}.btn-ripple__effect.to-remove{-webkit-animation:remove-ripple .2s linear;animation:remove-ripple .2s linear}.btn--outline .btn-ripple__effect{background:rgba(57,73,171,.5);z-index:-1}.btn__content{z-index:1;font-weight:400;pointer-events:none}@-webkit-keyframes remove-ripple{from{opacity:1}to{opacity:0}}@keyframes remove-ripple{from{opacity:1}to{opacity:0}}@-webkit-keyframes a-ripple{0%{opacity:0;padding-bottom:0;width:0}25%{opacity:1}100%{width:200%;padding-bottom:200%}}@keyframes a-ripple{0%{opacity:0;padding-bottom:0;width:0}25%{opacity:1}100%{width:200%;padding-bottom:200%}}.input-group input{background-color:rgba(0,0,0,.4);border:none;border-bottom:2px solid #3949ab;color:#fff;padding:6px}.input-group input:focus,.input-group.focus input{border-color:#ff0089}.input-group.valid input{border-color:#39ab48}.input-group.invalid input{border-color:#cf1b1b}.input-group{display:flex;flex-direction:column;margin-bottom:3px;position:relative;padding-top:.7rem}.input-group.no-space{padding-top:.1rem}.input-group label{font-size:.7rem;position:absolute;top:.7rem;left:6px;height:1rem;vertical-align:middle;transform:translateY(50%);color:#dcdcdc;transition:all .2s ease-out}.input-group input:focus~label,.input-group.focus label{top:0;left:0;transform:translateY(0);font-size:.6rem}.input-group .error{display:none}.input-group.invalid .error{margin-top:2px;display:block;font-size:.7rem;color:#f32f32}.switch{display:flex;flex-direction:row;align-items:center;padding:5px 20px 5px 0}.switch input{position:absolute;-webkit-appearance:none;-moz-appearance:none;appearance:none;opacity:0}.switch label{display:block;border-radius:10px;width:40px;height:20px;background-color:#e9e9e9;position:relative;cursor:pointer;margin-right:.5rem}.switch label:after{content:'';background-color:#c51162;position:absolute;top:2px;left:2px;height:16px;width:16px;border-radius:10px;transition:.5s}.switch input:checked+label:after{transform:translateX(20px);background-color:#007769} \ No newline at end of file diff --git a/public/theme/admin/images/icon/ic_arrow_back_24px.svg b/public/theme/admin/images/icon/ic_arrow_back_24px.svg new file mode 100644 index 0000000..d153fea --- /dev/null +++ b/public/theme/admin/images/icon/ic_arrow_back_24px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/theme/admin/images/icon/ic_delete_24px.svg b/public/theme/admin/images/icon/ic_delete_24px.svg new file mode 100644 index 0000000..e8dde6e --- /dev/null +++ b/public/theme/admin/images/icon/ic_delete_24px.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/theme/admin/js/scripts.js b/public/theme/admin/js/scripts.js index 6aa374b..fa36f40 100644 --- a/public/theme/admin/js/scripts.js +++ b/public/theme/admin/js/scripts.js @@ -196,6 +196,193 @@ class VRipple { } } const rippler = new VRipple(); + + + +(function () { + window._openVSelect = null; + + requestAnimationFrame(e => { + document.body.addEventListener('click', ev => { + if (window._openVSelect && ev.target.closest('v-select') !== window._openVSelect) { + window._openVSelect.toggle(false); + } + }) + }) + + class VSelectElement extends HTMLElement { + constructor() { + super(); + let self = this; + self._in = this.attachInternals(); + self._in.role = 'select'; + self.setAttribute('tabindex', 0); + self.update(); + } + + static get formAssociated() { + return true; + } + + static get observedAttributes() { + return ['required', 'validity']; + } + + get required() { + return this.hasAttribute('required'); + } + + set required(flag) { + this.toggleAttribute('required', Boolean(flag)); + } + + get name() { + return this.getAttribute('name'); + } + + set name(val) { + this.toggleAttribute('name', val); + } + + get form() { + return this._in.form; + } + + get options() { + return $$('v-options v-option', this); + } + + get selected() { + return $$('v-options v-option[selected]', this); + } + + update() { + let selected = [], + lbl = $('v-label', this), + fd = new FormData(); + this.selected.forEach(e => { + selected.push(e.innerText); + fd.append(this.name, e.value); + }) + lbl.attributeChangedCallback('value', '', selected.join(", ")); + if (this.required && selected.length === 0) { + this._in.setValidity({customError: true}, "Option is needed"); + } else { + this._in.setValidity({}); + } + this._in.setFormValue(fd); + } + + checkValidity() { + return this._in.checkValidity(); + } + + reportValidity() { + return this._in.reportValidity(); + } + + toggle(open) { + if (window._openVSelect && open) { + window._openVSelect.toggleSelect(false); + } + const options = $('v-options', this); + if (!open || this.isOpen) { + options.style.maxHeight = '0'; + window._openVSelect = false; + this.isOpen = false; + this.update(); + } else { + options.focus(); + let height = 0, + children = options.children; + for (let i = 0; i < children.length; i++) { + height += children[i].offsetHeight; + } + options.style.maxHeight = height + 'px'; + window._openVSelect = this; + this.isOpen = true; + } + } + } + + class VSelectOptionElement extends HTMLElement { + constructor() { + super(); + this._in = this.attachInternals(); + this._in.role = 'option'; + this.addEventListener('click', e => { + let parent = this.parentNode.parentNode, + select = !this.selected; + if (!parent.hasAttribute('multiple')) { + parent.toggle(false); + for (let item of parent.selected) { + if (item !== this) { + item.removeAttribute('selected'); + } + } + } + if (!this.disabled) { + this.attributeChangedCallback('selected', false, select, true); + this.parentNode.parentNode.update(); + } + }); + } + + static get observedAttributes() { + return ['selected', 'disabled', 'value']; + } + + attributeChangedCallback(name, oldValue, newValue, force) { + if (name === 'selected' && this.hasAttribute('disabled')) { + this.removeAttribute(name); + return; + } + if (name === 'disabled' && newValue === true && this.hasAttribute('selected')) { + this.attributeChangedCallback('selected', false, false); + } + + if (force) { + if (newValue) { + this.setAttribute(name, newValue); + } else { + this.removeAttribute(name); + } + } + this[name] = newValue; + } + } + + class VLabel extends HTMLElement { + constructor() { + super(); + this.empty = this.getAttribute('empty') || ""; + this.innerHTML = this.getAttribute("value") || this.empty; + this.addEventListener('click', this.openPopUp.bind(this)); + } + + static get observedAttributes() { + return ['empty', 'value']; + } + + openPopUp() { + this.parentNode.toggle(true); + } + + attributeChangedCallback(name, oldValue, newValue) { + if (name === 'value') { + this.innerHTML = newValue || this.empty; + } + this[name] = newValue; + } + } + + customElements.define("v-label", VLabel); + customElements.define("v-option", VSelectOptionElement); + customElements.define("v-select", VSelectElement); +})(); + + + class FormHandler { constructor(selector, parent, cb, err) { this.cb = cb || console.log; @@ -263,4 +450,4 @@ class FormHandler { $('.error-message', el).classList.remove('hide'); }) } -})() \ No newline at end of file +})(); \ No newline at end of file diff --git a/public/theme/admin/js/scripts.min.js b/public/theme/admin/js/scripts.min.js index 47e60ed..327f1fa 100644 --- a/public/theme/admin/js/scripts.min.js +++ b/public/theme/admin/js/scripts.min.js @@ -1 +1 @@ -class VUtils{static makePublic(){VUtils.isUsed||(this.initHandlers(),VUtils.isUsed=!0,console.log("[VUtils] is now available in the Global Space! no VUtils. anymore needed"))}static initHandlers(){window.$=this.$,window.$$=this.$$,window.tryCatch=this.tryCatch,VUtils.nodePrototypes()}static $(e,t){return(t=t||document).querySelector(e)}static $$(e,t){return(t=t||document).querySelectorAll(e)}static tryCatch(e,t,i){e=VUtils.wrap(e,[]),i=i||console.error,t=t||console.log;try{t(...e)}catch(e){i(e)}}static forEach(e,t,i){for(let s=0;s{let n=e.target;if(e.detail instanceof HTMLElement&&(n=e.detail),n instanceof HTMLElement)if(n.matches(t))VUtils.tryCatch([e,n],i,s);else{const o=n.find(t);o&&VUtils.tryCatch([e,o],i,s)}})},Node.prototype.addMultiListener=function(e,t,i={}){let s=e.split(" ");for(let e of s)this.addEventListener(e,t,i)}}}VUtils.makePublic();class VRipple{constructor(e={}){if(!VUtils.isUsed)throw"VRipply is only with Public VUtils usable!";let t=this;if(t.options=JSON.parse('{"classes":["btn-ripple__effect"],"target":"body","selector":".btn-ripple"}'),VUtils.mergeOptions(t.options,e),t.options.selector.indexOf("#")>-1)throw"ID's are not allowed as selector!";this.instanceCheck(),this.ripples=[],requestAnimationFrame(this.initHandler.bind(this))}instanceCheck(){let e=this.options;const t=[e.target,e.selector,e.classes.join(".")].join(" ");VRipple.instances=VRipple.instances||{},VRipple.instances[t]=this}initHandler(){let e=this,t=e.options.selector;$(e.options.target).addDelegatedEventListener("mousedown touchstart",t,(t,i)=>{let s=t.touches?t.touches[0]:t,n=i.parentNode,o=i.createNew("span",e.options),l=n.getBoundingClientRect(),r=s.clientX-l.left,a=s.clientY-l.top;o.style.top=a+"px",o.style.left=r+"px",o._mouseDown=!0,o._animationEnded=!1,e.ripples.push(o)}),document.body.addDelegatedEventListener("animationend","."+VUtils.get(e.options.classes,""),e.rippleEnd.bind(e)),document.body._vRippleInit||(document.body.addMultiListener("mouseup touchend mouseleave rippleClose",t=>{let i=Object.keys(VRipple.instances);for(let s of i)for(let i of VRipple.instances[s].ripples)e.rippleEnd.bind(VRipple.instances[s])(t,i)}),document.body._vRippleInit=!0)}rippleEnd(e,t){t.parentNode&&("animationend"===e.type?t._animationEnded=!0:t._mouseDown=!1,!t._mouseDown&&t._animationEnded&&(t.classList.contains("to-remove")?(t.parentNode.removeChild(t),this.ripples.splice(this.ripples.indexOf(t),1)):t.classList.add("to-remove")))}}const rippler=new VRipple;class FormHandler{constructor(e,t,i,s){this.cb=i||console.log,this.err=s||console.err,$(t).addDelegatedEventListener("submit",e,this.handleEvent.bind(this))}handleEvent(e,t){if(e.preventDefault(),t.checkValidity()){if(""===(t.action??""))return void console.error("No URL Found on Form",t);fetch(t.action,{method:t.method.toUpperCase(),credentials:"same-origin",body:new FormData(t),redirect:"manual"}).then(e=>{if(!e.ok)throw new Error("Network response errored");return e.json()}).then(e=>this.cb(e,t)).catch(e=>this.err(e,t))}else VUtils.forEach($$("input",t),e=>{if(!e.checkValidity()){let t=e.parentNode;t.classList.remove("valid"),t.classList.add("invalid")}})}}$("body").addDelegatedEventListener("change input","input",(e,t)=>{let i=$(".error-message",t.find("form"));i&&i.classList.add("hide");let s=t.parentNode;""===t.value?s.classList.remove("focus"):s.classList.add("focus"),t.checkValidity()?(s.classList.add("valid"),s.classList.remove("invalid")):(s.classList.remove("valid"),s.classList.add("invalid"))}),$("#login")&&new FormHandler("form#login","body",()=>{location.reload()},(e,t)=>{$(".error-message",t).classList.remove("hide")}); \ No newline at end of file +class VUtils{static makePublic(){VUtils.isUsed||(this.initHandlers(),VUtils.isUsed=!0,console.log("[VUtils] is now available in the Global Space! no VUtils. anymore needed"))}static initHandlers(){window.$=this.$,window.$$=this.$$,window.tryCatch=this.tryCatch,VUtils.nodePrototypes()}static $(e,t){return(t=t||document).querySelector(e)}static $$(e,t){return(t=t||document).querySelectorAll(e)}static tryCatch(e,t,i){e=VUtils.wrap(e,[]),i=i||console.error,t=t||console.log;try{t(...e)}catch(e){i(e)}}static forEach(e,t,i){for(let s=0;s{let n=e.target;if(e.detail instanceof HTMLElement&&(n=e.detail),n instanceof HTMLElement)if(n.matches(t))VUtils.tryCatch([e,n],i,s);else{const o=n.find(t);o&&VUtils.tryCatch([e,o],i,s)}})},Node.prototype.addMultiListener=function(e,t,i={}){let s=e.split(" ");for(let e of s)this.addEventListener(e,t,i)}}}VUtils.makePublic();class VRipple{constructor(e={}){if(!VUtils.isUsed)throw"VRipply is only with Public VUtils usable!";let t=this;if(t.options=JSON.parse('{"classes":["btn-ripple__effect"],"target":"body","selector":".btn-ripple"}'),VUtils.mergeOptions(t.options,e),t.options.selector.indexOf("#")>-1)throw"ID's are not allowed as selector!";this.instanceCheck(),this.ripples=[],requestAnimationFrame(this.initHandler.bind(this))}instanceCheck(){let e=this.options;const t=[e.target,e.selector,e.classes.join(".")].join(" ");VRipple.instances=VRipple.instances||{},VRipple.instances[t]=this}initHandler(){let e=this,t=e.options.selector;$(e.options.target).addDelegatedEventListener("mousedown touchstart",t,(t,i)=>{let s=t.touches?t.touches[0]:t,n=i.parentNode,o=i.createNew("span",e.options),r=n.getBoundingClientRect(),l=s.clientX-r.left,a=s.clientY-r.top;o.style.top=a+"px",o.style.left=l+"px",o._mouseDown=!0,o._animationEnded=!1,e.ripples.push(o)}),document.body.addDelegatedEventListener("animationend","."+VUtils.get(e.options.classes,""),e.rippleEnd.bind(e)),document.body._vRippleInit||(document.body.addMultiListener("mouseup touchend mouseleave rippleClose",t=>{let i=Object.keys(VRipple.instances);for(let s of i)for(let i of VRipple.instances[s].ripples)e.rippleEnd.bind(VRipple.instances[s])(t,i)}),document.body._vRippleInit=!0)}rippleEnd(e,t){t.parentNode&&("animationend"===e.type?t._animationEnded=!0:t._mouseDown=!1,!t._mouseDown&&t._animationEnded&&(t.classList.contains("to-remove")?(t.parentNode.removeChild(t),this.ripples.splice(this.ripples.indexOf(t),1)):t.classList.add("to-remove")))}}const rippler=new VRipple;!function(){window._openVSelect=null,requestAnimationFrame(e=>{document.body.addEventListener("click",e=>{window._openVSelect&&e.target.closest("v-select")!==window._openVSelect&&window._openVSelect.toggle(!1)})});class e extends HTMLElement{constructor(){super();let e=this;e._in=this.attachInternals(),e._in.role="select",e.setAttribute("tabindex",0),e.update()}static get formAssociated(){return!0}static get observedAttributes(){return["required","validity"]}get required(){return this.hasAttribute("required")}set required(e){this.toggleAttribute("required",Boolean(e))}get name(){return this.getAttribute("name")}set name(e){this.toggleAttribute("name",e)}get form(){return this._in.form}get options(){return $$("v-options v-option",this)}get selected(){return $$("v-options v-option[selected]",this)}update(){let e=[],t=$("v-label",this),i=new FormData;this.selected.forEach(t=>{e.push(t.innerText),i.append(this.name,t.value)}),t.attributeChangedCallback("value","",e.join(", ")),this.required&&0===e.length?this._in.setValidity({customError:!0},"Option is needed"):this._in.setValidity({}),this._in.setFormValue(i)}checkValidity(){return this._in.checkValidity()}reportValidity(){return this._in.reportValidity()}toggle(e){window._openVSelect&&e&&window._openVSelect.toggleSelect(!1);const t=$("v-options",this);if(!e||this.isOpen)t.style.maxHeight="0",window._openVSelect=!1,this.isOpen=!1,this.update();else{t.focus();let e=0,i=t.children;for(let t=0;t{let t=this.parentNode.parentNode,i=!this.selected;if(!t.hasAttribute("multiple")){t.toggle(!1);for(let e of t.selected)e!==this&&e.removeAttribute("selected")}this.disabled||(this.attributeChangedCallback("selected",!1,i,!0),this.parentNode.parentNode.update())})}static get observedAttributes(){return["selected","disabled","value"]}attributeChangedCallback(e,t,i,s){"selected"===e&&this.hasAttribute("disabled")?this.removeAttribute(e):("disabled"===e&&!0===i&&this.hasAttribute("selected")&&this.attributeChangedCallback("selected",!1,!1),s&&(i?this.setAttribute(e,i):this.removeAttribute(e)),this[e]=i)}}class i extends HTMLElement{constructor(){super(),this.empty=this.getAttribute("empty")||"",this.innerHTML=this.getAttribute("value")||this.empty,this.addEventListener("click",this.openPopUp.bind(this))}static get observedAttributes(){return["empty","value"]}openPopUp(){this.parentNode.toggle(!0)}attributeChangedCallback(e,t,i){"value"===e&&(this.innerHTML=i||this.empty),this[e]=i}}customElements.define("v-label",i),customElements.define("v-option",t),customElements.define("v-select",e)}();class FormHandler{constructor(e,t,i,s){this.cb=i||console.log,this.err=s||console.err,$(t).addDelegatedEventListener("submit",e,this.handleEvent.bind(this))}handleEvent(e,t){if(e.preventDefault(),t.checkValidity()){if(""===(t.action??""))return void console.error("No URL Found on Form",t);fetch(t.action,{method:t.method.toUpperCase(),credentials:"same-origin",body:new FormData(t),redirect:"manual"}).then(e=>{if(!e.ok)throw new Error("Network response errored");return e.json()}).then(e=>this.cb(e,t)).catch(e=>this.err(e,t))}else VUtils.forEach($$("input",t),e=>{if(!e.checkValidity()){let t=e.parentNode;t.classList.remove("valid"),t.classList.add("invalid")}})}}$("body").addDelegatedEventListener("change input","input",(e,t)=>{let i=$(".error-message",t.find("form"));i&&i.classList.add("hide");let s=t.parentNode;""===t.value?s.classList.remove("focus"):s.classList.add("focus"),t.checkValidity()?(s.classList.add("valid"),s.classList.remove("invalid")):(s.classList.remove("valid"),s.classList.add("invalid"))}),$("#login")&&new FormHandler("form#login","body",()=>{location.reload()},(e,t)=>{$(".error-message",t).classList.remove("hide")}); \ No newline at end of file diff --git a/tpl/admin/admin-panel.php b/tpl/admin/admin-panel.php index e129e08..f09a386 100644 --- a/tpl/admin/admin-panel.php +++ b/tpl/admin/admin-panel.php @@ -1,144 +1,495 @@
-

Roles

-
-

Overview

-
Admin (secured) -
-
-
Moderator -
-
-
Predator -
-
-

Add new Role

-
-
- - - New Role Name is required + + +

Roles

+
+ +
+

Overview

+
+
+
Admin (secured)
+
+
+
+
Moderator
+
+
+
+
Predator
+
-
-
-
-
-
-

Role: Admin

-
- - - If enabled role is active. -
-
-

Change Name

-
- - - Role Name is required +
+

Add new Role

+
+ + + New Role Name is required +
+
-
-
-

View Permissions

-
+ + + Dies ist der Footer. + + + + + + +

Role: Admin

+
+ +
+
+
+
+ +
+

Role Status

+
+ + + If enabled role is active. +
+
+
+

Role Name

+
+ + + New Role Name is required +
+
+
+ +
+

Module

+
Meta-Data
+
Pages
+
Roles
+
SEO-URL
+
Users
+
VENOM-Status
+
+
+

Edit

+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+
+

View

- Meta-Data +
- Pages +
- Roles +
- SEO-URL +
- Users +
- VENOM-Status +
-
-
-

View Permissions

-
-
- - - Meta-Data -
+ + +
+ + + +
+
+ + Dies ist der Footer. + + -
- - - Pages -
-
- - - Roles + + +

Users

+
+ +
+

All Users

+
+
+
ALLE Alphabetisch ordnen!
- -
- - - SEO-URL +
+
+
derflieger (Ina Ruh)
- -
- - - Users +
+
+
engineertrooper (Dominic Seela)
- -
- - - VENOM-Status +
+
+
versustunez (Maurice Grönewald)
+
+
+
+
vollglaswasser (Marie Joseph)
-
+
+

Add new User

+
+
+ + + New User Name is required +
+
+ +
+ + + Dies ist der Footer. + + - -
+ + + +

User: engineertrooper

+
+ +
+
+
+
+ +
+
Username:
+
+ + + User Name is required +
+
Author Name:
+
+ + + Author Name is required +
+
+
+
E-Mail:
+
+ + + E-Mail Address is required +
+
+
+
New Password:
+
+ + + New Password is required +
+
New Password repeat:
+
+ + + New Password Repeat is required +
+
+
+ +
+ + + +
+
+ + Dies ist der Footer. + +
+ + + + +

Pages

+
+ +

All Pages

+
+ +
Name
+
Edit, View, Delete
+
ID
+
Status
+
+ +
Sonnenuntergang am Meer
+
+
+
+
+
+
+
+
+
+
+
+
+ 4 +
+
+ + + + Visible + Privat + + +
+
+ +
Fernsehen zu Zweit mit Nebenwirkungen
+
+
+
+
+
+
+
+
+
+
+
+
+ 7 +
+
+ + + + Visible + Privat + + +
+
+ +
Spiele 1 und 2 und 3
+
+
+
+
+
+
+
+
+
+
+
+
+ 14 +
+
+ + + + Visible + Privat + + +
+
+ +
+

Add new Page

+
+ + + New Page Name is required +
+ +
+
+ + Dies ist der Footer. + +
+ + + + +

Page: Fernsehen zu Zweit mit Nebenwirkungen

+
+ +
+
+
+
+ +

Seitenname:

+
+ +
+ + + New Page Name is required +
+
+ + hier kommt das Editorfeld hin + + +

Page Information

+
+ +
Author
+
ID
+
SEO-URL
+
Status
+
+ +
+ + + + engineertrooper (Dominic Seela) + versustunez (Maurice Grönewald) + + +
+
+ 7 +
+
+ https://beispiel.seo-url.de/blabla/23455234 +
+
+ + + + Visible + Privat + + +
+
+ +
+ + + +
+
+ + Dies ist der Footer + +
-
+
\ No newline at end of file From f7b7b6c956e3dc4b6afdd9d65b4a4eb3a3c54610 Mon Sep 17 00:00:00 2001 From: engineerTrooper Date: Tue, 17 Nov 2020 15:13:58 +0100 Subject: [PATCH 11/33] VENOM-10: added textarea with style and style correction on v-selects --- public/theme/admin/css/admin-panel.css | 2 +- tpl/admin/admin-panel.php | 91 +++++++++++++++++++++++++- 2 files changed, 90 insertions(+), 3 deletions(-) diff --git a/public/theme/admin/css/admin-panel.css b/public/theme/admin/css/admin-panel.css index afdab4e..0661e48 100644 --- a/public/theme/admin/css/admin-panel.css +++ b/public/theme/admin/css/admin-panel.css @@ -1 +1 @@ -:focus{outline:0}*{box-sizing:border-box}body,html{width:100vw;height:100vh;overflow:hidden;padding:0;margin:0;background:#1f2857;background:linear-gradient(144deg,#1f2954 25%,#000 50%,#5e002c 80%);background-attachment:fixed;color:#fff;font-family:sans-serif;font-size:16px}.hide{display:none!important}v-table{display:block}v-table v-table-footer,v-table v-table-header,v-table v-table-row{display:grid;grid-auto-columns:1fr;grid-auto-flow:column;-moz-column-gap:20px;column-gap:20px}v-table v-table-header h2{text-align:center}v-table v-table-row{margin:10px 0}v-table .grid12{grid-template-columns:repeat(12,[col-start] 1fr);-moz-column-gap:5px;column-gap:5px}v-table .grid12 .col-1to4{grid-column:1/3}v-table .grid12 .col-1to6{grid-column:1/6}v-table .grid12 .col-5to6{grid-column:5/6}v-table .grid12 .col-6to11{grid-column:6/11}v-table .grid12 .col-6to9{grid-column:6/9}v-table .grid12 .col-9to10{grid-column:9/10}v-table .grid12 .col-11to13{grid-column:11/13}v-label,v-option,v-options,v-select{display:inline-block;box-sizing:border-box}v-select{display:block;position:relative;margin-bottom:.5rem}v-label{padding:.5rem 1rem;background-color:#3949ab;color:#fff;cursor:pointer;display:block;border-radius:4px}v-options{position:absolute;display:flex;border-radius:4px;flex-direction:column;overflow:hidden;max-height:0;top:.5rem;left:.5rem;background-color:#1b1b1b;z-index:1000;cursor:pointer;color:#fff;box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23)}v-option{padding:.5rem 1rem}v-option:not([disabled]):hover{background-color:rgba(0,0,0,.3)}v-option[selected]{background-color:#3949ab;color:#fff}v-option[disabled]{color:#aaa}main{display:flex;height:100vh;overflow:hidden}.menu{width:220px;background-color:#1b1b1b;box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23);flex-shrink:0;display:flex;flex-direction:column}.menu .logo{text-align:center;font-family:monospace}.menu div[data-link]{padding:.75rem .5rem;position:relative}.menu div[data-link]:after{background-color:#3949ab;content:"";position:absolute;left:0;bottom:0;height:3px;width:100%;transform:scaleX(0);transition:transform .4s;transform-origin:left}.menu div[data-link]:hover:after{transform:scaleX(1)}.menu div[data-link]:last-child{margin-top:auto}.menu div[data-link].active{font-weight:700}.content-area{padding:0 1%;margin:1rem 1.5rem;flex-grow:1;overflow-y:auto;width:100%;max-height:100%;background:rgba(27,27,27,.5)}.content-area .inline{display:inline}.content-area .inline div{display:inline}.content-area .btn-line{margin-top:35px}.content-area .btn-line div{text-align:right}.content-area .btn-line div button{display:inline;margin-left:10px;min-width:100px}.content-area .modules div{padding:6px 20px 6px 0}.content-area .icon-cont div{display:inline-block;vertical-align:middle;margin-top:5px}.content-area .icon-cont .icon{width:18px;height:18px;background-size:cover;margin:0 15px 0 0;fill:#fff}.content-area .icon-cont .icon-add{background-image:url(../images/icon/ic_add_circle_outline_24px.svg)}.content-area .icon-cont .icon-edit{background-image:url(../images/icon/ic_edit_24px.svg)}.content-area .icon-cont .icon-visibility{background-image:url(../images/icon/ic_visibility_24px.svg)}.content-area .icon-cont .icon-delete{background-image:url(../images/icon/ic_delete_24px.svg)}.content-area .icon-cont .icon-arrow-back{background-image:url(../images/icon/ic_arrow_back_24px.svg);width:24px;height:24px}.content-area .description{margin-top:25px}.content-area .fix-pad{padding-top:14px} \ No newline at end of file +:focus{outline:0}*{box-sizing:border-box}body,html{width:100vw;height:100vh;overflow:hidden;padding:0;margin:0;background:#1f2857;background:linear-gradient(144deg,#1f2954 25%,#000 50%,#5e002c 80%);background-attachment:fixed;color:#fff;font-family:sans-serif;font-size:16px}.hide{display:none!important}v-table{display:block}v-table v-table-footer,v-table v-table-header,v-table v-table-row{display:grid;grid-auto-columns:1fr;grid-auto-flow:column;-moz-column-gap:20px;column-gap:20px}v-table v-table-header h2{text-align:center}v-table v-table-row{margin:10px 0}v-table .grid12{grid-template-columns:repeat(12,[col-start] 1fr);-moz-column-gap:5px;column-gap:5px}v-table .grid12 .col-1to4{grid-column:1/3}v-table .grid12 .col-1to6{grid-column:1/6}v-table .grid12 .col-5to6{grid-column:5/6}v-table .grid12 .col-6to11{grid-column:6/11}v-table .grid12 .col-6to9{grid-column:6/9}v-table .grid12 .col-9to10{grid-column:9/10}v-table .grid12 .col-11to13{grid-column:11/13}v-label,v-option,v-options,v-select{display:inline-block;box-sizing:border-box}v-select{display:block;position:relative;margin-bottom:.5rem}v-label{padding:.5rem 1rem;background-color:#3949ab;color:#fff;cursor:pointer;display:block;border-radius:4px}v-options{position:absolute;display:flex;border-radius:4px;flex-direction:column;overflow:hidden;max-height:0;top:.5rem;left:.5rem;background-color:#3949ab;z-index:1000;cursor:pointer;color:#fff;box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23)}v-option{padding:.5rem 1rem;min-width:200px}v-option:not([disabled]):hover{background-color:rgba(0,0,0,.3)}v-option[selected]{background-color:#3949ab;color:#fff}v-option[disabled]{color:#aaa}main{display:flex;height:100vh;overflow:hidden}.menu{width:220px;background-color:#1b1b1b;box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23);flex-shrink:0;display:flex;flex-direction:column}.menu .logo{text-align:center;font-family:monospace}.menu div[data-link]{padding:.75rem .5rem;position:relative}.menu div[data-link]:after{background-color:#3949ab;content:"";position:absolute;left:0;bottom:0;height:3px;width:100%;transform:scaleX(0);transition:transform .4s;transform-origin:left}.menu div[data-link]:hover:after{transform:scaleX(1)}.menu div[data-link]:last-child{margin-top:auto}.menu div[data-link].active{font-weight:700}.content-area{padding:0 1%;margin:1rem 1.5rem;flex-grow:1;overflow-y:auto;width:100%;max-height:100%;background:rgba(27,27,27,.5)}.content-area .inline{display:inline}.content-area .inline div{display:inline}.content-area .btn-line{margin-top:35px}.content-area .btn-line div{text-align:right}.content-area .btn-line div button{display:inline;margin-left:10px;min-width:100px}.content-area textarea{background:rgba(27,27,27,.5);color:#fff;margin:15px 0 0 0;font-family:sans-serif;font-size:1.1rem}.content-area .modules div{padding:6px 20px 6px 0}.content-area .icon-cont div{display:inline-block;vertical-align:middle;margin-top:5px}.content-area .icon-cont .icon{width:18px;height:18px;background-size:cover;margin:0 15px 0 0;fill:#fff}.content-area .icon-cont .icon-add{background-image:url(../images/icon/ic_add_circle_outline_24px.svg)}.content-area .icon-cont .icon-edit{background-image:url(../images/icon/ic_edit_24px.svg)}.content-area .icon-cont .icon-visibility{background-image:url(../images/icon/ic_visibility_24px.svg)}.content-area .icon-cont .icon-delete{background-image:url(../images/icon/ic_delete_24px.svg)}.content-area .icon-cont .icon-arrow-back{background-image:url(../images/icon/ic_arrow_back_24px.svg);width:24px;height:24px}.content-area .description{margin-top:25px}.content-area .fix-pad{padding-top:14px} \ No newline at end of file diff --git a/tpl/admin/admin-panel.php b/tpl/admin/admin-panel.php index f09a386..9f9b81c 100644 --- a/tpl/admin/admin-panel.php +++ b/tpl/admin/admin-panel.php @@ -261,7 +261,7 @@
E-Mail:
- + E-Mail Address is required
@@ -303,6 +303,72 @@ + + +

User: engineertrooper

+
+ +
+
+
+
+ +
+
Username:
+
+ + User Name is required +
+
Author Name:
+
+ + Author Name is required +
+
+
+
E-Mail:
+
+ + E-Mail Address is required +
+
+
+
New Password:
+
+ + + New Password is required +
+
New Password repeat:
+
+ + + New Password Repeat is required +
+
+
+ +
+ + + +
+
+ + Dies ist der Footer. + +
+ +

Pages

@@ -434,7 +500,15 @@
- hier kommt das Editorfeld hin + + + +
+ +

Page Information

@@ -491,5 +565,18 @@ Dies ist der Footer + + + + +

Next header line

+
+ + Hier steht Content + + + Dies ist der Footer + +
\ No newline at end of file From a34201ffc23fa6c78393e53f7eebb16e1da1ca96 Mon Sep 17 00:00:00 2001 From: versustunez Date: Tue, 17 Nov 2020 22:34:04 +0100 Subject: [PATCH 12/33] Fix Some errors Fix Input-Fields (not all fixed <3 pls dome) --- public/theme/admin/css/admin-panel.css | 2 +- public/theme/admin/css/style.css | 2 +- public/theme/admin/js/scripts.js | 256 +++++++++++++++++++++++-- public/theme/admin/js/scripts.min.js | 2 +- src/Venom/Models/User.php | 2 +- tpl/admin/admin-panel.php | 178 +++++++---------- 6 files changed, 308 insertions(+), 134 deletions(-) diff --git a/public/theme/admin/css/admin-panel.css b/public/theme/admin/css/admin-panel.css index 0661e48..9f98b0f 100644 --- a/public/theme/admin/css/admin-panel.css +++ b/public/theme/admin/css/admin-panel.css @@ -1 +1 @@ -:focus{outline:0}*{box-sizing:border-box}body,html{width:100vw;height:100vh;overflow:hidden;padding:0;margin:0;background:#1f2857;background:linear-gradient(144deg,#1f2954 25%,#000 50%,#5e002c 80%);background-attachment:fixed;color:#fff;font-family:sans-serif;font-size:16px}.hide{display:none!important}v-table{display:block}v-table v-table-footer,v-table v-table-header,v-table v-table-row{display:grid;grid-auto-columns:1fr;grid-auto-flow:column;-moz-column-gap:20px;column-gap:20px}v-table v-table-header h2{text-align:center}v-table v-table-row{margin:10px 0}v-table .grid12{grid-template-columns:repeat(12,[col-start] 1fr);-moz-column-gap:5px;column-gap:5px}v-table .grid12 .col-1to4{grid-column:1/3}v-table .grid12 .col-1to6{grid-column:1/6}v-table .grid12 .col-5to6{grid-column:5/6}v-table .grid12 .col-6to11{grid-column:6/11}v-table .grid12 .col-6to9{grid-column:6/9}v-table .grid12 .col-9to10{grid-column:9/10}v-table .grid12 .col-11to13{grid-column:11/13}v-label,v-option,v-options,v-select{display:inline-block;box-sizing:border-box}v-select{display:block;position:relative;margin-bottom:.5rem}v-label{padding:.5rem 1rem;background-color:#3949ab;color:#fff;cursor:pointer;display:block;border-radius:4px}v-options{position:absolute;display:flex;border-radius:4px;flex-direction:column;overflow:hidden;max-height:0;top:.5rem;left:.5rem;background-color:#3949ab;z-index:1000;cursor:pointer;color:#fff;box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23)}v-option{padding:.5rem 1rem;min-width:200px}v-option:not([disabled]):hover{background-color:rgba(0,0,0,.3)}v-option[selected]{background-color:#3949ab;color:#fff}v-option[disabled]{color:#aaa}main{display:flex;height:100vh;overflow:hidden}.menu{width:220px;background-color:#1b1b1b;box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23);flex-shrink:0;display:flex;flex-direction:column}.menu .logo{text-align:center;font-family:monospace}.menu div[data-link]{padding:.75rem .5rem;position:relative}.menu div[data-link]:after{background-color:#3949ab;content:"";position:absolute;left:0;bottom:0;height:3px;width:100%;transform:scaleX(0);transition:transform .4s;transform-origin:left}.menu div[data-link]:hover:after{transform:scaleX(1)}.menu div[data-link]:last-child{margin-top:auto}.menu div[data-link].active{font-weight:700}.content-area{padding:0 1%;margin:1rem 1.5rem;flex-grow:1;overflow-y:auto;width:100%;max-height:100%;background:rgba(27,27,27,.5)}.content-area .inline{display:inline}.content-area .inline div{display:inline}.content-area .btn-line{margin-top:35px}.content-area .btn-line div{text-align:right}.content-area .btn-line div button{display:inline;margin-left:10px;min-width:100px}.content-area textarea{background:rgba(27,27,27,.5);color:#fff;margin:15px 0 0 0;font-family:sans-serif;font-size:1.1rem}.content-area .modules div{padding:6px 20px 6px 0}.content-area .icon-cont div{display:inline-block;vertical-align:middle;margin-top:5px}.content-area .icon-cont .icon{width:18px;height:18px;background-size:cover;margin:0 15px 0 0;fill:#fff}.content-area .icon-cont .icon-add{background-image:url(../images/icon/ic_add_circle_outline_24px.svg)}.content-area .icon-cont .icon-edit{background-image:url(../images/icon/ic_edit_24px.svg)}.content-area .icon-cont .icon-visibility{background-image:url(../images/icon/ic_visibility_24px.svg)}.content-area .icon-cont .icon-delete{background-image:url(../images/icon/ic_delete_24px.svg)}.content-area .icon-cont .icon-arrow-back{background-image:url(../images/icon/ic_arrow_back_24px.svg);width:24px;height:24px}.content-area .description{margin-top:25px}.content-area .fix-pad{padding-top:14px} \ No newline at end of file +:focus{outline:0}*{box-sizing:border-box}body,html{width:100vw;height:100vh;overflow:hidden;padding:0;margin:0;background:#1f2857;background:linear-gradient(144deg,#1f2954 25%,#000 50%,#5e002c 80%);background-attachment:fixed;color:#fff;font-family:sans-serif;font-size:16px}.hide{display:none!important}v-table{display:block}v-table v-table-footer,v-table v-table-header,v-table v-table-row{display:grid;grid-auto-columns:1fr;grid-auto-flow:column;-moz-column-gap:20px;column-gap:20px}v-table v-table-header h2{text-align:center}v-table v-table-row{margin:10px 0}v-table .grid12{grid-template-columns:repeat(12,[col-start] 1fr);-moz-column-gap:5px;column-gap:5px}v-table .grid12 .col-1to4{grid-column:1/3}v-table .grid12 .col-1to6{grid-column:1/6}v-table .grid12 .col-5to6{grid-column:5/6}v-table .grid12 .col-6to11{grid-column:6/11}v-table .grid12 .col-6to9{grid-column:6/9}v-table .grid12 .col-9to10{grid-column:9/10}v-table .grid12 .col-11to13{grid-column:11/13}v-label,v-option,v-options,v-select{display:inline-block;box-sizing:border-box}v-select{display:block;position:relative;margin-bottom:.5rem}v-label{padding:.5rem 1rem;background-color:#3949ab;color:#fff;cursor:pointer;display:block;border-radius:4px}v-options{position:absolute;display:flex;border-radius:4px;flex-direction:column;overflow:hidden;max-height:0;top:.5rem;left:.5rem;background-color:#3949ab;z-index:1000;cursor:pointer;color:#fff;box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23)}v-option{padding:.5rem 1rem;min-width:200px}v-option:not([disabled]):hover{background-color:rgba(0,0,0,.3)}v-option[selected]{background-color:#3949ab;color:#fff}v-option[disabled]{color:#aaa}main{display:flex;height:100vh;overflow:hidden}.menu{width:220px;background-color:#1b1b1b;box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23);flex-shrink:0;display:flex;flex-direction:column}.menu .logo{text-align:center;font-family:monospace}.menu div[data-link]{padding:.75rem .5rem;position:relative}.menu div[data-link]:after{background-color:#3949ab;content:"";position:absolute;left:0;bottom:0;height:3px;width:100%;transform:scaleX(0);transition:transform .4s;transform-origin:left}.menu div[data-link]:hover:after{transform:scaleX(1)}.menu div[data-link]:last-child{margin-top:auto}.menu div[data-link].active{font-weight:700}.content-area{padding:0 1%;margin:1rem 1.5rem;flex-grow:1;overflow-y:auto;width:100%;max-height:100%;background:rgba(27,27,27,.5)}.content-area .inline{display:inline}.content-area .inline div{display:inline}.content-area .btn-line{margin-top:35px}.content-area .btn-line div{text-align:right}.content-area .btn-line div button{display:inline;margin-left:10px;min-width:100px}.content-area textarea{background:rgba(27,27,27,.5);color:#fff;margin:15px 0 0 0;font-family:sans-serif;font-size:1.1rem;min-width:100%}.content-area .modules div{padding:6px 20px 6px 0}.content-area .icon-cont div{display:inline-block;vertical-align:middle;margin-top:5px}.content-area .icon-cont .icon{width:18px;height:18px;background-size:cover;margin:0 15px 0 0;fill:#fff}.content-area .icon-cont .icon-add{background-image:url(../images/icon/ic_add_circle_outline_24px.svg)}.content-area .icon-cont .icon-edit{background-image:url(../images/icon/ic_edit_24px.svg)}.content-area .icon-cont .icon-visibility{background-image:url(../images/icon/ic_visibility_24px.svg)}.content-area .icon-cont .icon-delete{background-image:url(../images/icon/ic_delete_24px.svg)}.content-area .icon-cont .icon-arrow-back{background-image:url(../images/icon/ic_arrow_back_24px.svg);width:24px;height:24px}.content-area .description{margin-top:25px}.content-area .fix-pad{padding-top:14px} \ No newline at end of file diff --git a/public/theme/admin/css/style.css b/public/theme/admin/css/style.css index a49f290..be88cf8 100644 --- a/public/theme/admin/css/style.css +++ b/public/theme/admin/css/style.css @@ -1 +1 @@ -:focus{outline:0}*{box-sizing:border-box}body,html{width:100vw;height:100vh;overflow:hidden;padding:0;margin:0;background:#1f2857;background:linear-gradient(144deg,#1f2954 25%,#000 50%,#5e002c 80%);background-attachment:fixed;color:#fff;font-family:sans-serif;font-size:16px}.hide{display:none!important}.btn{border:none;background:#3949ab radial-gradient(circle at 0 0,#3949ab 0,rgba(0,0,0,.2) 100%) no-repeat;color:#fff;padding:5px 15px;margin:10px 0;cursor:pointer;position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;border-radius:4px;box-shadow:0 2px 5px 0 rgba(0,0,0,.26);overflow:hidden;display:flex;justify-content:center;align-items:center;transition:.5s}.btn--outline{background:0 0;border:1px solid #3949ab}.btn:focus{box-shadow:0 3px 8px 1px rgba(0,0,0,.4)}.btn--valid{background:#39ab48 radial-gradient(circle at 0 0,#39ab48 0,rgba(0,0,0,.2) 100%) no-repeat}.btn--accent{background:#ff0089 radial-gradient(circle at 0 0,#ff0089 0,rgba(0,0,0,.2) 100%) no-repeat}.btn--warn{background:#f87229 radial-gradient(circle at 0 0,#f87229 0,rgba(0,0,0,.2) 100%) no-repeat}.btn-fab{border-radius:2rem;width:2em;height:2em;padding:5px}.btn-ripple{position:absolute;top:0;left:0;width:100%;height:100%;overflow:hidden;background:0 0}.btn-ripple__effect{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);opacity:1;width:200%;height:0;padding-bottom:200%;border-radius:50%;background:rgba(190,190,190,.3);-webkit-animation:a-ripple .4s ease-in;animation:a-ripple .4s ease-in}.btn-ripple__effect.to-remove{-webkit-animation:remove-ripple .2s linear;animation:remove-ripple .2s linear}.btn--outline .btn-ripple__effect{background:rgba(57,73,171,.5);z-index:-1}.btn__content{z-index:1;font-weight:400;pointer-events:none}@-webkit-keyframes remove-ripple{from{opacity:1}to{opacity:0}}@keyframes remove-ripple{from{opacity:1}to{opacity:0}}@-webkit-keyframes a-ripple{0%{opacity:0;padding-bottom:0;width:0}25%{opacity:1}100%{width:200%;padding-bottom:200%}}@keyframes a-ripple{0%{opacity:0;padding-bottom:0;width:0}25%{opacity:1}100%{width:200%;padding-bottom:200%}}.input-group input{background-color:rgba(0,0,0,.4);border:none;border-bottom:2px solid #3949ab;color:#fff;padding:6px}.input-group input:focus,.input-group.focus input{border-color:#ff0089}.input-group.valid input{border-color:#39ab48}.input-group.invalid input{border-color:#cf1b1b}.input-group{display:flex;flex-direction:column;margin-bottom:3px;position:relative;padding-top:.7rem}.input-group.no-space{padding-top:.1rem}.input-group label{font-size:.7rem;position:absolute;top:.7rem;left:6px;height:1rem;vertical-align:middle;transform:translateY(50%);color:#dcdcdc;transition:all .2s ease-out}.input-group input:focus~label,.input-group.focus label{top:0;left:0;transform:translateY(0);font-size:.6rem}.input-group .error{display:none}.input-group.invalid .error{margin-top:2px;display:block;font-size:.7rem;color:#f32f32}.switch{display:flex;flex-direction:row;align-items:center;padding:5px 20px 5px 0}.switch input{position:absolute;-webkit-appearance:none;-moz-appearance:none;appearance:none;opacity:0}.switch label{display:block;border-radius:10px;width:40px;height:20px;background-color:#e9e9e9;position:relative;cursor:pointer;margin-right:.5rem}.switch label:after{content:'';background-color:#c51162;position:absolute;top:2px;left:2px;height:16px;width:16px;border-radius:10px;transition:.5s}.switch input:checked+label:after{transform:translateX(20px);background-color:#007769} \ No newline at end of file +:focus{outline:0}*{box-sizing:border-box}body,html{width:100vw;height:100vh;overflow:hidden;padding:0;margin:0;background:#1f2857;background:linear-gradient(144deg,#1f2954 25%,#000 50%,#5e002c 80%);background-attachment:fixed;color:#fff;font-family:sans-serif;font-size:16px}.hide{display:none!important}.btn{border:none;background:#3949ab radial-gradient(circle at 0 0,#3949ab 0,rgba(0,0,0,.2) 100%) no-repeat;color:#fff;padding:7px 1.1rem;margin:10px 0;cursor:pointer;position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;border-radius:4px;box-shadow:0 2px 5px 0 rgba(0,0,0,.26);overflow:hidden;display:flex;justify-content:center;align-items:center;transition:.5s}.btn--outline{background:0 0;border:1px solid #3949ab}.btn:focus{box-shadow:0 3px 8px 1px rgba(0,0,0,.4)}.btn--valid{background:#39ab48 radial-gradient(circle at 0 0,#39ab48 0,rgba(0,0,0,.2) 100%) no-repeat}.btn--accent{background:#ff0089 radial-gradient(circle at 0 0,#ff0089 0,rgba(0,0,0,.2) 100%) no-repeat}.btn--warn{background:#f87229 radial-gradient(circle at 0 0,#f87229 0,rgba(0,0,0,.2) 100%) no-repeat}.btn-fab{border-radius:2rem;width:2em;height:2em;padding:5px}.btn-ripple{position:absolute;top:0;left:0;width:100%;height:100%;overflow:hidden;background:0 0}.btn-ripple__effect{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);opacity:1;width:200%;height:0;padding-bottom:200%;border-radius:50%;background:rgba(190,190,190,.3);-webkit-animation:a-ripple .4s ease-in;animation:a-ripple .4s ease-in}.btn-ripple__effect.to-remove{-webkit-animation:remove-ripple .2s linear;animation:remove-ripple .2s linear}.btn--outline .btn-ripple__effect{background:rgba(57,73,171,.5);z-index:-1}.btn__content{z-index:1;font-weight:400;pointer-events:none}@-webkit-keyframes remove-ripple{from{opacity:1}to{opacity:0}}@keyframes remove-ripple{from{opacity:1}to{opacity:0}}@-webkit-keyframes a-ripple{0%{opacity:0;padding-bottom:0;width:0}25%{opacity:1}100%{width:200%;padding-bottom:200%}}@keyframes a-ripple{0%{opacity:0;padding-bottom:0;width:0}25%{opacity:1}100%{width:200%;padding-bottom:200%}}.input-group input{background-color:rgba(0,0,0,.4);border:none;border-bottom:2px solid #3949ab;color:#fff;padding:calc(.75rem - 1px)}.input-group input:focus,.input-group.focus input{border-color:#ff0089}.input-group.valid input{border-color:#39ab48}.input-group.invalid input{border-color:#cf1b1b}.input-group{display:flex;flex-direction:column;margin-bottom:3px;position:relative;padding-top:.7rem}.input-group.no-space{padding-top:.1rem}.input-group label{font-size:.7rem;position:absolute;top:.7rem;left:6px;height:1.5rem;pointer-events:none;vertical-align:middle;transform:translateY(50%);color:#dcdcdc;transition:all .2s ease-out}.input-group input:focus~label,.input-group.focus label{top:0;left:0;transform:translateY(0);font-size:.6rem}.input-group .error{display:none}.input-group.invalid .error{margin-top:2px;display:block;font-size:.7rem;color:#f32f32}.switch{display:flex;flex-direction:row;align-items:center;padding:5px 20px 5px 0}.switch input{position:absolute;-webkit-appearance:none;-moz-appearance:none;appearance:none;opacity:0}.switch label{display:block;border-radius:10px;width:40px;height:20px;background-color:#e9e9e9;position:relative;cursor:pointer;margin-right:.5rem}.switch label:after{content:'';background-color:#c51162;position:absolute;top:2px;left:2px;height:16px;width:16px;border-radius:10px;transition:.5s}.switch input:checked+label:after{transform:translateX(20px);background-color:#007769} \ No newline at end of file diff --git a/public/theme/admin/js/scripts.js b/public/theme/admin/js/scripts.js index fa36f40..01bf381 100644 --- a/public/theme/admin/js/scripts.js +++ b/public/theme/admin/js/scripts.js @@ -75,6 +75,10 @@ class VUtils { return valueOne === undefined ? valueTwo : valueOne; } + static tempId() { + return 'temp_' + Math.random().toString(36).substr(2, 16); + } + static nodePrototypes() { Node.prototype.find = function (selector) { return this.closest(selector); @@ -422,26 +426,61 @@ class FormHandler { } (function () { - const body = $('body'); - body.addDelegatedEventListener('change input', 'input', (e, el) => { - let errorMessage = $('.error-message', el.find('form')); - if (errorMessage) { - errorMessage.classList.add('hide') + class VButton extends HTMLElement { + constructor() { + super(); + let self = this; + self.id = self.id || VUtils.tempId(); + self.label = document.createElement('label'); + self.input = document.createElement('input'); + self.errorBox = document.createElement('span'); + self.errorBox.classList.add('error'); + self.errorBox.innerHTML = self.dataset.error; + self.label.setAttribute('for', self.id); + self.input.id = self.id; + self.label.innerHTML = self.dataset.label; + self.input.value = self.innerHTML.trim(); + self.innerHTML = ''; + self.input.required = self.hasAttribute('required'); + self.input.name = self.getAttribute('name'); + self.appendChild(self.input) + self.appendChild(self.label) + self.appendChild(self.errorBox); + self.input.addMultiListener('change input', self.cb.bind(self)); } - let parent = el.parentNode; - if (el.value === "") { - parent.classList.remove('focus') - } else { - parent.classList.add('focus') + + connectedCallback() { + let cl = this.classList; + if (this.input.value === "") { + cl.remove('focus') + } else { + cl.add('focus') + } } - if (el.checkValidity()) { - parent.classList.add('valid'); - parent.classList.remove('invalid'); - } else { - parent.classList.remove('valid'); - parent.classList.add('invalid'); + + cb(e) { + let el = e.currentTarget + let errorMessage = $('.error-message', el.find('form')); + if (errorMessage) { + errorMessage.classList.add('hide') + } + let cl = this.classList; + if (el.value === "") { + cl.remove('focus') + } else { + cl.add('focus') + } + if (el.checkValidity()) { + cl.add('valid'); + cl.remove('invalid'); + } else { + cl.remove('valid'); + cl.add('invalid'); + } } - }) + } + + customElements.define("v-button", VButton); if ($('#login')) { new FormHandler('form#login', 'body', () => { @@ -450,4 +489,187 @@ class FormHandler { $('.error-message', el).classList.remove('hide'); }) } +})(); +(() => { + class VEdit { + constructor(selector) { + let self = this; + self.editor = selector instanceof HTMLElement ? selector : $(selector); + self.todoOnKey = {}; + self.keys = []; + self.backup = []; + self.taberr = [">", " ", "\n", "<"]; + self.name = 'veditor-' + self.editor.id; + self.init(); + self.selfClosing = ["img", "area", "base", "br", "col", "embed", "hr", "img", "input", "link", "menuitem", "meta", "param", "source", "track"]; + self.restore(); + } + + init() { + let self = this; + self.editor.addEventListener('keydown', self.handleKey.bind(self)); + self.addKey('Tab', self.pressTab.bind(self)); + self.addKey('<', self.addEmptyTags.bind(self)); + self.addKey('ctrl-z', self.undo.bind(self)); + self.addKey('ctrl-s', self.store.bind(self)); + self.addKey('ctrl-shift-S', self.delete.bind(self)); + self.editor.classList.add(self.name, 'veditor') + } + + registerSelfClosing(name) { + this.selfClosing.push(name); + } + + restore() { + let item = localStorage.getItem(this.name); + if (item) { + this.editor.value = item; + } + } + + delete() { + localStorage.removeItem(this.name); + console.log(`[VEdit] Editor: ${this.name} removed`); + } + + store() { + localStorage.setItem(this.name, this.editor.value); + console.log(`[VEdit] Editor: ${this.name} saved`); + } + + handleKey(e) { + let self = this; + if ((e.ctrlKey && e.key === 'Control') + || (e.shiftKey && e.key === 'Shift')) { + return; + } + let key; + if (e.ctrlKey && e.shiftKey) { + key = 'ctrl-shift-' + e.key; + } else if (e.ctrlKey) { + key = 'ctrl-' + e.key; + } + if (key) { + if (this.keys.indexOf(key) !== -1) { + e.preventDefault(); + this.todoOnKey[key](); + return; + } + } + let cont = self.editor.value; + const pos = self.editor.selectionStart + if (self.backup.length > 50) { + self.backup.shift(); + } + self.backup.push([cont, pos]); + if (self.keys.indexOf(e.key) > -1) { + e.preventDefault(); + let w = self.todoOnKey[e.key](pos, cont, this.editor); + w[0].push(cont.substr(pos)) + self.afterWork(w); + } + } + + undo() { + let back = this.backup.pop() || [this.editor.value, this.editor.selectionStart]; + this.editor.value = back[0]; + this.editor.setSelectionRange(back[1], back[1]); + } + + afterWork(data) { + this.setText(data[0].join("")); + this.editor.setSelectionRange(data[1], data[1]); + } + + setText(text) { + this.editor.value = text; + } + + addKey(name, func) { + this.todoOnKey[name] = func; + this.keys.push(name); + } + + addEmptyTags(pos, cont, e) { + return [[cont.substr(0, pos), '<>'], pos + 1]; + } + + pressTab(pos, cont, e) { + let self = this; + let sub, prevContent, moveTo = pos; + if (pos === 0 || self.taberr.indexOf(cont[pos - 1]) !== -1) { + sub = ` `; + moveTo += 4; + prevContent = cont.substr(0, pos); + } else if (self.taberr.indexOf(cont[pos - 1]) === -1) { + let i = 2; + while (self.taberr.indexOf(cont[pos - i]) === -1 && pos - i > 0) { + i++; + } + if (pos - i > 0) { + i -= 1; + } + let gen = self.generateTag(cont.substr(pos - i, i).trim()); + sub = gen[0]; + moveTo = pos - i + gen[1]; + prevContent = cont.substr(0, pos - i); + } + return [[prevContent, sub], moveTo] + } + + generateTag(sub) { + let raw, + groups = {'.': [], '#': []}, + keys = Object.keys(groups), + cGroup = 'cl', + split = sub.split(/([#.])/g); + raw = split.shift(); + for (let item of split) { + if (keys.indexOf(item) > -1) { + cGroup = item; + continue; + } + groups[cGroup].push(item); + } + let second = ''; + if (groups["."].length > 0) { + second += ` class="${groups["."].join(" ")}"`; + } + if (groups['#'].length > 0) { + second += ` id="${groups['#'].join("-")}"`; + } + const c = this.selfClosing; + let close = ''; + if (c.indexOf(raw.trim()) === -1) { + close = ``; + } + let pre = `<${raw}${second}>`; + return [`${pre}${close}`, pre.length]; + } + } + + class VEditor extends HTMLElement { + constructor() { + super(); + this.editor = document.createElement('textarea'); + this.editor.innerHTML = this.innerHTML; + this.editor.id = this.getAttribute('name'); + for (let attribute of this.attributes) { + this.editor.setAttribute(attribute.name, attribute.value); + } + this.innerHTML = ''; + this.appendChild(this.editor); + this.edit = new VEdit(this.editor); + } + + connectedCallback() { + this.edit.restore(); + } + + disconnectedCallback() { + this.edit.save(); + } + } + + customElements.define("v-editor", VEditor); })(); \ No newline at end of file diff --git a/public/theme/admin/js/scripts.min.js b/public/theme/admin/js/scripts.min.js index 327f1fa..e93880f 100644 --- a/public/theme/admin/js/scripts.min.js +++ b/public/theme/admin/js/scripts.min.js @@ -1 +1 @@ -class VUtils{static makePublic(){VUtils.isUsed||(this.initHandlers(),VUtils.isUsed=!0,console.log("[VUtils] is now available in the Global Space! no VUtils. anymore needed"))}static initHandlers(){window.$=this.$,window.$$=this.$$,window.tryCatch=this.tryCatch,VUtils.nodePrototypes()}static $(e,t){return(t=t||document).querySelector(e)}static $$(e,t){return(t=t||document).querySelectorAll(e)}static tryCatch(e,t,i){e=VUtils.wrap(e,[]),i=i||console.error,t=t||console.log;try{t(...e)}catch(e){i(e)}}static forEach(e,t,i){for(let s=0;s{let n=e.target;if(e.detail instanceof HTMLElement&&(n=e.detail),n instanceof HTMLElement)if(n.matches(t))VUtils.tryCatch([e,n],i,s);else{const o=n.find(t);o&&VUtils.tryCatch([e,o],i,s)}})},Node.prototype.addMultiListener=function(e,t,i={}){let s=e.split(" ");for(let e of s)this.addEventListener(e,t,i)}}}VUtils.makePublic();class VRipple{constructor(e={}){if(!VUtils.isUsed)throw"VRipply is only with Public VUtils usable!";let t=this;if(t.options=JSON.parse('{"classes":["btn-ripple__effect"],"target":"body","selector":".btn-ripple"}'),VUtils.mergeOptions(t.options,e),t.options.selector.indexOf("#")>-1)throw"ID's are not allowed as selector!";this.instanceCheck(),this.ripples=[],requestAnimationFrame(this.initHandler.bind(this))}instanceCheck(){let e=this.options;const t=[e.target,e.selector,e.classes.join(".")].join(" ");VRipple.instances=VRipple.instances||{},VRipple.instances[t]=this}initHandler(){let e=this,t=e.options.selector;$(e.options.target).addDelegatedEventListener("mousedown touchstart",t,(t,i)=>{let s=t.touches?t.touches[0]:t,n=i.parentNode,o=i.createNew("span",e.options),r=n.getBoundingClientRect(),l=s.clientX-r.left,a=s.clientY-r.top;o.style.top=a+"px",o.style.left=l+"px",o._mouseDown=!0,o._animationEnded=!1,e.ripples.push(o)}),document.body.addDelegatedEventListener("animationend","."+VUtils.get(e.options.classes,""),e.rippleEnd.bind(e)),document.body._vRippleInit||(document.body.addMultiListener("mouseup touchend mouseleave rippleClose",t=>{let i=Object.keys(VRipple.instances);for(let s of i)for(let i of VRipple.instances[s].ripples)e.rippleEnd.bind(VRipple.instances[s])(t,i)}),document.body._vRippleInit=!0)}rippleEnd(e,t){t.parentNode&&("animationend"===e.type?t._animationEnded=!0:t._mouseDown=!1,!t._mouseDown&&t._animationEnded&&(t.classList.contains("to-remove")?(t.parentNode.removeChild(t),this.ripples.splice(this.ripples.indexOf(t),1)):t.classList.add("to-remove")))}}const rippler=new VRipple;!function(){window._openVSelect=null,requestAnimationFrame(e=>{document.body.addEventListener("click",e=>{window._openVSelect&&e.target.closest("v-select")!==window._openVSelect&&window._openVSelect.toggle(!1)})});class e extends HTMLElement{constructor(){super();let e=this;e._in=this.attachInternals(),e._in.role="select",e.setAttribute("tabindex",0),e.update()}static get formAssociated(){return!0}static get observedAttributes(){return["required","validity"]}get required(){return this.hasAttribute("required")}set required(e){this.toggleAttribute("required",Boolean(e))}get name(){return this.getAttribute("name")}set name(e){this.toggleAttribute("name",e)}get form(){return this._in.form}get options(){return $$("v-options v-option",this)}get selected(){return $$("v-options v-option[selected]",this)}update(){let e=[],t=$("v-label",this),i=new FormData;this.selected.forEach(t=>{e.push(t.innerText),i.append(this.name,t.value)}),t.attributeChangedCallback("value","",e.join(", ")),this.required&&0===e.length?this._in.setValidity({customError:!0},"Option is needed"):this._in.setValidity({}),this._in.setFormValue(i)}checkValidity(){return this._in.checkValidity()}reportValidity(){return this._in.reportValidity()}toggle(e){window._openVSelect&&e&&window._openVSelect.toggleSelect(!1);const t=$("v-options",this);if(!e||this.isOpen)t.style.maxHeight="0",window._openVSelect=!1,this.isOpen=!1,this.update();else{t.focus();let e=0,i=t.children;for(let t=0;t{let t=this.parentNode.parentNode,i=!this.selected;if(!t.hasAttribute("multiple")){t.toggle(!1);for(let e of t.selected)e!==this&&e.removeAttribute("selected")}this.disabled||(this.attributeChangedCallback("selected",!1,i,!0),this.parentNode.parentNode.update())})}static get observedAttributes(){return["selected","disabled","value"]}attributeChangedCallback(e,t,i,s){"selected"===e&&this.hasAttribute("disabled")?this.removeAttribute(e):("disabled"===e&&!0===i&&this.hasAttribute("selected")&&this.attributeChangedCallback("selected",!1,!1),s&&(i?this.setAttribute(e,i):this.removeAttribute(e)),this[e]=i)}}class i extends HTMLElement{constructor(){super(),this.empty=this.getAttribute("empty")||"",this.innerHTML=this.getAttribute("value")||this.empty,this.addEventListener("click",this.openPopUp.bind(this))}static get observedAttributes(){return["empty","value"]}openPopUp(){this.parentNode.toggle(!0)}attributeChangedCallback(e,t,i){"value"===e&&(this.innerHTML=i||this.empty),this[e]=i}}customElements.define("v-label",i),customElements.define("v-option",t),customElements.define("v-select",e)}();class FormHandler{constructor(e,t,i,s){this.cb=i||console.log,this.err=s||console.err,$(t).addDelegatedEventListener("submit",e,this.handleEvent.bind(this))}handleEvent(e,t){if(e.preventDefault(),t.checkValidity()){if(""===(t.action??""))return void console.error("No URL Found on Form",t);fetch(t.action,{method:t.method.toUpperCase(),credentials:"same-origin",body:new FormData(t),redirect:"manual"}).then(e=>{if(!e.ok)throw new Error("Network response errored");return e.json()}).then(e=>this.cb(e,t)).catch(e=>this.err(e,t))}else VUtils.forEach($$("input",t),e=>{if(!e.checkValidity()){let t=e.parentNode;t.classList.remove("valid"),t.classList.add("invalid")}})}}$("body").addDelegatedEventListener("change input","input",(e,t)=>{let i=$(".error-message",t.find("form"));i&&i.classList.add("hide");let s=t.parentNode;""===t.value?s.classList.remove("focus"):s.classList.add("focus"),t.checkValidity()?(s.classList.add("valid"),s.classList.remove("invalid")):(s.classList.remove("valid"),s.classList.add("invalid"))}),$("#login")&&new FormHandler("form#login","body",()=>{location.reload()},(e,t)=>{$(".error-message",t).classList.remove("hide")}); \ No newline at end of file +class VUtils{static makePublic(){VUtils.isUsed||(this.initHandlers(),VUtils.isUsed=!0,console.log("[VUtils] is now available in the Global Space! no VUtils. anymore needed"))}static initHandlers(){window.$=this.$,window.$$=this.$$,window.tryCatch=this.tryCatch,VUtils.nodePrototypes()}static $(e,t){return(t=t||document).querySelector(e)}static $$(e,t){return(t=t||document).querySelectorAll(e)}static tryCatch(e,t,i){e=VUtils.wrap(e,[]),i=i||console.error,t=t||console.log;try{t(...e)}catch(e){i(e)}}static forEach(e,t,i){for(let s=0;s{let n=e.target;if(e.detail instanceof HTMLElement&&(n=e.detail),n instanceof HTMLElement)if(n.matches(t))VUtils.tryCatch([e,n],i,s);else{const r=n.find(t);r&&VUtils.tryCatch([e,r],i,s)}})},Node.prototype.addMultiListener=function(e,t,i={}){let s=e.split(" ");for(let e of s)this.addEventListener(e,t,i)}}}VUtils.makePublic();class VRipple{constructor(e={}){if(!VUtils.isUsed)throw"VRipply is only with Public VUtils usable!";let t=this;if(t.options=JSON.parse('{"classes":["btn-ripple__effect"],"target":"body","selector":".btn-ripple"}'),VUtils.mergeOptions(t.options,e),t.options.selector.indexOf("#")>-1)throw"ID's are not allowed as selector!";this.instanceCheck(),this.ripples=[],requestAnimationFrame(this.initHandler.bind(this))}instanceCheck(){let e=this.options;const t=[e.target,e.selector,e.classes.join(".")].join(" ");VRipple.instances=VRipple.instances||{},VRipple.instances[t]=this}initHandler(){let e=this,t=e.options.selector;$(e.options.target).addDelegatedEventListener("mousedown touchstart",t,(t,i)=>{let s=t.touches?t.touches[0]:t,n=i.parentNode,r=i.createNew("span",e.options),o=n.getBoundingClientRect(),l=s.clientX-o.left,a=s.clientY-o.top;r.style.top=a+"px",r.style.left=l+"px",r._mouseDown=!0,r._animationEnded=!1,e.ripples.push(r)}),document.body.addDelegatedEventListener("animationend","."+VUtils.get(e.options.classes,""),e.rippleEnd.bind(e)),document.body._vRippleInit||(document.body.addMultiListener("mouseup touchend mouseleave rippleClose",t=>{let i=Object.keys(VRipple.instances);for(let s of i)for(let i of VRipple.instances[s].ripples)e.rippleEnd.bind(VRipple.instances[s])(t,i)}),document.body._vRippleInit=!0)}rippleEnd(e,t){t.parentNode&&("animationend"===e.type?t._animationEnded=!0:t._mouseDown=!1,!t._mouseDown&&t._animationEnded&&(t.classList.contains("to-remove")?(t.parentNode.removeChild(t),this.ripples.splice(this.ripples.indexOf(t),1)):t.classList.add("to-remove")))}}const rippler=new VRipple;!function(){window._openVSelect=null,requestAnimationFrame(e=>{document.body.addEventListener("click",e=>{window._openVSelect&&e.target.closest("v-select")!==window._openVSelect&&window._openVSelect.toggle(!1)})});class e extends HTMLElement{constructor(){super();let e=this;e._in=this.attachInternals(),e._in.role="select",e.setAttribute("tabindex",0),e.update()}static get formAssociated(){return!0}static get observedAttributes(){return["required","validity"]}get required(){return this.hasAttribute("required")}set required(e){this.toggleAttribute("required",Boolean(e))}get name(){return this.getAttribute("name")}set name(e){this.toggleAttribute("name",e)}get form(){return this._in.form}get options(){return $$("v-options v-option",this)}get selected(){return $$("v-options v-option[selected]",this)}update(){let e=[],t=$("v-label",this),i=new FormData;this.selected.forEach(t=>{e.push(t.innerText),i.append(this.name,t.value)}),t.attributeChangedCallback("value","",e.join(", ")),this.required&&0===e.length?this._in.setValidity({customError:!0},"Option is needed"):this._in.setValidity({}),this._in.setFormValue(i)}checkValidity(){return this._in.checkValidity()}reportValidity(){return this._in.reportValidity()}toggle(e){window._openVSelect&&e&&window._openVSelect.toggleSelect(!1);const t=$("v-options",this);if(!e||this.isOpen)t.style.maxHeight="0",window._openVSelect=!1,this.isOpen=!1,this.update();else{t.focus();let e=0,i=t.children;for(let t=0;t{let t=this.parentNode.parentNode,i=!this.selected;if(!t.hasAttribute("multiple")){t.toggle(!1);for(let e of t.selected)e!==this&&e.removeAttribute("selected")}this.disabled||(this.attributeChangedCallback("selected",!1,i,!0),this.parentNode.parentNode.update())})}static get observedAttributes(){return["selected","disabled","value"]}attributeChangedCallback(e,t,i,s){"selected"===e&&this.hasAttribute("disabled")?this.removeAttribute(e):("disabled"===e&&!0===i&&this.hasAttribute("selected")&&this.attributeChangedCallback("selected",!1,!1),s&&(i?this.setAttribute(e,i):this.removeAttribute(e)),this[e]=i)}}class i extends HTMLElement{constructor(){super(),this.empty=this.getAttribute("empty")||"",this.innerHTML=this.getAttribute("value")||this.empty,this.addEventListener("click",this.openPopUp.bind(this))}static get observedAttributes(){return["empty","value"]}openPopUp(){this.parentNode.toggle(!0)}attributeChangedCallback(e,t,i){"value"===e&&(this.innerHTML=i||this.empty),this[e]=i}}customElements.define("v-label",i),customElements.define("v-option",t),customElements.define("v-select",e)}();class FormHandler{constructor(e,t,i,s){this.cb=i||console.log,this.err=s||console.err,$(t).addDelegatedEventListener("submit",e,this.handleEvent.bind(this))}handleEvent(e,t){if(e.preventDefault(),t.checkValidity()){if(""===(t.action??""))return void console.error("No URL Found on Form",t);fetch(t.action,{method:t.method.toUpperCase(),credentials:"same-origin",body:new FormData(t),redirect:"manual"}).then(e=>{if(!e.ok)throw new Error("Network response errored");return e.json()}).then(e=>this.cb(e,t)).catch(e=>this.err(e,t))}else VUtils.forEach($$("input",t),e=>{if(!e.checkValidity()){let t=e.parentNode;t.classList.remove("valid"),t.classList.add("invalid")}})}}!function(){class e extends HTMLElement{constructor(){super();let e=this;e.id=e.id||VUtils.tempId(),e.label=document.createElement("label"),e.input=document.createElement("input"),e.errorBox=document.createElement("span"),e.errorBox.classList.add("error"),e.errorBox.innerHTML=e.dataset.error,e.label.setAttribute("for",e.id),e.input.id=e.id,e.label.innerHTML=e.dataset.label,e.input.value=e.innerHTML.trim(),e.innerHTML="",e.input.required=e.hasAttribute("required"),e.input.name=e.getAttribute("name"),e.appendChild(e.input),e.appendChild(e.label),e.appendChild(e.errorBox),e.input.addMultiListener("change input",e.cb.bind(e))}connectedCallback(){let e=this.classList;""===this.input.value?e.remove("focus"):e.add("focus")}cb(e){let t=e.currentTarget,i=$(".error-message",t.find("form"));i&&i.classList.add("hide");let s=this.classList;""===t.value?s.remove("focus"):s.add("focus"),t.checkValidity()?(s.add("valid"),s.remove("invalid")):(s.remove("valid"),s.add("invalid"))}}customElements.define("v-button",e),$("#login")&&new FormHandler("form#login","body",()=>{location.reload()},(e,t)=>{$(".error-message",t).classList.remove("hide")})}(),(()=>{class e{constructor(e){let t=this;t.editor=e instanceof HTMLElement?e:$(e),t.todoOnKey={},t.keys=[],t.backup=[],t.taberr=[">"," ","\n","<"],t.name="veditor-"+t.editor.id,t.init(),t.selfClosing=["img","area","base","br","col","embed","hr","img","input","link","menuitem","meta","param","source","track"],t.restore()}init(){let e=this;e.editor.addEventListener("keydown",e.handleKey.bind(e)),e.addKey("Tab",e.pressTab.bind(e)),e.addKey("<",e.addEmptyTags.bind(e)),e.addKey("ctrl-z",e.undo.bind(e)),e.addKey("ctrl-s",e.store.bind(e)),e.addKey("ctrl-shift-S",e.delete.bind(e)),e.editor.classList.add(e.name,"veditor")}registerSelfClosing(e){this.selfClosing.push(e)}restore(){let e=localStorage.getItem(this.name);e&&(this.editor.value=e)}delete(){localStorage.removeItem(this.name),console.log(`[VEdit] Editor: ${this.name} removed`)}store(){localStorage.setItem(this.name,this.editor.value),console.log(`[VEdit] Editor: ${this.name} saved`)}handleKey(e){let t,i=this;if(e.ctrlKey&&"Control"===e.key||e.shiftKey&&"Shift"===e.key)return;if(e.ctrlKey&&e.shiftKey?t="ctrl-shift-"+e.key:e.ctrlKey&&(t="ctrl-"+e.key),t&&-1!==this.keys.indexOf(t))return e.preventDefault(),void this.todoOnKey[t]();let s=i.editor.value;const n=i.editor.selectionStart;if(i.backup.length>50&&i.backup.shift(),i.backup.push([s,n]),i.keys.indexOf(e.key)>-1){e.preventDefault();let t=i.todoOnKey[e.key](n,s,this.editor);t[0].push(s.substr(n)),i.afterWork(t)}}undo(){let e=this.backup.pop()||[this.editor.value,this.editor.selectionStart];this.editor.value=e[0],this.editor.setSelectionRange(e[1],e[1])}afterWork(e){this.setText(e[0].join("")),this.editor.setSelectionRange(e[1],e[1])}setText(e){this.editor.value=e}addKey(e,t){this.todoOnKey[e]=t,this.keys.push(e)}addEmptyTags(e,t,i){return[[t.substr(0,e),"<>"],e+1]}pressTab(e,t,i){let s,n,r=this,o=e;if(0===e||-1!==r.taberr.indexOf(t[e-1]))s=" ",o+=4,n=t.substr(0,e);else if(-1===r.taberr.indexOf(t[e-1])){let i=2;for(;-1===r.taberr.indexOf(t[e-i])&&e-i>0;)i++;e-i>0&&(i-=1);let l=r.generateTag(t.substr(e-i,i).trim());s=l[0],o=e-i+l[1],n=t.substr(0,e-i)}return[[n,s],o]}generateTag(e){let t,i={".":[],"#":[]},s=Object.keys(i),n="cl",r=e.split(/([#.])/g);t=r.shift();for(let e of r)s.indexOf(e)>-1?n=e:i[n].push(e);let o="";i["."].length>0&&(o+=` class="${i["."].join(" ")}"`),i["#"].length>0&&(o+=` id="${i["#"].join("-")}"`);let l="";-1===this.selfClosing.indexOf(t.trim())&&(l=``);let a=`<${t}${o}>`;return[`${a}${l}`,a.length]}}class t extends HTMLElement{constructor(){super(),this.editor=document.createElement("textarea"),this.editor.innerHTML=this.innerHTML,this.editor.id=this.getAttribute("name");for(let e of this.attributes)this.editor.setAttribute(e.name,e.value);this.innerHTML="",this.appendChild(this.editor),this.edit=new e(this.editor)}connectedCallback(){this.edit.restore()}disconnectedCallback(){this.edit.save()}}customElements.define("v-editor",t)})(); \ No newline at end of file diff --git a/src/Venom/Models/User.php b/src/Venom/Models/User.php index 27e054c..b18e57f 100644 --- a/src/Venom/Models/User.php +++ b/src/Venom/Models/User.php @@ -29,7 +29,7 @@ class User if (isset($_SESSION['userID']) || $this->username !== 'GUEST') { // try to load user from id! $user = DatabaseHandler::get()->getOne("SELECT * FROM users WHERE id = :id OR username = :name AND isActive = 1", [ - ':id' => $_SESSION['userID'], + ':id' => $_SESSION['userID'] ?? -1, ':name' => $this->username ]); if ($user !== null) { diff --git a/tpl/admin/admin-panel.php b/tpl/admin/admin-panel.php index 9f9b81c..4b4b03e 100644 --- a/tpl/admin/admin-panel.php +++ b/tpl/admin/admin-panel.php @@ -33,11 +33,13 @@

Add new Role

-
- - - New Role Name is required -
+

Role Name

-
- - - New Role Name is required -
+
@@ -206,7 +210,7 @@
-
versustunez (Maurice Grönewald)
+
versustunez (Maurice Grönwoldt)
@@ -216,11 +220,12 @@

Add new User

-
- - - New User Name is required -
+
- - -
- - - Dies ist der Footer. - - - -

Pages

@@ -463,11 +411,13 @@

Add new Page

-
- - - New Page Name is required -
+ -
- - - Dies ist der Footer. - -
- - - - -

Role: Admin

-
- -
-
-
-
- -
-

Role Status

-
- - - If enabled role is active. -
-
-
-

Role Name

- -
-
- -
-

Module

-
Meta-Data
-
Pages
-
Roles
-
SEO-URL
-
Users
-
VENOM-Status
-
-
-

Edit

-
- - - -
-
- - - -
-
- - - -
-
- - - -
-
- - - -
-
- - - -
-
-
-

View

-
- - - -
- -
- - - -
- -
- - - -
- -
- - - -
- -
- - - -
- -
- - - -
-
-
- -
- - - -
-
- - Dies ist der Footer. - -
- - - - -

Users

-
- -
-

All Users

-
-
-
ALLE Alphabetisch ordnen!
-
-
-
-
derflieger (Ina Ruh)
-
-
-
-
engineertrooper (Dominic Seela)
-
-
-
-
versustunez (Maurice Grönwoldt)
-
-
-
-
vollglaswasser (Marie Joseph)
-
-
-
-

Add new User

-
- -
- -
-
- - Dies ist der Footer. - -
- - - - -

User: engineertrooper

-
- -
-
-
-
- -
- EngineerTrooper - - Dominic Seela - -
-
- kontakt@engineertrooper.com - -
-
- kontakt@engineertrooper.com - -
New Password:
-
- - - New Password is required -
-
New Password repeat:
-
- - - New Password Repeat is required -
-
-
- -
- - - -
-
- - Dies ist der Footer. - -
- - - -

Pages

-
- -

All Pages

-
- -
Name
-
Edit, View, Delete
-
ID
-
Status
-
- -
Sonnenuntergang am Meer
-
-
-
-
-
-
-
-
-
-
-
-
- 4 -
-
- - - - Visible - Privat - - -
-
- -
Fernsehen zu Zweit mit Nebenwirkungen
-
-
-
-
-
-
-
-
-
-
-
-
- 7 -
-
- - - - Visible - Privat - - -
-
- -
Spiele 1 und 2 und 3
-
-
-
-
-
-
-
-
-
-
-
-
- 14 -
-
- - - - Visible - Privat - - -
-
- -
-

Add new Page

- - -
-
- - Dies ist der Footer. - -
- - - - -

Page: Fernsehen zu Zweit mit Nebenwirkungen

-
- -
-
-
-
- -

Seitenname:

-
- - - - - ! - - -
- -
-
- -

Page Information

-
- -
Author
-
ID
-
SEO-URL
-
Status
-
- -
- - - - engineertrooper (Dominic Seela) - versustunez (Maurice Grönwoldt) - - -
-
- 7 -
-
- https://beispiel.seo-url.de/blabla/23455234 -
-
- - - - Visible - Privat - - -
-
- -
- - - -
-
- - Dies ist der Footer - -
- - - - -

Next header line

-
- - Hier steht Content - - - Dies ist der Footer - -
+
+
+ + +
\ No newline at end of file diff --git a/tpl/admin/jsTemplates/includes/btn.tpl b/tpl/admin/jsTemplates/includes/btn.tpl new file mode 100644 index 0000000..61f906e --- /dev/null +++ b/tpl/admin/jsTemplates/includes/btn.tpl @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/tpl/admin/jsTemplates/includes/input.tpl b/tpl/admin/jsTemplates/includes/input.tpl new file mode 100644 index 0000000..8067657 --- /dev/null +++ b/tpl/admin/jsTemplates/includes/input.tpl @@ -0,0 +1,7 @@ + + \ No newline at end of file diff --git a/tpl/admin/jsTemplates/includes/select.tpl b/tpl/admin/jsTemplates/includes/select.tpl new file mode 100644 index 0000000..552c5f5 --- /dev/null +++ b/tpl/admin/jsTemplates/includes/select.tpl @@ -0,0 +1,8 @@ + + + + {foreach(object as item)} + ${item.name} + {/for} + + \ No newline at end of file diff --git a/tpl/admin/jsTemplates/includes/svg.tpl b/tpl/admin/jsTemplates/includes/svg.tpl new file mode 100644 index 0000000..269d709 --- /dev/null +++ b/tpl/admin/jsTemplates/includes/svg.tpl @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/tpl/admin/jsTemplates/rolesList.tpl b/tpl/admin/jsTemplates/rolesList.tpl new file mode 100644 index 0000000..6cf73d9 --- /dev/null +++ b/tpl/admin/jsTemplates/rolesList.tpl @@ -0,0 +1,23 @@ + + +

Roles

+
+ +
+

Overview

+ {foreach(roles as role,key)} +
+ + {include(includes/svg;icon=$role.icon)} + + ${role.name} +
+ {/for} +
+
+

Add new Role

+ {include(includes/input;label=New Role Name;name=newRoleame;error=New Role Name is required;type=text)} + {include(includes/btn;type=primary;content=Add)} +
+
+
\ No newline at end of file diff --git a/tpl/admin/login.php b/tpl/admin/login.php index 68035ac..00c2321 100644 --- a/tpl/admin/login.php +++ b/tpl/admin/login.php @@ -4,16 +4,21 @@

Login Failed

-
- - - Username is required -
-
- - - Password is required -
+ + + + + + + +
+
+ + + +
+
+ \ No newline at end of file diff --git a/tpl/admin/jsTemplates/pagesList.tpl b/tpl/admin/jsTemplates/pagesList.tpl new file mode 100644 index 0000000..465d1b2 --- /dev/null +++ b/tpl/admin/jsTemplates/pagesList.tpl @@ -0,0 +1,22 @@ +
+
+

Pages

+
+
+

Add new Page

+ {include(includes/input;label=New Page Name;name=newPageName;error=New Page Name is required;type=text)} + {include(includes/btn;type=primary;content=Add)} +
+
+

All Pages

+ {foreach(pages as page,key)} +
+ + {include(includes/svg;icon=$page.icon)} + + ${page.name} +
+ {/for} +
+
+ diff --git a/tpl/admin/jsTemplates/roleEdit.tpl b/tpl/admin/jsTemplates/roleEdit.tpl new file mode 100644 index 0000000..1bcf8af --- /dev/null +++ b/tpl/admin/jsTemplates/roleEdit.tpl @@ -0,0 +1,62 @@ +
+
+

Role: ${roles.name}

+
+
+ + {include(includes/svg;class=back-arrow;icon=vt-arrow-back)} + +
+
+

Role Status

+ {include(includes/switch;id=${switch.id};name=permissionEditMetaData;desc=If enabled role is active.)} +
+
+

Role Name

+ +
+ +

Privileges

+ +
+

Module

+
Meta-Data
+
Pages
+
Roles
+
SEO-URL
+
Users
+
VENOM-Status
+
+
+

Edit

+ {include(includes/switch;id=${switch.id};name=permissionEditMetaData)} + {include(includes/switch;id=${switch.id};name=permissionEditPages)} + {include(includes/switch;id=${switch.id};name=permissionEditRoles)} + {include(includes/switch;id=${switch.id};name=permissionEditSeoUrl)} + {include(includes/switch;id=${switch.id};name=permissionEditUsers)} + {include(includes/switch;id=${switch.id};name=permissionEditVenomStatus)} +
+
+

View

+ {include(includes/switch;id=${switch.id};name=permissionViewMetaData)} + {include(includes/switch;id=${switch.id};name=permissionViewPages)} + {include(includes/switch;id=${switch.id};name=permissionViewRoles)} + {include(includes/switch;id=${switch.id};name=permissionViewSeoUrl)} + {include(includes/switch;id=${switch.id};name=permissionViewUsers)} + {include(includes/switch;id=${switch.id};name=permissionViewVenomStatus)} +
+
+
+
+
+ {include(includes/btn;type=valid;content=Save)} + {include(includes/btn;type=primary;content=Reset)} + {include(includes/btn;type=warn;content=Delete Role)} +
+
+
\ No newline at end of file diff --git a/tpl/admin/jsTemplates/rolesList.tpl b/tpl/admin/jsTemplates/rolesList.tpl index 6cf73d9..4ded38c 100644 --- a/tpl/admin/jsTemplates/rolesList.tpl +++ b/tpl/admin/jsTemplates/rolesList.tpl @@ -1,23 +1,23 @@ - - +
+

Roles

- - +
+

Overview

{foreach(roles as role,key)} -
+
{include(includes/svg;icon=$role.icon)} - ${role.name} -
+ ${role.name} +
{/for}

Add new Role

- {include(includes/input;label=New Role Name;name=newRoleame;error=New Role Name is required;type=text)} + {include(includes/input;label=New Role Name;name=newRoleName;error=New Role Name is required;type=text)} {include(includes/btn;type=primary;content=Add)}
- - \ No newline at end of file +
+
\ No newline at end of file diff --git a/tpl/admin/jsTemplates/seoUrlEdit.tpl b/tpl/admin/jsTemplates/seoUrlEdit.tpl new file mode 100644 index 0000000..8380bbb --- /dev/null +++ b/tpl/admin/jsTemplates/seoUrlEdit.tpl @@ -0,0 +1,10 @@ +
+
+

SEO Urls Edit

+
+
+ + {include(includes/svg;class=back-arrow;icon=vt-arrow-back)} + +
+
\ No newline at end of file diff --git a/tpl/admin/jsTemplates/seoUrlList.tpl b/tpl/admin/jsTemplates/seoUrlList.tpl new file mode 100644 index 0000000..857cd51 --- /dev/null +++ b/tpl/admin/jsTemplates/seoUrlList.tpl @@ -0,0 +1,5 @@ +
+
+

SEO Urls

+
+
\ No newline at end of file diff --git a/tpl/admin/jsTemplates/userEdit.tpl b/tpl/admin/jsTemplates/userEdit.tpl new file mode 100644 index 0000000..9fd2ce0 --- /dev/null +++ b/tpl/admin/jsTemplates/userEdit.tpl @@ -0,0 +1,96 @@ +
+
+

User: engineertrooper

+
+
+ + {include(includes/svg;class=back-arrow;icon=vt-arrow-back)} + +
+
+ EngineerTrooper + +
+
+ Dominic Seela + +
+
+ kontakt@engineertrooper.com + +
+
+ + +
+
+ + +
+ +

Privileges

+ +
+

Module

+
Meta-Data
+
Pages
+
Roles
+
SEO-URL
+
Users
+
VENOM-Status
+
+
+

Edit

+ {include(includes/switch;id=${switch.id};name=permissionEditMetaData)} + {include(includes/switch;id=${switch.id};name=permissionEditPages)} + {include(includes/switch;id=${switch.id};name=permissionEditRoles)} + {include(includes/switch;id=${switch.id};name=permissionEditSeoUrl)} + {include(includes/switch;id=${switch.id};name=permissionEditUsers)} + {include(includes/switch;id=${switch.id};name=permissionEditVenomStatus)} +
+
+

View

+ {include(includes/switch;id=${switch.id};name=permissionViewMetaData)} + {include(includes/switch;id=${switch.id};name=permissionViewPages)} + {include(includes/switch;id=${switch.id};name=permissionViewRoles)} + {include(includes/switch;id=${switch.id};name=permissionViewSeoUrl)} + {include(includes/switch;id=${switch.id};name=permissionViewUsers)} + {include(includes/switch;id=${switch.id};name=permissionViewVenomStatus)} +
+
+
+
+
+ {include(includes/btn;type=valid;content=Save)} + {include(includes/btn;type=primary;content=Reset)} + {include(includes/btn;type=warn;content=Delete Role)} +
+
+
\ No newline at end of file diff --git a/tpl/admin/jsTemplates/usersList.tpl b/tpl/admin/jsTemplates/usersList.tpl new file mode 100644 index 0000000..5a19e04 --- /dev/null +++ b/tpl/admin/jsTemplates/usersList.tpl @@ -0,0 +1,25 @@ +
+
+

Users

+
+
+
+
+
+

Overview

+ {foreach(users as user,key)} +
+ + {include(includes/svg;icon=$user.icon)} + + ${user.name} +
+ {/for} +
+
+

Add new User

+ {include(includes/input;label=New User Name;name=newUserName;error=New User Name is required;type=text)} + {include(includes/btn;type=primary;content=Add)} +
+
+
\ No newline at end of file diff --git a/tpl/admin/jsTemplates/venomStatus.tpl b/tpl/admin/jsTemplates/venomStatus.tpl new file mode 100644 index 0000000..23acf36 --- /dev/null +++ b/tpl/admin/jsTemplates/venomStatus.tpl @@ -0,0 +1,5 @@ +
+
+

Venom Status

+
+
\ No newline at end of file From 147189288ffc070d1c9cf396c8b949bcac5c1cba Mon Sep 17 00:00:00 2001 From: versustunez Date: Sun, 6 Dec 2020 12:30:12 +0100 Subject: [PATCH 17/33] VENOM-10: Moved Modules to Own File to avoid User have to add it! FIXED composer.json --- base/module.base.php | 4 +--- composer.json | 4 ++-- src/Venom/Admin/AdminModulesLoader.php | 20 ++++++++++++++++++++ src/Venom/Venom.php | 4 ++++ 4 files changed, 27 insertions(+), 5 deletions(-) create mode 100644 src/Venom/Admin/AdminModulesLoader.php diff --git a/base/module.base.php b/base/module.base.php index ea74f37..e51a2b3 100755 --- a/base/module.base.php +++ b/base/module.base.php @@ -1,9 +1,7 @@ need to have the Module Class at parent with the init function ;) -$modules = [ - 'role' => Modules\RoleModule::class -]; +$modules = []; // register controllers that can handle templates ;) need to have a render function for this $controllers = [ diff --git a/composer.json b/composer.json index 718e2ae..0791e73 100755 --- a/composer.json +++ b/composer.json @@ -6,12 +6,12 @@ { "name": "Maurice Grönwoldt", "email": "mauricegroenwoldt@gmail.com", - "description": "founder" + "role": "founder" }, { "name": "Dominic Seela", "email": "kontakt@engineertrooper.com", - "description": "friendly developer, supporter" + "role": "friendly developer, supporter" } ], "require": { diff --git a/src/Venom/Admin/AdminModulesLoader.php b/src/Venom/Admin/AdminModulesLoader.php new file mode 100644 index 0000000..da4b77f --- /dev/null +++ b/src/Venom/Admin/AdminModulesLoader.php @@ -0,0 +1,20 @@ + \Modules\MetaDataModule::class, + 'overview' => \Modules\OverviewModule::class, + 'pages' => \Modules\PageModule::class, + 'role' => \Modules\RoleModule::class, + 'seoUrl' => \Modules\SeoUrlModule::class, + 'users' => \Modules\UserModule::class, + 'venomStatus' => \Modules\VenomStatusModule::class, + ]; + } +} \ No newline at end of file diff --git a/src/Venom/Venom.php b/src/Venom/Venom.php index 1c1421d..cda1587 100755 --- a/src/Venom/Venom.php +++ b/src/Venom/Venom.php @@ -5,6 +5,7 @@ namespace Venom; use Venom\Admin\AdminController; +use Venom\Admin\AdminModulesLoader; use Venom\Admin\AdminRouterInit; use Venom\Core\ArgumentHandler; use Venom\Core\Config; @@ -115,6 +116,9 @@ class Venom public function initModules(array $modules): void { + if (Config::getInstance()->isAdmin()) { + $modules = array_merge(AdminModulesLoader::getModules(), $modules); + } foreach ($modules as $key => $moduleClass) { $module = new $moduleClass; if ($module instanceof Module && $module->register($this)) { From 2d9fbc3b99a1db76c6f4b24b35fd107a3af68b8b Mon Sep 17 00:00:00 2001 From: engineerTrooper Date: Tue, 8 Dec 2020 16:32:59 +0100 Subject: [PATCH 18/33] VENOM-2: update minimized files --- public/theme/admin/css/admin-panel.css | 2 +- public/theme/admin/css/style.css | 2 +- public/theme/admin/js/scripts.min.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/public/theme/admin/css/admin-panel.css b/public/theme/admin/css/admin-panel.css index c726ea0..4335a94 100644 --- a/public/theme/admin/css/admin-panel.css +++ b/public/theme/admin/css/admin-panel.css @@ -1 +1 @@ -main{display:flex;height:100vh;overflow:hidden}.menu{width:220px;background-color:#1b1b1b;box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23);flex-shrink:0;display:flex;flex-direction:column}.menu .logo{text-align:center;font-family:monospace}.menu div[data-link]{padding:.75rem .5rem;position:relative}.menu div[data-link]:after{background-color:#3949ab;content:"";position:absolute;left:0;bottom:0;height:3px;width:100%;transform:scaleX(0);transition:transform .4s;transform-origin:left}.menu div[data-link]:hover:after{transform:scaleX(1)}.menu div[data-link]:last-child{margin-top:auto}.menu div[data-link].active{font-weight:700}.content-area{padding:0 1%;margin:1rem 1.5rem;flex-grow:1;overflow-y:auto;width:100%;max-height:100%;background:rgba(27,27,27,.5)}.content-area header{display:block;text-align:center}.content-area .back-arrow{width:36px;height:36px}.content-area .block{display:block}.content-area .flexbox{display:flex;flex-wrap:wrap}.content-area .flexbox div{width:50%}.content-area .inline{display:inline}.content-area .inline div{display:inline}.content-area .btn-line{margin-top:35px}.content-area .btn-line div{text-align:right}.content-area .btn-line div button{display:inline;margin-left:10px;min-width:100px}.content-area .role-edit .block{margin:40px 0}.content-area .role-edit .role-switches h4{padding:0 5px}.content-area .role-edit .role-switches div v-switch{padding:10px 5px;border-bottom:#5d5d5d 1px solid}.content-area .role-edit .role-switches .modules div{padding:14px 5px 8px 5px}.content-area textarea{background:rgba(27,27,27,.5);color:#fff;margin:15px 0 0 0;font-family:sans-serif;font-size:1.1rem;min-width:100%}.content-area .modules div{padding:6px 20px 6px 0}.content-area .fix-pad{padding-top:14px} \ No newline at end of file +main{display:flex;height:100vh;overflow:hidden}main h1{margin-top:30px;margin-bottom:20px}main h2{margin-top:35px;margin-bottom:25px}main h3{margin-top:20px;margin-bottom:15px}main h4{margin-top:15px;margin-bottom:10px}main.nav-open .menu{transform:translateX(0)}main.nav-open .app{transform:translateX(220px)}.app{transform:translateX(0);transition:transform .4s;flex-grow:1;overflow-y:auto;margin:.6rem .8rem;width:100%;max-height:100%;background:rgba(27,27,27,.5);position:relative}.app .nav-toggle{position:absolute;top:1rem;left:1rem}.menu{width:220px;background-color:#1b1b1b;box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23);height:100%;position:fixed;z-index:1;top:0;left:0;overflow-x:hidden;transition:.4s;transform:translateX(-220px);display:flex;flex-direction:column}.menu .logo{text-align:center;font-family:monospace}.menu div[data-link]{padding:.75rem .5rem;position:relative}.menu div[data-link]:after{background-color:#3949ab;content:"";position:absolute;left:0;bottom:0;height:3px;width:100%;transform:scaleX(0);transition:transform .4s;transform-origin:left}.menu div[data-link]:hover:after{transform:scaleX(1)}.menu div[data-link]:last-child{margin-top:auto}.menu div[data-link].active{font-weight:700}.content-area{width:calc(100% - 20px);padding-top:30px;margin:0 auto;display:block}.content-area header{display:block;text-align:center}.content-area header h2{margin:25px 0}.content-area .back-arrow{width:36px;height:36px}.content-area textarea{background:rgba(27,27,27,.5);color:#fff;margin:15px 0 0 0;font-family:sans-serif;font-size:1.1rem;min-width:100%}.content-area .modules div{padding:6px 20px 6px 0}.content-area .add-new,.content-area .overview{width:100%}.content-area .overview div{margin:10px 0}.content-area .overview .icon{display:inline-block}.content-area .add-new{padding-top:25px}@media only screen and (min-width:768px){.content-area{width:calc(100% - 40px)}.content-area .flexbox{display:flex}.content-area .overview{flex-grow:1;width:60%}.content-area .add-new{padding-top:0;flex-grow:1;width:40%}}@media only screen and (min-width:1024px){.content-area{max-width:860px;padding-top:0;margin:0 0 0 20px}}@media only screen and (min-width:1024px){main,main.nav-open{display:flex}main .app,main.nav-open .app{transform:translateX(0)}main .menu,main.nav-open .menu{position:relative;transform:translateX(0)}main .nav-toggle,main.nav-open .nav-toggle{display:none}}.role-edit .privileges .name{font-size:1.15rem}.role-edit .privileges .description{padding-top:2px} \ No newline at end of file diff --git a/public/theme/admin/css/style.css b/public/theme/admin/css/style.css index 15efef7..2fd37e3 100644 --- a/public/theme/admin/css/style.css +++ b/public/theme/admin/css/style.css @@ -1 +1 @@ -:focus{outline:0}*{box-sizing:border-box}body,html{width:100vw;height:100vh;overflow:hidden;padding:0;margin:0;background:#1f2857;background:linear-gradient(144deg,#1f2954 25%,#000 50%,#5e002c 80%);background-attachment:fixed;color:#fff;font-family:sans-serif;font-size:16px}.hide{display:none!important}.icon{width:1em;height:1em;vertical-align:middle;font-size:1em;shape-rendering:geometricPrecision;transition:transform .5s cubic-bezier(.22,.61,.36,1);stroke-width:5px;text-align:center;display:block;margin-right:.5rem}.icon-text{display:inline-block}[data-link]{cursor:pointer}[data-link] *{pointer-events:none}.btn{border:none;background:#3949ab radial-gradient(circle at 0 0,#3949ab 0,rgba(0,0,0,.2) 100%) no-repeat;color:#fff;padding:7px 1.1rem;margin:10px 0;cursor:pointer;position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;border-radius:4px;box-shadow:0 2px 5px 0 rgba(0,0,0,.26);overflow:hidden;display:flex;justify-content:center;align-items:center;transition:.5s}.btn--outline{background:0 0;border:1px solid #3949ab}.btn:focus{box-shadow:0 3px 8px 1px rgba(0,0,0,.4)}.btn--valid{background:#39ab48 radial-gradient(circle at 0 0,#39ab48 0,rgba(0,0,0,.2) 100%) no-repeat}.btn--accent{background:#ff0089 radial-gradient(circle at 0 0,#ff0089 0,rgba(0,0,0,.2) 100%) no-repeat}.btn--warn{background:#f87229 radial-gradient(circle at 0 0,#f87229 0,rgba(0,0,0,.2) 100%) no-repeat}.btn-fab{border-radius:2rem;width:2em;height:2em;padding:5px}.btn-ripple{position:absolute;top:0;left:0;width:100%;height:100%;overflow:hidden;background:0 0}.btn-ripple__effect{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);opacity:1;width:200%;height:0;padding-bottom:200%;border-radius:50%;background:rgba(190,190,190,.3);-webkit-animation:a-ripple .4s ease-in;animation:a-ripple .4s ease-in}.btn-ripple__effect.to-remove{-webkit-animation:remove-ripple .2s linear;animation:remove-ripple .2s linear}.btn--outline .btn-ripple__effect{background:rgba(57,73,171,.5);z-index:-1}.btn__content{z-index:1;font-weight:400;pointer-events:none}@-webkit-keyframes remove-ripple{from{opacity:1}to{opacity:0}}@keyframes remove-ripple{from{opacity:1}to{opacity:0}}@-webkit-keyframes a-ripple{0%{opacity:0;padding-bottom:0;width:0}25%{opacity:1}100%{width:200%;padding-bottom:200%}}@keyframes a-ripple{0%{opacity:0;padding-bottom:0;width:0}25%{opacity:1}100%{width:200%;padding-bottom:200%}}v-input input{background-color:rgba(0,0,0,.4);border:none;border-bottom:2px solid #3949ab;color:#fff;padding:calc(.75rem - 1px)}v-input input:focus,v-input.focus input{border-color:#ff0089}v-input.valid input{border-color:#39ab48}v-input.invalid input{border-color:#cf1b1b}v-input{display:flex;flex-direction:column;margin-bottom:5px;position:relative;padding-top:.7rem}v-input.no-space{padding-top:.1rem}v-input label{font-size:.7rem;position:absolute;top:.7rem;left:5px;height:1.5rem;pointer-events:none;vertical-align:middle;transform:translateY(50%);color:#dcdcdc;transition:all .2s ease-out}v-input input:focus~label,v-input.focus label{top:0;left:0;transform:translateY(0);font-size:.6rem}v-input .error{display:none}v-input.invalid .error{margin-top:2px;display:block;font-size:.7rem;color:#f32f32}v-switch{display:flex;flex-direction:row;align-items:center;padding:5px 20px 5px 0}v-switch input{position:absolute;-webkit-appearance:none;-moz-appearance:none;appearance:none;opacity:0}v-switch label{display:block;border-radius:10px;width:40px;height:20px;background-color:#e9e9e9;position:relative;cursor:pointer;margin-right:.5rem}v-switch label:after{content:'';background-color:#c51162;position:absolute;top:2px;left:2px;height:16px;width:16px;border-radius:10px;transition:.5s}v-switch input:checked+label:after{transform:translateX(20px);background-color:#007769}.loader{display:flex;justify-content:center;align-items:center;width:100vw;height:100vh;position:absolute;top:0;z-index:10000;background-color:rgba(0,0,0,.4);opacity:1;transition:opacity .3s}.loader.hide{display:block;opacity:0;pointer-events:none}.loader .spinner{-webkit-animation:rotator 1.4s linear infinite;animation:rotator 1.4s linear infinite}.loader .path{stroke-dasharray:187;stroke-dashoffset:0;transform-origin:center;-webkit-animation:dash 1.4s ease-in-out infinite,colors 5.6s ease-in-out infinite;animation:dash 1.4s ease-in-out infinite,colors 5.6s ease-in-out infinite}@-webkit-keyframes rotator{from{transform:rotate(0)}to{transform:rotate(270deg)}}@keyframes rotator{from{transform:rotate(0)}to{transform:rotate(270deg)}}@-webkit-keyframes colors{0%{stroke:#4285f4}25%{stroke:#de3e35}50%{stroke:#f7c223}75%{stroke:#1b9a59}100%{stroke:#4285f4}}@keyframes colors{0%{stroke:#4285f4}25%{stroke:#de3e35}50%{stroke:#f7c223}75%{stroke:#1b9a59}100%{stroke:#4285f4}}@-webkit-keyframes dash{0%{stroke-dashoffset:187}50%{stroke-dashoffset:46.75;transform:rotate(135deg)}100%{stroke-dashoffset:187;transform:rotate(450deg)}}@keyframes dash{0%{stroke-dashoffset:187}50%{stroke-dashoffset:46.75;transform:rotate(135deg)}100%{stroke-dashoffset:187;transform:rotate(450deg)}}v-table{display:block}v-table v-table-footer,v-table v-table-header,v-table v-table-row{display:grid;grid-auto-columns:1fr;grid-auto-flow:column;-moz-column-gap:20px;column-gap:20px}v-table v-table-row{background:rgba(27,27,27,.5);border-radius:4px;padding:15px;margin:10px 0}v-table v-table-row h4{margin:0 0 10px 0}v-table .grid12{grid-template-columns:repeat(12,[col-start] 1fr);-moz-column-gap:5px;column-gap:5px}v-table .grid12 .col-1to4{grid-column:1/3}v-table .grid12 .col-1to6{grid-column:1/6}v-table .grid12 .col-5to6{grid-column:5/6}v-table .grid12 .col-6to11{grid-column:6/11}v-table .grid12 .col-6to9{grid-column:6/9}v-table .grid12 .col-9to10{grid-column:9/10}v-table .grid12 .col-11to13{grid-column:11/13}v-label,v-option,v-options,v-select{display:inline-block;box-sizing:border-box}v-select{display:block;position:relative;margin-bottom:.5rem}v-label{padding:.5rem 1rem;background-color:#3949ab;color:#fff;cursor:pointer;display:block;border-radius:4px}v-options{position:absolute;display:flex;border-radius:4px;flex-direction:column;overflow:hidden;max-height:0;top:.5rem;left:.5rem;background-color:#3949ab;z-index:1000;cursor:pointer;color:#fff;box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23)}v-option{padding:.5rem 1rem;min-width:200px}v-option:not([disabled]):hover{background-color:rgba(0,0,0,.3)}v-option[selected]{background-color:#3949ab;color:#fff}v-option[disabled]{color:#aaa} \ No newline at end of file +:focus{outline:0}*{box-sizing:border-box}body,html{width:100vw;height:100vh;overflow:hidden;padding:0;margin:0;background:#1f2857;background:linear-gradient(144deg,#1f2954 25%,#000 50%,#5e002c 80%);background-attachment:fixed;color:#fff;font-family:sans-serif;font-size:16px}.hide{display:none!important}.icon{width:1em;height:1em;vertical-align:middle;font-size:1em;shape-rendering:geometricPrecision;transition:transform .5s cubic-bezier(.22,.61,.36,1);stroke-width:5px;text-align:center;display:block;margin-right:.5rem}[data-link]{cursor:pointer}[data-link] *{pointer-events:none}.btn{border:none;background:#3949ab radial-gradient(circle at 0 0,#3949ab 0,rgba(0,0,0,.2) 100%) no-repeat;color:#fff;padding:7px 1.1rem;margin:10px 0;cursor:pointer;position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;border-radius:4px;box-shadow:0 2px 5px 0 rgba(0,0,0,.26);overflow:hidden;display:flex;justify-content:center;align-items:center;transition:.5s}.btn--outline{background:0 0;border:1px solid #3949ab}.btn:focus{box-shadow:0 3px 8px 1px rgba(0,0,0,.4)}.btn--valid{background:#39ab48 radial-gradient(circle at 0 0,#39ab48 0,rgba(0,0,0,.2) 100%) no-repeat}.btn--accent{background:#ff0089 radial-gradient(circle at 0 0,#ff0089 0,rgba(0,0,0,.2) 100%) no-repeat}.btn--warn{background:#f87229 radial-gradient(circle at 0 0,#f87229 0,rgba(0,0,0,.2) 100%) no-repeat}.btn-fab{border-radius:2rem;width:2em;height:2em;padding:5px}.btn-ripple{position:absolute;top:0;left:0;width:100%;height:100%;overflow:hidden;background:0 0}.btn-ripple__effect{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);opacity:1;width:200%;height:0;padding-bottom:200%;border-radius:50%;background:rgba(190,190,190,.3);-webkit-animation:a-ripple .4s ease-in;animation:a-ripple .4s ease-in}.btn-ripple__effect.to-remove{-webkit-animation:remove-ripple .2s linear;animation:remove-ripple .2s linear}.btn--outline .btn-ripple__effect{background:rgba(57,73,171,.5);z-index:-1}.btn__content{z-index:1;font-weight:400;pointer-events:none}@-webkit-keyframes remove-ripple{from{opacity:1}to{opacity:0}}@keyframes remove-ripple{from{opacity:1}to{opacity:0}}@-webkit-keyframes a-ripple{0%{opacity:0;padding-bottom:0;width:0}25%{opacity:1}100%{width:200%;padding-bottom:200%}}@keyframes a-ripple{0%{opacity:0;padding-bottom:0;width:0}25%{opacity:1}100%{width:200%;padding-bottom:200%}}.btn-line{text-align:right}.btn-line button{margin-right:5px;margin-left:5px;display:inline-block}.btn-line button:last-child{margin-right:0}v-input input{background-color:rgba(0,0,0,.4);border:none;border-bottom:2px solid #3949ab;color:#fff;padding:calc(.75rem - 1px)}v-input input:focus,v-input.focus input{border-color:#ff0089}v-input.valid input{border-color:#39ab48}v-input.invalid input{border-color:#cf1b1b}v-input{display:flex;flex-direction:column;margin-bottom:5px;position:relative;padding-top:.7rem}v-input.no-space{padding-top:.1rem}v-input label{font-size:.7rem;position:absolute;top:.7rem;left:5px;height:1.5rem;pointer-events:none;vertical-align:middle;transform:translateY(50%);color:#dcdcdc;transition:all .2s ease-out}v-input input:focus~label,v-input.focus label{top:0;left:0;transform:translateY(0);font-size:.6rem}v-input .error{display:none}v-input.invalid .error{margin-top:2px;display:block;font-size:.7rem;color:#f32f32}v-switch{display:flex;flex-direction:row;align-items:center}v-switch input{position:absolute;-webkit-appearance:none;-moz-appearance:none;appearance:none;opacity:0}v-switch label{display:block;border-radius:10px;width:40px;height:20px;background-color:#e9e9e9;position:relative;cursor:pointer;margin-right:.5rem}v-switch label:after{content:'';background-color:#c51162;position:absolute;top:2px;left:2px;height:16px;width:16px;border-radius:10px;transition:.5s}v-switch input:checked+label:after{transform:translateX(20px);background-color:#007769}.loader{display:flex;justify-content:center;align-items:center;width:100vw;height:100vh;position:absolute;top:0;z-index:10000;background-color:rgba(0,0,0,.4);opacity:1;transition:opacity .3s}.loader.hide{display:block;opacity:0;pointer-events:none}.loader .spinner{-webkit-animation:rotator 1.4s linear infinite;animation:rotator 1.4s linear infinite}.loader .path{stroke-dasharray:187;stroke-dashoffset:0;transform-origin:center;-webkit-animation:dash 1.4s ease-in-out infinite,colors 5.6s ease-in-out infinite;animation:dash 1.4s ease-in-out infinite,colors 5.6s ease-in-out infinite}@-webkit-keyframes rotator{from{transform:rotate(0)}to{transform:rotate(270deg)}}@keyframes rotator{from{transform:rotate(0)}to{transform:rotate(270deg)}}@-webkit-keyframes colors{0%{stroke:#4285f4}25%{stroke:#de3e35}50%{stroke:#f7c223}75%{stroke:#1b9a59}100%{stroke:#4285f4}}@keyframes colors{0%{stroke:#4285f4}25%{stroke:#de3e35}50%{stroke:#f7c223}75%{stroke:#1b9a59}100%{stroke:#4285f4}}@-webkit-keyframes dash{0%{stroke-dashoffset:187}50%{stroke-dashoffset:46.75;transform:rotate(135deg)}100%{stroke-dashoffset:187;transform:rotate(450deg)}}@keyframes dash{0%{stroke-dashoffset:187}50%{stroke-dashoffset:46.75;transform:rotate(135deg)}100%{stroke-dashoffset:187;transform:rotate(450deg)}}v-table{display:block}v-table v-table-body,v-table v-table-footer,v-table v-table-header,v-table v-table-row{display:grid;grid-auto-columns:1fr;grid-auto-flow:row;-moz-column-gap:20px;column-gap:20px}v-table v-table-body{background:rgba(27,27,27,.5);padding:5px}v-table v-table-body v-table-row{grid-auto-flow:column;border-radius:2px;padding-bottom:10px;border-bottom:solid 1px #aaa;margin:5px 0}v-table v-table-body v-table-row:last-child{padding-bottom:0;border-bottom:none}v-table .grid12{grid-template-columns:repeat(12,[col-start] 1fr);-moz-column-gap:5px;column-gap:5px}v-table .grid12 .col-1to4{grid-column:1/3}v-table .grid12 .col-1to6{grid-column:1/6}v-table .grid12 .col-5to6{grid-column:5/6}v-table .grid12 .col-6to11{grid-column:6/11}v-table .grid12 .col-6to9{grid-column:6/9}v-table .grid12 .col-9to10{grid-column:9/10}v-table .grid12 .col-11to13{grid-column:11/13}v-label,v-option,v-options,v-select{display:inline-block;box-sizing:border-box}v-select{display:block;position:relative;margin-bottom:.5rem}v-label{padding:.5rem .5rem;background-color:rgba(27,27,27,.5);color:#fff;cursor:pointer;display:block;border-bottom:solid 3px #3949ab}v-options{position:absolute;display:flex;flex-direction:column;overflow:hidden;max-height:0;top:.5rem;left:.5rem;background-color:#004a3f;z-index:1000;cursor:pointer;color:#fff;box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23)}v-option{border-bottom:solid 1px #aaa;padding:.5rem 1rem .5rem 5px;min-width:240px}v-option:not([disabled]):hover{background-color:rgba(0,0,0,.3)}v-option[selected]{background-color:#3949ab;color:#fff}v-option[disabled]{color:#aaa} \ No newline at end of file diff --git a/public/theme/admin/js/scripts.min.js b/public/theme/admin/js/scripts.min.js index fa40768..f730976 100644 --- a/public/theme/admin/js/scripts.min.js +++ b/public/theme/admin/js/scripts.min.js @@ -1 +1 @@ -class VUtils{static makePublic(){VUtils.isUsed||(this.initHandlers(),VUtils.isUsed=!0,console.log("[VUtils] is now available in the Global Space! no VUtils. anymore needed"))}static initHandlers(){window.$=this.$,window.$$=this.$$,window.tryCatch=this.tryCatch,VUtils.nodePrototypes()}static $(e,t){return(t=t||document).querySelector(e)}static $$(e,t){return(t=t||document).querySelectorAll(e)}static tryCatch(e,t,i){e=VUtils.wrap(e,[]),i=i||console.error,t=t||console.log;try{t(...e)}catch(e){i(e)}}static forEach(e,t,i){for(let s=0;s{let n=e.target;if(e.detail instanceof HTMLElement&&(n=e.detail),n instanceof HTMLElement)if(n.matches(t))VUtils.tryCatch([e,n],i,s);else{const r=n.find(t);r&&VUtils.tryCatch([e,r],i,s)}})},Node.prototype.addMultiListener=function(e,t,i={}){let s=e.split(" ");for(let e of s)this.addEventListener(e,t,i)}}}VUtils.makePublic();class VRipple{constructor(e={}){if(!VUtils.isUsed)throw"VRipply is only with Public VUtils usable!";let t=this;if(t.options=JSON.parse('{"classes":["btn-ripple__effect"],"target":"body","selector":".btn-ripple"}'),VUtils.mergeOptions(t.options,e),t.options.selector.indexOf("#")>-1)throw"ID's are not allowed as selector!";this.instanceCheck(),this.ripples=[],requestAnimationFrame(this.initHandler.bind(this))}instanceCheck(){let e=this.options;const t=[e.target,e.selector,e.classes.join(".")].join(" ");VRipple.instances=VRipple.instances||{},VRipple.instances[t]=this}initHandler(){let e=this,t=e.options.selector;$(e.options.target).addDelegatedEventListener("mousedown touchstart",t,(t,i)=>{let s=t.touches?t.touches[0]:t,n=i.parentNode,r=i.createNew("span",e.options),a=n.getBoundingClientRect(),o=s.clientX-a.left,l=s.clientY-a.top;r.style.top=l+"px",r.style.left=o+"px",r._mouseDown=!0,r._animationEnded=!1,e.ripples.push(r)}),document.body.addDelegatedEventListener("animationend","."+VUtils.get(e.options.classes,""),e.rippleEnd.bind(e)),document.body._vRippleInit||(document.body.addMultiListener("mouseup touchend mouseleave rippleClose",t=>{let i=Object.keys(VRipple.instances);for(let s of i)for(let i of VRipple.instances[s].ripples)e.rippleEnd.bind(VRipple.instances[s])(t,i)}),document.body._vRippleInit=!0)}rippleEnd(e,t){t.parentNode&&("animationend"===e.type?t._animationEnded=!0:t._mouseDown=!1,!t._mouseDown&&t._animationEnded&&(t.classList.contains("to-remove")?(t.parentNode.removeChild(t),this.ripples.splice(this.ripples.indexOf(t),1)):t.classList.add("to-remove")))}}const rippler=new VRipple;!function(){window._openVSelect=null,requestAnimationFrame(e=>{document.body.addEventListener("click",e=>{window._openVSelect&&e.target.closest("v-select")!==window._openVSelect&&window._openVSelect.toggle(!1)})});class e extends HTMLElement{constructor(){super();let e=this;e._in=this.attachInternals(),e._in.role="select",e.setAttribute("tabindex",0),e.update()}static get formAssociated(){return!0}static get observedAttributes(){return["required","validity"]}get required(){return this.hasAttribute("required")}set required(e){this.toggleAttribute("required",Boolean(e))}get name(){return this.getAttribute("name")}set name(e){this.toggleAttribute("name",e)}get form(){return this._in.form}get options(){return $$("v-options v-option",this)}get selected(){return $$("v-options v-option[selected]",this)}update(){let e=[],t=$("v-label",this),i=new FormData;this.selected.forEach(t=>{e.push(t.innerText),i.append(this.name,t.value)}),t.attributeChangedCallback("value","",e.join(", ")),this.required&&0===e.length?this._in.setValidity({customError:!0},"Option is needed"):this._in.setValidity({}),this._in.setFormValue(i)}checkValidity(){return this._in.checkValidity()}reportValidity(){return this._in.reportValidity()}toggle(e){window._openVSelect&&e&&window._openVSelect.toggleSelect(!1);const t=$("v-options",this);if(!e||this.isOpen)t.style.maxHeight="0",window._openVSelect=!1,this.isOpen=!1,this.update();else{t.focus();let e=0,i=t.children;for(let t=0;t{let t=this.parentNode.parentNode,i=!this.selected;if(!t.hasAttribute("multiple")){t.toggle(!1);for(let e of t.selected)e!==this&&e.removeAttribute("selected")}this.disabled||(this.attributeChangedCallback("selected",!1,i,!0),this.parentNode.parentNode.update())})}static get observedAttributes(){return["selected","disabled","value"]}attributeChangedCallback(e,t,i,s){"selected"===e&&this.hasAttribute("disabled")?this.removeAttribute(e):("disabled"===e&&!0===i&&this.hasAttribute("selected")&&this.attributeChangedCallback("selected",!1,!1),s&&(i?this.setAttribute(e,i):this.removeAttribute(e)),this[e]=i)}}class i extends HTMLElement{constructor(){super(),this.empty=this.getAttribute("empty")||"",this.innerHTML=this.getAttribute("value")||this.empty,this.addEventListener("click",this.openPopUp.bind(this))}static get observedAttributes(){return["empty","value"]}openPopUp(){this.parentNode.toggle(!0)}attributeChangedCallback(e,t,i){"value"===e&&(this.innerHTML=i||this.empty),this[e]=i}}customElements.define("v-label",i),customElements.define("v-option",t),customElements.define("v-select",e)}();class FormHandler{constructor(e,t,i,s){this.cb=i||console.log,this.err=s||console.err,$(t).addDelegatedEventListener("submit",e,this.handleEvent.bind(this))}handleEvent(e,t){if(e.preventDefault(),t.checkValidity()){if(""===(t.action??""))return void console.error("No URL Found on Form",t);fetch(t.action,{method:t.method.toUpperCase(),credentials:"same-origin",body:new FormData(t),redirect:"manual"}).then(e=>{if(!e.ok)throw new Error("Network response errored");return e.json()}).then(e=>this.cb(e,t)).catch(e=>this.err(e,t))}else VUtils.forEach($$("input",t),e=>{if(!e.checkValidity()){let t=e.parentNode;t.classList.remove("valid"),t.classList.add("invalid")}})}}!function(){class e extends HTMLElement{constructor(){super();let e=this;e.id=e.id||VUtils.tempId();let t=e.innerHTML;e.innerHTML="";let i=e.input=e.createNew("input",{id:e.id}),s=e.createNew("label",{content:e.dataset.label});e.createNew("span",{classes:"error",content:e.dataset.error}),s.setAttribute("for",e.id),i.type=e.getAttribute("type")||"text",i.value=t.trim(),i.required=e.hasAttribute("required"),i.name=e.getAttribute("name"),i.addMultiListener("change input",e.cb.bind(e))}connectedCallback(){this.cb({currentTarget:this.input},!0)}cb(e,t){let i=e.currentTarget,s=$(".error-message",i.find("form"));s&&s.classList.add("hide");let n=this.classList;""===i.value?n.remove("focus"):n.add("focus"),i.checkValidity()?(n.add("valid"),n.remove("invalid")):t||(n.remove("valid"),n.add("invalid"))}}class t extends HTMLElement{constructor(){super();const e=this.dataset.id||VUtils.tempId();$("input",this).id=e,$("label",this).setAttribute("for",e)}}customElements.define("v-input",e),customElements.define("v-switch",t),$("#login")&&new FormHandler("form#login","body",()=>{location.reload()},(e,t)=>{$(".error-message",t).classList.remove("hide")})}(),(()=>{class e{constructor(e){let t=this;t.editor=e instanceof HTMLElement?e:$(e),t.todoOnKey={},t.keys=[],t.backup=[],t.taberr=[">"," ","\n","<"],t.name="veditor-"+t.editor.id,t.init(),t.selfClosing=["img","area","base","br","col","embed","hr","img","input","link","menuitem","meta","param","source","track"],t.restore()}init(){let e=this;e.editor.addEventListener("keydown",e.handleKey.bind(e)),e.addKey("Tab",e.pressTab.bind(e)),e.addKey("<",e.addEmptyTags.bind(e)),e.addKey("ctrl-z",e.undo.bind(e)),e.addKey("ctrl-s",e.store.bind(e)),e.addKey("ctrl-shift-S",e.delete.bind(e)),e.editor.classList.add(e.name,"veditor")}registerSelfClosing(e){this.selfClosing.push(e)}restore(){let e=localStorage.getItem(this.name);e&&(this.editor.value=e)}delete(){localStorage.removeItem(this.name),console.log(`[VEdit] Editor: ${this.name} removed`)}store(){localStorage.setItem(this.name,this.editor.value),console.log(`[VEdit] Editor: ${this.name} saved`)}handleKey(e){let t,i=this;if(e.ctrlKey&&"Control"===e.key||e.shiftKey&&"Shift"===e.key)return;if(e.ctrlKey&&e.shiftKey?t="ctrl-shift-"+e.key:e.ctrlKey&&(t="ctrl-"+e.key),t&&-1!==this.keys.indexOf(t))return e.preventDefault(),void this.todoOnKey[t]();let s=i.editor.value;const n=i.editor.selectionStart;if(i.backup.length>50&&i.backup.shift(),i.backup.push([s,n]),i.keys.indexOf(e.key)>-1){e.preventDefault();let t=i.todoOnKey[e.key](n,s,this.editor);t[0].push(s.substr(n)),i.afterWork(t)}}undo(){let e=this.backup.pop()||[this.editor.value,this.editor.selectionStart];this.editor.value=e[0],this.editor.setSelectionRange(e[1],e[1])}afterWork(e){this.setText(e[0].join("")),this.editor.setSelectionRange(e[1],e[1])}setText(e){this.editor.value=e}addKey(e,t){this.todoOnKey[e]=t,this.keys.push(e)}addEmptyTags(e,t,i){return[[t.substr(0,e),"<>"],e+1]}pressTab(e,t,i){let s,n,r=this,a=e;if(0===e||-1!==r.taberr.indexOf(t[e-1]))s=" ",a+=4,n=t.substr(0,e);else if(-1===r.taberr.indexOf(t[e-1])){let i=2;for(;-1===r.taberr.indexOf(t[e-i])&&e-i>0;)i++;e-i>0&&(i-=1);let o=r.generateTag(t.substr(e-i,i).trim());s=o[0],a=e-i+o[1],n=t.substr(0,e-i)}return[[n,s],a]}generateTag(e){let t,i={".":[],"#":[]},s=Object.keys(i),n="cl",r=e.split(/([#.])/g);t=r.shift();for(let e of r)s.indexOf(e)>-1?n=e:i[n].push(e);let a="";i["."].length>0&&(a+=` class="${i["."].join(" ")}"`),i["#"].length>0&&(a+=` id="${i["#"].join("-")}"`);let o="";-1===this.selfClosing.indexOf(t.trim())&&(o=``);let l=`<${t}${a}>`;return[`${l}${o}`,l.length]}}class t extends HTMLElement{constructor(){super(),this.editor=document.createElement("textarea"),this.editor.innerHTML=this.innerHTML,this.editor.id=this.getAttribute("name");for(let e of this.attributes)this.editor.setAttribute(e.name,e.value);this.innerHTML="",this.appendChild(this.editor),this.edit=new e(this.editor)}connectedCallback(){this.edit.restore()}disconnectedCallback(){this.edit.save()}}customElements.define("v-editor",t)})(),window.router=new class{constructor(e){this.options=e,document.body.addDelegatedEventListener("click","[data-link]",(e,t)=>{e.preventDefault(),$$("[data-link].active").forEach(e=>e.classList.remove("active"));let i=$(".loader").classList;i.remove("hide"),this.handleRouting(t.dataset).then(e=>{i.add("hide"),t.classList.add("active")})}),document.body.addEventListener("triggerRouter",e=>{let t=sessionStorage.getItem("url")||JSON.stringify({data:{link:$("[data-link].active").dataset.link}});this.handle(t)}),window.addEventListener("popstate",e=>{this.handle(e.state)}),this.components={},window.dispatchEvent(new CustomEvent("routerReady")),window.routerIsReady=!0}handle(e){e&&(e=JSON.parse(e),this.handleRouting(e.data).then(t=>{let i=$('[data-link="'+e.data.link+'"]');i&&i.classList.add("active")}))}async handleRouting(e){try{let t=e.link,i=this.components[t];if(""===t)return null;i&&(t=i.getUrl(e));let s=await this.handleRequest(t,!0);return s.reload?location.reload():(i=i||this.components[s.component]||null,i?(sessionStorage.setItem("url",JSON.stringify({data:e})),i.handle(s,e).then(t=>{$(this.options.toReplace).innerHTML=t,history.pushState(JSON.stringify({data:e}),document.title)})):await alert("Error"),null)}catch(e){return e}}async handleRequest(e,t){if(""!==(e=e.trim()))return await fetch(e,{credentials:"same-origin"}).then(e=>{if(!e.ok)throw"URL is Status: "+e.status;let i=e.headers.get("Content-Type");return-1!==i.indexOf("json")||t?e.json():-1!==i.indexOf("text")?e.text():e.blob()}).catch(e=>(console.error(e),null))}addComponent(e,t){this.components[e]=t}}({toReplace:".content-area"});class VTpeLCore{constructor(e={}){this.templates={},this.dir=e.path||"/tpl/",this.suffix=e.suffix||"tpl",this.path=e.template||`${this.dir}%s.${this.suffix}`,this.cache=void 0===e.cache||e.cache}async loadTemplate(e){if(this.templates[e])return null;let t=this.path.replace("%s",e),i=await fetch(t,{cache:"force-cache"});if(i.ok){let t=await i.text();this.addTpl(e,t)}return null}async loadArray(e){for(let t of e)await this.loadTemplate(t)}addTpl(e,t){(this.templates[e]=new VTpeLTemplate(e,t,this)).parseContent(this.cache)}async renderOn(e,t){return this.templates[e]?await this.templates[e].render(t):""}}const VParserTypes={content:0,variable:1,for:2,forEach:3,forContent:4,forEnd:5,if:6,ifContent:7,ifEnd:8,assign:9,include:10,none:-1};class VTpeLParser{constructor(e,t){let i=this;i.name=e,i.legex=t.trim(),i.index=0,i.content="",i.parsed=[],i.contexts=[0]}tokenize(){let e=this;for(e.index=0;e.index0&&(this.index+=s),s>0||-1===s}nextContainsRaw(e,t,i){"string"==typeof t&&(t=t.split(""));let s=t.length;if(s<1)return-1;for(let n=0;n ${o}] >> ${s}`),i.index=o+e.length,i.content=s.trim(),void i.addType(t);s+=l}if("\n"===a)return i.index=r.length,i.content=s.trim(),void i.addType(t);throw"Template variable at Position: "+n+" not closed!"}getOperator(e){let t=[];for(let i=0;i{let n=e.target;if(e.detail instanceof HTMLElement&&(n=e.detail),n instanceof HTMLElement)if(n.matches(t))VUtils.tryCatch([e,n],i,s);else{const r=n.find(t);r&&VUtils.tryCatch([e,r],i,s)}})},Node.prototype.addMultiListener=function(e,t,i={}){let s=e.split(" ");for(let e of s)this.addEventListener(e,t,i)}}}VUtils.makePublic();class VRipple{constructor(e={}){if(!VUtils.isUsed)throw"VRipply is only with Public VUtils usable!";let t=this;if(t.options=JSON.parse('{"classes":["btn-ripple__effect"],"target":"body","selector":".btn-ripple"}'),VUtils.mergeOptions(t.options,e),t.options.selector.indexOf("#")>-1)throw"ID's are not allowed as selector!";this.instanceCheck(),this.ripples=[],requestAnimationFrame(this.initHandler.bind(this))}instanceCheck(){let e=this.options;const t=[e.target,e.selector,e.classes.join(".")].join(" ");VRipple.instances=VRipple.instances||{},VRipple.instances[t]=this}initHandler(){let e=this,t=e.options.selector;$(e.options.target).addDelegatedEventListener("mousedown touchstart",t,(t,i)=>{let s=t.touches?t.touches[0]:t,n=i.parentNode,r=i.createNew("span",e.options),a=n.getBoundingClientRect(),o=s.clientX-a.left,l=s.clientY-a.top;r.style.top=l+"px",r.style.left=o+"px",r._mouseDown=!0,r._animationEnded=!1,e.ripples.push(r)}),document.body.addDelegatedEventListener("animationend","."+VUtils.get(e.options.classes,""),e.rippleEnd.bind(e)),document.body._vRippleInit||(document.body.addMultiListener("mouseup touchend mouseleave rippleClose",t=>{let i=Object.keys(VRipple.instances);for(let s of i)for(let i of VRipple.instances[s].ripples)e.rippleEnd.bind(VRipple.instances[s])(t,i)}),document.body._vRippleInit=!0)}rippleEnd(e,t){t.parentNode&&("animationend"===e.type?t._animationEnded=!0:t._mouseDown=!1,!t._mouseDown&&t._animationEnded&&(t.classList.contains("to-remove")?(t.parentNode.removeChild(t),this.ripples.splice(this.ripples.indexOf(t),1)):t.classList.add("to-remove")))}}const rippler=new VRipple;!function(){window._openVSelect=null,requestAnimationFrame(e=>{document.body.addEventListener("click",e=>{window._openVSelect&&e.target.closest("v-select")!==window._openVSelect&&window._openVSelect.toggle(!1)})});class e extends HTMLElement{constructor(){super();let e=this;e._in=this.attachInternals(),e._in.role="select",e.setAttribute("tabindex",0),e.update()}static get formAssociated(){return!0}static get observedAttributes(){return["required","validity"]}get required(){return this.hasAttribute("required")}set required(e){this.toggleAttribute("required",Boolean(e))}get name(){return this.getAttribute("name")}set name(e){this.toggleAttribute("name",e)}get form(){return this._in.form}get options(){return $$("v-options v-option",this)}get selected(){return $$("v-options v-option[selected]",this)}update(){let e=[],t=$("v-label",this),i=new FormData;this.selected.forEach(t=>{e.push(t.innerText),i.append(this.name,t.value)}),t.attributeChangedCallback("value","",e.join(", ")),this.required&&0===e.length?this._in.setValidity({customError:!0},"Option is needed"):this._in.setValidity({}),this._in.setFormValue(i)}checkValidity(){return this._in.checkValidity()}reportValidity(){return this._in.reportValidity()}toggle(e){window._openVSelect&&e&&window._openVSelect.toggleSelect(!1);const t=$("v-options",this);if(!e||this.isOpen)t.style.maxHeight="0",window._openVSelect=!1,this.isOpen=!1,this.update();else{t.focus();let e=0,i=t.children;for(let t=0;t{let t=this.parentNode.parentNode,i=!this.selected;if(!t.hasAttribute("multiple")){t.toggle(!1);for(let e of t.selected)e!==this&&e.removeAttribute("selected")}this.disabled||(this.attributeChangedCallback("selected",!1,i,!0),this.parentNode.parentNode.update())})}static get observedAttributes(){return["selected","disabled","value"]}attributeChangedCallback(e,t,i,s){"selected"===e&&this.hasAttribute("disabled")?this.removeAttribute(e):("disabled"===e&&!0===i&&this.hasAttribute("selected")&&this.attributeChangedCallback("selected",!1,!1),s&&(i?this.setAttribute(e,i):this.removeAttribute(e)),this[e]=i)}}class i extends HTMLElement{constructor(){super(),this.empty=this.getAttribute("empty")||"",this.innerHTML=this.getAttribute("value")||this.empty,this.addEventListener("click",this.openPopUp.bind(this))}static get observedAttributes(){return["empty","value"]}openPopUp(){this.parentNode.toggle(!0)}attributeChangedCallback(e,t,i){"value"===e&&(this.innerHTML=i||this.empty),this[e]=i}}customElements.define("v-label",i),customElements.define("v-option",t),customElements.define("v-select",e)}();class FormHandler{constructor(e,t,i,s){this.cb=i||console.log,this.err=s||console.err,$(t).addDelegatedEventListener("submit",e,this.handleEvent.bind(this))}handleEvent(e,t){if(e.preventDefault(),t.checkValidity()){if(""===(t.action??""))return void console.error("No URL Found on Form",t);fetch(t.action,{method:t.method.toUpperCase(),credentials:"same-origin",body:new FormData(t),redirect:"manual"}).then(e=>{if(!e.ok)throw new Error("Network response errored");return e.json()}).then(e=>this.cb(e,t)).catch(e=>this.err(e,t))}else VUtils.forEach($$("input",t),e=>{if(!e.checkValidity()){let t=e.parentNode;t.classList.remove("valid"),t.classList.add("invalid")}})}}!function(){class e extends HTMLElement{constructor(){super();let e=this;e.id=e.id||VUtils.tempId();let t=e.innerHTML;e.innerHTML="";let i=e.input=e.createNew("input",{id:e.id}),s=e.createNew("label",{content:e.dataset.label});e.createNew("span",{classes:"error",content:e.dataset.error}),s.setAttribute("for",e.id),i.type=e.getAttribute("type")||"text",i.value=t.trim(),i.required=e.hasAttribute("required"),i.name=e.getAttribute("name"),i.addMultiListener("change input",e.cb.bind(e))}connectedCallback(){this.cb({currentTarget:this.input},!0)}cb(e,t){let i=e.currentTarget,s=$(".error-message",i.find("form"));s&&s.classList.add("hide");let n=this.classList;""===i.value?n.remove("focus"):n.add("focus"),i.checkValidity()?(n.add("valid"),n.remove("invalid")):t||(n.remove("valid"),n.add("invalid"))}}class t extends HTMLElement{constructor(){super();const e=this.dataset.id||VUtils.tempId();$("input",this).id=e,$("label",this).setAttribute("for",e)}}customElements.define("v-input",e),customElements.define("v-switch",t),$("#login")&&new FormHandler("form#login","body",()=>{location.reload()},(e,t)=>{$(".error-message",t).classList.remove("hide")})}(),(()=>{class e{constructor(e){let t=this;t.editor=e instanceof HTMLElement?e:$(e),t.todoOnKey={},t.keys=[],t.backup=[],t.taberr=[">"," ","\n","<"],t.name="veditor-"+t.editor.id,t.init(),t.selfClosing=["img","area","base","br","col","embed","hr","img","input","link","menuitem","meta","param","source","track"],t.restore()}init(){let e=this;e.editor.addEventListener("keydown",e.handleKey.bind(e)),e.addKey("Tab",e.pressTab.bind(e)),e.addKey("<",e.addEmptyTags.bind(e)),e.addKey("ctrl-z",e.undo.bind(e)),e.addKey("ctrl-s",e.store.bind(e)),e.addKey("ctrl-shift-S",e.delete.bind(e)),e.editor.classList.add(e.name,"veditor")}registerSelfClosing(e){this.selfClosing.push(e)}restore(){let e=localStorage.getItem(this.name);e&&(this.editor.value=e)}delete(){localStorage.removeItem(this.name),console.log(`[VEdit] Editor: ${this.name} removed`)}store(){localStorage.setItem(this.name,this.editor.value),console.log(`[VEdit] Editor: ${this.name} saved`)}handleKey(e){let t,i=this;if(e.ctrlKey&&"Control"===e.key||e.shiftKey&&"Shift"===e.key)return;if(e.ctrlKey&&e.shiftKey?t="ctrl-shift-"+e.key:e.ctrlKey&&(t="ctrl-"+e.key),t&&-1!==this.keys.indexOf(t))return e.preventDefault(),void this.todoOnKey[t]();let s=i.editor.value;const n=i.editor.selectionStart;if(i.backup.length>50&&i.backup.shift(),i.backup.push([s,n]),i.keys.indexOf(e.key)>-1){e.preventDefault();let t=i.todoOnKey[e.key](n,s,this.editor);t[0].push(s.substr(n)),i.afterWork(t)}}undo(){let e=this.backup.pop()||[this.editor.value,this.editor.selectionStart];this.editor.value=e[0],this.editor.setSelectionRange(e[1],e[1])}afterWork(e){this.setText(e[0].join("")),this.editor.setSelectionRange(e[1],e[1])}setText(e){this.editor.value=e}addKey(e,t){this.todoOnKey[e]=t,this.keys.push(e)}addEmptyTags(e,t,i){return[[t.substr(0,e),"<>"],e+1]}pressTab(e,t,i){let s,n,r=this,a=e;if(0===e||-1!==r.taberr.indexOf(t[e-1]))s=" ",a+=4,n=t.substr(0,e);else if(-1===r.taberr.indexOf(t[e-1])){let i=2;for(;-1===r.taberr.indexOf(t[e-i])&&e-i>0;)i++;e-i>0&&(i-=1);let o=r.generateTag(t.substr(e-i,i).trim());s=o[0],a=e-i+o[1],n=t.substr(0,e-i)}return[[n,s],a]}generateTag(e){let t,i={".":[],"#":[]},s=Object.keys(i),n="cl",r=e.split(/([#.])/g);t=r.shift();for(let e of r)s.indexOf(e)>-1?n=e:i[n].push(e);let a="";i["."].length>0&&(a+=` class="${i["."].join(" ")}"`),i["#"].length>0&&(a+=` id="${i["#"].join("-")}"`);let o="";-1===this.selfClosing.indexOf(t.trim())&&(o=``);let l=`<${t}${a}>`;return[`${l}${o}`,l.length]}}class t extends HTMLElement{constructor(){super(),this.editor=document.createElement("textarea"),this.editor.innerHTML=this.innerHTML,this.editor.id=this.getAttribute("name");for(let e of this.attributes)this.editor.setAttribute(e.name,e.value);this.innerHTML="",this.appendChild(this.editor),this.edit=new e(this.editor)}connectedCallback(){this.edit.restore()}disconnectedCallback(){this.edit.save()}}customElements.define("v-editor",t)})(),(()=>{const e=$("main");function t(){return window.matchMedia("(max-width: 1023px)").matches}window.isMobileDevice=t,$("body").addDelegatedEventListener("click",".nav-toggle",(i,s)=>{t()&&e.classList.toggle("nav-open")})})(),window.router=new class{constructor(e){this.options=e,document.body.addDelegatedEventListener("click","[data-link]",(e,t)=>{e.preventDefault(),$$("[data-link].active").forEach(e=>e.classList.remove("active"));let i=$(".loader").classList;i.remove("hide"),this.handleRouting(t.dataset).then(e=>{i.add("hide"),t.classList.add("active")})}),document.body.addEventListener("triggerRouter",e=>{let t=sessionStorage.getItem("url")||JSON.stringify({data:{link:$("[data-link].active").dataset.link}});this.handle(t)}),window.addEventListener("popstate",e=>{this.handle(e.state)}),this.components={},window.dispatchEvent(new CustomEvent("routerReady")),window.routerIsReady=!0}handle(e){e&&(e=JSON.parse(e),this.handleRouting(e.data).then(t=>{let i=$('[data-link="'+e.data.link+'"]');i&&i.classList.add("active")}))}async handleRouting(e){try{let t=e.link,i=this.components[t];if(""===t)return null;i&&(t=i.getUrl(e));let s=await this.handleRequest(t,!0);return s.reload?location.reload():(i=i||this.components[s.component]||null,i?(sessionStorage.setItem("url",JSON.stringify({data:e})),i.handle(s,e).then(t=>{$(this.options.toReplace).innerHTML=t,history.pushState(JSON.stringify({data:e}),document.title)})):await alert("Error"),null)}catch(e){return e}}async handleRequest(e,t){if(""!==(e=e.trim()))return await fetch(e,{credentials:"same-origin"}).then(e=>{if(!e.ok)throw"URL is Status: "+e.status;let i=e.headers.get("Content-Type");return-1!==i.indexOf("json")||t?e.json():-1!==i.indexOf("text")?e.text():e.blob()}).catch(e=>(console.error(e),null))}addComponent(e,t){this.components[e]=t}}({toReplace:".content-area"});class VTpeLCore{constructor(e={}){this.templates={},this.dir=e.path||"/tpl/",this.suffix=e.suffix||"tpl",this.path=e.template||`${this.dir}%s.${this.suffix}`,this.cache=void 0===e.cache||e.cache}async loadTemplate(e){if(this.templates[e])return null;let t=this.path.replace("%s",e),i=await fetch(t,{cache:"force-cache"});if(i.ok){let t=await i.text();this.addTpl(e,t)}return null}async loadArray(e){for(let t of e)await this.loadTemplate(t)}addTpl(e,t){(this.templates[e]=new VTpeLTemplate(e,t,this)).parseContent(this.cache)}async renderOn(e,t){return this.templates[e]?await this.templates[e].render(t):""}}const VParserTypes={content:0,variable:1,for:2,forEach:3,forContent:4,forEnd:5,if:6,ifContent:7,ifEnd:8,assign:9,include:10,none:-1};class VTpeLParser{constructor(e,t){let i=this;i.name=e,i.legex=t.trim(),i.index=0,i.content="",i.parsed=[],i.contexts=[0]}tokenize(){let e=this;for(e.index=0;e.index0&&(this.index+=s),s>0||-1===s}nextContainsRaw(e,t,i){"string"==typeof t&&(t=t.split(""));let s=t.length;if(s<1)return-1;for(let n=0;n ${o}] >> ${s}`),i.index=o+e.length,i.content=s.trim(),void i.addType(t);s+=l}if("\n"===a)return i.index=r.length,i.content=s.trim(),void i.addType(t);throw"Template variable at Position: "+n+" not closed!"}getOperator(e){let t=[];for(let i=0;i Date: Tue, 8 Dec 2020 16:33:34 +0100 Subject: [PATCH 19/33] VENOM-10: added new function isMobileDevice --- public/theme/admin/js/scripts.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/public/theme/admin/js/scripts.js b/public/theme/admin/js/scripts.js index c943fb7..0bf8cc0 100644 --- a/public/theme/admin/js/scripts.js +++ b/public/theme/admin/js/scripts.js @@ -679,6 +679,21 @@ class FormHandler { customElements.define("v-editor", VEditor); })(); +(() => { + const mobileBreakpoint = 1023; + const main = $('main'); + + function isMobileDevice() { + return window.matchMedia("(max-width: " + mobileBreakpoint + "px)").matches; + } + window.isMobileDevice = isMobileDevice; + $('body').addDelegatedEventListener('click', '.nav-toggle', (e, el) => { + if (isMobileDevice()) { + main.classList.toggle('nav-open'); + } + }); + +})(); (() => { class Router { constructor(options) { From aacf885c561d4373dbbe87cb76789b0066cecd91 Mon Sep 17 00:00:00 2001 From: engineerTrooper Date: Tue, 8 Dec 2020 16:38:01 +0100 Subject: [PATCH 20/33] VENOM-2: changed code to V-Utils via VTepl -> result: minified code, added classes and flexbox, fixed data display at v-table --- tpl/admin/jsTemplates/pagesList.tpl | 26 ++--- tpl/admin/jsTemplates/roleEdit.tpl | 79 +++++++------- tpl/admin/jsTemplates/rolesList.tpl | 6 +- tpl/admin/jsTemplates/userEdit.tpl | 153 ++++++++++++---------------- tpl/admin/jsTemplates/usersList.tpl | 4 +- 5 files changed, 124 insertions(+), 144 deletions(-) diff --git a/tpl/admin/jsTemplates/pagesList.tpl b/tpl/admin/jsTemplates/pagesList.tpl index 465d1b2..29509ad 100644 --- a/tpl/admin/jsTemplates/pagesList.tpl +++ b/tpl/admin/jsTemplates/pagesList.tpl @@ -2,21 +2,21 @@

Pages

-
-

Add new Page

- {include(includes/input;label=New Page Name;name=newPageName;error=New Page Name is required;type=text)} - {include(includes/btn;type=primary;content=Add)} -
-
-

All Pages

- {foreach(pages as page,key)} -
- +
+

Add new Page

+ {include(includes/input;label=New Page Name;name=newPageName;error=New Page Name is required;type=text)} + {include(includes/btn;type=primary;content=Add)} +
+
+

All Pages

+ {foreach(pages as page,key)} +
+ {include(includes/svg;icon=$page.icon)} - ${page.name} + ${page.name} +
+ {/for}
- {/for} -
diff --git a/tpl/admin/jsTemplates/roleEdit.tpl b/tpl/admin/jsTemplates/roleEdit.tpl index 1bcf8af..70faaa6 100644 --- a/tpl/admin/jsTemplates/roleEdit.tpl +++ b/tpl/admin/jsTemplates/roleEdit.tpl @@ -7,50 +7,53 @@ {include(includes/svg;class=back-arrow;icon=vt-arrow-back)} -
+

Role Status

{include(includes/switch;id=${switch.id};name=permissionEditMetaData;desc=If enabled role is active.)}
-
+

Role Name

- + {include(includes/input;class=input-group;label=Change Name;name=roleName;error=Role Name is required;default=Admin)}
- +

Privileges

- -
-

Module

-
Meta-Data
-
Pages
-
Roles
-
SEO-URL
-
Users
-
VENOM-Status
-
-
-

Edit

- {include(includes/switch;id=${switch.id};name=permissionEditMetaData)} - {include(includes/switch;id=${switch.id};name=permissionEditPages)} - {include(includes/switch;id=${switch.id};name=permissionEditRoles)} - {include(includes/switch;id=${switch.id};name=permissionEditSeoUrl)} - {include(includes/switch;id=${switch.id};name=permissionEditUsers)} - {include(includes/switch;id=${switch.id};name=permissionEditVenomStatus)} -
-
-

View

- {include(includes/switch;id=${switch.id};name=permissionViewMetaData)} - {include(includes/switch;id=${switch.id};name=permissionViewPages)} - {include(includes/switch;id=${switch.id};name=permissionViewRoles)} - {include(includes/switch;id=${switch.id};name=permissionViewSeoUrl)} - {include(includes/switch;id=${switch.id};name=permissionViewUsers)} - {include(includes/switch;id=${switch.id};name=permissionViewVenomStatus)} -
-
+ + + Module + Edit + View + + + Meta-Data + {include(includes/switch;id=${switch.id};name=permissionEditMetaData)} + {include(includes/switch;id=${switch.id};name=permissionViewMetaData)} + + + Pages + {include(includes/switch;id=${switch.id};name=permissionEditPages)} + {include(includes/switch;id=${switch.id};name=permissionViewPages)} + + + Roles + {include(includes/switch;id=${switch.id};name=permissionEditRoles)} + {include(includes/switch;id=${switch.id};name=permissionViewRoles)} + + + SEO-URL + {include(includes/switch;id=${switch.id};name=permissionEditSeoUrl)} + {include(includes/switch;id=${switch.id};name=permissionViewSeoUrl)} + + + Users + {include(includes/switch;id=${switch.id};name=permissionEditUsers)} + {include(includes/switch;id=${switch.id};name=permissionViewUsers)} + + + VENOM-Status + {include(includes/switch;id=${switch.id};name=permissionEditVenomStatus)} + {include(includes/switch;id=${switch.id};name=permissionViewVenomStatus)} + +
diff --git a/tpl/admin/jsTemplates/rolesList.tpl b/tpl/admin/jsTemplates/rolesList.tpl index 4ded38c..b0d7c18 100644 --- a/tpl/admin/jsTemplates/rolesList.tpl +++ b/tpl/admin/jsTemplates/rolesList.tpl @@ -3,18 +3,18 @@

Roles

-
+

Overview

{foreach(roles as role,key)}
- + {include(includes/svg;icon=$role.icon)} ${role.name}
{/for}
-
+

Add new Role

{include(includes/input;label=New Role Name;name=newRoleName;error=New Role Name is required;type=text)} {include(includes/btn;type=primary;content=Add)} diff --git a/tpl/admin/jsTemplates/userEdit.tpl b/tpl/admin/jsTemplates/userEdit.tpl index 9fd2ce0..d4ad447 100644 --- a/tpl/admin/jsTemplates/userEdit.tpl +++ b/tpl/admin/jsTemplates/userEdit.tpl @@ -1,96 +1,73 @@
-

User: engineertrooper

+

User: engineertrooper

-
+
{include(includes/svg;class=back-arrow;icon=vt-arrow-back)} -
-
- EngineerTrooper - -
-
- Dominic Seela - -
-
- kontakt@engineertrooper.com - -
-
- - -
-
- - -
- -

Privileges

- -
-

Module

-
Meta-Data
-
Pages
-
Roles
-
SEO-URL
-
Users
-
VENOM-Status
-
-
-

Edit

- {include(includes/switch;id=${switch.id};name=permissionEditMetaData)} - {include(includes/switch;id=${switch.id};name=permissionEditPages)} - {include(includes/switch;id=${switch.id};name=permissionEditRoles)} - {include(includes/switch;id=${switch.id};name=permissionEditSeoUrl)} - {include(includes/switch;id=${switch.id};name=permissionEditUsers)} - {include(includes/switch;id=${switch.id};name=permissionEditVenomStatus)} -
-
-

View

- {include(includes/switch;id=${switch.id};name=permissionViewMetaData)} - {include(includes/switch;id=${switch.id};name=permissionViewPages)} - {include(includes/switch;id=${switch.id};name=permissionViewRoles)} - {include(includes/switch;id=${switch.id};name=permissionViewSeoUrl)} - {include(includes/switch;id=${switch.id};name=permissionViewUsers)} - {include(includes/switch;id=${switch.id};name=permissionViewVenomStatus)} -
-
-
-
-
- {include(includes/btn;type=valid;content=Save)} - {include(includes/btn;type=primary;content=Reset)} - {include(includes/btn;type=warn;content=Delete Role)}
-
+

User Data

+
+ {include(includes/input;class=input-group;label=Username;name=newUserName;error=New User Name is required;default=EngineerTrooper)} +
+
+ {include(includes/input;class=input-group;label=Author Name;name=newAuthorName;error=New Author Name is required;default=Dominic Seela)} +
+
+ {include(includes/input;class=input-group;label=E-Mail;name=newEMailAddress;error=E-Mail Address is required;default=kontakt@engineertrooper.com)} +
+
+ {include(includes/input;class=input-group;label=Password;name=newPassword;type=password;error=Password is required)} +
+
+ {include(includes/input;class=input-group;label=Password (Repeat);name=newPasswordRepeat;type=password;error=Password (Repeat) is required)} +
+ +

Privileges

+ + + Module + Edit + View + + + Meta-Data + {include(includes/switch;id=${switch.id};name=permissionEditMetaData)} + {include(includes/switch;id=${switch.id};name=permissionViewMetaData)} + + + Pages + {include(includes/switch;id=${switch.id};name=permissionEditPages)} + {include(includes/switch;id=${switch.id};name=permissionViewPages)} + + + Roles + {include(includes/switch;id=${switch.id};name=permissionEditRoles)} + {include(includes/switch;id=${switch.id};name=permissionViewRoles)} + + + SEO-URL + {include(includes/switch;id=${switch.id};name=permissionEditSeoUrl)} + {include(includes/switch;id=${switch.id};name=permissionViewSeoUrl)} + + + Users + {include(includes/switch;id=${switch.id};name=permissionEditUsers)} + {include(includes/switch;id=${switch.id};name=permissionViewUsers)} + + + VENOM-Status + {include(includes/switch;id=${switch.id};name=permissionEditVenomStatus)} + {include(includes/switch;id=${switch.id};name=permissionViewVenomStatus)} + + +
+
+
+ {include(includes/btn;type=valid;content=Save)} + {include(includes/btn;type=primary;content=Reset)} + {include(includes/btn;type=warn;content=Delete User)} +
+
\ No newline at end of file diff --git a/tpl/admin/jsTemplates/usersList.tpl b/tpl/admin/jsTemplates/usersList.tpl index 5a19e04..785aceb 100644 --- a/tpl/admin/jsTemplates/usersList.tpl +++ b/tpl/admin/jsTemplates/usersList.tpl @@ -5,7 +5,7 @@
-
+

Overview

{foreach(users as user,key)}
@@ -16,7 +16,7 @@
{/for}
-
+

Add new User

{include(includes/input;label=New User Name;name=newUserName;error=New User Name is required;type=text)} {include(includes/btn;type=primary;content=Add)} From e29163165f57bb894929eb367b20955117777184 Mon Sep 17 00:00:00 2001 From: engineerTrooper Date: Tue, 8 Dec 2020 16:38:43 +0100 Subject: [PATCH 21/33] VENOM-2: changed code to V-Utils via VTepl (50%) -> result: minified code --- tpl/admin/jsTemplates/pageEdit.tpl | 41 ++++++------------------------ 1 file changed, 8 insertions(+), 33 deletions(-) diff --git a/tpl/admin/jsTemplates/pageEdit.tpl b/tpl/admin/jsTemplates/pageEdit.tpl index 02638fe..5939dcc 100644 --- a/tpl/admin/jsTemplates/pageEdit.tpl +++ b/tpl/admin/jsTemplates/pageEdit.tpl @@ -1,23 +1,19 @@
-

Page Edit

+

Page Edit: Turbinen sind geil

{include(includes/svg;class=back-arrow;icon=vt-arrow-back)}
-

Page Information

+
- - +

Page Information

+ {include(includes/input;class=input-group;label=Page Name;name=PageName;error=Page Name is required;default=Turbinen sind geil)}
+ @@ -38,30 +34,9 @@
!
-
-
- -
-
- -
-
- - - -
+ {include(includes/btn;type=valid;content=Save)} + {include(includes/btn;type=primary;content=Reset)} + {include(includes/btn;type=warn;content=Delete Page)}
\ No newline at end of file From 6bac93205c6a07ca11b99de32bdcc8deb1183fbc Mon Sep 17 00:00:00 2001 From: engineerTrooper Date: Tue, 8 Dec 2020 16:40:23 +0100 Subject: [PATCH 22/33] VENOM-2: added v-input parameters --- tpl/admin/jsTemplates/includes/input.tpl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tpl/admin/jsTemplates/includes/input.tpl b/tpl/admin/jsTemplates/includes/input.tpl index 8067657..e61bab0 100644 --- a/tpl/admin/jsTemplates/includes/input.tpl +++ b/tpl/admin/jsTemplates/includes/input.tpl @@ -3,5 +3,6 @@ required name="${name}" type="${type}" - data-error="${error}"> + data-error="${error}" + class="${class}">${default} \ No newline at end of file From 147cd09d13a273b565a3c81ac65dc40dc62911bd Mon Sep 17 00:00:00 2001 From: engineerTrooper Date: Tue, 8 Dec 2020 16:41:23 +0100 Subject: [PATCH 23/33] VENOM-2: deleted class overview --- tpl/admin/jsTemplates/overview.tpl | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/tpl/admin/jsTemplates/overview.tpl b/tpl/admin/jsTemplates/overview.tpl index 18eb8bc..d12220f 100644 --- a/tpl/admin/jsTemplates/overview.tpl +++ b/tpl/admin/jsTemplates/overview.tpl @@ -1,5 +1,3 @@ -
-
-

Overview

-
-
\ No newline at end of file +
+

Overview

+
\ No newline at end of file From 7d540d809aae9412f85bb5cf521b0117c04fa78f Mon Sep 17 00:00:00 2001 From: engineerTrooper Date: Tue, 8 Dec 2020 16:41:59 +0100 Subject: [PATCH 24/33] VENOM-2: added app-container, added nav-button at mobile and tablets screens --- tpl/admin/admin-panel.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tpl/admin/admin-panel.php b/tpl/admin/admin-panel.php index 917fad8..7ea4948 100644 --- a/tpl/admin/admin-panel.php +++ b/tpl/admin/admin-panel.php @@ -10,7 +10,10 @@
Venom-Status
Ausloggen
-
+
+ +
+
From 85549fbd434c79f1cc2924c6b7a9d70a18f2e06e Mon Sep 17 00:00:00 2001 From: versustunez Date: Tue, 8 Dec 2020 18:37:59 +0100 Subject: [PATCH 25/33] VENOM-10: Clean Up Code --- public/theme/admin/css/admin-panel.css | 2 +- public/theme/admin/css/style.css | 2 +- public/theme/admin/js/scripts.js | 6 ++++++ public/theme/admin/js/scripts.min.js | 2 +- tpl/admin/jsTemplates/includes/btn.tpl | 2 +- tpl/admin/jsTemplates/includes/input.tpl | 1 + tpl/admin/jsTemplates/includes/select.tpl | 2 +- tpl/admin/jsTemplates/includes/switch.tpl | 2 +- tpl/admin/jsTemplates/roleEdit.tpl | 22 ++++++++-------------- tpl/admin/jsTemplates/userEdit.tpl | 22 +++++++--------------- 10 files changed, 28 insertions(+), 35 deletions(-) diff --git a/public/theme/admin/css/admin-panel.css b/public/theme/admin/css/admin-panel.css index 4335a94..5043b12 100644 --- a/public/theme/admin/css/admin-panel.css +++ b/public/theme/admin/css/admin-panel.css @@ -1 +1 @@ -main{display:flex;height:100vh;overflow:hidden}main h1{margin-top:30px;margin-bottom:20px}main h2{margin-top:35px;margin-bottom:25px}main h3{margin-top:20px;margin-bottom:15px}main h4{margin-top:15px;margin-bottom:10px}main.nav-open .menu{transform:translateX(0)}main.nav-open .app{transform:translateX(220px)}.app{transform:translateX(0);transition:transform .4s;flex-grow:1;overflow-y:auto;margin:.6rem .8rem;width:100%;max-height:100%;background:rgba(27,27,27,.5);position:relative}.app .nav-toggle{position:absolute;top:1rem;left:1rem}.menu{width:220px;background-color:#1b1b1b;box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23);height:100%;position:fixed;z-index:1;top:0;left:0;overflow-x:hidden;transition:.4s;transform:translateX(-220px);display:flex;flex-direction:column}.menu .logo{text-align:center;font-family:monospace}.menu div[data-link]{padding:.75rem .5rem;position:relative}.menu div[data-link]:after{background-color:#3949ab;content:"";position:absolute;left:0;bottom:0;height:3px;width:100%;transform:scaleX(0);transition:transform .4s;transform-origin:left}.menu div[data-link]:hover:after{transform:scaleX(1)}.menu div[data-link]:last-child{margin-top:auto}.menu div[data-link].active{font-weight:700}.content-area{width:calc(100% - 20px);padding-top:30px;margin:0 auto;display:block}.content-area header{display:block;text-align:center}.content-area header h2{margin:25px 0}.content-area .back-arrow{width:36px;height:36px}.content-area textarea{background:rgba(27,27,27,.5);color:#fff;margin:15px 0 0 0;font-family:sans-serif;font-size:1.1rem;min-width:100%}.content-area .modules div{padding:6px 20px 6px 0}.content-area .add-new,.content-area .overview{width:100%}.content-area .overview div{margin:10px 0}.content-area .overview .icon{display:inline-block}.content-area .add-new{padding-top:25px}@media only screen and (min-width:768px){.content-area{width:calc(100% - 40px)}.content-area .flexbox{display:flex}.content-area .overview{flex-grow:1;width:60%}.content-area .add-new{padding-top:0;flex-grow:1;width:40%}}@media only screen and (min-width:1024px){.content-area{max-width:860px;padding-top:0;margin:0 0 0 20px}}@media only screen and (min-width:1024px){main,main.nav-open{display:flex}main .app,main.nav-open .app{transform:translateX(0)}main .menu,main.nav-open .menu{position:relative;transform:translateX(0)}main .nav-toggle,main.nav-open .nav-toggle{display:none}}.role-edit .privileges .name{font-size:1.15rem}.role-edit .privileges .description{padding-top:2px} \ No newline at end of file +main{display:flex;height:100vh;overflow:hidden}main h1{margin-top:30px;margin-bottom:20px}main h2{margin-top:35px;margin-bottom:25px}main h3{margin-top:20px;margin-bottom:15px}main h4{margin-top:15px;margin-bottom:10px}main.nav-open .menu{transform:translateX(0)}main.nav-open .app{transform:translateX(220px)}.app{transform:translateX(0);transition:transform .4s;flex-grow:1;overflow-y:auto;margin:.6rem .8rem;width:100%;max-height:100%;background:rgba(27,27,27,.5);position:relative}.app .nav-toggle{position:absolute;top:1rem;left:1rem}.menu{width:220px;background-color:#1b1b1b;box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23);height:100%;position:fixed;z-index:1;top:0;left:0;overflow-x:hidden;transition:.4s;transform:translateX(-220px);display:flex;flex-direction:column}.menu .logo{text-align:center;font-family:monospace}.menu div[data-link]{padding:.75rem .5rem;position:relative}.menu div[data-link]:after{background-color:#3949ab;content:"";position:absolute;left:0;bottom:0;height:3px;width:100%;transform:scaleX(0);transition:transform .4s;transform-origin:left}.menu div[data-link]:hover:after{transform:scaleX(1)}.menu div[data-link]:last-child{margin-top:auto}.menu div[data-link].active{font-weight:700}.content-area{width:calc(100% - 20px);padding-top:30px;margin:0 auto;display:block}.content-area header{display:block;text-align:center}.content-area header h2{margin:25px 0}.content-area .back-arrow{width:36px;height:36px}.content-area textarea{background:rgba(27,27,27,.5);color:#fff;margin:15px 0 0 0;font-family:sans-serif;font-size:1.1rem;min-width:100%}.content-area .modules div{padding:6px 20px 6px 0}.content-area .add-new,.content-area .overview{width:100%}.content-area .overview div[data-link]{margin-right:10px;padding:10px;background-color:rgba(0,0,0,.3)}.content-area .overview div[data-link]:hover{background-color:rgba(0,0,0,.5)}.content-area .overview .icon{display:inline-block}.content-area .add-new{padding-top:25px}@media only screen and (min-width:768px){.content-area{width:calc(100% - 40px)}.content-area .flexbox{display:flex}.content-area .overview{flex-grow:1;width:60%}.content-area .add-new{padding-top:0;flex-grow:1;width:40%}}@media only screen and (min-width:1024px){.content-area{max-width:860px;padding-top:0;margin:0 0 0 20px}}@media only screen and (min-width:1024px){main,main.nav-open{display:flex}main .app,main.nav-open .app{transform:translateX(0)}main .menu,main.nav-open .menu{position:relative;transform:translateX(0)}main .nav-toggle,main.nav-open .nav-toggle{display:none}}.role-edit .privileges .name{font-size:1.15rem} \ No newline at end of file diff --git a/public/theme/admin/css/style.css b/public/theme/admin/css/style.css index 2fd37e3..913f3cd 100644 --- a/public/theme/admin/css/style.css +++ b/public/theme/admin/css/style.css @@ -1 +1 @@ -:focus{outline:0}*{box-sizing:border-box}body,html{width:100vw;height:100vh;overflow:hidden;padding:0;margin:0;background:#1f2857;background:linear-gradient(144deg,#1f2954 25%,#000 50%,#5e002c 80%);background-attachment:fixed;color:#fff;font-family:sans-serif;font-size:16px}.hide{display:none!important}.icon{width:1em;height:1em;vertical-align:middle;font-size:1em;shape-rendering:geometricPrecision;transition:transform .5s cubic-bezier(.22,.61,.36,1);stroke-width:5px;text-align:center;display:block;margin-right:.5rem}[data-link]{cursor:pointer}[data-link] *{pointer-events:none}.btn{border:none;background:#3949ab radial-gradient(circle at 0 0,#3949ab 0,rgba(0,0,0,.2) 100%) no-repeat;color:#fff;padding:7px 1.1rem;margin:10px 0;cursor:pointer;position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;border-radius:4px;box-shadow:0 2px 5px 0 rgba(0,0,0,.26);overflow:hidden;display:flex;justify-content:center;align-items:center;transition:.5s}.btn--outline{background:0 0;border:1px solid #3949ab}.btn:focus{box-shadow:0 3px 8px 1px rgba(0,0,0,.4)}.btn--valid{background:#39ab48 radial-gradient(circle at 0 0,#39ab48 0,rgba(0,0,0,.2) 100%) no-repeat}.btn--accent{background:#ff0089 radial-gradient(circle at 0 0,#ff0089 0,rgba(0,0,0,.2) 100%) no-repeat}.btn--warn{background:#f87229 radial-gradient(circle at 0 0,#f87229 0,rgba(0,0,0,.2) 100%) no-repeat}.btn-fab{border-radius:2rem;width:2em;height:2em;padding:5px}.btn-ripple{position:absolute;top:0;left:0;width:100%;height:100%;overflow:hidden;background:0 0}.btn-ripple__effect{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);opacity:1;width:200%;height:0;padding-bottom:200%;border-radius:50%;background:rgba(190,190,190,.3);-webkit-animation:a-ripple .4s ease-in;animation:a-ripple .4s ease-in}.btn-ripple__effect.to-remove{-webkit-animation:remove-ripple .2s linear;animation:remove-ripple .2s linear}.btn--outline .btn-ripple__effect{background:rgba(57,73,171,.5);z-index:-1}.btn__content{z-index:1;font-weight:400;pointer-events:none}@-webkit-keyframes remove-ripple{from{opacity:1}to{opacity:0}}@keyframes remove-ripple{from{opacity:1}to{opacity:0}}@-webkit-keyframes a-ripple{0%{opacity:0;padding-bottom:0;width:0}25%{opacity:1}100%{width:200%;padding-bottom:200%}}@keyframes a-ripple{0%{opacity:0;padding-bottom:0;width:0}25%{opacity:1}100%{width:200%;padding-bottom:200%}}.btn-line{text-align:right}.btn-line button{margin-right:5px;margin-left:5px;display:inline-block}.btn-line button:last-child{margin-right:0}v-input input{background-color:rgba(0,0,0,.4);border:none;border-bottom:2px solid #3949ab;color:#fff;padding:calc(.75rem - 1px)}v-input input:focus,v-input.focus input{border-color:#ff0089}v-input.valid input{border-color:#39ab48}v-input.invalid input{border-color:#cf1b1b}v-input{display:flex;flex-direction:column;margin-bottom:5px;position:relative;padding-top:.7rem}v-input.no-space{padding-top:.1rem}v-input label{font-size:.7rem;position:absolute;top:.7rem;left:5px;height:1.5rem;pointer-events:none;vertical-align:middle;transform:translateY(50%);color:#dcdcdc;transition:all .2s ease-out}v-input input:focus~label,v-input.focus label{top:0;left:0;transform:translateY(0);font-size:.6rem}v-input .error{display:none}v-input.invalid .error{margin-top:2px;display:block;font-size:.7rem;color:#f32f32}v-switch{display:flex;flex-direction:row;align-items:center}v-switch input{position:absolute;-webkit-appearance:none;-moz-appearance:none;appearance:none;opacity:0}v-switch label{display:block;border-radius:10px;width:40px;height:20px;background-color:#e9e9e9;position:relative;cursor:pointer;margin-right:.5rem}v-switch label:after{content:'';background-color:#c51162;position:absolute;top:2px;left:2px;height:16px;width:16px;border-radius:10px;transition:.5s}v-switch input:checked+label:after{transform:translateX(20px);background-color:#007769}.loader{display:flex;justify-content:center;align-items:center;width:100vw;height:100vh;position:absolute;top:0;z-index:10000;background-color:rgba(0,0,0,.4);opacity:1;transition:opacity .3s}.loader.hide{display:block;opacity:0;pointer-events:none}.loader .spinner{-webkit-animation:rotator 1.4s linear infinite;animation:rotator 1.4s linear infinite}.loader .path{stroke-dasharray:187;stroke-dashoffset:0;transform-origin:center;-webkit-animation:dash 1.4s ease-in-out infinite,colors 5.6s ease-in-out infinite;animation:dash 1.4s ease-in-out infinite,colors 5.6s ease-in-out infinite}@-webkit-keyframes rotator{from{transform:rotate(0)}to{transform:rotate(270deg)}}@keyframes rotator{from{transform:rotate(0)}to{transform:rotate(270deg)}}@-webkit-keyframes colors{0%{stroke:#4285f4}25%{stroke:#de3e35}50%{stroke:#f7c223}75%{stroke:#1b9a59}100%{stroke:#4285f4}}@keyframes colors{0%{stroke:#4285f4}25%{stroke:#de3e35}50%{stroke:#f7c223}75%{stroke:#1b9a59}100%{stroke:#4285f4}}@-webkit-keyframes dash{0%{stroke-dashoffset:187}50%{stroke-dashoffset:46.75;transform:rotate(135deg)}100%{stroke-dashoffset:187;transform:rotate(450deg)}}@keyframes dash{0%{stroke-dashoffset:187}50%{stroke-dashoffset:46.75;transform:rotate(135deg)}100%{stroke-dashoffset:187;transform:rotate(450deg)}}v-table{display:block}v-table v-table-body,v-table v-table-footer,v-table v-table-header,v-table v-table-row{display:grid;grid-auto-columns:1fr;grid-auto-flow:row;-moz-column-gap:20px;column-gap:20px}v-table v-table-body{background:rgba(27,27,27,.5);padding:5px}v-table v-table-body v-table-row{grid-auto-flow:column;border-radius:2px;padding-bottom:10px;border-bottom:solid 1px #aaa;margin:5px 0}v-table v-table-body v-table-row:last-child{padding-bottom:0;border-bottom:none}v-table .grid12{grid-template-columns:repeat(12,[col-start] 1fr);-moz-column-gap:5px;column-gap:5px}v-table .grid12 .col-1to4{grid-column:1/3}v-table .grid12 .col-1to6{grid-column:1/6}v-table .grid12 .col-5to6{grid-column:5/6}v-table .grid12 .col-6to11{grid-column:6/11}v-table .grid12 .col-6to9{grid-column:6/9}v-table .grid12 .col-9to10{grid-column:9/10}v-table .grid12 .col-11to13{grid-column:11/13}v-label,v-option,v-options,v-select{display:inline-block;box-sizing:border-box}v-select{display:block;position:relative;margin-bottom:.5rem}v-label{padding:.5rem .5rem;background-color:rgba(27,27,27,.5);color:#fff;cursor:pointer;display:block;border-bottom:solid 3px #3949ab}v-options{position:absolute;display:flex;flex-direction:column;overflow:hidden;max-height:0;top:.5rem;left:.5rem;background-color:#004a3f;z-index:1000;cursor:pointer;color:#fff;box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23)}v-option{border-bottom:solid 1px #aaa;padding:.5rem 1rem .5rem 5px;min-width:240px}v-option:not([disabled]):hover{background-color:rgba(0,0,0,.3)}v-option[selected]{background-color:#3949ab;color:#fff}v-option[disabled]{color:#aaa} \ No newline at end of file +:focus{outline:0}*{box-sizing:border-box}body,html{width:100vw;height:100vh;overflow:hidden;padding:0;margin:0;background:#1f2857;background:linear-gradient(144deg,#1f2954 25%,#000 50%,#5e002c 80%);background-attachment:fixed;color:#fff;font-family:sans-serif;font-size:16px}.hide{display:none!important}.icon{width:1em;height:1em;vertical-align:middle;font-size:1em;shape-rendering:geometricPrecision;transition:transform .5s cubic-bezier(.22,.61,.36,1);stroke-width:5px;text-align:center;margin-right:.5rem}.icon.block{display:block}.spacer{margin-top:20px}[data-link]{cursor:pointer}[data-link] *{pointer-events:none}.btn{border:none;background:#3949ab radial-gradient(circle at 0 0,#3949ab 0,rgba(0,0,0,.2) 100%) no-repeat;color:#fff;padding:7px 1.1rem;margin:10px 0;cursor:pointer;position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;border-radius:4px;box-shadow:0 2px 5px 0 rgba(0,0,0,.26);overflow:hidden;display:flex;justify-content:center;align-items:center;transition:.5s}.btn--outline{background:0 0;border:1px solid #3949ab}.btn:focus{box-shadow:0 3px 8px 1px rgba(0,0,0,.4)}.btn--valid{background:#39ab48 radial-gradient(circle at 0 0,#39ab48 0,rgba(0,0,0,.2) 100%) no-repeat}.btn--accent{background:#ff0089 radial-gradient(circle at 0 0,#ff0089 0,rgba(0,0,0,.2) 100%) no-repeat}.btn--warn{background:#f87229 radial-gradient(circle at 0 0,#f87229 0,rgba(0,0,0,.2) 100%) no-repeat}.btn-fab{border-radius:2rem;width:2em;height:2em;padding:5px}.btn-ripple{position:absolute;top:0;left:0;width:100%;height:100%;overflow:hidden;background:0 0}.btn-ripple__effect{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);opacity:1;width:200%;height:0;padding-bottom:200%;border-radius:50%;background:rgba(190,190,190,.3);-webkit-animation:a-ripple .4s ease-in;animation:a-ripple .4s ease-in}.btn-ripple__effect.to-remove{-webkit-animation:remove-ripple .2s linear;animation:remove-ripple .2s linear}.btn--outline .btn-ripple__effect{background:rgba(57,73,171,.5);z-index:-1}.btn__content{z-index:1;font-weight:400;pointer-events:none}@-webkit-keyframes remove-ripple{from{opacity:1}to{opacity:0}}@keyframes remove-ripple{from{opacity:1}to{opacity:0}}@-webkit-keyframes a-ripple{0%{opacity:0;padding-bottom:0;width:0}25%{opacity:1}100%{width:200%;padding-bottom:200%}}@keyframes a-ripple{0%{opacity:0;padding-bottom:0;width:0}25%{opacity:1}100%{width:200%;padding-bottom:200%}}.btn-line{text-align:right}.btn-line button{margin-right:5px;margin-left:5px;display:inline-block}.btn-line button:last-child{margin-right:0}v-input input{background-color:rgba(0,0,0,.4);border:none;border-bottom:2px solid #3949ab;color:#fff;padding:calc(.75rem - 1px)}v-input input:focus,v-input.focus input{border-color:#ff0089}v-input.valid input{border-color:#39ab48}v-input.invalid input{border-color:#cf1b1b}v-input{display:flex;flex-direction:column;margin-bottom:5px;position:relative;padding-top:.7rem}v-input.no-space{padding-top:.1rem}v-input label{font-size:.7rem;position:absolute;top:.7rem;left:5px;height:1.5rem;pointer-events:none;vertical-align:middle;transform:translateY(50%);color:#dcdcdc;transition:all .2s ease-out}v-input input:focus~label,v-input.focus label{top:0;left:0;transform:translateY(0);font-size:.6rem}v-input .error{display:none}v-input.invalid .error{margin-top:2px;display:block;font-size:.7rem;color:#f32f32}v-switch{display:flex;flex-direction:row;align-items:center}v-switch input{position:absolute;-webkit-appearance:none;-moz-appearance:none;appearance:none;opacity:0}v-switch label{display:block;border-radius:10px;width:40px;height:20px;background-color:#e9e9e9;position:relative;cursor:pointer;margin-right:.5rem}v-switch label:after{content:'';background-color:#c51162;position:absolute;top:2px;left:2px;height:16px;width:16px;border-radius:10px;transition:.5s}v-switch input:checked+label:after{transform:translateX(20px);background-color:#007769}.loader{display:flex;justify-content:center;align-items:center;width:100vw;height:100vh;position:absolute;top:0;z-index:10000;background-color:rgba(0,0,0,.4);opacity:1;transition:opacity .3s}.loader.hide{display:block;opacity:0;pointer-events:none}.loader .spinner{-webkit-animation:rotator 1.4s linear infinite;animation:rotator 1.4s linear infinite}.loader .path{stroke-dasharray:187;stroke-dashoffset:0;transform-origin:center;-webkit-animation:dash 1.4s ease-in-out infinite,colors 5.6s ease-in-out infinite;animation:dash 1.4s ease-in-out infinite,colors 5.6s ease-in-out infinite}@-webkit-keyframes rotator{from{transform:rotate(0)}to{transform:rotate(270deg)}}@keyframes rotator{from{transform:rotate(0)}to{transform:rotate(270deg)}}@-webkit-keyframes colors{0%{stroke:#4285f4}25%{stroke:#de3e35}50%{stroke:#f7c223}75%{stroke:#1b9a59}100%{stroke:#4285f4}}@keyframes colors{0%{stroke:#4285f4}25%{stroke:#de3e35}50%{stroke:#f7c223}75%{stroke:#1b9a59}100%{stroke:#4285f4}}@-webkit-keyframes dash{0%{stroke-dashoffset:187}50%{stroke-dashoffset:46.75;transform:rotate(135deg)}100%{stroke-dashoffset:187;transform:rotate(450deg)}}@keyframes dash{0%{stroke-dashoffset:187}50%{stroke-dashoffset:46.75;transform:rotate(135deg)}100%{stroke-dashoffset:187;transform:rotate(450deg)}}v-table{display:block}v-table v-table-body,v-table v-table-footer,v-table v-table-header,v-table v-table-row{display:grid;grid-auto-columns:1fr;grid-auto-flow:row;-moz-column-gap:20px;column-gap:20px;overflow:hidden}v-table v-table-header{background-color:#3949ab}v-table v-table-header,v-table v-table-row{grid-auto-flow:column;padding:10px;align-items:center}v-table v-table-body{background:rgba(255,255,255,.1);box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23);border-radius:5px}v-table v-table-body v-table-row:nth-child(odd){background-color:rgba(0,0,0,.3)}v-table v-table-body v-table-row:hover{background-color:rgba(0,0,0,.5)}v-label,v-option,v-options,v-select{display:inline-block;box-sizing:border-box}v-select{display:block;position:relative;margin-bottom:.5rem}v-label{padding:.5rem .5rem;background-color:rgba(0,0,0,.5);color:#fff;cursor:pointer;display:block;border-bottom:solid 3px #3949ab;margin-top:.5rem;position:relative}v-label:after{content:'';border:solid #fff;border-width:0 2px 2px 0;width:.5rem;height:.5rem;position:absolute;right:1em;margin-top:.1rem;transform:rotate(45deg);transition:.3s}v-label.open:after{transform:rotate(-135deg);margin-top:.4rem}v-options{position:absolute;display:flex;flex-direction:column;overflow:hidden;max-height:0;top:.5rem;left:.5rem;background-color:#1b1b1b;z-index:1000;cursor:pointer;color:#fff;border-radius:5px;box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23)}v-option{padding:.5rem 1rem .5rem 5px;min-width:240px}v-option:not(:last-child){border-bottom:solid 1px #333}v-option:not([disabled]):hover{background-color:rgba(0,0,0,.3)}v-option[selected]{background-color:#3949ab;color:#fff}v-option[disabled]{color:#aaa} \ No newline at end of file diff --git a/public/theme/admin/js/scripts.js b/public/theme/admin/js/scripts.js index 0bf8cc0..af262ac 100644 --- a/public/theme/admin/js/scripts.js +++ b/public/theme/admin/js/scripts.js @@ -310,6 +310,12 @@ const rippler = new VRipple(); window._openVSelect = this; this.isOpen = true; } + let l = $('v-label', this).classList; + if (this.isOpen) { + l.add('open'); + } else { + l.remove('open'); + } } } diff --git a/public/theme/admin/js/scripts.min.js b/public/theme/admin/js/scripts.min.js index f730976..9a43a4a 100644 --- a/public/theme/admin/js/scripts.min.js +++ b/public/theme/admin/js/scripts.min.js @@ -1 +1 @@ -class VUtils{static makePublic(){VUtils.isUsed||(this.initHandlers(),VUtils.isUsed=!0,console.log("[VUtils] is now available in the Global Space! no VUtils. anymore needed"))}static initHandlers(){window.$=this.$,window.$$=this.$$,window.tryCatch=this.tryCatch,VUtils.nodePrototypes()}static $(e,t){return(t=t||document).querySelector(e)}static $$(e,t){return(t=t||document).querySelectorAll(e)}static tryCatch(e,t,i){e=VUtils.wrap(e,[]),i=i||console.error,t=t||console.log;try{t(...e)}catch(e){i(e)}}static forEach(e,t,i){for(let s=0;s{let n=e.target;if(e.detail instanceof HTMLElement&&(n=e.detail),n instanceof HTMLElement)if(n.matches(t))VUtils.tryCatch([e,n],i,s);else{const r=n.find(t);r&&VUtils.tryCatch([e,r],i,s)}})},Node.prototype.addMultiListener=function(e,t,i={}){let s=e.split(" ");for(let e of s)this.addEventListener(e,t,i)}}}VUtils.makePublic();class VRipple{constructor(e={}){if(!VUtils.isUsed)throw"VRipply is only with Public VUtils usable!";let t=this;if(t.options=JSON.parse('{"classes":["btn-ripple__effect"],"target":"body","selector":".btn-ripple"}'),VUtils.mergeOptions(t.options,e),t.options.selector.indexOf("#")>-1)throw"ID's are not allowed as selector!";this.instanceCheck(),this.ripples=[],requestAnimationFrame(this.initHandler.bind(this))}instanceCheck(){let e=this.options;const t=[e.target,e.selector,e.classes.join(".")].join(" ");VRipple.instances=VRipple.instances||{},VRipple.instances[t]=this}initHandler(){let e=this,t=e.options.selector;$(e.options.target).addDelegatedEventListener("mousedown touchstart",t,(t,i)=>{let s=t.touches?t.touches[0]:t,n=i.parentNode,r=i.createNew("span",e.options),a=n.getBoundingClientRect(),o=s.clientX-a.left,l=s.clientY-a.top;r.style.top=l+"px",r.style.left=o+"px",r._mouseDown=!0,r._animationEnded=!1,e.ripples.push(r)}),document.body.addDelegatedEventListener("animationend","."+VUtils.get(e.options.classes,""),e.rippleEnd.bind(e)),document.body._vRippleInit||(document.body.addMultiListener("mouseup touchend mouseleave rippleClose",t=>{let i=Object.keys(VRipple.instances);for(let s of i)for(let i of VRipple.instances[s].ripples)e.rippleEnd.bind(VRipple.instances[s])(t,i)}),document.body._vRippleInit=!0)}rippleEnd(e,t){t.parentNode&&("animationend"===e.type?t._animationEnded=!0:t._mouseDown=!1,!t._mouseDown&&t._animationEnded&&(t.classList.contains("to-remove")?(t.parentNode.removeChild(t),this.ripples.splice(this.ripples.indexOf(t),1)):t.classList.add("to-remove")))}}const rippler=new VRipple;!function(){window._openVSelect=null,requestAnimationFrame(e=>{document.body.addEventListener("click",e=>{window._openVSelect&&e.target.closest("v-select")!==window._openVSelect&&window._openVSelect.toggle(!1)})});class e extends HTMLElement{constructor(){super();let e=this;e._in=this.attachInternals(),e._in.role="select",e.setAttribute("tabindex",0),e.update()}static get formAssociated(){return!0}static get observedAttributes(){return["required","validity"]}get required(){return this.hasAttribute("required")}set required(e){this.toggleAttribute("required",Boolean(e))}get name(){return this.getAttribute("name")}set name(e){this.toggleAttribute("name",e)}get form(){return this._in.form}get options(){return $$("v-options v-option",this)}get selected(){return $$("v-options v-option[selected]",this)}update(){let e=[],t=$("v-label",this),i=new FormData;this.selected.forEach(t=>{e.push(t.innerText),i.append(this.name,t.value)}),t.attributeChangedCallback("value","",e.join(", ")),this.required&&0===e.length?this._in.setValidity({customError:!0},"Option is needed"):this._in.setValidity({}),this._in.setFormValue(i)}checkValidity(){return this._in.checkValidity()}reportValidity(){return this._in.reportValidity()}toggle(e){window._openVSelect&&e&&window._openVSelect.toggleSelect(!1);const t=$("v-options",this);if(!e||this.isOpen)t.style.maxHeight="0",window._openVSelect=!1,this.isOpen=!1,this.update();else{t.focus();let e=0,i=t.children;for(let t=0;t{let t=this.parentNode.parentNode,i=!this.selected;if(!t.hasAttribute("multiple")){t.toggle(!1);for(let e of t.selected)e!==this&&e.removeAttribute("selected")}this.disabled||(this.attributeChangedCallback("selected",!1,i,!0),this.parentNode.parentNode.update())})}static get observedAttributes(){return["selected","disabled","value"]}attributeChangedCallback(e,t,i,s){"selected"===e&&this.hasAttribute("disabled")?this.removeAttribute(e):("disabled"===e&&!0===i&&this.hasAttribute("selected")&&this.attributeChangedCallback("selected",!1,!1),s&&(i?this.setAttribute(e,i):this.removeAttribute(e)),this[e]=i)}}class i extends HTMLElement{constructor(){super(),this.empty=this.getAttribute("empty")||"",this.innerHTML=this.getAttribute("value")||this.empty,this.addEventListener("click",this.openPopUp.bind(this))}static get observedAttributes(){return["empty","value"]}openPopUp(){this.parentNode.toggle(!0)}attributeChangedCallback(e,t,i){"value"===e&&(this.innerHTML=i||this.empty),this[e]=i}}customElements.define("v-label",i),customElements.define("v-option",t),customElements.define("v-select",e)}();class FormHandler{constructor(e,t,i,s){this.cb=i||console.log,this.err=s||console.err,$(t).addDelegatedEventListener("submit",e,this.handleEvent.bind(this))}handleEvent(e,t){if(e.preventDefault(),t.checkValidity()){if(""===(t.action??""))return void console.error("No URL Found on Form",t);fetch(t.action,{method:t.method.toUpperCase(),credentials:"same-origin",body:new FormData(t),redirect:"manual"}).then(e=>{if(!e.ok)throw new Error("Network response errored");return e.json()}).then(e=>this.cb(e,t)).catch(e=>this.err(e,t))}else VUtils.forEach($$("input",t),e=>{if(!e.checkValidity()){let t=e.parentNode;t.classList.remove("valid"),t.classList.add("invalid")}})}}!function(){class e extends HTMLElement{constructor(){super();let e=this;e.id=e.id||VUtils.tempId();let t=e.innerHTML;e.innerHTML="";let i=e.input=e.createNew("input",{id:e.id}),s=e.createNew("label",{content:e.dataset.label});e.createNew("span",{classes:"error",content:e.dataset.error}),s.setAttribute("for",e.id),i.type=e.getAttribute("type")||"text",i.value=t.trim(),i.required=e.hasAttribute("required"),i.name=e.getAttribute("name"),i.addMultiListener("change input",e.cb.bind(e))}connectedCallback(){this.cb({currentTarget:this.input},!0)}cb(e,t){let i=e.currentTarget,s=$(".error-message",i.find("form"));s&&s.classList.add("hide");let n=this.classList;""===i.value?n.remove("focus"):n.add("focus"),i.checkValidity()?(n.add("valid"),n.remove("invalid")):t||(n.remove("valid"),n.add("invalid"))}}class t extends HTMLElement{constructor(){super();const e=this.dataset.id||VUtils.tempId();$("input",this).id=e,$("label",this).setAttribute("for",e)}}customElements.define("v-input",e),customElements.define("v-switch",t),$("#login")&&new FormHandler("form#login","body",()=>{location.reload()},(e,t)=>{$(".error-message",t).classList.remove("hide")})}(),(()=>{class e{constructor(e){let t=this;t.editor=e instanceof HTMLElement?e:$(e),t.todoOnKey={},t.keys=[],t.backup=[],t.taberr=[">"," ","\n","<"],t.name="veditor-"+t.editor.id,t.init(),t.selfClosing=["img","area","base","br","col","embed","hr","img","input","link","menuitem","meta","param","source","track"],t.restore()}init(){let e=this;e.editor.addEventListener("keydown",e.handleKey.bind(e)),e.addKey("Tab",e.pressTab.bind(e)),e.addKey("<",e.addEmptyTags.bind(e)),e.addKey("ctrl-z",e.undo.bind(e)),e.addKey("ctrl-s",e.store.bind(e)),e.addKey("ctrl-shift-S",e.delete.bind(e)),e.editor.classList.add(e.name,"veditor")}registerSelfClosing(e){this.selfClosing.push(e)}restore(){let e=localStorage.getItem(this.name);e&&(this.editor.value=e)}delete(){localStorage.removeItem(this.name),console.log(`[VEdit] Editor: ${this.name} removed`)}store(){localStorage.setItem(this.name,this.editor.value),console.log(`[VEdit] Editor: ${this.name} saved`)}handleKey(e){let t,i=this;if(e.ctrlKey&&"Control"===e.key||e.shiftKey&&"Shift"===e.key)return;if(e.ctrlKey&&e.shiftKey?t="ctrl-shift-"+e.key:e.ctrlKey&&(t="ctrl-"+e.key),t&&-1!==this.keys.indexOf(t))return e.preventDefault(),void this.todoOnKey[t]();let s=i.editor.value;const n=i.editor.selectionStart;if(i.backup.length>50&&i.backup.shift(),i.backup.push([s,n]),i.keys.indexOf(e.key)>-1){e.preventDefault();let t=i.todoOnKey[e.key](n,s,this.editor);t[0].push(s.substr(n)),i.afterWork(t)}}undo(){let e=this.backup.pop()||[this.editor.value,this.editor.selectionStart];this.editor.value=e[0],this.editor.setSelectionRange(e[1],e[1])}afterWork(e){this.setText(e[0].join("")),this.editor.setSelectionRange(e[1],e[1])}setText(e){this.editor.value=e}addKey(e,t){this.todoOnKey[e]=t,this.keys.push(e)}addEmptyTags(e,t,i){return[[t.substr(0,e),"<>"],e+1]}pressTab(e,t,i){let s,n,r=this,a=e;if(0===e||-1!==r.taberr.indexOf(t[e-1]))s=" ",a+=4,n=t.substr(0,e);else if(-1===r.taberr.indexOf(t[e-1])){let i=2;for(;-1===r.taberr.indexOf(t[e-i])&&e-i>0;)i++;e-i>0&&(i-=1);let o=r.generateTag(t.substr(e-i,i).trim());s=o[0],a=e-i+o[1],n=t.substr(0,e-i)}return[[n,s],a]}generateTag(e){let t,i={".":[],"#":[]},s=Object.keys(i),n="cl",r=e.split(/([#.])/g);t=r.shift();for(let e of r)s.indexOf(e)>-1?n=e:i[n].push(e);let a="";i["."].length>0&&(a+=` class="${i["."].join(" ")}"`),i["#"].length>0&&(a+=` id="${i["#"].join("-")}"`);let o="";-1===this.selfClosing.indexOf(t.trim())&&(o=``);let l=`<${t}${a}>`;return[`${l}${o}`,l.length]}}class t extends HTMLElement{constructor(){super(),this.editor=document.createElement("textarea"),this.editor.innerHTML=this.innerHTML,this.editor.id=this.getAttribute("name");for(let e of this.attributes)this.editor.setAttribute(e.name,e.value);this.innerHTML="",this.appendChild(this.editor),this.edit=new e(this.editor)}connectedCallback(){this.edit.restore()}disconnectedCallback(){this.edit.save()}}customElements.define("v-editor",t)})(),(()=>{const e=$("main");function t(){return window.matchMedia("(max-width: 1023px)").matches}window.isMobileDevice=t,$("body").addDelegatedEventListener("click",".nav-toggle",(i,s)=>{t()&&e.classList.toggle("nav-open")})})(),window.router=new class{constructor(e){this.options=e,document.body.addDelegatedEventListener("click","[data-link]",(e,t)=>{e.preventDefault(),$$("[data-link].active").forEach(e=>e.classList.remove("active"));let i=$(".loader").classList;i.remove("hide"),this.handleRouting(t.dataset).then(e=>{i.add("hide"),t.classList.add("active")})}),document.body.addEventListener("triggerRouter",e=>{let t=sessionStorage.getItem("url")||JSON.stringify({data:{link:$("[data-link].active").dataset.link}});this.handle(t)}),window.addEventListener("popstate",e=>{this.handle(e.state)}),this.components={},window.dispatchEvent(new CustomEvent("routerReady")),window.routerIsReady=!0}handle(e){e&&(e=JSON.parse(e),this.handleRouting(e.data).then(t=>{let i=$('[data-link="'+e.data.link+'"]');i&&i.classList.add("active")}))}async handleRouting(e){try{let t=e.link,i=this.components[t];if(""===t)return null;i&&(t=i.getUrl(e));let s=await this.handleRequest(t,!0);return s.reload?location.reload():(i=i||this.components[s.component]||null,i?(sessionStorage.setItem("url",JSON.stringify({data:e})),i.handle(s,e).then(t=>{$(this.options.toReplace).innerHTML=t,history.pushState(JSON.stringify({data:e}),document.title)})):await alert("Error"),null)}catch(e){return e}}async handleRequest(e,t){if(""!==(e=e.trim()))return await fetch(e,{credentials:"same-origin"}).then(e=>{if(!e.ok)throw"URL is Status: "+e.status;let i=e.headers.get("Content-Type");return-1!==i.indexOf("json")||t?e.json():-1!==i.indexOf("text")?e.text():e.blob()}).catch(e=>(console.error(e),null))}addComponent(e,t){this.components[e]=t}}({toReplace:".content-area"});class VTpeLCore{constructor(e={}){this.templates={},this.dir=e.path||"/tpl/",this.suffix=e.suffix||"tpl",this.path=e.template||`${this.dir}%s.${this.suffix}`,this.cache=void 0===e.cache||e.cache}async loadTemplate(e){if(this.templates[e])return null;let t=this.path.replace("%s",e),i=await fetch(t,{cache:"force-cache"});if(i.ok){let t=await i.text();this.addTpl(e,t)}return null}async loadArray(e){for(let t of e)await this.loadTemplate(t)}addTpl(e,t){(this.templates[e]=new VTpeLTemplate(e,t,this)).parseContent(this.cache)}async renderOn(e,t){return this.templates[e]?await this.templates[e].render(t):""}}const VParserTypes={content:0,variable:1,for:2,forEach:3,forContent:4,forEnd:5,if:6,ifContent:7,ifEnd:8,assign:9,include:10,none:-1};class VTpeLParser{constructor(e,t){let i=this;i.name=e,i.legex=t.trim(),i.index=0,i.content="",i.parsed=[],i.contexts=[0]}tokenize(){let e=this;for(e.index=0;e.index0&&(this.index+=s),s>0||-1===s}nextContainsRaw(e,t,i){"string"==typeof t&&(t=t.split(""));let s=t.length;if(s<1)return-1;for(let n=0;n ${o}] >> ${s}`),i.index=o+e.length,i.content=s.trim(),void i.addType(t);s+=l}if("\n"===a)return i.index=r.length,i.content=s.trim(),void i.addType(t);throw"Template variable at Position: "+n+" not closed!"}getOperator(e){let t=[];for(let i=0;i{let n=e.target;if(e.detail instanceof HTMLElement&&(n=e.detail),n instanceof HTMLElement)if(n.matches(t))VUtils.tryCatch([e,n],i,s);else{const r=n.find(t);r&&VUtils.tryCatch([e,r],i,s)}})},Node.prototype.addMultiListener=function(e,t,i={}){let s=e.split(" ");for(let e of s)this.addEventListener(e,t,i)}}}VUtils.makePublic();class VRipple{constructor(e={}){if(!VUtils.isUsed)throw"VRipply is only with Public VUtils usable!";let t=this;if(t.options=JSON.parse('{"classes":["btn-ripple__effect"],"target":"body","selector":".btn-ripple"}'),VUtils.mergeOptions(t.options,e),t.options.selector.indexOf("#")>-1)throw"ID's are not allowed as selector!";this.instanceCheck(),this.ripples=[],requestAnimationFrame(this.initHandler.bind(this))}instanceCheck(){let e=this.options;const t=[e.target,e.selector,e.classes.join(".")].join(" ");VRipple.instances=VRipple.instances||{},VRipple.instances[t]=this}initHandler(){let e=this,t=e.options.selector;$(e.options.target).addDelegatedEventListener("mousedown touchstart",t,(t,i)=>{let s=t.touches?t.touches[0]:t,n=i.parentNode,r=i.createNew("span",e.options),a=n.getBoundingClientRect(),o=s.clientX-a.left,l=s.clientY-a.top;r.style.top=l+"px",r.style.left=o+"px",r._mouseDown=!0,r._animationEnded=!1,e.ripples.push(r)}),document.body.addDelegatedEventListener("animationend","."+VUtils.get(e.options.classes,""),e.rippleEnd.bind(e)),document.body._vRippleInit||(document.body.addMultiListener("mouseup touchend mouseleave rippleClose",t=>{let i=Object.keys(VRipple.instances);for(let s of i)for(let i of VRipple.instances[s].ripples)e.rippleEnd.bind(VRipple.instances[s])(t,i)}),document.body._vRippleInit=!0)}rippleEnd(e,t){t.parentNode&&("animationend"===e.type?t._animationEnded=!0:t._mouseDown=!1,!t._mouseDown&&t._animationEnded&&(t.classList.contains("to-remove")?(t.parentNode.removeChild(t),this.ripples.splice(this.ripples.indexOf(t),1)):t.classList.add("to-remove")))}}const rippler=new VRipple;!function(){window._openVSelect=null,requestAnimationFrame(e=>{document.body.addEventListener("click",e=>{window._openVSelect&&e.target.closest("v-select")!==window._openVSelect&&window._openVSelect.toggle(!1)})});class e extends HTMLElement{constructor(){super();let e=this;e._in=this.attachInternals(),e._in.role="select",e.setAttribute("tabindex",0),e.update()}static get formAssociated(){return!0}static get observedAttributes(){return["required","validity"]}get required(){return this.hasAttribute("required")}set required(e){this.toggleAttribute("required",Boolean(e))}get name(){return this.getAttribute("name")}set name(e){this.toggleAttribute("name",e)}get form(){return this._in.form}get options(){return $$("v-options v-option",this)}get selected(){return $$("v-options v-option[selected]",this)}update(){let e=[],t=$("v-label",this),i=new FormData;this.selected.forEach(t=>{e.push(t.innerText),i.append(this.name,t.value)}),t.attributeChangedCallback("value","",e.join(", ")),this.required&&0===e.length?this._in.setValidity({customError:!0},"Option is needed"):this._in.setValidity({}),this._in.setFormValue(i)}checkValidity(){return this._in.checkValidity()}reportValidity(){return this._in.reportValidity()}toggle(e){window._openVSelect&&e&&window._openVSelect.toggleSelect(!1);const t=$("v-options",this);if(!e||this.isOpen)t.style.maxHeight="0",window._openVSelect=!1,this.isOpen=!1,this.update();else{t.focus();let e=0,i=t.children;for(let t=0;t{let t=this.parentNode.parentNode,i=!this.selected;if(!t.hasAttribute("multiple")){t.toggle(!1);for(let e of t.selected)e!==this&&e.removeAttribute("selected")}this.disabled||(this.attributeChangedCallback("selected",!1,i,!0),this.parentNode.parentNode.update())})}static get observedAttributes(){return["selected","disabled","value"]}attributeChangedCallback(e,t,i,s){"selected"===e&&this.hasAttribute("disabled")?this.removeAttribute(e):("disabled"===e&&!0===i&&this.hasAttribute("selected")&&this.attributeChangedCallback("selected",!1,!1),s&&(i?this.setAttribute(e,i):this.removeAttribute(e)),this[e]=i)}}class i extends HTMLElement{constructor(){super(),this.empty=this.getAttribute("empty")||"",this.innerHTML=this.getAttribute("value")||this.empty,this.addEventListener("click",this.openPopUp.bind(this))}static get observedAttributes(){return["empty","value"]}openPopUp(){this.parentNode.toggle(!0)}attributeChangedCallback(e,t,i){"value"===e&&(this.innerHTML=i||this.empty),this[e]=i}}customElements.define("v-label",i),customElements.define("v-option",t),customElements.define("v-select",e)}();class FormHandler{constructor(e,t,i,s){this.cb=i||console.log,this.err=s||console.err,$(t).addDelegatedEventListener("submit",e,this.handleEvent.bind(this))}handleEvent(e,t){if(e.preventDefault(),t.checkValidity()){if(""===(t.action??""))return void console.error("No URL Found on Form",t);fetch(t.action,{method:t.method.toUpperCase(),credentials:"same-origin",body:new FormData(t),redirect:"manual"}).then(e=>{if(!e.ok)throw new Error("Network response errored");return e.json()}).then(e=>this.cb(e,t)).catch(e=>this.err(e,t))}else VUtils.forEach($$("input",t),e=>{if(!e.checkValidity()){let t=e.parentNode;t.classList.remove("valid"),t.classList.add("invalid")}})}}!function(){class e extends HTMLElement{constructor(){super();let e=this;e.id=e.id||VUtils.tempId();let t=e.innerHTML;e.innerHTML="";let i=e.input=e.createNew("input",{id:e.id}),s=e.createNew("label",{content:e.dataset.label});e.createNew("span",{classes:"error",content:e.dataset.error}),s.setAttribute("for",e.id),i.type=e.getAttribute("type")||"text",i.value=t.trim(),i.required=e.hasAttribute("required"),i.name=e.getAttribute("name"),i.addMultiListener("change input",e.cb.bind(e))}connectedCallback(){this.cb({currentTarget:this.input},!0)}cb(e,t){let i=e.currentTarget,s=$(".error-message",i.find("form"));s&&s.classList.add("hide");let n=this.classList;""===i.value?n.remove("focus"):n.add("focus"),i.checkValidity()?(n.add("valid"),n.remove("invalid")):t||(n.remove("valid"),n.add("invalid"))}}class t extends HTMLElement{constructor(){super();const e=this.dataset.id||VUtils.tempId();$("input",this).id=e,$("label",this).setAttribute("for",e)}}customElements.define("v-input",e),customElements.define("v-switch",t),$("#login")&&new FormHandler("form#login","body",()=>{location.reload()},(e,t)=>{$(".error-message",t).classList.remove("hide")})}(),(()=>{class e{constructor(e){let t=this;t.editor=e instanceof HTMLElement?e:$(e),t.todoOnKey={},t.keys=[],t.backup=[],t.taberr=[">"," ","\n","<"],t.name="veditor-"+t.editor.id,t.init(),t.selfClosing=["img","area","base","br","col","embed","hr","img","input","link","menuitem","meta","param","source","track"],t.restore()}init(){let e=this;e.editor.addEventListener("keydown",e.handleKey.bind(e)),e.addKey("Tab",e.pressTab.bind(e)),e.addKey("<",e.addEmptyTags.bind(e)),e.addKey("ctrl-z",e.undo.bind(e)),e.addKey("ctrl-s",e.store.bind(e)),e.addKey("ctrl-shift-S",e.delete.bind(e)),e.editor.classList.add(e.name,"veditor")}registerSelfClosing(e){this.selfClosing.push(e)}restore(){let e=localStorage.getItem(this.name);e&&(this.editor.value=e)}delete(){localStorage.removeItem(this.name),console.log(`[VEdit] Editor: ${this.name} removed`)}store(){localStorage.setItem(this.name,this.editor.value),console.log(`[VEdit] Editor: ${this.name} saved`)}handleKey(e){let t,i=this;if(e.ctrlKey&&"Control"===e.key||e.shiftKey&&"Shift"===e.key)return;if(e.ctrlKey&&e.shiftKey?t="ctrl-shift-"+e.key:e.ctrlKey&&(t="ctrl-"+e.key),t&&-1!==this.keys.indexOf(t))return e.preventDefault(),void this.todoOnKey[t]();let s=i.editor.value;const n=i.editor.selectionStart;if(i.backup.length>50&&i.backup.shift(),i.backup.push([s,n]),i.keys.indexOf(e.key)>-1){e.preventDefault();let t=i.todoOnKey[e.key](n,s,this.editor);t[0].push(s.substr(n)),i.afterWork(t)}}undo(){let e=this.backup.pop()||[this.editor.value,this.editor.selectionStart];this.editor.value=e[0],this.editor.setSelectionRange(e[1],e[1])}afterWork(e){this.setText(e[0].join("")),this.editor.setSelectionRange(e[1],e[1])}setText(e){this.editor.value=e}addKey(e,t){this.todoOnKey[e]=t,this.keys.push(e)}addEmptyTags(e,t,i){return[[t.substr(0,e),"<>"],e+1]}pressTab(e,t,i){let s,n,r=this,a=e;if(0===e||-1!==r.taberr.indexOf(t[e-1]))s=" ",a+=4,n=t.substr(0,e);else if(-1===r.taberr.indexOf(t[e-1])){let i=2;for(;-1===r.taberr.indexOf(t[e-i])&&e-i>0;)i++;e-i>0&&(i-=1);let o=r.generateTag(t.substr(e-i,i).trim());s=o[0],a=e-i+o[1],n=t.substr(0,e-i)}return[[n,s],a]}generateTag(e){let t,i={".":[],"#":[]},s=Object.keys(i),n="cl",r=e.split(/([#.])/g);t=r.shift();for(let e of r)s.indexOf(e)>-1?n=e:i[n].push(e);let a="";i["."].length>0&&(a+=` class="${i["."].join(" ")}"`),i["#"].length>0&&(a+=` id="${i["#"].join("-")}"`);let o="";-1===this.selfClosing.indexOf(t.trim())&&(o=``);let l=`<${t}${a}>`;return[`${l}${o}`,l.length]}}class t extends HTMLElement{constructor(){super(),this.editor=document.createElement("textarea"),this.editor.innerHTML=this.innerHTML,this.editor.id=this.getAttribute("name");for(let e of this.attributes)this.editor.setAttribute(e.name,e.value);this.innerHTML="",this.appendChild(this.editor),this.edit=new e(this.editor)}connectedCallback(){this.edit.restore()}disconnectedCallback(){this.edit.save()}}customElements.define("v-editor",t)})(),(()=>{const e=$("main");function t(){return window.matchMedia("(max-width: 1023px)").matches}window.isMobileDevice=t,$("body").addDelegatedEventListener("click",".nav-toggle",(i,s)=>{t()&&e.classList.toggle("nav-open")})})(),window.router=new class{constructor(e){this.options=e,document.body.addDelegatedEventListener("click","[data-link]",(e,t)=>{e.preventDefault(),$$("[data-link].active").forEach(e=>e.classList.remove("active"));let i=$(".loader").classList;i.remove("hide"),this.handleRouting(t.dataset).then(e=>{i.add("hide"),t.classList.add("active")})}),document.body.addEventListener("triggerRouter",e=>{let t=sessionStorage.getItem("url")||JSON.stringify({data:{link:$("[data-link].active").dataset.link}});this.handle(t)}),window.addEventListener("popstate",e=>{this.handle(e.state)}),this.components={},window.dispatchEvent(new CustomEvent("routerReady")),window.routerIsReady=!0}handle(e){e&&(e=JSON.parse(e),this.handleRouting(e.data).then(t=>{let i=$('[data-link="'+e.data.link+'"]');i&&i.classList.add("active")}))}async handleRouting(e){try{let t=e.link,i=this.components[t];if(""===t)return null;i&&(t=i.getUrl(e));let s=await this.handleRequest(t,!0);return s.reload?location.reload():(i=i||this.components[s.component]||null,i?(sessionStorage.setItem("url",JSON.stringify({data:e})),i.handle(s,e).then(t=>{$(this.options.toReplace).innerHTML=t,history.pushState(JSON.stringify({data:e}),document.title)})):await alert("Error"),null)}catch(e){return e}}async handleRequest(e,t){if(""!==(e=e.trim()))return await fetch(e,{credentials:"same-origin"}).then(e=>{if(!e.ok)throw"URL is Status: "+e.status;let i=e.headers.get("Content-Type");return-1!==i.indexOf("json")||t?e.json():-1!==i.indexOf("text")?e.text():e.blob()}).catch(e=>(console.error(e),null))}addComponent(e,t){this.components[e]=t}}({toReplace:".content-area"});class VTpeLCore{constructor(e={}){this.templates={},this.dir=e.path||"/tpl/",this.suffix=e.suffix||"tpl",this.path=e.template||`${this.dir}%s.${this.suffix}`,this.cache=void 0===e.cache||e.cache}async loadTemplate(e){if(this.templates[e])return null;let t=this.path.replace("%s",e),i=await fetch(t,{cache:"force-cache"});if(i.ok){let t=await i.text();this.addTpl(e,t)}return null}async loadArray(e){for(let t of e)await this.loadTemplate(t)}addTpl(e,t){(this.templates[e]=new VTpeLTemplate(e,t,this)).parseContent(this.cache)}async renderOn(e,t){return this.templates[e]?await this.templates[e].render(t):""}}const VParserTypes={content:0,variable:1,for:2,forEach:3,forContent:4,forEnd:5,if:6,ifContent:7,ifEnd:8,assign:9,include:10,none:-1};class VTpeLParser{constructor(e,t){let i=this;i.name=e,i.legex=t.trim(),i.index=0,i.content="",i.parsed=[],i.contexts=[0]}tokenize(){let e=this;for(e.index=0;e.index0&&(this.index+=s),s>0||-1===s}nextContainsRaw(e,t,i){"string"==typeof t&&(t=t.split(""));let s=t.length;if(s<1)return-1;for(let n=0;n ${o}] >> ${s}`),i.index=o+e.length,i.content=s.trim(),void i.addType(t);s+=l}if("\n"===a)return i.index=r.length,i.content=s.trim(),void i.addType(t);throw"Template variable at Position: "+n+" not closed!"}getOperator(e){let t=[];for(let i=0;i + ${content} \ No newline at end of file diff --git a/tpl/admin/jsTemplates/includes/input.tpl b/tpl/admin/jsTemplates/includes/input.tpl index e61bab0..9ae7ad0 100644 --- a/tpl/admin/jsTemplates/includes/input.tpl +++ b/tpl/admin/jsTemplates/includes/input.tpl @@ -1,4 +1,5 @@ + {foreach(object as item)} diff --git a/tpl/admin/jsTemplates/includes/switch.tpl b/tpl/admin/jsTemplates/includes/switch.tpl index 661bb18..ed625ab 100644 --- a/tpl/admin/jsTemplates/includes/switch.tpl +++ b/tpl/admin/jsTemplates/includes/switch.tpl @@ -1,4 +1,4 @@ - + ${desc} diff --git a/tpl/admin/jsTemplates/roleEdit.tpl b/tpl/admin/jsTemplates/roleEdit.tpl index 70faaa6..7eb73fd 100644 --- a/tpl/admin/jsTemplates/roleEdit.tpl +++ b/tpl/admin/jsTemplates/roleEdit.tpl @@ -2,27 +2,21 @@

Role: ${roles.name}

-
- - {include(includes/svg;class=back-arrow;icon=vt-arrow-back)} - -
-
-

Role Status

- {include(includes/switch;id=${switch.id};name=permissionEditMetaData;desc=If enabled role is active.)} -
-
-

Role Name

- {include(includes/input;class=input-group;label=Change Name;name=roleName;error=Role Name is required;default=Admin)} + + {include(includes/svg;class=back-arrow;icon=vt-arrow-back)} + +
+ {include(includes/switch;id=${switch.id};name=permissionEditMetaData;desc=Active)} + {include(includes/input;class=input-group;label=Name;name=roleName;error=Name is required;default=$roles.name;classes=spacer)}

Privileges

- + Module Edit View - + Meta-Data {include(includes/switch;id=${switch.id};name=permissionEditMetaData)} diff --git a/tpl/admin/jsTemplates/userEdit.tpl b/tpl/admin/jsTemplates/userEdit.tpl index d4ad447..ca7b8e0 100644 --- a/tpl/admin/jsTemplates/userEdit.tpl +++ b/tpl/admin/jsTemplates/userEdit.tpl @@ -8,29 +8,21 @@

User Data

-
+
{include(includes/input;class=input-group;label=Username;name=newUserName;error=New User Name is required;default=EngineerTrooper)} -
-
- {include(includes/input;class=input-group;label=Author Name;name=newAuthorName;error=New Author Name is required;default=Dominic Seela)} -
-
- {include(includes/input;class=input-group;label=E-Mail;name=newEMailAddress;error=E-Mail Address is required;default=kontakt@engineertrooper.com)} -
-
- {include(includes/input;class=input-group;label=Password;name=newPassword;type=password;error=Password is required)} -
-
- {include(includes/input;class=input-group;label=Password (Repeat);name=newPasswordRepeat;type=password;error=Password (Repeat) is required)} + {include(includes/input;class=input-group;label=Author Name;name=newAuthorName;error=New Author Name is required;default=Dominic Seela;classes=spacer)} + {include(includes/input;class=input-group;label=E-Mail;name=newEMailAddress;error=E-Mail Address is required;default=kontakt@engineertrooper.com;classes=spacer)} + {include(includes/input;class=input-group;label=Password;name=newPassword;type=password;error=Password is required;classes=spacer)} + {include(includes/input;class=input-group;label=Password (Repeat);name=newPasswordRepeat;type=password;error=Password (Repeat) is required;classes=spacer)}

Privileges

- + Module Edit View - + Meta-Data {include(includes/switch;id=${switch.id};name=permissionEditMetaData)} From 196ff0b1f15827bb86be5d5a48f2eafdd3b16371 Mon Sep 17 00:00:00 2001 From: engineerTrooper Date: Sun, 13 Dec 2020 01:26:02 +0100 Subject: [PATCH 26/33] VENOM: Fix Router cannot find Route if Trailing Slash is existing (VersusTuneZ) FIX Typos Change Folder WIP --- .../{Routing => Controller}/SeoController.php | 0 src/Venom/Routing/Router.php | 21 +++++++++++++------ src/modules/MetaDataModule.php | 2 +- src/modules/PageModule.php | 6 +++++- src/modules/UserModule.php | 2 +- src/modules/VenomStatusModule.php | 2 +- tpl/admin/jsTemplates/pageEdit.tpl | 9 +------- 7 files changed, 24 insertions(+), 18 deletions(-) rename src/Venom/{Routing => Controller}/SeoController.php (100%) diff --git a/src/Venom/Routing/SeoController.php b/src/Venom/Controller/SeoController.php similarity index 100% rename from src/Venom/Routing/SeoController.php rename to src/Venom/Controller/SeoController.php diff --git a/src/Venom/Routing/Router.php b/src/Venom/Routing/Router.php index ca5a5ce..8632246 100644 --- a/src/Venom/Routing/Router.php +++ b/src/Venom/Routing/Router.php @@ -41,6 +41,7 @@ class Router public function findRoute($url, $method): ?array { $url = $this->removeIfFirst($url, $this->prefix); + $url = $this->removeTrailingSlash($url); // check if full match... this can easily done if the url isset select the empty! $method = strtoupper($method); $route = $this->getRouteByName($url, $method); @@ -64,14 +65,16 @@ class Router return $rawString; } - /* @todo implement Security Check if SecurityModule is used */ private function getRouteByName($url, $method, $subRoute = '*', $params = []): ?array { - $routeAvailable = isset($this->routes[$url]); - $subRouteFound = isset($this->routes[$url]['routes'][$subRoute]); - $methodFound = isset($this->routes[$url]['routes'][$subRoute][$method]); - if ($routeAvailable && $subRouteFound && $methodFound) { - if ((Config::getInstance()->getSecurity()->useSecurity || $this->id === self::ADMIN_ROUTER) && isset($this->routes[$url]['roles'])) { + if (isset($this->routes[$url]) + && isset($this->routes[$url]['routes']) + && isset($this->routes[$url]['routes'][$subRoute]) + && isset($this->routes[$url]['routes'][$subRoute][$method]) + ) { + if ((Config::getInstance()->getSecurity()->useSecurity || $this->id === self::ADMIN_ROUTER) + && isset($this->routes[$url]['roles']) + ) { $roles = $this->routes[$url]['roles']; if (!in_array(User::GUEST_ROLE, $roles, true) && !Security::get()->hasRoles($roles)) { return null; @@ -122,4 +125,10 @@ class Router return false; } } + + private function removeTrailingSlash(string $rawString) + { + $len = strlen($rawString); + return $rawString[$len - 1] === '/' ? substr($rawString, 0, strlen($rawString) - 1) : $rawString; + } } \ No newline at end of file diff --git a/src/modules/MetaDataModule.php b/src/modules/MetaDataModule.php index 5650cc5..47e2859 100644 --- a/src/modules/MetaDataModule.php +++ b/src/modules/MetaDataModule.php @@ -51,7 +51,7 @@ class MetaDataModule implements Module, Route AdminHelper::sendResponse([ 'metaData' => [ ['id' => 1, 'name' => 'engineertrooper', 'icon' => 'vt-edit'], - ['id' => 2, 'name' => 'versustunze', 'icon' => 'vt-edit'] + ['id' => 2, 'name' => 'versustunez', 'icon' => 'vt-edit'] ] ]); } diff --git a/src/modules/PageModule.php b/src/modules/PageModule.php index 4b5771e..f677fa5 100644 --- a/src/modules/PageModule.php +++ b/src/modules/PageModule.php @@ -55,7 +55,7 @@ class PageModule implements Module, Route ['id' => 3, 'name' => 'Aufbau und Umbau des neuen VENOMs Plugins', 'icon' => 'vt-edit'], ['id' => 4, 'name' => 'Aber Mama hat gesagt!', 'icon' => 'vt-edit'], ['id' => 5, 'name' => 'Frische Fische nur heute!', 'icon' => 'vt-edit'] - ] + ], ]); } @@ -76,6 +76,10 @@ class PageModule implements Module, Route 'id' => $id, 'name' => 'Admin', 'icon' => 'vt-visibility', + 'users' => [ + ['value' => 1, 'name' => 'engineertrooper', 'icon' => 'vt-edit'], + ['value' => 2, 'name' => 'versustunez', 'icon' => 'vt-edit'] + ] ]); } } \ No newline at end of file diff --git a/src/modules/UserModule.php b/src/modules/UserModule.php index 1648c35..7daa631 100644 --- a/src/modules/UserModule.php +++ b/src/modules/UserModule.php @@ -51,7 +51,7 @@ class UserModule implements Module, Route AdminHelper::sendResponse([ 'users' => [ ['id' => 1, 'name' => 'engineertrooper', 'icon' => 'vt-edit'], - ['id' => 2, 'name' => 'versustunze', 'icon' => 'vt-edit'] + ['id' => 2, 'name' => 'versustunez', 'icon' => 'vt-edit'] ] ]); } diff --git a/src/modules/VenomStatusModule.php b/src/modules/VenomStatusModule.php index 97fdad7..4ff9c19 100644 --- a/src/modules/VenomStatusModule.php +++ b/src/modules/VenomStatusModule.php @@ -51,7 +51,7 @@ class VenomStatusModule implements Module, Route AdminHelper::sendResponse([ 'users' => [ ['id' => 1, 'name' => 'engineertrooper', 'icon' => 'vt-edit'], - ['id' => 2, 'name' => 'versustunze', 'icon' => 'vt-edit'] + ['id' => 2, 'name' => 'versustunez', 'icon' => 'vt-edit'] ] ]); } diff --git a/tpl/admin/jsTemplates/pageEdit.tpl b/tpl/admin/jsTemplates/pageEdit.tpl index 5939dcc..8d8eed7 100644 --- a/tpl/admin/jsTemplates/pageEdit.tpl +++ b/tpl/admin/jsTemplates/pageEdit.tpl @@ -13,14 +13,7 @@ {include(includes/input;class=input-group;label=Page Name;name=PageName;error=Page Name is required;default=Turbinen sind geil)}
- - - - - engineertrooper (Dominic Seela) - versustunez (Maurice Grönwoldt) - - + {include(includes/select;name=pageVisibility;label=Current Author;object=$users)}
From 430681d84fc60105fe2f907ee90b8ff5834390dc Mon Sep 17 00:00:00 2001 From: engineerTrooper Date: Wed, 16 Dec 2020 16:16:10 +0100 Subject: [PATCH 27/33] VENOM-10: fixed menu symbol to hamburger symbol (visible in tablet and mobile view) --- tpl/admin/admin-panel.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tpl/admin/admin-panel.php b/tpl/admin/admin-panel.php index 7ea4948..3288f7d 100644 --- a/tpl/admin/admin-panel.php +++ b/tpl/admin/admin-panel.php @@ -11,7 +11,7 @@
Ausloggen
- +
From 3c9be5efbab20717163f16da6eedc54916b20e23 Mon Sep 17 00:00:00 2001 From: engineerTrooper Date: Wed, 16 Dec 2020 16:16:54 +0100 Subject: [PATCH 28/33] VENOM: fixed Routing to class SeoController --- src/Venom/Controller/SeoController.php | 2 +- src/Venom/Core/Registry.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Venom/Controller/SeoController.php b/src/Venom/Controller/SeoController.php index 969f94d..307279f 100644 --- a/src/Venom/Controller/SeoController.php +++ b/src/Venom/Controller/SeoController.php @@ -1,7 +1,7 @@ can From 6f93d0d4dde9d678dd5f2d363602a72c1c6a8521 Mon Sep 17 00:00:00 2001 From: engineerTrooper Date: Wed, 16 Dec 2020 16:35:12 +0100 Subject: [PATCH 29/33] VENOM-10: Added Missing Css --- public/theme/admin/css/admin-panel.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/theme/admin/css/admin-panel.css b/public/theme/admin/css/admin-panel.css index 5043b12..063f839 100644 --- a/public/theme/admin/css/admin-panel.css +++ b/public/theme/admin/css/admin-panel.css @@ -1 +1 @@ -main{display:flex;height:100vh;overflow:hidden}main h1{margin-top:30px;margin-bottom:20px}main h2{margin-top:35px;margin-bottom:25px}main h3{margin-top:20px;margin-bottom:15px}main h4{margin-top:15px;margin-bottom:10px}main.nav-open .menu{transform:translateX(0)}main.nav-open .app{transform:translateX(220px)}.app{transform:translateX(0);transition:transform .4s;flex-grow:1;overflow-y:auto;margin:.6rem .8rem;width:100%;max-height:100%;background:rgba(27,27,27,.5);position:relative}.app .nav-toggle{position:absolute;top:1rem;left:1rem}.menu{width:220px;background-color:#1b1b1b;box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23);height:100%;position:fixed;z-index:1;top:0;left:0;overflow-x:hidden;transition:.4s;transform:translateX(-220px);display:flex;flex-direction:column}.menu .logo{text-align:center;font-family:monospace}.menu div[data-link]{padding:.75rem .5rem;position:relative}.menu div[data-link]:after{background-color:#3949ab;content:"";position:absolute;left:0;bottom:0;height:3px;width:100%;transform:scaleX(0);transition:transform .4s;transform-origin:left}.menu div[data-link]:hover:after{transform:scaleX(1)}.menu div[data-link]:last-child{margin-top:auto}.menu div[data-link].active{font-weight:700}.content-area{width:calc(100% - 20px);padding-top:30px;margin:0 auto;display:block}.content-area header{display:block;text-align:center}.content-area header h2{margin:25px 0}.content-area .back-arrow{width:36px;height:36px}.content-area textarea{background:rgba(27,27,27,.5);color:#fff;margin:15px 0 0 0;font-family:sans-serif;font-size:1.1rem;min-width:100%}.content-area .modules div{padding:6px 20px 6px 0}.content-area .add-new,.content-area .overview{width:100%}.content-area .overview div[data-link]{margin-right:10px;padding:10px;background-color:rgba(0,0,0,.3)}.content-area .overview div[data-link]:hover{background-color:rgba(0,0,0,.5)}.content-area .overview .icon{display:inline-block}.content-area .add-new{padding-top:25px}@media only screen and (min-width:768px){.content-area{width:calc(100% - 40px)}.content-area .flexbox{display:flex}.content-area .overview{flex-grow:1;width:60%}.content-area .add-new{padding-top:0;flex-grow:1;width:40%}}@media only screen and (min-width:1024px){.content-area{max-width:860px;padding-top:0;margin:0 0 0 20px}}@media only screen and (min-width:1024px){main,main.nav-open{display:flex}main .app,main.nav-open .app{transform:translateX(0)}main .menu,main.nav-open .menu{position:relative;transform:translateX(0)}main .nav-toggle,main.nav-open .nav-toggle{display:none}}.role-edit .privileges .name{font-size:1.15rem} \ No newline at end of file +main{display:flex;height:100vh;overflow:hidden}main h1{margin-top:30px;margin-bottom:20px}main h2{margin-top:35px;margin-bottom:25px}main h3{margin-top:20px;margin-bottom:15px}main h4{margin-top:15px;margin-bottom:10px}main.nav-open .menu{transform:translateX(0)}main.nav-open .app{transform:translateX(220px)}main.nav-open .app .nav-toggle span{top:0;transform:rotate(-45deg)}main.nav-open .app .nav-toggle span:before{top:0;transform:rotate(-90deg)}main.nav-open .app .nav-toggle span:after{opacity:0}.app{transform:translateX(0);transition:transform .4s;flex-grow:1;overflow-y:auto;margin:.6rem .8rem;width:100%;max-height:100%;background:rgba(27,27,27,.5);position:relative}.app .nav-toggle{position:absolute;top:1rem;left:1rem;margin-top:10px}.app .nav-toggle span,.app .nav-toggle span:after,.app .nav-toggle span:before{cursor:pointer;border-radius:1px;height:2.5px;width:25px;background:#fff;position:absolute;display:block;content:'';transition:.5s ease-in-out}.app .nav-toggle span:before{top:-8px}.app .nav-toggle span:after{bottom:-8px}.menu{width:220px;background-color:#1b1b1b;box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23);height:100%;position:fixed;z-index:1;top:0;left:0;overflow-x:hidden;transition:.4s;transform:translateX(-220px);display:flex;flex-direction:column}.menu .logo{text-align:center;font-family:monospace}.menu div[data-link]{padding:.75rem .5rem;position:relative}.menu div[data-link]:after{background-color:#3949ab;content:"";position:absolute;left:0;bottom:0;height:3px;width:100%;transform:scaleX(0);transition:transform .4s;transform-origin:left}.menu div[data-link]:hover:after{transform:scaleX(1)}.menu div[data-link]:last-child{margin-top:auto}.menu div[data-link].active{font-weight:700}.content-area{width:calc(100% - 20px);padding-top:30px;margin:0 auto;display:block}.content-area header{display:block;text-align:center}.content-area header h2{margin:25px 0}.content-area .back-arrow{width:36px;height:36px}.content-area textarea{background:rgba(27,27,27,.5);color:#fff;margin:15px 0 0 0;font-family:sans-serif;font-size:1.1rem;min-width:100%}.content-area .modules div{padding:6px 20px 6px 0}.content-area .add-new,.content-area .overview{width:100%}.content-area .overview div[data-link]{margin-right:10px;padding:10px;background-color:rgba(0,0,0,.3)}.content-area .overview div[data-link]:hover{background-color:rgba(0,0,0,.5)}.content-area .overview .icon{display:inline-block}.content-area .add-new{padding-top:25px}@media only screen and (min-width:768px){.content-area{width:calc(100% - 40px)}.content-area .flexbox{display:flex}.content-area .overview{flex-grow:1;width:60%}.content-area .add-new{padding-top:0;flex-grow:1;width:40%}}@media only screen and (min-width:1024px){.content-area{max-width:860px;padding-top:0;margin:0 0 0 20px}}@media only screen and (min-width:1024px){main,main.nav-open{display:flex}main .app,main.nav-open .app{transform:translateX(0)}main .menu,main.nav-open .menu{position:relative;transform:translateX(0)}main .nav-toggle,main.nav-open .nav-toggle{display:none}}.role-edit .privileges .name{font-size:1.15rem} \ No newline at end of file From 7b90160c482b5f7f64650be13c1b04932c007e8a Mon Sep 17 00:00:00 2001 From: engineerTrooper Date: Wed, 16 Dec 2020 17:05:38 +0100 Subject: [PATCH 30/33] VENOM-10: Fix Hamburger --- public/theme/admin/css/admin-panel.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/theme/admin/css/admin-panel.css b/public/theme/admin/css/admin-panel.css index 063f839..5d2f8a8 100644 --- a/public/theme/admin/css/admin-panel.css +++ b/public/theme/admin/css/admin-panel.css @@ -1 +1 @@ -main{display:flex;height:100vh;overflow:hidden}main h1{margin-top:30px;margin-bottom:20px}main h2{margin-top:35px;margin-bottom:25px}main h3{margin-top:20px;margin-bottom:15px}main h4{margin-top:15px;margin-bottom:10px}main.nav-open .menu{transform:translateX(0)}main.nav-open .app{transform:translateX(220px)}main.nav-open .app .nav-toggle span{top:0;transform:rotate(-45deg)}main.nav-open .app .nav-toggle span:before{top:0;transform:rotate(-90deg)}main.nav-open .app .nav-toggle span:after{opacity:0}.app{transform:translateX(0);transition:transform .4s;flex-grow:1;overflow-y:auto;margin:.6rem .8rem;width:100%;max-height:100%;background:rgba(27,27,27,.5);position:relative}.app .nav-toggle{position:absolute;top:1rem;left:1rem;margin-top:10px}.app .nav-toggle span,.app .nav-toggle span:after,.app .nav-toggle span:before{cursor:pointer;border-radius:1px;height:2.5px;width:25px;background:#fff;position:absolute;display:block;content:'';transition:.5s ease-in-out}.app .nav-toggle span:before{top:-8px}.app .nav-toggle span:after{bottom:-8px}.menu{width:220px;background-color:#1b1b1b;box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23);height:100%;position:fixed;z-index:1;top:0;left:0;overflow-x:hidden;transition:.4s;transform:translateX(-220px);display:flex;flex-direction:column}.menu .logo{text-align:center;font-family:monospace}.menu div[data-link]{padding:.75rem .5rem;position:relative}.menu div[data-link]:after{background-color:#3949ab;content:"";position:absolute;left:0;bottom:0;height:3px;width:100%;transform:scaleX(0);transition:transform .4s;transform-origin:left}.menu div[data-link]:hover:after{transform:scaleX(1)}.menu div[data-link]:last-child{margin-top:auto}.menu div[data-link].active{font-weight:700}.content-area{width:calc(100% - 20px);padding-top:30px;margin:0 auto;display:block}.content-area header{display:block;text-align:center}.content-area header h2{margin:25px 0}.content-area .back-arrow{width:36px;height:36px}.content-area textarea{background:rgba(27,27,27,.5);color:#fff;margin:15px 0 0 0;font-family:sans-serif;font-size:1.1rem;min-width:100%}.content-area .modules div{padding:6px 20px 6px 0}.content-area .add-new,.content-area .overview{width:100%}.content-area .overview div[data-link]{margin-right:10px;padding:10px;background-color:rgba(0,0,0,.3)}.content-area .overview div[data-link]:hover{background-color:rgba(0,0,0,.5)}.content-area .overview .icon{display:inline-block}.content-area .add-new{padding-top:25px}@media only screen and (min-width:768px){.content-area{width:calc(100% - 40px)}.content-area .flexbox{display:flex}.content-area .overview{flex-grow:1;width:60%}.content-area .add-new{padding-top:0;flex-grow:1;width:40%}}@media only screen and (min-width:1024px){.content-area{max-width:860px;padding-top:0;margin:0 0 0 20px}}@media only screen and (min-width:1024px){main,main.nav-open{display:flex}main .app,main.nav-open .app{transform:translateX(0)}main .menu,main.nav-open .menu{position:relative;transform:translateX(0)}main .nav-toggle,main.nav-open .nav-toggle{display:none}}.role-edit .privileges .name{font-size:1.15rem} \ No newline at end of file +main{display:flex;height:100vh;overflow:hidden}main h1{margin-top:30px;margin-bottom:20px}main h2{margin-top:35px;margin-bottom:25px}main h3{margin-top:20px;margin-bottom:15px}main h4{margin-top:15px;margin-bottom:10px}main.nav-open .menu{transform:translateX(0)}main.nav-open .app{transform:translateX(220px)}main.nav-open .app .nav-toggle span{transition:width .3s;width:0}main.nav-open .app .nav-toggle span:before{transform:translateY(8px) rotate(-135deg)}main.nav-open .app .nav-toggle span:after{transform:translateY(-8px) rotate(135deg)}.app{transform:translateX(0);transition:transform .4s;flex-grow:1;overflow-y:auto;margin:.6rem .8rem;width:100%;max-height:100%;background:rgba(27,27,27,.5);position:relative}.app .nav-toggle{position:absolute;top:1rem;left:1rem;margin-top:10px;height:25px}.app .nav-toggle span,.app .nav-toggle span:after,.app .nav-toggle span:before{cursor:pointer;border-radius:1px;height:2px;width:25px;background:#fff;position:absolute;content:'';transition:transform .5s,width .4s ease-in}.app .nav-toggle span:before{top:-8px}.app .nav-toggle span:after{bottom:-8px}.menu{width:220px;background-color:#1b1b1b;box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23);height:100%;position:fixed;z-index:1;top:0;left:0;overflow-x:hidden;transition:.4s;transform:translateX(-220px);display:flex;flex-direction:column}.menu .logo{text-align:center;font-family:monospace}.menu div[data-link]{padding:.75rem .5rem;position:relative}.menu div[data-link]:after{background-color:#3949ab;content:"";position:absolute;left:0;bottom:0;height:3px;width:100%;transform:scaleX(0);transition:transform .4s;transform-origin:left}.menu div[data-link]:hover:after{transform:scaleX(1)}.menu div[data-link]:last-child{margin-top:auto}.menu div[data-link].active{font-weight:700}.content-area{width:calc(100% - 20px);padding-top:30px;margin:0 auto;display:block}.content-area header{display:block;text-align:center}.content-area header h2{margin:25px 0}.content-area .back-arrow{width:36px;height:36px}.content-area textarea{background:rgba(27,27,27,.5);color:#fff;margin:15px 0 0 0;font-family:sans-serif;font-size:1.1rem;min-width:100%}.content-area .modules div{padding:6px 20px 6px 0}.content-area .add-new,.content-area .overview{width:100%}.content-area .overview div[data-link]{margin-right:10px;padding:10px;background-color:rgba(0,0,0,.3)}.content-area .overview div[data-link]:hover{background-color:rgba(0,0,0,.5)}.content-area .overview .icon{display:inline-block}.content-area .add-new{padding-top:25px}@media only screen and (min-width:768px){.content-area{width:calc(100% - 40px)}.content-area .flexbox{display:flex}.content-area .overview{flex-grow:1;width:60%}.content-area .add-new{padding-top:0;flex-grow:1;width:40%}}@media only screen and (min-width:1024px){.content-area{max-width:860px;padding-top:0;margin:0 0 0 20px}}@media only screen and (min-width:1024px){main,main.nav-open{display:flex}main .app,main.nav-open .app{transform:translateX(0)}main .menu,main.nav-open .menu{position:relative;transform:translateX(0)}main .nav-toggle,main.nav-open .nav-toggle{display:none}}.role-edit .privileges .name{font-size:1.15rem} \ No newline at end of file From 32a78ed1b9fb33c7a43620df5ab3e55a20a1bfdb Mon Sep 17 00:00:00 2001 From: engineerTrooper Date: Tue, 22 Dec 2020 18:07:13 +0100 Subject: [PATCH 31/33] VENOM: Hardcode WIP (versustunez) --- install/db.sql | 28 ++++--- public/theme/admin/css/admin-panel.css | 2 +- src/Venom/Admin/AdminModulesLoader.php | 20 +++-- src/Venom/Core/DatabaseHandler.php | 18 +++++ src/Venom/Core/Module.php | 6 ++ src/Venom/Helper/AdminHelper.php | 12 +++ src/Venom/Helper/ErrorHandler.php | 1 + src/Venom/Models/DataModel.php | 16 +--- src/Venom/Models/DatabaseObject.php | 20 ++++- src/Venom/Models/User.php | 8 +- src/Venom/Routing/Router.php | 2 + .../Meta/Controller/MetaAPIController.php | 36 +++++++++ .../Meta/Controller/MetaController.php | 18 +++++ src/modules/{ => Meta}/MetaDataModule.php | 7 +- src/modules/Meta/module.php | 22 +++++ src/modules/OverviewModule.php | 81 ------------------- .../User/Controller/UserAPIController.php | 64 +++++++++++++++ .../User/Controller/UserController.php | 36 +++++++++ src/modules/{ => User}/UserModule.php | 39 ++------- tpl/admin/admin-panel.php | 1 - tpl/admin/jsTemplates/usersList.tpl | 6 +- 21 files changed, 285 insertions(+), 158 deletions(-) create mode 100644 src/modules/Meta/Controller/MetaAPIController.php create mode 100644 src/modules/Meta/Controller/MetaController.php rename src/modules/{ => Meta}/MetaDataModule.php (90%) create mode 100644 src/modules/Meta/module.php delete mode 100644 src/modules/OverviewModule.php create mode 100644 src/modules/User/Controller/UserAPIController.php create mode 100644 src/modules/User/Controller/UserController.php rename src/modules/{ => User}/UserModule.php (52%) diff --git a/install/db.sql b/install/db.sql index 6e0d81d..9c4be2b 100755 --- a/install/db.sql +++ b/install/db.sql @@ -38,13 +38,23 @@ create table if not exists data create table if not exists users ( - id int(255) auto_increment not null unique primary key, - username varchar(255) not null unique, - email varchar(255) not null, - password varchar(255) not null, - token varchar(255) not null, - salt varchar(255) not null, - roles text default 'ROLE_GUEST' not null, - isActive tinyint(1) default 1 null + id int(255) auto_increment not null unique primary key, + username varchar(255) not null unique, + firstname varchar(255) not null, + lastname varchar(255) not null, + email varchar(255) not null, + password varchar(255) not null, + token varchar(255) not null, + salt varchar(255) not null, + roles text default 'ROLE_GUEST' not null, + isActive tinyint(1) default 1 null ) - comment 'User File'; \ No newline at end of file + comment 'User File'; + +create table if not exists roles +( + id int(255) auto_increment not null unique primary key, + name varchar(255) not null unique, + content JSON not null, + isActive tinyint(1) default 1 null +) \ No newline at end of file diff --git a/public/theme/admin/css/admin-panel.css b/public/theme/admin/css/admin-panel.css index 5d2f8a8..041b30e 100644 --- a/public/theme/admin/css/admin-panel.css +++ b/public/theme/admin/css/admin-panel.css @@ -1 +1 @@ -main{display:flex;height:100vh;overflow:hidden}main h1{margin-top:30px;margin-bottom:20px}main h2{margin-top:35px;margin-bottom:25px}main h3{margin-top:20px;margin-bottom:15px}main h4{margin-top:15px;margin-bottom:10px}main.nav-open .menu{transform:translateX(0)}main.nav-open .app{transform:translateX(220px)}main.nav-open .app .nav-toggle span{transition:width .3s;width:0}main.nav-open .app .nav-toggle span:before{transform:translateY(8px) rotate(-135deg)}main.nav-open .app .nav-toggle span:after{transform:translateY(-8px) rotate(135deg)}.app{transform:translateX(0);transition:transform .4s;flex-grow:1;overflow-y:auto;margin:.6rem .8rem;width:100%;max-height:100%;background:rgba(27,27,27,.5);position:relative}.app .nav-toggle{position:absolute;top:1rem;left:1rem;margin-top:10px;height:25px}.app .nav-toggle span,.app .nav-toggle span:after,.app .nav-toggle span:before{cursor:pointer;border-radius:1px;height:2px;width:25px;background:#fff;position:absolute;content:'';transition:transform .5s,width .4s ease-in}.app .nav-toggle span:before{top:-8px}.app .nav-toggle span:after{bottom:-8px}.menu{width:220px;background-color:#1b1b1b;box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23);height:100%;position:fixed;z-index:1;top:0;left:0;overflow-x:hidden;transition:.4s;transform:translateX(-220px);display:flex;flex-direction:column}.menu .logo{text-align:center;font-family:monospace}.menu div[data-link]{padding:.75rem .5rem;position:relative}.menu div[data-link]:after{background-color:#3949ab;content:"";position:absolute;left:0;bottom:0;height:3px;width:100%;transform:scaleX(0);transition:transform .4s;transform-origin:left}.menu div[data-link]:hover:after{transform:scaleX(1)}.menu div[data-link]:last-child{margin-top:auto}.menu div[data-link].active{font-weight:700}.content-area{width:calc(100% - 20px);padding-top:30px;margin:0 auto;display:block}.content-area header{display:block;text-align:center}.content-area header h2{margin:25px 0}.content-area .back-arrow{width:36px;height:36px}.content-area textarea{background:rgba(27,27,27,.5);color:#fff;margin:15px 0 0 0;font-family:sans-serif;font-size:1.1rem;min-width:100%}.content-area .modules div{padding:6px 20px 6px 0}.content-area .add-new,.content-area .overview{width:100%}.content-area .overview div[data-link]{margin-right:10px;padding:10px;background-color:rgba(0,0,0,.3)}.content-area .overview div[data-link]:hover{background-color:rgba(0,0,0,.5)}.content-area .overview .icon{display:inline-block}.content-area .add-new{padding-top:25px}@media only screen and (min-width:768px){.content-area{width:calc(100% - 40px)}.content-area .flexbox{display:flex}.content-area .overview{flex-grow:1;width:60%}.content-area .add-new{padding-top:0;flex-grow:1;width:40%}}@media only screen and (min-width:1024px){.content-area{max-width:860px;padding-top:0;margin:0 0 0 20px}}@media only screen and (min-width:1024px){main,main.nav-open{display:flex}main .app,main.nav-open .app{transform:translateX(0)}main .menu,main.nav-open .menu{position:relative;transform:translateX(0)}main .nav-toggle,main.nav-open .nav-toggle{display:none}}.role-edit .privileges .name{font-size:1.15rem} \ No newline at end of file +main{display:flex;height:100vh;overflow:hidden}main h1{margin-top:30px;margin-bottom:20px}main h2{margin-top:35px;margin-bottom:25px}main h3{margin-top:20px;margin-bottom:15px}main h4{margin-top:15px;margin-bottom:10px}main.nav-open .menu{transform:translateX(0)}main.nav-open .app{transform:translateX(220px)}main.nav-open .app .nav-toggle span{transition:width .3s;width:0}main.nav-open .app .nav-toggle span:before{transform:translateY(8px) rotate(-135deg)}main.nav-open .app .nav-toggle span:after{transform:translateY(-8px) rotate(135deg)}.app{transform:translateX(0);transition:transform .4s;flex-grow:1;overflow-y:auto;margin:.6rem .8rem;width:100%;max-height:100%;background:rgba(27,27,27,.5);position:relative}.app .nav-toggle{position:absolute;cursor:pointer;left:1rem;height:25px;width:25px}.app .nav-toggle span{transform:translateY(21px)}.app .nav-toggle span,.app .nav-toggle span:after,.app .nav-toggle span:before{border-radius:1px;height:2px;width:25px;background:#fff;position:absolute;content:'';transition:transform .5s,width .4s ease-in}.app .nav-toggle span:before{top:-8px}.app .nav-toggle span:after{bottom:-8px}.menu{width:220px;background-color:#1b1b1b;box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23);height:100%;position:fixed;z-index:1;top:0;left:0;overflow-x:hidden;transition:.4s;transform:translateX(-220px);display:flex;flex-direction:column}.menu .logo{text-align:center;font-family:monospace}.menu div[data-link]{padding:.75rem .5rem;position:relative}.menu div[data-link]:after{background-color:#3949ab;content:"";position:absolute;left:0;bottom:0;height:3px;width:100%;transform:scaleX(0);transition:transform .4s;transform-origin:left}.menu div[data-link]:hover:after{transform:scaleX(1)}.menu div[data-link]:last-child{margin-top:auto}.menu div[data-link].active{font-weight:700}.content-area{width:calc(100% - 20px);padding-top:30px;margin:0 auto;display:block}.content-area header{display:block;text-align:center}.content-area header h2{margin:25px 0}.content-area .back-arrow{width:36px;height:36px}.content-area textarea{background:rgba(27,27,27,.5);color:#fff;margin:15px 0 0 0;font-family:sans-serif;font-size:1.1rem;min-width:100%}.content-area .modules div{padding:6px 20px 6px 0}.content-area .add-new,.content-area .overview{width:100%}.content-area .overview div[data-link]{margin-right:10px;padding:10px;background-color:rgba(0,0,0,.3)}.content-area .overview div[data-link]:hover{background-color:rgba(0,0,0,.5)}.content-area .overview .icon{display:inline-block}.content-area .add-new{padding-top:25px}@media only screen and (min-width:768px){.content-area{width:calc(100% - 40px)}.content-area .flexbox{display:flex}.content-area .overview{flex-grow:1;width:60%}.content-area .add-new{padding-top:0;flex-grow:1;width:40%}}@media only screen and (min-width:1024px){.content-area{max-width:860px;padding-top:0;margin:0 0 0 20px}}@media only screen and (min-width:1024px){main,main.nav-open{display:flex}main .app,main.nav-open .app{transform:translateX(0)}main .menu,main.nav-open .menu{position:relative;transform:translateX(0)}main .nav-toggle,main.nav-open .nav-toggle{display:none}}.role-edit .privileges .name{font-size:1.15rem} \ No newline at end of file diff --git a/src/Venom/Admin/AdminModulesLoader.php b/src/Venom/Admin/AdminModulesLoader.php index da4b77f..38e687a 100644 --- a/src/Venom/Admin/AdminModulesLoader.php +++ b/src/Venom/Admin/AdminModulesLoader.php @@ -3,18 +3,24 @@ namespace Venom\Admin; +use Modules\Meta\MetaDataModule; +use Modules\PageModule; +use Modules\RoleModule; +use Modules\SeoUrlModule; +use Modules\User\UserModule; +use Modules\VenomStatusModule; + class AdminModulesLoader { public static function getModules(): array { return [ - 'metaData' => \Modules\MetaDataModule::class, - 'overview' => \Modules\OverviewModule::class, - 'pages' => \Modules\PageModule::class, - 'role' => \Modules\RoleModule::class, - 'seoUrl' => \Modules\SeoUrlModule::class, - 'users' => \Modules\UserModule::class, - 'venomStatus' => \Modules\VenomStatusModule::class, + 'metaData' => MetaDataModule::class, + 'pages' => PageModule::class, + 'role' => RoleModule::class, + 'seoUrl' => SeoUrlModule::class, + 'users' => UserModule::class, + 'venomStatus' => VenomStatusModule::class, ]; } } \ No newline at end of file diff --git a/src/Venom/Core/DatabaseHandler.php b/src/Venom/Core/DatabaseHandler.php index 1e76f2b..cf16f58 100755 --- a/src/Venom/Core/DatabaseHandler.php +++ b/src/Venom/Core/DatabaseHandler.php @@ -71,4 +71,22 @@ class DatabaseHandler $stmt = $this->db->prepare($query); return $stmt->execute($args); } + + // Returns a Select like this: SELECT id, name, ... FROM table || do what you want + public static function createEasySelect(array $fields, string $table): string + { + return "SELECT " . implode(",", $fields) . " FROM " . $table; + } + + public static function getUpdateString(array $data, string $table, string $where): array + { + $string = []; + $save = []; + foreach ($data as $key => $value) { + $k = ":" . strtolower($key); + $string[] = $key . "= " . $k; + $save[$k] = $value; + } + return ["UPDATE " . $table . " SET " . implode(",", $string) . " " . $where, $save]; + } } \ No newline at end of file diff --git a/src/Venom/Core/Module.php b/src/Venom/Core/Module.php index ea44204..821bd0a 100644 --- a/src/Venom/Core/Module.php +++ b/src/Venom/Core/Module.php @@ -8,6 +8,12 @@ use Venom\Venom; interface Module { + const NAME = "name"; + const AUTHOR = "author"; + const SECURE = "secure"; + const ROUTE = "routes"; + const DESC = "description"; + public function register(Venom $venom): bool; public function init(): void; diff --git a/src/Venom/Helper/AdminHelper.php b/src/Venom/Helper/AdminHelper.php index ab019ff..eb42ceb 100644 --- a/src/Venom/Helper/AdminHelper.php +++ b/src/Venom/Helper/AdminHelper.php @@ -22,4 +22,16 @@ class AdminHelper echo json_encode($response); die(); } + + public static function sendStatus(bool $isSuccess, string $message = "") + { + if ($message == "") { + $message = $isSuccess ? "Operation Success" : "Operation failed"; + } + echo json_encode([ + "status" => $isSuccess ? 'success' : 'failed', + "message" => $message + ]); + die(); + } } \ No newline at end of file diff --git a/src/Venom/Helper/ErrorHandler.php b/src/Venom/Helper/ErrorHandler.php index c3d9e68..a1b265f 100755 --- a/src/Venom/Helper/ErrorHandler.php +++ b/src/Venom/Helper/ErrorHandler.php @@ -4,6 +4,7 @@ namespace Venom\Helper; +use http\Exception\RuntimeException; use Venom\Core\ArgumentHandler; class ErrorHandler diff --git a/src/Venom/Models/DataModel.php b/src/Venom/Models/DataModel.php index e5dc2d1..a864bdb 100755 --- a/src/Venom/Models/DataModel.php +++ b/src/Venom/Models/DataModel.php @@ -9,23 +9,15 @@ class DataModel public const TYPE_CONTENT = 'content'; public const TYPE_FORM = 'form'; - public string $id; - public string $raw; - public string $generated; - public string $type; public int $active = 1; public function __construct( - string $id, - string $type = self::TYPE_CONTENT, - string $raw = '', - string $generated = '' + public string $id, + public string $type = self::TYPE_CONTENT, + public string $raw = '', + public string $generated = '' ) { - $this->id = $id; - $this->type = $type; - $this->raw = $raw; - $this->generated = $generated; } public function getId(): string diff --git a/src/Venom/Models/DatabaseObject.php b/src/Venom/Models/DatabaseObject.php index 26a8cd4..9c4ca50 100644 --- a/src/Venom/Models/DatabaseObject.php +++ b/src/Venom/Models/DatabaseObject.php @@ -4,12 +4,14 @@ namespace Venom\Models; +use JsonSerializable; + /** * Database Object to use queries like this $obj->id, $obj->value * also the option to print it in csv format ; as delimiter * @package Venom\Database */ -class DatabaseObject +class DatabaseObject implements JsonSerializable { private array $data = []; @@ -26,19 +28,29 @@ class DatabaseObject $this->data[$name] = $value; } - public function __isset($name) + public function __isset($name): bool { return isset($this->data[$name]); } - public function toString() + public function toString(): string { return implode(';', $this->data); } - public function getHead() + public function getHead(): string { $keys = array_keys($this->data); return implode(';', $keys); } + + public function getData(): array + { + return $this->data; + } + + public function jsonSerialize(): array + { + return $this->data; + } } \ No newline at end of file diff --git a/src/Venom/Models/User.php b/src/Venom/Models/User.php index b18e57f..310692d 100644 --- a/src/Venom/Models/User.php +++ b/src/Venom/Models/User.php @@ -12,9 +12,11 @@ class User public const GUEST_ROLE = 'ROLE_GUEST'; private string $username = 'GUEST'; private string $email = 'GUEST'; - private string $password = '---'; - private string $salt = '---'; - private string $token = '---'; + private string $firstname = ''; + private string $lastname = ''; + private string $password = ''; + private string $salt = ''; + private string $token = ''; private string $id = '-1'; private array $roles = []; private bool $isLoaded = false; diff --git a/src/Venom/Routing/Router.php b/src/Venom/Routing/Router.php index 8632246..698949f 100644 --- a/src/Venom/Routing/Router.php +++ b/src/Venom/Routing/Router.php @@ -6,6 +6,7 @@ namespace Venom\Routing; use Exception; use Venom\Core\Config; +use Venom\Exceptions\ExceptionHandler; use Venom\Models\User; use Venom\Security\Security; @@ -122,6 +123,7 @@ class Router $route->$fnc(...$params); return true; } catch (Exception $ex) { + ExceptionHandler::handleException($ex); return false; } } diff --git a/src/modules/Meta/Controller/MetaAPIController.php b/src/modules/Meta/Controller/MetaAPIController.php new file mode 100644 index 0000000..f5b277a --- /dev/null +++ b/src/modules/Meta/Controller/MetaAPIController.php @@ -0,0 +1,36 @@ +getRouter(Router::ADMIN_ROUTER)->addRoutes([ '/metaData' => [ - 'cl' => MetaDataModule::class, + 'cl' => MetaAPIController::class, 'roles' => ['ROLE_ADMIN'], 'routes' => [ '*' => [ @@ -40,6 +40,7 @@ class MetaDataModule implements Module, Route "GET" => 'getById', "POST" => 'update', "PUT" => 'insert', + "DELETE" => 'delete' ] ] ] diff --git a/src/modules/Meta/module.php b/src/modules/Meta/module.php new file mode 100644 index 0000000..bb7230c --- /dev/null +++ b/src/modules/Meta/module.php @@ -0,0 +1,22 @@ + 'MetaModule', + Module::DESC => 'Meta Data Module for SEO', + Module::AUTHOR => 'VstZ dev', + Module::SECURE => true, + MODULE::ROUTE => [ + '/' + ], + MODULE::TEMPLATES => [ + // Include Templates with shorter names! $render->include("meta_roles") + 'meta_roles' => 'PATH_TO_TEMPLATE' + ], + MODULE::ADMIN_TEMPLATES => [ + // + ] +]; +$venom = $venom ?? die(); +$venom->registerModule($module);*/ \ No newline at end of file diff --git a/src/modules/OverviewModule.php b/src/modules/OverviewModule.php deleted file mode 100644 index d3b9173..0000000 --- a/src/modules/OverviewModule.php +++ /dev/null @@ -1,81 +0,0 @@ -isAdmin()) { - $this->registerAdminRoutes($venom); - } - return true; - } - - public function init(): void - { - } - - private function registerAdminRoutes(Venom $venom) - { - $venom->getRouter(Router::ADMIN_ROUTER)->addRoutes([ - '/overview' => [ - 'cl' => OverviewModule::class, - 'roles' => ['ROLE_ADMIN'], - 'routes' => [ - '*' => [ - "GET" => 'get', - ], - '1' => [ - "GET" => 'getById', - "POST" => 'update', - "PUT" => 'insert', - ] - ] - ] - ]); - } - - public function get() - { - AdminHelper::sendResponse([ - 'pages' => [ - ['id' => 1, 'name' => 'Flamingos going wild!', 'icon' => 'vt-edit'], - ['id' => 2, 'name' => 'Turbinen sind geil.', 'icon' => 'vt-edit'], - ['id' => 3, 'name' => 'Aufbau und Umbau des neuen VENOMs Plugins', 'icon' => 'vt-edit'], - ['id' => 4, 'name' => 'Aber Mama hat gesagt!', 'icon' => 'vt-edit'], - ['id' => 5, 'name' => 'Frische Fische nur heute!', 'icon' => 'vt-edit'] - ] - ]); - } - - public function update(): bool - { - return false; - } - - public function insert(): bool - { - return false; - } - - public function getById($id) - { - AdminHelper::sendResponse([ - 'caseName' => 'ROLE_ADMIN', - 'id' => $id, - 'name' => 'Admin', - 'icon' => 'vt-visibility', - ]); - } -} \ No newline at end of file diff --git a/src/modules/User/Controller/UserAPIController.php b/src/modules/User/Controller/UserAPIController.php new file mode 100644 index 0000000..f64b6b4 --- /dev/null +++ b/src/modules/User/Controller/UserAPIController.php @@ -0,0 +1,64 @@ + 1, 'name' => 'engineertrooper', 'icon' => 'vt-edit'], + $data = UserController::get(["id", "username", "firstname", "lastname", "email", "isActive"]); + AdminHelper::sendResponse(["users" => $data]); + } + + public function getById($id) + { + $d = UserController::getById($id, ["id", "username", "firstname", "lastname", "email", "isActive"]); + AdminHelper::sendResponse($d); + } + + public function update($id) + { + $original = UserController::getById($id); + if ($original == null) { + AdminHelper::sendStatus(false, "User not Found"); + } + $args = ArgumentHandler::get(); + $data = []; + $d = $original->getData(); + foreach ($d as $key => $item) { + if ($args->hasPostItem($key)) { + $val = $args->getPostItem($key); + if ($val != $item) { + $data[$key] = $val; + } + } + } + parse_str(file_get_contents('php://input'), $_PUT); + var_dump(array_keys($_PUT)); + //var_dump($data, $d, $_POST); + // $args->getPostItem("username")//UPDATE users SET lastname='Doe', firstname='' WHERE id=2 + AdminHelper::sendStatus(UserController::update($id, $data)); + } + + public function delete($id) + { + AdminHelper::sendStatus(DatabaseHandler::get()->execute( + "DELETE FROM users WHERE id=:id", + [ + ':id' => $id + ] + )); + } + + public function create($id) + { + // INSERT INTO + AdminHelper::sendStatus(true); + } +} \ No newline at end of file diff --git a/src/modules/User/Controller/UserController.php b/src/modules/User/Controller/UserController.php new file mode 100644 index 0000000..dc7cffb --- /dev/null +++ b/src/modules/User/Controller/UserController.php @@ -0,0 +1,36 @@ +getOne($sel, [ + ':id' => $id + ]); + } + + public static function get(array $fields = ["*"]): array + { + return DatabaseHandler::get()->getAll(DatabaseHandler::createEasySelect($fields, "users")); + } + + public static function update($id, array $values = []): bool + { + if (count($values) === 0) { + return false; + } + return DatabaseHandler::get()->execute(...DatabaseHandler::getUpdateString($values, "users", "WHERE id = :id")); + } +} \ No newline at end of file diff --git a/src/modules/UserModule.php b/src/modules/User/UserModule.php similarity index 52% rename from src/modules/UserModule.php rename to src/modules/User/UserModule.php index 7daa631..48ebafa 100644 --- a/src/modules/UserModule.php +++ b/src/modules/User/UserModule.php @@ -1,9 +1,10 @@ getRouter(Router::ADMIN_ROUTER)->addRoutes([ '/users' => [ - 'cl' => UserModule::class, + 'cl' => UserAPIController::class, 'roles' => ['ROLE_ADMIN'], 'routes' => [ '*' => [ @@ -38,41 +39,11 @@ class UserModule implements Module, Route ], '1' => [ "GET" => 'getById', - "POST" => 'update', - "PUT" => 'insert', + "POST" => 'insert', + "PUT" => 'update', ] ] ] ]); } - - public function get() - { - AdminHelper::sendResponse([ - 'users' => [ - ['id' => 1, 'name' => 'engineertrooper', 'icon' => 'vt-edit'], - ['id' => 2, 'name' => 'versustunez', 'icon' => 'vt-edit'] - ] - ]); - } - - public function update(): bool - { - return false; - } - - public function insert(): bool - { - return false; - } - - public function getById($id) - { - AdminHelper::sendResponse([ - 'caseName' => 'ROLE_ADMIN', - 'id' => $id, - 'name' => 'Admin', - 'icon' => 'vt-visibility', - ]); - } } \ No newline at end of file diff --git a/tpl/admin/admin-panel.php b/tpl/admin/admin-panel.php index 3288f7d..8feb1c2 100644 --- a/tpl/admin/admin-panel.php +++ b/tpl/admin/admin-panel.php @@ -2,7 +2,6 @@
\ No newline at end of file