createUserAccountService(); $userAssignmentService = $userServicesFactory->createUserAssignmentService(); $userTenantRepository = $userServicesFactory->createUserTenantRepository(); $userRoleRepository = $userServicesFactory->createUserRoleRepository(); $userDepartmentRepository = $userServicesFactory->createUserDepartmentRepository(); $userPasswordPolicyService = $userServicesFactory->createUserPasswordPolicyService(); $authServiceFactory = new AuthServicesFactory(); $accessServicesFactory = new AccessServicesFactory(); $rolePermissionRepository = $accessServicesFactory->createRolePermissionRepository(); $authService = $authServiceFactory->createAuthService(); $apiTokenService = $authServiceFactory->createApiTokenService(); $passwordResetRepository = new PasswordResetRepository(); $rememberTokenRepository = new RememberTokenRepository(); $passwordMinLength = $userPasswordPolicyService->minLength(); $passwordHints = $userPasswordPolicyService->hints(); $currentUserId = (int) ($_SESSION['user']['id'] ?? 0); if ($currentUserId > 0) { permissionGateway()->getUserPermissions($currentUserId, true); } $uuid = trim((string) ($id ?? '')); $user = $uuid !== '' ? $userAccountService->findByUuid($uuid) : null; if (!$user) { Flash::error('User not found', 'admin/users', 'user_not_found'); Router::redirect('admin/users'); } $userId = (int) ($user['id'] ?? 0); $canViewUsers = permissionGateway()->userHas($currentUserId, PermissionService::USERS_VIEW); $canUpdateUser = permissionGateway()->userHas($currentUserId, PermissionService::USERS_UPDATE); $canUpdateSelf = permissionGateway()->userHas($currentUserId, PermissionService::USERS_SELF_UPDATE); $canUpdateAssignments = permissionGateway()->userHas($currentUserId, PermissionService::USERS_UPDATE_ASSIGNMENTS); $isOwnAccount = $currentUserId === $userId; $canEditUser = $isOwnAccount ? ($canUpdateUser || $canUpdateSelf) : $canUpdateUser; $canEditAssignments = $canEditUser && $canUpdateAssignments; if (!$isOwnAccount && !directoryServicesFactory()->createDirectoryScopeGateway()->canAccess('users', $userId, $currentUserId)) { Router::redirect('error/forbidden'); return; } if (!$canViewUsers && !$canEditUser) { Router::redirect('error/forbidden'); return; } $creatorId = (int) ($user['created_by'] ?? 0); if ($creatorId > 0) { $creator = $userAccountService->findById($creatorId); if ($creator) { $creatorName = trim(($creator['first_name'] ?? '') . ' ' . ($creator['last_name'] ?? '')); $user['created_by_label'] = $creatorName !== '' ? $creatorName : ($creator['email'] ?? ''); $user['created_by_uuid'] = $creator['uuid'] ?? null; } } $modifierId = (int) ($user['modified_by'] ?? 0); if ($modifierId > 0) { $modifier = $userAccountService->findById($modifierId); if ($modifier) { $modifierName = trim(($modifier['first_name'] ?? '') . ' ' . ($modifier['last_name'] ?? '')); $user['modified_by_label'] = $modifierName !== '' ? $modifierName : ($modifier['email'] ?? ''); $user['modified_by_uuid'] = $modifier['uuid'] ?? null; } } $activeChangedById = (int) ($user['active_changed_by'] ?? 0); if ($activeChangedById > 0) { $activeChanger = $userAccountService->findById($activeChangedById); if ($activeChanger) { $activeChangerName = trim(($activeChanger['first_name'] ?? '') . ' ' . ($activeChanger['last_name'] ?? '')); $user['active_changed_by_label'] = $activeChangerName !== '' ? $activeChangerName : ($activeChanger['email'] ?? ''); $user['active_changed_by_uuid'] = $activeChanger['uuid'] ?? null; } } $errors = []; $form = $user; // Users with tenants.update permission can see ALL tenants (to assign new tenants to users) $canManageTenants = permissionGateway()->userHas($currentUserId, PermissionService::TENANTS_UPDATE); $allowedTenantIds = $canManageTenants ? null : directoryServicesFactory()->createDirectoryScopeGateway()->getUserTenantIds($currentUserId); $tenants = directoryServicesFactory()->createTenantService()->list(); if ($allowedTenantIds) { $tenants = array_values(array_filter($tenants, static function (array $tenant) use ($allowedTenantIds): bool { $tenantId = (int) ($tenant['id'] ?? 0); return $tenantId > 0 && in_array($tenantId, $allowedTenantIds, true); })); } elseif (!$canManageTenants && directoryServicesFactory()->createDirectoryScopeGateway()->isStrict()) { $tenants = []; } $selectedTenantIds = $userTenantRepository->listTenantIdsByUserId($userId); if ($allowedTenantIds) { $selectedTenantIds = array_values(array_intersect($selectedTenantIds, $allowedTenantIds)); } $primaryTenantId = (int) ($user['primary_tenant_id'] ?? 0); if (!$primaryTenantId && count($selectedTenantIds) === 1) { $primaryTenantId = (int) $selectedTenantIds[0]; $user['primary_tenant_id'] = $primaryTenantId; } $roles = directoryServicesFactory()->createRoleService()->listActive(); $selectedRoleIds = $userRoleRepository->listRoleIdsByUserId($userId); $permissionRows = $rolePermissionRepository->listPermissionsWithRolesByRoleIds($selectedRoleIds); $selectedDepartmentIds = $userDepartmentRepository->listDepartmentIdsByUserId($userId); $departmentOptionsByTenant = directoryServicesFactory()->createDepartmentService()->groupActiveByTenantIds($selectedTenantIds); $canEditCustomFieldValues = $canEditUser && (permissionGateway()->userHas($currentUserId, PermissionService::CUSTOM_FIELDS_EDIT_VALUES) || ($isOwnAccount && $canUpdateSelf)); $customFieldDefinitionsByTenant = UserCustomFieldValueService::buildDefinitionsByTenant($selectedTenantIds); $customFieldDefinitionIds = []; foreach ($customFieldDefinitionsByTenant as $tenantDefinitions) { foreach ($tenantDefinitions as $definition) { $definitionId = (int) ($definition['id'] ?? 0); if ($definitionId > 0) { $customFieldDefinitionIds[] = $definitionId; } } } $customFieldValueMap = UserCustomFieldValueService::buildUserValueMap($userId, $customFieldDefinitionIds); $customFieldPostedValues = [ 'custom_field_values' => [], 'custom_field_values_multi' => [], ]; $passwordResets = []; if ($canViewUsers || $canEditUser) { $passwordResets = $passwordResetRepository->listByUserId($userId, 25); } $rememberTokens = []; if ($canViewUsers || $canEditUser) { $rememberTokens = $rememberTokenRepository->listByUserId($userId, 25); } $apiTokens = []; $canManageApiTokens = $canEditUser && permissionGateway()->userHas($currentUserId, PermissionService::API_TOKENS_MANAGE); if ($canViewUsers || $canEditUser) { $apiTokens = $apiTokenService->listForUser($userId); } $showApiTokens = !empty($apiTokens) || $canManageApiTokens; // Keep initial $isOwnAccount/$canEditUser values for view permissions. if (isset($_POST['email'])) { if (!$canEditUser) { Router::redirect('error/forbidden'); return; } $result = $userAccountService->updateFromAdmin($userId, $_POST, $currentUserId); $form = $result['form'] ?? $form; $errors = $result['errors'] ?? []; $tenantIds = $userAssignmentService->normalizeIdInput($_POST['tenant_ids'] ?? []); $existingTenantIds = $userTenantRepository->listTenantIdsByUserId($userId); $tenantIdsForSync = $tenantIds; if ($canEditAssignments && $allowedTenantIds) { $tenantIds = array_values(array_intersect($tenantIds, $allowedTenantIds)); $tenantIdsForSync = directoryServicesFactory()->createDirectoryScopeGateway()->mergeTenantIdsPreservingOutOfScope( $tenantIds, $existingTenantIds, $allowedTenantIds ); } elseif ($canEditAssignments && !$canManageTenants && directoryServicesFactory()->createDirectoryScopeGateway()->isStrict()) { $tenantIds = []; $tenantIdsForSync = $existingTenantIds; } if (!$canEditAssignments) { $tenantIds = $userTenantRepository->listTenantIdsByUserId($userId); if ($allowedTenantIds) { $tenantIds = array_values(array_intersect($tenantIds, $allowedTenantIds)); } $tenantIdsForSync = $tenantIds; } $selectedTenantIds = $tenantIds; $primaryTenantId = (int) ($_POST['primary_tenant_id'] ?? 0); $selectedRoleIds = $userAssignmentService->normalizeIdInput($_POST['role_ids'] ?? []); $selectedDepartmentIds = $userAssignmentService->normalizeIdInput($_POST['department_ids'] ?? []); $departmentOptionsByTenant = directoryServicesFactory()->createDepartmentService()->groupActiveByTenantIds($selectedTenantIds); $customFieldDefinitionsByTenant = UserCustomFieldValueService::buildDefinitionsByTenant($selectedTenantIds); $customFieldDefinitionIds = []; foreach ($customFieldDefinitionsByTenant as $tenantDefinitions) { foreach ($tenantDefinitions as $definition) { $definitionId = (int) ($definition['id'] ?? 0); if ($definitionId > 0) { $customFieldDefinitionIds[] = $definitionId; } } } $customFieldValueMap = UserCustomFieldValueService::buildUserValueMap($userId, $customFieldDefinitionIds); $customFieldPostedValues = [ 'custom_field_values' => is_array($_POST['custom_field_values'] ?? null) ? $_POST['custom_field_values'] : [], 'custom_field_values_multi' => is_array($_POST['custom_field_values_multi'] ?? null) ? $_POST['custom_field_values_multi'] : [], ]; if ($result['ok'] ?? false) { if ($canEditAssignments) { $db = DB::handle(); $db->begin_transaction(); try { $userAssignmentService->syncTenants($userId, $tenantIdsForSync, false); $userAssignmentService->syncRoles($userId, $selectedRoleIds, false); $userAssignmentService->syncDepartments($userId, $selectedDepartmentIds, false); $userAssignmentService->bumpAuthzVersion($userId); $db->commit(); } catch (\Throwable $exception) { $db->rollback(); $errors[] = t('User assignments can not be updated'); } // Update session if editing own account (tenant assignments changed) if ($currentUserId === $userId && !$errors) { $authService->loadTenantDataIntoSession($userId); } } $customFieldSyncResult = UserCustomFieldValueService::syncForUser( $userId, $selectedTenantIds, $_POST, $canEditCustomFieldValues ); if (!($customFieldSyncResult['ok'] ?? false)) { $errors = array_merge($errors, $customFieldSyncResult['errors'] ?? []); $customFieldValueMap = UserCustomFieldValueService::buildUserValueMap($userId, $customFieldDefinitionIds); } if ($currentUserId === $userId && isset($form['theme'])) { $themes = appThemes(); $themeValue = strtolower(trim((string) ($form['theme'] ?? ''))); $_SESSION['user']['theme'] = isset($themes[$themeValue]) ? $themeValue : appDefaultTheme(); } if ($currentUserId === $userId && isset($form['locale'])) { $_SESSION['user']['locale'] = $form['locale']; I18n::$locale = $form['locale']; } if (!$errors) { $action = (string) ($_POST['action'] ?? 'save'); if ($action === 'save_close') { Flash::success('User updated', 'admin/users', 'user_updated'); Router::redirect('admin/users'); } else { Flash::success('User updated', "admin/users/edit/{$uuid}", 'user_updated'); Router::redirect("admin/users/edit/{$uuid}"); } } } } Buffer::set('title', $isOwnAccount ? t('My account') : t('Edit user'));