diff options
Diffstat (limited to 'lib/private/Config')
-rw-r--r-- | lib/private/Config/ConfigManager.php | 268 | ||||
-rw-r--r-- | lib/private/Config/Lexicon/CoreConfigLexicon.php | 43 | ||||
-rw-r--r-- | lib/private/Config/UserConfig.php | 163 |
3 files changed, 396 insertions, 78 deletions
diff --git a/lib/private/Config/ConfigManager.php b/lib/private/Config/ConfigManager.php new file mode 100644 index 00000000000..ed516abdcbf --- /dev/null +++ b/lib/private/Config/ConfigManager.php @@ -0,0 +1,268 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OC\Config; + +use JsonException; +use OC\AppConfig; +use OCP\App\IAppManager; +use OCP\Config\Exceptions\TypeConflictException; +use OCP\Config\IUserConfig; +use OCP\Config\Lexicon\Entry; +use OCP\Config\Lexicon\Preset; +use OCP\Config\ValueType; +use OCP\IAppConfig; +use OCP\IConfig; +use OCP\Server; +use Psr\Log\LoggerInterface; + +/** + * tools to maintains configurations + * + * @since 32.0.0 + */ +class ConfigManager { + /** @since 32.0.0 */ + public const PRESET_CONFIGKEY = 'config_preset'; + + /** @var AppConfig|null $appConfig */ + private ?IAppConfig $appConfig = null; + /** @var UserConfig|null $userConfig */ + private ?IUserConfig $userConfig = null; + + public function __construct( + private readonly IConfig $config, + private readonly LoggerInterface $logger, + ) { + } + + /** + * Use the rename values from the list of ConfigLexiconEntry defined in each app ConfigLexicon + * to migrate config value to a new config key. + * Migration will only occur if new config key has no value in database. + * The previous value from the key set in rename will be deleted from the database when migration + * is over. + * + * This method should be mainly called during a new upgrade or when a new app is enabled. + * + * @param string|null $appId when set to NULL the method will be executed for all enabled apps of the instance + * + * @internal + * @since 32.0.0 + * @see Entry + */ + public function migrateConfigLexiconKeys(?string $appId = null): void { + if ($appId === null) { + $this->migrateConfigLexiconKeys('core'); + $appManager = Server::get(IAppManager::class); + foreach ($appManager->getEnabledApps() as $app) { + $this->migrateConfigLexiconKeys($app); + } + + return; + } + + $this->loadConfigServices(); + + // it is required to ignore aliases when moving config values + $this->appConfig->ignoreLexiconAliases(true); + $this->userConfig->ignoreLexiconAliases(true); + + $this->migrateAppConfigKeys($appId); + $this->migrateUserConfigKeys($appId); + + // switch back to normal behavior + $this->appConfig->ignoreLexiconAliases(false); + $this->userConfig->ignoreLexiconAliases(false); + } + + /** + * store in config.php the new preset + * refresh cached preset + */ + public function setLexiconPreset(Preset $preset): void { + $this->config->setSystemValue(self::PRESET_CONFIGKEY, $preset->value); + $this->loadConfigServices(); + $this->appConfig->clearCache(); + $this->userConfig->clearCacheAll(); + } + + /** + * config services cannot be load at __construct() or install will fail + */ + private function loadConfigServices(): void { + if ($this->appConfig === null) { + $this->appConfig = Server::get(IAppConfig::class); + } + if ($this->userConfig === null) { + $this->userConfig = Server::get(IUserConfig::class); + } + } + + /** + * Get details from lexicon related to AppConfig and search for entries with rename to initiate + * a migration to new config key + */ + private function migrateAppConfigKeys(string $appId): void { + $lexicon = $this->appConfig->getConfigDetailsFromLexicon($appId); + foreach ($lexicon['entries'] as $entry) { + // only interested in entries with rename set + if ($entry->getRename() === null) { + continue; + } + + // only migrate if rename config key has a value and the new config key hasn't + if ($this->appConfig->hasKey($appId, $entry->getRename()) + && !$this->appConfig->hasKey($appId, $entry->getKey())) { + try { + $this->migrateAppConfigValue($appId, $entry); + } catch (TypeConflictException $e) { + $this->logger->error('could not migrate AppConfig value', ['appId' => $appId, 'entry' => $entry, 'exception' => $e]); + continue; + } + } + + // we only delete previous config value if migration went fine. + $this->appConfig->deleteKey($appId, $entry->getRename()); + } + } + + /** + * Get details from lexicon related to UserConfig and search for entries with rename to initiate + * a migration to new config key + */ + private function migrateUserConfigKeys(string $appId): void { + $lexicon = $this->userConfig->getConfigDetailsFromLexicon($appId); + foreach ($lexicon['entries'] as $entry) { + // only interested in keys with rename set + if ($entry->getRename() === null) { + continue; + } + + foreach ($this->userConfig->getValuesByUsers($appId, $entry->getRename()) as $userId => $value) { + if ($this->userConfig->hasKey($userId, $appId, $entry->getKey())) { + continue; + } + + try { + $this->migrateUserConfigValue($userId, $appId, $entry); + } catch (TypeConflictException $e) { + $this->logger->error('could not migrate UserConfig value', ['userId' => $userId, 'appId' => $appId, 'entry' => $entry, 'exception' => $e]); + continue; + } + + $this->userConfig->deleteUserConfig($userId, $appId, $entry->getRename()); + } + } + } + + + /** + * converting value from rename to the new key + * + * @throws TypeConflictException if previous value does not fit the expected type + */ + private function migrateAppConfigValue(string $appId, Entry $entry): void { + $value = $this->appConfig->getValueMixed($appId, $entry->getRename(), lazy: null); + switch ($entry->getValueType()) { + case ValueType::STRING: + $this->appConfig->setValueString($appId, $entry->getKey(), $value); + return; + + case ValueType::INT: + $this->appConfig->setValueInt($appId, $entry->getKey(), $this->convertToInt($value)); + return; + + case ValueType::FLOAT: + $this->appConfig->setValueFloat($appId, $entry->getKey(), $this->convertToFloat($value)); + return; + + case ValueType::BOOL: + $this->appConfig->setValueBool($appId, $entry->getKey(), $this->convertToBool($value, $entry)); + return; + + case ValueType::ARRAY: + $this->appConfig->setValueArray($appId, $entry->getKey(), $this->convertToArray($value)); + return; + } + } + + /** + * converting value from rename to the new key + * + * @throws TypeConflictException if previous value does not fit the expected type + */ + private function migrateUserConfigValue(string $userId, string $appId, Entry $entry): void { + $value = $this->userConfig->getValueMixed($userId, $appId, $entry->getRename(), lazy: null); + switch ($entry->getValueType()) { + case ValueType::STRING: + $this->userConfig->setValueString($userId, $appId, $entry->getKey(), $value); + return; + + case ValueType::INT: + $this->userConfig->setValueInt($userId, $appId, $entry->getKey(), $this->convertToInt($value)); + return; + + case ValueType::FLOAT: + $this->userConfig->setValueFloat($userId, $appId, $entry->getKey(), $this->convertToFloat($value)); + return; + + case ValueType::BOOL: + $this->userConfig->setValueBool($userId, $appId, $entry->getKey(), $this->convertToBool($value, $entry)); + return; + + case ValueType::ARRAY: + $this->userConfig->setValueArray($userId, $appId, $entry->getKey(), $this->convertToArray($value)); + return; + } + } + + public function convertToInt(string $value): int { + if (!is_numeric($value) || (float)$value <> (int)$value) { + throw new TypeConflictException('Value is not an integer'); + } + + return (int)$value; + } + + public function convertToFloat(string $value): float { + if (!is_numeric($value)) { + throw new TypeConflictException('Value is not a float'); + } + + return (float)$value; + } + + public function convertToBool(string $value, ?Entry $entry = null): bool { + if (in_array(strtolower($value), ['true', '1', 'on', 'yes'])) { + $valueBool = true; + } elseif (in_array(strtolower($value), ['false', '0', 'off', 'no'])) { + $valueBool = false; + } else { + throw new TypeConflictException('Value cannot be converted to boolean'); + } + if ($entry?->hasOption(Entry::RENAME_INVERT_BOOLEAN) === true) { + $valueBool = !$valueBool; + } + + return $valueBool; + } + + public function convertToArray(string $value): array { + try { + $valueArray = json_decode($value, true, flags: JSON_THROW_ON_ERROR); + } catch (JsonException) { + throw new TypeConflictException('Value is not a valid json'); + } + if (!is_array($valueArray)) { + throw new TypeConflictException('Value is not an array'); + } + + return $valueArray; + } +} diff --git a/lib/private/Config/Lexicon/CoreConfigLexicon.php b/lib/private/Config/Lexicon/CoreConfigLexicon.php deleted file mode 100644 index 34a0b883c54..00000000000 --- a/lib/private/Config/Lexicon/CoreConfigLexicon.php +++ /dev/null @@ -1,43 +0,0 @@ -<?php - -declare(strict_types=1); -/** - * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors - * SPDX-License-Identifier: AGPL-3.0-or-later - */ - -namespace OC\Config\Lexicon; - -use NCU\Config\Lexicon\ConfigLexiconEntry; -use NCU\Config\Lexicon\ConfigLexiconStrictness; -use NCU\Config\Lexicon\IConfigLexicon; -use NCU\Config\ValueType; - -/** - * ConfigLexicon for 'core' app/user configs - */ -class CoreConfigLexicon implements IConfigLexicon { - public function getStrictness(): ConfigLexiconStrictness { - return ConfigLexiconStrictness::IGNORE; - } - - /** - * @inheritDoc - * @return ConfigLexiconEntry[] - */ - public function getAppConfigs(): array { - return [ - new ConfigLexiconEntry('lastcron', ValueType::INT, 0, 'timestamp of last cron execution'), - ]; - } - - /** - * @inheritDoc - * @return ConfigLexiconEntry[] - */ - public function getUserConfigs(): array { - return [ - new ConfigLexiconEntry('lang', ValueType::STRING, null, 'language'), - ]; - } -} diff --git a/lib/private/Config/UserConfig.php b/lib/private/Config/UserConfig.php index 77a86a5e1c7..04ba0e29db0 100644 --- a/lib/private/Config/UserConfig.php +++ b/lib/private/Config/UserConfig.php @@ -11,20 +11,23 @@ namespace OC\Config; use Generator; use InvalidArgumentException; use JsonException; -use NCU\Config\Exceptions\IncorrectTypeException; -use NCU\Config\Exceptions\TypeConflictException; -use NCU\Config\Exceptions\UnknownKeyException; -use NCU\Config\IUserConfig; -use NCU\Config\Lexicon\ConfigLexiconEntry; -use NCU\Config\Lexicon\ConfigLexiconStrictness; -use NCU\Config\ValueType; use OC\AppFramework\Bootstrap\Coordinator; +use OCP\Config\Exceptions\IncorrectTypeException; +use OCP\Config\Exceptions\TypeConflictException; +use OCP\Config\Exceptions\UnknownKeyException; +use OCP\Config\IUserConfig; +use OCP\Config\Lexicon\Entry; +use OCP\Config\Lexicon\ILexicon; +use OCP\Config\Lexicon\Preset; +use OCP\Config\Lexicon\Strictness; +use OCP\Config\ValueType; use OCP\DB\Exception as DBException; use OCP\DB\IResult; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IConfig; use OCP\IDBConnection; use OCP\Security\ICrypto; +use OCP\Server; use Psr\Log\LoggerInterface; /** @@ -62,8 +65,10 @@ class UserConfig implements IUserConfig { private array $fastLoaded = []; /** @var array<string, boolean> ['user_id' => bool] */ private array $lazyLoaded = []; - /** @var array<array-key, array{entries: array<array-key, ConfigLexiconEntry>, strictness: ConfigLexiconStrictness}> ['app_id' => ['strictness' => ConfigLexiconStrictness, 'entries' => ['config_key' => ConfigLexiconEntry[]]] */ + /** @var array<string, array{entries: array<string, Entry>, aliases: array<string, string>, strictness: Strictness}> ['app_id' => ['strictness' => ConfigLexiconStrictness, 'entries' => ['config_key' => ConfigLexiconEntry[]]] */ private array $configLexiconDetails = []; + private bool $ignoreLexiconAliases = false; + private ?Preset $configLexiconPreset = null; public function __construct( protected IDBConnection $connection, @@ -150,6 +155,7 @@ class UserConfig implements IUserConfig { public function hasKey(string $userId, string $app, string $key, ?bool $lazy = false): bool { $this->assertParams($userId, $app, $key); $this->loadConfig($userId, $lazy); + $this->matchAndApplyLexiconDefinition($userId, $app, $key); if ($lazy === null) { $appCache = $this->getValues($userId, $app); @@ -178,6 +184,7 @@ class UserConfig implements IUserConfig { public function isSensitive(string $userId, string $app, string $key, ?bool $lazy = false): bool { $this->assertParams($userId, $app, $key); $this->loadConfig($userId, $lazy); + $this->matchAndApplyLexiconDefinition($userId, $app, $key); if (!isset($this->valueDetails[$userId][$app][$key])) { throw new UnknownKeyException('unknown config key'); @@ -201,6 +208,7 @@ class UserConfig implements IUserConfig { public function isIndexed(string $userId, string $app, string $key, ?bool $lazy = false): bool { $this->assertParams($userId, $app, $key); $this->loadConfig($userId, $lazy); + $this->matchAndApplyLexiconDefinition($userId, $app, $key); if (!isset($this->valueDetails[$userId][$app][$key])) { throw new UnknownKeyException('unknown config key'); @@ -222,6 +230,8 @@ class UserConfig implements IUserConfig { * @since 31.0.0 */ public function isLazy(string $userId, string $app, string $key): bool { + $this->matchAndApplyLexiconDefinition($userId, $app, $key); + // there is a huge probability the non-lazy config are already loaded // meaning that we can start by only checking if a current non-lazy key exists if ($this->hasKey($userId, $app, $key, false)) { @@ -349,6 +359,7 @@ class UserConfig implements IUserConfig { ?array $userIds = null, ): array { $this->assertParams('', $app, $key, allowEmptyUser: true); + $this->matchAndApplyLexiconDefinition('', $app, $key); $qb = $this->connection->getQueryBuilder(); $qb->select('userid', 'configvalue', 'type') @@ -464,6 +475,7 @@ class UserConfig implements IUserConfig { */ private function searchUsersByTypedValue(string $app, string $key, string|array $value, bool $caseInsensitive = false): Generator { $this->assertParams('', $app, $key, allowEmptyUser: true); + $this->matchAndApplyLexiconDefinition('', $app, $key); $qb = $this->connection->getQueryBuilder(); $qb->from('preferences'); @@ -541,6 +553,7 @@ class UserConfig implements IUserConfig { string $default = '', ?bool $lazy = false, ): string { + $this->matchAndApplyLexiconDefinition($userId, $app, $key); try { $lazy ??= $this->isLazy($userId, $app, $key); } catch (UnknownKeyException) { @@ -710,10 +723,18 @@ class UserConfig implements IUserConfig { ValueType $type, ): string { $this->assertParams($userId, $app, $key); - if (!$this->matchAndApplyLexiconDefinition($userId, $app, $key, $lazy, $type, default: $default)) { - // returns default if strictness of lexicon is set to WARNING (block and report) + $origKey = $key; + $matched = $this->matchAndApplyLexiconDefinition($userId, $app, $key, $lazy, $type, default: $default); + if ($default === null) { + // there is no logical reason for it to be null + throw new \Exception('default cannot be null'); + } + + // returns default if strictness of lexicon is set to WARNING (block and report) + if (!$matched) { return $default; } + $this->loadConfig($userId, $lazy); /** @@ -746,6 +767,15 @@ class UserConfig implements IUserConfig { } $this->decryptSensitiveValue($userId, $app, $key, $value); + + // in case the key was modified while running matchAndApplyLexiconDefinition() we are + // interested to check options in case a modification of the value is needed + // ie inverting value from previous key when using lexicon option RENAME_INVERT_BOOLEAN + if ($origKey !== $key && $type === ValueType::BOOL) { + $configManager = Server::get(ConfigManager::class); + $value = ($configManager->convertToBool($value, $this->getLexiconEntry($app, $key))) ? '1' : '0'; + } + return $value; } @@ -764,6 +794,7 @@ class UserConfig implements IUserConfig { public function getValueType(string $userId, string $app, string $key, ?bool $lazy = null): ValueType { $this->assertParams($userId, $app, $key); $this->loadConfig($userId, $lazy); + $this->matchAndApplyLexiconDefinition($userId, $app, $key); if (!isset($this->valueDetails[$userId][$app][$key]['type'])) { throw new UnknownKeyException('unknown config key'); @@ -788,6 +819,7 @@ class UserConfig implements IUserConfig { public function getValueFlags(string $userId, string $app, string $key, bool $lazy = false): int { $this->assertParams($userId, $app, $key); $this->loadConfig($userId, $lazy); + $this->matchAndApplyLexiconDefinition($userId, $app, $key); if (!isset($this->valueDetails[$userId][$app][$key])) { throw new UnknownKeyException('unknown config key'); @@ -1045,6 +1077,11 @@ class UserConfig implements IUserConfig { int $flags, ValueType $type, ): bool { + // Primary email addresses are always(!) expected to be lowercase + if ($app === 'settings' && $key === 'email') { + $value = strtolower($value); + } + $this->assertParams($userId, $app, $key); if (!$this->matchAndApplyLexiconDefinition($userId, $app, $key, $lazy, $type, $flags)) { // returns false as database is not updated @@ -1129,8 +1166,8 @@ class UserConfig implements IUserConfig { * we only accept a different type from the one stored in database * if the one stored in database is not-defined (VALUE_MIXED) */ - if ($currType !== ValueType::MIXED && - $currType !== $type) { + if ($currType !== ValueType::MIXED + && $currType !== $type) { try { $currTypeDef = $currType->getDefinition(); $typeDef = $type->getDefinition(); @@ -1197,8 +1234,8 @@ class UserConfig implements IUserConfig { public function updateType(string $userId, string $app, string $key, ValueType $type = ValueType::MIXED): bool { $this->assertParams($userId, $app, $key); $this->loadConfigAll($userId); - // confirm key exists - $this->isLazy($userId, $app, $key); + $this->matchAndApplyLexiconDefinition($userId, $app, $key); + $this->isLazy($userId, $app, $key); // confirm key exists $update = $this->connection->getQueryBuilder(); $update->update('preferences') @@ -1227,6 +1264,7 @@ class UserConfig implements IUserConfig { public function updateSensitive(string $userId, string $app, string $key, bool $sensitive): bool { $this->assertParams($userId, $app, $key); $this->loadConfigAll($userId); + $this->matchAndApplyLexiconDefinition($userId, $app, $key); try { if ($sensitive === $this->isSensitive($userId, $app, $key, null)) { @@ -1282,6 +1320,8 @@ class UserConfig implements IUserConfig { */ public function updateGlobalSensitive(string $app, string $key, bool $sensitive): void { $this->assertParams('', $app, $key, allowEmptyUser: true); + $this->matchAndApplyLexiconDefinition('', $app, $key); + foreach (array_keys($this->getValuesByUsers($app, $key)) as $userId) { try { $this->updateSensitive($userId, $app, $key, $sensitive); @@ -1311,6 +1351,7 @@ class UserConfig implements IUserConfig { public function updateIndexed(string $userId, string $app, string $key, bool $indexed): bool { $this->assertParams($userId, $app, $key); $this->loadConfigAll($userId); + $this->matchAndApplyLexiconDefinition($userId, $app, $key); try { if ($indexed === $this->isIndexed($userId, $app, $key, null)) { @@ -1366,6 +1407,8 @@ class UserConfig implements IUserConfig { */ public function updateGlobalIndexed(string $app, string $key, bool $indexed): void { $this->assertParams('', $app, $key, allowEmptyUser: true); + $this->matchAndApplyLexiconDefinition('', $app, $key); + foreach (array_keys($this->getValuesByUsers($app, $key)) as $userId) { try { $this->updateIndexed($userId, $app, $key, $indexed); @@ -1392,6 +1435,7 @@ class UserConfig implements IUserConfig { public function updateLazy(string $userId, string $app, string $key, bool $lazy): bool { $this->assertParams($userId, $app, $key); $this->loadConfigAll($userId); + $this->matchAndApplyLexiconDefinition($userId, $app, $key); try { if ($lazy === $this->isLazy($userId, $app, $key)) { @@ -1426,6 +1470,7 @@ class UserConfig implements IUserConfig { */ public function updateGlobalLazy(string $app, string $key, bool $lazy): void { $this->assertParams('', $app, $key, allowEmptyUser: true); + $this->matchAndApplyLexiconDefinition('', $app, $key); $update = $this->connection->getQueryBuilder(); $update->update('preferences') @@ -1451,6 +1496,8 @@ class UserConfig implements IUserConfig { public function getDetails(string $userId, string $app, string $key): array { $this->assertParams($userId, $app, $key); $this->loadConfigAll($userId); + $this->matchAndApplyLexiconDefinition($userId, $app, $key); + $lazy = $this->isLazy($userId, $app, $key); if ($lazy) { @@ -1498,6 +1545,8 @@ class UserConfig implements IUserConfig { */ public function deleteUserConfig(string $userId, string $app, string $key): void { $this->assertParams($userId, $app, $key); + $this->matchAndApplyLexiconDefinition($userId, $app, $key); + $qb = $this->connection->getQueryBuilder(); $qb->delete('preferences') ->where($qb->expr()->eq('userid', $qb->createNamedParameter($userId))) @@ -1520,6 +1569,8 @@ class UserConfig implements IUserConfig { */ public function deleteKey(string $app, string $key): void { $this->assertParams('', $app, $key, allowEmptyUser: true); + $this->matchAndApplyLexiconDefinition('', $app, $key); + $qb = $this->connection->getQueryBuilder(); $qb->delete('preferences') ->where($qb->expr()->eq('appid', $qb->createNamedParameter($app))) @@ -1538,6 +1589,7 @@ class UserConfig implements IUserConfig { */ public function deleteApp(string $app): void { $this->assertParams('', $app, allowEmptyUser: true); + $qb = $this->connection->getQueryBuilder(); $qb->delete('preferences') ->where($qb->expr()->eq('appid', $qb->createNamedParameter($app))); @@ -1583,7 +1635,8 @@ class UserConfig implements IUserConfig { */ public function clearCacheAll(): void { $this->lazyLoaded = $this->fastLoaded = []; - $this->lazyCache = $this->fastCache = $this->valueDetails = []; + $this->lazyCache = $this->fastCache = $this->valueDetails = $this->configLexiconDetails = []; + $this->configLexiconPreset = null; } /** @@ -1830,7 +1883,8 @@ class UserConfig implements IUserConfig { } /** - * match and apply current use of config values with defined lexicon + * Match and apply current use of config values with defined lexicon. + * Set $lazy to NULL only if only interested into checking that $key is alias. * * @throws UnknownKeyException * @throws TypeConflictException @@ -1839,18 +1893,28 @@ class UserConfig implements IUserConfig { private function matchAndApplyLexiconDefinition( string $userId, string $app, - string $key, - bool &$lazy, - ValueType &$type, + string &$key, + ?bool &$lazy = null, + ValueType &$type = ValueType::MIXED, int &$flags = 0, - string &$default = '', + ?string &$default = null, ): bool { $configDetails = $this->getConfigDetailsFromLexicon($app); + if (array_key_exists($key, $configDetails['aliases']) && !$this->ignoreLexiconAliases) { + // in case '$rename' is set in ConfigLexiconEntry, we use the new config key + $key = $configDetails['aliases'][$key]; + } + if (!array_key_exists($key, $configDetails['entries'])) { return $this->applyLexiconStrictness($configDetails['strictness'], 'The user config key ' . $app . '/' . $key . ' is not defined in the config lexicon'); } - /** @var ConfigLexiconEntry $configValue */ + // if lazy is NULL, we ignore all check on the type/lazyness/default from Lexicon + if ($lazy === null) { + return true; + } + + /** @var Entry $configValue */ $configValue = $configDetails['entries'][$key]; if ($type === ValueType::MIXED) { // we overwrite if value was requested as mixed @@ -1871,8 +1935,10 @@ class UserConfig implements IUserConfig { return true; } - // default from Lexicon got priority but it can still be overwritten by admin - $default = $this->getSystemDefault($app, $configValue) ?? $configValue->getDefault() ?? $default; + // only look for default if needed, default from Lexicon got priority if not overwritten by admin + if ($default !== null) { + $default = $this->getSystemDefault($app, $configValue) ?? $configValue->getDefault($this->getLexiconPreset()) ?? $default; + } // returning false will make get() returning $default and set() not changing value in database return !$enforcedValue; @@ -1889,7 +1955,7 @@ class UserConfig implements IUserConfig { * * The entry is converted to string to fit the expected type when managing default value */ - private function getSystemDefault(string $appId, ConfigLexiconEntry $configValue): ?string { + private function getSystemDefault(string $appId, Entry $configValue): ?string { $default = $this->config->getSystemValue('lexicon.default.userconfig', [])[$appId][$configValue->getKey()] ?? null; if ($default === null) { // no system default, using default default. @@ -1902,28 +1968,28 @@ class UserConfig implements IUserConfig { /** * manage ConfigLexicon behavior based on strictness set in IConfigLexicon * - * @see IConfigLexicon::getStrictness() - * @param ConfigLexiconStrictness|null $strictness + * @param Strictness|null $strictness * @param string $line * * @return bool TRUE if conflict can be fully ignored * @throws UnknownKeyException + *@see ILexicon::getStrictness() */ - private function applyLexiconStrictness(?ConfigLexiconStrictness $strictness, string $line = ''): bool { + private function applyLexiconStrictness(?Strictness $strictness, string $line = ''): bool { if ($strictness === null) { return true; } switch ($strictness) { - case ConfigLexiconStrictness::IGNORE: + case Strictness::IGNORE: return true; - case ConfigLexiconStrictness::NOTICE: + case Strictness::NOTICE: $this->logger->notice($line); return true; - case ConfigLexiconStrictness::WARNING: + case Strictness::WARNING: $this->logger->warning($line); return false; - case ConfigLexiconStrictness::EXCEPTION: + case Strictness::EXCEPTION: throw new UnknownKeyException($line); } @@ -1935,23 +2001,50 @@ class UserConfig implements IUserConfig { * * @param string $appId * - * @return array{entries: array<array-key, ConfigLexiconEntry>, strictness: ConfigLexiconStrictness} + * @return array{entries: array<string, Entry>, aliases: array<string, string>, strictness: Strictness} + *@internal + * */ - private function getConfigDetailsFromLexicon(string $appId): array { + public function getConfigDetailsFromLexicon(string $appId): array { if (!array_key_exists($appId, $this->configLexiconDetails)) { - $entries = []; + $entries = $aliases = []; $bootstrapCoordinator = \OCP\Server::get(Coordinator::class); $configLexicon = $bootstrapCoordinator->getRegistrationContext()?->getConfigLexicon($appId); foreach ($configLexicon?->getUserConfigs() ?? [] as $configEntry) { $entries[$configEntry->getKey()] = $configEntry; + if ($configEntry->getRename() !== null) { + $aliases[$configEntry->getRename()] = $configEntry->getKey(); + } } $this->configLexiconDetails[$appId] = [ 'entries' => $entries, - 'strictness' => $configLexicon?->getStrictness() ?? ConfigLexiconStrictness::IGNORE + 'aliases' => $aliases, + 'strictness' => $configLexicon?->getStrictness() ?? Strictness::IGNORE ]; } return $this->configLexiconDetails[$appId]; } + + private function getLexiconEntry(string $appId, string $key): ?Entry { + return $this->getConfigDetailsFromLexicon($appId)['entries'][$key] ?? null; + } + + /** + * if set to TRUE, ignore aliases defined in Config Lexicon during the use of the methods of this class + * + * @internal + */ + public function ignoreLexiconAliases(bool $ignore): void { + $this->ignoreLexiconAliases = $ignore; + } + + private function getLexiconPreset(): Preset { + if ($this->configLexiconPreset === null) { + $this->configLexiconPreset = Preset::tryFrom($this->config->getSystemValueInt(ConfigManager::PRESET_CONFIGKEY, 0)) ?? Preset::NONE; + } + + return $this->configLexiconPreset; + } } |