Repository: rmsramos/activitylog Branch: main Commit: 428eb3183305 Files: 107 Total size: 131.3 KB Directory structure: gitextract_nvk_dftn/ ├── .editorconfig ├── .gitattributes ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug.yml │ │ └── config.yml │ └── workflows/ │ └── fix-php-code-style-issues.yml ├── .gitignore ├── LICENSE.md ├── README.md ├── composer.json ├── config/ │ └── filament-activitylog.php ├── package.json ├── pint.json ├── postcss.config.js ├── resources/ │ ├── css/ │ │ └── plugin.css │ ├── dist/ │ │ └── activitylog.css │ ├── lang/ │ │ ├── ar/ │ │ │ ├── action.php │ │ │ ├── forms.php │ │ │ ├── tables.php │ │ │ └── timeline.php │ │ ├── de/ │ │ │ ├── action.php │ │ │ ├── forms.php │ │ │ ├── tables.php │ │ │ └── timeline.php │ │ ├── en/ │ │ │ ├── action.php │ │ │ ├── forms.php │ │ │ ├── infolists.php │ │ │ ├── notifications.php │ │ │ ├── tables.php │ │ │ └── timeline.php │ │ ├── es/ │ │ │ ├── action.php │ │ │ ├── forms.php │ │ │ ├── infolists.php │ │ │ ├── notifications.php │ │ │ ├── tables.php │ │ │ └── timeline.php │ │ ├── fa/ │ │ │ ├── action.php │ │ │ ├── forms.php │ │ │ ├── infolists.php │ │ │ ├── notifications.php │ │ │ ├── tables.php │ │ │ └── timeline.php │ │ ├── fr/ │ │ │ ├── action.php │ │ │ ├── forms.php │ │ │ ├── tables.php │ │ │ └── timeline.php │ │ ├── he/ │ │ │ ├── action.php │ │ │ ├── forms.php │ │ │ ├── tables.php │ │ │ └── timeline.php │ │ ├── id/ │ │ │ ├── action.php │ │ │ ├── forms.php │ │ │ ├── tables.php │ │ │ └── timeline.php │ │ ├── it/ │ │ │ ├── action.php │ │ │ ├── forms.php │ │ │ ├── tables.php │ │ │ └── timeline.php │ │ ├── lv/ │ │ │ ├── action.php │ │ │ ├── forms.php │ │ │ ├── tables.php │ │ │ └── timeline.php │ │ ├── nl/ │ │ │ ├── action.php │ │ │ ├── forms.php │ │ │ ├── tables.php │ │ │ └── timeline.php │ │ ├── pl/ │ │ │ ├── action.php │ │ │ ├── forms.php │ │ │ ├── tables.php │ │ │ └── timeline.php │ │ ├── pt_BR/ │ │ │ ├── action.php │ │ │ ├── forms.php │ │ │ ├── infolists.php │ │ │ ├── notifications.php │ │ │ ├── tables.php │ │ │ └── timeline.php │ │ ├── pt_PT/ │ │ │ ├── action.php │ │ │ ├── forms.php │ │ │ ├── tables.php │ │ │ └── timeline.php │ │ └── tr/ │ │ ├── action.php │ │ ├── forms.php │ │ ├── tables.php │ │ └── timeline.php │ └── views/ │ ├── .gitkeep │ └── filament/ │ ├── infolists/ │ │ └── components/ │ │ ├── time-line-icon-entry.blade.php │ │ ├── time-line-propertie-entry.blade.php │ │ ├── time-line-repeatable-entry.blade.php │ │ └── time-line-title-entry.blade.php │ └── tables/ │ └── columns/ │ └── activity-logs-properties.blade.php ├── src/ │ ├── Actions/ │ │ ├── ActivityLogTimelineSimpleAction.php │ │ ├── ActivityLogTimelineTableAction.php │ │ └── Concerns/ │ │ └── ActionContent.php │ ├── ActivitylogPlugin.php │ ├── ActivitylogServiceProvider.php │ ├── Helpers/ │ │ └── ActivityLogHelper.php │ ├── Infolists/ │ │ ├── Components/ │ │ │ ├── TimeLineIconEntry.php │ │ │ ├── TimeLinePropertiesEntry.php │ │ │ ├── TimeLineRepeatableEntry.php │ │ │ └── TimeLineTitleEntry.php │ │ └── Concerns/ │ │ └── HasModifyState.php │ ├── RelationManagers/ │ │ └── ActivitylogRelationManager.php │ ├── Resources/ │ │ └── ActivitylogResource/ │ │ ├── ActivitylogResource.php │ │ ├── Pages/ │ │ │ ├── ListActivitylog.php │ │ │ └── ViewActivitylog.php │ │ └── Schemas/ │ │ └── ActivitylogForm.php │ └── Traits/ │ └── HasCustomActivityResource.php └── tailwind.config.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ root = true [*] charset = utf-8 end_of_line = lf indent_size = 4 indent_style = space insert_final_newline = true trim_trailing_whitespace = true [*.md] trim_trailing_whitespace = false [*.{yml,yaml}] indent_size = 2 ================================================ FILE: .gitattributes ================================================ # Path-based git attributes # https://www.kernel.org/pub/software/scm/git/docs/gitattributes.html # Ignore all test and documentation with "export-ignore". /.github export-ignore /.gitattributes export-ignore /.gitignore export-ignore /phpunit.xml.dist export-ignore /art export-ignore /docs export-ignore /tests export-ignore /workbench export-ignore /.editorconfig export-ignore /.php_cs.dist.php export-ignore /psalm.xml export-ignore /psalm.xml.dist export-ignore /testbench.yaml export-ignore /UPGRADING.md export-ignore /phpstan.neon.dist export-ignore /phpstan-baseline.neon export-ignore ================================================ FILE: .github/ISSUE_TEMPLATE/bug.yml ================================================ name: Bug report description: Report a problem you're experiencing labels: bug,unconfirmed body: - type: markdown attributes: value: | Before opening a bug report, please search the existing issues (both open and closed). --- Thank you for taking the time to file a bug report. To address this bug as fast as possible, we need some information. - type: input id: package-version attributes: label: Package Version description: Please provide the full version of the package you have installed. placeholder: v1.0.0 validations: required: true - type: input id: laravel-version attributes: label: Laravel Version description: Please provide the full Laravel version of your project. placeholder: v10.0.0 validations: required: true - type: input id: php-version attributes: label: PHP Version description: Please provide the full PHP version of your server. placeholder: PHP 8.3.0 validations: required: true - type: textarea id: description attributes: label: Problem description description: What happened when you experienced the problem? validations: required: true - type: textarea id: expectation attributes: label: Expected behavior description: What did you expect to happen instead? validations: required: true - type: textarea id: steps attributes: label: Steps to reproduce description: Which steps do we need to take to reproduce the problem? Any code examples need to be **as short as possible**, remove any code that is unrelated to the bug. **This issue will be automatically closed and not reviewed if detailed replication steps are missing.** validations: required: true - type: input id: reproduction attributes: label: Reproduction repository (issue will be closed if this is not valid) description: The URL of a public GitHub repository which reproduces the problem. **Please do not link to your actual project**, what we need instead is a _minimal_ reproduction in a fresh project without any unnecessary code. This means it doesn\'t matter if your real project is private / confidential, since we want a link to a separate, isolated reproduction. This allows us to fix the problem much quicker. **This issue will be automatically closed and not reviewed if this is missing. Please make sure to format the URL starting with `https://github.com` - only repositories hosted on GitHub are accepted. validations: required: true - type: textarea id: logs attributes: label: Relevant log output description: If applicable, provide relevant log output. No need for backticks here. render: shell ================================================ FILE: .github/ISSUE_TEMPLATE/config.yml ================================================ blank_issues_enabled: false contact_links: - name: Ask a question url: https://github.com/Rmsramos/activitylog/discussions/new?category=q-a about: Ask the community for help - name: Request a feature url: https://github.com/Rmsramos/activitylog/discussions/new?category=ideas about: Share ideas for new features - name: Report a security issue url: https://github.com/Rmsramos/activitylog/security/policy about: Learn how to notify us for sensitive bugs ================================================ FILE: .github/workflows/fix-php-code-style-issues.yml ================================================ name: Fix PHP code style issues on: push: paths: - '**.php' permissions: contents: write jobs: php-code-styling: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v3 with: ref: ${{ github.head_ref }} - name: Fix PHP code style issues uses: aglipanci/laravel-pint-action@2.3.0 - name: Commit changes uses: stefanzweifel/git-auto-commit-action@v4 with: commit_message: Fix styling ================================================ FILE: .gitignore ================================================ .idea .phpunit.cache build composer.lock coverage docs phpunit.xml phpstan.neon testbench.yaml vendor node_modules ================================================ FILE: LICENSE.md ================================================ The MIT License (MIT) Copyright (c) Rmsramos 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: README.md ================================================ # ActivityLog ### Spatie/Laravel-activitylog for Filament [![Latest Version on Packagist](https://img.shields.io/packagist/v/rmsramos/activitylog.svg?style=flat-square)](https://packagist.org/packages/rmsramos/activitylog) [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg)](LICENSE.md) [![GitHub Code Style Action Status](https://img.shields.io/github/actions/workflow/status/rmsramos/activitylog/fix-php-code-style-issues.yml?branch=main&label=code%20style&style=flat-square)](https://github.com/rmsramos/activitylog/actions?query=workflow%3A"Fix+PHP+code+style+issues"+branch%3Amain) [![Total Downloads](https://img.shields.io/packagist/dt/rmsramos/activitylog.svg?style=flat-square)](https://packagist.org/packages/rmsramos/activitylog/stats)
![Screenshot of Application Feature](https://raw.githubusercontent.com/rmsramos/activitylog/main/arts/cover.jpeg)
This package provides a Filament resource that shows you all of the activity logs and detailed view of each log created using the `spatie/laravel-activitylog` package. It also provides a relationship manager for related models. ## Requirements - Laravel v12 - Filament v3 - Spatie/Laravel-activitylog v4 ## Languages Supported ActivityLog Plugin is translated for : - 🇧🇷 Brazilian Portuguese - 🇺🇸 English - 🇩🇪 German - 🇪🇸 Spanish - 🇫🇷 French - 🇮🇷 Persian - 🇦🇪 Arabic - 🇵🇹 Portuguese - 🇮🇱 Hebrew - 🇳🇱 Dutch - 🇱🇻 Latvian ## Installation You can install the package via composer: ```bash composer require rmsramos/activitylog ``` After that run the install command: ```bash php artisan activitylog:install ``` This will publish the config & migrations from `spatie/laravel-activitylog` And run migrates ```bash php artisan migrate ``` You can manually publish the configuration file with: ```bash php artisan vendor:publish --tag="activitylog-config" ``` This is the contents of the published config file: ```php return [ 'resources' => [ 'label' => 'Activity Log', 'plural_label' => 'Activity Logs', 'hide_restore_action' => false, 'restore_action_label' => 'Restore', 'hide_resource_action' => false, 'hide_restore_model_action' => true, 'resource_action_label' => 'View', 'navigation_item' => true, 'navigation_group' => null, 'navigation_icon' => 'heroicon-o-shield-check', 'navigation_sort' => null, 'default_sort_column' => 'id', 'default_sort_direction' => 'desc', 'navigation_count_badge' => false, 'resource' => \Rmsramos\Activitylog\Resources\ActivitylogResource::class, ], 'date_format' => 'd/m/Y', 'datetime_format' => 'd/m/Y H:i:s', ]; ``` Optionally, you can publish the views using ```bash php artisan vendor:publish --tag="activitylog-views" ``` ## Usage ### Basic Spatie ActivityLog usage In you `Model` add `Spatie\Activitylog\Traits\LogsActivity` trait, and configure `getActivitylogOption` function For more configuration, Please review [Spatie Docs](https://spatie.be/docs/laravel-activitylog/v4) ```php use Illuminate\Database\Eloquent\Model; use Spatie\Activitylog\Traits\LogsActivity; use Spatie\Activitylog\LogOptions; class NewsItem extends Model { use LogsActivity; protected $fillable = ['name', 'text']; public function getActivitylogOptions(): LogOptions { return LogOptions::defaults() ->logOnly(['name', 'text']); } } ``` ## Plugin usage ![Screenshot of Application Feature](https://raw.githubusercontent.com/rmsramos/activitylog/main/arts/resource.png) In your Panel ServiceProvider `(App\Providers\Filament)` active the plugin Add the `Rmsramos\Activitylog\ActivitylogPlugin` to your panel config ```php use Rmsramos\Activitylog\ActivitylogPlugin; public function panel(Panel $panel): Panel { return $panel ->plugins([ ActivitylogPlugin::make(), ]); } ``` ## Customising the ActivitylogResource You can swap out the `ActivitylogResource` used by updating the `->resource()` value. Use this to create your own `CustomResource` class and extend the original at `\Rmsramos\Activitylog\Resources\ActivitylogResource::class`. This will allow you to customise everything such as the views, table, form and permissions. > [!NOTE] > If you wish to change the resource on List and View page be sure to replace the `getPages` method on the new resource and create your own version of the `ListPage` and `ViewPage` classes to reference the custom `CustomResource`. ```php use Rmsramos\Activitylog\ActivitylogPlugin; public function panel(Panel $panel): Panel { return $panel ->plugins([ ActivitylogPlugin::make() ->resource(\Path\For\Your\CustomResource::class), ]); } ``` ## Customising label Resource You can swap out the `Resource label` used by updating the `->label()` and `->pluralLabel()` value. ```php use Rmsramos\Activitylog\ActivitylogPlugin; public function panel(Panel $panel): Panel { return $panel ->plugins([ ActivitylogPlugin::make() ->label('Log') ->pluralLabel('Logs'), ]); } ``` ## Displaying the resource in the navigation You can enable or disable the `Resource navigation item` by updating the `->navigationItem()` value. ```php use Rmsramos\Activitylog\ActivitylogPlugin; public function panel(Panel $panel): Panel { return $panel ->plugins([ ActivitylogPlugin::make() ->navigationItem(false), // by default is true ]); } ``` ## Grouping resource navigation items You can add a `Resource navigation group` updating the `->navigationGroup()` value. ```php use Rmsramos\Activitylog\ActivitylogPlugin; public function panel(Panel $panel): Panel { return $panel ->plugins([ ActivitylogPlugin::make() ->navigationGroup('Activity Log'), ]); } ``` ## Customising a resource navigation icon You can swap out the `Resource navigation icon` used by updating the `->navigationIcon()` value. ```php use Rmsramos\Activitylog\ActivitylogPlugin; public function panel(Panel $panel): Panel { return $panel ->plugins([ ActivitylogPlugin::make() ->navigationIcon('heroicon-o-shield-check'), ]); } ``` ## Active a count badge You can active `Count Badge` updating the `->navigationCountBadge()` value. ```php use Rmsramos\Activitylog\ActivitylogPlugin; public function panel(Panel $panel): Panel { return $panel ->plugins([ ActivitylogPlugin::make() ->navigationCountBadge(true), ]); } ``` ## Set navigation sort You can set the `Resource navigation sort` used by updating the `->navigationSort()` value. ```php use Rmsramos\Activitylog\ActivitylogPlugin; public function panel(Panel $panel): Panel { return $panel ->plugins([ ActivitylogPlugin::make() ->navigationSort(3), ]); } ``` ## Authorization If you would like to prevent certain users from accessing the logs resource, you should add a authorize callback in the `ActivitylogPlugin` chain. ```php use Rmsramos\Activitylog\ActivitylogPlugin; public function panel(Panel $panel): Panel { return $panel ->plugins([ ActivitylogPlugin::make() ->authorize( fn () => auth()->user()->id === 1 ), ]); } ``` ## Translate Resource Names To translate resource names in the activity log, add a `translateSubject` callback within the `ActivitylogPlugin` chain ```php use Rmsramos\Activitylog\ActivitylogPlugin; public function panel(Panel $panel): Panel { return $panel ->plugins([ ActivitylogPlugin::make() ->translateSubject(fn($label) => __("yourCustomLangFile.".$label)), ]); } ``` ## Translate Activity log key Names To translate the names of the keys in the activity log, add a `translateLogKey` callback within the `ActivitylogPlugin` chain ```php use Rmsramos\Activitylog\ActivitylogPlugin; public function panel(Panel $panel): Panel { return $panel ->plugins([ ActivitylogPlugin::make() ->translateLogKey(fn($label) => __("yourCustomLangFile.".$label)), ]); } ``` ## Customize Date Parsing To customize how dates are parsed, depending on user preferences or settings: ```php use Rmsramos\Activitylog\ActivitylogPlugin; use Morilog\Jalali\Jalalian; use Carbon\Carbon; public function panel(Panel $panel): Panel { return $panel ->plugins([ ActivitylogPlugin::make() ->dateParser( fn($date) => auth()->user()->isJalaliCalendar() ? Jalalian::fromDateTime($date) : Carbon::parse($date) ) ]); } ``` ## Customize Date and DateTime Formats To customize the format of dates and datetime columns based on user settings: ```php use Rmsramos\Activitylog\ActivitylogPlugin; public function panel(Panel $panel): Panel { return $panel ->plugins([ ActivitylogPlugin::make() ->dateFormat('Y-m-d') ->datetimeFormat(fn() => auth()->user()->getFilamentDateTimeFormat()) ]); } ``` ## Customize DateTime Columns To conditionally customize datetime columns in the UI, depending on the user's calendar preference: ```php use Rmsramos\Activitylog\ActivitylogPlugin; public function panel(Panel $panel): Panel { return $panel ->plugins([ ActivitylogPlugin::make() ->customizeDatetimeColumn(function ($column) { return $column->when( auth()->user()->isJalaliCalendar(), function ($column) { return $column->jalaliDateTime(); } ); }) ]); } ``` ## Customize Date Picker Fields To customize date picker fields in forms, depending on user preferences: ```php use Rmsramos\Activitylog\ActivitylogPlugin; public function panel(Panel $panel): Panel { return $panel ->plugins([ ActivitylogPlugin::make() ->customizeDatePicker(function ($field) { return $field->when( auth()->user()->isJalaliCalendar(), function ($field) { return $field->jalali(); } ); }) ]); } ``` ## Manipulate View Action Using Custom Activity Resource Trait Implement `getFilamentActualResourceModel` in the trait `HasCustomActivityResource` to determine the actual model related to the activity record for generating valid URLs. ```php use Rmsramos\Activitylog\Traits\HasCustomActivityResource; trait HasCustomActivityResource { public function getFilamentActualResourceModel($record) { $record = $record->subject->translatable; $model = null; switch ($record::class) { case FirstTranslatableModel::class: $model = $record->firstModel; break; case SecondTranslatableModel::class: $model = $record->secondModel; break; default: throw new Exception("Error Translatable subject model not found. record = ".$record::class, 1); break; } return $model; } } ``` ### Hide Restore / View Action To hide the restore / view action globally for a resource within the `ActivitylogPlugin`, you can use the `isRestoreActionHidden` and `isResourceActionHidden` method. these are particularly useful in scenarios where you do not want users to have the ability to restore or view entries from the activity log. you can also customize the label of view action: ```php use Rmsramos\Activitylog\ActivitylogPlugin; public function panel(Panel $panel): Panel { return $panel ->plugins([ ActivitylogPlugin::make() ->isRestoreActionHidden(true) ->isResourceActionHidden(true) ->resourceActionLabel("Sample Label") ]); } ``` ### Show Restore (Soft Deletes) In the `laravel-activitylog` configuration file `config/activitylog.php`: ```php return [ 'subject_returns_soft_deleted_models' => true, ] ``` To globally display the restore (soft delete) action of a resource within the `ActivitylogPlugin`, you can use the `isRestoreModelActionHidden` method. This is particularly useful in scenarios where you do not want users to have the ability to restore activity log entries: ```php use Rmsramos\Activitylog\ActivitylogPlugin; public function panel(Panel $panel): Panel { return $panel ->plugins([ ActivitylogPlugin::make() ->isRestoreModelActionHidden(false) ]); } ``` ### Role Policy To ensure ActivitylogResource access via RolePolicy you would need to add the following to your AppServiceProvider: ```php use App\Policies\ActivityPolicy; use Illuminate\Support\Facades\Gate; use Spatie\Activitylog\Models\Activity; class AppServiceProvider extends ServiceProvider { public function boot(): void { Gate::policy(Activity::class, ActivityPolicy::class); } } ``` ## Full configuration ```php use Rmsramos\Activitylog\ActivitylogPlugin; public function panel(Panel $panel): Panel { return $panel ->plugins([ ActivitylogPlugin::make() ->resource(\Path\For\Your\CustomResource::class) ->label('Log') ->pluralLabel('Logs') ->navigationItem(true) ->navigationGroup('Activity Log') ->navigationIcon('heroicon-o-shield-check') ->navigationCountBadge(true) ->navigationSort(2) ->authorize( fn () => auth()->user()->id === 1 ) ->translateSubject(fn($label) => __("yourCustomLangFile.".$label)), ->dateParser( fn($date) => auth()->user()->isJalaliCalendar() ? Jalalian::fromDateTime($date) : Carbon::parse($date) ) ->dateFormat('Y-m-d') ->datetimeFormat(fn() => auth()->user()->getFilamentDateTimeFormat()) ->customizeDatetimeColumn(function ($column) { return $column->when( auth()->user()->isJalaliCalendar(), function ($column) { return $column->jalaliDateTime(); } ); }) ->customizeDatePicker(function ($field) { return $field->when( auth()->user()->isJalaliCalendar(), function ($field) { return $field->jalali(); } ); }) ->isRestoreActionHidden(true) ->isResourceActionHidden(true) ->isRestoreModelActionHidden(false) ->resourceActionLabel("Sample Label"), ]); } ``` ## Relationship manager If you have a model that uses the `Spatie\Activitylog\Traits\LogsActivity` trait, you can add the `Rmsramos\Activitylog\RelationManagers\ActivitylogRelationManager` relationship manager to your Filament resource to display all of the activity logs that are performed on your model. ![Screenshot of Application Feature](https://raw.githubusercontent.com/rmsramos/activitylog/main/arts/relationManager.png) ```php use Rmsramos\Activitylog\RelationManagers\ActivitylogRelationManager; public static function getRelations(): array { return [ ActivitylogRelationManager::class, ]; } ``` ## Timeline Action ![Screenshot of Application Feature](https://raw.githubusercontent.com/rmsramos/activitylog/main/arts/timeline.png) To make viewing activity logs easier, you can use a custom action. In your UserResource in the table function, add the `ActivityLogTimelineTableAction`. ```php use Rmsramos\Activitylog\Actions\ActivityLogTimelineTableAction; public static function table(Table $table): Table { return $table ->actions([ ActivityLogTimelineTableAction::make('Activities'), ]); } ``` you can pass a matrix with the relationships, remember to configure your `Models`. ```php public static function table(Table $table): Table { return $table ->actions([ ActivityLogTimelineTableAction::make('Activities') ->withRelations(['profile', 'address']), //opcional ]); } ``` You can configure the icons and colors, by default the `'heroicon-m-check'` icon and the `'primary'` color are used. ```php use Rmsramos\Activitylog\Actions\ActivityLogTimelineTableAction; public static function table(Table $table): Table { return $table ->actions([ ActivityLogTimelineTableAction::make('Activities') ->timelineIcons([ 'created' => 'heroicon-m-check-badge', 'updated' => 'heroicon-m-pencil-square', ]) ->timelineIconColors([ 'created' => 'info', 'updated' => 'warning', ]) ]); } ``` You can limit the number of results in the query by passing a limit, by default the last 10 records are returned. ```php use Rmsramos\Activitylog\Actions\ActivityLogTimelineTableAction; public static function table(Table $table): Table { return $table ->actions([ ActivityLogTimelineTableAction::make('Activities') ->limit(30), ]); } ``` ## Full Timeline configuration ```php use Rmsramos\Activitylog\Actions\ActivityLogTimelineTableAction; public static function table(Table $table): Table { return $table ->actions([ ActivityLogTimelineTableAction::make('Activities') ->withRelations(['profile', 'address']) ->timelineIcons([ 'created' => 'heroicon-m-check-badge', 'updated' => 'heroicon-m-pencil-square', ]) ->timelineIconColors([ 'created' => 'info', 'updated' => 'warning', ]) ->limit(10), ]); } ``` ## Changelog Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently. ## Contributing Please see [CONTRIBUTING](CONTRIBUTING.md) for details. ## Security Vulnerabilities Please review [our security policy](../../security/policy) on how to report security vulnerabilities. ## Acknowledgements Special acknowledgment goes to these remarkable tools and people (developers), the Activity Log plugin only exists due to the inspiration and at some point the use of these people's codes. - [Jay-Are Ocero](https://github.com/199ocero/activity-timeline) - [Alex Justesen](https://github.com/alexjustesen) - [z3d0x](https://github.com/z3d0x/filament-logger) - [Filament](https://github.com/filamentphp/filament) - [Spatie Activitylog Contributors](https://github.com/spatie/laravel-activitylog#credits) ## Credits - [Rômulo Ramos](https://github.com/rmsramos) - [All Contributors](../../contributors) ## License The MIT License (MIT). Please see [License File](LICENSE.md) for more information. ================================================ FILE: composer.json ================================================ { "name": "rmsramos/activitylog", "description": "This is my package activitylog", "keywords": [ "Rmsramos", "laravel", "activitylog" ], "homepage": "https://github.com/rmsramos/activitylog", "license": "MIT", "authors": [ { "name": "Rômulo Ramos", "email": "rmsramos@gmail.com", "role": "Developer" } ], "require": { "php": "^8.2", "illuminate/contracts": "^11.0||^12.0", "spatie/laravel-activitylog": "^4.8", "spatie/laravel-package-tools": "^1.16" }, "require-dev": { "larastan/larastan": "^2.9", "laravel/pint": "^1.16", "nunomaduro/collision": "^8.1.1||^7.10.0", "orchestra/testbench": "^9.0.0||^8.22.0", "pestphp/pest": "^2.34", "pestphp/pest-plugin-arch": "^2.7", "pestphp/pest-plugin-laravel": "^2.3", "phpstan/extension-installer": "^1.3", "phpstan/phpstan-deprecation-rules": "^1.1", "phpstan/phpstan-phpunit": "^1.3" }, "autoload": { "psr-4": { "Rmsramos\\Activitylog\\": "src/", "Rmsramos\\Activitylog\\Database\\Factories\\": "database/factories/" } }, "autoload-dev": { "psr-4": { "Rmsramos\\Activitylog\\Tests\\": "tests/", "Workbench\\App\\": "workbench/app/" } }, "scripts": { "post-autoload-dump": "@composer run prepare", "clear": "@php vendor/bin/testbench package:purge-activitylog --ansi", "prepare": "@php vendor/bin/testbench package:discover --ansi", "build": [ "@composer run prepare", "@php vendor/bin/testbench workbench:build --ansi" ], "start": [ "Composer\\Config::disableProcessTimeout", "@composer run build", "@php vendor/bin/testbench serve" ], "analyse": "vendor/bin/phpstan analyse", "test": "vendor/bin/pest", "test-coverage": "vendor/bin/pest --coverage", "format": "vendor/bin/pint" }, "config": { "sort-packages": true, "allow-plugins": { "pestphp/pest-plugin": true, "phpstan/extension-installer": true } }, "extra": { "laravel": { "providers": [ "Rmsramos\\Activitylog\\ActivitylogServiceProvider" ], "aliases": { "Activitylog": "Rmsramos\\Activitylog\\Facades\\Activitylog" } } }, "minimum-stability": "dev", "prefer-stable": true } ================================================ FILE: config/filament-activitylog.php ================================================ [ 'label' => 'Activity Log', 'plural_label' => 'Activity Logs', 'hide_restore_action' => false, 'restore_action_label' => 'Restore', 'hide_resource_action' => false, 'hide_restore_model_action' => true, 'resource_action_label' => 'View', 'navigation_item' => true, 'navigation_group' => null, 'navigation_icon' => 'heroicon-o-shield-check', 'navigation_sort' => null, 'default_sort_column' => 'id', 'default_sort_direction' => 'desc', 'navigation_count_badge' => false, 'resource' => \Rmsramos\Activitylog\Resources\ActivitylogResource\ActivitylogResource::class, ], 'date_format' => 'd/m/Y', 'datetime_format' => 'd/m/Y H:i:s', ]; ================================================ FILE: package.json ================================================ { "private": true, "type": "module", "scripts": { "dev:styles": "npx tailwindcss -i resources/css/plugin.css -o resources/dist/activitylog.css --postcss --watch", "build:styles": "npx tailwindcss -i resources/css/plugin.css -o resources/dist/activitylog.css --postcss --minify && npm run purge", "purge": "filament-purge -i resources/dist/activitylog.css -o resources/dist/activitylog.css -v 3.x", "dev": "npm-run-all --parallel dev:*", "build": "npm-run-all build:*" }, "devDependencies": { "@awcodes/filament-plugin-purge": "^1.1.0", "@tailwindcss/forms": "^0.5.3", "@tailwindcss/typography": "^0.5.9", "alpinejs": "^3.10.5", "autoprefixer": "^10.4.7", "esbuild": "^0.17.0", "npm-run-all": "^4.1.5", "postcss": "^8.4.14", "postcss-import": "^15.1.0", "tailwindcss": "^3.1.6" } } ================================================ FILE: pint.json ================================================ { "preset": "laravel", "rules": { "method_chaining_indentation": true, "group_import": false, "single_import_per_statement": false, "no_unused_imports": true, "array_indentation": true, "array_syntax": { "syntax": "short" }, "binary_operator_spaces": { "default": "single_space", "operators": { "=": "align_single_space_minimal", "=>": "align_single_space_minimal" } }, "blank_line_after_namespace": true, "blank_line_after_opening_tag": false, "class_attributes_separation": { "elements": { "property": "one" } }, "concat_space": { "spacing": "one" }, "declare_equal_normalize": { "space": "single" }, "elseif": false, "encoding": true, "indentation_type": true, "no_useless_else": false, "no_useless_return": true, "ordered_imports": true, "ternary_operator_spaces": true, "no_extra_blank_lines": true, "no_multiline_whitespace_around_double_arrow": true, "multiline_whitespace_before_semicolons": true, "no_singleline_whitespace_before_semicolons": true, "no_spaces_around_offset": true, "ternary_to_null_coalescing": true, "whitespace_after_comma_in_array": true, "trim_array_spaces": true, "trailing_comma_in_multiline": true, "unary_operator_spaces": true, "blank_line_before_statement": { "statements": [ "break", "continue", "declare", "return", "throw", "try", "continue", "do", "exit", "for", "foreach", "if", "include", "include_once", "require", "require_once" ] } } } ================================================ FILE: postcss.config.js ================================================ export default { plugins: { tailwindcss: {}, autoprefixer: {}, }, }; ================================================ FILE: resources/css/plugin.css ================================================ @tailwind components; @tailwind utilities; ================================================ FILE: resources/dist/activitylog.css ================================================ .-start-3{inset-inline-start:-.75rem}.mr-1{margin-right:.25rem}.divide-solid>:not([hidden])~:not([hidden]){border-style:solid}.bg-gray-500\/10{background-color:#6b72801a}.ring-1,.ring-4{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}:is(.dark .dark\:divide-gray-700)>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(55 65 81/var(--tw-divide-opacity))}:is(.dark .dark\:border-gray-700){--tw-border-opacity:1;border-color:rgb(55 65 81/var(--tw-border-opacity))}:is(.dark .dark\:bg-gray-700){--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity))}:is(.dark .dark\:bg-white\/5){background-color:#ffffff0d}:is(.dark .dark\:text-gray-200){--tw-text-opacity:1;color:rgb(229 231 235/var(--tw-text-opacity))}:is(.dark .dark\:text-gray-500){--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity))}:is(.dark .dark\:ring-gray-900){--tw-ring-opacity:1;--tw-ring-color:rgb(17 24 39/var(--tw-ring-opacity))}:is(.dark .dark\:ring-white\/10){--tw-ring-color:#ffffff1a} ================================================ FILE: resources/lang/ar/action.php ================================================ [ 'heading' => 'سجل نشاط المستخدم', 'description' => 'تتبع جميع أنشطة المستخدم', 'tooltip' => 'أنشطة المستخدم', ], ]; ================================================ FILE: resources/lang/ar/forms.php ================================================ [ 'log_name' => [ 'label' => 'النوع', ], 'event' => [ 'label' => 'الحدث', ], 'subject_type' => [ 'label' => 'الموضوع', ], 'causer' => [ 'label' => 'المستخدم', ], 'description' => [ 'label' => 'الوصف', ], 'properties' => [ 'label' => 'الخصائص', ], 'created_at' => [ 'label' => 'تاريخ التسجيل', ], 'old' => [ 'label' => 'القديم', ], 'attributes' => [ 'label' => 'الجديد', ], ], ]; ================================================ FILE: resources/lang/ar/tables.php ================================================ [ 'log_name' => [ 'label' => 'النوع', ], 'event' => [ 'label' => 'الحدث', ], 'subject_type' => [ 'label' => 'الموضوع', ], 'causer' => [ 'label' => 'المستخدم', ], 'properties' => [ 'label' => 'الخصائص', ], 'created_at' => [ 'label' => 'تاريخ التسجيل', ], ], 'filters' => [ 'created_at' => [ 'label' => 'تاريخ التسجيل', 'created_from' => 'تاريخ الإنشاء من', 'created_until' => 'تاريخ الإنشاء حتى', ], 'event' => [ 'label' => 'الحدث', ], ], ]; ================================================ FILE: resources/lang/ar/timeline.php ================================================ [ 'modifiedTitle' => 'تم تعديل %s بواسطة %s.
تم التحديث في: %s', ], 'properties' => [ 'modifiedProperties' => 'تم تعديل الخصائص التالية بواسطة %s:
%s', 'compareOldAndNewValues' => [ 'notEquals' => '- %s من %s إلى %s', 'equals' => '- %s %s', ], 'getNewValues' => '- %s %s', ], ]; ================================================ FILE: resources/lang/de/action.php ================================================ [ 'heading' => 'User Aktivitäten Log', 'description' => 'Tracke alle Aktivitäten, die von Benutzern in der Anwendung durchgeführt werden.', 'tooltip' => 'User Aktivitäten', ], ]; ================================================ FILE: resources/lang/de/forms.php ================================================ [ 'log_name' => [ 'label' => 'Typ', ], 'event' => [ 'label' => 'Ereigniss', ], 'subject_type' => [ 'label' => 'Betreff', ], 'causer' => [ 'label' => 'Benutzer', ], 'description' => [ 'label' => 'Beschreibung', ], 'properties' => [ 'label' => 'Attribute', ], 'created_at' => [ 'label' => 'Logzeitpunkt', ], 'old' => [ 'label' => 'Alt', ], 'attributes' => [ 'label' => 'Neu', ], ], ]; ================================================ FILE: resources/lang/de/tables.php ================================================ [ 'log_name' => [ 'label' => 'Typ', ], 'event' => [ 'label' => 'Ereignis', ], 'subject_type' => [ 'label' => 'Betreff', ], 'causer' => [ 'label' => 'Benutzer', ], 'properties' => [ 'label' => 'Attribute', ], 'created_at' => [ 'label' => 'Logzeitpunkt', ], ], 'filters' => [ 'created_at' => [ 'label' => 'Logzeitpunkt', 'created_from' => 'Geloggt von ', 'created_until' => 'Geloggt bis', ], 'event' => [ 'label' => 'Ereignis', ], ], ]; ================================================ FILE: resources/lang/de/timeline.php ================================================ [ 'modifiedTitle' => '%s wurde %s von %s.
Änderung am: %s', ], 'properties' => [ 'modifiedProperties' => '%s %s das folgende:
%s', 'compareOldAndNewValues' => [ 'notEquals' => '- %s von %s auf %s', 'equals' => '- %s %s', ], 'getNewValues' => '- %s %s', ], ]; ================================================ FILE: resources/lang/en/action.php ================================================ [ 'heading' => 'User Activity Log', 'description' => 'Track all user activities', 'tooltip' => 'User Activities', ], 'event' => [ 'created' => 'created', 'deleted' => 'deleted', 'updated' => 'updated', 'restored' => 'restored', ], 'view' => 'View', 'edit' => 'Edit', 'restore' => 'Restore', 'restore_soft_delete' => [ 'label' => 'Restore Model', 'modal_heading' => 'Restore Deleted Model', 'modal_description' => 'This will restore the model that was deleted (soft delete).', ], ]; ================================================ FILE: resources/lang/en/forms.php ================================================ 'Changes', 'fields' => [ 'log_name' => [ 'label' => 'Type', ], 'event' => [ 'label' => 'Event', ], 'subject_type' => [ 'label' => 'Subject', ], 'causer' => [ 'label' => 'User', ], 'description' => [ 'label' => 'Description', ], 'properties' => [ 'label' => 'Properties', ], 'created_at' => [ 'label' => 'Logged at', ], 'old' => [ 'label' => 'Old', ], 'attributes' => [ 'label' => 'New', ], ], ]; ================================================ FILE: resources/lang/en/infolists.php ================================================ [ 'created_by_at' => 'The :subject was :event by :causer.
Updated at: :update_at', 'updater_updated' => ':causer :event the following:
:changes', 'from_oldvalue_to_newvalue' => '- :key from :old_value to :new_value', 'to_newvalue' => '- :key :new_value', 'unknown' => 'Unknown', ], ]; ================================================ FILE: resources/lang/en/notifications.php ================================================ 'No properties to restore.', 'activity_restored' => 'Activity restored.', 'activity_restored_successfully' => 'Activity restored successfully.', 'failed_to_restore_activity' => 'Failed to restore activity : :error.', 'subject_not_found' => 'Subject not found.', 'unable_to_restore_this_model' => 'Unable to restore this model', 'model_successfully_restored' => 'Model successfully restored', 'error_restoring_model' => 'Erro ao restaurar modelo', ]; ================================================ FILE: resources/lang/en/tables.php ================================================ [ 'log_name' => [ 'label' => 'Type', ], 'event' => [ 'label' => 'Event', ], 'subject_type' => [ 'label' => 'Subject', 'soft_deleted' => ' (Soft Deleted)', 'deleted' => ' (Deleted)', ], 'causer' => [ 'label' => 'User', ], 'properties' => [ 'label' => 'Properties', ], 'created_at' => [ 'label' => 'Logged at', ], ], 'filters' => [ 'created_at' => [ 'label' => 'Logged at', 'created_from' => 'Created from ', 'created_from_indicator' => 'Created from : :created_from', 'created_until' => 'Created until ', 'created_until_indicator' => 'Created until : :created_until', ], 'event' => [ 'label' => 'Event', ], 'log_name' => [ 'label' => 'Log name', ], ], ]; ================================================ FILE: resources/lang/en/timeline.php ================================================ [ 'modifiedTitle' => 'The %s was %s by %s.
Updated at: %s', ], 'properties' => [ 'modifiedProperties' => '%s %s the following:
%s', 'compareOldAndNewValues' => [ 'notEquals' => '- %s from %s to %s', 'equals' => '- %s %s', ], 'getNewValues' => '- %s %s', ], ]; ================================================ FILE: resources/lang/es/action.php ================================================ [ 'heading' => 'Registro de actividad de usuario', 'description' => 'Realizar un seguimiento de todas las actividades de los usuarios', 'tooltip' => 'Actividades de usuario', ], 'event' => [ 'created' => 'creado', 'deleted' => 'eliminado', 'updated' => 'actualizado', 'restored' => 'restaurado', ], 'view' => 'Ver', 'edit' => 'Editar', 'restore' => 'Restaurar', 'restore_soft_delete' => [ 'label' => 'Restaurar modelo', 'modal_heading' => 'Restaurar modelo eliminado', 'modal_description' => 'Esto restaurará el modelo que fue eliminado (borrado lógico).', ], ]; ================================================ FILE: resources/lang/es/forms.php ================================================ 'Cambios', 'fields' => [ 'log_name' => [ 'label' => 'Tipo', ], 'event' => [ 'label' => 'Evento', ], 'subject_type' => [ 'label' => 'Sujeto', ], 'causer' => [ 'label' => 'Usuario', ], 'description' => [ 'label' => 'Descripción', ], 'properties' => [ 'label' => 'Propiedades', ], 'created_at' => [ 'label' => 'Registrado en', ], 'old' => [ 'label' => 'Anterior', ], 'attributes' => [ 'label' => 'Nuevo', ], ], ]; ================================================ FILE: resources/lang/es/infolists.php ================================================ [ 'created_by_at' => 'El :subject fue :event por :causer.
Actualizado en: :update_at', 'updater_updated' => ':causer :event lo siguiente:
:changes', 'from_oldvalue_to_newvalue' => '- :key de :old_value a :new_value', 'to_newvalue' => '- :key :new_value', 'unknown' => 'Desconocido', ], ]; ================================================ FILE: resources/lang/es/notifications.php ================================================ 'No hay propiedades para restaurar.', 'activity_restored' => 'Actividad restaurada.', 'activity_restored_successfully' => 'Actividad restaurada con éxito.', 'failed_to_restore_activity' => 'Error al restaurar la actividad: :error.', 'subject_not_found' => 'Sujeto no encontrado.', 'unable_to_restore_this_model' => 'No se puede restaurar este modelo.', 'model_successfully_restored' => 'Modelo restaurado con éxito.', 'error_restoring_model' => 'Error al restaurar modelo', ]; ================================================ FILE: resources/lang/es/tables.php ================================================ [ 'log_name' => [ 'label' => 'Tipo', ], 'event' => [ 'label' => 'Evento', ], 'subject_type' => [ 'label' => 'Sujeto', 'soft_deleted' => ' (borrado lógico)', 'deleted' => ' (eliminado)', ], 'causer' => [ 'label' => 'Usuario', ], 'properties' => [ 'label' => 'Propiedades', ], 'created_at' => [ 'label' => 'Registrado en', ], ], 'filters' => [ 'created_at' => [ 'label' => 'Registrado en', 'created_from' => 'Creado desde ', 'created_from_indicator' => 'Creado desde: :created_from', 'created_until' => 'Creado hasta ', 'created_until_indicator' => 'Creado hasta: :created_until', ], 'event' => [ 'label' => 'Evento', ], ], ]; ================================================ FILE: resources/lang/es/timeline.php ================================================ [ 'modifiedTitle' => 'El %s fue %s por %s.
Actualizado en: %s', ], 'properties' => [ 'modifiedProperties' => '%s %s lo siguiente:
%s', 'compareOldAndNewValues' => [ 'notEquals' => '- %s de %s a %s', 'equals' => '- %s %s', ], 'getNewValues' => '- %s %s', ], ]; ================================================ FILE: resources/lang/fa/action.php ================================================ [ 'heading' => 'سیاهه‌فعالیت‌کاربر', 'description' => 'ردیابی تمام فعالیت های کاربر', 'tooltip' => 'فعالیت های کاربر', ], 'event' => [ 'created' => 'ایجاد شد', 'deleted' => 'حذف شد', 'updated' => 'بروزرسانی شد', 'restored' => 'بازیابی شد', ], 'view' => 'نمایش', 'edit' => 'ویرایش', 'restore' => 'بازیابی', ]; ================================================ FILE: resources/lang/fa/forms.php ================================================ 'تغییرات', 'fields' => [ 'log_name' => [ 'label' => 'نوع', ], 'event' => [ 'label' => 'رویداد', ], 'subject_type' => [ 'label' => 'موضوع مورد سیاهه', ], 'causer' => [ 'label' => 'کاربر', ], 'description' => [ 'label' => 'توضیحات', ], 'properties' => [ 'label' => 'تغییرات', ], 'created_at' => [ 'label' => 'ورود به سیستم در', ], 'old' => [ 'label' => 'قدیمی', ], 'attributes' => [ 'label' => 'جدید', ], ], ]; ================================================ FILE: resources/lang/fa/infolists.php ================================================ [ 'created_by_at' => ':subject توسط :causer در تاریخ :update_at :event.', 'updater_updated' => 'ویژگی های زیر توسط :causer :event :
:changes ', 'from_oldvalue_to_newvalue' => 'مقدار ویژگی :key از :old_value به :new_value تغییر کرد.', 'to_newvalue' => 'ویژگی :key به :new_value مقدار دهی شد.', 'unknown' => 'ناشناس', ], ]; ================================================ FILE: resources/lang/fa/notifications.php ================================================ 'مقادیری برای بازگردانی وجود ندارد.', 'activity_restored' => 'فعالیت بازگردانی شد.', 'activity_restored_successfully' => 'فعالیت با موفقیت بازگردانی شد.', 'failed_to_restore_activity' => 'بازگردانی فعالیت ناموفق بود. خطا : :error', ]; ================================================ FILE: resources/lang/fa/tables.php ================================================ [ 'log_name' => [ 'label' => 'نوع', ], 'event' => [ 'label' => 'رویداد', ], 'subject_type' => [ 'label' => 'مفعول', ], 'causer' => [ 'label' => 'کاربر', ], 'properties' => [ 'label' => 'تغییرات', ], 'created_at' => [ 'label' => 'ورود به سیستم در', ], ], 'filters' => [ 'created_at' => [ 'label' => 'ورود به سیستم در', 'created_from' => 'ایجاد شده از ', 'created_from_indicator' => 'ایجاد شده از تاریخ : :created_from', 'created_until' => 'ایجاد شده تا ', 'created_until_indicator' => 'ایجاد شده تا تاریخ : :created_until', ], 'event' => [ 'label' => 'رویداد', ], ], ]; ================================================ FILE: resources/lang/fa/timeline.php ================================================ [ 'modifiedTitle' => 'The %s was %s by %s.
Updated at: %s', ], 'properties' => [ 'modifiedProperties' => '%s %s the following:
%s', 'compareOldAndNewValues' => [ 'notEquals' => '- %s from %s to %s', 'equals' => '- %s %s', ], 'getNewValues' => '- %s %s', ], ]; ================================================ FILE: resources/lang/fr/action.php ================================================ [ 'heading' => 'Journal d\'activité des utilisateurs', 'description' => 'Suivre toutes les activités des utilisateurs', 'tooltip' => 'Activités des utilisateurs', ], ]; ================================================ FILE: resources/lang/fr/forms.php ================================================ [ 'log_name' => [ 'label' => 'Type', ], 'event' => [ 'label' => 'Événement', ], 'subject_type' => [ 'label' => 'Sujet', ], 'causer' => [ 'label' => 'Utilisateur', ], 'description' => [ 'label' => 'Description', ], 'properties' => [ 'label' => 'Propriétés', ], 'created_at' => [ 'label' => 'Enregistré à', ], 'old' => [ 'label' => 'Ancien', ], 'attributes' => [ 'label' => 'Nouveau', ], ], ]; ================================================ FILE: resources/lang/fr/tables.php ================================================ [ 'log_name' => [ 'label' => 'Type', ], 'event' => [ 'label' => 'Événement', ], 'subject_type' => [ 'label' => 'Sujet', ], 'causer' => [ 'label' => 'Utilisateur', ], 'properties' => [ 'label' => 'Propriétés', ], 'created_at' => [ 'label' => 'Enregistré à', ], ], 'filters' => [ 'created_at' => [ 'label' => 'Enregistré à', 'created_from' => 'Créé à partir de ', 'created_until' => 'Créé jusqu\'à ', ], 'event' => [ 'label' => 'Événement', ], ], ]; ================================================ FILE: resources/lang/fr/timeline.php ================================================ [ 'modifiedTitle' => 'The %s was %s by %s.
Updated at: %s', ], 'properties' => [ 'modifiedProperties' => '%s %s the following:
%s', 'compareOldAndNewValues' => [ 'notEquals' => '- %s from %s to %s', 'equals' => '- %s %s', ], 'getNewValues' => '- %s %s', ], ]; ================================================ FILE: resources/lang/he/action.php ================================================ [ 'heading' => 'יומן פעילות משתמש', 'description' => 'הצגת כל פעילויות המשתמש', 'tooltip' => 'פעילויות משתמש', ], ]; ================================================ FILE: resources/lang/he/forms.php ================================================ [ 'log_name' => [ 'label' => 'סוג', ], 'event' => [ 'label' => 'אירוע', ], 'subject_type' => [ 'label' => 'נושא', ], 'causer' => [ 'label' => 'משתמש', ], 'description' => [ 'label' => 'תיאור', ], 'properties' => [ 'label' => 'מאפיינים', ], 'created_at' => [ 'label' => 'נוצר', ], 'old' => [ 'label' => 'ישן', ], 'attributes' => [ 'label' => 'חדש', ], ], ]; ================================================ FILE: resources/lang/he/tables.php ================================================ [ 'log_name' => [ 'label' => 'סוג', ], 'event' => [ 'label' => 'אירוע', ], 'subject_type' => [ 'label' => 'נושא', ], 'causer' => [ 'label' => 'משתמש', ], 'properties' => [ 'label' => 'מאפיינים', ], 'created_at' => [ 'label' => 'נוצר', ], ], 'filters' => [ 'created_at' => [ 'label' => 'נוצר', 'created_from' => 'נוצר מ', 'created_until' => 'נוצר עד', ], 'event' => [ 'label' => 'אירוע', ], ], ]; ================================================ FILE: resources/lang/he/timeline.php ================================================ [ 'modifiedTitle' => 'The %s was %s by %s.
Updated at: %s', ], 'properties' => [ 'modifiedProperties' => '%s %s the following:
%s', 'compareOldAndNewValues' => [ 'notEquals' => '- %s from %s to %s', 'equals' => '- %s %s', ], 'getNewValues' => '- %s %s', ], ]; ================================================ FILE: resources/lang/id/action.php ================================================ [ 'heading' => 'Log Aktivitas Pengguna', 'description' => 'Lacak semua aktivitas pengguna', 'tooltip' => 'Aktivitas Pengguna', ], ]; ================================================ FILE: resources/lang/id/forms.php ================================================ [ 'log_name' => [ 'label' => 'Jenis', ], 'event' => [ 'label' => 'Peristiwa', ], 'subject_type' => [ 'label' => 'Subjek', ], 'causer' => [ 'label' => 'Pengguna', ], 'description' => [ 'label' => 'Keterangan', ], 'properties' => [ 'label' => 'Properti', ], 'created_at' => [ 'label' => 'Masuk di', ], 'old' => [ 'label' => 'Tua', ], 'attributes' => [ 'label' => 'Baru', ], ], ]; ================================================ FILE: resources/lang/id/tables.php ================================================ [ 'log_name' => [ 'label' => 'Jenis', ], 'event' => [ 'label' => 'Peristiwa', ], 'subject_type' => [ 'label' => 'Subjek', ], 'causer' => [ 'label' => 'Pengguna', ], 'properties' => [ 'label' => 'Properti', ], 'created_at' => [ 'label' => 'Masuk di', ], ], 'filters' => [ 'created_at' => [ 'label' => 'Masuk di', 'created_from' => 'Dibuat dari', 'created_until' => 'Dibuat sampai', ], 'event' => [ 'label' => 'Peristiwa', ], ], ]; ================================================ FILE: resources/lang/id/timeline.php ================================================ [ 'modifiedTitle' => 'The %s was %s by %s.
Updated at: %s', ], 'properties' => [ 'modifiedProperties' => '%s %s the following:
%s', 'compareOldAndNewValues' => [ 'notEquals' => '- %s from %s to %s', 'equals' => '- %s %s', ], 'getNewValues' => '- %s %s', ], ]; ================================================ FILE: resources/lang/it/action.php ================================================ [ 'heading' => 'Log Attività Utente', 'description' => 'Traccia tutte le attività dell\'utente', 'tooltip' => 'Attività dell\'utente', ], ]; ================================================ FILE: resources/lang/it/forms.php ================================================ [ 'log_name' => [ 'label' => 'Tipo', ], 'event' => [ 'label' => 'Evento', ], 'subject_type' => [ 'label' => 'Soggetto', ], 'causer' => [ 'label' => 'Utente', ], 'description' => [ 'label' => 'Descrizione', ], 'properties' => [ 'label' => 'Proprietà', ], 'created_at' => [ 'label' => 'Loggato il', ], 'old' => [ 'label' => 'Vecchio', ], 'attributes' => [ 'label' => 'Nuovo', ], ], ]; ================================================ FILE: resources/lang/it/tables.php ================================================ [ 'log_name' => [ 'label' => 'Tipo', ], 'event' => [ 'label' => 'Evento', ], 'subject_type' => [ 'label' => 'Soggetto', ], 'causer' => [ 'label' => 'Utente', ], 'properties' => [ 'label' => 'Proprietà', ], 'created_at' => [ 'label' => 'Loggato il', ], ], 'filters' => [ 'created_at' => [ 'label' => 'Loggato il', 'created_from' => 'Creato da ', 'created_until' => 'Creato fino al ', ], 'event' => [ 'label' => 'Evento', ], ], ]; ================================================ FILE: resources/lang/it/timeline.php ================================================ [ 'modifiedTitle' => 'The %s was %s by %s.
Updated at: %s', ], 'properties' => [ 'modifiedProperties' => '%s %s the following:
%s', 'compareOldAndNewValues' => [ 'notEquals' => '- %s from %s to %s', 'equals' => '- %s %s', ], 'getNewValues' => '- %s %s', ], ]; ================================================ FILE: resources/lang/lv/action.php ================================================ [ 'heading' => 'Lietotāju darbību žurnāls', 'description' => 'Sekojiet līdzi visām lietotāju darbībām', 'tooltip' => 'Lietotāju darbības', ], ]; ================================================ FILE: resources/lang/lv/forms.php ================================================ [ 'log_name' => [ 'label' => 'Tips', ], 'event' => [ 'label' => 'Notikums', ], 'subject_type' => [ 'label' => 'Objekts', ], 'causer' => [ 'label' => 'Lietotājs', ], 'description' => [ 'label' => 'Apraksts', ], 'properties' => [ 'label' => 'Īpašības', ], 'created_at' => [ 'label' => 'Ierakstīts', ], 'old' => [ 'label' => 'Vecs', ], 'attributes' => [ 'label' => 'Jauns', ], ], ]; ================================================ FILE: resources/lang/lv/tables.php ================================================ [ 'log_name' => [ 'label' => 'Tips', ], 'event' => [ 'label' => 'Notikums', ], 'subject_type' => [ 'label' => 'Objekts', ], 'causer' => [ 'label' => 'Lietotājs', ], 'properties' => [ 'label' => 'Īpašības', ], 'created_at' => [ 'label' => 'Ierakstīts', ], ], 'filters' => [ 'created_at' => [ 'label' => 'Ierakstīts', 'created_from' => 'Izveidots no ', 'created_until' => 'Izveidots līdz ', ], 'event' => [ 'label' => 'Notikums', ], ], ]; ================================================ FILE: resources/lang/lv/timeline.php ================================================ [ 'modifiedTitle' => '%s tika %s ar lietotāju %s.
Atjaunots: %s', ], 'properties' => [ 'modifiedProperties' => '%s %s sekojošu:
%s', 'compareOldAndNewValues' => [ 'notEquals' => '- %s no %s uz %s', 'equals' => '- %s %s', ], 'getNewValues' => '- %s %s', ], ]; ================================================ FILE: resources/lang/nl/action.php ================================================ [ 'heading' => 'Gebruikersctiviteiten Log', 'description' => 'Volg alle gebruikersactiviteiten', 'tooltip' => 'Gebruikersctiviteiten', ], ]; ================================================ FILE: resources/lang/nl/forms.php ================================================ [ 'log_name' => [ 'label' => 'Type', ], 'event' => [ 'label' => 'Event', ], 'subject_type' => [ 'label' => 'Onderwerp', ], 'causer' => [ 'label' => 'Gebruiker', ], 'description' => [ 'label' => 'Omschrijving', ], 'properties' => [ 'label' => 'Velden', ], 'created_at' => [ 'label' => 'Gelogd op', ], 'old' => [ 'label' => 'Oud', ], 'attributes' => [ 'label' => 'Nieuw', ], ], ]; ================================================ FILE: resources/lang/nl/tables.php ================================================ [ 'log_name' => [ 'label' => 'Type', ], 'event' => [ 'label' => 'Event', ], 'subject_type' => [ 'label' => 'Onderwerp', ], 'causer' => [ 'label' => 'Gebruiker', ], 'properties' => [ 'label' => 'Velden', ], 'created_at' => [ 'label' => 'Gelogd op', ], ], 'filters' => [ 'created_at' => [ 'label' => 'Gelogd op', 'created_from' => 'Aangemaakt van ', 'created_until' => 'Aangemaakt tot ', ], 'event' => [ 'label' => 'Event', ], ], ]; ================================================ FILE: resources/lang/nl/timeline.php ================================================ [ 'modifiedTitle' => 'De %s is %s door %s.
Geupdate om: %s', ], 'properties' => [ 'modifiedProperties' => '%s %s het volgende:
%s', 'compareOldAndNewValues' => [ 'notEquals' => '- %s van %s tot %s', 'equals' => '- %s %s', ], 'getNewValues' => '- %s %s', ], ]; ================================================ FILE: resources/lang/pl/action.php ================================================ [ 'heading' => 'Dziennik zdarzeń', 'description' => 'Sprawdź historię wszystkich zmian', 'tooltip' => 'Historia zmian', ], ]; ================================================ FILE: resources/lang/pl/forms.php ================================================ [ 'log_name' => [ 'label' => 'Typ', ], 'event' => [ 'label' => 'Zdarzenie', ], 'subject_type' => [ 'label' => 'Element', ], 'causer' => [ 'label' => 'Użytkownik', ], 'description' => [ 'label' => 'Opis', ], 'properties' => [ 'label' => 'Właściwości', ], 'created_at' => [ 'label' => 'Data zdarzenia', ], 'old' => [ 'label' => 'Przed zmianą', ], 'attributes' => [ 'label' => 'Po zmianie', ], ], ]; ================================================ FILE: resources/lang/pl/tables.php ================================================ [ 'log_name' => [ 'label' => 'Typ', ], 'event' => [ 'label' => 'Zdarzenie', ], 'subject_type' => [ 'label' => 'Element', ], 'causer' => [ 'label' => 'Użytkownik', ], 'properties' => [ 'label' => 'Właściwości', ], 'created_at' => [ 'label' => 'Data zdarzenia', ], ], 'filters' => [ 'created_at' => [ 'label' => 'Data zdarzenia', 'created_from' => 'Utworzony od ', 'created_until' => 'Utworzony do ', ], 'event' => [ 'label' => 'Zdarzenie', ], ], ]; ================================================ FILE: resources/lang/pl/timeline.php ================================================ [ 'modifiedTitle' => 'The %s was %s by %s.
Updated at: %s', ], 'properties' => [ 'modifiedProperties' => '%s %s the following:
%s', 'compareOldAndNewValues' => [ 'notEquals' => '- %s from %s to %s', 'equals' => '- %s %s', ], 'getNewValues' => '- %s %s', ], ]; ================================================ FILE: resources/lang/pt_BR/action.php ================================================ [ 'heading' => 'Registro de Logs do usuário', 'description' => 'Acompanhe todas as atividades do usuário', 'tooltip' => 'Atividades do usuário', ], 'event' => [ 'created' => 'criado', 'deleted' => 'excluído', 'updated' => 'atualizado', 'restored' => 'restaurado', ], 'view' => 'Visualizar', 'edit' => 'Editar', 'restore' => 'Restaurar', 'restore_soft_delete' => [ 'label' => 'Restaurar Modelo', 'modal_heading' => 'Restaurar Modelo Excluído', 'modal_description' => 'Isso irá restaurar o modelo que foi excluído (soft delete).', ], ]; ================================================ FILE: resources/lang/pt_BR/forms.php ================================================ 'Mudanças', 'fields' => [ 'log_name' => [ 'label' => 'Tipo', ], 'event' => [ 'label' => 'Evento', ], 'subject_type' => [ 'label' => 'Assunto', ], 'causer' => [ 'label' => 'Usuário', ], 'description' => [ 'label' => 'Descrição', ], 'properties' => [ 'label' => 'Propriedades', ], 'created_at' => [ 'label' => 'Criando em', ], 'old' => [ 'label' => 'Antigo', ], 'attributes' => [ 'label' => 'Novo', ], ], ]; ================================================ FILE: resources/lang/pt_BR/infolists.php ================================================ [ 'created_by_at' => 'O :subject foi :event por :causer.
Atualizado em: :update_at', 'updater_updated' => ':causer :event o seguinte:
:changes', 'from_oldvalue_to_newvalue' => '- :key de :old_value para :new_value', 'to_newvalue' => '- :key :new_value', 'unknown' => 'Desconhecido', ], ]; ================================================ FILE: resources/lang/pt_BR/notifications.php ================================================ 'Nenhuma propriedade para restaurar.', 'activity_restored' => 'Atividade restaurada.', 'activity_restored_successfully' => 'Atividade restaurada com sucesso.', 'failed_to_restore_activity' => 'Falha ao restaurar a atividade: :error.', 'subject_not_found' => 'Registro não encontrado.', 'unable_to_restore_this_model' => 'Não é possível restaurar este modelo', 'model_successfully_restored' => 'Modelo restaurado com sucesso', 'error_restoring_model' => 'Erro ao restaurar modelo', ]; ================================================ FILE: resources/lang/pt_BR/tables.php ================================================ [ 'log_name' => [ 'label' => 'Tipo', ], 'event' => [ 'label' => 'Evento', ], 'subject_type' => [ 'label' => 'Assunto', 'soft_deleted' => ' (Delete suave)', 'deleted' => ' (Deletado)', ], 'causer' => [ 'label' => 'Usuário', ], 'properties' => [ 'label' => 'Propriedades', ], 'created_at' => [ 'label' => 'Criado em', ], ], 'filters' => [ 'created_at' => [ 'label' => 'Criado em', 'created_from' => 'Criado a partir de ', 'created_from_indicator' => 'Criado a partir de : :created_from', 'created_until' => 'Criado até ', 'created_until_indicator' => 'Criado até : :created_until', ], 'event' => [ 'label' => 'Eventos', ], ], ]; ================================================ FILE: resources/lang/pt_BR/timeline.php ================================================ [ 'modifiedTitle' => 'O %s foi %s por %s.
Atualizado em: %s', ], 'properties' => [ 'modifiedProperties' => '%s %s o seguinte:
%s', 'compareOldAndNewValues' => [ 'notEquals' => '- %s de %s para %s', 'equals' => '- %s %s', ], 'getNewValues' => '- %s %s', ], ]; ================================================ FILE: resources/lang/pt_PT/action.php ================================================ [ 'heading' => 'Registro de Logs do utilizador', 'description' => 'Acompanhe todas as atividades do utilizador.', 'tooltip' => 'Atividades do utilizador', ], ]; ================================================ FILE: resources/lang/pt_PT/forms.php ================================================ [ 'log_name' => [ 'label' => 'Tipo', ], 'event' => [ 'label' => 'Evento', ], 'subject_type' => [ 'label' => 'Assunto', ], 'causer' => [ 'label' => 'Utilizador', ], 'description' => [ 'label' => 'Descrição', ], 'properties' => [ 'label' => 'Propriedades', ], 'created_at' => [ 'label' => 'Criando em', ], 'old' => [ 'label' => 'Antigo', ], 'attributes' => [ 'label' => 'Novo', ], ], ]; ================================================ FILE: resources/lang/pt_PT/tables.php ================================================ [ 'log_name' => [ 'label' => 'Tipo', ], 'event' => [ 'label' => 'Evento', ], 'subject_type' => [ 'label' => 'Assunto', ], 'causer' => [ 'label' => 'Utilizador', ], 'properties' => [ 'label' => 'Propriedades', ], 'created_at' => [ 'label' => 'Criado em', ], ], 'filters' => [ 'created_at' => [ 'label' => 'Criado em', 'created_from' => 'Criado a partir de ', 'created_until' => 'Criado até ', ], 'event' => [ 'label' => 'Eventos', ], ], ]; ================================================ FILE: resources/lang/pt_PT/timeline.php ================================================ [ 'modifiedTitle' => 'The %s was %s by %s.
Updated at: %s', ], 'properties' => [ 'modifiedProperties' => '%s %s the following:
%s', 'compareOldAndNewValues' => [ 'notEquals' => '- %s from %s to %s', 'equals' => '- %s %s', ], 'getNewValues' => '- %s %s', ], ]; ================================================ FILE: resources/lang/tr/action.php ================================================ [ 'heading' => 'Kullanıcı Aktivite Günlüğü', 'description' => 'Tüm kullanıcı aktivitelerini takip edin', 'tooltip' => 'Kullanıcı Aktiviteleri', ], ]; ================================================ FILE: resources/lang/tr/forms.php ================================================ [ 'log_name' => [ 'label' => 'Tip', ], 'event' => [ 'label' => 'Olay', ], 'subject_type' => [ 'label' => 'Konu', ], 'causer' => [ 'label' => 'Kullanıcı', ], 'description' => [ 'label' => 'Açıklama', ], 'properties' => [ 'label' => 'Özellikler', ], 'created_at' => [ 'label' => 'Kayıt Tarihi', ], 'old' => [ 'label' => 'Eski', ], 'attributes' => [ 'label' => 'Yeni', ], ], ]; ================================================ FILE: resources/lang/tr/tables.php ================================================ [ 'log_name' => [ 'label' => 'Tip', ], 'event' => [ 'label' => 'Olay', ], 'subject_type' => [ 'label' => 'Konu', ], 'causer' => [ 'label' => 'Kullanıcı', ], 'properties' => [ 'label' => 'Özellikler', ], 'created_at' => [ 'label' => 'Kayıt Tarihi', ], ], 'filters' => [ 'created_at' => [ 'label' => 'Kayıt Tarihi', 'created_from' => 'Kayıt tarihinden ', 'created_until' => 'Kayıt tarihine ', ], 'event' => [ 'label' => 'Olay', ], ], ]; ================================================ FILE: resources/lang/tr/timeline.php ================================================ [ 'modifiedTitle' => 'The %s was %s by %s.
Updated at: %s', ], 'properties' => [ 'modifiedProperties' => '%s %s the following:
%s', 'compareOldAndNewValues' => [ 'notEquals' => '- %s from %s to %s', 'equals' => '- %s %s', ], 'getNewValues' => '- %s %s', ], ]; ================================================ FILE: resources/views/.gitkeep ================================================ ================================================ FILE: resources/views/filament/infolists/components/time-line-icon-entry.blade.php ================================================ @php use Filament\Infolists\Components\IconEntry\IconEntrySize; @endphp
merge($getExtraAttributes(), escape: false) ->class([ 'absolute flex items-center justify-center w-6 h-6 bg-gray-200 rounded-full -start-3 ring-4 ring-white dark:bg-gray-700 dark:ring-gray-900', ]) }} > @if (count($arrayState = \Illuminate\Support\Arr::wrap($getState()))) @foreach ($arrayState as $state) @if ($icon = $getIcon($state)) @php $color = $getColor($state) ?? 'gray'; $size = $getSize($state) ?? IconEntrySize::Large; @endphp 'fi-in-icon-item-size-xs h-3 w-3', IconEntrySize::Small, 'sm' => 'fi-in-icon-item-size-sm h-4 w-4', IconEntrySize::Medium, 'md' => 'fi-in-icon-item-size-md h-5 w-5', IconEntrySize::Large, 'lg' => 'fi-in-icon-item-size-lg h-6 w-6', IconEntrySize::ExtraLarge, 'xl' => 'fi-in-icon-item-size-xl h-7 w-7', IconEntrySize::TwoExtraLarge, IconEntrySize::ExtraExtraLarge, '2xl' => 'fi-in-icon-item-size-2xl h-8 w-8', default => $size, }, match ($color) { 'gray' => 'fi-color-gray text-gray-400 dark:text-gray-500', default => 'fi-color-custom text-custom-500 dark:text-custom-400', }, ]) @style([ \Filament\Support\get_color_css_variables( $color, shades: [400, 500], alias: 'infolists::components.icon-entry.item', ) => $color !== 'gray', ]) /> @endif @endforeach @elseif (($placeholder = $getPlaceholder()) !== null) {{ $placeholder }} @endif
================================================ FILE: resources/views/filament/infolists/components/time-line-propertie-entry.blade.php ================================================
{{ $getModifiedState() ?? (!is_array($getState()) ? $getState() ?? $getPlaceholder() : null) }}
================================================ FILE: resources/views/filament/infolists/components/time-line-repeatable-entry.blade.php ================================================ @php $isContained = $isContained(); @endphp
merge([ 'id' => $getId(), ], escape: false) ->merge($getExtraAttributes(), escape: false) ->class([ 'fi-in-repeatable', 'fi-contained' => $isContained, ]) }} > @if (count($childComponentContainers = $getChildComponentContainers()))
    @foreach ($childComponentContainers as $container)
  1. $isContained, ]) > {{ $container }}
  2. @endforeach
@elseif (($placeholder = $getPlaceholder()) !== null) {{ $placeholder }} @endif
================================================ FILE: resources/views/filament/infolists/components/time-line-title-entry.blade.php ================================================
merge($getExtraAttributes(), escape: false) ->class(['fi-in-text w-full -mt-6']) }} > {{ $getModifiedState() }}
================================================ FILE: resources/views/filament/tables/columns/activity-logs-properties.blade.php ================================================
@foreach($getState() ?? [] as $key => $value) {{ $key }} @if(is_array($value)) @foreach ($value as $nestedKey => $nestedValue) {{ $nestedKey }}: {{ is_array($nestedValue) ? json_encode($nestedValue) : $nestedValue }} @endforeach @else {{ $value }} @endif @endforeach
================================================ FILE: src/Actions/ActivityLogTimelineSimpleAction.php ================================================ 'heroicon-m-plus', 'updated' => 'heroicon-m-pencil-square', 'deleted' => 'heroicon-m-trash', 'restored' => 'heroicon-m-arrow-uturn-left', ]; protected ?array $timelineIconColors = [ 'created' => 'success', 'updated' => 'warning', 'deleted' => 'danger', 'restored' => 'info', ]; protected ?int $limit = 10; protected Closure $modifyQueryUsing; protected Closure|Builder $query; protected ?Closure $activitiesUsing; protected ?Closure $modifyTitleUsing; protected ?Closure $shouldModifyTitleUsing; public static function getDefaultName(): ?string { return 'activitylog_timeline'; } protected function setUp(): void { parent::setUp(); $this->configureInfolist(); $this->configureModal(); $this->activitiesUsing = null; $this->modifyTitleUsing = null; $this->shouldModifyTitleUsing = fn () => true; $this->modifyQueryUsing = fn ($builder) => $builder; $this->modalHeading = __('activitylog::action.modal.heading'); $this->modalDescription = __('activitylog::action.modal.description'); $this->query = function (?Model $record) { if (! $record) { return Activity::query()->whereRaw('1 = 0'); } return Activity::query() ->with([ 'subject' => function ($query) { if (method_exists($query, 'withTrashed')) { $query->withTrashed(); } }, 'causer', ]) ->where(function (Builder $query) use ($record) { $query->where('subject_type', $record->getMorphClass()) ->where('subject_id', $record->getKey()); if ($relations = $this->getWithRelations()) { foreach ($relations as $relation) { if (method_exists($record, $relation)) { try { $relationInstance = $record->{$relation}(); if ($relationInstance instanceof BelongsToMany) { $subjectType = $relationInstance->getPivotClass(); $relatedIds = $relationInstance->pluck($relationInstance->getTable().'.id')->toArray(); if (! empty($relatedIds)) { $query->orWhere(function (Builder $q) use ($subjectType, $relatedIds) { $q->where('subject_type', $subjectType) ->whereIn('subject_id', $relatedIds); }); } continue; } $relatedModel = $relationInstance->getRelated(); $relatedIds = $relationInstance->pluck('id')->toArray(); if (! empty($relatedIds)) { $query->orWhere(function (Builder $q) use ($relatedModel, $relatedIds) { $q->where('subject_type', $relatedModel->getMorphClass()) ->whereIn('subject_id', $relatedIds); }); } } catch (\Exception $e) { // Ignore errors } } } } }); }; } protected function configureInfolist(): void { $this->infolist(function (?Model $record, Infolist $infolist) { $activities = $this->getActivityLogRecord($record, $this->getWithRelations()); $formattedActivities = $activities->map(function ($activity) { return [ 'id' => $activity->id, 'log_name' => $activity->log_name, 'updated_at' => $activity->updated_at, 'activityData' => $activity->activityData, ]; })->toArray(); return $infolist ->state(['activities' => $formattedActivities]) ->schema($this->getSchema()); }); } protected function configureModal(): void { $this->slideOver() ->modalIcon('heroicon-o-eye') ->modalFooterActions(fn () => []) ->tooltip(__('activitylog::action.modal.tooltip')) ->icon('heroicon-o-bell-alert'); } protected function getSchema(): array { return [ TimeLineRepeatableEntry::make('activities') ->schema([ TimeLineIconEntry::make('activityData.event') ->icon(function ($state) { return $this->getTimelineIcons()[$state] ?? 'heroicon-m-check'; }) ->color(function ($state) { return $this->getTimelineIconColors()[$state] ?? 'primary'; }), TimeLineTitleEntry::make('activityData') ->configureTitleUsing($this->modifyTitleUsing) ->shouldConfigureTitleUsing($this->shouldModifyTitleUsing), TimeLinePropertiesEntry::make('activityData'), TextEntry::make('log_name') ->hiddenLabel() ->badge(), TextEntry::make('updated_at') ->hiddenLabel() ->since() ->badge(), ]), ]; } public function withRelations(?array $relations = null): ?StaticAction { $this->withRelations = $relations; return $this; } public function timelineIcons(?array $timelineIcons = null): ?StaticAction { $this->timelineIcons = $timelineIcons; return $this; } public function timelineIconColors(?array $timelineIconColors = null): ?StaticAction { $this->timelineIconColors = $timelineIconColors; return $this; } public function limit(?int $limit = 10): ?StaticAction { $this->limit = $limit; return $this; } public function query(Closure|Builder|null $query): static { $this->query = $query; return $this; } public function modifyQueryUsing(Closure $closure): static { $this->modifyQueryUsing = $closure; return $this; } public function modifyTitleUsing(Closure $closure): static { $this->modifyTitleUsing = $closure; return $this; } public function shouldModifyTitleUsing(Closure $closure): static { $this->shouldModifyTitleUsing = $closure; return $this; } public function activitiesUsing(Closure $closure): static { $this->activitiesUsing = $closure; return $this; } public function getWithRelations(): ?array { return $this->evaluate($this->withRelations); } public function getTimelineIcons(): ?array { return $this->evaluate($this->timelineIcons); } public function getTimelineIconColors(): ?array { return $this->evaluate($this->timelineIconColors); } public function getLimit(): ?int { return $this->evaluate($this->limit); } public function getQuery(): ?Builder { return $this->evaluate($this->query); } public function getModifyQueryUsing(Builder $builder): Builder { return $this->evaluate($this->modifyQueryUsing, ['builder' => $builder]); } public function getActivitiesUsing(): ?Collection { return $this->evaluate($this->activitiesUsing); } protected function getActivities(?Model $record, ?array $relations = null): Collection { if ($activities = $this->getActivitiesUsing()) { return $activities; } if (! $record) { return collect(); } $builder = $this->getQuery() ->latest() ->limit($this->getLimit()); return $this->getModifyQueryUsing($builder)->get(); } protected function getActivityLogRecord(?Model $record, ?array $relations = null): Collection { $activities = $this->getActivities($record, $relations); return $activities->transform(function ($activity) { $activity->activityData = $this->formatActivityData($activity); return $activity; }); } protected function formatActivityData($activity): array { $properties = []; if ($activity->properties) { if (is_string($activity->properties)) { $properties = json_decode($activity->properties, true) ?? []; } elseif (is_array($activity->properties)) { $properties = $activity->properties; } elseif (is_object($activity->properties) && method_exists($activity->properties, 'toArray')) { $properties = $activity->properties->toArray(); } } if ($activity->event === 'restored') { if (empty($properties) && $activity->description !== 'restored') { $properties['description'] = $activity->description; } } return [ 'log_name' => $activity->log_name, 'description' => $activity->description, 'subject' => $activity->subject, 'event' => $activity->event, 'causer' => $activity->causer, 'properties' => $this->formatDateValues($properties), 'batch_uuid' => $activity->batch_uuid, 'update' => $activity->updated_at, ]; } protected static function formatDateValues(array|string|null $value): array|string|null { if (is_null($value)) { return $value; } if (is_array($value)) { foreach ($value as &$item) { $item = self::formatDateValues($item); } return $value; } if (is_numeric($value) && ! preg_match('/^\d{10,}$/', $value)) { return $value; } if (is_bool($value)) { return $value ? 'true' : 'false'; } try { $parser = ActivitylogPlugin::get()->getDateParser(); return $parser($value)->format(ActivitylogPlugin::get()->getDatetimeFormat()); } catch (InvalidFormatException $e) { return $value; } catch (\Exception $e) { return $value; } } } ================================================ FILE: src/ActivitylogPlugin.php ================================================ resources([ $this->getResource(), ]); } public function boot(Panel $panel): void { // } public static function get(): static { return filament(app(static::class)->getId()); } public function getResource(): string { return $this->resource ?? config('filament-activitylog.resources.resource'); } public function getLabel(): string { return $this->evaluate($this->label) ?? config('filament-activitylog.resources.label'); } public function getResourceActionLabel(): string { return $this->evaluate($this->resourceActionLabel) ?? config('filament-activitylog.resources.resource_action_label'); } public function getIsResourceActionHidden(): bool { return $this->evaluate($this->isResourceActionHidden) ?? config('filament-activitylog.resources.hide_resource_action'); } public function getIsRestoreActionHidden(): bool { return $this->evaluate($this->isRestoreActionHidden) ?? config('filament-activitylog.resources.hide_restore_action'); } public function getIsRestoreModelActionHidden(): bool { return $this->evaluate($this->isRestoreModelActionHidden) ?? config('filament-activitylog.resources.hide_restore_model_action'); } public function getPluralLabel(): string { return $this->evaluate($this->pluralLabel) ?? config('filament-activitylog.resources.plural_label'); } public function getNavigationItem(): bool { return $this->evaluate($this->navigationItem) ?? config('filament-activitylog.resources.navigation_item'); } public function getNavigationGroup(): ?string { return $this->evaluate($this->navigationGroup) ?? config('filament-activitylog.resources.navigation_group'); } public function getDateFormat(): ?string { return $this->evaluate($this->dateFormat) ?? config('filament-activitylog.date_format'); } public function getDatetimeFormat(): ?string { return $this->evaluate($this->datetimeFormat) ?? config('filament-activitylog.datetime_format'); } public function getDatetimeColumnCallback(): ?Closure { return $this->datetimeColumnCallback; } public function getDatePickerCallback(): ?Closure { return $this->datePickerCallback; } public function getTranslateSubject($label): ?string { if (is_null($this->translateSubject)) { return $label; } $callable = $this->translateSubject; return $callable($label); } public function getTranslateLogKey($label): ?string { if (is_null($this->translateLogKey)) { return $label; } $callable = $this->translateLogKey; return $callable($label); } public function getDateParser(): ?Closure { return $this->dateParser ?? fn ($date) => Carbon::parse($date); } public function getNavigationIcon(): ?string { return $this->navigationIcon ?? config('filament-activitylog.resources.navigation_icon'); } public function getNavigationSort(): ?int { return $this->navigationSort ?? config('filament-activitylog.resources.navigation_sort'); } public function getNavigationCountBadge(): ?bool { return $this->navigationCountBadge ?? config('filament-activitylog.resources.navigation_count_badge'); } public function resource(string $resource): static { $this->resource = $resource; return $this; } public function label(string|Closure $label): static { $this->label = $label; return $this; } public function resourceActionLabel(string|Closure $label): static { $this->resourceActionLabel = $label; return $this; } public function isResourceActionHidden(bool|Closure $isHidden): static { $this->isResourceActionHidden = $isHidden; return $this; } public function isRestoreActionHidden(bool|Closure $isHidden): static { $this->isRestoreActionHidden = $isHidden; return $this; } public function isRestoreModelActionHidden(bool|Closure $isHidden): static { $this->isRestoreModelActionHidden = $isHidden; return $this; } public function pluralLabel(string|Closure $label): static { $this->pluralLabel = $label; return $this; } public function navigationItem(Closure|bool $value = true): static { $this->navigationItem = $value; return $this; } public function navigationGroup(string|Closure|null $group = null): static { $this->navigationGroup = $group; return $this; } public function dateParser(?Closure $parser = null): static { $this->dateParser = $parser; return $this; } public function dateFormat(string|Closure|null $format = null): static { $this->dateFormat = $format; return $this; } public function datetimeFormat(string|Closure|null $format = null): static { $this->datetimeFormat = $format; return $this; } public function customizeDatetimeColumn(Closure $callable): self { $this->datetimeColumnCallback = $callable; return $this; } public function customizeDatePicker(Closure $callable): self { $this->datePickerCallback = $callable; return $this; } public function translateSubject(string|Closure|null $callable = null): static { $this->translateSubject = $callable; return $this; } public function translateLogKey(string|Closure|null $callable = null): static { $this->translateLogKey = $callable; return $this; } public function navigationIcon(string $icon): static { $this->navigationIcon = $icon; return $this; } public function navigationSort(int $order): static { $this->navigationSort = $order; return $this; } public function navigationCountBadge(bool $show = true): static { $this->navigationCountBadge = $show; return $this; } public function authorize(bool|Closure $callback = true): static { $this->authorizeUsing = $callback; return $this; } public function isAuthorized(): bool { return $this->evaluate($this->authorizeUsing) === true; } } ================================================ FILE: src/ActivitylogServiceProvider.php ================================================ name('activitylog') ->hasConfigFile('filament-activitylog') ->hasViews('activitylog') ->hasTranslations() ->hasInstallCommand(function (InstallCommand $installCommand) { $installCommand ->publishConfigFile() ->askToStarRepoOnGitHub('rmsramos/activitylog') ->startWith(function (InstallCommand $installCommand) { $installCommand->call('vendor:publish', [ '--provider' => "Spatie\Activitylog\ActivitylogServiceProvider", '--tag' => 'activitylog-migrations', ]); }); }); } public function packageBooted(): void { $assets = [ Css::make('activitylog-styles', __DIR__ . '/../resources/dist/activitylog.css'), ]; FilamentAsset::register($assets, 'rmsramos/activitylog'); } } ================================================ FILE: src/Helpers/ActivityLogHelper.php ================================================ configureIconEntry(); } protected string $view = 'activitylog::filament.infolists.components.time-line-icon-entry'; protected function configureIconEntry() { $this ->hiddenLabel() ->size('w-4 h-4'); } } ================================================ FILE: src/Infolists/Components/TimeLinePropertiesEntry.php ================================================ configurePropertieEntry(); } protected function configurePropertieEntry(): void { $this ->hiddenLabel() ->modifyState(fn ($state) => $this->modifiedProperties($state)); } protected function modifiedProperties($state): ?HtmlString { $properties = $state['properties']; if (! empty($properties)) { $changes = $this->getPropertyChanges($properties); $causerName = $this->getCauserName($state['causer']); return new HtmlString(trans('activitylog::infolists.components.updater_updated', [ 'causer' => $causerName, 'event' => __('activitylog::action.event.' . $state['event']), 'changes' => implode('
', $changes), ])); } return null; } protected function getPropertyChanges(array $properties): array { $changes = []; if (isset($properties['old'], $properties['attributes'])) { $changes = $this->compareOldAndNewValues($properties['old'], $properties['attributes']); } elseif (isset($properties['attributes'])) { $changes = $this->getNewValues($properties['attributes']); } elseif (isset($properties['old'])) { $changes = $this->getNewValues($properties['old']); } return $changes; } protected function compareOldAndNewValues(array $oldValues, array $newValues): array { $changes = []; foreach ($newValues as $key => $newValue) { $oldValue = is_array($oldValues[$key]) ? json_encode($oldValues[$key]) : $oldValues[$key] ?? '-'; $newValue = $this->formatNewValue($newValue); if (isset($oldValues[$key]) && $oldValues[$key] != $newValue) { $changes[] = trans('activitylog::infolists.components.from_oldvalue_to_newvalue', [ 'key' => ActivitylogPlugin::get()->getTranslateLogKey($key), 'old_value' => htmlspecialchars($oldValue), 'new_value' => htmlspecialchars($newValue), ]); } else { $changes[] = trans('activitylog::infolists.components.to_newvalue', [ 'key' => ActivitylogPlugin::get()->getTranslateLogKey($key), 'new_value' => htmlspecialchars($newValue), ]); } } return $changes; } protected function getNewValues(array $newValues): array { return array_map( fn ($key, $value) => sprintf( __('activitylog::timeline.properties.getNewValues'), ActivitylogPlugin::get()->getTranslateLogKey($key), htmlspecialchars($this->formatNewValue($value)) ), array_keys($newValues), $newValues ); } protected function formatNewValue($value): string { return is_array($value) ? json_encode($value) : $value ?? '—'; } } ================================================ FILE: src/Infolists/Components/TimeLineRepeatableEntry.php ================================================ configureRepeatableEntry(); } protected function configureRepeatableEntry(): void { $this ->extraAttributes(['style' => 'margin-left:1.25rem;']) ->contained(false) ->hiddenLabel(); } protected string $view = 'activitylog::filament.infolists.components.time-line-repeatable-entry'; } ================================================ FILE: src/Infolists/Components/TimeLineTitleEntry.php ================================================ configureTitleEntry(); } protected string $view = 'activitylog::filament.infolists.components.time-line-title-entry'; public function configureTitleUsing(?Closure $configureTitleUsing): TimeLineTitleEntry { $this->configureTitleUsing = $configureTitleUsing; return $this; } public function shouldConfigureTitleUsing(?Closure $condition): TimeLineTitleEntry { $this->shouldConfigureTitleUsing = $condition; return $this; } protected function configureTitleEntry() { $this ->hiddenLabel() ->modifyState(fn ($state) => $this->modifiedTitle($state)); } protected function modifiedTitle($state): string|HtmlString|Closure { if ($this->configureTitleUsing !== null && $this->shouldConfigureTitleUsing !== null && $this->evaluate($this->shouldConfigureTitleUsing)) { return $this->evaluate($this->configureTitleUsing, ['state' => $state]); } else { if ($state['description'] == $state['event'] || $state['event'] === 'restored') { $className = property_exists($state['subject'], 'activityTitleName') && ! empty($state['subject']::$activityTitleName) ? $state['subject']::$activityTitleName : Str::lower(Str::snake(class_basename($state['subject']), ' ')); $causerName = $this->getCauserName($state['causer']); $parser = ActivitylogPlugin::get()->getDateParser(); $update_at = $parser($state['update'])->format(ActivitylogPlugin::get()->getDatetimeFormat()); return new HtmlString( __('activitylog::infolists.components.created_by_at', [ 'subject' => ActivitylogPlugin::get()->getTranslateSubject($className), 'event' => __('activitylog::action.event.' . $state['event']), 'causer' => $causerName, 'update_at' => $update_at, ]), ); } } return ''; } } ================================================ FILE: src/Infolists/Concerns/HasModifyState.php ================================================ state = $callback; return $this; } public function getModifiedState(): null|string|HtmlString { return $this->evaluate($this->state); } protected function getCauserName($causer): string { return $causer->name ?? $causer->first_name ?? $causer->last_name ?? $causer->username ?? trans('activitylog::infolists.components.unknown'); } } ================================================ FILE: src/RelationManagers/ActivitylogRelationManager.php ================================================ getPluralLabel()) ->kebab() ->replace('-', ' ') ->headline(); } public function form(Form $form): Form { return ActivitylogResource::form($form); } public function table(Table $table): Table { return ActivitylogResource::table( $table ->heading(ActivitylogPlugin::get()->getPluralLabel()) ->actions([ ViewAction::make(), ]) ); } } ================================================ FILE: src/Resources/ActivitylogResource/ActivitylogResource.php ================================================ getLabel(); } public static function getPluralModelLabel(): string { return ActivitylogPlugin::get()->getPluralLabel(); } public static function getNavigationIcon(): string { return ActivitylogPlugin::get()->getNavigationIcon(); } public static function getNavigationLabel(): string { return Str::title(static::getPluralModelLabel()) ?? Str::title(static::getModelLabel()); } public static function getNavigationSort(): ?int { return ActivitylogPlugin::get()->getNavigationSort(); } public static function getNavigationGroup(): ?string { return ActivitylogPlugin::get()->getNavigationGroup(); } public static function getNavigationBadge(): ?string { return ActivitylogPlugin::get()->getNavigationCountBadge() ? number_format(static::getModel()::count()) : null; } protected static function getResourceUrl(Activity $record): string { $panelID = Filament::getCurrentPanel()->getId(); if ($record->subject_type && $record->subject_id) { try { $model = app($record->subject_type); if (ActivityLogHelper::classUsesTrait($model, HasCustomActivityResource::class)) { $resourceModel = $model->getFilamentActualResourceModel($record); $resourcePluralName = ActivityLogHelper::getResourcePluralName($resourceModel); return route('filament.'.$panelID.'.resources.'.$resourcePluralName.'.edit', ['record' => $resourceModel->id]); } // Fallback to a standard resource mapping $resourcePluralName = ActivityLogHelper::getResourcePluralName($record->subject_type); return route('filament.'.$panelID.'.resources.'.$resourcePluralName.'.edit', ['record' => $record->subject_id]); } catch (Exception $e) { // If there's any error generating the URL, return placeholder return '#'; } } return '#'; } public static function form(Schema $schema): Schema { return ActivitylogForm::configure($schema); } protected static function flattenArrayForKeyValue(array $data): array { $flattened = []; foreach ($data as $key => $value) { if (is_array($value) || is_object($value)) { $flattened[$key] = json_encode($value, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE); } else { $flattened[$key] = $value; } } return $flattened; } public static function table(Table $table): Table { return $table ->columns([ static::getLogNameColumnComponent(), static::getEventColumnComponent(), static::getSubjectTypeColumnComponent(), static::getCauserNameColumnComponent(), static::getPropertiesColumnComponent(), static::getCreatedAtColumnComponent(), ]) ->defaultSort( config('filament-activitylog.resources.default_sort_column', 'created_at'), config('filament-activitylog.resources.default_sort_direction', 'desc') ) ->filters([ static::getDateFilterComponent(), static::getEventFilterComponent(), static::getLogNameFilterComponent(), ]); } public static function getEloquentQuery(): Builder { return parent::getEloquentQuery() ->with([ 'subject' => function ($query) { if (method_exists($query, 'withTrashed')) { $query->withTrashed(); } }, 'causer', ]); } public static function getLogNameColumnComponent(): Column { return TextColumn::make('log_name') ->label(__('activitylog::tables.columns.log_name.label')) ->formatStateUsing(fn ($state) => $state ? ucwords($state) : '-') ->searchable() ->sortable() ->badge(); } public static function getEventColumnComponent(): Column { return TextColumn::make('event') ->label(__('activitylog::tables.columns.event.label')) ->formatStateUsing(fn ($state) => $state ? ucwords(__('activitylog::action.event.'.$state)) : '-') ->badge() ->color(fn (?string $state): string => match ($state) { 'draft' => 'gray', 'updated' => 'warning', 'created' => 'success', 'deleted' => 'danger', 'restored' => 'info', default => 'primary', }) ->searchable() ->sortable(); } public static function getSubjectTypeColumnComponent(): Column { return TextColumn::make('subject_type') ->label(__('activitylog::tables.columns.subject_type.label')) ->formatStateUsing(function ($state, Model $record) { /** @var Activity $record */ if (! $state) { return '-'; } $subjectInfo = Str::of($state)->afterLast('\\')->headline().' # '.$record->subject_id; if ($record->subject) { if (method_exists($record->subject, 'trashed') && $record->subject->trashed()) { $subjectInfo .= __('activitylog::tables.columns.subject_type.soft_deleted'); } } else { $subjectInfo .= __('activitylog::tables.columns.subject_type.deleted'); } return $subjectInfo; }) ->searchable() ->hidden(fn (Livewire $livewire) => $livewire instanceof ActivitylogRelationManager); } public static function getCauserNameColumnComponent(): Column { return TextColumn::make('causer.name') ->label(__('activitylog::tables.columns.causer.label')) ->getStateUsing(function (Model $record) { /** @var Activity $record */ if ($record->causer_id === null || $record->causer === null) { return new HtmlString('—'); } return $record->causer->name ?? new HtmlString('—'); }) ->searchable(); } public static function getPropertiesColumnComponent(): Column { return ViewColumn::make('properties') ->searchable() ->label(__('activitylog::tables.columns.properties.label')) ->view('activitylog::filament.tables.columns.activity-logs-properties') ->toggleable(isToggledHiddenByDefault: true); } public static function getCreatedAtColumnComponent(): Column { $column = TextColumn::make('created_at') ->label(__('activitylog::tables.columns.created_at.label')) ->dateTime(ActivitylogPlugin::get()->getDatetimeFormat()) ->searchable() ->sortable(); // Apply the custom callback if set $callback = ActivitylogPlugin::get()->getDatetimeColumnCallback(); if ($callback) { $column = $callback($column); } return $column; } public static function getDatePickerCompoment(string $label): DatePicker { $field = DatePicker::make($label) ->format(ActivitylogPlugin::get()->getDateFormat()) ->label(__('activitylog::tables.filters.created_at.'.$label)); // Apply the custom callback if set $callback = ActivitylogPlugin::get()->getDatePickerCallback(); if ($callback) { $field = $callback($field); } return $field; } public static function getDateFilterComponent(): Filter { return Filter::make('created_at') ->label(__('activitylog::tables.filters.created_at.label')) ->indicateUsing(function (array $data): array { $indicators = []; $parser = ActivitylogPlugin::get()->getDateParser(); if ($data['created_from'] ?? null) { $indicators['created_from'] = __('activitylog::tables.filters.created_at.created_from_indicator', [ 'created_from' => $parser($data['created_from']) ->format(ActivitylogPlugin::get()->getDateFormat()), ]); } if ($data['created_until'] ?? null) { $indicators['created_until'] = __('activitylog::tables.filters.created_at.created_until_indicator', [ 'created_until' => $parser($data['created_until']) ->format(ActivitylogPlugin::get()->getDateFormat()), ]); } return $indicators; }) ->form([ self::getDatePickerCompoment('created_from'), self::getDatePickerCompoment('created_until'), ]) ->query(function (Builder $query, array $data): Builder { return $query ->when( $data['created_from'] ?? null, fn (Builder $query, $date): Builder => $query->whereDate('created_at', '>=', $date), ) ->when( $data['created_until'] ?? null, fn (Builder $query, $date): Builder => $query->whereDate('created_at', '<=', $date), ); }); } public static function getEventFilterComponent(): SelectFilter { return SelectFilter::make('event') ->label(__('activitylog::tables.filters.event.label')) ->options(static::getModel()::distinct() ->pluck('event', 'event') ->mapWithKeys(fn ($value, $key) => [$key => __('activitylog::action.event.'.$value)]) ); } public static function getLogNameFilterComponent(): SelectFilter { return SelectFilter::make('log_name') ->label(__('activitylog::tables.filters.log_name.label')) ->options(static::getModel()::distinct()->pluck('log_name', 'log_name')->filter()); } public static function getPages(): array { return [ 'index' => ListActivitylog::route('/'), 'view' => ViewActivitylog::route('/{record}'), ]; } public static function shouldRegisterNavigation(): bool { $plugin = Filament::getCurrentPanel()?->getPlugin('rmsramos/activitylog'); return $plugin?->getNavigationItem() ?? false; } public static function canAccess(): bool { $policy = Gate::getPolicyFor(static::getModel()); if ($policy && method_exists($policy, 'viewAny')) { return static::canViewAny(); } return ActivitylogPlugin::get()->isAuthorized(); } protected static function canViewResource(Activity $record): bool { if ($record->subject_type && $record->subject_id) { try { $model = app($record->subject_type); if (ActivityLogHelper::classUsesTrait($model, HasCustomActivityResource::class)) { $resourceModel = $model->getFilamentActualResourceModel($record); $user = auth()->user(); return $user && $user->can('update', $resourceModel); } // Fallback to check if the user can edit the model using a generic policy $user = auth()->user(); return $user && $record->subject && $user->can('update', $record->subject); } catch (Exception $e) { return false; } } return false; } public static function restoreActivity(int|string $key): void { $activity = Activity::find($key); if (! $activity) { Notification::make() ->title(__('activitylog::notifications.activity_not_found')) ->danger() ->send(); return; } $oldProperties = data_get($activity, 'properties.old'); $newProperties = data_get($activity, 'properties.attributes'); if ($oldProperties === null) { Notification::make() ->title(__('activitylog::notifications.no_properties_to_restore')) ->danger() ->send(); return; } try { $record = $activity->subject; if (! $record) { Notification::make() ->title(__('activitylog::notifications.subject_not_found')) ->danger() ->send(); return; } // Temporarily disable activity logging to prevent updated log activity()->withoutLogs(function () use ($record, $oldProperties) { $record->update($oldProperties); }); if (auth()->user()) { activity() ->performedOn($record) ->causedBy(auth()->user()) ->withProperties([ 'attributes' => $oldProperties, 'old' => $newProperties, ]) ->tap(function ($log) { $log->event = 'restored'; }) ->log('restored'); } Notification::make() ->title(__('activitylog::notifications.activity_restored_successfully')) ->success() ->send(); } catch (ModelNotFoundException $e) { Notification::make() ->title(__('activitylog::notifications.record_not_found')) ->danger() ->send(); } catch (Exception $e) { Notification::make() ->title(__('activitylog::notifications.failed_to_restore_activity', ['error' => $e->getMessage()])) ->danger() ->send(); } } public static function canRestoreSubjectFromSoftDelete(Activity $record): bool { if (ActivitylogPlugin::get()->getIsRestoreModelActionHidden()) { return false; } if ($record->event !== 'deleted') { return false; } if (! $record->subject) { return false; } if (! method_exists($record->subject, 'trashed') || ! method_exists($record->subject, 'restore')) { return false; } if (! $record->subject->trashed()) { return false; } $user = auth()->user(); if ($user && method_exists($record->subject, 'exists')) { try { return $user->can('restore', $record->subject); } catch (\Exception $e) { return true; } } return true; } public static function restoreSubjectFromSoftDelete(Activity $record): void { if (! static::canRestoreSubjectFromSoftDelete($record)) { Notification::make() ->title(__('activitylog::notifications.unable_to_restore_this_model')) ->danger() ->send(); return; } try { DB::beginTransaction(); $subject = $record->subject; $beforeRestore = $subject->toArray(); activity()->withoutLogs(function () use ($subject) { $subject->restore(); }); $subject->refresh(); $afterRestore = $subject->toArray(); if (auth()->user()) { activity() ->performedOn($subject) ->causedBy(auth()->user()) ->withProperties([ 'attributes' => $afterRestore, 'old' => $beforeRestore, 'restore_metadata' => [ 'restored_from_soft_delete' => true, 'original_activity_id' => $record->id, 'restore_type' => 'soft_delete', ], ]) ->tap(function ($log) { $log->event = 'restored'; }) ->log('restored'); } DB::commit(); Notification::make() ->title(__('activitylog::notifications.model_successfully_restored')) ->success() ->send(); } catch (Exception $e) { DB::rollBack(); Notification::make() ->title(__('activitylog::notifications.error_restoring_model')) ->body('Erro: '.$e->getMessage()) ->danger() ->send(); } } } ================================================ FILE: src/Resources/ActivitylogResource/Pages/ListActivitylog.php ================================================ components([ Section::make([ TextInput::make('causer_id') ->afterStateHydrated(function ($component, ?Model $record) { return $component->state($record?->causer?->name ?? '-'); }) ->label(__('activitylog::forms.fields.causer.label')), TextInput::make('subject_type') ->afterStateHydrated(function ($component, ?Model $record, $state) { /** @var Activity $record */ return $state ? $component->state(Str::of($state)->afterLast('\\')->headline().' # '.$record->subject_id) : $component->state('-'); }) ->label(__('activitylog::forms.fields.subject_type.label')), Textarea::make('description') ->label(__('activitylog::forms.fields.description.label')) ->rows(2) ->columnSpan('full'), ]), Section::make([ TextEntry::make('log_name') ->content(function (?Model $record): string { /** @var Activity $record */ return $record?->log_name ? ucwords($record->log_name) : '-'; }) ->label(__('activitylog::forms.fields.log_name.label')), TextEntry::make('event') ->content(function (?Model $record): string { /** @var Activity $record */ return $record?->event ? ucwords(__('activitylog::action.event.'.$record->event)) : '-'; }) ->label(__('activitylog::forms.fields.event.label')), TextEntry::make('created_at') ->label(__('activitylog::forms.fields.created_at.label')) ->content(function (?Model $record): string { /** @var Activity $record */ if (! $record?->created_at) { return '-'; } $parser = ActivitylogPlugin::get()->getDateParser(); return $parser($record->created_at) ->format(ActivitylogPlugin::get()->getDatetimeFormat()); }), ]), ]); } } ================================================ FILE: src/Traits/HasCustomActivityResource.php ================================================