mirror of
https://github.com/nextcloud/server.git
synced 2026-02-19 02:38:40 -05:00
feat(theming): Allow to configure primary color separate from background in admin settings
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
This commit is contained in:
parent
9d2c3c1164
commit
705335e6a5
13 changed files with 197 additions and 125 deletions
|
|
@ -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()),
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ namespace OCA\Theming;
|
|||
* @psalm-type ThemingBackground = array{
|
||||
* backgroundImage: ?string,
|
||||
* backgroundColor: string,
|
||||
* primaryColor: string,
|
||||
* version: int,
|
||||
* }
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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', ''),
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Reference in a new issue