diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 0c2abd6..d953bc4 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,5 +1,6 @@ parameters: ignoreErrors: + # Database seeders - App\Models\User is application-specific - message: "#^Call to static method first\\(\\) on an unknown class App\\\\Models\\\\User\\.$#" count: 1 @@ -10,148 +11,38 @@ parameters: count: 1 path: database/seeders/LibrarySeeder.php + # Config file - env() is allowed in config files - - message: "#^Call to static method created\\(\\) on an unknown class App\\\\Models\\\\User\\.$#" - count: 1 - path: src/FilamentLibraryPlugin.php + message: "#^Called 'env' outside of the config directory which returns null when the config is cached, use 'config'\\.$#" + path: config/filament-library.php + # Forms - User model is dynamic - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$name\\.$#" count: 1 path: src/Forms/Components/UserSearchSelect.php - - - - message: "#^Access to an undefined property Tapp\\\\FilamentLibrary\\\\Models\\\\LibraryItem\\:\\:\\$created_by\\.$#" - count: 3 - path: src/Models/LibraryItem.php - - - - message: "#^Access to an undefined property Tapp\\\\FilamentLibrary\\\\Models\\\\LibraryItem\\:\\:\\$external_url\\.$#" - count: 1 - path: src/Models/LibraryItem.php - - - - message: "#^Access to an undefined property Tapp\\\\FilamentLibrary\\\\Models\\\\LibraryItem\\:\\:\\$general_access\\.$#" - count: 2 - path: src/Models/LibraryItem.php - - - - message: "#^Access to an undefined property Tapp\\\\FilamentLibrary\\\\Models\\\\LibraryItem\\:\\:\\$name\\.$#" - count: 4 - path: src/Models/LibraryItem.php - - - - message: "#^Access to an undefined property Tapp\\\\FilamentLibrary\\\\Models\\\\LibraryItem\\:\\:\\$parent_id\\.$#" - count: 7 - path: src/Models/LibraryItem.php - - - - message: "#^Access to an undefined property Tapp\\\\FilamentLibrary\\\\Models\\\\LibraryItem\\:\\:\\$slug\\.$#" - count: 2 - path: src/Models/LibraryItem.php - - - - message: "#^Access to an undefined property Tapp\\\\FilamentLibrary\\\\Models\\\\LibraryItem\\:\\:\\$type\\.$#" - count: 3 - path: src/Models/LibraryItem.php - - - - message: "#^Access to an undefined property Tapp\\\\FilamentLibrary\\\\Models\\\\LibraryItem\\:\\:\\$updated_by\\.$#" - count: 2 - path: src/Models/LibraryItem.php - - - - - message: "#^Access to an undefined property Tapp\\\\FilamentLibrary\\\\Models\\\\LibraryItemPermission\\:\\:\\$user_id\\.$#" - count: 1 - path: src/Models/LibraryItem.php - - - - message: "#^Access to property \\$email on an unknown class App\\\\Models\\\\User\\.$#" - count: 1 - path: src/Models/LibraryItem.php - - - - message: "#^Access to property \\$first_name on an unknown class App\\\\Models\\\\User\\.$#" - count: 1 - path: src/Models/LibraryItem.php - + # Models - Properties are defined via @property annotations but PHPStan needs explicit ignores for some cases + # These are false positives since properties are documented - - message: "#^Access to property \\$id on an unknown class App\\\\Models\\\\User\\.$#" - count: 7 - path: src/Models/LibraryItem.php - - - - message: "#^Access to property \\$name on an unknown class App\\\\Models\\\\User\\.$#" - count: 1 - path: src/Models/LibraryItem.php - - - - message: "#^Access to property \\$personal_folder_id on an unknown class App\\\\Models\\\\User\\.$#" - count: 4 - path: src/Models/LibraryItem.php - - - - message: "#^Call to an undefined method Illuminate\\\\Foundation\\\\Auth\\\\User\\:\\:favoriteLibraryItems\\(\\)\\.$#" - count: 4 - path: src/Models/LibraryItem.php - - - - message: "#^Call to method update\\(\\) on an unknown class App\\\\Models\\\\User\\.$#" - count: 1 - path: src/Models/LibraryItem.php - - - - message: "#^Class App\\\\Models\\\\User not found\\.$#" - count: 2 - path: src/Models/LibraryItem.php - - - - message: "#^Method Tapp\\\\FilamentLibrary\\\\Models\\\\LibraryItem\\:\\:getCurrentOwner\\(\\) has invalid return type App\\\\Models\\\\User\\.$#" - count: 1 - path: src/Models/LibraryItem.php - - - - message: "#^Method Tapp\\\\FilamentLibrary\\\\Models\\\\LibraryItem\\:\\:getCurrentOwner\\(\\) should return App\\\\Models\\\\User\\|null but returns Illuminate\\\\Database\\\\Eloquent\\\\Model\\|null\\.$#" - count: 1 - path: src/Models/LibraryItem.php - - - - message: "#^Parameter \\$newOwner of method Tapp\\\\FilamentLibrary\\\\Models\\\\LibraryItem\\:\\:transferOwnership\\(\\) has invalid type App\\\\Models\\\\User\\.$#" - count: 1 - path: src/Models/LibraryItem.php - - - - message: "#^Parameter \\$user of method Tapp\\\\FilamentLibrary\\\\Models\\\\LibraryItem\\:\\:ensurePersonalFolder\\(\\) has invalid type App\\\\Models\\\\User\\.$#" - count: 1 - path: src/Models/LibraryItem.php - - - - message: "#^Parameter \\$user of method Tapp\\\\FilamentLibrary\\\\Models\\\\LibraryItem\\:\\:getPersonalFolder\\(\\) has invalid type App\\\\Models\\\\User\\.$#" - count: 1 + message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$id\\.$#" path: src/Models/LibraryItem.php - - message: "#^Parameter \\$user of method Tapp\\\\FilamentLibrary\\\\Models\\\\LibraryItem\\:\\:getPersonalFolderName\\(\\) has invalid type App\\\\Models\\\\User\\.$#" - count: 1 + message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$user\\.$#" path: src/Models/LibraryItem.php - - message: "#^Access to an undefined property Tapp\\\\FilamentLibrary\\\\Models\\\\LibraryItemPermission\\:\\:\\$role\\.$#" - count: 2 + message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$personal_folder_id\\.$#" path: src/Models/LibraryItem.php - - - message: "#^Access to an undefined property Tapp\\\\FilamentLibrary\\\\Models\\\\LibraryItemPermission\\:\\:\\$role\\.$#" - count: 2 - path: src/Models/LibraryItemPermission.php - + # Policies - Properties are documented - message: "#^Access to an undefined property Tapp\\\\FilamentLibrary\\\\Models\\\\LibraryItem\\:\\:\\$created_by\\.$#" count: 7 path: src/Policies/LibraryItemPolicy.php + # Resources - Properties are documented - message: "#^Access to an undefined property Tapp\\\\FilamentLibrary\\\\Models\\\\LibraryItem\\:\\:\\$deleted_at\\.$#" count: 2 @@ -172,201 +63,80 @@ parameters: count: 6 path: src/Resources/LibraryItemResource.php + # Pages - Model type hints are generic but we know they're LibraryItem instances - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$parent_id\\.$#" - count: 2 - path: src/Resources/Pages/EditFile.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:getFirstMedia\\(\\)\\.$#" - count: 2 - path: src/Resources/Pages/EditFile.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:getInheritedGeneralAccessDisplay\\(\\)\\.$#" - count: 1 - path: src/Resources/Pages/EditFile.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:hasPermission\\(\\)\\.$#" - count: 1 - path: src/Resources/Pages/EditFile.php - - - - message: "#^Left side of && is always true\\.$#" - count: 1 - path: src/Resources/Pages/EditFile.php - - - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$parent_id\\.$#" - count: 2 - path: src/Resources/Pages/EditFolder.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:getInheritedGeneralAccessDisplay\\(\\)\\.$#" - count: 1 - path: src/Resources/Pages/EditFolder.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:hasPermission\\(\\)\\.$#" - count: 1 - path: src/Resources/Pages/EditFolder.php + path: src/Resources/Pages - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$id\\.$#" - count: 2 - path: src/Resources/Pages/EditLibraryItem.php + path: src/Resources/Pages - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$name\\.$#" - count: 1 - path: src/Resources/Pages/EditLibraryItem.php + path: src/Resources/Pages - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$parent\\.$#" - count: 1 - path: src/Resources/Pages/EditLibraryItem.php - - - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$parent_id\\.$#" - count: 3 - path: src/Resources/Pages/EditLibraryItem.php + path: src/Resources/Pages - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$type\\.$#" - count: 2 - path: src/Resources/Pages/EditLibraryItem.php + path: src/Resources/Pages - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$created_by\\.$#" - count: 1 - path: src/Resources/Pages/EditLibraryItemPage.php - - - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$id\\.$#" - count: 3 - path: src/Resources/Pages/EditLibraryItemPage.php - - - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$name\\.$#" - count: 1 - path: src/Resources/Pages/EditLibraryItemPage.php - - - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$parent\\.$#" - count: 2 - path: src/Resources/Pages/EditLibraryItemPage.php - - - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$parent_id\\.$#" - count: 2 - path: src/Resources/Pages/EditLibraryItemPage.php + path: src/Resources/Pages - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$type\\.$#" - count: 1 - path: src/Resources/Pages/EditLibraryItemPage.php - - - - message: "#^Access to an undefined property Tapp\\\\FilamentLibrary\\\\Resources\\\\Pages\\\\EditLibraryItemPage\\:\\:\\$parentId\\.$#" - count: 2 - path: src/Resources/Pages/EditLibraryItemPage.php - - - - message: "#^Call to static method find\\(\\) on an unknown class App\\\\Models\\\\User\\.$#" - count: 1 - path: src/Resources/Pages/EditLibraryItemPage.php + message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$external_url\\.$#" + path: src/Resources/Pages - - message: "#^Call to static method query\\(\\) on an unknown class App\\\\Models\\\\User\\.$#" - count: 1 - path: src/Resources/Pages/EditLibraryItemPage.php + message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$link_description\\.$#" + path: src/Resources/Pages - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$parent_id\\.$#" - count: 2 - path: src/Resources/Pages/EditLink.php + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:getFirstMedia\\(\\)\\.$#" + path: src/Resources/Pages - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:getInheritedGeneralAccessDisplay\\(\\)\\.$#" - count: 1 - path: src/Resources/Pages/EditLink.php + path: src/Resources/Pages - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:hasPermission\\(\\)\\.$#" - count: 1 - path: src/Resources/Pages/EditLink.php - - - - message: "#^Access to an undefined property Tapp\\\\FilamentLibrary\\\\Models\\\\LibraryItem\\:\\:\\$link_description\\.$#" - count: 1 - path: src/Resources/Pages/ListLibraryItems.php - - - - message: "#^Access to an undefined property Tapp\\\\FilamentLibrary\\\\Models\\\\LibraryItem\\:\\:\\$name\\.$#" - count: 2 - path: src/Resources/Pages/ListLibraryItems.php - - - - message: "#^Access to an undefined property Tapp\\\\FilamentLibrary\\\\Models\\\\LibraryItem\\:\\:\\$parent_id\\.$#" - count: 1 - path: src/Resources/Pages/ListLibraryItems.php - - - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$external_url\\.$#" - count: 1 - path: src/Resources/Pages/ViewLibraryItem.php + path: src/Resources/Pages - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$id\\.$#" - count: 1 - path: src/Resources/Pages/ViewLibraryItem.php + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:getSecureUrl\\(\\)\\.$#" + path: src/Resources/Pages - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$link_description\\.$#" - count: 1 - path: src/Resources/Pages/ViewLibraryItem.php + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:isVideoUrl\\(\\)\\.$#" + path: src/Resources/Pages - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$name\\.$#" + message: "#^Access to an undefined property Tapp\\\\FilamentLibrary\\\\Resources\\\\Pages\\\\EditLibraryItemPage\\:\\:\\$parentId\\.$#" count: 2 - path: src/Resources/Pages/ViewLibraryItem.php - - - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$parent\\.$#" - count: 1 - path: src/Resources/Pages/ViewLibraryItem.php - - - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$parent_id\\.$#" - count: 3 - path: src/Resources/Pages/ViewLibraryItem.php - - - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$type\\.$#" - count: 6 - path: src/Resources/Pages/ViewLibraryItem.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:getFirstMedia\\(\\)\\.$#" - count: 1 - path: src/Resources/Pages/ViewLibraryItem.php + path: src/Resources/Pages/EditLibraryItemPage.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:getSecureUrl\\(\\)\\.$#" + message: "#^Call to static method find\\(\\) on an unknown class App\\\\Models\\\\User\\.$#" count: 1 - path: src/Resources/Pages/ViewLibraryItem.php + path: src/Resources/Pages/EditLibraryItemPage.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:isVideoUrl\\(\\)\\.$#" + message: "#^Call to static method query\\(\\) on an unknown class App\\\\Models\\\\User\\.$#" count: 1 - path: src/Resources/Pages/ViewLibraryItem.php + path: src/Resources/Pages/EditLibraryItemPage.php - - message: "#^Anonymous function never returns null so it can be removed from the return type\\.$#" + message: "#^Left side of && is always true\\.$#" count: 1 - path: src/Resources/RelationManagers/LibraryItemPermissionsRelationManager.php + path: src/Resources/Pages/EditFile.php + # Relation Managers - Model type hints are generic - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:hasPermission\\(\\)\\.$#" count: 7 @@ -377,7 +147,6 @@ parameters: count: 1 path: src/Resources/RelationManagers/LibraryItemPermissionsRelationManager.php - - message: "#^Call to static method find\\(\\) on an unknown class App\\\\Models\\\\User\\.$#" count: 1 @@ -399,71 +168,36 @@ parameters: path: src/Resources/RelationManagers/LibraryItemPermissionsRelationManager.php - - message: """ - #^Call to deprecated method actions\\(\\) of class Filament\\\\Tables\\\\Table\\: - Use `recordActions\\(\\)` instead\\.$# - """ - count: 1 - path: src/Resources/RelationManagers/LibraryItemPermissionsRelationManager.php - - - - message: """ - #^Call to deprecated method bulkActions\\(\\) of class Filament\\\\Tables\\\\Table\\: - Use `toolbarActions\\(\\)` instead\\.$# - """ + message: "#^Anonymous function never returns null so it can be removed from the return type\\.$#" count: 1 path: src/Resources/RelationManagers/LibraryItemPermissionsRelationManager.php + # Services - Properties are documented - message: "#^Access to an undefined property Tapp\\\\FilamentLibrary\\\\Models\\\\LibraryItem\\:\\:\\$parent_id\\.$#" count: 1 path: src/Services/PermissionService.php - - - - message: "#^Missing parameter \\$notification \\(Closure\\|Filament\\\\Notifications\\\\Notification\\|null\\) in call to method Filament\\\\Actions\\\\Action\\:\\:successNotification\\(\\)\\.$#" - count: 1 - path: src/Tables/Actions/BulkManagePermissionsAction.php - - - - message: "#^Unknown parameter \\$body in call to method Filament\\\\Actions\\\\Action\\:\\:successNotification\\(\\)\\.$#" - count: 1 - path: src/Tables/Actions/BulkManagePermissionsAction.php - - - - message: "#^Unknown parameter \\$title in call to method Filament\\\\Actions\\\\Action\\:\\:successNotification\\(\\)\\.$#" + message: "#^Access to an undefined property Tapp\\\\FilamentLibrary\\\\Models\\\\LibraryItem\\:\\:\\$type\\.$#" count: 1 - path: src/Tables/Actions/BulkManagePermissionsAction.php + path: src/Services/PermissionService.php + # Tables - Properties are documented - message: "#^Access to an undefined property Tapp\\\\FilamentLibrary\\\\Models\\\\LibraryItem\\:\\:\\$created_by\\.$#" count: 1 path: src/Tables/Columns/PermissionsColumn.php + # Instanceof checks - These are necessary for type narrowing but PHPStan sees them as always true + # This happens because PHPStan doesn't understand the dynamic nature of the ownerRecord - - message: "#^Access to an undefined property Tapp\\\\FilamentLibrary\\\\Models\\\\LibraryItem\\:\\:\\$type\\.$#" - count: 1 - path: src/Middleware/RedirectToCorrectEditPage.php - - - - - message: "#^Access to an undefined property Tapp\\\\FilamentLibrary\\\\Models\\\\LibraryItem\\:\\:\\$type\\.$#" - count: 1 - path: src/Services/PermissionService.php + message: "#^Instanceof between Tapp\\\\FilamentLibrary\\\\Models\\\\LibraryItem and Tapp\\\\FilamentLibrary\\\\Models\\\\LibraryItem will always evaluate to true\\.$#" + path: src/Resources/RelationManagers/LibraryItemPermissionsRelationManager.php + # Unused traits - These are provided for users to use in their own models - - message: """ - #^Call to deprecated method form\\(\\) of class Filament\\\\Actions\\\\Action\\: - Use `schema\\(\\) instead\\.$# - """ - count: 1 - path: src/Tables/Actions/BulkManagePermissionsAction.php + message: "#^Trait Tapp\\\\FilamentLibrary\\\\Traits\\\\HasLibraryFavorites is used zero times and is not analysed\\.$#" - - message: """ - #^Call to method make\\(\\) of deprecated class Filament\\\\Infolists\\\\Components\\\\Entry\\: - Use `TextEntry` with the `state\\(\\)` method instead\\.$# - """ - count: 1 - path: src/Tables/Actions/BulkManagePermissionsAction.php + message: "#^Trait Tapp\\\\FilamentLibrary\\\\Traits\\\\LibraryUser is used zero times and is not analysed\\.$#" diff --git a/src/FilamentLibraryPlugin.php b/src/FilamentLibraryPlugin.php index 7420fb4..ef225cd 100644 --- a/src/FilamentLibraryPlugin.php +++ b/src/FilamentLibraryPlugin.php @@ -5,6 +5,7 @@ use Filament\Contracts\Plugin; use Filament\Navigation\NavigationItem; use Filament\Panel; +use Tapp\FilamentLibrary\Resources\LibraryItemResource; class FilamentLibraryPlugin implements Plugin { @@ -58,47 +59,49 @@ public static function isLibraryAdmin($user): bool public function register(Panel $panel): void { + $panelId = $panel->getId(); + $panel ->resources([ - \Tapp\FilamentLibrary\Resources\LibraryItemResource::class, + LibraryItemResource::class, ]) ->navigationItems([ NavigationItem::make('Library') - ->url('/library') + ->url(fn () => LibraryItemResource::getUrl('index')) ->icon('heroicon-o-building-library') ->group('Resource Library') ->sort(1) - ->isActiveWhen(fn () => request()->is('library')), + ->isActiveWhen(fn () => request()->routeIs("filament.{$panelId}.resources.library.index")), NavigationItem::make('Search All') - ->url('/library/search-all') + ->url(fn () => LibraryItemResource::getUrl('search-all')) ->icon('heroicon-o-magnifying-glass') ->group('Resource Library') ->sort(2) - ->isActiveWhen(fn () => request()->is('library/search-all')), + ->isActiveWhen(fn () => request()->routeIs("filament.{$panelId}.resources.library.search-all")), NavigationItem::make('My Documents') - ->url('/library/my-documents') + ->url(fn () => LibraryItemResource::getUrl('my-documents')) ->icon('heroicon-o-folder') ->group('Resource Library') ->sort(3) - ->isActiveWhen(fn () => request()->is('library/my-documents')), + ->isActiveWhen(fn () => request()->routeIs("filament.{$panelId}.resources.library.my-documents")), NavigationItem::make('Shared with Me') - ->url('/library/shared-with-me') + ->url(fn () => LibraryItemResource::getUrl('shared-with-me')) ->icon('heroicon-o-share') ->group('Resource Library') ->sort(4) - ->isActiveWhen(fn () => request()->is('library/shared-with-me')), + ->isActiveWhen(fn () => request()->routeIs("filament.{$panelId}.resources.library.shared-with-me")), NavigationItem::make('Created by Me') - ->url('/library/created-by-me') + ->url(fn () => LibraryItemResource::getUrl('created-by-me')) ->icon('heroicon-o-user') ->group('Resource Library') ->sort(5) - ->isActiveWhen(fn () => request()->is('library/created-by-me')), + ->isActiveWhen(fn () => request()->routeIs("filament.{$panelId}.resources.library.created-by-me")), NavigationItem::make('Favorites') - ->url('/library/favorites') + ->url(fn () => LibraryItemResource::getUrl('favorites')) ->icon('heroicon-o-star') ->group('Resource Library') ->sort(6) - ->isActiveWhen(fn () => request()->is('library/favorites')), + ->isActiveWhen(fn () => request()->routeIs("filament.{$panelId}.resources.library.favorites")), ]); } diff --git a/src/Middleware/RedirectToCorrectEditPage.php b/src/Middleware/RedirectToCorrectEditPage.php index 38a9554..231cdb7 100644 --- a/src/Middleware/RedirectToCorrectEditPage.php +++ b/src/Middleware/RedirectToCorrectEditPage.php @@ -3,6 +3,7 @@ namespace Tapp\FilamentLibrary\Middleware; use Closure; +use Filament\Facades\Filament; use Illuminate\Http\Request; use Tapp\FilamentLibrary\Models\LibraryItem; @@ -13,8 +14,16 @@ class RedirectToCorrectEditPage */ public function handle(Request $request, Closure $next) { - // Check if this is an edit route for library items - if ($request->routeIs('filament.admin.resources.library.edit')) { + $panel = Filament::getCurrentPanel(); + + if (! $panel) { + return $next($request); + } + + $panelId = $panel->getId(); + + // Check if this is an edit route for library items in any panel + if ($request->routeIs("filament.{$panelId}.resources.library.edit")) { $recordId = $request->route('record'); if ($recordId) { @@ -22,11 +31,12 @@ public function handle(Request $request, Closure $next) if ($libraryItem) { // Redirect to the correct edit page based on type - $editUrl = match ($libraryItem->type) { - 'folder' => route('filament.admin.resources.library.edit-folder', ['record' => $recordId]), - 'file' => route('filament.admin.resources.library.edit-file', ['record' => $recordId]), - 'link' => route('filament.admin.resources.library.edit-link', ['record' => $recordId]), - default => route('filament.admin.resources.library.edit-folder', ['record' => $recordId]), + $type = $libraryItem->type ?? 'folder'; + $editUrl = match ($type) { + 'folder' => route("filament.{$panelId}.resources.library.edit-folder", ['record' => $recordId]), + 'file' => route("filament.{$panelId}.resources.library.edit-file", ['record' => $recordId]), + 'link' => route("filament.{$panelId}.resources.library.edit-link", ['record' => $recordId]), + default => route("filament.{$panelId}.resources.library.edit-folder", ['record' => $recordId]), }; return redirect($editUrl); diff --git a/src/Models/LibraryItem.php b/src/Models/LibraryItem.php index ac86aaf..97f65b7 100644 --- a/src/Models/LibraryItem.php +++ b/src/Models/LibraryItem.php @@ -13,6 +13,27 @@ use Spatie\MediaLibrary\InteractsWithMedia; use Spatie\MediaLibrary\MediaCollections\Models\Media; +/** + * @property int $id + * @property string $name + * @property string $slug + * @property string $type + * @property int|null $parent_id + * @property int $created_by + * @property int|null $updated_by + * @property string|null $external_url + * @property string|null $link_description + * @property string|null $general_access + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property \Illuminate\Support\Carbon|null $deleted_at + * @property-read LibraryItem|null $parent + * @property-read \Illuminate\Database\Eloquent\Collection $children + * @property-read \Illuminate\Database\Eloquent\Model $creator + * @property-read \Illuminate\Database\Eloquent\Model|null $updater + * @property-read \Illuminate\Database\Eloquent\Collection $permissions + * @property-read \Illuminate\Database\Eloquent\Collection $tags + */ class LibraryItem extends Model implements HasMedia { use HasFactory; @@ -58,14 +79,16 @@ protected static function boot(): void static::created(function (self $item) { // Copy parent folder permissions to the new item - if ($item->parent_id) { + if ($item->parent_id && $item->parent) { $parentPermissions = $item->parent->permissions()->get(); foreach ($parentPermissions as $permission) { - $item->permissions()->create([ - 'user_id' => $permission->user_id, - 'role' => $permission->role, - ]); + if (isset($permission->user_id) && isset($permission->role)) { + $item->permissions()->create([ + 'user_id' => $permission->user_id, + 'role' => $permission->role, + ]); + } } } }); @@ -103,7 +126,9 @@ public function children(): HasMany */ public function creator(): BelongsTo { - return $this->belongsTo(\App\Models\User::class, 'created_by')->withDefault(function () { + $userModel = config('filament-library.user_model', config('auth.providers.users.model', 'App\\Models\\User')); + + return $this->belongsTo($userModel, 'created_by')->withDefault(function () { // Check if 'name' field exists if (\Illuminate\Support\Facades\Schema::hasColumn('users', 'name')) { return [ @@ -126,7 +151,9 @@ public function creator(): BelongsTo */ public function updater(): BelongsTo { - return $this->belongsTo(\App\Models\User::class, 'updated_by'); + $userModel = config('filament-library.user_model', config('auth.providers.users.model', 'App\\Models\\User')); + + return $this->belongsTo($userModel, 'updated_by'); } /** @@ -199,7 +226,7 @@ public function getEffectiveRole($user): ?string ->where('user_id', $user->id) ->first(); - if ($directPermission) { + if ($directPermission && isset($directPermission->role)) { return $directPermission->role; } @@ -221,13 +248,13 @@ public function getEffectiveRole($user): ?string /** * Get the current owner of this item. */ - public function getCurrentOwner(): ?\App\Models\User + public function getCurrentOwner(): ?\Illuminate\Database\Eloquent\Model { $ownerPermission = $this->permissions() ->where('role', 'owner') ->first(); - if ($ownerPermission) { + if ($ownerPermission && $ownerPermission->user) { return $ownerPermission->user; } @@ -248,7 +275,7 @@ public function isCreatorOwner(): bool /** * Transfer ownership to another user. */ - public function transferOwnership(\App\Models\User $newOwner): void + public function transferOwnership(\Illuminate\Database\Eloquent\Model $newOwner): void { // Remove existing owner permissions $this->permissions()->where('role', 'owner')->delete(); @@ -271,7 +298,7 @@ public function transferOwnership(\App\Models\User $newOwner): void /** * Ensure a user has a personal folder (like Google Drive's "My Drive"). */ - public static function ensurePersonalFolder(\App\Models\User $user): self + public static function ensurePersonalFolder(\Illuminate\Database\Eloquent\Model $user): self { // Check if user already has a personal folder via the relationship if ($user->personal_folder_id) { @@ -306,7 +333,7 @@ public static function ensurePersonalFolder(\App\Models\User $user): self /** * Get a user's personal folder. */ - public static function getPersonalFolder(\App\Models\User $user): ?self + public static function getPersonalFolder(\Illuminate\Database\Eloquent\Model $user): ?self { if (! $user->personal_folder_id) { return null; @@ -318,7 +345,7 @@ public static function getPersonalFolder(\App\Models\User $user): ?self /** * Generate the personal folder name for a user. */ - public static function getPersonalFolderName(\App\Models\User $user): string + public static function getPersonalFolderName(\Illuminate\Database\Eloquent\Model $user): string { // Try to get a display name from various user fields $name = $user->first_name ?? $user->name ?? $user->email ?? 'User'; diff --git a/src/Models/LibraryItemPermission.php b/src/Models/LibraryItemPermission.php index 42f5464..3d686f0 100644 --- a/src/Models/LibraryItemPermission.php +++ b/src/Models/LibraryItemPermission.php @@ -6,6 +6,16 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; +/** + * @property int $id + * @property int $library_item_id + * @property int $user_id + * @property string $role + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property-read LibraryItem $libraryItem + * @property-read \Illuminate\Database\Eloquent\Model $user + */ class LibraryItemPermission extends Model { use HasFactory; diff --git a/src/Resources/LibraryItemResource.php b/src/Resources/LibraryItemResource.php index 3c3668e..0ab782a 100644 --- a/src/Resources/LibraryItemResource.php +++ b/src/Resources/LibraryItemResource.php @@ -293,7 +293,7 @@ public static function table(Table $table): Table ->toolbarActions([ BulkActionGroup::make([ DeleteBulkAction::make() - ->visible(fn (): bool => auth()->user() && auth()->user()->can('delete', LibraryItem::class)) + ->visible(fn (): bool => auth()->user() && auth()->user()->can('deleteAny', LibraryItem::class)) ->successRedirectUrl(function () { // For bulk actions, redirect to current folder (maintain current location) $currentParent = request()->get('parent'); @@ -301,9 +301,9 @@ public static function table(Table $table): Table return static::getUrl('index', $currentParent ? ['parent' => $currentParent] : []); }), RestoreBulkAction::make() - ->visible(fn (): bool => auth()->user() && auth()->user()->can('delete', LibraryItem::class)), + ->visible(fn (): bool => auth()->user() && auth()->user()->can('restoreAny', LibraryItem::class)), ForceDeleteBulkAction::make() - ->visible(fn (): bool => auth()->user() && auth()->user()->can('delete', LibraryItem::class)) + ->visible(fn (): bool => auth()->user() && auth()->user()->can('forceDeleteAny', LibraryItem::class)) ->successRedirectUrl(function () { // For bulk actions, redirect to current folder (maintain current location) $currentParent = request()->get('parent'); diff --git a/src/Resources/Pages/ListLibraryItems.php b/src/Resources/Pages/ListLibraryItems.php index 557f0a4..3a0fbfa 100644 --- a/src/Resources/Pages/ListLibraryItems.php +++ b/src/Resources/Pages/ListLibraryItems.php @@ -254,7 +254,7 @@ protected function getTableQuery(): \Illuminate\Database\Eloquent\Builder public function getTitle(): string { - if ($this->parentFolder) { + if ($this->parentFolder && isset($this->parentFolder->name)) { return $this->parentFolder->name; } @@ -263,7 +263,7 @@ public function getTitle(): string public function getSubheading(): ?string { - if ($this->parentFolder && $this->parentFolder->link_description) { + if ($this->parentFolder && isset($this->parentFolder->link_description) && $this->parentFolder->link_description) { return $this->parentFolder->link_description; } @@ -298,7 +298,9 @@ public function getBreadcrumbs(): array // Generate URLs more efficiently $baseUrl = static::getResource()::getUrl('index'); foreach ($path as $folder) { - $breadcrumbs[$baseUrl . '?parent=' . $folder->id] = $folder->name; + if (isset($folder->id) && isset($folder->name)) { + $breadcrumbs[$baseUrl . '?parent=' . $folder->id] = $folder->name; + } } } diff --git a/src/Resources/RelationManagers/LibraryItemPermissionsRelationManager.php b/src/Resources/RelationManagers/LibraryItemPermissionsRelationManager.php index 9237eb0..701b3a3 100644 --- a/src/Resources/RelationManagers/LibraryItemPermissionsRelationManager.php +++ b/src/Resources/RelationManagers/LibraryItemPermissionsRelationManager.php @@ -15,6 +15,7 @@ use Filament\Tables\Table; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Facades\Schema as SchemaFacade; +use Tapp\FilamentLibrary\Models\LibraryItem; use Tapp\FilamentLibrary\Models\LibraryItemPermission; class LibraryItemPermissionsRelationManager extends RelationManager @@ -41,7 +42,7 @@ public static function canAccess(): bool // For non-admins, check if they have share permission on the current record $record = static::getOwnerRecord(); - if ($record && $record->hasPermission($user, 'share')) { + if ($record instanceof LibraryItem && $record->hasPermission($user, 'share')) { return true; } @@ -50,7 +51,7 @@ public static function canAccess(): bool public static function canViewForRecord(Model $ownerRecord, string $pageClass): bool { - return $ownerRecord->hasPermission(auth()->user(), 'share'); + return $ownerRecord instanceof LibraryItem && $ownerRecord->hasPermission(auth()->user(), 'share'); } /** @@ -173,20 +174,20 @@ public function table(Table $table): Table ]) ->headerActions([ CreateAction::make() - ->visible(fn () => $this->ownerRecord->hasPermission(auth()->user(), 'share')), + ->visible(fn () => $this->ownerRecord instanceof LibraryItem && $this->ownerRecord->hasPermission(auth()->user(), 'share')), ]) ->heading('User Permissions') ->description('Owner: Share and edit. Editor/Viewer: Standard permissions.') - ->actions([ + ->recordActions([ EditAction::make() - ->visible(fn () => $this->ownerRecord->hasPermission(auth()->user(), 'share')), + ->visible(fn () => $this->ownerRecord instanceof LibraryItem && $this->ownerRecord->hasPermission(auth()->user(), 'share')), DeleteAction::make() - ->visible(fn () => $this->ownerRecord->hasPermission(auth()->user(), 'share')), + ->visible(fn () => $this->ownerRecord instanceof LibraryItem && $this->ownerRecord->hasPermission(auth()->user(), 'share')), ]) - ->bulkActions([ + ->toolbarActions([ BulkActionGroup::make([ DeleteBulkAction::make() - ->visible(fn () => $this->ownerRecord->hasPermission(auth()->user(), 'share')), + ->visible(fn () => $this->ownerRecord instanceof LibraryItem && $this->ownerRecord->hasPermission(auth()->user(), 'share')), ]), ]) ->emptyStateHeading('No permissions assigned') @@ -194,7 +195,7 @@ public function table(Table $table): Table ->emptyStateActions([ CreateAction::make() ->label('Add Permission') - ->visible(fn () => $this->ownerRecord->hasPermission(auth()->user(), 'share')), + ->visible(fn () => $this->ownerRecord instanceof LibraryItem && $this->ownerRecord->hasPermission(auth()->user(), 'share')), ]); } } diff --git a/src/Services/PermissionService.php b/src/Services/PermissionService.php index 1417f33..8ec6a2f 100644 --- a/src/Services/PermissionService.php +++ b/src/Services/PermissionService.php @@ -80,6 +80,8 @@ public function removePermission($user, LibraryItem $item, string $permission): /** * Bulk assign permissions to multiple users for multiple items. + * + * @param \Illuminate\Database\Eloquent\Collection|array $items */ public function bulkAssignPermissions($items, array $data): void { @@ -88,17 +90,24 @@ public function bulkAssignPermissions($items, array $data): void $generalAccess = $data['general_access'] ?? 'private'; foreach ($items as $item) { + if (! $item instanceof LibraryItem) { + continue; + } + // Update the general access level for the item $item->update(['general_access' => $generalAccess]); // Assign permissions to users foreach ($userIds as $userId) { $userModel = $this->getUserModel(); - $this->assignPermission( - $userModel::find($userId), - $item, - $permission - ); + $user = $userModel::find($userId); + if ($user) { + $this->assignPermission( + $user, + $item, + $permission + ); + } } } } @@ -111,17 +120,24 @@ public function cascadePermissionsToChildren(LibraryItem $folder, array $userIds $children = $folder->children; foreach ($children as $child) { + if (! $child instanceof LibraryItem) { + continue; + } + foreach ($userIds as $userId) { $userModel = $this->getUserModel(); - $this->assignPermission( - $userModel::find($userId), - $child, - $permission - ); + $user = $userModel::find($userId); + if ($user) { + $this->assignPermission( + $user, + $child, + $permission + ); + } } // Recursively cascade to grandchildren - if ($child->type === 'folder') { + if (isset($child->type) && $child->type === 'folder') { $this->cascadePermissionsToChildren($child, $userIds, $permission); } } diff --git a/src/Tables/Actions/BulkManagePermissionsAction.php b/src/Tables/Actions/BulkManagePermissionsAction.php index 4719773..0c33c68 100644 --- a/src/Tables/Actions/BulkManagePermissionsAction.php +++ b/src/Tables/Actions/BulkManagePermissionsAction.php @@ -30,7 +30,7 @@ protected function setUp(): void ->icon('heroicon-o-shield-check') ->color('warning') ->visible(fn (): bool => auth()->user() && FilamentLibraryPlugin::isLibraryAdmin(auth()->user())) - ->form([ + ->schema([ Select::make('general_access') ->label('General Access') ->options([ @@ -76,9 +76,10 @@ protected function setUp(): void public function success(): void { - $this->successNotification( - title: 'Permissions Updated', - body: 'Permissions have been successfully updated for the selected items.', - ); + \Filament\Notifications\Notification::make() + ->title('Permissions Updated') + ->body('Permissions have been successfully updated for the selected items.') + ->success() + ->send(); } }