Skip to content

Commit e22914b

Browse files
Merge pull request #53449 from nextcloud/feat/noid/preset-config
feat(lexicon): configurable presets
2 parents 672e4f5 + e64be71 commit e22914b

File tree

12 files changed

+256
-19
lines changed

12 files changed

+256
-19
lines changed

core/Command/Config/Preset.php

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
7+
* SPDX-License-Identifier: AGPL-3.0-or-later
8+
*/
9+
namespace OC\Core\Command\Config;
10+
11+
use NCU\Config\Lexicon\Preset as ConfigLexiconPreset;
12+
use OC\Config\ConfigManager;
13+
use OC\Core\Command\Base;
14+
use OCP\IConfig;
15+
use Symfony\Component\Console\Input\InputArgument;
16+
use Symfony\Component\Console\Input\InputInterface;
17+
use Symfony\Component\Console\Input\InputOption;
18+
use Symfony\Component\Console\Output\OutputInterface;
19+
20+
class Preset extends Base {
21+
public function __construct(
22+
private readonly IConfig $config,
23+
private readonly ConfigManager $configManager,
24+
) {
25+
parent::__construct();
26+
}
27+
28+
protected function configure() {
29+
parent::configure();
30+
$this->setName('config:preset')
31+
->setDescription('Select a config preset')
32+
->addArgument('preset', InputArgument::OPTIONAL, 'Preset to use for all unset config values', '')
33+
->addOption('list', '', InputOption::VALUE_NONE, 'display available preset');
34+
}
35+
36+
protected function execute(InputInterface $input, OutputInterface $output): int {
37+
if ($input->getOption('list')) {
38+
$this->getEnum('', $list);
39+
$this->writeArrayInOutputFormat($input, $output, $list);
40+
return self::SUCCESS;
41+
}
42+
43+
$presetArg = $input->getArgument('preset');
44+
if ($presetArg !== '') {
45+
$preset = $this->getEnum($presetArg, $list);
46+
if ($preset === null) {
47+
$output->writeln('<error>Invalid preset: ' . $presetArg . '</error>');
48+
$output->writeln('Available presets: ' . implode(', ', $list));
49+
return self::INVALID;
50+
}
51+
52+
$this->configManager->setLexiconPreset($preset);
53+
}
54+
55+
$current = ConfigLexiconPreset::tryFrom($this->config->getSystemValueInt(ConfigManager::PRESET_CONFIGKEY, 0)) ?? ConfigLexiconPreset::NONE;
56+
$this->writeArrayInOutputFormat($input, $output, [$current->name], 'current preset: ');
57+
return self::SUCCESS;
58+
}
59+
60+
private function getEnum(string $name, ?array &$list = null): ?ConfigLexiconPreset {
61+
$list = [];
62+
foreach (ConfigLexiconPreset::cases() as $case) {
63+
$list[] = $case->name;
64+
if (strtolower($case->name) === strtolower($name)) {
65+
return $case;
66+
}
67+
}
68+
69+
return null;
70+
}
71+
}

core/register_command.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
use OC\Core\Command\Config\App\SetConfig;
2828
use OC\Core\Command\Config\Import;
2929
use OC\Core\Command\Config\ListConfigs;
30+
use OC\Core\Command\Config\Preset;
3031
use OC\Core\Command\Db\AddMissingColumns;
3132
use OC\Core\Command\Db\AddMissingIndices;
3233
use OC\Core\Command\Db\AddMissingPrimaryKeys;
@@ -149,6 +150,7 @@
149150
$application->add(Server::get(SetConfig::class));
150151
$application->add(Server::get(Import::class));
151152
$application->add(Server::get(ListConfigs::class));
153+
$application->add(Server::get(Preset::class));
152154
$application->add(Server::get(Command\Config\System\DeleteConfig::class));
153155
$application->add(Server::get(Command\Config\System\GetConfig::class));
154156
$application->add(Server::get(Command\Config\System\SetConfig::class));

lib/composer/composer/autoload_classmap.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
'NCU\\Config\\Lexicon\\ConfigLexiconEntry' => $baseDir . '/lib/unstable/Config/Lexicon/ConfigLexiconEntry.php',
1515
'NCU\\Config\\Lexicon\\ConfigLexiconStrictness' => $baseDir . '/lib/unstable/Config/Lexicon/ConfigLexiconStrictness.php',
1616
'NCU\\Config\\Lexicon\\IConfigLexicon' => $baseDir . '/lib/unstable/Config/Lexicon/IConfigLexicon.php',
17+
'NCU\\Config\\Lexicon\\Preset' => $baseDir . '/lib/unstable/Config/Lexicon/Preset.php',
1718
'NCU\\Config\\ValueType' => $baseDir . '/lib/unstable/Config/ValueType.php',
1819
'NCU\\Federation\\ISignedCloudFederationProvider' => $baseDir . '/lib/unstable/Federation/ISignedCloudFederationProvider.php',
1920
'NCU\\Security\\Signature\\Enum\\DigestAlgorithm' => $baseDir . '/lib/unstable/Security/Signature/Enum/DigestAlgorithm.php',
@@ -1250,6 +1251,7 @@
12501251
'OC\\Core\\Command\\Config\\App\\SetConfig' => $baseDir . '/core/Command/Config/App/SetConfig.php',
12511252
'OC\\Core\\Command\\Config\\Import' => $baseDir . '/core/Command/Config/Import.php',
12521253
'OC\\Core\\Command\\Config\\ListConfigs' => $baseDir . '/core/Command/Config/ListConfigs.php',
1254+
'OC\\Core\\Command\\Config\\Preset' => $baseDir . '/core/Command/Config/Preset.php',
12531255
'OC\\Core\\Command\\Config\\System\\Base' => $baseDir . '/core/Command/Config/System/Base.php',
12541256
'OC\\Core\\Command\\Config\\System\\CastHelper' => $baseDir . '/core/Command/Config/System/CastHelper.php',
12551257
'OC\\Core\\Command\\Config\\System\\DeleteConfig' => $baseDir . '/core/Command/Config/System/DeleteConfig.php',

lib/composer/composer/autoload_static.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
5555
'NCU\\Config\\Lexicon\\ConfigLexiconEntry' => __DIR__ . '/../../..' . '/lib/unstable/Config/Lexicon/ConfigLexiconEntry.php',
5656
'NCU\\Config\\Lexicon\\ConfigLexiconStrictness' => __DIR__ . '/../../..' . '/lib/unstable/Config/Lexicon/ConfigLexiconStrictness.php',
5757
'NCU\\Config\\Lexicon\\IConfigLexicon' => __DIR__ . '/../../..' . '/lib/unstable/Config/Lexicon/IConfigLexicon.php',
58+
'NCU\\Config\\Lexicon\\Preset' => __DIR__ . '/../../..' . '/lib/unstable/Config/Lexicon/Preset.php',
5859
'NCU\\Config\\ValueType' => __DIR__ . '/../../..' . '/lib/unstable/Config/ValueType.php',
5960
'NCU\\Federation\\ISignedCloudFederationProvider' => __DIR__ . '/../../..' . '/lib/unstable/Federation/ISignedCloudFederationProvider.php',
6061
'NCU\\Security\\Signature\\Enum\\DigestAlgorithm' => __DIR__ . '/../../..' . '/lib/unstable/Security/Signature/Enum/DigestAlgorithm.php',
@@ -1291,6 +1292,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
12911292
'OC\\Core\\Command\\Config\\App\\SetConfig' => __DIR__ . '/../../..' . '/core/Command/Config/App/SetConfig.php',
12921293
'OC\\Core\\Command\\Config\\Import' => __DIR__ . '/../../..' . '/core/Command/Config/Import.php',
12931294
'OC\\Core\\Command\\Config\\ListConfigs' => __DIR__ . '/../../..' . '/core/Command/Config/ListConfigs.php',
1295+
'OC\\Core\\Command\\Config\\Preset' => __DIR__ . '/../../..' . '/core/Command/Config/Preset.php',
12941296
'OC\\Core\\Command\\Config\\System\\Base' => __DIR__ . '/../../..' . '/core/Command/Config/System/Base.php',
12951297
'OC\\Core\\Command\\Config\\System\\CastHelper' => __DIR__ . '/../../..' . '/core/Command/Config/System/CastHelper.php',
12961298
'OC\\Core\\Command\\Config\\System\\DeleteConfig' => __DIR__ . '/../../..' . '/core/Command/Config/System/DeleteConfig.php',

lib/private/AppConfig.php

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use NCU\Config\Lexicon\ConfigLexiconEntry;
1515
use NCU\Config\Lexicon\ConfigLexiconStrictness;
1616
use NCU\Config\Lexicon\IConfigLexicon;
17+
use NCU\Config\Lexicon\Preset;
1718
use OC\AppFramework\Bootstrap\Coordinator;
1819
use OC\Config\ConfigManager;
1920
use OCP\DB\Exception as DBException;
@@ -64,12 +65,13 @@ class AppConfig implements IAppConfig {
6465
/** @var array<string, array{entries: array<string, ConfigLexiconEntry>, aliases: array<string, string>, strictness: ConfigLexiconStrictness}> ['app_id' => ['strictness' => ConfigLexiconStrictness, 'entries' => ['config_key' => ConfigLexiconEntry[]]] */
6566
private array $configLexiconDetails = [];
6667
private bool $ignoreLexiconAliases = false;
67-
68+
private ?Preset $configLexiconPreset = null;
6869
/** @var ?array<string, string> */
6970
private ?array $appVersionsCache = null;
7071

7172
public function __construct(
7273
protected IDBConnection $connection,
74+
protected IConfig $config,
7375
protected LoggerInterface $logger,
7476
protected ICrypto $crypto,
7577
) {
@@ -438,9 +440,17 @@ private function getTypedValue(
438440
): string {
439441
$this->assertParams($app, $key, valueType: $type);
440442
$origKey = $key;
441-
if (!$this->matchAndApplyLexiconDefinition($app, $key, $lazy, $type, $default)) {
442-
return $default; // returns default if strictness of lexicon is set to WARNING (block and report)
443+
$matched = $this->matchAndApplyLexiconDefinition($app, $key, $lazy, $type, $default);
444+
if ($default === null) {
445+
// there is no logical reason for it to be null
446+
throw new \Exception('default cannot be null');
447+
}
448+
449+
// returns default if strictness of lexicon is set to WARNING (block and report)
450+
if (!$matched) {
451+
return $default;
443452
}
453+
444454
$this->loadConfig($app, $lazy);
445455

446456
/**
@@ -1146,7 +1156,8 @@ public function deleteApp(string $app): void {
11461156
*/
11471157
public function clearCache(bool $reload = false): void {
11481158
$this->lazyLoaded = $this->fastLoaded = false;
1149-
$this->lazyCache = $this->fastCache = $this->valueTypes = [];
1159+
$this->lazyCache = $this->fastCache = $this->valueTypes = $this->configLexiconDetails = [];
1160+
$this->configLexiconPreset = null;
11501161

11511162
if (!$reload) {
11521163
return;
@@ -1592,7 +1603,7 @@ private function matchAndApplyLexiconDefinition(
15921603
string &$key,
15931604
?bool &$lazy = null,
15941605
int &$type = self::VALUE_MIXED,
1595-
string &$default = '',
1606+
?string &$default = null,
15961607
): bool {
15971608
if (in_array($key,
15981609
[
@@ -1629,7 +1640,11 @@ private function matchAndApplyLexiconDefinition(
16291640
}
16301641

16311642
$lazy = $configValue->isLazy();
1632-
$default = $configValue->getDefault() ?? $default; // default from Lexicon got priority
1643+
// only look for default if needed, default from Lexicon got priority
1644+
if ($default !== null) {
1645+
$default = $configValue->getDefault($this->getLexiconPreset()) ?? $default;
1646+
}
1647+
16331648
if ($configValue->isFlagged(self::FLAG_SENSITIVE)) {
16341649
$type |= self::VALUE_SENSITIVE;
16351650
}
@@ -1715,6 +1730,14 @@ public function ignoreLexiconAliases(bool $ignore): void {
17151730
$this->ignoreLexiconAliases = $ignore;
17161731
}
17171732

1733+
private function getLexiconPreset(): Preset {
1734+
if ($this->configLexiconPreset === null) {
1735+
$this->configLexiconPreset = Preset::tryFrom($this->config->getSystemValueInt(ConfigManager::PRESET_CONFIGKEY, 0)) ?? Preset::NONE;
1736+
}
1737+
1738+
return $this->configLexiconPreset;
1739+
}
1740+
17181741
/**
17191742
* Returns the installed versions of all apps
17201743
*

lib/private/Config/ConfigManager.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@
1212
use NCU\Config\Exceptions\TypeConflictException;
1313
use NCU\Config\IUserConfig;
1414
use NCU\Config\Lexicon\ConfigLexiconEntry;
15+
use NCU\Config\Lexicon\Preset;
1516
use NCU\Config\ValueType;
1617
use OC\AppConfig;
1718
use OCP\App\IAppManager;
1819
use OCP\IAppConfig;
20+
use OCP\IConfig;
1921
use OCP\Server;
2022
use Psr\Log\LoggerInterface;
2123

@@ -25,12 +27,16 @@
2527
* @since 32.0.0
2628
*/
2729
class ConfigManager {
30+
/** @since 32.0.0 */
31+
public const PRESET_CONFIGKEY = 'config_preset';
32+
2833
/** @var AppConfig|null $appConfig */
2934
private ?IAppConfig $appConfig = null;
3035
/** @var UserConfig|null $userConfig */
3136
private ?IUserConfig $userConfig = null;
3237

3338
public function __construct(
39+
private readonly IConfig $config,
3440
private readonly LoggerInterface $logger,
3541
) {
3642
}
@@ -74,6 +80,17 @@ public function migrateConfigLexiconKeys(?string $appId = null): void {
7480
$this->userConfig->ignoreLexiconAliases(false);
7581
}
7682

83+
/**
84+
* store in config.php the new preset
85+
* refresh cached preset
86+
*/
87+
public function setLexiconPreset(Preset $preset): void {
88+
$this->config->setSystemValue(self::PRESET_CONFIGKEY, $preset->value);
89+
$this->loadConfigServices();
90+
$this->appConfig->clearCache();
91+
$this->userConfig->clearCacheAll();
92+
}
93+
7794
/**
7895
* config services cannot be load at __construct() or install will fail
7996
*/

lib/private/Config/UserConfig.php

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use NCU\Config\IUserConfig;
1818
use NCU\Config\Lexicon\ConfigLexiconEntry;
1919
use NCU\Config\Lexicon\ConfigLexiconStrictness;
20+
use NCU\Config\Lexicon\Preset;
2021
use NCU\Config\ValueType;
2122
use OC\AppFramework\Bootstrap\Coordinator;
2223
use OCP\DB\Exception as DBException;
@@ -66,6 +67,7 @@ class UserConfig implements IUserConfig {
6667
/** @var array<string, array{entries: array<string, ConfigLexiconEntry>, aliases: array<string, string>, strictness: ConfigLexiconStrictness}> ['app_id' => ['strictness' => ConfigLexiconStrictness, 'entries' => ['config_key' => ConfigLexiconEntry[]]] */
6768
private array $configLexiconDetails = [];
6869
private bool $ignoreLexiconAliases = false;
70+
private ?Preset $configLexiconPreset = null;
6971

7072
public function __construct(
7173
protected IDBConnection $connection,
@@ -721,10 +723,17 @@ private function getTypedValue(
721723
): string {
722724
$this->assertParams($userId, $app, $key);
723725
$origKey = $key;
724-
if (!$this->matchAndApplyLexiconDefinition($userId, $app, $key, $lazy, $type, default: $default)) {
725-
// returns default if strictness of lexicon is set to WARNING (block and report)
726+
$matched = $this->matchAndApplyLexiconDefinition($userId, $app, $key, $lazy, $type, default: $default);
727+
if ($default === null) {
728+
// there is no logical reason for it to be null
729+
throw new \Exception('default cannot be null');
730+
}
731+
732+
// returns default if strictness of lexicon is set to WARNING (block and report)
733+
if (!$matched) {
726734
return $default;
727735
}
736+
728737
$this->loadConfig($userId, $lazy);
729738

730739
/**
@@ -1625,7 +1634,8 @@ public function clearCache(string $userId, bool $reload = false): void {
16251634
*/
16261635
public function clearCacheAll(): void {
16271636
$this->lazyLoaded = $this->fastLoaded = [];
1628-
$this->lazyCache = $this->fastCache = $this->valueDetails = [];
1637+
$this->lazyCache = $this->fastCache = $this->valueDetails = $this->configLexiconDetails = [];
1638+
$this->configLexiconPreset = null;
16291639
}
16301640

16311641
/**
@@ -1886,7 +1896,7 @@ private function matchAndApplyLexiconDefinition(
18861896
?bool &$lazy = null,
18871897
ValueType &$type = ValueType::MIXED,
18881898
int &$flags = 0,
1889-
string &$default = '',
1899+
?string &$default = null,
18901900
): bool {
18911901
$configDetails = $this->getConfigDetailsFromLexicon($app);
18921902
if (array_key_exists($key, $configDetails['aliases']) && !$this->ignoreLexiconAliases) {
@@ -1924,8 +1934,10 @@ private function matchAndApplyLexiconDefinition(
19241934
return true;
19251935
}
19261936

1927-
// default from Lexicon got priority but it can still be overwritten by admin
1928-
$default = $this->getSystemDefault($app, $configValue) ?? $configValue->getDefault() ?? $default;
1937+
// only look for default if needed, default from Lexicon got priority if not overwritten by admin
1938+
if ($default !== null) {
1939+
$default = $this->getSystemDefault($app, $configValue) ?? $configValue->getDefault($this->getLexiconPreset()) ?? $default;
1940+
}
19291941

19301942
// returning false will make get() returning $default and set() not changing value in database
19311943
return !$enforcedValue;
@@ -2025,4 +2037,12 @@ private function getLexiconEntry(string $appId, string $key): ?ConfigLexiconEntr
20252037
public function ignoreLexiconAliases(bool $ignore): void {
20262038
$this->ignoreLexiconAliases = $ignore;
20272039
}
2040+
2041+
private function getLexiconPreset(): Preset {
2042+
if ($this->configLexiconPreset === null) {
2043+
$this->configLexiconPreset = Preset::tryFrom($this->config->getSystemValueInt(ConfigManager::PRESET_CONFIGKEY, 0)) ?? Preset::NONE;
2044+
}
2045+
2046+
return $this->configLexiconPreset;
2047+
}
20282048
}

0 commit comments

Comments
 (0)