From a4c28d4c319e9edcec3fcfe1e0f6156c13b7f135 Mon Sep 17 00:00:00 2001 From: thapacodes4u Date: Mon, 13 Oct 2025 16:43:33 +0545 Subject: [PATCH 1/4] fix: resolving dep. issues when migrating to filamentphpv4 --- .lando.dist.yml | 4 +- composer.json | 6 +- database/seeders/BannerSeeder.php | 4 +- .../Resources/BannerPositionResource.php | 36 +++++---- .../Pages/CreateBannerPosition.php | 7 +- .../Pages/EditBannerPosition.php | 6 +- .../Pages/ListBannerPositions.php | 6 +- .../Pages/ViewBannerPosition.php | 3 +- .../BannerRelationManager.php | 51 ++++++------ src/Admin/Filament/Resources/MenuResource.php | 46 +++++------ .../MenuResource/Pages/CreateMenu.php | 7 +- .../Resources/MenuResource/Pages/EditMenu.php | 6 +- .../MenuResource/Pages/ListMenus.php | 6 +- .../MenuResource/Pages/SortMenuItems.php | 79 ++++++++----------- .../MenuItemsRelationManager.php | 60 +++++++------- src/Admin/Filament/Resources/PageResource.php | 25 +++--- .../Filament/Resources/SectionResource.php | 25 +++--- src/Enums/MenuItemType.php | 8 +- src/Policies/BannerPositionPolicy.php | 20 ++--- tests/Pest.php | 1 + 20 files changed, 198 insertions(+), 208 deletions(-) diff --git a/.lando.dist.yml b/.lando.dist.yml index 75bac01..2b2a147 100644 --- a/.lando.dist.yml +++ b/.lando.dist.yml @@ -2,11 +2,9 @@ name: eclipsephp-cms services: appserver: - type: php:custom + type: php:8.3 xdebug: "debug,develop,coverage" via: cli - overrides: - image: slimdeluxe/php:8.2-v1.2 tooling: php: service: appserver diff --git a/composer.json b/composer.json index f102d54..418a0ab 100644 --- a/composer.json +++ b/composer.json @@ -41,14 +41,14 @@ } }, "require": { - "php": "^8.2", + "php": "^8.3", "bezhansalleh/filament-shield": "^4.0", "datalinx/php-utils": "^2.5", "eclipsephp/common": "dev-main", "filament/filament": "^4.0", - "filament/spatie-laravel-translatable-plugin": "^3.3", + "lara-zeus/spatie-translatable": "^1.0", "spatie/image": "^3.8", - "solution-forest/filament-tree": "^2.1", + "solution-forest/filament-tree": "^3.0", "spatie/laravel-package-tools": "^1.19" }, "require-dev": { diff --git a/database/seeders/BannerSeeder.php b/database/seeders/BannerSeeder.php index 3ade359..30b1e3f 100644 --- a/database/seeders/BannerSeeder.php +++ b/database/seeders/BannerSeeder.php @@ -58,7 +58,7 @@ private function addImages($banner, $type, $suffix): void $banner->images()->create([ 'type_id' => $type->id, - 'file' => $hidpiFilename, + 'file' => ['en' => $hidpiFilename, 'sl' => $hidpiFilename], 'image_width' => $hidpiWidth, 'image_height' => $hidpiHeight, 'is_hidpi' => true, @@ -74,7 +74,7 @@ private function addImages($banner, $type, $suffix): void $banner->images()->create([ 'type_id' => $type->id, - 'file' => $regularFilename, + 'file' => ['en' => $regularFilename, 'sl' => $regularFilename], 'image_width' => $type->image_width, 'image_height' => $type->image_height, 'is_hidpi' => false, diff --git a/src/Admin/Filament/Resources/BannerPositionResource.php b/src/Admin/Filament/Resources/BannerPositionResource.php index d9e2d12..457f751 100644 --- a/src/Admin/Filament/Resources/BannerPositionResource.php +++ b/src/Admin/Filament/Resources/BannerPositionResource.php @@ -5,14 +5,16 @@ use BezhanSalleh\FilamentShield\Contracts\HasShieldPermissions; use Eclipse\Cms\Admin\Filament\Resources\BannerPositionResource\Pages; use Eclipse\Cms\Models\Banner\Position as BannerPosition; +use Filament\Actions; use Filament\Forms; -use Filament\Forms\Form; -use Filament\Resources\Concerns\Translatable; use Filament\Resources\Resource; +use Filament\Schemas\Components\Section; +use Filament\Schemas\Schema; use Filament\Tables; use Filament\Tables\Table; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\SoftDeletingScope; +use LaraZeus\SpatieTranslatable\Resources\Concerns\Translatable; class BannerPositionResource extends Resource implements HasShieldPermissions { @@ -20,9 +22,9 @@ class BannerPositionResource extends Resource implements HasShieldPermissions protected static ?string $model = BannerPosition::class; - protected static ?string $navigationIcon = 'heroicon-o-photo'; + protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-photo'; - protected static ?string $navigationGroup = 'CMS'; + protected static string|\UnitEnum|null $navigationGroup = 'CMS'; protected static ?string $modelLabel = 'Banner'; @@ -44,11 +46,12 @@ public static function getPermissionPrefixes(): array ]; } - public static function form(Form $form): Form + public static function form(Schema $schema): Schema { - return $form - ->schema([ - Forms\Components\Section::make('Position') + return $schema + ->components([ + Section::make('Position') + ->columnSpanFull() ->hidden( fn (string $context): bool => ($context === 'view') ) @@ -64,7 +67,8 @@ public static function form(Form $form): Form ->alphaDash(), ]), - Forms\Components\Section::make('Image Types') + Section::make('Image Types') + ->columnSpanFull() ->hidden( fn (string $context): bool => ($context === 'view') ) @@ -131,17 +135,17 @@ public static function table(Table $table): Table ->filters([ Tables\Filters\TrashedFilter::make(), ]) - ->actions([ - Tables\Actions\ViewAction::make() + ->recordActions([ + Actions\ViewAction::make() ->label('Manage banners'), - Tables\Actions\EditAction::make() + Actions\EditAction::make() ->label('Edit position'), - Tables\Actions\DeleteAction::make() + Actions\DeleteAction::make() ->label('Delete position'), ]) - ->bulkActions([ - Tables\Actions\BulkActionGroup::make([ - Tables\Actions\DeleteBulkAction::make(), + ->toolbarActions([ + Actions\BulkActionGroup::make([ + Actions\DeleteBulkAction::make(), ]), ]); } diff --git a/src/Admin/Filament/Resources/BannerPositionResource/Pages/CreateBannerPosition.php b/src/Admin/Filament/Resources/BannerPositionResource/Pages/CreateBannerPosition.php index 96c4a2c..dc14978 100644 --- a/src/Admin/Filament/Resources/BannerPositionResource/Pages/CreateBannerPosition.php +++ b/src/Admin/Filament/Resources/BannerPositionResource/Pages/CreateBannerPosition.php @@ -3,12 +3,13 @@ namespace Eclipse\Cms\Admin\Filament\Resources\BannerPositionResource\Pages; use Eclipse\Cms\Admin\Filament\Resources\BannerPositionResource; -use Filament\Actions; use Filament\Resources\Pages\CreateRecord; +use LaraZeus\SpatieTranslatable\Actions\LocaleSwitcher; +use LaraZeus\SpatieTranslatable\Resources\Pages\CreateRecord\Concerns\Translatable; class CreateBannerPosition extends CreateRecord { - use CreateRecord\Concerns\Translatable; + use Translatable; protected static string $resource = BannerPositionResource::class; @@ -22,7 +23,7 @@ protected function getRedirectUrl(): string protected function getHeaderActions(): array { return [ - Actions\LocaleSwitcher::make(), + LocaleSwitcher::make(), ]; } } diff --git a/src/Admin/Filament/Resources/BannerPositionResource/Pages/EditBannerPosition.php b/src/Admin/Filament/Resources/BannerPositionResource/Pages/EditBannerPosition.php index 0540f75..bc74979 100644 --- a/src/Admin/Filament/Resources/BannerPositionResource/Pages/EditBannerPosition.php +++ b/src/Admin/Filament/Resources/BannerPositionResource/Pages/EditBannerPosition.php @@ -5,10 +5,12 @@ use Eclipse\Cms\Admin\Filament\Resources\BannerPositionResource; use Filament\Actions; use Filament\Resources\Pages\EditRecord; +use LaraZeus\SpatieTranslatable\Actions\LocaleSwitcher; +use LaraZeus\SpatieTranslatable\Resources\Pages\EditRecord\Concerns\Translatable; class EditBannerPosition extends EditRecord { - use EditRecord\Concerns\Translatable; + use Translatable; protected static ?string $breadcrumb = 'Edit Position'; @@ -17,6 +19,7 @@ class EditBannerPosition extends EditRecord protected function getHeaderActions(): array { return [ + LocaleSwitcher::make(), Actions\ViewAction::make() ->color('primary') ->icon('heroicon-o-photo') @@ -24,7 +27,6 @@ protected function getHeaderActions(): array Actions\DeleteAction::make() ->icon('heroicon-o-trash') ->label('Delete Position'), - Actions\LocaleSwitcher::make(), ]; } } diff --git a/src/Admin/Filament/Resources/BannerPositionResource/Pages/ListBannerPositions.php b/src/Admin/Filament/Resources/BannerPositionResource/Pages/ListBannerPositions.php index e372677..13237af 100644 --- a/src/Admin/Filament/Resources/BannerPositionResource/Pages/ListBannerPositions.php +++ b/src/Admin/Filament/Resources/BannerPositionResource/Pages/ListBannerPositions.php @@ -5,20 +5,22 @@ use Eclipse\Cms\Admin\Filament\Resources\BannerPositionResource; use Filament\Actions; use Filament\Resources\Pages\ListRecords; +use LaraZeus\SpatieTranslatable\Actions\LocaleSwitcher; +use LaraZeus\SpatieTranslatable\Resources\Pages\ListRecords\Concerns\Translatable; class ListBannerPositions extends ListRecords { - use ListRecords\Concerns\Translatable; + use Translatable; protected static string $resource = BannerPositionResource::class; protected function getHeaderActions(): array { return [ + LocaleSwitcher::make(), Actions\CreateAction::make() ->icon('heroicon-o-plus-circle') ->label('New Position'), - Actions\LocaleSwitcher::make(), ]; } } diff --git a/src/Admin/Filament/Resources/BannerPositionResource/Pages/ViewBannerPosition.php b/src/Admin/Filament/Resources/BannerPositionResource/Pages/ViewBannerPosition.php index 5753d5e..6c85289 100644 --- a/src/Admin/Filament/Resources/BannerPositionResource/Pages/ViewBannerPosition.php +++ b/src/Admin/Filament/Resources/BannerPositionResource/Pages/ViewBannerPosition.php @@ -5,10 +5,11 @@ use Eclipse\Cms\Admin\Filament\Resources\BannerPositionResource; use Filament\Actions; use Filament\Resources\Pages\ViewRecord; +use LaraZeus\SpatieTranslatable\Resources\Pages\ViewRecord\Concerns\Translatable; class ViewBannerPosition extends ViewRecord { - use ViewRecord\Concerns\Translatable; + use Translatable; protected static ?string $breadcrumb = 'Manage Banners'; diff --git a/src/Admin/Filament/Resources/BannerPositionResource/RelationManagers/BannerRelationManager.php b/src/Admin/Filament/Resources/BannerPositionResource/RelationManagers/BannerRelationManager.php index 68264e0..a5cbb79 100644 --- a/src/Admin/Filament/Resources/BannerPositionResource/RelationManagers/BannerRelationManager.php +++ b/src/Admin/Filament/Resources/BannerPositionResource/RelationManagers/BannerRelationManager.php @@ -6,18 +6,19 @@ use Eclipse\Cms\Rules\BannerImageDimensionRule; use Eclipse\Common\Filament\Tables\Columns\ImageColumn; use Eclipse\Common\Helpers\MediaHelper; +use Filament\Actions; use Filament\Forms; -use Filament\Forms\Components\FileUpload; -use Filament\Forms\Form; -use Filament\Forms\Get; use Filament\Infolists; -use Filament\Infolists\Infolist; -use Filament\Resources\RelationManagers\Concerns\Translatable; use Filament\Resources\RelationManagers\RelationManager; +use Filament\Schemas\Components\Grid; +use Filament\Schemas\Components\Utilities\Get; +use Filament\Schemas\Schema; use Filament\Tables; use Filament\Tables\Table; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\SoftDeletingScope; +use LaraZeus\SpatieTranslatable\Actions\LocaleSwitcher; +use LaraZeus\SpatieTranslatable\Resources\RelationManagers\Concerns\Translatable; class BannerRelationManager extends RelationManager { @@ -49,11 +50,10 @@ protected function getDynamicImageColumns(): array $locale = $this->activeLocale ?? app()->getLocale(); $image = $record->images->where('type_id', $imageType->id)->first(); - if ($image && $image->getTranslation('file', $locale)) { + if ($image?->getTranslation('file', $locale)) { return $image->getTranslation('file', $locale); } - // Fallback for test environment when MediaHelper is not autoloaded if (class_exists(MediaHelper::class)) { return MediaHelper::getPlaceholderImageUrl( 'Not Found', @@ -74,12 +74,12 @@ protected function getDynamicImageColumns(): array })->toArray(); } - public function form(Form $form): Form + public function form(Schema $schema): Schema { - return $form - ->schema([ - Forms\Components\Section::make() - ->compact() + return $schema + ->components([ + Grid::make() + ->columnSpanFull() ->schema([ Forms\Components\TextInput::make('name') ->required() @@ -106,7 +106,7 @@ public function form(Form $form): Form Forms\Components\Hidden::make('is_hidpi'), Forms\Components\Hidden::make('image_width'), Forms\Components\Hidden::make('image_height'), - FileUpload::make('file') + Forms\Components\FileUpload::make('file') ->hiddenLabel() ->image() ->directory('banners') @@ -213,10 +213,10 @@ function (Get $get): BannerImageDimensionRule|string { ]); } - public function infolist(Infolist $infolist): Infolist + public function infolist(Schema $schema): Schema { - return $infolist - ->schema([ + return $schema + ->components([ Infolists\Components\TextEntry::make('name'), Infolists\Components\TextEntry::make('link') @@ -230,7 +230,7 @@ public function infolist(Infolist $infolist): Infolist ->boolean() ->label('Open in new tab'), - Infolists\Components\Grid::make() + Grid::make() ->columnSpanFull() ->schema( fn (Banner $record) => $this->getOwnerRecord()->imageTypes()->get() @@ -238,7 +238,6 @@ public function infolist(Infolist $infolist): Infolist $locale = app()->getLocale(); $image = $record->images->where('type_id', $imageType->id)->first(); - // Only show image entry if there's an actual image if (! $image || ! $image->getTranslation('file', $locale)) { return null; } @@ -250,7 +249,7 @@ public function infolist(Infolist $infolist): Infolist ->height('auto') ->getStateUsing(fn () => $image->getTranslation('file', $locale)); }) - ->filter() // Remove null entries + ->filter() ->toArray() ), ]); @@ -306,8 +305,8 @@ public function table(Table $table): Table ]) ->reorderable('sort') ->headerActions([ - Tables\Actions\LocaleSwitcher::make(), - Tables\Actions\CreateAction::make() + LocaleSwitcher::make(), + Actions\CreateAction::make() ->icon('heroicon-o-plus-circle') ->label('Add banner') ->mutateFormDataUsing(function (array $data): array { @@ -319,13 +318,13 @@ public function table(Table $table): Table }), ]) ->actions([ - Tables\Actions\ViewAction::make(), - Tables\Actions\EditAction::make(), - Tables\Actions\DeleteAction::make(), + Actions\ViewAction::make(), + Actions\EditAction::make(), + Actions\DeleteAction::make(), ]) ->bulkActions([ - Tables\Actions\BulkActionGroup::make([ - Tables\Actions\DeleteBulkAction::make(), + Actions\BulkActionGroup::make([ + Actions\DeleteBulkAction::make(), ]), ]) ->modifyQueryUsing( diff --git a/src/Admin/Filament/Resources/MenuResource.php b/src/Admin/Filament/Resources/MenuResource.php index b43578c..ad1ac2b 100644 --- a/src/Admin/Filament/Resources/MenuResource.php +++ b/src/Admin/Filament/Resources/MenuResource.php @@ -6,16 +6,17 @@ use Eclipse\Cms\Admin\Filament\Resources\MenuResource\Pages; use Eclipse\Cms\Admin\Filament\Resources\MenuResource\RelationManagers; use Eclipse\Cms\Models\Menu; +use Filament\Actions; use Filament\Forms; -use Filament\Forms\Form; use Filament\Infolists; -use Filament\Infolists\Infolist; -use Filament\Resources\Concerns\Translatable; use Filament\Resources\Resource; +use Filament\Schemas\Components\Section; +use Filament\Schemas\Schema; use Filament\Tables; use Filament\Tables\Table; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\SoftDeletingScope; +use LaraZeus\SpatieTranslatable\Resources\Concerns\Translatable; class MenuResource extends Resource implements HasShieldPermissions { @@ -23,17 +24,18 @@ class MenuResource extends Resource implements HasShieldPermissions protected static ?string $model = Menu::class; - protected static ?string $navigationIcon = 'heroicon-o-bars-3'; + protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-bars-3'; - protected static ?string $navigationGroup = 'CMS'; + protected static string|\UnitEnum|null $navigationGroup = 'CMS'; protected static ?int $navigationSort = 3; - public static function form(Form $form): Form + public static function form(Schema $schema): Schema { - return $form - ->schema([ - Forms\Components\Section::make() + return $schema + ->components([ + Section::make() + ->columnSpanFull() ->compact() ->columns(2) ->schema([ @@ -50,11 +52,11 @@ public static function form(Form $form): Form ]); } - public static function infolist(Infolist $infolist): Infolist + public static function infolist(Schema $schema): Schema { - return $infolist - ->schema([ - Infolists\Components\Section::make('Information') + return $schema + ->components([ + Section::make('Information') ->compact() ->schema([ Infolists\Components\TextEntry::make('title') @@ -72,7 +74,7 @@ public static function infolist(Infolist $infolist): Infolist ]) ->columns(3), - Infolists\Components\Section::make('Timestamps') + Section::make('Timestamps') ->compact() ->schema([ Infolists\Components\TextEntry::make('created_at') @@ -117,15 +119,15 @@ public static function table(Table $table): Table ->filters([ Tables\Filters\TrashedFilter::make(), ]) - ->actions([ - Tables\Actions\EditAction::make(), - Tables\Actions\DeleteAction::make(), + ->recordActions([ + Actions\EditAction::make(), + Actions\DeleteAction::make(), ]) - ->bulkActions([ - Tables\Actions\BulkActionGroup::make([ - Tables\Actions\DeleteBulkAction::make(), - Tables\Actions\ForceDeleteBulkAction::make(), - Tables\Actions\RestoreBulkAction::make(), + ->toolbarActions([ + Actions\BulkActionGroup::make([ + Actions\DeleteBulkAction::make(), + Actions\ForceDeleteBulkAction::make(), + Actions\RestoreBulkAction::make(), ]), ]); } diff --git a/src/Admin/Filament/Resources/MenuResource/Pages/CreateMenu.php b/src/Admin/Filament/Resources/MenuResource/Pages/CreateMenu.php index 1ea29b5..8307aee 100644 --- a/src/Admin/Filament/Resources/MenuResource/Pages/CreateMenu.php +++ b/src/Admin/Filament/Resources/MenuResource/Pages/CreateMenu.php @@ -3,20 +3,21 @@ namespace Eclipse\Cms\Admin\Filament\Resources\MenuResource\Pages; use Eclipse\Cms\Admin\Filament\Resources\MenuResource; -use Filament\Actions; use Filament\Facades\Filament; use Filament\Resources\Pages\CreateRecord; +use LaraZeus\SpatieTranslatable\Actions\LocaleSwitcher; +use LaraZeus\SpatieTranslatable\Resources\Pages\CreateRecord\Concerns\Translatable; class CreateMenu extends CreateRecord { - use CreateRecord\Concerns\Translatable; + use Translatable; protected static string $resource = MenuResource::class; protected function getHeaderActions(): array { return [ - Actions\LocaleSwitcher::make(), + LocaleSwitcher::make(), ]; } diff --git a/src/Admin/Filament/Resources/MenuResource/Pages/EditMenu.php b/src/Admin/Filament/Resources/MenuResource/Pages/EditMenu.php index bc9f557..6f90310 100644 --- a/src/Admin/Filament/Resources/MenuResource/Pages/EditMenu.php +++ b/src/Admin/Filament/Resources/MenuResource/Pages/EditMenu.php @@ -5,17 +5,19 @@ use Eclipse\Cms\Admin\Filament\Resources\MenuResource; use Filament\Actions; use Filament\Resources\Pages\EditRecord; +use LaraZeus\SpatieTranslatable\Actions\LocaleSwitcher; +use LaraZeus\SpatieTranslatable\Resources\Pages\EditRecord\Concerns\Translatable; class EditMenu extends EditRecord { - use EditRecord\Concerns\Translatable; + use Translatable; protected static string $resource = MenuResource::class; protected function getHeaderActions(): array { return [ - Actions\LocaleSwitcher::make(), + LocaleSwitcher::make(), Actions\DeleteAction::make(), Actions\ForceDeleteAction::make(), Actions\RestoreAction::make(), diff --git a/src/Admin/Filament/Resources/MenuResource/Pages/ListMenus.php b/src/Admin/Filament/Resources/MenuResource/Pages/ListMenus.php index 3d0e320..36a5255 100644 --- a/src/Admin/Filament/Resources/MenuResource/Pages/ListMenus.php +++ b/src/Admin/Filament/Resources/MenuResource/Pages/ListMenus.php @@ -5,17 +5,19 @@ use Eclipse\Cms\Admin\Filament\Resources\MenuResource; use Filament\Actions; use Filament\Resources\Pages\ListRecords; +use LaraZeus\SpatieTranslatable\Actions\LocaleSwitcher; +use LaraZeus\SpatieTranslatable\Resources\Pages\ListRecords\Concerns\Translatable; class ListMenus extends ListRecords { - use ListRecords\Concerns\Translatable; + use Translatable; protected static string $resource = MenuResource::class; protected function getHeaderActions(): array { return [ - Actions\LocaleSwitcher::make(), + LocaleSwitcher::make(), Actions\CreateAction::make(), ]; } diff --git a/src/Admin/Filament/Resources/MenuResource/Pages/SortMenuItems.php b/src/Admin/Filament/Resources/MenuResource/Pages/SortMenuItems.php index babfaf7..4846fca 100644 --- a/src/Admin/Filament/Resources/MenuResource/Pages/SortMenuItems.php +++ b/src/Admin/Filament/Resources/MenuResource/Pages/SortMenuItems.php @@ -4,28 +4,25 @@ use Eclipse\Cms\Admin\Filament\Resources\MenuResource; use Eclipse\Cms\Enums\MenuItemType; +use Eclipse\Cms\Models\Menu; use Eclipse\Cms\Models\Menu\Item; -use Filament\Actions; -use Filament\Resources\Concerns\Translatable; -use Filament\Resources\Pages\Concerns\InteractsWithRecord; -use Filament\Resources\Pages\Page; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; -use SolutionForest\FilamentTree\Components\Tree; -use SolutionForest\FilamentTree\Concern\InteractWithTree; -use SolutionForest\FilamentTree\Contract\HasTree; +use LaraZeus\SpatieTranslatable\Actions\LocaleSwitcher; +use SolutionForest\FilamentTree\Concern\TreeRecords\Translatable; +use SolutionForest\FilamentTree\Resources\Pages\TreePage as BasePage; -class SortMenuItems extends Page implements HasTree +class SortMenuItems extends BasePage { - use InteractsWithRecord, InteractWithTree, Translatable; + use Translatable; protected static string $resource = MenuResource::class; - protected static string $view = 'filament-tree::pages.tree'; + protected static int $maxDepth = 5; - public function mount(int|string $record): void + protected function getMenu(): Menu { - $this->record = $this->resolveRecord($record); + return Menu::findOrFail(request()->route('record')); } public function getTitle(): string @@ -35,49 +32,26 @@ public function getTitle(): string public function getSubheading(): ?string { - return "Drag and drop to reorder menu items for: {$this->record->title}"; + return "Drag and drop to reorder menu items for: {$this->getMenu()->title}"; } protected function getTreeQuery(): Builder { - return Item::query()->where('menu_id', $this->record->id); + return Item::query() + ->with('children') + ->where('menu_id', $this->getMenu()->id); } - public function getBreadcrumbs(): array + public function getTreeRecordTitle(?Model $record = null): string { - $resource = static::getResource(); - - $breadcrumbs = [ - $resource::getUrl() => $resource::getBreadcrumb(), - $resource::getUrl('edit', [ - 'record' => $this->record->id, - ]) => "Edit {$this->record->title}", - ...(filled($breadcrumb = $this->getBreadcrumb()) ? [$breadcrumb] : []), - ]; - - if (filled($cluster = static::getCluster())) { - return $cluster::unshiftClusterBreadcrumbs($breadcrumbs); + if (! $record) { + return ''; } - return $breadcrumbs; - } - - public function getModel(): string - { - return Item::class; + return $record->label; } - public function getMaxDepth(): int - { - return 5; - } - - public static function tree(Tree $tree): Tree - { - return $tree; - } - - protected function getTreeRecordIcon(?Model $record = null): ?string + public function getTreeRecordIcon(?Model $record = null): ?string { if (! $record) { return 'heroicon-o-bars-3'; @@ -94,7 +68,22 @@ protected function getTreeRecordIcon(?Model $record = null): ?string protected function getActions(): array { return [ - Actions\LocaleSwitcher::make(), + LocaleSwitcher::make(), ]; } + + protected function hasDeleteAction(): bool + { + return false; + } + + protected function hasEditAction(): bool + { + return false; + } + + protected function hasViewAction(): bool + { + return false; + } } diff --git a/src/Admin/Filament/Resources/MenuResource/RelationManagers/MenuItemsRelationManager.php b/src/Admin/Filament/Resources/MenuResource/RelationManagers/MenuItemsRelationManager.php index cbf0752..40be116 100644 --- a/src/Admin/Filament/Resources/MenuResource/RelationManagers/MenuItemsRelationManager.php +++ b/src/Admin/Filament/Resources/MenuResource/RelationManagers/MenuItemsRelationManager.php @@ -7,23 +7,22 @@ use Eclipse\Cms\Models\Menu\Item; use Eclipse\Common\Foundation\Models\Scopes\ActiveScope; use Eclipse\Common\Foundation\Plugins\HasLinkables; +use Filament\Actions; use Filament\Facades\Filament; use Filament\Forms; -use Filament\Forms\Form; -use Filament\Forms\Get; -use Filament\Forms\Set; -use Filament\Resources\RelationManagers\Concerns\Translatable; use Filament\Resources\RelationManagers\RelationManager; +use Filament\Schemas\Components\Utilities\Get; +use Filament\Schemas\Components\Utilities\Set; +use Filament\Schemas\Schema; use Filament\Tables; use Filament\Tables\Contracts\HasTable; -use Filament\Tables\Filters\SelectFilter; -use Filament\Tables\Filters\TernaryFilter; -use Filament\Tables\Filters\TrashedFilter; use Filament\Tables\Table; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletingScope; use Illuminate\Support\HtmlString; +use LaraZeus\SpatieTranslatable\Actions\LocaleSwitcher; +use LaraZeus\SpatieTranslatable\Resources\RelationManagers\Concerns\Translatable; class MenuItemsRelationManager extends RelationManager { @@ -82,26 +81,26 @@ protected function getMenuItemFormSchema(?int $excludeId = null): array ->searchable() ->preload() ->required() - ->visible(fn (Get $get) => $get('type') === 'Linkable'), + ->visible(fn (Get $get) => $get('type') === MenuItemType::Linkable), Forms\Components\TextInput::make('custom_url') ->columnSpanFull() ->label('Custom URL') ->required() - ->visible(fn (Get $get) => $get('type') === 'CustomUrl'), + ->visible(fn (Get $get) => $get('type') === MenuItemType::CustomUrl), Forms\Components\Toggle::make('new_tab') ->columnSpanFull() ->label('Open in new tab') ->default(false) - ->visible(fn (Get $get) => in_array($get('type'), ['Linkable', 'CustomUrl'])), + ->visible(fn (Get $get) => in_array($get('type'), [MenuItemType::Linkable, MenuItemType::CustomUrl])), Forms\Components\Toggle::make('is_active') ->columnSpanFull() ->default(true), ]; } - public function form(Form $form): Form + public function form(Schema $schema): Schema { - return $form->schema($this->getMenuItemFormSchema()); + return $schema->components($this->getMenuItemFormSchema()); } public function table(Table $table): Table @@ -127,7 +126,7 @@ public function table(Table $table): Table ->sortable(false), ]) ->filters([ - TernaryFilter::make('is_active') + Tables\Filters\TernaryFilter::make('is_active') ->label('Status') ->placeholder('Active only') ->trueLabel('All') @@ -150,34 +149,35 @@ public function table(Table $table): Table return []; }), - TrashedFilter::make(), + Tables\Filters\TrashedFilter::make(), - SelectFilter::make('type') + Tables\Filters\SelectFilter::make('type') ->options(MenuItemType::class) ->multiple(), - SelectFilter::make('parent_id') + Tables\Filters\SelectFilter::make('parent_id') ->label('Parent Item') ->options(fn () => Item::getParentOptions($this->getOwnerRecord()->id)) ->searchable(), - TernaryFilter::make('new_tab') + Tables\Filters\TernaryFilter::make('new_tab') ->label('Opens in New Tab'), ]) ->headerActions([ - Tables\Actions\CreateAction::make() + LocaleSwitcher::make(), + Actions\CreateAction::make() ->label('New Menu Item') ->icon('heroicon-o-plus-circle'), - Tables\Actions\Action::make('sort') + Actions\Action::make('sort') ->label('Sort Items') ->icon('heroicon-o-arrows-up-down') ->url(fn () => MenuResource::getUrl('sort-items', ['record' => $this->getOwnerRecord()])) ->color('gray'), ]) ->actions([ - Tables\Actions\EditAction::make(), - Tables\Actions\DeleteAction::make(), - Tables\Actions\RestoreAction::make(), - Tables\Actions\ForceDeleteAction::make(), - Tables\Actions\Action::make('addSubitem') + Actions\EditAction::make(), + Actions\DeleteAction::make(), + Actions\RestoreAction::make(), + Actions\ForceDeleteAction::make(), + Actions\Action::make('addSubitem') ->icon('heroicon-o-plus-circle') ->color('warning') ->label('Add Sub-item') @@ -194,8 +194,8 @@ public function table(Table $table): Table }), ]) ->bulkActions([ - Tables\Actions\BulkActionGroup::make([ - Tables\Actions\BulkAction::make('activate') + Actions\BulkActionGroup::make([ + Actions\BulkAction::make('activate') ->label('Activate') ->icon('heroicon-o-check-circle') ->color('success') @@ -210,7 +210,7 @@ public function table(Table $table): Table return blank($filterState['value']); }), - Tables\Actions\BulkAction::make('deactivate') + Actions\BulkAction::make('deactivate') ->label('Deactivate') ->icon('heroicon-o-x-circle') ->color('warning') @@ -229,9 +229,9 @@ public function table(Table $table): Table return filled($filterState['value']); }), - Tables\Actions\DeleteBulkAction::make(), - Tables\Actions\RestoreBulkAction::make(), - Tables\Actions\ForceDeleteBulkAction::make(), + Actions\DeleteBulkAction::make(), + Actions\RestoreBulkAction::make(), + Actions\ForceDeleteBulkAction::make(), ]), ]); } diff --git a/src/Admin/Filament/Resources/PageResource.php b/src/Admin/Filament/Resources/PageResource.php index d69a051..b0ba9f5 100644 --- a/src/Admin/Filament/Resources/PageResource.php +++ b/src/Admin/Filament/Resources/PageResource.php @@ -6,14 +6,7 @@ use Eclipse\Cms\Admin\Filament\Resources\PageResource\Pages\EditPage; use Eclipse\Cms\Admin\Filament\Resources\PageResource\Pages\ListPages; use Eclipse\Cms\Models\Page; -use Filament\Actions\BulkActionGroup; -use Filament\Actions\DeleteAction; -use Filament\Actions\DeleteBulkAction; -use Filament\Actions\EditAction; -use Filament\Actions\ForceDeleteAction; -use Filament\Actions\ForceDeleteBulkAction; -use Filament\Actions\RestoreAction; -use Filament\Actions\RestoreBulkAction; +use Filament\Actions; use Filament\Forms\Components\MarkdownEditor; use Filament\Forms\Components\Placeholder; use Filament\Forms\Components\TextInput; @@ -93,16 +86,16 @@ public static function table(Table $table): Table TrashedFilter::make(), ]) ->recordActions([ - EditAction::make(), - DeleteAction::make(), - RestoreAction::make(), - ForceDeleteAction::make(), + Actions\EditAction::make(), + Actions\DeleteAction::make(), + Actions\RestoreAction::make(), + Actions\ForceDeleteAction::make(), ]) ->toolbarActions([ - BulkActionGroup::make([ - DeleteBulkAction::make(), - RestoreBulkAction::make(), - ForceDeleteBulkAction::make(), + Actions\BulkActionGroup::make([ + Actions\DeleteBulkAction::make(), + Actions\RestoreBulkAction::make(), + Actions\ForceDeleteBulkAction::make(), ]), ]); } diff --git a/src/Admin/Filament/Resources/SectionResource.php b/src/Admin/Filament/Resources/SectionResource.php index dcf20ea..7de7a98 100644 --- a/src/Admin/Filament/Resources/SectionResource.php +++ b/src/Admin/Filament/Resources/SectionResource.php @@ -6,14 +6,7 @@ use Eclipse\Cms\Admin\Filament\Resources\SectionResource\Pages\EditSection; use Eclipse\Cms\Admin\Filament\Resources\SectionResource\Pages\ListSections; use Eclipse\Cms\Models\Section; -use Filament\Actions\BulkActionGroup; -use Filament\Actions\DeleteAction; -use Filament\Actions\DeleteBulkAction; -use Filament\Actions\EditAction; -use Filament\Actions\ForceDeleteAction; -use Filament\Actions\ForceDeleteBulkAction; -use Filament\Actions\RestoreAction; -use Filament\Actions\RestoreBulkAction; +use Filament\Actions; use Filament\Forms\Components\TextInput; use Filament\Resources\Resource; use Filament\Schemas\Schema; @@ -58,16 +51,16 @@ public static function table(Table $table): Table TrashedFilter::make(), ]) ->recordActions([ - EditAction::make(), - DeleteAction::make(), - RestoreAction::make(), - ForceDeleteAction::make(), + Actions\EditAction::make(), + Actions\DeleteAction::make(), + Actions\RestoreAction::make(), + Actions\ForceDeleteAction::make(), ]) ->toolbarActions([ - BulkActionGroup::make([ - DeleteBulkAction::make(), - RestoreBulkAction::make(), - ForceDeleteBulkAction::make(), + Actions\BulkActionGroup::make([ + Actions\DeleteBulkAction::make(), + Actions\RestoreBulkAction::make(), + Actions\ForceDeleteBulkAction::make(), ]), ]); } diff --git a/src/Enums/MenuItemType.php b/src/Enums/MenuItemType.php index 2fc9a0f..3b5f5e9 100644 --- a/src/Enums/MenuItemType.php +++ b/src/Enums/MenuItemType.php @@ -4,11 +4,11 @@ use Filament\Support\Contracts\HasLabel; -enum MenuItemType implements HasLabel +enum MenuItemType: string implements HasLabel { - case Linkable; - case CustomUrl; - case Group; + case Linkable = 'Linkable'; + case CustomUrl = 'CustomUrl'; + case Group = 'Group'; public function getLabel(): ?string { diff --git a/src/Policies/BannerPositionPolicy.php b/src/Policies/BannerPositionPolicy.php index ad73940..9dde0f4 100644 --- a/src/Policies/BannerPositionPolicy.php +++ b/src/Policies/BannerPositionPolicy.php @@ -12,51 +12,51 @@ class BannerPositionPolicy public function viewAny(Authorizable $user): bool { - return $user->can('view_any_banner::position'); + return $user->can('view_any_position'); } public function view(Authorizable $user, Position $position): bool { - return $user->can('manage_banners_banner::position'); + return $user->can('view_position'); } public function create(Authorizable $user): bool { - return $user->can('create_banner::position'); + return $user->can('create_position'); } public function update(Authorizable $user, Position $position): bool { - return $user->can('update_banner::position'); + return $user->can('update_position'); } public function delete(Authorizable $user, Position $position): bool { - return $user->can('delete_banner::position'); + return $user->can('delete_position'); } public function deleteAny(Authorizable $user): bool { - return $user->can('delete_any_banner::position'); + return $user->can('delete_any_position'); } public function forceDelete(Authorizable $user, Position $position): bool { - return $user->can('force_delete_banner::position'); + return $user->can('force_delete_position'); } public function forceDeleteAny(Authorizable $user): bool { - return $user->can('force_delete_any_banner::position'); + return $user->can('force_delete_any_position'); } public function restore(Authorizable $user, Position $position): bool { - return $user->can('restore_banner::position'); + return $user->can('restore_position'); } public function restoreAny(Authorizable $user): bool { - return $user->can('restore_any_banner::position'); + return $user->can('restore_any_position'); } } diff --git a/tests/Pest.php b/tests/Pest.php index 809bcea..ee911e5 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -23,6 +23,7 @@ '--all' => null, '--panel' => 'admin', '--option' => 'permissions', + '--minimal' => null, ]); }) ->in(__DIR__); From c499d27e10a8f2fbf0fd6498eeb0d54a47459c62 Mon Sep 17 00:00:00 2001 From: thapacodes4u Date: Mon, 13 Oct 2025 17:40:28 +0545 Subject: [PATCH 2/4] fix: reverting php version to 8.2 --- .lando.dist.yml | 4 +++- composer.json | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.lando.dist.yml b/.lando.dist.yml index 2b2a147..75bac01 100644 --- a/.lando.dist.yml +++ b/.lando.dist.yml @@ -2,9 +2,11 @@ name: eclipsephp-cms services: appserver: - type: php:8.3 + type: php:custom xdebug: "debug,develop,coverage" via: cli + overrides: + image: slimdeluxe/php:8.2-v1.2 tooling: php: service: appserver diff --git a/composer.json b/composer.json index 418a0ab..18de3d9 100644 --- a/composer.json +++ b/composer.json @@ -41,7 +41,7 @@ } }, "require": { - "php": "^8.3", + "php": "^8.2", "bezhansalleh/filament-shield": "^4.0", "datalinx/php-utils": "^2.5", "eclipsephp/common": "dev-main", From 4ba76139e684a37fa87a43e7a662ed9aa263e834 Mon Sep 17 00:00:00 2001 From: thapacodes4u Date: Tue, 14 Oct 2025 04:33:06 +0545 Subject: [PATCH 3/4] fix: should be calling to new translation plugin --- workbench/app/Providers/AdminPanelProvider.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/workbench/app/Providers/AdminPanelProvider.php b/workbench/app/Providers/AdminPanelProvider.php index 3d3736a..16f7a7d 100644 --- a/workbench/app/Providers/AdminPanelProvider.php +++ b/workbench/app/Providers/AdminPanelProvider.php @@ -10,8 +10,8 @@ use Filament\Pages\Dashboard; use Filament\Panel; use Filament\PanelProvider; -use Filament\SpatieLaravelTranslatablePlugin; use Filament\Support\Facades\FilamentView; +use LaraZeus\SpatieTranslatable\SpatieTranslatablePlugin; use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse; use Illuminate\Cookie\Middleware\EncryptCookies; use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken; @@ -46,11 +46,9 @@ public function panel(Panel $panel): Panel ]) ->plugins([ FilamentShieldPlugin::make(), - SpatieLaravelTranslatablePlugin::make() + SpatieTranslatablePlugin::make() ->defaultLocales(['en', 'sl']), CmsPlugin::make(), - SpatieLaravelTranslatablePlugin::make() - ->defaultLocales(['en', 'sl']), ]) ->pages([ Dashboard::class, From da805a1f212c50d89bdc8db672edc92b53250e27 Mon Sep 17 00:00:00 2001 From: thapacodes4u Date: Tue, 14 Oct 2025 17:18:45 +0545 Subject: [PATCH 4/4] fix: making all test pass by refactoring test --- .../Resources/BannerPositionResource.php | 1 + src/Policies/MenuPolicy.php | 5 ++ tests/Feature/BannerPositionResourceTest.php | 40 ++++++++++++-- tests/Feature/MenuResourceTest.php | 42 +++++++++++++-- tests/Pest.php | 15 ------ tests/TestCase.php | 54 +++++++++++++++++-- tests/Unit/MenuItemTest.php | 16 +++--- .../app/Providers/AdminPanelProvider.php | 5 +- 8 files changed, 141 insertions(+), 37 deletions(-) diff --git a/src/Admin/Filament/Resources/BannerPositionResource.php b/src/Admin/Filament/Resources/BannerPositionResource.php index 457f751..dd2627e 100644 --- a/src/Admin/Filament/Resources/BannerPositionResource.php +++ b/src/Admin/Filament/Resources/BannerPositionResource.php @@ -34,6 +34,7 @@ public static function getPermissionPrefixes(): array { return [ 'view_any', + 'view', 'create', 'update', 'delete', diff --git a/src/Policies/MenuPolicy.php b/src/Policies/MenuPolicy.php index b71c7c6..4808fc8 100644 --- a/src/Policies/MenuPolicy.php +++ b/src/Policies/MenuPolicy.php @@ -15,6 +15,11 @@ public function viewAny(Authorizable $user): bool return $user->can('view_any_menu'); } + public function view(Authorizable $user, Menu $menu): bool + { + return $user->can('view_menu'); + } + public function create(Authorizable $user): bool { return $user->can('create_menu'); diff --git a/tests/Feature/BannerPositionResourceTest.php b/tests/Feature/BannerPositionResourceTest.php index c98f825..ef3c3a4 100644 --- a/tests/Feature/BannerPositionResourceTest.php +++ b/tests/Feature/BannerPositionResourceTest.php @@ -11,16 +11,15 @@ use function Pest\Livewire\livewire; -beforeEach(function () { - $this->setUpSuperAdmin(); -}); - it('can render position index page', function () { + $this->setUpUserWithPermissions(['view_any_position']); + $this->get(BannerPositionResource::getUrl('index')) ->assertSuccessful(); }); it('can list positions', function () { + $this->setUpUserWithPermissions(['view_any_position']); $positions = Position::factory()->count(3)->create(); livewire(ListBannerPositions::class) @@ -28,11 +27,14 @@ }); it('can render position create page', function () { + $this->setUpUserWithPermissions(['view_any_position', 'create_position']); + $this->get(BannerPositionResource::getUrl('create')) ->assertSuccessful(); }); it('can create position', function () { + $this->setUpUserWithPermissions(['view_any_position', 'create_position']); $newData = Position::factory()->make(); livewire(CreateBannerPosition::class) @@ -54,6 +56,7 @@ }); it('can create position without image types', function () { + $this->setUpUserWithPermissions(['view_any_position', 'create_position']); $newData = Position::factory()->make([ 'name' => 'Header Banner', 'code' => 'header', @@ -74,6 +77,7 @@ }); it('can render position edit page', function () { + $this->setUpUserWithPermissions(['view_any_position', 'view_position', 'update_position']); $position = Position::factory()->create(); $this->get(BannerPositionResource::getUrl('edit', ['record' => $position])) @@ -81,6 +85,7 @@ }); it('can retrieve position data for editing', function () { + $this->setUpUserWithPermissions(['view_any_position', 'view_position', 'update_position']); $position = Position::factory()->create(); livewire(EditBannerPosition::class, [ @@ -93,6 +98,7 @@ }); it('can save position', function () { + $this->setUpUserWithPermissions(['view_any_position', 'view_position', 'update_position']); $position = Position::factory()->create(); $newData = Position::factory()->make(); @@ -112,6 +118,7 @@ }); it('can delete position', function () { + $this->setUpUserWithPermissions(['view_any_position', 'delete_position']); $position = Position::factory()->create(); livewire(ListBannerPositions::class) @@ -121,6 +128,8 @@ }); it('can validate position creation', function () { + $this->setUpUserWithPermissions(['view_any_position', 'create_position']); + livewire(CreateBannerPosition::class) ->fillForm([ 'name' => null, @@ -130,6 +139,7 @@ }); it('can filter positions', function () { + $this->setUpUserWithPermissions(['view_any_position']); $positions = Position::factory()->count(5)->create(); livewire(ListBannerPositions::class) @@ -138,6 +148,7 @@ }); it('can render position view page', function () { + $this->setUpUserWithPermissions(['view_any_position', 'view_position']); $position = Position::factory()->create(); $this->get(BannerPositionResource::getUrl('view', ['record' => $position])) @@ -145,6 +156,7 @@ }); it('can view position and manage banners', function () { + $this->setUpUserWithPermissions(['view_any_position', 'view_position']); $position = Position::factory()->create(); livewire(ViewBannerPosition::class, [ @@ -154,7 +166,7 @@ }); it('can access edit from view page', function () { - $this->setUpSuperAdmin(); + $this->setUpUserWithPermissions(['view_any_position', 'view_position', 'update_position']); $position = Position::factory()->create(); @@ -169,6 +181,7 @@ }); it('deletes all related banners and image files when position is deleted', function () { + $this->setUpUserWithPermissions(['delete_position']); Storage::fake(); Storage::put('banners/banner1-desktop.png', 'fake-image-content'); @@ -216,3 +229,20 @@ expect(Storage::exists('banners/banner1-mobile@2x.png'))->toBeFalse(); expect(Storage::exists('banners/banner1-mobile@2x_1x.png'))->toBeFalse(); }); + +test('super admin bypasses all position authorization checks', function () { + $this->setUpSuperAdmin(); + $position = Position::factory()->create(); + + $this->get(BannerPositionResource::getUrl('index'))->assertSuccessful(); + $this->get(BannerPositionResource::getUrl('create'))->assertSuccessful(); + $this->get(BannerPositionResource::getUrl('edit', ['record' => $position]))->assertSuccessful(); + $this->get(BannerPositionResource::getUrl('view', ['record' => $position]))->assertSuccessful(); + + livewire(CreateBannerPosition::class)->assertSuccessful(); + livewire(EditBannerPosition::class, ['record' => $position->getRouteKey()])->assertSuccessful(); + livewire(ViewBannerPosition::class, ['record' => $position->getRouteKey()])->assertSuccessful(); + livewire(ListBannerPositions::class)->callTableAction('delete', $position); + + $this->assertSoftDeleted($position); +}); diff --git a/tests/Feature/MenuResourceTest.php b/tests/Feature/MenuResourceTest.php index 586a2bb..25a049b 100644 --- a/tests/Feature/MenuResourceTest.php +++ b/tests/Feature/MenuResourceTest.php @@ -8,16 +8,15 @@ use function Pest\Livewire\livewire; -beforeEach(function () { - $this->setUpSuperAdmin(); -}); - it('can render menu index page', function () { + $this->setUpUserWithPermissions(['view_any_menu']); + $this->get(MenuResource::getUrl('index')) ->assertSuccessful(); }); it('can list menus', function () { + $this->setUpUserWithPermissions(['view_any_menu']); $menus = Menu::factory()->count(10)->create(); livewire(ListMenus::class) @@ -25,11 +24,14 @@ }); it('can render menu create page', function () { + $this->setUpUserWithPermissions(['view_any_menu', 'create_menu']); + $this->get(MenuResource::getUrl('create')) ->assertSuccessful(); }); it('can create menu', function () { + $this->setUpUserWithPermissions(['view_any_menu', 'create_menu']); $newData = Menu::factory()->make(); livewire(CreateMenu::class) @@ -52,6 +54,8 @@ }); it('can validate menu creation', function () { + $this->setUpUserWithPermissions(['view_any_menu', 'create_menu']); + livewire(CreateMenu::class) ->fillForm([ 'title' => null, @@ -61,6 +65,7 @@ }); it('can render menu edit page', function () { + $this->setUpUserWithPermissions(['view_any_menu', 'view_menu', 'update_menu']); $menu = Menu::factory()->create(); $this->get(MenuResource::getUrl('edit', ['record' => $menu])) @@ -68,6 +73,7 @@ }); it('can retrieve menu data for editing', function () { + $this->setUpUserWithPermissions(['view_any_menu', 'view_menu', 'update_menu']); $menu = Menu::factory()->create(); livewire(EditMenu::class, [ @@ -81,6 +87,7 @@ }); it('can save menu', function () { + $this->setUpUserWithPermissions(['view_any_menu', 'view_menu', 'update_menu']); $menu = Menu::factory()->create(); $newData = Menu::factory()->make(); @@ -102,6 +109,7 @@ }); it('can validate menu editing', function () { + $this->setUpUserWithPermissions(['view_any_menu', 'view_menu', 'update_menu']); $menu = Menu::factory()->create(); livewire(EditMenu::class, [ @@ -115,6 +123,7 @@ }); it('can delete menu', function () { + $this->setUpUserWithPermissions(['view_any_menu', 'delete_menu']); $menu = Menu::factory()->create(); livewire(ListMenus::class) @@ -124,6 +133,7 @@ }); it('can bulk delete menus', function () { + $this->setUpUserWithPermissions(['view_any_menu', 'delete_any_menu']); $menus = Menu::factory()->count(10)->create(); livewire(ListMenus::class) @@ -135,6 +145,7 @@ }); it('can search menus', function () { + $this->setUpUserWithPermissions(['view_any_menu']); $menus = Menu::factory()->count(10)->create(); $title = $menus->first()->title['en'] ?? $menus->first()->title; @@ -146,6 +157,7 @@ }); it('can sort menus', function () { + $this->setUpUserWithPermissions(['view_any_menu']); $menus = Menu::factory()->count(10)->create(); livewire(ListMenus::class) @@ -156,6 +168,7 @@ }); it('can filter menus by active status', function () { + $this->setUpUserWithPermissions(['view_any_menu']); $activeMenus = Menu::factory()->active()->count(5)->create(); $inactiveMenus = Menu::factory()->inactive()->count(5)->create(); @@ -201,6 +214,7 @@ }); it('can render menu item sorting page', function () { + $this->setUpUserWithPermissions(['view_any_menu', 'view_menu', 'update_menu']); $menu = Menu::factory()->create(); $this->get(MenuResource::getUrl('sort-items', ['record' => $menu])) @@ -208,6 +222,7 @@ }); it('can access menu item sorting page from relation manager', function () { + $this->setUpUserWithPermissions(['view_any_menu', 'view_menu', 'update_menu']); $menu = Menu::factory()->create(); $response = $this->get(MenuResource::getUrl('edit', ['record' => $menu])); @@ -221,6 +236,7 @@ }); it('deletes all menu items when menu is soft deleted', function () { + $this->setUpUserWithPermissions(['delete_menu']); $menu = Menu::factory()->create(); $rootItem = $menu->allItems()->create([ 'label' => ['en' => 'Root Item'], @@ -255,6 +271,7 @@ }); it('force deletes all menu items when menu is force deleted', function () { + $this->setUpUserWithPermissions(['force_delete_menu']); $menu = Menu::factory()->create(); $rootItem = $menu->allItems()->create([ 'label' => ['en' => 'Root Item'], @@ -289,6 +306,7 @@ }); it('can restore menu', function () { + $this->setUpUserWithPermissions(['restore_menu']); $menu = Menu::factory()->create(); $menu->delete(); @@ -299,6 +317,7 @@ }); it('can force delete menu and its items', function () { + $this->setUpUserWithPermissions(['delete_menu', 'force_delete_menu']); $menu = Menu::factory()->create(); $item = $menu->allItems()->create([ 'label' => ['en' => 'Test Item'], @@ -315,3 +334,18 @@ $this->assertModelMissing($menu); $this->assertModelMissing($item); }); + +test('super admin bypasses all menu authorization checks', function () { + $this->setUpSuperAdmin(); + $menu = Menu::factory()->create(); + + $this->get(MenuResource::getUrl('index'))->assertSuccessful(); + $this->get(MenuResource::getUrl('create'))->assertSuccessful(); + $this->get(MenuResource::getUrl('edit', ['record' => $menu]))->assertSuccessful(); + + livewire(CreateMenu::class)->assertSuccessful(); + livewire(EditMenu::class, ['record' => $menu->getRouteKey()])->assertSuccessful(); + livewire(ListMenus::class)->callTableAction('delete', $menu); + + $this->assertSoftDeleted($menu); +}); diff --git a/tests/Pest.php b/tests/Pest.php index ee911e5..843a171 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -1,7 +1,6 @@ use(RefreshDatabase::class) - ->beforeEach(function () { - // Seed roles and permissions with Filament Shield plugin - Artisan::call('shield:generate', [ - '--all' => null, - '--panel' => 'admin', - '--option' => 'permissions', - '--minimal' => null, - ]); - }) ->in(__DIR__); /* @@ -53,8 +43,3 @@ | global functions to help you to reduce the number of lines of code in your test files. | */ - -function something() -{ - // .. -} diff --git a/tests/TestCase.php b/tests/TestCase.php index b8b6a15..07e3145 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -2,11 +2,13 @@ namespace Tests; +use Filament\Facades\Filament; use Illuminate\Foundation\Testing\RefreshDatabase; use Orchestra\Testbench\Concerns\WithWorkbench; use Orchestra\Testbench\TestCase as BaseTestCase; use Spatie\Permission\Models\Permission; use Spatie\Permission\Models\Role; +use Spatie\Permission\PermissionRegistrar; use Workbench\App\Models\User; abstract class TestCase extends BaseTestCase @@ -19,7 +21,6 @@ abstract class TestCase extends BaseTestCase protected function setUp(): void { - // Always show errors when testing ini_set('display_errors', 1); error_reporting(E_ALL); @@ -34,6 +35,11 @@ protected function setUp(): void config(['scout.driver' => null]); } + protected function defineEnvironment($app): void + { + $app['config']->set('app.key', 'base64:0qAvnB4fU0hiVsd01U1b/GljkPRLBS50IQ7I4DS7fxI='); + } + /** * Run database migrations */ @@ -50,13 +56,53 @@ protected function migrate(): self protected function setUpSuperAdmin(): self { $this->migrate(); - $this->superAdmin = User::factory()->make(); - $this->superAdmin->assignRole('super_admin')->save(); - $this->actingAs($this->superAdmin); + $this->superAdmin = User::factory()->create(); + + $this->createAllPermissions(); + + $role = Role::firstOrCreate(['name' => 'super_admin', 'guard_name' => 'web']); + $role->syncPermissions(Permission::all()); + + $this->superAdmin->assignRole('super_admin'); + + app()[PermissionRegistrar::class]->forgetCachedPermissions(); + + Filament::setCurrentPanel(Filament::getPanel('admin')); + $this->actingAs($this->superAdmin, 'web'); return $this; } + protected function createAllPermissions(): void + { + $permissions = [ + 'view_any_menu', + 'view_menu', + 'create_menu', + 'update_menu', + 'delete_menu', + 'delete_any_menu', + 'force_delete_menu', + 'force_delete_any_menu', + 'restore_menu', + 'restore_any_menu', + 'view_any_position', + 'view_position', + 'create_position', + 'update_position', + 'delete_position', + 'delete_any_position', + 'force_delete_position', + 'force_delete_any_position', + 'restore_position', + 'restore_any_position', + ]; + + foreach ($permissions as $permission) { + Permission::firstOrCreate(['name' => $permission, 'guard_name' => 'web']); + } + } + /** * Set up a common user with no roles or permissions */ diff --git a/tests/Unit/MenuItemTest.php b/tests/Unit/MenuItemTest.php index 4f467da..54ff0a4 100644 --- a/tests/Unit/MenuItemTest.php +++ b/tests/Unit/MenuItemTest.php @@ -38,10 +38,12 @@ $parent = Item::factory()->create(['menu_id' => $menu->id]); $child = Item::factory()->create(['menu_id' => $menu->id, 'parent_id' => $parent->id]); - expect($child->parent)->toBeInstanceOf(Item::class) - ->and($child->parent->id)->toBe($parent->id) - ->and($parent->children)->toHaveCount(1) - ->and($parent->children->first()->id)->toBe($child->id); + $childWithParent = Item::withoutGlobalScopes()->with('parent')->find($child->id); + expect($childWithParent->parent)->toBeInstanceOf(Item::class) + ->and($childWithParent->parent->id)->toBe($parent->id); + + expect(Item::withoutGlobalScopes()->where('parent_id', $parent->id)->count())->toBe(1); + expect(Item::withoutGlobalScopes()->where('parent_id', $parent->id)->first()->id)->toBe($child->id); }); it('has translatable label', function () { @@ -238,7 +240,7 @@ $child1 = Item::factory()->create(['menu_id' => $menu->id, 'parent_id' => $parent->id]); $child2 = Item::factory()->create(['menu_id' => $menu->id, 'parent_id' => $parent->id]); - expect($parent->children)->toHaveCount(2); + expect(Item::withoutGlobalScopes()->where('parent_id', $parent->id)->count())->toBe(2); expect($child1->trashed())->toBeFalse(); expect($child2->trashed())->toBeFalse(); @@ -255,8 +257,8 @@ $parent = Item::factory()->active()->create(['menu_id' => $menu->id, 'parent_id' => $grandparent->id]); $child = Item::factory()->active()->create(['menu_id' => $menu->id, 'parent_id' => $parent->id]); - expect($grandparent->children)->toHaveCount(1); - expect($parent->children)->toHaveCount(1); + expect(Item::withoutGlobalScopes()->where('parent_id', $grandparent->id)->count())->toBe(1); + expect(Item::withoutGlobalScopes()->where('parent_id', $parent->id)->count())->toBe(1); $grandparent->delete(); diff --git a/workbench/app/Providers/AdminPanelProvider.php b/workbench/app/Providers/AdminPanelProvider.php index 16f7a7d..20ec93b 100644 --- a/workbench/app/Providers/AdminPanelProvider.php +++ b/workbench/app/Providers/AdminPanelProvider.php @@ -11,7 +11,6 @@ use Filament\Panel; use Filament\PanelProvider; use Filament\Support\Facades\FilamentView; -use LaraZeus\SpatieTranslatable\SpatieTranslatablePlugin; use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse; use Illuminate\Cookie\Middleware\EncryptCookies; use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken; @@ -20,6 +19,7 @@ use Illuminate\Session\Middleware\StartSession; use Illuminate\Support\Facades\Blade; use Illuminate\View\Middleware\ShareErrorsFromSession; +use LaraZeus\SpatieTranslatable\SpatieTranslatablePlugin; class AdminPanelProvider extends PanelProvider { @@ -45,7 +45,8 @@ public function panel(Panel $panel): Panel Authenticate::class, ]) ->plugins([ - FilamentShieldPlugin::make(), + FilamentShieldPlugin::make() + ->registerNavigation(false), SpatieTranslatablePlugin::make() ->defaultLocales(['en', 'sl']), CmsPlugin::make(),