@extends('layouts/default') {{-- Page title --}} @section('title') @if ($user->id) {{ trans('admin/users/table.updateuser') }} {{ $user->display_name }} @else {{ trans('admin/users/table.createuser') }} @endif @parent @stop {{-- Page content --}} @section('content') {{-- novalidate: this form is wired to jQuery Validate (see the snipeValidatorOptions block in layouts/default.blade.php) which handles required, url, email, and complexity rules. Without novalidate the browser's HTML5 native validation fires FIRST on type=url / type=email fields and blocks submission with its own popup on the first invalid field, so jQuery Validate never gets a chance to walk the whole form and highlight every empty required field at once. --}} {{-- Editable only if the user isn't LDAP-managed, or this is a clone. LDAP-managed users get a locked notice + hidden field so validation still passes. --}} @if ($user->ldap_import!='1' || str_contains(Route::currentRouteName(), 'clone')) {{-- Locked branch (demo mode or actor can't edit auth fields for this user) emits `disabled` and skips the readonly/onfocus autofill guard, since a disabled input can't be focused, typed into, or targeted by password managers anyway. Bootstrap's .form-control[disabled] handles the not-allowed cursor. --}} @if ((! Gate::allows('canEditAuthFields', $user)) || ((! Gate::allows('editableOnDemo')) && ($user->id))) @else @endif @else {{ trans('general.managed_ldap') }} @endif @cannot('canEditAuthFields', $user) {{ trans('general.action_permission_generic', ['action' => trans('general.edit'), 'item_type' => trans('general.username')]) }} @endcannot {{-- Rendered ABOVE the password fields so the activated checkbox sits above the password inputs whose visibility it controls. snipeit.js hides the password rows when this checkbox is unchecked. Keeping the checkbox above avoids the layout jump caused by rows above the toggle appearing/disappearing. --}} @if (((!Gate::allows('editableOnDemo')) && ($user->id)) || (!Gate::allows('canEditAuthFields', $user)) || ($user->id == auth()->user()->id)) {{-- Disabled branch: no label column, the checkbox row spans col-md-9 col-md-offset-3 and carries up to three distinct conditional help-blocks (higher-role edit gate, demo app-lock, own-account gate). Each has its own icon and wording. Used with an empty label and a full input slot to keep the wrapper consistent. --}} @cannot('canEditAuthFields', $user) {{ trans('general.action_permission_generic', ['action' => trans('general.edit'), 'item_type' => trans('general.login_status')]) }} @endcannot @if ($user->id == auth()->user()->id) {{ trans('admin/users/general.activated_disabled_help_text') }} @endif @else @endif {{-- Inline display style pre-hides the row when the user is landing on the form with activated unchecked (typical for new-user create). Avoids the FOUC that would happen if we rendered the fields visible and let snipeit.js hide them on document-ready. JS still toggles visibility on subsequent changes to the activated checkbox. The wand generator button sits in the row's after_input slot (col-md-1 sibling of the input column) so it lines up with the "new" button pattern from x-input.user-select instead of being fused into the input-group. --}} {{-- Wand is available whenever the actor can edit auth fields on a non-LDAP record AND either they're creating a new user (demo mode still lets you spin up test accounts) OR they're editing outside demo mode. Editing an existing user in demo mode is the only combination that hides the wand. --}} @if (Gate::allows('canEditAuthFields', $user) && $user->ldap_import != '1' && (! $user->id || Gate::allows('editableOnDemo'))) @endif @if ($user->ldap_import!='1' || str_contains(Route::currentRouteName(), 'clone'))
@if ((! Gate::allows('canEditAuthFields', $user)) || ((! Gate::allows('editableOnDemo')) && ($user->id))) @else id)) ? ' required' : '' }}> @endif {{-- jQuery's multi-selector: this eye toggles the visibility of both the password and the confirmation field in one click, so the confirmation row doesn't need its own eye addon. --}} {{ trans('general.toggle_password_visibility') }}
@else

{{ trans('general.managed_ldap') }}

@endif @cannot('canEditAuthFields', $user) {{ trans('general.action_permission_generic', ['action' => trans('general.edit'), 'item_type' => trans('general.password')]) }} @endcannot
@if (($user->ldap_import!='1') || str_contains(Route::currentRouteName(), 'clone'))
@if ((! Gate::allows('canEditAuthFields', $user)) || ((! Gate::allows('editableOnDemo')) && ($user->id))) @else id) ? ' required' : '' }} onfocus="this.removeAttribute('readonly');" readonly> @endif {{-- Shares the same multi-selector data-toggle as the password field's eye so both eyes and both fields stay in sync — see the .toggle-password handler in snipeit.js. --}} {{ trans('general.toggle_password_visibility') }}
@cannot('canEditAuthFields', $user) {{ trans('general.action_permission_generic', ['action' => trans('general.edit'), 'item_type' => trans('general.password')]) }} @endcannot
@endif @if ((! Gate::allows('canEditAuthFields', $user)) || ((! Gate::allows('editableOnDemo')) && ($user->id))) @else id && ! config('app.lock_passwords')) data-toggles-checkbox="#send_welcome" @endif> @endif @cannot('canEditAuthFields', $user) {{ trans('general.action_permission_generic', ['action' => trans('general.edit'), 'item_type' => trans('general.email')]) }} @endcannot {{-- Starts disabled. snipeit.js flips it enabled once the #email input has more than 5 chars, via the data-toggles-checkbox attribute we render on #email below. When app.lock_passwords is on we don't render that attribute, so the checkbox stays permanently disabled. --}} @if (!$user->id) @endif {{-- Avatar upload is hidden when editing an existing user in demo mode (would otherwise let visitors overwrite arbitrary users' avatars). Creation flow keeps it available so demo-mode operators can still spin up new accounts with an image. --}} @if (! $user->id || Gate::allows('editableOnDemo')) @else @if ($user->avatar) @endif @endif

{{ trans('admin/hardware/form.optional_infos') }}


{{-- Custom input slot because we need getRawOriginal here, not the computed accessor. The row wrapper (label + error) still comes from the component. --}} {{-- When the actor has the rights and the FMCS pivot to actually manage companies, we render the dropdown; otherwise the target's current companies (or "(none)") in read-only labels. Either way one or more help-blocks may follow. --}} @if ((Gate::allows('canEditAuthFields', $user)) && (\App\Models\Company::canManageUsersCompanies())) @if ($snipeSettings->full_multiple_companies_support == '1') @cannot('superadmin') {{ trans('general.fmcs_company_select_note') }} @endcannot @can('superadmin') {{ trans('general.fmcs_company_select_superadmin_note') }} @endcan @endif @if (! auth()->user()->canGrantFloaterStatus()) {{ trans('admin/users/general.floater_mode_warning_help') }} @endif @else

@if ($user->companies->isNotEmpty()) @foreach ($user->companies as $company) {!! $company->present()->formattedNameLink !!} @endforeach @else {{ trans('admin/users/general.no_companies_assigned') }} @endif

@if (! Gate::allows('canEditAuthFields', $user)) {{ trans('admin/users/general.cannot_edit_privileged_user_companies') }} @else {{ trans('admin/users/general.cannot_manage_companies_without_membership') }} @endif @endif
@if ($snipeSettings->two_factor_enabled!='') @if ($snipeSettings->two_factor_enabled=='1') @endif {{-- Reset 2FA lives on the user detail page (resources/views/users/view.blade.php via #confirmTwoFactorResetModal) so operators go through a confirmation modal that posts to the users.two_factor_reset web route. The equivalent inline AJAX widget that used to live here was removed for redundancy and to eliminate a chunk of inline JS ahead of the Vite migration. --}} @endif @if ($groups->count()) @if ((!Gate::allows('editableOnDemo') || (!Auth::user()->isSuperUser()))) @if (count($userGroups->keys()) > 0)
    @foreach ($groups as $id => $group) {!! ($userGroups->keys()->contains($id) ? '
  • '.e($group).'
  • ' : '') !!} @endforeach
@endif {{ trans('admin/users/general.group_memberships_helpblock') }} @else {{ trans('admin/users/table.groupnotes') }} @endif @else

{{ trans('admin/users/table.nogroup') }} {{ trans('admin/settings/general.admin_settings') }} > {{ trans('general.groups') }}

@endif
@if (auth()->user()->isAdmin() && !auth()->user()->isSuperUser()) {{ trans('admin/users/general.superadmin_permission_warning') }} @elseif (!auth()->user()->isAdmin() && !auth()->user()->isSuperUser() && auth()->id() === $user->id) {{ trans('admin/users/general.self_permission_warning') }} @elseif (!auth()->user()->isAdmin() && !auth()->user()->isSuperUser() && auth()->id() !== $user->id) {{ trans('admin/users/general.admin_permission_warning') }} @endif @if (auth()->user()->isSuperUser() || auth()->user()->isAdmin() || (auth()->id() !== $user->id && !$user->isSuperUser()))
@include('partials.forms.edit.permissions-base', ['use_inherit' => true, 'groupPermissions' => $userPermissions])
@endif
@stop