Merge branch 'master' into engineers-branch
This commit is contained in:
commit
2364c224fc
18 changed files with 263 additions and 41 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -462,7 +462,7 @@ MigrationBackup/
|
||||||
|
|
||||||
# real ignore for venom
|
# real ignore for venom
|
||||||
conf/config.inc.php
|
conf/config.inc.php
|
||||||
conf/module.inc.php
|
conf/modules.inc.php
|
||||||
conf/routers.inc.php
|
conf/routers.inc.php
|
||||||
conf/lang.inc.php
|
conf/lang.inc.php
|
||||||
logs/Exception.log
|
logs/Exception.log
|
1
base/config.base.php
Normal file → Executable file
1
base/config.base.php
Normal file → Executable file
|
@ -42,6 +42,7 @@ $config->setSecurity([
|
||||||
// all themes are in __DIR__/public/theme/
|
// all themes are in __DIR__/public/theme/
|
||||||
$config->setRender([
|
$config->setRender([
|
||||||
'theme' => 'default', //very important! it will search for a folder with this name.
|
'theme' => 'default', //very important! it will search for a folder with this name.
|
||||||
|
'assetDir' => 'default',
|
||||||
'baseFile' => 'base', //this will called after all templates are rendered...
|
'baseFile' => 'base', //this will called after all templates are rendered...
|
||||||
'useCache' => false, //is only on big systems good
|
'useCache' => false, //is only on big systems good
|
||||||
'cacheName' => 'defaultCache', //this is for bigger systems, ignore it
|
'cacheName' => 'defaultCache', //this is for bigger systems, ignore it
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
require_once __DIR__ . '/../lang/de.php';
|
require_once __DIR__ . '/../lang/example.php';
|
||||||
|
|
2
base/module.base.php
Normal file → Executable file
2
base/module.base.php
Normal file → Executable file
|
@ -5,5 +5,5 @@ $modules = [];
|
||||||
|
|
||||||
// register controllers that can handle templates ;) need to have a render function for this
|
// register controllers that can handle templates ;) need to have a render function for this
|
||||||
$controllers = [
|
$controllers = [
|
||||||
'test' => \Modules\TestController::class,
|
'test' => \Controllers\TestController::class,
|
||||||
];
|
];
|
3
composer.json
Normal file → Executable file
3
composer.json
Normal file → Executable file
|
@ -14,7 +14,8 @@
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"Venom\\": "./src/Venom/",
|
"Venom\\": "./src/Venom/",
|
||||||
"Modules\\": "./src/modules/"
|
"Modules\\": "./src/modules/",
|
||||||
|
"Controllers\\": "./src/controllers/"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
11
install/db.sql
Normal file → Executable file
11
install/db.sql
Normal file → Executable file
|
@ -16,3 +16,14 @@ create table if not exists language
|
||||||
isDefault tinyint(1) default 0 null
|
isDefault tinyint(1) default 0 null
|
||||||
)
|
)
|
||||||
comment 'Language File';
|
comment 'Language File';
|
||||||
|
|
||||||
|
create table if not exists data
|
||||||
|
(
|
||||||
|
id int(255) auto_increment not null unique primary key,
|
||||||
|
identity varchar(255) not null unique,
|
||||||
|
isActive tinyint(1) default 1 null,
|
||||||
|
generated longtext not null,
|
||||||
|
raw longtext not null,
|
||||||
|
datatype enum ('content', 'form')
|
||||||
|
)
|
||||||
|
comment 'DataLoader File';
|
||||||
|
|
12
src/Venom/Core/ArgumentHandler.php
Normal file → Executable file
12
src/Venom/Core/ArgumentHandler.php
Normal file → Executable file
|
@ -29,14 +29,16 @@ class ArgumentHandler
|
||||||
|
|
||||||
public function getItem(string $key, $default = null)
|
public function getItem(string $key, $default = null)
|
||||||
{
|
{
|
||||||
if (isset($this->arguments[$key])) {
|
return $this->arguments[$key] ?? $default;
|
||||||
return $this->arguments[$key];
|
|
||||||
}
|
|
||||||
return $default;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setItem(string $key, $item)
|
public function setItem(string $key, $item): void
|
||||||
{
|
{
|
||||||
$this->arguments[$key] = $item;
|
$this->arguments[$key] = $item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function hasItem(string $key): bool
|
||||||
|
{
|
||||||
|
return isset($this->arguments[$key]);
|
||||||
|
}
|
||||||
}
|
}
|
4
src/Venom/Core/DatabaseHandler.php
Normal file → Executable file
4
src/Venom/Core/DatabaseHandler.php
Normal file → Executable file
|
@ -66,9 +66,9 @@ class DatabaseHandler
|
||||||
return $stmt->fetchAll();
|
return $stmt->fetchAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function execute(string $query, array $args): void
|
public function execute(string $query, array $args): bool
|
||||||
{
|
{
|
||||||
$stmt = $this->db->prepare($query);
|
$stmt = $this->db->prepare($query);
|
||||||
$stmt->execute($args);
|
return $stmt->execute($args);
|
||||||
}
|
}
|
||||||
}
|
}
|
22
src/Venom/Helper/ErrorHandler.php
Normal file → Executable file
22
src/Venom/Helper/ErrorHandler.php
Normal file → Executable file
|
@ -8,13 +8,25 @@ use Venom\Core\ArgumentHandler;
|
||||||
|
|
||||||
class ErrorHandler
|
class ErrorHandler
|
||||||
{
|
{
|
||||||
|
public const ERROR_KEY = 'errorHandler';
|
||||||
|
|
||||||
public static function setFatalError(): void
|
public static function setFatalError(): void
|
||||||
{
|
{
|
||||||
self::setError(500);
|
self::setError(500);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function setError(int $errorCode): void
|
||||||
|
{
|
||||||
|
http_response_code($errorCode);
|
||||||
|
$handler = ArgumentHandler::get();
|
||||||
|
if (!$handler->hasItem('cl')) {
|
||||||
|
$handler->setItem('cl', 'error');
|
||||||
|
$handler->setItem('fnc', 'handleError');
|
||||||
|
$handler->setItem('errorCode', $errorCode);
|
||||||
|
$handler->setItem(self::ERROR_KEY, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static function setNotFound(): void
|
public static function setNotFound(): void
|
||||||
{
|
{
|
||||||
self::setError(404);
|
self::setError(404);
|
||||||
|
@ -24,12 +36,4 @@ class ErrorHandler
|
||||||
{
|
{
|
||||||
self::setError(204);
|
self::setError(204);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function setError(int $errorCode): void
|
|
||||||
{
|
|
||||||
http_response_code($errorCode);
|
|
||||||
ArgumentHandler::get()->setItem('cl', 'error');
|
|
||||||
ArgumentHandler::get()->setItem('fnc', 'handleError');
|
|
||||||
ArgumentHandler::get()->setItem('errorCode', $errorCode);
|
|
||||||
}
|
|
||||||
}
|
}
|
90
src/Venom/Models/DataModel.php
Executable file
90
src/Venom/Models/DataModel.php
Executable file
|
@ -0,0 +1,90 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace Venom\Models;
|
||||||
|
|
||||||
|
|
||||||
|
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 = ''
|
||||||
|
)
|
||||||
|
{
|
||||||
|
$this->id = $id;
|
||||||
|
$this->type = $type;
|
||||||
|
$this->raw = $raw;
|
||||||
|
$this->generated = $generated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getId(): string
|
||||||
|
{
|
||||||
|
return $this->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setId(string $id): void
|
||||||
|
{
|
||||||
|
$this->id = $id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRaw(): string
|
||||||
|
{
|
||||||
|
return $this->raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setRaw(string $raw): void
|
||||||
|
{
|
||||||
|
$this->raw = $raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getGenerated(): string
|
||||||
|
{
|
||||||
|
return $this->generated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setGenerated(string $generated): void
|
||||||
|
{
|
||||||
|
$this->generated = $generated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getType(): string
|
||||||
|
{
|
||||||
|
return $this->type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setType(string $type): void
|
||||||
|
{
|
||||||
|
$this->type = $type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function validate(): bool
|
||||||
|
{
|
||||||
|
return $this->type !== '' && $this->id !== '' && $this->generated !== '' && $this->raw !== '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isActive(): bool
|
||||||
|
{
|
||||||
|
return $this->active === 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getActive(): int
|
||||||
|
{
|
||||||
|
return $this->active;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setActive(bool $value): void
|
||||||
|
{
|
||||||
|
$this->active = $value ? 1 : 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,7 +4,6 @@
|
||||||
namespace Venom\Security;
|
namespace Venom\Security;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Security
|
class Security
|
||||||
{
|
{
|
||||||
private static ?Security $instance = null;
|
private static ?Security $instance = null;
|
||||||
|
|
23
src/Venom/Venom.php
Normal file → Executable file
23
src/Venom/Venom.php
Normal file → Executable file
|
@ -32,6 +32,8 @@ class Venom
|
||||||
|
|
||||||
public function run(): void
|
public function run(): void
|
||||||
{
|
{
|
||||||
|
$arguments = ArgumentHandler::get();
|
||||||
|
$arguments->setItem(ErrorHandler::ERROR_KEY, false);
|
||||||
// we need to load the current controller and the current start template.
|
// we need to load the current controller and the current start template.
|
||||||
// after this we can start the renderer
|
// after this we can start the renderer
|
||||||
if (Config::getInstance()->isRouterEnabled()) {
|
if (Config::getInstance()->isRouterEnabled()) {
|
||||||
|
@ -45,7 +47,10 @@ class Venom
|
||||||
}
|
}
|
||||||
$registry = Registry::getInstance();
|
$registry = Registry::getInstance();
|
||||||
$registry->getLang()->initLang();
|
$registry->getLang()->initLang();
|
||||||
|
// if site is errored then dont load via SEO
|
||||||
|
if (!$arguments->getItem(ErrorHandler::ERROR_KEY)) {
|
||||||
$registry->getSeo()->loadSite();
|
$registry->getSeo()->loadSite();
|
||||||
|
}
|
||||||
$this->renderer->init($this->findController());
|
$this->renderer->init($this->findController());
|
||||||
$this->renderer->render();
|
$this->renderer->render();
|
||||||
}
|
}
|
||||||
|
@ -68,7 +73,16 @@ class Venom
|
||||||
{
|
{
|
||||||
$cl = ArgumentHandler::get()->getItem('cl');
|
$cl = ArgumentHandler::get()->getItem('cl');
|
||||||
if ($cl !== null && isset($this->controllers[$cl])) {
|
if ($cl !== null && isset($this->controllers[$cl])) {
|
||||||
return $this->controllers[$cl];
|
return $this->loadController($this->controllers[$cl]);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function loadController($controllerClass): ?RenderController
|
||||||
|
{
|
||||||
|
$controller = new $controllerClass;
|
||||||
|
if ($controller instanceof RenderController && $controller->register()) {
|
||||||
|
return $controller;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -85,12 +99,7 @@ class Venom
|
||||||
|
|
||||||
public function initControllers(array $controllers): void
|
public function initControllers(array $controllers): void
|
||||||
{
|
{
|
||||||
foreach ($controllers as $key => $controllerClass) {
|
$this->controllers = $controllers;
|
||||||
$controller = new $controllerClass;
|
|
||||||
if ($controller instanceof RenderController && $controller->register()) {
|
|
||||||
$this->controllers[$key] = $controller;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addRouter(string $name, Router $router): void
|
public function addRouter(string $name, Router $router): void
|
||||||
|
|
11
src/Venom/Views/Asset.php
Normal file → Executable file
11
src/Venom/Views/Asset.php
Normal file → Executable file
|
@ -43,9 +43,10 @@ class Asset
|
||||||
|
|
||||||
public function getImagePath(string $filepath, bool $useAbsolute = false)
|
public function getImagePath(string $filepath, bool $useAbsolute = false)
|
||||||
{
|
{
|
||||||
$preDir = '/content/';
|
$config = Config::getInstance();
|
||||||
|
$preDir = '/' . $config->getRenderer()->uploadDir;
|
||||||
if ($useAbsolute) {
|
if ($useAbsolute) {
|
||||||
$preDir = Config::getInstance()->getBaseUrl() . $preDir;
|
$preDir = $config->getBaseUrl() . $preDir;
|
||||||
}
|
}
|
||||||
return $preDir . $filepath;
|
return $preDir . $filepath;
|
||||||
}
|
}
|
||||||
|
@ -61,7 +62,7 @@ class Asset
|
||||||
usort($this->jsFiles, function ($a, $b) {
|
usort($this->jsFiles, function ($a, $b) {
|
||||||
return $a['pos'] <=> $b['pos'];
|
return $a['pos'] <=> $b['pos'];
|
||||||
});
|
});
|
||||||
$theme = $this->getPath('/theme/' . Config::getInstance()->getRenderer()->theme . '/js/');
|
$theme = $this->getPath('/theme/' . Config::getInstance()->getRenderer()->assetDir . '/js/');
|
||||||
foreach ($this->jsFiles as $key => $file) {
|
foreach ($this->jsFiles as $key => $file) {
|
||||||
echo '<script src="' . $theme . $file['file'] . '" id="js-' . $key . '"></script>';
|
echo '<script src="' . $theme . $file['file'] . '" id="js-' . $key . '"></script>';
|
||||||
}
|
}
|
||||||
|
@ -72,7 +73,7 @@ class Asset
|
||||||
$preDir = $base;
|
$preDir = $base;
|
||||||
$config = Config::getInstance();
|
$config = Config::getInstance();
|
||||||
$baseUrl = Config::getInstance()->getBaseUrl();
|
$baseUrl = Config::getInstance()->getBaseUrl();
|
||||||
if ($baseUrl != '' && $config->getRenderer()->useStaticUrl) {
|
if ($baseUrl !== '' && $config->getRenderer()->useStaticUrl) {
|
||||||
$preDir = Config::getInstance()->getBaseUrl() . $preDir;
|
$preDir = Config::getInstance()->getBaseUrl() . $preDir;
|
||||||
}
|
}
|
||||||
return $preDir;
|
return $preDir;
|
||||||
|
@ -83,7 +84,7 @@ class Asset
|
||||||
usort($this->cssFiles, function ($a, $b) {
|
usort($this->cssFiles, function ($a, $b) {
|
||||||
return $a['pos'] <=> $b['pos'];
|
return $a['pos'] <=> $b['pos'];
|
||||||
});
|
});
|
||||||
$theme = $this->getPath('/theme/' . Config::getInstance()->getRenderer()->theme . '/css/');
|
$theme = $this->getPath('/theme/' . Config::getInstance()->getRenderer()->assetDir . '/css/');
|
||||||
foreach ($this->cssFiles as $key => $file) {
|
foreach ($this->cssFiles as $key => $file) {
|
||||||
echo '<link rel="stylesheet" href="' . $theme . $file['file'] . '" id="css-' . $key . '">';
|
echo '<link rel="stylesheet" href="' . $theme . $file['file'] . '" id="css-' . $key . '">';
|
||||||
}
|
}
|
||||||
|
|
94
src/Venom/Views/DataLoader.php
Executable file
94
src/Venom/Views/DataLoader.php
Executable file
|
@ -0,0 +1,94 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace Venom\Views;
|
||||||
|
|
||||||
|
use RuntimeException;
|
||||||
|
use Venom\Core\DatabaseHandler;
|
||||||
|
use Venom\Models\DataModel;
|
||||||
|
|
||||||
|
class DataLoader
|
||||||
|
{
|
||||||
|
private static ?DataLoader $instance = null;
|
||||||
|
|
||||||
|
private function __construct()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function get(): DataLoader
|
||||||
|
{
|
||||||
|
if (self::$instance === null) {
|
||||||
|
self::$instance = new DataLoader();
|
||||||
|
}
|
||||||
|
return self::$instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function loadById(string $id): ?DataModel
|
||||||
|
{
|
||||||
|
if ($id === '') {
|
||||||
|
throw new RuntimeException('Try to Load empty ID from Database');
|
||||||
|
}
|
||||||
|
$data = DatabaseHandler::get()->getOne('SELECT identity, raw, generated, datatype FROM data WHERE identity = :id and isActive = 1 LIMIT 1', [
|
||||||
|
':id' => $id
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($data !== null) {
|
||||||
|
$model = new DataModel($data->identity, $data->datatype, $data->raw, $data->generated);
|
||||||
|
$model->setActive(true);
|
||||||
|
return $model;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateData(DataModel $model): bool
|
||||||
|
{
|
||||||
|
if ($model->getId() === '') {
|
||||||
|
return $this->insertData($model);
|
||||||
|
}
|
||||||
|
return DatabaseHandler::get()->execute(
|
||||||
|
"UPDATE data SET identity=:id, isActive=:isActive, generated=:gen, raw=:raw, datatype=:dt WHERE identity=:id",
|
||||||
|
[
|
||||||
|
':id' => $model->getId(),
|
||||||
|
':isActive' => $model->getActive(),
|
||||||
|
':gen' => $model->getGenerated(),
|
||||||
|
':raw' => $model->getRaw(),
|
||||||
|
':dt' => $model->getType()
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function insertData(DataModel $model): bool
|
||||||
|
{
|
||||||
|
|
||||||
|
$this->validateModel($model);
|
||||||
|
return DatabaseHandler::get()->execute(
|
||||||
|
"INSERT INTO data (identity, isActive, generated, raw, datatype) VALUES (:id, :isActive, :gen, :raw, :dt)",
|
||||||
|
[
|
||||||
|
':id' => $model->getId(),
|
||||||
|
':isActive' => $model->getActive(),
|
||||||
|
':gen' => $model->getGenerated(),
|
||||||
|
':raw' => $model->getRaw(),
|
||||||
|
':dt' => $model->getType()
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function validateModel(DataModel $model): void
|
||||||
|
{
|
||||||
|
if ($model->getId() === '') {
|
||||||
|
$model->setId($this->generateID());
|
||||||
|
}
|
||||||
|
if (!$model->validate()) {
|
||||||
|
$id = htmlspecialchars($model->getId());
|
||||||
|
throw new RuntimeException("DataModel with id: \"$id\" is invalid!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generateID(string $id = ''): string
|
||||||
|
{
|
||||||
|
if ($id === '') {
|
||||||
|
$id = bin2hex(random_bytes(16));
|
||||||
|
}
|
||||||
|
return $id;
|
||||||
|
}
|
||||||
|
}
|
14
src/Venom/Views/VenomRenderer.php
Normal file → Executable file
14
src/Venom/Views/VenomRenderer.php
Normal file → Executable file
|
@ -16,7 +16,6 @@ class VenomRenderer
|
||||||
private array $vars = [];
|
private array $vars = [];
|
||||||
private string $baseTemplate = '';
|
private string $baseTemplate = '';
|
||||||
private string $templateDir = '';
|
private string $templateDir = '';
|
||||||
private string $assetsDir = '';
|
|
||||||
|
|
||||||
public function __construct(Venom $venom)
|
public function __construct(Venom $venom)
|
||||||
{
|
{
|
||||||
|
@ -48,6 +47,13 @@ class VenomRenderer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function renderTemplate($template): void
|
||||||
|
{
|
||||||
|
// random variable name... to remove it instantly
|
||||||
|
echo $this->includeTemplate($template, '1408138186');
|
||||||
|
unset($this->vars['1408138186']);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* function will load a template (without extension!) into a variable and return the content
|
* function will load a template (without extension!) into a variable and return the content
|
||||||
* @param $template
|
* @param $template
|
||||||
|
@ -78,12 +84,16 @@ class VenomRenderer
|
||||||
return $this->vars[$name];
|
return $this->vars[$name];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function deleteVar($name)
|
||||||
|
{
|
||||||
|
unset($this->vars[$name]);
|
||||||
|
}
|
||||||
|
|
||||||
public function init(?RenderController $controller): void
|
public function init(?RenderController $controller): void
|
||||||
{
|
{
|
||||||
$this->controller = $controller;
|
$this->controller = $controller;
|
||||||
$data = Config::getInstance()->getRenderer();
|
$data = Config::getInstance()->getRenderer();
|
||||||
$this->baseTemplate = $data->baseFile . '.php' ?? 'base.php';
|
$this->baseTemplate = $data->baseFile . '.php' ?? 'base.php';
|
||||||
$this->templateDir = __DIR__ . '/../../../tpl/' . $data->theme . '/';
|
$this->templateDir = __DIR__ . '/../../../tpl/' . $data->theme . '/';
|
||||||
$this->assetsDir = __DIR__ . '/../../../public/theme/' . $data->theme . '/';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
0
src/controllers/.gitkeep
Executable file
0
src/controllers/.gitkeep
Executable file
2
src/modules/TestController.php → src/controllers/TestController.php
Normal file → Executable file
2
src/modules/TestController.php → src/controllers/TestController.php
Normal file → Executable file
|
@ -1,7 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
|
||||||
namespace Modules;
|
namespace Controllers;
|
||||||
|
|
||||||
|
|
||||||
use Venom\Views\Asset;
|
use Venom\Views\Asset;
|
Loading…
Reference in a new issue