diff options
Diffstat (limited to 'apps/theming')
21 files changed, 244 insertions, 86 deletions
diff --git a/apps/theming/css/default.css b/apps/theming/css/default.css index 1f0a241307b..666d2781ee2 100644 --- a/apps/theming/css/default.css +++ b/apps/theming/css/default.css @@ -1,6 +1,5 @@ :root { --color-main-background: #ffffff; - --color-main-background-not-plain: #0082c9; --color-main-background-rgb: 255,255,255; --color-main-background-translucent: rgba(var(--color-main-background-rgb), .97); --color-main-background-blur: rgba(var(--color-main-background-rgb), .8); @@ -52,10 +51,11 @@ --header-menu-item-height: 44px; --header-menu-profile-item-height: 66px; --breakpoint-mobile: 1024px; - --primary-invert-if-bright: no; --background-invert-if-dark: no; --background-invert-if-bright: invert(100%); - --image-main-background: url('/core/img/app-background.jpg'); + --image-background: url('/core/img/app-background.jpg'); + --color-background-plain: #0082c9; + --primary-invert-if-bright: no; --color-primary: #00639a; --color-primary-default: #0082c9; --color-primary-text: #ffffff; diff --git a/apps/theming/css/settings-admin.css b/apps/theming/css/settings-admin.css index 00d4e2414fa..283d76e4305 100644 --- a/apps/theming/css/settings-admin.css +++ b/apps/theming/css/settings-admin.css @@ -26,6 +26,8 @@ } #theming form.uploadButton { width: 411px; + display: flex; + align-items: center; } #theming form .theme-undo, #theming .theme-remove-bg { @@ -41,6 +43,10 @@ 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, @@ -55,6 +61,8 @@ #theming label span { display: inline-block; min-width: 175px; + max-width: 175px; + white-space: wrap; padding: 8px 0px; vertical-align: top; } @@ -120,6 +128,14 @@ #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 { diff --git a/apps/theming/css/settings-admin.css.map b/apps/theming/css/settings-admin.css.map index b5e657a4e30..bb1b36671de 100644 --- a/apps/theming/css/settings-admin.css.map +++ b/apps/theming/css/settings-admin.css.map @@ -1 +1 @@ -{"version":3,"sourceRoot":"","sources":["settings-admin.scss"],"names":[],"mappings":"AACI;EACI;;AAGJ;AAAA;EAEI;;AAGJ;EACI;;AAGJ;EACI;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEJ;EACI;;AAEJ;AAAA;EAEI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EAQI;;AAGJ;EACI;EACA;EACA;EACA;;AAGJ;AAAA;EAEI;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;;AAGJ;EACI;EACA;EACA;;AAGJ;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAIR;EACI;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;;AAGP;EAEO;;AAGP;EACO;;;AAIR;AACA;EACI;;AACA;EACI","file":"settings-admin.css"}
\ No newline at end of file +{"version":3,"sourceRoot":"","sources":["settings-admin.scss"],"names":[],"mappings":"AACI;EACI;;AAGJ;AAAA;EAEI;;AAGJ;EACI;;AAGJ;EACI;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;;AAEJ;AAAA;EAEI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;;AAEJ;EAEI;;AAGJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EAQI;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;;AAGJ;AAAA;EAEI;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;;AAGJ;EACI;EACA;EACA;;AAGJ;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAIR;EACI;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;;AAGP;EAEO;;AAGP;EACO;;AAGJ;EACI;EACA;;AACD;EACK;EACA;;;AAKZ;AACA;EACI;;AACA;EACI","file":"settings-admin.css"}
\ No newline at end of file diff --git a/apps/theming/css/settings-admin.scss b/apps/theming/css/settings-admin.scss index f43d3b8c417..60d9a823a0b 100644 --- a/apps/theming/css/settings-admin.scss +++ b/apps/theming/css/settings-admin.scss @@ -31,6 +31,8 @@ } form.uploadButton { width: 411px; + display: flex; + align-items: center; } form .theme-undo, .theme-remove-bg { @@ -46,7 +48,14 @@ 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, @@ -61,6 +70,8 @@ label span { display: inline-block; min-width: 175px; + max-width: 175px; + white-space: wrap; padding: 8px 0px; vertical-align: top; } @@ -137,6 +148,15 @@ #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 */ diff --git a/apps/theming/js/settings-admin.js b/apps/theming/js/settings-admin.js index 9fd1639ec3e..5617f7b67c8 100644 --- a/apps/theming/js/settings-admin.js +++ b/apps/theming/js/settings-admin.js @@ -173,6 +173,11 @@ window.addEventListener('DOMContentLoaded', function () { var el = $(this); }); + $('#userThemingDisabled').change(function(e) { + var checked = e.target.checked + setThemingValue('disable-user-theming', checked ? 'yes' : 'no') + }); + function onChange(e) { var el = $(this); var setting = el.parent().find('div[data-setting]').data('setting'); diff --git a/apps/theming/lib/Command/UpdateConfig.php b/apps/theming/lib/Command/UpdateConfig.php index bb226668943..c327c92492f 100644 --- a/apps/theming/lib/Command/UpdateConfig.php +++ b/apps/theming/lib/Command/UpdateConfig.php @@ -33,7 +33,7 @@ use Symfony\Component\Console\Output\OutputInterface; class UpdateConfig extends Command { public const SUPPORTED_KEYS = [ - 'name', 'url', 'imprintUrl', 'privacyUrl', 'slogan', 'color' + 'name', 'url', 'imprintUrl', 'privacyUrl', 'slogan', 'color', 'disable-user-theming' ]; public const SUPPORTED_IMAGE_KEYS = [ diff --git a/apps/theming/lib/Controller/ThemingController.php b/apps/theming/lib/Controller/ThemingController.php index e671c2d53e8..2cac9a345a4 100644 --- a/apps/theming/lib/Controller/ThemingController.php +++ b/apps/theming/lib/Controller/ThemingController.php @@ -151,6 +151,11 @@ class ThemingController extends Controller { $error = $this->l10n->t('The given color is invalid'); } break; + case 'disable-user-theming': + if ($value !== "yes" && $value !== "no") { + $error = $this->l10n->t('Disable-user-theming should be true or false'); + } + break; } if ($error !== null) { return new DataResponse([ diff --git a/apps/theming/lib/ImageManager.php b/apps/theming/lib/ImageManager.php index 60b695f1c90..560a4c981fe 100644 --- a/apps/theming/lib/ImageManager.php +++ b/apps/theming/lib/ImageManager.php @@ -45,6 +45,7 @@ use OCP\ITempManager; use OCP\IURLGenerator; class ImageManager { + public const SupportedImageKeys = ['background', 'logo', 'logoheader', 'favicon']; /** @var IConfig */ private $config; @@ -53,7 +54,6 @@ class ImageManager { /** @var IURLGenerator */ private $urlGenerator; /** @var array */ - private $supportedImageKeys = ['background', 'logo', 'logoheader', 'favicon']; /** @var ICacheFactory */ private $cacheFactory; /** @var ILogger */ @@ -142,7 +142,7 @@ class ImageManager { */ public function getCustomImages(): array { $images = []; - foreach ($this->supportedImageKeys as $key) { + foreach ($this::SupportedImageKeys as $key) { $images[$key] = [ 'mime' => $this->config->getAppValue('theming', $key . 'Mime', ''), 'url' => $this->getImageUrl($key), diff --git a/apps/theming/lib/Settings/Admin.php b/apps/theming/lib/Settings/Admin.php index e89ea6b6fe9..4576bea1df4 100644 --- a/apps/theming/lib/Settings/Admin.php +++ b/apps/theming/lib/Settings/Admin.php @@ -82,6 +82,7 @@ class Admin implements IDelegatedSettings { 'images' => $this->imageManager->getCustomImages(), 'imprintUrl' => $this->themingDefaults->getImprintUrl(), 'privacyUrl' => $this->themingDefaults->getPrivacyUrl(), + 'userThemingDisabled' => $this->themingDefaults->isUserThemingDisabled(), ]; return new TemplateResponse($this->appName, 'settings-admin', $parameters, ''); diff --git a/apps/theming/lib/Settings/Personal.php b/apps/theming/lib/Settings/Personal.php index 5da72bf0158..7ba4da15191 100644 --- a/apps/theming/lib/Settings/Personal.php +++ b/apps/theming/lib/Settings/Personal.php @@ -27,6 +27,7 @@ namespace OCA\Theming\Settings; use OCA\Theming\ITheme; use OCA\Theming\Service\ThemesService; +use OCA\Theming\ThemingDefaults; use OCP\AppFramework\Http\TemplateResponse; use OCP\AppFramework\Services\IInitialState; use OCP\IConfig; @@ -39,15 +40,18 @@ class Personal implements ISettings { private IConfig $config; private ThemesService $themesService; private IInitialState $initialStateService; + private ThemingDefaults $themingDefaults; public function __construct(string $appName, IConfig $config, ThemesService $themesService, - IInitialState $initialStateService) { + IInitialState $initialStateService, + ThemingDefaults $themingDefaults) { $this->appName = $appName; $this->config = $config; $this->themesService = $themesService; $this->initialStateService = $initialStateService; + $this->themingDefaults = $themingDefaults; } public function getForm(): TemplateResponse { @@ -72,6 +76,7 @@ class Personal implements ISettings { $this->initialStateService->provideInitialState('themes', array_values($themes)); $this->initialStateService->provideInitialState('enforceTheme', $enforcedTheme); + $this->initialStateService->provideInitialState('isUserThemingDisabled', $this->themingDefaults->isUserThemingDisabled()); Util::addScript($this->appName, 'theming-settings'); return new TemplateResponse($this->appName, 'settings-personal'); diff --git a/apps/theming/lib/Themes/CommonThemeTrait.php b/apps/theming/lib/Themes/CommonThemeTrait.php index d88a6a319fb..8802933d24d 100644 --- a/apps/theming/lib/Themes/CommonThemeTrait.php +++ b/apps/theming/lib/Themes/CommonThemeTrait.php @@ -24,6 +24,9 @@ declare(strict_types=1); */ namespace OCA\Theming\Themes; +use OCA\Theming\AppInfo\Application; +use OCA\Theming\ImageManager; +use OCA\Theming\Service\BackgroundService; use OCA\Theming\Util; trait CommonThemeTrait { @@ -41,6 +44,15 @@ trait CommonThemeTrait { // primary related colours return [ + // invert filter if primary is too bright + // to be used for legacy reasons only. Use inline + // svg with proper css variable instead or material + // design icons. + // ⚠️ Using 'no' as a value to make sure we specify an + // invalid one with no fallback. 'unset' could here fallback to some + // other theme with media queries + '--primary-invert-if-bright' => $this->util->invertTextColor($this->primaryColor) ? 'invert(100%)' : 'no', + '--color-primary' => $this->primaryColor, '--color-primary-default' => $this->defaultPrimaryColor, '--color-primary-text' => $this->util->invertTextColor($this->primaryColor) ? '#000000' : '#ffffff', @@ -63,4 +75,82 @@ trait CommonThemeTrait { '--gradient-primary-background' => 'linear-gradient(40deg, var(--color-primary) 0%, var(--color-primary-hover) 100%)', ]; } + + /** + * Generate admin theming background-related variables + */ + protected function generateGlobalBackgroundVariables(): array { + $backgroundDeleted = $this->config->getAppValue(Application::APP_ID, 'backgroundMime', '') === 'backgroundColor'; + $hasCustomLogoHeader = $this->imageManager->hasImage('logo') || $this->imageManager->hasImage('logoheader'); + + $variables = []; + + // If primary as background has been request or if we have a custom primary colour + // let's not define the background image + if ($backgroundDeleted && $this->themingDefaults->isUserThemingDisabled()) { + $variables['--image-background-plain'] = 'true'; + $variables['--color-background-plain'] = $this->themingDefaults->getColorPrimary(); + } + + // Register image variables only if custom-defined + foreach (ImageManager::SupportedImageKeys as $image) { + if ($this->imageManager->hasImage($image)) { + $imageUrl = $this->imageManager->getImageUrl($image); + if ($image === 'background') { + // If background deleted is set, ignoring variable + if ($backgroundDeleted) { + continue; + } + $variables['--image-background-size'] = 'cover'; + } + $variables["--image-$image"] = "url('" . $imageUrl . "')"; + } + } + + if ($hasCustomLogoHeader) { + $variables["--image-logoheader-custom"] = 'true'; + } + + return $variables; + } + + /** + * Generate user theming background-related variables + */ + protected function generateUserBackgroundVariables(): array { + $user = $this->userSession->getUser(); + if ($user !== null + && !$this->themingDefaults->isUserThemingDisabled() + && $this->appManager->isEnabledForUser(Application::APP_ID)) { + $themingBackground = $this->config->getUserValue($user->getUID(), Application::APP_ID, 'background', 'default'); + $currentVersion = (int)$this->config->getUserValue($user->getUID(), Application::APP_ID, 'userCacheBuster', '0'); + + // The user uploaded a custom background + if ($themingBackground === '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 + ]; + } + + // The user picked a shipped background + if (isset(BackgroundService::SHIPPED_BACKGROUNDS[$themingBackground])) { + return [ + '--image-background' => "url('" . $this->urlGenerator->linkTo(Application::APP_ID, "/img/background/$themingBackground") . "')", + '--color-background-plain' => $this->themingDefaults->getColorPrimary(), + ]; + } + + // The user picked a static colour + if (substr($themingBackground, 0, 1) === '#') { + return [ + '--image-background' => 'no', + '--color-background-plain' => $this->themingDefaults->getColorPrimary(), + ]; + } + } + + return []; + } } diff --git a/apps/theming/lib/Themes/DefaultTheme.php b/apps/theming/lib/Themes/DefaultTheme.php index aae4c4eca4c..11d65de9a80 100644 --- a/apps/theming/lib/Themes/DefaultTheme.php +++ b/apps/theming/lib/Themes/DefaultTheme.php @@ -24,7 +24,6 @@ declare(strict_types=1); */ namespace OCA\Theming\Themes; -use OCA\Theming\AppInfo\Application; use OCA\Theming\ImageManager; use OCA\Theming\ITheme; use OCA\Theming\Service\BackgroundService; @@ -35,7 +34,6 @@ use OCP\IConfig; use OCP\IL10N; use OCP\IURLGenerator; use OCP\IUserSession; -use OCP\Server; class DefaultTheme implements ITheme { use CommonThemeTrait; @@ -47,6 +45,7 @@ class DefaultTheme implements ITheme { public ImageManager $imageManager; public IConfig $config; public IL10N $l; + public IAppManager $appManager; public string $defaultPrimaryColor; public string $primaryColor; @@ -57,7 +56,8 @@ class DefaultTheme implements ITheme { IURLGenerator $urlGenerator, ImageManager $imageManager, IConfig $config, - IL10N $l) { + IL10N $l, + IAppManager $appManager) { $this->util = $util; $this->themingDefaults = $themingDefaults; $this->userSession = $userSession; @@ -65,6 +65,7 @@ class DefaultTheme implements ITheme { $this->imageManager = $imageManager; $this->config = $config; $this->l = $l; + $this->appManager = $appManager; $this->defaultPrimaryColor = $this->themingDefaults->getDefaultColorPrimary(); $this->primaryColor = $this->themingDefaults->getColorPrimary(); @@ -108,12 +109,8 @@ class DefaultTheme implements ITheme { $colorBoxShadow = $this->util->darken($colorMainBackground, 70); $colorBoxShadowRGB = join(',', $this->util->hexToRGB($colorBoxShadow)); - $hasCustomLogoHeader = $this->imageManager->hasImage('logo') || $this->imageManager->hasImage('logoheader'); - $hasCustomPrimaryColour = !empty($this->config->getAppValue(Application::APP_ID, 'color')); - $variables = [ '--color-main-background' => $colorMainBackground, - '--color-main-background-not-plain' => $this->themingDefaults->getColorPrimary(), '--color-main-background-rgb' => $colorMainBackgroundRGB, '--color-main-background-translucent' => 'rgba(var(--color-main-background-rgb), .97)', '--color-main-background-blur' => 'rgba(var(--color-main-background-rgb), .8)', @@ -190,67 +187,18 @@ class DefaultTheme implements ITheme { // mobile. Keep in sync with core/js/js.js '--breakpoint-mobile' => '1024px', - - // invert filter if primary is too bright - // to be used for legacy reasons only. Use inline - // svg with proper css variable instead or material - // design icons. - // ⚠️ Using 'no' as a value to make sure we specify an - // invalid one with no fallback. 'unset' could here fallback to some - // other theme with media queries - '--primary-invert-if-bright' => $this->util->invertTextColor($this->primaryColor) ? 'invert(100%)' : 'no', '--background-invert-if-dark' => 'no', '--background-invert-if-bright' => 'invert(100%)', - '--image-main-background' => "url('" . $this->urlGenerator->imagePath('core', 'app-background.jpg') . "')", + // Default last fallback values + '--image-background' => "url('" . $this->urlGenerator->imagePath('core', 'app-background.jpg') . "')", + '--color-background-plain' => $this->defaultPrimaryColor, ]; // Primary variables $variables = array_merge($variables, $this->generatePrimaryVariables($colorMainBackground, $colorMainText)); - - $backgroundDeleted = $this->config->getAppValue(Application::APP_ID, 'backgroundMime', '') === 'backgroundColor'; - // If primary as background has been request or if we have a custom primary colour - // let's not define the background image - if ($backgroundDeleted || $hasCustomPrimaryColour) { - $variables["--image-background-plain"] = 'true'; - } - - // Register image variables only if custom-defined - foreach (['logo', 'logoheader', 'favicon', 'background'] as $image) { - if ($this->imageManager->hasImage($image)) { - $imageUrl = $this->imageManager->getImageUrl($image); - if ($image === 'background') { - // If background deleted is set, ignoring variable - if ($backgroundDeleted) { - continue; - } - $variables['--image-background-size'] = 'cover'; - $variables['--image-main-background'] = "url('" . $imageUrl . "')"; - } - $variables["--image-$image"] = "url('" . $imageUrl . "')"; - } - } - - if ($hasCustomLogoHeader) { - $variables["--image-logoheader-custom"] = 'true'; - } - - $appManager = Server::get(IAppManager::class); - $user = $this->userSession->getUser(); - if ($appManager->isEnabledForUser(Application::APP_ID) && $user !== null) { - $themingBackground = $this->config->getUserValue($user->getUID(), Application::APP_ID, 'background', 'default'); - $currentVersion = (int)$this->config->getUserValue($user->getUID(), Application::APP_ID, 'userCacheBuster', '0'); - - if ($themingBackground === 'custom') { - $cacheBuster = substr(sha1($user->getUID() . '_' . $currentVersion), 0, 8); - $variables['--image-main-background'] = "url('" . $this->urlGenerator->linkToRouteAbsolute('theming.userTheme.getBackground') . "?v=$cacheBuster')"; - } elseif (isset(BackgroundService::SHIPPED_BACKGROUNDS[$themingBackground])) { - $variables['--image-main-background'] = "url('" . $this->urlGenerator->linkTo(Application::APP_ID, "/img/background/$themingBackground") . "')"; - } elseif (substr($themingBackground, 0, 1) === '#') { - unset($variables['--image-main-background']); - $variables['--color-main-background-plain'] = $this->themingDefaults->getColorPrimary(); - } - } + $variables = array_merge($variables, $this->generateGlobalBackgroundVariables()); + $variables = array_merge($variables, $this->generateUserBackgroundVariables()); return $variables; } diff --git a/apps/theming/lib/ThemingDefaults.php b/apps/theming/lib/ThemingDefaults.php index 9d5183a6504..eee44e81fda 100644 --- a/apps/theming/lib/ThemingDefaults.php +++ b/apps/theming/lib/ThemingDefaults.php @@ -220,6 +220,10 @@ class ThemingDefaults extends \OC_Defaults { // admin-defined primary color $defaultColor = $this->getDefaultColorPrimary(); + + if ($this->isUserThemingDisabled()) { + return $defaultColor; + } // user-defined primary color $themingBackground = ''; @@ -494,4 +498,11 @@ class ThemingDefaults extends \OC_Defaults { public function getTextColorPrimary() { return $this->util->invertTextColor($this->getColorPrimary()) ? '#000000' : '#ffffff'; } + + /** + * Has the admin disabled user customization + */ + public function isUserThemingDisabled(): bool { + return $this->config->getAppValue('theming', 'disable-user-theming', 'no') === 'yes'; + } } diff --git a/apps/theming/src/UserThemes.vue b/apps/theming/src/UserThemes.vue index c65b19bed7e..4c92e75199d 100644 --- a/apps/theming/src/UserThemes.vue +++ b/apps/theming/src/UserThemes.vue @@ -64,11 +64,16 @@ <NcSettingsSection :title="t('theming', 'Background')" class="background"> - <p>{{ t('theming', 'Set a custom background') }}</p> - <BackgroundSettings class="background__grid" - :background="background" - :theming-default-background="themingDefaultBackground" - @update:background="updateBackground" /> + <template v-if="isUserThemingDisabled"> + <p>{{ t('theming', 'Customization has been disabled by your administrator') }}</p> + </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" /> + </template> </NcSettingsSection> </section> </template> @@ -90,6 +95,7 @@ const shortcutsDisabled = loadState('theming', 'shortcutsDisabled', false) const background = loadState('theming', 'background') const themingDefaultBackground = loadState('theming', 'themingDefaultBackground') const shippedBackgroundList = loadState('theming', 'shippedBackgrounds') +const isUserThemingDisabled = loadState('theming', 'isUserThemingDisabled') console.debug('Available themes', availableThemes) @@ -109,6 +115,7 @@ export default { shortcutsDisabled, background, themingDefaultBackground, + isUserThemingDisabled, } }, diff --git a/apps/theming/templates/settings-admin.php b/apps/theming/templates/settings-admin.php index 6014f0e8579..acaa7b168e8 100644 --- a/apps/theming/templates/settings-admin.php +++ b/apps/theming/templates/settings-admin.php @@ -81,7 +81,7 @@ style('theming', 'settings-admin'); <form class="uploadButton" method="post" action="<?php p($_['uploadLogoRoute']) ?>" data-image-key="background"> <input type="hidden" id="theming-backgroundMime" value="<?php p($_['images']['background']['mime']); ?>" /> <input type="hidden" name="key" value="background" /> - <label for="upload-login-background"><span><?php p($l->t('Login image')) ?></span></label> + <label for="upload-login-background"><span><?php p($l->t('Background and login image')) ?></span></label> <input id="upload-login-background" class="fileupload" name="image" type="file"> <label for="upload-login-background" class="button icon-upload svg" id="upload-login-background" title="<?php p($l->t("Upload new login background")) ?>"></label> <div data-setting="backgroundMime" data-toggle="tooltip" data-original-title="<?php p($l->t('Reset to default')); ?>" class="theme-undo icon icon-history"></div> @@ -93,7 +93,6 @@ style('theming', 'settings-admin'); </div> <h3 class="inlineblock"><?php p($l->t('Advanced options')); ?></h3> - <div class="advanced-options"> <div> <label> @@ -131,6 +130,16 @@ style('theming', 'settings-admin'); <div data-setting="faviconMime" data-toggle="tooltip" data-original-title="<?php p($l->t('Reset to default')); ?>" class="theme-undo icon icon-history"></div> </form> </div> + <div class="advanced-options" id="user-theming"> + <label><span><?php p($l->t('User settings')); ?></span></label> + <div> + <p class="info"> + <?php p($l->t('Although you can select and customize your instance, users can change their background and colors. If you want to enforce your customization, you can check this box.')); ?> + </p> + <input id="userThemingDisabled" class="checkbox" type="checkbox" <?php p($_['userThemingDisabled'] ? 'checked="checked"' : ''); ?> /> + <label for="userThemingDisabled"><?php p($l->t('Disable user theming')) ?></label> + </div> + </div> </div> <div class="theming-hints"> diff --git a/apps/theming/tests/Service/ThemesServiceTest.php b/apps/theming/tests/Service/ThemesServiceTest.php index 62f00ab0e31..ba970b92394 100644 --- a/apps/theming/tests/Service/ThemesServiceTest.php +++ b/apps/theming/tests/Service/ThemesServiceTest.php @@ -34,11 +34,9 @@ use OCA\Theming\Service\ThemesService; use OCA\Theming\Themes\LightTheme; use OCA\Theming\ThemingDefaults; use OCA\Theming\Util; -use OCP\AppFramework\Http\DataResponse; -use OCP\AppFramework\OCS\OCSBadRequestException; use OCP\IConfig; use OCP\IL10N; -use OCP\IRequest; +use OCP\App\IAppManager; use OCP\IURLGenerator; use OCP\IUser; use OCP\IUserSession; @@ -280,6 +278,7 @@ class ThemesServiceTest extends TestCase { $urlGenerator = $this->createMock(IURLGenerator::class); $imageManager = $this->createMock(ImageManager::class); $l10n = $this->createMock(IL10N::class); + $appManager = $this->createMock(IAppManager::class); $this->themes = [ 'default' => new DefaultTheme( @@ -290,6 +289,7 @@ class ThemesServiceTest extends TestCase { $imageManager, $this->config, $l10n, + $appManager, ), 'light' => new LightTheme( $util, @@ -299,6 +299,7 @@ class ThemesServiceTest extends TestCase { $imageManager, $this->config, $l10n, + $appManager, ), 'dark' => new DarkTheme( $util, @@ -308,6 +309,7 @@ class ThemesServiceTest extends TestCase { $imageManager, $this->config, $l10n, + $appManager, ), 'light-highcontrast' => new HighContrastTheme( $util, @@ -317,6 +319,7 @@ class ThemesServiceTest extends TestCase { $imageManager, $this->config, $l10n, + $appManager, ), 'dark-highcontrast' => new DarkHighContrastTheme( $util, @@ -326,6 +329,7 @@ class ThemesServiceTest extends TestCase { $imageManager, $this->config, $l10n, + $appManager, ), 'opendyslexic' => new DyslexiaFont( $util, @@ -335,6 +339,7 @@ class ThemesServiceTest extends TestCase { $imageManager, $this->config, $l10n, + $appManager, ), ]; } diff --git a/apps/theming/tests/Settings/AdminTest.php b/apps/theming/tests/Settings/AdminTest.php index 8f259e29ba5..df884f4f803 100644 --- a/apps/theming/tests/Settings/AdminTest.php +++ b/apps/theming/tests/Settings/AdminTest.php @@ -117,6 +117,7 @@ class AdminTest extends TestCase { 'images' => [], 'imprintUrl' => '', 'privacyUrl' => '', + 'userThemingDisabled' => false, ]; $expected = new TemplateResponse('theming', 'settings-admin', $params, ''); @@ -176,6 +177,7 @@ class AdminTest extends TestCase { 'images' => [], 'imprintUrl' => '', 'privacyUrl' => '', + 'userThemingDisabled' => false ]; $expected = new TemplateResponse('theming', 'settings-admin', $params, ''); diff --git a/apps/theming/tests/Settings/PersonalTest.php b/apps/theming/tests/Settings/PersonalTest.php index 8597461a175..f8f6052a0f8 100644 --- a/apps/theming/tests/Settings/PersonalTest.php +++ b/apps/theming/tests/Settings/PersonalTest.php @@ -40,6 +40,7 @@ use OCA\Theming\Themes\LightTheme; use OCA\Theming\ThemingDefaults; use OCA\Theming\Util; use OCA\Theming\ITheme; +use OCP\App\IAppManager; use OCP\AppFramework\Http\TemplateResponse; use OCP\AppFramework\Services\IInitialState; use OCP\IConfig; @@ -52,6 +53,7 @@ class PersonalTest extends TestCase { private IConfig $config; private ThemesService $themesService; private IInitialState $initialStateService; + private ThemingDefaults $themingDefaults; /** @var ITheme[] */ private $themes; @@ -61,6 +63,7 @@ class PersonalTest extends TestCase { $this->config = $this->createMock(IConfig::class); $this->themesService = $this->createMock(ThemesService::class); $this->initialStateService = $this->createMock(IInitialState::class); + $this->themingDefaults = $this->createMock(ThemingDefaults::class); $this->initThemes(); @@ -73,7 +76,8 @@ class PersonalTest extends TestCase { Application::APP_ID, $this->config, $this->themesService, - $this->initialStateService + $this->initialStateService, + $this->themingDefaults, ); } @@ -107,11 +111,12 @@ class PersonalTest extends TestCase { ->with('enforce_theme', '') ->willReturn($enforcedTheme); - $this->initialStateService->expects($this->exactly(2)) + $this->initialStateService->expects($this->exactly(3)) ->method('provideInitialState') ->withConsecutive( ['themes', $themesState], ['enforceTheme', $enforcedTheme], + ['isUserThemingDisabled', false] ); $expected = new TemplateResponse('theming', 'settings-personal'); @@ -134,6 +139,7 @@ class PersonalTest extends TestCase { $imageManager = $this->createMock(ImageManager::class); $config = $this->createMock(IConfig::class); $l10n = $this->createMock(IL10N::class); + $appManager = $this->createMock(IAppManager::class); $themingDefaults->expects($this->any()) ->method('getColorPrimary') @@ -152,6 +158,7 @@ class PersonalTest extends TestCase { $imageManager, $config, $l10n, + $appManager, ), 'light' => new LightTheme( $util, @@ -161,6 +168,7 @@ class PersonalTest extends TestCase { $imageManager, $config, $l10n, + $appManager, ), 'dark' => new DarkTheme( $util, @@ -170,6 +178,7 @@ class PersonalTest extends TestCase { $imageManager, $config, $l10n, + $appManager, ), 'light-highcontrast' => new HighContrastTheme( $util, @@ -179,6 +188,7 @@ class PersonalTest extends TestCase { $imageManager, $config, $l10n, + $appManager, ), 'dark-highcontrast' => new DarkHighContrastTheme( $util, @@ -188,6 +198,7 @@ class PersonalTest extends TestCase { $imageManager, $config, $l10n, + $appManager, ), 'opendyslexic' => new DyslexiaFont( $util, @@ -197,6 +208,7 @@ class PersonalTest extends TestCase { $imageManager, $config, $l10n, + $appManager, ), ]; } diff --git a/apps/theming/tests/Themes/DefaultThemeTest.php b/apps/theming/tests/Themes/DefaultThemeTest.php index eafd66ef663..eb9f41d378f 100644 --- a/apps/theming/tests/Themes/DefaultThemeTest.php +++ b/apps/theming/tests/Themes/DefaultThemeTest.php @@ -28,6 +28,7 @@ use OCA\Theming\ITheme; use OCA\Theming\Themes\DefaultTheme; use OCA\Theming\ThemingDefaults; use OCA\Theming\Util; +use OCP\App\IAppManager; use OCP\Files\IAppData; use OCP\IConfig; use OCP\IL10N; @@ -48,6 +49,8 @@ class DefaultThemeTest extends TestCase { private $config; /** @var IL10N|MockObject */ private $l10n; + /** @var IAppManager|MockObject */ + private $appManager; private DefaultTheme $defaultTheme; @@ -58,10 +61,11 @@ class DefaultThemeTest extends TestCase { $this->imageManager = $this->createMock(ImageManager::class); $this->config = $this->createMock(IConfig::class); $this->l10n = $this->createMock(IL10N::class); + $this->appManager = $this->createMock(IAppManager::class); $util = new Util( $this->config, - $this->createMock(AppManager::class), + $this->appManager, $this->createMock(IAppData::class) ); @@ -97,6 +101,7 @@ class DefaultThemeTest extends TestCase { $this->imageManager, $this->config, $this->l10n, + $this->appManager, ); parent::setUp(); diff --git a/apps/theming/tests/Themes/DyslexiaFontTest.php b/apps/theming/tests/Themes/DyslexiaFontTest.php index 8a0df960205..1a0f0adebec 100644 --- a/apps/theming/tests/Themes/DyslexiaFontTest.php +++ b/apps/theming/tests/Themes/DyslexiaFontTest.php @@ -29,6 +29,7 @@ use OCA\Theming\ITheme; use OCA\Theming\Themes\DyslexiaFont; use OCA\Theming\ThemingDefaults; use OCA\Theming\Util; +use OCP\App\IAppManager; use OCP\Files\IAppData; use OCP\ICacheFactory; use OCP\IConfig; @@ -51,6 +52,8 @@ class DyslexiaFontTest extends TestCase { private $config; /** @var IL10N|MockObject */ private $l10n; + /** @var IAppManager|MockObject */ + private $appManager; private DyslexiaFont $dyslexiaFont; @@ -60,6 +63,7 @@ class DyslexiaFontTest extends TestCase { $this->imageManager = $this->createMock(ImageManager::class); $this->config = $this->createMock(IConfig::class); $this->l10n = $this->createMock(IL10N::class); + $this->appManager = $this->createMock(IAppManager::class); $util = new Util( $this->config, @@ -104,6 +108,7 @@ class DyslexiaFontTest extends TestCase { $this->imageManager, $this->config, $this->l10n, + $this->appManager, ); parent::setUp(); diff --git a/apps/theming/tests/ThemingDefaultsTest.php b/apps/theming/tests/ThemingDefaultsTest.php index 5106b2551b8..d8940670137 100644 --- a/apps/theming/tests/ThemingDefaultsTest.php +++ b/apps/theming/tests/ThemingDefaultsTest.php @@ -424,20 +424,30 @@ class ThemingDefaultsTest extends TestCase { public function testGetColorPrimaryWithDefault() { $this->config - ->expects($this->once()) + ->expects($this->at(0)) ->method('getAppValue') ->with('theming', 'color', null) ->willReturn($this->defaults->getColorPrimary()); + $this->config + ->expects($this->at(1)) + ->method('getAppValue') + ->with('theming', 'disable-user-theming', 'no') + ->willReturn('no'); $this->assertEquals($this->defaults->getColorPrimary(), $this->template->getColorPrimary()); } - public function testgetColorPrimaryWithCustom() { + public function testGetColorPrimaryWithCustom() { $this->config - ->expects($this->once()) + ->expects($this->at(0)) ->method('getAppValue') ->with('theming', 'color', null) ->willReturn('#fff'); + $this->config + ->expects($this->at(1)) + ->method('getAppValue') + ->with('theming', 'disable-user-theming', 'no') + ->willReturn('no'); $this->assertEquals('#fff', $this->template->getColorPrimary()); } @@ -613,14 +623,16 @@ class ThemingDefaultsTest extends TestCase { ->method('deleteAppValue') ->with('theming', 'color'); $this->config - ->expects($this->exactly(2)) + ->expects($this->exactly(3)) ->method('getAppValue') ->withConsecutive( ['theming', 'cachebuster', '0'], ['theming', 'color', null], + ['theming', 'disable-user-theming', 'no'], )->willReturnOnConsecutiveCalls( '15', $this->defaults->getColorPrimary(), + 'no', ); $this->config ->expects($this->once()) |