queryAll(); } catch (\Throwable) { // fail-open: may not be available in CLI context } return [ 'addressbook.nav' => [ 'url' => lurl('address-book'), 'activeSearch' => trim((string) ($query['search'] ?? '')), 'activeTenants' => appNormalizeStringList($query['tenants'] ?? ''), 'activeDepartments' => appNormalizePositiveIntList($query['departments'] ?? ''), 'activeRoles' => appNormalizePositiveIntList($query['roles'] ?? ''), 'activeCustomFilters' => self::normalizeCustomFilterQuery($query), 'peopleGroups' => is_array($session['available_departments_by_tenant'] ?? null) ? $session['available_departments_by_tenant'] : [], ], ]; } /** * Normalize address-book custom field query keys/values. * * @param array $rawQuery * @return array> */ public static function normalizeCustomFilterQuery(array $rawQuery): array { $normalized = []; foreach ($rawQuery as $rawKey => $rawValue) { $key = strtolower(trim((string) $rawKey)); if ($key === '') { continue; } if (preg_match('/^cf_[a-f0-9-]{36}$/', $key)) { $value = trim((string) $rawValue); if ($value !== '') { $normalized[$key] = $value; } continue; } if (preg_match('/^cfm_[a-f0-9-]{36}$/', $key)) { $ids = appNormalizePositiveIntList($rawValue); if ($ids) { $normalized[$key] = $ids; } continue; } if (preg_match('/^cfd_[a-f0-9-]{36}_(from|to)$/', $key)) { $value = trim((string) $rawValue); $dt = \DateTimeImmutable::createFromFormat('Y-m-d', $value); if ($dt && $dt->format('Y-m-d') === $value) { $normalized[$key] = $value; } } } ksort($normalized, SORT_STRING); return $normalized; } }