diff --git a/README.md b/README.md index 2f6e0f6..817a566 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,20 @@ This is an authentication plugin for Filament Admin with Laravel-permission php artisan config:clear ``` -4. Then execute the following commands: + +4. Register the plugin in your Panel provider: + > **Important: Register the plugin in your Panel provider after version 2.x** + ``` bash + use SolutionForest\FilamentAccessManagement\FilamentAccessManagementPanel; + + public function panel(Panel $panel): Panel + { + return $panel + ->plugin(FilamentAccessManagementPanel::make()); + } + ``` + +5. Then execute the following commands: ```bash php artisan filament-access-management:install ``` @@ -48,23 +61,13 @@ This is an authentication plugin for Filament Admin with Laravel-permission You can also create the super admin user with: ```bash - php artisan make:super-admin-user - ``` -5. Register the plugin in your Panel provider: - > **Important: Register the plugin in your Panel provider after version 2.x** - ``` bash - use SolutionForest\FilamentAccessManagement\FilamentAccessManagementPanel; - - public function panel(Panel $panel): Panel - { - return $panel - ->plugin(FilamentAccessManagementPanel::make()); - } - ``` - +6. Call upgrade command to upgrade data after version **2.2.0** + ```bash + php artisan filament-access-management:upgrade + ``` ## Publish Configs, Views, Translations and Migrations diff --git a/database/migrations/upgrade_menu_table.php.stub b/database/migrations/upgrade_menu_table.php.stub new file mode 100644 index 0000000..d7177e8 --- /dev/null +++ b/database/migrations/upgrade_menu_table.php.stub @@ -0,0 +1,33 @@ +boolean('is_filament_panel')->after('uri')->default(false); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + if (empty(Utils::getMenuTableName())) { + throw new \Exception('Error: config/filament-access-management.php not found and defaults could not be merged. Please publish the package configuration before proceeding, or drop the tables manually.'); + } + + Schema::table(Utils::getMenuTableName(), function (Blueprint $table) { + $table->dropColumn('is_filament_panel'); + }); + } +}; diff --git a/database/seeders/NavigationSeeder.php b/database/seeders/NavigationSeeder.php index 0c30521..113272d 100644 --- a/database/seeders/NavigationSeeder.php +++ b/database/seeders/NavigationSeeder.php @@ -2,9 +2,9 @@ namespace SolutionForest\FilamentAccessManagement\Database\Seeders; +use Filament\Facades\Filament; use Filament\Navigation\NavigationItem; use Filament\Pages\Dashboard; -use Filament\Resources\Resource; use Illuminate\Database\Seeder; use SolutionForest\FilamentAccessManagement\Facades\FilamentAuthenticate; use SolutionForest\FilamentAccessManagement\Support\Utils; @@ -27,9 +27,37 @@ public function run(): void /** @var \Illuminate\Support\Collection> */ $navigationGroup = collect(array_merge($pages, $resources)) ->filter(fn ($item) => is_string($item) && method_exists($item, 'getNavigationItems')) - ->map(fn ($item) => $item::getNavigationItems()) - ->flatten() - ->filter(fn ($navItem) => is_a($navItem, NavigationItem::class)) + ->map(function ($itemFQCN) { + if (is_subclass_of($itemFQCN, \Filament\Resources\Pages\Page::class) || + is_subclass_of($itemFQCN, \Filament\Pages\Page::class)) { + + $path = $itemFQCN::getRoutePath(); + + } elseif (is_subclass_of($itemFQCN, \Filament\Resources\Resource::class)) { + try { + + $path = (string)str($itemFQCN::getRoutePrefix())->prepend('/')->rtrim('/'); + + } catch (\Exception $e) { + return null; + } + + } else { + return null; + } + return NavigationItem::make($itemFQCN::getNavigationLabel()) + ->group($itemFQCN::getNavigationGroup()) + ->parentItem($itemFQCN::getNavigationParentItem()) + ->icon($itemFQCN::getNavigationIcon()) + ->activeIcon($itemFQCN::getActiveNavigationIcon()) + // ->isActiveWhen(fn (): bool => request()->routeIs($itemFQCN::getNavigationItemActiveRoutePattern())) + ->sort($itemFQCN::getNavigationSort()) + ->badge($itemFQCN::getNavigationBadge(), color: $itemFQCN::getNavigationBadgeColor()) + ->badgeTooltip($itemFQCN::getNavigationBadgeTooltip()) + ->url($path); + // ->url($itemFQCN::getNavigationUrl()); + }) + ->filter() ->groupBy(fn (NavigationItem $navItem) => $navItem->getGroup()) ->sortKeys(); @@ -48,9 +76,11 @@ public function run(): void parent: $parentId, icon: $navItem->getIcon(), activeIcon: $navItem->getActiveIcon(), - uri: admin_base_path($navItem->getUrl()), + // uri: admin_base_path($navItem->getUrl()), + uri: $navItem->getUrl(), badge: $navItem->getBadge(), badgeColor: $navItem->getBadgeColor(), + isFilamentPanel: true, ); } } diff --git a/resources/lang/en/filament-access-management.php b/resources/lang/en/filament-access-management.php index e945c0a..34a4db7 100644 --- a/resources/lang/en/filament-access-management.php +++ b/resources/lang/en/filament-access-management.php @@ -20,6 +20,7 @@ 'field.menu.icon' => 'ICON', 'field.menu.parent' => 'Parent', 'field.menu.uri' => 'URI', + 'field.menu.is_filament_panel' => 'Filament Panel?', 'field.guard_name' => 'Guard Name', 'field.title' => 'Title', diff --git a/src/Commands/Upgrade.php b/src/Commands/Upgrade.php new file mode 100644 index 0000000..81711f4 --- /dev/null +++ b/src/Commands/Upgrade.php @@ -0,0 +1,60 @@ +upgradeMenuUrl(); + + return static::SUCCESS; + } + + private function upgradeMenuUrl() + { + $model = Utils::getMenuModel(); + + // Find the old uri on FilamentAccessManagement v1 + $v1PathRecords = $model::where(function ($query) { + return $query + // ->orWhere('uri', '/') // Admin default page on filament v2 + ->orWhere('uri', '/admin') // Admin dashboard page on filament v2 + ->orWhere('uri', 'like', '/admin/%'); // The page(s) under admin on filament v2 + }) + ->where('is_filament_panel', false) // default value + ->get(); + + progress('Updating uri of menu as current version', count($v1PathRecords), function () use ($v1PathRecords) { + + foreach ($v1PathRecords as $v1PathRecord) { + + try { + + $newUri = (string)str($v1PathRecord->uri) + ->replace('/admin', ''); + + $v1PathRecord->update([ + 'uri' => $newUri, + 'is_filament_panel' => true, + ]); + + } catch (\Exception $e) { + $this->error("Updating uri of menu failed (Detail: {$e->getMessage()})"); + } + } + }); + + } +} diff --git a/src/FilamentAccessManagementServiceProvider.php b/src/FilamentAccessManagementServiceProvider.php index 3fead0a..10024a6 100644 --- a/src/FilamentAccessManagementServiceProvider.php +++ b/src/FilamentAccessManagementServiceProvider.php @@ -54,6 +54,7 @@ protected function getCommands(): array return [ Commands\MakeSuperAdminUser::class, Commands\MakeMenu::class, + Commands\Upgrade::class, ]; } @@ -61,6 +62,7 @@ protected function getMigrations(): array { return [ 'create_filament_admin_tables', + 'upgrade_menu_table', ]; } @@ -93,7 +95,7 @@ public function bootingPackage(): void public function packageBooted(): void { parent::packageBooted(); - + if ($this->app->runningInConsole()) { $configFiles = [ diff --git a/src/Models/Menu.php b/src/Models/Menu.php index 377f56c..719005b 100644 --- a/src/Models/Menu.php +++ b/src/Models/Menu.php @@ -7,8 +7,6 @@ use SolutionForest\FilamentAccessManagement\Facades\FilamentAuthenticate; use SolutionForest\FilamentAccessManagement\Support\Utils; use SolutionForest\FilamentTree\Concern\ModelTree; -use SolutionForest\FilamentTree\Support\Utils as FilamentTreeHelper; -use Spatie\EloquentSortable\SortableTrait; class Menu extends Model { @@ -19,16 +17,37 @@ class Menu extends Model 'icon', 'active_icon', 'uri', + 'is_filament_panel', 'badge', 'badge_color', 'parent_id', 'order', ]; + public $casts = [ + 'is_filament_panel' => 'boolean', + ]; + public function getNavigationUrl(): ?string { $uriColumnName = $this->determineUriColumnName(); - return empty($this->{$uriColumnName}) ? null : admin_url($this->{$uriColumnName}); + if (empty($this->{$uriColumnName})) { + return null; + } + if ($this->is_filament_panel && $panel = (filament()->getCurrentPanel() ?? filament()->getDefaultPanel())) { + + $pathInPanel = (string)str($panel->getPath()) + ->trim('/') + ->append('/') + ->when($panel->hasTenancy(), + fn ($str) => $str + ->append(filament()->getTenant()?->getKey()) + ->append('/')) + ->append(trim($this->{$uriColumnName}, '/')); + + return url($pathInPanel); + } + return $this->{$uriColumnName}; } public function determineTitleColumnName() : string @@ -61,7 +80,6 @@ public function determineBadgeColorColumnName() : string return 'badge_color'; } - protected static function boot() { parent::boot(); diff --git a/src/Pages/Menu.php b/src/Pages/Menu.php index 89f5869..c1291bc 100644 --- a/src/Pages/Menu.php +++ b/src/Pages/Menu.php @@ -37,6 +37,10 @@ protected function getFormSchema(): array ->label(__('filament-access-management::filament-access-management.field.menu.uri')) ->helperText('Relative path or external URL'), + Forms\Components\Toggle::make('is_filament_panel') + ->label(__('filament-access-management::filament-access-management.field.menu.is_filament_panel')) + ->inlineLabel(), + IconPicker::make('icon') ->label(__('filament-access-management::filament-access-management.field.menu.icon')) ->preload() diff --git a/src/Support/Menu.php b/src/Support/Menu.php index 4b6ff3e..a22a4eb 100644 --- a/src/Support/Menu.php +++ b/src/Support/Menu.php @@ -27,10 +27,15 @@ public static function createNavigation(string $title, ?string $activeIcon= null, ?string $uri= null, ?string $badge= null, - ?string $badgeColor= null): Model + ?string $badgeColor= null, + bool $isFilamentPanel= false): Model { return Utils::getMenuModel()::firstOrCreate( - ['title' => $title, 'parent_id' => $parent ?? -1], + [ + 'title' => $title, + 'parent_id' => $parent ?? -1, + 'is_filament_panel' => $isFilamentPanel, + ], [ 'icon' => $icon, 'active_icon' => $activeIcon, @@ -174,22 +179,38 @@ private static function buildNavigationGroupItems(array $treeItems = [], ?string } $model = app(Utils::getMenuModel()); - - $labelColumnName = method_exists($model, 'determineTitleColumnName') ? $model->determineTitleColumnName() : 'title'; - $iconColumnName = method_exists($model, 'determineIconColumnName') ? $model->determineIconColumnName() : 'icon'; - $activeIconColumnName = method_exists($model, 'determineActiveIconColumnName') ? $model->determineActiveIconColumnName() : 'active_icon'; - $uriColumnName = method_exists($model, 'determineUriColumnName') ? $model->determineUriColumnName() : 'uri'; - $badgeColumnName = method_exists($model, 'determineBadgeColumnName') ? $model->determineBadgeColumnName() : 'badge'; - $badgeColorColumnName = method_exists($model, 'determineBadgeColorColumnName') ? $model->determineBadgeColorColumnName() : 'badge_color'; - $orderColumnName = method_exists($model, 'determineOrderColumnName') ? $model->determineOrderColumnName() : FilamentTree\Support\Utils::orderColumnName(); - return collect($treeItems) ->map(function (array $treeItem) { static::handleTranslatable($treeItem); return $treeItem; }) - ->map(fn (array $treeItem) => - NavigationItem::make() + ->map(function (array $treeItem) use ($model) { + + $labelColumnName = method_exists($model, 'determineTitleColumnName') ? $model->determineTitleColumnName() : 'title'; + $iconColumnName = method_exists($model, 'determineIconColumnName') ? $model->determineIconColumnName() : 'icon'; + $activeIconColumnName = method_exists($model, 'determineActiveIconColumnName') ? $model->determineActiveIconColumnName() : 'active_icon'; + $uriColumnName = method_exists($model, 'determineUriColumnName') ? $model->determineUriColumnName() : 'uri'; + $badgeColumnName = method_exists($model, 'determineBadgeColumnName') ? $model->determineBadgeColumnName() : 'badge'; + $badgeColorColumnName = method_exists($model, 'determineBadgeColorColumnName') ? $model->determineBadgeColorColumnName() : 'badge_color'; + $orderColumnName = method_exists($model, 'determineOrderColumnName') ? $model->determineOrderColumnName() : FilamentTree\Support\Utils::orderColumnName(); + + $url = trim(($treeItem[$uriColumnName] ?? "/"), '/'); + + if (($treeItem['is_filament_panel'] ?? false) == true && $panel = (filament()->getCurrentPanel() ?? filament()->getDefaultPanel())) { + + $pathInPanel = (string)str($panel->getPath()) + ->trim('/') + ->append('/') + ->when($panel->hasTenancy(), + fn ($str) => $str + ->append(filament()->getTenant()?->getKey()) + ->append('/')) + ->append($url); + + $url = url($pathInPanel); + } + + return NavigationItem::make() ->label(static::ensureNavigationLabel($treeItem[$labelColumnName]) ?? "") ->group($groupLabel ?? "") ->groupIcon($groupIcon ?? "") @@ -198,7 +219,7 @@ private static function buildNavigationGroupItems(array $treeItems = [], ?string ->isActiveWhen(fn (): bool => request()->is(trim(($treeItem[$uriColumnName] ?? "/"), '/'))) ->sort(intval($treeItem[$orderColumnName] ?? 0)) ->badge(($treeItem[$badgeColumnName] ?? null), color: ($treeItem[$badgeColorColumnName] ?? null)) - ->url(admin_url(trim(($treeItem[$uriColumnName] ?? "/"), '/'))) - )->toArray(); + ->url($url); + })->toArray(); } } diff --git a/src/helpers.php b/src/helpers.php index 302fd78..96169a3 100644 --- a/src/helpers.php +++ b/src/helpers.php @@ -12,6 +12,9 @@ function filament_auth(): FilamentAuthenticate } } +/** + * @deprecated version 2.2.0 + */ if (! function_exists('admin_url')) { /** * Get admin url. @@ -31,13 +34,16 @@ function admin_url($path = '', $parameters = [], $secure = null) } } +/** + * @deprecated version 2.2.0 + */ if (! function_exists('admin_base_path')) { /** * Get admin base path. */ - function admin_base_path($path = '') + function admin_base_path($path = '', $panel = null) { - $prefix = '/'.trim(config('filament.path'), '/'); + $prefix = '/'.trim(config('filament.path', (filament()->getPanel($panel) ?? filament()->getCurrentPanel() ?? filament()->getDefaultPanel())->getPath()), '/'); $prefix = ($prefix == '/') ? '' : $prefix;