From 2176b6f10bb028b3e9e972b1cc3634b1c9810a08 Mon Sep 17 00:00:00 2001 From: PatrickePatate Date: Tue, 7 Jan 2025 16:14:00 +0100 Subject: [PATCH 1/4] =?UTF-8?q?User=20Profile=20Picture=20support=20=20=20?= =?UTF-8?q?=F0=9F=8E=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/config.php | 1 + demo/app/Models/User.php | 8 ++++++++ demo/app/Providers/DemoSharpServiceProvider.php | 1 + docs/guide/authentication.md | 4 +++- resources/js/Layouts/Layout.vue | 5 ++++- resources/js/types/generated.d.ts | 1 + src/Config/SharpConfigBuilder.php | 8 ++++++++ src/Data/UserData.php | 2 ++ 8 files changed, 28 insertions(+), 2 deletions(-) diff --git a/config/config.php b/config/config.php index 091f61473..129acabd7 100644 --- a/config/config.php +++ b/config/config.php @@ -101,6 +101,7 @@ // Name of the login and password attributes of the User Model. 'login_attribute' => 'email', 'password_attribute' => 'password', + 'avatar_attribute' => 'avatar', 'rate_limiting' => [ 'enabled' => true, diff --git a/demo/app/Models/User.php b/demo/app/Models/User.php index 47a047ab8..ec161bcd4 100644 --- a/demo/app/Models/User.php +++ b/demo/app/Models/User.php @@ -2,6 +2,7 @@ namespace App\Models; +use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\MorphOne; @@ -38,6 +39,13 @@ public function avatar(): MorphOne ->where('model_key', 'avatar'); } + public function avatarUrl(): Attribute + { + return Attribute::make( + get: fn () => $this->avatar?->thumbnail(500), + ); + } + public function getDefaultAttributesFor(string $attribute): array { return in_array($attribute, ['avatar']) diff --git a/demo/app/Providers/DemoSharpServiceProvider.php b/demo/app/Providers/DemoSharpServiceProvider.php index 464f0919e..3c4c6a7d5 100644 --- a/demo/app/Providers/DemoSharpServiceProvider.php +++ b/demo/app/Providers/DemoSharpServiceProvider.php @@ -42,6 +42,7 @@ protected function configureSharp(SharpConfigBuilder $config): void ->setAuthCustomGuard('web') ->setLoginAttributes('email', 'password') ->setUserDisplayAttribute('name') + ->setUserAvatarAttribute('avatar_url') ->enable2faCustom(Demo2faNotificationHandler::class) ->enableLoginRateLimiting(maxAttempts: 3) ->suggestRememberMeOnLoginForm() diff --git a/docs/guide/authentication.md b/docs/guide/authentication.md index d6d6f6640..0b9498c9c 100644 --- a/docs/guide/authentication.md +++ b/docs/guide/authentication.md @@ -14,12 +14,14 @@ class SharpServiceProvider extends SharpAppServiceProvider $config ->setLoginAttributes('login', 'pwd') ->setUserDisplayAttribute('last_name') + ->setUserAvatarAttribute('avatar_url') // [...] } } ``` -The `setUserDisplayAttribute()` is useful to display the user's name in the Sharp UI. Default is `name`. +- The `setUserDisplayAttribute()` is useful to display the user's name in the Sharp UI. Default is `name`. +- The `setUserAvatarAttribute()` is useful to display the user's avatar in the Sharp UI. Default is `avatar`, when the attribute returns null, the default user icon is displayed instead. ## Login form diff --git a/resources/js/Layouts/Layout.vue b/resources/js/Layouts/Layout.vue index 533cc187b..71ca2fadd 100644 --- a/resources/js/Layouts/Layout.vue +++ b/resources/js/Layouts/Layout.vue @@ -206,7 +206,10 @@ class="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground" > - + +
{{ auth().user.name }} diff --git a/resources/js/types/generated.d.ts b/resources/js/types/generated.d.ts index 333ffd568..6100d8558 100644 --- a/resources/js/types/generated.d.ts +++ b/resources/js/types/generated.d.ts @@ -852,6 +852,7 @@ export type ShowTextFieldData = { export type UserData = { name: string | null; email: string | null; + avatar: string | null; }; export type UserMenuData = { items: Array; diff --git a/src/Config/SharpConfigBuilder.php b/src/Config/SharpConfigBuilder.php index fdea30efe..095dc64fe 100644 --- a/src/Config/SharpConfigBuilder.php +++ b/src/Config/SharpConfigBuilder.php @@ -49,6 +49,7 @@ class SharpConfigBuilder 'login_page_url' => null, 'display_attribute' => 'name', 'login_attribute' => 'email', + 'avatar_attribute' => 'avatar', 'password_attribute' => 'password', 'impersonate' => [ 'enabled' => false, @@ -327,6 +328,13 @@ public function setUserDisplayAttribute(string $displayAttribute): self return $this; } + public function setUserAvatarAttribute(string $avatar): self + { + $this->config['auth']['avatar_attribute'] = $avatar; + + return $this; + } + public function setAuthCustomGuard(?string $guardName): self { $this->config['auth']['guard'] = $guardName; diff --git a/src/Data/UserData.php b/src/Data/UserData.php index 652064e01..d16b42fd3 100644 --- a/src/Data/UserData.php +++ b/src/Data/UserData.php @@ -12,6 +12,7 @@ final class UserData extends Data public function __construct( public ?string $name, public ?string $email, + public ?string $avatar, ) {} public static function from(Authenticatable $user): self @@ -19,6 +20,7 @@ public static function from(Authenticatable $user): self return new self( name: $user->{sharp()->config()->get('auth.display_attribute')} ?? null, email: $user->{sharp()->config()->get('auth.login_attribute')} ?? null, + avatar: $user->{sharp()->config()->get('auth.avatar_attribute')} ?? null, ); } } From 3f4c4dbe20f1ebf861a1e8f9e9e56a96119e67d0 Mon Sep 17 00:00:00 2001 From: antoine Date: Tue, 20 May 2025 10:44:54 +0200 Subject: [PATCH 2/4] wip --- demo/app/Providers/DemoSharpServiceProvider.php | 2 +- src/Config/SharpConfigBuilder.php | 11 ++++++++--- src/Data/UserData.php | 2 +- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/demo/app/Providers/DemoSharpServiceProvider.php b/demo/app/Providers/DemoSharpServiceProvider.php index cf5f6a95d..ec3f74961 100644 --- a/demo/app/Providers/DemoSharpServiceProvider.php +++ b/demo/app/Providers/DemoSharpServiceProvider.php @@ -28,7 +28,7 @@ protected function configureSharp(SharpConfigBuilder $config): void ->setAuthCustomGuard('web') ->setLoginAttributes('email', 'password') ->setUserDisplayAttribute('name') - ->setUserAvatarAttribute('avatar_url') + ->setUserAvatar(fn () => auth()->user()->avatar?->thumbnail(200)) ->enable2faCustom(Demo2faNotificationHandler::class) ->enableLoginRateLimiting(maxAttempts: 3) ->suggestRememberMeOnLoginForm() diff --git a/src/Config/SharpConfigBuilder.php b/src/Config/SharpConfigBuilder.php index 4ec6e782c..2fb01177d 100644 --- a/src/Config/SharpConfigBuilder.php +++ b/src/Config/SharpConfigBuilder.php @@ -15,6 +15,7 @@ use Code16\Sharp\Utils\Entities\SharpEntityResolver; use Code16\Sharp\Utils\Filters\GlobalRequiredFilter; use Code16\Sharp\Utils\Menu\SharpMenu; +use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Contracts\Auth\PasswordBroker; use Illuminate\Contracts\View\View; use Illuminate\Foundation\Vite; @@ -61,7 +62,7 @@ class SharpConfigBuilder 'logout_page_url' => null, 'display_attribute' => 'name', 'login_attribute' => 'email', - 'avatar_attribute' => 'avatar', + 'avatar' => null, 'password_attribute' => 'password', 'impersonate' => [ 'enabled' => false, @@ -415,9 +416,13 @@ public function setUserDisplayAttribute(string $displayAttribute): self return $this; } - public function setUserAvatarAttribute(string $avatar): self + /** + * @param (Closure(Authenticatable $user): string) $userAvatar + * @return $this + */ + public function setUserAvatar(Closure $userAvatar): self { - $this->config['auth']['avatar_attribute'] = $avatar; + $this->config['auth']['avatar'] = $userAvatar; return $this; } diff --git a/src/Data/UserData.php b/src/Data/UserData.php index d16b42fd3..80a6b5ed4 100644 --- a/src/Data/UserData.php +++ b/src/Data/UserData.php @@ -20,7 +20,7 @@ public static function from(Authenticatable $user): self return new self( name: $user->{sharp()->config()->get('auth.display_attribute')} ?? null, email: $user->{sharp()->config()->get('auth.login_attribute')} ?? null, - avatar: $user->{sharp()->config()->get('auth.avatar_attribute')} ?? null, + avatar: ($avatar = sharp()->config()->get('auth.avatar')) ? $avatar($user) : null, ); } } From 4461848957b7ddfb9e42a9da66449c06b8f97a14 Mon Sep 17 00:00:00 2001 From: antoine Date: Tue, 20 May 2025 10:57:50 +0200 Subject: [PATCH 3/4] Docs + front --- docs/guide/authentication.md | 4 ++-- resources/js/Layouts/Layout.vue | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/docs/guide/authentication.md b/docs/guide/authentication.md index 363cb4401..417d6112f 100644 --- a/docs/guide/authentication.md +++ b/docs/guide/authentication.md @@ -14,14 +14,14 @@ class SharpServiceProvider extends SharpAppServiceProvider $config ->setLoginAttributes('login', 'pwd') ->setUserDisplayAttribute('last_name') - ->setUserAvatarAttribute('avatar_url') + ->setUserAvatar(fn () => auth()->user()->avatar) // [...] } } ``` - The `setUserDisplayAttribute()` is useful to display the user's name in the Sharp UI. Default is `name`. -- The `setUserAvatarAttribute()` is useful to display the user's avatar in the Sharp UI. Default is `avatar`, when the attribute returns null, the default user icon is displayed instead. +- The `setUserAvatar()` is useful to display the user's avatar in the Sharp UI. By default, a user icon is displayed instead. ## Login form diff --git a/resources/js/Layouts/Layout.vue b/resources/js/Layouts/Layout.vue index 679c4eacb..77c66c97a 100644 --- a/resources/js/Layouts/Layout.vue +++ b/resources/js/Layouts/Layout.vue @@ -215,8 +215,13 @@ export function useMenuBoundaryElement() { size="lg" class="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground" > - - + + +
{{ auth().user.name }} From d1d01dd35bc1eeca0861d9271d6615e042654e4b Mon Sep 17 00:00:00 2001 From: antoine Date: Tue, 20 May 2025 12:10:08 +0200 Subject: [PATCH 4/4] allow string / closure to avatar attribute --- demo/app/Providers/DemoSharpServiceProvider.php | 2 +- docs/guide/authentication.md | 4 ++-- src/Config/SharpConfigBuilder.php | 8 ++++---- src/Data/UserData.php | 7 ++++++- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/demo/app/Providers/DemoSharpServiceProvider.php b/demo/app/Providers/DemoSharpServiceProvider.php index ec3f74961..0ae63a48d 100644 --- a/demo/app/Providers/DemoSharpServiceProvider.php +++ b/demo/app/Providers/DemoSharpServiceProvider.php @@ -28,7 +28,7 @@ protected function configureSharp(SharpConfigBuilder $config): void ->setAuthCustomGuard('web') ->setLoginAttributes('email', 'password') ->setUserDisplayAttribute('name') - ->setUserAvatar(fn () => auth()->user()->avatar?->thumbnail(200)) + ->setUserAvatarAttribute(fn () => auth()->user()->avatar?->thumbnail(200)) ->enable2faCustom(Demo2faNotificationHandler::class) ->enableLoginRateLimiting(maxAttempts: 3) ->suggestRememberMeOnLoginForm() diff --git a/docs/guide/authentication.md b/docs/guide/authentication.md index 417d6112f..8b8c202cd 100644 --- a/docs/guide/authentication.md +++ b/docs/guide/authentication.md @@ -14,14 +14,14 @@ class SharpServiceProvider extends SharpAppServiceProvider $config ->setLoginAttributes('login', 'pwd') ->setUserDisplayAttribute('last_name') - ->setUserAvatar(fn () => auth()->user()->avatar) + ->setUserAvatarAttribute('avatar_url') // [...] } } ``` - The `setUserDisplayAttribute()` is useful to display the user's name in the Sharp UI. Default is `name`. -- The `setUserAvatar()` is useful to display the user's avatar in the Sharp UI. By default, a user icon is displayed instead. +- The `setUserAvatarAttribute()` is useful to display the user's avatar in the Sharp UI. By default, a user icon is displayed instead. ## Login form diff --git a/src/Config/SharpConfigBuilder.php b/src/Config/SharpConfigBuilder.php index 2fb01177d..b6d02e0c3 100644 --- a/src/Config/SharpConfigBuilder.php +++ b/src/Config/SharpConfigBuilder.php @@ -62,7 +62,7 @@ class SharpConfigBuilder 'logout_page_url' => null, 'display_attribute' => 'name', 'login_attribute' => 'email', - 'avatar' => null, + 'avatar_attribute' => null, 'password_attribute' => 'password', 'impersonate' => [ 'enabled' => false, @@ -417,12 +417,12 @@ public function setUserDisplayAttribute(string $displayAttribute): self } /** - * @param (Closure(Authenticatable $user): string) $userAvatar + * @param (string|Closure(Authenticatable $user): string) $avatarAttribute * @return $this */ - public function setUserAvatar(Closure $userAvatar): self + public function setUserAvatarAttribute(string|Closure $avatarAttribute): self { - $this->config['auth']['avatar'] = $userAvatar; + $this->config['auth']['avatar_attribute'] = $avatarAttribute; return $this; } diff --git a/src/Data/UserData.php b/src/Data/UserData.php index 80a6b5ed4..91c5710ee 100644 --- a/src/Data/UserData.php +++ b/src/Data/UserData.php @@ -2,6 +2,7 @@ namespace Code16\Sharp\Data; +use Closure; use Illuminate\Contracts\Auth\Authenticatable; /** @@ -20,7 +21,11 @@ public static function from(Authenticatable $user): self return new self( name: $user->{sharp()->config()->get('auth.display_attribute')} ?? null, email: $user->{sharp()->config()->get('auth.login_attribute')} ?? null, - avatar: ($avatar = sharp()->config()->get('auth.avatar')) ? $avatar($user) : null, + avatar: match (true) { + is_string(sharp()->config()->get('auth.avatar_attribute')) => $user->{sharp()->config()->get('auth.avatar_attribute')}, + sharp()->config()->get('auth.avatar_attribute') instanceof Closure => sharp()->config()->get('auth.avatar_attribute')($user), + default => null, + }, ); } }