splitCommaValues($query['tenants'] ?? ''); $activeRoles = $this->splitCommaValues($query['roles'] ?? ''); $activeDepartments = $this->splitCommaValues($query['departments'] ?? ''); $tenantIds = $this->normalizeIds($this->directoryGateway->getUserTenantIds($currentUserId)); $tenants = $this->directoryGateway->listTenants(); if ($tenantIds) { $allowedTenantMap = array_fill_keys($tenantIds, true); $tenants = array_values(array_filter( $tenants, static function (array $tenant) use ($allowedTenantMap): bool { $tenantId = (int) ($tenant['id'] ?? 0); return $tenantId > 0 && isset($allowedTenantMap[$tenantId]); } )); } elseif ($this->directoryGateway->isStrictScope()) { $tenants = []; } $tenantItems = array_map( static fn (array $tenant): array => [ 'id' => (string) ($tenant['uuid'] ?? ''), 'description' => (string) ($tenant['description'] ?? ''), ], $tenants ); $departments = $tenantIds ? $this->directoryGateway->listDepartmentsByTenantIds($tenantIds) : ($this->directoryGateway->isStrictScope() ? [] : $this->directoryGateway->listDepartments()); $roles = $this->directoryGateway->listActiveRoles(); $customFieldFilterSpec = $this->customFieldGateway->extractFilterSpec($query, $currentUserId); $customFieldFilterDefinitions = $this->normalizeDefinitions( $customFieldFilterSpec['definitions'] ?? [] ); $customFieldFilterQuery = is_array($customFieldFilterSpec['query'] ?? null) ? $customFieldFilterSpec['query'] : []; $customFieldDefinitionIds = []; foreach ($customFieldFilterDefinitions as $definition) { $definitionId = (int) ($definition['id'] ?? 0); if ($definitionId > 0) { $customFieldDefinitionIds[] = $definitionId; } } $customFieldDefinitionIds = array_values(array_unique($customFieldDefinitionIds)); $customFieldOptions = $this->customFieldGateway->listOptionsByDefinitionIds( $customFieldDefinitionIds, true ); $customFieldOptionsByDefinition = []; foreach ($customFieldOptions as $option) { $definitionId = (int) ($option['definition_id'] ?? 0); if ($definitionId <= 0) { continue; } $customFieldOptionsByDefinition[$definitionId] ??= []; $customFieldOptionsByDefinition[$definitionId][] = [ 'id' => (string) ((int) ($option['id'] ?? 0)), 'description' => (string) ($option['label'] ?? ''), ]; } $tenantLabelById = []; foreach ($tenants as $tenant) { $tenantId = (int) ($tenant['id'] ?? 0); if ($tenantId <= 0) { continue; } $tenantLabelById[$tenantId] = (string) ($tenant['description'] ?? ''); } foreach ($customFieldFilterDefinitions as &$definition) { $definitionId = (int) ($definition['id'] ?? 0); $tenantId = (int) ($definition['tenant_id'] ?? 0); $definition['options'] = $customFieldOptionsByDefinition[$definitionId] ?? []; $definition['tenant_label'] = $tenantLabelById[$tenantId] ?? ''; } unset($definition); $tenantDepartmentMap = []; $departmentMapByTenantId = []; foreach ($departments as $department) { $departmentId = (int) ($department['id'] ?? 0); $departmentTenantId = (int) ($department['tenant_id'] ?? 0); if ($departmentId <= 0 || $departmentTenantId <= 0) { continue; } $departmentMapByTenantId[$departmentTenantId] ??= []; $departmentMapByTenantId[$departmentTenantId][] = $departmentId; } foreach ($tenants as $tenant) { $tenantId = (int) ($tenant['id'] ?? 0); $tenantUuid = (string) ($tenant['uuid'] ?? ''); if ($tenantId <= 0 || $tenantUuid === '') { continue; } $tenantDepartmentMap[$tenantUuid] = array_map( 'strval', $departmentMapByTenantId[$tenantId] ?? [] ); } return [ 'activeTenants' => $activeTenants, 'activeRoles' => $activeRoles, 'activeDepartments' => $activeDepartments, 'tenantItems' => $tenantItems, 'departments' => $departments, 'roles' => $roles, 'customFieldFilterDefinitions' => $customFieldFilterDefinitions, 'customFieldFilterQuery' => $customFieldFilterQuery, 'tenantDepartmentMap' => $tenantDepartmentMap, ]; } public function buildGridResult(int $currentUserId, array $query): array { $limit = (int) ($query['limit'] ?? 10); $offset = (int) ($query['offset'] ?? 0); $search = trim((string) ($query['search'] ?? '')); $order = (string) ($query['order'] ?? 'last_name'); $dir = (string) ($query['dir'] ?? 'asc'); $tenant = trim((string) ($query['tenant'] ?? '')); $tenants = $query['tenants'] ?? ''; $departments = $query['departments'] ?? ''; $roles = $query['roles'] ?? ''; $customFieldFilterSpec = $this->customFieldGateway->extractFilterSpec($query, $currentUserId); $result = $this->userAccountService->listPaged([ 'limit' => $limit, 'offset' => $offset, 'search' => $search, 'order' => $order, 'dir' => $dir, 'tenant' => $tenant, 'tenants' => $tenants, 'departments' => $departments, 'roles' => $roles, 'customFieldFilterSpec' => $customFieldFilterSpec, 'tenantUserId' => $currentUserId, 'active' => 'active', ]); $rows = []; foreach (($result['rows'] ?? []) as $row) { $tenantList = $this->normalizeLabelList($row['tenant_labels'] ?? []); $departmentList = $this->normalizeLabelList($row['department_labels'] ?? []); $roleList = $this->normalizeLabelList($row['role_labels'] ?? []); $uuid = (string) ($row['uuid'] ?? ''); $displayName = trim((string) ($row['display_name'] ?? '')); if ($displayName === '') { $displayName = trim((string) ($row['first_name'] ?? '') . ' ' . (string) ($row['last_name'] ?? '')); } if ($displayName === '') { $displayName = (string) ($row['email'] ?? ''); } $rows[] = [ 'uuid' => $uuid, 'display_name' => $displayName, 'first_name' => (string) ($row['first_name'] ?? ''), 'last_name' => (string) ($row['last_name'] ?? ''), 'email' => (string) ($row['email'] ?? ''), 'phone' => (string) ($row['phone'] ?? ''), 'mobile' => (string) ($row['mobile'] ?? ''), 'short_dial' => (string) ($row['short_dial'] ?? ''), 'tenants' => $tenantList, 'departments' => $departmentList, 'roles' => $roleList, 'has_avatar' => $uuid !== '' && $this->avatarGateway->hasAvatar($uuid), ]; } return [ 'data' => $rows, 'total' => (int) ($result['total'] ?? 0), ]; } public function buildViewContext(int $currentUserId, string $uuid): array { $uuid = trim($uuid); if ($uuid === '') { return ['status' => 'invalid_uuid']; } $user = $this->userAccountService->findByUuid($uuid); if (!$user || !isset($user['id'])) { return ['status' => 'not_found']; } $targetUserId = (int) $user['id']; if ($targetUserId <= 0) { return ['status' => 'not_found']; } if ( $currentUserId !== $targetUserId && !$this->directoryGateway->canAccessUser($currentUserId, $targetUserId) ) { return ['status' => 'forbidden']; } $viewerTenantIds = $this->normalizeIds($this->directoryGateway->getUserTenantIds($currentUserId)); $assignments = $this->userAssignmentService->buildAssignmentsForUser($targetUserId); $departmentMap = []; foreach (($assignments['departments'] ?? []) as $department) { if (empty($department['active'])) { continue; } $tenantId = (int) ($department['tenant_id'] ?? 0); $label = trim((string) ($department['description'] ?? '')); if ($tenantId <= 0 || $label === '' || !in_array($tenantId, $viewerTenantIds, true)) { continue; } $departmentMap[$tenantId] ??= []; $departmentMap[$tenantId][] = $label; } foreach ($departmentMap as $tenantId => $labels) { $labels = array_values(array_unique($labels)); natcasesort($labels); $departmentMap[$tenantId] = array_values($labels); } $primaryTenantId = (int) ($user['primary_tenant_id'] ?? 0); $tenantGroups = []; foreach (($assignments['tenants'] ?? []) as $tenant) { $tenantId = (int) ($tenant['id'] ?? 0); $tenantLabel = trim((string) ($tenant['description'] ?? '')); $status = strtolower(trim((string) ($tenant['status'] ?? ''))); if ( $tenantId <= 0 || $tenantLabel === '' || $status !== 'active' || !in_array($tenantId, $viewerTenantIds, true) ) { continue; } $tenantGroups[] = [ 'label' => $tenantLabel, 'is_primary' => $primaryTenantId > 0 && $tenantId === $primaryTenantId, 'departments' => $departmentMap[$tenantId] ?? [], ]; } usort($tenantGroups, static fn (array $a, array $b): int => strnatcasecmp( (string) $a['label'], (string) $b['label'] )); $roleLabels = []; foreach (($assignments['roles'] ?? []) as $role) { if (empty($role['active'])) { continue; } $label = trim((string) ($role['description'] ?? '')); if ($label !== '') { $roleLabels[] = $label; } } $roleLabels = array_values(array_unique($roleLabels)); natcasesort($roleLabels); $roleLabels = array_values($roleLabels); $avatarUuid = (string) ($user['uuid'] ?? ''); $user['has_avatar'] = $avatarUuid !== '' && $this->avatarGateway->hasAvatar($avatarUuid); $user['tenant_groups'] = $tenantGroups; $user['role_labels'] = $roleLabels; return [ 'status' => 'ok', 'user' => $user, ]; } private function splitCommaValues($value): array { return array_values(array_filter( array_map('trim', explode(',', (string) $value)), static fn (string $item): bool => $item !== '' )); } private function normalizeDefinitions($definitions): array { if (!is_array($definitions)) { return []; } return array_values(array_filter( $definitions, static fn ($definition): bool => is_array($definition) )); } private function normalizeIds(array $values): array { $ids = array_values(array_unique(array_map('intval', $values))); return array_values(array_filter($ids, static fn (int $id): bool => $id > 0)); } private function normalizeLabelList($value): array { if (is_string($value)) { if ($value === '') { return []; } $items = explode('||', $value); } elseif (is_array($value)) { $items = $value; } else { return []; } return array_values(array_filter(array_map( static fn ($item): string => trim((string) $item), $items ), static fn (string $item): bool => $item !== '')); } }