Skip to content

Commit 859f630

Browse files
committed
feat(lexicon): preset()
Signed-off-by: Maxence Lange <[email protected]>
1 parent dc910cf commit 859f630

File tree

12 files changed

+216
-8
lines changed

12 files changed

+216
-8
lines changed

core/Command/Config/Preset.php

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

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
@@ -12,6 +12,7 @@
1212
'NCU\\Config\\Exceptions\\UnknownKeyException' => $baseDir . '/lib/unstable/Config/Exceptions/UnknownKeyException.php',
1313
'NCU\\Config\\IUserConfig' => $baseDir . '/lib/unstable/Config/IUserConfig.php',
1414
'NCU\\Config\\Lexicon\\ConfigLexiconEntry' => $baseDir . '/lib/unstable/Config/Lexicon/ConfigLexiconEntry.php',
15+
'NCU\\Config\\Lexicon\\ConfigLexiconPreset' => $baseDir . '/lib/unstable/Config/Lexicon/ConfigLexiconPreset.php',
1516
'NCU\\Config\\Lexicon\\ConfigLexiconStrictness' => $baseDir . '/lib/unstable/Config/Lexicon/ConfigLexiconStrictness.php',
1617
'NCU\\Config\\Lexicon\\IConfigLexicon' => $baseDir . '/lib/unstable/Config/Lexicon/IConfigLexicon.php',
1718
'NCU\\Config\\ValueType' => $baseDir . '/lib/unstable/Config/ValueType.php',
@@ -1244,6 +1245,7 @@
12441245
'OC\\Core\\Command\\Config\\App\\SetConfig' => $baseDir . '/core/Command/Config/App/SetConfig.php',
12451246
'OC\\Core\\Command\\Config\\Import' => $baseDir . '/core/Command/Config/Import.php',
12461247
'OC\\Core\\Command\\Config\\ListConfigs' => $baseDir . '/core/Command/Config/ListConfigs.php',
1248+
'OC\\Core\\Command\\Config\\Preset' => $baseDir . '/core/Command/Config/Preset.php',
12471249
'OC\\Core\\Command\\Config\\System\\Base' => $baseDir . '/core/Command/Config/System/Base.php',
12481250
'OC\\Core\\Command\\Config\\System\\CastHelper' => $baseDir . '/core/Command/Config/System/CastHelper.php',
12491251
'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
@@ -53,6 +53,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
5353
'NCU\\Config\\Exceptions\\UnknownKeyException' => __DIR__ . '/../../..' . '/lib/unstable/Config/Exceptions/UnknownKeyException.php',
5454
'NCU\\Config\\IUserConfig' => __DIR__ . '/../../..' . '/lib/unstable/Config/IUserConfig.php',
5555
'NCU\\Config\\Lexicon\\ConfigLexiconEntry' => __DIR__ . '/../../..' . '/lib/unstable/Config/Lexicon/ConfigLexiconEntry.php',
56+
'NCU\\Config\\Lexicon\\ConfigLexiconPreset' => __DIR__ . '/../../..' . '/lib/unstable/Config/Lexicon/ConfigLexiconPreset.php',
5657
'NCU\\Config\\Lexicon\\ConfigLexiconStrictness' => __DIR__ . '/../../..' . '/lib/unstable/Config/Lexicon/ConfigLexiconStrictness.php',
5758
'NCU\\Config\\Lexicon\\IConfigLexicon' => __DIR__ . '/../../..' . '/lib/unstable/Config/Lexicon/IConfigLexicon.php',
5859
'NCU\\Config\\ValueType' => __DIR__ . '/../../..' . '/lib/unstable/Config/ValueType.php',
@@ -1285,6 +1286,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
12851286
'OC\\Core\\Command\\Config\\App\\SetConfig' => __DIR__ . '/../../..' . '/core/Command/Config/App/SetConfig.php',
12861287
'OC\\Core\\Command\\Config\\Import' => __DIR__ . '/../../..' . '/core/Command/Config/Import.php',
12871288
'OC\\Core\\Command\\Config\\ListConfigs' => __DIR__ . '/../../..' . '/core/Command/Config/ListConfigs.php',
1289+
'OC\\Core\\Command\\Config\\Preset' => __DIR__ . '/../../..' . '/core/Command/Config/Preset.php',
12881290
'OC\\Core\\Command\\Config\\System\\Base' => __DIR__ . '/../../..' . '/core/Command/Config/System/Base.php',
12891291
'OC\\Core\\Command\\Config\\System\\CastHelper' => __DIR__ . '/../../..' . '/core/Command/Config/System/CastHelper.php',
12901292
'OC\\Core\\Command\\Config\\System\\DeleteConfig' => __DIR__ . '/../../..' . '/core/Command/Config/System/DeleteConfig.php',

lib/private/AppConfig.php

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use InvalidArgumentException;
1313
use JsonException;
1414
use NCU\Config\Lexicon\ConfigLexiconEntry;
15+
use NCU\Config\Lexicon\ConfigLexiconPreset;
1516
use NCU\Config\Lexicon\ConfigLexiconStrictness;
1617
use NCU\Config\Lexicon\IConfigLexicon;
1718
use OC\AppFramework\Bootstrap\Coordinator;
@@ -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 ?ConfigLexiconPreset $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
) {
@@ -1147,6 +1149,8 @@ public function deleteApp(string $app): void {
11471149
public function clearCache(bool $reload = false): void {
11481150
$this->lazyLoaded = $this->fastLoaded = false;
11491151
$this->lazyCache = $this->fastCache = $this->valueTypes = [];
1152+
$this->configLexiconDetails = [];
1153+
$this->configLexiconPreset = null;
11501154

11511155
if (!$reload) {
11521156
return;
@@ -1629,7 +1633,7 @@ private function matchAndApplyLexiconDefinition(
16291633
}
16301634

16311635
$lazy = $configValue->isLazy();
1632-
$default = $configValue->getDefault() ?? $default; // default from Lexicon got priority
1636+
$default = $configValue->getDefault($this->getLexiconPreset()) ?? $default; // default from Lexicon got priority
16331637
if ($configValue->isFlagged(self::FLAG_SENSITIVE)) {
16341638
$type |= self::VALUE_SENSITIVE;
16351639
}
@@ -1715,6 +1719,14 @@ public function ignoreLexiconAliases(bool $ignore): void {
17151719
$this->ignoreLexiconAliases = $ignore;
17161720
}
17171721

1722+
private function getLexiconPreset(): ConfigLexiconPreset {
1723+
if ($this->configLexiconPreset === null) {
1724+
$this->configLexiconPreset = ConfigLexiconPreset::tryFrom($this->config->getSystemValueInt(ConfigManager::PRESET_CONFIGKEY, 0)) ?? ConfigLexiconPreset::NONE;
1725+
}
1726+
1727+
return $this->configLexiconPreset;
1728+
}
1729+
17181730
/**
17191731
* Returns the installed versions of all apps
17201732
*

lib/private/Config/ConfigManager.php

Lines changed: 16 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\ConfigLexiconPreset;
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,16 @@ 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(ConfigLexiconPreset $preset): void {
88+
$this->config->setSystemValue(self::PRESET_CONFIGKEY, $preset->value);
89+
$this->appConfig->clearCache();
90+
$this->userConfig->clearCacheAll();
91+
}
92+
7793
/**
7894
* config services cannot be load at __construct() or install will fail
7995
*/

lib/private/Config/UserConfig.php

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use NCU\Config\Exceptions\UnknownKeyException;
1717
use NCU\Config\IUserConfig;
1818
use NCU\Config\Lexicon\ConfigLexiconEntry;
19+
use NCU\Config\Lexicon\ConfigLexiconPreset;
1920
use NCU\Config\Lexicon\ConfigLexiconStrictness;
2021
use NCU\Config\ValueType;
2122
use OC\AppFramework\Bootstrap\Coordinator;
@@ -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 ?ConfigLexiconPreset $configLexiconPreset = null;
6971

7072
public function __construct(
7173
protected IDBConnection $connection,
@@ -1626,6 +1628,8 @@ public function clearCache(string $userId, bool $reload = false): void {
16261628
public function clearCacheAll(): void {
16271629
$this->lazyLoaded = $this->fastLoaded = [];
16281630
$this->lazyCache = $this->fastCache = $this->valueDetails = [];
1631+
$this->configLexiconDetails = [];
1632+
$this->configLexiconPreset = null;
16291633
}
16301634

16311635
/**
@@ -1925,7 +1929,7 @@ private function matchAndApplyLexiconDefinition(
19251929
}
19261930

19271931
// default from Lexicon got priority but it can still be overwritten by admin
1928-
$default = $this->getSystemDefault($app, $configValue) ?? $configValue->getDefault() ?? $default;
1932+
$default = $this->getSystemDefault($app, $configValue) ?? $configValue->getDefault($this->getLexiconPreset()) ?? $default;
19291933

19301934
// returning false will make get() returning $default and set() not changing value in database
19311935
return !$enforcedValue;
@@ -2025,4 +2029,12 @@ private function getLexiconEntry(string $appId, string $key): ?ConfigLexiconEntr
20252029
public function ignoreLexiconAliases(bool $ignore): void {
20262030
$this->ignoreLexiconAliases = $ignore;
20272031
}
2032+
2033+
private function getLexiconPreset(): ConfigLexiconPreset {
2034+
if ($this->configLexiconPreset === null) {
2035+
$this->configLexiconPreset = ConfigLexiconPreset::tryFrom($this->config->getSystemValueInt(ConfigManager::PRESET_CONFIGKEY, 0)) ?? ConfigLexiconPreset::NONE;
2036+
}
2037+
2038+
return $this->configLexiconPreset;
2039+
}
20282040
}

lib/unstable/Config/Lexicon/ConfigLexiconEntry.php

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
namespace NCU\Config\Lexicon;
1010

11+
use Closure;
1112
use NCU\Config\ValueType;
1213

1314
/**
@@ -24,7 +25,7 @@ class ConfigLexiconEntry {
2425
private ?string $default = null;
2526

2627
/**
27-
* @param string $key config key
28+
* @param string $key config key, can only contain alphanumerical chars and -._
2829
* @param ValueType $type type of config value
2930
* @param string $definition optional description of config key available when using occ command
3031
* @param bool $lazy set config value as lazy
@@ -39,14 +40,19 @@ class ConfigLexiconEntry {
3940
public function __construct(
4041
private readonly string $key,
4142
private readonly ValueType $type,
42-
private null|string|int|float|bool|array $defaultRaw = null,
43+
private null|string|int|float|bool|array|Closure $defaultRaw = null,
4344
string $definition = '',
4445
private readonly bool $lazy = false,
4546
private readonly int $flags = 0,
4647
private readonly bool $deprecated = false,
4748
private readonly ?string $rename = null,
4849
private readonly int $options = 0,
4950
) {
51+
// key can only contain alphanumeric chars, underscore "_" , dash "-" and dot "."
52+
if (preg_match('/[^[:alnum:]_]/', $key)) {
53+
throw new \Exception('invalid config key');
54+
}
55+
5056
/** @psalm-suppress UndefinedClass */
5157
if (\OC::$CLI) { // only store definition if ran from CLI
5258
$this->definition = $definition;
@@ -124,12 +130,18 @@ private function convertFromArray(array $default): string {
124130
* @return string|null NULL if no default is set
125131
* @experimental 31.0.0
126132
*/
127-
public function getDefault(): ?string {
133+
public function getDefault(ConfigLexiconPreset $preset): ?string {
128134
if ($this->defaultRaw === null) {
129135
return null;
130136
}
131137

138+
if ($this->defaultRaw instanceof Closure) {
139+
/** @psalm-suppress MixedAssignment we expect closure to returns string|int|float|bool|array */
140+
$this->defaultRaw = ($this->defaultRaw)($preset);
141+
}
142+
132143
if ($this->default === null) {
144+
/** @psalm-suppress MixedArgument closure should be managed previously */
133145
$this->default = $this->convertToString($this->defaultRaw);
134146
}
135147

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
6+
* SPDX-License-Identifier: AGPL-3.0-only
7+
*/
8+
9+
namespace NCU\Config\Lexicon;
10+
11+
/**
12+
* list of preset to handle the default behavior of the instance
13+
*
14+
* @see ConfigLexiconEntry::preset
15+
*
16+
* - **ConfigLexiconPreset::LARGE** - Large size organisation (> 50k accounts)
17+
* - **ConfigLexiconPreset::MEDIUM** - Medium size organisation (> 100 accounts)
18+
* - **ConfigLexiconPreset::SMALL** - Small size organisation (< 100 accounts)
19+
* - **ConfigLexiconPreset::SHARED** - Shared hosting
20+
* - **ConfigLexiconPreset::EDUCATION** - School/University
21+
* - **ConfigLexiconPreset::CLUB** - Club/Association
22+
* - **ConfigLexiconPreset::FAMILY** - Family
23+
* - **ConfigLexiconPreset::PRIVATE** - Private
24+
*
25+
* @experimental 32.0.0
26+
*/
27+
enum ConfigLexiconPreset: int {
28+
/** @experimental 32.0.0 */
29+
case LARGE = 8;
30+
/** @experimental 32.0.0 */
31+
case MEDIUM = 7;
32+
/** @experimental 32.0.0 */
33+
case SMALL = 6;
34+
/** @experimental 32.0.0 */
35+
case SHARED = 5;
36+
/** @experimental 32.0.0 */
37+
case EDUCATION = 4;
38+
/** @experimental 32.0.0 */
39+
case CLUB = 3;
40+
/** @experimental 32.0.0 */
41+
case FAMILY = 2;
42+
/** @experimental 32.0.0 */
43+
case PRIVATE = 1;
44+
/** @experimental 32.0.0 */
45+
case NONE = 0;
46+
}

tests/lib/AppConfigTest.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use OCP\Exceptions\AppConfigTypeConflictException;
1313
use OCP\Exceptions\AppConfigUnknownKeyException;
1414
use OCP\IAppConfig;
15+
use OCP\IConfig;
1516
use OCP\IDBConnection;
1617
use OCP\Security\ICrypto;
1718
use OCP\Server;
@@ -27,6 +28,7 @@
2728
class AppConfigTest extends TestCase {
2829
protected IAppConfig $appConfig;
2930
protected IDBConnection $connection;
31+
private IConfig $config;
3032
private LoggerInterface $logger;
3133
private ICrypto $crypto;
3234

@@ -89,6 +91,7 @@ protected function setUp(): void {
8991
parent::setUp();
9092

9193
$this->connection = Server::get(IDBConnection::class);
94+
$this->config = Server::get(IConfig::class);
9295
$this->logger = Server::get(LoggerInterface::class);
9396
$this->crypto = Server::get(ICrypto::class);
9497

@@ -179,6 +182,7 @@ private function generateAppConfig(bool $preLoading = true): IAppConfig {
179182
/** @var AppConfig $config */
180183
$config = new AppConfig(
181184
$this->connection,
185+
$this->config,
182186
$this->logger,
183187
$this->crypto,
184188
);

0 commit comments

Comments
 (0)