diff --git a/app/modules/card/CardModule.php b/app/modules/card/CardModule.php new file mode 100644 index 0000000..9d2d86e --- /dev/null +++ b/app/modules/card/CardModule.php @@ -0,0 +1,8 @@ +paymentType(2)->bank(323, 42, 75)->client(15); + + return CardNumber::generate($customer, $formatter); + } + +} \ No newline at end of file diff --git a/app/modules/card/manifest.json b/app/modules/card/manifest.json new file mode 100644 index 0000000..eaaf81e --- /dev/null +++ b/app/modules/card/manifest.json @@ -0,0 +1,11 @@ +{ + "name": "Card", + "version": "0.1", + "author": "ITGuild", + "slug": "card", + "description": "Card module", + "module_class": "app\\modules\\card\\CardModule", + "module_class_file": "{APP}/modules/card/CardModule.php", + "routs": "routs/card.php", + "migration_path": "migrations" +} \ No newline at end of file diff --git a/app/modules/card/routs/card.php b/app/modules/card/routs/card.php new file mode 100644 index 0000000..cd5f47e --- /dev/null +++ b/app/modules/card/routs/card.php @@ -0,0 +1,15 @@ +group(["prefix" => "admin"], function (CgRouteCollector $router) { + App::$collector->group(["before" => "auth"], function (RouteCollector $router) { + App::$collector->group(["prefix" => "card"], function (CGRouteCollector $router) { +// App::$collector->get('/test', [\app\modules\card\controllers\CardController::class, 'actionTestCard']); + }); + }); +}); \ No newline at end of file diff --git a/app/modules/tgbot/TgbotModule.php b/app/modules/tgbot/TgbotModule.php new file mode 100644 index 0000000..d9f814b --- /dev/null +++ b/app/modules/tgbot/TgbotModule.php @@ -0,0 +1,12 @@ +beginForm(isset($model) ? "/admin/tg-bot/edit/" . $model->id : "/admin/tg-bot"); + +$form->field(class: \itguild\forms\inputs\TextInput::class, name: "bot_id", params: [ + 'class' => "form-control", + 'placeholder' => 'Bot ID', + 'value' => $model->bot_id ?? '' +]) + ->setLabel("Bot ID") + ->render(); + +$form->field(class: \itguild\forms\inputs\TextInput::class, name: "dialog_id", params: [ + 'class' => "form-control", + 'placeholder' => 'Dialog ID', + 'value' => $model->dialog_id ?? '' +]) + ->setLabel("Dialog ID") + ->render(); + +$form->field(class: \itguild\forms\inputs\TextInput::class, name: "username", params: [ + 'class' => "form-control", + 'placeholder' => 'Username', + 'value' => $model->username ?? '' +]) + ->setLabel("Username") + ->render(); + +$form->field(class: \itguild\forms\inputs\TextInput::class, name: "first_name", params: [ + 'class' => "form-control", + 'placeholder' => 'First name', + 'value' => $model->first_name ?? '' +]) + ->setLabel("First name") + ->render(); + +$form->field(class: \itguild\forms\inputs\TextInput::class, name: "last_name", params: [ + 'class' => "form-control", + 'placeholder' => 'Last name', + 'value' => $model->last_name ?? '' +]) + ->setLabel("Last name") + ->render(); + + +$form->field(\itguild\forms\inputs\Select::class, 'status', [ + 'class' => "form-control", + 'value' => $model->status ?? '' +]) + ->setLabel("Статус") + ->setOptions(Tgbot::getStatus()) + ->render(); +?> +
+
+ 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/app/modules/tgbot/views/tgbot/index.php b/app/modules/tgbot/views/tgbot/index.php new file mode 100644 index 0000000..c1c59a0 --- /dev/null +++ b/app/modules/tgbot/views/tgbot/index.php @@ -0,0 +1,41 @@ +setTitle("Список существующих диалогов"); + +$table = new ListEloquentTable(new EloquentDataProvider(\app\modules\tgbot\models\Tgbot::class, [ + 'currentPage' => $page_number, + 'perPage' => 8, + 'params' => ["class" => "table table-bordered", "border" => "2"], + 'baseUrl' => "/admin/tg-bot", +])); + +$table->beforePrint(function () { + return PrimaryBtn::create("Создать", "/admin/tg-bot/create")->fetch(); +}); + +$table->columns([ + "status" => [ + "value" => function ($cell) { + return \app\modules\tgbot\models\Tgbot::getStatus()[$cell]; + }] +]); + + +$table->addAction(\kernel\IGTabel\action_column\ViewActionColumn::class); +$table->addAction(\kernel\IGTabel\action_column\DeleteActionColumn::class); +$table->addAction(\kernel\IGTabel\action_column\EditActionColumn::class); +$table->create(); +$table->render(); \ No newline at end of file diff --git a/app/modules/tgbot/views/tgbot/view.php b/app/modules/tgbot/views/tgbot/view.php new file mode 100644 index 0000000..1fcbda1 --- /dev/null +++ b/app/modules/tgbot/views/tgbot/view.php @@ -0,0 +1,30 @@ + ["class" => "table table-bordered", "border" => "2"], + 'baseUrl' => "/admin/tg-bot", +])); +$table->beforePrint(function () use ($tg) { + $btn = PrimaryBtn::create("Список", "/admin/tg-bot")->fetch(); + $btn .= SuccessBtn::create("Редактировать", "/admin/tg-bot/update/" . $tg->id)->fetch(); + $btn .= DangerBtn::create("Удалить", "/admin/tg-bot/delete/" . $tg->id)->fetch(); + return $btn; +}); +$table->rows([ + 'status' => (function ($data) { + return \app\modules\tgbot\models\Tgbot::getStatus()[$data]; + }) +]); +$table->create(); +$table->render(); \ No newline at end of file diff --git a/composer.json b/composer.json index 84a938a..37d7975 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,10 @@ "guzzlehttp/guzzle": "^7.9", "phpmailer/phpmailer": "^6.9", "zircote/swagger-php": "^4.11", - "doctrine/annotations": "^2.0" + "doctrine/annotations": "^2.0", + "ext-gd": "*", + "dragon-code/card-number": "^1.6", + "endroid/qr-code": "^6.0" }, "autoload": { "psr-4": { diff --git a/kernel/FileUpload.php b/kernel/FileUpload.php index 7a1705b..a61893c 100644 --- a/kernel/FileUpload.php +++ b/kernel/FileUpload.php @@ -36,7 +36,9 @@ class FileUpload $newFileName = md5(time() . $this->fileName) . '.' . $this->fileExtension; if (in_array($this->fileExtension, $this->allowedFileExtensions)) { $this->uploadDir = $uploadDir . mb_substr($newFileName, 0, 2) . '/' . mb_substr($newFileName, 2, 2) . '/'; - mkdir(ROOT_DIR . $this->uploadDir, 0777, true); + $oldMask = umask(0); + mkdir(ROOT_DIR . $this->uploadDir, 0775, true); + umask($oldMask); $uploadFileDir = ROOT_DIR . $this->uploadDir; $this->destPath = $uploadFileDir . $newFileName; $this->uploadFile = $this->uploadDir . $newFileName; @@ -49,7 +51,9 @@ class FileUpload } else { if (in_array($this->fileExtension, $this->allowedFileExtensions)) { $this->uploadDir = $uploadDir; - mkdir(ROOT_DIR . $this->uploadDir, 0777, true); + $oldMask = umask(0); + mkdir(ROOT_DIR . $this->uploadDir, 0775, true); + umask($oldMask); $uploadFileDir = ROOT_DIR . $this->uploadDir; $this->destPath = $uploadFileDir . $this->fileName; $this->uploadFile = $this->uploadDir . $this->fileName; diff --git a/kernel/RestController.php b/kernel/RestController.php index 2d7596d..99ba279 100644 --- a/kernel/RestController.php +++ b/kernel/RestController.php @@ -17,13 +17,32 @@ class RestController return []; } + protected function filters(): array + { + return []; + } + #[NoReturn] public function actionIndex(): void { $request = new Request(); + $get = $request->get(); $page = $request->get('page') ?? 1; $perPage = $request->get('per_page') ?? 10; $query = $this->model->query(); + if ($this->filters()) { + foreach ($this->filters() as $filter){ + if (key_exists($filter, $get)){ + if (is_numeric($get[$filter])){ + $query->where($filter, $get[$filter]); + } + elseif (is_string($get[$filter])){ + $query->where($filter,'like', '%' . $get[$filter] . '%'); + } + } + } + } + if ($page > 1) { $query->skip(($page - 1) * $perPage)->take($perPage); } else { @@ -31,7 +50,7 @@ class RestController } $expand = $this->expand(); - $expandParams = explode( ",", $request->get('expand') ?? ""); + $expandParams = explode(",", $request->get('expand') ?? ""); $finalExpand = array_intersect($expandParams, $expand); if ($finalExpand) { $res = $query->get()->load($finalExpand)->toArray(); @@ -46,14 +65,14 @@ class RestController { $expand = $this->expand(); $request = new Request(); - $expandParams = explode( ",", $request->get('expand') ?? ""); + $expandParams = explode(",", $request->get('expand') ?? ""); $model = $this->model->where("id", $id)->first(); $finalExpand = array_intersect($expandParams, $expand); - if ($finalExpand){ + if ($finalExpand) { $model->load($finalExpand); } $res = []; - if ($model){ + if ($model) { $res = $model->toArray(); } @@ -64,7 +83,7 @@ class RestController { $model = $this->model->where("id", $id)->first(); $res = []; - if ($model){ + if ($model) { $res = $model->toArray(); } @@ -78,7 +97,7 @@ class RestController { $request = new Request(); $data = $request->post(); - foreach ($this->model->getFillable() as $item){ + foreach ($this->model->getFillable() as $item) { $this->model->{$item} = $data[$item] ?? null; } $this->model->save(); @@ -93,8 +112,8 @@ class RestController $model = $this->model->where('id', $id)->first(); - foreach ($model->getFillable() as $item){ - if (!empty($data[$item])){ + foreach ($model->getFillable() as $item) { + if (!empty($data[$item])) { $model->{$item} = $data[$item] ?? null; } } @@ -117,5 +136,4 @@ class RestController } - } \ No newline at end of file diff --git a/kernel/admin_themes/default/layout/main.php b/kernel/admin_themes/default/layout/main.php index db00106..98be7fb 100644 --- a/kernel/admin_themes/default/layout/main.php +++ b/kernel/admin_themes/default/layout/main.php @@ -26,10 +26,13 @@
-
- - -
+
+ + +
- +
diff --git a/kernel/app_modules/card/CardModule.php b/kernel/app_modules/card/CardModule.php new file mode 100644 index 0000000..fbe306d --- /dev/null +++ b/kernel/app_modules/card/CardModule.php @@ -0,0 +1,67 @@ +menuService = new MenuService(); + $this->consoleService = new ConsoleService(); + $this->migrationService = new MigrationService(); + } + + /** + * @throws \Exception + */ + public function init(): void + { + $this->migrationService->runAtPath("{KERNEL_APP_MODULES}/card/migrations"); + + $this->consoleService->runComposerRequire("dragon-code/card-number"); + $this->consoleService->runComposerRequire("endroid/qr-code"); + + $this->menuService->createItem([ + "label" => "Card", + "url" => "/admin/card", + "slug" => "card", + ]); + + $this->menuService->createItem([ + "label" => "Список карт", + "url" => "/admin/card", + "slug" => "card_list", + "parent_slug" => "card" + ]); + + $this->menuService->createItem([ + "label" => "Шаблоны карт", + "url" => "/admin/card_template", + "slug" => "card_template", + "parent_slug" => "card" + ]); + } + + /** + * @throws \Exception + */ + public function deactivate(): void + { + $this->menuService->removeItemBySlug("card"); + $this->menuService->removeItemBySlug("card_list"); + $this->menuService->removeItemBySlug("card_template"); + $this->migrationService->rollbackAtPath("{KERNEL_APP_MODULES}/card/migrations"); + $this->consoleService->runComposerRemove("dragon-code/card-number"); + $this->consoleService->runComposerRemove("endroid/qr-code"); + } +} \ No newline at end of file diff --git a/kernel/app_modules/card/controllers/CardController.php b/kernel/app_modules/card/controllers/CardController.php new file mode 100644 index 0000000..c2e0657 --- /dev/null +++ b/kernel/app_modules/card/controllers/CardController.php @@ -0,0 +1,101 @@ +cgView->viewPath = KERNEL_APP_MODULES_DIR . "/card/views/"; + $this->cardService = new CardService(); + } + + public function actionCreate(): void + { + $this->cgView->render("form.php"); + } + + #[NoReturn] public function actionAdd(): void + { + $cardForm = new CreateCardForm(); + $cardForm->load($_REQUEST); + if ($cardForm->validate()){ + $card = $this->cardService->create($cardForm); + if ($card){ + $this->redirect("/admin/card/" . $card->id); + } + } + Flash::setMessage("error", $cardForm->getErrorsStr()); + $this->redirect("/admin/card/create"); + } + + public function actionIndex($page_number = 1): void + { + $this->cgView->render("index.php", ['page_number' => $page_number]); + } + + /** + * @throws Exception + */ + public function actionView($id): void + { + $card = Card::find($id); + + if (!$card){ + throw new Exception(message: "The card not found"); + } + $this->cgView->render("view.php", ['card' => $card]); + } + + /** + * @throws Exception + */ + public function actionUpdate($id): void + { + $model = Card::find($id); + if (!$model){ + throw new Exception(message: "The card not found"); + } + + $this->cgView->render("form.php", ['model' => $model]); + } + + /** + * @throws Exception + */ + public function actionEdit($id): void + { + $card = Card::find($id); + if (!$card){ + throw new Exception(message: "The card not found"); + } + $cardForm = new CreateCardForm(); + $cardService = new CardService(); + $cardForm->load($_REQUEST); + if ($cardForm->validate()) { + $card = $cardService->update($cardForm, $card); + if ($card) { + $this->redirect("/admin/card/" . $card->id); + } + } + $this->redirect("/admin/card/update/" . $id); + } + + #[NoReturn] public function actionDelete($id): void + { + $card = Card::find($id)->first(); + $card->delete(); + $this->redirect("/admin/card/"); + } +} \ No newline at end of file diff --git a/kernel/app_modules/card/controllers/CardRestController.php b/kernel/app_modules/card/controllers/CardRestController.php new file mode 100644 index 0000000..8d0c781 --- /dev/null +++ b/kernel/app_modules/card/controllers/CardRestController.php @@ -0,0 +1,25 @@ +model = new Card(); + } + + protected function expand(): array + { + return ["cardFile"]; + } + + protected function filters(): array + { + return ['pin', 'user_id', 'payment_type', 'bank_id', 'info', 'program', 'username', 'status']; + } +} \ No newline at end of file diff --git a/kernel/app_modules/card/controllers/CardTemplateController.php b/kernel/app_modules/card/controllers/CardTemplateController.php new file mode 100644 index 0000000..7ade0f1 --- /dev/null +++ b/kernel/app_modules/card/controllers/CardTemplateController.php @@ -0,0 +1,125 @@ +cgView->viewPath = KERNEL_APP_MODULES_DIR . "/card/views/card_template/"; + $this->cardService = new CardService(); + $this->cardTemplateService = new CardTemplateService(); + } + + public function actionCreate(): void + { + $this->cgView->render("form.php"); + } + + #[NoReturn] public function actionAdd(): void + { + $cardForm = new CreateCardTemplateForm(); + $cardForm->load($_REQUEST); + + if (isset($_FILES['path']) && $_FILES['path']['error'] === UPLOAD_ERR_OK) { + $file = new FileUpload($_FILES['path'], ['jpg', 'jpeg', 'png']); + $file->upload(); + $cardForm->setItem('path', $file->getUploadFile()); + } + + if ($cardForm->validate()){ + $cardTemplate = $this->cardTemplateService->create($cardForm); + if ($cardTemplate){ + $this->redirect("/admin/card_template/" . $cardTemplate->id); + } + } + + Flash::setMessage("error", $cardForm->getErrorsStr()); + $this->redirect("/admin/card_template/create"); + } + + public function actionIndex($page_number = 1): void + { + $this->cgView->render("index.php", ['page_number' => $page_number]); + } + + /** + * @throws Exception + */ + public function actionView($id): void + { + $card = CardTemplate::find($id); + + if (!$card){ + throw new Exception(message: "The card template not found"); + } + $this->cgView->render("view.php", ['card' => $card]); + } + + /** + * @throws Exception + */ + public function actionUpdate($id): void + { + $model = CardTemplate::find($id); + if (!$model){ + throw new Exception(message: "The card template not found"); + } + + $this->cgView->render("form.php", ['model' => $model]); + } + + /** + * @throws Exception + */ + public function actionEdit($id): void + { + $cardTemplate = CardTemplate::find($id); + if (!$cardTemplate){ + throw new Exception(message: "The card not found"); + } + $cardTemplateForm = new CreateCardTemplateForm(); + $cardTemplateForm->load($_REQUEST); + + if (isset($_FILES['path']) && $_FILES['path']['error'] === UPLOAD_ERR_OK) { + $file = new FileUpload($_FILES['path'], ['jpg', 'jpeg', 'png']); + $file->upload(); + $cardTemplateForm->setItem('path', $file->getUploadFile()); + } + + if ($cardTemplateForm->validateForUpdate()) { + $card = $this->cardTemplateService->update($cardTemplateForm, $cardTemplate); + if ($card) { + $this->redirect("/admin/card_template/" . $card->id); + } + } + Flash::setMessage("error", $cardTemplateForm->getErrorsStr()); + + $this->redirect("/admin/card_template/update/" . $id); + } + + #[NoReturn] public function actionDelete($id): void + { + $card = CardTemplate::find($id)->first(); + $card->delete(); + $this->redirect("/admin/card_template/"); + } + +} \ No newline at end of file diff --git a/kernel/app_modules/card/migrations/2024_12_23_171228_create_card_table.php b/kernel/app_modules/card/migrations/2024_12_23_171228_create_card_table.php new file mode 100644 index 0000000..a226858 --- /dev/null +++ b/kernel/app_modules/card/migrations/2024_12_23_171228_create_card_table.php @@ -0,0 +1,61 @@ +schema->create('card_template', function (Blueprint $table) { + $table->increments('id')->from(105545); + $table->string('path')->nullable(false); + $table->string('title')->nullable(false); + + $table->integer('status')->default(1); + $table->timestamps(); + }); + + \kernel\App::$db->schema->create('card_file', function (Blueprint $table) { + $table->increments('id'); + $table->string('file')->nullable(false); + $table->integer('card_id')->nullable(false); + + $table->integer('status')->default(1); + $table->timestamps(); + }); + + \kernel\App::$db->schema->create('card', function (Blueprint $table) { + $table->increments('id')->from(105545); + $table->integer('user_id')->nullable(false); + $table->integer('balance')->nullable(false)->default(0); + $table->integer('payment_type')->nullable(false); + $table->integer('bank_id')->nullable(false); + $table->integer('info')->nullable(false); + $table->integer('program')->nullable(false); + $table->integer('cvc')->nullable(false); + $table->integer('pin')->nullable(false); + $table->string('username')->nullable(); + $table->integer('card_template_id')->nullable(); + $table->integer('card_file_id')->nullable(); + + $table->integer('status')->default(1); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + \kernel\App::$db->schema->dropIfExists('card'); + \kernel\App::$db->schema->dropIfExists('card_template'); + \kernel\App::$db->schema->dropIfExists('card_file'); + } +}; diff --git a/kernel/app_modules/card/models/Card.php b/kernel/app_modules/card/models/Card.php new file mode 100644 index 0000000..3627efb --- /dev/null +++ b/kernel/app_modules/card/models/Card.php @@ -0,0 +1,85 @@ + 'Заголовок', + // 'entity' => 'Сущность', + // 'slug' => 'Slug', + // 'status' => 'Статус', + // ] + + return [ + 'user_id' => 'ID пользователя', + 'payment_type' => 'Платежная система', + 'balance' => 'Баланс', + 'bank_id' => 'ID банка', + 'info' => 'Информация о банке', + 'program' => 'Программа', + 'cvc' => 'CVC', + 'pin' => 'PIN', + 'username' => 'Username', + 'card_template_id' => 'Шаблон', + 'card_file_id' => 'Карта', + 'status' => 'Статус', + ]; + } + + /** + * @return string[] + */ + public static function getStatus(): array + { + return [ + self::DISABLE_STATUS => "Не активный", + self::ACTIVE_STATUS => "Активный", + ]; + } + + public function cardTemplate(): \Illuminate\Database\Eloquent\Relations\BelongsTo + { + return $this->belongsTo(CardTemplate::class); + } + + public function cardFile(): \Illuminate\Database\Eloquent\Relations\BelongsTo + { + return $this->belongsTo(CardFile::class); + } + +// public function user(): \Illuminate\Database\Eloquent\Relations\BelongsTo +// { +// return $this->belongsTo(User::class); +// } + +} \ No newline at end of file diff --git a/kernel/app_modules/card/models/CardFile.php b/kernel/app_modules/card/models/CardFile.php new file mode 100644 index 0000000..9a31111 --- /dev/null +++ b/kernel/app_modules/card/models/CardFile.php @@ -0,0 +1,51 @@ + 'Заголовок', + // 'entity' => 'Сущность', + // 'slug' => 'Slug', + // 'status' => 'Статус', + // ] + + return [ + 'file' => 'Файл карты', + 'card_id' => 'Карта', + 'status' => 'Статус', + ]; + } + + /** + * @return string[] + */ + public static function getStatus(): array + { + return [ + self::DISABLE_STATUS => "Не активный", + self::ACTIVE_STATUS => "Активный", + ]; + } + +} \ No newline at end of file diff --git a/kernel/app_modules/card/models/CardTemplate.php b/kernel/app_modules/card/models/CardTemplate.php new file mode 100644 index 0000000..3a8cb47 --- /dev/null +++ b/kernel/app_modules/card/models/CardTemplate.php @@ -0,0 +1,50 @@ + 'Заголовок', + // 'entity' => 'Сущность', + // 'slug' => 'Slug', + // 'status' => 'Статус', + // ] + + return [ + 'path' => 'Шаблон', + 'title' => 'Название', + 'status' => 'Статус', + ]; + } + + /** + * @return string[] + */ + public static function getStatus(): array + { + return [ + self::DISABLE_STATUS => "Не активный", + self::ACTIVE_STATUS => "Активный", + ]; + } + +} \ No newline at end of file diff --git a/kernel/app_modules/card/models/forms/CreateCardForm.php b/kernel/app_modules/card/models/forms/CreateCardForm.php new file mode 100644 index 0000000..af91fb6 --- /dev/null +++ b/kernel/app_modules/card/models/forms/CreateCardForm.php @@ -0,0 +1,36 @@ + 'required|min-str-len:5|max-str-len:30', + // 'entity' => 'required', + // 'slug' => '', + // 'status' => '' + // ]; + return [ + 'user_id' => 'required|alpha-numeric', + 'payment_type' => 'required|alpha-numeric', + 'balance' => 'alpha-numeric', + 'bank_id' => 'required|alpha-numeric', + 'info' => 'required|alpha-numeric', + 'program' => 'required|alpha-numeric', + 'cvc' => 'required|alpha-numeric', + 'pin' => 'required|alpha-numeric', + 'username' => 'required|min-str-len:5|max-str-len:20', + 'card_template_id' => 'required|alpha-numeric', + 'card_file_id' => 'alpha-numeric', + 'status' => '', + ]; + } + +} \ No newline at end of file diff --git a/kernel/app_modules/card/models/forms/CreateCardTemplateForm.php b/kernel/app_modules/card/models/forms/CreateCardTemplateForm.php new file mode 100644 index 0000000..02b9f58 --- /dev/null +++ b/kernel/app_modules/card/models/forms/CreateCardTemplateForm.php @@ -0,0 +1,38 @@ + 'required|min-str-len:5|max-str-len:30', + // 'entity' => 'required', + // 'slug' => '', + // 'status' => '' + // ]; + return [ + 'path' => 'required|min-str-len:4', + 'title' => 'required', + 'status' => '' + ]; + } + + public function rulesForUpdate(): array + { + return [ + 'path' => '', + 'title' => 'required', + 'status' => '' + ]; + } + + + +} \ No newline at end of file diff --git a/kernel/app_modules/card/routs/card.php b/kernel/app_modules/card/routs/card.php new file mode 100644 index 0000000..96a39c4 --- /dev/null +++ b/kernel/app_modules/card/routs/card.php @@ -0,0 +1,41 @@ +group(["prefix" => "admin"], function (CgRouteCollector $router) { + App::$collector->group(["before" => "auth"], function (RouteCollector $router) { + App::$collector->group(["prefix" => "card"], function (CGRouteCollector $router) { + App::$collector->get('/', [\app\modules\card\controllers\CardController::class, 'actionIndex']); + App::$collector->get('/page/{page_number}', [\app\modules\card\controllers\CardController::class, 'actionIndex']); + App::$collector->get('/create', [\app\modules\card\controllers\CardController::class, 'actionCreate']); + App::$collector->post("/", [\app\modules\card\controllers\CardController::class, 'actionAdd']); + App::$collector->get('/{id}', [\app\modules\card\controllers\CardController::class, 'actionView']); + App::$collector->any('/update/{id}', [\app\modules\card\controllers\CardController::class, 'actionUpdate']); + App::$collector->any("/edit/{id}", [\app\modules\card\controllers\CardController::class, 'actionEdit']); + App::$collector->get('/delete/{id}', [\app\modules\card\controllers\CardController::class, 'actionDelete']); + }); + }); +}); + +App::$collector->group(["prefix" => "admin"], function (CgRouteCollector $router) { + App::$collector->group(["before" => "auth"], function (RouteCollector $router) { + App::$collector->group(["prefix" => "card_template"], function (CGRouteCollector $router) { + App::$collector->get('/', [\kernel\app_modules\card\controllers\CardTemplateController::class, 'actionIndex']); + App::$collector->get('/page/{page_number}', [\kernel\app_modules\card\controllers\CardTemplateController::class, 'actionIndex']); + App::$collector->get('/create', [\kernel\app_modules\card\controllers\CardTemplateController::class, 'actionCreate']); + App::$collector->post("/", [\kernel\app_modules\card\controllers\CardTemplateController::class, 'actionAdd']); + App::$collector->get('/{id}', [\kernel\app_modules\card\controllers\CardTemplateController::class, 'actionView']); + App::$collector->any('/update/{id}', [\kernel\app_modules\card\controllers\CardTemplateController::class, 'actionUpdate']); + App::$collector->any("/edit/{id}", [\kernel\app_modules\card\controllers\CardTemplateController::class, 'actionEdit']); + App::$collector->get('/delete/{id}', [\kernel\app_modules\card\controllers\CardTemplateController::class, 'actionDelete']); + }); + }); +}); + +App::$collector->group(["prefix" => "api"], function (CgRouteCollector $router){ + App::$collector->group(['before' => 'bearer'], function (CgRouteCollector $router){ + $router->rest("card", [\kernel\app_modules\card\controllers\CardRestController::class]); + }); +}); \ No newline at end of file diff --git a/kernel/app_modules/card/services/CardFileService.php b/kernel/app_modules/card/services/CardFileService.php new file mode 100644 index 0000000..8d70cbd --- /dev/null +++ b/kernel/app_modules/card/services/CardFileService.php @@ -0,0 +1,113 @@ +file = $cardFile; + $model->status = CardFile::ACTIVE_STATUS; + $model->card_id = $card->id; + // $model->slug = Slug::createSlug($form_model->getItem('title'), Card::class); // Генерация уникального slug + + if ($model->save()) { + return $model; + } + + return false; + } + + public function update(FormModel $form_model, CardFile $cardTemplate): false|CardFile + { + // Пример обновления: + $cardTemplate->file = $form_model->getItem('file'); + $cardTemplate->card_id = $form_model->getItem('card_id'); + $cardTemplate->status = $form_model->getItem('status'); + + if ($cardTemplate->save()) { + return $cardTemplate; + } + + return false; + } + + public static function createCardPNG(Card $card): false|string + { + if ($card->cardTemplate) { + $formatter = BankFormatter::create(); + + $customer = BankFactory::create()->paymentType($card->payment_type)->bank($card->bank_id, $card->info, $card->program)->client($card->id); + + $cardNumber = CardNumber::generate($customer, $formatter); + //Card + $newFileName = md5(time() . $card->id) . '.png'; + $uploadDir = "/resources/cards/"; + $uploadDirUri = $uploadDir . mb_substr($newFileName, 0, 2) . '/' . mb_substr($newFileName, 2, 2) . '/'; + $oldMask = umask(0); + mkdir(ROOT_DIR . $uploadDirUri, 0775, true); + umask($oldMask); + $uploadFileDir = ROOT_DIR . $uploadDirUri; + + $img = ROOT_DIR . "/" . $card->cardTemplate->path; // Ссылка на файл + $font = RESOURCES_DIR . "/tmp/arialmt.ttf"; // Ссылка на шрифт + + $qr = self::createQr($card->id); + $qrImg = new ImageGD($qr->getDataUri()); + $img = new ImageGD($img); + + $pngFilePath = RESOURCES_DIR . "/tmp/" . $card->id . ".png"; + $pngFileUrl = "/resources/tmp/" . $card->id . ".png"; + + $img->addText(font_size: 14, degree: 0, x: 15, y: 25, color: "#000000", font: $font, text: "KO coin"); + $img->addText(font_size: 14, degree: 0, x: 15, y: 45, color: "#000000", font: $font, text: "BGroup\ITGuild"); + $img->addText(font_size: 14, degree: 0, x: 15, y: 65, color: "#000000", font: $font, text: $card->cardTemplate->title . " card"); + $img->addText(font_size: 18, degree: 0, x: 15, y: 180, color: "#000000", font: $font, text: $cardNumber); + $img->addText(font_size: 12, degree: 0, x: 15, y: 200, color: "#000000", font: $font, text: $card->username); + $img->addImg($qrImg->getImg(), 200, 15, 0, 0, 124, 124, 100); + $img->save($uploadFileDir . $newFileName); + + return $uploadDirUri . $newFileName; + } + + return false; + } + + public static function createQr(string|int $text): \Endroid\QrCode\Writer\Result\ResultInterface + { + $writer = new PngWriter(); + + $qrCode = new QrCode( + data: $text, + encoding: new Encoding('UTF-8'), + errorCorrectionLevel: ErrorCorrectionLevel::Low, + size: 120, + margin: 2, + roundBlockSizeMode: RoundBlockSizeMode::Margin, + foregroundColor: new Color(0, 0, 0), + backgroundColor: new Color(255, 255, 255) + ); + + return $writer->write($qrCode); + } + +} \ No newline at end of file diff --git a/kernel/app_modules/card/services/CardService.php b/kernel/app_modules/card/services/CardService.php new file mode 100644 index 0000000..160a48b --- /dev/null +++ b/kernel/app_modules/card/services/CardService.php @@ -0,0 +1,108 @@ +cardFileService = new CardFileService(); + } + + public function create(FormModel $form_model): false|Card + { + $model = new Card(); + // Пример заполнения: + $model->user_id = $form_model->getItem('user_id'); + $model->payment_type = $form_model->getItem('payment_type') ?? 2; + $model->bank_id = $form_model->getItem('bank_id') ?? 232; + $model->info = $form_model->getItem('info') ?? 42; + $model->program = $form_model->getItem('program') ?? 74; + $model->balance = $form_model->getItem('balance') ?? 0; + $model->cvc = $form_model->getItem('cvc'); + $model->pin = $form_model->getItem('pin'); + $model->username = $form_model->getItem('username'); + $model->card_template_id = $form_model->getItem('card_template_id'); + $model->status = $form_model->getItem('status'); + + if ($model->save()) { + $cardFile = $this->cardFileService->create($model); + if ($cardFile) { + $model->card_file_id = $cardFile->id; + $model->save(); + } + + return $model; + } + + return false; + } + + public function update(FormModel $form_model, Card $card): false|Card + { + // Пример обновления: + $card->user_id = $form_model->getItem('user_id'); + $card->payment_type = $form_model->getItem('payment_type'); + $card->bank_id = $form_model->getItem('bank_id'); + $card->info = $form_model->getItem('info'); + $card->program = $form_model->getItem('program'); + $card->balance = $form_model->getItem('balance'); + $card->cvc = $form_model->getItem('cvc'); + $card->pin = $form_model->getItem('pin'); + $card->username = $form_model->getItem('username'); + $card->status = $form_model->getItem('status'); + + if ($card->card_template_id !== (int)$form_model->getItem('card_template_id')) { + $card->card_template_id = $form_model->getItem('card_template_id'); + $cardFile = $this->cardFileService->create($card); + if ($cardFile) { + $card->card_file_id = $cardFile->id; + } + } + + if ($card->save()) { + return $card; + } + + return false; + } + + public static function createCardPNG(Card $card): false|string + { + if ($card->cardTemplate) { + $formatter = BankFormatter::create(); + + $customer = BankFactory::create()->paymentType($card->payment_type)->bank($card->bank_id, $card->info, $card->program)->client($card->id); + + $cardNumber = CardNumber::generate($customer, $formatter); + //Card + $img = ROOT_DIR . "/" . $card->cardTemplate->path; // Ссылка на файл + $font = RESOURCES_DIR . "/tmp/arialmt.ttf"; // Ссылка на шрифт + + $img = new ImageGD($img); + + $pngFilePath = RESOURCES_DIR . "/tmp/" . $card->id . ".png"; + $pngFileUrl = "/resources/tmp/" . $card->id . ".png"; + + $img->addText(font_size: 14, degree: 0, x: 15, y: 25, color: "#000000", font: $font, text: "KO coin"); + $img->addText(font_size: 14, degree: 0, x: 15, y: 45, color: "#000000", font: $font, text: "BGroup\ITGuild"); + $img->addText(font_size: 18, degree: 0, x: 15, y: 180, color: "#000000", font: $font, text: $cardNumber); + $img->addText(font_size: 12, degree: 0, x: 15, y: 200, color: "#000000", font: $font, text: $card->username); + $img->save($pngFilePath); + + return $pngFileUrl; + } + + return false; + } +} \ No newline at end of file diff --git a/kernel/app_modules/card/services/CardTemplateService.php b/kernel/app_modules/card/services/CardTemplateService.php new file mode 100644 index 0000000..88c71f6 --- /dev/null +++ b/kernel/app_modules/card/services/CardTemplateService.php @@ -0,0 +1,55 @@ +path = $form_model->getItem('path'); + $model->status = $form_model->getItem('status'); + $model->title = $form_model->getItem('title'); + // $model->slug = Slug::createSlug($form_model->getItem('title'), Card::class); // Генерация уникального slug + + if ($model->save()) { + return $model; + } + + return false; + } + + public function update(FormModel $form_model, CardTemplate $cardTemplate): false|CardTemplate + { + // Пример обновления: + $cardTemplate->title = $form_model->getItem('title'); + $cardTemplate->path = $form_model->getItem('path') ?? $cardTemplate->path; + $cardTemplate->status = $form_model->getItem('status'); + + if ($cardTemplate->save()) { + return $cardTemplate; + } + + return false; + } + + public static function getTemplatesList(): array + { + $arr = []; + foreach (CardTemplate::all()->toArray() as $cardTemplate){ + $arr[$cardTemplate['id']] = $cardTemplate['title']; + } + if (!empty($arr)) { + return $arr; + } + + return []; + } + +} \ No newline at end of file diff --git a/kernel/app_modules/card/views/card_template/form.php b/kernel/app_modules/card/views/card_template/form.php new file mode 100644 index 0000000..fffb840 --- /dev/null +++ b/kernel/app_modules/card/views/card_template/form.php @@ -0,0 +1,63 @@ +beginForm(isset($model) ? "/admin/card_template/edit/" . $model->id : "/admin/card_template", 'multipart/form-data'); + +// Пример формы: + + +$form->field(\itguild\forms\inputs\TextInput::class, 'title', [ + 'class' => "form-control", + 'placeholder' => 'Название', + 'value' => $model->title ?? '' +]) + ->setLabel("Заголовок") + ->render(); + +$form->field(\itguild\forms\inputs\File::class, 'path', [ + 'class' => "form-control", + 'placeholder' => 'Путь к файлу', + 'value' => $model->path ?? '' +]) + ->setLabel(\kernel\app_modules\card\models\CardTemplate::labels()['path']) + ->render(); + +$form->field(class: \itguild\forms\inputs\Select::class, name: "status", params: [ + 'class' => "form-control", + 'value' => $model->status ?? '' +]) + ->setLabel("Статус") + ->setOptions(\kernel\app_modules\card\models\CardTemplate::getStatus()) + ->render(); + + +?> +
+
+ 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/app_modules/card/views/card_template/index.php b/kernel/app_modules/card/views/card_template/index.php new file mode 100644 index 0000000..ecfe92f --- /dev/null +++ b/kernel/app_modules/card/views/card_template/index.php @@ -0,0 +1,81 @@ +setTitle("Список шаблонов card"); +$view->setMeta([ + 'description' => 'Список шаблонов card системы' +]); + +//Для использования таблицы с моделью, необходимо создать таблицу в базе данных +$table = new ListEloquentTable(new EloquentDataProvider(\kernel\app_modules\card\models\CardTemplate::class, [ + 'currentPage' => $page_number, + 'perPage' => 8, + 'params' => ["class" => "table table-bordered", "border" => "2"], + 'baseUrl' => "/admin/card_template" +])); + + +//$table = new \Itguild\Tables\ListJsonTable(json_encode( +// [ +// 'meta' => [ +// 'total' => 0, +// 'totalWithFilters' => 0, +// 'columns' => [ +// 'title', +// 'slug', +// 'status', +// ], +// 'perPage' => 5, +// 'currentPage' => 1, +// 'baseUrl' => '/admin/some', +// 'params' => [ +// 'class' => 'table table-bordered', +// 'border' => 2 +// ] +// ], +// 'filters' => [], +// 'data' => [], +// ] +//)); + +$table->beforePrint(function () { + return IconBtnCreateWidget::create(['url' => '/admin/card_template/create'])->run(); +}); + +$table->columns([ + 'status' => [ + 'value' => function ($data) { + return \kernel\app_modules\card\models\CardTemplate::getStatus()[$data]; + } + ], + 'path' => [ + 'value' => function ($data) { + return \kernel\helpers\Html::img($data, ['width' => '200px']); + } + ] +]); + +$table->addAction(function($row) { + return IconBtnViewWidget::create(['url' => '/admin/card_template/' . $row['id']])->run(); +}); +$table->addAction(function($row) { + return IconBtnEditWidget::create(['url' => '/admin/card_template/update/' . $row['id']])->run(); +}); +$table->addAction(function($row) { + return IconBtnDeleteWidget::create(['url' => '/admin/card_template/delete/' . $row['id']])->run(); +}); +$table->create(); +$table->render(); \ No newline at end of file diff --git a/kernel/app_modules/card/views/card_template/view.php b/kernel/app_modules/card/views/card_template/view.php new file mode 100644 index 0000000..594c7f3 --- /dev/null +++ b/kernel/app_modules/card/views/card_template/view.php @@ -0,0 +1,34 @@ + ["class" => "table table-bordered", "border" => "2"], + 'baseUrl' => "/admin/card_template", +])); + +$table->rows([ + 'status' => [ + 'value' => function ($data) { + return \kernel\app_modules\card\models\CardTemplate::getStatus()[$data]; + } + ] +]); + +$table->beforePrint(function () use ($card) { + $btn = IconBtnListWidget::create(['url' => '/admin/card_template'])->run(); + $btn .= IconBtnEditWidget::create(['url' => '/admin/card_template/update/' . $card->id])->run(); + $btn .= IconBtnDeleteWidget::create(['url' => '/admin/card_template/delete/' . $card->id])->run(); + return $btn; +}); + +$table->create(); +$table->render(); \ No newline at end of file diff --git a/kernel/app_modules/card/views/form.php b/kernel/app_modules/card/views/form.php new file mode 100644 index 0000000..ba6c749 --- /dev/null +++ b/kernel/app_modules/card/views/form.php @@ -0,0 +1,125 @@ +beginForm(isset($model) ? "/admin/card/edit/" . $model->id : "/admin/card", 'multipart/form-data'); + +// Пример формы: + +$form->field(class: \itguild\forms\inputs\Select::class, name: "user_id", params: [ + 'class' => "form-control", + 'value' => $model->user_id ?? '' +]) + ->setLabel("Пользователи") + ->setOptions(\kernel\modules\user\service\UserService::createUsernameArr()) + ->render(); + +$form->field(\itguild\forms\inputs\TextInput::class, 'username', [ + 'class' => "form-control", + 'placeholder' => 'Username', + 'value' => $model->username ?? '' +]) + ->setLabel("Username") + ->render(); + +$form->field(\itguild\forms\inputs\TextInput::class, 'balance', [ + 'class' => "form-control", + 'placeholder' => 'Баланс', + 'value' => $model->balance ?? '' +]) + ->setLabel("Баланс") + ->render(); + +$form->field(\itguild\forms\inputs\TextInput::class, 'payment_type', [ + 'class' => "form-control", + 'placeholder' => 'Тип оплаты', + 'value' => $model->payment_type ?? '' +]) + ->setLabel("Тип оплаты") + ->render(); + +$form->field(\itguild\forms\inputs\TextInput::class, 'bank_id', [ + 'class' => "form-control", + 'placeholder' => 'ID банка', + 'value' => $model->bank_id ?? '' +]) + ->setLabel("ID банка") + ->render(); + +$form->field(\itguild\forms\inputs\TextInput::class, 'info', [ + 'class' => "form-control", + 'placeholder' => 'Информация', + 'value' => $model->info ?? '' +]) + ->setLabel("Информация") + ->render(); + +$form->field(\itguild\forms\inputs\TextInput::class, 'program', [ + 'class' => "form-control", + 'placeholder' => 'Программа', + 'value' => $model->program ?? '' +]) + ->setLabel("Программа") + ->render(); + +$form->field(\itguild\forms\inputs\TextInput::class, 'cvc', [ + 'class' => "form-control", + 'placeholder' => 'CVC', + 'value' => $model->cvc ?? '' +]) + ->setLabel("CVC") + ->render(); + +$form->field(\itguild\forms\inputs\TextInput::class, 'pin', [ + 'class' => "form-control", + 'placeholder' => 'PIN', + 'value' => $model->pin ?? '' +]) + ->setLabel("PIN") + ->render(); + +$form->field(class: \itguild\forms\inputs\Select::class, name: "card_template_id", params: [ + 'class' => "form-control", + 'value' => $model->card_template_id ?? '' +]) + ->setLabel("Шаблон") + ->setOptions(\kernel\app_modules\card\services\CardTemplateService::getTemplatesList()) + ->render(); + +$form->field(class: \itguild\forms\inputs\Select::class, name: "status", params: [ + 'class' => "form-control", + 'value' => $model->status ?? '' +]) + ->setLabel("Статус") + ->setOptions(Card::getStatus()) + ->render(); + +?> +
+
+ 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/app_modules/card/views/index.php b/kernel/app_modules/card/views/index.php new file mode 100644 index 0000000..cf5e896 --- /dev/null +++ b/kernel/app_modules/card/views/index.php @@ -0,0 +1,92 @@ +setTitle("Список card"); +$view->setMeta([ + 'description' => 'Список card системы' +]); + +//Для использования таблицы с моделью, необходимо создать таблицу в базе данных +$table = new ListEloquentTable(new EloquentDataProvider(Card::class, [ + 'currentPage' => $page_number, + 'perPage' => 8, + 'params' => ["class" => "table table-bordered", "border" => "2"], + 'baseUrl' => "/admin/card" +])); + +$table->columns([ + 'user_id' => [ + 'value' => function ($data) { + return User::find($data)->username ?? ''; + } + ], + 'card_template_id' => [ + 'value' => function ($data) { + return \kernel\app_modules\card\models\CardTemplate::find($data)->title; + } + ], + 'card_file_id' => [ + 'value' => function ($data) { + $file = \kernel\app_modules\card\models\CardFile::find($data)->file; + return $file ? \kernel\helpers\Html::img($file) : ""; + } + ], + 'status' => [ + 'value' => function ($data) { + return Card::getStatus()[$data]; + } + ] +]); + +//$table = new \Itguild\Tables\ListJsonTable(json_encode( +// [ +// 'meta' => [ +// 'total' => 0, +// 'totalWithFilters' => 0, +// 'columns' => [ +// 'title', +// 'slug', +// 'status', +// ], +// 'perPage' => 5, +// 'currentPage' => 1, +// 'baseUrl' => '/admin/some', +// 'params' => [ +// 'class' => 'table table-bordered', +// 'border' => 2 +// ] +// ], +// 'filters' => [], +// 'data' => [], +// ] +//)); + +$table->beforePrint(function () { + return IconBtnCreateWidget::create(['url' => '/admin/card/create'])->run(); +}); + +$table->addAction(function($row) { + return IconBtnViewWidget::create(['url' => '/admin/card/' . $row['id']])->run(); +}); +$table->addAction(function($row) { + return IconBtnEditWidget::create(['url' => '/admin/card/update/' . $row['id']])->run(); +}); +$table->addAction(function($row) { + return IconBtnDeleteWidget::create(['url' => '/admin/card/delete/' . $row['id']])->run(); +}); +$table->create(); +$table->render(); \ No newline at end of file diff --git a/kernel/app_modules/card/views/view.php b/kernel/app_modules/card/views/view.php new file mode 100644 index 0000000..7ebfc9e --- /dev/null +++ b/kernel/app_modules/card/views/view.php @@ -0,0 +1,71 @@ + ["class" => "table table-bordered", "border" => "2"], + 'baseUrl' => "/admin/card", +])); +$table->beforePrint(function () use ($card) { + $btn = IconBtnListWidget::create(['url' => '/admin/card'])->run(); + $btn .= IconBtnEditWidget::create(['url' => '/admin/card/update/' . $card->id])->run(); + $btn .= IconBtnDeleteWidget::create(['url' => '/admin/card/delete/' . $card->id])->run(); + return $btn; +}); + +$table->rows([ + 'card_file_id' => function ($data) { + $file = \kernel\app_modules\card\models\CardFile::find($data)->file; + return $file ? \kernel\helpers\Html::img($file) : ""; + }, + 'status' => function ($data) { + return \kernel\app_modules\card\models\Card::getStatus()[$data]; + } +]); + +$table->create(); +$table->render(); + +//$writer = new PngWriter(); +// +//// Create QR code +//$qrCode = new QrCode( +// data: $card->id, +// encoding: new Encoding('UTF-8'), +// errorCorrectionLevel: ErrorCorrectionLevel::Low, +// size: 120, +// margin: 2, +// roundBlockSizeMode: RoundBlockSizeMode::Margin, +// foregroundColor: new Color(0, 0, 0), +// backgroundColor: new Color(255, 255, 255) +//); +// +// +//$result = $writer->write($qrCode); +//echo ""; +// +//$path = $card->cardFile->file; +//$mainImg = new \kernel\helpers\ImageGD(ROOT_DIR . $path); +//$qrImg = new \kernel\helpers\ImageGD($result->getDataUri()); +//$mainImg->addImg($qrImg->getImg(), 200, 15, 0, 0, 124, 124, 100); +//$mainImg->save(RESOURCES_DIR . "/tmp/with_qr.png"); + +//$cardFile = \kernel\app_modules\card\services\CardService::createCardPNG($card); +//echo \kernel\helpers\Html::img("/resources/tmp/card_tpl.png"); +//if ($cardFile) { +// echo \kernel\helpers\Html::img($cardFile); +//} + diff --git a/kernel/app_modules/tgbot/TgbotModule.php b/kernel/app_modules/tgbot/TgbotModule.php new file mode 100644 index 0000000..674e489 --- /dev/null +++ b/kernel/app_modules/tgbot/TgbotModule.php @@ -0,0 +1,39 @@ +menuService = new MenuService(); + $this->migrationService = new MigrationService(); + } + + /** + * @throws \Exception + */ + public function init(): void + { + $this->migrationService->runAtPath("{KERNEL_APP_MODULES}/tgbot/migrations"); + + $this->menuService->createItem([ + "label" => "TG bot", + "url" => "/admin/tg-bot", + "slug" => "tg-bot", + ]); + } + + public function deactivate() + { + $this->menuService->removeItemBySlug("tg-bot"); + $this->migrationService->rollbackAtPath("{KERNEL_APP_MODULES}/tgbot/migrations"); + } +} \ No newline at end of file diff --git a/kernel/app_modules/tgbot/controllers/TgBotRestController.php b/kernel/app_modules/tgbot/controllers/TgBotRestController.php new file mode 100644 index 0000000..17ddc58 --- /dev/null +++ b/kernel/app_modules/tgbot/controllers/TgBotRestController.php @@ -0,0 +1,91 @@ +model = new Tgbot(); + } + + #[NoReturn] public function actionStore(): void + { + $request = new Request(); + $data = $request->post(); + + $tgBot = $this->model->where("bot_id", $data['bot_id'])->where("dialog_id", $data['dialog_id'])->first(); + + if (!$tgBot){ + foreach ($this->model->getFillable() as $item){ + $this->model->{$item} = $data[$item] ?? null; + } + + $userService = new UserService(); + $userForm = new CreateUserForm(); + $username = $data['username']; + $userForm->load([ + 'username' => $username, + 'password' => TokenService::random_bytes(20), + 'email' => $username . "@hookahdnr.ru" + ]); + + $user = $userService->create($userForm); + if ($user) { + $this->model->user_id = $user->id; + } + + $this->model->save(); + + $this->renderApi($this->model->toArray()); + } + + $this->renderApi($tgBot->toArray()); + } + + #[NoReturn] public function actionCreateCard(): void + { + $cardService = new CardService(); + $request = new Request(); + $data = $request->post(); + $form = new CreateCardForm(); + $form->load($data); + $form->setItem('payment_type', 2); + $form->setItem('bank_id', 323); + $form->setItem('info', 42); + $form->setItem('program', 74); + $form->setItem('cvc', 101); + $form->setItem('pin', 1111); + $form->setItem('status', 1); + + if ($form->validate()){ + $model = $cardService->create($form); + + $this->renderApi($model->load(['cardFile'])->toArray()); + } + + $this->renderApi([]); + } + + #[NoReturn] public function actionGetByDialog(int $dialog_id, int $bot_id): void + { + $model = \kernel\app_modules\tgbot\models\Tgbot::where(['dialog_id' => $dialog_id, 'bot_id' => $bot_id])->first(); + if ($model) { + $this->renderApi($model->toArray()); + } + $this->renderApi([]); + } + +} \ No newline at end of file diff --git a/kernel/app_modules/tgbot/controllers/TgbotController.php b/kernel/app_modules/tgbot/controllers/TgbotController.php new file mode 100644 index 0000000..706d7bd --- /dev/null +++ b/kernel/app_modules/tgbot/controllers/TgbotController.php @@ -0,0 +1,111 @@ +cgView->viewPath = KERNEL_APP_MODULES_DIR . "/tgbot/views/tgbot/"; + $this->botService = new TgBotService(); + } + + public function actionCreate(): void + { + $this->cgView->render("form.php"); + } + + public function actionIndex($page_number = 1): void + { + $this->cgView->render("index.php", ['page_number' => $page_number]); + } + + #[NoReturn] public function actionAdd(): void + { + $tgForm = new CreateTgBotForm(); + $tgForm->load($_REQUEST); + if ($tgForm->validate()) { + $tg = $this->botService->create($tgForm); + if ($tg) { + $this->redirect("/admin/tg-bot/view/" . $tg->id); + } + } + $this->redirect("/admin/tg-bot/create"); + } + + /** + * @throws Exception + */ + public function actionUpdate($id): void + { + $model = Tgbot::find($id); + if (!$model) { + throw new Exception(message: "The dialog not found"); + } + + $this->cgView->render("form.php", ['model' => $model]); + } + + /** + * @throws Exception + */ + public function actionEdit($id): void + { + $tg = Tgbot::find($id); + if (!$tg) { + throw new Exception(message: "The tag not found"); + } + $tgForm = new CreateTgBotForm(); + $tgService = new TgBotService(); + $tgForm->load($_REQUEST); + if ($tgForm->validate()) { + $tg = $tgService->update($tgForm, $tg); + if ($tg) { + $this->redirect("/admin/tg-bot/view/" . $tg->id); + } + } + $this->redirect("/admin/tg-bot/update/" . $id); + } + + /** + * @throws Exception + */ + public function actionView($id): void + { + $tg = Tgbot::find($id); + + if (!$tg) { + throw new Exception(message: "The dialog not found"); + } + $this->cgView->render("view.php", ['tg' => $tg]); + } + + /** + * @throws Exception + */ + #[NoReturn] public function actionDelete(int $id): void + { + $post = Tgbot::find($id)->first(); + if (!$post){ + throw new Exception(message: "The tg client not found"); + } + + $post->delete(); + $this->redirect("/admin/tg-bot/"); + } + +} \ No newline at end of file diff --git a/kernel/app_modules/tgbot/migrations/2024_12_08_172205_create_tgbot_table.php b/kernel/app_modules/tgbot/migrations/2024_12_08_172205_create_tgbot_table.php new file mode 100644 index 0000000..e29c253 --- /dev/null +++ b/kernel/app_modules/tgbot/migrations/2024_12_08_172205_create_tgbot_table.php @@ -0,0 +1,35 @@ +schema->create('tgbot', function (Blueprint $table) { + $table->increments('id'); + $table->bigInteger('bot_id')->nullable(false); + $table->bigInteger('dialog_id')->nullable(false); + $table->integer('user_id')->nullable(); + $table->string('username', 255)->nullable(false); + $table->string('first_name', 255)->nullable(); + $table->string('last_name', 255)->nullable(); + $table->integer('status')->nullable()->default(1); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + \kernel\App::$db->schema->dropIfExists('tgbot'); + } +}; diff --git a/kernel/app_modules/tgbot/models/Tgbot.php b/kernel/app_modules/tgbot/models/Tgbot.php new file mode 100644 index 0000000..9d02763 --- /dev/null +++ b/kernel/app_modules/tgbot/models/Tgbot.php @@ -0,0 +1,51 @@ + 'Bot ID', + 'dialog_id' => 'Dialog ID', + 'user_id' => 'User ID', + 'username' => 'Username', + 'first_name' => 'First name', + 'last_name' => 'Last name', + 'status' => 'Статус', + ]; + } + + /** + * @return string[] + */ + public static function getStatus(): array + { + return [ + self::DISABLE_STATUS => "Не активный", + self::ACTIVE_STATUS => "Активный", + ]; + } + + +} \ No newline at end of file diff --git a/kernel/app_modules/tgbot/models/forms/CreateTgBotForm.php b/kernel/app_modules/tgbot/models/forms/CreateTgBotForm.php new file mode 100644 index 0000000..1859436 --- /dev/null +++ b/kernel/app_modules/tgbot/models/forms/CreateTgBotForm.php @@ -0,0 +1,30 @@ + 'required', + 'dialog_id' => 'required', + 'username' => 'required', + 'first_name' => '', + 'last_name' => '', + 'status' => '' + ]; + } + +} \ No newline at end of file diff --git a/kernel/app_modules/tgbot/routs/tgbot.php b/kernel/app_modules/tgbot/routs/tgbot.php new file mode 100644 index 0000000..020f712 --- /dev/null +++ b/kernel/app_modules/tgbot/routs/tgbot.php @@ -0,0 +1,33 @@ +group(["prefix" => "admin"], function (CgRouteCollector $router) { + App::$collector->group(["before" => "auth"], function (RouteCollector $router) { + App::$collector->group(["prefix" => "tg-bot"], function (CGRouteCollector $router) { + App::$collector->get('/', [\app\modules\tgbot\controllers\TgbotController::class, 'actionIndex']); + App::$collector->get('/page/{page_number}', [\app\modules\tag\controllers\TagController::class, 'actionIndex']); + App::$collector->get('/create', [\app\modules\tgbot\controllers\TgbotController::class, 'actionCreate']); + App::$collector->post("/", [\app\modules\tgbot\controllers\TgbotController::class, 'actionAdd']); + App::$collector->get('/view/{id}', [\app\modules\tgbot\controllers\TgbotController::class, 'actionView']); + App::$collector->any('/update/{id}', [\app\modules\tgbot\controllers\TgbotController::class, 'actionUpdate']); + App::$collector->any("/edit/{id}", [\app\modules\tgbot\controllers\TgbotController::class, 'actionEdit']); + App::$collector->get('/delete/{id}', [\app\modules\tgbot\controllers\TgbotController::class, 'actionDelete']); + }); + App::$collector->group(["prefix" => "settings"], function (CGRouteCollector $router) { + App::$collector->get('/tag', [\app\modules\tag\controllers\TagController::class, 'actionSettings']); + App::$collector->post('/tag/update', [\app\modules\tag\controllers\TagController::class, 'actionSaveSettings']); + }); + }); +}); + +App::$collector->group(["prefix" => "api"], function (CgRouteCollector $router){ + App::$collector->group(['before' => 'bearer'], function (CgRouteCollector $router){ + $router->rest("tg-bot", [\app\modules\tgbot\controllers\TgBotRestController::class]); + $router->post('/tg-bot/create-card', [\app\modules\tgbot\controllers\TgBotRestController::class, 'actionCreateCard']); + $router->get('/tg-bot/get-by-dialog/{dialog_id}/{bot_id}', [\app\modules\tgbot\controllers\TgBotRestController::class, 'actionGetByDialog']); + }); +}); \ No newline at end of file diff --git a/kernel/app_modules/tgbot/services/TgBotService.php b/kernel/app_modules/tgbot/services/TgBotService.php new file mode 100644 index 0000000..6beeab8 --- /dev/null +++ b/kernel/app_modules/tgbot/services/TgBotService.php @@ -0,0 +1,48 @@ +bot_id = $form_model->getItem('bot_id'); + $model->dialog_id = $form_model->getItem('dialog_id'); + $model->user_id = $form_model->getItem('user_id'); + $model->username = $form_model->getItem('username'); + $model->first_name = $form_model->getItem('first_name'); + $model->last_name = $form_model->getItem('last_name'); + $model->status = $form_model->getItem('status'); + if ($model->save()){ + return $model; + } + + return false; + } + + public function update(FormModel $form_model, Tgbot $model): false|Tgbot + { + $model->bot_id = $form_model->getItem('bot_id'); + $model->dialog_id = $form_model->getItem('dialog_id'); + $model->user_id = $form_model->getItem('user_id'); + $model->username = $form_model->getItem('username'); + $model->first_name = $form_model->getItem('first_name'); + $model->last_name = $form_model->getItem('last_name'); + $model->status = $form_model->getItem('status'); + + if ($model->save()){ + return $model; + } + + return false; + } + +} \ No newline at end of file diff --git a/kernel/app_modules/tgbot/views/tgbot/form.php b/kernel/app_modules/tgbot/views/tgbot/form.php new file mode 100644 index 0000000..98bf8f1 --- /dev/null +++ b/kernel/app_modules/tgbot/views/tgbot/form.php @@ -0,0 +1,83 @@ +beginForm(isset($model) ? "/admin/tg-bot/edit/" . $model->id : "/admin/tg-bot"); + +$form->field(class: \itguild\forms\inputs\TextInput::class, name: "bot_id", params: [ + 'class' => "form-control", + 'placeholder' => 'Bot ID', + 'value' => $model->bot_id ?? '' +]) + ->setLabel("Bot ID") + ->render(); + +$form->field(class: \itguild\forms\inputs\TextInput::class, name: "dialog_id", params: [ + 'class' => "form-control", + 'placeholder' => 'Dialog ID', + 'value' => $model->dialog_id ?? '' +]) + ->setLabel("Dialog ID") + ->render(); + +$form->field(class: \itguild\forms\inputs\TextInput::class, name: "username", params: [ + 'class' => "form-control", + 'placeholder' => 'Username', + 'value' => $model->username ?? '' +]) + ->setLabel("Username") + ->render(); + +$form->field(class: \itguild\forms\inputs\TextInput::class, name: "first_name", params: [ + 'class' => "form-control", + 'placeholder' => 'First name', + 'value' => $model->first_name ?? '' +]) + ->setLabel("First name") + ->render(); + +$form->field(class: \itguild\forms\inputs\TextInput::class, name: "last_name", params: [ + 'class' => "form-control", + 'placeholder' => 'Last name', + 'value' => $model->last_name ?? '' +]) + ->setLabel("Last name") + ->render(); + + +$form->field(\itguild\forms\inputs\Select::class, 'status', [ + 'class' => "form-control", + 'value' => $model->status ?? '' +]) + ->setLabel("Статус") + ->setOptions(Tgbot::getStatus()) + ->render(); +?> +
+
+ 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/app_modules/tgbot/views/tgbot/index.php b/kernel/app_modules/tgbot/views/tgbot/index.php new file mode 100644 index 0000000..35249ca --- /dev/null +++ b/kernel/app_modules/tgbot/views/tgbot/index.php @@ -0,0 +1,59 @@ +setTitle("Список существующих диалогов"); + +$table = new ListEloquentTable(new EloquentDataProvider(\app\modules\tgbot\models\Tgbot::class, [ + 'currentPage' => $page_number, + 'perPage' => 8, + 'params' => ["class" => "table table-bordered", "border" => "2"], + 'baseUrl' => "/admin/tg-bot", + 'searchPrefix' => "", + 'searchParams' => (new \kernel\Request())->get(), +])); + +$table->beforePrint(function () { + return PrimaryBtn::create("Создать", "/admin/tg-bot/create")->fetch(); +}); + +$table->columns([ + "status" => [ + "value" => function ($cell) { + return \app\modules\tgbot\models\Tgbot::getStatus()[$cell]; + } + ], + "username" => [ + "filter" => [ + "class" => \Itguild\Tables\Filter\InputTextFilter::class + ] + ], + "bot_id" => [ + "filter" => [ + "class" => \Itguild\Tables\Filter\InputTextFilter::class + ] + ], + "dialog_id" => [ + "filter" => [ + "class" => \Itguild\Tables\Filter\InputTextFilter::class + ] + ] +]); + + +$table->addAction(\kernel\IGTabel\action_column\ViewActionColumn::class); +$table->addAction(\kernel\IGTabel\action_column\DeleteActionColumn::class); +$table->addAction(\kernel\IGTabel\action_column\EditActionColumn::class); +$table->create(); +$table->render(); \ No newline at end of file diff --git a/kernel/app_modules/tgbot/views/tgbot/view.php b/kernel/app_modules/tgbot/views/tgbot/view.php new file mode 100644 index 0000000..1fcbda1 --- /dev/null +++ b/kernel/app_modules/tgbot/views/tgbot/view.php @@ -0,0 +1,30 @@ + ["class" => "table table-bordered", "border" => "2"], + 'baseUrl' => "/admin/tg-bot", +])); +$table->beforePrint(function () use ($tg) { + $btn = PrimaryBtn::create("Список", "/admin/tg-bot")->fetch(); + $btn .= SuccessBtn::create("Редактировать", "/admin/tg-bot/update/" . $tg->id)->fetch(); + $btn .= DangerBtn::create("Удалить", "/admin/tg-bot/delete/" . $tg->id)->fetch(); + return $btn; +}); +$table->rows([ + 'status' => (function ($data) { + return \app\modules\tgbot\models\Tgbot::getStatus()[$data]; + }) +]); +$table->create(); +$table->render(); \ No newline at end of file diff --git a/kernel/console/controllers/AdminThemeController.php b/kernel/console/controllers/AdminThemeController.php index 2fc1739..d33d44b 100644 --- a/kernel/console/controllers/AdminThemeController.php +++ b/kernel/console/controllers/AdminThemeController.php @@ -18,31 +18,15 @@ class AdminThemeController extends ConsoleController throw new \Exception('Missing admin theme path "--path" specified'); } - $zip = new ZipArchive; - $tmpThemeDir = md5(time()); - $res = $zip->open(ROOT_DIR . $this->argv['path']); - if ($res === TRUE) { - $tmpThemeDirFull = RESOURCES_DIR . '/tmp/ad/' . $tmpThemeDir . "/"; - $zip->extractTo($tmpThemeDirFull); - $zip->close(); - $this->out->r('Архив распакован', 'green'); + if (file_exists(ROOT_DIR . $this->argv['path'])) { + $adminThemeService = new AdminThemeService(); + if ($adminThemeService->install($this->argv['path'])) { + $this->out->r("Тема админ-панели установлена", 'green'); + } else { + $this->out->r("Ошибка установки темы админ-панели", 'red'); + } } else { - $this->out->r('Message: Ошибка чтения архива', 'red'); - } - - if (file_exists($tmpThemeDirFull . "meta/manifest.json")){ - $manifestJson = getConst(file_get_contents($tmpThemeDirFull . "meta/manifest.json")); - $manifest = Manifest::getWithVars($manifestJson); - $this->out->r('manifest.json инициализирован', 'green'); - - $fileHelper = new Files(); - $fileHelper->copy_folder($tmpThemeDirFull . "meta", $manifest['theme_path']); - $fileHelper->copy_folder($tmpThemeDirFull . "resources", $manifest['resource_path']); - - $this->out->r("Удаляем временные файлы", 'green'); - $fileHelper->recursiveRemoveDir($tmpThemeDirFull); - - $this->out->r("Тема " . $manifest['name'] . " установлена", 'green'); + $this->out->r("Тема админ-панели не найдена", 'red'); } } @@ -53,17 +37,11 @@ class AdminThemeController extends ConsoleController } if (file_exists(ROOT_DIR . $this->argv['path'])) { - $themeName = basename($this->argv['path']); - $active_admin_theme = Option::where("key", "active_admin_theme")->first(); - if ($active_admin_theme->value === ROOT_DIR . $this->argv['path']) { - $this->out->r("Меняем тему на базовую", 'green'); - $adminThemeService = new AdminThemeService(); - $adminThemeService->setActiveAdminTheme(KERNEL_ADMIN_THEMES_DIR . '/default'); - $this->out->r("Тема изменена", 'green'); - } - $fileHelper = new Files(); - $fileHelper->recursiveRemoveDir(ROOT_DIR . $this->argv['path']); - $fileHelper->recursiveRemoveDir(RESOURCES_DIR . '/' . $themeName); + $adminThemeService = new AdminThemeService(); + $adminThemeService->uninstall($this->argv['path']); + + + $this->out->r("Тема удалена", 'green'); } else { @@ -71,4 +49,22 @@ class AdminThemeController extends ConsoleController } } + /** + * @throws \Exception + */ + public function actionPackTheme(): void + { + if (!isset($this->argv['path'])) { + throw new \Exception('Missing admin theme path "--path" specified'); + } + + if (file_exists(ROOT_DIR . $this->argv['path'])) { + $adminThemeService = new AdminThemeService(); + $adminThemeService->pack($this->argv['path']); + $this->out->r("Тема админ-панели заархивирована", 'green'); + } else { + $this->out->r("Тема админ-панели не найдена", 'red'); + } + } + } \ No newline at end of file diff --git a/kernel/console/routs/cli.php b/kernel/console/routs/cli.php index d0f1032..4676cc7 100644 --- a/kernel/console/routs/cli.php +++ b/kernel/console/routs/cli.php @@ -35,6 +35,10 @@ App::$collector->group(["prefix" => "admin-theme"], callback: function (RouteCol [\kernel\console\controllers\AdminThemeController::class, 'actionUninstallTheme'], additionalInfo: ['description' => 'Удалить тему админ-панели', 'params' => ['--path' => 'Путь к удаляемой теме']] ); + App::$collector->console('pack', + [\kernel\console\controllers\AdminThemeController::class, 'actionPackTheme'], + additionalInfo: ['description' => 'Заархивировать тему админ-панели', 'params' => ['--path' => 'Путь к теме, которую нужно заархивировать']] + ); }); App::$collector->group(["prefix" => "secure"], callback: function (RouteCollector $router){ diff --git a/kernel/filters/BootstrapSelectFilter.php b/kernel/filters/BootstrapSelectFilter.php new file mode 100644 index 0000000..86daceb --- /dev/null +++ b/kernel/filters/BootstrapSelectFilter.php @@ -0,0 +1,23 @@ +name, [ + 'class' => 'form-control', + 'options' => $this->params['options'], + 'value' => $this->value, + 'prompt' => $this->params['prompt'] ?? null, + ]); + + return "" . $select->create()->fetch() . ""; + } +} \ No newline at end of file diff --git a/kernel/filters/BootstrapTextFilter.php b/kernel/filters/BootstrapTextFilter.php new file mode 100644 index 0000000..32aec8b --- /dev/null +++ b/kernel/filters/BootstrapTextFilter.php @@ -0,0 +1,21 @@ +name, [ + 'value' => $this->value, + 'class' => "form-control" + ]); + + return "" . $textInput->create()->fetch() . ""; + + } +} \ No newline at end of file diff --git a/kernel/helpers/ImageGD.php b/kernel/helpers/ImageGD.php new file mode 100644 index 0000000..3b15b05 --- /dev/null +++ b/kernel/helpers/ImageGD.php @@ -0,0 +1,48 @@ +img = imagecreatefrompng($resource); + } + else { + $this->img = imagecreatetruecolor($width, $height); + } + imagesavealpha($this->img, true); + } + + public function addText(string $font_size, int $degree, int $x, int $y, string $color, string $font, string $text): void + { + $rgbArr = $this->hexToRgb($color); + $color = imagecolorallocate($this->img, $rgbArr[0], $rgbArr[1], $rgbArr[2]); + imagettftext($this->img, $font_size, $degree, $x, $y, $color, $font, $text); + } + + public function addImg(\GdImage $gdImage, int $location_x, int $location_y, int $offset_src_x, int $offset_src_y, int $src_width, int $src_height, int $no_transparent): void + { + imagecopymerge($this->img, $gdImage, $location_x, $location_y, $offset_src_x, $offset_src_y, $src_width, $src_height, $no_transparent); + } + + public function getImg() + { + return $this->img; + } + + public function save(string $path): void + { + imagepng($this->img, $path); + imagedestroy($this->img); + } + + protected function hexToRgb(string $hex) + { + return sscanf($hex, "#%02x%02x%02x"); + } + +} \ No newline at end of file diff --git a/kernel/manifest.json b/kernel/manifest.json index 6521135..e19c591 100644 --- a/kernel/manifest.json +++ b/kernel/manifest.json @@ -1,6 +1,6 @@ { "name": "Kernel", - "version": "0.1.1", + "version": "0.1.2", "author": "ITGuild", "slug": "kernel", "type": "kernel", diff --git a/kernel/modules/menu/views/index.php b/kernel/modules/menu/views/index.php index 0cad83d..d3bbfcd 100644 --- a/kernel/modules/menu/views/index.php +++ b/kernel/modules/menu/views/index.php @@ -19,12 +19,15 @@ $table = new ListEloquentTable(new EloquentDataProvider(Menu::class, [ 'baseUrl' => "/admin/settings/menu", ])); $table->columns([ - 'parent_id' => (function ($data) { + 'parent_id' => function ($data) { if ($data == 0) return null; return Menu::find($data)->label; - }), + }, 'icon_file' => function ($data) { return $data ? "" : ""; + }, + 'status' => function ($data) { + return Menu::getStatus()[$data]; } ]); $table->beforePrint(function () { diff --git a/kernel/modules/module_shop_client/controllers/ModuleShopClientController.php b/kernel/modules/module_shop_client/controllers/ModuleShopClientController.php index d4d4102..13c4e6c 100644 --- a/kernel/modules/module_shop_client/controllers/ModuleShopClientController.php +++ b/kernel/modules/module_shop_client/controllers/ModuleShopClientController.php @@ -10,14 +10,12 @@ use kernel\Flash; use kernel\helpers\Debug; use kernel\helpers\Files; use kernel\helpers\RESTClient; -use kernel\helpers\SMTP; -use kernel\Mailing; +use kernel\modules\module_shop_client\services\ModuleShopClientService; use kernel\Request; use kernel\services\AdminThemeService; use kernel\services\KernelService; use kernel\services\ModuleService; use kernel\services\ModuleShopService; -use kernel\services\TokenService; use PHPMailer\PHPMailer\Exception; class ModuleShopClientController extends AdminController @@ -26,6 +24,7 @@ class ModuleShopClientController extends AdminController protected Client $client; protected ModuleService $moduleService; protected KernelService $kernelService; + protected ModuleShopClientService $moduleShopClientService; protected function init(): void { @@ -35,6 +34,7 @@ class ModuleShopClientController extends AdminController $this->client = new Client(); $this->moduleService = new ModuleService(); $this->kernelService = new KernelService(); + $this->moduleShopClientService = new ModuleShopClientService(); } /** @@ -42,7 +42,6 @@ class ModuleShopClientController extends AdminController */ public function actionIndex(int $page_number = 1): void { - if ($this->moduleService->issetModuleShopToken()) { if ($this->moduleService->isServerAvailable()) { @@ -157,24 +156,71 @@ 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(); $filters = $request->get(); if ($this->moduleService->issetModuleShopToken()) { if ($this->moduleService->isServerAvailable()) { - $modules_info = []; +// $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) { + $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 $key => $module) { + foreach ($filters as $column => $value) { if ($value === '') continue; - if ($module[$key] !== $value) { - break; + if (is_numeric($value)) { + if ($module[$column] !== $value) { + unset($modules_info[$key]); + } + } elseif (is_string($value)) { + if (!str_contains($module[$column], $value)) { + unset($modules_info[$key]); + } } - - $modules_info[] = $module; } } $module_count = count($modules_info); @@ -188,6 +234,7 @@ class ModuleShopClientController extends AdminController 'per_page' => $per_page, 'kernelService' => new KernelService(), 'adminThemeService' => new AdminThemeService(), + 'filterValues' => $filters ]); } else { $this->cgView->render("module_shop_error_connection.php"); @@ -209,7 +256,7 @@ class ModuleShopClientController extends AdminController $moduleShopService = new ModuleShopService(); $result = $moduleShopService->email_auth($address); - if ($result['status'] == 'success'){ + if ($result['status'] == 'success') { $this->cgView->render('enter_code.php', ['email' => $address]); } @@ -224,7 +271,7 @@ class ModuleShopClientController extends AdminController $moduleShopService = new ModuleShopService(); $result = $moduleShopService->code_check($code); - if (isset($result['access_token'])){ + if (isset($result['access_token'])) { $envFile = \EnvEditor\EnvFile::loadFrom(ROOT_DIR . "/.env"); diff --git a/kernel/modules/module_shop_client/services/ModuleShopClientService.php b/kernel/modules/module_shop_client/services/ModuleShopClientService.php new file mode 100644 index 0000000..d86afca --- /dev/null +++ b/kernel/modules/module_shop_client/services/ModuleShopClientService.php @@ -0,0 +1,22 @@ +getBody()->getContents(), true); +// return array_slice($modules_info, $perPage * ($pageNumber - 1), $perPage); +// } +// +// public function getModulesInfoWithFilters(string $url, int $perPage, int $pageNumber): \Psr\Http\Message\ResponseInterface +// { +// $modules_info = RESTClient::request($url); +// $modules_info = json_decode($modules_info->getBody()->getContents(), true); +// return array_slice($modules_info, $perPage * ($pageNumber - 1), $perPage); +// } +} \ No newline at end of file diff --git a/kernel/modules/module_shop_client/views/index.php b/kernel/modules/module_shop_client/views/index.php index 930793c..f3474d4 100644 --- a/kernel/modules/module_shop_client/views/index.php +++ b/kernel/modules/module_shop_client/views/index.php @@ -7,6 +7,7 @@ * @var \kernel\services\ModuleService $moduleService * @var \kernel\services\KernelService $kernelService * @var \kernel\services\AdminThemeService $adminThemeService + * @var array $filterValues */ use Itguild\Tables\ListJsonTable; @@ -42,15 +43,21 @@ $table->addAction(function ($row, $url) use ($moduleService) { $table->columns([ 'type' => [ 'filter' => [ - 'class' => \Itguild\Tables\Filter\SelectFilter::class, - 'param' => ['kernel', 'entity'], - 'value' => "kernel" + 'class' => \kernel\filters\BootstrapSelectFilter::class, + 'params' => [ + 'options' => [ + 'kernel' => 'kernel', + 'entity' => 'entity', + ], + 'prompt' => 'Не выбрано' + ], + 'value' => $filterValues['type'] ?? '' ], ] ]); -$table->addAction(function ($row, $url) use ($moduleService){ - if ($row['slug'] !== 'kernel') { +$table->addAction(function ($row, $url) use ($moduleService) { + if ($row['type'] === 'entity' || $row['type'] === 'additional_property') { if ($moduleService->isInstall($row['slug'])) { $url = "$url/delete/?slug=" . $row['slug']; @@ -61,16 +68,19 @@ $table->addAction(function ($row, $url) use ($moduleService){ return \kernel\widgets\IconBtn\IconBtnInstallWidget::create(['url' => $url])->run(); } } - return null; + + return false; }); $table->addAction(function ($row, $url) use ($moduleService) { - $slug = $row['slug']; - if ($moduleService->isInstall($slug)) { - if (!$moduleService->isLastVersion($slug)) { - $url = "$url/update/?slug=" . $slug; + if ($row['type'] === 'entity' || $row['type'] === 'additional_property') { + $slug = $row['slug']; + if ($moduleService->isInstall($slug)) { + if (!$moduleService->isLastVersion($slug)) { + $url = "$url/update/?slug=" . $slug; - return \kernel\widgets\IconBtn\IconBtnUpdateWidget::create(['url' => $url])->run(); + return \kernel\widgets\IconBtn\IconBtnUpdateWidget::create(['url' => $url])->run(); + } } } @@ -78,8 +88,7 @@ $table->addAction(function ($row, $url) use ($moduleService) { }); $table->addAction(function ($row, $url) use ($kernelService) { - $slug = $row['slug']; - if ($slug === 'kernel') { + if ($row['type'] === 'kernel') { if (!$kernelService->isLastVersion()) { $url = "$url/kernel/update_form/"; @@ -91,9 +100,8 @@ $table->addAction(function ($row, $url) use ($kernelService) { }); $table->addAction(function ($row, $url) use ($adminThemeService) { - $type = $row['type']; - $slug = $row['slug']; - if ($type === 'admin_theme') { + if ($row['type'] === 'admin_theme') { + $slug = $row['slug']; if ($adminThemeService->isInstall($slug)) { if (!$adminThemeService->isLastVersion($slug)) { $url = "$url/admin_theme/update/"; diff --git a/kernel/modules/post/views/index.php b/kernel/modules/post/views/index.php index 045feb1..4b47124 100644 --- a/kernel/modules/post/views/index.php +++ b/kernel/modules/post/views/index.php @@ -15,11 +15,15 @@ use kernel\widgets\IconBtn\IconBtnDeleteWidget; use kernel\widgets\IconBtn\IconBtnEditWidget; use kernel\widgets\IconBtn\IconBtnViewWidget; +$get = (new \kernel\Request())->get(); + $table = new ListEloquentTable(new EloquentDataProvider(Post::class, [ 'currentPage' => $page_number, 'perPage' => 3, 'params' => ["class" => "table table-bordered", "border" => "2"], - 'baseUrl' => "/admin/post" + 'baseUrl' => "/admin/post", + 'searchPrefix' => "", + 'searchParams' => $get, ])); $view->setTitle("Список постов"); @@ -36,36 +40,57 @@ foreach ($additionals as $additional) { }); } +//\kernel\helpers\Debug::dd($request); + $table->columns([ - 'created_at' => function ($data) { - if (!$data){ - return null; - } - - return (new DateTimeImmutable($data))->format("d-m-Y"); - }, + 'title' => [ + 'filter' => [ + 'class' => \kernel\filters\BootstrapTextFilter::class, + 'value' => $get['title'] ?? '' + ] + ], + 'content' => [ + 'filter' => [ + 'class' => \kernel\filters\BootstrapTextFilter::class, + 'value' => $get['content'] ?? '' + ] + ], + 'created_at' => [ + 'format' => 'date:d-m-Y', + ], 'updated_at' => function ($data) { - if (!$data){ + if (!$data) { return null; } return (new DateTimeImmutable($data))->format("d-m-Y"); }, - 'user_id' => (function ($data) { - return User::find($data)->username; - }) + 'user_id' => [ + 'value' => function ($data) { + return User::find($data)->username; + }, + 'filter' => [ + 'class' => \kernel\filters\BootstrapSelectFilter::class, + 'params' => [ + 'options' => \kernel\modules\user\service\UserService::createUsernameArr(), + 'prompt' => 'Не выбрано' + ], + 'value' => $get['user_id'] ?? '', + ], + ] ]); + $table->beforePrint(function () { return IconBtnCreateWidget::create(['url' => '/admin/post/create'])->run(); }); -$table->addAction(function($row) { +$table->addAction(function ($row) { return IconBtnViewWidget::create(['url' => '/admin/post/view/' . $row['id']])->run(); }); -$table->addAction(function($row) { +$table->addAction(function ($row) { return IconBtnEditWidget::create(['url' => '/admin/post/update/' . $row['id']])->run(); }); -$table->addAction(function($row) { +$table->addAction(function ($row) { return IconBtnDeleteWidget::create(['url' => '/admin/post/delete/' . $row['id']])->run(); }); $table->create(); diff --git a/kernel/modules/user/controllers/UserController.php b/kernel/modules/user/controllers/UserController.php index fdd8534..ddf0651 100644 --- a/kernel/modules/user/controllers/UserController.php +++ b/kernel/modules/user/controllers/UserController.php @@ -6,6 +6,8 @@ use Exception; use JetBrains\PhpStorm\NoReturn; use kernel\AdminController; use kernel\EntityRelation; +use kernel\FileUpload; +use kernel\helpers\Debug; use kernel\modules\user\models\forms\CreateUserForm; use kernel\modules\user\models\User; use kernel\modules\user\service\UserService; @@ -35,10 +37,17 @@ class UserController extends AdminController { $userForm = new CreateUserForm(); $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->validate()){ $user = $this->userService->create($userForm); - $entityRelation = new EntityRelation(); $entityRelation->saveEntityRelation(entity: "user", model: $user, request: new Request()); @@ -98,6 +107,13 @@ class UserController extends AdminController $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); @@ -128,4 +144,13 @@ class UserController extends AdminController $this->redirect("/admin/user/"); } + public function actionProfile(): void + { + $user = UserService::getAuthUser(); + if (!$user){ + throw new Exception(message: "The user not found"); + } + $this->cgView->render("view.php", ['user' => $user]); + } + } \ No newline at end of file diff --git a/kernel/modules/user/migrations/2024_09_23_125827_create_user_table.php b/kernel/modules/user/migrations/2024_09_23_125827_create_user_table.php index a762579..2b2ab0f 100644 --- a/kernel/modules/user/migrations/2024_09_23_125827_create_user_table.php +++ b/kernel/modules/user/migrations/2024_09_23_125827_create_user_table.php @@ -20,6 +20,7 @@ return new class extends Migration $table->string('username', 255)->nullable(false); $table->string('email', 255); $table->string('password_hash', 255); + $table->string('user_photo', 255)->nullable(); $table->integer('role')->default(1); $table->string('access_token', 255)->nullable(true); $table->dateTime('access_token_expires_at')->nullable(true); diff --git a/kernel/modules/user/models/User.php b/kernel/modules/user/models/User.php index 3dd03c1..78afe96 100644 --- a/kernel/modules/user/models/User.php +++ b/kernel/modules/user/models/User.php @@ -7,6 +7,7 @@ use Illuminate\Database\Eloquent\Model; * @property string $username * @property string $email * @property string $password_hash + * @property string $user_photo * @property string $access_token * @property string $access_token_expires_at * @method static find($id) @@ -17,7 +18,7 @@ class User extends Model { const ADMIN_USER_ROLE = 9; protected $table = 'user'; - protected $fillable = ['username', 'email', 'password_hash', 'role', 'access_token', 'access_token_expires_at']; + protected $fillable = ['username', 'email', 'password_hash', 'user_photo', 'role', 'access_token', 'access_token_expires_at']; protected array $dates = ['deleted at']; public static function labels(): array @@ -25,6 +26,7 @@ class User extends Model { return [ 'username' => 'Логин', 'email' => 'Email', + 'user_photo' => 'Фото профиля', 'created_at' => 'Создан', 'updated_at' => 'Обновлен', ]; diff --git a/kernel/modules/user/models/forms/CreateUserForm.php b/kernel/modules/user/models/forms/CreateUserForm.php index 73c5f77..bcf37df 100644 --- a/kernel/modules/user/models/forms/CreateUserForm.php +++ b/kernel/modules/user/models/forms/CreateUserForm.php @@ -12,7 +12,8 @@ class CreateUserForm extends FormModel return [ 'username' => 'required|min-str-len:5|max-str-len:30', 'password' => 'required|min-str-len:5|max-str-len:30', - 'email' => 'required|email' + 'email' => 'required|email', + 'user_photo' => '' ]; } @@ -21,7 +22,9 @@ class CreateUserForm extends FormModel return [ 'username' => 'required|min-str-len:5|max-str-len:30', 'password' => '', - 'email' => 'required|email' + 'email' => 'required|email', + 'user_photo' => '' + ]; } diff --git a/kernel/modules/user/routs/user.php b/kernel/modules/user/routs/user.php index 5572c92..7360b89 100644 --- a/kernel/modules/user/routs/user.php +++ b/kernel/modules/user/routs/user.php @@ -17,6 +17,7 @@ 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']); }); }); }); \ No newline at end of file diff --git a/kernel/modules/user/service/UserService.php b/kernel/modules/user/service/UserService.php index 9e8dd5d..9915c2a 100644 --- a/kernel/modules/user/service/UserService.php +++ b/kernel/modules/user/service/UserService.php @@ -15,6 +15,7 @@ class UserService $model->username = $form_model->getItem('username'); $model->email = $form_model->getItem('email'); $model->password_hash = password_hash($form_model->getItem('password'), PASSWORD_DEFAULT); + $model->user_photo = $form_model->getItem('user_photo'); if ($model->save()){ return $model; } @@ -29,6 +30,7 @@ class UserService if ($form_model->getItem('password')) { $user->password_hash = password_hash($form_model->getItem('password'), PASSWORD_DEFAULT); } + $user->user_photo = $form_model->getItem('user_photo'); if ($user->save()){ return $user; } @@ -80,6 +82,28 @@ class UserService return ''; } + public static function getAuthUserId(): string + { + $user = self::getAuthUser(); + if ($user){ + return $user->id; + } + + return ''; + } + + public static function getAuthUserPhoto(): string|null + { + $user = self::getAuthUser(); + if ($user){ + if ($user->user_photo) { + return $user->user_photo; + } + } + + return null; + } + public function getByAccessToken(string $token) { return $this->getByField("access_token", $token); diff --git a/kernel/modules/user/views/form.php b/kernel/modules/user/views/form.php index e32d35f..89f94d2 100644 --- a/kernel/modules/user/views/form.php +++ b/kernel/modules/user/views/form.php @@ -32,6 +32,16 @@ $form->field(class: \itguild\forms\inputs\TextInput::class, name: "email", param ->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(); diff --git a/kernel/modules/user/views/index.php b/kernel/modules/user/views/index.php index e492f5e..abfce5b 100644 --- a/kernel/modules/user/views/index.php +++ b/kernel/modules/user/views/index.php @@ -17,13 +17,15 @@ use kernel\widgets\IconBtn\IconBtnDeleteWidget; use kernel\widgets\IconBtn\IconBtnEditWidget; use kernel\widgets\IconBtn\IconBtnViewWidget; +$get = (new \kernel\Request())->get(); + $table = new ListEloquentTable(new EloquentDataProvider(User::class, [ 'currentPage' => $page_number, 'perPage' => 3, 'params' => ["class" => "table table-bordered", "border" => "2"], 'baseUrl' => "/admin/user", - 'filters' => ['email'], -])); + 'searchPrefix' => "", + 'searchParams' => $get,])); $entityRelation = new \kernel\EntityRelation(); $additionals = $entityRelation->getEntityRelationsBySlug("user"); @@ -37,9 +39,13 @@ foreach ($additionals as $additional) { $table->columns([ 'username' => [ "filter" => [ - 'class' => \Itguild\Tables\Filter\InputTextFilter::class + 'class' => \kernel\filters\BootstrapTextFilter::class, + 'value' => $get['username'] ?? null, ] ], + 'user_photo' => function ($data) { + return $data ? "" : ""; + }, 'created_at' => function ($data) { if (!$data){ return null; diff --git a/kernel/modules/user/views/view.php b/kernel/modules/user/views/view.php index 856186f..f234627 100644 --- a/kernel/modules/user/views/view.php +++ b/kernel/modules/user/views/view.php @@ -35,6 +35,9 @@ foreach ($additionals as $key => $additional) { } $table->rows([ + 'user_photo' => function ($data) { + return $data ? "" : ""; + }, 'created_at' => function ($data) { if (!$data){ return null; diff --git a/kernel/services/AdminThemeService.php b/kernel/services/AdminThemeService.php index 457fa02..f8d5aea 100644 --- a/kernel/services/AdminThemeService.php +++ b/kernel/services/AdminThemeService.php @@ -4,12 +4,16 @@ namespace kernel\services; use DirectoryIterator; use kernel\helpers\Debug; +use kernel\helpers\Files; use kernel\helpers\Manifest; use kernel\helpers\RESTClient; use kernel\models\Option; +use ZipArchive; class AdminThemeService { + protected array $errors = []; + protected Option $option; protected string $active_theme; protected ModuleService $moduleService; @@ -22,6 +26,24 @@ class AdminThemeService $this->moduleService = new ModuleService(); } + /** + * @return array + */ + public function getErrors(): array + { + return $this->errors; + } + + /** + * @param $msg + * @return void + */ + public function addError($msg): void + { + $this->errors[] = $msg; + } + + public function findActiveAdminTheme(): void { $model = Option::where("key", "active_admin_theme")->first(); @@ -50,7 +72,7 @@ class AdminThemeService $info = []; $theme = getConst($theme); $info['path'] = $theme; - if (file_exists($theme . "/manifest.json")){ + if (file_exists($theme . "/manifest.json")) { $manifest = file_get_contents($theme . "/manifest.json"); $manifest = Manifest::getWithVars($manifest); $manifest['preview'] = $manifest['resource'] . "/" . $manifest['preview']; @@ -60,6 +82,11 @@ class AdminThemeService return $info; } + public function getAdminThemeInfoBySlug(string $slug) + { + // TODO + } + public function isInstall(string $slug): bool { $adminThemePaths = Option::where("key", "admin_theme_paths")->first(); @@ -85,12 +112,13 @@ class AdminThemeService public function isLastVersion(string $slug): bool { if ($this->moduleService->isServerAvailable()) { - $modules_info = RESTClient::request($_ENV['MODULE_SHOP_URL'] . '/api/module_shop/gb_slug'); + $modulesInfo = RESTClient::request($_ENV['MODULE_SHOP_URL'] . '/api/module_shop/gb_slug'); - $modules_info = json_decode($modules_info->getBody()->getContents(), true); + $modulesInfo = json_decode($modulesInfo->getBody()->getContents(), true); $themeInfo = $this->getAdminThemeInfo($slug); - foreach ($modules_info as $mod) { +// Debug::dd($themeInfo); + foreach ($modulesInfo as $mod) { if ($mod['slug'] === $themeInfo['slug'] && $mod['version'] === $themeInfo['version']) { return true; } @@ -99,4 +127,79 @@ class AdminThemeService return false; } + + public function pack(string $path): void + { + $themeName = basename($path); + + $tmpThemeDirFull = RESOURCES_DIR . '/tmp/ad/' . $themeName . "/"; + + $fileHelper = new Files(); + $fileHelper->copy_folder(ROOT_DIR . $path, $tmpThemeDirFull . 'meta/'); + $fileHelper->copy_folder(RESOURCES_DIR . '/' . $themeName, $tmpThemeDirFull . 'resources/'); + + if (!is_dir(RESOURCES_DIR . '/tmp/admin_themes')) { + $old_mask = umask(0); + mkdir(RESOURCES_DIR . '/tmp/admin_themes', 0775, true); + umask($old_mask); + } + $fileHelper->pack($tmpThemeDirFull, RESOURCES_DIR . '/tmp/admin_themes/' . $themeName . '.igt'); + } + + public function install(string $path): bool + { + $zip = new ZipArchive; + $tmpThemeDir = md5(time()); + $res = $zip->open(ROOT_DIR . $path); + if ($res === TRUE) { + $tmpThemeDirFull = RESOURCES_DIR . '/tmp/ad/' . $tmpThemeDir . "/"; + $zip->extractTo($tmpThemeDirFull); + $zip->close(); + } else { + $this->addError('unable to open zip archive'); + return false; + } + + if (!file_exists($tmpThemeDirFull . "meta/manifest.json")){ + $this->addError('manifest.json not found'); + return false; + } + + $manifestJson = getConst(file_get_contents($tmpThemeDirFull . "meta/manifest.json")); + $manifest = Manifest::getWithVars($manifestJson); + + $fileHelper = new Files(); + if (isset($manifest['theme_path'])) { + $fileHelper->copy_folder($tmpThemeDirFull . "meta", $manifest['theme_path']); + } else { + $fileHelper->copy_folder($tmpThemeDirFull . "meta", APP_DIR . '/admin_themes/' . $manifest['slug']); + } + + if (isset($manifest['resource_path'])) { + $fileHelper->copy_folder($tmpThemeDirFull . "resources", $manifest['resource_path']); + } else { + $fileHelper->copy_folder($tmpThemeDirFull . "resources", RESOURCES_DIR . '/' . $manifest['slug']); + } + + $fileHelper->recursiveRemoveDir($tmpThemeDirFull); + unlink(ROOT_DIR . $path); + return true; + } + + public function uninstall(string $path): void + { + $themeInfo = $this->getAdminThemeInfo(APP_DIR . '/admin_themes/' . basename($path)); + + $active_admin_theme = Option::where("key", "active_admin_theme")->first(); + if ($active_admin_theme->value === ROOT_DIR . $path) { + $this->setActiveAdminTheme(KERNEL_ADMIN_THEMES_DIR . '/default'); + } + $fileHelper = new Files(); + if (file_exists(ROOT_DIR . $path)) { + $fileHelper->recursiveRemoveDir(ROOT_DIR . $path); + } + if (file_exists(RESOURCES_DIR . '/' . $themeInfo['slug'])) { + $fileHelper->recursiveRemoveDir(RESOURCES_DIR . '/' . $themeInfo['slug']); + } + } } \ No newline at end of file diff --git a/kernel/services/ModuleService.php b/kernel/services/ModuleService.php index 56ca7be..2a0eb69 100644 --- a/kernel/services/ModuleService.php +++ b/kernel/services/ModuleService.php @@ -362,11 +362,15 @@ class ModuleService if (file_exists(KERNEL_APP_MODULES_DIR . '/' . $moduleName)) { $fileHelper->copy_folder(KERNEL_APP_MODULES_DIR . '/' . $moduleName, $tmpModuleDirFull . 'kernel/'); } else { - mkdir($tmpModuleDirFull . 'kernel/'); + $old_mask = umask(0); + mkdir($tmpModuleDirFull . 'kernel/', 0775, true); + umask($old_mask); } if (!is_dir(RESOURCES_DIR . '/tmp/modules')) { - mkdir(RESOURCES_DIR . '/tmp/modules', 0777, true); + $old_mask = umask(0); + mkdir(RESOURCES_DIR . '/tmp/modules', 0775, true); + umask($old_mask); } $fileHelper->pack($tmpModuleDirFull, RESOURCES_DIR . '/tmp/modules/' . $moduleName . '.igm'); } diff --git a/kernel/templates/views/index_template b/kernel/templates/views/index_template index a7a7171..44953ea 100644 --- a/kernel/templates/views/index_template +++ b/kernel/templates/views/index_template @@ -51,6 +51,16 @@ $table = new \Itguild\Tables\ListJsonTable(json_encode( ] )); +// Пример фильтра +$table->columns([ + 'title' => [ + 'filter' => [ + 'class' => \Itguild\Tables\Filter\InputTextFilter::class, + 'value' => $get['title'] ?? '' + ] + ], +] + $table->beforePrint(function () { return IconBtnCreateWidget::create(['url' => '/admin/{slug}/create'])->run(); }); diff --git a/resources/cards/09/74/09746c31e3c2d4b9011b5ec7100327b9.png b/resources/cards/09/74/09746c31e3c2d4b9011b5ec7100327b9.png new file mode 100644 index 0000000..55ee2d4 Binary files /dev/null and b/resources/cards/09/74/09746c31e3c2d4b9011b5ec7100327b9.png differ diff --git a/resources/cards/16/f6/16f6b7cc7cf87c9e01258d7a0c3cca2a.png b/resources/cards/16/f6/16f6b7cc7cf87c9e01258d7a0c3cca2a.png new file mode 100644 index 0000000..b8dab65 Binary files /dev/null and b/resources/cards/16/f6/16f6b7cc7cf87c9e01258d7a0c3cca2a.png differ diff --git a/resources/cards/20/e0/20e02dddafbc4ce86b664f0f09054eaa.png b/resources/cards/20/e0/20e02dddafbc4ce86b664f0f09054eaa.png new file mode 100644 index 0000000..a479239 Binary files /dev/null and b/resources/cards/20/e0/20e02dddafbc4ce86b664f0f09054eaa.png differ diff --git a/resources/cards/2b/9b/2b9b111df147fc06ac238eb502411fa2.png b/resources/cards/2b/9b/2b9b111df147fc06ac238eb502411fa2.png new file mode 100644 index 0000000..879f154 Binary files /dev/null and b/resources/cards/2b/9b/2b9b111df147fc06ac238eb502411fa2.png differ diff --git a/resources/cards/30/32/3032ee84a2000b5d7f9514121c0aea27.png b/resources/cards/30/32/3032ee84a2000b5d7f9514121c0aea27.png new file mode 100644 index 0000000..af252db Binary files /dev/null and b/resources/cards/30/32/3032ee84a2000b5d7f9514121c0aea27.png differ diff --git a/resources/cards/30/71/307191ee25a7e8c47ae7e4094dec9118.png b/resources/cards/30/71/307191ee25a7e8c47ae7e4094dec9118.png new file mode 100644 index 0000000..a5b7846 Binary files /dev/null and b/resources/cards/30/71/307191ee25a7e8c47ae7e4094dec9118.png differ diff --git a/resources/cards/4e/45/4e45b87053b11992b4f9d672e384d17d.png b/resources/cards/4e/45/4e45b87053b11992b4f9d672e384d17d.png new file mode 100644 index 0000000..2316d78 Binary files /dev/null and b/resources/cards/4e/45/4e45b87053b11992b4f9d672e384d17d.png differ diff --git a/resources/cards/50/5b/505bf4c5653fec43955834e34ec972b6.png b/resources/cards/50/5b/505bf4c5653fec43955834e34ec972b6.png new file mode 100644 index 0000000..1f9d1f7 Binary files /dev/null and b/resources/cards/50/5b/505bf4c5653fec43955834e34ec972b6.png differ diff --git a/resources/cards/52/26/52267e4abd7a8a916f2544f2dcc22652.png b/resources/cards/52/26/52267e4abd7a8a916f2544f2dcc22652.png new file mode 100644 index 0000000..5352a62 Binary files /dev/null and b/resources/cards/52/26/52267e4abd7a8a916f2544f2dcc22652.png differ diff --git a/resources/cards/62/c7/62c7f9029e99617d3da083d601263243.png b/resources/cards/62/c7/62c7f9029e99617d3da083d601263243.png new file mode 100644 index 0000000..f1a739c Binary files /dev/null and b/resources/cards/62/c7/62c7f9029e99617d3da083d601263243.png differ diff --git a/resources/cards/65/cd/65cde5232852e7dde7f922e28a7653bb.png b/resources/cards/65/cd/65cde5232852e7dde7f922e28a7653bb.png new file mode 100644 index 0000000..0632007 Binary files /dev/null and b/resources/cards/65/cd/65cde5232852e7dde7f922e28a7653bb.png differ diff --git a/resources/cards/68/80/688033092237eb1fb3c6bfcdf43be6c5.png b/resources/cards/68/80/688033092237eb1fb3c6bfcdf43be6c5.png new file mode 100644 index 0000000..8239be7 Binary files /dev/null and b/resources/cards/68/80/688033092237eb1fb3c6bfcdf43be6c5.png differ diff --git a/resources/cards/75/49/75495dd042700e2b18da8f9a7fb53092.png b/resources/cards/75/49/75495dd042700e2b18da8f9a7fb53092.png new file mode 100644 index 0000000..7a3b743 Binary files /dev/null and b/resources/cards/75/49/75495dd042700e2b18da8f9a7fb53092.png differ diff --git a/resources/cards/77/1a/771a98a6059ab49065a88381d388af25.png b/resources/cards/77/1a/771a98a6059ab49065a88381d388af25.png new file mode 100644 index 0000000..042a1b9 Binary files /dev/null and b/resources/cards/77/1a/771a98a6059ab49065a88381d388af25.png differ diff --git a/resources/cards/78/34/783462006ea7700cd3ce9cf81531271e.png b/resources/cards/78/34/783462006ea7700cd3ce9cf81531271e.png new file mode 100644 index 0000000..f9e1ec1 Binary files /dev/null and b/resources/cards/78/34/783462006ea7700cd3ce9cf81531271e.png differ diff --git a/resources/cards/78/88/788828213d1587d63f32ccbee1cf934c.png b/resources/cards/78/88/788828213d1587d63f32ccbee1cf934c.png new file mode 100644 index 0000000..bb51109 Binary files /dev/null and b/resources/cards/78/88/788828213d1587d63f32ccbee1cf934c.png differ diff --git a/resources/cards/80/a1/80a1be42bd087780fb36ccbed93777e6.png b/resources/cards/80/a1/80a1be42bd087780fb36ccbed93777e6.png new file mode 100644 index 0000000..a298be2 Binary files /dev/null and b/resources/cards/80/a1/80a1be42bd087780fb36ccbed93777e6.png differ diff --git a/resources/cards/90/87/9087c80dee43164e8ebff940be11de7d.png b/resources/cards/90/87/9087c80dee43164e8ebff940be11de7d.png new file mode 100644 index 0000000..4dd8a19 Binary files /dev/null and b/resources/cards/90/87/9087c80dee43164e8ebff940be11de7d.png differ diff --git a/resources/cards/95/67/9567e1797dd6d7cab63824c8dbed79ce.png b/resources/cards/95/67/9567e1797dd6d7cab63824c8dbed79ce.png new file mode 100644 index 0000000..f9e1ec1 Binary files /dev/null and b/resources/cards/95/67/9567e1797dd6d7cab63824c8dbed79ce.png differ diff --git a/resources/cards/ad/42/ad42d146847ac4016add0911576d3c1f.png b/resources/cards/ad/42/ad42d146847ac4016add0911576d3c1f.png new file mode 100644 index 0000000..af252db Binary files /dev/null and b/resources/cards/ad/42/ad42d146847ac4016add0911576d3c1f.png differ diff --git a/resources/cards/b2/e9/b2e90173c24257cca05823ac68a969d1.png b/resources/cards/b2/e9/b2e90173c24257cca05823ac68a969d1.png new file mode 100644 index 0000000..26bab4b Binary files /dev/null and b/resources/cards/b2/e9/b2e90173c24257cca05823ac68a969d1.png differ diff --git a/resources/cards/bb/2c/bb2ca43576a194c74dad2c2cc20b019d.png b/resources/cards/bb/2c/bb2ca43576a194c74dad2c2cc20b019d.png new file mode 100644 index 0000000..2df47dc Binary files /dev/null and b/resources/cards/bb/2c/bb2ca43576a194c74dad2c2cc20b019d.png differ diff --git a/resources/cards/be/24/be2428c241ddbaddff7ebc4d58ecd361.png b/resources/cards/be/24/be2428c241ddbaddff7ebc4d58ecd361.png new file mode 100644 index 0000000..af252db Binary files /dev/null and b/resources/cards/be/24/be2428c241ddbaddff7ebc4d58ecd361.png differ diff --git a/resources/cards/bf/fd/bffdc6537e1c23db57fc6862688429d7.png b/resources/cards/bf/fd/bffdc6537e1c23db57fc6862688429d7.png new file mode 100644 index 0000000..971b901 Binary files /dev/null and b/resources/cards/bf/fd/bffdc6537e1c23db57fc6862688429d7.png differ diff --git a/resources/cards/c6/6a/c66a921ac2b308d3197d49a5c9c84c5b.png b/resources/cards/c6/6a/c66a921ac2b308d3197d49a5c9c84c5b.png new file mode 100644 index 0000000..7a9cc17 Binary files /dev/null and b/resources/cards/c6/6a/c66a921ac2b308d3197d49a5c9c84c5b.png differ diff --git a/resources/cards/cb/e8/cbe823e92ba74a6a1285ee8379efe01a.png b/resources/cards/cb/e8/cbe823e92ba74a6a1285ee8379efe01a.png new file mode 100644 index 0000000..5013228 Binary files /dev/null and b/resources/cards/cb/e8/cbe823e92ba74a6a1285ee8379efe01a.png differ diff --git a/resources/cards/d8/8d/d88d2da6914ade7332b4f056a037fd5c.png b/resources/cards/d8/8d/d88d2da6914ade7332b4f056a037fd5c.png new file mode 100644 index 0000000..0a4a35b Binary files /dev/null and b/resources/cards/d8/8d/d88d2da6914ade7332b4f056a037fd5c.png differ diff --git a/resources/cards/f3/dd/f3dd2dc864246d73970329b75ca4e412.png b/resources/cards/f3/dd/f3dd2dc864246d73970329b75ca4e412.png new file mode 100644 index 0000000..49bdfb1 Binary files /dev/null and b/resources/cards/f3/dd/f3dd2dc864246d73970329b75ca4e412.png differ