admin themes
This commit is contained in:
22
kernel/AdminController.php
Normal file
22
kernel/AdminController.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace kernel;
|
||||
|
||||
use kernel\Controller;
|
||||
use kernel\services\AdminThemeService;
|
||||
|
||||
class AdminController extends Controller
|
||||
{
|
||||
protected AdminThemeService $adminThemeService;
|
||||
|
||||
protected function init()
|
||||
{
|
||||
$this->adminThemeService = new AdminThemeService();
|
||||
$active_theme = $this->adminThemeService->getActiveAdminThemeInfo();
|
||||
$this->cgView->layoutPath = getConst($active_theme['layout_path']);
|
||||
$this->cgView->layout = "/" . $active_theme['layout'];
|
||||
|
||||
$this->cgView->addVarToLayout("resources", $active_theme['resource']);
|
||||
}
|
||||
|
||||
}
|
@ -38,6 +38,7 @@ class App
|
||||
|
||||
public function setRouting(): void
|
||||
{
|
||||
include KERNEL_DIR . "/routs/admin.php";
|
||||
include ROOT_DIR . "/rout.php";
|
||||
}
|
||||
|
||||
|
@ -2,9 +2,14 @@
|
||||
|
||||
namespace kernel;
|
||||
|
||||
use app\helpers\Debug;
|
||||
|
||||
class CgView
|
||||
{
|
||||
public string $viewPath = '';
|
||||
|
||||
public string $layoutPath = '';
|
||||
public array $varToLayout = [];
|
||||
public bool|string $layout = false;
|
||||
|
||||
public function __construct()
|
||||
@ -26,29 +31,47 @@ class CgView
|
||||
return $content;
|
||||
}
|
||||
|
||||
public function addVarToLayout($key, $value): void
|
||||
{
|
||||
$this->varToLayout[$key] = $value;
|
||||
}
|
||||
|
||||
private function createContent(string $view, array $data = []): false|string
|
||||
{
|
||||
ob_start();
|
||||
|
||||
foreach ($data as $key => $datum){
|
||||
foreach ($data as $key => $datum) {
|
||||
${"$key"} = $datum;
|
||||
}
|
||||
|
||||
include ($this->viewPath . $view);
|
||||
include($this->viewPath . $view);
|
||||
|
||||
$content = ob_get_contents();
|
||||
ob_end_clean ();
|
||||
ob_end_clean();
|
||||
|
||||
ob_start();
|
||||
|
||||
$file_content = $content;
|
||||
|
||||
if ($this->layout){
|
||||
if (file_exists($this->viewPath . $this->layout)){
|
||||
include ($this->viewPath . $this->layout);
|
||||
$layoutPath = $this->viewPath;
|
||||
|
||||
if ($this->layout) {
|
||||
|
||||
if ($this->layoutPath !== '') {
|
||||
|
||||
$layoutPath = $this->layoutPath;
|
||||
}
|
||||
if (file_exists($layoutPath . $this->layout)) {
|
||||
if ($this->varToLayout){
|
||||
foreach ($this->varToLayout as $key => $datum) {
|
||||
${"$key"} = $datum;
|
||||
}
|
||||
}
|
||||
include($layoutPath . $this->layout);
|
||||
$file_content = ob_get_contents();
|
||||
}
|
||||
}
|
||||
ob_end_clean ();
|
||||
|
||||
ob_end_clean();
|
||||
|
||||
return $file_content;
|
||||
}
|
||||
|
225
kernel/Request.php
Normal file
225
kernel/Request.php
Normal file
@ -0,0 +1,225 @@
|
||||
<?php
|
||||
|
||||
namespace kernel;
|
||||
|
||||
use Rakit\Validation\Validator;
|
||||
|
||||
#[\AllowDynamicProperties]
|
||||
class Request
|
||||
{
|
||||
/**
|
||||
* @var string $host Абсолютный адрес сервера
|
||||
*/
|
||||
public string $host;
|
||||
|
||||
/**
|
||||
* @var array $headers Заголовки запроса
|
||||
*/
|
||||
public array $headers;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public array $data = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public array $errors = [];
|
||||
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->headers = $this->getRequestHeaders();
|
||||
$this->load();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function messages(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает абсолютный адрес сервера.
|
||||
* @return string
|
||||
*/
|
||||
public function getHost(): string
|
||||
{
|
||||
if ($this->host !== null) {
|
||||
return $this->host;
|
||||
}
|
||||
|
||||
$http = $this->getIsSecure() ? 'https' : 'http';
|
||||
|
||||
if ($this->headerExist('Host')) {
|
||||
$this->host = $http . '://' . $this->getHeader('Host');
|
||||
} elseif (isset($_SERVER['SERVER_NAME'])) {
|
||||
$this->host = $http . '://' . $_SERVER['SERVER_NAME'];
|
||||
}
|
||||
|
||||
return $this->host;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Возвращает true если шифрование https, иначе false.
|
||||
* @return bool
|
||||
*/
|
||||
public function getIsSecure(): bool
|
||||
{
|
||||
if (isset($_SERVER['HTTPS']) && (strcasecmp($_SERVER['HTTPS'], 'on') === 0 || $_SERVER['HTTPS'] == 1)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Проверяет был ли передан заголовок запроса.
|
||||
* @return bool
|
||||
*/
|
||||
public function headerExist($header): bool
|
||||
{
|
||||
return isset($this->headers[$header]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Возвращает заголовок запроса
|
||||
* @param string $header Заголовок.
|
||||
* @param mixed $defaultValue Значение если, параметр не передан.
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function getHeader($header, $defaultValue = null): mixed
|
||||
{
|
||||
return $this->headers[$header] ?? $defaultValue;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Возвращает GET - параметр.
|
||||
* @param string $param Параметр.
|
||||
* @param mixed $defaultValue Значение если, параметр не передан.
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($param = null, $defaultValue = null): mixed
|
||||
{
|
||||
if (is_null($param)) {
|
||||
return $_GET;
|
||||
}
|
||||
|
||||
return $_GET[$param] ?? $defaultValue;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Возвращает POST - параметр.
|
||||
* @param string $param Параметр.
|
||||
* @param mixed $defaultValue Значение если, параметр не передан.
|
||||
* @return mixed
|
||||
*/
|
||||
public function post($param = null, $defaultValue = null): mixed
|
||||
{
|
||||
if (is_null($param)) {
|
||||
return $_POST;
|
||||
}
|
||||
|
||||
return $_POST[$param] ?? $defaultValue;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Был ли POST - запрос.
|
||||
* @return bool
|
||||
*/
|
||||
public function isPost(): bool
|
||||
{
|
||||
return ($_SERVER['REQUEST_METHOD'] === 'POST');
|
||||
}
|
||||
|
||||
/**
|
||||
* Был ли GET - запрос.
|
||||
* @return bool
|
||||
*/
|
||||
public function isGet(): bool
|
||||
{
|
||||
return ($_SERVER['REQUEST_METHOD'] === 'GET');
|
||||
}
|
||||
|
||||
/**
|
||||
* Загружаем свойсва
|
||||
*/
|
||||
public function load(): void
|
||||
{
|
||||
if (!empty($_REQUEST)) {
|
||||
foreach ($_REQUEST as $key => $item) {
|
||||
$this->{$key} = $item;
|
||||
$this->data[$key] = $item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function validate(): bool
|
||||
{
|
||||
if (!empty($this->data)) {
|
||||
$valid = new Validator();
|
||||
$validation = $valid->make($this->data, $this->rules());
|
||||
$validation->setMessages($this->messages());
|
||||
$validation->validate();
|
||||
if ($validation->fails()) {
|
||||
$this->errors = $validation->errors();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getMessagesArray(): array
|
||||
{
|
||||
$msgs = [];
|
||||
if($this->errors){
|
||||
foreach ($this->errors->toArray() as $item){
|
||||
$msgs[] = array_values($item)[0];
|
||||
}
|
||||
}
|
||||
|
||||
return $msgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function getRequestHeaders(): array
|
||||
{
|
||||
$headers = array();
|
||||
foreach ($_SERVER as $key => $value) {
|
||||
if (substr($key, 0, 5) <> 'HTTP_') {
|
||||
continue;
|
||||
}
|
||||
$header = str_replace(' ', '-', ucwords(str_replace('_', ' ', strtolower(substr($key, 5)))));
|
||||
$headers[$header] = $value;
|
||||
}
|
||||
|
||||
return $headers;
|
||||
}
|
||||
}
|
75
kernel/admin_themes/default/layout/main.php
Normal file
75
kernel/admin_themes/default/layout/main.php
Normal file
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
/**
|
||||
* @var $content
|
||||
* @var string $resources
|
||||
*/
|
||||
?>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Sidebar 01</title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
|
||||
<link href="https://fonts.googleapis.com/css?family=Poppins:300,400,500,600,700,800,900" rel="stylesheet">
|
||||
|
||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||
<link rel="stylesheet" href="<?=$resources?>/css/style.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="wrapper d-flex align-items-stretch">
|
||||
<nav id="sidebar">
|
||||
<div class="p-4 pt-5">
|
||||
<a href="#" class="img logo rounded-circle mb-5" style="background-image: url(/resources/admin_theme/images/logo.jpg);"></a>
|
||||
<?php \kernel\widgets\MenuWidget::create()->run(); ?>
|
||||
<div class="footer">
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- Page Content -->
|
||||
<div id="content" class="p-4 p-md-5">
|
||||
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||
<div class="container-fluid">
|
||||
|
||||
<button type="button" id="sidebarCollapse" class="btn btn-primary">
|
||||
<i class="fa fa-bars"></i>
|
||||
<span class="sr-only">Toggle Menu</span>
|
||||
</button>
|
||||
<button class="btn btn-dark d-inline-block d-lg-none ml-auto" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<i class="fa fa-bars"></i>
|
||||
</button>
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||
<ul class="nav navbar-nav ml-auto">
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" href="#">Home</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#">About</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#">Portfolio</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#">Contact</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<?= $content ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="<?=$resources?>/js/jquery.min.js"></script>
|
||||
<script src="<?=$resources?>/js/popper.js"></script>
|
||||
<script src="<?=$resources?>/js/bootstrap.min.js"></script>
|
||||
<script src="<?=$resources?>/js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
10
kernel/admin_themes/default/manifest.json
Normal file
10
kernel/admin_themes/default/manifest.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "Default",
|
||||
"version": "0.1",
|
||||
"description": "Default admin theme",
|
||||
"preview": "preview.png",
|
||||
"resource": "/resources/default",
|
||||
"resource_path": "{RESOURCES}/default",
|
||||
"layout": "main.php",
|
||||
"layout_path": "{KERNEL_ADMIN_THEMES}/default/layout"
|
||||
}
|
72
kernel/admin_themes/simple/layout/main.php
Normal file
72
kernel/admin_themes/simple/layout/main.php
Normal file
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
/**
|
||||
* @var $content
|
||||
* @var string $resources
|
||||
*/
|
||||
?>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Sidebar 01</title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
|
||||
<link href="https://fonts.googleapis.com/css?family=Poppins:300,400,500,600,700,800,900" rel="stylesheet">
|
||||
|
||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||
<link rel="stylesheet" href="<?=$resources?>/css/style.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="wrapper d-flex align-items-stretch">
|
||||
<nav id="sidebar">
|
||||
<div class="p-4 pt-5">
|
||||
<a href="#" class="img logo rounded-circle mb-5" style="background-image: url(<?=$resources?>/images/33.png);"></a>
|
||||
<?php \kernel\widgets\MenuWidget::create()->run(); ?>
|
||||
<div class="footer">
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- Page Content -->
|
||||
<div id="content" class="p-4 p-md-5">
|
||||
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||
<div class="container-fluid">
|
||||
|
||||
<button type="button" id="sidebarCollapse" class="btn btn-primary">
|
||||
<i class="fa fa-bars"></i>
|
||||
<span class="sr-only">Toggle Menu</span>
|
||||
</button>
|
||||
<button class="btn btn-dark d-inline-block d-lg-none ml-auto" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<i class="fa fa-bars"></i>
|
||||
</button>
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||
<ul class="nav navbar-nav ml-auto">
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" href="#">Home</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#">About</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#">Portfolio</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<?= $content ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="<?=$resources?>/js/jquery.min.js"></script>
|
||||
<script src="<?=$resources?>/js/popper.js"></script>
|
||||
<script src="<?=$resources?>/js/bootstrap.min.js"></script>
|
||||
<script src="<?=$resources?>/js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
10
kernel/admin_themes/simple/manifest.json
Normal file
10
kernel/admin_themes/simple/manifest.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "Simple",
|
||||
"version": "0.1",
|
||||
"description": "Simple admin theme",
|
||||
"preview": "preview.png",
|
||||
"resource": "/resources/simple",
|
||||
"resource_path": "{RESOURCES}/simple",
|
||||
"layout": "main.php",
|
||||
"layout_path": "{KERNEL_ADMIN_THEMES}/simple/layout"
|
||||
}
|
@ -78,12 +78,14 @@ class MigrationController extends ConsoleController
|
||||
public function actionRollback(): void
|
||||
{
|
||||
try {
|
||||
$step = $this->argv['step'] ?? 1;
|
||||
$dmr = new DatabaseMigrationRepository(App::$db->capsule->getDatabaseManager(), 'migration');
|
||||
|
||||
$m = new Migrator($dmr, App::$db->capsule->getDatabaseManager(), new Filesystem());
|
||||
//$migrationPaths = array_merge(App::$migrationsPaths, [WORKSPACE_DIR . '/console/migrations']);
|
||||
$migrationPaths = [ROOT_DIR . '/console/migrations'];
|
||||
$res = $m->rollback($migrationPaths);
|
||||
$migrationPaths = [ROOT_DIR . '/migrations'];
|
||||
$res = $m->rollback($migrationPaths, ['step' => $step]);
|
||||
print_r($step);
|
||||
foreach ($res as $re){
|
||||
$this->out->r(basename($re), 'green');
|
||||
}
|
||||
|
@ -18,6 +18,9 @@ use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Menu extends Model
|
||||
{
|
||||
const DISABLE_STATUS = 0;
|
||||
const ACTIVE_STATUS = 1;
|
||||
|
||||
protected $table = 'menu';
|
||||
protected $fillable = ['parent_id', 'icon_file', 'icon_font', 'label', 'url', 'status'];
|
||||
protected array $dates = ['deleted_at'];
|
||||
@ -43,4 +46,15 @@ class Menu extends Model
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public static function getStatus(): array
|
||||
{
|
||||
return [
|
||||
self::DISABLE_STATUS => "Не активный",
|
||||
self::ACTIVE_STATUS => "Активный",
|
||||
];
|
||||
}
|
||||
}
|
40
kernel/models/Option.php
Normal file
40
kernel/models/Option.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace kernel\models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Option extends Model
|
||||
{
|
||||
|
||||
const DISABLE_STATUS = 0;
|
||||
const ACTIVE_STATUS = 1;
|
||||
|
||||
protected $table = 'option';
|
||||
protected $fillable = ['key', 'value', 'label', 'status', 'created_at', 'updated_at'];
|
||||
protected array $dates = ['deleted_at'];
|
||||
|
||||
public static function labels(): array
|
||||
{
|
||||
return [
|
||||
'key' => 'Ключ',
|
||||
'value' => 'Значение',
|
||||
'label' => 'Заголовок',
|
||||
'status' => 'Статус',
|
||||
'created_at' => 'Дата создания',
|
||||
'updated_at' => 'Дата редактирования',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public static function getStatus(): array
|
||||
{
|
||||
return [
|
||||
self::DISABLE_STATUS => "Не активный",
|
||||
self::ACTIVE_STATUS => "Активный",
|
||||
];
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace kernel\modules\admin_themes\controllers;
|
||||
|
||||
use app\helpers\Debug;
|
||||
use DirectoryIterator;
|
||||
use JetBrains\PhpStorm\NoReturn;
|
||||
use kernel\AdminController;
|
||||
use kernel\Controller;
|
||||
use kernel\models\Option;
|
||||
use kernel\Request;
|
||||
use kernel\services\AdminThemeService;
|
||||
|
||||
class AdminThemeController extends AdminController
|
||||
{
|
||||
protected function init(): void
|
||||
{
|
||||
parent::init();
|
||||
$this->cgView->viewPath = KERNEL_MODULES_DIR . "/admin_themes/views/";
|
||||
}
|
||||
|
||||
public function actionIndex(): void
|
||||
{
|
||||
$admin_theme_paths = Option::where("key", "admin_theme_paths")->first();
|
||||
$dirs = [];
|
||||
if ($admin_theme_paths){
|
||||
$path = json_decode($admin_theme_paths->value);
|
||||
foreach ($path->paths as $p){
|
||||
$dirs[] = getConst($p);
|
||||
}
|
||||
}
|
||||
|
||||
$info_to_table = [];
|
||||
$meta = [];
|
||||
$meta['columns'] = ["preview" => "Превью", "name" => "Название", "version" => "Версия", "description" => "Описание"];
|
||||
$meta['params'] = ["class" => "table table-bordered"];
|
||||
$meta['perPage'] = 10;
|
||||
$meta['baseUrl'] = "/admin/settings/admin-themes";
|
||||
$meta['currentPage'] = 1;
|
||||
|
||||
$info_to_table['meta'] = $meta;
|
||||
|
||||
$themes_info = [];
|
||||
foreach ($dirs as $dir){
|
||||
$i = 1;
|
||||
foreach (new DirectoryIterator($dir) as $fileInfo) {
|
||||
$info = [];
|
||||
if($fileInfo->isDot()) continue;
|
||||
$info['id'] = $i;
|
||||
$themes_info[] = array_merge($info, $this->adminThemeService->getAdminThemeInfo($fileInfo->getPathname()));
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
$info_to_table['meta']['total'] = count($themes_info);
|
||||
$info_to_table['data'] = $themes_info;
|
||||
|
||||
$this->cgView->render("index.php", ['json' => json_encode($info_to_table, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)]);
|
||||
}
|
||||
|
||||
#[NoReturn] public function actionActivate(): void
|
||||
{
|
||||
$request = new Request();
|
||||
$this->adminThemeService->setActiveAdminTheme($request->get("p"));
|
||||
|
||||
$this->cgView->render("view.php", ['data' => $this->adminThemeService->getAdminThemeInfo($request->get("p"))]);
|
||||
}
|
||||
|
||||
}
|
29
kernel/modules/admin_themes/views/index.php
Normal file
29
kernel/modules/admin_themes/views/index.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
/**
|
||||
* @var $json string
|
||||
*/
|
||||
|
||||
use app\helpers\Debug;
|
||||
use Itguild\EloquentTable\EloquentDataProvider;
|
||||
use Itguild\EloquentTable\ListEloquentTable;
|
||||
use kernel\models\Option;
|
||||
|
||||
|
||||
$table = new \Itguild\Tables\ListJsonTable($json);
|
||||
$table->columns([
|
||||
'preview' => function ($data) {
|
||||
return "<img src='$data' width='200px'>";
|
||||
}
|
||||
]);
|
||||
$table->addAction(function ($row, $url){
|
||||
$path = $row['path'];
|
||||
$active_admin_theme = Option::where("key", "active_admin_theme")->first();
|
||||
$btn = "<a class='btn btn-primary' href='$url/activate/?p=$path' style='margin: 3px; width: 150px;' >Активировать</a>";;
|
||||
if ($path === $active_admin_theme->value){
|
||||
$btn = "Активна";
|
||||
}
|
||||
|
||||
return $btn;
|
||||
});
|
||||
$table->create();
|
||||
$table->render();
|
30
kernel/modules/admin_themes/views/view.php
Normal file
30
kernel/modules/admin_themes/views/view.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
/**
|
||||
* @var array $data
|
||||
*/
|
||||
|
||||
use kernel\IGTabel\btn\DangerBtn;
|
||||
use kernel\IGTabel\btn\PrimaryBtn;
|
||||
use kernel\IGTabel\btn\SuccessBtn;
|
||||
|
||||
$table_info = [
|
||||
"meta" => [
|
||||
"rows" => ["preview" => "Превью", "name" => "Название", "version" => "Версия", "description" => "Описание"],
|
||||
"params" => ["class" => "table table-bordered"],
|
||||
"baseUrl" => "/admin/settings/admin-themes",
|
||||
],
|
||||
"data" => $data
|
||||
];
|
||||
|
||||
$table = new \Itguild\Tables\ViewJsonTable(json_encode($table_info, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
||||
$table->rows([
|
||||
'preview' => function ($data) {
|
||||
return "<img src='$data' width='500px'>";
|
||||
}
|
||||
]);
|
||||
$table->beforePrint(function () {
|
||||
$btn = PrimaryBtn::create("Список", "/admin/settings/admin-themes")->fetch();
|
||||
return $btn;
|
||||
});
|
||||
$table->create();
|
||||
$table->render();
|
107
kernel/modules/menu/controllers/MenuController.php
Normal file
107
kernel/modules/menu/controllers/MenuController.php
Normal file
@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
namespace kernel\modules\menu\controllers;
|
||||
|
||||
use app\helpers\Debug;
|
||||
use app\models\forms\CreateMenuForm;
|
||||
use app\services\MenuService;
|
||||
use Exception;
|
||||
use JetBrains\PhpStorm\NoReturn;
|
||||
use kernel\Controller;
|
||||
use kernel\models\Menu;
|
||||
use Twig\Error\LoaderError;
|
||||
use Twig\Error\RuntimeError;
|
||||
use Twig\Error\SyntaxError;
|
||||
|
||||
class MenuController extends Controller
|
||||
{
|
||||
protected function init(): void
|
||||
{
|
||||
$this->cgView->viewPath = KERNEL_MODULES_DIR . "/menu/views/";
|
||||
$this->cgView->layoutPath = ROOT_DIR . "/views/admin/";
|
||||
$this->cgView->layout = "layouts/main.php";
|
||||
}
|
||||
|
||||
public function actionCreate(): void
|
||||
{
|
||||
$this->cgView->render("form.php");
|
||||
}
|
||||
|
||||
#[NoReturn] public function actionAdd(): void
|
||||
{
|
||||
$menuForm = new CreateMenuForm();
|
||||
$menuService = new MenuService();
|
||||
$menuForm->load($_REQUEST);
|
||||
if ($menuForm->validate()){
|
||||
$menuItem = $menuService->create($menuForm);
|
||||
if ($menuItem){
|
||||
$this->redirect("/admin/settings/menu/" . $menuItem->id);
|
||||
}
|
||||
}
|
||||
$this->redirect("/admin/settings/menu/create");
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function actionIndex($page_number = 1): void
|
||||
{
|
||||
$this->cgView->render("index.php", ['page_number' => $page_number]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function actionView($id): void
|
||||
{
|
||||
$menuItem = Menu::find($id);
|
||||
|
||||
if (!$menuItem){
|
||||
throw new Exception(message: "The menu item not found");
|
||||
}
|
||||
$this->cgView->render("view.php", ['menu' => $menuItem]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RuntimeError
|
||||
* @throws SyntaxError
|
||||
* @throws LoaderError|Exception
|
||||
*/
|
||||
public function actionUpdate($id): void
|
||||
{
|
||||
$model = Menu::find($id);
|
||||
if (!$model){
|
||||
throw new Exception(message: "The menu item not found");
|
||||
}
|
||||
|
||||
$this->cgView->render("form.php", ['model' => $model]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function actionEdit($id): void
|
||||
{
|
||||
$menuItem = Menu::find($id);
|
||||
if (!$menuItem){
|
||||
throw new Exception(message: "The menu item not found");
|
||||
}
|
||||
$menuForm = new CreateMenuForm();
|
||||
$menuService = new MenuService();
|
||||
$menuForm->load($_REQUEST);
|
||||
if ($menuForm->validate()){
|
||||
$menuItem = $menuService->update($menuForm, $menuItem);
|
||||
if ($menuItem){
|
||||
$this->redirect("/admin/settings/menu/" . $menuItem->id);
|
||||
}
|
||||
}
|
||||
$this->redirect("/admin/settings/menu/update/" . $id);
|
||||
}
|
||||
|
||||
#[NoReturn] public function actionDelete($id): void
|
||||
{
|
||||
Menu::find($id)->delete();
|
||||
$this->redirect("/admin/settings/menu/");
|
||||
}
|
||||
|
||||
}
|
60
kernel/modules/menu/models/Menu.php
Normal file
60
kernel/modules/menu/models/Menu.php
Normal file
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace kernel\modules\menu\models;
|
||||
|
||||
use app\helpers\Debug;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* @property int $id
|
||||
* @property int parent_id
|
||||
* @property string icon_file
|
||||
* @property string icon_font
|
||||
* @property string label
|
||||
* @property string url
|
||||
* @property int status
|
||||
* @method static find($id)
|
||||
*/
|
||||
|
||||
class Menu extends Model
|
||||
{
|
||||
const DISABLE_STATUS = 0;
|
||||
const ACTIVE_STATUS = 1;
|
||||
|
||||
protected $table = 'menu';
|
||||
protected $fillable = ['parent_id', 'icon_file', 'icon_font', 'label', 'url', 'status'];
|
||||
protected array $dates = ['deleted_at'];
|
||||
|
||||
public static function labels(): array
|
||||
{
|
||||
return [
|
||||
'parent_id' => 'Родительский пункт меню',
|
||||
'icon_file' => 'Путь к иконке',
|
||||
'icon_font' => 'Иконка',
|
||||
'label' => 'Заголовок',
|
||||
'url' => 'URL',
|
||||
'status' => 'Статус',
|
||||
];
|
||||
}
|
||||
|
||||
public static function getChild(int $id)
|
||||
{
|
||||
$collection = Menu::where("parent_id", $id)->get();
|
||||
if (!$collection->isEmpty()){
|
||||
return $collection;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public static function getStatus(): array
|
||||
{
|
||||
return [
|
||||
self::DISABLE_STATUS => "Не активный",
|
||||
self::ACTIVE_STATUS => "Активный",
|
||||
];
|
||||
}
|
||||
}
|
78
kernel/modules/menu/views/form.php
Normal file
78
kernel/modules/menu/views/form.php
Normal file
@ -0,0 +1,78 @@
|
||||
<?php
|
||||
/**
|
||||
* @var Menu $model
|
||||
*/
|
||||
|
||||
use kernel\models\Menu;
|
||||
|
||||
$form = new \itguild\forms\ActiveForm();
|
||||
$form->beginForm(isset($model) ? "/admin/settings/menu/edit/" . $model->id : "/admin/menu");
|
||||
|
||||
$form->field(class: \itguild\forms\inputs\Select::class, name: "parent_id", params: [
|
||||
'class' => "form-control",
|
||||
'value' => $model->parent_id ?? ''
|
||||
])
|
||||
->setLabel("Родительский пункт меню")
|
||||
->setOptions(\app\services\MenuService::createLabelArr())
|
||||
->render();
|
||||
|
||||
$form->field(class: \itguild\forms\inputs\TextInput::class, name: "icon_file", params: [
|
||||
'class' => "form-control",
|
||||
'value' => $model->icon_file ?? ''
|
||||
])
|
||||
->setLabel("Путь к иконке")
|
||||
->render();
|
||||
|
||||
$form->field(class: \itguild\forms\inputs\TextInput::class, name: "icon_font", params: [
|
||||
'class' => "form-control",
|
||||
'value' => $model->icon_font ?? ''
|
||||
])
|
||||
->setLabel("Иконка")
|
||||
->render();
|
||||
|
||||
$form->field(class: \itguild\forms\inputs\TextInput::class, name: "label", params: [
|
||||
'class' => "form-control",
|
||||
'value' => $model->label ?? ''
|
||||
])
|
||||
->setLabel("Заголовок")
|
||||
->render();
|
||||
|
||||
$form->field(class: \itguild\forms\inputs\TextInput::class, name: "url", params: [
|
||||
'class' => "form-control",
|
||||
'value' => $model->url ?? ''
|
||||
])
|
||||
->setLabel("URL")
|
||||
->render();
|
||||
|
||||
$form->field(class: \itguild\forms\inputs\Select::class, name: "status", params: [
|
||||
'class' => "form-control",
|
||||
'value' => $model->status ?? '1'
|
||||
])
|
||||
->setLabel("Статус")
|
||||
->setOptions(Menu::getStatus())
|
||||
->render();
|
||||
?>
|
||||
<div class="row">
|
||||
<div class="col-sm-2">
|
||||
<?php
|
||||
$form->field(\itguild\forms\inputs\Button::class, name: "btn-submit", params: [
|
||||
'class' => "btn btn-primary ",
|
||||
'value' => 'Отправить',
|
||||
'typeInput' => 'submit'
|
||||
])
|
||||
->render();
|
||||
?>
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<?php
|
||||
$form->field(\itguild\forms\inputs\Button::class, name: "btn-reset", params: [
|
||||
'class' => "btn btn-warning",
|
||||
'value' => 'Сбросить',
|
||||
'typeInput' => 'reset'
|
||||
])
|
||||
->render();
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
$form->endForm();
|
49
kernel/modules/menu/views/index.php
Normal file
49
kernel/modules/menu/views/index.php
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
/**
|
||||
* @var \Illuminate\Database\Eloquent\Collection $menuItem
|
||||
* @var int $page_number
|
||||
*/
|
||||
|
||||
use app\tables\columns\menu\MenuDeleteActionColumn;
|
||||
use app\tables\columns\menu\MenuEditActionColumn;
|
||||
use app\tables\columns\menu\MenuViewActionColumn;
|
||||
use Itguild\EloquentTable\EloquentDataProvider;
|
||||
use Itguild\EloquentTable\ListEloquentTable;
|
||||
use kernel\IGTabel\btn\PrimaryBtn;
|
||||
use kernel\models\Menu;
|
||||
|
||||
$table = new ListEloquentTable(new EloquentDataProvider(Menu::class, [
|
||||
'currentPage' => $page_number,
|
||||
'perPage' => 8,
|
||||
'params' => ["class" => "table table-bordered", "border" => "2"],
|
||||
'baseUrl' => "/admin/settings/menu",
|
||||
]));
|
||||
$table->columns([
|
||||
'parent_id' => (function ($data) {
|
||||
if ($data == 0) return null;
|
||||
return Menu::find($data)->label;
|
||||
}),
|
||||
'created_at' => function ($data) {
|
||||
if (!$data){
|
||||
return null;
|
||||
}
|
||||
|
||||
return (new DateTimeImmutable($data))->format("d-m-Y");
|
||||
},
|
||||
'updated_at' => function ($data) {
|
||||
if (!$data){
|
||||
return null;
|
||||
}
|
||||
|
||||
return (new DateTimeImmutable($data))->format("d-m-Y");
|
||||
}
|
||||
]);
|
||||
$table->beforePrint(function () {
|
||||
return PrimaryBtn::create("Создать", "/admin/menu/create")->fetch();
|
||||
//return (new PrimaryBtn("Создать", "/admin/user/create"))->fetch();
|
||||
});
|
||||
$table->addAction(MenuViewActionColumn::class);
|
||||
$table->addAction(MenuEditActionColumn::class);
|
||||
$table->addAction(MenuDeleteActionColumn::class);
|
||||
$table->create();
|
||||
$table->render();
|
51
kernel/modules/menu/views/view.php
Normal file
51
kernel/modules/menu/views/view.php
Normal file
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @var \Illuminate\Database\Eloquent\Collection $menu
|
||||
*/
|
||||
|
||||
use app\models\User;
|
||||
use Itguild\EloquentTable\ViewEloquentTable;
|
||||
use Itguild\EloquentTable\ViewJsonTableEloquentModel;
|
||||
use kernel\IGTabel\btn\DangerBtn;
|
||||
use kernel\IGTabel\btn\PrimaryBtn;
|
||||
use kernel\IGTabel\btn\SuccessBtn;
|
||||
use kernel\models\Menu;
|
||||
|
||||
$table = new ViewEloquentTable(new ViewJsonTableEloquentModel($menu, [
|
||||
'params' => ["class" => "table table-bordered", "border" => "2"],
|
||||
'baseUrl' => "/admin/menu",
|
||||
]));
|
||||
$r = new ViewJsonTableEloquentModel($menu, [
|
||||
'params' => ["class" => "table table-bordered", "border" => "2"],
|
||||
'baseUrl' => "/admin/menu",
|
||||
]);
|
||||
\app\helpers\Debug::dd($r->getJson());
|
||||
$table->beforePrint(function () use ($menu) {
|
||||
$btn = PrimaryBtn::create("Список", "/admin/settings/menu")->fetch();
|
||||
$btn .= SuccessBtn::create("Редактировать", "/admin/settings/menu/update/" . $menu->id)->fetch();
|
||||
$btn .= DangerBtn::create("Удалить", "/admin/settings/menu/delete/" . $menu->id)->fetch();
|
||||
return $btn;
|
||||
});
|
||||
$table->rows([
|
||||
'parent_id' => (function ($data) {
|
||||
if ($data == 0) return null;
|
||||
return Menu::find($data)->label;
|
||||
}),
|
||||
'created_at' => function ($data) {
|
||||
if (!$data){
|
||||
return null;
|
||||
}
|
||||
|
||||
return (new DateTimeImmutable($data))->format("d-m-Y");
|
||||
},
|
||||
'updated_at' => function ($data) {
|
||||
if (!$data){
|
||||
return null;
|
||||
}
|
||||
|
||||
return (new DateTimeImmutable($data))->format("d-m-Y");
|
||||
}
|
||||
]);
|
||||
$table->create();
|
||||
$table->render();
|
52
kernel/routs/admin.php
Normal file
52
kernel/routs/admin.php
Normal file
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
use app\controllers\MainController;
|
||||
use kernel\App;
|
||||
use kernel\modules\admin_themes\controllers\AdminThemeController;
|
||||
use Phroute\Phroute\RouteCollector;
|
||||
|
||||
|
||||
App::$collector->group(["prefix" => "admin"], function (RouteCollector $router){
|
||||
App::$collector->group(["prefix" => "user"], callback: function (RouteCollector $router){
|
||||
App::$collector->get('/', [\app\controllers\UserController::class, 'actionIndex']);
|
||||
App::$collector->get('/page/{page_number}', [\app\controllers\UserController::class, 'actionIndex']);
|
||||
App::$collector->get('/create', [\app\controllers\UserController::class, 'actionCreate']);
|
||||
App::$collector->post("/", [\app\controllers\UserController::class, 'actionAdd']);
|
||||
App::$collector->get('/{id}', [\app\controllers\UserController::class, 'actionView']);
|
||||
App::$collector->any('/update/{id}', [\app\controllers\UserController::class, 'actionUpdate']);
|
||||
App::$collector->any("/edit/{id}", [\app\controllers\UserController::class, 'actionEdit']);
|
||||
App::$collector->get('/delete/{id}', [\app\controllers\UserController::class, 'actionDelete']);
|
||||
});
|
||||
App::$collector->group(["prefix" => "post"], function (RouteCollector $router){
|
||||
App::$collector->get('/', [\app\controllers\PostController::class, 'actionIndex']);
|
||||
App::$collector->get('/page/{page_number}', [\app\controllers\PostController::class, 'actionIndex']);
|
||||
App::$collector->get('/create', [\app\controllers\PostController::class, 'actionCreate']);
|
||||
App::$collector->post("/", [\app\controllers\PostController::class, 'actionAdd']);
|
||||
App::$collector->get('/{id}', [\app\controllers\PostController::class, 'actionView']);
|
||||
App::$collector->any('/update/{id}', [\app\controllers\PostController::class, 'actionUpdate']);
|
||||
App::$collector->any("/edit/{id}", [\app\controllers\PostController::class, 'actionEdit']);
|
||||
App::$collector->get('/delete/{id}', [\app\controllers\PostController::class, 'actionDelete']);
|
||||
});
|
||||
App::$collector->group(["prefix" => "settings"], function (RouteCollector $router){
|
||||
App::$collector->group(["prefix" => "menu"], function (RouteCollector $router){
|
||||
App::$collector->get('/', [\kernel\modules\menu\controllers\MenuController::class, 'actionIndex']);
|
||||
App::$collector->get('/page/{page_number}', [\kernel\modules\menu\controllers\MenuController::class, 'actionIndex']);
|
||||
App::$collector->get('/create', [\kernel\modules\menu\controllers\MenuController::class, 'actionCreate']);
|
||||
App::$collector->post("/", [\kernel\modules\menu\controllers\MenuController::class, 'actionAdd']);
|
||||
App::$collector->get('/{id}', [\kernel\modules\menu\controllers\MenuController::class, 'actionView']);
|
||||
App::$collector->any('/update/{id}', [\kernel\modules\menu\controllers\MenuController::class, 'actionUpdate']);
|
||||
App::$collector->any("/edit/{id}", [\kernel\modules\menu\controllers\MenuController::class, 'actionEdit']);
|
||||
App::$collector->get('/delete/{id}', [\kernel\modules\menu\controllers\MenuController::class, 'actionDelete']);
|
||||
});
|
||||
App::$collector->group(["prefix" => "admin-themes"], function (RouteCollector $router){
|
||||
App::$collector->get('/', [AdminThemeController::class, 'actionIndex']);
|
||||
App::$collector->get('/activate', [AdminThemeController::class, 'actionActivate']);
|
||||
// App::$collector->get('/create', [\kernel\modules\menu\controllers\MenuController::class, 'actionCreate']);
|
||||
// App::$collector->post("/", [\kernel\modules\menu\controllers\MenuController::class, 'actionAdd']);
|
||||
// App::$collector->get('/{id}', [\kernel\modules\menu\controllers\MenuController::class, 'actionView']);
|
||||
// App::$collector->any('/update/{id}', [\kernel\modules\menu\controllers\MenuController::class, 'actionUpdate']);
|
||||
// App::$collector->any("/edit/{id}", [\kernel\modules\menu\controllers\MenuController::class, 'actionEdit']);
|
||||
// App::$collector->get('/delete/{id}', [\kernel\modules\menu\controllers\MenuController::class, 'actionDelete']);
|
||||
});
|
||||
});
|
||||
});
|
56
kernel/services/AdminThemeService.php
Normal file
56
kernel/services/AdminThemeService.php
Normal file
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace kernel\services;
|
||||
|
||||
use kernel\models\Option;
|
||||
|
||||
class AdminThemeService
|
||||
{
|
||||
protected Option $option;
|
||||
protected string $active_theme;
|
||||
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->option = new Option();
|
||||
$this->findActiveAdminTheme();
|
||||
}
|
||||
|
||||
public function findActiveAdminTheme(): void
|
||||
{
|
||||
$model = Option::where("key", "active_admin_theme")->first();
|
||||
$this->active_theme = $model->value;
|
||||
}
|
||||
|
||||
public function getActiveAdminTheme(): string
|
||||
{
|
||||
return $this->active_theme;
|
||||
}
|
||||
|
||||
public function setActiveAdminTheme(string $theme): void
|
||||
{
|
||||
$active_admin_theme = Option::where("key", "active_admin_theme")->first();
|
||||
$active_admin_theme->value = $theme;
|
||||
$active_admin_theme->save();
|
||||
}
|
||||
|
||||
public function getActiveAdminThemeInfo(): false|array|string
|
||||
{
|
||||
return $this->getAdminThemeInfo($this->active_theme);
|
||||
}
|
||||
|
||||
public function getAdminThemeInfo(string $theme): false|array|string
|
||||
{
|
||||
$info = [];
|
||||
$info['path'] = $theme;
|
||||
if (file_exists($theme . "/manifest.json")){
|
||||
$manifest = file_get_contents($theme . "/manifest.json");
|
||||
$manifest = json_decode($manifest, true);
|
||||
$manifest['preview'] = $manifest['resource'] . "/" . $manifest['preview'];
|
||||
$info = array_merge($info, $manifest);
|
||||
}
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user