splitCommaValues($query['tenants'] ?? ''); $activeRoles = $this->splitCommaValues($query['roles'] ?? ''); $activeDepartments = $this->splitCommaValues($query['departments'] ?? ''); $tenantIds = $this->normalizeIds($this->scopeGateway->getUserTenantIds($currentUserId)); $tenants = $this->tenantService->list(); 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->scopeGateway->isStrict()) { $tenants = []; } $tenantItems = array_map( static fn (array $tenant): array => [ 'id' => (string) ($tenant['uuid'] ?? ''), 'description' => (string) ($tenant['description'] ?? ''), ], $tenants ); $departments = $tenantIds ? $this->departmentService->listByTenantIds($tenantIds) : ($this->scopeGateway->isStrict() ? [] : $this->departmentService->list()); $roles = $this->roleService->listActive(); $customFieldFilterSpec = $this->customFieldValueService->extractCustomFieldFilterSpec($query, $currentUserId); $customFieldFilterDefinitions = $this->normalizeDefinitions( $customFieldFilterSpec['definitions'] ); $customFieldFilterQuery = $customFieldFilterSpec['query']; $customFieldDefinitionIds = []; foreach ($customFieldFilterDefinitions as $definition) { $definitionId = (int) ($definition['id'] ?? 0); if ($definitionId > 0) { $customFieldDefinitionIds[] = $definitionId; } } $customFieldDefinitionIds = array_values(array_unique($customFieldDefinitionIds)); $customFieldOptions = TenantCustomFieldOptionRepository::listByDefinitionIds( $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] ?? [] ); } $activeTenant = trim((string) ($query['tenant'] ?? '')); $tenantTabs = array_values(array_filter(array_map( static function (array $tenant): array { return [ 'uuid' => (string) ($tenant['uuid'] ?? ''), 'description' => (string) ($tenant['description'] ?? ''), ]; }, $tenants ), static fn (array $tab): bool => $tab['uuid'] !== '')); usort($tenantTabs, static fn (array $a, array $b): int => strnatcasecmp( (string) $a['description'], (string) $b['description'] )); return [ 'activeTenants' => $activeTenants, 'activeRoles' => $activeRoles, 'activeDepartments' => $activeDepartments, 'activeTenant' => $activeTenant, 'tenantItems' => $tenantItems, 'tenantTabs' => $tenantTabs, '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->customFieldValueService->extractCustomFieldFilterSpec($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'] ?? ''); } $primaryTenantLabel = $tenantList[0] ?? ''; $primaryDepartmentLabel = $departmentList[0] ?? ''; $extraAssignmentsCount = max(0, count($tenantList) - 1) + max(0, count($departmentList) - 1); $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, 'primary_tenant_label' => $primaryTenantLabel, 'primary_department_label' => $primaryDepartmentLabel, 'extra_assignments_count' => $extraAssignmentsCount, 'has_avatar' => $uuid !== '' && $this->avatarService->hasAvatar($uuid), ]; } return [ 'data' => $rows, 'total' => (int) ($result['total'] ?? 0), ]; } public function buildViewContext(int $currentUserId, string $uuid): array { return $this->userProfileViewService->buildProfile($currentUserId, $uuid); } /** * Transform custom field filter definitions into chip metadata for the grid UI. * * @param list> $definitions * @return list> */ public function buildCustomFieldChipMeta(array $definitions): array { $chips = []; foreach ($definitions as $definition) { $uuid = strtolower(trim((string) ($definition['uuid'] ?? ''))); $type = strtolower(trim((string) ($definition['type'] ?? ''))); $label = trim((string) ($definition['label'] ?? '')); $options = is_array($definition['options'] ?? null) ? $definition['options'] : []; if ($uuid === '' || $type === '' || $label === '') { continue; } if (in_array($type, ['select', 'boolean'], true)) { $optionMap = gridOptionMapFromItems($options); if ($type === 'boolean') { $optionMap = ['1' => t('Yes'), '0' => t('No')]; } $chips[] = [ 'type' => 'scalar', 'param' => 'cf_' . $uuid, 'label' => $label, 'options' => $optionMap, ]; continue; } if ($type === 'multiselect') { $chips[] = [ 'type' => 'multi_csv', 'param' => 'cfm_' . $uuid, 'label' => $label, 'options' => gridOptionMapFromItems($options), ]; continue; } if ($type === 'date') { $chips[] = [ 'type' => 'date_range', 'label' => $label, 'from_param' => 'cfd_' . $uuid . '_from', 'to_param' => 'cfd_' . $uuid . '_to', ]; } } return $chips; } 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)); } // Accepts either a '||'-delimited DB aggregate string (from GROUP_CONCAT) or a plain array. 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 !== '')); } }