Files
cms-core/Classes/Authentication/UserSettingsSchema.php
T

364 lines
11 KiB
PHP

<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\Core\Authentication;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Provides unified access to backend user settings configuration.
*
* This class consolidates access to user settings configuration stored in
* TCA at $GLOBALS['TCA']['be_users']['columns']['user_settings'].
*
* @internal This class is not part of the TYPO3 Core API yet.
*/
readonly class UserSettingsSchema
{
/**
* Get all column configurations in legacy format.
* This merges TCA-based config with legacy global, preferring TCA.
*
* @return array<string, array>
*/
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<string>
*/
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;
}
}