Skip to content

Commit 781149c

Browse files
feat: opt-in shared library UX and filter-based permission assignment
Keep classic Shared with Me and single-user assignment as defaults. Host apps can enable nested shared items in Library, hide Shared with Me, bulk Manage Permissions, and community/role/signup user filters. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 2024c53 commit 781149c

8 files changed

Lines changed: 493 additions & 103 deletions

File tree

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,19 @@
22

33
All notable changes to `:package_name` will be documented in this file.
44

5+
## v1.3.5 - 2026-07-10
6+
7+
### What's Changed
8+
9+
* Opt-in sharing/permission UX for host apps (defaults preserve classic behavior):
10+
* `sharing.shared_with_me` (default `true`)
11+
* `sharing.show_nested_shared_in_library` (default `false`)
12+
* `permissions.bulk_manage_action` (default `false`)
13+
* `permissions.filter_based_assignment` (default `false`)
14+
* optional `user_filters` for community / role / signup date
15+
16+
**Full Changelog**: https://github.com/TappNetwork/Filament-Library/compare/v1.3.4...v1.3.5
17+
518
## v1.3.4 - 2026-07-09
619

720
### What's Changed

config/filament-library.php

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,79 @@
1818
*/
1919
'user_model' => env('FILAMENT_LIBRARY_USER_MODEL', 'App\\Models\\User'),
2020

21+
/*
22+
|--------------------------------------------------------------------------
23+
| Sharing behavior
24+
|--------------------------------------------------------------------------
25+
|
26+
| Defaults preserve the classic package UX. Host apps can opt into the
27+
| newer "shared items live in the main library" workflow.
28+
|
29+
*/
30+
'sharing' => [
31+
/*
32+
| Show the Shared with Me navigation item and page.
33+
*/
34+
'shared_with_me' => env('FILAMENT_LIBRARY_SHARED_WITH_ME', true),
35+
36+
/*
37+
| When true, nested (non-root) items with explicit per-user permissions
38+
| also appear in the main Library view. Root-level shared items already
39+
| appear there regardless of this setting.
40+
*/
41+
'show_nested_shared_in_library' => env('FILAMENT_LIBRARY_SHOW_NESTED_SHARED', false),
42+
],
43+
44+
/*
45+
|--------------------------------------------------------------------------
46+
| Permission assignment UI
47+
|--------------------------------------------------------------------------
48+
|
49+
| Defaults keep the classic single-user permission form and hide the
50+
| library-table bulk Manage Permissions action.
51+
|
52+
*/
53+
'permissions' => [
54+
/*
55+
| Show Manage Permissions as a table bulk action.
56+
*/
57+
'bulk_manage_action' => env('FILAMENT_LIBRARY_BULK_MANAGE_PERMISSIONS', false),
58+
59+
/*
60+
| When true, the permissions relation manager uses filter-based
61+
| multi-user assignment. When false, it uses the classic single-user form.
62+
*/
63+
'filter_based_assignment' => env('FILAMENT_LIBRARY_FILTER_BASED_ASSIGNMENT', false),
64+
],
65+
66+
/*
67+
|--------------------------------------------------------------------------
68+
| User assignment filters
69+
|--------------------------------------------------------------------------
70+
|
71+
| Optional filters shown when filter-based assignment (or the bulk manage
72+
| action) is enabled. Host apps can enable community and role filters by
73+
| pointing these options at their own models.
74+
|
75+
*/
76+
'user_filters' => [
77+
'community' => [
78+
'enabled' => false,
79+
'model' => null,
80+
'title_attribute' => 'name',
81+
'user_foreign_key' => 'community_id',
82+
],
83+
'role' => [
84+
'enabled' => false,
85+
'model' => null,
86+
'title_attribute' => 'name',
87+
],
88+
'signup_date' => [
89+
'enabled' => false,
90+
'column' => 'created_at',
91+
],
92+
],
93+
2194
/*
2295
|--------------------------------------------------------------------------
2396
| Model class overrides

src/FilamentLibraryPlugin.php

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -164,12 +164,7 @@ public function register(Panel $panel): void
164164
->group('Resource Library')
165165
->sort(3)
166166
->isActiveWhen(fn () => request()->routeIs("filament.{$panelId}.resources.library.my-documents")),
167-
NavigationItem::make('Shared with Me')
168-
->url(fn () => $libraryItemResourceClass::getUrl('shared-with-me'))
169-
->icon('heroicon-o-share')
170-
->group('Resource Library')
171-
->sort(4)
172-
->isActiveWhen(fn () => request()->routeIs("filament.{$panelId}.resources.library.shared-with-me")),
167+
...static::sharedWithMeNavigationItem($panelId, $libraryItemResourceClass),
173168
NavigationItem::make('Created by Me')
174169
->url(fn () => $libraryItemResourceClass::getUrl('created-by-me'))
175170
->icon('heroicon-o-user')
@@ -208,6 +203,26 @@ public function boot(Panel $panel): void
208203
});
209204
}
210205

206+
/**
207+
* @param class-string<LibraryItemResource> $libraryItemResourceClass
208+
* @return array<int, NavigationItem>
209+
*/
210+
protected static function sharedWithMeNavigationItem(string $panelId, string $libraryItemResourceClass): array
211+
{
212+
if (! config('filament-library.sharing.shared_with_me', true)) {
213+
return [];
214+
}
215+
216+
return [
217+
NavigationItem::make('Shared with Me')
218+
->url(fn () => $libraryItemResourceClass::getUrl('shared-with-me'))
219+
->icon('heroicon-o-share')
220+
->group('Resource Library')
221+
->sort(4)
222+
->isActiveWhen(fn () => request()->routeIs("filament.{$panelId}.resources.library.shared-with-me")),
223+
];
224+
}
225+
211226
public static function make(): static
212227
{
213228
return app(static::class);

src/Resources/LibraryItemResource.php

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
use Tapp\FilamentLibrary\FilamentLibraryPlugin;
2424
use Tapp\FilamentLibrary\Models\LibraryItem;
2525
use Tapp\FilamentLibrary\Resources\RelationManagers\LibraryItemPermissionsRelationManager;
26+
use Tapp\FilamentLibrary\Tables\Actions\BulkManagePermissionsAction;
2627

2728
class LibraryItemResource extends Resource
2829
{
@@ -165,7 +166,6 @@ public static function table(Table $table): Table
165166
// - PublicLibrary (public files)
166167
// - MyLibrary (personal documents)
167168
// - CreatedByMe (user's created items)
168-
// - SharedWithMe (shared items)
169169
// - SearchAll (search results)
170170
// The list pages only override getTableQuery() for filtering, not the columns
171171
return $table
@@ -338,6 +338,7 @@ public static function table(Table $table): Table
338338
])
339339
->toolbarActions([
340340
BulkActionGroup::make([
341+
...static::bulkManagePermissionsActions(),
341342
DeleteBulkAction::make()
342343
->visible(fn (): bool => auth()->user() && auth()->user()->can('deleteAny', static::getModel()))
343344
->successRedirectUrl(function () {
@@ -382,7 +383,7 @@ public static function getPages(): array
382383
return [
383384
'index' => Pages\ListLibraryItems::route('/'),
384385
'my-documents' => Pages\MyLibrary::route('/my-documents'),
385-
'shared-with-me' => Pages\SharedWithMe::route('/shared-with-me'),
386+
...static::sharedWithMePage(),
386387
'created-by-me' => Pages\CreatedByMe::route('/created-by-me'),
387388
'favorites' => Pages\Favorites::route('/favorites'),
388389
'public' => Pages\PublicLibrary::route('/public'),
@@ -398,6 +399,34 @@ public static function getPages(): array
398399
];
399400
}
400401

402+
/**
403+
* @return array<string, \Filament\Resources\Pages\PageRegistration>
404+
*/
405+
protected static function sharedWithMePage(): array
406+
{
407+
if (! config('filament-library.sharing.shared_with_me', true)) {
408+
return [];
409+
}
410+
411+
return [
412+
'shared-with-me' => Pages\SharedWithMe::route('/shared-with-me'),
413+
];
414+
}
415+
416+
/**
417+
* @return array<int, BulkManagePermissionsAction>
418+
*/
419+
protected static function bulkManagePermissionsActions(): array
420+
{
421+
if (! config('filament-library.permissions.bulk_manage_action', false)) {
422+
return [];
423+
}
424+
425+
return [
426+
BulkManagePermissionsAction::make(),
427+
];
428+
}
429+
401430
public static function getEditUrl($record): string
402431
{
403432
return match ($record->type) {

src/Resources/Pages/ListLibraryItems.php

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -227,30 +227,37 @@ protected function getTableQuery(): Builder
227227
if ($this->parentId) {
228228
$query->where('parent_id', $this->parentId);
229229
} else {
230-
// Show items at root level based on user permissions
231230
$user = auth()->user();
232-
$query->whereNull('parent_id')
233-
->where(function ($q) use ($user) {
234-
$q->where('general_access', 'anyone_can_view');
235-
236-
// Admins can see all items (including private/inherit)
237-
if ($user && FilamentLibraryPlugin::isLibraryAdmin($user)) {
238-
$q->orWhere(function ($adminQuery) {
239-
$adminQuery->whereIn('general_access', ['private', 'inherit']);
231+
$showNestedShared = (bool) config('filament-library.sharing.show_nested_shared_in_library', false);
232+
233+
$query->where(function (Builder $visibility) use ($user, $showNestedShared): void {
234+
$visibility->where(function (Builder $root) use ($user): void {
235+
$root->whereNull('parent_id')
236+
->where(function (Builder $access) use ($user): void {
237+
$access->where('general_access', 'anyone_can_view');
238+
239+
if ($user && FilamentLibraryPlugin::isLibraryAdmin($user)) {
240+
$access->orWhereIn('general_access', ['private', 'inherit']);
241+
}
242+
243+
if ($user) {
244+
$access->orWhere('created_by', $user->id)
245+
->orWhereHas('permissions', function (Builder $permissions) use ($user): void {
246+
$permissions->where('user_id', $user->id);
247+
});
248+
}
240249
});
241-
}
242-
243-
// Creators can see their own items (even if private)
244-
if ($user) {
245-
$q->orWhere('created_by', $user->id);
246-
247-
// Users can see items explicitly shared with them
248-
$q->orWhereHas('permissions', function ($pq) use ($user) {
249-
$pq->where('user_id', $user->id);
250-
});
251-
}
252-
})
253-
->where('name', 'not like', "%'s Personal Folder");
250+
});
251+
252+
if ($user && $showNestedShared) {
253+
$visibility->orWhere(function (Builder $sharedNested) use ($user): void {
254+
$sharedNested->whereNotNull('parent_id')
255+
->whereHas('permissions', function (Builder $permissions) use ($user): void {
256+
$permissions->where('user_id', $user->id);
257+
});
258+
});
259+
}
260+
})->where('name', 'not like', "%'s Personal Folder");
254261
}
255262

256263
return $query;

0 commit comments

Comments
 (0)