*/ public function getColumns(): array { $columns = []; $tcaColumns = $GLOBALS['TCA']['be_users']['columns']['user_settings']['columns'] ?? []; foreach ($tcaColumns as $fieldName => $tcaConfig) { $columns[$fieldName] = $this->resolveTcaColumn($fieldName, $tcaConfig); } return $columns; } /** * Get configuration for a specific field in legacy format. */ public function getColumn(string $fieldName): ?array { $tcaConfig = $GLOBALS['TCA']['be_users']['columns']['user_settings']['columns'][$fieldName] ?? null; if ($tcaConfig !== null) { return $this->resolveTcaColumn($fieldName, $tcaConfig); } return null; } /** * Returns a "fake TCA" for the be_users_settings pseudo-table. */ public function getTca(): array { $columns = $GLOBALS['TCA']['be_users']['columns']['user_settings']['columns'] ?? []; foreach ($columns as $fieldName => $columnConfig) { $partitionedFieldName = $this->getTcaFieldName($fieldName); $columns[$partitionedFieldName] = $this->resolveInheritFromParent($fieldName, $columnConfig); } return [ 'be_users_settings' => [ 'ctrl' => [ 'title' => 'backend.user_profile:user_settings', ], 'columns' => $columns, 'types' => [ '0' => [ 'showitem' => $this->getTcaShowitem(), ], ], ], ]; } /** * Returns a partitioned field name for use in TCA. * - e.g. `be_users__password`, reflecting `be_users` values * - e.g. `user_settings__titleLen`, reflecting JSON values */ public function getTcaFieldName(string $fieldName): string { $configuration = $this->getColumn($fieldName); $partition = ($configuration['table'] ?? null) === 'be_users' ? 'be_users' : 'user_settings'; return $partition . '__' . $fieldName; } private function resolveTcaFieldName(string $fieldName, bool $strict = true): string { $configuration = $this->getColumn($fieldName); if ($configuration !== null) { $partition = ($configuration['table'] ?? null) === 'be_users' ? 'be_users' : 'user_settings'; return $partition . '__' . $fieldName; } if (!$strict) { return $fieldName; } throw new \LogicException( sprintf( 'Column "%s" not found in UserSettingsSchema', $fieldName ), 1776439141 ); } /** * Get the partitioned showitem string to be used as virtual TCA. */ public function getTcaShowitem(): string { $items = GeneralUtility::trimExplode(',', $this->getRawShowitem(), true); $items = array_map( fn(string $fieldName): string => $this->resolveTcaFieldName($fieldName, false), $items ); return implode(',', $items); } /** * Get the raw showitem string. */ public function getRawShowitem(): string { return trim($GLOBALS['TCA']['be_users']['columns']['user_settings']['showitem'] ?? ''); } /** * @return list */ public function getJsonFieldSettingKeys(): array { $keys = []; foreach ($this->getColumns() as $key => $config) { // Fields with 'table' => 'be_users' are stored in be_users columns directly // Also skip non-storable types like 'button' and 'mfa' $type = $config['type'] ?? 'text'; if (($config['table'] ?? '') !== 'be_users' && !in_array($type, ['button', 'mfa'], true) ) { $keys[] = $key; } } return $keys; } /** * @return string[] */ public function getDbColumnSettingKeys(): array { $keys = []; foreach ($this->getColumns() as $key => $config) { $type = $config['type'] ?? 'text'; if (($config['table'] ?? '') === 'be_users' && !in_array($type, ['button', 'mfa', 'password'], true) ) { $keys[] = $key; } } return $keys; } public function isJsonFieldSetting(string $key): bool { return in_array($key, $this->getJsonFieldSettingKeys(), true); } public function isDbColumnSetting(string $key): bool { return in_array($key, $this->getDbColumnSettingKeys(), true); } /** * Returns field names that should trigger a JS persistent storage update * when their value changes in User Settings, so JS components can react * immediately without a page reload. * * @return string[] */ public function getPersistentUpdateFieldNames(): array { $keys = []; foreach ($this->getColumns() as $key => $config) { if (!empty($config['persistentUpdate'])) { $keys[] = $key; } } return $keys; } public function getDefault(string $key): mixed { $config = $this->getColumn($key); return $config['default'] ?? null; } /** * Resolves inheritFromParent for a TCA column by merging with the * parent be_users TCA column configuration. Returns TCA format * (without legacy conversion). * * @internal */ public function resolveInheritFromParent(string $fieldName, array $tcaConfig): array { if (!empty($tcaConfig['inheritFromParent'])) { $parentConfig = $GLOBALS['TCA']['be_users']['columns'][$fieldName] ?? []; $tcaConfig = array_replace_recursive($parentConfig, $tcaConfig); unset($tcaConfig['inheritFromParent']); } return $tcaConfig; } /** * Resolves a TCA column configuration, handling inheritFromParent, * and converts to legacy format. */ private function resolveTcaColumn(string $fieldName, array $tcaConfig): array { return $this->convertTcaToLegacyFormat($fieldName, $this->resolveInheritFromParent($fieldName, $tcaConfig)); } /** * Converts a TCA column configuration to legacy format. */ private function convertTcaToLegacyFormat(string $fieldName, array $tcaConfig): array { $legacyConfig = [ 'label' => $tcaConfig['label'] ?? '', ]; $config = $tcaConfig['config'] ?? []; $tcaType = $config['type'] ?? 'input'; $renderType = $config['renderType'] ?? ''; // Determine if this field is stored in be_users table // Fields with inheritFromParent that exist in be_users columns are table fields if (isset($GLOBALS['TCA']['be_users']['columns'][$fieldName])) { $legacyConfig['table'] = 'be_users'; } // Convert TCA type to legacy type switch ($tcaType) { case 'input': $legacyConfig['type'] = 'text'; if (isset($config['max'])) { $legacyConfig['max'] = $config['max']; } break; case 'email': $legacyConfig['type'] = 'email'; if (isset($config['max'])) { $legacyConfig['max'] = $config['max']; } break; case 'number': $legacyConfig['type'] = 'number'; break; case 'password': $legacyConfig['type'] = 'password'; break; case 'check': $legacyConfig['type'] = 'check'; break; case 'select': $legacyConfig['type'] = 'select'; if (isset($config['items'])) { $legacyConfig['items'] = $this->convertSelectItemsToLegacy($config['items']); } if (isset($config['itemsProcFunc'])) { $legacyConfig['itemsProcFunc'] = $config['itemsProcFunc']; } break; case 'language': $legacyConfig['type'] = 'language'; break; case 'file': $legacyConfig['type'] = 'avatar'; break; case 'button': $legacyConfig['type'] = 'button'; if (isset($config['buttonLabel'])) { $legacyConfig['buttonlabel'] = $config['buttonLabel']; } if (isset($config['confirm'])) { $legacyConfig['confirm'] = $config['confirm']; } if (isset($config['confirmData'])) { $legacyConfig['confirmData'] = $config['confirmData']; } break; case 'mfa': $legacyConfig['type'] = 'mfa'; break; case 'user': $legacyConfig['type'] = 'user'; if (isset($config['renderType'])) { $legacyConfig['userFunc'] = $config['renderType']; } break; default: $legacyConfig['type'] = 'text'; } // Copy additional properties if (isset($config['default'])) { $legacyConfig['default'] = $config['default']; } if (isset($tcaConfig['access'])) { $legacyConfig['access'] = $tcaConfig['access']; } if (!empty($tcaConfig['persistentUpdate'])) { $legacyConfig['persistentUpdate'] = true; } return $legacyConfig; } /** * Converts TCA select items format to legacy format. * Handles both TCA format ([['label' => '...', 'value' => '...'], ...]) * and legacy format (['value' => 'label', ...]). */ private function convertSelectItemsToLegacy(array $items): array { $legacyItems = []; foreach ($items as $key => $item) { if (is_array($item) && isset($item['value']) && isset($item['label'])) { // TCA format: [['label' => '...', 'value' => '...'], ...] $legacyItems[$item['value']] = $item['label']; } elseif (is_string($item)) { // Legacy format: ['value' => 'label', ...] $legacyItems[$key] = $item; } } return $legacyItems; } }