Separate user uploadable avatars into own setting (#1286)

This commit is contained in:
Boy132 2025-04-23 16:02:52 +02:00 committed by GitHub
parent 90fd73f6a4
commit 914e215bc0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 42 additions and 52 deletions

View File

@ -2,11 +2,11 @@
namespace App\Extensions\Avatar; namespace App\Extensions\Avatar;
use Filament\AvatarProviders\Contracts\AvatarProvider as AvatarProviderContract; use App\Models\User;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
use Illuminate\Support\Str; use Illuminate\Support\Str;
abstract class AvatarProvider implements AvatarProviderContract abstract class AvatarProvider
{ {
/** /**
* @var array<string, static> * @var array<string, static>
@ -33,6 +33,8 @@ abstract class AvatarProvider implements AvatarProviderContract
abstract public function getId(): string; abstract public function getId(): string;
abstract public function get(User $user): ?string;
public function getName(): string public function getName(): string
{ {
return Str::title($this->getId()); return Str::title($this->getId());

View File

@ -4,8 +4,6 @@ namespace App\Extensions\Avatar\Providers;
use App\Extensions\Avatar\AvatarProvider; use App\Extensions\Avatar\AvatarProvider;
use App\Models\User; use App\Models\User;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Model;
class GravatarProvider extends AvatarProvider class GravatarProvider extends AvatarProvider
{ {
@ -14,10 +12,9 @@ class GravatarProvider extends AvatarProvider
return 'gravatar'; return 'gravatar';
} }
public function get(Model|Authenticatable $record): string public function get(User $user): string
{ {
/** @var User $record */ return 'https://gravatar.com/avatar/' . md5($user->email);
return 'https://gravatar.com/avatar/' . md5($record->email);
} }
public static function register(): self public static function register(): self

View File

@ -1,29 +0,0 @@
<?php
namespace App\Extensions\Avatar\Providers;
use App\Extensions\Avatar\AvatarProvider;
use Filament\AvatarProviders\UiAvatarsProvider as FilamentUiAvatarsProvider;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Storage;
class LocalAvatarProvider extends AvatarProvider
{
public function getId(): string
{
return 'local';
}
public function get(Model|Authenticatable $record): string
{
$path = 'avatars/' . $record->getKey() . '.png';
return Storage::disk('public')->exists($path) ? Storage::url($path) : (new FilamentUiAvatarsProvider())->get($record);
}
public static function register(): self
{
return new self();
}
}

View File

@ -3,9 +3,7 @@
namespace App\Extensions\Avatar\Providers; namespace App\Extensions\Avatar\Providers;
use App\Extensions\Avatar\AvatarProvider; use App\Extensions\Avatar\AvatarProvider;
use Filament\AvatarProviders\UiAvatarsProvider as FilamentUiAvatarsProvider; use App\Models\User;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Model;
class UiAvatarsProvider extends AvatarProvider class UiAvatarsProvider extends AvatarProvider
{ {
@ -19,9 +17,10 @@ class UiAvatarsProvider extends AvatarProvider
return 'UI Avatars'; return 'UI Avatars';
} }
public function get(Model|Authenticatable $record): string public function get(User $user): ?string
{ {
return (new FilamentUiAvatarsProvider())->get($record); // UI Avatars is the default of filament so just return null here
return null;
} }
public static function register(): self public static function register(): self

View File

@ -161,11 +161,20 @@ class Settings extends Page implements HasForms
->default(env('FILAMENT_TOP_NAVIGATION', config('panel.filament.top-navigation'))), ->default(env('FILAMENT_TOP_NAVIGATION', config('panel.filament.top-navigation'))),
Select::make('FILAMENT_AVATAR_PROVIDER') Select::make('FILAMENT_AVATAR_PROVIDER')
->label(trans('admin/setting.general.avatar_provider')) ->label(trans('admin/setting.general.avatar_provider'))
->columnSpan(2)
->native(false) ->native(false)
->options(collect(AvatarProvider::getAll())->mapWithKeys(fn ($provider) => [$provider->getId() => $provider->getName()])) ->options(collect(AvatarProvider::getAll())->mapWithKeys(fn ($provider) => [$provider->getId() => $provider->getName()]))
->selectablePlaceholder(false) ->selectablePlaceholder(false)
->default(env('FILAMENT_AVATAR_PROVIDER', config('panel.filament.avatar-provider'))), ->default(env('FILAMENT_AVATAR_PROVIDER', config('panel.filament.avatar-provider'))),
Toggle::make('FILAMENT_UPLOADABLE_AVATARS')
->label(trans('admin/setting.general.uploadable_avatars'))
->inline(false)
->onIcon('tabler-check')
->offIcon('tabler-x')
->onColor('success')
->offColor('danger')
->formatStateUsing(fn ($state) => (bool) $state)
->afterStateUpdated(fn ($state, Set $set) => $set('FILAMENT_UPLOADABLE_AVATARS', (bool) $state))
->default(env('FILAMENT_UPLOADABLE_AVATARS', config('panel.filament.uploadable-avatars'))),
]), ]),
ToggleButtons::make('PANEL_USE_BINARY_PREFIX') ToggleButtons::make('PANEL_USE_BINARY_PREFIX')
->label(trans('admin/setting.general.unit_prefix')) ->label(trans('admin/setting.general.unit_prefix'))

View File

@ -128,7 +128,7 @@ class EditProfile extends BaseEditProfile
->options(fn (LanguageService $languageService) => $languageService->getAvailableLanguages()) ->options(fn (LanguageService $languageService) => $languageService->getAvailableLanguages())
->native(false), ->native(false),
FileUpload::make('avatar') FileUpload::make('avatar')
->visible(fn () => config('panel.filament.avatar-provider') === 'local') ->visible(fn () => config('panel.filament.uploadable-avatars'))
->avatar() ->avatar()
->acceptedFileTypes(['image/png']) ->acceptedFileTypes(['image/png'])
->directory('avatars') ->directory('avatars')

View File

@ -4,6 +4,7 @@ namespace App\Models;
use App\Contracts\Validatable; use App\Contracts\Validatable;
use App\Exceptions\DisplayException; use App\Exceptions\DisplayException;
use App\Extensions\Avatar\AvatarProvider;
use App\Rules\Username; use App\Rules\Username;
use App\Facades\Activity; use App\Facades\Activity;
use App\Traits\HasValidation; use App\Traits\HasValidation;
@ -23,6 +24,7 @@ use Illuminate\Auth\Authenticatable;
use Illuminate\Notifications\Notifiable; use Illuminate\Notifications\Notifiable;
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
use App\Models\Traits\HasAccessTokens; use App\Models\Traits\HasAccessTokens;
use Filament\Models\Contracts\HasAvatar;
use Illuminate\Auth\Passwords\CanResetPassword; use Illuminate\Auth\Passwords\CanResetPassword;
use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\Access\Authorizable; use Illuminate\Foundation\Auth\Access\Authorizable;
@ -30,6 +32,7 @@ use Illuminate\Database\Eloquent\Relations\MorphToMany;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract; use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract; use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
use Illuminate\Support\Facades\Storage;
use ResourceBundle; use ResourceBundle;
use Spatie\Permission\Traits\HasRoles; use Spatie\Permission\Traits\HasRoles;
@ -87,7 +90,7 @@ use Spatie\Permission\Traits\HasRoles;
* @method static Builder|User whereUsername($value) * @method static Builder|User whereUsername($value)
* @method static Builder|User whereUuid($value) * @method static Builder|User whereUuid($value)
*/ */
class User extends Model implements AuthenticatableContract, AuthorizableContract, CanResetPasswordContract, FilamentUser, HasName, HasTenants, Validatable class User extends Model implements AuthenticatableContract, AuthorizableContract, CanResetPasswordContract, FilamentUser, HasAvatar, HasName, HasTenants, Validatable
{ {
use Authenticatable; use Authenticatable;
use Authorizable { can as protected canned; } use Authorizable { can as protected canned; }
@ -372,6 +375,21 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac
return $this->username; return $this->username;
} }
public function getFilamentAvatarUrl(): ?string
{
if (config('panel.filament.uploadable-avatars')) {
$path = "avatars/$this->id.png";
if (Storage::disk('public')->exists($path)) {
return Storage::url($path);
}
}
$provider = AvatarProvider::getProvider(config('panel.filament.avatar-provider'));
return $provider?->get($this);
}
public function canTarget(Model $user): bool public function canTarget(Model $user): bool
{ {
if ($this->isRootAdmin()) { if ($this->isRootAdmin()) {

View File

@ -11,7 +11,6 @@ use App\Checks\PanelVersionCheck;
use App\Checks\ScheduleCheck; use App\Checks\ScheduleCheck;
use App\Checks\UsedDiskSpaceCheck; use App\Checks\UsedDiskSpaceCheck;
use App\Extensions\Avatar\Providers\GravatarProvider; use App\Extensions\Avatar\Providers\GravatarProvider;
use App\Extensions\Avatar\Providers\LocalAvatarProvider;
use App\Extensions\Avatar\Providers\UiAvatarsProvider; use App\Extensions\Avatar\Providers\UiAvatarsProvider;
use App\Extensions\OAuth\Providers\GitlabProvider; use App\Extensions\OAuth\Providers\GitlabProvider;
use App\Models; use App\Models;
@ -121,7 +120,6 @@ class AppServiceProvider extends ServiceProvider
// Default Avatar providers // Default Avatar providers
GravatarProvider::register(); GravatarProvider::register();
UiAvatarsProvider::register(); UiAvatarsProvider::register();
LocalAvatarProvider::register();
FilamentColor::register([ FilamentColor::register([
'danger' => Color::Red, 'danger' => Color::Red,

View File

@ -2,7 +2,6 @@
namespace App\Providers\Filament; namespace App\Providers\Filament;
use App\Extensions\Avatar\AvatarProvider;
use App\Filament\Pages\Auth\Login; use App\Filament\Pages\Auth\Login;
use App\Filament\Pages\Auth\EditProfile; use App\Filament\Pages\Auth\EditProfile;
use App\Http\Middleware\LanguageMiddleware; use App\Http\Middleware\LanguageMiddleware;
@ -39,7 +38,6 @@ class AdminPanelProvider extends PanelProvider
->favicon(config('app.favicon', '/pelican.ico')) ->favicon(config('app.favicon', '/pelican.ico'))
->topNavigation(config('panel.filament.top-navigation', false)) ->topNavigation(config('panel.filament.top-navigation', false))
->maxContentWidth(config('panel.filament.display-width', 'screen-2xl')) ->maxContentWidth(config('panel.filament.display-width', 'screen-2xl'))
->defaultAvatarProvider(fn () => get_class(AvatarProvider::getProvider(config('panel.filament.avatar-provider'))))
->login(Login::class) ->login(Login::class)
->passwordReset() ->passwordReset()
->userMenuItems([ ->userMenuItems([

View File

@ -2,7 +2,6 @@
namespace App\Providers\Filament; namespace App\Providers\Filament;
use App\Extensions\Avatar\AvatarProvider;
use App\Filament\Pages\Auth\Login; use App\Filament\Pages\Auth\Login;
use App\Filament\Pages\Auth\EditProfile; use App\Filament\Pages\Auth\EditProfile;
use Filament\Facades\Filament; use Filament\Facades\Filament;
@ -35,7 +34,6 @@ class AppPanelProvider extends PanelProvider
->favicon(config('app.favicon', '/pelican.ico')) ->favicon(config('app.favicon', '/pelican.ico'))
->topNavigation(config('panel.filament.top-navigation', false)) ->topNavigation(config('panel.filament.top-navigation', false))
->maxContentWidth(config('panel.filament.display-width', 'screen-2xl')) ->maxContentWidth(config('panel.filament.display-width', 'screen-2xl'))
->defaultAvatarProvider(fn () => get_class(AvatarProvider::getProvider(config('panel.filament.avatar-provider'))))
->navigation(false) ->navigation(false)
->profile(EditProfile::class, false) ->profile(EditProfile::class, false)
->login(Login::class) ->login(Login::class)

View File

@ -2,7 +2,6 @@
namespace App\Providers\Filament; namespace App\Providers\Filament;
use App\Extensions\Avatar\AvatarProvider;
use App\Filament\App\Resources\ServerResource\Pages\ListServers; use App\Filament\App\Resources\ServerResource\Pages\ListServers;
use App\Filament\Pages\Auth\Login; use App\Filament\Pages\Auth\Login;
use App\Filament\Admin\Resources\ServerResource\Pages\EditServer; use App\Filament\Admin\Resources\ServerResource\Pages\EditServer;
@ -42,7 +41,6 @@ class ServerPanelProvider extends PanelProvider
->favicon(config('app.favicon', '/pelican.ico')) ->favicon(config('app.favicon', '/pelican.ico'))
->topNavigation(config('panel.filament.top-navigation', false)) ->topNavigation(config('panel.filament.top-navigation', false))
->maxContentWidth(config('panel.filament.display-width', 'screen-2xl')) ->maxContentWidth(config('panel.filament.display-width', 'screen-2xl'))
->defaultAvatarProvider(fn () => get_class(AvatarProvider::getProvider(config('panel.filament.avatar-provider'))))
->login(Login::class) ->login(Login::class)
->passwordReset() ->passwordReset()
->userMenuItems([ ->userMenuItems([

View File

@ -53,6 +53,7 @@ return [
'top-navigation' => env('FILAMENT_TOP_NAVIGATION', false), 'top-navigation' => env('FILAMENT_TOP_NAVIGATION', false),
'display-width' => env('FILAMENT_WIDTH', 'screen-2xl'), 'display-width' => env('FILAMENT_WIDTH', 'screen-2xl'),
'avatar-provider' => env('FILAMENT_AVATAR_PROVIDER', 'gravatar'), 'avatar-provider' => env('FILAMENT_AVATAR_PROVIDER', 'gravatar'),
'uploadable-avatars' => env('FILAMENT_UPLOADABLE_AVATARS', false),
], ],
'use_binary_prefix' => env('PANEL_USE_BINARY_PREFIX', true), 'use_binary_prefix' => env('PANEL_USE_BINARY_PREFIX', true),

View File

@ -35,6 +35,7 @@ return [
'set_to_cf' => 'Set to Cloudflare IPs', 'set_to_cf' => 'Set to Cloudflare IPs',
'display_width' => 'Display Width', 'display_width' => 'Display Width',
'avatar_provider' => 'Avatar Provider', 'avatar_provider' => 'Avatar Provider',
'uploadable_avatars' => 'Allow users to upload own avatar?',
], ],
'captcha' => [ 'captcha' => [
'enable' => 'Enable', 'enable' => 'Enable',