mirror of https://github.com/nextcloud/server
Allow to remove the background and select a custom colour
Signed-off-by: John Molakvoæ <skjnldsv@protonmail.com>
This commit is contained in:
parent
a884f311b7
commit
cedae7c6d7
|
@ -88,6 +88,11 @@ return [
|
|||
'url' => '/background/{type}',
|
||||
'verb' => 'POST',
|
||||
],
|
||||
[
|
||||
'name' => 'userTheme#deleteBackground',
|
||||
'url' => '/background/custom',
|
||||
'verb' => 'DELETE',
|
||||
],
|
||||
],
|
||||
'ocs' => [
|
||||
[
|
||||
|
|
|
@ -0,0 +1,148 @@
|
|||
#theming input {
|
||||
width: 230px;
|
||||
}
|
||||
#theming input:focus,
|
||||
#theming input:active {
|
||||
padding-right: 30px;
|
||||
}
|
||||
#theming .fileupload {
|
||||
display: none;
|
||||
}
|
||||
#theming div > label {
|
||||
position: relative;
|
||||
}
|
||||
#theming .theme-undo {
|
||||
position: absolute;
|
||||
top: -7px;
|
||||
right: 4px;
|
||||
cursor: pointer;
|
||||
opacity: 0.3;
|
||||
padding: 7px;
|
||||
vertical-align: top;
|
||||
display: inline-block;
|
||||
visibility: hidden;
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
}
|
||||
#theming form.uploadButton {
|
||||
width: 411px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
#theming form .theme-undo,
|
||||
#theming .theme-remove-bg {
|
||||
cursor: pointer;
|
||||
opacity: 0.3;
|
||||
padding: 7px;
|
||||
vertical-align: top;
|
||||
display: inline-block;
|
||||
float: right;
|
||||
position: relative;
|
||||
top: 4px;
|
||||
right: 0px;
|
||||
visibility: visible;
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
margin-left: auto;
|
||||
}
|
||||
#theming form .theme-undo:not([style*="display:"]) ~ .theme-remove-bg {
|
||||
margin-left: 0;
|
||||
}
|
||||
#theming input[type=text]:hover + .theme-undo,
|
||||
#theming input[type=text] + .theme-undo:hover,
|
||||
#theming input[type=text]:focus + .theme-undo,
|
||||
#theming input[type=text]:active + .theme-undo,
|
||||
#theming input[type=url]:hover + .theme-undo,
|
||||
#theming input[type=url] + .theme-undo:hover,
|
||||
#theming input[type=url]:focus + .theme-undo,
|
||||
#theming input[type=url]:active + .theme-undo {
|
||||
visibility: visible;
|
||||
}
|
||||
#theming label span {
|
||||
display: inline-block;
|
||||
min-width: 175px;
|
||||
max-width: 175px;
|
||||
white-space: wrap;
|
||||
padding: 8px 0px;
|
||||
vertical-align: top;
|
||||
}
|
||||
#theming .icon-upload,
|
||||
#theming .uploadButton .icon-loading-small {
|
||||
padding: 8px 20px;
|
||||
width: 20px;
|
||||
margin: 2px 0px;
|
||||
min-height: 32px;
|
||||
display: inline-block;
|
||||
}
|
||||
#theming #theming_settings_status {
|
||||
height: 26px;
|
||||
margin: 10px;
|
||||
}
|
||||
#theming #theming_settings_loading {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin-right: 10px;
|
||||
}
|
||||
#theming #theming_settings_msg {
|
||||
vertical-align: middle;
|
||||
border-radius: 3px;
|
||||
}
|
||||
#theming #theming-preview {
|
||||
width: 230px;
|
||||
height: 140px;
|
||||
background-size: cover;
|
||||
background-position: center center;
|
||||
text-align: center;
|
||||
margin-left: 178px;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 20px;
|
||||
cursor: pointer;
|
||||
background-color: var(--color-primary-default);
|
||||
background-image: var(--image-background-default, var(--image-background-plain, linear-gradient(40deg, #0082c9 0%, #30b6ff 100%)));
|
||||
}
|
||||
#theming #theming-preview #theming-preview-logo {
|
||||
cursor: pointer;
|
||||
width: 20%;
|
||||
height: 20%;
|
||||
margin-top: 20px;
|
||||
display: inline-block;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
background-image: var(--image-logo, url("../../../core/img/logo/logo.svg"));
|
||||
}
|
||||
#theming .theming-hints {
|
||||
margin-top: 20px;
|
||||
}
|
||||
#theming .image-preview {
|
||||
display: inline-block;
|
||||
width: 80px;
|
||||
height: 36px;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
#theming #theming-preview-logoheader {
|
||||
background-image: var(--image-logoheader);
|
||||
}
|
||||
#theming #theming-preview-favicon {
|
||||
background-image: var(--image-favicon);
|
||||
}
|
||||
#theming #user-theming {
|
||||
margin-top: 44px;
|
||||
display: flex;
|
||||
}
|
||||
#theming #user-theming > div {
|
||||
max-width: 400px;
|
||||
margin-bottom: 44px;
|
||||
}
|
||||
|
||||
/* transition effects for theming value changes */
|
||||
#header {
|
||||
transition: background-color 500ms linear;
|
||||
}
|
||||
#header svg, #header img {
|
||||
transition: 500ms filter linear;
|
||||
}
|
||||
|
||||
/*# sourceMappingURL=settings-admin.css.map */
|
|
@ -0,0 +1,168 @@
|
|||
#theming {
|
||||
input {
|
||||
width: 230px;
|
||||
}
|
||||
|
||||
input:focus,
|
||||
input:active {
|
||||
padding-right: 30px;
|
||||
}
|
||||
|
||||
.fileupload {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div > label {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.theme-undo {
|
||||
position: absolute;
|
||||
top: -7px; // input padding
|
||||
right: 4px; // input right margin + border
|
||||
cursor: pointer;
|
||||
opacity: .3;
|
||||
padding: 7px;
|
||||
vertical-align: top;
|
||||
display: inline-block;
|
||||
visibility: hidden;
|
||||
height: 32px; // height of input
|
||||
width: 32px; // height of input
|
||||
}
|
||||
form.uploadButton {
|
||||
width: 411px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
form .theme-undo,
|
||||
.theme-remove-bg {
|
||||
cursor: pointer;
|
||||
opacity: .3;
|
||||
padding: 7px;
|
||||
vertical-align: top;
|
||||
display: inline-block;
|
||||
float: right;
|
||||
position: relative;
|
||||
top: 4px;
|
||||
right: 0px;
|
||||
visibility: visible;
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
// right align
|
||||
margin-left: auto;
|
||||
}
|
||||
form .theme-undo:not([style*="display:"]) ~ .theme-remove-bg {
|
||||
// Only align the undo button if both are shown
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
input[type='text']:hover + .theme-undo,
|
||||
input[type='text'] + .theme-undo:hover,
|
||||
input[type='text']:focus + .theme-undo,
|
||||
input[type='text']:active + .theme-undo,
|
||||
input[type='url']:hover + .theme-undo,
|
||||
input[type='url'] + .theme-undo:hover,
|
||||
input[type='url']:focus + .theme-undo,
|
||||
input[type='url']:active + .theme-undo{
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
label span {
|
||||
display: inline-block;
|
||||
min-width: 175px;
|
||||
max-width: 175px;
|
||||
white-space: wrap;
|
||||
padding: 8px 0px;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.icon-upload,
|
||||
.uploadButton .icon-loading-small {
|
||||
padding: 8px 20px;
|
||||
width: 20px;
|
||||
margin: 2px 0px;
|
||||
min-height: 32px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#theming_settings_status {
|
||||
height: 26px;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
#theming_settings_loading {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
#theming_settings_msg {
|
||||
vertical-align: middle;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
#theming-preview {
|
||||
width: 230px;
|
||||
height: 140px;
|
||||
background-size: cover;
|
||||
background-position: center center;
|
||||
text-align: center;
|
||||
margin-left: 178px;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 20px;
|
||||
cursor: pointer;
|
||||
background-color: var(--color-primary-default);
|
||||
background-image: var(--image-background-default, var(--image-background-plain, linear-gradient(40deg, #0082c9 0%, #30b6ff 100%)));
|
||||
|
||||
#theming-preview-logo {
|
||||
cursor: pointer;
|
||||
width: 20%;
|
||||
height: 20%;
|
||||
margin-top: 20px;
|
||||
display: inline-block;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
background-image: var(--image-logo, url('../../../core/img/logo/logo.svg'));
|
||||
}
|
||||
}
|
||||
|
||||
.theming-hints {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.image-preview {
|
||||
display: inline-block;
|
||||
width: 80px;
|
||||
height: 36px;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
#theming-preview-logoheader {
|
||||
// Only using --image-logoheader to show the custom value only
|
||||
background-image: var(--image-logoheader);
|
||||
}
|
||||
|
||||
#theming-preview-favicon {
|
||||
background-image: var(--image-favicon);
|
||||
}
|
||||
|
||||
#user-theming {
|
||||
margin-top: 44px;
|
||||
display: flex;
|
||||
& > div {
|
||||
max-width: 400px;
|
||||
margin-bottom: 44px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* transition effects for theming value changes */
|
||||
#header {
|
||||
transition: background-color 500ms linear;
|
||||
svg, img {
|
||||
transition: 500ms filter linear;
|
||||
}
|
||||
}
|
|
@ -36,10 +36,6 @@ class UpdateConfig extends Command {
|
|||
'name', 'url', 'imprintUrl', 'privacyUrl', 'slogan', 'color', 'disable-user-theming'
|
||||
];
|
||||
|
||||
public const SUPPORTED_IMAGE_KEYS = [
|
||||
'background', 'logo', 'favicon', 'logoheader'
|
||||
];
|
||||
|
||||
private $themingDefaults;
|
||||
private $imageManager;
|
||||
private $config;
|
||||
|
@ -87,14 +83,14 @@ class UpdateConfig extends Command {
|
|||
$value = $this->config->getAppValue('theming', $key, '');
|
||||
$output->writeln('- ' . $key . ': ' . $value . '');
|
||||
}
|
||||
foreach (self::SUPPORTED_IMAGE_KEYS as $key) {
|
||||
foreach (ImageManager::SUPPORTED_IMAGE_KEYS as $key) {
|
||||
$value = $this->config->getAppValue('theming', $key . 'Mime', '');
|
||||
$output->writeln('- ' . $key . ': ' . $value . '');
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!in_array($key, self::SUPPORTED_KEYS, true) && !in_array($key, self::SUPPORTED_IMAGE_KEYS, true)) {
|
||||
if (!in_array($key, self::SUPPORTED_KEYS, true) && !in_array($key, ImageManager::SUPPORTED_IMAGE_KEYS, true)) {
|
||||
$output->writeln('<error>Invalid config key provided</error>');
|
||||
return 1;
|
||||
}
|
||||
|
@ -115,7 +111,7 @@ class UpdateConfig extends Command {
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (in_array($key, self::SUPPORTED_IMAGE_KEYS, true)) {
|
||||
if (in_array($key, ImageManager::SUPPORTED_IMAGE_KEYS, true)) {
|
||||
if (strpos($value, '/') !== 0) {
|
||||
$output->writeln('<error>The image file needs to be provided as an absolute path: ' . $value . '.</error>');
|
||||
return 1;
|
||||
|
|
|
@ -155,21 +155,34 @@ class UserThemeController extends OCSController {
|
|||
/**
|
||||
* @NoAdminRequired
|
||||
*/
|
||||
public function setBackground(string $type = 'default', string $value = ''): JSONResponse {
|
||||
public function deleteBackground(): JSONResponse {
|
||||
$currentVersion = (int)$this->config->getUserValue($this->userId, Application::APP_ID, 'userCacheBuster', '0');
|
||||
$this->backgroundService->deleteBackgroundImage();
|
||||
return new JSONResponse([
|
||||
'backgroundImage' => null,
|
||||
'backgroundColor' => $this->themingDefaults->getColorPrimary(),
|
||||
'version' => $currentVersion,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @NoAdminRequired
|
||||
*/
|
||||
public function setBackground(string $type = BackgroundService::BACKGROUND_DEFAULT, string $value = ''): JSONResponse {
|
||||
$currentVersion = (int)$this->config->getUserValue($this->userId, Application::APP_ID, 'userCacheBuster', '0');
|
||||
|
||||
try {
|
||||
switch ($type) {
|
||||
case 'shipped':
|
||||
case BackgroundService::BACKGROUND_SHIPPED:
|
||||
$this->backgroundService->setShippedBackground($value);
|
||||
break;
|
||||
case 'custom':
|
||||
case BackgroundService::BACKGROUND_CUSTOM:
|
||||
$this->backgroundService->setFileBackground($value);
|
||||
break;
|
||||
case 'color':
|
||||
$this->backgroundService->setColorBackground($value);
|
||||
break;
|
||||
case 'default':
|
||||
case BackgroundService::BACKGROUND_DEFAULT:
|
||||
$this->backgroundService->setDefaultBackground();
|
||||
break;
|
||||
default:
|
||||
|
@ -185,8 +198,8 @@ class UserThemeController extends OCSController {
|
|||
$this->config->setUserValue($this->userId, Application::APP_ID, 'userCacheBuster', (string)$currentVersion);
|
||||
|
||||
return new JSONResponse([
|
||||
'type' => $type,
|
||||
'value' => $value,
|
||||
'backgroundImage' => $this->config->getUserValue($this->userId, Application::APP_ID, 'background_image', BackgroundService::BACKGROUND_DEFAULT),
|
||||
'backgroundColor' => $this->themingDefaults->getColorPrimary(),
|
||||
'version' => $currentVersion,
|
||||
]);
|
||||
}
|
||||
|
|
|
@ -33,6 +33,8 @@
|
|||
*/
|
||||
namespace OCA\Theming;
|
||||
|
||||
use OCA\Theming\AppInfo\Application;
|
||||
use OCA\Theming\Service\BackgroundService;
|
||||
use OCP\Files\IAppData;
|
||||
use OCP\Files\NotFoundException;
|
||||
use OCP\Files\NotPermittedException;
|
||||
|
@ -45,7 +47,7 @@ use OCP\ITempManager;
|
|||
use OCP\IURLGenerator;
|
||||
|
||||
class ImageManager {
|
||||
public const SupportedImageKeys = ['background', 'logo', 'logoheader', 'favicon'];
|
||||
public const SUPPORTED_IMAGE_KEYS = ['background', 'logo', 'logoheader', 'favicon'];
|
||||
|
||||
/** @var IConfig */
|
||||
private $config;
|
||||
|
@ -74,8 +76,14 @@ class ImageManager {
|
|||
$this->appData = $appData;
|
||||
}
|
||||
|
||||
public function getImageUrl(string $key, bool $useSvg = true): string {
|
||||
$cacheBusterCounter = $this->config->getAppValue('theming', 'cachebuster', '0');
|
||||
/**
|
||||
* Get a globally defined image (admin theming settings)
|
||||
*
|
||||
* @param string $key the image key
|
||||
* @return string the image url
|
||||
*/
|
||||
public function getImageUrl(string $key): string {
|
||||
$cacheBusterCounter = $this->config->getAppValue(Application::APP_ID, 'cachebuster', '0');
|
||||
if ($this->hasImage($key)) {
|
||||
return $this->urlGenerator->linkToRoute('theming.Theming.getImage', [ 'key' => $key ]) . '?v=' . $cacheBusterCounter;
|
||||
}
|
||||
|
@ -86,13 +94,16 @@ class ImageManager {
|
|||
case 'favicon':
|
||||
return $this->urlGenerator->imagePath('core', 'logo/logo.png') . '?v=' . $cacheBusterCounter;
|
||||
case 'background':
|
||||
return $this->urlGenerator->imagePath('core', 'background.png') . '?v=' . $cacheBusterCounter;
|
||||
return $this->urlGenerator->linkTo(Application::APP_ID, "img/background/" . BackgroundService::DEFAULT_BACKGROUND);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
public function getImageUrlAbsolute(string $key, bool $useSvg = true): string {
|
||||
return $this->urlGenerator->getAbsoluteURL($this->getImageUrl($key, $useSvg));
|
||||
/**
|
||||
* Get the absolute url. See getImageUrl
|
||||
*/
|
||||
public function getImageUrlAbsolute(string $key): string {
|
||||
return $this->urlGenerator->getAbsoluteURL($this->getImageUrl($key));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -136,6 +147,20 @@ class ImageManager {
|
|||
return $mimeSetting !== '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, array{mime: string, url: string}>
|
||||
*/
|
||||
public function getCustomImages(): array {
|
||||
$images = [];
|
||||
foreach (self::SUPPORTED_IMAGE_KEYS as $key) {
|
||||
$images[$key] = [
|
||||
'mime' => $this->config->getAppValue('theming', $key . 'Mime', ''),
|
||||
'url' => $this->getImageUrl($key),
|
||||
];
|
||||
}
|
||||
return $images;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get folder for current theming files
|
||||
*
|
||||
|
|
|
@ -27,6 +27,7 @@ declare(strict_types=1);
|
|||
namespace OCA\Theming\Jobs;
|
||||
|
||||
use OCA\Theming\AppInfo\Application;
|
||||
use OCP\App\IAppManager;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\BackgroundJob\IJobList;
|
||||
use OCP\BackgroundJob\QueuedJob;
|
||||
|
|
|
@ -84,16 +84,32 @@ class BeforeTemplateRenderedListener implements IEventListener {
|
|||
if (!empty($user)) {
|
||||
$userId = $user->getUID();
|
||||
|
||||
/** User background */
|
||||
$this->initialState->provideInitialState(
|
||||
'background',
|
||||
$this->config->getUserValue($userId, Application::APP_ID, 'background', 'default'),
|
||||
'backgroundImage',
|
||||
$this->config->getUserValue($userId, Application::APP_ID, 'background_image', BackgroundService::BACKGROUND_DEFAULT),
|
||||
);
|
||||
|
||||
/** User color */
|
||||
$this->initialState->provideInitialState(
|
||||
'backgroundColor',
|
||||
$this->config->getUserValue($userId, Application::APP_ID, 'background_image', BackgroundService::BACKGROUND_DEFAULT),
|
||||
);
|
||||
|
||||
/**
|
||||
* Admin background. `backgroundColor` if disabled,
|
||||
* mime type if defined and empty by default
|
||||
*/
|
||||
$this->initialState->provideInitialState(
|
||||
'themingDefaultBackground',
|
||||
$this->config->getAppValue('theming', 'backgroundMime', ''),
|
||||
);
|
||||
$this->initialState->provideInitialState(
|
||||
'defaultShippedBackground',
|
||||
BackgroundService::DEFAULT_BACKGROUND,
|
||||
);
|
||||
|
||||
/** List of all shipped backgrounds */
|
||||
$this->initialState->provideInitialState(
|
||||
'shippedBackgrounds',
|
||||
BackgroundService::SHIPPED_BACKGROUNDS,
|
||||
|
|
|
@ -48,6 +48,12 @@ class BackgroundService {
|
|||
public const DEFAULT_COLOR = '#0082c9';
|
||||
public const DEFAULT_ACCESSIBLE_COLOR = '#006aa3';
|
||||
|
||||
public const BACKGROUND_SHIPPED = 'shipped';
|
||||
public const BACKGROUND_CUSTOM = 'custom';
|
||||
public const BACKGROUND_DEFAULT = 'default';
|
||||
public const BACKGROUND_DISABLED = 'disabled';
|
||||
|
||||
public const DEFAULT_BACKGROUND = 'kamil-porembinski-clouds.jpg';
|
||||
public const SHIPPED_BACKGROUNDS = [
|
||||
'anatoly-mikhaltsov-butterfly-wing-scale.jpg' => [
|
||||
'attribution' => 'Butterfly wing scale (Anatoly Mikhaltsov, CC BY-SA)',
|
||||
|
@ -153,7 +159,7 @@ class BackgroundService {
|
|||
}
|
||||
|
||||
public function setDefaultBackground(): void {
|
||||
$this->config->deleteUserValue($this->userId, Application::APP_ID, 'background');
|
||||
$this->config->deleteUserValue($this->userId, Application::APP_ID, 'background_image');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -165,7 +171,7 @@ class BackgroundService {
|
|||
* @throws NoUserException
|
||||
*/
|
||||
public function setFileBackground($path): void {
|
||||
$this->config->setUserValue($this->userId, Application::APP_ID, 'background', 'custom');
|
||||
$this->config->setUserValue($this->userId, Application::APP_ID, 'background_image', self::BACKGROUND_DEFAULT);
|
||||
$userFolder = $this->rootFolder->getUserFolder($this->userId);
|
||||
|
||||
/** @var File $file */
|
||||
|
@ -183,27 +189,28 @@ class BackgroundService {
|
|||
if (!array_key_exists($fileName, self::SHIPPED_BACKGROUNDS)) {
|
||||
throw new InvalidArgumentException('The given file name is invalid');
|
||||
}
|
||||
$this->config->setUserValue($this->userId, Application::APP_ID, 'background', $fileName);
|
||||
$this->config->setUserValue($this->userId, Application::APP_ID, 'background_image', $fileName);
|
||||
$this->setColorBackground(self::SHIPPED_BACKGROUNDS[$fileName]['primary_color']);
|
||||
}
|
||||
|
||||
public function setColorBackground(string $color): void {
|
||||
if (!preg_match('/^#([0-9a-f]{3}|[0-9a-f]{6})$/i', $color)) {
|
||||
throw new InvalidArgumentException('The given color is invalid');
|
||||
}
|
||||
$this->config->setUserValue($this->userId, Application::APP_ID, 'background', $color);
|
||||
$this->config->setUserValue($this->userId, Application::APP_ID, 'background_color', $color);
|
||||
}
|
||||
|
||||
public function deleteBackgroundImage(): void {
|
||||
$this->config->setUserValue($this->userId, Application::APP_ID, 'background_image', self::BACKGROUND_DISABLED);
|
||||
}
|
||||
|
||||
public function getBackground(): ?ISimpleFile {
|
||||
$background = $this->config->getUserValue($this->userId, Application::APP_ID, 'background', 'default');
|
||||
if ($background === 'custom') {
|
||||
$background = $this->config->getUserValue($this->userId, Application::APP_ID, 'background_image', self::BACKGROUND_DEFAULT);
|
||||
if ($background === self::BACKGROUND_CUSTOM) {
|
||||
try {
|
||||
return $this->getAppDataFolder()->getFile('background.jpg');
|
||||
} catch (NotFoundException | NotPermittedException $e) {
|
||||
try {
|
||||
// Fallback can be removed in 26
|
||||
$dashboardFolder = $this->appDataFactory->get('dashboard');
|
||||
return $dashboardFolder->getFolder($this->userId)->getFile('background.jpg');
|
||||
} catch (\Throwable $t) {}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
|
|
@ -88,6 +88,10 @@ trait CommonThemeTrait {
|
|||
|
||||
$variables = [];
|
||||
|
||||
// Default last fallback values
|
||||
$variables['--image-background-default'] = $backgroundDeleted ?: "url('" . $this->themingDefaults->getBackground() . "')";
|
||||
$variables['--color-background-plain'] = $this->defaultPrimaryColor;
|
||||
|
||||
// If primary as background has been request or if we have a custom primary colour
|
||||
// let's not define the background image
|
||||
if ($backgroundDeleted) {
|
||||
|
@ -98,7 +102,7 @@ trait CommonThemeTrait {
|
|||
}
|
||||
|
||||
// Register image variables only if custom-defined
|
||||
foreach (ImageManager::SupportedImageKeys as $image) {
|
||||
foreach (ImageManager::SUPPORTED_IMAGE_KEYS as $image) {
|
||||
if ($this->imageManager->hasImage($image)) {
|
||||
$imageUrl = $this->imageManager->getImageUrl($image);
|
||||
if ($image === 'background') {
|
||||
|
@ -110,6 +114,7 @@ trait CommonThemeTrait {
|
|||
$variables['--image-background-size'] = 'cover';
|
||||
$variables['--image-background-default'] = "url('" . $imageUrl . "')";
|
||||
}
|
||||
// --image-background is overriden by user theming
|
||||
$variables["--image-$image"] = "url('" . $imageUrl . "')";
|
||||
}
|
||||
}
|
||||
|
@ -129,32 +134,32 @@ trait CommonThemeTrait {
|
|||
if ($user !== null
|
||||
&& !$this->themingDefaults->isUserThemingDisabled()
|
||||
&& $this->appManager->isEnabledForUser(Application::APP_ID)) {
|
||||
$themingBackground = $this->config->getUserValue($user->getUID(), Application::APP_ID, 'background', 'default');
|
||||
$backgroundImage = $this->config->getUserValue($user->getUID(), Application::APP_ID, 'background_image', BackgroundService::BACKGROUND_DEFAULT);
|
||||
$currentVersion = (int)$this->config->getUserValue($user->getUID(), Application::APP_ID, 'userCacheBuster', '0');
|
||||
|
||||
// The user removed the background
|
||||
if ($backgroundImage === BackgroundService::BACKGROUND_DISABLED) {
|
||||
return [
|
||||
'--image-background' => 'no',
|
||||
'--color-background-plain' => $this->themingDefaults->getColorPrimary(),
|
||||
];
|
||||
}
|
||||
|
||||
// The user uploaded a custom background
|
||||
if ($themingBackground === 'custom') {
|
||||
if ($backgroundImage === BackgroundService::BACKGROUND_CUSTOM) {
|
||||
$cacheBuster = substr(sha1($user->getUID() . '_' . $currentVersion), 0, 8);
|
||||
return [
|
||||
'--image-background' => "url('" . $this->urlGenerator->linkToRouteAbsolute('theming.userTheme.getBackground') . "?v=$cacheBuster')",
|
||||
// TODO: implement primary color from custom background --color-background-plain
|
||||
'--color-background-plain' => $this->themingDefaults->getColorPrimary(),
|
||||
];
|
||||
}
|
||||
|
||||
// The user picked a shipped background
|
||||
if (isset(BackgroundService::SHIPPED_BACKGROUNDS[$themingBackground])) {
|
||||
if (isset(BackgroundService::SHIPPED_BACKGROUNDS[$backgroundImage])) {
|
||||
return [
|
||||
'--image-background' => "url('" . $this->urlGenerator->linkTo(Application::APP_ID, "/img/background/$themingBackground") . "')",
|
||||
'--color-background-plain' => $this->themingDefaults->getColorPrimary(),
|
||||
'--background-image-invert-if-bright' => BackgroundService::SHIPPED_BACKGROUNDS[$themingBackground]['theming'] ?? null === BackgroundService::THEMING_MODE_DARK ? 'invert(100%)' : 'no',
|
||||
];
|
||||
}
|
||||
|
||||
// The user picked a static colour
|
||||
if (substr($themingBackground, 0, 1) === '#') {
|
||||
return [
|
||||
'--image-background' => 'no',
|
||||
'--image-background' => "url('" . $this->urlGenerator->linkTo(Application::APP_ID, "img/background/$backgroundImage") . "')",
|
||||
'--color-background-plain' => $this->themingDefaults->getColorPrimary(),
|
||||
'--background-image-invert-if-bright' => BackgroundService::SHIPPED_BACKGROUNDS[$backgroundImage]['theming'] ?? null === BackgroundService::THEMING_MODE_DARK ? 'invert(100%)' : 'no',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -190,11 +190,6 @@ class DefaultTheme implements ITheme {
|
|||
'--background-invert-if-dark' => 'no',
|
||||
'--background-invert-if-bright' => 'invert(100%)',
|
||||
'--background-image-invert-if-bright' => 'no',
|
||||
|
||||
// Default last fallback values
|
||||
'--image-background' => "url('" . $this->urlGenerator->imagePath('core', 'app-background.jpg') . "')",
|
||||
'--image-background-default' => "url('" . $this->urlGenerator->imagePath('core', 'app-background.jpg') . "')",
|
||||
'--color-background-plain' => $this->defaultPrimaryColor,
|
||||
];
|
||||
|
||||
// Primary variables
|
||||
|
|
|
@ -226,23 +226,12 @@ class ThemingDefaults extends \OC_Defaults {
|
|||
}
|
||||
|
||||
// user-defined primary color
|
||||
$themingBackground = '';
|
||||
if (!empty($user)) {
|
||||
$themingBackground = $this->config->getUserValue($user->getUID(), Application::APP_ID, 'background', '');
|
||||
// If the user selected the default background
|
||||
if ($themingBackground === '') {
|
||||
return BackgroundService::DEFAULT_COLOR;
|
||||
}
|
||||
|
||||
$themingBackground = $this->config->getUserValue($user->getUID(), Application::APP_ID, 'background_color', '');
|
||||
// If the user selected a specific colour
|
||||
if (preg_match('/^\#([0-9a-f]{3}|[0-9a-f]{6})$/i', $themingBackground)) {
|
||||
return $themingBackground;
|
||||
}
|
||||
|
||||
// if the user-selected background is a background reference
|
||||
if (isset(BackgroundService::SHIPPED_BACKGROUNDS[$themingBackground]['primary_color'])) {
|
||||
return BackgroundService::SHIPPED_BACKGROUNDS[$themingBackground]['primary_color'];
|
||||
}
|
||||
}
|
||||
|
||||
// If the default color is not valid, return the default background one
|
||||
|
@ -477,7 +466,7 @@ class ThemingDefaults extends \OC_Defaults {
|
|||
$returnValue = $this->getSlogan();
|
||||
break;
|
||||
case 'color':
|
||||
$returnValue = $this->getColorPrimary();
|
||||
$returnValue = $this->getDefaultColorPrimary();
|
||||
break;
|
||||
case 'logo':
|
||||
case 'logoheader':
|
||||
|
|
|
@ -69,10 +69,7 @@
|
|||
</template>
|
||||
<template v-else>
|
||||
<p>{{ t('theming', 'Set a custom background') }}</p>
|
||||
<BackgroundSettings class="background__grid"
|
||||
:background="background"
|
||||
:theming-default-background="themingDefaultBackground"
|
||||
@update:background="updateBackground" />
|
||||
<BackgroundSettings class="background__grid" @update:background="refreshGlobalStyles" />
|
||||
</template>
|
||||
</NcSettingsSection>
|
||||
</section>
|
||||
|
@ -92,8 +89,6 @@ const availableThemes = loadState('theming', 'themes', [])
|
|||
const enforceTheme = loadState('theming', 'enforceTheme', '')
|
||||
const shortcutsDisabled = loadState('theming', 'shortcutsDisabled', false)
|
||||
|
||||
const background = loadState('theming', 'background')
|
||||
const themingDefaultBackground = loadState('theming', 'themingDefaultBackground')
|
||||
const isUserThemingDisabled = loadState('theming', 'isUserThemingDisabled')
|
||||
|
||||
console.debug('Available themes', availableThemes)
|
||||
|
@ -111,10 +106,10 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
availableThemes,
|
||||
|
||||
// Admin defined configs
|
||||
enforceTheme,
|
||||
shortcutsDisabled,
|
||||
background,
|
||||
themingDefaultBackground,
|
||||
isUserThemingDisabled,
|
||||
}
|
||||
},
|
||||
|
@ -173,9 +168,21 @@ export default {
|
|||
},
|
||||
|
||||
methods: {
|
||||
// Refresh server-side generated theming CSS
|
||||
refreshGlobalStyles() {
|
||||
[...document.head.querySelectorAll('link.theme')].forEach(theme => {
|
||||
const url = new URL(theme.href)
|
||||
url.searchParams.set('v', Date.now())
|
||||
const newTheme = theme.cloneNode()
|
||||
newTheme.href = url.toString()
|
||||
newTheme.onload = () => theme.remove()
|
||||
document.head.append(newTheme)
|
||||
})
|
||||
},
|
||||
|
||||
updateBackground(data) {
|
||||
this.background = (data.type === 'custom' || data.type === 'default') ? data.type : data.value
|
||||
this.$emit('update:background')
|
||||
this.refreshGlobalStyles()
|
||||
},
|
||||
|
||||
changeTheme({ enabled, id }) {
|
||||
|
|
|
@ -26,68 +26,75 @@
|
|||
<template>
|
||||
<div class="background-selector">
|
||||
<!-- Custom background -->
|
||||
<button class="background filepicker"
|
||||
:class="{ active: background === 'custom' }"
|
||||
<button class="background background__filepicker"
|
||||
:class="{ 'background--active': backgroundImage === 'custom' }"
|
||||
tabindex="0"
|
||||
@click="pickFile">
|
||||
{{ t('theming', 'Pick from Files') }}
|
||||
{{ t('theming', 'Custom background') }}
|
||||
</button>
|
||||
|
||||
<!-- Default background -->
|
||||
<button class="background default"
|
||||
<button class="background background__default"
|
||||
:class="{ 'icon-loading': loading === 'default', 'background--active': backgroundImage === 'default' }"
|
||||
:data-color-bright="invertTextColor(Theming.defaultColor)"
|
||||
:style="{ '--border-color': Theming.defaultColor }"
|
||||
tabindex="0"
|
||||
:class="{ 'icon-loading': loading === 'default', active: background === 'default' }"
|
||||
@click="setDefault">
|
||||
{{ t('theming', 'Default image') }}
|
||||
{{ t('theming', 'Default background') }}
|
||||
<Check :size="44" />
|
||||
</button>
|
||||
|
||||
<!-- Custom color picker -->
|
||||
<NcColorPicker v-model="Theming.color" @input="debouncePickColor">
|
||||
<button class="background color"
|
||||
:class="{ active: background === Theming.color}"
|
||||
tabindex="0"
|
||||
<button class="background background__color"
|
||||
:data-color="Theming.color"
|
||||
:data-color-bright="invertTextColor(Theming.color)"
|
||||
:style="{ backgroundColor: Theming.color, color: invertTextColor(Theming.color) ? '#000000' : '#ffffff'}">
|
||||
{{ t('theming', 'Custom color') }}
|
||||
:style="{ backgroundColor: Theming.color, '--border-color': Theming.color}"
|
||||
tabindex="0">
|
||||
{{ t('theming', 'Change color') }}
|
||||
</button>
|
||||
</NcColorPicker>
|
||||
|
||||
<!-- Default admin primary color -->
|
||||
<button class="background color"
|
||||
:class="{ active: background === Theming.defaultColor }"
|
||||
tabindex="0"
|
||||
:data-color="Theming.defaultColor"
|
||||
:data-color-bright="invertTextColor(Theming.defaultColor)"
|
||||
:style="{ color: invertTextColor(Theming.defaultColor) ? '#000000' : '#ffffff'}"
|
||||
@click="debouncePickColor">
|
||||
{{ t('theming', 'Plain background') }}
|
||||
</button>
|
||||
|
||||
<!-- Background set selection -->
|
||||
<button v-for="shippedBackground in shippedBackgrounds"
|
||||
:key="shippedBackground.name"
|
||||
v-tooltip="shippedBackground.details.attribution"
|
||||
:class="{ 'icon-loading': loading === shippedBackground.name, active: background === shippedBackground.name }"
|
||||
tabindex="0"
|
||||
class="background"
|
||||
:class="{ 'icon-loading': loading === shippedBackground.name, 'background--active': backgroundImage === shippedBackground.name }"
|
||||
:data-color-bright="shippedBackground.details.theming === 'dark'"
|
||||
:style="{ 'background-image': 'url(' + shippedBackground.preview + ')' }"
|
||||
@click="setShipped(shippedBackground.name)" />
|
||||
:style="{ backgroundImage: 'url(' + shippedBackground.preview + ')', '--border-color': shippedBackground.details.primary_color }"
|
||||
class="background background__shipped"
|
||||
tabindex="0"
|
||||
@click="setShipped(shippedBackground.name)">
|
||||
<Check :size="44" />
|
||||
</button>
|
||||
|
||||
<!-- Remove background -->
|
||||
<button class="background background__delete"
|
||||
tabindex="0"
|
||||
@click="removeBackground">
|
||||
{{ t('theming', 'Remove background') }}
|
||||
<Close :size="24" />
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { generateUrl } from '@nextcloud/router'
|
||||
import { getBackgroundUrl } from '../helpers/getBackgroundUrl.js'
|
||||
import { generateFilePath, generateUrl } from '@nextcloud/router'
|
||||
import { loadState } from '@nextcloud/initial-state'
|
||||
import { prefixWithBaseUrl } from '../helpers/prefixWithBaseUrl.js'
|
||||
import axios from '@nextcloud/axios'
|
||||
import Check from 'vue-material-design-icons/Check.vue'
|
||||
import Close from 'vue-material-design-icons/Close.vue'
|
||||
import debounce from 'debounce'
|
||||
import NcColorPicker from '@nextcloud/vue/dist/Components/NcColorPicker'
|
||||
import Tooltip from '@nextcloud/vue/dist/Directives/Tooltip'
|
||||
|
||||
const backgroundColor = loadState('theming', 'backgroundColor')
|
||||
const backgroundImage = loadState('theming', 'backgroundImage')
|
||||
const shippedBackgroundList = loadState('theming', 'shippedBackgrounds')
|
||||
const themingDefaultBackground = loadState('theming', 'themingDefaultBackground')
|
||||
const defaultShippedBackground = loadState('theming', 'defaultShippedBackground')
|
||||
|
||||
const prefixWithBaseUrl = (url) => generateFilePath('theming', '', 'img/background/') + url
|
||||
|
||||
export default {
|
||||
name: 'BackgroundSettings',
|
||||
|
@ -96,38 +103,49 @@ export default {
|
|||
},
|
||||
|
||||
components: {
|
||||
Check,
|
||||
Close,
|
||||
NcColorPicker,
|
||||
},
|
||||
|
||||
props: {
|
||||
background: {
|
||||
type: String,
|
||||
default: 'default',
|
||||
},
|
||||
themingDefaultBackground: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
backgroundImage: generateUrl('/apps/theming/background') + '?v=' + Date.now(),
|
||||
loading: false,
|
||||
Theming: loadState('theming', 'data', {}),
|
||||
|
||||
// User background image and color settings
|
||||
backgroundImage,
|
||||
backgroundColor,
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
shippedBackgrounds() {
|
||||
return Object.keys(shippedBackgroundList).map(fileName => {
|
||||
return {
|
||||
name: fileName,
|
||||
url: prefixWithBaseUrl(fileName),
|
||||
preview: prefixWithBaseUrl('preview/' + fileName),
|
||||
details: shippedBackgroundList[fileName],
|
||||
}
|
||||
})
|
||||
return Object.keys(shippedBackgroundList)
|
||||
.map(fileName => {
|
||||
return {
|
||||
name: fileName,
|
||||
url: prefixWithBaseUrl(fileName),
|
||||
preview: prefixWithBaseUrl('preview/' + fileName),
|
||||
details: shippedBackgroundList[fileName],
|
||||
}
|
||||
})
|
||||
.filter(background => {
|
||||
// If the admin did not changed the global background
|
||||
// let's hide the default background to not show it twice
|
||||
if (!this.isGlobalBackgroundDeleted && !this.isGlobalBackgroundDefault) {
|
||||
return background.name !== defaultShippedBackground
|
||||
}
|
||||
return true
|
||||
})
|
||||
},
|
||||
|
||||
isGlobalBackgroundDefault() {
|
||||
return !!themingDefaultBackground
|
||||
},
|
||||
|
||||
isGlobalBackgroundDeleted() {
|
||||
return themingDefaultBackground === 'backgroundColor'
|
||||
},
|
||||
},
|
||||
|
||||
|
@ -163,20 +181,24 @@ export default {
|
|||
: null
|
||||
},
|
||||
|
||||
/**
|
||||
* Update local state
|
||||
*
|
||||
* @param {object} data destructuring object
|
||||
* @param {string} data.backgroundColor background color value
|
||||
* @param {string} data.backgroundImage background image value
|
||||
* @param {string} data.version cache buster number
|
||||
* @see https://github.com/nextcloud/server/blob/c78bd45c64d9695724fc44fe8453a88824b85f2f/apps/theming/lib/Controller/UserThemeController.php#L187-L191
|
||||
*/
|
||||
async update(data) {
|
||||
const background = data.type === 'custom' || data.type === 'default' ? data.type : data.value
|
||||
this.backgroundImage = getBackgroundUrl(background, data.version, this.themingDefaultBackground)
|
||||
if (data.type === 'color' || (data.type === 'default' && this.themingDefaultBackground === 'backgroundColor')) {
|
||||
this.$emit('update:background', data)
|
||||
this.loading = false
|
||||
return
|
||||
}
|
||||
const image = new Image()
|
||||
image.onload = () => {
|
||||
this.$emit('update:background', data)
|
||||
this.loading = false
|
||||
}
|
||||
image.src = this.backgroundImage
|
||||
// Update state
|
||||
this.backgroundImage = data.backgroundImage
|
||||
this.backgroundColor = data.backgroundColor
|
||||
this.Theming.color = data.backgroundColor
|
||||
|
||||
// Notify parent and reload style
|
||||
this.$emit('update:background')
|
||||
this.loading = false
|
||||
},
|
||||
|
||||
async setDefault() {
|
||||
|
@ -197,15 +219,21 @@ export default {
|
|||
this.update(result.data)
|
||||
},
|
||||
|
||||
debouncePickColor: debounce(function() {
|
||||
this.pickColor(...arguments)
|
||||
}, 200),
|
||||
async removeBackground() {
|
||||
this.loading = 'remove'
|
||||
const result = await axios.delete(generateUrl('/apps/theming/background/custom'))
|
||||
this.update(result.data)
|
||||
},
|
||||
|
||||
async pickColor(event) {
|
||||
this.loading = 'color'
|
||||
const color = event?.target?.dataset?.color || this.Theming?.color || '#0082c9'
|
||||
const result = await axios.post(generateUrl('/apps/theming/background/color'), { value: color })
|
||||
this.update(result.data)
|
||||
},
|
||||
debouncePickColor: debounce(function() {
|
||||
this.pickColor(...arguments)
|
||||
}, 200),
|
||||
|
||||
pickFile() {
|
||||
window.OC.dialogs.filepicker(t('theming', 'Select a background from your files'), (path, type) => {
|
||||
|
@ -225,50 +253,61 @@ export default {
|
|||
justify-content: center;
|
||||
|
||||
.background {
|
||||
overflow: hidden;
|
||||
width: 176px;
|
||||
height: 96px;
|
||||
margin: 8px;
|
||||
background-size: cover;
|
||||
background-position: center center;
|
||||
text-align: center;
|
||||
border-radius: var(--border-radius-large);
|
||||
border: 2px solid var(--color-main-background);
|
||||
overflow: hidden;
|
||||
border-radius: var(--border-radius-large);
|
||||
background-position: center center;
|
||||
background-size: cover;
|
||||
|
||||
&.current {
|
||||
background-image: var(--color-background-dark);
|
||||
&__default {
|
||||
background-color: var(--color-primary-default);
|
||||
background-image: var(--image-background-default);
|
||||
}
|
||||
|
||||
&.filepicker, &.default, &.color {
|
||||
&__filepicker, &__default, &__color {
|
||||
border-color: var(--color-border);
|
||||
}
|
||||
|
||||
&.color {
|
||||
background-color: var(--color-primary-default);
|
||||
&__color {
|
||||
color: var(--color-primary-text);
|
||||
background-color: var(--color-primary-default);
|
||||
}
|
||||
|
||||
&.active,
|
||||
// Text and svg icon dark on bright background
|
||||
&[data-color-bright] {
|
||||
color: black;
|
||||
}
|
||||
|
||||
&--active,
|
||||
&:hover,
|
||||
&:focus {
|
||||
border: 2px solid var(--color-primary);
|
||||
// Use theme color primary, see inline css variable in template
|
||||
border: 2px solid var(--border-color, var(--color-primary)) !important;
|
||||
}
|
||||
|
||||
&.active:not(.icon-loading) {
|
||||
&:after {
|
||||
background-image: var(--original-icon-checkmark-white);
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
background-size: 44px;
|
||||
content: '';
|
||||
display: block;
|
||||
height: 100%;
|
||||
}
|
||||
// Icon
|
||||
span {
|
||||
margin: 4px;
|
||||
}
|
||||
|
||||
&[data-color-bright]:after {
|
||||
background-image: var(--original-icon-checkmark-dark);
|
||||
&__default,
|
||||
&__shipped {
|
||||
color: white;
|
||||
span {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&--active:not(.icon-loading) {
|
||||
span {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
/**
|
||||
* @copyright Copyright (c) 2020 Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @author Avior <florian.bouillon@delta-wings.net>
|
||||
* @author Julien Veyssier <eneiluj@posteo.net>
|
||||
* @author Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @license AGPL-3.0-or-later
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
import { generateUrl } from '@nextcloud/router'
|
||||
import { prefixWithBaseUrl } from './prefixWithBaseUrl.js'
|
||||
|
||||
export const getBackgroundUrl = (background, time = 0, themingDefaultBackground = '') => {
|
||||
const enabledThemes = window.OCA?.Theming?.enabledThemes || []
|
||||
const isDarkTheme = (enabledThemes.length === 0 || enabledThemes[0] === 'default')
|
||||
? window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
: enabledThemes.join('').indexOf('dark') !== -1
|
||||
|
||||
if (background === 'default') {
|
||||
if (themingDefaultBackground && themingDefaultBackground !== 'backgroundColor') {
|
||||
return generateUrl('/apps/theming/image/background') + '?v=' + window.OCA.Theming.cacheBuster
|
||||
}
|
||||
|
||||
if (isDarkTheme) {
|
||||
return prefixWithBaseUrl('eduardo-neves-pedra-azul.jpg')
|
||||
}
|
||||
|
||||
return prefixWithBaseUrl('kamil-porembinski-clouds.jpg')
|
||||
} else if (background === 'custom') {
|
||||
return generateUrl('/apps/theming/background') + '?v=' + time
|
||||
}
|
||||
|
||||
return prefixWithBaseUrl(background)
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
/**
|
||||
* @copyright Copyright (c) 2020 Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @author Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @license AGPL-3.0-or-later
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
import { generateFilePath } from '@nextcloud/router'
|
||||
|
||||
export const prefixWithBaseUrl = (url) => generateFilePath('theming', '', 'img/background/') + url
|
|
@ -680,7 +680,7 @@ class ThemingControllerTest extends TestCase {
|
|||
|
||||
public function testGetLoginBackground() {
|
||||
$file = $this->createMock(ISimpleFile::class);
|
||||
$file->method('getName')->willReturn('background.png');
|
||||
$file->method('getName')->willReturn('app-background.jpg');
|
||||
$file->method('getMTime')->willReturn(42);
|
||||
$this->imageManager->expects($this->once())
|
||||
->method('getImage')
|
||||
|
|
|
@ -484,7 +484,7 @@ class ThemingDefaultsTest extends TestCase {
|
|||
$this->config
|
||||
->expects($this->once())
|
||||
->method('getUserValue')
|
||||
->with('user', 'theming', 'background', '')
|
||||
->with('user', 'theming', 'background_image', '')
|
||||
->willReturn(array_keys(BackgroundService::SHIPPED_BACKGROUNDS)[$backgroundIndex]);
|
||||
$this->config
|
||||
->expects($this->exactly(2))
|
||||
|
@ -509,7 +509,7 @@ class ThemingDefaultsTest extends TestCase {
|
|||
$this->config
|
||||
->expects($this->once())
|
||||
->method('getUserValue')
|
||||
->with('user', 'theming', 'background', '')
|
||||
->with('user', 'theming', 'background_image', '')
|
||||
->willReturn('#fff');
|
||||
$this->config
|
||||
->expects($this->exactly(2))
|
||||
|
@ -534,7 +534,7 @@ class ThemingDefaultsTest extends TestCase {
|
|||
$this->config
|
||||
->expects($this->once())
|
||||
->method('getUserValue')
|
||||
->with('user', 'theming', 'background', '')
|
||||
->with('user', 'theming', 'background_image', '')
|
||||
->willReturn('nextcloud');
|
||||
$this->config
|
||||
->expects($this->exactly(3))
|
||||
|
@ -781,7 +781,7 @@ class ThemingDefaultsTest extends TestCase {
|
|||
['logo', true, 'custom-logo?v=0'],
|
||||
['logoheader', true, 'custom-logoheader?v=0'],
|
||||
['favicon', true, 'custom-favicon?v=0'],
|
||||
['background', true, 'custom-background?v=0'],
|
||||
['background_image', true, 'custom-background?v=0'],
|
||||
]);
|
||||
|
||||
$expected = [
|
||||
|
|
Binary file not shown.
|
@ -47,7 +47,7 @@ html {
|
|||
|
||||
body {
|
||||
background-color: var(--color-background-plain, var(--color-main-background));
|
||||
background-image: var(--image-background-plain, var(--image-background));
|
||||
background-image: var(--image-background-plain, var(--image-background, var(--image-background-default)));
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
position: fixed;
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 186 KiB |
Binary file not shown.
Before Width: | Height: | Size: 9.4 KiB |
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 76 KiB |
Loading…
Reference in New Issue