From ff40fbc73de232022ae4d03f24b707716804a9b6 Mon Sep 17 00:00:00 2001 From: Dennis Koch Date: Sat, 26 Aug 2023 12:58:17 +0200 Subject: [PATCH 1/4] ListActions for Filament v2 --- readme.md | 79 ++++++++++++++-- resources/views/pages/list-actions.blade.php | 82 +++++++++++++++++ src/Pages/ListActions.php | 94 ++++++++++++++++++++ 3 files changed, 248 insertions(+), 7 deletions(-) create mode 100644 resources/views/pages/list-actions.blade.php create mode 100644 src/Pages/ListActions.php diff --git a/readme.md b/readme.md index 7c91938..ac6d9e6 100644 --- a/readme.md +++ b/readme.md @@ -7,7 +7,7 @@ ![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 a page to the Filament Admin panel to view the activity log generated by [`spatie/laravel-activitylog`](https://github.com/spatie/laravel-activitylog). +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) @@ -21,25 +21,90 @@ Install via Composer. composer require pxlrbt/filament-activity-log ``` -> **Warning** +> **Info** > This plugin only offers a page to show activities related to your model. You need [`spatie/laravel-activitylog`](https://github.com/spatie/laravel-activitylog) installed and configured for it to work. It is important you are using the `LogsActivity` trait as per [Spatie's docs](https://spatie.be/docs/laravel-activitylog/v4/advanced-usage/logging-model-events) for this work as we use the '->activities()' method of the trait. ## 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. -### Create a page +## Listing activities for a resource -Create the page inside your resources `Pages/` directory. Replace `UserResource` with your resource. +![Screenshot](./.github/resources/screenshot.png) + +### Setup spatie/laravel-activitylog + +Make sure your resource model uses the `LogsActivity` trait. ```php Pages\ListOrders::route('/'), + 'create' => Pages\CreateOrder::route('/create'), + 'activities' => Pages\ListOrderActivites::route('/{record}/activities'), + 'edit' => Pages\EditOrder::route('/{record}/edit'), + ]; +} +``` + + +## Listing activities for a user + +### Setup spatie/laravel-activitylog + +Make sure your user model uses the `CausesActivity` trait. +```php + Pages\ListUsers::route('/'), 'create' => Pages\CreateUser::route('/create'), - 'activities' => Pages\ListUserActivites::route('/{record}/activities'), + 'activities' => Pages\ListUserActionsActivites::route('/{record}/activities'), 'edit' => Pages\EditUser::route('/{record}/edit'), ]; } diff --git a/resources/views/pages/list-actions.blade.php b/resources/views/pages/list-actions.blade.php new file mode 100644 index 0000000..e069c69 --- /dev/null +++ b/resources/views/pages/list-actions.blade.php @@ -0,0 +1,82 @@ + +
+ @foreach($this->getActivities() as $activityItem) +
config('filament.dark_mode'), + ])> +
+
+
+ @php + $resource = \Filament\Facades\Filament::getModelResource($activityItem->subject_type); + @endphp + + {{ $resource::getModelLabel() }}: + + @if ($resource::hasRecordTitle()) + {{ $resource::getRecordTitle($activityItem->subject) }} + @else + {{ $activityItem->subject_id }} + @endif +
+
+ @lang('filament-activity-log::activities.events.' . $activityItem->event) + {{ $activityItem->created_at->format(__('filament-activity-log::activities.default_datetime_format')) }} +
+
+
+ + + + + + + @lang('filament-activity-log::activities.table.field') + + + @lang('filament-activity-log::activities.table.old') + + + @lang('filament-activity-log::activities.table.new') + + + @php + /* @var \Spatie\Activitylog\Models\Activity $activityItem */ + $changes = $activityItem->getChangesAttribute(); + @endphp + @foreach(data_get($changes, 'attributes', []) as $field => $change) + @php + $oldValue = data_get($changes, "old.{$field}"); + $newValue = data_get($changes, "attributes.{$field}"); + @endphp + $loop->even])> + + {{ $this->getFieldLabel($field) }} + + + @if(is_array($oldValue)) +
{{ json_encode($oldValue, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) }}
+ @else + {{ $oldValue }} + @endif +
+ + @if(is_array($newValue)) +
{{ json_encode($newValue, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) }}
+ @else + {{ $newValue }} + @endif +
+
+ @endforeach +
+
+ @endforeach + + +
+
diff --git a/src/Pages/ListActions.php b/src/Pages/ListActions.php new file mode 100644 index 0000000..65850d2 --- /dev/null +++ b/src/Pages/ListActions.php @@ -0,0 +1,94 @@ +record = $this->resolveRecord($record); + } + + public function getTitle(): string + { + return __('filament-activity-log::activities.title', ['record' => $this->getRecordTitle()]); + } + + public function getActivities() + { + return $this->paginateTableQuery( + $this->record->actions()->latest()->getQuery() + ); + } + + public function getFieldLabel(string $name): string + { + static::$fieldLabelMap ??= $this->createFieldLabelMap(); + + return static::$fieldLabelMap[$name] ?? $name; + } + + protected function createFieldLabelMap(): Collection + { + $form = static::getResource()::form(new Form()); + + $components = collect($form->getSchema()); + $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(), + ]); + } + + protected function getIdentifiedTableQueryStringPropertyNameFor(string $property): string + { + return $property; + } + + protected function getDefaultTableRecordsPerPageSelectOption(): int + { + return 10; + } + + protected function getTableRecordsPerPageSelectOptions(): array + { + return [10, 25, 50]; + } +} From 422aad568ea113e6d3d648b29476f3454176e659 Mon Sep 17 00:00:00 2001 From: Dennis Koch Date: Tue, 28 Apr 2026 14:07:00 +0200 Subject: [PATCH 2/4] Add ListActivitiesByCauser page --- readme.md | 46 ++++-- resources/views/pages/list-actions.blade.php | 82 ---------- .../pages/list-activities-by-causer.blade.php | 107 +++++++++++++ ...p => list-activities-by-subject.blade.php} | 2 +- src/Pages/ListActions.php | 94 ------------ src/Pages/ListActivities.php | 143 +---------------- src/Pages/ListActivitiesByCauser.php | 109 +++++++++++++ src/Pages/ListActivitiesBySubject.php | 145 ++++++++++++++++++ 8 files changed, 395 insertions(+), 333 deletions(-) delete mode 100644 resources/views/pages/list-actions.blade.php create mode 100644 resources/views/pages/list-activities-by-causer.blade.php rename resources/views/pages/{list-activities.blade.php => list-activities-by-subject.blade.php} (98%) delete mode 100644 src/Pages/ListActions.php create mode 100644 src/Pages/ListActivitiesByCauser.php create mode 100644 src/Pages/ListActivitiesBySubject.php diff --git a/readme.md b/readme.md index ac6d9e6..03ec0b5 100644 --- a/readme.md +++ b/readme.md @@ -15,23 +15,25 @@ This package adds pages to the Filament Admin panel to view the activity log gen Install via Composer. -**Requires PHP 8.0 and Filament 2.0** +**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 ``` > **Info** -> This plugin only offers a page to show activities related to your model. You need [`spatie/laravel-activitylog`](https://github.com/spatie/laravel-activitylog) installed and configured for it to work. It is important you are using the `LogsActivity` trait as per [Spatie's docs](https://spatie.be/docs/laravel-activitylog/v4/advanced-usage/logging-model-events) for this work as we use the '->activities()' method of the trait. +> 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. ## 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 resource +## 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. @@ -39,28 +41,34 @@ 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. @@ -71,40 +79,44 @@ public static function getPages(): array return [ 'index' => Pages\ListOrders::route('/'), 'create' => Pages\CreateOrder::route('/create'), - 'activities' => Pages\ListOrderActivites::route('/{record}/activities'), + 'activities' => Pages\ListOrderActivities::route('/{record}/activities'), 'edit' => Pages\EditOrder::route('/{record}/edit'), ]; } ``` -## Listing activities for a user +## 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\ListUserActionsActivites::route('/{record}/activities'), + 'activities' => Pages\ListUserActivities::route('/{record}/activities'), 'edit' => Pages\EditUser::route('/{record}/edit'), ]; } diff --git a/resources/views/pages/list-actions.blade.php b/resources/views/pages/list-actions.blade.php deleted file mode 100644 index e069c69..0000000 --- a/resources/views/pages/list-actions.blade.php +++ /dev/null @@ -1,82 +0,0 @@ - -
- @foreach($this->getActivities() as $activityItem) -
config('filament.dark_mode'), - ])> -
-
-
- @php - $resource = \Filament\Facades\Filament::getModelResource($activityItem->subject_type); - @endphp - - {{ $resource::getModelLabel() }}: - - @if ($resource::hasRecordTitle()) - {{ $resource::getRecordTitle($activityItem->subject) }} - @else - {{ $activityItem->subject_id }} - @endif -
-
- @lang('filament-activity-log::activities.events.' . $activityItem->event) - {{ $activityItem->created_at->format(__('filament-activity-log::activities.default_datetime_format')) }} -
-
-
- - - - - - - @lang('filament-activity-log::activities.table.field') - - - @lang('filament-activity-log::activities.table.old') - - - @lang('filament-activity-log::activities.table.new') - - - @php - /* @var \Spatie\Activitylog\Models\Activity $activityItem */ - $changes = $activityItem->getChangesAttribute(); - @endphp - @foreach(data_get($changes, 'attributes', []) as $field => $change) - @php - $oldValue = data_get($changes, "old.{$field}"); - $newValue = data_get($changes, "attributes.{$field}"); - @endphp - $loop->even])> - - {{ $this->getFieldLabel($field) }} - - - @if(is_array($oldValue)) -
{{ json_encode($oldValue, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) }}
- @else - {{ $oldValue }} - @endif -
- - @if(is_array($newValue)) -
{{ json_encode($newValue, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) }}
- @else - {{ $newValue }} - @endif -
-
- @endforeach -
-
- @endforeach - - -
-
diff --git a/resources/views/pages/list-activities-by-causer.blade.php b/resources/views/pages/list-activities-by-causer.blade.php new file mode 100644 index 0000000..5e841ba --- /dev/null +++ b/resources/views/pages/list-activities-by-causer.blade.php @@ -0,0 +1,107 @@ + +
+ @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 + + +
+
diff --git a/resources/views/pages/list-activities.blade.php b/resources/views/pages/list-activities-by-subject.blade.php similarity index 98% rename from resources/views/pages/list-activities.blade.php rename to resources/views/pages/list-activities-by-subject.blade.php index d0c3533..569b456 100644 --- a/resources/views/pages/list-activities.blade.php +++ b/resources/views/pages/list-activities-by-subject.blade.php @@ -7,7 +7,7 @@ @php /* @var \Spatie\Activitylog\Models\Activity $activityItem */ - $changes = $activityItem->getChangesAttribute(); + $changes = $activityItem->attribute_changes ?? collect(); @endphp
record = $this->resolveRecord($record); - } - - public function getTitle(): string - { - return __('filament-activity-log::activities.title', ['record' => $this->getRecordTitle()]); - } - - public function getActivities() - { - return $this->paginateTableQuery( - $this->record->actions()->latest()->getQuery() - ); - } - - public function getFieldLabel(string $name): string - { - static::$fieldLabelMap ??= $this->createFieldLabelMap(); - - return static::$fieldLabelMap[$name] ?? $name; - } - - protected function createFieldLabelMap(): Collection - { - $form = static::getResource()::form(new Form()); - - $components = collect($form->getSchema()); - $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(), - ]); - } - - protected function getIdentifiedTableQueryStringPropertyNameFor(string $property): string - { - return $property; - } - - protected function getDefaultTableRecordsPerPageSelectOption(): int - { - return 10; - } - - protected function getTableRecordsPerPageSelectOptions(): array - { - return [10, 25, 50]; - } -} diff --git a/src/Pages/ListActivities.php b/src/Pages/ListActivities.php index daf2c4c..2b7002a 100644 --- a/src/Pages/ListActivities.php +++ b/src/Pages/ListActivities.php @@ -2,144 +2,9 @@ namespace pxlrbt\FilamentActivityLog\Pages; -use Exception; -use Filament\Forms\Components\Field; -use Filament\Forms\Components\MorphToSelect; -use Filament\Forms\Contracts\HasForms; -use Filament\Notifications\Notification; -use Filament\Pages\Concerns\InteractsWithFormActions; -use Filament\Resources\Pages\Concerns\InteractsWithRecord; -use Filament\Resources\Pages\Page; -use Filament\Schemas\Schema; -use Filament\Tables\Enums\PaginationMode; -use Illuminate\Support\Collection; -use Livewire\WithPagination; -use pxlrbt\FilamentActivityLog\Pages\Concerns\CanPaginate; - -abstract class ListActivities extends Page implements HasForms +/** + * @deprecated Use ListActivitiesBySubject directly + */ +abstract class ListActivities extends ListActivitiesBySubject { - use CanPaginate; - use InteractsWithFormActions; - use InteractsWithRecord; - use WithPagination; - - protected string $view = 'filament-activity-log::pages.list-activities'; - - protected static Collection $fieldLabelMap; - - 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->activities()->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->activities() - ->whereKey($key) - ->first(); - - $oldProperties = data_get($activity, 'properties.old'); - - if ($oldProperties === null) { - $this->sendRestoreFailureNotification(); - - return; - } - - try { - $this->record->update($oldProperties); - - $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(); - } } diff --git a/src/Pages/ListActivitiesByCauser.php b/src/Pages/ListActivitiesByCauser.php new file mode 100644 index 0000000..c38de3e --- /dev/null +++ b/src/Pages/ListActivitiesByCauser.php @@ -0,0 +1,109 @@ +> */ + 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(), + ]); + } +} diff --git a/src/Pages/ListActivitiesBySubject.php b/src/Pages/ListActivitiesBySubject.php new file mode 100644 index 0000000..e750b13 --- /dev/null +++ b/src/Pages/ListActivitiesBySubject.php @@ -0,0 +1,145 @@ +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(); + + $oldProperties = data_get($activity, 'attribute_changes.old'); + + if ($oldProperties === null) { + $this->sendRestoreFailureNotification(); + + return; + } + + try { + $this->record->update($oldProperties); + + $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(); + } +} From fd4a5a910416e1a40e2e465e656ad1a3b7aabde9 Mon Sep 17 00:00:00 2001 From: pxlrbt Date: Tue, 28 Apr 2026 12:07:51 +0000 Subject: [PATCH 3/4] Apply style changes --- src/Pages/ListActivities.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Pages/ListActivities.php b/src/Pages/ListActivities.php index 2b7002a..4f91f31 100644 --- a/src/Pages/ListActivities.php +++ b/src/Pages/ListActivities.php @@ -5,6 +5,4 @@ /** * @deprecated Use ListActivitiesBySubject directly */ -abstract class ListActivities extends ListActivitiesBySubject -{ -} +abstract class ListActivities extends ListActivitiesBySubject {} From f7361bc97ea6a3cdb83fd57112f44cf4b78103ee Mon Sep 17 00:00:00 2001 From: Dennis Koch Date: Tue, 28 Apr 2026 14:11:43 +0200 Subject: [PATCH 4/4] Merge main --- src/Pages/ListActivitiesBySubject.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Pages/ListActivitiesBySubject.php b/src/Pages/ListActivitiesBySubject.php index e750b13..c7301c4 100644 --- a/src/Pages/ListActivitiesBySubject.php +++ b/src/Pages/ListActivitiesBySubject.php @@ -109,16 +109,16 @@ public function restoreActivity(int|string $key) ->whereKey($key) ->first(); - $oldProperties = data_get($activity, 'attribute_changes.old'); + $oldAttributes = data_get($activity, 'attribute_changes.old'); - if ($oldProperties === null) { + if ($oldAttributes === null) { $this->sendRestoreFailureNotification(); return; } try { - $this->record->update($oldProperties); + $this->record->update($oldAttributes); $this->sendRestoreSuccessNotification(); } catch (Exception $e) {