diff --git a/composer.json b/composer.json index 0729b89..18faaa0 100644 --- a/composer.json +++ b/composer.json @@ -15,8 +15,9 @@ "creocoder/yii2-nested-sets": "0.9.*", "php-forge/html": "dev-main", "sjaakp/yii2-icon": "^1.0", + "yii2-extensions/bootbox": "dev-main", + "yii2-extensions/bootstrap5":"dev-main", "yii2-extensions/core-library": "dev-main", - "yiisoft/yii2-bootstrap5":"^2.0", "yiisoft/yii2": "^2.2" }, "require-dev": { @@ -76,7 +77,9 @@ "sort-packages": true, "allow-plugins": { "yiisoft/yii2-composer": true, - "yiisoft/config": true + "yiisoft/config": true, + "composer/installers": true, + "oomphinc/composer-installers-extender": true } }, "scripts": { diff --git a/config/extension.php b/config/extension.php index b4d134b..c1b4ae9 100644 --- a/config/extension.php +++ b/config/extension.php @@ -17,5 +17,17 @@ ], ], ], + 'urlManager' => [ + 'enablePrettyUrl' => true, + 'showScriptName' => false, + 'rules' => [ + 'category/delete/' => 'category/delete', + 'category/disable/' => 'category/disable', + 'category/enable/' => 'category/enable', + 'category/index/' => 'category/index', + 'category/index/page//per-page/' => 'category/index', + 'category/update/' => 'category/update', + ], + ], ], ]; diff --git a/config/params-web.php b/config/params-web.php index d666ebe..3e78c7a 100644 --- a/config/params-web.php +++ b/config/params-web.php @@ -5,15 +5,22 @@ use Yii\Blog\UseCase\Category\CategoryController; return [ - 'app.aliases' => [ - '@yii-blog' => '@vendor/yii2-extensions/blog', - '@yii-blog/migration' => '@yii-blog/src/Framework/Migration', - ], 'app.controllerMap' => [ 'category' => [ 'class' => CategoryController::class, ], ], + 'app.menu.islogged' => [ + [ + 'label' => 'Category', + 'url' => ['/category/index'], + 'order' => 1, + 'category' => 'yii.user', + 'linkOptions' => [ + 'data-method' => 'post', + ], + ], + ], 'app.params' => [ 'icons' => '@npm/fortawesome--fontawesome-free/svgs/{family}/{name}.svg', ], diff --git a/config/params-console.php b/config/params.php similarity index 86% rename from config/params-console.php rename to config/params.php index 9dd5c8d..d0efc3d 100644 --- a/config/params-console.php +++ b/config/params.php @@ -3,7 +3,7 @@ declare(strict_types=1); return [ - 'console.aliases' => [ + 'app.aliases' => [ '@yii-blog' => '@vendor/yii2-extensions/blog', '@yii-blog/migration' => '@yii-blog/src/Framework/Migration', ], diff --git a/src/ActiveRecord/Category.php b/src/ActiveRecord/Category.php index ffca92c..f0500ae 100644 --- a/src/ActiveRecord/Category.php +++ b/src/ActiveRecord/Category.php @@ -5,10 +5,17 @@ namespace Yii\Blog\ActiveRecord; use creocoder\nestedsets\NestedSetsBehavior; +use yii\behaviors\SluggableBehavior; +use Yii\Blog\BlogModule; use Yii\Blog\Framework\Behavior\SortableBehavior; use Yii\Blog\Query\CategoryQuery; use yii\db\ActiveQuery; use yii\db\ActiveRecord; +use Yiisoft\Strings\Inflector; + +use function preg_match; +use function preg_replace; +use function strtolower; final class Category extends ActiveRecord { @@ -20,6 +27,22 @@ public function behaviors(): array 'treeAttribute' => 'tree', ], 'sorteable' => SortableBehavior::class, + 'slug' => [ + 'class' => SluggableBehavior::class, + 'attribute' => 'title', + 'ensureUnique' => true, + 'immutable' => true, + 'slugAttribute' => 'slug', + 'value' => function (): string { + $slug = (new Inflector)->toSlug($this->title); + + if (preg_match(BlogModule::SLUG_PATTERN, $slug) === false) { + $slug = preg_replace('/[^a-z0-9]+/', '-', strtolower($this->title)); + } + + return $slug; + }, + ], ]; } @@ -39,6 +62,7 @@ public function scenarios(): array parent::scenarios(), [ 'register' => ['title', 'description', 'image_file', 'slug', 'status'], + 'update' => ['title', 'description', 'image_file', 'slug', 'status'], ], ); } diff --git a/src/ActiveRecord/Seo.php b/src/ActiveRecord/Seo.php index 1892673..408713d 100644 --- a/src/ActiveRecord/Seo.php +++ b/src/ActiveRecord/Seo.php @@ -8,6 +8,16 @@ final class Seo extends ActiveRecord { + public function safeAttributes(): array + { + return [ + 'h1', + 'title', + 'keywords', + 'title', + ]; + } + public static function tableName(): string { return 'seo'; diff --git a/src/BlogModule.php b/src/BlogModule.php index b450458..4ee6145 100644 --- a/src/BlogModule.php +++ b/src/BlogModule.php @@ -8,16 +8,16 @@ final class BlogModule extends Module { - public const PHOTO_THUMB_WIDTH = 120; public const PHOTO_THUMB_HEIGHT = 90; - public const STATUS_OFF = 0; - public const STATUS_ON = 1; + public const PHOTO_THUMB_WIDTH = 120; + public const SLUG_PATTERN = '/^[0-9a-z-]{0,128}$/'; + public const STATUS_DISABLE = 0; + public const STATUS_ACTIVE = 1; public function __construct( $id, Module $module, public readonly bool $floatLabels = true, - public readonly string $slugPattern = '/^[0-9a-z-]{0,128}$/', array $config = [], ) { parent::__construct($id, $module, $config); diff --git a/src/Framework/EventHandler/CategoryRegisterEventHandler.php b/src/Framework/EventHandler/CategoryRegisterEventHandler.php index c8243b9..6a2f444 100644 --- a/src/Framework/EventHandler/CategoryRegisterEventHandler.php +++ b/src/Framework/EventHandler/CategoryRegisterEventHandler.php @@ -7,8 +7,11 @@ use Yii; use yii\base\BootstrapInterface; use yii\base\Event; +use Yii\Blog\UseCase\Category\CategoryEvent; +use Yii\Blog\UseCase\Category\Delete\DeleteAction; +use Yii\Blog\UseCase\Category\Enable\EnableAction; use Yii\Blog\UseCase\Category\Register\RegisterAction; -use Yii\Blog\UseCase\Category\Register\RegisterEvent; +use Yii\Blog\UseCase\Category\Update\UpdateAction; use yii\web\Application; final class CategoryRegisterEventHandler implements BootstrapInterface @@ -18,10 +21,45 @@ final class CategoryRegisterEventHandler implements BootstrapInterface */ public function bootstrap($app): void { + Event::on( + DeleteAction::class, + CategoryEvent::AFTER_DELETE, + static function () use ($app): void { + $app->session->setFlash('success', Yii::t('yii.blog', 'Your category has been successfully deleted.')); + }, + ); + + Event::on( + DeleteAction::class, + CategoryEvent::DELETE_ERROR, + static function () use ($app): void { + $app->session->setFlash('danger', Yii::t('yii.blog', 'Your category has not been deleted.')); + }, + ); + + Event::on( + DeleteAction::class, + CategoryEvent::DELETE_NODE_CATEGORY, + static function () use ($app): void { + $app->session->setFlash( + 'success', + Yii::t('yii.blog', 'Your category and its sub categories have been successfully deleted.'), + ); + }, + ); + + Event::on( + EnableAction::class, + CategoryEvent::AFTER_STATUS_ACTIVE, + static function () use ($app): void { + $app->session->setFlash('success', Yii::t('yii.blog', 'Your category has been successfully enabled.')); + }, + ); + Event::on( RegisterAction::class, - RegisterEvent::AFTER_REGISTER, - static function (RegisterEvent $registerEvent) use ($app): void { + CategoryEvent::AFTER_REGISTER, + static function (CategoryEvent $registerEvent) use ($app): void { match ($registerEvent->id) { null => $app->session->setFlash( 'success', @@ -34,5 +72,21 @@ static function (RegisterEvent $registerEvent) use ($app): void { }; }, ); + + Event::on( + UpdateAction::class, + CategoryEvent::AFTER_UPDATE, + static function () use ($app): void { + $app->session->setFlash('success', Yii::t('yii.blog', 'Your category has been successfully updated.')); + }, + ); + + Event::on( + UpdateAction::class, + CategoryEvent::NOT_FOUND, + static function () use ($app): void { + $app->session->setFlash('danger', Yii::t('yii.blog', 'Your category has not been found.')); + }, + ); } } diff --git a/src/Framework/Migration/M202411020800Category.php b/src/Framework/Migration/M202411020800Category.php index 8b52816..af2a945 100644 --- a/src/Framework/Migration/M202411020800Category.php +++ b/src/Framework/Migration/M202411020800Category.php @@ -22,7 +22,7 @@ public function up(): void 'lft' => $this->integer(), 'rgt' => $this->integer(), 'depth' => $this->integer(), - 'status' => $this->boolean()->defaultValue(1) + 'status' => $this->integer()->defaultValue(1), ], $this->tableOptions, ); diff --git a/src/UseCase/Category/CategoryController.php b/src/UseCase/Category/CategoryController.php index eff7f28..781a333 100644 --- a/src/UseCase/Category/CategoryController.php +++ b/src/UseCase/Category/CategoryController.php @@ -8,6 +8,8 @@ use Yii\Blog\ActiveRecord\Category; use Yii\CoreLibrary\Repository\FinderRepositoryInterface; use yii\data\ArrayDataProvider; +use yii\filters\AccessControl; +use yii\filters\VerbFilter; use yii\web\Controller; final class CategoryController extends Controller @@ -33,9 +35,47 @@ public function __construct( public function actions(): array { return [ + 'delete' => [ + 'class' => Delete\DeleteAction::class, + ], + 'disable' => [ + 'class' => Disable\DisableAction::class, + ], + 'enable' => [ + 'class' => Enable\EnableAction::class, + ], 'register' => [ 'class' => Register\RegisterAction::class, ], + 'update' => [ + 'class' => Update\UpdateAction::class, + ], + ]; + } + + public function behaviors(): array + { + return [ + 'access' => [ + 'class' => AccessControl::class, + 'rules' => [ + [ + 'allow' => true, + 'actions' => ['delete', 'disable', 'enable', 'index', 'register', 'update'], + 'roles' => ['@'], + ], + ], + ], + 'verbs' => [ + 'class' => VerbFilter::class, + 'actions' => [ + 'delete' => ['post'], + 'disable' => ['post'], + 'enable' => ['post'], + 'register' => ['post'], + 'update' => ['post'], + ], + ], ]; } diff --git a/src/UseCase/Category/CategoryEvent.php b/src/UseCase/Category/CategoryEvent.php new file mode 100644 index 0000000..f5c4f7b --- /dev/null +++ b/src/UseCase/Category/CategoryEvent.php @@ -0,0 +1,29 @@ + Category::class, 'message' => Yii::t('yii.blog', 'This title has already been taken.'), + 'when' => fn (): bool => $this->action === 'register', ], ['description', 'string', 'max' => 1024], ['image_file', 'image'], ['slug', 'string', 'max' => 128], - [ - 'slug', - 'match', - 'pattern' => $this->blogModule->slugPattern, - 'message' => Yii::t('yii.blog', 'Slug can contain only 0-9, a-z and "-" characters (max: 128).'), - ], - [ - 'slug', - 'unique', - 'targetClass' => Category::class, - 'message' => Yii::t('yii.blog', 'This slug has already been taken.'), - ], ['parent', 'string'], ['status', 'integer'], - ['status', 'default', 'value' => BlogModule::STATUS_ON], + [ + 'status', + 'default', + 'value' => BlogModule::STATUS_ACTIVE, + 'when' => fn (): bool => $this->action === 'register', + ], ]; } } diff --git a/src/UseCase/Category/Delete/DeleteAction.php b/src/UseCase/Category/Delete/DeleteAction.php new file mode 100644 index 0000000..3098470 --- /dev/null +++ b/src/UseCase/Category/Delete/DeleteAction.php @@ -0,0 +1,57 @@ +finderRepository->findByOneCondition($this->category, ['slug' => $slug]); + $id = $category->id ?? null; + + $registerEvent = new CategoryEvent($id); + + if ($category === null) { + $this->trigger(CategoryEvent::NOT_FOUND, $registerEvent); + + return $this->controller->redirect(['category/index']); + } + + $this->trigger(CategoryEvent::BEFORE_DELETE, $registerEvent); + + if ($category->depth === 0 && $category->deleteWithChildren() > 0) { + $this->trigger(CategoryEvent::DELETE_NODE_CATEGORY, $registerEvent); + + return $this->controller->redirect(['category/index']); + } + + match ($this->persintenceRepository->delete($category)) { + true => $this->trigger(CategoryEvent::AFTER_DELETE, $registerEvent), + false => $this->trigger(CategoryEvent::DELETE_ERROR, $registerEvent), + }; + + return $this->controller->redirect(['category/index']); + } +} diff --git a/src/UseCase/Category/Disable/DisableAction.php b/src/UseCase/Category/Disable/DisableAction.php new file mode 100644 index 0000000..37a5e3a --- /dev/null +++ b/src/UseCase/Category/Disable/DisableAction.php @@ -0,0 +1,51 @@ +finderRepository->findByOneCondition($this->category, ['slug' => $slug]); + $id = $category->id ?? null; + + $registerEvent = new CategoryEvent($id); + + if ($category === null) { + $this->trigger(CategoryEvent::NOT_FOUND, $registerEvent); + + return $this->controller->redirect(['category/index']); + } + + $this->trigger(CategoryEvent::BEFORE_STATUS_DISABLE, $registerEvent); + + if ($this->persistenceRepository->updateAtttributes($category, ['status' => BlogModule::STATUS_DISABLE])) { + $this->trigger(CategoryEvent::AFTER_STATUS_DISABLE, $registerEvent); + } + + return $this->controller->redirect(['category/index']); + } +} diff --git a/src/UseCase/Category/Enable/EnableAction.php b/src/UseCase/Category/Enable/EnableAction.php new file mode 100644 index 0000000..25078fb --- /dev/null +++ b/src/UseCase/Category/Enable/EnableAction.php @@ -0,0 +1,51 @@ +finderRepository->findByOneCondition($this->category, ['slug' => $slug]); + $id = $category->id ?? null; + + $registerEvent = new CategoryEvent($id); + + if ($category === null) { + $this->trigger(CategoryEvent::NOT_FOUND, $registerEvent); + + return $this->controller->redirect(['category/index']); + } + + $this->trigger(CategoryEvent::BEFORE_STATUS_ACTIVE, $registerEvent); + + if ($this->persistenceRepository->updateAtttributes($category, ['status' => BlogModule::STATUS_ACTIVE])) { + $this->trigger(CategoryEvent::AFTER_STATUS_ACTIVE, $registerEvent); + } + + return $this->controller->redirect(['category/index']); + } +} diff --git a/src/UseCase/Category/Register/RegisterAction.php b/src/UseCase/Category/Register/RegisterAction.php index e7cc7d6..921095a 100644 --- a/src/UseCase/Category/Register/RegisterAction.php +++ b/src/UseCase/Category/Register/RegisterAction.php @@ -4,14 +4,16 @@ namespace Yii\Blog\UseCase\Category\Register; +use Yii; use yii\base\Action; -use yii\base\Model; use Yii\Blog\ActiveRecord\Category; use Yii\Blog\BlogModule; +use Yii\Blog\UseCase\Category\CategoryEvent; use Yii\Blog\UseCase\Category\CategoryService; use Yii\Blog\Widget\Seo\SeoForm; use Yii\Blog\Widget\Seo\SeoService; use Yii\CoreLibrary\Validator\AjaxValidator; +use yii\db\Connection; use yii\web\Controller; use yii\web\Request; use yii\web\Response; @@ -23,6 +25,7 @@ public function __construct( Controller $controller, private readonly AjaxValidator $ajaxValidator, private readonly BlogModule $blogModule, + private readonly Connection $db, private readonly CategoryService $categoryService, private readonly RegisterService $registerService, private readonly Request $request, @@ -34,11 +37,11 @@ public function __construct( public function run(string $id = null): string|Response { - $categoryForm = new $this->controller->formModelClass($this->blogModule); + $categoryForm = new $this->controller->formModelClass($this->blogModule, $this->id); $this->ajaxValidator->validate($categoryForm); - $registerEvent = new RegisterEvent($id); - $this->trigger(RegisterEvent::BEFORE_REGISTER, $registerEvent); + $registerEvent = new CategoryEvent($id); + $this->trigger(CategoryEvent::BEFORE_REGISTER, $registerEvent); $seoForm = new SeoForm(); @@ -46,21 +49,25 @@ public function run(string $id = null): string|Response $categoryForm->load($this->request->post()) && $seoForm->load($this->request->post()) && $categoryForm->validate() && + $seoForm->validate() && $this->registerService->run($categoryForm, $id) && $this->seoService->run($seoForm, Category::class, $categoryForm->id) ) { - $this->trigger(RegisterEvent::AFTER_REGISTER, $registerEvent); + $this->trigger(CategoryEvent::AFTER_REGISTER, $registerEvent); return $this->controller->redirect(['category/index']); } return $this->controller->render( - 'register', + 'crud', [ 'blogModule' => $this->blogModule, + 'buttonTitle' => Yii::t('yii.blog', 'Register'), 'formModel' => $categoryForm, - 'nodeTree' => $this->categoryService->buildNodeTree(), 'id' => $id, + 'nodeTree' => $this->categoryService->buildNodeTree(), + 'seoForm' => $seoForm, + 'title' => Yii::t('yii.blog', 'Register category'), ], ); } diff --git a/src/UseCase/Category/Register/RegisterEvent.php b/src/UseCase/Category/Register/RegisterEvent.php deleted file mode 100644 index ab92e40..0000000 --- a/src/UseCase/Category/Register/RegisterEvent.php +++ /dev/null @@ -1,17 +0,0 @@ -finderRepository->findByOneCondition($this->category, ['slug' => $slug]); + $id = $category->id ?? null; + + $registerEvent = new CategoryEvent($id); + + if ($category === null) { + $this->trigger(CategoryEvent::NOT_FOUND, $registerEvent); + + return $this->controller->redirect(['category/index']); + } + + $seo = $category->seo; + + $this->trigger(CategoryEvent::BEFORE_UPDATE, $registerEvent); + + $categoryForm = new $this->controller->formModelClass($this->blogModule, $this->id); + $categoryForm->setAttributes($category->getAttributes()); + + $seoForm = new SeoForm(); + $seoForm->setAttributes($seo->getAttributes()); + + $this->ajaxValidator->validate($categoryForm); + $this->ajaxValidator->validate($seoForm); + + if ( + $categoryForm->load($this->request->post()) && + $seoForm->load($this->request->post()) && + $categoryForm->validate() && + $seoForm->validate() + ) { + match ($this->updateService->run($category, $categoryForm, $seoForm)) { + true => $this->trigger(CategoryEvent::AFTER_UPDATE, $registerEvent), + default => $this->trigger(CategoryEvent::NOT_UPDATE, $registerEvent), + }; + + return $this->controller->redirect(['category/index']); + } + + return $this->controller->render( + 'crud', + [ + 'blogModule' => $this->blogModule, + 'buttonTitle' => Yii::t('yii.blog', 'Update'), + 'formModel' => $categoryForm, + 'id' => $category->id === $category->tree ? null : $category->tree, + 'nodeTree' => $this->categoryService->buildNodeTree(), + 'seoForm' => $seoForm, + 'title' => Yii::t('yii.blog', 'Update category'), + ], + ); + } +} diff --git a/src/UseCase/Category/Update/UpdateService.php b/src/UseCase/Category/Update/UpdateService.php new file mode 100644 index 0000000..4139399 --- /dev/null +++ b/src/UseCase/Category/Update/UpdateService.php @@ -0,0 +1,29 @@ +setScenario('update'); + $category->setAttributes($categoryForm->getAttributes()); + + $seo = $category->seo; + $seo->setAttributes($seoForm->getAttributes()); + + return ($this->persistenceRepository->update($category) || $this->persistenceRepository->update($seo)); + } +} diff --git a/src/UseCase/Category/view/_form.php b/src/UseCase/Category/view/_form.php index 66b758b..a8d97c1 100644 --- a/src/UseCase/Category/view/_form.php +++ b/src/UseCase/Category/view/_form.php @@ -9,18 +9,21 @@ use PHPForge\Html\P; use PHPForge\Html\Select; use PHPForge\Html\Tag; -use yii\base\Model; use Yii\Blog\BlogModule; +use Yii\Blog\UseCase\Category\CategoryForm; +use Yii\Blog\Widget\Seo\SeoForm; use Yii\Blog\Widget\Seo\SeoWidget; use yii\bootstrap5\ActiveForm; +use yii\web\View; /** * @var BlogModule $blogModule - * @var Model $formModel + * @var CategoryForm $formModel * @var string|null $id * @var array $nodeTree + * @var SeoForm $seoForm * @var string $title - * @var yii\web\View $this + * @var View $this */ $this->title = $title; $tabInput = 1; @@ -48,10 +51,11 @@ Select::widget() ->ariaLabel('Select for parent category') ->autofocus(true) - ->class('form-select') + ->class('form-select mt-2') ->id('registerform-title') - ->name('CategoryForm[parent]') ->items($nodeTree) + ->labelContent(Yii::t('yii.blog', 'Parent category:')) + ->name('CategoryForm[parent]') ->tabIndex($tabInput++) ->value($id) ?> @@ -87,19 +91,19 @@ ->textInput( [ 'oninvalid' => 'this.setCustomValidity("' . Yii::t('yii.blog', 'Enter slug Here.') . '")', - 'required' => true, + 'required' => false, 'tabindex' => $tabInput++, ], ) ?> - [$form, $blogModule, $tabInput]]) ?> + [$form, $blogModule, $seoForm, $tabInput]]) ?> class('d-grid gap-2') ->content( Button::widget() ->class('btn btn-lg btn-primary btn-block mt-3') - ->content(Yii::t('yii.blog', 'Register')) + ->content($buttonTitle) ->name('category-button') ->submit() ->tabIndex(9) diff --git a/src/UseCase/Category/view/crud.php b/src/UseCase/Category/view/crud.php new file mode 100644 index 0000000..efc7b7b --- /dev/null +++ b/src/UseCase/Category/view/crud.php @@ -0,0 +1,31 @@ +render( + '_form', + [ + 'blogModule' => $blogModule, + 'buttonTitle' => $buttonTitle, + 'formModel' => $formModel, + 'id' => $id, + 'nodeTree' => $nodeTree, + 'seoForm' => $seoForm, + 'title' => $title, + ], +); diff --git a/src/UseCase/Category/view/index.php b/src/UseCase/Category/view/index.php index a727818..5c6295a 100644 --- a/src/UseCase/Category/view/index.php +++ b/src/UseCase/Category/view/index.php @@ -11,16 +11,18 @@ use PHPForge\Html\Span; use PHPForge\Html\Tag; use sjaakp\icon\Icon; -use yii\bootstrap5\BootstrapPluginAsset; +use yii\bootbox\BootboxAsset; use yii\grid\ActionColumn; use yii\grid\GridView; use yii\helpers\Url; use yii\web\View; +use yii\grid\GridViewAsset; /** * @var View $this */ -BootstrapPluginAsset::register($this); +BootboxAsset::register($this); +GridViewAsset::register($this); $this->title = Yii::t('yii.blog', 'Categories'); @@ -42,12 +44,13 @@ $value = I::widget()->class('caret'); } - return $value .= match ($model->depth > 0) { - true => A::widget() - ->class($model->status == 0 ? 'smooth' : '') - ->content($model->title) - ->href(Url::to(['/category/index', 'id' => $model->id])), - default => Span::widget()->class($model->status == 0 ? 'smooth' : '')->content($model->title), + return $value .= match($model->depth > 0) { + true => Span::widget() + ->class($model->status === '0' ? 'text-decoration-line-through' : 'text-primary') + ->content($model->title), + default => Span::widget() + ->class($model->status === '0' ? 'text-decoration-line-through' : '') + ->content($model->title), }; }, 'contentOptions' => function ($model) { @@ -58,7 +61,9 @@ ], [ 'attribute' => 'description', - 'contentOptions' => ['style' => 'text-align: justify;'], + 'contentOptions' => static fn(stdClass $model) => $model->status === '0' + ? ['class' => 'text-decoration-line-through', 'style' => 'text-align: justify;'] + : ['style' => 'text-align: justify;'], 'label' => Yii::t('yii.blog', 'Description'), 'headerOptions' => ['style' => 'min-width: 640px;width: 640px;'], ], @@ -68,44 +73,70 @@ 'header' => Yii::t('yii.blog', 'Actions'), 'headerOptions' => ['class' => 'text-center'], 'buttons' => [ - 'delete' => function ($url) { + 'delete' => function (string $url, stdClass $model) { return A::widget() ->class('border-0 fa-stack text-danger') ->content( Icon::renderIcon('solid', 'circle', ['class' => 'fa-stack-2x']), Icon::renderIcon('solid', 'trash', ['class' => 'fa-stack-1x fa-inverse']), ) - ->href($url) - ->title(Yii::t('yii.blog', 'Delete')) - ->render(); - }, - 'update' => function ($url) { - return A::widget() - ->class('border-0 fa-stack text-primary') - ->content( - Icon::renderIcon('solid', 'circle', ['class' => 'fa-stack-2x']), - Icon::renderIcon('solid', 'pen-to-square', ['class' => 'fa-stack-1x fa-inverse']), + ->dataAttributes( + [ + 'method' => 'POST', + 'confirm' => Yii::t('yii.blog', 'Are you sure to delete this user?'), + ], ) - ->href($url) - ->title(Yii::t('yii.blog', 'Update')) + ->href(Url::to(['/category/delete', 'slug' => $model->slug])) + ->title(Yii::t('yii.blog', 'Delete')) ->render(); }, - 'off' => static function (string $url, stdClass $model): string { + 'status' => static function (string $url, stdClass $model): string { return match ($model->status) { '1' => A::widget() - ->class('border-0 fa-stack text-dark-emphasis') + ->class('border-0 fa-stack text-danger') ->content( Icon::renderIcon('solid', 'circle', ['class' => 'fa-stack-2x']), Icon::renderIcon('solid', 'eye-slash', ['class' => 'fa-stack-1x fa-inverse']), ) - ->href($url) - ->title(Yii::t('yii.blog', 'Turn off')) + ->dataAttributes( + [ + 'method' => 'POST', + 'confirm' => Yii::t('yii.blog', 'Are you sure to disable this category?'), + ], + ) + ->href(Url::to(['/category/disable', 'slug' => $model->slug])) + ->title(Yii::t('yii.blog', 'Disable category')) + ->render(), + default => A::widget() + ->class('border-0 fa-stack text-success') + ->content( + Icon::renderIcon('solid', 'circle', ['class' => 'fa-stack-2x']), + Icon::renderIcon('solid', 'eye', ['class' => 'fa-stack-1x fa-inverse']), + ) + ->dataAttributes( + [ + 'method' => 'POST', + 'confirm' => Yii::t('yii.blog', 'Are you sure to enable this category?'), + ], + ) + ->href(Url::to(['/category/enable', 'slug' => $model->slug])) + ->title(Yii::t('yii.blog', 'Enable category')) ->render(), - default => '', }; }, + 'update' => static function (string $url, stdClass $model) { + return A::widget() + ->class('border-0 fa-stack text-primary') + ->content( + Icon::renderIcon('solid', 'circle', ['class' => 'fa-stack-2x']), + Icon::renderIcon('solid', 'pen-to-square', ['class' => 'fa-stack-1x fa-inverse']), + ) + ->href(Url::to(['/category/update', 'slug' => $model->slug])) + ->title(Yii::t('yii.blog', 'Update')) + ->render(); + }, ], - 'template' => '{update} {delete} {off}', + 'template' => '{update} {delete} {status}', ], [ 'class' => ActionColumn::class, @@ -168,6 +199,9 @@ 'linkOptions' => ['class' => 'page-link'], 'options' => ['class' => 'pagination float-end ml-auto mb-5'], ], + 'rowOptions' => function ($model) { + return $model->depth == 0 ? ['class' => 'fw-bold'] : []; + }, 'tableOptions' => ['class' => 'table table-borderless'], ], ), diff --git a/src/UseCase/Category/view/register.php b/src/UseCase/Category/view/register.php deleted file mode 100644 index cae4a63..0000000 --- a/src/UseCase/Category/view/register.php +++ /dev/null @@ -1,19 +0,0 @@ -render( - '_form', - [ - 'blogModule' => $blogModule, - 'formModel' => $formModel, - 'id' => $id, - 'nodeTree' => $nodeTree, - 'title' => Yii::t('yii.blog', 'Register category'), - ], -); diff --git a/src/Widget/Seo/SeoForm.php b/src/Widget/Seo/SeoForm.php index 6bab59d..ff333c7 100644 --- a/src/Widget/Seo/SeoForm.php +++ b/src/Widget/Seo/SeoForm.php @@ -27,15 +27,10 @@ public function rules(): array public function attributeLabels(): array { return [ - 'description' => Yii::t('yii.blog', 'Seo Description'), 'h1' => Yii::t('yii.blog', 'Seo H1'), - 'keywords' => Yii::t('yii.blog', 'Seo Keywords'), 'title' => Yii::t('yii.blog', 'Seo Title'), + 'keywords' => Yii::t('yii.blog', 'Seo Keywords'), + 'description' => Yii::t('yii.blog', 'Seo Description'), ]; } - - public function isEmpty(): bool - { - return (!$this->h1 && !$this->title && !$this->keywords && !$this->description); - } } diff --git a/src/Widget/Seo/SeoWidget.php b/src/Widget/Seo/SeoWidget.php index c99dc93..49ba6ff 100644 --- a/src/Widget/Seo/SeoWidget.php +++ b/src/Widget/Seo/SeoWidget.php @@ -16,6 +16,7 @@ final class SeoWidget extends Widget public function __construct( private readonly ActiveForm $form, private readonly BlogModule $blogModule, + private readonly SeoForm $seoForm, private int $tabInput = 1, $config = [] ) { @@ -24,14 +25,12 @@ public function __construct( public function run(): string { - $seoModel = new SeoForm(); - return $this->render( 'index', [ 'blogModule' => $this->blogModule, 'form' => $this->form, - 'formModel' => $seoModel, + 'formModel' => $this->seoForm, 'tabInput' => $this->tabInput, ], ); diff --git a/src/Widget/Seo/view/index.php b/src/Widget/Seo/view/index.php index f88e894..ef114f5 100644 --- a/src/Widget/Seo/view/index.php +++ b/src/Widget/Seo/view/index.php @@ -31,5 +31,5 @@ field($formModel, 'h1')->textInput(['tabindex' => $tabInput++]) ?> field($formModel, 'title')->textInput(['tabindex' => $tabInput++]) ?> field($formModel, 'keywords')->textInput(['tabindex' => $tabInput++]) ?> - field($formModel, 'description')->textInput(['tabindex' => $tabInput++]) ?> + field($formModel, 'description')->textArea(['style' => 'height: 120px', 'tabindex' => $tabInput++]) ?>