feat: Allow registering settings as Less config vars through Settings Extender (#3011)

Co-authored-by: Alexander Skvortsov <38059171+askvortsov1@users.noreply.github.com>
This commit is contained in:
Sami Mazouz 2021-11-01 15:41:19 +01:00 committed by GitHub
parent 62b92ba02e
commit e8ffdead39
8 changed files with 163 additions and 17 deletions

View File

@ -21,6 +21,7 @@ class Settings implements ExtenderInterface
{
private $settings = [];
private $defaults = [];
private $lessConfigs = [];
/**
* Serialize a setting value to the ForumSerializer attributes.
@ -61,6 +62,28 @@ class Settings implements ExtenderInterface
return $this;
}
/**
* Register a setting as a LESS configuration variable.
*
* @param string $configName: The name of the configuration variable, in hyphen case.
* @param string $key: The key of the setting.
* @param string|callable|null $callback: Optional callback to modify the value.
*
* The callback can be a closure or an invokable class, and should accept:
* - mixed $value: The value of the setting.
*
* The callable should return:
* - mixed $value: The modified value.
*
* @return self
*/
public function registerLessConfigVar(string $configName, string $key, $callback = null): self
{
$this->lessConfigs[$configName] = compact('key', 'callback');
return $this;
}
public function extend(Container $container, Extension $extension = null)
{
if (! empty($this->defaults)) {
@ -97,5 +120,19 @@ class Settings implements ExtenderInterface
}
);
}
if (! empty($this->lessConfigs)) {
$container->extend('flarum.less.config', function (array $existingConfig, Container $container) {
$config = $this->lessConfigs;
foreach ($config as $var => $data) {
if (isset($data['callback'])) {
$config[$var]['callback'] = ContainerUtil::wrapCallback($data['callback'], $container);
}
}
return array_merge($existingConfig, $config);
});
}
}
}

View File

@ -163,7 +163,8 @@ class ForumServiceProvider extends AbstractServiceProvider
$validator = new ValidateCustomLess(
$container->make('flarum.assets.forum'),
$container->make('flarum.locales'),
$container
$container,
$container->make('flarum.less.config')
);
$validator->whenSettingsSaved($event);
}
@ -175,7 +176,8 @@ class ForumServiceProvider extends AbstractServiceProvider
$validator = new ValidateCustomLess(
$container->make('flarum.assets.forum'),
$container->make('flarum.locales'),
$container
$container,
$container->make('flarum.less.config')
);
$validator->whenSettingsSaving($event);
}

View File

@ -43,20 +43,21 @@ class ValidateCustomLess
protected $container;
/**
* @param Assets $assets
* @param LocaleManager $locales
* @param Container $container
* @var array
*/
public function __construct(Assets $assets, LocaleManager $locales, Container $container)
protected $customLessSettings;
public function __construct(Assets $assets, LocaleManager $locales, Container $container, array $customLessSettings = [])
{
$this->assets = $assets;
$this->locales = $locales;
$this->container = $container;
$this->customLessSettings = $customLessSettings;
}
public function whenSettingsSaving(Saving $event)
{
if (! isset($event->settings['custom_less'])) {
if (! isset($event->settings['custom_less']) && ! $this->hasDirtyCustomLessSettings($event)) {
return;
}
@ -95,7 +96,7 @@ class ValidateCustomLess
public function whenSettingsSaved(Saved $event)
{
if (! isset($event->settings['custom_less'])) {
if (! isset($event->settings['custom_less']) && ! $this->hasDirtyCustomLessSettings($event)) {
return;
}
@ -105,4 +106,24 @@ class ValidateCustomLess
$this->assets->makeLocaleCss($locale)->flush();
}
}
/**
* @param Saved|Saving $event
* @return bool
*/
protected function hasDirtyCustomLessSettings($event): bool
{
if (empty($this->customLessSettings)) {
return false;
}
$dirtySettings = array_intersect(
array_keys($event->settings),
array_map(function ($setting) {
return $setting['key'];
}, $this->customLessSettings)
);
return ! empty($dirtySettings);
}
}

View File

@ -106,6 +106,29 @@ class FrontendServiceProvider extends AbstractServiceProvider
];
}
);
$this->container->singleton('flarum.less.config', function (Container $container) {
return [
'config-primary-color' => [
'key' => 'theme_primary_color',
],
'config-secondary-color' => [
'key' => 'theme_secondary_color',
],
'config-dark-mode' => [
'key' => 'theme_dark_mode',
'callback' => function ($value) {
return $value ? 'true' : 'false';
},
],
'config-colored-header' => [
'key' => 'theme_colored_header',
'callback' => function ($value) {
return $value ? 'true' : 'false';
},
],
];
});
}
/**
@ -132,17 +155,18 @@ class FrontendServiceProvider extends AbstractServiceProvider
private function addLessVariables(SourceCollector $sources)
{
$sources->addString(function () {
$vars = $this->container->make('flarum.less.config');
$settings = $this->container->make(SettingsRepositoryInterface::class);
$vars = [
'config-primary-color' => $settings->get('theme_primary_color', '#000'),
'config-secondary-color' => $settings->get('theme_secondary_color', '#000'),
'config-dark-mode' => $settings->get('theme_dark_mode') ? 'true' : 'false',
'config-colored-header' => $settings->get('theme_colored_header') ? 'true' : 'false'
];
return array_reduce(array_keys($vars), function ($string, $name) use ($vars, $settings) {
$var = $vars[$name];
$value = $settings->get($var['key'], $var['default'] ?? null);
return array_reduce(array_keys($vars), function ($string, $name) use ($vars) {
return $string."@$name: {$vars[$name]};";
if (isset($var['callback'])) {
$value = $var['callback']($value);
}
return $string."@$name: {$value};";
}, '');
});
}

View File

@ -39,6 +39,7 @@ class RecompileFrontendAssets
public function whenSettingsSaved(Saved $event)
{
// @deprecated 'theme_' check, to be removed in 2.0
if (preg_grep('/^theme_/i', array_keys($event->settings))) {
$this->flushCss();
}

View File

@ -22,7 +22,10 @@ class SettingsServiceProvider extends AbstractServiceProvider
public function register()
{
$this->container->singleton('flarum.settings.default', function () {
return new Collection();
return new Collection([
'theme_primary_color' => '#4D698E',
'theme_secondary_color' => '#4D698E',
]);
});
$this->container->singleton(SettingsRepositoryInterface::class, function (Container $container) {

View File

@ -0,0 +1,3 @@
body {
--custom-config-setting: @custom-config-setting;
}

View File

@ -220,6 +220,61 @@ class SettingsTest extends TestCase
$this->assertEquals(null, $value);
}
/**
* @test
*/
public function custom_less_var_does_not_work_by_default()
{
$this->extend(
(new Extend\Frontend('forum'))
->css(__DIR__.'/../../fixtures/less/config.less'),
);
$response = $this->send($this->request('GET', '/'));
$this->assertEquals(500, $response->getStatusCode());
}
/**
* @test
*/
public function custom_less_var_works_if_registered()
{
$this->extend(
(new Extend\Frontend('forum'))
->css(__DIR__.'/../../fixtures/less/config.less'),
(new Extend\Settings())
->registerLessConfigVar('custom-config-setting', 'custom-prefix.custom_setting')
);
$response = $this->send($this->request('GET', '/'));
$cssFilePath = $this->app()->getContainer()->make('filesystem')->disk('flarum-assets')->path('forum.css');
$this->assertStringContainsString('--custom-config-setting:customValue', file_get_contents($cssFilePath));
$this->assertEquals(200, $response->getStatusCode());
}
/**
* @test
*/
public function cant_save_setting_if_invalid_less_var()
{
$this->extend(
(new Extend\Settings())
->registerLessConfigVar('custom-config-setting2', 'custom-prefix.custom_setting2')
);
$response = $this->send($this->request('POST', '/api/settings', [
'authenticatedAs' => 1,
'json' => [
'custom-prefix.custom_setting2' => '@muralf'
],
]));
$this->assertEquals(422, $response->getStatusCode());
}
}
class CustomInvokableClass