-
Notifications
You must be signed in to change notification settings - Fork 40
Add Backup Templates Plugin #117
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
ad6e495
86c2b7e
1e66b32
f78ccf9
31869bc
7e7941d
06f21f4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| # Backup Templates (by Ebnater) | ||
|
|
||
| Create reusable backup ignore presets for specific servers. | ||
|
|
||
| This plugin adds per-server backup templates that can be selected when creating a backup. Selecting a template automatically fills the ignored paths field with the saved preset. | ||
|
|
||
| ## Features | ||
|
|
||
| - Create backup templates with predefined ignored paths | ||
| - Store multiple templates per server | ||
| - Apply templates directly from the backup creation form | ||
| - Keep templates separated per server |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| <?php | ||
|
|
||
| use Illuminate\Database\Migrations\Migration; | ||
| use Illuminate\Database\Schema\Blueprint; | ||
| use Illuminate\Support\Facades\Schema; | ||
|
|
||
| return new class extends Migration | ||
| { | ||
| public function up(): void | ||
| { | ||
| Schema::create('backup_templates', function (Blueprint $table) { | ||
| $table->increments('id'); | ||
|
|
||
| $table->unsignedInteger('server_id'); | ||
| $table->foreign('server_id')->references('id')->on('servers')->cascadeOnDelete(); | ||
|
|
||
| $table->string('name'); | ||
| $table->text('ignored')->nullable(); | ||
|
|
||
| $table->timestamps(); | ||
|
|
||
| $table->unique(['server_id', 'name']); | ||
| }); | ||
| } | ||
|
|
||
| public function down(): void | ||
| { | ||
| Schema::dropIfExists('backup_templates'); | ||
| } | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| <?php | ||
|
|
||
| return [ | ||
| 'permissions' => [ | ||
| 'backupTemplates_desc' => 'Berechtigungen zum Verwalten von Backup-Vorlagen für diesen Server.', | ||
| 'backupTemplates_create' => 'Erlaubt es einem Benutzer, Backup-Vorlagen für diesen Server zu erstellen, zu bearbeiten und zu löschen.', | ||
| ], | ||
Ebnater marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ]; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| <?php | ||
|
|
||
| return [ | ||
| 'template' => 'Backup Template|Backup Templates', | ||
|
|
||
| 'permissions' => [ | ||
| 'group' => 'Berechtigungen zum Verwalten von Backup-Vorlagen für diesen Server.', | ||
| 'create' => 'Erlaubt es einem Benutzer, Backup-Vorlagen für diesen Server zu erstellen, zu bearbeiten und zu löschen.', | ||
| ], | ||
|
|
||
| 'fields' => [ | ||
| 'name' => 'Name', | ||
| 'ignored' => 'Ignorierte Dateien und Ordner', | ||
| 'ignored_help' => 'Verwenden Sie einen Pfad pro Zeile, der dem Pelican-Backup-Ignore-Format entspricht.', | ||
| ], | ||
|
|
||
| 'backup_form' => [ | ||
| 'template' => 'Ignorier-Vorlage', | ||
| 'template_placeholder' => 'Keine Vorlage ausgewählt', | ||
| 'template_help' => 'Verwenden Sie eine Vorlage, um ignorierte Pfade automatisch auszufüllen.', | ||
| ], | ||
| ]; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| <?php | ||
|
|
||
| return [ | ||
| 'permissions' => [ | ||
| 'backupTemplates_desc' => 'Permissions for managing backup templates for this server.', | ||
| 'backupTemplates_create' => 'Allows a user to create, edit, and delete backup templates for this server.', | ||
| ], | ||
Ebnater marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ]; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| <?php | ||
|
|
||
| return [ | ||
| 'template' => 'Backup Template|Backup Templates', | ||
|
|
||
| 'permissions' => [ | ||
| 'group' => 'Permissions for managing backup templates for this server.', | ||
| 'create' => 'Allows a user to create, edit, and delete backup templates for this server.', | ||
| ], | ||
|
|
||
| 'fields' => [ | ||
| 'name' => 'Name', | ||
| 'ignored' => 'Ignored Files and Folders', | ||
| 'ignored_help' => 'Use one path per line, matching Pelican backup ignore format.', | ||
| ], | ||
|
|
||
| 'backup_form' => [ | ||
| 'template' => 'Ignore Preset', | ||
| 'template_placeholder' => 'No preset selected', | ||
| 'template_help' => 'Select a template to auto-fill ignored paths.', | ||
| ], | ||
| ]; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| { | ||
| "id": "backup-templates", | ||
| "name": "Backup Templates", | ||
| "author": "Ebnater", | ||
| "version": "1.0.0", | ||
| "description": "Provides backup templates for your Pelican site.", | ||
| "category": "plugin", | ||
| "url": "https://github.com/pelican-dev/plugins/tree/main/backup-templates", | ||
| "update_url": null, | ||
| "namespace": "Ebnater\\BackupTemplates", | ||
| "class": "BackupTemplatesPlugin", | ||
| "panels": [ | ||
| "server" | ||
| ], | ||
| "panel_version": null, | ||
| "composer_packages": [] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| <?php | ||
|
|
||
| namespace Ebnater\BackupTemplates; | ||
|
|
||
| use Filament\Contracts\Plugin; | ||
| use Filament\Panel; | ||
|
|
||
| class BackupTemplatesPlugin implements Plugin | ||
| { | ||
| public function getId(): string | ||
| { | ||
| return 'backup-templates'; | ||
| } | ||
|
|
||
| public function register(Panel $panel): void | ||
| { | ||
| $id = str($panel->getId())->title(); | ||
|
|
||
| $panel->discoverResources(plugin_path($this->getId(), "src/Filament/$id/Resources"), "Ebnater\\BackupTemplates\\Filament\\$id\\Resources"); | ||
| } | ||
|
|
||
| public function boot(Panel $panel): void {} | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,103 @@ | ||
| <?php | ||
|
|
||
| namespace Ebnater\BackupTemplates\Filament\Server\Resources\BackupTemplates; | ||
|
|
||
| use App\Enums\SubuserPermission; | ||
| use App\Models\Server; | ||
| use Ebnater\BackupTemplates\Filament\Server\Resources\BackupTemplates\Pages\ListBackupTemplates; | ||
| use Ebnater\BackupTemplates\Models\BackupTemplate; | ||
| use Filament\Actions\DeleteAction; | ||
| use Filament\Actions\EditAction; | ||
|
Comment on lines
+9
to
+10
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result: In Filament v3, the table “Edit” row action class is: use Filament\Tables\Actions\EditAction;Use it in your table like: ->actions([
EditAction::make(),
])(For non-table actions, the namespace is 🏁 Script executed: cat -n backup-templates/src/Filament/Server/Resources/BackupTemplates/BackupTemplateResource.phpRepository: pelican-dev/plugins Length of output: 4017 Use correct namespace for table record actions. Lines 9-10 import from use Filament\Tables\Actions\DeleteAction;
use Filament\Tables\Actions\EditAction;The 🤖 Prompt for AI Agents |
||
| use Filament\Facades\Filament; | ||
| use Filament\Forms\Components\Textarea; | ||
| use Filament\Forms\Components\TextInput; | ||
| use Filament\Resources\Resource; | ||
| use Filament\Schemas\Schema; | ||
| use Filament\Tables\Columns\TextColumn; | ||
| use Filament\Tables\Table; | ||
| use Illuminate\Database\Eloquent\Builder; | ||
|
|
||
| class BackupTemplateResource extends Resource | ||
| { | ||
| protected static ?string $model = BackupTemplate::class; | ||
|
|
||
| protected static ?int $navigationSort = 31; | ||
|
|
||
| protected static string|\BackedEnum|null $navigationIcon = 'tabler-template'; | ||
|
|
||
| public static function canAccess(): bool | ||
| { | ||
| /** @var Server|null $server */ | ||
| $server = Filament::getTenant(); | ||
|
|
||
| return $server !== null | ||
| && user()?->can(SubuserPermission::BackupRead, $server) | ||
| && parent::canAccess(); | ||
| } | ||
|
|
||
| public static function getNavigationLabel(): string | ||
| { | ||
| return trans_choice('backup-templates::strings.template', 2); | ||
| } | ||
|
|
||
| public static function getModelLabel(): string | ||
| { | ||
| return trans_choice('backup-templates::strings.template', 1); | ||
| } | ||
|
|
||
| public static function getPluralModelLabel(): string | ||
| { | ||
| return trans_choice('backup-templates::strings.template', 2); | ||
| } | ||
|
|
||
| public static function form(Schema $schema): Schema | ||
| { | ||
| return $schema | ||
| ->components([ | ||
| TextInput::make('name') | ||
| ->label(trans('backup-templates::strings.fields.name')) | ||
| ->required() | ||
| ->maxLength(255), | ||
| Textarea::make('ignored') | ||
| ->label(trans('backup-templates::strings.fields.ignored')) | ||
| ->helperText(trans('backup-templates::strings.fields.ignored_help')) | ||
| ->rows(8) | ||
| ->columnSpanFull(), | ||
| ]); | ||
| } | ||
|
|
||
| public static function table(Table $table): Table | ||
| { | ||
| return $table | ||
| ->columns([ | ||
| TextColumn::make('name') | ||
| ->label(trans('backup-templates::strings.fields.name')) | ||
| ->searchable() | ||
| ->sortable(), | ||
| TextColumn::make('ignored') | ||
| ->label(trans('backup-templates::strings.fields.ignored')) | ||
| ->lineClamp(2) | ||
| ->placeholder('-'), | ||
| ]) | ||
| ->recordActions([ | ||
| EditAction::make(), | ||
| DeleteAction::make(), | ||
| ]); | ||
| } | ||
|
|
||
| public static function getPages(): array | ||
| { | ||
| return [ | ||
| 'index' => ListBackupTemplates::route('/'), | ||
| ]; | ||
| } | ||
|
|
||
| public static function getEloquentQuery(): Builder | ||
| { | ||
| /** @var Server|null $server */ | ||
| $server = Filament::getTenant(); | ||
|
|
||
| return parent::getEloquentQuery() | ||
| ->when($server, fn (Builder $query) => $query->where('server_id', $server->id)); | ||
| } | ||
|
Comment on lines
+95
to
+102
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Potential data exposure if query executed without tenant. The 🛡️ Proposed defensive fix public static function getEloquentQuery(): Builder
{
/** `@var` Server|null $server */
$server = Filament::getTenant();
+ if (!$server) {
+ return parent::getEloquentQuery()->whereRaw('1 = 0');
+ }
+
return parent::getEloquentQuery()
- ->when($server, fn (Builder $query) => $query->where('server_id', $server->id));
+ ->where('server_id', $server->id);
}🤖 Prompt for AI Agents |
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| <?php | ||
|
|
||
| namespace Ebnater\BackupTemplates\Filament\Server\Resources\BackupTemplates\Pages; | ||
|
|
||
| use App\Models\Server; | ||
| use Ebnater\BackupTemplates\Filament\Server\Resources\BackupTemplates\BackupTemplateResource; | ||
| use Filament\Actions\CreateAction; | ||
| use Filament\Facades\Filament; | ||
| use Filament\Resources\Pages\ListRecords; | ||
|
|
||
| class ListBackupTemplates extends ListRecords | ||
| { | ||
| protected static string $resource = BackupTemplateResource::class; | ||
|
|
||
| public function getBreadcrumbs(): array | ||
| { | ||
| return []; | ||
| } | ||
|
|
||
| protected function getHeaderActions(): array | ||
| { | ||
| return [ | ||
| CreateAction::make() | ||
| ->authorize(function (): bool { | ||
| /** @var Server|null $server */ | ||
| $server = Filament::getTenant(); | ||
|
|
||
| return $server !== null && user()?->can('backupTemplates.create', $server); | ||
| }) | ||
| ->createAnother(false) | ||
| ->mutateDataUsing(function (array $data): array { | ||
| /** @var Server|null $server */ | ||
| $server = Filament::getTenant(); | ||
|
|
||
| if ($server) { | ||
| $data['server_id'] = $server->id; | ||
| } | ||
|
|
||
| return $data; | ||
| }), | ||
| ]; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| <?php | ||
|
|
||
| namespace Ebnater\BackupTemplates\Models; | ||
|
|
||
| use App\Models\Server; | ||
| use Illuminate\Database\Eloquent\Model; | ||
| use Illuminate\Database\Eloquent\Relations\BelongsTo; | ||
|
|
||
| /** | ||
| * @property int $id | ||
| * @property int $server_id | ||
| * @property string $name | ||
| * @property string|null $ignored | ||
| * @property Server $server | ||
| */ | ||
| class BackupTemplate extends Model | ||
| { | ||
| protected $fillable = [ | ||
| 'server_id', | ||
| 'name', | ||
| 'ignored', | ||
| ]; | ||
|
|
||
| public function server(): BelongsTo | ||
| { | ||
| return $this->belongsTo(Server::class, 'server_id'); | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.