aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorJan-Hendrik Willms <tleilax+studip@gmail.com>2025-07-24 15:44:21 +0200
committerRon Lucke <lucke@elan-ev.de>2025-07-24 15:44:21 +0200
commit141aa4f820c2a1ea1606b4a1b1f2f4a937556646 (patch)
tree30ffec030af52a5454fed9e16836a1c24cc955c2 /lib
parentadeef25cdd76acffda1462d4d949d447dba4acf6 (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.php36
-rw-r--r--lib/classes/PageLayout.php18
-rw-r--r--lib/models/Theme.php115
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);
+ }
+ }