From 0de5069e96edbfe12ac07aa501c93178d8b4593a Mon Sep 17 00:00:00 2001 From: Bezhan Salleh Date: Fri, 12 Jan 2024 08:07:30 +0430 Subject: [PATCH 1/4] wip --- src/FilamentShield.php | 55 ++++++++++++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 16 deletions(-) diff --git a/src/FilamentShield.php b/src/FilamentShield.php index 54d2387..8d8b57d 100755 --- a/src/FilamentShield.php +++ b/src/FilamentShield.php @@ -2,16 +2,17 @@ namespace BezhanSalleh\FilamentShield; -use BezhanSalleh\FilamentShield\Support\Utils; use Closure; +use Illuminate\Support\Str; +use Filament\Widgets\Widget; use Filament\Facades\Filament; -use Filament\Support\Concerns\EvaluatesClosures; use Filament\Widgets\TableWidget; -use Filament\Widgets\Widget; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Lang; -use Illuminate\Support\Str; +use Filament\Widgets\WidgetConfiguration; use Spatie\Permission\PermissionRegistrar; +use BezhanSalleh\FilamentShield\Support\Utils; +use Filament\Support\Concerns\EvaluatesClosures; class FilamentShield { @@ -251,25 +252,41 @@ public static function getWidgets(): ?array return collect($widgets) ->filter(function ($widget) { if (Utils::isGeneralExcludeEnabled()) { + if ($widget instanceof WidgetConfiguration) { + $widget = $widget->widget; + } return ! in_array(Str::afterLast($widget, '\\'), Utils::getExcludedWidgets()); } return true; }) - ->reduce(function ($widgets, $widget) { - - $name = Str::of(class_basename($widget)) + ->map(function($widget) { + $permission = Str::of(class_basename($widget)) ->prepend( Str::of(Utils::getWidgetPermissionPrefix()) ->append('_') ->toString() ) ->toString(); + return [ + 'class' => $widget, + 'permission' => $permission + ]; + }) + // ->reduce(function ($widgets, $widget) { - $widgets["{$name}"] = "{$name}"; + // $name = Str::of(class_basename($widget)) + // ->prepend( + // Str::of(Utils::getWidgetPermissionPrefix()) + // ->append('_') + // ->toString() + // ) + // ->toString(); - return $widgets; - }, collect()) + // $widgets["{$name}"] = "{$name}"; + + // return $widgets; + // }, collect()) ->toArray(); } @@ -278,10 +295,21 @@ public static function getWidgets(): ?array */ public static function getLocalizedWidgetLabel(string $widget): string { - $class = static::transformClassString($widget, false); + $classString = str($widget) + ->after( + str(Utils::getWidgetPermissionPrefix()) + ->append('_') + ) + ->studly() + ->toString(); + $class = class_basename($classString); $widgetInstance = app()->make($class); + if ($widgetInstance instanceof WidgetConfiguration) { + $widgetInstance = $widgetInstance->widget; + } + return match (true) { $widgetInstance instanceof TableWidget => (string) invade($widgetInstance)->makeTable()->getHeading(), ! ($widgetInstance instanceof TableWidget) && $widgetInstance instanceof Widget && method_exists($widgetInstance, 'getHeading') => (string) invade($widgetInstance)->getHeading(), @@ -318,11 +346,6 @@ protected static function transformClassString(string $string, bool $isPageClass ->first(fn ($item) => class_basename($item) == Str::of($string)->after($prefix)->studly()); } - protected static function hasHeadingForShield(object | string $class): bool - { - return method_exists($class, 'getHeadingForShield'); - } - protected function getDefaultPermissionIdentifier(string $resource): string { return Str::of($resource) From 44c7567a93900d355ed12c3e3522d5d78ea66a22 Mon Sep 17 00:00:00 2001 From: Bezhan Salleh Date: Fri, 12 Jan 2024 09:11:00 +0430 Subject: [PATCH 2/4] fixes #304 --- src/FilamentShield.php | 150 ++++++++++++++--------------------------- 1 file changed, 51 insertions(+), 99 deletions(-) diff --git a/src/FilamentShield.php b/src/FilamentShield.php index 8d8b57d..13c3833 100755 --- a/src/FilamentShield.php +++ b/src/FilamentShield.php @@ -125,28 +125,24 @@ public function getResources(): ?array } return collect($resources) - ->unique() - ->filter(function ($resource) { + ->reject(function ($resource) { if (Utils::isGeneralExcludeEnabled()) { - return ! in_array( + return in_array( Str::of($resource)->afterLast('\\'), Utils::getExcludedResouces() ); } - - return true; }) - ->reduce(function ($resources, $resource) { + ->mapWithKeys(function($resource) { $name = $this->getPermissionIdentifier($resource); - - $resources["{$name}"] = [ - 'resource' => "{$name}", - 'model' => Str::of($resource::getModel())->afterLast('\\'), - 'fqcn' => $resource, + return [ + $name => [ + 'resource' => "{$name}", + 'model' => Str::of($resource::getModel())->afterLast('\\'), + 'fqcn' => $resource, + ] ]; - - return $resources; - }, collect()) + }) ->sortKeys() ->toArray(); } @@ -196,27 +192,26 @@ public static function getPages(): ?array } return collect($pages) - ->filter(function ($page) { + ->reject(function ($page) { if (Utils::isGeneralExcludeEnabled()) { - return ! in_array(Str::afterLast($page, '\\'), Utils::getExcludedPages()); + return in_array(Str::afterLast($page, '\\'), Utils::getExcludedPages()); } - - return true; }) - ->reduce(function ($pages, $page) { - - $name = Str::of(class_basename($page)) + ->mapWithKeys(function ($page) { + $permission = Str::of(class_basename($page)) ->prepend( Str::of(Utils::getPagePermissionPrefix()) ->append('_') ->toString() ) ->toString(); - - $pages["{$name}"] = "{$name}"; - - return $pages; - }, collect()) + return [ + $permission => [ + 'class' => $page, + 'permission' => $permission + ] + ]; + }) ->toArray(); } @@ -225,13 +220,11 @@ public static function getPages(): ?array */ public static function getLocalizedPageLabel(string $page): string { - $object = static::transformClassString($page); + $pageInstance = app()->make($page); - $pageObject = new $object(); - - return $pageObject->getTitle() - ?? $pageObject->getHeading() - ?? $pageObject->getNavigationLabel() + return $pageInstance->getTitle() + ?? $pageInstance->getHeading() + ?? $pageInstance->getNavigationLabel() ?? ''; } @@ -250,43 +243,34 @@ public static function getWidgets(): ?array } return collect($widgets) - ->filter(function ($widget) { + ->reject(function ($widget) { if (Utils::isGeneralExcludeEnabled()) { - if ($widget instanceof WidgetConfiguration) { - $widget = $widget->widget; - } - return ! in_array(Str::afterLast($widget, '\\'), Utils::getExcludedWidgets()); + return in_array( + needle: str( + static::getWidgetInstanceFromWidgetConfiguration($widget) + ) + ->afterLast('\\') + ->toString(), + haystack: Utils::getExcludedWidgets() + ); } - - return true; }) - ->map(function($widget) { - $permission = Str::of(class_basename($widget)) + ->mapWithKeys(function($widget) { + $permission = Str::of(class_basename(static::getWidgetInstanceFromWidgetConfiguration($widget))) ->prepend( Str::of(Utils::getWidgetPermissionPrefix()) ->append('_') ->toString() ) ->toString(); + return [ - 'class' => $widget, - 'permission' => $permission + $permission => [ + 'class' => static::getWidgetInstanceFromWidgetConfiguration($widget), + 'permission' => $permission + ] ]; }) - // ->reduce(function ($widgets, $widget) { - - // $name = Str::of(class_basename($widget)) - // ->prepend( - // Str::of(Utils::getWidgetPermissionPrefix()) - // ->append('_') - // ->toString() - // ) - // ->toString(); - - // $widgets["{$name}"] = "{$name}"; - - // return $widgets; - // }, collect()) ->toArray(); } @@ -295,57 +279,18 @@ public static function getWidgets(): ?array */ public static function getLocalizedWidgetLabel(string $widget): string { - $classString = str($widget) - ->after( - str(Utils::getWidgetPermissionPrefix()) - ->append('_') - ) - ->studly() - ->toString(); - $class = class_basename($classString); - - $widgetInstance = app()->make($class); - - if ($widgetInstance instanceof WidgetConfiguration) { - $widgetInstance = $widgetInstance->widget; - } + $widgetInstance = app()->make($widget); return match (true) { $widgetInstance instanceof TableWidget => (string) invade($widgetInstance)->makeTable()->getHeading(), ! ($widgetInstance instanceof TableWidget) && $widgetInstance instanceof Widget && method_exists($widgetInstance, 'getHeading') => (string) invade($widgetInstance)->getHeading(), - default => Str::of($widget) - ->after(Utils::getWidgetPermissionPrefix() . '_') + default => str($widget) + ->afterLast('\\') ->headline() ->toString(), }; } - protected static function transformClassString(string $string, bool $isPageClass = true): string - { - $pages = Filament::getPages(); - if (Utils::discoverAllPages()) { - $pages = []; - foreach (Filament::getPanels() as $panel) { - $pages = array_merge($pages, $panel->getPages()); - } - $pages = array_unique($pages); - } - - $widgets = Filament::getWidgets(); - if (Utils::discoverAllWidgets()) { - $widgets = []; - foreach (Filament::getPanels() as $panel) { - $widgets = array_merge($widgets, $panel->getWidgets()); - } - $widgets = array_unique($widgets); - } - - $prefix = Str::of($isPageClass ? Utils::getPagePermissionPrefix() : Utils::getWidgetPermissionPrefix())->append('_'); - - return (string) collect($isPageClass ? $pages : $widgets) - ->first(fn ($item) => class_basename($item) == Str::of($string)->after($prefix)->studly()); - } - protected function getDefaultPermissionIdentifier(string $resource): string { return Str::of($resource) @@ -355,4 +300,11 @@ protected function getDefaultPermissionIdentifier(string $resource): string ->snake() ->replace('_', '::'); } + + protected static function getWidgetInstanceFromWidgetConfiguration(string|WidgetConfiguration $widget):string + { + return $widget instanceof WidgetConfiguration + ? $widget->widget + : $widget; + } } From c9f8c25a411ec0ae4d6460d21120c7428e89d787 Mon Sep 17 00:00:00 2001 From: Bezhan Salleh Date: Fri, 12 Jan 2024 09:11:25 +0430 Subject: [PATCH 3/4] performance improvments --- src/Commands/MakeShieldGenerateCommand.php | 20 ++-- src/Resources/RoleResource.php | 101 ++++++++++----------- 2 files changed, 57 insertions(+), 64 deletions(-) diff --git a/src/Commands/MakeShieldGenerateCommand.php b/src/Commands/MakeShieldGenerateCommand.php index 3299e5b..1893b71 100644 --- a/src/Commands/MakeShieldGenerateCommand.php +++ b/src/Commands/MakeShieldGenerateCommand.php @@ -152,11 +152,11 @@ protected function generatablePages(): ?array return collect(FilamentShield::getPages()) ->filter(function ($page) { if ($this->excludePages) { - return ! in_array($page, $this->pages); + return ! in_array($page['class'], $this->pages); } if ($this->onlyPages) { - return in_array($page, $this->pages); + return in_array($page['class'], $this->pages); } return true; @@ -169,11 +169,11 @@ protected function generatableWidgets(): ?array return collect(FilamentShield::getWidgets()) ->filter(function ($widget) { if ($this->excludeWidgets) { - return ! in_array($widget, $this->widgets); + return ! in_array($widget['class'], $this->widgets); } if ($this->onlyWidgets) { - return in_array($widget, $this->widgets); + return in_array($widget['class'], $this->widgets); } return true; @@ -213,14 +213,14 @@ protected function generateForPages(array $pages): Collection { return collect($pages) ->values() - ->each(fn (string $page) => FilamentShield::generateForPage($page)); + ->each(fn (array $page) => FilamentShield::generateForPage($page['permission'])); } protected function generateForWidgets(array $widgets): Collection { return collect($widgets) ->values() - ->each(fn (string $widget) => FilamentShield::generateForWidget($widget)); + ->each(fn (array $widget) => FilamentShield::generateForWidget($widget['permission'])); } protected function resourceInfo(array $resources): void @@ -261,8 +261,8 @@ protected function pageInfo(array $pages): void collect($pages)->map(function ($page, $key) { return [ '#' => $key + 1, - 'Page' => Str::replace(config('filament-shield.permission_prefixes.page') . '_', '', $page), - 'Permission' => $page, + 'Page' => $page['class'], + 'Permission' => $page['permission'], ]; }) ); @@ -280,8 +280,8 @@ protected function widgetInfo(array $widgets): void collect($widgets)->map(function ($widget, $key) { return [ '#' => $key + 1, - 'Widget' => Str::replace(config('filament-shield.permission_prefixes.widget') . '_', '', $widget), - 'Permission' => $widget, + 'Widget' => $widget['class'], + 'Permission' => $widget['permission'], ]; }) ); diff --git a/src/Resources/RoleResource.php b/src/Resources/RoleResource.php index 02d9202..b45b109 100644 --- a/src/Resources/RoleResource.php +++ b/src/Resources/RoleResource.php @@ -319,55 +319,21 @@ public static function canGloballySearch(): bool public static function getResourceEntitiesSchema(): ?array { - if (blank(static::$permissionsCollection)) { - static::$permissionsCollection = Utils::getPermissionModel()::all(); - } + static::$permissionsCollection = static::$permissionsCollection ?: Utils::getPermissionModel()::all(); - return collect(FilamentShield::getResources())->sortKeys()->reduce(function ($entities, $entity) { - - $entities[] = Forms\Components\Section::make(FilamentShield::getLocalizedResourceLabel($entity['fqcn'])) - ->description(fn () => new HtmlString('' . Utils::showModelPath($entity['fqcn']) . '')) - ->compact() - ->schema([ - Forms\Components\CheckboxList::make($entity['resource']) - ->label('') - ->options(fn (): array => static::getResourcePermissionOptions($entity)) - ->live() - ->afterStateHydrated(function (Component $component, $livewire, string $operation, ?Model $record, Forms\Set $set) use ($entity) { - static::setPermissionStateForRecordPermissions( - component: $component, - operation: $operation, - permissions: static::getResourcePermissionOptions($entity), - record: $record - ); - - static::toggleSelectAllViaEntities($livewire, $set); - }) - ->afterStateUpdated(fn ($livewire, Forms\Set $set) => static::toggleSelectAllViaEntities($livewire, $set)) - ->selectAllAction(fn (FormAction $action, Component $component, $livewire, Forms\Set $set) => static::bulkToggleableAction( - action: $action, - component: $component, - livewire: $livewire, - set: $set - )) - ->deselectAllAction(fn (FormAction $action, Component $component, $livewire, Forms\Set $set) => static::bulkToggleableAction( - action: $action, - component: $component, - livewire: $livewire, - set: $set, - resetState: true - )) - ->dehydrated(fn ($state) => blank($state) ? false : true) - ->bulkToggleable() - ->gridDirection('row') - ->columns(FilamentShieldPlugin::get()->getResourceCheckboxListColumns()), - ]) - ->columnSpan(FilamentShieldPlugin::get()->getSectionColumnSpan()) - ->collapsible(); - - return $entities; - }, collect()) - ?->toArray() ?? []; + return collect(FilamentShield::getResources()) + ->sortKeys() + ->map(function ($entity) { + return Forms\Components\Section::make(FilamentShield::getLocalizedResourceLabel($entity['fqcn'])) + ->description(fn () => new HtmlString('' . Utils::showModelPath($entity['fqcn']) . '')) + ->compact() + ->schema([ + static::getCheckBoxListComponentForResource($entity), + ]) + ->columnSpan(FilamentShieldPlugin::get()->getSectionColumnSpan()) + ->collapsible(); + }) + ->toArray(); } public static function getResourceTabBadgeCount(): ?int @@ -445,8 +411,8 @@ public static function toggleSelectAllViaEntities($livewire, Forms\Set $set): vo public static function getPageOptions(): array { return collect(FilamentShield::getPages()) - ->flatMap(fn ($pagePermission) => [ - $pagePermission => FilamentShield::getLocalizedPageLabel($pagePermission), + ->flatMap(fn ($page) => [ + $page['permission'] => FilamentShield::getLocalizedPageLabel($page['class']), ]) ->toArray(); } @@ -454,8 +420,8 @@ public static function getPageOptions(): array public static function getWidgetOptions(): array { return collect(FilamentShield::getWidgets()) - ->flatMap(fn ($widgetPermission) => [ - $widgetPermission => FilamentShield::getLocalizedWidgetLabel($widgetPermission), + ->flatMap(fn ($widget) => [ + $widget['permission'] => FilamentShield::getLocalizedWidgetLabel($widget['class']), ]) ->toArray(); } @@ -479,8 +445,8 @@ protected static function getCustomEntities(): ?Collection }); $entitiesPermissions = $resourcePermissions - ->merge(FilamentShield::getPages()) - ->merge(FilamentShield::getWidgets()) + ->merge(collect(FilamentShield::getPages())->map(fn($page) => $page['permission'])->values()) + ->merge(collect(FilamentShield::getWidgets())->map(fn ($widget) => $widget['permission'])->values()) ->values(); return static::$permissionsCollection->whereNotIn('name', $entitiesPermissions)->pluck('name'); @@ -496,4 +462,31 @@ public static function bulkToggleableAction(FormAction $action, Component $compo static::toggleSelectAllViaEntities($livewire, $set); }); } + + public static function getCheckBoxListComponentForResource(array $entity): Component + { + $permissionsArray = static::getResourcePermissionOptions($entity); + + return Forms\Components\CheckboxList::make($entity['resource']) + ->label('') + ->options(fn (): array => $permissionsArray) + ->live() + ->afterStateHydrated(function (Component $component, $livewire, string $operation, ?Model $record, Forms\Set $set) use ($permissionsArray) { + static::setPermissionStateForRecordPermissions( + component: $component, + operation: $operation, + permissions: $permissionsArray, + record: $record + ); + + static::toggleSelectAllViaEntities($livewire, $set); + }) + ->afterStateUpdated(fn ($livewire, Forms\Set $set) => static::toggleSelectAllViaEntities($livewire, $set)) + ->selectAllAction(fn (FormAction $action, Component $component, $livewire, Forms\Set $set) => static::bulkToggleableAction($action, $component, $livewire, $set)) + ->deselectAllAction(fn (FormAction $action, Component $component, $livewire, Forms\Set $set) => static::bulkToggleableAction($action, $component, $livewire, $set, true)) + ->dehydrated(fn ($state) => !blank($state)) + ->bulkToggleable() + ->gridDirection('row') + ->columns(FilamentShieldPlugin::get()->getResourceCheckboxListColumns()); + } } From 695fd39ea2d2bd19102c61592ee71630fdf6f0bd Mon Sep 17 00:00:00 2001 From: bezhanSalleh Date: Fri, 12 Jan 2024 04:42:00 +0000 Subject: [PATCH 4/4] Fix styling --- src/FilamentShield.php | 32 +++++++++++++++++--------------- src/Resources/RoleResource.php | 4 ++-- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/FilamentShield.php b/src/FilamentShield.php index 13c3833..69e8ee3 100755 --- a/src/FilamentShield.php +++ b/src/FilamentShield.php @@ -2,17 +2,17 @@ namespace BezhanSalleh\FilamentShield; +use BezhanSalleh\FilamentShield\Support\Utils; use Closure; -use Illuminate\Support\Str; -use Filament\Widgets\Widget; use Filament\Facades\Filament; +use Filament\Support\Concerns\EvaluatesClosures; use Filament\Widgets\TableWidget; +use Filament\Widgets\Widget; +use Filament\Widgets\WidgetConfiguration; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Lang; -use Filament\Widgets\WidgetConfiguration; +use Illuminate\Support\Str; use Spatie\Permission\PermissionRegistrar; -use BezhanSalleh\FilamentShield\Support\Utils; -use Filament\Support\Concerns\EvaluatesClosures; class FilamentShield { @@ -133,14 +133,15 @@ public function getResources(): ?array ); } }) - ->mapWithKeys(function($resource) { + ->mapWithKeys(function ($resource) { $name = $this->getPermissionIdentifier($resource); + return [ $name => [ 'resource' => "{$name}", 'model' => Str::of($resource::getModel())->afterLast('\\'), 'fqcn' => $resource, - ] + ], ]; }) ->sortKeys() @@ -205,11 +206,12 @@ public static function getPages(): ?array ->toString() ) ->toString(); + return [ $permission => [ 'class' => $page, - 'permission' => $permission - ] + 'permission' => $permission, + ], ]; }) ->toArray(); @@ -247,15 +249,15 @@ public static function getWidgets(): ?array if (Utils::isGeneralExcludeEnabled()) { return in_array( needle: str( - static::getWidgetInstanceFromWidgetConfiguration($widget) - ) + static::getWidgetInstanceFromWidgetConfiguration($widget) + ) ->afterLast('\\') ->toString(), haystack: Utils::getExcludedWidgets() ); } }) - ->mapWithKeys(function($widget) { + ->mapWithKeys(function ($widget) { $permission = Str::of(class_basename(static::getWidgetInstanceFromWidgetConfiguration($widget))) ->prepend( Str::of(Utils::getWidgetPermissionPrefix()) @@ -267,8 +269,8 @@ public static function getWidgets(): ?array return [ $permission => [ 'class' => static::getWidgetInstanceFromWidgetConfiguration($widget), - 'permission' => $permission - ] + 'permission' => $permission, + ], ]; }) ->toArray(); @@ -301,7 +303,7 @@ protected function getDefaultPermissionIdentifier(string $resource): string ->replace('_', '::'); } - protected static function getWidgetInstanceFromWidgetConfiguration(string|WidgetConfiguration $widget):string + protected static function getWidgetInstanceFromWidgetConfiguration(string | WidgetConfiguration $widget): string { return $widget instanceof WidgetConfiguration ? $widget->widget diff --git a/src/Resources/RoleResource.php b/src/Resources/RoleResource.php index b45b109..792a071 100644 --- a/src/Resources/RoleResource.php +++ b/src/Resources/RoleResource.php @@ -445,7 +445,7 @@ protected static function getCustomEntities(): ?Collection }); $entitiesPermissions = $resourcePermissions - ->merge(collect(FilamentShield::getPages())->map(fn($page) => $page['permission'])->values()) + ->merge(collect(FilamentShield::getPages())->map(fn ($page) => $page['permission'])->values()) ->merge(collect(FilamentShield::getWidgets())->map(fn ($widget) => $widget['permission'])->values()) ->values(); @@ -484,7 +484,7 @@ public static function getCheckBoxListComponentForResource(array $entity): Compo ->afterStateUpdated(fn ($livewire, Forms\Set $set) => static::toggleSelectAllViaEntities($livewire, $set)) ->selectAllAction(fn (FormAction $action, Component $component, $livewire, Forms\Set $set) => static::bulkToggleableAction($action, $component, $livewire, $set)) ->deselectAllAction(fn (FormAction $action, Component $component, $livewire, Forms\Set $set) => static::bulkToggleableAction($action, $component, $livewire, $set, true)) - ->dehydrated(fn ($state) => !blank($state)) + ->dehydrated(fn ($state) => ! blank($state)) ->bulkToggleable() ->gridDirection('row') ->columns(FilamentShieldPlugin::get()->getResourceCheckboxListColumns());