aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFerdinand Thiessen <opensource@fthiessen.de>2024-01-20 01:56:18 +0100
committerFerdinand Thiessen <opensource@fthiessen.de>2024-05-21 20:36:21 +0200
commit705335e6a55b9ef0e4ebf0bb83c07efac23e52c8 (patch)
treef392d4cb7170ba1e4b78b02280cf18e2edf27cca
parent9d2c3c1164926eadb61d1ec8360644dfeb75a78c (diff)
downloadnextcloud-server-705335e6a55b9ef0e4ebf0bb83c07efac23e52c8.tar.gz
nextcloud-server-705335e6a55b9ef0e4ebf0bb83c07efac23e52c8.zip
feat(theming): Allow to configure primary color separate from background in admin settings
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
-rw-r--r--apps/theming/lib/Capabilities.php15
-rw-r--r--apps/theming/lib/Command/UpdateConfig.php9
-rw-r--r--apps/theming/lib/Controller/ThemingController.php15
-rw-r--r--apps/theming/lib/Controller/UserThemeController.php5
-rw-r--r--apps/theming/lib/ImageManager.php1
-rw-r--r--apps/theming/lib/ResponseDefinitions.php1
-rw-r--r--apps/theming/lib/Service/BackgroundService.php93
-rw-r--r--apps/theming/lib/Service/JSDataService.php18
-rw-r--r--apps/theming/lib/Settings/Admin.php3
-rw-r--r--apps/theming/lib/Themes/CommonThemeTrait.php5
-rw-r--r--apps/theming/src/AdminTheming.vue69
-rw-r--r--apps/theming/src/components/BackgroundSettings.vue68
-rw-r--r--apps/theming/src/components/admin/ColorPickerField.vue20
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('<warning>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('<error>The given color is invalid: ' . $value . '</error>');
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 <christoph@winzerhof-wurst.at>
* @author Daniel Kesselberg <mail@danielkesselberg.de>
+ * @author Ferdinand Thiessen <opensource@fthiessen.de>
* @author Gary Kim <gary@garykim.dev>
* @author Jacob Neplokh <me@jacobneplokh.com>
* @author John Molakvoæ <skjnldsv@protonmail.com>
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 <hey@jancborchardt.net>
* @author Julius Härtl <jus@bitgrid.net>
* @author Christopher Ng <chrng8@gmail.com>
+ * @author Ferdinand Thiessen <opensource@fthiessen.de>
*
* @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 <coding@schilljs.com>
* @author John Molakvoæ <skjnldsv@protonmail.com>
+ * @author Ferdinand Thiessen <opensource@fthiessen.de>
*
* @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')" />
<!-- Primary color picker -->
- <ColorPickerField :name="colorPickerField.name"
- :default-value="colorPickerField.defaultValue"
- :display-name="colorPickerField.displayName"
- :value.sync="colorPickerField.value"
+ <ColorPickerField :name="primaryColorPickerField.name"
+ :description="primaryColorPickerField.description"
+ :default-value="primaryColorPickerField.defaultValue"
+ :display-name="primaryColorPickerField.displayName"
+ :value.sync="primaryColorPickerField.value"
+ data-admin-theming-setting-primary-color
+ @update:theming="$emit('update:theming')" />
+
+ <!-- Background color picker -->
+ <ColorPickerField :name="backgroundColorPickerField.name"
+ :description="backgroundColorPickerField.description"
+ :default-value="defaultBackground"
+ :display-name="backgroundColorPickerField.displayName"
+ :value.sync="backgroundColorPickerField.value"
+ data-admin-theming-setting-primary-color
@update:theming="$emit('update:theming')" />
<!-- Default background picker -->
@@ -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('')
+ },
+ },
}
</script>
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') }}
- <ImageEdit v-if="backgroundImage !== 'custom'" :size="26" />
+ <ImageEdit v-if="backgroundImage !== 'custom'" :size="20" />
<Check :size="44" />
</button>
+ <!-- Custom color picker -->
+ <NcColorPicker v-model="Theming.backgroundColor" @input="debouncePickColor">
+ <button :class="{
+ 'icon-loading': loading === 'color',
+ 'background background__color': true,
+ 'background--active': backgroundImage === 'color'
+ }"
+ :data-color="Theming.backgroundColor"
+ :data-color-bright="invertTextColor(Theming.backgroundColor)"
+ :style="{ backgroundColor: Theming.backgroundColor, '--border-color': Theming.backgroundColor}"
+ data-user-theming-background-color
+ tabindex="0">
+ {{ t('theming', 'Plain background') /* TRANSLATORS: Background using a single color */ }}
+ <ColorPalette v-if="backgroundImage !== 'color'" :size="20" />
+ <Check :size="44" />
+ </button>
+ </NcColorPicker>
+
<!-- Default background -->
<button :aria-pressed="backgroundImage === 'default'"
:class="{
@@ -57,31 +74,6 @@
<Check :size="44" />
</button>
- <!-- Custom color picker -->
- <div class="background-color"
- data-user-theming-background-color>
- <NcColorPicker v-model="Theming.color"
- @input="debouncePickColor">
- <NcButton type="ternary">
- {{ t('theming', 'Change color') }}
- </NcButton>
- </NcColorPicker>
- </div>
-
- <!-- Remove background -->
- <button :aria-pressed="isBackgroundDisabled"
- :class="{
- 'background background__delete': true,
- 'background--active': isBackgroundDisabled
- }"
- data-user-theming-background-clear
- tabindex="0"
- @click="removeBackground">
- {{ t('theming', 'No background') }}
- <Close v-if="!isBackgroundDisabled" :size="32" />
- <Check :size="44" />
- </button>
-
<!-- Background set selection -->
<button v-for="shippedBackground in shippedBackgrounds"
:key="shippedBackground.name"
@@ -93,7 +85,6 @@
'icon-loading': loading === shippedBackground.name,
'background--active': backgroundImage === shippedBackground.name
}"
- :data-color-bright="shippedBackground.details.theming === 'dark'"
:data-user-theming-background-shipped="shippedBackground.name"
:style="{ backgroundImage: 'url(' + shippedBackground.preview + ')', '--border-color': shippedBackground.details.primary_color }"
tabindex="0"
@@ -112,12 +103,11 @@ import { Palette } from 'node-vibrant/lib/color.js'
import axios from '@nextcloud/axios'
import debounce from 'debounce'
import NcColorPicker from '@nextcloud/vue/dist/Components/NcColorPicker.js'
-import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
import Vibrant from 'node-vibrant'
import Check from 'vue-material-design-icons/Check.vue'
-import Close from 'vue-material-design-icons/Close.vue'
import ImageEdit from 'vue-material-design-icons/ImageEdit.vue'
+import ColorPalette from 'vue-material-design-icons/Palette.vue'
const backgroundImage = loadState('theming', 'backgroundImage')
const shippedBackgroundList = loadState('theming', 'shippedBackgrounds')
@@ -126,14 +116,14 @@ const defaultShippedBackground = loadState('theming', 'defaultShippedBackground'
const prefixWithBaseUrl = (url) => generateFilePath('theming', '', 'img/background/') + url
+console.warn(loadState('theming', 'data', {}))
export default {
name: 'BackgroundSettings',
components: {
Check,
- Close,
+ ColorPalette,
ImageEdit,
- NcButton,
NcColorPicker,
},
@@ -175,11 +165,6 @@ export default {
isGlobalBackgroundDeleted() {
return themingDefaultBackground === 'backgroundColor'
},
-
- isBackgroundDisabled() {
- return this.backgroundImage === 'disabled'
- || !this.backgroundImage
- },
},
methods: {
@@ -226,7 +211,7 @@ export default {
async update(data) {
// Update state
this.backgroundImage = data.backgroundImage
- this.Theming.color = data.backgroundColor
+ this.Theming.backgroundColor = data.backgroundColor
// Notify parent and reload style
this.$emit('update:background')
@@ -359,14 +344,19 @@ export default {
height: 96px;
margin: 8px;
text-align: center;
+ word-wrap: break-word;
+ hyphens: auto;
border: 2px solid var(--color-main-background);
border-radius: var(--border-radius-large);
background-position: center center;
background-size: cover;
&__filepicker {
+ background-color: var(--color-main-text);
+ background-color: var(--color-background-dark);
+
&.background--active {
- color: white;
+ color: var(--color-background-plain-text);
background-image: var(--image-background);
}
}
diff --git a/apps/theming/src/components/admin/ColorPickerField.vue b/apps/theming/src/components/admin/ColorPickerField.vue
index fad40408b37..1cac5418d7a 100644
--- a/apps/theming/src/components/admin/ColorPickerField.vue
+++ b/apps/theming/src/components/admin/ColorPickerField.vue
@@ -26,14 +26,16 @@
<div class="field__row">
<NcColorPicker :value.sync="localValue"
:advanced-fields="true"
- data-admin-theming-setting-primary-color-picker
@update:value="debounceSave">
- <NcButton type="secondary"
- :id="id">
+ <NcButton :id="id"
+ class="field__button"
+ type="primary"
+ :aria-label="t('theming', 'Select a custom color')"
+ data-admin-theming-setting-primary-color-picker>
<template #icon>
<Palette :size="20" />
</template>
- {{ t('theming', 'Change color') }}
+ {{ value }}
</NcButton>
</NcColorPicker>
<div class="field__color-preview" data-admin-theming-setting-primary-color />
@@ -47,6 +49,9 @@
</template>
</NcButton>
</div>
+ <div v-if="description" class="description">
+ {{ description }}
+ </div>
<NcNoteCard v-if="errorMessage"
type="error"
@@ -86,6 +91,10 @@ export default {
type: String,
required: true,
},
+ description: {
+ type: String,
+ default: '',
+ },
value: {
type: String,
required: true,
@@ -110,6 +119,9 @@ export default {
<style lang="scss" scoped>
@import './shared/field.scss';
+.description {
+ color: var(--color-text-maxcontrast);
+}
.field {
&__color-preview {