diff --git a/framework/core/src/Extend/Theme.php b/framework/core/src/Extend/Theme.php index 26a860c4e..0d332b862 100644 --- a/framework/core/src/Extend/Theme.php +++ b/framework/core/src/Extend/Theme.php @@ -19,6 +19,7 @@ class Theme implements ExtenderInterface private $lessImportOverrides = []; private $fileSourceOverrides = []; private $customFunctions = []; + private $lessVariables = []; /** * This can be used to override LESS files that are imported within the code. @@ -100,12 +101,51 @@ class Theme implements ExtenderInterface return $this; } + /** + * Defines a new Less variable to be accessible in all Less files. + * + * If you want to expose a setting from your database to Less, you should use + * the `registerLessConfigVar` extender from `Extend\Settings` instead. + * + * Please note the value returned from the callable will be inserted directly + * into the Less source. If it is unsafe in some way (e.g., contains a + * semi-colon), this will result in potential security issues with your + * stylesheet. + * + * Likewise, if you need your variable to be a string, you should surround it + * with quotes yourself. + * + * ```php + * (new Extend\Theme()) + * ->addCustomLessVariable('my-extension__asset_path', function () { + * $url = resolve(UrlGenerator::class); + * $assetUrl = $url->to('forum')->base().'/assets/extensions/my-extension/my-asset.jpg'; + * return "\"$assetUrl\""; + * }) + * ``` + * + * @param string $variableName Name of the variable identifier. + * @param callable $value The PHP function to run, which returns the value for the variable. + * + * @return self + */ + public function addCustomLessVariable(string $variableName, callable $value): self + { + $this->lessVariables[$variableName] = $value; + + return $this; + } + public function extend(Container $container, Extension $extension = null) { $container->extend('flarum.frontend.custom_less_functions', function (array $customFunctions) { return array_merge($customFunctions, $this->customFunctions); }); + $container->extend('flarum.less.custom_variables', function (array $lessVariables) { + return array_merge($this->lessVariables, $lessVariables); + }); + $container->extend('flarum.assets.factory', function (callable $factory) { return function (...$args) use ($factory) { /** @var Assets $assets */ diff --git a/framework/core/src/Frontend/FrontendServiceProvider.php b/framework/core/src/Frontend/FrontendServiceProvider.php index 86af8f1bb..bcb5a11bd 100644 --- a/framework/core/src/Frontend/FrontendServiceProvider.php +++ b/framework/core/src/Frontend/FrontendServiceProvider.php @@ -156,6 +156,13 @@ class FrontendServiceProvider extends AbstractServiceProvider ], ]; }); + + $this->container->singleton( + 'flarum.less.custom_variables', + function (Container $container) { + return []; + } + ); } /** @@ -184,9 +191,11 @@ class FrontendServiceProvider extends AbstractServiceProvider { $sources->addString(function () { $vars = $this->container->make('flarum.less.config'); + $extDefinedVars = $this->container->make('flarum.less.custom_variables'); + $settings = $this->container->make(SettingsRepositoryInterface::class); - return array_reduce(array_keys($vars), function ($string, $name) use ($vars, $settings) { + $customLess = array_reduce(array_keys($vars), function ($string, $name) use ($vars, $settings) { $var = $vars[$name]; $value = $settings->get($var['key'], $var['default'] ?? null); @@ -196,6 +205,12 @@ class FrontendServiceProvider extends AbstractServiceProvider return $string."@$name: {$value};"; }, ''); + + foreach ($extDefinedVars as $name => $value) { + $customLess .= "@$name: {$value()};"; + } + + return $customLess; }); } } diff --git a/framework/core/tests/fixtures/less/custom_variable.less b/framework/core/tests/fixtures/less/custom_variable.less new file mode 100644 index 000000000..a8db66edc --- /dev/null +++ b/framework/core/tests/fixtures/less/custom_variable.less @@ -0,0 +1,3 @@ +.dummy_var_test { + --x: @doesnt-exist; +} diff --git a/framework/core/tests/integration/extenders/ThemeTest.php b/framework/core/tests/integration/extenders/ThemeTest.php index ba8e50e0b..57faea58f 100644 --- a/framework/core/tests/integration/extenders/ThemeTest.php +++ b/framework/core/tests/integration/extenders/ThemeTest.php @@ -132,4 +132,28 @@ class ThemeTest extends TestCase $this->assertStringContainsString('.dummy_func_test{color:green}', $contents); $this->assertStringContainsString('.dummy_func_test2{--x:1000;--y:false}', $contents); } + + /** + * @test + */ + public function theme_extender_can_add_custom_variable() + { + $this->extend( + (new Extend\Frontend('forum')) + ->css(__DIR__.'/../../fixtures/less/custom_variable.less'), + (new Extend\Theme) + ->addCustomLessVariable('doesnt-exist', function () { + return 'it does'; + }) + ); + + $response = $this->send($this->request('GET', '/')); + + $this->assertEquals(200, $response->getStatusCode()); + + $cssFilePath = $this->app()->getContainer()->make('filesystem')->disk('flarum-assets')->path('forum.css'); + $contents = file_get_contents($cssFilePath); + + $this->assertStringContainsString('.dummy_var_test{--x:it does}', $contents); + } }