Repository: pxlrbt/filament-activity-log Branch: main Commit: 570988d1530a Files: 28 Total size: 37.7 KB Directory structure: gitextract_vyqha24x/ ├── .github/ │ ├── FUNDING.yml │ └── workflows/ │ └── code-style.yml ├── .gitignore ├── LICENSE.md ├── composer.json ├── readme.md ├── resources/ │ ├── css/ │ │ └── styles.css │ ├── lang/ │ │ ├── ar/ │ │ │ └── activities.php │ │ ├── ckb/ │ │ │ └── activities.php │ │ ├── cs/ │ │ │ └── activities.php │ │ ├── de/ │ │ │ └── activities.php │ │ ├── en/ │ │ │ └── activities.php │ │ ├── es/ │ │ │ └── activities.php │ │ ├── fa/ │ │ │ └── activities.php │ │ ├── fr/ │ │ │ └── activities.php │ │ ├── id/ │ │ │ └── activities.php │ │ ├── it/ │ │ │ └── activities.php │ │ ├── nl/ │ │ │ └── activities.php │ │ ├── pt_BR/ │ │ │ └── activities.php │ │ ├── tr/ │ │ │ └── activities.php │ │ └── zh_TW/ │ │ └── activities.php │ └── views/ │ └── pages/ │ ├── list-activities-by-causer.blade.php │ └── list-activities-by-subject.blade.php └── src/ ├── FilamentActivityLogServiceProvider.php └── Pages/ ├── Concerns/ │ └── CanPaginate.php ├── ListActivities.php ├── ListActivitiesByCauser.php └── ListActivitiesBySubject.php ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/FUNDING.yml ================================================ github: pxlrbt ================================================ FILE: .github/workflows/code-style.yml ================================================ name: Code Style on: pull_request: push: branches: - main jobs: php-cs-fixer: name: Run Laravel Pint runs-on: ubuntu-latest permissions: contents: write steps: - name: Setup PHP uses: shivammathur/setup-php@v2 with: php-version: '8.4' - name: Git checkout uses: actions/checkout@v3 with: ref: ${{ github.head_ref }} - name: Install dependencies run: composer install -n --prefer-dist - name: Run Laravel Pint run: ./vendor/bin/pint - name: Commit changes uses: stefanzweifel/git-auto-commit-action@v4 with: commit_message: Apply style changes file_pattern: '*.php' ================================================ FILE: .gitignore ================================================ /vendor composer.phar composer.lock .DS_Store .idea phpunit.xml .phpunit.result.cache .php-cs-fixer.cache ================================================ FILE: LICENSE.md ================================================ MIT License Copyright (c) [2022] [Dennis Koch] Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: composer.json ================================================ { "name": "pxlrbt/filament-activity-log", "description": "Spatie's Laravel Activity Log integrated into Filament", "license": "MIT", "keywords": [ "activity log", "filament", "laravel-filament" ], "authors": [ { "name": "Dennis Koch", "email": "info@pixelarbeit.de" } ], "require": { "php": "^8.4", "filament/filament": "^4.0|^5.0", "spatie/laravel-activitylog": "^5.0" }, "autoload": { "psr-4": { "pxlrbt\\FilamentActivityLog\\": "src/" } }, "extra": { "laravel": { "providers": [ "\\pxlrbt\\FilamentActivityLog\\FilamentActivityLogServiceProvider" ] } }, "require-dev": { "laravel/pint": "^1.5" }, "scripts": { "pint": "vendor/bin/pint" } } ================================================ FILE: readme.md ================================================ ![header](./.github/resources/header.png) # Filament Activity Log [![Latest Version on Packagist](https://img.shields.io/packagist/v/pxlrbt/filament-activity-log.svg?include_prereleases)](https://packagist.org/packages/pxlrbt/filament-activity-log) [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg)](LICENSE.md) ![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/pxlrbt/filament-activity-log/code-style.yml?branch=main&label=Code%20style&style=flat-square) [![Total Downloads](https://img.shields.io/packagist/dt/pxlrbt/filament-activity-log.svg)](https://packagist.org/packages/pxlrbt/filament-activity-log) This package adds pages to the Filament Admin panel to view the activity log generated by [`spatie/laravel-activitylog`](https://github.com/spatie/laravel-activitylog). ![Screenshot](./.github/resources/screenshot.png) ## Installation | Plugin Version | Filament Version | Activitylog | PHP Version | |----------------|------------------|-------------|-------------| | 0.1.x | 2.x | 4.x | \> 8.0 | | 1.x | 3.x | 4.x | \> 8.1 | | 2.x | 4.x, 5.x | 4.x | \> 8.1 | | 3.x | 5.x | 5.x | \> 8.4 | Install via Composer. **Requires PHP 8.1, Filament 4.0 or 5.0, and spatie/laravel-activitylog 4.7 or 5.0** ```bash composer require pxlrbt/filament-activity-log ``` > **Warning** > This plugin offers two pages: one listing activities **on a subject** (use `ListActivitiesBySubject`) and one listing activities **caused by** a record such as a user (use `ListActivitiesByCauser`). You need [`spatie/laravel-activitylog`](https://github.com/spatie/laravel-activitylog) installed and configured for it to work. The subject page uses the `LogsActivity` trait's `activitiesAsSubject()` relation; the causer page uses the `CausesActivity` trait's `activitiesAsCauser()` relation. ## Filament v4 Upgrade Make sure you have a custom theme, add this line and recompile: `@import '../../../../vendor/pxlrbt/filament-activity-log/resources/css/styles.css';` ## Usage Make sure you use a **custom theme** and the vendor folder for this plugins is published, so that it includes the Tailwind CSS classes. ## Listing activities for a subject ![Screenshot](./.github/resources/screenshot.png) Use `ListActivitiesBySubject` to show all activities recorded **on** a record (e.g. every change to an order). ### Setup spatie/laravel-activitylog Make sure your resource model uses the `LogsActivity` trait. ```php **Note** > The legacy class `ListActivities` is kept as an abstract alias of `ListActivitiesBySubject`, so existing subclasses continue to work. ### Register the page Add the page to your resource's `getPages()` method. ```php public static function getPages(): array { return [ 'index' => Pages\ListOrders::route('/'), 'create' => Pages\CreateOrder::route('/create'), 'activities' => Pages\ListOrderActivities::route('/{record}/activities'), 'edit' => Pages\EditOrder::route('/{record}/edit'), ]; } ``` ## Listing activities caused by a record Use `ListActivitiesByCauser` to show all activities a record (typically a user) has caused across every subject. Each row links to the affected subject's resource when one is registered. ### Setup spatie/laravel-activitylog Make sure your user model uses the `CausesActivity` trait. ```php Pages\ListUsers::route('/'), 'create' => Pages\CreateUser::route('/create'), 'activities' => Pages\ListUserActivities::route('/{record}/activities'), 'edit' => Pages\EditUser::route('/{record}/edit'), ]; } ``` ## Contributing If you want to contribute to this packages, you may want to test it in a real Filament project: - Fork this repository to your GitHub account. - Create a Filament app locally. - Clone your fork in your Filament app's root directory. - In the `/filament-activity-log` directory, create a branch for your fix, e.g. `fix/error-message`. Install the packages in your app's `composer.json`: ```json "require": { "pxlrbt/filament-activity-log": "dev-fix/error-message as main-dev", }, "repositories": [ { "type": "path", "url": "filament-activity-log" } ] ``` Now, run `composer update`. ================================================ FILE: resources/css/styles.css ================================================ @source '../views/**' ================================================ FILE: resources/lang/ar/activities.php ================================================ 'سجل عمليات', 'title' => 'سجل عمليات :record', 'default_datetime_format' => 'Y-m-d, H:i:s', 'table' => [ 'field' => 'الحقل', 'old' => 'سابقاً', 'new' => 'حالياً', 'restore' => 'أسترجاع', ], 'events' => [ 'updated' => 'تحديث', 'created' => 'إنشاء', 'deleted' => 'حذف', 'restored' => 'استعادة', 'restore_successful' => 'تم الاسترجاع بنجاح', 'restore_failed' => 'فشل الاستراجع', ], ]; ================================================ FILE: resources/lang/ckb/activities.php ================================================ 'نرخی پێشوو', 'title' => 'نرخەکانی پێشووی :record', 'default_datetime_format' => 'Y-m-d, H:i:s', 'table' => [ 'field' => 'خانە', 'old' => 'کۆن', 'new' => 'نوێ', ], 'events' => [ 'updated' => 'نوێکراوەتەوە', 'Created' => 'دروستکراوە', 'deleted' => 'سڕایەوە', 'restored' => 'گەڕاندنەوە', 'restore_successful' => 'بە سەرکەوتوویی گەڕێنرایەوە', 'restore_failed' => 'گەڕاندنەوە شکستی هێنا', ], ]; ================================================ FILE: resources/lang/cs/activities.php ================================================ 'Log', 'title' => 'Log entity ":record"', 'default_datetime_format' => 'j.n.Y H:i:s', 'table' => [ 'field' => 'Pole', 'old' => 'Původní', 'new' => 'Nové', 'restore' => 'Obnovit', ], 'events' => [ 'updated' => 'Upraveno', 'created' => 'Vytvořeno', 'deleted' => 'Smazáno', 'restored' => 'Obnoveno', 'restore_successful' => 'Úspěšně obnoveno', 'restore_failed' => 'Obnovení selhalo', ], ]; ================================================ FILE: resources/lang/de/activities.php ================================================ 'Historie', 'title' => 'Historie :record', 'default_datetime_format' => 'd.m.Y, H:i:s \U\h\r', 'table' => [ 'field' => 'Feld', 'old' => 'Alt', 'new' => 'Neu', 'restore' => 'Wiederherstellen', ], 'events' => [ 'updated' => 'Aktualisiert', 'created' => 'Erstellt', 'deleted' => 'Gelöscht', 'restored' => 'Wiederhergestellt', 'restore_successful' => 'Erfolgreich wiederhergestellt', 'restore_failed' => 'Wiederherstellung fehlgeschlagen', ], ]; ================================================ FILE: resources/lang/en/activities.php ================================================ 'History', 'title' => 'History :record', 'default_datetime_format' => 'Y-m-d, H:i:s', 'table' => [ 'field' => 'Field', 'old' => 'Old', 'new' => 'New', 'restore' => 'Restore', ], 'events' => [ 'updated' => 'Updated', 'created' => 'Created', 'deleted' => 'Deleted', 'restored' => 'Restored', 'restore_successful' => 'Restored successfully', 'restore_failed' => 'Restore failed', ], ]; ================================================ FILE: resources/lang/es/activities.php ================================================ 'Historial', 'title' => 'Historial :record', 'default_datetime_format' => 'd/m/Y, H:i:s', 'table' => [ 'field' => 'Campo', 'old' => 'Anterior', 'new' => 'Nuevo', 'restore' => 'Restaurar', ], 'events' => [ 'updated' => 'Actualizado', 'created' => 'Creado', 'deleted' => 'Eliminado', 'restored' => 'Restaurado', 'restore_successful' => 'Restauración exitosa', 'restore_failed' => 'Restauración fallida', ], ]; ================================================ FILE: resources/lang/fa/activities.php ================================================ 'تاریخچه', 'title' => 'تاریخچه :record', 'default_datetime_format' => 'Y-m-d، H:i:s', 'table' => [ 'field' => 'فیلد', 'old' => 'قدیمی', 'new' => 'جدید', 'restore' => 'بازیابی', ], 'events' => [ 'updated' => 'به‌روزرسانی شد', 'created' => 'ایجاد شد', 'deleted' => 'حذف شد', 'restored' => 'بازیابی شد', 'restore_successful' => 'با موفقیت بازیابی شد', 'restore_failed' => 'بازیابی ناموفق بود', ], ]; ================================================ FILE: resources/lang/fr/activities.php ================================================ 'Historique', 'title' => 'Historique :record', 'default_datetime_format' => 'd/m/Y, H:i:s', 'table' => [ 'field' => 'Champ', 'old' => 'Ancien', 'new' => 'Nouveau', 'restore' => 'Restaurer', ], 'events' => [ 'updated' => 'Mis à jour', 'created' => 'Créé', 'deleted' => 'Effacé', 'restored' => 'Restauré', 'restore_successful' => 'Restauré avec succès', 'restore_failed' => 'Échec de la restauration', ], ]; ================================================ FILE: resources/lang/id/activities.php ================================================ 'Riwayat', 'title' => 'Riwayat :record', 'default_datetime_format' => 'd/m/Y H:i:s', 'table' => [ 'field' => 'Bagian', 'old' => 'Sebelum', 'new' => 'Sesudah', 'restore' => 'Pulihkan', ], 'events' => [ 'updated' => 'Terbarui', 'created' => 'Terbuat', 'deleted' => 'Terhapus', 'restored' => 'Terpulihkan', 'restore_successful' => 'Sukses memulihkan', 'restore_failed' => 'Gagal memulihkan', ], ]; ================================================ FILE: resources/lang/it/activities.php ================================================ 'Cronologia', 'title' => 'Cronologia :record', 'default_datetime_format' => 'd/m/Y, H:i:s', 'table' => [ 'field' => 'Campo', 'old' => 'Vecchio', 'new' => 'Nuovo', 'restore' => 'Ripristina', ], 'events' => [ 'updated' => 'Aggiornato', 'created' => 'Creato', 'deleted' => 'Eliminato', 'restored' => 'Ripristinato', 'restore_successful' => 'Ripristinato con successo', 'restore_failed' => 'Ripristino fallito', ], ]; ================================================ FILE: resources/lang/nl/activities.php ================================================ 'Geschiedenis', 'title' => 'Geschiedenis :record', 'default_datetime_format' => 'Y-m-d, H:i:s', 'table' => [ 'field' => 'Veld', 'old' => 'Oud', 'new' => 'Nieuw', 'restore' => 'Herstellen', ], 'events' => [ 'updated' => 'Bewerkt', 'created' => 'Aangemaakt', 'deleted' => 'Verwijderd', 'restored' => 'Hersteld', 'restore_successful' => 'Succesvol hersteld', 'restore_failed' => 'Herstellen mislukt', ], ]; ================================================ FILE: resources/lang/pt_BR/activities.php ================================================ 'Histórico', 'title' => 'Histórico :record', 'default_datetime_format' => 'd/m/Y H:i:s', 'table' => [ 'field' => 'Campo', 'old' => 'Antes', 'new' => 'Depois', 'restore' => 'Restaurado', ], 'events' => [ 'updated' => 'Atualizado', 'created' => 'Criado', 'deleted' => 'Excluído', 'restored' => 'Restaurado', 'restore_successful' => 'Restaurado com sucesso', 'restore_failed' => 'Falha na restauração', ], ]; ================================================ FILE: resources/lang/tr/activities.php ================================================ 'Geçmiş', 'title' => ':record Geçmişi', 'default_datetime_format' => 'd.m.Y, H:i:s', 'table' => [ 'field' => 'Alan', 'old' => 'Eski Değer', 'new' => 'Yeni Değer', 'restore' => 'Geri Yükle', ], 'events' => [ 'updated' => 'Güncellendi', 'created' => 'Oluşturuldu', 'deleted' => 'Silindi', 'restored' => 'Geri Yüklendi', 'restore_successful' => 'Başarıyla Geri Yüklendi', 'restore_failed' => 'Geri Yükleme Başarısız', ], ]; ================================================ FILE: resources/lang/zh_TW/activities.php ================================================ '歷史記錄', 'title' => '歷史記錄::record', 'default_datetime_format' => 'Y-m-d H:i:s', 'table' => [ 'field' => '欄位', 'old' => '舊值', 'new' => '新值', 'restore' => '還原', ], 'events' => [ 'updated' => '已更新', 'created' => '已建立', 'deleted' => '已刪除', 'restored' => '已還原', 'restore_successful' => '還原成功', 'restore_failed' => '還原失敗', ], ]; ================================================ FILE: resources/views/pages/list-activities-by-causer.blade.php ================================================
@foreach($this->getActivities() as $activityItem) @php /* @var \Spatie\Activitylog\Models\Activity $activityItem */ $changes = $activityItem->attribute_changes ?? collect(); $resource = $this->getSubjectResource($activityItem->subject_type); @endphp
@if ($resource) {{ $resource::getModelLabel() }}: @if ($activityItem->subject && $resource::hasRecordTitle()) @php $url = $activityItem->subject->exists ? $resource::getUrl('edit', ['record' => $activityItem->subject]) : null; @endphp @if ($url) {{ $resource::getRecordTitle($activityItem->subject) }} @else {{ $resource::getRecordTitle($activityItem->subject) }} @endif @else {{ $activityItem->subject_id }} @endif @else {{ $activityItem->subject_type }}: {{ $activityItem->subject_id }} @endif {{ __('filament-activity-log::activities.events.' . $activityItem->event) }} {{ $activityItem->created_at->format(__('filament-activity-log::activities.default_datetime_format')) }}
@if ($changes->isNotEmpty()) @foreach (data_get($changes, 'attributes', []) as $field => $change) @php $oldValue = data_get($changes, "old.{$field}", ''); $newValue = data_get($changes, "attributes.{$field}", ''); @endphp $loop->even ]) > @endforeach
{{ __('filament-activity-log::activities.table.field') }} {{ __('filament-activity-log::activities.table.old') }} {{ __('filament-activity-log::activities.table.new') }}
{{ $this->getFieldLabel($resource, $field) }} @if(is_array($oldValue))
{{ json_encode($oldValue, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) }}
@elseif (is_bool($oldValue)) {{ $oldValue ? 'true' : 'false' }} @else {{ $oldValue }} @endif
@if (is_bool($newValue)) {{ $newValue ? 'true' : 'false' }} @elseif(is_array($newValue))
{{ json_encode($newValue, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) }}
@else {{ $newValue }} @endif
@endif
@endforeach
================================================ FILE: resources/views/pages/list-activities-by-subject.blade.php ================================================ @php use \Illuminate\Support\Js; @endphp
@foreach($this->getActivities() as $activityItem) @php /* @var \Spatie\Activitylog\Models\Activity $activityItem */ $changes = $activityItem->attribute_changes ?? collect(); @endphp
@if ($activityItem->causer) @endif
{{ $activityItem->causer?->name }} {{ __('filament-activity-log::activities.events.' . $activityItem->event) }} {{ $activityItem->created_at->format(__('filament-activity-log::activities.default_datetime_format')) }}
@if ($this->canRestoreActivity() && $changes->isNotEmpty()) @lang('filament-activity-log::activities.table.restore') @endif
@if ($changes->isNotEmpty()) @foreach (data_get($changes, 'attributes', []) as $field => $change) @php $oldValue = isset($changes['old'][$field]) ? $changes['old'][$field] : ''; $newValue = isset($changes['attributes'][$field]) ? $changes['attributes'][$field] : ''; @endphp $loop->even ]) > @endforeach
{{ __('filament-activity-log::activities.table.field') }} {{ __('filament-activity-log::activities.table.old') }} {{ __('filament-activity-log::activities.table.new') }}
{{ $this->getFieldLabel($field) }} @if(is_array($oldValue))
{{ json_encode($oldValue, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) }}
@else {{ $oldValue }} @endif
@if (is_bool($newValue)) {{ $newValue ? 'true' : 'false' }} @elseif(is_array($newValue))
{{ json_encode($newValue, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) }}
@else {{ $newValue }} @endif
@endif
@endforeach
================================================ FILE: src/FilamentActivityLogServiceProvider.php ================================================ name(static::$name) ->hasViews() ->hasTranslations(); } } ================================================ FILE: src/Pages/Concerns/CanPaginate.php ================================================ put([ $this->getPerPageSessionKey() => $this->getRecordsPerPage(), ]); $this->resetPage(); } protected function paginateQuery(Builder $query): Paginator|CursorPaginator { $perPage = $this->getRecordsPerPage(); $mode = $this->getPaginationMode(); if ($mode === PaginationMode::Simple) { return $query->simplePaginate( perPage: ($perPage === 'all') ? $query->toBase()->getCountForPagination() : $perPage, pageName: $this->getPaginationPageName(), ); } if ($mode === PaginationMode::Cursor) { return $query->cursorPaginate( perPage: ($perPage === 'all') ? $query->toBase()->getCountForPagination() : $perPage, cursorName: $this->getPaginationPageName(), ); } $total = $query->toBase()->getCountForPagination(); /** @var LengthAwarePaginator $records */ $records = $query->paginate( perPage: ($perPage === 'all') ? $total : $perPage, pageName: $this->getPaginationPageName(), total: $total, ); return $records->onEachSide(0); } public function getRecordsPerPage(): int|string|null { return $this->recordsPerPage; } public function getTablePage(): int { return $this->getPage($this->getPaginationPageName()); } public function getDefaultRecordsPerPageSelectOption(): int|string { $option = session()->get( $this->getPerPageSessionKey(), $this->defaultRecordsPerPageSelectOption, ); $pageOptions = $this->getRecordsPerPageSelectOptions(); if (in_array($option, $pageOptions)) { return $option; } session()->remove($this->getPerPageSessionKey()); return $pageOptions[0]; } public function getPaginationPageName(): string { return 'recordsPerPage'; } public function getPerPageSessionKey(): string { $name = md5($this::class); return "pages.{$name}_per_page"; } /** * @return array | null */ protected function getRecordsPerPageSelectOptions(): ?array { return [10, 25, 50]; } } ================================================ FILE: src/Pages/ListActivities.php ================================================ > */ protected static array $fieldLabelMaps = []; public function mount($record) { $this->record = $this->resolveRecord($record); $this->recordsPerPage = $this->getDefaultRecordsPerPageSelectOption(); } public function getBreadcrumb(): string { return static::$breadcrumb ?? __('filament-activity-log::activities.breadcrumb'); } public function getTitle(): string { return __('filament-activity-log::activities.title', ['record' => $this->getRecordTitle()]); } public function getActivities() { return $this->paginateQuery( $this->record->activitiesAsCauser()->with('subject')->latest()->getQuery() ); } public function getPaginationMode(): PaginationMode { return PaginationMode::Default; } public function getSubjectResource(string $subjectType): ?string { $modelClass = Relation::getMorphedModel($subjectType) ?? $subjectType; return Filament::getModelResource($modelClass); } public function getFieldLabel(?string $resourceClass, string $name): string { if ($resourceClass === null) { return $name; } static::$fieldLabelMaps[$resourceClass] ??= $this->createFieldLabelMap($resourceClass); return static::$fieldLabelMaps[$resourceClass][$name] ?? $name; } /** @param class-string $resourceClass */ protected function createFieldLabelMap(string $resourceClass): Collection { $schema = $resourceClass::form(new Schema($this)); $components = collect($schema->getComponents()); $extracted = collect(); while (($component = $components->shift()) !== null) { if ($component instanceof Field || $component instanceof MorphToSelect) { $extracted->push($component); continue; } $children = $component->getChildComponents(); if (count($children) > 0) { $components = $components->merge($children); continue; } $extracted->push($component); } return $extracted ->filter(fn ($field) => $field instanceof Field) ->mapWithKeys(fn (Field $field) => [ $field->getName() => $field->getLabel(), ]); } } ================================================ FILE: src/Pages/ListActivitiesBySubject.php ================================================ record = $this->resolveRecord($record); $this->recordsPerPage = $this->getDefaultRecordsPerPageSelectOption(); } public function getBreadcrumb(): string { return static::$breadcrumb ?? __('filament-activity-log::activities.breadcrumb'); } public function getTitle(): string { return __('filament-activity-log::activities.title', ['record' => $this->getRecordTitle()]); } public function getActivities() { return $this->paginateQuery( $this->record->activitiesAsSubject()->with('causer')->latest()->getQuery() ); } public function getPaginationMode(): PaginationMode { return PaginationMode::Default; } public function getFieldLabel(string $name): string { static::$fieldLabelMap ??= $this->createFieldLabelMap(); return static::$fieldLabelMap[$name] ?? $name; } protected function createFieldLabelMap(): Collection { $schema = static::getResource()::form(new Schema($this)); $components = collect($schema->getComponents()); $extracted = collect(); while (($component = $components->shift()) !== null) { if ($component instanceof Field || $component instanceof MorphToSelect) { $extracted->push($component); continue; } $children = $component->getChildComponents(); if (count($children) > 0) { $components = $components->merge($children); continue; } $extracted->push($component); } return $extracted ->filter(fn ($field) => $field instanceof Field) ->mapWithKeys(fn (Field $field) => [ $field->getName() => $field->getLabel(), ]); } public function canRestoreActivity(): bool { return static::getResource()::canRestore($this->record); } public function restoreActivity(int|string $key) { if (! $this->canRestoreActivity()) { abort(403); } $activity = $this->record->activitiesAsSubject() ->whereKey($key) ->first(); $oldAttributes = data_get($activity, 'attribute_changes.old'); if ($oldAttributes === null) { $this->sendRestoreFailureNotification(); return; } try { $this->record->update($oldAttributes); $this->sendRestoreSuccessNotification(); } catch (Exception $e) { $this->sendRestoreFailureNotification($e->getMessage()); } } protected function sendRestoreSuccessNotification(): Notification { return Notification::make() ->title(__('filament-activity-log::activities.events.restore_successful')) ->success() ->send(); } protected function sendRestoreFailureNotification(?string $message = null): Notification { return Notification::make() ->title(__('filament-activity-log::activities.events.restore_failed')) ->body($message) ->danger() ->send(); } }