diff --git a/app/Actions/Storage/Admin/Dump/DumpAdminAction.php b/app/Actions/Storage/Admin/Dump/DumpAdminAction.php index a41810254..f69986cf4 100644 --- a/app/Actions/Storage/Admin/Dump/DumpAdminAction.php +++ b/app/Actions/Storage/Admin/Dump/DumpAdminAction.php @@ -5,7 +5,7 @@ declare(strict_types=1); namespace App\Actions\Storage\Admin\Dump; use App\Concerns\Repositories\Admin\ReconcilesDumpRepositories; -use App\Models\Admin\ActionLog; +use App\Models\Admin\Activity; use App\Models\Admin\Announcement; use App\Models\Admin\Dump; use App\Models\Admin\Feature; @@ -27,7 +27,8 @@ class DumpAdminAction extends DumpAction { return [ 'action_events', // Nova events - ActionLog::TABLE, + 'action_logs', + Activity::TABLE, Announcement::TABLE, Dump::TABLE, Feature::TABLE, diff --git a/app/Concerns/Filament/ActionLogs/HasActionLogs.php b/app/Concerns/Filament/ActionLogs/HasActionLogs.php deleted file mode 100644 index d6291f81d..000000000 --- a/app/Concerns/Filament/ActionLogs/HasActionLogs.php +++ /dev/null @@ -1,86 +0,0 @@ -batchId = Str::orderedUuid()->__toString(); - - return $this->batchId; - } - - public function createActionLog(Action $action, Model $record, ?bool $shouldCreateNewBatchId = true): void - { - if ($shouldCreateNewBatchId) { - $this->createBatchId(); - } - - $this->recordLog = $record; - - $actionLog = ActionLog::modelActioned( - $this->batchId, - $action, - $this->recordLog, - ); - - $this->actionLog = $actionLog; - } - - public function updateLog(Model $relatedModel, Model $pivot): void - { - $this->actionLog->update([ - ActionLog::ATTRIBUTE_TARGET_TYPE => $relatedModel->getMorphClass(), - ActionLog::ATTRIBUTE_TARGET_ID => $relatedModel->getKey(), - ActionLog::ATTRIBUTE_MODEL_TYPE => $pivot->getMorphClass(), - ActionLog::ATTRIBUTE_MODEL_ID => $pivot->getKey(), - ]); - } - - public function failedLog(Throwable|string|null $exception): void - { - $this->actionLog->failed($exception); - - // Filament notifications - if ($this instanceof Action) { - $this->failureNotificationTitle(Str::limit($exception, 200)); - $this->failure(); - } - } - - public function finishedLog(): void - { - if (($actionLog = $this->actionLog) && ! $this->hasFailedLog()) { - $actionLog->finished(); - // Filament notifications - if ($this instanceof Action) { - $this->success(); - } - } - } - - public function batchFinishedLog(): void - { - $this->actionLog->batchFinished(); - } - - public function hasFailedLog(): bool - { - return $this->actionLog->hasFailed(); - } -} diff --git a/app/Concerns/Filament/ActionLogs/HasPivotActionLogs.php b/app/Concerns/Filament/ActionLogs/HasPivotActionLogs.php deleted file mode 100644 index 5add24300..000000000 --- a/app/Concerns/Filament/ActionLogs/HasPivotActionLogs.php +++ /dev/null @@ -1,48 +0,0 @@ -getOwnerRecord(); - - $relation = $livewire->getRelationship(); - - if ($relation instanceof BelongsToMany) { - ActionLog::modelPivot( - $actionName, - $ownerRecord, - $record, - $record->getRelationValue($relation->getPivotAccessor()) ?? $record, - $action, - ); - } - } - - public function associateActionLog(string $actionName, BaseRelationManager $livewire, Model $record, Action $action): void - { - $ownerRecord = $livewire->getOwnerRecord(); - - $relation = $livewire->getRelationship(); - - if ($relation instanceof HasMany) { - ActionLog::modelAssociated( - $actionName, - $ownerRecord, - $record, - $action, - ); - } - } -} diff --git a/app/Concerns/Filament/ActionLogs/ModelHasActionLogs.php b/app/Concerns/Filament/ActionLogs/ModelHasActionLogs.php deleted file mode 100644 index 9385da62e..000000000 --- a/app/Concerns/Filament/ActionLogs/ModelHasActionLogs.php +++ /dev/null @@ -1,16 +0,0 @@ -morphMany(ActionLog::class, 'actionable'); - } -} diff --git a/app/Concerns/Filament/HasActivityLogs.php b/app/Concerns/Filament/HasActivityLogs.php new file mode 100644 index 000000000..d563c3104 --- /dev/null +++ b/app/Concerns/Filament/HasActivityLogs.php @@ -0,0 +1,66 @@ +activity = activity() + ->performedOn($record) + ->withProperties($action->getData()) + ->tap(function (Activity $activity): void { + $activity->status = ActivityStatus::RUNNING; + }) + ->event($action->getLabel()) + ->log('actioned'); + } + + public function failedLog(Throwable|string|null $exception): void + { + $this->activity?->update([ + Activity::ATTRIBUTE_STATUS => ActivityStatus::FAILED, + Activity::ATTRIBUTE_EXCEPTION => Str::limit($exception, 200), + Activity::ATTRIBUTE_FINISHED_AT => now(), + ]); + + // Filament notifications + if ($this instanceof Action) { + $this->failureNotificationTitle(Str::limit($exception, 200)); + $this->failure(); + } + } + + public function finishedLog(): void + { + if (! $this->hasFailedLog()) { + $this->activity?->update([ + Activity::ATTRIBUTE_STATUS => ActivityStatus::FINISHED, + Activity::ATTRIBUTE_FINISHED_AT => now(), + ]); + + // Filament notifications + if ($this instanceof Action) { + $this->success(); + } + } + } + + public function hasFailedLog(): bool + { + return $this->activity?->status === ActivityStatus::FAILED; + } +} diff --git a/app/Enums/Models/Admin/ActionLogStatus.php b/app/Enums/Models/Admin/ActivityStatus.php similarity index 71% rename from app/Enums/Models/Admin/ActionLogStatus.php rename to app/Enums/Models/Admin/ActivityStatus.php index a0a25574d..81bbbdbd7 100644 --- a/app/Enums/Models/Admin/ActionLogStatus.php +++ b/app/Enums/Models/Admin/ActivityStatus.php @@ -9,7 +9,7 @@ use Filament\Support\Colors\Color; use Filament\Support\Contracts\HasColor; use Filament\Support\Contracts\HasLabel; -enum ActionLogStatus: int implements HasColor, HasLabel +enum ActivityStatus: int implements HasColor, HasLabel { use LocalizesName; @@ -25,9 +25,9 @@ enum ActionLogStatus: int implements HasColor, HasLabel public function getColor(): string|array { return match ($this) { - ActionLogStatus::RUNNING => Color::Amber, - ActionLogStatus::FAILED => 'danger', - ActionLogStatus::FINISHED => 'success', + ActivityStatus::RUNNING => Color::Amber, + ActivityStatus::FAILED => 'danger', + ActivityStatus::FINISHED => 'success', }; } } diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php index 7c606bba1..6a74441d1 100644 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -4,8 +4,6 @@ declare(strict_types=1); namespace App\Exceptions; -use App\Models\Admin\ActionLog; -use Filament\Facades\Filament; use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; use Throwable; @@ -27,10 +25,6 @@ class Handler extends ExceptionHandler */ public function register(): void { - $this->reportable(function (Throwable $e): void { - if (Filament::isServing() && $this->shouldReport($e)) { - ActionLog::updateCurrentActionLogToFailed($e); - } - }); + $this->reportable(function (Throwable $e): void {}); } } diff --git a/app/Filament/Actions/Base/AttachAction.php b/app/Filament/Actions/Base/AttachAction.php index 48c3e708a..110fb94c0 100644 --- a/app/Filament/Actions/Base/AttachAction.php +++ b/app/Filament/Actions/Base/AttachAction.php @@ -4,10 +4,8 @@ declare(strict_types=1); namespace App\Filament\Actions\Base; -use App\Concerns\Filament\ActionLogs\HasPivotActionLogs; use App\Filament\Components\Fields\Select; use App\Filament\RelationManagers\BaseRelationManager; -use App\Models\Admin\ActionLog; use App\Models\Wiki\Image; use Filament\Actions\AttachAction as BaseAttachAction; use Filament\Facades\Filament; @@ -20,8 +18,6 @@ use Illuminate\Support\Str; class AttachAction extends BaseAttachAction { - use HasPivotActionLogs; - protected function setUp(): void { parent::setUp(); @@ -56,13 +52,7 @@ class AttachAction extends BaseAttachAction if ($this->shouldShowCreateOption($model)) { return $select ->createOptionForm(fn (Schema $schema) => Filament::getModelResource($model)::form($schema)->getComponents()) - ->createOptionUsing(function (array $data) use ($model) { - $created = $model::query()->create($data); - - ActionLog::modelCreated($created); - - return $created->getKey(); - }); + ->createOptionUsing(fn (array $data) => $model::query()->create($data)->getKey()); } return $select; @@ -72,8 +62,6 @@ class AttachAction extends BaseAttachAction $action->getRecordSelect(), ...$livewire->getPivotComponents(), ]); - - $this->after(fn (BaseRelationManager $livewire, Model $record, AttachAction $action) => $this->pivotActionLog('Attach', $livewire, $record, $action)); } /** diff --git a/app/Filament/Actions/Base/CreateAction.php b/app/Filament/Actions/Base/CreateAction.php index bb6527776..ae9041c47 100644 --- a/app/Filament/Actions/Base/CreateAction.php +++ b/app/Filament/Actions/Base/CreateAction.php @@ -4,23 +4,18 @@ declare(strict_types=1); namespace App\Filament\Actions\Base; -use App\Concerns\Filament\ActionLogs\HasPivotActionLogs; use App\Filament\RelationManagers\BaseRelationManager; use App\Filament\Resources\Base\BaseListResources; use App\Filament\Resources\Base\BaseManageResources; -use App\Models\Admin\ActionLog; use Filament\Actions\CreateAction as BaseCreateAction; use Filament\Facades\Filament; use Filament\Schemas\Schema; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsToMany; -use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Support\Facades\Gate; class CreateAction extends BaseCreateAction { - use HasPivotActionLogs; - protected function setUp(): void { parent::setUp(); @@ -38,20 +33,6 @@ class CreateAction extends BaseCreateAction return null; }); - $this->after(function (BaseManageResources|BaseListResources|BaseRelationManager $livewire, Model $record, CreateAction $action): void { - if ($livewire instanceof BaseListResources) { - ActionLog::modelCreated($record); - } - - if ($livewire instanceof BaseRelationManager) { - $relationship = $livewire->getRelationship(); - - if ($relationship instanceof HasMany) { - $this->associateActionLog('Create and Associate', $livewire, $record, $action); - } - } - }); - $this->visible(function (BaseManageResources|BaseListResources|BaseRelationManager $livewire, string $model): bool { if ($livewire instanceof BaseRelationManager && $livewire->getRelationship() instanceof BelongsToMany) { return false; diff --git a/app/Filament/Actions/Base/DeleteAction.php b/app/Filament/Actions/Base/DeleteAction.php index e3f20d4ec..67b547461 100644 --- a/app/Filament/Actions/Base/DeleteAction.php +++ b/app/Filament/Actions/Base/DeleteAction.php @@ -5,7 +5,6 @@ declare(strict_types=1); namespace App\Filament\Actions\Base; use App\Actions\Http\Api\List\Playlist\Track\DestroyTrackAction; -use App\Models\Admin\ActionLog; use App\Models\List\Playlist\PlaylistTrack; use Filament\Actions\DeleteAction as BaseDeleteAction; use Filament\Support\Icons\Heroicon; @@ -36,8 +35,6 @@ class DeleteAction extends BaseDeleteAction return (bool) $result; }); - $this->after(fn (Model $record): ActionLog => ActionLog::modelDeleted($record)); - $this->authorize(true); } } diff --git a/app/Filament/Actions/Base/DetachAction.php b/app/Filament/Actions/Base/DetachAction.php index 59f3b0863..dd4b91325 100644 --- a/app/Filament/Actions/Base/DetachAction.php +++ b/app/Filament/Actions/Base/DetachAction.php @@ -4,13 +4,11 @@ declare(strict_types=1); namespace App\Filament\Actions\Base; -use App\Concerns\Filament\ActionLogs\HasPivotActionLogs; use App\Filament\RelationManagers\BaseRelationManager; use App\Filament\Resources\Base\BaseListResources; use App\Filament\Resources\Base\BaseManageResources; use App\Filament\Resources\Base\BaseViewResource; use Filament\Actions\DetachAction as BaseDetachAction; -use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Gate; @@ -18,8 +16,6 @@ use Illuminate\Support\Str; class DetachAction extends BaseDetachAction { - use HasPivotActionLogs; - protected function setUp(): void { parent::setUp(); @@ -54,13 +50,5 @@ class DetachAction extends BaseDetachAction }); $this->authorize(true); - - $this->after(function (BaseRelationManager $livewire, Model $record): void { - $relationship = $livewire->getRelationship(); - - if ($relationship instanceof BelongsToMany) { - $this->pivotActionLog('Detach', $livewire, $record); - } - }); } } diff --git a/app/Filament/Actions/Base/EditAction.php b/app/Filament/Actions/Base/EditAction.php index 66f9aff90..0a4de4fda 100644 --- a/app/Filament/Actions/Base/EditAction.php +++ b/app/Filament/Actions/Base/EditAction.php @@ -4,26 +4,19 @@ declare(strict_types=1); namespace App\Filament\Actions\Base; -use App\Concerns\Filament\ActionLogs\HasActionLogs; -use App\Concerns\Filament\ActionLogs\HasPivotActionLogs; use App\Filament\RelationManagers\BaseRelationManager; use App\Filament\Resources\Base\BaseListResources; use App\Filament\Resources\Base\BaseManageResources; use App\Filament\Resources\Base\BaseViewResource; -use App\Models\Admin\ActionLog; use Filament\Actions\EditAction as BaseEditAction; use Filament\Schemas\Schema; use Filament\Support\Enums\IconSize; use Filament\Support\Icons\Heroicon; use Illuminate\Database\Eloquent\Model; -use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Support\Facades\Gate; class EditAction extends BaseEditAction { - use HasActionLogs; - use HasPivotActionLogs; - protected function setUp(): void { parent::setUp(); @@ -38,16 +31,6 @@ class EditAction extends BaseEditAction ...($livewire instanceof BaseRelationManager ? $livewire->getPivotComponents() : []), ]); - $this->after(function (BaseListResources|BaseViewResource|BaseManageResources|BaseRelationManager $livewire, Model $record, EditAction $action): void { - if ($livewire instanceof BaseListResources || $livewire instanceof BaseViewResource) { - ActionLog::modelUpdated($record); - } - - if ($livewire instanceof BaseRelationManager && $livewire->getRelationship() instanceof BelongsToMany) { - $this->pivotActionLog('Update Attached', $livewire, $record, $action); - } - }); - $this->beforeFormFilled(function (Model $record): void { Gate::authorize('update', $record); }); diff --git a/app/Filament/Actions/Base/ReplicateAction.php b/app/Filament/Actions/Base/ReplicateAction.php index 6ce51f44b..eeff5aa71 100644 --- a/app/Filament/Actions/Base/ReplicateAction.php +++ b/app/Filament/Actions/Base/ReplicateAction.php @@ -7,7 +7,6 @@ namespace App\Filament\Actions\Base; use App\Filament\RelationManagers\BaseRelationManager; use App\Filament\Resources\Base\BaseListResources; use App\Filament\Resources\Base\BaseManageResources; -use App\Models\Admin\ActionLog; use Filament\Actions\ReplicateAction as BaseReplicateAction; use Filament\Facades\Filament; use Filament\Schemas\Schema; @@ -22,7 +21,5 @@ class ReplicateAction extends BaseReplicateAction $this->schema(fn (Schema $schema, BaseListResources|BaseManageResources|BaseRelationManager $livewire): array => $livewire->form($schema)->getComponents()); $this->successRedirectUrl(fn (Model $replica) => Filament::getModelResource($replica)::getUrl('view', ['record' => $replica])); - - $this->after(fn (Model $replica): ActionLog => ActionLog::modelCreated($replica)); } } diff --git a/app/Filament/Actions/Base/RestoreAction.php b/app/Filament/Actions/Base/RestoreAction.php index f554c6182..0392e9bb1 100644 --- a/app/Filament/Actions/Base/RestoreAction.php +++ b/app/Filament/Actions/Base/RestoreAction.php @@ -5,7 +5,6 @@ declare(strict_types=1); namespace App\Filament\Actions\Base; use App\Contracts\Models\SoftDeletable; -use App\Models\Admin\ActionLog; use Filament\Actions\RestoreAction as BaseRestoreAction; use Filament\Support\Icons\Heroicon; use Illuminate\Database\Eloquent\Model; @@ -33,8 +32,6 @@ class RestoreAction extends BaseRestoreAction return $record->trashed(); }); - $this->after(fn (Model $record): ActionLog => ActionLog::modelRestored($record)); - $this->using(function (Model&SoftDeletable $record): bool { Gate::authorize('restore', $record); diff --git a/app/Filament/Actions/BaseAction.php b/app/Filament/Actions/BaseAction.php index aec84fbab..6e4c62edd 100644 --- a/app/Filament/Actions/BaseAction.php +++ b/app/Filament/Actions/BaseAction.php @@ -4,7 +4,8 @@ declare(strict_types=1); namespace App\Filament\Actions; -use App\Concerns\Filament\ActionLogs\HasActionLogs; +use App\Concerns\Filament\HasActivityLogs; +use App\Enums\Models\Admin\ActivityStatus; use App\Filament\RelationManagers\BaseRelationManager; use App\Filament\Resources\Base\BaseViewResource; use Filament\Actions\Action; @@ -15,7 +16,7 @@ use Illuminate\Support\Str; abstract class BaseAction extends Action { - use HasActionLogs; + use HasActivityLogs; public static function getDefaultName(): ?string { @@ -32,10 +33,10 @@ abstract class BaseAction extends Action $this->before(function (BaseAction $action, mixed $livewire): void { if ($livewire instanceof BaseRelationManager) { - $this->createActionLog($action, $livewire->getOwnerRecord()); + $this->createActivityLog($action, $livewire->getOwnerRecord()); $livewire->dispatch('updateAllRelationManager'); } elseif (($record = $this->getRecord()) instanceof Model) { - $this->createActionLog($action, $record); + $this->createActivityLog($action, $record); } }); @@ -44,6 +45,11 @@ abstract class BaseAction extends Action return; } + $this->activity?->update([ + 'status' => ActivityStatus::FINISHED, + 'finished_at' => now(), + ]); + $this->finishedLog(); if ($livewire instanceof BaseViewResource || $livewire instanceof BaseRelationManager) { diff --git a/app/Filament/Actions/Storage/StorageAction.php b/app/Filament/Actions/Storage/StorageAction.php index bbd232b84..8c3c9d10e 100644 --- a/app/Filament/Actions/Storage/StorageAction.php +++ b/app/Filament/Actions/Storage/StorageAction.php @@ -6,9 +6,7 @@ namespace App\Filament\Actions\Storage; use App\Contracts\Actions\Storage\StorageAction as BaseStorageAction; use App\Filament\Actions\BaseAction; -use App\Filament\RelationManagers\BaseRelationManager; use Illuminate\Database\Eloquent\Model; -use Illuminate\Database\Eloquent\Relations\BelongsToMany; abstract class StorageAction extends BaseAction { @@ -57,22 +55,5 @@ abstract class StorageAction extends BaseAction } $this->afterStorageAction($record, $data); - - $livewire = $this->getLivewire(); - if ($livewire instanceof BaseRelationManager && $record instanceof Model) { - $relation = $livewire->getRelationship(); - $pivot = $record; - - if ($relation instanceof BelongsToMany) { - $pivotClass = $relation->getPivotClass(); - - $pivot = $pivotClass::query() - ->where($livewire->getOwnerRecord()->getKeyName(), $livewire->getOwnerRecord()->getKey()) - ->where($record->getKeyName(), $record->getKey()) - ->first(); - } - - $this->updateLog($record, $pivot); - } } } diff --git a/app/Filament/BulkActions/Base/DeleteBulkAction.php b/app/Filament/BulkActions/Base/DeleteBulkAction.php index 95bf441fe..44d935afa 100644 --- a/app/Filament/BulkActions/Base/DeleteBulkAction.php +++ b/app/Filament/BulkActions/Base/DeleteBulkAction.php @@ -4,15 +4,11 @@ declare(strict_types=1); namespace App\Filament\BulkActions\Base; -use App\Concerns\Filament\ActionLogs\HasActionLogs; use Filament\Actions\DeleteBulkAction as BaseDeleteBulkAction; -use Illuminate\Support\Collection; use Illuminate\Support\Facades\Gate; class DeleteBulkAction extends BaseDeleteBulkAction { - use HasActionLogs; - protected function setUp(): void { parent::setUp(); @@ -22,12 +18,5 @@ class DeleteBulkAction extends BaseDeleteBulkAction $this->authorize(true); $this->before(fn (string $model) => Gate::authorize('forceDeleteAny', $model)); - - $this->after(function (DeleteBulkAction $action, Collection $records): void { - foreach ($records as $record) { - $this->createActionLog($action, $record); - $this->finishedLog(); - } - }); } } diff --git a/app/Filament/BulkActions/Base/DetachBulkAction.php b/app/Filament/BulkActions/Base/DetachBulkAction.php index f636d623b..a58eb7978 100644 --- a/app/Filament/BulkActions/Base/DetachBulkAction.php +++ b/app/Filament/BulkActions/Base/DetachBulkAction.php @@ -4,15 +4,11 @@ declare(strict_types=1); namespace App\Filament\BulkActions\Base; -use App\Concerns\Filament\ActionLogs\HasActionLogs; use Filament\Actions\DetachBulkAction as BaseDetachBulkAction; -use Illuminate\Support\Collection; use Illuminate\Support\Facades\Gate; class DetachBulkAction extends BaseDetachBulkAction { - use HasActionLogs; - protected function setUp(): void { parent::setUp(); @@ -22,12 +18,5 @@ class DetachBulkAction extends BaseDetachBulkAction $this->authorize(true); $this->before(fn (string $model) => Gate::authorize('forceDeleteAny', $model)); - - $this->after(function (DetachBulkAction $action, Collection $records): void { - foreach ($records as $record) { - $this->createActionLog($action, $record); - $this->finishedLog(); - } - }); } } diff --git a/app/Filament/BulkActions/Base/ForceDeleteBulkAction.php b/app/Filament/BulkActions/Base/ForceDeleteBulkAction.php index b8d2c63c9..a3e331932 100644 --- a/app/Filament/BulkActions/Base/ForceDeleteBulkAction.php +++ b/app/Filament/BulkActions/Base/ForceDeleteBulkAction.php @@ -4,14 +4,11 @@ declare(strict_types=1); namespace App\Filament\BulkActions\Base; -use App\Concerns\Filament\ActionLogs\HasActionLogs; use Filament\Actions\ForceDeleteBulkAction as BaseForceDeleteBulkAction; use Illuminate\Support\Facades\Gate; class ForceDeleteBulkAction extends BaseForceDeleteBulkAction { - use HasActionLogs; - protected function setUp(): void { parent::setUp(); diff --git a/app/Filament/BulkActions/Base/RestoreBulkAction.php b/app/Filament/BulkActions/Base/RestoreBulkAction.php index c0559de75..d1fbab3e2 100644 --- a/app/Filament/BulkActions/Base/RestoreBulkAction.php +++ b/app/Filament/BulkActions/Base/RestoreBulkAction.php @@ -4,15 +4,11 @@ declare(strict_types=1); namespace App\Filament\BulkActions\Base; -use App\Concerns\Filament\ActionLogs\HasActionLogs; use Filament\Actions\RestoreBulkAction as BaseRestoreBulkAction; -use Illuminate\Support\Collection; use Illuminate\Support\Facades\Gate; class RestoreBulkAction extends BaseRestoreBulkAction { - use HasActionLogs; - protected function setUp(): void { parent::setUp(); @@ -22,12 +18,5 @@ class RestoreBulkAction extends BaseRestoreBulkAction $this->authorize(true); $this->before(fn (string $model) => Gate::authorize('restoreAny', $model)); - - $this->after(function (RestoreBulkAction $action, Collection $records): void { - foreach ($records as $record) { - $this->createActionLog($action, $record); - $this->finishedLog(); - } - }); } } diff --git a/app/Filament/BulkActions/BaseBulkAction.php b/app/Filament/BulkActions/BaseBulkAction.php index 33bd19705..04aba2ebf 100644 --- a/app/Filament/BulkActions/BaseBulkAction.php +++ b/app/Filament/BulkActions/BaseBulkAction.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace App\Filament\BulkActions; -use App\Concerns\Filament\ActionLogs\HasActionLogs; use Filament\Actions\BulkAction; use Filament\Support\Enums\Width; use Illuminate\Support\Collection; @@ -14,22 +13,12 @@ use Illuminate\Support\Collection; */ abstract class BaseBulkAction extends BulkAction { - use HasActionLogs; - protected function setUp(): void { parent::setUp(); $this->requiresConfirmation(); - $this->before(function (BaseBulkAction $action, Collection $records): void { - foreach ($records as $record) { - $this->createActionLog($action, $record, false); - } - }); - - $this->after(fn () => $this->batchFinishedLog()); - $this->modalWidth(Width::FourExtraLarge); $this->action(fn (Collection $records, array $data) => $this->handle($records, $data)); diff --git a/app/Filament/BulkActions/Models/Wiki/Video/VideoDiscordNotificationBulkAction.php b/app/Filament/BulkActions/Models/Wiki/Video/VideoDiscordNotificationBulkAction.php index a860e9a23..f9ac0af8f 100644 --- a/app/Filament/BulkActions/Models/Wiki/Video/VideoDiscordNotificationBulkAction.php +++ b/app/Filament/BulkActions/Models/Wiki/Video/VideoDiscordNotificationBulkAction.php @@ -44,11 +44,7 @@ class VideoDiscordNotificationBulkAction extends BaseBulkAction $action = new DiscordVideoNotificationActionAction(); - $result = $action->handle($videos, $data); - - if ($result->hasFailed()) { - $this->failedLog($result->getMessage()); - } + $action->handle($videos, $data); } /** diff --git a/app/Filament/BulkActions/Storage/StorageBulkAction.php b/app/Filament/BulkActions/Storage/StorageBulkAction.php index f5cf7d98a..d22b105d7 100644 --- a/app/Filament/BulkActions/Storage/StorageBulkAction.php +++ b/app/Filament/BulkActions/Storage/StorageBulkAction.php @@ -34,10 +34,6 @@ abstract class StorageBulkAction extends BaseBulkAction $action->then($storageResults); $actionResult = $storageResults->toActionResult(); - - if ($actionResult->hasFailed()) { - $this->failedLog($actionResult->getMessage()); - } } } } diff --git a/app/Filament/RelationManagers/Base/ActionLogRelationManager.php b/app/Filament/RelationManagers/Base/ActivityRelationManager.php similarity index 55% rename from app/Filament/RelationManagers/Base/ActionLogRelationManager.php rename to app/Filament/RelationManagers/Base/ActivityRelationManager.php index e7964269c..fbd748ae4 100644 --- a/app/Filament/RelationManagers/Base/ActionLogRelationManager.php +++ b/app/Filament/RelationManagers/Base/ActivityRelationManager.php @@ -5,23 +5,28 @@ declare(strict_types=1); namespace App\Filament\RelationManagers\Base; use App\Filament\RelationManagers\BaseRelationManager; -use App\Filament\Resources\Admin\ActionLogResource; +use App\Filament\Resources\Admin\ActivityResource; use App\Filament\Resources\BaseResource; -use App\Models\Admin\ActionLog; +use App\Models\Admin\Activity; use Filament\Tables\Table; -class ActionLogRelationManager extends BaseRelationManager +class ActivityRelationManager extends BaseRelationManager { - protected static string $relationship = 'actionlogs'; + protected static string $relationship = 'activities'; - protected static ?string $recordTitleAttribute = ActionLog::ATTRIBUTE_ID; + protected static ?string $recordTitleAttribute = Activity::ATTRIBUTE_ID; /** * The resource of the relation manager. * * @var class-string|null */ - protected static ?string $relatedResource = ActionLogResource::class; + protected static ?string $relatedResource = ActivityResource::class; + + public static function isLazy(): true + { + return true; + } public function table(Table $table): Table { diff --git a/app/Filament/Resources/Admin/ActionLog/Pages/ManageActionLogs.php b/app/Filament/Resources/Admin/ActionLog/Pages/ManageActionLogs.php deleted file mode 100644 index 2f7433b63..000000000 --- a/app/Filament/Resources/Admin/ActionLog/Pages/ManageActionLogs.php +++ /dev/null @@ -1,13 +0,0 @@ -|null */ - protected static ?string $model = ActionLog::class; + protected static ?string $model = Activity::class; public static function getModelLabel(): string { - return __('filament.resources.singularLabel.action_log'); + return __('filament.resources.singularLabel.activity'); } public static function getPluralModelLabel(): string { - return __('filament.resources.label.action_logs'); + return __('filament.resources.label.activities'); } public static function getNavigationGroup(): NavigationGroup @@ -69,18 +67,7 @@ class ActionLogResource extends BaseResource public static function getRecordTitleAttribute(): string { - return ActionLog::ATTRIBUTE_NAME; - } - - public static function getEloquentQuery(): Builder - { - $query = parent::getEloquentQuery(); - - // Necessary to prevent lazy loading when loading related resources - return $query->with([ - ActionLog::RELATION_USER, - ActionLog::RELATION_TARGET, - ]); + return Activity::ATTRIBUTE_NAME; } public static function form(Schema $schema): Schema @@ -88,10 +75,10 @@ class ActionLogResource extends BaseResource return $schema ->components([ // TODO: JSON values are not being displayed - KeyValue::make(ActionLog::ATTRIBUTE_FIELDS) - ->label(__('filament.fields.action_log.fields.name')) - ->keyLabel(__('filament.fields.action_log.fields.keys')) - ->valueLabel(__('filament.fields.action_log.fields.values')) + KeyValue::make(Activity::ATTRIBUTE_PROPERTIES) + ->label(__('filament.fields.activity.fields.name')) + ->keyLabel(__('filament.fields.activity.fields.keys')) + ->valueLabel(__('filament.fields.activity.fields.values')) ->columnSpanFull() ->hidden(fn ($state): bool => is_null($state)) ->formatStateUsing(fn (?array $state) => collect($state)->mapWithKeys(function ($value, $key): array { @@ -102,8 +89,8 @@ class ActionLogResource extends BaseResource return [$key => blank($value) ? '-' : $value]; })->toArray()), - Textarea::make(ActionLog::ATTRIBUTE_EXCEPTION) - ->label(__('filament.fields.action_log.exception')) + Textarea::make(Activity::ATTRIBUTE_EXCEPTION) + ->label(__('filament.fields.activity.exception')) ->disabled() ->columnSpanFull(), ]); @@ -114,30 +101,30 @@ class ActionLogResource extends BaseResource return parent::table($table) ->recordUrl('') ->columns([ - TextColumn::make(ActionLog::ATTRIBUTE_ID) + TextColumn::make(Activity::ATTRIBUTE_ID) ->label(__('filament.fields.base.id')), - TextColumn::make(ActionLog::ATTRIBUTE_NAME) - ->label(__('filament.fields.action_log.name')) + TextColumn::make(Activity::ATTRIBUTE_NAME) + ->label(__('filament.fields.activity.name')) ->searchable(), - BelongsToColumn::make(ActionLog::RELATION_USER, UserResource::class), + BelongsToColumn::make(Activity::RELATION_USER, UserResource::class), - TextColumn::make(ActionLog::ATTRIBUTE_TARGET) - ->label(__('filament.fields.action_log.target')) + TextColumn::make(Activity::RELATION_RELATED) + ->label(__('filament.fields.activity.target')) ->formatStateUsing(fn ($state): string => Str::headline(class_basename($state)).': '.$state->getName()), - TextColumn::make(ActionLog::ATTRIBUTE_STATUS) - ->label(__('filament.fields.action_log.status')) - ->formatStateUsing(fn (ActionLogStatus $state): ?string => $state->localize()) + TextColumn::make(Activity::ATTRIBUTE_STATUS) + ->label(__('filament.fields.activity.status')) + ->formatStateUsing(fn (ActivityStatus $state): ?string => $state->localize()) ->badge(), TextColumn::make(BaseModel::ATTRIBUTE_CREATED_AT) - ->label(__('filament.fields.action_log.happened_at')) + ->label(__('filament.fields.activity.happened_at')) ->dateTime(), - TextColumn::make(ActionLog::ATTRIBUTE_FINISHED_AT) - ->label(__('filament.fields.action_log.finished_at')) + TextColumn::make(Activity::ATTRIBUTE_FINISHED_AT) + ->label(__('filament.fields.activity.finished_at')) ->dateTime(), ]); } @@ -146,33 +133,33 @@ class ActionLogResource extends BaseResource { return $schema ->components([ - TextEntry::make(ActionLog::ATTRIBUTE_NAME) - ->label(__('filament.fields.action_log.name')) + TextEntry::make(Activity::ATTRIBUTE_NAME) + ->label(__('filament.fields.activity.name')) ->formatStateUsing(fn ($state): string => ucfirst((string) $state)), - BelongsToEntry::make(ActionLog::RELATION_USER, UserResource::class), + BelongsToEntry::make(Activity::RELATION_USER, UserResource::class), - TextEntry::make(ActionLog::ATTRIBUTE_TARGET) - ->label(__('filament.fields.action_log.target')) + TextEntry::make(Activity::RELATION_RELATED) + ->label(__('filament.fields.activity.target')) ->formatStateUsing(fn ($state): string => class_basename($state).': '.$state->getName()), - TextEntry::make(ActionLog::ATTRIBUTE_STATUS) - ->label(__('filament.fields.action_log.status')) - ->formatStateUsing(fn (ActionLogStatus $state): ?string => $state->localize()) + TextEntry::make(Activity::ATTRIBUTE_STATUS) + ->label(__('filament.fields.activity.status')) + ->formatStateUsing(fn (ActivityStatus $state): ?string => $state->localize()) ->badge(), TextEntry::make(BaseModel::ATTRIBUTE_CREATED_AT) - ->label(__('filament.fields.action_log.happened_at')) + ->label(__('filament.fields.activity.happened_at')) ->dateTime(), - TextEntry::make(ActionLog::ATTRIBUTE_FINISHED_AT) - ->label(__('filament.fields.action_log.finished_at')) + TextEntry::make(Activity::ATTRIBUTE_FINISHED_AT) + ->label(__('filament.fields.activity.finished_at')) ->dateTime(), - KeyValueEntry::make(ActionLog::ATTRIBUTE_FIELDS) - ->label(__('filament.fields.action_log.fields.name')) - ->keyLabel(__('filament.fields.action_log.fields.keys')) - ->valueLabel(__('filament.fields.action_log.fields.values')) + KeyValueEntry::make(Activity::ATTRIBUTE_PROPERTIES) + ->label(__('filament.fields.activity.fields.name')) + ->keyLabel(__('filament.fields.activity.fields.keys')) + ->valueLabel(__('filament.fields.activity.fields.values')) ->columnSpanFull() ->hidden(fn ($state): bool => is_null($state)) ->formatStateUsing(fn (?array $state) => collect($state)->mapWithKeys(function ($value, $key): array { @@ -183,8 +170,8 @@ class ActionLogResource extends BaseResource return [$key => blank($value) ? '-' : $value]; })->toArray()), - TextEntry::make(ActionLog::ATTRIBUTE_EXCEPTION) - ->label(__('filament.fields.action_log.exception')) + TextEntry::make(Activity::ATTRIBUTE_EXCEPTION) + ->label(__('filament.fields.activity.exception')) ->columnSpanFull() ->size(TextSize::Large), ]) @@ -197,15 +184,9 @@ class ActionLogResource extends BaseResource public static function getFilters(): array { return [ - SelectFilter::make(ActionLog::ATTRIBUTE_STATUS) - ->label(__('filament.fields.action_log.status')) - ->options(ActionLogStatus::class), - - DateFilter::make(BaseModel::ATTRIBUTE_CREATED_AT) - ->label(__('filament.fields.action_log.happened_at')), - - DateFilter::make(ActionLog::ATTRIBUTE_FINISHED_AT) - ->label(__('filament.fields.action_log.finished_at')), + SelectFilter::make(Activity::ATTRIBUTE_STATUS) + ->label(__('filament.fields.activity.status')) + ->options(ActivityStatus::class), ]; } @@ -270,7 +251,7 @@ class ActionLogResource extends BaseResource public static function getPages(): array { return [ - 'index' => ManageActionLogs::route('/'), + 'index' => ManageActivities::route('/'), ]; } } diff --git a/app/Filament/Resources/BaseResource.php b/app/Filament/Resources/BaseResource.php index b3ea6387e..ccbe21fdd 100644 --- a/app/Filament/Resources/BaseResource.php +++ b/app/Filament/Resources/BaseResource.php @@ -15,7 +15,7 @@ use App\Filament\Actions\Base\ViewAction; use App\Filament\BulkActions\Base\DeleteBulkAction; use App\Filament\BulkActions\Base\ForceDeleteBulkAction; use App\Filament\BulkActions\Base\RestoreBulkAction; -use App\Filament\RelationManagers\Base\ActionLogRelationManager; +use App\Filament\RelationManagers\Base\ActivityRelationManager; use App\Models\BaseModel; use Filament\Actions\Action; use Filament\Actions\ActionGroup; @@ -169,7 +169,7 @@ abstract class BaseResource extends Resource public static function getBaseRelations(): array { return [ - ActionLogRelationManager::class, + ActivityRelationManager::class, AuditsRelationManager::class, ]; } diff --git a/app/Models/Admin/ActionLog.php b/app/Models/Admin/ActionLog.php deleted file mode 100644 index ca6876d18..000000000 --- a/app/Models/Admin/ActionLog.php +++ /dev/null @@ -1,409 +0,0 @@ - - */ - protected $fillable = [ - ActionLog::ATTRIBUTE_BATCH_ID, - ActionLog::ATTRIBUTE_NAME, - ActionLog::ATTRIBUTE_USER, - ActionLog::ATTRIBUTE_ACTIONABLE_TYPE, - ActionLog::ATTRIBUTE_ACTIONABLE_ID, - ActionLog::ATTRIBUTE_TARGET_TYPE, - ActionLog::ATTRIBUTE_TARGET_ID, - ActionLog::ATTRIBUTE_MODEL_TYPE, - ActionLog::ATTRIBUTE_MODEL_ID, - ActionLog::ATTRIBUTE_FIELDS, - ActionLog::ATTRIBUTE_STATUS, - ActionLog::ATTRIBUTE_EXCEPTION, - ActionLog::ATTRIBUTE_FINISHED_AT, - ]; - - /** - * Get the attributes that should be cast. - * - * @return array - */ - protected function casts(): array - { - return [ - ActionLog::ATTRIBUTE_FIELDS => 'array', - ActionLog::ATTRIBUTE_STATUS => ActionLogStatus::class, - ]; - } - - /** - * When an exception is thrown, the current action logs should be handled. - */ - public static function updateCurrentActionLogToFailed(Throwable $e): void - { - if ($actionLog = Session::get('currentActionLog')) { - ActionLog::query() - ->where(ActionLog::ATTRIBUTE_BATCH_ID, $actionLog) - ->where(ActionLog::ATTRIBUTE_STATUS, ActionLogStatus::RUNNING->value) - ->update([ - ActionLog::ATTRIBUTE_STATUS => ActionLogStatus::FAILED->value, - ActionLog::ATTRIBUTE_EXCEPTION => $e->__toString(), - ActionLog::ATTRIBUTE_FINISHED_AT => Date::now(), - ]); - } - } - - public function getName(): string - { - return Str::of($this->name) - ->append(' - ') - ->append($this->target()->getName()) - ->__toString(); - } - - public function actionable(): MorphTo - { - return $this->morphTo(); - } - - /** - * Get the target of the action for user interface linking. - */ - public function target(): MorphTo|BaseModel - { - return $this->morphTo() - ->withTrashed(); - } - - /** - * @return BelongsTo - */ - public function user(): BelongsTo - { - return $this->belongsTo(User::class, ActionLog::ATTRIBUTE_USER); - } - - /** - * Get the user id for the action log. - */ - public static function getUserId(): int - { - return Filament::auth()->id(); - } - - /** - * Register an action log for a model created. - */ - public static function modelCreated(Model $model): ActionLog - { - return ActionLog::query()->create([ - ActionLog::ATTRIBUTE_BATCH_ID => Str::orderedUuid()->__toString(), - ActionLog::ATTRIBUTE_USER => ActionLog::getUserId(), - ActionLog::ATTRIBUTE_NAME => 'Create', - ActionLog::ATTRIBUTE_ACTIONABLE_TYPE => $model->getMorphClass(), - ActionLog::ATTRIBUTE_ACTIONABLE_ID => $model->getKey(), - ActionLog::ATTRIBUTE_TARGET_TYPE => $model->getMorphClass(), - ActionLog::ATTRIBUTE_TARGET_ID => $model->getKey(), - ActionLog::ATTRIBUTE_MODEL_TYPE => $model->getMorphClass(), - ActionLog::ATTRIBUTE_MODEL_ID => $model->getKey(), - ActionLog::ATTRIBUTE_FIELDS => static::getFields($model->getAttributes(), $model), - ActionLog::ATTRIBUTE_STATUS => ActionLogStatus::FINISHED->value, - ActionLog::ATTRIBUTE_FINISHED_AT => Date::now(), - ]); - } - - /** - * Register an action log for a model updated. - */ - public static function modelUpdated(Model $model): ActionLog - { - return ActionLog::query()->create([ - ActionLog::ATTRIBUTE_BATCH_ID => Str::orderedUuid()->__toString(), - ActionLog::ATTRIBUTE_USER => ActionLog::getUserId(), - ActionLog::ATTRIBUTE_NAME => 'Update', - ActionLog::ATTRIBUTE_ACTIONABLE_TYPE => $model->getMorphClass(), - ActionLog::ATTRIBUTE_ACTIONABLE_ID => $model->getKey(), - ActionLog::ATTRIBUTE_TARGET_TYPE => $model->getMorphClass(), - ActionLog::ATTRIBUTE_TARGET_ID => $model->getKey(), - ActionLog::ATTRIBUTE_MODEL_TYPE => $model->getMorphClass(), - ActionLog::ATTRIBUTE_MODEL_ID => $model->getKey(), - ActionLog::ATTRIBUTE_FIELDS => static::getFields($model->getChanges(), $model), - ActionLog::ATTRIBUTE_STATUS => ActionLogStatus::FINISHED->value, - ActionLog::ATTRIBUTE_FINISHED_AT => Date::now(), - ]); - } - - /** - * Register an action log for a model deleted. - */ - public static function modelDeleted(Model $model): ActionLog - { - return ActionLog::modelSoftDeleted('Delete', $model); - } - - /** - * Register an action log for a model restored. - */ - public static function modelRestored(Model $model): ActionLog - { - return ActionLog::modelSoftDeleted('Restore', $model); - } - - /** - * Register an action log for a model that is soft-deleted. - */ - public static function modelSoftDeleted(string $actionName, Model $model): ActionLog - { - return ActionLog::query()->create([ - ActionLog::ATTRIBUTE_BATCH_ID => Str::orderedUuid()->__toString(), - ActionLog::ATTRIBUTE_USER => ActionLog::getUserId(), - ActionLog::ATTRIBUTE_NAME => $actionName, - ActionLog::ATTRIBUTE_ACTIONABLE_TYPE => $model->getMorphClass(), - ActionLog::ATTRIBUTE_ACTIONABLE_ID => $model->getKey(), - ActionLog::ATTRIBUTE_TARGET_TYPE => $model->getMorphClass(), - ActionLog::ATTRIBUTE_TARGET_ID => $model->getKey(), - ActionLog::ATTRIBUTE_MODEL_TYPE => $model->getMorphClass(), - ActionLog::ATTRIBUTE_MODEL_ID => $model->getKey(), - ActionLog::ATTRIBUTE_FIELDS => static::getFields($model->getAttributes(), $model), - ActionLog::ATTRIBUTE_STATUS => ActionLogStatus::FINISHED->value, - ActionLog::ATTRIBUTE_FINISHED_AT => Date::now(), - ]); - } - - /** - * Register an action log for a model attached. - */ - public static function modelPivot(string $actionName, Model $related, Model $parent, Model $pivot, ?Action $action = null): ActionLog - { - $data = $action?->getData(); - - return ActionLog::query()->create([ - ActionLog::ATTRIBUTE_BATCH_ID => Str::orderedUuid()->__toString(), - ActionLog::ATTRIBUTE_USER => ActionLog::getUserId(), - ActionLog::ATTRIBUTE_NAME => $actionName, - ActionLog::ATTRIBUTE_ACTIONABLE_TYPE => $related->getMorphClass(), - ActionLog::ATTRIBUTE_ACTIONABLE_ID => $related->getKey(), - ActionLog::ATTRIBUTE_TARGET_TYPE => $parent->getMorphClass(), - ActionLog::ATTRIBUTE_TARGET_ID => $parent->getKey(), - ActionLog::ATTRIBUTE_MODEL_TYPE => $pivot->getMorphClass(), - ActionLog::ATTRIBUTE_MODEL_ID => $pivot instanceof Pivot ? null : $pivot->getKey(), - ActionLog::ATTRIBUTE_FIELDS => $data ? static::getFields($data) : static::getFields($pivot->getAttributes(), $pivot), - ActionLog::ATTRIBUTE_STATUS => ActionLogStatus::FINISHED->value, - ActionLog::ATTRIBUTE_FINISHED_AT => Date::now(), - ]); - } - - /** - * Register an action log for a model associated (HasMany). - */ - public static function modelAssociated(string $actionName, Model $related, Model $parent, Action $action): ActionLog - { - return ActionLog::query()->create([ - ActionLog::ATTRIBUTE_BATCH_ID => Str::orderedUuid()->__toString(), - ActionLog::ATTRIBUTE_USER => ActionLog::getUserId(), - ActionLog::ATTRIBUTE_NAME => $actionName, - ActionLog::ATTRIBUTE_ACTIONABLE_TYPE => $related->getMorphClass(), - ActionLog::ATTRIBUTE_ACTIONABLE_ID => $related->getKey(), - ActionLog::ATTRIBUTE_TARGET_TYPE => $parent->getMorphClass(), - ActionLog::ATTRIBUTE_TARGET_ID => $parent->getKey(), - ActionLog::ATTRIBUTE_MODEL_TYPE => $related->getMorphClass(), - ActionLog::ATTRIBUTE_MODEL_ID => $related->getKey(), - ActionLog::ATTRIBUTE_FIELDS => static::getFields($action->getData()), - ActionLog::ATTRIBUTE_STATUS => ActionLogStatus::FINISHED->value, - ActionLog::ATTRIBUTE_FINISHED_AT => Date::now(), - ]); - } - - /** - * Register an action log for when a model has an action executed. - */ - public static function modelActioned(string $batchId, Action $action, Model $model): ActionLog - { - return ActionLog::query()->create([ - ActionLog::ATTRIBUTE_BATCH_ID => $batchId, - ActionLog::ATTRIBUTE_USER => ActionLog::getUserId(), - ActionLog::ATTRIBUTE_NAME => $action->getLabel(), - ActionLog::ATTRIBUTE_ACTIONABLE_TYPE => $model->getMorphClass(), - ActionLog::ATTRIBUTE_ACTIONABLE_ID => $model->getKey(), - ActionLog::ATTRIBUTE_TARGET_TYPE => $model->getMorphClass(), - ActionLog::ATTRIBUTE_TARGET_ID => $model->getKey(), - ActionLog::ATTRIBUTE_MODEL_TYPE => $model->getMorphClass(), - ActionLog::ATTRIBUTE_MODEL_ID => $model->getKey(), - ActionLog::ATTRIBUTE_FIELDS => static::getFields($action->getData()), - ActionLog::ATTRIBUTE_STATUS => ActionLogStatus::RUNNING->value, - ]); - } - - /** - * Update the all the models status of a batch to running. - */ - public function batchRunning(): void - { - $this->query()->where(ActionLog::ATTRIBUTE_BATCH_ID, $this->batch_id) - ->whereNotIn(ActionLog::ATTRIBUTE_STATUS, [ActionLogStatus::FINISHED->value, ActionLogStatus::FAILED->value]) - ->update([ - ActionLog::ATTRIBUTE_STATUS => ActionLogStatus::RUNNING->value, - ]); - } - - /** - * Update the all the models status of a batch to finished. - */ - public function batchFinished(): void - { - $this->query()->where(ActionLog::ATTRIBUTE_BATCH_ID, $this->batch_id) - ->whereNotIn(ActionLog::ATTRIBUTE_STATUS, [ActionLogStatus::FINISHED->value, ActionLogStatus::FAILED->value]) - ->update([ - ActionLog::ATTRIBUTE_STATUS => ActionLogStatus::FINISHED->value, - ActionLog::ATTRIBUTE_FINISHED_AT => Date::now(), - ]); - } - - /** - * Update the all the models status of a batch to failed. - */ - public function batchFailed(Throwable|string|null $exception = null): void - { - $this->query()->where(ActionLog::ATTRIBUTE_BATCH_ID, $this->batch_id) - ->whereNotIn(ActionLog::ATTRIBUTE_STATUS, [ActionLogStatus::FINISHED->value, ActionLogStatus::FAILED->value]) - ->update([ - ActionLog::ATTRIBUTE_STATUS => ActionLogStatus::FAILED->value, - ActionLog::ATTRIBUTE_EXCEPTION => $exception ? $exception->__toString() : null, - ActionLog::ATTRIBUTE_FINISHED_AT => Date::now(), - ]); - } - - /** - * Update the model status to finished. - */ - public function finished(): void - { - $this->updateStatus(ActionLogStatus::FINISHED); - } - - /** - * Update the model status to failed. - */ - public function failed(Throwable|string|null $exception = null): void - { - $this->updateStatus(ActionLogStatus::FAILED, $exception); - } - - /** - * Check if the model status is failed. - */ - public function hasFailed(): bool - { - return $this->status === ActionLogStatus::FAILED; - } - - /** - * Update the status of a given action event. - */ - public function updateStatus(ActionLogStatus $status, Throwable|string|null $exception = null): void - { - if ($exception instanceof Throwable) { - $exception = $exception->__toString(); - } - - $this->update([ - ActionLog::ATTRIBUTE_STATUS => $status->value, - ActionLog::ATTRIBUTE_EXCEPTION => $exception, - ActionLog::ATTRIBUTE_FINISHED_AT => Date::now(), - ]); - } - - /** - * Format the fields to store. - */ - protected static function getFields(array $fields, ?Model $model = null): array - { - return collect($fields) - ->forget(Model::CREATED_AT) - ->forget(Model::UPDATED_AT) - ->forget(ModelConstants::ATTRIBUTE_DELETED_AT) - ->map(function ($value, $key) use ($model): string { - if ($model instanceof Model) { - if (in_array($key, $model->getHidden())) { - return DiscordEmbedField::DEFAULT_FIELD_VALUE; - } - - return DiscordEmbedField::formatEmbedFieldValue($model->getAttribute($key)); - } - - return DiscordEmbedField::formatEmbedFieldValue($value); - }) - ->toArray(); - } -} diff --git a/app/Models/Admin/Activity.php b/app/Models/Admin/Activity.php new file mode 100644 index 000000000..247735111 --- /dev/null +++ b/app/Models/Admin/Activity.php @@ -0,0 +1,47 @@ + + */ + protected function casts(): array + { + return [ + ...parent::casts(), + Activity::ATTRIBUTE_FINISHED_AT => 'datetime', + Activity::ATTRIBUTE_STATUS => ActivityStatus::class, + ]; + } + + public function related(): MorphTo + { + return $this->morphTo(); + } +} diff --git a/app/Models/Auth/User.php b/app/Models/Auth/User.php index 5fde73f85..8c93cf168 100644 --- a/app/Models/Auth/User.php +++ b/app/Models/Auth/User.php @@ -14,7 +14,7 @@ use App\Events\Auth\User\UserCreated; use App\Events\Auth\User\UserDeleted; use App\Events\Auth\User\UserRestored; use App\Events\Auth\User\UserUpdated; -use App\Models\Admin\ActionLog; +use App\Models\Admin\Activity; use App\Models\List\ExternalProfile; use App\Models\List\Playlist; use App\Models\User\Like; @@ -229,13 +229,13 @@ class User extends Authenticatable implements Auditable, FilamentUser, HasAvatar } /** - * Get the action logs that the user has executed. + * Get the activities that the user has executed. * - * @return HasMany + * @return MorphMany */ - public function actionlogs(): HasMany + public function activities(): MorphMany { - return $this->hasMany(ActionLog::class, ActionLog::ATTRIBUTE_USER); + return $this->morphMany(Activity::class, 'causer'); } /** diff --git a/app/Models/BaseModel.php b/app/Models/BaseModel.php index e95cd3637..a7f88fb10 100644 --- a/app/Models/BaseModel.php +++ b/app/Models/BaseModel.php @@ -4,10 +4,11 @@ declare(strict_types=1); namespace App\Models; -use App\Concerns\Filament\ActionLogs\ModelHasActionLogs; use App\Contracts\Models\HasSubtitle; use App\Contracts\Models\Nameable; +use App\Models\Admin\Activity; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\MorphMany; use Illuminate\Support\Carbon; use Illuminate\Support\Facades\Config; use Illuminate\Support\Str; @@ -18,8 +19,6 @@ use Illuminate\Support\Str; */ abstract class BaseModel extends Model implements HasSubtitle, Nameable { - use ModelHasActionLogs; - /** * The storage format of the model's date columns. * @@ -42,4 +41,10 @@ abstract class BaseModel extends Model implements HasSubtitle, Nameable $this->setConnection(Config::get($connectionKey)); } } + + /** @return MorphMany */ + public function activities(): MorphMany + { + return $this->morphMany(Activity::class, 'subject'); + } } diff --git a/app/Observers/Admin/ActionLogObserver.php b/app/Observers/Admin/ActionLogObserver.php deleted file mode 100644 index 5e21cd815..000000000 --- a/app/Observers/Admin/ActionLogObserver.php +++ /dev/null @@ -1,32 +0,0 @@ -status === ActionLogStatus::RUNNING) { - Session::put('currentActionLog', $actionLog->batch_id); - } - } - - /** - * Handle the ActionLog "updating" event. - */ - public function updating(ActionLog $actionLog): void - { - if ($actionLog->status === ActionLogStatus::RUNNING) { - Session::put('currentActionLog', $actionLog->batch_id); - } - } -} diff --git a/composer.json b/composer.json index 7e76bd509..c38933c54 100644 --- a/composer.json +++ b/composer.json @@ -53,6 +53,7 @@ "propaganistas/laravel-disposable-email": "^2.4.27", "spatie/db-dumper": "^3.8.3", "spatie/eloquent-sortable": "^5.0.1", + "spatie/laravel-activitylog": "^5.0", "spatie/laravel-permission": "^6.25.0", "staudenmeir/belongs-to-through": "^2.18", "staudenmeir/eloquent-has-many-deep": "^1.22.1", diff --git a/composer.lock b/composer.lock index aba39784a..d59e5cdf5 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "6755161c85626cab15eacc8869e6836d", + "content-hash": "069560ab509c56cc16fc63007f65e7af", "packages": [ { "name": "awcodes/recently", @@ -8925,6 +8925,99 @@ ], "time": "2024-05-17T09:06:10+00:00" }, + { + "name": "spatie/laravel-activitylog", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/spatie/laravel-activitylog.git", + "reference": "0e00fe74fd071cc572a045459f6d4c9de33130bd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/laravel-activitylog/zipball/0e00fe74fd071cc572a045459f6d4c9de33130bd", + "reference": "0e00fe74fd071cc572a045459f6d4c9de33130bd", + "shasum": "" + }, + "require": { + "illuminate/config": "^12.0 || ^13.0", + "illuminate/database": "^12.0 || ^13.0", + "illuminate/support": "^12.0 || ^13.0", + "php": "^8.4", + "spatie/laravel-package-tools": "^1.6.3" + }, + "require-dev": { + "ext-json": "*", + "larastan/larastan": "^3.0", + "laravel/pint": "^1.29", + "orchestra/testbench": "^10.0 || ^11.0", + "pestphp/pest": "^4.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Spatie\\Activitylog\\ActivitylogServiceProvider" + ] + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Spatie\\Activitylog\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + }, + { + "name": "Sebastian De Deyne", + "email": "sebastian@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + }, + { + "name": "Tom Witkowski", + "email": "dev.gummibeer@gmail.com", + "homepage": "https://gummibeer.de", + "role": "Developer" + } + ], + "description": "A very simple activity logger to monitor the users of your website or application", + "homepage": "https://github.com/spatie/activitylog", + "keywords": [ + "activity", + "laravel", + "log", + "spatie", + "user" + ], + "support": { + "issues": "https://github.com/spatie/laravel-activitylog/issues", + "source": "https://github.com/spatie/laravel-activitylog/tree/5.0.0" + }, + "funding": [ + { + "url": "https://spatie.be/open-source/support-us", + "type": "custom" + }, + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2026-03-25T10:04:54+00:00" + }, { "name": "spatie/laravel-package-tools", "version": "1.93.0", diff --git a/config/activitylog.php b/config/activitylog.php new file mode 100644 index 000000000..64492b65d --- /dev/null +++ b/config/activitylog.php @@ -0,0 +1,75 @@ + env('ACTIVITYLOG_ENABLED', true), + + /* + * When the clean command is executed, all recording activities older than + * the number of days specified here will be deleted. + */ + 'clean_after_days' => 365, + + /* + * If no log name is passed to the activity() helper + * we use this default log name. + */ + 'default_log_name' => 'default', + + /* + * You can specify an auth driver here that gets user models. + * If this is null we'll use the current Laravel auth driver. + */ + 'default_auth_driver' => null, + + /* + * If set to true, the subject relationship on activities + * will include soft deleted models. + */ + 'include_soft_deleted_subjects' => false, + + /* + * This model will be used to log activity. + * It should implement the Spatie\Activitylog\Contracts\Activity interface + * and extend Illuminate\Database\Eloquent\Model. + */ + 'activity_model' => Activity::class, + + /* + * These attributes will be excluded from logging for all models. + * Model-specific exclusions via logExcept() are merged with these. + */ + 'default_except_attributes' => [], + + /* + * When enabled, activities are buffered in memory and inserted in a + * single bulk query after the response has been sent to the client. + * This can significantly reduce the number of database queries when + * many activities are logged during a single request. + * + * Only enable this if your application logs a high volume of activities + * per request. Buffered activities will not have an ID until the + * buffer is flushed. + */ + 'buffer' => [ + 'enabled' => env('ACTIVITYLOG_BUFFER_ENABLED', false), + ], + + /* + * These action classes can be overridden to customize how activities + * are logged and cleaned. Your custom classes must extend the originals. + */ + 'actions' => [ + 'log_activity' => LogActivityAction::class, + 'clean_log' => CleanActivityLogAction::class, + ], +]; diff --git a/database/migrations/2024_06_25_145723_create_action_logs_table.php b/database/migrations/2024_06_25_145723_create_action_logs_table.php deleted file mode 100644 index a6aa71c3a..000000000 --- a/database/migrations/2024_06_25_145723_create_action_logs_table.php +++ /dev/null @@ -1,39 +0,0 @@ -bigIncrements('id'); - $table->string('batch_id'); - - $table->unsignedBigInteger('user_id')->nullable(); - $table->foreign('user_id')->references('id')->on('users')->nullOnDelete(); - - $table->string('name'); - $table->morphs('actionable'); - $table->morphs('target'); - $table->string('model_type'); - $table->uuid('model_id')->nullable(); - $table->integer('status')->nullable(); - $table->json('fields')->nullable(); - $table->text('exception')->nullable(); - $table->timestamps(6); - $table->timestamp('finished_at', 6)->nullable(); - - $table->index(['batch_id', 'model_type', 'model_id']); - }); - } - } -}; diff --git a/database/migrations/2026_05_07_204832_create_activity_log_table.php b/database/migrations/2026_05_07_204832_create_activity_log_table.php new file mode 100644 index 000000000..07725d0e1 --- /dev/null +++ b/database/migrations/2026_05_07_204832_create_activity_log_table.php @@ -0,0 +1,31 @@ +id(); + $table->string('log_name')->nullable()->index(); + $table->text('description'); + $table->nullableMorphs('subject', 'subject'); + $table->nullableMorphs('related', 'related'); + $table->string('event')->nullable(); + $table->nullableMorphs('causer', 'causer'); + $table->json('attribute_changes')->nullable(); + $table->json('properties')->nullable(); + $table->integer('status')->nullable(); + $table->text('exception')->nullable(); + $table->timestamps(6); + $table->timestamp('finished_at', 6)->nullable(); + }); + } + } +}; diff --git a/lang/en/enums.php b/lang/en/enums.php index 2a8c7e31d..b81f6bfd4 100644 --- a/lang/en/enums.php +++ b/lang/en/enums.php @@ -2,7 +2,7 @@ declare(strict_types=1); -use App\Enums\Models\Admin\ActionLogStatus; +use App\Enums\Models\Admin\ActivityStatus; use App\Enums\Models\List\ExternalEntryStatus; use App\Enums\Models\List\ExternalProfileSite; use App\Enums\Models\List\ExternalProfileVisibility; @@ -20,10 +20,10 @@ use App\Enums\Models\Wiki\VideoSource; use App\Enums\Pivots\Document\PageRoleType; return [ - ActionLogStatus::class => [ - ActionLogStatus::RUNNING->name => 'Running', - ActionLogStatus::FAILED->name => 'Failed', - ActionLogStatus::FINISHED->name => 'Finished', + ActivityStatus::class => [ + ActivityStatus::RUNNING->name => 'Running', + ActivityStatus::FAILED->name => 'Failed', + ActivityStatus::FINISHED->name => 'Finished', ], AnimeFormat::class => [ AnimeFormat::TV->name => 'TV', diff --git a/lang/en/filament.php b/lang/en/filament.php index 44527133a..646928e50 100644 --- a/lang/en/filament.php +++ b/lang/en/filament.php @@ -515,7 +515,7 @@ return [ ], ], 'fields' => [ - 'action_log' => [ + 'activity' => [ 'name' => 'Name', 'target' => 'Target', 'status' => 'Status', @@ -1015,7 +1015,7 @@ return [ ], 'resources' => [ 'label' => [ - 'action_logs' => 'Action Logs', + 'activities' => 'Activities', 'anime_theme_entries' => 'Anime Theme Entries', 'anime_themes' => 'Anime Themes', 'anime' => 'Anime', @@ -1051,7 +1051,7 @@ return [ 'videos' => 'Videos', ], 'singularLabel' => [ - 'action_log' => 'Action Log', + 'activity' => 'Activity', 'anime_theme_entry' => 'Anime Theme Entry', 'anime_theme' => 'Anime Theme', 'anime' => 'Anime',