mirror of
				https://github.com/pelican-dev/panel.git
				synced 2025-11-04 10:16:51 +01:00 
			
		
		
		
	Co-authored-by: notCharles <charles@pelican.dev> Co-authored-by: Lance Pioch <lancepioch@gmail.com> Co-authored-by: Boy132 <mail@boy132.de> Co-authored-by: RMartinOscar <40749467+RMartinOscar@users.noreply.github.com>
		
			
				
	
	
		
			347 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			347 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
<?php
 | 
						|
 | 
						|
namespace App\Filament\Admin\Resources;
 | 
						|
 | 
						|
use App\Filament\Admin\Resources\WebhookResource\Pages;
 | 
						|
use App\Filament\Admin\Resources\WebhookResource\Pages\EditWebhookConfiguration;
 | 
						|
use App\Livewire\AlertBanner;
 | 
						|
use App\Models\WebhookConfiguration;
 | 
						|
use App\Traits\Filament\CanCustomizePages;
 | 
						|
use App\Traits\Filament\CanCustomizeRelations;
 | 
						|
use App\Traits\Filament\CanModifyForm;
 | 
						|
use App\Traits\Filament\CanModifyTable;
 | 
						|
use Filament\Forms\Components\Checkbox;
 | 
						|
use Filament\Forms\Components\CheckboxList;
 | 
						|
use Filament\Forms\Components\ColorPicker;
 | 
						|
use Filament\Forms\Components\KeyValue;
 | 
						|
use Filament\Forms\Components\Repeater;
 | 
						|
use Filament\Forms\Components\Section;
 | 
						|
use Filament\Forms\Components\Textarea;
 | 
						|
use Filament\Forms\Components\TextInput;
 | 
						|
use Filament\Forms\Components\ToggleButtons;
 | 
						|
use Filament\Forms\Form;
 | 
						|
use Filament\Resources\Pages\PageRegistration;
 | 
						|
use Filament\Forms\Get;
 | 
						|
use Filament\Resources\Resource;
 | 
						|
use Filament\Forms\Set;
 | 
						|
use Filament\Tables\Actions\DeleteBulkAction;
 | 
						|
use Filament\Tables\Actions\ViewAction;
 | 
						|
use Filament\Tables\Actions\EditAction;
 | 
						|
use Filament\Tables\Actions\ReplicateAction;
 | 
						|
use Filament\Tables\Actions\CreateAction;
 | 
						|
use Filament\Tables\Columns\IconColumn;
 | 
						|
use Filament\Tables\Columns\TextColumn;
 | 
						|
use Filament\Tables\Filters\SelectFilter;
 | 
						|
use Filament\Tables\Table;
 | 
						|
use Livewire\Features\SupportEvents\HandlesEvents;
 | 
						|
use App\Enums\WebhookType;
 | 
						|
use Filament\Forms\Components\Component;
 | 
						|
use Livewire\Component as Livewire;
 | 
						|
 | 
						|
class WebhookResource extends Resource
 | 
						|
{
 | 
						|
    use CanCustomizePages;
 | 
						|
    use CanCustomizeRelations;
 | 
						|
    use CanModifyForm;
 | 
						|
    use CanModifyTable;
 | 
						|
    use HandlesEvents;
 | 
						|
 | 
						|
    protected static ?string $model = WebhookConfiguration::class;
 | 
						|
 | 
						|
    protected static ?string $navigationIcon = 'tabler-webhook';
 | 
						|
 | 
						|
    protected static ?string $recordTitleAttribute = 'description';
 | 
						|
 | 
						|
    public static function getNavigationLabel(): string
 | 
						|
    {
 | 
						|
        return trans('admin/webhook.nav_title');
 | 
						|
    }
 | 
						|
 | 
						|
    public static function getModelLabel(): string
 | 
						|
    {
 | 
						|
        return trans('admin/webhook.model_label');
 | 
						|
    }
 | 
						|
 | 
						|
    public static function getPluralModelLabel(): string
 | 
						|
    {
 | 
						|
        return trans('admin/webhook.model_label_plural');
 | 
						|
    }
 | 
						|
 | 
						|
    public static function getNavigationBadge(): ?string
 | 
						|
    {
 | 
						|
        return static::getModel()::count() ?: null;
 | 
						|
    }
 | 
						|
 | 
						|
    public static function getNavigationGroup(): ?string
 | 
						|
    {
 | 
						|
        return trans('admin/dashboard.advanced');
 | 
						|
    }
 | 
						|
 | 
						|
    public static function defaultTable(Table $table): Table
 | 
						|
    {
 | 
						|
        return $table
 | 
						|
            ->columns([
 | 
						|
                IconColumn::make('type'),
 | 
						|
                TextColumn::make('endpoint')
 | 
						|
                    ->label(trans('admin/webhook.table.endpoint'))
 | 
						|
                    ->formatStateUsing(fn (string $state) => str($state)->after('://'))
 | 
						|
                    ->limit(60)
 | 
						|
                    ->wrap(),
 | 
						|
                TextColumn::make('description')
 | 
						|
                    ->label(trans('admin/webhook.table.description')),
 | 
						|
                TextColumn::make('endpoint')
 | 
						|
                    ->label(trans('admin/webhook.table.endpoint')),
 | 
						|
            ])
 | 
						|
            ->actions([
 | 
						|
                ViewAction::make()
 | 
						|
                    ->hidden(fn (WebhookConfiguration $record) => static::canEdit($record)),
 | 
						|
                EditAction::make(),
 | 
						|
                ReplicateAction::make()
 | 
						|
                    ->iconButton()
 | 
						|
                    ->tooltip(trans('filament-actions::replicate.single.label'))
 | 
						|
                    ->modal(false)
 | 
						|
                    ->excludeAttributes(['created_at', 'updated_at'])
 | 
						|
                    ->beforeReplicaSaved(fn (WebhookConfiguration $replica) => $replica->description .= ' Copy ' . now()->format('Y-m-d H:i:s'))
 | 
						|
                    ->successRedirectUrl(fn (WebhookConfiguration $replica) => EditWebhookConfiguration::getUrl(['record' => $replica])),
 | 
						|
            ])
 | 
						|
            ->groupedBulkActions([
 | 
						|
                DeleteBulkAction::make(),
 | 
						|
            ])
 | 
						|
            ->emptyStateIcon('tabler-webhook')
 | 
						|
            ->emptyStateDescription('')
 | 
						|
            ->emptyStateHeading(trans('admin/webhook.no_webhooks'))
 | 
						|
            ->emptyStateActions([
 | 
						|
                CreateAction::make(),
 | 
						|
            ])
 | 
						|
            ->persistFiltersInSession()
 | 
						|
            ->filters([
 | 
						|
                SelectFilter::make('type')
 | 
						|
                    ->options(WebhookType::class)
 | 
						|
                    ->attribute('type'),
 | 
						|
            ]);
 | 
						|
    }
 | 
						|
 | 
						|
    public static function defaultForm(Form $form): Form
 | 
						|
    {
 | 
						|
        return $form
 | 
						|
            ->schema([
 | 
						|
                ToggleButtons::make('type')
 | 
						|
                    ->live()
 | 
						|
                    ->inline()
 | 
						|
                    ->options(WebhookType::class)
 | 
						|
                    ->default(WebhookType::Regular->value)
 | 
						|
                    ->afterStateHydrated(function (string $state) {
 | 
						|
                        if ($state === WebhookType::Discord->value) {
 | 
						|
                            self::sendHelpBanner();
 | 
						|
                        }
 | 
						|
                    })
 | 
						|
                    ->afterStateUpdated(function (string $state) {
 | 
						|
                        if ($state === WebhookType::Discord->value) {
 | 
						|
                            self::sendHelpBanner();
 | 
						|
                        }
 | 
						|
                    }),
 | 
						|
                TextInput::make('description')
 | 
						|
                    ->label(trans('admin/webhook.description'))
 | 
						|
                    ->required(),
 | 
						|
                TextInput::make('endpoint')
 | 
						|
                    ->label(trans('admin/webhook.endpoint'))
 | 
						|
                    ->activeUrl()
 | 
						|
                    ->required()
 | 
						|
                    ->columnSpanFull()
 | 
						|
                    ->afterStateUpdated(fn (string $state, Set $set) => $set('type', str($state)->contains('discord.com') ? WebhookType::Discord->value : WebhookType::Regular->value)),
 | 
						|
                Section::make(trans('admin/webhook.regular'))
 | 
						|
                    ->hidden(fn (Get $get) => $get('type') === WebhookType::Discord->value)
 | 
						|
                    ->dehydratedWhenHidden()
 | 
						|
                    ->schema(fn () => self::getRegularFields())
 | 
						|
                    ->formBefore(),
 | 
						|
                Section::make(trans('admin/webhook.discord'))
 | 
						|
                    ->hidden(fn (Get $get) => $get('type') === WebhookType::Regular->value)
 | 
						|
                    ->dehydratedWhenHidden()
 | 
						|
                    ->afterStateUpdated(fn (Livewire $livewire) => $livewire->dispatch('refresh-widget'))
 | 
						|
                    ->schema(fn () => self::getDiscordFields())
 | 
						|
                    ->view('filament.components.webhooksection')
 | 
						|
                    ->aside()
 | 
						|
                    ->formBefore(),
 | 
						|
                Section::make(trans('admin/webhook.events'))
 | 
						|
                    ->collapsible()
 | 
						|
                    ->collapsed(fn (Get $get) => count($get('events') ?? []))
 | 
						|
                    ->schema([
 | 
						|
                        CheckboxList::make('events')
 | 
						|
                            ->live()
 | 
						|
                            ->options(fn () => WebhookConfiguration::filamentCheckboxList())
 | 
						|
                            ->searchable()
 | 
						|
                            ->bulkToggleable()
 | 
						|
                            ->columns(3)
 | 
						|
                            ->columnSpanFull()
 | 
						|
                            ->required(),
 | 
						|
                    ]),
 | 
						|
            ]);
 | 
						|
    }
 | 
						|
 | 
						|
    /** @return Component[] */
 | 
						|
    private static function getRegularFields(): array
 | 
						|
    {
 | 
						|
        return [
 | 
						|
            KeyValue::make('headers')
 | 
						|
                ->label(trans('admin/webhook.headers')),
 | 
						|
        ];
 | 
						|
    }
 | 
						|
 | 
						|
    /** @return Component[] */
 | 
						|
    private static function getDiscordFields(): array
 | 
						|
    {
 | 
						|
        return [
 | 
						|
            Section::make(trans('admin/webhook.discord_message.profile'))
 | 
						|
                ->collapsible()
 | 
						|
                ->schema([
 | 
						|
                    TextInput::make('username')
 | 
						|
                        ->live(debounce: 500)
 | 
						|
                        ->label(trans('admin/webhook.discord_message.username')),
 | 
						|
                    TextInput::make('avatar_url')
 | 
						|
                        ->live(debounce: 500)
 | 
						|
                        ->label(trans('admin/webhook.discord_message.avatar_url')),
 | 
						|
                ]),
 | 
						|
            Section::make(trans('admin/webhook.discord_message.message'))
 | 
						|
                ->collapsible()
 | 
						|
                ->schema([
 | 
						|
                    TextInput::make('content')
 | 
						|
                        ->label(trans('admin/webhook.discord_message.message'))
 | 
						|
                        ->live(debounce: 500)
 | 
						|
                        ->required(fn (Get $get) => empty($get('embeds'))),
 | 
						|
                    TextInput::make('thread_name')
 | 
						|
                        ->label(trans('admin/webhook.discord_message.forum_thread')),
 | 
						|
                    CheckboxList::make('flags')
 | 
						|
                        ->label('Flags')
 | 
						|
                        ->options([
 | 
						|
                            (1 << 2) => trans('admin/webhook.discord_message.supress_embeds'),
 | 
						|
                            (1 << 12) => trans('admin/webhook.discord_message.supress_notifications'),
 | 
						|
                        ])
 | 
						|
                        ->descriptions([
 | 
						|
                            (1 << 2) => trans('admin/webhook.discord_message.supress_embeds_text'),
 | 
						|
                            (1 << 12) => trans('admin/webhook.discord_message.supress_notifications_text'),
 | 
						|
                        ]),
 | 
						|
                    CheckboxList::make('allowed_mentions')
 | 
						|
                        ->label(trans('admin/webhook.discord_embed.allowed_mentions'))
 | 
						|
                        ->options([
 | 
						|
                            'roles' => trans('admin/webhook.discord_embed.roles'),
 | 
						|
                            'users' => trans('admin/webhook.discord_embed.users'),
 | 
						|
                            'everyone' => trans('admin/webhook.discord_embed.everyone'),
 | 
						|
                        ]),
 | 
						|
                ]),
 | 
						|
            Repeater::make('embeds')
 | 
						|
                ->live(debounce: 500)
 | 
						|
                ->itemLabel(fn (array $state) => $state['title'])
 | 
						|
                ->addActionLabel(trans('admin/webhook.discord_embed.add_embed'))
 | 
						|
                ->required(fn (Get $get) => empty($get('content')))
 | 
						|
                ->reorderable()
 | 
						|
                ->collapsible()
 | 
						|
                ->maxItems(10)
 | 
						|
                ->schema([
 | 
						|
                    Section::make(trans('admin/webhook.discord_embed.author'))
 | 
						|
                        ->collapsible()
 | 
						|
                        ->collapsed()
 | 
						|
                        ->schema([
 | 
						|
                            TextInput::make('author.name')
 | 
						|
                                ->live(debounce: 500)
 | 
						|
                                ->label(trans('admin/webhook.discord_embed.author'))
 | 
						|
                                ->required(fn (Get $get) => filled($get('author.url')) || filled($get('author.icon_url'))),
 | 
						|
                            TextInput::make('author.url')
 | 
						|
                                ->live(debounce: 500)
 | 
						|
                                ->label(trans('admin/webhook.discord_embed.author_url')),
 | 
						|
                            TextInput::make('author.icon_url')
 | 
						|
                                ->live(debounce: 500)
 | 
						|
                                ->label(trans('admin/webhook.discord_embed.author_icon_url')),
 | 
						|
                        ]),
 | 
						|
                    Section::make(trans('admin/webhook.discord_embed.body'))
 | 
						|
                        ->collapsible()
 | 
						|
                        ->collapsed()
 | 
						|
                        ->schema([
 | 
						|
                            TextInput::make('title')
 | 
						|
                                ->live(debounce: 500)
 | 
						|
                                ->label(trans('admin/webhook.discord_embed.title'))
 | 
						|
                                ->required(fn (Get $get) => $get('description') === null),
 | 
						|
                            Textarea::make('description')
 | 
						|
                                ->live(debounce: 500)
 | 
						|
                                ->label(trans('admin/webhook.discord_embed.body'))
 | 
						|
                                ->required(fn (Get $get) => $get('title') === null),
 | 
						|
                            ColorPicker::make('color')
 | 
						|
                                ->live(debounce: 500)
 | 
						|
                                ->label(trans('admin/webhook.discord_embed.color'))
 | 
						|
                                ->hex(),
 | 
						|
                            TextInput::make('url')
 | 
						|
                                ->live(debounce: 500)
 | 
						|
                                ->label(trans('admin/webhook.discord_embed.url')),
 | 
						|
                        ]),
 | 
						|
                    Section::make(trans('admin/webhook.discord_embed.images'))
 | 
						|
                        ->collapsible()
 | 
						|
                        ->collapsed()
 | 
						|
                        ->schema([
 | 
						|
                            TextInput::make('image.url')
 | 
						|
                                ->live(debounce: 500)
 | 
						|
                                ->label(trans('admin/webhook.discord_embed.image_url')),
 | 
						|
                            TextInput::make('thumbnail.url')
 | 
						|
                                ->live(debounce: 500)
 | 
						|
                                ->label(trans('admin/webhook.discord_embed.image_thumbnail')),
 | 
						|
                        ]),
 | 
						|
                    Section::make(trans('admin/webhook.discord_embed.footer'))
 | 
						|
                        ->collapsible()
 | 
						|
                        ->collapsed()
 | 
						|
                        ->schema([
 | 
						|
                            TextInput::make('footer.text')
 | 
						|
                                ->live(debounce: 500)
 | 
						|
                                ->label(trans('admin/webhook.discord_embed.footer')),
 | 
						|
                            Checkbox::make('has_timestamp')
 | 
						|
                                ->live(debounce: 500)
 | 
						|
                                ->label(trans('admin/webhook.discord_embed.has_timestamp')),
 | 
						|
                            TextInput::make('footer.icon_url')
 | 
						|
                                ->live(debounce: 500)
 | 
						|
                                ->label(trans('admin/webhook.discord_embed.footer_icon_url')),
 | 
						|
                        ]),
 | 
						|
                    Section::make(trans('admin/webhook.discord_embed.fields'))
 | 
						|
                        ->collapsible()->collapsed()
 | 
						|
                        ->schema([
 | 
						|
                            Repeater::make('fields')
 | 
						|
                                ->reorderable()
 | 
						|
                                ->addActionLabel(trans('admin/webhook.discord_embed.add_field'))
 | 
						|
                                ->collapsible()
 | 
						|
                                ->schema([
 | 
						|
                                    TextInput::make('name')
 | 
						|
                                        ->live(debounce: 500)
 | 
						|
                                        ->label(trans('admin/webhook.discord_embed.field_name'))
 | 
						|
                                        ->required(),
 | 
						|
                                    Textarea::make('value')
 | 
						|
                                        ->live(debounce: 500)
 | 
						|
                                        ->label(trans('admin/webhook.discord_embed.field_value'))
 | 
						|
                                        ->rows(4)
 | 
						|
                                        ->required(),
 | 
						|
                                    Checkbox::make('inline')
 | 
						|
                                        ->live(debounce: 500)
 | 
						|
                                        ->label(trans('admin/webhook.discord_embed.inline_field')),
 | 
						|
                                ]),
 | 
						|
                        ]),
 | 
						|
                ]),
 | 
						|
        ];
 | 
						|
    }
 | 
						|
 | 
						|
    public static function sendHelpBanner(): void
 | 
						|
    {
 | 
						|
        AlertBanner::make('discord_webhook_help')
 | 
						|
            ->title(trans('admin/webhook.help'))
 | 
						|
            ->body(trans('admin/webhook.help_text'))
 | 
						|
            ->icon('tabler-question-mark')
 | 
						|
            ->info()
 | 
						|
            ->send();
 | 
						|
    }
 | 
						|
 | 
						|
    /** @return array<string, PageRegistration> */
 | 
						|
    public static function getDefaultPages(): array
 | 
						|
    {
 | 
						|
        return [
 | 
						|
            'index' => Pages\ListWebhookConfigurations::route('/'),
 | 
						|
            'create' => Pages\CreateWebhookConfiguration::route('/create'),
 | 
						|
            'view' => Pages\ViewWebhookConfiguration::route('/{record}'),
 | 
						|
            'edit' => Pages\EditWebhookConfiguration::route('/{record}/edit'),
 | 
						|
        ];
 | 
						|
    }
 | 
						|
}
 |