mirror of
https://github.com/pelican-dev/panel.git
synced 2026-02-24 11:20:41 +03:00
Compare commits
2 Commits
lance/1742
...
charles/ne
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c1fa74ebfd | ||
|
|
0e810f3110 |
@@ -37,13 +37,20 @@ use Filament\Schemas\Components\Section;
|
||||
use Filament\Schemas\Components\Tabs;
|
||||
use Filament\Schemas\Components\Tabs\Tab;
|
||||
use Filament\Schemas\Components\Utilities\Get;
|
||||
use Filament\Schemas\Components\View;
|
||||
use Filament\Schemas\Schema;
|
||||
use Filament\Support\Colors\Color;
|
||||
use Filament\Support\Enums\IconSize;
|
||||
use Filament\Support\Enums\Width;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Filament\Tables\Columns\ImageColumn;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Concerns\InteractsWithTable;
|
||||
use Filament\Tables\Contracts\HasTable;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
// Filament Tables imports
|
||||
use Illuminate\Support\HtmlString;
|
||||
use Illuminate\Validation\Rules\Password;
|
||||
use Laravel\Socialite\Facades\Socialite;
|
||||
@@ -52,10 +59,11 @@ use Livewire\Features\SupportFileUploads\TemporaryUploadedFile;
|
||||
/**
|
||||
* @method User getUser()
|
||||
*/
|
||||
class EditProfile extends BaseEditProfile
|
||||
class EditProfile extends BaseEditProfile implements HasTable
|
||||
{
|
||||
use CanCustomizeHeaderActions;
|
||||
use CanCustomizeHeaderWidgets;
|
||||
use InteractsWithTable;
|
||||
|
||||
protected OAuthService $oauthService;
|
||||
|
||||
@@ -158,15 +166,22 @@ class EditProfile extends BaseEditProfile
|
||||
if ($fileUpload->getDisk()->exists($path)) {
|
||||
return $path;
|
||||
}
|
||||
|
||||
return null; // explicit return to satisfy static analysis
|
||||
})
|
||||
->deleteUploadedFileUsing(function (FileUpload $fileUpload, $file) {
|
||||
if ($file instanceof TemporaryUploadedFile) {
|
||||
return $file->delete();
|
||||
// Ensure we return a boolean even if delete() doesn't return one
|
||||
$file->delete();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($fileUpload->getDisk()->exists($file)) {
|
||||
return $fileUpload->getDisk()->delete($file);
|
||||
}
|
||||
|
||||
return false; // explicit return
|
||||
}),
|
||||
]),
|
||||
Tab::make('oauth')
|
||||
@@ -412,19 +427,8 @@ class EditProfile extends BaseEditProfile
|
||||
->label(trans('profile.tabs.activity'))
|
||||
->icon('tabler-history')
|
||||
->schema([
|
||||
Repeater::make('activity')
|
||||
->hiddenLabel()
|
||||
->inlineLabel(false)
|
||||
->deletable(false)
|
||||
->addable(false)
|
||||
->relationship(null, function (Builder $query) {
|
||||
$query->orderBy('timestamp', 'desc');
|
||||
})
|
||||
->schema([
|
||||
TextEntry::make('log')
|
||||
->hiddenLabel()
|
||||
->state(fn (ActivityLog $log) => new HtmlString($log->htmlable())),
|
||||
]),
|
||||
View::make('filament.pages.profile.activity-table')
|
||||
->columnSpanFull(),
|
||||
]),
|
||||
Tab::make('customization')
|
||||
->label(trans('profile.tabs.customization'))
|
||||
@@ -548,6 +552,64 @@ class EditProfile extends BaseEditProfile
|
||||
->inlineLabel(!static::isSimple());
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the Filament Table that will be rendered in the Activity tab.
|
||||
*/
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
$user = $this->getUser();
|
||||
|
||||
return $table
|
||||
->query(fn () => ActivityLog::query()
|
||||
->with('actor')
|
||||
->whereHas('subjects', fn ($q) => $q->where('subject_id', $user->getKey())->where('subject_type', $user->getMorphClass()))
|
||||
->orderBy('timestamp', 'desc')
|
||||
)
|
||||
->columns([
|
||||
ImageColumn::make('avatar')
|
||||
->label('')
|
||||
->getStateUsing(fn ($record) => Filament::getUserAvatarUrl($record->actor ?? $user))
|
||||
->circular()
|
||||
->imageWidth(50)->imageHeight(50)
|
||||
->toggleable()
|
||||
->alignCenter(),
|
||||
TextColumn::make('timestamp')
|
||||
->label(trans('profile.activity.timestamp'))
|
||||
->tooltip(fn ($state) => $state->format('M j, Y g:ia'))
|
||||
->formatStateUsing(fn ($state) => $state->diffForHumans())
|
||||
->toggleable(),
|
||||
TextColumn::make('actor.username')
|
||||
->label(trans('profile.activity.actor'))
|
||||
->formatStateUsing(fn ($state, $record) => $record->actor?->username ?? trans('activity.system'))
|
||||
->toggleable()
|
||||
->searchable(),
|
||||
TextColumn::make('ip')
|
||||
->label(trans('profile.activity.ip'))
|
||||
->visible(fn () => user()?->can('seeIps activityLog'))
|
||||
->toggleable()
|
||||
->searchable(),
|
||||
TextColumn::make('event')
|
||||
->label(trans('profile.activity.event'))
|
||||
->toggleable()
|
||||
->toggledHiddenByDefault(true)
|
||||
->searchable(),
|
||||
TextColumn::make('properties')
|
||||
->label(trans('activity.metadata'))
|
||||
->formatStateUsing(function ($state, $record) {
|
||||
$label = strip_tags(($record->getLabel() ?? ''));
|
||||
|
||||
if (trim($label) !== '') {
|
||||
return $label;
|
||||
}
|
||||
})
|
||||
->limit(200)
|
||||
->wrap()
|
||||
->searchable(),
|
||||
])
|
||||
->defaultSort('timestamp', 'desc')
|
||||
->paginated();
|
||||
}
|
||||
|
||||
protected function getFormActions(): array
|
||||
{
|
||||
return [];
|
||||
|
||||
@@ -168,33 +168,33 @@ class ActivityLog extends Model implements HasIcon, HasLabel
|
||||
return user()?->can('seeIps activityLog') ? $this->ip : null;
|
||||
}
|
||||
|
||||
public function htmlable(): string
|
||||
{
|
||||
$user = $this->actor;
|
||||
if (!$user instanceof User) {
|
||||
$user = new User([
|
||||
'email' => 'system@pelican.dev',
|
||||
'username' => 'system',
|
||||
]);
|
||||
}
|
||||
|
||||
$avatarUrl = Filament::getUserAvatarUrl($user);
|
||||
$username = str($user->username)->stripTags();
|
||||
$ip = $this->getIp();
|
||||
$ip = $ip ? $ip . ' — ' : '';
|
||||
|
||||
return "
|
||||
<div style='display: flex; align-items: center;'>
|
||||
<img width='50px' height='50px' src='{$avatarUrl}' style='margin-right: 15px; border-radius: 50%;' />
|
||||
|
||||
<div>
|
||||
<p>$username — $this->event</p>
|
||||
<p>{$this->getLabel()}</p>
|
||||
<p>$ip<span title='{$this->timestamp->format('M j, Y g:ia')}'>{$this->timestamp->diffForHumans()}</span></p>
|
||||
</div>
|
||||
</div>
|
||||
";
|
||||
}
|
||||
// public function htmlable(): string
|
||||
// {
|
||||
// $user = $this->actor;
|
||||
// if (!$user instanceof User) {
|
||||
// $user = new User([
|
||||
// 'email' => 'system@pelican.dev',
|
||||
// 'username' => 'system',
|
||||
// ]);
|
||||
// }
|
||||
//
|
||||
// $avatarUrl = Filament::getUserAvatarUrl($user);
|
||||
// $username = str($user->username)->stripTags();
|
||||
// $ip = $this->getIp();
|
||||
// $ip = $ip ? $ip . ' — ' : '';
|
||||
//
|
||||
// return "
|
||||
// <div style='display: flex; align-items: center;'>
|
||||
// <img width='50px' height='50px' src='{$avatarUrl}' style='margin-right: 15px; border-radius: 50%;' />
|
||||
//
|
||||
// <div>
|
||||
// <p>$username — $this->event</p>
|
||||
// <p>{$this->getLabel()}</p>
|
||||
// <p>$ip<span title='{$this->timestamp->format('M j, Y g:ia')}'>{$this->timestamp->diffForHumans()}</span></p>
|
||||
// </div>
|
||||
// </div>
|
||||
// ";
|
||||
// }
|
||||
|
||||
/**
|
||||
* @return array<string, string>
|
||||
|
||||
@@ -257,12 +257,12 @@ class PluginService
|
||||
}
|
||||
}
|
||||
|
||||
public function buildAssets(): bool
|
||||
public function buildAssets(bool $throw = false): bool
|
||||
{
|
||||
try {
|
||||
$result = Process::path(base_path())->timeout(300)->run('yarn install');
|
||||
if ($result->failed()) {
|
||||
throw new Exception('Could not install dependencies: ' . $result->errorOutput());
|
||||
throw new Exception('Could not install yarn dependencies: ' . $result->errorOutput());
|
||||
}
|
||||
|
||||
$result = Process::path(base_path())->timeout(600)->run('yarn build');
|
||||
@@ -272,7 +272,7 @@ class PluginService
|
||||
|
||||
return true;
|
||||
} catch (Exception $exception) {
|
||||
if ($this->isDevModeActive()) {
|
||||
if ($throw || $this->isDevModeActive()) {
|
||||
throw ($exception);
|
||||
}
|
||||
|
||||
@@ -296,7 +296,7 @@ class PluginService
|
||||
}
|
||||
}
|
||||
|
||||
$this->buildAssets();
|
||||
$this->buildAssets($plugin->isTheme());
|
||||
|
||||
$this->runPluginMigrations($plugin);
|
||||
|
||||
|
||||
@@ -12,6 +12,13 @@ return [
|
||||
'2fa' => '2FA',
|
||||
'customization' => 'Customization',
|
||||
],
|
||||
'activity' => [
|
||||
'timestamp' => 'Timestamp',
|
||||
'actor' => 'Actor',
|
||||
'ip' => 'IP Address',
|
||||
'event' => 'Event',
|
||||
'metadata' => 'Metadata',
|
||||
],
|
||||
'username' => 'Username',
|
||||
'admin' => 'Admin',
|
||||
'exit_admin' => 'Exit Admin',
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{{ $this->table }}
|
||||
Reference in New Issue
Block a user