diff --git a/config/filament-activitylog.php b/config/filament-activitylog.php
index ad68b21..112e1d3 100755
--- a/config/filament-activitylog.php
+++ b/config/filament-activitylog.php
@@ -17,6 +17,6 @@
'navigation_count_badge' => false,
'resource' => \Rmsramos\Activitylog\Resources\ActivitylogResource::class,
],
- 'date_format' => 'd/m/Y',
- 'datetime_format' => 'd/m/Y H:i:s',
+ 'date_format' => 'd/m/Y',
+ 'datetime_format' => 'd/m/Y H:i:s',
];
diff --git a/resources/lang/en/forms.php b/resources/lang/en/forms.php
index ecda00a..5a203fa 100755
--- a/resources/lang/en/forms.php
+++ b/resources/lang/en/forms.php
@@ -2,7 +2,7 @@
return [
'changes' => 'Changes',
- 'fields' => [
+ 'fields' => [
'log_name' => [
'label' => 'Type',
],
diff --git a/resources/lang/en/infolists.php b/resources/lang/en/infolists.php
index cedaf25..271d6cb 100644
--- a/resources/lang/en/infolists.php
+++ b/resources/lang/en/infolists.php
@@ -2,10 +2,10 @@
return [
'components' => [
- "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"
+ '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',
],
];
diff --git a/resources/lang/en/notifications.php b/resources/lang/en/notifications.php
index da098fe..6dbd4e7 100644
--- a/resources/lang/en/notifications.php
+++ b/resources/lang/en/notifications.php
@@ -1,8 +1,9 @@
'No properties to restore.'
- 'activity_restored' => 'Activity restored.'
- 'activity_restored_successfully' => 'Activity restored successfully.'
- 'failed_to_restore_activity' => 'Failed to restore activity : :error.'
+ 'no_properties_to_restore' => '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.',
];
diff --git a/resources/lang/en/tables.php b/resources/lang/en/tables.php
index 9bb8e8c..ecc8cf6 100755
--- a/resources/lang/en/tables.php
+++ b/resources/lang/en/tables.php
@@ -23,11 +23,11 @@
],
'filters' => [
'created_at' => [
- 'label' => 'Logged at',
- 'created_from' => 'Created from ',
+ 'label' => 'Logged at',
+ 'created_from' => 'Created from ',
'created_from_indicator' => 'Created from : :created_from',
- 'created_until' => 'Created until ',
- 'created_until_indicator' => 'Created until : :created_from',
+ 'created_until' => 'Created until ',
+ 'created_until_indicator' => 'Created until : :created_until',
],
'event' => [
'label' => 'Event',
diff --git a/resources/lang/fa/action.php b/resources/lang/fa/action.php
index 461b811..491557a 100644
--- a/resources/lang/fa/action.php
+++ b/resources/lang/fa/action.php
@@ -14,5 +14,5 @@
],
'view' => 'نمایش',
'edit' => 'ویرایش',
- 'restore' => 'بازیابی'
+ 'restore' => 'بازیابی',
];
diff --git a/resources/lang/fa/forms.php b/resources/lang/fa/forms.php
index 76cc146..00f3d6f 100644
--- a/resources/lang/fa/forms.php
+++ b/resources/lang/fa/forms.php
@@ -2,7 +2,7 @@
return [
'changes' => 'تغییرات',
- 'fields' => [
+ 'fields' => [
'log_name' => [
'label' => 'نوع',
],
diff --git a/resources/lang/fa/infolists.php b/resources/lang/fa/infolists.php
index 774bd81..104ccdb 100644
--- a/resources/lang/fa/infolists.php
+++ b/resources/lang/fa/infolists.php
@@ -2,10 +2,10 @@
return [
'components' => [
- "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" => "ناشناس"
+ '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' => 'ناشناس',
],
];
diff --git a/resources/lang/fa/notifications.php b/resources/lang/fa/notifications.php
index 3fcbda7..789c22e 100644
--- a/resources/lang/fa/notifications.php
+++ b/resources/lang/fa/notifications.php
@@ -1,8 +1,8 @@
'مقادیری برای بازگردانی وجود ندارد.',
- 'activity_restored' => 'فعالیت بازگردانی شد.',
+ 'no_properties_to_restore' => 'مقادیری برای بازگردانی وجود ندارد.',
+ 'activity_restored' => 'فعالیت بازگردانی شد.',
'activity_restored_successfully' => 'فعالیت با موفقیت بازگردانی شد.',
- 'failed_to_restore_activity' => 'بازگردانی فعالیت ناموفق بود. خطا : :error',
+ 'failed_to_restore_activity' => 'بازگردانی فعالیت ناموفق بود. خطا : :error',
];
diff --git a/resources/lang/fa/tables.php b/resources/lang/fa/tables.php
index a77a900..efed7c8 100644
--- a/resources/lang/fa/tables.php
+++ b/resources/lang/fa/tables.php
@@ -23,10 +23,10 @@
],
'filters' => [
'created_at' => [
- 'label' => 'ورود به سیستم در',
- 'created_from' => 'ایجاد شده از ',
+ 'label' => 'ورود به سیستم در',
+ 'created_from' => 'ایجاد شده از ',
'created_from_indicator' => 'ایجاد شده از تاریخ : :created_from',
- 'created_until' => 'ایجاد شده تا ',
+ 'created_until' => 'ایجاد شده تا ',
'created_until_indicator' => 'ایجاد شده تا تاریخ : :created_until',
],
'event' => [
diff --git a/resources/lang/pt_BR/action.php b/resources/lang/pt_BR/action.php
index 60627fd..20db56f 100644
--- a/resources/lang/pt_BR/action.php
+++ b/resources/lang/pt_BR/action.php
@@ -6,4 +6,13 @@
'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',
];
diff --git a/resources/lang/pt_BR/forms.php b/resources/lang/pt_BR/forms.php
index c4e2c2f..a18a6be 100755
--- a/resources/lang/pt_BR/forms.php
+++ b/resources/lang/pt_BR/forms.php
@@ -1,7 +1,8 @@
[
+ 'changes' => 'Mudanças',
+ 'fields' => [
'log_name' => [
'label' => 'Tipo',
],
diff --git a/resources/lang/pt_BR/infolists.php b/resources/lang/pt_BR/infolists.php
new file mode 100644
index 0000000..33d87e9
--- /dev/null
+++ b/resources/lang/pt_BR/infolists.php
@@ -0,0 +1,11 @@
+ [
+ 'created_by_at' => 'O :subject foi :event por :causer.
Atualizado em: :update_at',
+ 'updater_updated' => ':causer :event para:
:changes',
+ 'from_oldvalue_to_newvalue' => '- :key de :old_value para :new_value',
+ 'to_newvalue' => '- :key :new_value',
+ 'unknown' => 'Desconhecido',
+ ],
+];
diff --git a/resources/lang/pt_BR/notifications.php b/resources/lang/pt_BR/notifications.php
new file mode 100644
index 0000000..4179a6e
--- /dev/null
+++ b/resources/lang/pt_BR/notifications.php
@@ -0,0 +1,9 @@
+ '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.',
+];
diff --git a/resources/lang/pt_BR/tables.php b/resources/lang/pt_BR/tables.php
index fc956e8..5d2eb5d 100755
--- a/resources/lang/pt_BR/tables.php
+++ b/resources/lang/pt_BR/tables.php
@@ -23,9 +23,11 @@
],
'filters' => [
'created_at' => [
- 'label' => 'Criado em',
- 'created_from' => 'Criado a partir de ',
- 'created_until' => 'Criado 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',
diff --git a/src/Actions/Concerns/ActionContent.php b/src/Actions/Concerns/ActionContent.php
index 5bce4de..163dcda 100644
--- a/src/Actions/Concerns/ActionContent.php
+++ b/src/Actions/Concerns/ActionContent.php
@@ -2,8 +2,6 @@
namespace Rmsramos\Activitylog\Actions\Concerns;
-use Carbon\Carbon;
-use Carbon\CarbonImmutable;
use Carbon\Exceptions\InvalidFormatException;
use Closure;
use Filament\Actions\StaticAction;
@@ -12,30 +10,30 @@
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
+use Rmsramos\Activitylog\ActivitylogPlugin;
use Rmsramos\Activitylog\Infolists\Components\TimeLineIconEntry;
use Rmsramos\Activitylog\Infolists\Components\TimeLinePropertiesEntry;
use Rmsramos\Activitylog\Infolists\Components\TimeLineRepeatableEntry;
use Rmsramos\Activitylog\Infolists\Components\TimeLineTitleEntry;
use Spatie\Activitylog\Models\Activity;
-use Rmsramos\Activitylog\ActivitylogPlugin;
trait ActionContent
{
private ?array $withRelations = null;
private ?array $timelineIcons = [
- 'created' => 'heroicon-m-plus',
- 'updated' => 'heroicon-m-pencil-square',
- 'deleted' => 'heroicon-m-trash',
- 'restored' => 'heroicon-m-arrow-uturn-left',
- ];
+ 'created' => 'heroicon-m-plus',
+ 'updated' => 'heroicon-m-pencil-square',
+ 'deleted' => 'heroicon-m-trash',
+ 'restored' => 'heroicon-m-arrow-uturn-left',
+ ];
private ?array $timelineIconColors = [
- 'created' => 'success',
- 'updated' => 'warning',
- 'deleted' => 'danger',
- 'restored' => 'info',
- ];
+ 'created' => 'success',
+ 'updated' => 'warning',
+ 'deleted' => 'danger',
+ 'restored' => 'info',
+ ];
private ?int $limit = 10;
@@ -66,32 +64,57 @@ protected function setUp(): void
$this->modifyQueryUsing = fn ($builder) => $builder;
$this->modalHeading = __('activitylog::action.modal.heading');
$this->modalDescription = __('activitylog::action.modal.description');
- $this->query = function (?Model $record) {
+
+ $this->query = function (?Model $record) {
+ if (! $record) {
+ return Activity::query()->whereRaw('1 = 0');
+ }
+
return Activity::query()
->with(['subject', 'causer'])
->where(function (Builder $query) use ($record) {
- $query->where(function (Builder $q) use ($record) {
- $q->where('subject_type', $record->getMorphClass())
- ->where('subject_id', $record->getKey());
- })->when($this->getWithRelations(), function (Builder $query, array $relations) use ($record) {
+ $query->where('subject_type', $record->getMorphClass())
+ ->where('subject_id', $record->getKey());
+
+ if ($relations = $this->getWithRelations()) {
foreach ($relations as $relation) {
- $model = get_class($record->{$relation}()->getRelated());
- $query->orWhere(function (Builder $q) use ($record, $model, $relation) {
- $q->where('subject_type', (new $model)->getMorphClass())
- ->whereIn('subject_id', $record->{$relation}()->pluck('id'));
- });
+ if (method_exists($record, $relation)) {
+ try {
+ $relationInstance = $record->{$relation}();
+ $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) {
+ }
+ }
}
- });
+ }
});
-
};
}
private 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' => $this->getActivityLogRecord($record, $this->getWithRelations())])
+ ->state(['activities' => $formattedActivities])
->schema($this->getSchema());
});
}
@@ -139,11 +162,6 @@ public function withRelations(?array $relations = null): ?StaticAction
return $this;
}
- public function getWithRelations(): ?array
- {
- return $this->evaluate($this->withRelations);
- }
-
public function timelineIcons(?array $timelineIcons = null): ?StaticAction
{
$this->timelineIcons = $timelineIcons;
@@ -151,11 +169,6 @@ public function timelineIcons(?array $timelineIcons = null): ?StaticAction
return $this;
}
- public function getTimelineIcons(): ?array
- {
- return $this->evaluate($this->timelineIcons);
- }
-
public function timelineIconColors(?array $timelineIconColors = null): ?StaticAction
{
$this->timelineIconColors = $timelineIconColors;
@@ -163,11 +176,6 @@ public function timelineIconColors(?array $timelineIconColors = null): ?StaticAc
return $this;
}
- public function getTimelineIconColors(): ?array
- {
- return $this->evaluate($this->timelineIconColors);
- }
-
public function limit(?int $limit = 10): ?StaticAction
{
$this->limit = $limit;
@@ -175,11 +183,6 @@ public function limit(?int $limit = 10): ?StaticAction
return $this;
}
- public function getLimit(): ?int
- {
- return $this->evaluate($this->limit);
- }
-
public function query(Closure|Builder|null $query): static
{
$this->query = $query;
@@ -187,11 +190,6 @@ public function query(Closure|Builder|null $query): static
return $this;
}
- public function getQuery(): ?Builder
- {
- return $this->evaluate($this->query);
- }
-
public function modifyQueryUsing(Closure $closure): static
{
$this->modifyQueryUsing = $closure;
@@ -199,11 +197,6 @@ public function modifyQueryUsing(Closure $closure): static
return $this;
}
- public function getModifyQueryUsing(Builder $builder): Builder
- {
- return $this->evaluate($this->modifyQueryUsing, ['builder' => $builder]);
- }
-
public function modifyTitleUsing(Closure $closure): static
{
$this->modifyTitleUsing = $closure;
@@ -225,6 +218,36 @@ public function activitiesUsing(Closure $closure): static
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);
@@ -234,14 +257,17 @@ protected function getActivities(?Model $record, ?array $relations = null): Coll
{
if ($activities = $this->getActivitiesUsing()) {
return $activities;
- } else {
- $builder = $this->getQuery()
- ->latest()
- ->limit($this->getLimit());
+ }
- return $this->getModifyQueryUsing($builder)
- ->get();
+ 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
@@ -257,13 +283,25 @@ protected function getActivityLogRecord(?Model $record, ?array $relations = null
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();
+ }
+ }
+
return [
'log_name' => $activity->log_name,
'description' => $activity->description,
'subject' => $activity->subject,
'event' => $activity->event,
'causer' => $activity->causer,
- 'properties' => $this->formatDateValues(json_decode($activity->properties, true)),
+ 'properties' => $this->formatDateValues($properties),
'batch_uuid' => $activity->batch_uuid,
'update' => $activity->updated_at,
];
@@ -290,41 +328,11 @@ private static function formatDateValues(array|string|null $value): array|string
try {
$parser = ActivitylogPlugin::get()->getDateParser();
- return $parser($value)
- ->format(ActivitylogPlugin::get()->getDatetimeFormat());
+ return $parser($value)->format(ActivitylogPlugin::get()->getDatetimeFormat());
} catch (InvalidFormatException $e) {
return $value;
+ } catch (\Exception $e) {
+ return $value;
}
-
- return $value;
- }
-
- private static function isValidDate(string $dateString, string $dateFormat = 'Y-m-d', string $dateTimeFormat = 'Y-m-d H:i:s'): bool|string
- {
- try {
-
- $dateTime = CarbonImmutable::createFromFormat($dateFormat, $dateString);
-
- if ($dateTime && $dateTime->format($dateFormat) === $dateString) {
- return true;
- }
-
- } catch (InvalidFormatException $e) {
-
- }
-
- try {
-
- $dateTime = CarbonImmutable::createFromFormat($dateTimeFormat, $dateString);
-
- if ($dateTime && $dateTime->format($dateTimeFormat) === $dateString) {
- return true;
- }
-
- } catch (InvalidFormatException $e) {
-
- }
-
- return false;
}
}
diff --git a/src/ActivitylogPlugin.php b/src/ActivitylogPlugin.php
index 4e38a18..704ecd4 100755
--- a/src/ActivitylogPlugin.php
+++ b/src/ActivitylogPlugin.php
@@ -6,7 +6,6 @@
use Filament\Contracts\Plugin;
use Filament\Panel;
use Filament\Support\Concerns\EvaluatesClosures;
-use DateTimeInterface;
use Illuminate\Support\Carbon;
class ActivitylogPlugin implements Plugin
@@ -37,7 +36,7 @@ class ActivitylogPlugin implements Plugin
protected ?Closure $datePickerCallback = null;
- protected Closure|null $translateSubject = null;
+ protected ?Closure $translateSubject = null;
protected ?string $navigationIcon = null;
@@ -139,16 +138,18 @@ public function getDatePickerCallback(): ?Closure
public function getTranslateSubject($label): ?string
{
- if(is_null($this->translateSubject))
+ if (is_null($this->translateSubject)) {
return $label;
+ }
$callable = $this->translateSubject;
+
return $callable($label);
}
public function getDateParser(): ?Closure
- {
- return $this->dateParser ?? fn($date) => Carbon::parse($date);
+ {
+ return $this->dateParser ?? fn ($date) => Carbon::parse($date);
}
public function getNavigationIcon(): ?string
@@ -222,7 +223,7 @@ public function navigationGroup(string|Closure|null $group = null): static
return $this;
}
- public function dateParser(Closure|null $parser = null): static
+ public function dateParser(?Closure $parser = null): static
{
$this->dateParser = $parser;
@@ -246,12 +247,14 @@ public function datetimeFormat(string|Closure|null $format = null): static
public function customizeDatetimeColumn(Closure $callable): self
{
$this->datetimeColumnCallback = $callable;
+
return $this;
}
public function customizeDatePicker(Closure $callable): self
{
$this->datePickerCallback = $callable;
+
return $this;
}
diff --git a/src/Helpers/ActivityLogHelper.php b/src/Helpers/ActivityLogHelper.php
index 00257e2..1c5ce57 100644
--- a/src/Helpers/ActivityLogHelper.php
+++ b/src/Helpers/ActivityLogHelper.php
@@ -9,13 +9,13 @@ class ActivityLogHelper
/**
* Checks if a class uses a specific trait.
*
- * @param mixed $class The class or object instance to check.
- * @param string $trait The fully qualified name of the trait to look for.
- * @return bool
+ * @param mixed $class The class or object instance to check.
+ * @param string $trait The fully qualified name of the trait to look for.
*/
public static function classUsesTrait($class, $trait): bool
{
$traits = class_uses_recursive($class);
+
return in_array($trait, $traits);
}
diff --git a/src/Infolists/Components/TimeLinePropertiesEntry.php b/src/Infolists/Components/TimeLinePropertiesEntry.php
index 293da53..1060db7 100644
--- a/src/Infolists/Components/TimeLinePropertiesEntry.php
+++ b/src/Infolists/Components/TimeLinePropertiesEntry.php
@@ -34,10 +34,10 @@ private function modifiedProperties($state): ?HtmlString
$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 new HtmlString(trans('activitylog::infolists.components.updater_updated', [
+ 'causer' => $causerName,
+ 'event' => __('activitylog::action.event.' . $state['event']),
+ 'changes' => implode('
', $changes),
]));
}
@@ -66,17 +66,17 @@ private function compareOldAndNewValues(array $oldValues, array $newValues): arr
$newValue = $this->formatNewValue($newValue);
if (isset($oldValues[$key]) && $oldValues[$key] != $newValue) {
- $changes[] = trans("activitylog::infolists.components.from_oldvalue_to_newvalue",
+ $changes[] = trans('activitylog::infolists.components.from_oldvalue_to_newvalue',
[
- "key" => $key,
- "old_value" => htmlspecialchars($oldValue),
- "new_value" => htmlspecialchars($newValue)
+ 'key' => $key,
+ 'old_value' => htmlspecialchars($oldValue),
+ 'new_value' => htmlspecialchars($newValue),
]);
} else {
- $changes[] = trans("activitylog::infolists.components.to_newvalue",
+ $changes[] = trans('activitylog::infolists.components.to_newvalue',
[
- "key" => $key,
- "new_value" => htmlspecialchars($newValue)
+ 'key' => $key,
+ 'new_value' => htmlspecialchars($newValue),
]);
}
}
diff --git a/src/Infolists/Components/TimeLineTitleEntry.php b/src/Infolists/Components/TimeLineTitleEntry.php
index 2aa14ed..5124e02 100644
--- a/src/Infolists/Components/TimeLineTitleEntry.php
+++ b/src/Infolists/Components/TimeLineTitleEntry.php
@@ -2,15 +2,14 @@
namespace Rmsramos\Activitylog\Infolists\Components;
-use Carbon\Carbon;
use Closure;
use Filament\Forms\Components\Concerns\CanAllowHtml;
use Filament\Infolists\Components\Entry;
use Filament\Support\Concerns\HasExtraAttributes;
use Illuminate\Support\HtmlString;
use Illuminate\Support\Str;
-use Rmsramos\Activitylog\Infolists\Concerns\HasModifyState;
use Rmsramos\Activitylog\ActivitylogPlugin;
+use Rmsramos\Activitylog\Infolists\Concerns\HasModifyState;
class TimeLineTitleEntry extends Entry
{
@@ -61,16 +60,16 @@ private function modifiedTitle($state): string|HtmlString|Closure
$className = Str::lower(Str::snake(class_basename($state['subject']), ' '));
$causerName = $this->getCauserName($state['causer']);
- $parser = ActivitylogPlugin::get()->getDateParser();
+ $parser = ActivitylogPlugin::get()->getDateParser();
$update_at = $parser($state['update'])->format(ActivitylogPlugin::get()->getDatetimeFormat());
return new HtmlString(
- __("activitylog::infolists.components.created_by_at",
+ __('activitylog::infolists.components.created_by_at',
[
- "subject" => ActivitylogPlugin::get()->getTranslateSubject($className),
- "event"=>__("activitylog::action.event.".$state['event']),
- "causer"=>$causerName,
- "update_at"=>$update_at
+ 'subject' => ActivitylogPlugin::get()->getTranslateSubject($className),
+ 'event' => __('activitylog::action.event.' . $state['event']),
+ 'causer' => $causerName,
+ 'update_at' => $update_at,
]),
);
}
diff --git a/src/Infolists/Concerns/HasModifyState.php b/src/Infolists/Concerns/HasModifyState.php
index 219f453..596ab57 100644
--- a/src/Infolists/Concerns/HasModifyState.php
+++ b/src/Infolists/Concerns/HasModifyState.php
@@ -23,6 +23,6 @@ public function getModifiedState(): null|string|HtmlString
private function getCauserName($causer): string
{
- return $causer->name ?? $causer->first_name ?? $causer->last_name ?? $causer->username ?? trans("activitylog::infolists.components.unknown");
+ return $causer->name ?? $causer->first_name ?? $causer->last_name ?? $causer->username ?? trans('activitylog::infolists.components.unknown');
}
}
diff --git a/src/Resources/ActivitylogResource.php b/src/Resources/ActivitylogResource.php
index 771861a..63babd1 100755
--- a/src/Resources/ActivitylogResource.php
+++ b/src/Resources/ActivitylogResource.php
@@ -2,7 +2,9 @@
namespace Rmsramos\Activitylog\Resources;
+use Exception;
use Filament\Facades\Filament;
+use Filament\Forms\Components\Actions\Action;
use Filament\Forms\Components\DatePicker;
use Filament\Forms\Components\KeyValue;
use Filament\Forms\Components\Placeholder;
@@ -11,6 +13,7 @@
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Form;
+use Filament\Notifications\Notification;
use Filament\Resources\Resource;
use Filament\Tables\Columns\Column;
use Filament\Tables\Columns\TextColumn;
@@ -20,22 +23,19 @@
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
-use Illuminate\Support\Carbon;
+use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\HtmlString;
use Illuminate\Support\Str;
use Livewire\Component as Livewire;
use Rmsramos\Activitylog\Actions\Concerns\ActionContent;
use Rmsramos\Activitylog\ActivitylogPlugin;
+use Rmsramos\Activitylog\Helpers\ActivityLogHelper;
use Rmsramos\Activitylog\RelationManagers\ActivitylogRelationManager;
use Rmsramos\Activitylog\Resources\ActivitylogResource\Pages\ListActivitylog;
use Rmsramos\Activitylog\Resources\ActivitylogResource\Pages\ViewActivitylog;
-use Spatie\Activitylog\Models\Activity;
-use Filament\Forms\Components\Actions\Action;
-use Filament\Notifications\Notification;
-use Rmsramos\Activitylog\Helpers\ActivityLogHelper;
use Rmsramos\Activitylog\Traits\HasCustomActivityResource;
-
+use Spatie\Activitylog\Models\Activity;
class ActivitylogResource extends Resource
{
@@ -88,80 +88,123 @@ public static function restoreActivity(int|string $key): void
{
$activity = Activity::find($key);
- $oldProperties = data_get($activity, 'properties.old');
+ 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"))
+ ->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);
});
// Log the restore event
- activity()
- ->performedOn($record)
- ->causedBy(auth()->user())
- ->withProperties(["attributes"=>$oldProperties, "old" => $newProperties])
- ->tap(function ($log) {
- $log->event = 'restored';
- })
- ->log('restored');
+ $user = auth()->user();
+
+ if ($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"))
+ ->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()]))
+ ->title(__('activitylog::notifications.failed_to_restore_activity', ['error' => $e->getMessage()]))
->danger()
->send();
}
}
- private static function getResourceUrl($record)
+ private static function getResourceUrl(Activity $record): string
{
$panelID = Filament::getCurrentPanel()->getId();
if ($record->subject_type && $record->subject_id) {
- $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]);
+ 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 '#';
}
- private static function canViewResource($record)
+ private static function canViewResource(Activity $record): bool
{
- return true;
if ($record->subject_type && $record->subject_id) {
- $model = app($record->subject_type);
- if (ActivityLogHelper::classUsesTrait($model, HasCustomActivityResource::class)) {
- $resourceModel = $model->getFilamentActualResourceModel($record);
- return auth()->user()->can('update', $resourceModel);
- }
-
- // Fallback to check if the user can edit the model using a generic policy
- return auth()->user()->can('update', $record->subject);
+ 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;
@@ -176,14 +219,14 @@ public static function form(Form $form): Form
TextInput::make('causer_id')
->afterStateHydrated(function ($component, ?Model $record) {
/** @phpstan-ignore-next-line */
- return $component->state($record->causer?->name);
+ return $component->state($record?->causer?->name ?? '-');
})
->label(__('activitylog::forms.fields.causer.label')),
TextInput::make('subject_type')
->afterStateHydrated(function ($component, ?Model $record, $state) {
- /** @var Activity&ActivityModel $record */
- return $state ? $component->state(Str::of($state)->afterLast('\\')->headline() . ' # ' . $record->subject_id) : '-';
+ /** @var Activity $record */
+ return $state ? $component->state(Str::of($state)->afterLast('\\')->headline() . ' # ' . $record->subject_id) : $component->state('-');
})
->label(__('activitylog::forms.fields.subject_type.label')),
@@ -195,75 +238,89 @@ public static function form(Form $form): Form
Section::make([
Placeholder::make('log_name')
->content(function (?Model $record): string {
- /** @var Activity&ActivityModel $record */
- return $record->log_name ? ucwords($record->log_name) : '-';
+ /** @var Activity $record */
+ return $record?->log_name ? ucwords($record->log_name) : '-';
})
->label(__('activitylog::forms.fields.log_name.label')),
Placeholder::make('event')
->content(function (?Model $record): string {
- /** @phpstan-ignore-next-line */
- return $record?->event ? ucwords(__('activitylog::action.event.'.$record?->event)) : '-';
+ /** @var Activity $record */
+ return $record?->event ? ucwords(__('activitylog::action.event.' . $record->event)) : '-';
})
->label(__('activitylog::forms.fields.event.label')),
Placeholder::make('created_at')
->label(__('activitylog::forms.fields.created_at.label'))
->content(function (?Model $record): string {
- /** @var Activity&ActivityModel $record */
+ /** @var Activity $record */
+ if (! $record?->created_at) {
+ return '-';
+ }
$parser = ActivitylogPlugin::get()->getDateParser();
- return $record->created_at ?
- $parser($record->created_at)
- ->format(ActivitylogPlugin::get()->getDatetimeFormat())
- : '-';
+ return $parser($record->created_at)
+ ->format(ActivitylogPlugin::get()->getDatetimeFormat());
}),
])->grow(false),
])->from('md'),
Section::make(__('activitylog::forms.changes'))
->headerActions([
- Action::make(__('activitylog::action.restore'))
- ->icon('heroicon-o-eye')
+ Action::make('restore')
+ ->label(__('activitylog::action.restore'))
+ ->icon('heroicon-o-arrow-uturn-left')
->color('primary')
->action(fn (Activity $record) => self::restoreActivity($record->id))
- ->visible(fn () => !ActivitylogPlugin::get()->getIsRestoreActionHidden() ?? true)
- ->authorize(fn () => auth()->user()->can("restore_activitylog"))
+ ->visible(fn () => ! ActivitylogPlugin::get()->getIsRestoreActionHidden())
+ ->authorize(fn () => auth()->user()?->can('restore_activitylog') ?? false)
->requiresConfirmation(),
- Action::make(__('activitylog::action.edit'))
- ->label(fn () => ActivitylogPlugin::get()->getResourceActionLabel() ?? __('activitylog::action.edit'))
+ Action::make('edit')
+ ->label(__('activitylog::action.edit'))
->icon('heroicon-o-eye')
->color('info')
- ->url(fn ($record) => self::getResourceUrl($record))
- ->visible(fn () => !ActivitylogPlugin::get()->getIsResourceActionHidden() ?? true)
- ->authorize(fn ($record) => self::canViewResource($record))
- ,
+ ->url(fn (Activity $record) => self::getResourceUrl($record))
+ ->visible(fn () => ! ActivitylogPlugin::get()->getIsResourceActionHidden())
+ ->authorize(fn (Activity $record) => self::canViewResource($record)),
])
->columns()
- ->visible(fn ($record) => $record->properties?->count() > 0)
+ ->visible(fn (?Model $record) => $record?->properties?->count() > 0)
->schema(function (?Model $record) {
- /** @var Activity&ActivityModel $record */
- $properties = $record->properties->except(['attributes', 'old']);
+ /** @var Activity $record */
+ if (! $record?->properties) {
+ return [];
+ }
- $schema = [];
+ $properties = $record->properties->except(['attributes', 'old']);
+ $schema = [];
if ($properties->count()) {
$schema[] = KeyValue::make('properties')
+ ->afterStateHydrated(function (KeyValue $component) use ($properties) {
+ $component->state($properties->toArray());
+ })
->label(__('activitylog::forms.fields.properties.label'))
- ->columnSpan('full');
+ ->columnSpan('full')
+ ->disabled();
}
if ($old = $record->properties->get('old')) {
$schema[] = KeyValue::make('old')
- ->formatStateUsing(fn () => self::formatDateValues($old))
- ->label(__('activitylog::forms.fields.old.label'));
+ ->afterStateHydrated(function (KeyValue $component) use ($old) {
+ $component->state(is_array($old) ? $old : []);
+ })
+ ->label(__('activitylog::forms.fields.old.label'))
+ ->disabled();
}
if ($attributes = $record->properties->get('attributes')) {
$schema[] = KeyValue::make('attributes')
- ->formatStateUsing(fn () => self::formatDateValues($attributes))
- ->label(__('activitylog::forms.fields.attributes.label'));
+ ->afterStateHydrated(function (KeyValue $component) use ($attributes) {
+ $component->state(is_array($attributes) ? $attributes : []);
+ })
+ ->label(__('activitylog::forms.fields.attributes.label'))
+ ->disabled();
}
return $schema;
@@ -282,7 +339,10 @@ public static function table(Table $table): Table
static::getPropertiesColumnComponent(),
static::getCreatedAtColumnComponent(),
])
- ->defaultSort(config('filament-activitylog.resources.default_sort_column', 'created_at'), config('filament-activitylog.resources.default_sort_direction', 'asc'))
+ ->defaultSort(
+ config('filament-activitylog.resources.default_sort_column', 'created_at'),
+ config('filament-activitylog.resources.default_sort_direction', 'desc')
+ )
->filters([
static::getDateFilterComponent(),
static::getEventFilterComponent(),
@@ -293,7 +353,7 @@ public static function getLogNameColumnComponent(): Column
{
return TextColumn::make('log_name')
->label(__('activitylog::tables.columns.log_name.label'))
- ->formatStateUsing(fn ($state) => ucwords($state))
+ ->formatStateUsing(fn ($state) => $state ? ucwords($state) : '-')
->searchable()
->sortable()
->badge();
@@ -303,15 +363,15 @@ public static function getEventColumnComponent(): Column
{
return TextColumn::make('event')
->label(__('activitylog::tables.columns.event.label'))
- ->formatStateUsing(fn ($state) => ucwords(__("activitylog::action.event.".$state)))
+ ->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',
+ ->color(fn (?string $state): string => match ($state) {
+ 'draft' => 'gray',
+ 'updated' => 'warning',
+ 'created' => 'success',
+ 'deleted' => 'danger',
+ 'restored' => 'info',
+ default => 'primary',
})
->searchable()
->sortable();
@@ -322,7 +382,7 @@ public static function getSubjectTypeColumnComponent(): Column
return TextColumn::make('subject_type')
->label(__('activitylog::tables.columns.subject_type.label'))
->formatStateUsing(function ($state, Model $record) {
- /** @var Activity&ActivityModel $record */
+ /** @var Activity $record */
if (! $state) {
return '-';
}
@@ -338,13 +398,12 @@ public static function getCauserNameColumnComponent(): Column
return TextColumn::make('causer.name')
->label(__('activitylog::tables.columns.causer.label'))
->getStateUsing(function (Model $record) {
- // Check if causer is null or causer_id is null
- if ($record->causer_id == null || $record->causer == null) {
+ /** @var Activity $record */
+ if ($record->causer_id === null || $record->causer === null) {
return new HtmlString('—');
}
- // Return the causer's name if causer exists
- return $record->causer->name;
+ return $record->causer->name ?? new HtmlString('—');
})
->searchable();
}
@@ -368,6 +427,7 @@ public static function getCreatedAtColumnComponent(): Column
// Apply the custom callback if set
$callback = ActivitylogPlugin::get()->getDatetimeColumnCallback();
+
if ($callback) {
$column = $callback($column);
}
@@ -381,22 +441,20 @@ public static function getDateFilterComponent(): Filter
->label(__('activitylog::tables.filters.created_at.label'))
->indicateUsing(function (array $data): array {
$indicators = [];
- $parser = ActivitylogPlugin::get()->getDateParser();
+ $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())
- ]);
+ $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())
- ]);
+ $indicators['created_until'] = __('activitylog::tables.filters.created_at.created_until_indicator', [
+ 'created_until' => $parser($data['created_until'])
+ ->format(ActivitylogPlugin::get()->getDateFormat()),
+ ]);
}
return $indicators;
@@ -408,24 +466,25 @@ public static function getDateFilterComponent(): Filter
->query(function (Builder $query, array $data): Builder {
return $query
->when(
- $data['created_from'],
+ $data['created_from'] ?? null,
fn (Builder $query, $date): Builder => $query->whereDate('created_at', '>=', $date),
)
->when(
- $data['created_until'],
+ $data['created_until'] ?? null,
fn (Builder $query, $date): Builder => $query->whereDate('created_at', '<=', $date),
);
});
}
- public static function getDatePickerCompoment($label): DatePicker
+ public static function getDatePickerCompoment(string $label): DatePicker
{
$field = DatePicker::make($label)
- ->format(ActivitylogPlugin::get()->getDateFormat())
- ->label(__('activitylog::tables.filters.created_at.'.$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);
}
@@ -433,7 +492,7 @@ public static function getDatePickerCompoment($label): DatePicker
return $field;
}
- public static function getEventFilterCompoment(): SelectFilter
+ public static function getEventFilterComponent(): SelectFilter
{
return SelectFilter::make('event')
->label(__('activitylog::tables.filters.event.label'))
@@ -443,7 +502,6 @@ public static function getEventFilterCompoment(): SelectFilter
);
}
-
public static function getPages(): array
{
return [
@@ -456,7 +514,7 @@ public static function shouldRegisterNavigation(): bool
{
$plugin = Filament::getCurrentPanel()?->getPlugin('rmsramos/activitylog');
- return $plugin->getNavigationItem();
+ return $plugin?->getNavigationItem() ?? false;
}
public static function canAccess(): bool
@@ -465,8 +523,8 @@ public static function canAccess(): bool
if ($policy && method_exists($policy, 'viewAny')) {
return static::canViewAny();
- } else {
- return ActivitylogPlugin::get()->isAuthorized();
}
+
+ return ActivitylogPlugin::get()->isAuthorized();
}
}
diff --git a/src/Traits/HasCustomActivityResource.php b/src/Traits/HasCustomActivityResource.php
index 2e6004a..fd54362 100644
--- a/src/Traits/HasCustomActivityResource.php
+++ b/src/Traits/HasCustomActivityResource.php
@@ -3,7 +3,6 @@
namespace Rmsramos\Activitylog\Traits;
use Illuminate\Database\Eloquent\Model;
-use \Illuminate\Support\Str;
trait HasCustomActivityResource
{
@@ -11,9 +10,8 @@ trait HasCustomActivityResource
* Retrieve the associated Filament resource model.
*
* @param Model $record The activity log record providing context.
- * @return Model
*/
- public function getFilamentActualResourceModel($record) : Model
+ public function getFilamentActualResourceModel($record): Model
{
return $record;
}