From b5f6cc345bff6d84b29d028d53708b47268f67b1 Mon Sep 17 00:00:00 2001 From: notCharles Date: Sun, 20 Jul 2025 13:00:01 -0400 Subject: [PATCH] Relocate some actions to toolbarActions --- .../Server/Resources/BackupResource.php | 48 ++- .../BackupResource/Pages/ListBackups.php | 45 -- .../FileResource/Pages/ListFiles.php | 403 +++++++++--------- .../Server/Resources/ScheduleResource.php | 15 + .../ScheduleResource/Pages/ListSchedules.php | 8 +- .../Server/Resources/UserResource.php | 104 ++++- .../UserResource/Pages/ListUsers.php | 142 +----- 7 files changed, 371 insertions(+), 394 deletions(-) diff --git a/app/Filament/Server/Resources/BackupResource.php b/app/Filament/Server/Resources/BackupResource.php index ad3f70338..266fd13c9 100644 --- a/app/Filament/Server/Resources/BackupResource.php +++ b/app/Filament/Server/Resources/BackupResource.php @@ -15,6 +15,7 @@ use App\Services\Backups\DownloadLinkService; use App\Filament\Components\Tables\Columns\BytesColumn; use App\Filament\Components\Tables\Columns\DateTimeColumn; use App\Services\Backups\DeleteBackupService; +use App\Services\Backups\InitiateBackupService; use App\Traits\Filament\BlockAccessInConflict; use App\Traits\Filament\CanCustomizePages; use App\Traits\Filament\CanCustomizeRelations; @@ -23,6 +24,7 @@ use App\Traits\Filament\CanModifyTable; use App\Traits\Filament\HasLimitBadge; use Filament\Actions\Action; use Filament\Actions\ActionGroup; +use Filament\Actions\CreateAction; use Filament\Actions\DeleteAction; use Filament\Facades\Filament; use Filament\Forms\Components\Checkbox; @@ -34,12 +36,14 @@ use Filament\Notifications\Notification; use Filament\Resources\Pages\PageRegistration; use Filament\Resources\Resource; use Filament\Schemas\Schema; +use Filament\Support\Enums\IconSize; use Filament\Tables\Columns\IconColumn; use Filament\Tables\Columns\TextColumn; use Filament\Tables\Table; use Illuminate\Database\Eloquent\Model; use Illuminate\Http\Client\ConnectionException; use Illuminate\Http\Request; +use Symfony\Component\HttpKernel\Exception\HttpException; use Throwable; class BackupResource extends Resource @@ -122,18 +126,21 @@ class BackupResource extends Resource ->recordActions([ ActionGroup::make([ Action::make('lock') + ->iconSize(IconSize::Large) ->icon(fn (Backup $backup) => !$backup->is_locked ? 'tabler-lock' : 'tabler-lock-open') ->authorize(fn () => auth()->user()->can(Permission::ACTION_BACKUP_DELETE, $server)) ->label(fn (Backup $backup) => !$backup->is_locked ? 'Lock' : 'Unlock') ->action(fn (BackupController $backupController, Backup $backup, Request $request) => $backupController->toggleLock($request, $server, $backup)) ->visible(fn (Backup $backup) => $backup->status === BackupStatus::Successful), Action::make('download') + ->iconSize(IconSize::Large) ->color('primary') ->icon('tabler-download') ->authorize(fn () => auth()->user()->can(Permission::ACTION_BACKUP_DOWNLOAD, $server)) ->url(fn (DownloadLinkService $downloadLinkService, Backup $backup, Request $request) => $downloadLinkService->handle($backup, $request->user()), true) ->visible(fn (Backup $backup) => $backup->status === BackupStatus::Successful), Action::make('restore') + ->iconSize(IconSize::Large) ->color('success') ->icon('tabler-folder-up') ->authorize(fn () => auth()->user()->can(Permission::ACTION_BACKUP_RESTORE, $server)) @@ -185,6 +192,7 @@ class BackupResource extends Resource }) ->visible(fn (Backup $backup) => $backup->status === BackupStatus::Successful), DeleteAction::make('delete') + ->iconSize(IconSize::Large) ->disabled(fn (Backup $backup) => $backup->is_locked) ->modalDescription(fn (Backup $backup) => 'Do you wish to delete ' . $backup->name . '?') ->modalSubmitActionLabel('Delete Backup') @@ -207,7 +215,45 @@ class BackupResource extends Resource ->log(); }) ->visible(fn (Backup $backup) => $backup->status !== BackupStatus::InProgress), - ]), + ])->iconSize(IconSize::ExtraLarge), + ]) + ->toolbarActions([ + CreateAction::make() + ->authorize(fn () => auth()->user()->can(Permission::ACTION_BACKUP_CREATE, $server)) + ->icon('tabler-file-zip') + ->tooltip(fn () => $server->backups()->count() >= $server->backup_limit ? 'Backup Limit Reached' : 'Create Backup') + ->disabled(fn () => $server->backups()->count() >= $server->backup_limit) + ->color(fn () => $server->backups()->count() >= $server->backup_limit ? 'danger' : 'primary') + ->createAnother(false) + ->hiddenLabel()->iconButton()->iconSize(IconSize::ExtraLarge) + ->action(function (InitiateBackupService $initiateBackupService, $data) use ($server) { + $action = $initiateBackupService->setIgnoredFiles(explode(PHP_EOL, $data['ignored'] ?? '')); + + if (auth()->user()->can(Permission::ACTION_BACKUP_DELETE, $server)) { + $action->setIsLocked((bool) $data['is_locked']); + } + + try { + $backup = $action->handle($server, $data['name']); + + Activity::event('server:backup.start') + ->subject($backup) + ->property(['name' => $backup->name, 'locked' => (bool) $data['is_locked']]) + ->log(); + + return Notification::make() + ->title('Backup Created') + ->body($backup->name . ' created.') + ->success() + ->send(); + } catch (HttpException $e) { + return Notification::make() + ->danger() + ->title('Backup Failed') + ->body($e->getMessage() . ' Try again' . ($e->getHeaders()['Retry-After'] ? ' in ' . $e->getHeaders()['Retry-After'] . ' seconds.' : '')) + ->send(); + } + }), ]); } diff --git a/app/Filament/Server/Resources/BackupResource/Pages/ListBackups.php b/app/Filament/Server/Resources/BackupResource/Pages/ListBackups.php index 59ff3b594..742da1cf4 100644 --- a/app/Filament/Server/Resources/BackupResource/Pages/ListBackups.php +++ b/app/Filament/Server/Resources/BackupResource/Pages/ListBackups.php @@ -2,20 +2,12 @@ namespace App\Filament\Server\Resources\BackupResource\Pages; -use App\Facades\Activity; use App\Filament\Server\Resources\BackupResource; -use App\Models\Permission; -use App\Models\Server; -use App\Services\Backups\InitiateBackupService; use App\Traits\Filament\CanCustomizeHeaderActions; use App\Traits\Filament\CanCustomizeHeaderWidgets; use Filament\Actions\Action; use Filament\Actions\ActionGroup; -use Filament\Actions\CreateAction; -use Filament\Facades\Filament; -use Filament\Notifications\Notification; use Filament\Resources\Pages\ListRecords; -use Symfony\Component\HttpKernel\Exception\HttpException; class ListBackups extends ListRecords { @@ -27,44 +19,7 @@ class ListBackups extends ListRecords /** @return array */ protected function getDefaultHeaderActions(): array { - /** @var Server $server */ - $server = Filament::getTenant(); - return [ - CreateAction::make() - ->authorize(fn () => auth()->user()->can(Permission::ACTION_BACKUP_CREATE, $server)) - ->label(fn () => $server->backups()->count() >= $server->backup_limit ? 'Backup limit reached' : 'Create Backup') - ->disabled(fn () => $server->backups()->count() >= $server->backup_limit) - ->color(fn () => $server->backups()->count() >= $server->backup_limit ? 'danger' : 'primary') - ->createAnother(false) - ->action(function (InitiateBackupService $initiateBackupService, $data) use ($server) { - $action = $initiateBackupService->setIgnoredFiles(explode(PHP_EOL, $data['ignored'] ?? '')); - - if (auth()->user()->can(Permission::ACTION_BACKUP_DELETE, $server)) { - $action->setIsLocked((bool) $data['is_locked']); - } - - try { - $backup = $action->handle($server, $data['name']); - - Activity::event('server:backup.start') - ->subject($backup) - ->property(['name' => $backup->name, 'locked' => (bool) $data['is_locked']]) - ->log(); - - return Notification::make() - ->title('Backup Created') - ->body($backup->name . ' created.') - ->success() - ->send(); - } catch (HttpException $e) { - return Notification::make() - ->danger() - ->title('Backup Failed') - ->body($e->getMessage() . ' Try again' . ($e->getHeaders()['Retry-After'] ? ' in ' . $e->getHeaders()['Retry-After'] . ' seconds.' : '')) - ->send(); - } - }), ]; } diff --git a/app/Filament/Server/Resources/FileResource/Pages/ListFiles.php b/app/Filament/Server/Resources/FileResource/Pages/ListFiles.php index 836cfaa37..5e7e5df64 100644 --- a/app/Filament/Server/Resources/FileResource/Pages/ListFiles.php +++ b/app/Filament/Server/Resources/FileResource/Pages/ListFiles.php @@ -18,6 +18,7 @@ use App\Traits\Filament\CanCustomizeHeaderWidgets; use Filament\Actions\Action; use Filament\Actions\ActionGroup; use Filament\Actions\BulkAction; +use Filament\Actions\BulkActionGroup; use Filament\Actions\DeleteAction; use Filament\Actions\DeleteBulkAction; use Filament\Actions\EditAction; @@ -34,6 +35,7 @@ use Filament\Resources\Pages\PageRegistration; use Filament\Schemas\Components\Tabs; use Filament\Schemas\Components\Tabs\Tab; use Filament\Schemas\Components\Utilities\Get; +use Filament\Support\Enums\IconSize; use Filament\Tables\Columns\TextColumn; use Filament\Tables\Table; use Illuminate\Database\Eloquent\Collection; @@ -116,7 +118,7 @@ class ListFiles extends ListRecords Action::make('view') ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_READ, $server)) ->label('Open') - ->icon('tabler-eye') + ->icon('tabler-eye')->iconSize(IconSize::Large) ->visible(fn (File $file) => $file->is_directory) ->url(fn (File $file) => self::getUrl(['path' => join_paths($this->path, $file->name)])), EditAction::make('edit') @@ -128,7 +130,7 @@ class ListFiles extends ListRecords Action::make('rename') ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_UPDATE, $server)) ->label('Rename') - ->icon('tabler-forms') + ->icon('tabler-forms')->iconSize(IconSize::Large) ->schema([ TextInput::make('name') ->label('File name') @@ -156,7 +158,7 @@ class ListFiles extends ListRecords Action::make('copy') ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_CREATE, $server)) ->label('Copy') - ->icon('tabler-copy') + ->icon('tabler-copy')->iconSize(IconSize::Large) ->visible(fn (File $file) => $file->is_file) ->action(function (File $file) { $this->getDaemonFileRepository()->copyFile(join_paths($this->path, $file->name)); @@ -175,13 +177,13 @@ class ListFiles extends ListRecords Action::make('download') ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_READ_CONTENT, $server)) ->label('Download') - ->icon('tabler-download') + ->icon('tabler-download')->iconSize(IconSize::Large) ->visible(fn (File $file) => $file->is_file) ->url(fn (File $file) => DownloadFiles::getUrl(['path' => join_paths($this->path, $file->name)]), true), Action::make('move') ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_UPDATE, $server)) ->label('Move') - ->icon('tabler-replace') + ->icon('tabler-replace')->iconSize(IconSize::Large) ->schema([ TextInput::make('location') ->label('New location') @@ -216,7 +218,7 @@ class ListFiles extends ListRecords Action::make('permissions') ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_UPDATE, $server)) ->label('Permissions') - ->icon('tabler-license') + ->icon('tabler-license')->iconSize(IconSize::Large) ->schema([ CheckboxList::make('owner') ->bulkToggleable() @@ -272,7 +274,7 @@ class ListFiles extends ListRecords Action::make('archive') ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_ARCHIVE, $server)) ->label('Archive') - ->icon('tabler-archive') + ->icon('tabler-archive')->iconSize(IconSize::Large) ->schema([ TextInput::make('name') ->label('Archive name') @@ -299,7 +301,7 @@ class ListFiles extends ListRecords Action::make('unarchive') ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_ARCHIVE, $server)) ->label('Unarchive') - ->icon('tabler-archive') + ->icon('tabler-archive')->iconSize(IconSize::Large) ->visible(fn (File $file) => $file->isArchive()) ->action(function (File $file) { $this->getDaemonFileRepository()->decompressFile($this->path, $file->name); @@ -316,11 +318,11 @@ class ListFiles extends ListRecords return redirect(ListFiles::getUrl(['path' => $this->path])); }), - ]), + ])->iconSize(IconSize::Large), DeleteAction::make() ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_DELETE, $server)) ->label('') - ->icon('tabler-trash') + ->icon('tabler-trash')->iconSize(IconSize::Large) ->requiresConfirmation() ->modalHeading(fn (File $file) => trans('filament-actions::delete.single.modal.heading', ['label' => $file->name . ' ' . ($file->is_directory ? 'folder' : 'file')])) ->action(function (File $file) { @@ -333,77 +335,208 @@ class ListFiles extends ListRecords ->log(); }), ]) - ->groupedBulkActions([ - BulkAction::make('move') - ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_UPDATE, $server)) - ->schema([ - TextInput::make('location') - ->label('Directory') - ->hint('Enter the new directory, relative to the current directory.') - ->required() - ->live(), - TextEntry::make('new_location') - ->state(fn (Get $get) => resolve_path('./' . join_paths($this->path, $get('location') ?? ''))), - ]) - ->action(function (Collection $files, $data) { - $location = rtrim($data['location'], '/'); + ->toolbarActions([ + BulkActionGroup::make([ + BulkAction::make('move') + ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_UPDATE, $server)) + ->schema([ + TextInput::make('location') + ->label('Directory') + ->hint('Enter the new directory, relative to the current directory.') + ->required() + ->live(), + TextEntry::make('new_location') + ->state(fn (Get $get) => resolve_path('./' . join_paths($this->path, $get('location') ?? ''))), + ]) + ->action(function (Collection $files, $data) { + $location = rtrim($data['location'], '/'); - $files = $files->map(fn ($file) => ['to' => join_paths($location, $file['name']), 'from' => $file['name']])->toArray(); - $this->getDaemonFileRepository()->renameFiles($this->path, $files); + $files = $files->map(fn ($file) => ['to' => join_paths($location, $file['name']), 'from' => $file['name']])->toArray(); + $this->getDaemonFileRepository()->renameFiles($this->path, $files); - Activity::event('server:file.rename') - ->property('directory', $this->path) - ->property('files', $files) - ->log(); + Activity::event('server:file.rename') + ->property('directory', $this->path) + ->property('files', $files) + ->log(); - Notification::make() - ->title(count($files) . ' Files were moved to ' . resolve_path(join_paths($this->path, $location))) - ->success() - ->send(); - }), - BulkAction::make('archive') - ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_ARCHIVE, $server)) + Notification::make() + ->title(count($files) . ' Files were moved to ' . resolve_path(join_paths($this->path, $location))) + ->success() + ->send(); + }), + BulkAction::make('archive') + ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_ARCHIVE, $server)) + ->schema([ + TextInput::make('name') + ->label('Archive name') + ->placeholder(fn () => 'archive-' . str(Carbon::now()->toRfc3339String())->replace(':', '')->before('+0000') . 'Z') + ->suffix('.tar.gz'), + ]) + ->action(function ($data, Collection $files) { + $files = $files->map(fn ($file) => $file['name'])->toArray(); + + $archive = $this->getDaemonFileRepository()->compressFiles($this->path, $files, $data['name']); + + Activity::event('server:file.compress') + ->property('name', $archive['name']) + ->property('directory', $this->path) + ->property('files', $files) + ->log(); + + Notification::make() + ->title('Archive created') + ->body($archive['name']) + ->success() + ->send(); + + return redirect(ListFiles::getUrl(['path' => $this->path])); + }), + DeleteBulkAction::make() + ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_DELETE, $server)) + ->action(function (Collection $files) { + $files = $files->map(fn ($file) => $file['name'])->toArray(); + $this->getDaemonFileRepository()->deleteFiles($this->path, $files); + + Activity::event('server:file.delete') + ->property('directory', $this->path) + ->property('files', $files) + ->log(); + + Notification::make() + ->title(count($files) . ' Files deleted.') + ->success() + ->send(); + }), + ]), + Action::make('new_file') + ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_CREATE, $server)) + ->tooltip('New File') + ->hiddenLabel()->icon('tabler-file-plus')->iconButton()->iconSize(IconSize::ExtraLarge) + ->color('primary') + ->keyBindings('') + ->modalSubmitActionLabel('Create') + ->action(function ($data) { + $path = join_paths($this->path, $data['name']); + try { + $this->getDaemonFileRepository()->putContent($path, $data['editor'] ?? ''); + + Activity::event('server:file.write') + ->property('file', join_paths($path, $data['name'])) + ->log(); + } catch (FileExistsException) { + AlertBanner::make() + ->title('' . $path . ' already exists!') + ->danger() + ->closable() + ->send(); + + $this->redirect(self::getUrl(['path' => dirname($path)])); + } + }) ->schema([ TextInput::make('name') - ->label('Archive name') - ->placeholder(fn () => 'archive-' . str(Carbon::now()->toRfc3339String())->replace(':', '')->before('+0000') . 'Z') - ->suffix('.tar.gz'), - ]) - ->action(function ($data, Collection $files) { - $files = $files->map(fn ($file) => $file['name'])->toArray(); + ->label('Name') + ->required(), + CodeEditor::make('editor') + ->hiddenLabel(), + ]), + Action::make('new_folder') + ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_CREATE, $server)) + ->hiddenLabel()->icon('tabler-folder-plus')->iconButton()->iconSize(IconSize::ExtraLarge) + ->tooltip('New Folder') + ->color('primary') + ->action(function ($data) { + try { + $this->getDaemonFileRepository()->createDirectory($data['name'], $this->path); - $archive = $this->getDaemonFileRepository()->compressFiles($this->path, $files, $data['name']); + Activity::event('server:file.create-directory') + ->property(['directory' => $this->path, 'name' => $data['name']]) + ->log(); + } catch (FileExistsException) { + $path = join_paths($this->path, $data['name']); + AlertBanner::make() + ->title('' . $path . ' already exists!') + ->danger() + ->closable() + ->send(); - Activity::event('server:file.compress') - ->property('name', $archive['name']) - ->property('directory', $this->path) - ->property('files', $files) - ->log(); + $this->redirect(self::getUrl(['path' => dirname($path)])); + } + }) + ->schema([ + TextInput::make('name') + ->label('Folder Name') + ->required(), + ]), + Action::make('upload') + ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_CREATE, $server)) + ->hiddenLabel()->icon('tabler-upload')->iconButton()->iconSize(IconSize::ExtraLarge) + ->tooltip('Upload') + ->color('success') + ->action(function ($data) { + if (count($data['files']) > 0 && !isset($data['url'])) { + /** @var UploadedFile $file */ + foreach ($data['files'] as $file) { + $this->getDaemonFileRepository()->putContent(join_paths($this->path, $file->getClientOriginalName()), $file->getContent()); - Notification::make() - ->title('Archive created') - ->body($archive['name']) - ->success() - ->send(); + Activity::event('server:file.uploaded') + ->property('directory', $this->path) + ->property('file', $file->getClientOriginalName()) + ->log(); + } + } elseif ($data['url'] !== null) { + $this->getDaemonFileRepository()->pull($data['url'], $this->path); + + Activity::event('server:file.pull') + ->property('url', $data['url']) + ->property('directory', $this->path) + ->log(); + } return redirect(ListFiles::getUrl(['path' => $this->path])); - }), - DeleteBulkAction::make() - ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_DELETE, $server)) - ->action(function (Collection $files) { - $files = $files->map(fn ($file) => $file['name'])->toArray(); - $this->getDaemonFileRepository()->deleteFiles($this->path, $files); - - Activity::event('server:file.delete') - ->property('directory', $this->path) - ->property('files', $files) - ->log(); - - Notification::make() - ->title(count($files) . ' Files deleted.') - ->success() - ->send(); - }), + }) + ->schema([ + Tabs::make() + ->contained(false) + ->schema([ + Tab::make('Upload Files') + ->live() + ->schema([ + FileUpload::make('files') + ->storeFiles(false) + ->previewable(false) + ->preserveFilenames() + ->maxSize((int) round($server->node->upload_size * (config('panel.use_binary_prefix') ? 1.048576 * 1024 : 1000))) + ->multiple(), + ]), + Tab::make('Upload From URL') + ->live() + ->disabled(fn (Get $get) => count($get('files')) > 0) + ->schema([ + TextInput::make('url') + ->label('URL') + ->url(), + ]), + ]), + ]), + Action::make('search') + ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_READ, $server)) + ->hiddenLabel()->iconButton()->iconSize(IconSize::ExtraLarge) + ->tooltip('Global Search') + ->color('primary') + ->icon('tabler-world-search') + ->modalSubmitActionLabel('Search') + ->schema([ + TextInput::make('searchTerm') + ->placeholder('Enter a search term, e.g. *.txt') + ->required() + ->regex('/^[^*]*\*?[^*]*$/') + ->minValue(3), + ]) + ->action(fn ($data) => redirect(SearchFiles::getUrl([ + 'searchTerm' => $data['searchTerm'], + 'path' => $this->path, + ]))), ]); } @@ -412,133 +545,7 @@ class ListFiles extends ListRecords */ protected function getDefaultHeaderActions(): array { - /** @var Server $server */ - $server = Filament::getTenant(); - - return [ - Action::make('new_file') - ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_CREATE, $server)) - ->label('New File') - ->color('gray') - ->keyBindings('') - ->modalSubmitActionLabel('Create') - ->action(function ($data) { - $path = join_paths($this->path, $data['name']); - try { - $this->getDaemonFileRepository()->putContent($path, $data['editor'] ?? ''); - - Activity::event('server:file.write') - ->property('file', join_paths($path, $data['name'])) - ->log(); - } catch (FileExistsException) { - AlertBanner::make() - ->title('' . $path . ' already exists!') - ->danger() - ->closable() - ->send(); - - $this->redirect(self::getUrl(['path' => dirname($path)])); - } - }) - ->schema([ - TextInput::make('name') - ->label('Name') - ->required(), - CodeEditor::make('editor') - ->hiddenLabel(), - ]), - Action::make('new_folder') - ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_CREATE, $server)) - ->label('New Folder') - ->color('gray') - ->action(function ($data) { - try { - $this->getDaemonFileRepository()->createDirectory($data['name'], $this->path); - - Activity::event('server:file.create-directory') - ->property(['directory' => $this->path, 'name' => $data['name']]) - ->log(); - } catch (FileExistsException) { - $path = join_paths($this->path, $data['name']); - AlertBanner::make() - ->title('' . $path . ' already exists!') - ->danger() - ->closable() - ->send(); - - $this->redirect(self::getUrl(['path' => dirname($path)])); - } - }) - ->schema([ - TextInput::make('name') - ->label('Folder Name') - ->required(), - ]), - Action::make('upload') - ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_CREATE, $server)) - ->label('Upload') - ->action(function ($data) { - if (count($data['files']) > 0 && !isset($data['url'])) { - /** @var UploadedFile $file */ - foreach ($data['files'] as $file) { - $this->getDaemonFileRepository()->putContent(join_paths($this->path, $file->getClientOriginalName()), $file->getContent()); - - Activity::event('server:file.uploaded') - ->property('directory', $this->path) - ->property('file', $file->getClientOriginalName()) - ->log(); - } - } elseif ($data['url'] !== null) { - $this->getDaemonFileRepository()->pull($data['url'], $this->path); - - Activity::event('server:file.pull') - ->property('url', $data['url']) - ->property('directory', $this->path) - ->log(); - } - - return redirect(ListFiles::getUrl(['path' => $this->path])); - }) - ->schema([ - Tabs::make() - ->contained(false) - ->schema([ - Tab::make('Upload Files') - ->live() - ->schema([ - FileUpload::make('files') - ->storeFiles(false) - ->previewable(false) - ->preserveFilenames() - ->maxSize((int) round($server->node->upload_size * (config('panel.use_binary_prefix') ? 1.048576 * 1024 : 1000))) - ->multiple(), - ]), - Tab::make('Upload From URL') - ->live() - ->disabled(fn (Get $get) => count($get('files')) > 0) - ->schema([ - TextInput::make('url') - ->label('URL') - ->url(), - ]), - ]), - ]), - Action::make('search') - ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_READ, $server)) - ->label('Global Search') - ->modalSubmitActionLabel('Search') - ->schema([ - TextInput::make('searchTerm') - ->placeholder('Enter a search term, e.g. *.txt') - ->required() - ->regex('/^[^*]*\*?[^*]*$/') - ->minValue(3), - ]) - ->action(fn ($data) => redirect(SearchFiles::getUrl([ - 'searchTerm' => $data['searchTerm'], - 'path' => $this->path, - ]))), - ]; + return []; } /** diff --git a/app/Filament/Server/Resources/ScheduleResource.php b/app/Filament/Server/Resources/ScheduleResource.php index e7180a150..ad16c4d05 100644 --- a/app/Filament/Server/Resources/ScheduleResource.php +++ b/app/Filament/Server/Resources/ScheduleResource.php @@ -2,6 +2,7 @@ namespace App\Filament\Server\Resources; +use App\Filament\Components\Actions\ImportScheduleAction; use App\Filament\Server\Resources\ScheduleResource\Pages\ListSchedules; use App\Filament\Server\Resources\ScheduleResource\Pages\CreateSchedule; use App\Filament\Server\Resources\ScheduleResource\Pages\ViewSchedule; @@ -20,6 +21,7 @@ use App\Traits\Filament\CanModifyForm; use App\Traits\Filament\CanModifyTable; use Carbon\Carbon; use Exception; +use Filament\Actions\CreateAction; use Filament\Actions\DeleteAction; use Filament\Actions\EditAction; use Filament\Actions\ViewAction; @@ -38,6 +40,7 @@ use Filament\Resources\Pages\PageRegistration; use Filament\Resources\RelationManagers\RelationManager; use Filament\Resources\Resource; use Filament\Schemas\Schema; +use Filament\Support\Enums\IconSize; use Filament\Support\Exceptions\Halt; use Filament\Tables\Columns\IconColumn; use Filament\Tables\Columns\TextColumn; @@ -334,6 +337,18 @@ class ScheduleResource extends Resource ->property('name', $schedule->name) ->log(); }), + ]) + ->toolbarActions([ + CreateAction::make() + ->hiddenLabel()->iconButton()->iconSize(IconSize::ExtraLarge) + ->icon('tabler-calendar-plus') + ->color('primary') + ->tooltip('New Schedule'), + ImportScheduleAction::make() + ->hiddenLabel()->iconButton()->iconSize(IconSize::ExtraLarge) + ->icon('tabler-file-import') + ->color('success') + ->tooltip('Import Schedule'), ]); } diff --git a/app/Filament/Server/Resources/ScheduleResource/Pages/ListSchedules.php b/app/Filament/Server/Resources/ScheduleResource/Pages/ListSchedules.php index 512aeecc3..5107e3495 100644 --- a/app/Filament/Server/Resources/ScheduleResource/Pages/ListSchedules.php +++ b/app/Filament/Server/Resources/ScheduleResource/Pages/ListSchedules.php @@ -2,13 +2,11 @@ namespace App\Filament\Server\Resources\ScheduleResource\Pages; -use App\Filament\Components\Actions\ImportScheduleAction; use App\Filament\Server\Resources\ScheduleResource; use App\Traits\Filament\CanCustomizeHeaderActions; use App\Traits\Filament\CanCustomizeHeaderWidgets; use Filament\Actions\Action; use Filament\Actions\ActionGroup; -use Filament\Actions\CreateAction; use Filament\Resources\Pages\ListRecords; class ListSchedules extends ListRecords @@ -21,11 +19,7 @@ class ListSchedules extends ListRecords /** @return array */ protected function getDefaultHeaderActions(): array { - return [ - CreateAction::make() - ->label('New Schedule'), - ImportScheduleAction::make(), - ]; + return []; } public function getBreadcrumbs(): array diff --git a/app/Filament/Server/Resources/UserResource.php b/app/Filament/Server/Resources/UserResource.php index 510b4654a..6f5e775de 100644 --- a/app/Filament/Server/Resources/UserResource.php +++ b/app/Filament/Server/Resources/UserResource.php @@ -2,10 +2,12 @@ namespace App\Filament\Server\Resources; +use App\Facades\Activity; use App\Filament\Server\Resources\UserResource\Pages\ListUsers; use App\Models\Permission; use App\Models\Server; use App\Models\User; +use App\Services\Subusers\SubuserCreationService; use App\Services\Subusers\SubuserDeletionService; use App\Services\Subusers\SubuserUpdateService; use App\Traits\Filament\BlockAccessInConflict; @@ -13,6 +15,8 @@ use App\Traits\Filament\CanCustomizePages; use App\Traits\Filament\CanCustomizeRelations; use App\Traits\Filament\CanModifyTable; use App\Traits\Filament\HasLimitBadge; +use Exception; +use Filament\Actions\CreateAction; use Filament\Facades\Filament; use Filament\Actions\Action; use Filament\Forms\Components\CheckboxList; @@ -22,12 +26,14 @@ use Filament\Schemas\Components\Section; use Filament\Schemas\Components\Tabs; use Filament\Schemas\Components\Tabs\Tab; use Filament\Forms\Components\TextInput; +use Filament\Schemas\Components\Utilities\Get; use Filament\Schemas\Components\Utilities\Set; use Filament\Notifications\Notification; use Filament\Actions\DeleteAction; use Filament\Resources\Pages\PageRegistration; use Filament\Resources\Resource; use Filament\Actions\EditAction; +use Filament\Support\Enums\IconSize; use Filament\Tables\Columns\ImageColumn; use Filament\Tables\Columns\TextColumn; use Filament\Tables\Table; @@ -113,7 +119,7 @@ class UserResource extends Resource return $table ->paginated(false) - ->searchable(false) +// ->searchable(false) TODO toolbarActions do not render without the search bar :/ ->columns([ ImageColumn::make('picture') ->visibleFrom('lg') @@ -221,7 +227,101 @@ class UserResource extends Resource return $data; }), - ]); + ]) + ->toolbarActions([ + CreateAction::make('invite') + ->hiddenLabel()->iconButton()->iconSize(IconSize::ExtraLarge) + ->icon('tabler-user-plus') + ->tooltip('Invite User') + ->createAnother(false) + ->authorize(fn () => auth()->user()->can(Permission::ACTION_USER_CREATE, $server)) + ->schema([ + Grid::make() + ->columnSpanFull() + ->columns([ + 'default' => 1, + 'sm' => 1, + 'md' => 5, + 'lg' => 6, + ]) + ->schema([ + TextInput::make('email') + ->email() + ->inlineLabel() + ->columnSpan([ + 'default' => 1, + 'sm' => 1, + 'md' => 4, + 'lg' => 5, + ]) + ->required(), + Actions::make([ + Action::make('assignAll') + ->label('Assign All') + ->action(function (Set $set, Get $get) use ($permissionsArray) { + $permissions = $permissionsArray; + foreach ($permissions as $key => $value) { + $allValues = array_unique($value); + $set($key, $allValues); + } + }), + ]) + ->columnSpan([ + 'default' => 1, + 'sm' => 1, + 'md' => 1, + 'lg' => 1, + ]), + Tabs::make() + ->columnSpanFull() + ->schema($tabs), + ]), + ]) + ->modalHeading('Invite User') + ->modalIcon('tabler-user-plus') + ->modalSubmitActionLabel('Invite') + ->successNotificationTitle(null) + ->failureNotificationTitle(null) + ->action(function (Action $action, array $data, SubuserCreationService $service) use ($server) { + $email = strtolower($data['email']); + + $permissions = collect($data) + ->forget('email') + ->flatMap(fn ($permissions, $key) => collect($permissions)->map(fn ($permission) => "$key.$permission")) + ->push(Permission::ACTION_WEBSOCKET_CONNECT) + ->unique() + ->all(); + + try { + $subuser = $service->handle($server, $email, $permissions); + + Activity::event('server:subuser.create') + ->subject($subuser->user) + ->property([ + 'email' => $data['email'], + 'permissions' => $permissions, + ]); + + Notification::make() + ->title('User Invited!') + ->success() + ->send(); + } catch (Exception $exception) { + Notification::make() + ->title('Failed') + ->body($exception->getMessage()) + ->danger() + ->send(); + + $action->failure(); + + return; + } + + $action->success(); + + return redirect(self::getUrl(tenant: $server)); + }), ]); } /** @return array */ diff --git a/app/Filament/Server/Resources/UserResource/Pages/ListUsers.php b/app/Filament/Server/Resources/UserResource/Pages/ListUsers.php index 378443660..d8a7b8328 100644 --- a/app/Filament/Server/Resources/UserResource/Pages/ListUsers.php +++ b/app/Filament/Server/Resources/UserResource/Pages/ListUsers.php @@ -2,28 +2,11 @@ namespace App\Filament\Server\Resources\UserResource\Pages; -use App\Facades\Activity; use App\Filament\Server\Resources\UserResource; -use App\Models\Permission; -use App\Models\Server; -use App\Services\Subusers\SubuserCreationService; use App\Traits\Filament\CanCustomizeHeaderActions; use App\Traits\Filament\CanCustomizeHeaderWidgets; -use Exception; -use Filament\Actions\CreateAction; use Filament\Actions\Action; use Filament\Actions\ActionGroup; -use Filament\Facades\Filament; -use Filament\Forms\Components\CheckboxList; -use Filament\Schemas\Components\Actions; -use Filament\Schemas\Components\Grid; -use Filament\Schemas\Components\Section; -use Filament\Schemas\Components\Tabs; -use Filament\Schemas\Components\Tabs\Tab; -use Filament\Forms\Components\TextInput; -use Filament\Schemas\Components\Utilities\Get; -use Filament\Schemas\Components\Utilities\Set; -use Filament\Notifications\Notification; use Filament\Resources\Pages\ListRecords; class ListUsers extends ListRecords @@ -36,130 +19,7 @@ class ListUsers extends ListRecords /** @return array */ protected function getDefaultHeaderActions(): array { - /** @var Server $server */ - $server = Filament::getTenant(); - - $tabs = []; - $permissionsArray = []; - - foreach (Permission::permissionData() as $data) { - $options = []; - $descriptions = []; - - foreach ($data['permissions'] as $permission) { - $options[$permission] = str($permission)->headline(); - $descriptions[$permission] = trans('server/users.permissions.' . $data['name'] . '_' . str($permission)->replace('-', '_')); - $permissionsArray[$data['name']][] = $permission; - } - - $tabs[] = Tab::make(str($data['name'])->headline()) - ->schema([ - Section::make() - ->description(trans('server/users.permissions.' . $data['name'] . '_desc')) - ->icon($data['icon']) - ->schema([ - CheckboxList::make($data['name']) - ->label('') - ->bulkToggleable() - ->columns(2) - ->options($options) - ->descriptions($descriptions), - ]), - ]); - } - - return [ - CreateAction::make('invite') - ->label('Invite User') - ->createAnother(false) - ->authorize(fn () => auth()->user()->can(Permission::ACTION_USER_CREATE, $server)) - ->schema([ - Grid::make() - ->columnSpanFull() - ->columns([ - 'default' => 1, - 'sm' => 1, - 'md' => 5, - 'lg' => 6, - ]) - ->schema([ - TextInput::make('email') - ->email() - ->inlineLabel() - ->columnSpan([ - 'default' => 1, - 'sm' => 1, - 'md' => 4, - 'lg' => 5, - ]) - ->required(), - Actions::make([ - Action::make('assignAll') - ->label('Assign All') - ->action(function (Set $set, Get $get) use ($permissionsArray) { - $permissions = $permissionsArray; - foreach ($permissions as $key => $value) { - $allValues = array_unique($value); - $set($key, $allValues); - } - }), - ]) - ->columnSpan([ - 'default' => 1, - 'sm' => 1, - 'md' => 1, - 'lg' => 1, - ]), - Tabs::make() - ->columnSpanFull() - ->schema($tabs), - ]), - ]) - ->modalHeading('Invite User') - ->modalSubmitActionLabel('Invite') - ->successNotificationTitle(null) - ->failureNotificationTitle(null) - ->action(function (Action $action, array $data, SubuserCreationService $service) use ($server) { - $email = strtolower($data['email']); - - $permissions = collect($data) - ->forget('email') - ->flatMap(fn ($permissions, $key) => collect($permissions)->map(fn ($permission) => "$key.$permission")) - ->push(Permission::ACTION_WEBSOCKET_CONNECT) - ->unique() - ->all(); - - try { - $subuser = $service->handle($server, $email, $permissions); - - Activity::event('server:subuser.create') - ->subject($subuser->user) - ->property([ - 'email' => $data['email'], - 'permissions' => $permissions, - ]); - - Notification::make() - ->title('User Invited!') - ->success() - ->send(); - } catch (Exception $exception) { - Notification::make() - ->title('Failed') - ->body($exception->getMessage()) - ->danger() - ->send(); - - $action->failure(); - - return; - } - - $action->success(); - - return redirect(self::getUrl(tenant: $server)); - }), - ]; + return []; } public function getBreadcrumbs(): array