diff --git a/kernel/modules/module_shop_client/controllers/ModuleShopClientController.php b/kernel/modules/module_shop_client/controllers/ModuleShopClientController.php index 13c4e6c..e6a7a75 100644 --- a/kernel/modules/module_shop_client/controllers/ModuleShopClientController.php +++ b/kernel/modules/module_shop_client/controllers/ModuleShopClientController.php @@ -5,6 +5,7 @@ namespace kernel\modules\module_shop_client\controllers; use GuzzleHttp\Client; use GuzzleHttp\Exception\GuzzleException; use JetBrains\PhpStorm\NoReturn; +use Josantonius\Session\Facades\Session; use kernel\AdminController; use kernel\Flash; use kernel\helpers\Debug; @@ -156,49 +157,6 @@ class ModuleShopClientController extends AdminController $this->redirect('/admin/module_shop_client', 302); } -// public function actionSearch(int $page_number = 1): void -// { -// $request = new Request(); -// $filters = $request->get(); -//// Debug::dd($filters); -// if ($this->moduleService->issetModuleShopToken()) { -// if ($this->moduleService->isServerAvailable()) { -// $modules_info = []; -// $per_page = 8; -// $modules = RESTClient::request($_ENV['MODULE_SHOP_URL'] . '/api/module_shop/gb_slug'); -// $modules = json_decode($modules->getBody()->getContents(), true); -// foreach ($modules as $module) { -// foreach ($filters as $key => $value) { -// if ($value === '') continue; -// if ($module[$key] !== $value) { -// break; -// } -// -// $modules_info[] = $module; -// } -// } -// $module_count = count($modules_info); -// $modules_info = array_slice($modules_info, $per_page * ($page_number - 1), $per_page); -// -// $this->cgView->render("index.php", [ -// 'modules_info' => $modules_info, -// 'moduleService' => $this->moduleService, -// 'page_number' => $page_number, -// 'module_count' => $module_count, -// 'per_page' => $per_page, -// 'kernelService' => new KernelService(), -// 'adminThemeService' => new AdminThemeService(), -// 'filterValues' => $filters -// ]); -// } else { -// $this->cgView->render("module_shop_error_connection.php"); -// } -// -// } else { -// $this->cgView->render("login_at_module_shop.php"); -// } -// } - public function actionSearch(int $page_number = 1): void { $request = new Request(); @@ -285,4 +243,56 @@ class ModuleShopClientController extends AdminController $this->cgView->render('module_shop_error_connection.php'); } + #[NoReturn] public function actionAdminThemeInstall(): void + { + $request = new Request(); + $id = $request->get("id"); + $adminThemeInfo = RESTClient::request($_ENV['MODULE_SHOP_URL'] . '/api/module_shop/install/' . $id); + + $adminThemeInfo = json_decode($adminThemeInfo->getBody()->getContents(), true); + Files::uploadByUrl($_ENV['MODULE_SHOP_URL'] . $adminThemeInfo['path_to_archive'], RESOURCES_DIR . "/tmp/admin_themes"); + if ($this->adminThemeService->install('/resources/tmp/admin_themes/' . basename($adminThemeInfo['path_to_archive']))) { + Flash::setMessage("success", "Тема админ-панели успешно установлена."); + } else { + Session::start(); + Session::set("error", implode(";", $this->adminThemeService->getErrors())); + } + + $this->redirect('/admin/module_shop_client', 302); + } + + #[NoReturn] public function actionAdminThemeUpdate(): void + { + $request = new Request(); + $slug = $request->get("slug"); + $modules_info = RESTClient::request($_ENV['MODULE_SHOP_URL'] . '/api/module_shop/gb_slug'); + + $modules_info = json_decode($modules_info->getBody()->getContents(), true); + foreach ($modules_info as $module) { + if ($module['slug'] === $slug) { + $path = $module['path_to_archive']; + } + } + if (isset($path)) { + Files::uploadByUrl($_ENV['MODULE_SHOP_URL'] . $path, RESOURCES_DIR . "/tmp/admin_themes"); + $this->adminThemeService->update('/resources/tmp/admin_themes/' . basename($path)); + Flash::setMessage("success", "Тема админ-панели успешно обновлена."); + } else { + Flash::setMessage("error", "Ошибка обновления темы админ-панели."); + } + + $this->redirect('/admin/module_shop_client', 302); + } + + #[NoReturn] public function actionAdminThemeDelete(): void + { + $request = new Request(); + $slug = $request->get("slug"); + $adminThemeInfo = $this->adminThemeService->getAdminThemeInfoBySlug($slug); + $this->adminThemeService->uninstall($adminThemeInfo['path']); + + Flash::setMessage("success", "Тема админ-панели успешно удалена."); + $this->redirect('/admin/module_shop_client', 302); + } + } \ No newline at end of file diff --git a/kernel/modules/module_shop_client/routs/module_shop_client.php b/kernel/modules/module_shop_client/routs/module_shop_client.php index 945ccd2..52647d2 100644 --- a/kernel/modules/module_shop_client/routs/module_shop_client.php +++ b/kernel/modules/module_shop_client/routs/module_shop_client.php @@ -24,7 +24,9 @@ App::$collector->group(["prefix" => "admin"], function (RouteCollector $router){ }); App::$collector->group(["prefix" => "admin_theme"], function (RouteCollector $router) { App::$collector->get('/install', [\kernel\modules\module_shop_client\controllers\ModuleShopClientController::class, 'actionAdminThemeInstall']); - App::$collector->post('/update', [\kernel\modules\module_shop_client\controllers\ModuleShopClientController::class, 'actionAdminThemeUpdate']); + App::$collector->get('/update', [\kernel\modules\module_shop_client\controllers\ModuleShopClientController::class, 'actionAdminThemeUpdate']); + App::$collector->get('/delete', [\kernel\modules\module_shop_client\controllers\ModuleShopClientController::class, 'actionAdminThemeDelete']); + }); }); }); diff --git a/kernel/modules/module_shop_client/views/index.php b/kernel/modules/module_shop_client/views/index.php index f3474d4..e0d1c8c 100644 --- a/kernel/modules/module_shop_client/views/index.php +++ b/kernel/modules/module_shop_client/views/index.php @@ -104,7 +104,7 @@ $table->addAction(function ($row, $url) use ($adminThemeService) { $slug = $row['slug']; if ($adminThemeService->isInstall($slug)) { if (!$adminThemeService->isLastVersion($slug)) { - $url = "$url/admin_theme/update/"; + $url = "$url/admin_theme/update/?slug=" . $row['slug']; return \kernel\widgets\IconBtn\IconBtnUpdateWidget::create(['url' => $url])->run(); } @@ -114,6 +114,22 @@ $table->addAction(function ($row, $url) use ($adminThemeService) { return false; }); +$table->addAction(function ($row, $url) use ($adminThemeService) { + if ($row['type'] === 'admin_theme') { + if ($adminThemeService->isInstall($row['slug'])) { + $url = "$url/admin_theme/delete/?slug=" . $row['slug']; + + return \kernel\widgets\IconBtn\IconBtnDeleteWidget::create(['url' => $url])->run(); + } else { + $url = "$url/admin_theme/install/?id=" . $row['id']; + + return \kernel\widgets\IconBtn\IconBtnInstallWidget::create(['url' => $url])->run(); + } + } + + return false; +}); + $table->afterPrint(function () { return \kernel\IGTabel\btn\PrimaryBtn::create('Сбросить все фильтры', '/admin/module_shop_client')->fetch(); }); diff --git a/kernel/modules/user/controllers/UserController.php b/kernel/modules/user/controllers/UserController.php index ddf0651..60a6028 100644 --- a/kernel/modules/user/controllers/UserController.php +++ b/kernel/modules/user/controllers/UserController.php @@ -150,7 +150,47 @@ class UserController extends AdminController if (!$user){ throw new Exception(message: "The user not found"); } - $this->cgView->render("view.php", ['user' => $user]); + $this->cgView->render("view_profile.php", ['user' => $user]); + } + + public function actionProfileUpdate(): void + { + $model = UserService::getAuthUser(); + if (!$model){ + throw new Exception(message: "The user not found"); + } + + $this->cgView->render("form_profile.php", ['model' => $model]); + } + + public function actionProfileEdit(): void + { + $user = UserService::getAuthUser(); + if (!$user){ + throw new Exception(message: "The user not found"); + } + + $userForm = new CreateUserForm(); + $userService = new UserService(); + $userForm->load($_REQUEST); + + if (isset($_FILES['user_photo']) && $_FILES['user_photo']['error'] === UPLOAD_ERR_OK) { + $file = new FileUpload($_FILES['user_photo'], ['jpg', 'jpeg', 'png']); + $file->upload(); + $userForm->setItem('user_photo', $file->getUploadFile()); + } + + if ($userForm->validateForUpdate()){ + $user = $userService->update($userForm, $user); + + $entityRelation = new EntityRelation(); + $entityRelation->saveEntityRelation(entity: "user", model: $user, request: new Request()); + + if ($user){ + $this->redirect("/admin/user/profile"); + } + } + $this->redirect("/admin/user/profile/update"); } } \ No newline at end of file diff --git a/kernel/modules/user/routs/user.php b/kernel/modules/user/routs/user.php index 7360b89..d5dc543 100644 --- a/kernel/modules/user/routs/user.php +++ b/kernel/modules/user/routs/user.php @@ -17,7 +17,11 @@ App::$collector->group(["prefix" => "admin"], function (RouteCollector $router){ App::$collector->any('/update/{id}', [\kernel\modules\user\controllers\UserController::class, 'actionUpdate']); App::$collector->any("/edit/{id}", [\kernel\modules\user\controllers\UserController::class, 'actionEdit']); App::$collector->get('/delete/{id}', [\kernel\modules\user\controllers\UserController::class, 'actionDelete']); - App::$collector->get('/profile', [\kernel\modules\user\controllers\UserController::class, 'actionProfile']); + App::$collector->group(["prefix" => "profile"], callback: function (RouteCollector $router) { + App::$collector->get('/', [\kernel\modules\user\controllers\UserController::class, 'actionProfile']); + App::$collector->get('/update', [\kernel\modules\user\controllers\UserController::class, 'actionProfileUpdate']); + App::$collector->any('/edit', [\kernel\modules\user\controllers\UserController::class, 'actionProfileEdit']); + }); }); }); }); \ No newline at end of file diff --git a/kernel/modules/user/views/form_profile.php b/kernel/modules/user/views/form_profile.php new file mode 100644 index 0000000..0c9ca1a --- /dev/null +++ b/kernel/modules/user/views/form_profile.php @@ -0,0 +1,74 @@ +beginForm("/admin/user/profile/edit", enctype: 'multipart/form-data'); + +$form->field(class: \itguild\forms\inputs\TextInput::class, name: "username", params: [ + 'class' => "form-control", + 'placeholder' => 'Логин', + 'value' => $model->username ?? '' +]) + ->setLabel("Логин") + ->render(); + +$form->field(class: \itguild\forms\inputs\TextInput::class, name: "password", params: [ + 'class' => "form-control", + 'type' => "password", +]) + ->setLabel("Пароль") + ->render(); + +$form->field(class: \itguild\forms\inputs\TextInput::class, name: "email", params: [ + 'class' => "form-control", + 'type' => "email", + 'placeholder' => 'test@mail.ru', + 'value' => $model->email ?? '' +]) + ->setLabel("Email") + ->render(); + +if (!empty($model->user_photo)){ + echo "
"; +} +$form->field(class: \itguild\forms\inputs\File::class, name: "user_photo", params: [ + 'class' => "form-control", + 'value' => $model->user_photo ?? '' +]) + ->setLabel("Фото профиля") + ->render(); + +$entityRelations = new \kernel\EntityRelation(); +if (!isset($model)) { + $model = new User(); +} +$entityRelations->renderEntityAdditionalPropertyFormBySlug("user", $model); +?> +
+
+ field(\itguild\forms\inputs\Button::class, name: "btn-submit", params: [ + 'class' => "btn btn-primary ", + 'value' => 'Отправить', + 'typeInput' => 'submit' + ]) + ->render(); + ?> +
+
+ field(\itguild\forms\inputs\Button::class, name: "btn-reset", params: [ + 'class' => "btn btn-warning", + 'value' => 'Сбросить', + 'typeInput' => 'reset' + ]) + ->render(); + ?> +
+
+endForm(); diff --git a/kernel/modules/user/views/view_profile.php b/kernel/modules/user/views/view_profile.php new file mode 100644 index 0000000..b9b027f --- /dev/null +++ b/kernel/modules/user/views/view_profile.php @@ -0,0 +1,56 @@ + ["class" => "table table-bordered", "border" => "2"], + 'baseUrl' => "/admin/user", +])); +$table->beforePrint(function () use ($user) { + $btn = IconBtnEditWidget::create(['url' => '/admin/user/profile/edit'])->run(); + $btn .= IconBtnDeleteWidget::create(['url' => '/admin/user/delete/' . $user->id])->run(); + return $btn; +}); + +$entityRelation = new \kernel\EntityRelation(); +$additionals = $entityRelation->getEntityAdditionalProperty("user", $user); + +foreach ($additionals as $key => $additional) { + $table->addRow($key, function () use ($additional) { + return $additional; + }, ['after' => 'email']); +} + +$table->rows([ + 'user_photo' => function ($data) { + return $data ? "" : ""; + }, + '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(); \ No newline at end of file diff --git a/kernel/services/AdminThemeService.php b/kernel/services/AdminThemeService.php index f8d5aea..0d41286 100644 --- a/kernel/services/AdminThemeService.php +++ b/kernel/services/AdminThemeService.php @@ -82,12 +82,22 @@ class AdminThemeService return $info; } - public function getAdminThemeInfoBySlug(string $slug) + public function getAdminThemeInfoBySlug(string $slug): false|array|string { - // TODO + $dirs = $this->getAdminThemeDirs(); + foreach ($dirs as $dir) { + foreach (new DirectoryIterator($dir) as $fileInfo) { + if ($fileInfo->isDot()) continue; + if ($this->getAdminThemeInfo($fileInfo->getPathname())['slug'] === $slug) { + return $this->getAdminThemeInfo($fileInfo->getPathname()); + } + } + } + + return false; } - public function isInstall(string $slug): bool + public function getAdminThemeDirs(): array { $adminThemePaths = Option::where("key", "admin_theme_paths")->first(); $dirs = []; @@ -97,6 +107,12 @@ class AdminThemeService $dirs[] = getConst($p); } } + return $dirs; + } + + public function isInstall(string $slug): bool + { + $dirs = $this->getAdminThemeDirs(); foreach ($dirs as $dir) { foreach (new DirectoryIterator($dir) as $fileInfo) { if ($fileInfo->isDot()) continue; @@ -116,8 +132,7 @@ class AdminThemeService $modulesInfo = json_decode($modulesInfo->getBody()->getContents(), true); - $themeInfo = $this->getAdminThemeInfo($slug); -// Debug::dd($themeInfo); + $themeInfo = $this->getAdminThemeInfoBySlug($slug); foreach ($modulesInfo as $mod) { if ($mod['slug'] === $themeInfo['slug'] && $mod['version'] === $themeInfo['version']) { return true; @@ -195,11 +210,20 @@ class AdminThemeService $this->setActiveAdminTheme(KERNEL_ADMIN_THEMES_DIR . '/default'); } $fileHelper = new Files(); - if (file_exists(ROOT_DIR . $path)) { - $fileHelper->recursiveRemoveDir(ROOT_DIR . $path); + if (file_exists($path)) { + $fileHelper->recursiveRemoveDir($path); } if (file_exists(RESOURCES_DIR . '/' . $themeInfo['slug'])) { $fileHelper->recursiveRemoveDir(RESOURCES_DIR . '/' . $themeInfo['slug']); } } + + public function update(string $path): bool + { + if ($this->install($path)) { + return true; + } + + return false; + } } \ No newline at end of file diff --git a/resources/custom/scss/bootstrap/_images.scss b/resources/custom/scss/bootstrap/_images.scss index 5986757..cb9795e 100755 --- a/resources/custom/scss/bootstrap/_images.scss +++ b/resources/custom/scss/bootstrap/_images.scss @@ -1,9 +1,9 @@ -// Responsive default_user_photo (ensure default_user_photo don't scale beyond their parents) +// Responsive images (ensure images don't scale beyond their parents) // // This is purposefully opt-in via an explicit class rather than being the default for all ``s. -// We previously tried the "default_user_photo are responsive by default" approach in Bootstrap v2, +// We previously tried the "images are responsive by default" approach in Bootstrap v2, // and abandoned it in Bootstrap v3 because it breaks lots of third-party widgets (including Google Maps) -// which weren't expecting the default_user_photo within themselves to be involuntarily resized. +// which weren't expecting the images within themselves to be involuntarily resized. // See also https://github.com/twbs/bootstrap/issues/18178 .img-fluid { @include img-fluid; diff --git a/resources/custom/scss/bootstrap/_reboot.scss b/resources/custom/scss/bootstrap/_reboot.scss index 3a55596..c55d42e 100755 --- a/resources/custom/scss/bootstrap/_reboot.scss +++ b/resources/custom/scss/bootstrap/_reboot.scss @@ -249,7 +249,7 @@ figure { img { vertical-align: middle; - border-style: none; // Remove the border on default_user_photo inside links in IE 10-. + border-style: none; // Remove the border on images inside links in IE 10-. } svg { diff --git a/resources/custom/scss/bootstrap/mixins/_image.scss b/resources/custom/scss/bootstrap/mixins/_image.scss index e6ee7a7..a76a608 100755 --- a/resources/custom/scss/bootstrap/mixins/_image.scss +++ b/resources/custom/scss/bootstrap/mixins/_image.scss @@ -5,12 +5,12 @@ // Responsive image // -// Keep default_user_photo from scaling beyond the width of their parents. +// Keep images from scaling beyond the width of their parents. @mixin img-fluid { // Part 1: Set a maximum relative to the parent max-width: 100%; - // Part 2: Override the height to auto, otherwise default_user_photo will be stretched + // Part 2: Override the height to auto, otherwise images will be stretched // when setting a width and height attribute on the img element. height: auto; }