diff --git a/app/Filament/Server/Resources/AllocationResource.php b/app/Filament/Server/Resources/AllocationResource.php index a344c56c5..fef880499 100644 --- a/app/Filament/Server/Resources/AllocationResource.php +++ b/app/Filament/Server/Resources/AllocationResource.php @@ -94,7 +94,7 @@ class AllocationResource extends Resource ]) ->toolbarActions([ Action::make('addAllocation') - ->hiddenLabel()->iconButton()->iconSize(IconSize::Large) + ->hiddenLabel()->iconButton()->iconSize(IconSize::ExtraLarge) ->icon(fn () => $server->allocations()->count() >= $server->allocation_limit ? 'tabler-network-off' : 'tabler-network') ->authorize(fn () => auth()->user()->can(Permission::ACTION_ALLOCATION_CREATE, $server)) ->tooltip(fn () => $server->allocations()->count() >= $server->allocation_limit ? trans('server/network.limit') : trans('server/network.add')) diff --git a/app/Filament/Server/Resources/BackupResource.php b/app/Filament/Server/Resources/BackupResource.php index 187f6e20e..0c77a785e 100644 --- a/app/Filament/Server/Resources/BackupResource.php +++ b/app/Filament/Server/Resources/BackupResource.php @@ -255,7 +255,7 @@ class BackupResource extends Resource ->log(); }) ->visible(fn (Backup $backup) => $backup->status !== BackupStatus::InProgress), - ])->iconSize(IconSize::ExtraLarge), + ])->iconSize(IconSize::Large), ]) ->toolbarActions([ CreateAction::make() diff --git a/app/Filament/Server/Resources/DatabaseResource.php b/app/Filament/Server/Resources/DatabaseResource.php index 7904942c5..f16a97cff 100644 --- a/app/Filament/Server/Resources/DatabaseResource.php +++ b/app/Filament/Server/Resources/DatabaseResource.php @@ -141,7 +141,7 @@ class DatabaseResource extends Resource ]) ->toolbarActions([ CreateAction::make('new') - ->hiddenLabel()->iconButton()->iconSize(IconSize::Large) + ->hiddenLabel()->iconButton()->iconSize(IconSize::ExtraLarge) ->icon(fn () => $server->databases()->count() >= $server->database_limit ? 'tabler-database-x' : 'tabler-database-plus') ->tooltip(fn () => $server->databases()->count() >= $server->database_limit ? trans('server/database.limit') : trans('server/database.create_database')) ->disabled(fn () => $server->databases()->count() >= $server->database_limit) diff --git a/app/Filament/Server/Resources/FileResource/Pages/EditFiles.php b/app/Filament/Server/Resources/FileResource/Pages/EditFiles.php index ffa4589b6..47c0d72e7 100644 --- a/app/Filament/Server/Resources/FileResource/Pages/EditFiles.php +++ b/app/Filament/Server/Resources/FileResource/Pages/EditFiles.php @@ -118,7 +118,7 @@ class EditFiles extends Page ->icon('tabler-x') ->url(fn () => ListFiles::getUrl(['path' => dirname($this->path)])), ]) - ->footerActionsAlignment(Alignment::End) //TODO MISSING PADDING + ->footerActionsAlignment(Alignment::End) ->schema([ Select::make('lang') ->label(trans('server/file.actions.new_file.syntax')) diff --git a/app/Filament/Server/Resources/FileResource/Pages/ListFiles.php b/app/Filament/Server/Resources/FileResource/Pages/ListFiles.php index 7a134babe..e6f8bff54 100644 --- a/app/Filament/Server/Resources/FileResource/Pages/ListFiles.php +++ b/app/Filament/Server/Resources/FileResource/Pages/ListFiles.php @@ -2,6 +2,8 @@ namespace App\Filament\Server\Resources\FileResource\Pages; +use App\Exceptions\Repository\FileExistsException; +use App\Livewire\AlertBanner; use Exception; use App\Facades\Activity; use App\Filament\Server\Resources\FileResource; @@ -22,17 +24,23 @@ use Filament\Actions\DeleteBulkAction; use Filament\Actions\EditAction; use Filament\Facades\Filament; use Filament\Forms\Components\CheckboxList; +use Filament\Forms\Components\CodeEditor; +use Filament\Forms\Components\FileUpload; use Filament\Forms\Components\TextInput; use Filament\Infolists\Components\TextEntry; use Filament\Notifications\Notification; use Filament\Panel; use Filament\Resources\Pages\ListRecords; 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\Enums\PaginationMode; use Filament\Tables\Table; use Illuminate\Database\Eloquent\Collection; +use Illuminate\Http\UploadedFile; use Illuminate\Routing\Route; use Illuminate\Support\Carbon; use Illuminate\Support\Facades\Route as RouteFacade; @@ -78,10 +86,13 @@ class ListFiles extends ListRecords $files = File::get($server, $this->path); return $table - ->paginated([25, 50]) + ->paginated() + ->paginationMode(PaginationMode::Simple) + ->searchable() ->defaultPaginationPageOption(25) ->query(fn () => $files->orderByDesc('is_directory')) ->defaultSort('name') + ->deferLoading() ->columns([ TextColumn::make('name') ->label(trans('server/file.name')) @@ -410,6 +421,137 @@ class ListFiles extends ListRecords ->send(); }), ]), + + Action::make('new_file') + ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_CREATE, $server)) + ->tooltip(trans('server/file.actions.new_file.title')) + ->hiddenLabel()->icon('tabler-file-plus')->iconButton()->iconSize(IconSize::ExtraLarge) + ->color('primary') + ->modalSubmitActionLabel(trans('server/file.actions.new_file.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('file_already_exists') + ->title('' . $path . ' already exists!') + ->danger() + ->closable() + ->send(); + + $this->redirect(self::getUrl(['path' => dirname($path)])); + } + }) + ->schema([ + TextInput::make('name') + ->label(trans('server/file.actions.new_file.file_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(trans('server/file.actions.new_folder.title')) + ->color('primary') + ->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('folder_already_exists') + ->title('' . $path . ' already exists!') + ->danger() + ->closable() + ->send(); + + $this->redirect(self::getUrl(['path' => dirname($path)])); + } + }) + ->schema([ + TextInput::make('name') + ->label(trans('server/file.actions.new_folder.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(trans('server/file.actions.upload.title')) + ->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()); + + 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(trans('server/file.actions.upload.from_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(trans('server/file.actions.upload.url')) + ->live() + ->disabled(fn (Get $get) => count($get('files')) > 0) + ->schema([ + TextInput::make('url') + ->label(trans('server/file.actions.upload.url')) + ->url(), + ]), + ]), + ]), + Action::make('search') + ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_READ, $server)) + ->hiddenLabel()->iconButton()->iconSize(IconSize::ExtraLarge) + ->tooltip(trans('server/file.actions.global_search.title')) + ->color('primary') + ->icon('tabler-world-search') + ->modalHeading(trans('server/file.actions.global_search.title')) + ->modalSubmitActionLabel(trans('server/file.actions.global_search.search')) + ->schema([ + TextInput::make('searchTerm') + ->label(trans('server/file.actions.global_search.search_term')) + ->placeholder(trans('server/file.actions.global_search.search_term_placeholder')) + ->required() + ->regex('/^[^*]*\*?[^*]*$/') + ->minValue(3), + ]) + ->action(fn ($data) => redirect(SearchFiles::getUrl([ + 'searchTerm' => $data['searchTerm'], + 'path' => $this->path, + ]))), ]); } diff --git a/app/Filament/Server/Resources/ScheduleResource/Pages/EditSchedule.php b/app/Filament/Server/Resources/ScheduleResource/Pages/EditSchedule.php index 226b534c7..b2a29f5a7 100644 --- a/app/Filament/Server/Resources/ScheduleResource/Pages/EditSchedule.php +++ b/app/Filament/Server/Resources/ScheduleResource/Pages/EditSchedule.php @@ -49,7 +49,7 @@ class EditSchedule extends EditRecord { return [ DeleteAction::make() - ->hiddenLabel()->iconButton()->iconSize(IconSize::Large) + ->hiddenLabel()->iconButton()->iconSize(IconSize::ExtraLarge) ->icon('tabler-trash') ->tooltip(trans('server/schedule.delete')) ->after(function ($record) { @@ -58,15 +58,15 @@ class EditSchedule extends EditRecord ->log(); }), ExportScheduleAction::make() - ->hiddenLabel()->iconButton()->iconSize(IconSize::Large) + ->hiddenLabel()->iconButton()->iconSize(IconSize::ExtraLarge) ->icon('tabler-download') ->tooltip(trans('server/schedule.export')), $this->getSaveFormAction()->formId('form') - ->hiddenLabel()->iconButton()->iconSize(IconSize::Large) + ->hiddenLabel()->iconButton()->iconSize(IconSize::ExtraLarge) ->icon('tabler-device-floppy') ->tooltip(trans('server/schedule.save')), $this->getCancelFormAction()->formId('form') - ->hiddenLabel()->iconButton()->iconSize(IconSize::Large) + ->hiddenLabel()->iconButton()->iconSize(IconSize::ExtraLarge) ->icon('tabler-cancel') ->tooltip(trans('server/schedule.cancel')), ]; diff --git a/app/Filament/Server/Resources/ScheduleResource/Pages/ViewSchedule.php b/app/Filament/Server/Resources/ScheduleResource/Pages/ViewSchedule.php index 2c55ef019..2bc12d3e6 100644 --- a/app/Filament/Server/Resources/ScheduleResource/Pages/ViewSchedule.php +++ b/app/Filament/Server/Resources/ScheduleResource/Pages/ViewSchedule.php @@ -43,7 +43,7 @@ class ViewSchedule extends ViewRecord $this->fillForm(); }), EditAction::make() - ->hiddenLabel()->iconButton()->iconSize(IconSize::Large) + ->hiddenLabel()->iconButton()->iconSize(IconSize::ExtraLarge) ->icon('tabler-calendar-code') ->tooltip(trans('server/schedule.edit')), ]; diff --git a/app/Filament/Server/Resources/UserResource.php b/app/Filament/Server/Resources/UserResource.php index 5d1f0c0fc..e47a71d3c 100644 --- a/app/Filament/Server/Resources/UserResource.php +++ b/app/Filament/Server/Resources/UserResource.php @@ -119,7 +119,6 @@ class UserResource extends Resource return $table ->paginated(false) -// ->searchable(false) TODO toolbarActions do not render without the search bar :/ ->columns([ ImageColumn::make('picture') ->visibleFrom('lg')