Compare commits

...

2 Commits

Author SHA1 Message Date
Charles
c1fa74ebfd WIP 2026-01-14 13:21:01 -05:00
Boy132
0e810f3110 Throw yarn errors when installing themes (#2104) 2026-01-14 08:23:24 +01:00
5 changed files with 117 additions and 47 deletions

View File

@@ -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 [];

View File

@@ -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>

View File

@@ -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);

View File

@@ -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',

View File

@@ -0,0 +1 @@
{{ $this->table }}