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
================================================

# Filament Activity Log
[](https://packagist.org/packages/pxlrbt/filament-activity-log)
[](LICENSE.md)

[](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).

## 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

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
<?php
use Spatie\Activitylog\Models\Concerns\LogsActivity;
class Order extends Model
{
use LogsActivity;
}
```
### Create a ListActivitiesBySubject page
Create the page inside your resource's `Pages/` directory. Replace `OrderResource` with your resource.
```php
<?php
namespace App\Filament\Resources\Orders\Pages;
use pxlrbt\FilamentActivityLog\Pages\ListActivitiesBySubject;
class ListOrderActivities extends ListActivitiesBySubject
{
protected static string $resource = OrderResource::class;
}
```
> **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
<?php
use Spatie\Activitylog\Models\Concerns\CausesActivity;
class User extends Authenticatable implements FilamentUser
{
use CausesActivity;
}
```
### Create a ListActivitiesByCauser page
Create the page inside your `UserResource`'s `Pages/` directory.
```php
<?php
namespace App\Filament\Resources\Users\Pages;
use pxlrbt\FilamentActivityLog\Pages\ListActivitiesByCauser;
class ListUserActivities extends ListActivitiesByCauser
{
protected static string $resource = UserResource::class;
}
```
See https://spatie.be/docs/laravel-activitylog/v5/advanced-usage/logging-model-events for more information on the topic.
### Register the page
Add the page to your resource's `getPages()` method.
```php
public static function getPages(): array
{
return [
'index' => 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
================================================
<?php
return [
'breadcrumb' => 'سجل عمليات',
'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
================================================
<?php
return [
'breadcrumb' => 'نرخی پێشوو',
'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
================================================
<?php
return [
'breadcrumb' => '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
================================================
<?php
return [
'breadcrumb' => '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
================================================
<?php
return [
'breadcrumb' => '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
================================================
<?php
return [
'breadcrumb' => '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
================================================
<?php
return [
'breadcrumb' => 'تاریخچه',
'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
================================================
<?php
return [
'breadcrumb' => '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
================================================
<?php
return [
'breadcrumb' => '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
================================================
<?php
return [
'breadcrumb' => '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
================================================
<?php
return [
'breadcrumb' => '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
================================================
<?php
return [
'breadcrumb' => '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
================================================
<?php
return [
'breadcrumb' => '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
================================================
<?php
return [
'breadcrumb' => '歷史記錄',
'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
================================================
<x-filament-panels::page>
<div class="space-y-6">
@foreach($this->getActivities() as $activityItem)
@php
/* @var \Spatie\Activitylog\Models\Activity $activityItem */
$changes = $activityItem->attribute_changes ?? collect();
$resource = $this->getSubjectResource($activityItem->subject_type);
@endphp
<div @class([
'p-2 space-y-2 bg-white rounded-xl shadow',
'dark:border-gray-600 dark:bg-gray-800',
])>
<div class="p-2">
<div class="flex justify-between">
<div class="flex flex-col text-start">
<span class="font-bold">
@if ($resource)
{{ $resource::getModelLabel() }}:
@if ($activityItem->subject && $resource::hasRecordTitle())
@php
$url = $activityItem->subject->exists
? $resource::getUrl('edit', ['record' => $activityItem->subject])
: null;
@endphp
@if ($url)
<a href="{{ $url }}" class="underline">{{ $resource::getRecordTitle($activityItem->subject) }}</a>
@else
{{ $resource::getRecordTitle($activityItem->subject) }}
@endif
@else
{{ $activityItem->subject_id }}
@endif
@else
{{ $activityItem->subject_type }}: {{ $activityItem->subject_id }}
@endif
</span>
<span class="text-xs text-gray-500">
{{ __('filament-activity-log::activities.events.' . $activityItem->event) }} {{ $activityItem->created_at->format(__('filament-activity-log::activities.default_datetime_format')) }}
</span>
</div>
</div>
</div>
@if ($changes->isNotEmpty())
<table class="fi-ta-table w-full overflow-hidden text-sm">
<thead>
<th class="fi-ta-header-cell">
{{ __('filament-activity-log::activities.table.field') }}
</th>
<th class="fi-ta-header-cell">
{{ __('filament-activity-log::activities.table.old') }}
</th>
<th class="fi-ta-header-cell">
{{ __('filament-activity-log::activities.table.new') }}
</th>
</thead>
<tbody>
@foreach (data_get($changes, 'attributes', []) as $field => $change)
@php
$oldValue = data_get($changes, "old.{$field}", '');
$newValue = data_get($changes, "attributes.{$field}", '');
@endphp
<tr
@class([
'fi-ta-row',
'bg-gray-100/30' => $loop->even
])
>
<td class="fi-ta-cell px-4 py-2 align-top sm:first-of-type:ps-6 sm:last-of-type:pe-6" width="20%">
{{ $this->getFieldLabel($resource, $field) }}
</td>
<td width="40%" class="fi-ta-cell px-4 py-2 align-top break-all whitespace-normal">
@if(is_array($oldValue))
<pre class="text-xs text-gray-500">{{ json_encode($oldValue, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) }}</pre>
@elseif (is_bool($oldValue))
<span class="text-xs text-gray-500">{{ $oldValue ? 'true' : 'false' }}</span>
@else
{{ $oldValue }}
@endif
</td>
<td width="40%" class="fi-ta-cell px-4 py-2 align-top break-all whitespace-normal">
@if (is_bool($newValue))
<span class="text-xs text-gray-500">{{ $newValue ? 'true' : 'false' }}</span>
@elseif(is_array($newValue))
<pre class="text-xs text-gray-500">{{ json_encode($newValue, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) }}</pre>
@else
{{ $newValue }}
@endif
</td>
</tr>
@endforeach
</tbody>
</table>
@endif
</div>
@endforeach
<x-filament::pagination
currentPageOptionProperty="recordsPerPage"
:page-options="$this->getRecordsPerPageSelectOptions()"
:paginator="$this->getActivities()"
/>
</div>
</x-filament-panels::page>
================================================
FILE: resources/views/pages/list-activities-by-subject.blade.php
================================================
@php
use \Illuminate\Support\Js;
@endphp
<x-filament-panels::page>
<div class="space-y-6">
@foreach($this->getActivities() as $activityItem)
@php
/* @var \Spatie\Activitylog\Models\Activity $activityItem */
$changes = $activityItem->attribute_changes ?? collect();
@endphp
<div @class([
'p-2 space-y-2 bg-white rounded-xl shadow',
'dark:border-gray-600 dark:bg-gray-800',
])>
<div class="p-2">
<div class="flex justify-between">
<div class="flex items-center gap-4">
@if ($activityItem->causer)
<x-filament-panels::avatar.user :user="$activityItem->causer" class="!w-7 !h-7"/>
@endif
<div class="flex flex-col text-start">
<span class="font-bold">{{ $activityItem->causer?->name }}</span>
<span class="text-xs text-gray-500">
{{ __('filament-activity-log::activities.events.' . $activityItem->event) }} {{ $activityItem->created_at->format(__('filament-activity-log::activities.default_datetime_format')) }}
</span>
</div>
</div>
<div class="flex flex-col text-xs text-gray-500 justify-end">
@if ($this->canRestoreActivity() && $changes->isNotEmpty())
<x-filament::button
tag="button"
icon="heroicon-o-arrow-path-rounded-square"
labeled-from="sm"
color="gray"
class="right"
wire:click="restoreActivity({{ Js::from($activityItem->getKey()) }})"
>
@lang('filament-activity-log::activities.table.restore')
</x-filament::button>
@endif
</div>
</div>
</div>
@if ($changes->isNotEmpty())
<table class="fi-ta-table w-full overflow-hidden text-sm">
<thead>
<th class="fi-ta-header-cell">
{{ __('filament-activity-log::activities.table.field') }}
</th>
<th class="fi-ta-header-cell">
{{ __('filament-activity-log::activities.table.old') }}
</th>
<th class="fi-ta-header-cell">
{{ __('filament-activity-log::activities.table.new') }}
</th>
</thead>
<tbody>
@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
<tr
@class([
'fi-ta-row',
'bg-gray-100/30' => $loop->even
])
>
<td class="fi-ta-cell px-4 py-2 align-top sm:first-of-type:ps-6 sm:last-of-type:pe-6" width="20%">
{{ $this->getFieldLabel($field) }}
</td>
<td width="40%" class="fi-ta-cell px-4 py-2 align-top break-all whitespace-normal">
@if(is_array($oldValue))
<pre class="text-xs text-gray-500">{{ json_encode($oldValue, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) }}</pre>
@else
{{ $oldValue }}
@endif
</td>
<td width="40%" class="fi-ta-cell px-4 py-2 align-top break-all whitespace-normal">
@if (is_bool($newValue))
<span class="text-xs text-gray-500">{{ $newValue ? 'true' : 'false' }}</span>
@elseif(is_array($newValue))
<pre class="text-xs text-gray-500">{{ json_encode($newValue, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) }}</pre>
@else
{{ $newValue }}
@endif
</td>
</tr>
@endforeach
</tbody>
</table>
@endif
</div>
@endforeach
<x-filament::pagination
currentPageOptionProperty="recordsPerPage"
:page-options="$this->getRecordsPerPageSelectOptions()"
:paginator="$this->getActivities()"
/>
</div>
</x-filament-panels::page>
================================================
FILE: src/FilamentActivityLogServiceProvider.php
================================================
<?php
namespace pxlrbt\FilamentActivityLog;
use Spatie\LaravelPackageTools\Package;
use Spatie\LaravelPackageTools\PackageServiceProvider;
class FilamentActivityLogServiceProvider extends PackageServiceProvider
{
public static string $name = 'filament-activity-log';
public function configurePackage(Package $package): void
{
$package
->name(static::$name)
->hasViews()
->hasTranslations();
}
}
================================================
FILE: src/Pages/Concerns/CanPaginate.php
================================================
<?php
namespace pxlrbt\FilamentActivityLog\Pages\Concerns;
use Filament\Tables\Enums\PaginationMode;
use Illuminate\Contracts\Pagination\CursorPaginator;
use Illuminate\Contracts\Pagination\Paginator;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Pagination\LengthAwarePaginator;
trait CanPaginate
{
/**
* @var int | string | null
*/
public $recordsPerPage = null;
protected int|string|null $defaultRecordsPerPageSelectOption = null;
public function updatedRecordsPerPage(): void
{
session()->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<int | string> | null
*/
protected function getRecordsPerPageSelectOptions(): ?array
{
return [10, 25, 50];
}
}
================================================
FILE: src/Pages/ListActivities.php
================================================
<?php
namespace pxlrbt\FilamentActivityLog\Pages;
/**
* @deprecated Use ListActivitiesBySubject directly
*/
abstract class ListActivities extends ListActivitiesBySubject {}
================================================
FILE: src/Pages/ListActivitiesByCauser.php
================================================
<?php
namespace pxlrbt\FilamentActivityLog\Pages;
use Filament\Facades\Filament;
use Filament\Forms\Components\Field;
use Filament\Forms\Components\MorphToSelect;
use Filament\Forms\Contracts\HasForms;
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\Database\Eloquent\Relations\Relation;
use Illuminate\Support\Collection;
use Livewire\WithPagination;
use pxlrbt\FilamentActivityLog\Pages\Concerns\CanPaginate;
abstract class ListActivitiesByCauser extends Page implements HasForms
{
use CanPaginate;
use InteractsWithFormActions;
use InteractsWithRecord;
use WithPagination;
protected string $view = 'filament-activity-log::pages.list-activities-by-causer';
/** @var array<class-string, Collection<string, string>> */
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
================================================
<?php
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 ListActivitiesBySubject extends Page implements HasForms
{
use CanPaginate;
use InteractsWithFormActions;
use InteractsWithRecord;
use WithPagination;
protected string $view = 'filament-activity-log::pages.list-activities-by-subject';
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->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();
}
}
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
SYMBOL INDEX (33 symbols across 5 files)
FILE: src/FilamentActivityLogServiceProvider.php
class FilamentActivityLogServiceProvider (line 8) | class FilamentActivityLogServiceProvider extends PackageServiceProvider
method configurePackage (line 12) | public function configurePackage(Package $package): void
FILE: src/Pages/Concerns/CanPaginate.php
type CanPaginate (line 11) | trait CanPaginate
method updatedRecordsPerPage (line 20) | public function updatedRecordsPerPage(): void
method paginateQuery (line 29) | protected function paginateQuery(Builder $query): Paginator|CursorPagi...
method getRecordsPerPage (line 61) | public function getRecordsPerPage(): int|string|null
method getTablePage (line 66) | public function getTablePage(): int
method getDefaultRecordsPerPageSelectOption (line 71) | public function getDefaultRecordsPerPageSelectOption(): int|string
method getPaginationPageName (line 89) | public function getPaginationPageName(): string
method getPerPageSessionKey (line 94) | public function getPerPageSessionKey(): string
method getRecordsPerPageSelectOptions (line 104) | protected function getRecordsPerPageSelectOptions(): ?array
FILE: src/Pages/ListActivities.php
class ListActivities (line 8) | abstract class ListActivities extends ListActivitiesBySubject {}
FILE: src/Pages/ListActivitiesByCauser.php
class ListActivitiesByCauser (line 19) | abstract class ListActivitiesByCauser extends Page implements HasForms
method mount (line 31) | public function mount($record)
method getBreadcrumb (line 37) | public function getBreadcrumb(): string
method getTitle (line 42) | public function getTitle(): string
method getActivities (line 47) | public function getActivities()
method getPaginationMode (line 54) | public function getPaginationMode(): PaginationMode
method getSubjectResource (line 59) | public function getSubjectResource(string $subjectType): ?string
method getFieldLabel (line 66) | public function getFieldLabel(?string $resourceClass, string $name): s...
method createFieldLabelMap (line 78) | protected function createFieldLabelMap(string $resourceClass): Collection
FILE: src/Pages/ListActivitiesBySubject.php
class ListActivitiesBySubject (line 19) | abstract class ListActivitiesBySubject extends Page implements HasForms
method mount (line 30) | public function mount($record)
method getBreadcrumb (line 36) | public function getBreadcrumb(): string
method getTitle (line 41) | public function getTitle(): string
method getActivities (line 46) | public function getActivities()
method getPaginationMode (line 53) | public function getPaginationMode(): PaginationMode
method getFieldLabel (line 58) | public function getFieldLabel(string $name): string
method createFieldLabelMap (line 65) | protected function createFieldLabelMap(): Collection
method canRestoreActivity (line 97) | public function canRestoreActivity(): bool
method restoreActivity (line 102) | public function restoreActivity(int|string $key)
method sendRestoreSuccessNotification (line 129) | protected function sendRestoreSuccessNotification(): Notification
method sendRestoreFailureNotification (line 137) | protected function sendRestoreFailureNotification(?string $message = n...
Condensed preview — 28 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (42K chars).
[
{
"path": ".github/FUNDING.yml",
"chars": 15,
"preview": "github: pxlrbt\n"
},
{
"path": ".github/workflows/code-style.yml",
"chars": 743,
"preview": "name: Code Style\non:\n pull_request:\n push:\n branches:\n - main\n\njobs:\n php-cs-fixer:\n name: Run Laravel Pin"
},
{
"path": ".gitignore",
"chars": 106,
"preview": "/vendor\ncomposer.phar\ncomposer.lock\n.DS_Store\n.idea\nphpunit.xml\n.phpunit.result.cache\n.php-cs-fixer.cache\n"
},
{
"path": "LICENSE.md",
"chars": 1072,
"preview": "MIT License\n\nCopyright (c) [2022] [Dennis Koch]\n\nPermission is hereby granted, free of charge, to any person obtaining a"
},
{
"path": "composer.json",
"chars": 899,
"preview": "{\n \"name\": \"pxlrbt/filament-activity-log\",\n \"description\": \"Spatie's Laravel Activity Log integrated into Filament"
},
{
"path": "readme.md",
"chars": 5679,
"preview": "\n\n# Filament Activity Log\n\n[ as $activityItem)\n\n "
},
{
"path": "resources/views/pages/list-activities-by-subject.blade.php",
"chars": 5658,
"preview": "@php\n use \\Illuminate\\Support\\Js;\n@endphp\n<x-filament-panels::page>\n <div class=\"space-y-6\">\n @foreach($thi"
},
{
"path": "src/FilamentActivityLogServiceProvider.php",
"chars": 459,
"preview": "<?php\n\nnamespace pxlrbt\\FilamentActivityLog;\n\nuse Spatie\\LaravelPackageTools\\Package;\nuse Spatie\\LaravelPackageTools\\Pac"
},
{
"path": "src/Pages/Concerns/CanPaginate.php",
"chars": 2857,
"preview": "<?php\n\nnamespace pxlrbt\\FilamentActivityLog\\Pages\\Concerns;\n\nuse Filament\\Tables\\Enums\\PaginationMode;\nuse Illuminate\\Co"
},
{
"path": "src/Pages/ListActivities.php",
"chars": 177,
"preview": "<?php\n\nnamespace pxlrbt\\FilamentActivityLog\\Pages;\n\n/**\n * @deprecated Use ListActivitiesBySubject directly\n */\nabstract"
},
{
"path": "src/Pages/ListActivitiesByCauser.php",
"chars": 3306,
"preview": "<?php\n\nnamespace pxlrbt\\FilamentActivityLog\\Pages;\n\nuse Filament\\Facades\\Filament;\nuse Filament\\Forms\\Components\\Field;\n"
},
{
"path": "src/Pages/ListActivitiesBySubject.php",
"chars": 4146,
"preview": "<?php\n\nnamespace pxlrbt\\FilamentActivityLog\\Pages;\n\nuse Exception;\nuse Filament\\Forms\\Components\\Field;\nuse Filament\\For"
}
]
About this extraction
This page contains the full source code of the pxlrbt/filament-activity-log GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 28 files (37.7 KB), approximately 9.9k tokens, and a symbol index with 33 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.