diff options
| author | Jan-Hendrik Willms <tleilax+studip@gmail.com> | 2025-07-24 15:44:21 +0200 |
|---|---|---|
| committer | Ron Lucke <lucke@elan-ev.de> | 2025-07-24 15:44:21 +0200 |
| commit | 141aa4f820c2a1ea1606b4a1b1f2f4a937556646 (patch) | |
| tree | 30ffec030af52a5454fed9e16836a1c24cc955c2 /lib | |
| parent | adeef25cdd76acffda1462d4d949d447dba4acf6 (diff) | |
use plugin assets for theme css, fixes #5737
Closes #5737
Merge request studip/studip!4366
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/classes/JsonApi/Schemas/Theme.php | 36 | ||||
| -rw-r--r-- | lib/classes/PageLayout.php | 18 | ||||
| -rw-r--r-- | lib/models/Theme.php | 115 |
3 files changed, 138 insertions, 31 deletions
diff --git a/lib/classes/JsonApi/Schemas/Theme.php b/lib/classes/JsonApi/Schemas/Theme.php index 8b8c09f..dfb7d1f 100644 --- a/lib/classes/JsonApi/Schemas/Theme.php +++ b/lib/classes/JsonApi/Schemas/Theme.php @@ -2,10 +2,7 @@ namespace JsonApi\Schemas; -use JsonApi\Schemas\SchemaProvider; use Neomerx\JsonApi\Contracts\Schema\ContextInterface; -use Neomerx\JsonApi\Schema\Link; -use Neomerx\JsonApi\Contracts\Schema\LinkInterface; class Theme extends SchemaProvider { @@ -25,22 +22,23 @@ class Theme extends SchemaProvider public function getAttributes($resource, ContextInterface $context): iterable { return [ - 'name' => $resource['name'], - 'active' => (bool)$resource['active'], - 'origin' => $resource['origin'], - 'studip_min_version' => $resource['studip_min_version'], - 'studip_max_version' => $resource['studip_max_version'], - 'author' => $resource['author'], - 'description' => $resource['description'], - 'type' => $resource['type'], - 'values' => empty($resource['values']) ? null : json_decode($resource['values']), - - 'mkdate' => date('c', $resource['mkdate']), - 'chdate' => date('c', $resource['chdate']), + 'name' => $resource->name, + 'active' => (bool) $resource->active, + 'origin' => $resource->origin, + 'studip_min_version' => $resource->studip_min_version, + 'studip_max_version' => $resource->studip_max_version, + 'author' => $resource->author, + 'description' => $resource->description, + 'type' => $resource->type, + 'values' => $resource->values->getArrayCopy() ?: null, + + 'mkdate' => date('c', $resource->mkdate), + 'chdate' => date('c', $resource->chdate), ]; } /** + * @param \Theme $resource * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function getRelationships($resource, ContextInterface $context): iterable @@ -48,15 +46,21 @@ class Theme extends SchemaProvider return []; } + /** + * @param \Theme $resource + */ public function hasResourceMeta($resource): bool { return true; } + /** + * @param \Theme $resource + */ public function getResourceMeta($resource): iterable { return [ 'colorKeyCategories' => $resource->getColorKeyCategories(), ]; } -}
\ No newline at end of file +} diff --git a/lib/classes/PageLayout.php b/lib/classes/PageLayout.php index f7d21c9..c529fa6 100644 --- a/lib/classes/PageLayout.php +++ b/lib/classes/PageLayout.php @@ -142,6 +142,17 @@ class PageLayout self::addHeadElement('script', [], 'window.Vue.use = () => {};'); self::addStylesheet('studip-base.css?v=' . $v, ['media' => 'screen']); + + try { + $old_base = URLHelper::setBaseURL($GLOBALS['ABSOLUTE_URI_STUDIP']); + self::addHeadElement('link', [ + 'rel' => 'stylesheet', + 'href' => Theme::getDownloadURL(), + ]); + URLHelper::setBaseURL($old_base); + } catch (Exception) { + } + self::addScript('studip-base.js?v=' . $v); self::addScript('studip-wysiwyg.js?v=' . $v); @@ -161,13 +172,6 @@ class PageLayout URLHelper::setBaseURL($old_base); } - - $old_base = URLHelper::setBaseURL($GLOBALS['ABSOLUTE_URI_STUDIP']); - self::addHeadElement('link', [ - 'rel' => 'stylesheet', - 'href' => URLHelper::getURL('theme.php', ignore_registered_params: true) - ]); - URLHelper::setBaseURL($old_base); } /** diff --git a/lib/models/Theme.php b/lib/models/Theme.php index 2b25d6e..824d8cd 100644 --- a/lib/models/Theme.php +++ b/lib/models/Theme.php @@ -2,10 +2,12 @@ /** * Theme.php - Stud.IP Theme Model Class - * + * * @author Ron Lucke <lucke@elan-ev.de> * @license GPL2 or any later version - * + * + * @property int $id + * @property bool $active * @property string $name * @property string $origin * @property string $version @@ -13,8 +15,8 @@ * @property string $studip_max_version * @property string $author * @property string $description - * @property \JSONArrayObject $values * @property string $type + * @property JSONArrayObject $values * @property int $mkdate database column * @property int $chdate database column */ @@ -57,9 +59,37 @@ $config['db_table'] = 'themes'; $config['serialized_fields']['values'] = JSONArrayObject::class; + $config['registered_callbacks']['after_store'][] = function (Theme $theme): void { + if ( + $theme->isFieldDirty('active') + || ( + $theme->active + && $theme->isFieldDirty('values') + ) + ) { + self::loadActiveThemes(true); + self::getThemeAsset()->writeContent(self::getActiveCSS()); + } + }; + parent::configure($config); } + public static function getThemeAsset(): PluginAsset + { + $asset = new PluginAsset('studip-theme'); + if ($asset->isNew()) { + $asset->plugin_id = 0; + $asset->type = 'css'; + $asset->filename = 'theme.css'; + $asset->storagename = 'theme.css'; + $asset->store(); + + $asset->writeContent(self::getActiveCSS()); + } + return $asset; + } + /** * @return static[] */ @@ -72,24 +102,93 @@ ]; } + public static function getDownloadURL(): string + { + $asset = self::getThemeAsset(); + return URLHelper::getLink( + "assets.php/css/{$asset->id}#{$asset->filename}", + ['v' => $asset->chdate], + true + ); + + } + + public static function getActiveCSS(): string + { + $css = ''; + foreach (self::getActiveThemes() as $theme) { + if ($theme) { + $css .= $theme->render() . PHP_EOL; + } + } + return $css; + } + public static function getActiveLightTheme(): ?static { - return self::findOneBySQL('active = 1 AND type = "light"'); + return self::loadActiveThemes()['light']; } public static function getActiveDarkTheme(): ?static { - return self::findOneBySQL('active = 1 AND type = "dark"'); + return self::loadActiveThemes()['dark']; } public static function getActiveHighContrastTheme(): ?static { - return self::findOneBySQL('active = 1 AND type = "high-contrast"'); + return self::loadActiveThemes()['high-contrast']; + } + + protected static ?array $active_themes = null; + + public static function loadActiveThemes(bool $force = false): array + { + if ($force || self::$active_themes === null) { + self::$active_themes = [ + 'light' => null, + 'dark' => null, + 'high-contrast' => null, + ]; + self::findEachBySQL( + function (self $theme): void { + self::$active_themes[$theme->type] = $theme; + }, + 'active = 1' + ); + } + return self::$active_themes; } - public static function getcolorKeyCategories(): array + public static function getColorKeyCategories(): array { return self::COLOR_KEY_CATEGORIES; } - }
\ No newline at end of file + public function render(): string + { + $lines = []; + + $indent = ' '; + if ($this->type === 'dark') { + $lines[] = '@media (prefers-color-scheme: dark) {'; + } elseif ($this->type === 'high-contrast') { + $lines[] = '@media (prefers-contrast: more) {'; + } else { + $indent = ''; + } + + $lines[] = $indent . ':root {'; + foreach ($this->values as $name => $value) { + + if ($value !== '') { + $lines[] = $indent . " {$name}: {$value};"; + } + } + $lines[] = $indent . '}'; + + if ($indent !== '') { + $lines[] = '}'; + } + return implode(PHP_EOL, $lines); + } + } |
