2026-02-04 23:31:53 +01:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
use MintyPHP\Buffer;
|
|
|
|
|
use MintyPHP\Support\Flash;
|
|
|
|
|
use MintyPHP\Support\Guard;
|
|
|
|
|
use MintyPHP\I18n;
|
|
|
|
|
use MintyPHP\Router;
|
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
|
|
|
use MintyPHP\DB;
|
2026-02-11 19:28:12 +01:00
|
|
|
use MintyPHP\Repository\Access\UserRoleRepository;
|
|
|
|
|
use MintyPHP\Repository\Tenant\UserTenantRepository;
|
|
|
|
|
use MintyPHP\Repository\Org\UserDepartmentRepository;
|
|
|
|
|
use MintyPHP\Repository\Access\RolePermissionRepository;
|
|
|
|
|
use MintyPHP\Repository\Auth\PasswordResetRepository;
|
|
|
|
|
use MintyPHP\Repository\Auth\RememberTokenRepository;
|
|
|
|
|
use MintyPHP\Service\Auth\AuthService;
|
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
|
|
|
use MintyPHP\Service\CustomField\UserCustomFieldValueService;
|
2026-02-11 19:28:12 +01:00
|
|
|
use MintyPHP\Service\Org\DepartmentService;
|
|
|
|
|
use MintyPHP\Service\Access\RoleService;
|
|
|
|
|
use MintyPHP\Service\Tenant\TenantService;
|
|
|
|
|
use MintyPHP\Service\User\UserService;
|
|
|
|
|
use MintyPHP\Service\Access\PermissionService;
|
|
|
|
|
use MintyPHP\Service\Tenant\TenantScopeService;
|
2026-02-04 23:31:53 +01:00
|
|
|
|
|
|
|
|
Guard::requireLogin();
|
|
|
|
|
|
|
|
|
|
$currentUserId = (int) ($_SESSION['user']['id'] ?? 0);
|
|
|
|
|
if ($currentUserId > 0) {
|
|
|
|
|
PermissionService::getUserPermissions($currentUserId, true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$uuid = trim((string) ($id ?? ''));
|
|
|
|
|
$user = $uuid !== '' ? UserService::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 = PermissionService::userHas($currentUserId, PermissionService::USERS_VIEW);
|
|
|
|
|
$canUpdateUser = PermissionService::userHas($currentUserId, PermissionService::USERS_UPDATE);
|
|
|
|
|
$canUpdateSelf = PermissionService::userHas($currentUserId, PermissionService::USERS_SELF_UPDATE);
|
|
|
|
|
$canUpdateAssignments = PermissionService::userHas($currentUserId, PermissionService::USERS_UPDATE_ASSIGNMENTS);
|
|
|
|
|
$isOwnAccount = $currentUserId === $userId;
|
|
|
|
|
$canEditUser = $isOwnAccount ? ($canUpdateUser || $canUpdateSelf) : $canUpdateUser;
|
|
|
|
|
$canEditAssignments = $canEditUser && $canUpdateAssignments;
|
|
|
|
|
if (!$isOwnAccount && !TenantScopeService::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 = UserService::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 = UserService::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 = UserService::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 = PermissionService::userHas($currentUserId, PermissionService::TENANTS_UPDATE);
|
|
|
|
|
$allowedTenantIds = $canManageTenants ? null : TenantScopeService::getUserTenantIds($currentUserId);
|
|
|
|
|
|
|
|
|
|
$tenants = TenantService::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 && TenantScopeService::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;
|
|
|
|
|
}
|
2026-02-11 19:28:12 +01:00
|
|
|
$roles = RoleService::listActive();
|
2026-02-04 23:31:53 +01:00
|
|
|
$selectedRoleIds = UserRoleRepository::listRoleIdsByUserId($userId);
|
|
|
|
|
$permissionRows = RolePermissionRepository::listPermissionsWithRolesByRoleIds($selectedRoleIds);
|
|
|
|
|
$selectedDepartmentIds = UserDepartmentRepository::listDepartmentIdsByUserId($userId);
|
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
|
|
|
$departmentOptionsByTenant = DepartmentService::groupActiveByTenantIds($selectedTenantIds);
|
|
|
|
|
$canEditCustomFieldValues = $canEditUser
|
|
|
|
|
&& (PermissionService::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;
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-02-04 23:31:53 +01:00
|
|
|
}
|
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
|
|
|
$customFieldValueMap = UserCustomFieldValueService::buildUserValueMap($userId, $customFieldDefinitionIds);
|
|
|
|
|
$customFieldPostedValues = [
|
|
|
|
|
'custom_field_values' => [],
|
|
|
|
|
'custom_field_values_multi' => [],
|
|
|
|
|
];
|
2026-02-11 19:28:12 +01:00
|
|
|
$passwordResets = [];
|
|
|
|
|
if ($canViewUsers || $canEditUser) {
|
|
|
|
|
$passwordResets = PasswordResetRepository::listByUserId($userId, 25);
|
|
|
|
|
}
|
|
|
|
|
$rememberTokens = [];
|
|
|
|
|
if ($canViewUsers || $canEditUser) {
|
|
|
|
|
$rememberTokens = RememberTokenRepository::listByUserId($userId, 25);
|
|
|
|
|
}
|
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
|
|
|
$apiTokens = [];
|
|
|
|
|
$canManageApiTokens = $canEditUser
|
|
|
|
|
&& PermissionService::userHas($currentUserId, PermissionService::API_TOKENS_MANAGE);
|
|
|
|
|
if ($canViewUsers || $canEditUser) {
|
|
|
|
|
$apiTokens = \MintyPHP\Service\Auth\ApiTokenService::listForUser($userId);
|
|
|
|
|
}
|
|
|
|
|
$showApiTokens = !empty($apiTokens) || $canManageApiTokens;
|
2026-02-04 23:31:53 +01:00
|
|
|
// Keep initial $isOwnAccount/$canEditUser values for view permissions.
|
|
|
|
|
|
|
|
|
|
if (isset($_POST['email'])) {
|
|
|
|
|
if (!$canEditUser) {
|
|
|
|
|
Router::redirect('error/forbidden');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
$result = UserService::updateFromAdmin($userId, $_POST, $currentUserId);
|
|
|
|
|
$form = $result['form'] ?? $form;
|
|
|
|
|
$errors = $result['errors'] ?? [];
|
|
|
|
|
$tenantIds = UserService::normalizeIdInput($_POST['tenant_ids'] ?? []);
|
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
|
|
|
$existingTenantIds = UserTenantRepository::listTenantIdsByUserId($userId);
|
|
|
|
|
$tenantIdsForSync = $tenantIds;
|
|
|
|
|
if ($canEditAssignments && $allowedTenantIds) {
|
2026-02-04 23:31:53 +01:00
|
|
|
$tenantIds = array_values(array_intersect($tenantIds, $allowedTenantIds));
|
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
|
|
|
$tenantIdsForSync = TenantScopeService::mergeTenantIdsPreservingOutOfScope(
|
|
|
|
|
$tenantIds,
|
|
|
|
|
$existingTenantIds,
|
|
|
|
|
$allowedTenantIds
|
|
|
|
|
);
|
|
|
|
|
} elseif ($canEditAssignments && !$canManageTenants && TenantScopeService::isStrict()) {
|
2026-02-04 23:31:53 +01:00
|
|
|
$tenantIds = [];
|
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
|
|
|
$tenantIdsForSync = $existingTenantIds;
|
|
|
|
|
}
|
|
|
|
|
if (!$canEditAssignments) {
|
|
|
|
|
$tenantIds = UserTenantRepository::listTenantIdsByUserId($userId);
|
|
|
|
|
if ($allowedTenantIds) {
|
|
|
|
|
$tenantIds = array_values(array_intersect($tenantIds, $allowedTenantIds));
|
|
|
|
|
}
|
|
|
|
|
$tenantIdsForSync = $tenantIds;
|
2026-02-04 23:31:53 +01:00
|
|
|
}
|
|
|
|
|
$selectedTenantIds = $tenantIds;
|
|
|
|
|
$primaryTenantId = (int) ($_POST['primary_tenant_id'] ?? 0);
|
|
|
|
|
$selectedRoleIds = UserService::normalizeIdInput($_POST['role_ids'] ?? []);
|
|
|
|
|
$selectedDepartmentIds = UserService::normalizeIdInput($_POST['department_ids'] ?? []);
|
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
|
|
|
$departmentOptionsByTenant = DepartmentService::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'] : [],
|
|
|
|
|
];
|
2026-02-04 23:31:53 +01:00
|
|
|
|
|
|
|
|
if ($result['ok'] ?? false) {
|
|
|
|
|
if ($canEditAssignments) {
|
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
|
|
|
$db = DB::handle();
|
|
|
|
|
$db->begin_transaction();
|
|
|
|
|
try {
|
|
|
|
|
UserService::syncTenants($userId, $tenantIdsForSync, false);
|
|
|
|
|
UserService::syncRoles($userId, $selectedRoleIds, false);
|
|
|
|
|
UserService::syncDepartments($userId, $selectedDepartmentIds, false);
|
|
|
|
|
UserService::bumpAuthzVersion($userId);
|
|
|
|
|
$db->commit();
|
|
|
|
|
} catch (\Throwable $exception) {
|
|
|
|
|
$db->rollback();
|
|
|
|
|
$errors[] = t('User assignments can not be updated');
|
|
|
|
|
}
|
2026-02-04 23:31:53 +01:00
|
|
|
|
|
|
|
|
// Update session if editing own account (tenant assignments changed)
|
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
|
|
|
if ($currentUserId === $userId && !$errors) {
|
2026-02-04 23:31:53 +01:00
|
|
|
AuthService::loadTenantDataIntoSession($userId);
|
|
|
|
|
}
|
|
|
|
|
}
|
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
|
|
|
$customFieldSyncResult = UserCustomFieldValueService::syncForUser(
|
|
|
|
|
$userId,
|
|
|
|
|
$selectedTenantIds,
|
|
|
|
|
$_POST,
|
|
|
|
|
$canEditCustomFieldValues
|
|
|
|
|
);
|
|
|
|
|
if (!($customFieldSyncResult['ok'] ?? false)) {
|
|
|
|
|
$errors = array_merge($errors, $customFieldSyncResult['errors'] ?? []);
|
|
|
|
|
$customFieldValueMap = UserCustomFieldValueService::buildUserValueMap($userId, $customFieldDefinitionIds);
|
|
|
|
|
}
|
2026-02-04 23:31:53 +01:00
|
|
|
if ($currentUserId === $userId && isset($form['theme'])) {
|
2026-02-11 19:28:12 +01:00
|
|
|
$themes = appThemes();
|
|
|
|
|
$themeValue = strtolower(trim((string) ($form['theme'] ?? '')));
|
|
|
|
|
$_SESSION['user']['theme'] = isset($themes[$themeValue]) ? $themeValue : appDefaultTheme();
|
2026-02-04 23:31:53 +01:00
|
|
|
}
|
|
|
|
|
if ($currentUserId === $userId && isset($form['locale'])) {
|
|
|
|
|
$_SESSION['user']['locale'] = $form['locale'];
|
|
|
|
|
I18n::$locale = $form['locale'];
|
|
|
|
|
}
|
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
|
|
|
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}");
|
|
|
|
|
}
|
2026-02-04 23:31:53 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Buffer::set('title', $isOwnAccount ? t('My account') : t('Edit user'));
|