mirror of
https://github.com/pelican-dev/panel.git
synced 2025-11-09 20:19:26 +01:00
add toggle for externally managed users
This commit is contained in:
parent
8e006ac32d
commit
a9165c66f7
@ -33,6 +33,7 @@ use Filament\Forms\Components\FileUpload;
|
|||||||
use Filament\Forms\Components\Repeater;
|
use Filament\Forms\Components\Repeater;
|
||||||
use Filament\Forms\Components\Select;
|
use Filament\Forms\Components\Select;
|
||||||
use Filament\Forms\Components\TextInput;
|
use Filament\Forms\Components\TextInput;
|
||||||
|
use Filament\Forms\Components\Toggle;
|
||||||
use Filament\Infolists\Components\TextEntry;
|
use Filament\Infolists\Components\TextEntry;
|
||||||
use Filament\Notifications\Notification;
|
use Filament\Notifications\Notification;
|
||||||
use Filament\Resources\Pages\PageRegistration;
|
use Filament\Resources\Pages\PageRegistration;
|
||||||
@ -224,67 +225,73 @@ class UserResource extends Resource
|
|||||||
'md' => 1,
|
'md' => 1,
|
||||||
'lg' => 1,
|
'lg' => 1,
|
||||||
]),
|
]),
|
||||||
Select::make('timezone')
|
Toggle::make('is_managed_externally')
|
||||||
->label(trans('profile.timezone'))
|
->label(trans('admin/user.is_managed_externally'))
|
||||||
|
->hintIcon('tabler-question-mark', trans('admin/user.is_managed_externally_helper'))
|
||||||
|
->inline(false)
|
||||||
->columnSpan([
|
->columnSpan([
|
||||||
'default' => 1,
|
'default' => 1,
|
||||||
'md' => 1,
|
'md' => 1,
|
||||||
'lg' => 1,
|
'lg' => 1,
|
||||||
])
|
]),
|
||||||
->required()
|
Section::make(trans('profile.tabs.customization'))
|
||||||
->prefixIcon('tabler-clock-pin')
|
->collapsible()
|
||||||
->default(fn () => config('app.timezone', 'UTC'))
|
->columnSpanFull()
|
||||||
->selectablePlaceholder(false)
|
->columns(2)
|
||||||
->options(fn () => collect(DateTimeZone::listIdentifiers())->mapWithKeys(fn ($tz) => [$tz => $tz]))
|
->schema([
|
||||||
->searchable()
|
Select::make('timezone')
|
||||||
->native(false),
|
->label(trans('profile.timezone'))
|
||||||
Select::make('language')
|
->required()
|
||||||
->label(trans('profile.language'))
|
->prefixIcon('tabler-clock-pin')
|
||||||
->columnSpan([
|
->default(fn () => config('app.timezone', 'UTC'))
|
||||||
'default' => 1,
|
->selectablePlaceholder(false)
|
||||||
'md' => 1,
|
->options(fn () => collect(DateTimeZone::listIdentifiers())->mapWithKeys(fn ($tz) => [$tz => $tz]))
|
||||||
'lg' => 1,
|
->searchable()
|
||||||
])
|
->native(false),
|
||||||
->required()
|
Select::make('language')
|
||||||
->prefixIcon('tabler-flag')
|
->label(trans('profile.language'))
|
||||||
->live()
|
->required()
|
||||||
->default('en')
|
->prefixIcon('tabler-flag')
|
||||||
->searchable()
|
->live()
|
||||||
->selectablePlaceholder(false)
|
->default('en')
|
||||||
->options(fn (LanguageService $languageService) => $languageService->getAvailableLanguages())
|
->searchable()
|
||||||
->native(false),
|
->selectablePlaceholder(false)
|
||||||
FileUpload::make('avatar')
|
->options(fn (LanguageService $languageService) => $languageService->getAvailableLanguages())
|
||||||
->visible(fn (?User $user, FileUpload $fileUpload) => $user ? $fileUpload->getDisk()->exists($fileUpload->getDirectory() . '/' . $user->id . '.png') : false)
|
->native(false),
|
||||||
->avatar()
|
FileUpload::make('avatar')
|
||||||
->directory('avatars')
|
->visible(fn (?User $user, FileUpload $fileUpload) => $user ? $fileUpload->getDisk()->exists($fileUpload->getDirectory() . '/' . $user->id . '.png') : false)
|
||||||
->disk('public')
|
->columnSpanFull()
|
||||||
->formatStateUsing(function (FileUpload $fileUpload, ?User $user) {
|
->avatar()
|
||||||
if (!$user) {
|
->directory('avatars')
|
||||||
return null;
|
->disk('public')
|
||||||
}
|
->formatStateUsing(function (FileUpload $fileUpload, ?User $user) {
|
||||||
$path = $fileUpload->getDirectory() . '/' . $user->id . '.png';
|
if (!$user) {
|
||||||
if ($fileUpload->getDisk()->exists($path)) {
|
return null;
|
||||||
return $path;
|
}
|
||||||
}
|
$path = $fileUpload->getDirectory() . '/' . $user->id . '.png';
|
||||||
})
|
if ($fileUpload->getDisk()->exists($path)) {
|
||||||
->deleteUploadedFileUsing(function (FileUpload $fileUpload, $file) {
|
return $path;
|
||||||
if ($file instanceof TemporaryUploadedFile) {
|
}
|
||||||
return $file->delete();
|
})
|
||||||
}
|
->deleteUploadedFileUsing(function (FileUpload $fileUpload, $file) {
|
||||||
|
if ($file instanceof TemporaryUploadedFile) {
|
||||||
|
return $file->delete();
|
||||||
|
}
|
||||||
|
|
||||||
if ($fileUpload->getDisk()->exists($file)) {
|
if ($fileUpload->getDisk()->exists($file)) {
|
||||||
return $fileUpload->getDisk()->delete($file);
|
return $fileUpload->getDisk()->delete($file);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
]),
|
||||||
Section::make(trans('profile.tabs.oauth'))
|
Section::make(trans('profile.tabs.oauth'))
|
||||||
->visible(fn (?User $user) => $user)
|
->visible(fn (?User $user) => $user)
|
||||||
->collapsible()
|
->collapsible()
|
||||||
->columnSpanFull()
|
->columnSpanFull()
|
||||||
->schema(function (OAuthService $oauthService, ?User $user) {
|
->schema(function (OAuthService $oauthService, ?User $user) {
|
||||||
|
|
||||||
if (!$user) {
|
if (!$user) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$actions = [];
|
$actions = [];
|
||||||
foreach ($user->oauth ?? [] as $schema => $_) {
|
foreach ($user->oauth ?? [] as $schema => $_) {
|
||||||
$schema = $oauthService->get($schema);
|
$schema = $oauthService->get($schema);
|
||||||
|
|||||||
@ -94,12 +94,14 @@ class EditProfile extends BaseEditProfile
|
|||||||
->icon('tabler-user-cog')
|
->icon('tabler-user-cog')
|
||||||
->schema([
|
->schema([
|
||||||
TextInput::make('username')
|
TextInput::make('username')
|
||||||
|
->disabled(fn (User $user) => $user->is_managed_externally)
|
||||||
->prefixIcon('tabler-user')
|
->prefixIcon('tabler-user')
|
||||||
->label(trans('profile.username'))
|
->label(trans('profile.username'))
|
||||||
->required()
|
->required()
|
||||||
->maxLength(255)
|
->maxLength(255)
|
||||||
->unique(),
|
->unique(),
|
||||||
TextInput::make('email')
|
TextInput::make('email')
|
||||||
|
->disabled(fn (User $user) => $user->is_managed_externally)
|
||||||
->prefixIcon('tabler-mail')
|
->prefixIcon('tabler-mail')
|
||||||
->label(trans('profile.email'))
|
->label(trans('profile.email'))
|
||||||
->email()
|
->email()
|
||||||
@ -107,6 +109,7 @@ class EditProfile extends BaseEditProfile
|
|||||||
->maxLength(255)
|
->maxLength(255)
|
||||||
->unique(),
|
->unique(),
|
||||||
TextInput::make('password')
|
TextInput::make('password')
|
||||||
|
->hidden(fn (User $user) => $user->is_managed_externally)
|
||||||
->label(trans('profile.password'))
|
->label(trans('profile.password'))
|
||||||
->password()
|
->password()
|
||||||
->prefixIcon('tabler-password')
|
->prefixIcon('tabler-password')
|
||||||
@ -537,7 +540,6 @@ class EditProfile extends BaseEditProfile
|
|||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
|
|
||||||
])
|
])
|
||||||
->operation('edit')
|
->operation('edit')
|
||||||
->model($this->getUser())
|
->model($this->getUser())
|
||||||
|
|||||||
@ -22,6 +22,7 @@ class StoreUserRequest extends ApplicationApiRequest
|
|||||||
|
|
||||||
return collect($rules)->only([
|
return collect($rules)->only([
|
||||||
'external_id',
|
'external_id',
|
||||||
|
'is_managed_externally',
|
||||||
'email',
|
'email',
|
||||||
'username',
|
'username',
|
||||||
'password',
|
'password',
|
||||||
@ -39,6 +40,7 @@ class StoreUserRequest extends ApplicationApiRequest
|
|||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'external_id' => 'Third Party Identifier',
|
'external_id' => 'Third Party Identifier',
|
||||||
|
'is_managed_externally' => 'Is managed by Third Party?',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,7 +26,7 @@ class UpdateEmailRequest extends ClientApiRequest
|
|||||||
throw new InvalidPasswordProvidedException(trans('validation.internal.invalid_password'));
|
throw new InvalidPasswordProvidedException(trans('validation.internal.invalid_password'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return !$this->user()->is_managed_externally;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function rules(): array
|
public function rules(): array
|
||||||
|
|||||||
@ -25,7 +25,7 @@ class UpdatePasswordRequest extends ClientApiRequest
|
|||||||
throw new InvalidPasswordProvidedException(trans('validation.internal.invalid_password'));
|
throw new InvalidPasswordProvidedException(trans('validation.internal.invalid_password'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return !$this->user()->is_managed_externally;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function rules(): array
|
public function rules(): array
|
||||||
|
|||||||
@ -48,6 +48,7 @@ use Spatie\Permission\Traits\HasRoles;
|
|||||||
*
|
*
|
||||||
* @property int $id
|
* @property int $id
|
||||||
* @property string|null $external_id
|
* @property string|null $external_id
|
||||||
|
* @property bool $is_managed_externally
|
||||||
* @property string $uuid
|
* @property string $uuid
|
||||||
* @property string $username
|
* @property string $username
|
||||||
* @property string $email
|
* @property string $email
|
||||||
@ -117,6 +118,7 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac
|
|||||||
*/
|
*/
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'external_id',
|
'external_id',
|
||||||
|
'is_managed_externally',
|
||||||
'username',
|
'username',
|
||||||
'email',
|
'email',
|
||||||
'password',
|
'password',
|
||||||
@ -139,6 +141,7 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac
|
|||||||
*/
|
*/
|
||||||
protected $attributes = [
|
protected $attributes = [
|
||||||
'external_id' => null,
|
'external_id' => null,
|
||||||
|
'is_managed_externally' => false,
|
||||||
'language' => 'en',
|
'language' => 'en',
|
||||||
'timezone' => 'UTC',
|
'timezone' => 'UTC',
|
||||||
'mfa_app_secret' => null,
|
'mfa_app_secret' => null,
|
||||||
@ -153,6 +156,7 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac
|
|||||||
'uuid' => ['nullable', 'string', 'size:36', 'unique:users,uuid'],
|
'uuid' => ['nullable', 'string', 'size:36', 'unique:users,uuid'],
|
||||||
'email' => ['required', 'email', 'between:1,255', 'unique:users,email'],
|
'email' => ['required', 'email', 'between:1,255', 'unique:users,email'],
|
||||||
'external_id' => ['sometimes', 'nullable', 'string', 'max:255', 'unique:users,external_id'],
|
'external_id' => ['sometimes', 'nullable', 'string', 'max:255', 'unique:users,external_id'],
|
||||||
|
'is_managed_externally' => ['boolean'],
|
||||||
'username' => ['required', 'between:1,255', 'unique:users,username'],
|
'username' => ['required', 'between:1,255', 'unique:users,username'],
|
||||||
'password' => ['sometimes', 'nullable', 'string'],
|
'password' => ['sometimes', 'nullable', 'string'],
|
||||||
'language' => ['string'],
|
'language' => ['string'],
|
||||||
|
|||||||
@ -31,6 +31,7 @@ class UserTransformer extends BaseTransformer
|
|||||||
return [
|
return [
|
||||||
'id' => $user->id,
|
'id' => $user->id,
|
||||||
'external_id' => $user->external_id,
|
'external_id' => $user->external_id,
|
||||||
|
'is_managed_externally' => $user->is_managed_externally,
|
||||||
'uuid' => $user->uuid,
|
'uuid' => $user->uuid,
|
||||||
'username' => $user->username,
|
'username' => $user->username,
|
||||||
'email' => $user->email,
|
'email' => $user->email,
|
||||||
|
|||||||
@ -26,6 +26,7 @@ class UserFactory extends Factory
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
'external_id' => null,
|
'external_id' => null,
|
||||||
|
'is_managed_externally' => false,
|
||||||
'uuid' => Uuid::uuid4()->toString(),
|
'uuid' => Uuid::uuid4()->toString(),
|
||||||
'username' => $this->faker->userName() . '_' . Str::random(10),
|
'username' => $this->faker->userName() . '_' . Str::random(10),
|
||||||
'email' => Str::random(32) . '@example.com',
|
'email' => Str::random(32) . '@example.com',
|
||||||
|
|||||||
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('users', function (Blueprint $table) {
|
||||||
|
$table->boolean('is_managed_externally')->default(false)->after('external_id');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('users', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('is_managed_externally');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -10,6 +10,8 @@ return [
|
|||||||
'username' => 'Username',
|
'username' => 'Username',
|
||||||
'password' => 'Password',
|
'password' => 'Password',
|
||||||
'external_id' => 'External ID',
|
'external_id' => 'External ID',
|
||||||
|
'is_managed_externally' => 'Is managed externally?',
|
||||||
|
'is_managed_externally_helper' => 'If your users are managed by external software (e.g. a billing software) you may enable this to prevent users from changing their username, e-mail and password from within the panel.',
|
||||||
'password_help' => 'Providing a user password is optional. New user email will prompt users to create a password the first time they login.',
|
'password_help' => 'Providing a user password is optional. New user email will prompt users to create a password the first time they login.',
|
||||||
'admin_roles' => 'Admin Roles',
|
'admin_roles' => 'Admin Roles',
|
||||||
'roles' => 'Roles',
|
'roles' => 'Roles',
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user