Skip to content

Commit

Permalink
update activity log list
Browse files Browse the repository at this point in the history
  • Loading branch information
Boy132 committed Jan 24, 2025
1 parent 68a87b7 commit 3c87e41
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 52 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,18 @@
use App\Filament\Admin\Resources\UserResource\Pages\EditUser;
use App\Filament\Server\Resources\ActivityResource;
use App\Models\ActivityLog;
use App\Models\User;
use App\Filament\Components\Tables\Columns\DateTimeColumn;
use App\Models\User;
use Filament\Forms\Components\Actions\Action;
use Filament\Forms\Components\DateTimePicker;
use Filament\Forms\Components\KeyValue;
use Filament\Forms\Components\Placeholder;
use Filament\Forms\Components\TextInput;
use Filament\Resources\Pages\ListRecords;
use Filament\Tables\Actions\ViewAction;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
use Illuminate\Support\HtmlString;

class ListActivities extends ListRecords
{
Expand All @@ -22,29 +29,45 @@ public function table(Table $table): Table
TextColumn::make('event')
->html()
->description(fn ($state) => $state)
->formatStateUsing(function ($state, ActivityLog $activityLog) {
$properties = $activityLog->wrapProperties();

return trans_choice('activity.'.str($state)->replace(':', '.'), array_get($properties, 'count', 1), $properties);
})
->tooltip(function (ActivityLog $activityLog) {
if ($files = array_get($activityLog->properties, 'files')) {
return is_array($files) ? implode(',', $files) : null;
}

if ($logs = array_get($activityLog->properties, 'logs')) {
return $logs;
}
}),
->icon(fn (ActivityLog $activityLog) => $activityLog->getIcon())
->formatStateUsing(fn (ActivityLog $activityLog) => $activityLog->getLabel()),
TextColumn::make('user')
->state(fn (ActivityLog $activityLog) => $activityLog->actor instanceof User ? $activityLog->actor->username : 'System')
->tooltip(fn (ActivityLog $activityLog) => auth()->user()->can('seeIps activityLog') ? $activityLog->ip : '')
->url(fn (ActivityLog $activityLog): string => $activityLog->actor instanceof User ? EditUser::getUrl(['record' => $activityLog->actor], panel: 'admin', tenant: null) : ''),
->url(fn (ActivityLog $activityLog) => $activityLog->actor instanceof User ? EditUser::getUrl(['record' => $activityLog->actor], panel: 'admin') : '')
->grow(false),
DateTimeColumn::make('timestamp')
->since()
->sortable(),
->sortable()
->grow(false),
])
->defaultSort('timestamp', 'desc');
->defaultSort('timestamp', 'desc')
->actions([
ViewAction::make()
//->visible(fn (ActivityLog $activityLog) => $activityLog->hasAdditionalMetadata())
->form([
Placeholder::make('event')
->content(fn (ActivityLog $activityLog) => new HtmlString($activityLog->getLabel())),
TextInput::make('user')
->formatStateUsing(function (ActivityLog $activityLog) {
if (!$activityLog->actor instanceof User) {
return 'System';
}

return "{$activityLog->actor->username} ({$activityLog->actor->email})" . (auth()->user()->can('seeIps activityLog') ? " - {$activityLog->ip}" : '');
})
->hintAction(
Action::make('edit')
->label(__('filament-actions::edit.single.label'))
->icon('tabler-edit')
->visible(fn (ActivityLog $activityLog) => $activityLog->actor instanceof User && auth()->user()->can('update user'))
->url(fn (ActivityLog $activityLog) => EditUser::getUrl(['record' => $activityLog->actor], panel: 'admin'))
),
DateTimePicker::make('timestamp'),
KeyValue::make('properties')
->label('Metadata'),
]),
]);
}

public function getBreadcrumbs(): array
Expand Down
54 changes: 50 additions & 4 deletions app/Models/ActivityLog.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
use Carbon\Carbon;
use Illuminate\Support\Facades\Event;
use App\Events\ActivityLogged;
use Filament\Support\Contracts\HasIcon;
use Filament\Support\Contracts\HasLabel;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\MassPrunable;
use Illuminate\Database\Eloquent\Relations\HasOne;
Expand Down Expand Up @@ -47,7 +49,7 @@
* @method static Builder|ActivityLog whereProperties($value)
* @method static Builder|ActivityLog whereTimestamp($value)
*/
class ActivityLog extends Model
class ActivityLog extends Model implements HasIcon, HasLabel
{
use MassPrunable;

Expand Down Expand Up @@ -143,6 +145,22 @@ protected static function boot(): void
});
}

public function getIcon(): string
{
if ($this->apiKey) {
return 'tabler-api';
}

return $this->actor instanceof User ? 'tabler-user' : 'tabler-device-desktop';
}

public function getLabel(): string
{
$properties = $this->wrapProperties();

return trans_choice('activity.'.str($this->event)->replace(':', '.'), array_key_exists('count', $properties) ? $properties['count'] : 1, $properties);
}

public function htmlable(): string
{
$user = $this->actor;
Expand All @@ -152,16 +170,14 @@ public function htmlable(): string
'username' => 'system',
]);
}
$properties = $this->wrapProperties();
$event = trans_choice('activity.'.str($this->event)->replace(':', '.'), array_key_exists('count', $properties) ? $properties['count'] : 1, $properties);

return "
<div style='display: flex; align-items: center;'>
<img width='50px' height='50px' src='{$user->getFilamentAvatarUrl()}' style='margin-right: 15px' />
<div>
<p>$user->username$this->event</p>
<p>$event</p>
<p>{$this->getLabel()}</p>
<p>$this->ip — <span title='{$this->timestamp->format('M j, Y g:ia')}'>{$this->timestamp->diffForHumans()}</span></p>
</div>
</div>
Expand Down Expand Up @@ -201,4 +217,34 @@ public function wrapProperties(): array

return $properties->toArray();
}

/**
* Determines if there are any log properties that we've not already exposed
* in the response language string and that are not just the IP address or
* the browser useragent.
*
* This is used by the front-end to selectively display an "additional metadata"
* button that is pointless if there is nothing the user can't already see from
* the event description.
*/
public function hasAdditionalMetadata(): bool
{
if (!$this->properties || $this->properties->isEmpty()) {
return [];
}

$properties = $this->wrapProperties();
$event = trans_choice('activity.'.str($this->event)->replace(':', '.'), array_key_exists('count', $properties) ? $properties['count'] : 1);

preg_match_all('/:(?<key>[\w.-]+\w)(?:[^\w:]?|$)/', $event, $matches);

$exclude = array_merge($matches['key'], ['ip', 'useragent', 'using_sftp']);
foreach ($this->properties->keys() as $key) {
if (!in_array($key, $exclude, true)) {
return true;
}
}

return false;
}
}
32 changes: 2 additions & 30 deletions app/Transformers/Api/Client/ActivityLogTransformer.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public function transform(ActivityLog $model): array
'ip' => $this->canViewIP($model->actor) ? $model->ip : null,
'description' => $model->description,
'properties' => $model->wrapProperties(),
'has_additional_metadata' => $this->hasAdditionalMetadata($model),
'has_additional_metadata' => $model->hasAdditionalMetadata(),
'timestamp' => $model->timestamp->toAtomString(),
];
}
Expand All @@ -43,40 +43,12 @@ public function includeActor(ActivityLog $model): ResourceAbstract
return $this->item($model->actor, $this->makeTransformer(UserTransformer::class), User::RESOURCE_NAME);
}

/**
* Determines if there are any log properties that we've not already exposed
* in the response language string and that are not just the IP address or
* the browser useragent.
*
* This is used by the front-end to selectively display an "additional metadata"
* button that is pointless if there is nothing the user can't already see from
* the event description.
*/
protected function hasAdditionalMetadata(ActivityLog $model): bool
{
if (is_null($model->properties) || $model->properties->isEmpty()) {
return false;
}

$str = trans('activity.' . str_replace(':', '.', $model->event));
preg_match_all('/:(?<key>[\w.-]+\w)(?:[^\w:]?|$)/', $str, $matches);

$exclude = array_merge($matches['key'], ['ip', 'useragent', 'using_sftp']);
foreach ($model->properties->keys() as $key) {
if (!in_array($key, $exclude, true)) {
return true;
}
}

return false;
}

/**
* Determines if the user can view the IP address in the output either because they are the
* actor that performed the action, or because they are an administrator on the Panel.
*/
protected function canViewIP(?Model $actor = null): bool
{
return $actor?->is($this->request->user()) || $this->request->user()->isRootAdmin();
return $actor?->is($this->request->user()) || $this->request->user()->can('seeIps activityLog');
}
}

0 comments on commit 3c87e41

Please sign in to comment.