From 705335e6a55b9ef0e4ebf0bb83c07efac23e52c8 Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Sat, 20 Jan 2024 01:56:18 +0100 Subject: [PATCH] feat(theming): Allow to configure primary color separate from background in admin settings Signed-off-by: Ferdinand Thiessen --- apps/theming/lib/Capabilities.php | 15 +-- apps/theming/lib/Command/UpdateConfig.php | 9 +- .../lib/Controller/ThemingController.php | 15 ++- .../lib/Controller/UserThemeController.php | 5 +- apps/theming/lib/ImageManager.php | 1 + apps/theming/lib/ResponseDefinitions.php | 1 + .../theming/lib/Service/BackgroundService.php | 93 +++++++++++-------- apps/theming/lib/Service/JSDataService.php | 18 ++-- apps/theming/lib/Settings/Admin.php | 3 +- apps/theming/lib/Themes/CommonThemeTrait.php | 5 +- apps/theming/src/AdminTheming.vue | 69 ++++++++++++-- .../src/components/BackgroundSettings.vue | 68 ++++++-------- .../src/components/admin/ColorPickerField.vue | 20 +++- 13 files changed, 197 insertions(+), 125 deletions(-) diff --git a/apps/theming/lib/Capabilities.php b/apps/theming/lib/Capabilities.php index b0c6a71731b..eec2b5187ea 100644 --- a/apps/theming/lib/Capabilities.php +++ b/apps/theming/lib/Capabilities.php @@ -101,8 +101,9 @@ class Capabilities implements IPublicCapability { $colorText = $this->util->invertTextColor($color) ? '#000000' : '#ffffff'; $backgroundLogo = $this->config->getAppValue('theming', 'backgroundMime', ''); - $backgroundPlain = $backgroundLogo === 'backgroundColor' || ($backgroundLogo === '' && $color !== '#0082c9'); - $background = $backgroundPlain ? $color : $this->url->getAbsoluteURL($this->theming->getBackground()); + $backgroundColor = $this->theming->getDefaultColorBackground(); + $backgroundPlain = $backgroundLogo === 'backgroundColor' || ($backgroundLogo === '' && $backgroundColor !== BackgroundService::DEFAULT_COLOR); + $background = $backgroundPlain ? $backgroundColor : $this->url->getAbsoluteURL($this->theming->getBackground()); $user = $this->userSession->getUser(); if ($user instanceof IUser) { @@ -112,10 +113,9 @@ class Capabilities implements IPublicCapability { * @see \OCA\Theming\Themes\CommonThemeTrait::generateUserBackgroundVariables() */ $color = $this->theming->getColorPrimary(); - if ($color === BackgroundService::DEFAULT_COLOR) { - $color = BackgroundService::DEFAULT_ACCESSIBLE_COLOR; - } - $colorText = $this->util->invertTextColor($color) ? '#000000' : '#ffffff'; + $colorText = $this->theming->getTextColorPrimary(); + $backgroundColor = $this->theming->getColorBackground(); + $backgroundText = $this->theming->getTextColorBackground(); $backgroundImage = $this->config->getUserValue($user->getUID(), Application::APP_ID, 'background_image', BackgroundService::BACKGROUND_DEFAULT); if ($backgroundImage === BackgroundService::BACKGROUND_CUSTOM) { @@ -126,7 +126,7 @@ class Capabilities implements IPublicCapability { $background = $this->url->linkTo(Application::APP_ID, "img/background/$backgroundImage"); } elseif ($backgroundImage !== BackgroundService::BACKGROUND_DEFAULT) { $backgroundPlain = true; - $background = $color; + $background = $backgroundColor; } } @@ -142,6 +142,7 @@ class Capabilities implements IPublicCapability { 'color-element-dark' => $this->util->elementColor($color, false), 'logo' => $this->url->getAbsoluteURL($this->theming->getLogo()), 'background' => $background, + 'background-text' => $backgroundText, 'background-plain' => $backgroundPlain, 'background-default' => !$this->util->isBackgroundThemed(), 'logoheader' => $this->url->getAbsoluteURL($this->theming->getLogo()), diff --git a/apps/theming/lib/Command/UpdateConfig.php b/apps/theming/lib/Command/UpdateConfig.php index de180db6ce9..88f029a72bf 100644 --- a/apps/theming/lib/Command/UpdateConfig.php +++ b/apps/theming/lib/Command/UpdateConfig.php @@ -33,7 +33,7 @@ use Symfony\Component\Console\Output\OutputInterface; class UpdateConfig extends Command { public const SUPPORTED_KEYS = [ - 'name', 'url', 'imprintUrl', 'privacyUrl', 'slogan', 'color', 'disable-user-theming' + 'name', 'url', 'imprintUrl', 'privacyUrl', 'slogan', 'color', 'primary_color', 'disable-user-theming' ]; private $themingDefaults; @@ -128,8 +128,13 @@ class UpdateConfig extends Command { $value = $this->imageManager->updateImage($key, $value); $key = $key . 'Mime'; } + + if ($key === 'color') { + $output->writeln('Using "color" is depreacted, use "primary_color" instead'); + $key = 'primary_color'; + } - if ($key === 'color' && !preg_match('/^\#([0-9a-f]{3}|[0-9a-f]{6})$/i', $value)) { + if ($key === 'primary_color' && !preg_match('/^\#([0-9a-f]{3}|[0-9a-f]{6})$/i', $value)) { $output->writeln('The given color is invalid: ' . $value . ''); return 1; } diff --git a/apps/theming/lib/Controller/ThemingController.php b/apps/theming/lib/Controller/ThemingController.php index 91012d1e37a..024e5433808 100644 --- a/apps/theming/lib/Controller/ThemingController.php +++ b/apps/theming/lib/Controller/ThemingController.php @@ -50,13 +50,11 @@ use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Http\FileDisplayResponse; use OCP\AppFramework\Http\JSONResponse; use OCP\AppFramework\Http\NotFoundResponse; -use OCP\Files\IAppData; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; use OCP\IConfig; use OCP\IL10N; use OCP\IRequest; -use OCP\ITempManager; use OCP\IURLGenerator; use ScssPhp\ScssPhp\Compiler; @@ -73,8 +71,6 @@ class ThemingController extends Controller { private ThemingDefaults $themingDefaults; private IL10N $l10n; private IConfig $config; - private ITempManager $tempManager; - private IAppData $appData; private IURLGenerator $urlGenerator; private IAppManager $appManager; private ImageManager $imageManager; @@ -86,8 +82,6 @@ class ThemingController extends Controller { IConfig $config, ThemingDefaults $themingDefaults, IL10N $l, - ITempManager $tempManager, - IAppData $appData, IURLGenerator $urlGenerator, IAppManager $appManager, ImageManager $imageManager, @@ -98,8 +92,6 @@ class ThemingController extends Controller { $this->themingDefaults = $themingDefaults; $this->l10n = $l; $this->config = $config; - $this->tempManager = $tempManager; - $this->appData = $appData; $this->urlGenerator = $urlGenerator; $this->appManager = $appManager; $this->imageManager = $imageManager; @@ -151,7 +143,12 @@ class ThemingController extends Controller { $error = $this->l10n->t('The given slogan is too long'); } break; - case 'color': + case 'primary_color': + if (!preg_match('/^\#([0-9a-f]{3}|[0-9a-f]{6})$/i', $value)) { + $error = $this->l10n->t('The given color is invalid'); + } + break; + case 'background_color': if (!preg_match('/^\#([0-9a-f]{3}|[0-9a-f]{6})$/i', $value)) { $error = $this->l10n->t('The given color is invalid'); } diff --git a/apps/theming/lib/Controller/UserThemeController.php b/apps/theming/lib/Controller/UserThemeController.php index 75fa414b170..309b9c04482 100644 --- a/apps/theming/lib/Controller/UserThemeController.php +++ b/apps/theming/lib/Controller/UserThemeController.php @@ -58,7 +58,6 @@ class UserThemeController extends OCSController { protected ?string $userId = null; private IConfig $config; - private IUserSession $userSession; private ThemesService $themesService; private ThemingDefaults $themingDefaults; private BackgroundService $backgroundService; @@ -72,7 +71,6 @@ class UserThemeController extends OCSController { BackgroundService $backgroundService) { parent::__construct($appName, $request); $this->config = $config; - $this->userSession = $userSession; $this->themesService = $themesService; $this->themingDefaults = $themingDefaults; $this->backgroundService = $backgroundService; @@ -186,7 +184,8 @@ class UserThemeController extends OCSController { $this->backgroundService->deleteBackgroundImage(); return new JSONResponse([ 'backgroundImage' => null, - 'backgroundColor' => $this->themingDefaults->getColorPrimary(), + 'backgroundColor' => $this->themingDefaults->getColorBackground(), + 'primaryColor' => $this->themingDefaults->getColorPrimary(), 'version' => $currentVersion, ]); } diff --git a/apps/theming/lib/ImageManager.php b/apps/theming/lib/ImageManager.php index 994e3f35118..69ab14731e8 100644 --- a/apps/theming/lib/ImageManager.php +++ b/apps/theming/lib/ImageManager.php @@ -4,6 +4,7 @@ * * @author Christoph Wurst * @author Daniel Kesselberg + * @author Ferdinand Thiessen * @author Gary Kim * @author Jacob Neplokh * @author John Molakvoæ diff --git a/apps/theming/lib/ResponseDefinitions.php b/apps/theming/lib/ResponseDefinitions.php index f1b0d09790a..29a1c029fdf 100644 --- a/apps/theming/lib/ResponseDefinitions.php +++ b/apps/theming/lib/ResponseDefinitions.php @@ -30,6 +30,7 @@ namespace OCA\Theming; * @psalm-type ThemingBackground = array{ * backgroundImage: ?string, * backgroundColor: string, + * primaryColor: string, * version: int, * } */ diff --git a/apps/theming/lib/Service/BackgroundService.php b/apps/theming/lib/Service/BackgroundService.php index 375511b37d3..edbcda47c05 100644 --- a/apps/theming/lib/Service/BackgroundService.php +++ b/apps/theming/lib/Service/BackgroundService.php @@ -8,6 +8,7 @@ declare(strict_types=1); * @author Jan C. Borchardt * @author Julius Härtl * @author Christopher Ng + * @author Ferdinand Thiessen * * @license GNU AGPL version 3 or any later version * @@ -30,7 +31,6 @@ namespace OCA\Theming\Service; use InvalidArgumentException; use OC\User\NoUserException; use OCA\Theming\AppInfo\Application; -use OCA\Theming\ThemingDefaults; use OCP\Files\File; use OCP\Files\IAppData; use OCP\Files\IRootFolder; @@ -43,160 +43,177 @@ use OCP\Lock\LockedException; use OCP\PreConditionNotMetException; class BackgroundService { - // true when the background is bright and need dark icons - public const THEMING_MODE_DARK = 'dark'; public const DEFAULT_COLOR = '#0082c9'; public const DEFAULT_ACCESSIBLE_COLOR = '#00679e'; + /** + * One of our shipped background images is used + */ public const BACKGROUND_SHIPPED = 'shipped'; + /** + * A custom background image is used + */ public const BACKGROUND_CUSTOM = 'custom'; + /** + * The default background image is used + */ public const BACKGROUND_DEFAULT = 'default'; - public const BACKGROUND_DISABLED = 'disabled'; + /** + * Just a background color is used + */ + public const BACKGROUND_COLOR = 'color'; public const DEFAULT_BACKGROUND_IMAGE = 'kamil-porembinski-clouds.jpg'; + + /** + * 'attribution': Name, artist and license + * 'description': Alternative text + * 'attribution_url': URL for attribution + * 'background_color': Cached mean color of the top part to calculate app menu colors and use as fallback + * 'primary_color': Recommended primary color for this theme / image + */ public const SHIPPED_BACKGROUNDS = [ 'hannah-maclean-soft-floral.jpg' => [ 'attribution' => 'Soft floral (Hannah MacLean, CC0)', 'description' => 'Abstract background picture in yellow and white color whith a flower on it', 'attribution_url' => 'https://stocksnap.io/photo/soft-floral-XOYWCCW5PA', - 'theming' => self::THEMING_MODE_DARK, - 'primary_color' => '#D8A06C', + 'background_color' => '#e4d2c1', + 'primary_color' => '#9f652f', ], 'ted-moravec-morning-fog.jpg' => [ 'attribution' => 'Morning fog (Ted Moravec, Public Domain)', 'description' => 'Background picture of a forest shrouded in fog', 'attribution_url' => 'https://flickr.com/photos/tmoravec/52392410261', - 'theming' => self::THEMING_MODE_DARK, - 'primary_color' => '#38A084', + 'background_color' => '#f6f7f6', + 'primary_color' => '#114c3b', ], 'stefanus-martanto-setyo-husodo-underwater-ocean.jpg' => [ 'attribution' => 'Underwater ocean (Stefanus Martanto Setyo Husodo, CC0)', 'description' => 'Background picture of an underwater ocean', 'attribution_url' => 'https://stocksnap.io/photo/underwater-ocean-TJA9LBH4WS', + 'background_color' => '#003351', 'primary_color' => '#04577e', ], 'zoltan-voros-rhythm-and-blues.jpg' => [ 'attribution' => 'Rhythm and blues (Zoltán Vörös, CC BY)', 'description' => 'Abstract background picture of sand dunes during night', 'attribution_url' => 'https://flickr.com/photos/v923z/51634409289/', + 'background_color' => '#1c2437', 'primary_color' => '#1c243c', ], 'anatoly-mikhaltsov-butterfly-wing-scale.jpg' => [ 'attribution' => 'Butterfly wing scale (Anatoly Mikhaltsov, CC BY-SA)', 'description' => 'Background picture of a red-ish butterfly wing under microscope', 'attribution_url' => 'https://commons.wikimedia.org/wiki/File:%D0%A7%D0%B5%D1%88%D1%83%D0%B9%D0%BA%D0%B8_%D0%BA%D1%80%D1%8B%D0%BB%D0%B0_%D0%B1%D0%B0%D0%B1%D0%BE%D1%87%D0%BA%D0%B8.jpg', + 'background_color' => '#652e11', 'primary_color' => '#a53c17', ], 'bernie-cetonia-aurata-take-off-composition.jpg' => [ 'attribution' => 'Cetonia aurata take off composition (Bernie, Public Domain)', 'description' => 'Montage of a cetonia aurata bug that takes off with white background', 'attribution_url' => 'https://commons.wikimedia.org/wiki/File:Cetonia_aurata_take_off_composition_05172009.jpg', - 'theming' => self::THEMING_MODE_DARK, - 'primary_color' => '#869171', + 'background_color' => '#dee0d3', + 'primary_color' => '#56633d', ], 'dejan-krsmanovic-ribbed-red-metal.jpg' => [ 'attribution' => 'Ribbed red metal (Dejan Krsmanovic, CC BY)', 'description' => 'Abstract background picture of red ribbed metal with two horizontal white elements on top of it', 'attribution_url' => 'https://www.flickr.com/photos/dejankrsmanovic/42971456774/', + 'background_color' => '#9b171c', 'primary_color' => '#9c4236', ], 'eduardo-neves-pedra-azul.jpg' => [ 'attribution' => 'Pedra azul milky way (Eduardo Neves, CC BY-SA)', 'description' => 'Background picture of the milky way during night with a mountain in front of it', 'attribution_url' => 'https://commons.wikimedia.org/wiki/File:Pedra_Azul_Milky_Way.jpg', + 'background_color' => '#1d242d', 'primary_color' => '#4f6071', ], 'european-space-agency-barents-bloom.jpg' => [ 'attribution' => 'Barents bloom (European Space Agency, CC BY-SA)', 'description' => 'Abstract background picture of blooming barents in blue and green colors', 'attribution_url' => 'https://www.esa.int/ESA_Multimedia/Images/2016/08/Barents_bloom', + 'background_color' => '#1c383d', 'primary_color' => '#396475', ], 'hannes-fritz-flippity-floppity.jpg' => [ 'attribution' => 'Flippity floppity (Hannes Fritz, CC BY-SA)', 'description' => 'Abstract background picture of many pairs of flip flops hanging on a wall in multiple colors', 'attribution_url' => 'http://hannes.photos/flippity-floppity', + 'background_color' => '#5b2d53', 'primary_color' => '#98415a', ], 'hannes-fritz-roulette.jpg' => [ 'attribution' => 'Roulette (Hannes Fritz, CC BY-SA)', 'description' => 'Background picture of a rotating giant wheel during night', 'attribution_url' => 'http://hannes.photos/roulette', + 'background_color' => '#000000', 'primary_color' => '#845334', ], 'hannes-fritz-sea-spray.jpg' => [ 'attribution' => 'Sea spray (Hannes Fritz, CC BY-SA)', 'description' => 'Background picture of a stone coast with fog and sea behind it', 'attribution_url' => 'http://hannes.photos/sea-spray', + 'background_color' => '#333f47', 'primary_color' => '#4f6071', ], 'kamil-porembinski-clouds.jpg' => [ 'attribution' => 'Clouds (Kamil Porembiński, CC BY-SA)', 'description' => 'Background picture of white clouds on in front of a blue sky', 'attribution_url' => 'https://www.flickr.com/photos/paszczak000/8715851521/', + 'background_color' => '#00679e', 'primary_color' => self::DEFAULT_COLOR, ], 'bernard-spragg-new-zealand-fern.jpg' => [ 'attribution' => 'New zealand fern (Bernard Spragg, CC0)', 'description' => 'Abstract background picture of fern leafes', 'attribution_url' => 'https://commons.wikimedia.org/wiki/File:NZ_Fern.(Blechnum_chambersii)_(11263534936).jpg', + 'background_color' => '#0c3c03', 'primary_color' => '#316b26', ], 'rawpixel-pink-tapioca-bubbles.jpg' => [ 'attribution' => 'Pink tapioca bubbles (Rawpixel, CC BY)', 'description' => 'Abstract background picture of pink tapioca bubbles', 'attribution_url' => 'https://www.flickr.com/photos/byrawpixel/27665140298/in/photostream/', - 'theming' => self::THEMING_MODE_DARK, - 'primary_color' => '#b17ab4', + 'background_color' => '#c56e95', + 'primary_color' => '#7b4e7e', ], 'nasa-waxing-crescent-moon.jpg' => [ 'attribution' => 'Waxing crescent moon (NASA, Public Domain)', 'description' => 'Background picture of glowing earth in foreground and moon in the background', 'attribution_url' => 'https://www.nasa.gov/image-feature/a-waxing-crescent-moon', + 'background_color' => '#000002', 'primary_color' => '#005ac1', ], 'tommy-chau-already.jpg' => [ 'attribution' => 'Cityscape (Tommy Chau, CC BY)', 'description' => 'Background picture of a skyscraper city during night', 'attribution_url' => 'https://www.flickr.com/photos/90975693@N05/16910999368', + 'background_color' => '#35229f', 'primary_color' => '#6a2af4', ], 'tommy-chau-lion-rock-hill.jpg' => [ 'attribution' => 'Lion rock hill (Tommy Chau, CC BY)', 'description' => 'Background picture of mountains during sunset or sunrise', 'attribution_url' => 'https://www.flickr.com/photos/90975693@N05/17136440246', - 'theming' => self::THEMING_MODE_DARK, - 'primary_color' => '#c074a9', + 'background_color' => '#cb92b7', + 'primary_color' => '#7f4f70', ], 'lali-masriera-yellow-bricks.jpg' => [ 'attribution' => 'Yellow bricks (Lali Masriera, CC BY)', 'description' => 'Background picture of yellow bricks with some yellow tubes', 'attribution_url' => 'https://www.flickr.com/photos/visualpanic/3982464447', - 'theming' => self::THEMING_MODE_DARK, - 'primary_color' => '#bc8210', + 'background_color' => '#c78a19', + 'primary_color' => '#7f5700', ], ]; - private IRootFolder $rootFolder; - private IAppData $appData; - private IConfig $config; - private string $userId; - private ThemingDefaults $themingDefaults; - - public function __construct(IRootFolder $rootFolder, - IAppData $appData, - IConfig $config, - ?string $userId, - ThemingDefaults $themingDefaults) { - if ($userId === null) { - return; - } - - $this->rootFolder = $rootFolder; - $this->config = $config; - $this->userId = $userId; - $this->appData = $appData; - $this->themingDefaults = $themingDefaults; + public function __construct( + private IRootFolder $rootFolder, + private IAppData $appData, + private IConfig $config, + private ?string $userId, + ) { } public function setDefaultBackground(): void { @@ -236,6 +253,7 @@ class BackgroundService { if (!array_key_exists($fileName, self::SHIPPED_BACKGROUNDS)) { throw new InvalidArgumentException('The given file name is invalid'); } + $this->setColorBackground(self::SHIPPED_BACKGROUNDS[$fileName]['background_color']); $this->config->setUserValue($this->userId, Application::APP_ID, 'background_image', $fileName); $this->config->setUserValue($this->userId, Application::APP_ID, 'primary_color', self::SHIPPED_BACKGROUNDS[$fileName]['primary_color']); } @@ -248,10 +266,11 @@ class BackgroundService { throw new InvalidArgumentException('The given color is invalid'); } $this->config->setUserValue($this->userId, Application::APP_ID, 'background_color', $color); + $this->config->setUserValue($this->userId, Application::APP_ID, 'background_image', self::BACKGROUND_COLOR); } public function deleteBackgroundImage(): void { - $this->config->setUserValue($this->userId, Application::APP_ID, 'background_image', self::BACKGROUND_DISABLED); + $this->config->setUserValue($this->userId, Application::APP_ID, 'background_image', self::BACKGROUND_COLOR); } public function getBackground(): ?ISimpleFile { diff --git a/apps/theming/lib/Service/JSDataService.php b/apps/theming/lib/Service/JSDataService.php index 5dc7b3fd632..65f91f9dda2 100644 --- a/apps/theming/lib/Service/JSDataService.php +++ b/apps/theming/lib/Service/JSDataService.php @@ -28,23 +28,16 @@ namespace OCA\Theming\Service; use OCA\Theming\ThemingDefaults; use OCA\Theming\Util; -use OCP\IConfig; class JSDataService implements \JsonSerializable { - private ThemingDefaults $themingDefaults; - private Util $util; - private IConfig $appConfig; - private ThemesService $themesService; public function __construct( - ThemingDefaults $themingDefaults, - Util $util, - IConfig $appConfig, - ThemesService $themesService + private ThemingDefaults $themingDefaults, + private Util $util, + private ThemesService $themesService, ) { $this->themingDefaults = $themingDefaults; $this->util = $util; - $this->appConfig = $appConfig; $this->themesService = $themesService; } @@ -53,13 +46,16 @@ class JSDataService implements \JsonSerializable { 'name' => $this->themingDefaults->getName(), 'url' => $this->themingDefaults->getBaseUrl(), 'slogan' => $this->themingDefaults->getSlogan(), - 'color' => $this->themingDefaults->getColorPrimary(), + 'color' => $this->themingDefaults->getColorPrimary(), // deprecated use primaryColor + 'primaryColor' => $this->themingDefaults->getColorPrimary(), + 'backgroundColor' => $this->themingDefaults->getColorBackground(), 'defaultColor' => $this->themingDefaults->getDefaultColorPrimary(), 'imprintUrl' => $this->themingDefaults->getImprintUrl(), 'privacyUrl' => $this->themingDefaults->getPrivacyUrl(), 'inverted' => $this->util->invertTextColor($this->themingDefaults->getColorPrimary()), 'cacheBuster' => $this->util->getCacheBuster(), 'enabledThemes' => $this->themesService->getEnabledThemes(), + '' => 'color is deprecated since Nextcloud 29, use primaryColor instead' ]; } } diff --git a/apps/theming/lib/Settings/Admin.php b/apps/theming/lib/Settings/Admin.php index dc5d9a2a6a4..8a34432a073 100644 --- a/apps/theming/lib/Settings/Admin.php +++ b/apps/theming/lib/Settings/Admin.php @@ -75,7 +75,8 @@ class Admin implements IDelegatedSettings { 'name' => $this->themingDefaults->getEntity(), 'url' => $this->themingDefaults->getBaseUrl(), 'slogan' => $this->themingDefaults->getSlogan(), - 'color' => $this->themingDefaults->getDefaultColorPrimary(), + 'primaryColor' => $this->themingDefaults->getDefaultColorPrimary(), + 'backgroundColor' => $this->themingDefaults->getDefaultColorBackground(), 'logoMime' => $this->config->getAppValue(Application::APP_ID, 'logoMime', ''), 'allowedMimeTypes' => $allowedMimeTypes, 'backgroundMime' => $this->config->getAppValue(Application::APP_ID, 'backgroundMime', ''), diff --git a/apps/theming/lib/Themes/CommonThemeTrait.php b/apps/theming/lib/Themes/CommonThemeTrait.php index 6d74a7bcdfc..efeb3f0d97c 100644 --- a/apps/theming/lib/Themes/CommonThemeTrait.php +++ b/apps/theming/lib/Themes/CommonThemeTrait.php @@ -6,6 +6,7 @@ declare(strict_types=1); * * @author Joas Schilling * @author John Molakvoæ + * @author Ferdinand Thiessen * * @license GNU AGPL version 3 or any later version * @@ -143,8 +144,8 @@ trait CommonThemeTrait { '--background-image-invert-if-bright' => $isBackgroundBright ? 'invert(100%)' : 'no', ]; - // The user removed the background - if ($backgroundImage === BackgroundService::BACKGROUND_DISABLED) { + // Only use a background color without an image + if ($backgroundImage === BackgroundService::BACKGROUND_COLOR) { // Might be defined already by admin theming, needs to be overridden $variables['--image-background'] = 'none'; } diff --git a/apps/theming/src/AdminTheming.vue b/apps/theming/src/AdminTheming.vue index 2face1629ef..f0831d8e944 100644 --- a/apps/theming/src/AdminTheming.vue +++ b/apps/theming/src/AdminTheming.vue @@ -47,10 +47,21 @@ @update:theming="$emit('update:theming')" /> - + + + @@ -122,8 +133,8 @@ import AppMenuSection from './components/admin/AppMenuSection.vue' const { backgroundMime, + backgroundColor, canThemeIcons, - color, docUrl, docUrlIcons, faviconMime, @@ -133,6 +144,7 @@ const { logoMime, name, notThemableErrorMessage, + primaryColor, privacyPolicyUrl, slogan, url, @@ -170,11 +182,19 @@ const textFields = [ }, ] -const colorPickerField = { - name: 'color', - value: color, +const primaryColorPickerField = { + name: 'primary_color', + value: primaryColor, defaultValue: '#0082c9', - displayName: t('theming', 'Color'), + displayName: t('theming', 'Primary color'), + description: t('theming', 'The primary color is used for highlighting elements like important buttons. It might get slightly adjusted depending on the current color schema.'), +} + +const backgroundColorPickerField = { + name: 'background_color', + value: backgroundColor, + displayName: t('theming', 'Background color'), + description: t('theming', 'Instead of a background image you can also configure a plain background color. If you use a background image changing this color will influence the color of the app menu icons.'), } const fileInputFields = [ @@ -267,7 +287,8 @@ export default { data() { return { textFields, - colorPickerField, + backgroundColorPickerField, + primaryColorPickerField, fileInputFields, advancedTextFields, advancedFileInputFields, @@ -279,8 +300,36 @@ export default { docUrlIcons, isThemable, notThemableErrorMessage, + + defaultBackground: this.calculateDefaultBackground(), } }, + + watch: { + backgroundColorPickerField: { + deep: true, + handler() { + this.defaultBackground = this.calculateDefaultBackground() + }, + }, + }, + + methods: { + calculateDefaultBackground() { + const toHex = (num) => `00${num.toString(16)}`.slice(-2) + const style = window.getComputedStyle(document.body).backgroundImage + const match = style.match(/url\("(http.+)"\)/) + if (!match) { + return '#0082c9' + } + const context = document.createElement('canvas').getContext('2d') + const img = new Image() + img.src = match[1] + context.imageSmoothingEnabled = true + context.drawImage(img, 0, 0, 1, 1) + return '#' + [...context.getImageData(0, 0, 1, 1).data.slice(0, 3)].map(toHex).join('') + }, + }, } diff --git a/apps/theming/src/components/BackgroundSettings.vue b/apps/theming/src/components/BackgroundSettings.vue index deb0a93a51a..166fdbc5f4b 100644 --- a/apps/theming/src/components/BackgroundSettings.vue +++ b/apps/theming/src/components/BackgroundSettings.vue @@ -32,15 +32,32 @@ 'background background__filepicker': true, 'background--active': backgroundImage === 'custom' }" - :data-color-bright="invertTextColor(Theming.color)" data-user-theming-background-custom tabindex="0" @click="pickFile"> {{ t('theming', 'Custom background') }} - + + + + + + - -
- - - {{ t('theming', 'Change color') }} - - -
- - - -