Skip to content

Commit

Permalink
Merge pull request #42977 from nextcloud/theming/create-color-from-ba…
Browse files Browse the repository at this point in the history
…ckground

theming: Separate `primary` and `background` colors - fix the header menu colors
  • Loading branch information
susnux authored May 21, 2024
2 parents 5ae18ee + 651afb8 commit 576e249
Show file tree
Hide file tree
Showing 76 changed files with 1,473 additions and 799 deletions.
4 changes: 2 additions & 2 deletions apps/dashboard/src/DashboardApp.vue
Original file line number Diff line number Diff line change
Expand Up @@ -471,8 +471,8 @@ export default {
background-attachment: fixed;
> h2 {
// this is shown directly on the background which has `color-primary`, so we need `color-primary-text`
color: var(--color-primary-text);
// this is shown directly on the background image / color
color: var(--color-background-plain-text);
text-align: center;
font-size: 32px;
line-height: 130%;
Expand Down
6 changes: 3 additions & 3 deletions apps/settings/src/components/AdminAI.vue
Original file line number Diff line number Diff line change
Expand Up @@ -190,9 +190,9 @@ export default {

.draggable__number {
border-radius: 20px;
border: 2px solid var(--color-primary-default);
color: var(--color-primary-default);
padding: 0px 7px;
border: 2px solid var(--color-primary-element);
color: var(--color-primary-element);
padding: 0px 7px;
margin-right: 3px;
}

Expand Down
4 changes: 2 additions & 2 deletions apps/theming/css/default.css
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@
--primary-invert-if-bright: no;
--primary-invert-if-dark: invert(100%);
--color-primary: #00679e;
--color-primary-default: #0082c9;
--color-primary-text: #ffffff;
--color-primary-hover: #3285b1;
--color-primary-light: #e5eff5;
Expand All @@ -85,6 +84,7 @@
--color-primary-element-light-hover: #dbe4ea;
--color-primary-element-light-text: #00293f;
--gradient-primary-background: linear-gradient(40deg, var(--color-primary) 0%, var(--color-primary-hover) 100%);
--image-background-default: url('/apps/theming/img/background/kamil-porembinski-clouds.jpg');
--color-background-plain: #00679e;
--color-background-plain-text: #ffffff;
--image-background: url('/apps/theming/img/background/kamil-porembinski-clouds.jpg');
}
19 changes: 8 additions & 11 deletions apps/theming/lib/Capabilities.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ public function __construct(ThemingDefaults $theming, Util $util, IURLGenerator
* color-element-dark: string,
* logo: string,
* background: string,
* background-text: string,
* background-plain: bool,
* background-default: bool,
* logoheader: string,
Expand All @@ -94,15 +95,13 @@ public function __construct(ThemingDefaults $theming, Util $util, IURLGenerator
*/
public function getCapabilities() {
$color = $this->theming->getDefaultColorPrimary();
// Same as in DefaultTheme
if ($color === BackgroundService::DEFAULT_COLOR) {
$color = BackgroundService::DEFAULT_ACCESSIBLE_COLOR;
}
$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->getColorBackground();
$backgroundText = $this->theming->getTextColorBackground();
$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) {
Expand All @@ -112,10 +111,7 @@ public function getCapabilities() {
* @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();

$backgroundImage = $this->config->getUserValue($user->getUID(), Application::APP_ID, 'background_image', BackgroundService::BACKGROUND_DEFAULT);
if ($backgroundImage === BackgroundService::BACKGROUND_CUSTOM) {
Expand All @@ -126,7 +122,7 @@ public function getCapabilities() {
$background = $this->url->linkTo(Application::APP_ID, "img/background/$backgroundImage");
} elseif ($backgroundImage !== BackgroundService::BACKGROUND_DEFAULT) {
$backgroundPlain = true;
$background = $color;
$background = $backgroundColor;
}
}

Expand All @@ -142,6 +138,7 @@ public function getCapabilities() {
'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()),
Expand Down
9 changes: 7 additions & 2 deletions apps/theming/lib/Command/UpdateConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@

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;
Expand Down Expand Up @@ -128,8 +128,13 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$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;
}
Expand Down
15 changes: 6 additions & 9 deletions apps/theming/lib/Controller/ThemingController.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,11 @@
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;

Expand All @@ -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;
Expand All @@ -86,8 +82,6 @@ public function __construct(
IConfig $config,
ThemingDefaults $themingDefaults,
IL10N $l,
ITempManager $tempManager,
IAppData $appData,
IURLGenerator $urlGenerator,
IAppManager $appManager,
ImageManager $imageManager,
Expand All @@ -98,8 +92,6 @@ public function __construct(
$this->themingDefaults = $themingDefaults;
$this->l10n = $l;
$this->config = $config;
$this->tempManager = $tempManager;
$this->appData = $appData;
$this->urlGenerator = $urlGenerator;
$this->appManager = $appManager;
$this->imageManager = $imageManager;
Expand Down Expand Up @@ -151,7 +143,12 @@ public function updateStylesheet($setting, $value) {
$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');
}
Expand Down
8 changes: 4 additions & 4 deletions apps/theming/lib/Controller/UserThemeController.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -72,7 +71,6 @@ public function __construct(string $appName,
BackgroundService $backgroundService) {
parent::__construct($appName, $request);
$this->config = $config;
$this->userSession = $userSession;
$this->themesService = $themesService;
$this->themingDefaults = $themingDefaults;
$this->backgroundService = $backgroundService;
Expand Down Expand Up @@ -186,7 +184,8 @@ public function deleteBackground(): JSONResponse {
$this->backgroundService->deleteBackgroundImage();
return new JSONResponse([
'backgroundImage' => null,
'backgroundColor' => $this->themingDefaults->getColorPrimary(),
'backgroundColor' => $this->themingDefaults->getColorBackground(),
'primaryColor' => $this->themingDefaults->getColorPrimary(),
'version' => $currentVersion,
]);
}
Expand Down Expand Up @@ -241,7 +240,8 @@ public function setBackground(string $type = BackgroundService::BACKGROUND_DEFAU

return new JSONResponse([
'backgroundImage' => $this->config->getUserValue($this->userId, Application::APP_ID, 'background_image', BackgroundService::BACKGROUND_DEFAULT),
'backgroundColor' => $this->themingDefaults->getColorPrimary(),
'backgroundColor' => $this->themingDefaults->getColorBackground(),
'primaryColor' => $this->themingDefaults->getColorPrimary(),
'version' => $currentVersion,
]);
}
Expand Down
83 changes: 49 additions & 34 deletions apps/theming/lib/ImageManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*
* @author Christoph Wurst <[email protected]>
* @author Daniel Kesselberg <[email protected]>
* @author Ferdinand Thiessen <[email protected]>
* @author Gary Kim <[email protected]>
* @author Jacob Neplokh <[email protected]>
* @author John Molakvoæ <[email protected]>
Expand Down Expand Up @@ -56,6 +57,7 @@ public function __construct(
private ICacheFactory $cacheFactory,
private LoggerInterface $logger,
private ITempManager $tempManager,
private BackgroundService $backgroundService,
) {
}

Expand All @@ -77,7 +79,11 @@ public function getImageUrl(string $key): string {
case 'favicon':
return $this->urlGenerator->imagePath('core', 'logo/logo.png') . '?v=' . $cacheBusterCounter;
case 'background':
return $this->urlGenerator->linkTo(Application::APP_ID, 'img/background/' . BackgroundService::DEFAULT_BACKGROUND_IMAGE);
// Removing the background defines its mime as 'backgroundColor'
$mimeSetting = $this->config->getAppValue('theming', 'backgroundMime', '');
if ($mimeSetting !== 'backgroundColor') {
return $this->urlGenerator->linkTo(Application::APP_ID, 'img/background/' . BackgroundService::DEFAULT_BACKGROUND_IMAGE);
}
}
return '';
}
Expand Down Expand Up @@ -227,47 +233,56 @@ public function updateImage(string $key, string $tmpFile): string {
throw new \Exception('Unsupported image type: ' . $detectedMimeType);
}

if ($key === 'background' && $this->shouldOptimizeBackgroundImage($detectedMimeType, filesize($tmpFile))) {
try {
// Optimize the image since some people may upload images that will be
// either to big or are not progressive rendering.
$newImage = @imagecreatefromstring(file_get_contents($tmpFile));
if ($newImage === false) {
throw new \Exception('Could not read background image, possibly corrupted.');
}
if ($key === 'background') {
if ($this->shouldOptimizeBackgroundImage($detectedMimeType, filesize($tmpFile))) {
try {
// Optimize the image since some people may upload images that will be
// either to big or are not progressive rendering.
$newImage = @imagecreatefromstring(file_get_contents($tmpFile));
if ($newImage === false) {
throw new \Exception('Could not read background image, possibly corrupted.');
}

// Preserve transparency
imagesavealpha($newImage, true);
imagealphablending($newImage, true);
// Preserve transparency
imagesavealpha($newImage, true);
imagealphablending($newImage, true);

$newWidth = (imagesx($newImage) < 4096 ? imagesx($newImage) : 4096);
$newHeight = (int)(imagesy($newImage) / (imagesx($newImage) / $newWidth));
$outputImage = imagescale($newImage, $newWidth, $newHeight);
if ($outputImage === false) {
throw new \Exception('Could not scale uploaded background image.');
}
$imageWidth = imagesx($newImage);
$imageHeight = imagesy($newImage);

$newTmpFile = $this->tempManager->getTemporaryFile();
imageinterlace($outputImage, true);
// Keep jpeg images encoded as jpeg
if (str_contains($detectedMimeType, 'image/jpeg')) {
if (!imagejpeg($outputImage, $newTmpFile, 90)) {
throw new \Exception('Could not recompress background image as JPEG');
/** @var int */
$newWidth = min(4096, $imageWidth);
$newHeight = intval($imageHeight / ($imageWidth / $newWidth));
$outputImage = imagescale($newImage, $newWidth, $newHeight);
if ($outputImage === false) {
throw new \Exception('Could not scale uploaded background image.');
}
} else {
if (!imagepng($outputImage, $newTmpFile, 8)) {
throw new \Exception('Could not recompress background image as PNG');

$newTmpFile = $this->tempManager->getTemporaryFile();
imageinterlace($outputImage, true);
// Keep jpeg images encoded as jpeg
if (str_contains($detectedMimeType, 'image/jpeg')) {
if (!imagejpeg($outputImage, $newTmpFile, 90)) {
throw new \Exception('Could not recompress background image as JPEG');
}
} else {
if (!imagepng($outputImage, $newTmpFile, 8)) {
throw new \Exception('Could not recompress background image as PNG');
}
}
}
$tmpFile = $newTmpFile;
imagedestroy($outputImage);
} catch (\Exception $e) {
if (is_resource($outputImage) || $outputImage instanceof \GdImage) {
$tmpFile = $newTmpFile;
imagedestroy($outputImage);
}
} catch (\Exception $e) {
if (isset($outputImage) && is_resource($outputImage) || $outputImage instanceof \GdImage) {
imagedestroy($outputImage);
}

$this->logger->debug($e->getMessage());
$this->logger->debug($e->getMessage());
}
}

// For background images we need to announce it
$this->backgroundService->setGlobalBackground($tmpFile);
}

$target->putContent(file_get_contents($tmpFile));
Expand Down
16 changes: 13 additions & 3 deletions apps/theming/lib/Listener/BeforePreferenceListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,24 @@ public function handle(Event $event): void {
}

private function handleThemingValues(BeforePreferenceSetEvent|BeforePreferenceDeletedEvent $event): void {
if ($event->getConfigKey() !== 'shortcuts_disabled') {
$allowedKeys = ['shortcuts_disabled', 'primary_color'];

if (!in_array($event->getConfigKey(), $allowedKeys)) {
// Not allowed config key
return;
}

if ($event instanceof BeforePreferenceSetEvent) {
$event->setValid($event->getConfigValue() === 'yes');
return;
switch ($event->getConfigKey()) {
case 'shortcuts_disabled':
$event->setValid($event->getConfigValue() === 'yes');
break;
case 'primary_color':
$event->setValid(preg_match('/^\#([0-9a-f]{3}|[0-9a-f]{6})$/i', $event->getConfigValue()) === 1);
break;
default:
$event->setValid(false);
}
}

$event->setValid(true);
Expand Down
Loading

0 comments on commit 576e249

Please sign in to comment.