Passed
Push — master ( d6ea10...3323b1 )
by John
16:21 queued 13s
created
apps/theming/lib/Themes/DefaultTheme.php 2 patches
Indentation   +172 added lines, -172 removed lines patch added patch discarded remove patch
@@ -36,176 +36,176 @@
 block discarded – undo
36 36
 use OCP\IUserSession;
37 37
 
38 38
 class DefaultTheme implements ITheme {
39
-	use CommonThemeTrait;
40
-
41
-	public Util $util;
42
-	public ThemingDefaults $themingDefaults;
43
-	public IUserSession $userSession;
44
-	public IURLGenerator $urlGenerator;
45
-	public ImageManager $imageManager;
46
-	public IConfig $config;
47
-	public IL10N $l;
48
-	public IAppManager $appManager;
49
-
50
-	public string $defaultPrimaryColor;
51
-	public string $primaryColor;
52
-
53
-	public function __construct(Util $util,
54
-								ThemingDefaults $themingDefaults,
55
-								IUserSession $userSession,
56
-								IURLGenerator $urlGenerator,
57
-								ImageManager $imageManager,
58
-								IConfig $config,
59
-								IL10N $l,
60
-								IAppManager $appManager) {
61
-		$this->util = $util;
62
-		$this->themingDefaults = $themingDefaults;
63
-		$this->userSession = $userSession;
64
-		$this->urlGenerator = $urlGenerator;
65
-		$this->imageManager = $imageManager;
66
-		$this->config = $config;
67
-		$this->l = $l;
68
-		$this->appManager = $appManager;
69
-
70
-		$this->defaultPrimaryColor = $this->themingDefaults->getDefaultColorPrimary();
71
-		$this->primaryColor = $this->themingDefaults->getColorPrimary();
72
-
73
-		// Override default defaultPrimaryColor if set to improve accessibility
74
-		if ($this->primaryColor === BackgroundService::DEFAULT_COLOR) {
75
-			$this->primaryColor = BackgroundService::DEFAULT_ACCESSIBLE_COLOR;
76
-		}
77
-	}
78
-
79
-	public function getId(): string {
80
-		return 'default';
81
-	}
82
-
83
-	public function getType(): int {
84
-		return ITheme::TYPE_THEME;
85
-	}
86
-
87
-	public function getTitle(): string {
88
-		return $this->l->t('System default theme');
89
-	}
90
-
91
-	public function getEnableLabel(): string {
92
-		return $this->l->t('Enable the system default');
93
-	}
94
-
95
-	public function getDescription(): string {
96
-		return $this->l->t('Using the default system appearance.');
97
-	}
98
-
99
-	public function getMediaQuery(): string {
100
-		return '';
101
-	}
102
-
103
-	public function getCSSVariables(): array {
104
-		$colorMainText = '#222222';
105
-		$colorMainTextRgb = join(',', $this->util->hexToRGB($colorMainText));
106
-		$colorTextMaxcontrast = $this->util->lighten($colorMainText, 33);
107
-		$colorMainBackground = '#ffffff';
108
-		$colorMainBackgroundRGB = join(',', $this->util->hexToRGB($colorMainBackground));
109
-		$colorBoxShadow = $this->util->darken($colorMainBackground, 70);
110
-		$colorBoxShadowRGB = join(',', $this->util->hexToRGB($colorBoxShadow));
111
-
112
-		$variables = [
113
-			'--color-main-background' => $colorMainBackground,
114
-			'--color-main-background-rgb' => $colorMainBackgroundRGB,
115
-			'--color-main-background-translucent' => 'rgba(var(--color-main-background-rgb), .97)',
116
-			'--color-main-background-blur' => 'rgba(var(--color-main-background-rgb), .8)',
117
-			'--filter-background-blur' => 'blur(25px)',
118
-
119
-			// to use like this: background-image: linear-gradient(0, var('--gradient-main-background));
120
-			'--gradient-main-background' => 'var(--color-main-background) 0%, var(--color-main-background-translucent) 85%, transparent 100%',
121
-
122
-			// used for different active/hover/focus/disabled states
123
-			'--color-background-hover' => $this->util->darken($colorMainBackground, 4),
124
-			'--color-background-dark' => $this->util->darken($colorMainBackground, 7),
125
-			'--color-background-darker' => $this->util->darken($colorMainBackground, 14),
126
-
127
-			'--color-placeholder-light' => $this->util->darken($colorMainBackground, 10),
128
-			'--color-placeholder-dark' => $this->util->darken($colorMainBackground, 20),
129
-
130
-			// max contrast for WCAG compliance
131
-			'--color-main-text' => $colorMainText,
132
-			'--color-text-maxcontrast' => $colorTextMaxcontrast,
133
-			'--color-text-maxcontrast-default' => $colorTextMaxcontrast,
134
-			'--color-text-maxcontrast-background-blur' => $this->util->darken($colorTextMaxcontrast, 7),
135
-			'--color-text-light' => $colorMainText,
136
-			'--color-text-lighter' => $this->util->lighten($colorMainText, 33),
137
-
138
-			'--color-scrollbar' => 'rgba(' . $colorMainTextRgb . ', .15)',
139
-
140
-			// info/warning/success feedback colours
141
-			'--color-error' => '#e9322d',
142
-			'--color-error-rgb' => join(',', $this->util->hexToRGB('#e9322d')),
143
-			'--color-error-hover' => $this->util->mix('#e9322d', $colorMainBackground, 60),
144
-			'--color-warning' => '#eca700',
145
-			'--color-warning-rgb' => join(',', $this->util->hexToRGB('#eca700')),
146
-			'--color-warning-hover' => $this->util->mix('#eca700', $colorMainBackground, 60),
147
-			'--color-success' => '#46ba61',
148
-			'--color-success-rgb' => join(',', $this->util->hexToRGB('#46ba61')),
149
-			'--color-success-hover' => $this->util->mix('#46ba61', $colorMainBackground, 60),
150
-
151
-			// used for the icon loading animation
152
-			'--color-loading-light' => '#cccccc',
153
-			'--color-loading-dark' => '#444444',
154
-
155
-			'--color-box-shadow-rgb' => $colorBoxShadowRGB,
156
-			'--color-box-shadow' => "rgba(var(--color-box-shadow-rgb), 0.5)",
157
-
158
-			'--color-border' => $this->util->darken($colorMainBackground, 7),
159
-			'--color-border-dark' => $this->util->darken($colorMainBackground, 14),
160
-
161
-			'--font-face' => "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Cantarell, Ubuntu, 'Helvetica Neue', Arial, sans-serif, 'Noto Color Emoji', 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'",
162
-			'--default-font-size' => '15px',
163
-
164
-			// TODO: support "(prefers-reduced-motion)"
165
-			'--animation-quick' => '100ms',
166
-			'--animation-slow' => '300ms',
167
-
168
-			// Default variables --------------------------------------------
169
-			'--border-radius' => '3px',
170
-			'--border-radius-large' => '10px',
171
-			// pill-style button, value is large so big buttons also have correct roundness
172
-			'--border-radius-pill' => '100px',
173
-
174
-			'--default-clickable-area' => '44px',
175
-			'--default-line-height' => '24px',
176
-			'--default-grid-baseline' => '4px',
177
-
178
-			// various structure data
179
-			'--header-height' => '50px',
180
-			'--navigation-width' => '300px',
181
-			'--sidebar-min-width' => '300px',
182
-			'--sidebar-max-width' => '500px',
183
-			'--list-min-width' => '200px',
184
-			'--list-max-width' => '300px',
185
-			'--header-menu-item-height' => '44px',
186
-			'--header-menu-profile-item-height' => '66px',
187
-
188
-			// mobile. Keep in sync with core/js/js.js
189
-			'--breakpoint-mobile' => '1024px',
190
-			'--background-invert-if-dark' => 'no',
191
-			'--background-invert-if-bright' => 'invert(100%)',
192
-			'--background-image-invert-if-bright' => 'no',
193
-
194
-			// Default last fallback values
195
-			'--image-background' => "url('" . $this->urlGenerator->imagePath('core', 'app-background.jpg') . "')",
196
-			'--image-background-default' => "url('" . $this->urlGenerator->imagePath('core', 'app-background.jpg') . "')",
197
-			'--color-background-plain' => $this->defaultPrimaryColor,
198
-		];
199
-
200
-		// Primary variables
201
-		$variables = array_merge($variables, $this->generatePrimaryVariables($colorMainBackground, $colorMainText));
202
-		$variables = array_merge($variables, $this->generateGlobalBackgroundVariables());
203
-		$variables = array_merge($variables, $this->generateUserBackgroundVariables());
204
-
205
-		return $variables;
206
-	}
207
-
208
-	public function getCustomCss(): string {
209
-		return '';
210
-	}
39
+    use CommonThemeTrait;
40
+
41
+    public Util $util;
42
+    public ThemingDefaults $themingDefaults;
43
+    public IUserSession $userSession;
44
+    public IURLGenerator $urlGenerator;
45
+    public ImageManager $imageManager;
46
+    public IConfig $config;
47
+    public IL10N $l;
48
+    public IAppManager $appManager;
49
+
50
+    public string $defaultPrimaryColor;
51
+    public string $primaryColor;
52
+
53
+    public function __construct(Util $util,
54
+                                ThemingDefaults $themingDefaults,
55
+                                IUserSession $userSession,
56
+                                IURLGenerator $urlGenerator,
57
+                                ImageManager $imageManager,
58
+                                IConfig $config,
59
+                                IL10N $l,
60
+                                IAppManager $appManager) {
61
+        $this->util = $util;
62
+        $this->themingDefaults = $themingDefaults;
63
+        $this->userSession = $userSession;
64
+        $this->urlGenerator = $urlGenerator;
65
+        $this->imageManager = $imageManager;
66
+        $this->config = $config;
67
+        $this->l = $l;
68
+        $this->appManager = $appManager;
69
+
70
+        $this->defaultPrimaryColor = $this->themingDefaults->getDefaultColorPrimary();
71
+        $this->primaryColor = $this->themingDefaults->getColorPrimary();
72
+
73
+        // Override default defaultPrimaryColor if set to improve accessibility
74
+        if ($this->primaryColor === BackgroundService::DEFAULT_COLOR) {
75
+            $this->primaryColor = BackgroundService::DEFAULT_ACCESSIBLE_COLOR;
76
+        }
77
+    }
78
+
79
+    public function getId(): string {
80
+        return 'default';
81
+    }
82
+
83
+    public function getType(): int {
84
+        return ITheme::TYPE_THEME;
85
+    }
86
+
87
+    public function getTitle(): string {
88
+        return $this->l->t('System default theme');
89
+    }
90
+
91
+    public function getEnableLabel(): string {
92
+        return $this->l->t('Enable the system default');
93
+    }
94
+
95
+    public function getDescription(): string {
96
+        return $this->l->t('Using the default system appearance.');
97
+    }
98
+
99
+    public function getMediaQuery(): string {
100
+        return '';
101
+    }
102
+
103
+    public function getCSSVariables(): array {
104
+        $colorMainText = '#222222';
105
+        $colorMainTextRgb = join(',', $this->util->hexToRGB($colorMainText));
106
+        $colorTextMaxcontrast = $this->util->lighten($colorMainText, 33);
107
+        $colorMainBackground = '#ffffff';
108
+        $colorMainBackgroundRGB = join(',', $this->util->hexToRGB($colorMainBackground));
109
+        $colorBoxShadow = $this->util->darken($colorMainBackground, 70);
110
+        $colorBoxShadowRGB = join(',', $this->util->hexToRGB($colorBoxShadow));
111
+
112
+        $variables = [
113
+            '--color-main-background' => $colorMainBackground,
114
+            '--color-main-background-rgb' => $colorMainBackgroundRGB,
115
+            '--color-main-background-translucent' => 'rgba(var(--color-main-background-rgb), .97)',
116
+            '--color-main-background-blur' => 'rgba(var(--color-main-background-rgb), .8)',
117
+            '--filter-background-blur' => 'blur(25px)',
118
+
119
+            // to use like this: background-image: linear-gradient(0, var('--gradient-main-background));
120
+            '--gradient-main-background' => 'var(--color-main-background) 0%, var(--color-main-background-translucent) 85%, transparent 100%',
121
+
122
+            // used for different active/hover/focus/disabled states
123
+            '--color-background-hover' => $this->util->darken($colorMainBackground, 4),
124
+            '--color-background-dark' => $this->util->darken($colorMainBackground, 7),
125
+            '--color-background-darker' => $this->util->darken($colorMainBackground, 14),
126
+
127
+            '--color-placeholder-light' => $this->util->darken($colorMainBackground, 10),
128
+            '--color-placeholder-dark' => $this->util->darken($colorMainBackground, 20),
129
+
130
+            // max contrast for WCAG compliance
131
+            '--color-main-text' => $colorMainText,
132
+            '--color-text-maxcontrast' => $colorTextMaxcontrast,
133
+            '--color-text-maxcontrast-default' => $colorTextMaxcontrast,
134
+            '--color-text-maxcontrast-background-blur' => $this->util->darken($colorTextMaxcontrast, 7),
135
+            '--color-text-light' => $colorMainText,
136
+            '--color-text-lighter' => $this->util->lighten($colorMainText, 33),
137
+
138
+            '--color-scrollbar' => 'rgba(' . $colorMainTextRgb . ', .15)',
139
+
140
+            // info/warning/success feedback colours
141
+            '--color-error' => '#e9322d',
142
+            '--color-error-rgb' => join(',', $this->util->hexToRGB('#e9322d')),
143
+            '--color-error-hover' => $this->util->mix('#e9322d', $colorMainBackground, 60),
144
+            '--color-warning' => '#eca700',
145
+            '--color-warning-rgb' => join(',', $this->util->hexToRGB('#eca700')),
146
+            '--color-warning-hover' => $this->util->mix('#eca700', $colorMainBackground, 60),
147
+            '--color-success' => '#46ba61',
148
+            '--color-success-rgb' => join(',', $this->util->hexToRGB('#46ba61')),
149
+            '--color-success-hover' => $this->util->mix('#46ba61', $colorMainBackground, 60),
150
+
151
+            // used for the icon loading animation
152
+            '--color-loading-light' => '#cccccc',
153
+            '--color-loading-dark' => '#444444',
154
+
155
+            '--color-box-shadow-rgb' => $colorBoxShadowRGB,
156
+            '--color-box-shadow' => "rgba(var(--color-box-shadow-rgb), 0.5)",
157
+
158
+            '--color-border' => $this->util->darken($colorMainBackground, 7),
159
+            '--color-border-dark' => $this->util->darken($colorMainBackground, 14),
160
+
161
+            '--font-face' => "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Cantarell, Ubuntu, 'Helvetica Neue', Arial, sans-serif, 'Noto Color Emoji', 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'",
162
+            '--default-font-size' => '15px',
163
+
164
+            // TODO: support "(prefers-reduced-motion)"
165
+            '--animation-quick' => '100ms',
166
+            '--animation-slow' => '300ms',
167
+
168
+            // Default variables --------------------------------------------
169
+            '--border-radius' => '3px',
170
+            '--border-radius-large' => '10px',
171
+            // pill-style button, value is large so big buttons also have correct roundness
172
+            '--border-radius-pill' => '100px',
173
+
174
+            '--default-clickable-area' => '44px',
175
+            '--default-line-height' => '24px',
176
+            '--default-grid-baseline' => '4px',
177
+
178
+            // various structure data
179
+            '--header-height' => '50px',
180
+            '--navigation-width' => '300px',
181
+            '--sidebar-min-width' => '300px',
182
+            '--sidebar-max-width' => '500px',
183
+            '--list-min-width' => '200px',
184
+            '--list-max-width' => '300px',
185
+            '--header-menu-item-height' => '44px',
186
+            '--header-menu-profile-item-height' => '66px',
187
+
188
+            // mobile. Keep in sync with core/js/js.js
189
+            '--breakpoint-mobile' => '1024px',
190
+            '--background-invert-if-dark' => 'no',
191
+            '--background-invert-if-bright' => 'invert(100%)',
192
+            '--background-image-invert-if-bright' => 'no',
193
+
194
+            // Default last fallback values
195
+            '--image-background' => "url('" . $this->urlGenerator->imagePath('core', 'app-background.jpg') . "')",
196
+            '--image-background-default' => "url('" . $this->urlGenerator->imagePath('core', 'app-background.jpg') . "')",
197
+            '--color-background-plain' => $this->defaultPrimaryColor,
198
+        ];
199
+
200
+        // Primary variables
201
+        $variables = array_merge($variables, $this->generatePrimaryVariables($colorMainBackground, $colorMainText));
202
+        $variables = array_merge($variables, $this->generateGlobalBackgroundVariables());
203
+        $variables = array_merge($variables, $this->generateUserBackgroundVariables());
204
+
205
+        return $variables;
206
+    }
207
+
208
+    public function getCustomCss(): string {
209
+        return '';
210
+    }
211 211
 }
Please login to merge, or discard this patch.
Spacing   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -135,7 +135,7 @@  discard block
 block discarded – undo
135 135
 			'--color-text-light' => $colorMainText,
136 136
 			'--color-text-lighter' => $this->util->lighten($colorMainText, 33),
137 137
 
138
-			'--color-scrollbar' => 'rgba(' . $colorMainTextRgb . ', .15)',
138
+			'--color-scrollbar' => 'rgba('.$colorMainTextRgb.', .15)',
139 139
 
140 140
 			// info/warning/success feedback colours
141 141
 			'--color-error' => '#e9322d',
@@ -192,8 +192,8 @@  discard block
 block discarded – undo
192 192
 			'--background-image-invert-if-bright' => 'no',
193 193
 
194 194
 			// Default last fallback values
195
-			'--image-background' => "url('" . $this->urlGenerator->imagePath('core', 'app-background.jpg') . "')",
196
-			'--image-background-default' => "url('" . $this->urlGenerator->imagePath('core', 'app-background.jpg') . "')",
195
+			'--image-background' => "url('".$this->urlGenerator->imagePath('core', 'app-background.jpg')."')",
196
+			'--image-background-default' => "url('".$this->urlGenerator->imagePath('core', 'app-background.jpg')."')",
197 197
 			'--color-background-plain' => $this->defaultPrimaryColor,
198 198
 		];
199 199
 
Please login to merge, or discard this patch.
apps/theming/lib/Themes/CommonThemeTrait.php 2 patches
Indentation   +131 added lines, -131 removed lines patch added patch discarded remove patch
@@ -30,135 +30,135 @@
 block discarded – undo
30 30
 use OCA\Theming\Util;
31 31
 
32 32
 trait CommonThemeTrait {
33
-	public Util $util;
34
-
35
-	/**
36
-	 * Generate primary-related variables
37
-	 * This is shared between multiple themes because colorMainBackground and colorMainText
38
-	 * will change in between.
39
-	 */
40
-	protected function generatePrimaryVariables(string $colorMainBackground, string $colorMainText): array {
41
-		$colorPrimaryLight = $this->util->mix($this->primaryColor, $colorMainBackground, -80);
42
-		$colorPrimaryElement = $this->util->elementColor($this->primaryColor);
43
-		$colorPrimaryElementDefault = $this->util->elementColor($this->defaultPrimaryColor);
44
-		$colorPrimaryElementLight = $this->util->mix($colorPrimaryElement, $colorMainBackground, -80);
45
-
46
-		// primary related colours
47
-		return [
48
-			// invert filter if primary is too bright
49
-			// to be used for legacy reasons only. Use inline
50
-			// svg with proper css variable instead or material
51
-			// design icons.
52
-			// ⚠️ Using 'no' as a value to make sure we specify an
53
-			// invalid one with no fallback. 'unset' could here fallback to some
54
-			// other theme with media queries
55
-			'--primary-invert-if-bright' => $this->util->invertTextColor($this->primaryColor) ? 'invert(100%)' : 'no',
56
-
57
-			'--color-primary' => $this->primaryColor,
58
-			'--color-primary-default' => $this->defaultPrimaryColor,
59
-			'--color-primary-text' => $this->util->invertTextColor($this->primaryColor) ? '#000000' : '#ffffff',
60
-			'--color-primary-hover' => $this->util->mix($this->primaryColor, $colorMainBackground, 60),
61
-			'--color-primary-light' => $colorPrimaryLight,
62
-			'--color-primary-light-text' => $this->util->mix($this->primaryColor, $this->util->invertTextColor($colorPrimaryLight) ? '#000000' : '#ffffff', -20),
63
-			'--color-primary-light-hover' => $this->util->mix($colorPrimaryLight, $colorMainText, 90),
64
-			'--color-primary-text-dark' => $this->util->darken($this->util->invertTextColor($this->primaryColor) ? '#000000' : '#ffffff', 7),
65
-
66
-			// used for buttons, inputs...
67
-			'--color-primary-element' => $colorPrimaryElement,
68
-			'--color-primary-element-default-hover' => $this->util->mix($colorPrimaryElementDefault, $colorMainBackground, 60),
69
-			'--color-primary-element-text' => $this->util->invertTextColor($colorPrimaryElement) ? '#000000' : '#ffffff',
70
-			'--color-primary-element-hover' => $this->util->mix($colorPrimaryElement, $colorMainBackground, 60),
71
-			'--color-primary-element-light' => $colorPrimaryElementLight,
72
-			'--color-primary-element-light-text' => $this->util->mix($colorPrimaryElement, $this->util->invertTextColor($colorPrimaryElementLight) ? '#000000' : '#ffffff', -20),
73
-			'--color-primary-element-light-hover' => $this->util->mix($colorPrimaryElementLight, $colorMainText, 90),
74
-			'--color-primary-element-text-dark' => $this->util->darken($this->util->invertTextColor($colorPrimaryElement) ? '#000000' : '#ffffff', 7),
75
-
76
-			// to use like this: background-image: var(--gradient-primary-background);
77
-			'--gradient-primary-background' => 'linear-gradient(40deg, var(--color-primary) 0%, var(--color-primary-hover) 100%)',
78
-		];
79
-	}
80
-
81
-	/**
82
-	 * Generate admin theming background-related variables
83
-	 */
84
-	protected function generateGlobalBackgroundVariables(): array {
85
-		$user = $this->userSession->getUser();
86
-		$backgroundDeleted = $this->config->getAppValue(Application::APP_ID, 'backgroundMime', '') === 'backgroundColor';
87
-		$hasCustomLogoHeader = $this->imageManager->hasImage('logo') || $this->imageManager->hasImage('logoheader');
88
-
89
-		$variables = [];
90
-
91
-		// If primary as background has been request or if we have a custom primary colour
92
-		// let's not define the background image
93
-		if ($backgroundDeleted) {
94
-			$variables['--color-background-plain'] = $this->themingDefaults->getColorPrimary();
95
-			if ($this->themingDefaults->isUserThemingDisabled() || $user === null) {
96
-				$variables['--image-background-plain'] = 'true';
97
-			}
98
-		}
99
-
100
-		// Register image variables only if custom-defined
101
-		foreach (ImageManager::SupportedImageKeys as $image) {
102
-			if ($this->imageManager->hasImage($image)) {
103
-				$imageUrl = $this->imageManager->getImageUrl($image);
104
-				if ($image === 'background') {
105
-					// If background deleted is set, ignoring variable
106
-					if ($backgroundDeleted) {
107
-						$variables['--image-background-default'] = 'no';
108
-						continue;
109
-					}
110
-					$variables['--image-background-size'] = 'cover';
111
-					$variables['--image-background-default'] = "url('" . $imageUrl . "')";
112
-				}
113
-				$variables["--image-$image"] = "url('" . $imageUrl . "')";
114
-			}
115
-		}
116
-
117
-		if ($hasCustomLogoHeader) {
118
-			$variables["--image-logoheader-custom"] = 'true';
119
-		}
120
-
121
-		return $variables;
122
-	}
123
-
124
-	/**
125
-	 * Generate user theming background-related variables
126
-	 */
127
-	protected function generateUserBackgroundVariables(): array {
128
-		$user = $this->userSession->getUser();
129
-		if ($user !== null
130
-			&& !$this->themingDefaults->isUserThemingDisabled()
131
-			&& $this->appManager->isEnabledForUser(Application::APP_ID)) {
132
-			$themingBackground = $this->config->getUserValue($user->getUID(), Application::APP_ID, 'background', 'default');
133
-			$currentVersion = (int)$this->config->getUserValue($user->getUID(), Application::APP_ID, 'userCacheBuster', '0');
134
-
135
-			// The user uploaded a custom background
136
-			if ($themingBackground === 'custom') {
137
-				$cacheBuster = substr(sha1($user->getUID() . '_' . $currentVersion), 0, 8);
138
-				return [
139
-					'--image-background' => "url('" . $this->urlGenerator->linkToRouteAbsolute('theming.userTheme.getBackground') . "?v=$cacheBuster')",
140
-					// TODO: implement primary color from custom background --color-background-plain
141
-				];
142
-			}
143
-
144
-			// The user picked a shipped background
145
-			if (isset(BackgroundService::SHIPPED_BACKGROUNDS[$themingBackground])) {
146
-				return [
147
-					'--image-background' => "url('" . $this->urlGenerator->linkTo(Application::APP_ID, "/img/background/$themingBackground") . "')",
148
-					'--color-background-plain' => $this->themingDefaults->getColorPrimary(),
149
-					'--background-image-invert-if-bright' => BackgroundService::SHIPPED_BACKGROUNDS[$themingBackground]['theming'] ?? null === BackgroundService::THEMING_MODE_DARK ? 'invert(100%)' : 'no',
150
-				];
151
-			}
152
-
153
-			// The user picked a static colour
154
-			if (substr($themingBackground, 0, 1) === '#') {
155
-				return [
156
-					'--image-background' => 'no',
157
-					'--color-background-plain' => $this->themingDefaults->getColorPrimary(),
158
-				];
159
-			}
160
-		}
161
-
162
-		return [];
163
-	}
33
+    public Util $util;
34
+
35
+    /**
36
+     * Generate primary-related variables
37
+     * This is shared between multiple themes because colorMainBackground and colorMainText
38
+     * will change in between.
39
+     */
40
+    protected function generatePrimaryVariables(string $colorMainBackground, string $colorMainText): array {
41
+        $colorPrimaryLight = $this->util->mix($this->primaryColor, $colorMainBackground, -80);
42
+        $colorPrimaryElement = $this->util->elementColor($this->primaryColor);
43
+        $colorPrimaryElementDefault = $this->util->elementColor($this->defaultPrimaryColor);
44
+        $colorPrimaryElementLight = $this->util->mix($colorPrimaryElement, $colorMainBackground, -80);
45
+
46
+        // primary related colours
47
+        return [
48
+            // invert filter if primary is too bright
49
+            // to be used for legacy reasons only. Use inline
50
+            // svg with proper css variable instead or material
51
+            // design icons.
52
+            // ⚠️ Using 'no' as a value to make sure we specify an
53
+            // invalid one with no fallback. 'unset' could here fallback to some
54
+            // other theme with media queries
55
+            '--primary-invert-if-bright' => $this->util->invertTextColor($this->primaryColor) ? 'invert(100%)' : 'no',
56
+
57
+            '--color-primary' => $this->primaryColor,
58
+            '--color-primary-default' => $this->defaultPrimaryColor,
59
+            '--color-primary-text' => $this->util->invertTextColor($this->primaryColor) ? '#000000' : '#ffffff',
60
+            '--color-primary-hover' => $this->util->mix($this->primaryColor, $colorMainBackground, 60),
61
+            '--color-primary-light' => $colorPrimaryLight,
62
+            '--color-primary-light-text' => $this->util->mix($this->primaryColor, $this->util->invertTextColor($colorPrimaryLight) ? '#000000' : '#ffffff', -20),
63
+            '--color-primary-light-hover' => $this->util->mix($colorPrimaryLight, $colorMainText, 90),
64
+            '--color-primary-text-dark' => $this->util->darken($this->util->invertTextColor($this->primaryColor) ? '#000000' : '#ffffff', 7),
65
+
66
+            // used for buttons, inputs...
67
+            '--color-primary-element' => $colorPrimaryElement,
68
+            '--color-primary-element-default-hover' => $this->util->mix($colorPrimaryElementDefault, $colorMainBackground, 60),
69
+            '--color-primary-element-text' => $this->util->invertTextColor($colorPrimaryElement) ? '#000000' : '#ffffff',
70
+            '--color-primary-element-hover' => $this->util->mix($colorPrimaryElement, $colorMainBackground, 60),
71
+            '--color-primary-element-light' => $colorPrimaryElementLight,
72
+            '--color-primary-element-light-text' => $this->util->mix($colorPrimaryElement, $this->util->invertTextColor($colorPrimaryElementLight) ? '#000000' : '#ffffff', -20),
73
+            '--color-primary-element-light-hover' => $this->util->mix($colorPrimaryElementLight, $colorMainText, 90),
74
+            '--color-primary-element-text-dark' => $this->util->darken($this->util->invertTextColor($colorPrimaryElement) ? '#000000' : '#ffffff', 7),
75
+
76
+            // to use like this: background-image: var(--gradient-primary-background);
77
+            '--gradient-primary-background' => 'linear-gradient(40deg, var(--color-primary) 0%, var(--color-primary-hover) 100%)',
78
+        ];
79
+    }
80
+
81
+    /**
82
+     * Generate admin theming background-related variables
83
+     */
84
+    protected function generateGlobalBackgroundVariables(): array {
85
+        $user = $this->userSession->getUser();
86
+        $backgroundDeleted = $this->config->getAppValue(Application::APP_ID, 'backgroundMime', '') === 'backgroundColor';
87
+        $hasCustomLogoHeader = $this->imageManager->hasImage('logo') || $this->imageManager->hasImage('logoheader');
88
+
89
+        $variables = [];
90
+
91
+        // If primary as background has been request or if we have a custom primary colour
92
+        // let's not define the background image
93
+        if ($backgroundDeleted) {
94
+            $variables['--color-background-plain'] = $this->themingDefaults->getColorPrimary();
95
+            if ($this->themingDefaults->isUserThemingDisabled() || $user === null) {
96
+                $variables['--image-background-plain'] = 'true';
97
+            }
98
+        }
99
+
100
+        // Register image variables only if custom-defined
101
+        foreach (ImageManager::SupportedImageKeys as $image) {
102
+            if ($this->imageManager->hasImage($image)) {
103
+                $imageUrl = $this->imageManager->getImageUrl($image);
104
+                if ($image === 'background') {
105
+                    // If background deleted is set, ignoring variable
106
+                    if ($backgroundDeleted) {
107
+                        $variables['--image-background-default'] = 'no';
108
+                        continue;
109
+                    }
110
+                    $variables['--image-background-size'] = 'cover';
111
+                    $variables['--image-background-default'] = "url('" . $imageUrl . "')";
112
+                }
113
+                $variables["--image-$image"] = "url('" . $imageUrl . "')";
114
+            }
115
+        }
116
+
117
+        if ($hasCustomLogoHeader) {
118
+            $variables["--image-logoheader-custom"] = 'true';
119
+        }
120
+
121
+        return $variables;
122
+    }
123
+
124
+    /**
125
+     * Generate user theming background-related variables
126
+     */
127
+    protected function generateUserBackgroundVariables(): array {
128
+        $user = $this->userSession->getUser();
129
+        if ($user !== null
130
+            && !$this->themingDefaults->isUserThemingDisabled()
131
+            && $this->appManager->isEnabledForUser(Application::APP_ID)) {
132
+            $themingBackground = $this->config->getUserValue($user->getUID(), Application::APP_ID, 'background', 'default');
133
+            $currentVersion = (int)$this->config->getUserValue($user->getUID(), Application::APP_ID, 'userCacheBuster', '0');
134
+
135
+            // The user uploaded a custom background
136
+            if ($themingBackground === 'custom') {
137
+                $cacheBuster = substr(sha1($user->getUID() . '_' . $currentVersion), 0, 8);
138
+                return [
139
+                    '--image-background' => "url('" . $this->urlGenerator->linkToRouteAbsolute('theming.userTheme.getBackground') . "?v=$cacheBuster')",
140
+                    // TODO: implement primary color from custom background --color-background-plain
141
+                ];
142
+            }
143
+
144
+            // The user picked a shipped background
145
+            if (isset(BackgroundService::SHIPPED_BACKGROUNDS[$themingBackground])) {
146
+                return [
147
+                    '--image-background' => "url('" . $this->urlGenerator->linkTo(Application::APP_ID, "/img/background/$themingBackground") . "')",
148
+                    '--color-background-plain' => $this->themingDefaults->getColorPrimary(),
149
+                    '--background-image-invert-if-bright' => BackgroundService::SHIPPED_BACKGROUNDS[$themingBackground]['theming'] ?? null === BackgroundService::THEMING_MODE_DARK ? 'invert(100%)' : 'no',
150
+                ];
151
+            }
152
+
153
+            // The user picked a static colour
154
+            if (substr($themingBackground, 0, 1) === '#') {
155
+                return [
156
+                    '--image-background' => 'no',
157
+                    '--color-background-plain' => $this->themingDefaults->getColorPrimary(),
158
+                ];
159
+            }
160
+        }
161
+
162
+        return [];
163
+    }
164 164
 }
Please login to merge, or discard this patch.
Spacing   +6 added lines, -6 removed lines patch added patch discarded remove patch
@@ -108,9 +108,9 @@  discard block
 block discarded – undo
108 108
 						continue;
109 109
 					}
110 110
 					$variables['--image-background-size'] = 'cover';
111
-					$variables['--image-background-default'] = "url('" . $imageUrl . "')";
111
+					$variables['--image-background-default'] = "url('".$imageUrl."')";
112 112
 				}
113
-				$variables["--image-$image"] = "url('" . $imageUrl . "')";
113
+				$variables["--image-$image"] = "url('".$imageUrl."')";
114 114
 			}
115 115
 		}
116 116
 
@@ -130,13 +130,13 @@  discard block
 block discarded – undo
130 130
 			&& !$this->themingDefaults->isUserThemingDisabled()
131 131
 			&& $this->appManager->isEnabledForUser(Application::APP_ID)) {
132 132
 			$themingBackground = $this->config->getUserValue($user->getUID(), Application::APP_ID, 'background', 'default');
133
-			$currentVersion = (int)$this->config->getUserValue($user->getUID(), Application::APP_ID, 'userCacheBuster', '0');
133
+			$currentVersion = (int) $this->config->getUserValue($user->getUID(), Application::APP_ID, 'userCacheBuster', '0');
134 134
 
135 135
 			// The user uploaded a custom background
136 136
 			if ($themingBackground === 'custom') {
137
-				$cacheBuster = substr(sha1($user->getUID() . '_' . $currentVersion), 0, 8);
137
+				$cacheBuster = substr(sha1($user->getUID().'_'.$currentVersion), 0, 8);
138 138
 				return [
139
-					'--image-background' => "url('" . $this->urlGenerator->linkToRouteAbsolute('theming.userTheme.getBackground') . "?v=$cacheBuster')",
139
+					'--image-background' => "url('".$this->urlGenerator->linkToRouteAbsolute('theming.userTheme.getBackground')."?v=$cacheBuster')",
140 140
 					// TODO: implement primary color from custom background --color-background-plain
141 141
 				];
142 142
 			}
@@ -144,7 +144,7 @@  discard block
 block discarded – undo
144 144
 			// The user picked a shipped background
145 145
 			if (isset(BackgroundService::SHIPPED_BACKGROUNDS[$themingBackground])) {
146 146
 				return [
147
-					'--image-background' => "url('" . $this->urlGenerator->linkTo(Application::APP_ID, "/img/background/$themingBackground") . "')",
147
+					'--image-background' => "url('".$this->urlGenerator->linkTo(Application::APP_ID, "/img/background/$themingBackground")."')",
148 148
 					'--color-background-plain' => $this->themingDefaults->getColorPrimary(),
149 149
 					'--background-image-invert-if-bright' => BackgroundService::SHIPPED_BACKGROUNDS[$themingBackground]['theming'] ?? null === BackgroundService::THEMING_MODE_DARK ? 'invert(100%)' : 'no',
150 150
 				];
Please login to merge, or discard this patch.
apps/theming/lib/Settings/Admin.php 1 patch
Indentation   +78 added lines, -78 removed lines patch added patch discarded remove patch
@@ -39,91 +39,91 @@
 block discarded – undo
39 39
 use OCP\Util;
40 40
 
41 41
 class Admin implements IDelegatedSettings {
42
-	private string $appName;
43
-	private IConfig $config;
44
-	private IL10N $l;
45
-	private ThemingDefaults $themingDefaults;
46
-	private IInitialState $initialState;
47
-	private IURLGenerator $urlGenerator;
48
-	private ImageManager $imageManager;
42
+    private string $appName;
43
+    private IConfig $config;
44
+    private IL10N $l;
45
+    private ThemingDefaults $themingDefaults;
46
+    private IInitialState $initialState;
47
+    private IURLGenerator $urlGenerator;
48
+    private ImageManager $imageManager;
49 49
 
50
-	public function __construct(string $appName,
51
-								IConfig $config,
52
-								IL10N $l,
53
-								ThemingDefaults $themingDefaults,
54
-								IInitialState $initialState,
55
-								IURLGenerator $urlGenerator,
56
-								ImageManager $imageManager) {
57
-		$this->appName = $appName;
58
-		$this->config = $config;
59
-		$this->l = $l;
60
-		$this->themingDefaults = $themingDefaults;
61
-		$this->initialState = $initialState;
62
-		$this->urlGenerator = $urlGenerator;
63
-		$this->imageManager = $imageManager;
64
-	}
50
+    public function __construct(string $appName,
51
+                                IConfig $config,
52
+                                IL10N $l,
53
+                                ThemingDefaults $themingDefaults,
54
+                                IInitialState $initialState,
55
+                                IURLGenerator $urlGenerator,
56
+                                ImageManager $imageManager) {
57
+        $this->appName = $appName;
58
+        $this->config = $config;
59
+        $this->l = $l;
60
+        $this->themingDefaults = $themingDefaults;
61
+        $this->initialState = $initialState;
62
+        $this->urlGenerator = $urlGenerator;
63
+        $this->imageManager = $imageManager;
64
+    }
65 65
 
66
-	/**
67
-	 * @return TemplateResponse
68
-	 */
69
-	public function getForm(): TemplateResponse {
70
-		$themable = true;
71
-		$errorMessage = '';
72
-		$theme = $this->config->getSystemValue('theme', '');
73
-		if ($theme !== '') {
74
-			$themable = false;
75
-			$errorMessage = $this->l->t('You are already using a custom theme. Theming app settings might be overwritten by that.');
76
-		}
66
+    /**
67
+     * @return TemplateResponse
68
+     */
69
+    public function getForm(): TemplateResponse {
70
+        $themable = true;
71
+        $errorMessage = '';
72
+        $theme = $this->config->getSystemValue('theme', '');
73
+        if ($theme !== '') {
74
+            $themable = false;
75
+            $errorMessage = $this->l->t('You are already using a custom theme. Theming app settings might be overwritten by that.');
76
+        }
77 77
 
78
-		$this->initialState->provideInitialState('adminThemingParameters', [
79
-			'isThemable' => $themable,
80
-			'notThemableErrorMessage' => $errorMessage,
81
-			'name' => $this->themingDefaults->getEntity(),
82
-			'url' => $this->themingDefaults->getBaseUrl(),
83
-			'slogan' => $this->themingDefaults->getSlogan(),
84
-			'color' => $this->themingDefaults->getDefaultColorPrimary(),
85
-			'logoMime' => $this->config->getAppValue(Application::APP_ID, 'logoMime', ''),
86
-			'backgroundMime' => $this->config->getAppValue(Application::APP_ID, 'backgroundMime', ''),
87
-			'logoheaderMime' => $this->config->getAppValue(Application::APP_ID, 'logoheaderMime', ''),
88
-			'faviconMime' => $this->config->getAppValue(Application::APP_ID, 'faviconMime', ''),
89
-			'legalNoticeUrl' => $this->themingDefaults->getImprintUrl(),
90
-			'privacyPolicyUrl' => $this->themingDefaults->getPrivacyUrl(),
91
-			'docUrl' => $this->urlGenerator->linkToDocs('admin-theming'),
92
-			'docUrlIcons' => $this->urlGenerator->linkToDocs('admin-theming-icons'),
93
-			'canThemeIcons' => $this->imageManager->shouldReplaceIcons(),
94
-			'userThemingDisabled' => $this->themingDefaults->isUserThemingDisabled(),
95
-		]);
78
+        $this->initialState->provideInitialState('adminThemingParameters', [
79
+            'isThemable' => $themable,
80
+            'notThemableErrorMessage' => $errorMessage,
81
+            'name' => $this->themingDefaults->getEntity(),
82
+            'url' => $this->themingDefaults->getBaseUrl(),
83
+            'slogan' => $this->themingDefaults->getSlogan(),
84
+            'color' => $this->themingDefaults->getDefaultColorPrimary(),
85
+            'logoMime' => $this->config->getAppValue(Application::APP_ID, 'logoMime', ''),
86
+            'backgroundMime' => $this->config->getAppValue(Application::APP_ID, 'backgroundMime', ''),
87
+            'logoheaderMime' => $this->config->getAppValue(Application::APP_ID, 'logoheaderMime', ''),
88
+            'faviconMime' => $this->config->getAppValue(Application::APP_ID, 'faviconMime', ''),
89
+            'legalNoticeUrl' => $this->themingDefaults->getImprintUrl(),
90
+            'privacyPolicyUrl' => $this->themingDefaults->getPrivacyUrl(),
91
+            'docUrl' => $this->urlGenerator->linkToDocs('admin-theming'),
92
+            'docUrlIcons' => $this->urlGenerator->linkToDocs('admin-theming-icons'),
93
+            'canThemeIcons' => $this->imageManager->shouldReplaceIcons(),
94
+            'userThemingDisabled' => $this->themingDefaults->isUserThemingDisabled(),
95
+        ]);
96 96
 
97
-		Util::addScript($this->appName, 'admin-theming');
97
+        Util::addScript($this->appName, 'admin-theming');
98 98
 
99
-		return new TemplateResponse($this->appName, 'settings-admin');
100
-	}
99
+        return new TemplateResponse($this->appName, 'settings-admin');
100
+    }
101 101
 
102
-	/**
103
-	 * @return string the section ID, e.g. 'sharing'
104
-	 */
105
-	public function getSection(): string {
106
-		return $this->appName;
107
-	}
102
+    /**
103
+     * @return string the section ID, e.g. 'sharing'
104
+     */
105
+    public function getSection(): string {
106
+        return $this->appName;
107
+    }
108 108
 
109
-	/**
110
-	 * @return int whether the form should be rather on the top or bottom of
111
-	 * the admin section. The forms are arranged in ascending order of the
112
-	 * priority values. It is required to return a value between 0 and 100.
113
-	 *
114
-	 * E.g.: 70
115
-	 */
116
-	public function getPriority(): int {
117
-		return 5;
118
-	}
109
+    /**
110
+     * @return int whether the form should be rather on the top or bottom of
111
+     * the admin section. The forms are arranged in ascending order of the
112
+     * priority values. It is required to return a value between 0 and 100.
113
+     *
114
+     * E.g.: 70
115
+     */
116
+    public function getPriority(): int {
117
+        return 5;
118
+    }
119 119
 
120
-	public function getName(): ?string {
121
-		return null; // Only one setting in this section
122
-	}
120
+    public function getName(): ?string {
121
+        return null; // Only one setting in this section
122
+    }
123 123
 
124
-	public function getAuthorizedAppConfig(): array {
125
-		return [
126
-			$this->appName => '/.*/',
127
-		];
128
-	}
124
+    public function getAuthorizedAppConfig(): array {
125
+        return [
126
+            $this->appName => '/.*/',
127
+        ];
128
+    }
129 129
 }
Please login to merge, or discard this patch.
apps/theming/lib/Settings/Personal.php 1 patch
Indentation   +57 added lines, -57 removed lines patch added patch discarded remove patch
@@ -36,70 +36,70 @@
 block discarded – undo
36 36
 
37 37
 class Personal implements ISettings {
38 38
 
39
-	protected string $appName;
40
-	private IConfig $config;
41
-	private ThemesService $themesService;
42
-	private IInitialState $initialStateService;
43
-	private ThemingDefaults $themingDefaults;
39
+    protected string $appName;
40
+    private IConfig $config;
41
+    private ThemesService $themesService;
42
+    private IInitialState $initialStateService;
43
+    private ThemingDefaults $themingDefaults;
44 44
 
45
-	public function __construct(string $appName,
46
-								IConfig $config,
47
-								ThemesService $themesService,
48
-								IInitialState $initialStateService,
49
-								ThemingDefaults $themingDefaults) {
50
-		$this->appName = $appName;
51
-		$this->config = $config;
52
-		$this->themesService = $themesService;
53
-		$this->initialStateService = $initialStateService;
54
-		$this->themingDefaults = $themingDefaults;
55
-	}
45
+    public function __construct(string $appName,
46
+                                IConfig $config,
47
+                                ThemesService $themesService,
48
+                                IInitialState $initialStateService,
49
+                                ThemingDefaults $themingDefaults) {
50
+        $this->appName = $appName;
51
+        $this->config = $config;
52
+        $this->themesService = $themesService;
53
+        $this->initialStateService = $initialStateService;
54
+        $this->themingDefaults = $themingDefaults;
55
+    }
56 56
 
57
-	public function getForm(): TemplateResponse {
58
-		$enforcedTheme = $this->config->getSystemValueString('enforce_theme', '');
57
+    public function getForm(): TemplateResponse {
58
+        $enforcedTheme = $this->config->getSystemValueString('enforce_theme', '');
59 59
 
60
-		$themes = array_map(function($theme) {
61
-			return [
62
-				'id' => $theme->getId(),
63
-				'type' => $theme->getType(),
64
-				'title' => $theme->getTitle(),
65
-				'enableLabel' => $theme->getEnableLabel(),
66
-				'description' => $theme->getDescription(),
67
-				'enabled' => $this->themesService->isEnabled($theme),
68
-			];
69
-		}, $this->themesService->getThemes());
60
+        $themes = array_map(function($theme) {
61
+            return [
62
+                'id' => $theme->getId(),
63
+                'type' => $theme->getType(),
64
+                'title' => $theme->getTitle(),
65
+                'enableLabel' => $theme->getEnableLabel(),
66
+                'description' => $theme->getDescription(),
67
+                'enabled' => $this->themesService->isEnabled($theme),
68
+            ];
69
+        }, $this->themesService->getThemes());
70 70
 
71
-		if ($enforcedTheme !== '') {
72
-			$themes = array_filter($themes, function($theme) use ($enforcedTheme) {
73
-				return $theme['type'] !== ITheme::TYPE_THEME || $theme['id'] === $enforcedTheme;
74
-			});
75
-		}
71
+        if ($enforcedTheme !== '') {
72
+            $themes = array_filter($themes, function($theme) use ($enforcedTheme) {
73
+                return $theme['type'] !== ITheme::TYPE_THEME || $theme['id'] === $enforcedTheme;
74
+            });
75
+        }
76 76
 
77
-		$this->initialStateService->provideInitialState('themes', array_values($themes));
78
-		$this->initialStateService->provideInitialState('enforceTheme', $enforcedTheme);
79
-		$this->initialStateService->provideInitialState('isUserThemingDisabled', $this->themingDefaults->isUserThemingDisabled());
77
+        $this->initialStateService->provideInitialState('themes', array_values($themes));
78
+        $this->initialStateService->provideInitialState('enforceTheme', $enforcedTheme);
79
+        $this->initialStateService->provideInitialState('isUserThemingDisabled', $this->themingDefaults->isUserThemingDisabled());
80 80
 
81
-		Util::addScript($this->appName, 'personal-theming');
81
+        Util::addScript($this->appName, 'personal-theming');
82 82
 
83
-		return new TemplateResponse($this->appName, 'settings-personal');
84
-	}
83
+        return new TemplateResponse($this->appName, 'settings-personal');
84
+    }
85 85
 
86
-	/**
87
-	 * @return string the section ID, e.g. 'sharing'
88
-	 * @since 9.1
89
-	 */
90
-	public function getSection(): string {
91
-		return $this->appName;
92
-	}
86
+    /**
87
+     * @return string the section ID, e.g. 'sharing'
88
+     * @since 9.1
89
+     */
90
+    public function getSection(): string {
91
+        return $this->appName;
92
+    }
93 93
 
94
-	/**
95
-	 * @return int whether the form should be rather on the top or bottom of
96
-	 * the admin section. The forms are arranged in ascending order of the
97
-	 * priority values. It is required to return a value between 0 and 100.
98
-	 *
99
-	 * E.g.: 70
100
-	 * @since 9.1
101
-	 */
102
-	public function getPriority(): int {
103
-		return 40;
104
-	}
94
+    /**
95
+     * @return int whether the form should be rather on the top or bottom of
96
+     * the admin section. The forms are arranged in ascending order of the
97
+     * priority values. It is required to return a value between 0 and 100.
98
+     *
99
+     * E.g.: 70
100
+     * @since 9.1
101
+     */
102
+    public function getPriority(): int {
103
+        return 40;
104
+    }
105 105
 }
Please login to merge, or discard this patch.
apps/theming/lib/ThemingDefaults.php 1 patch
Indentation   +450 added lines, -450 removed lines patch added patch discarded remove patch
@@ -55,454 +55,454 @@
 block discarded – undo
55 55
 
56 56
 class ThemingDefaults extends \OC_Defaults {
57 57
 
58
-	private IConfig $config;
59
-	private IL10N $l;
60
-	private ImageManager $imageManager;
61
-	private IUserSession $userSession;
62
-	private IURLGenerator $urlGenerator;
63
-	private ICacheFactory $cacheFactory;
64
-	private Util $util;
65
-	private IAppManager $appManager;
66
-	private INavigationManager $navigationManager;
67
-
68
-	private string $name;
69
-	private string $title;
70
-	private string $entity;
71
-	private string $productName;
72
-	private string $url;
73
-	private string $color;
74
-
75
-	private string $iTunesAppId;
76
-	private string $iOSClientUrl;
77
-	private string $AndroidClientUrl;
78
-	private string $FDroidClientUrl;
79
-
80
-	/**
81
-	 * ThemingDefaults constructor.
82
-	 *
83
-	 * @param IConfig $config
84
-	 * @param IL10N $l
85
-	 * @param ImageManager $imageManager
86
-	 * @param IUserSession $userSession
87
-	 * @param IURLGenerator $urlGenerator
88
-	 * @param ICacheFactory $cacheFactory
89
-	 * @param Util $util
90
-	 * @param IAppManager $appManager
91
-	 */
92
-	public function __construct(IConfig $config,
93
-								IL10N $l,
94
-								IUserSession $userSession,
95
-								IURLGenerator $urlGenerator,
96
-								ICacheFactory $cacheFactory,
97
-								Util $util,
98
-								ImageManager $imageManager,
99
-								IAppManager $appManager,
100
-								INavigationManager $navigationManager
101
-	) {
102
-		parent::__construct();
103
-		$this->config = $config;
104
-		$this->l = $l;
105
-		$this->imageManager = $imageManager;
106
-		$this->userSession = $userSession;
107
-		$this->urlGenerator = $urlGenerator;
108
-		$this->cacheFactory = $cacheFactory;
109
-		$this->util = $util;
110
-		$this->appManager = $appManager;
111
-		$this->navigationManager = $navigationManager;
112
-
113
-		$this->name = parent::getName();
114
-		$this->title = parent::getTitle();
115
-		$this->entity = parent::getEntity();
116
-		$this->productName = parent::getProductName();
117
-		$this->url = parent::getBaseUrl();
118
-		$this->color = parent::getColorPrimary();
119
-		$this->iTunesAppId = parent::getiTunesAppId();
120
-		$this->iOSClientUrl = parent::getiOSClientUrl();
121
-		$this->AndroidClientUrl = parent::getAndroidClientUrl();
122
-		$this->FDroidClientUrl = parent::getFDroidClientUrl();
123
-	}
124
-
125
-	public function getName() {
126
-		return strip_tags($this->config->getAppValue('theming', 'name', $this->name));
127
-	}
128
-
129
-	public function getHTMLName() {
130
-		return $this->config->getAppValue('theming', 'name', $this->name);
131
-	}
132
-
133
-	public function getTitle() {
134
-		return strip_tags($this->config->getAppValue('theming', 'name', $this->title));
135
-	}
136
-
137
-	public function getEntity() {
138
-		return strip_tags($this->config->getAppValue('theming', 'name', $this->entity));
139
-	}
140
-
141
-	public function getProductName() {
142
-		return strip_tags($this->config->getAppValue('theming', 'productName', $this->productName));
143
-	}
144
-
145
-	public function getBaseUrl() {
146
-		return $this->config->getAppValue('theming', 'url', $this->url);
147
-	}
148
-
149
-	/**
150
-	 * We pass a string and sanitizeHTML will return a string too in that case
151
-	 * @psalm-suppress InvalidReturnStatement
152
-	 * @psalm-suppress InvalidReturnType
153
-	 */
154
-	public function getSlogan(?string $lang = null) {
155
-		return \OCP\Util::sanitizeHTML($this->config->getAppValue('theming', 'slogan', parent::getSlogan($lang)));
156
-	}
157
-
158
-	public function getImprintUrl() {
159
-		return (string)$this->config->getAppValue('theming', 'imprintUrl', '');
160
-	}
161
-
162
-	public function getPrivacyUrl() {
163
-		return (string)$this->config->getAppValue('theming', 'privacyUrl', '');
164
-	}
165
-
166
-	public function getShortFooter() {
167
-		$slogan = $this->getSlogan();
168
-		$baseUrl = $this->getBaseUrl();
169
-		if ($baseUrl !== '') {
170
-			$footer = '<a href="' . $baseUrl . '" target="_blank"' .
171
-				' rel="noreferrer noopener" class="entity-name">' . $this->getEntity() . '</a>';
172
-		} else {
173
-			$footer = '<span class="entity-name">' .$this->getEntity() . '</span>';
174
-		}
175
-		$footer .= ($slogan !== '' ? ' – ' . $slogan : '');
176
-
177
-		$links = [
178
-			[
179
-				'text' => $this->l->t('Legal notice'),
180
-				'url' => (string)$this->getImprintUrl()
181
-			],
182
-			[
183
-				'text' => $this->l->t('Privacy policy'),
184
-				'url' => (string)$this->getPrivacyUrl()
185
-			],
186
-		];
187
-
188
-		$navigation = $this->navigationManager->getAll(INavigationManager::TYPE_GUEST);
189
-		$guestNavigation = array_map(function ($nav) {
190
-			return [
191
-				'text' => $nav['name'],
192
-				'url' => $nav['href']
193
-			];
194
-		}, $navigation);
195
-		$links = array_merge($links, $guestNavigation);
196
-
197
-		$legalLinks = '';
198
-		$divider = '';
199
-		foreach ($links as $link) {
200
-			if ($link['url'] !== ''
201
-				&& filter_var($link['url'], FILTER_VALIDATE_URL)
202
-			) {
203
-				$legalLinks .= $divider . '<a href="' . $link['url'] . '" class="legal" target="_blank"' .
204
-					' rel="noreferrer noopener">' . $link['text'] . '</a>';
205
-				$divider = ' · ';
206
-			}
207
-		}
208
-		if ($legalLinks !== '') {
209
-			$footer .= '<br/>' . $legalLinks;
210
-		}
211
-
212
-		return $footer;
213
-	}
214
-
215
-	/**
216
-	 * Color that is used for the header as well as for mail headers
217
-	 */
218
-	public function getColorPrimary(): string {
219
-		$user = $this->userSession->getUser();
220
-
221
-		// admin-defined primary color
222
-		$defaultColor = $this->getDefaultColorPrimary();
223
-
224
-		if ($this->isUserThemingDisabled()) {
225
-			return $defaultColor;
226
-		}
227
-
228
-		// user-defined primary color
229
-		$themingBackground = '';
230
-		if (!empty($user)) {
231
-			$themingBackground = $this->config->getUserValue($user->getUID(), Application::APP_ID, 'background', '');
232
-			// If the user selected the default background
233
-			if ($themingBackground === '') {
234
-				return BackgroundService::DEFAULT_COLOR;
235
-			}
236
-
237
-			// If the user selected a specific colour
238
-			if (preg_match('/^\#([0-9a-f]{3}|[0-9a-f]{6})$/i', $themingBackground)) {
239
-				return $themingBackground;
240
-			}
241
-
242
-			// if the user-selected background is a background reference
243
-			if (isset(BackgroundService::SHIPPED_BACKGROUNDS[$themingBackground]['primary_color'])) {
244
-				return BackgroundService::SHIPPED_BACKGROUNDS[$themingBackground]['primary_color'];
245
-			}
246
-		}
247
-
248
-		// If the default color is not valid, return the default background one
249
-		if (!preg_match('/^\#([0-9a-f]{3}|[0-9a-f]{6})$/i', $defaultColor)) {
250
-			return BackgroundService::DEFAULT_COLOR;
251
-		}
252
-
253
-		// Finally, return the system global primary color
254
-		return $defaultColor;
255
-	}
256
-
257
-	/**
258
-	 * Return the default color primary
259
-	 */
260
-	public function getDefaultColorPrimary(): string {
261
-		$color = $this->config->getAppValue(Application::APP_ID, 'color');
262
-		if (!preg_match('/^\#([0-9a-f]{3}|[0-9a-f]{6})$/i', $color)) {
263
-			$color = '#0082c9';
264
-		}
265
-		return $color;
266
-	}
267
-
268
-	/**
269
-	 * Themed logo url
270
-	 *
271
-	 * @param bool $useSvg Whether to point to the SVG image or a fallback
272
-	 * @return string
273
-	 */
274
-	public function getLogo($useSvg = true): string {
275
-		$logo = $this->config->getAppValue('theming', 'logoMime', '');
276
-
277
-		// short cut to avoid setting up the filesystem just to check if the logo is there
278
-		//
279
-		// explanation: if an SVG is requested and the app config value for logoMime is set then the logo is there.
280
-		// otherwise we need to check it and maybe also generate a PNG from the SVG (that's done in getImage() which
281
-		// needs to be called then)
282
-		if ($useSvg === true && $logo !== false) {
283
-			$logoExists = true;
284
-		} else {
285
-			try {
286
-				$this->imageManager->getImage('logo', $useSvg);
287
-				$logoExists = true;
288
-			} catch (\Exception $e) {
289
-				$logoExists = false;
290
-			}
291
-		}
292
-
293
-		$cacheBusterCounter = $this->config->getAppValue('theming', 'cachebuster', '0');
294
-
295
-		if (!$logo || !$logoExists) {
296
-			if ($useSvg) {
297
-				$logo = $this->urlGenerator->imagePath('core', 'logo/logo.svg');
298
-			} else {
299
-				$logo = $this->urlGenerator->imagePath('core', 'logo/logo.png');
300
-			}
301
-			return $logo . '?v=' . $cacheBusterCounter;
302
-		}
303
-
304
-		return $this->urlGenerator->linkToRoute('theming.Theming.getImage', [ 'key' => 'logo', 'useSvg' => $useSvg, 'v' => $cacheBusterCounter ]);
305
-	}
306
-
307
-	/**
308
-	 * Themed background image url
309
-	 *
310
-	 * @return string
311
-	 */
312
-	public function getBackground(): string {
313
-		return $this->imageManager->getImageUrl('background');
314
-	}
315
-
316
-	/**
317
-	 * @return string
318
-	 */
319
-	public function getiTunesAppId() {
320
-		return $this->config->getAppValue('theming', 'iTunesAppId', $this->iTunesAppId);
321
-	}
322
-
323
-	/**
324
-	 * @return string
325
-	 */
326
-	public function getiOSClientUrl() {
327
-		return $this->config->getAppValue('theming', 'iOSClientUrl', $this->iOSClientUrl);
328
-	}
329
-
330
-	/**
331
-	 * @return string
332
-	 */
333
-	public function getAndroidClientUrl() {
334
-		return $this->config->getAppValue('theming', 'AndroidClientUrl', $this->AndroidClientUrl);
335
-	}
336
-
337
-	/**
338
-	 * @return string
339
-	 */
340
-	public function getFDroidClientUrl() {
341
-		return $this->config->getAppValue('theming', 'FDroidClientUrl', $this->FDroidClientUrl);
342
-	}
343
-
344
-	/**
345
-	 * @return array scss variables to overwrite
346
-	 */
347
-	public function getScssVariables() {
348
-		$cacheBuster = $this->config->getAppValue('theming', 'cachebuster', '0');
349
-		$cache = $this->cacheFactory->createDistributed('theming-' . $cacheBuster . '-' . $this->urlGenerator->getBaseUrl());
350
-		if ($value = $cache->get('getScssVariables')) {
351
-			return $value;
352
-		}
353
-
354
-		$variables = [
355
-			'theming-cachebuster' => "'" . $cacheBuster . "'",
356
-			'theming-logo-mime' => "'" . $this->config->getAppValue('theming', 'logoMime') . "'",
357
-			'theming-background-mime' => "'" . $this->config->getAppValue('theming', 'backgroundMime') . "'",
358
-			'theming-logoheader-mime' => "'" . $this->config->getAppValue('theming', 'logoheaderMime') . "'",
359
-			'theming-favicon-mime' => "'" . $this->config->getAppValue('theming', 'faviconMime') . "'"
360
-		];
361
-
362
-		$variables['image-logo'] = "url('".$this->imageManager->getImageUrl('logo')."')";
363
-		$variables['image-logoheader'] = "url('".$this->imageManager->getImageUrl('logoheader')."')";
364
-		$variables['image-favicon'] = "url('".$this->imageManager->getImageUrl('favicon')."')";
365
-		$variables['image-login-background'] = "url('".$this->imageManager->getImageUrl('background')."')";
366
-		$variables['image-login-plain'] = 'false';
367
-
368
-		if ($this->config->getAppValue('theming', 'color', '') !== '') {
369
-			$variables['color-primary'] = $this->getColorPrimary();
370
-			$variables['color-primary-text'] = $this->getTextColorPrimary();
371
-			$variables['color-primary-element'] = $this->util->elementColor($this->getColorPrimary());
372
-		}
373
-
374
-		if ($this->config->getAppValue('theming', 'backgroundMime', '') === 'backgroundColor') {
375
-			$variables['image-login-plain'] = 'true';
376
-		}
377
-
378
-		$variables['has-legal-links'] = 'false';
379
-		if ($this->getImprintUrl() !== '' || $this->getPrivacyUrl() !== '') {
380
-			$variables['has-legal-links'] = 'true';
381
-		}
382
-
383
-		$cache->set('getScssVariables', $variables);
384
-		return $variables;
385
-	}
386
-
387
-	/**
388
-	 * Check if the image should be replaced by the theming app
389
-	 * and return the new image location then
390
-	 *
391
-	 * @param string $app name of the app
392
-	 * @param string $image filename of the image
393
-	 * @return bool|string false if image should not replaced, otherwise the location of the image
394
-	 */
395
-	public function replaceImagePath($app, $image) {
396
-		if ($app === '' || $app === 'files_sharing') {
397
-			$app = 'core';
398
-		}
399
-		$cacheBusterValue = $this->config->getAppValue('theming', 'cachebuster', '0');
400
-
401
-		$route = false;
402
-		if ($image === 'favicon.ico' && ($this->imageManager->shouldReplaceIcons() || $this->getCustomFavicon() !== null)) {
403
-			$route = $this->urlGenerator->linkToRoute('theming.Icon.getFavicon', ['app' => $app]);
404
-		}
405
-		if (($image === 'favicon-touch.png' || $image === 'favicon-fb.png') && ($this->imageManager->shouldReplaceIcons() || $this->getCustomFavicon() !== null)) {
406
-			$route = $this->urlGenerator->linkToRoute('theming.Icon.getTouchIcon', ['app' => $app]);
407
-		}
408
-		if ($image === 'manifest.json') {
409
-			try {
410
-				$appPath = $this->appManager->getAppPath($app);
411
-				if (file_exists($appPath . '/img/manifest.json')) {
412
-					return false;
413
-				}
414
-			} catch (AppPathNotFoundException $e) {
415
-			}
416
-			$route = $this->urlGenerator->linkToRoute('theming.Theming.getManifest', ['app' => $app ]);
417
-		}
418
-		if (strpos($image, 'filetypes/') === 0 && file_exists(\OC::$SERVERROOT . '/core/img/' . $image)) {
419
-			$route = $this->urlGenerator->linkToRoute('theming.Icon.getThemedIcon', ['app' => $app, 'image' => $image]);
420
-		}
421
-
422
-		if ($route) {
423
-			return $route . '?v=' . $cacheBusterValue;
424
-		}
425
-
426
-		return false;
427
-	}
428
-
429
-	protected function getCustomFavicon(): ?ISimpleFile {
430
-		try {
431
-			return $this->imageManager->getImage('favicon');
432
-		} catch (NotFoundException $e) {
433
-			return null;
434
-		}
435
-	}
436
-
437
-	/**
438
-	 * Increases the cache buster key
439
-	 */
440
-	public function increaseCacheBuster(): void {
441
-		$cacheBusterKey = (int)$this->config->getAppValue('theming', 'cachebuster', '0');
442
-		$this->config->setAppValue('theming', 'cachebuster', (string)($cacheBusterKey + 1));
443
-		$this->cacheFactory->createDistributed('theming-')->clear();
444
-		$this->cacheFactory->createDistributed('imagePath')->clear();
445
-	}
446
-
447
-	/**
448
-	 * Update setting in the database
449
-	 *
450
-	 * @param string $setting
451
-	 * @param string $value
452
-	 */
453
-	public function set($setting, $value) {
454
-		$this->config->setAppValue('theming', $setting, $value);
455
-		$this->increaseCacheBuster();
456
-	}
457
-
458
-	/**
459
-	 * Revert settings to the default value
460
-	 *
461
-	 * @param string $setting setting which should be reverted
462
-	 * @return string default value
463
-	 */
464
-	public function undo($setting) {
465
-		$this->config->deleteAppValue('theming', $setting);
466
-		$this->increaseCacheBuster();
467
-
468
-		$returnValue = '';
469
-		switch ($setting) {
470
-			case 'name':
471
-				$returnValue = $this->getEntity();
472
-				break;
473
-			case 'url':
474
-				$returnValue = $this->getBaseUrl();
475
-				break;
476
-			case 'slogan':
477
-				$returnValue = $this->getSlogan();
478
-				break;
479
-			case 'color':
480
-				$returnValue = $this->getColorPrimary();
481
-				break;
482
-			case 'logo':
483
-			case 'logoheader':
484
-			case 'background':
485
-			case 'favicon':
486
-				$this->imageManager->delete($setting);
487
-				break;
488
-		}
489
-
490
-		return $returnValue;
491
-	}
492
-
493
-	/**
494
-	 * Color of text in the header and primary buttons
495
-	 *
496
-	 * @return string
497
-	 */
498
-	public function getTextColorPrimary() {
499
-		return $this->util->invertTextColor($this->getColorPrimary()) ? '#000000' : '#ffffff';
500
-	}
501
-
502
-	/**
503
-	 * Has the admin disabled user customization
504
-	 */
505
-	public function isUserThemingDisabled(): bool {
506
-		return $this->config->getAppValue('theming', 'disable-user-theming', 'no') === 'yes';
507
-	}
58
+    private IConfig $config;
59
+    private IL10N $l;
60
+    private ImageManager $imageManager;
61
+    private IUserSession $userSession;
62
+    private IURLGenerator $urlGenerator;
63
+    private ICacheFactory $cacheFactory;
64
+    private Util $util;
65
+    private IAppManager $appManager;
66
+    private INavigationManager $navigationManager;
67
+
68
+    private string $name;
69
+    private string $title;
70
+    private string $entity;
71
+    private string $productName;
72
+    private string $url;
73
+    private string $color;
74
+
75
+    private string $iTunesAppId;
76
+    private string $iOSClientUrl;
77
+    private string $AndroidClientUrl;
78
+    private string $FDroidClientUrl;
79
+
80
+    /**
81
+     * ThemingDefaults constructor.
82
+     *
83
+     * @param IConfig $config
84
+     * @param IL10N $l
85
+     * @param ImageManager $imageManager
86
+     * @param IUserSession $userSession
87
+     * @param IURLGenerator $urlGenerator
88
+     * @param ICacheFactory $cacheFactory
89
+     * @param Util $util
90
+     * @param IAppManager $appManager
91
+     */
92
+    public function __construct(IConfig $config,
93
+                                IL10N $l,
94
+                                IUserSession $userSession,
95
+                                IURLGenerator $urlGenerator,
96
+                                ICacheFactory $cacheFactory,
97
+                                Util $util,
98
+                                ImageManager $imageManager,
99
+                                IAppManager $appManager,
100
+                                INavigationManager $navigationManager
101
+    ) {
102
+        parent::__construct();
103
+        $this->config = $config;
104
+        $this->l = $l;
105
+        $this->imageManager = $imageManager;
106
+        $this->userSession = $userSession;
107
+        $this->urlGenerator = $urlGenerator;
108
+        $this->cacheFactory = $cacheFactory;
109
+        $this->util = $util;
110
+        $this->appManager = $appManager;
111
+        $this->navigationManager = $navigationManager;
112
+
113
+        $this->name = parent::getName();
114
+        $this->title = parent::getTitle();
115
+        $this->entity = parent::getEntity();
116
+        $this->productName = parent::getProductName();
117
+        $this->url = parent::getBaseUrl();
118
+        $this->color = parent::getColorPrimary();
119
+        $this->iTunesAppId = parent::getiTunesAppId();
120
+        $this->iOSClientUrl = parent::getiOSClientUrl();
121
+        $this->AndroidClientUrl = parent::getAndroidClientUrl();
122
+        $this->FDroidClientUrl = parent::getFDroidClientUrl();
123
+    }
124
+
125
+    public function getName() {
126
+        return strip_tags($this->config->getAppValue('theming', 'name', $this->name));
127
+    }
128
+
129
+    public function getHTMLName() {
130
+        return $this->config->getAppValue('theming', 'name', $this->name);
131
+    }
132
+
133
+    public function getTitle() {
134
+        return strip_tags($this->config->getAppValue('theming', 'name', $this->title));
135
+    }
136
+
137
+    public function getEntity() {
138
+        return strip_tags($this->config->getAppValue('theming', 'name', $this->entity));
139
+    }
140
+
141
+    public function getProductName() {
142
+        return strip_tags($this->config->getAppValue('theming', 'productName', $this->productName));
143
+    }
144
+
145
+    public function getBaseUrl() {
146
+        return $this->config->getAppValue('theming', 'url', $this->url);
147
+    }
148
+
149
+    /**
150
+     * We pass a string and sanitizeHTML will return a string too in that case
151
+     * @psalm-suppress InvalidReturnStatement
152
+     * @psalm-suppress InvalidReturnType
153
+     */
154
+    public function getSlogan(?string $lang = null) {
155
+        return \OCP\Util::sanitizeHTML($this->config->getAppValue('theming', 'slogan', parent::getSlogan($lang)));
156
+    }
157
+
158
+    public function getImprintUrl() {
159
+        return (string)$this->config->getAppValue('theming', 'imprintUrl', '');
160
+    }
161
+
162
+    public function getPrivacyUrl() {
163
+        return (string)$this->config->getAppValue('theming', 'privacyUrl', '');
164
+    }
165
+
166
+    public function getShortFooter() {
167
+        $slogan = $this->getSlogan();
168
+        $baseUrl = $this->getBaseUrl();
169
+        if ($baseUrl !== '') {
170
+            $footer = '<a href="' . $baseUrl . '" target="_blank"' .
171
+                ' rel="noreferrer noopener" class="entity-name">' . $this->getEntity() . '</a>';
172
+        } else {
173
+            $footer = '<span class="entity-name">' .$this->getEntity() . '</span>';
174
+        }
175
+        $footer .= ($slogan !== '' ? ' – ' . $slogan : '');
176
+
177
+        $links = [
178
+            [
179
+                'text' => $this->l->t('Legal notice'),
180
+                'url' => (string)$this->getImprintUrl()
181
+            ],
182
+            [
183
+                'text' => $this->l->t('Privacy policy'),
184
+                'url' => (string)$this->getPrivacyUrl()
185
+            ],
186
+        ];
187
+
188
+        $navigation = $this->navigationManager->getAll(INavigationManager::TYPE_GUEST);
189
+        $guestNavigation = array_map(function ($nav) {
190
+            return [
191
+                'text' => $nav['name'],
192
+                'url' => $nav['href']
193
+            ];
194
+        }, $navigation);
195
+        $links = array_merge($links, $guestNavigation);
196
+
197
+        $legalLinks = '';
198
+        $divider = '';
199
+        foreach ($links as $link) {
200
+            if ($link['url'] !== ''
201
+                && filter_var($link['url'], FILTER_VALIDATE_URL)
202
+            ) {
203
+                $legalLinks .= $divider . '<a href="' . $link['url'] . '" class="legal" target="_blank"' .
204
+                    ' rel="noreferrer noopener">' . $link['text'] . '</a>';
205
+                $divider = ' · ';
206
+            }
207
+        }
208
+        if ($legalLinks !== '') {
209
+            $footer .= '<br/>' . $legalLinks;
210
+        }
211
+
212
+        return $footer;
213
+    }
214
+
215
+    /**
216
+     * Color that is used for the header as well as for mail headers
217
+     */
218
+    public function getColorPrimary(): string {
219
+        $user = $this->userSession->getUser();
220
+
221
+        // admin-defined primary color
222
+        $defaultColor = $this->getDefaultColorPrimary();
223
+
224
+        if ($this->isUserThemingDisabled()) {
225
+            return $defaultColor;
226
+        }
227
+
228
+        // user-defined primary color
229
+        $themingBackground = '';
230
+        if (!empty($user)) {
231
+            $themingBackground = $this->config->getUserValue($user->getUID(), Application::APP_ID, 'background', '');
232
+            // If the user selected the default background
233
+            if ($themingBackground === '') {
234
+                return BackgroundService::DEFAULT_COLOR;
235
+            }
236
+
237
+            // If the user selected a specific colour
238
+            if (preg_match('/^\#([0-9a-f]{3}|[0-9a-f]{6})$/i', $themingBackground)) {
239
+                return $themingBackground;
240
+            }
241
+
242
+            // if the user-selected background is a background reference
243
+            if (isset(BackgroundService::SHIPPED_BACKGROUNDS[$themingBackground]['primary_color'])) {
244
+                return BackgroundService::SHIPPED_BACKGROUNDS[$themingBackground]['primary_color'];
245
+            }
246
+        }
247
+
248
+        // If the default color is not valid, return the default background one
249
+        if (!preg_match('/^\#([0-9a-f]{3}|[0-9a-f]{6})$/i', $defaultColor)) {
250
+            return BackgroundService::DEFAULT_COLOR;
251
+        }
252
+
253
+        // Finally, return the system global primary color
254
+        return $defaultColor;
255
+    }
256
+
257
+    /**
258
+     * Return the default color primary
259
+     */
260
+    public function getDefaultColorPrimary(): string {
261
+        $color = $this->config->getAppValue(Application::APP_ID, 'color');
262
+        if (!preg_match('/^\#([0-9a-f]{3}|[0-9a-f]{6})$/i', $color)) {
263
+            $color = '#0082c9';
264
+        }
265
+        return $color;
266
+    }
267
+
268
+    /**
269
+     * Themed logo url
270
+     *
271
+     * @param bool $useSvg Whether to point to the SVG image or a fallback
272
+     * @return string
273
+     */
274
+    public function getLogo($useSvg = true): string {
275
+        $logo = $this->config->getAppValue('theming', 'logoMime', '');
276
+
277
+        // short cut to avoid setting up the filesystem just to check if the logo is there
278
+        //
279
+        // explanation: if an SVG is requested and the app config value for logoMime is set then the logo is there.
280
+        // otherwise we need to check it and maybe also generate a PNG from the SVG (that's done in getImage() which
281
+        // needs to be called then)
282
+        if ($useSvg === true && $logo !== false) {
283
+            $logoExists = true;
284
+        } else {
285
+            try {
286
+                $this->imageManager->getImage('logo', $useSvg);
287
+                $logoExists = true;
288
+            } catch (\Exception $e) {
289
+                $logoExists = false;
290
+            }
291
+        }
292
+
293
+        $cacheBusterCounter = $this->config->getAppValue('theming', 'cachebuster', '0');
294
+
295
+        if (!$logo || !$logoExists) {
296
+            if ($useSvg) {
297
+                $logo = $this->urlGenerator->imagePath('core', 'logo/logo.svg');
298
+            } else {
299
+                $logo = $this->urlGenerator->imagePath('core', 'logo/logo.png');
300
+            }
301
+            return $logo . '?v=' . $cacheBusterCounter;
302
+        }
303
+
304
+        return $this->urlGenerator->linkToRoute('theming.Theming.getImage', [ 'key' => 'logo', 'useSvg' => $useSvg, 'v' => $cacheBusterCounter ]);
305
+    }
306
+
307
+    /**
308
+     * Themed background image url
309
+     *
310
+     * @return string
311
+     */
312
+    public function getBackground(): string {
313
+        return $this->imageManager->getImageUrl('background');
314
+    }
315
+
316
+    /**
317
+     * @return string
318
+     */
319
+    public function getiTunesAppId() {
320
+        return $this->config->getAppValue('theming', 'iTunesAppId', $this->iTunesAppId);
321
+    }
322
+
323
+    /**
324
+     * @return string
325
+     */
326
+    public function getiOSClientUrl() {
327
+        return $this->config->getAppValue('theming', 'iOSClientUrl', $this->iOSClientUrl);
328
+    }
329
+
330
+    /**
331
+     * @return string
332
+     */
333
+    public function getAndroidClientUrl() {
334
+        return $this->config->getAppValue('theming', 'AndroidClientUrl', $this->AndroidClientUrl);
335
+    }
336
+
337
+    /**
338
+     * @return string
339
+     */
340
+    public function getFDroidClientUrl() {
341
+        return $this->config->getAppValue('theming', 'FDroidClientUrl', $this->FDroidClientUrl);
342
+    }
343
+
344
+    /**
345
+     * @return array scss variables to overwrite
346
+     */
347
+    public function getScssVariables() {
348
+        $cacheBuster = $this->config->getAppValue('theming', 'cachebuster', '0');
349
+        $cache = $this->cacheFactory->createDistributed('theming-' . $cacheBuster . '-' . $this->urlGenerator->getBaseUrl());
350
+        if ($value = $cache->get('getScssVariables')) {
351
+            return $value;
352
+        }
353
+
354
+        $variables = [
355
+            'theming-cachebuster' => "'" . $cacheBuster . "'",
356
+            'theming-logo-mime' => "'" . $this->config->getAppValue('theming', 'logoMime') . "'",
357
+            'theming-background-mime' => "'" . $this->config->getAppValue('theming', 'backgroundMime') . "'",
358
+            'theming-logoheader-mime' => "'" . $this->config->getAppValue('theming', 'logoheaderMime') . "'",
359
+            'theming-favicon-mime' => "'" . $this->config->getAppValue('theming', 'faviconMime') . "'"
360
+        ];
361
+
362
+        $variables['image-logo'] = "url('".$this->imageManager->getImageUrl('logo')."')";
363
+        $variables['image-logoheader'] = "url('".$this->imageManager->getImageUrl('logoheader')."')";
364
+        $variables['image-favicon'] = "url('".$this->imageManager->getImageUrl('favicon')."')";
365
+        $variables['image-login-background'] = "url('".$this->imageManager->getImageUrl('background')."')";
366
+        $variables['image-login-plain'] = 'false';
367
+
368
+        if ($this->config->getAppValue('theming', 'color', '') !== '') {
369
+            $variables['color-primary'] = $this->getColorPrimary();
370
+            $variables['color-primary-text'] = $this->getTextColorPrimary();
371
+            $variables['color-primary-element'] = $this->util->elementColor($this->getColorPrimary());
372
+        }
373
+
374
+        if ($this->config->getAppValue('theming', 'backgroundMime', '') === 'backgroundColor') {
375
+            $variables['image-login-plain'] = 'true';
376
+        }
377
+
378
+        $variables['has-legal-links'] = 'false';
379
+        if ($this->getImprintUrl() !== '' || $this->getPrivacyUrl() !== '') {
380
+            $variables['has-legal-links'] = 'true';
381
+        }
382
+
383
+        $cache->set('getScssVariables', $variables);
384
+        return $variables;
385
+    }
386
+
387
+    /**
388
+     * Check if the image should be replaced by the theming app
389
+     * and return the new image location then
390
+     *
391
+     * @param string $app name of the app
392
+     * @param string $image filename of the image
393
+     * @return bool|string false if image should not replaced, otherwise the location of the image
394
+     */
395
+    public function replaceImagePath($app, $image) {
396
+        if ($app === '' || $app === 'files_sharing') {
397
+            $app = 'core';
398
+        }
399
+        $cacheBusterValue = $this->config->getAppValue('theming', 'cachebuster', '0');
400
+
401
+        $route = false;
402
+        if ($image === 'favicon.ico' && ($this->imageManager->shouldReplaceIcons() || $this->getCustomFavicon() !== null)) {
403
+            $route = $this->urlGenerator->linkToRoute('theming.Icon.getFavicon', ['app' => $app]);
404
+        }
405
+        if (($image === 'favicon-touch.png' || $image === 'favicon-fb.png') && ($this->imageManager->shouldReplaceIcons() || $this->getCustomFavicon() !== null)) {
406
+            $route = $this->urlGenerator->linkToRoute('theming.Icon.getTouchIcon', ['app' => $app]);
407
+        }
408
+        if ($image === 'manifest.json') {
409
+            try {
410
+                $appPath = $this->appManager->getAppPath($app);
411
+                if (file_exists($appPath . '/img/manifest.json')) {
412
+                    return false;
413
+                }
414
+            } catch (AppPathNotFoundException $e) {
415
+            }
416
+            $route = $this->urlGenerator->linkToRoute('theming.Theming.getManifest', ['app' => $app ]);
417
+        }
418
+        if (strpos($image, 'filetypes/') === 0 && file_exists(\OC::$SERVERROOT . '/core/img/' . $image)) {
419
+            $route = $this->urlGenerator->linkToRoute('theming.Icon.getThemedIcon', ['app' => $app, 'image' => $image]);
420
+        }
421
+
422
+        if ($route) {
423
+            return $route . '?v=' . $cacheBusterValue;
424
+        }
425
+
426
+        return false;
427
+    }
428
+
429
+    protected function getCustomFavicon(): ?ISimpleFile {
430
+        try {
431
+            return $this->imageManager->getImage('favicon');
432
+        } catch (NotFoundException $e) {
433
+            return null;
434
+        }
435
+    }
436
+
437
+    /**
438
+     * Increases the cache buster key
439
+     */
440
+    public function increaseCacheBuster(): void {
441
+        $cacheBusterKey = (int)$this->config->getAppValue('theming', 'cachebuster', '0');
442
+        $this->config->setAppValue('theming', 'cachebuster', (string)($cacheBusterKey + 1));
443
+        $this->cacheFactory->createDistributed('theming-')->clear();
444
+        $this->cacheFactory->createDistributed('imagePath')->clear();
445
+    }
446
+
447
+    /**
448
+     * Update setting in the database
449
+     *
450
+     * @param string $setting
451
+     * @param string $value
452
+     */
453
+    public function set($setting, $value) {
454
+        $this->config->setAppValue('theming', $setting, $value);
455
+        $this->increaseCacheBuster();
456
+    }
457
+
458
+    /**
459
+     * Revert settings to the default value
460
+     *
461
+     * @param string $setting setting which should be reverted
462
+     * @return string default value
463
+     */
464
+    public function undo($setting) {
465
+        $this->config->deleteAppValue('theming', $setting);
466
+        $this->increaseCacheBuster();
467
+
468
+        $returnValue = '';
469
+        switch ($setting) {
470
+            case 'name':
471
+                $returnValue = $this->getEntity();
472
+                break;
473
+            case 'url':
474
+                $returnValue = $this->getBaseUrl();
475
+                break;
476
+            case 'slogan':
477
+                $returnValue = $this->getSlogan();
478
+                break;
479
+            case 'color':
480
+                $returnValue = $this->getColorPrimary();
481
+                break;
482
+            case 'logo':
483
+            case 'logoheader':
484
+            case 'background':
485
+            case 'favicon':
486
+                $this->imageManager->delete($setting);
487
+                break;
488
+        }
489
+
490
+        return $returnValue;
491
+    }
492
+
493
+    /**
494
+     * Color of text in the header and primary buttons
495
+     *
496
+     * @return string
497
+     */
498
+    public function getTextColorPrimary() {
499
+        return $this->util->invertTextColor($this->getColorPrimary()) ? '#000000' : '#ffffff';
500
+    }
501
+
502
+    /**
503
+     * Has the admin disabled user customization
504
+     */
505
+    public function isUserThemingDisabled(): bool {
506
+        return $this->config->getAppValue('theming', 'disable-user-theming', 'no') === 'yes';
507
+    }
508 508
 }
Please login to merge, or discard this patch.
apps/theming/lib/ImageManager.php 2 patches
Indentation   +266 added lines, -266 removed lines patch added patch discarded remove patch
@@ -45,270 +45,270 @@
 block discarded – undo
45 45
 use OCP\IURLGenerator;
46 46
 
47 47
 class ImageManager {
48
-	public const SupportedImageKeys = ['background', 'logo', 'logoheader', 'favicon'];
49
-
50
-	/** @var IConfig */
51
-	private $config;
52
-	/** @var IAppData */
53
-	private $appData;
54
-	/** @var IURLGenerator */
55
-	private $urlGenerator;
56
-	/** @var ICacheFactory */
57
-	private $cacheFactory;
58
-	/** @var ILogger */
59
-	private $logger;
60
-	/** @var ITempManager */
61
-	private $tempManager;
62
-
63
-	public function __construct(IConfig $config,
64
-								IAppData $appData,
65
-								IURLGenerator $urlGenerator,
66
-								ICacheFactory $cacheFactory,
67
-								ILogger $logger,
68
-								ITempManager $tempManager) {
69
-		$this->config = $config;
70
-		$this->urlGenerator = $urlGenerator;
71
-		$this->cacheFactory = $cacheFactory;
72
-		$this->logger = $logger;
73
-		$this->tempManager = $tempManager;
74
-		$this->appData = $appData;
75
-	}
76
-
77
-	public function getImageUrl(string $key, bool $useSvg = true): string {
78
-		$cacheBusterCounter = $this->config->getAppValue('theming', 'cachebuster', '0');
79
-		if ($this->hasImage($key)) {
80
-			return $this->urlGenerator->linkToRoute('theming.Theming.getImage', [ 'key' => $key ]) . '?v=' . $cacheBusterCounter;
81
-		}
82
-
83
-		switch ($key) {
84
-			case 'logo':
85
-			case 'logoheader':
86
-			case 'favicon':
87
-				return $this->urlGenerator->imagePath('core', 'logo/logo.png') . '?v=' . $cacheBusterCounter;
88
-			case 'background':
89
-				return $this->urlGenerator->imagePath('core', 'background.png') . '?v=' . $cacheBusterCounter;
90
-		}
91
-		return '';
92
-	}
93
-
94
-	public function getImageUrlAbsolute(string $key, bool $useSvg = true): string {
95
-		return $this->urlGenerator->getAbsoluteURL($this->getImageUrl($key, $useSvg));
96
-	}
97
-
98
-	/**
99
-	 * @param string $key
100
-	 * @param bool $useSvg
101
-	 * @return ISimpleFile
102
-	 * @throws NotFoundException
103
-	 * @throws NotPermittedException
104
-	 */
105
-	public function getImage(string $key, bool $useSvg = true): ISimpleFile {
106
-		$logo = $this->config->getAppValue('theming', $key . 'Mime', '');
107
-		$folder = $this->getRootFolder()->getFolder('images');
108
-
109
-		if ($logo === '' || !$folder->fileExists($key)) {
110
-			throw new NotFoundException();
111
-		}
112
-
113
-		if (!$useSvg && $this->shouldReplaceIcons()) {
114
-			if (!$folder->fileExists($key . '.png')) {
115
-				try {
116
-					$finalIconFile = new \Imagick();
117
-					$finalIconFile->setBackgroundColor('none');
118
-					$finalIconFile->readImageBlob($folder->getFile($key)->getContent());
119
-					$finalIconFile->setImageFormat('png32');
120
-					$pngFile = $folder->newFile($key . '.png');
121
-					$pngFile->putContent($finalIconFile->getImageBlob());
122
-					return $pngFile;
123
-				} catch (\ImagickException $e) {
124
-					$this->logger->info('The image was requested to be no SVG file, but converting it to PNG failed: ' . $e->getMessage());
125
-				}
126
-			} else {
127
-				return $folder->getFile($key . '.png');
128
-			}
129
-		}
130
-
131
-		return $folder->getFile($key);
132
-	}
133
-
134
-	public function hasImage(string $key): bool {
135
-		$mimeSetting = $this->config->getAppValue('theming', $key . 'Mime', '');
136
-		return $mimeSetting !== '';
137
-	}
138
-
139
-	/**
140
-	 * Get folder for current theming files
141
-	 *
142
-	 * @return ISimpleFolder
143
-	 * @throws NotPermittedException
144
-	 */
145
-	public function getCacheFolder(): ISimpleFolder {
146
-		$cacheBusterValue = $this->config->getAppValue('theming', 'cachebuster', '0');
147
-		try {
148
-			$folder = $this->getRootFolder()->getFolder($cacheBusterValue);
149
-		} catch (NotFoundException $e) {
150
-			$folder = $this->getRootFolder()->newFolder($cacheBusterValue);
151
-			$this->cleanup();
152
-		}
153
-		return $folder;
154
-	}
155
-
156
-	/**
157
-	 * Get a file from AppData
158
-	 *
159
-	 * @param string $filename
160
-	 * @throws NotFoundException
161
-	 * @return \OCP\Files\SimpleFS\ISimpleFile
162
-	 * @throws NotPermittedException
163
-	 */
164
-	public function getCachedImage(string $filename): ISimpleFile {
165
-		$currentFolder = $this->getCacheFolder();
166
-		return $currentFolder->getFile($filename);
167
-	}
168
-
169
-	/**
170
-	 * Store a file for theming in AppData
171
-	 *
172
-	 * @param string $filename
173
-	 * @param string $data
174
-	 * @return \OCP\Files\SimpleFS\ISimpleFile
175
-	 * @throws NotFoundException
176
-	 * @throws NotPermittedException
177
-	 */
178
-	public function setCachedImage(string $filename, string $data): ISimpleFile {
179
-		$currentFolder = $this->getCacheFolder();
180
-		if ($currentFolder->fileExists($filename)) {
181
-			$file = $currentFolder->getFile($filename);
182
-		} else {
183
-			$file = $currentFolder->newFile($filename);
184
-		}
185
-		$file->putContent($data);
186
-		return $file;
187
-	}
188
-
189
-	public function delete(string $key): void {
190
-		/* ignore exceptions, since we don't want to fail hard if something goes wrong during cleanup */
191
-		try {
192
-			$file = $this->getRootFolder()->getFolder('images')->getFile($key);
193
-			$file->delete();
194
-		} catch (NotFoundException $e) {
195
-		} catch (NotPermittedException $e) {
196
-		}
197
-		try {
198
-			$file = $this->getRootFolder()->getFolder('images')->getFile($key . '.png');
199
-			$file->delete();
200
-		} catch (NotFoundException $e) {
201
-		} catch (NotPermittedException $e) {
202
-		}
203
-	}
204
-
205
-	public function updateImage(string $key, string $tmpFile): string {
206
-		$this->delete($key);
207
-
208
-		try {
209
-			$folder = $this->getRootFolder()->getFolder('images');
210
-		} catch (NotFoundException $e) {
211
-			$folder = $this->getRootFolder()->newFolder('images');
212
-		}
213
-
214
-		$target = $folder->newFile($key);
215
-		$supportedFormats = $this->getSupportedUploadImageFormats($key);
216
-		$detectedMimeType = mime_content_type($tmpFile);
217
-		if (!in_array($detectedMimeType, $supportedFormats, true)) {
218
-			throw new \Exception('Unsupported image type');
219
-		}
220
-
221
-		if ($key === 'background' && strpos($detectedMimeType, 'image/svg') === false && strpos($detectedMimeType, 'image/gif') === false) {
222
-			// Optimize the image since some people may upload images that will be
223
-			// either to big or are not progressive rendering.
224
-			$newImage = @imagecreatefromstring(file_get_contents($tmpFile));
225
-
226
-			// Preserve transparency
227
-			imagesavealpha($newImage, true);
228
-			imagealphablending($newImage, true);
229
-
230
-			$tmpFile = $this->tempManager->getTemporaryFile();
231
-			$newWidth = (int)(imagesx($newImage) < 4096 ? imagesx($newImage) : 4096);
232
-			$newHeight = (int)(imagesy($newImage) / (imagesx($newImage) / $newWidth));
233
-			$outputImage = imagescale($newImage, $newWidth, $newHeight);
234
-
235
-			imageinterlace($outputImage, 1);
236
-			imagepng($outputImage, $tmpFile, 8);
237
-			imagedestroy($outputImage);
238
-
239
-			$target->putContent(file_get_contents($tmpFile));
240
-		} else {
241
-			$target->putContent(file_get_contents($tmpFile));
242
-		}
243
-
244
-		return $detectedMimeType;
245
-	}
246
-
247
-	/**
248
-	 * Returns a list of supported mime types for image uploads.
249
-	 * "favicon" images are only allowed to be SVG when imagemagick with SVG support is available.
250
-	 *
251
-	 * @param string $key The image key, e.g. "favicon"
252
-	 * @return string[]
253
-	 */
254
-	private function getSupportedUploadImageFormats(string $key): array {
255
-		$supportedFormats = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
256
-
257
-		if ($key !== 'favicon' || $this->shouldReplaceIcons() === true) {
258
-			$supportedFormats[] = 'image/svg+xml';
259
-			$supportedFormats[] = 'image/svg';
260
-		}
261
-
262
-		if ($key === 'favicon') {
263
-			$supportedFormats[] = 'image/x-icon';
264
-			$supportedFormats[] = 'image/vnd.microsoft.icon';
265
-		}
266
-
267
-		return $supportedFormats;
268
-	}
269
-
270
-	/**
271
-	 * remove cached files that are not required any longer
272
-	 *
273
-	 * @throws NotPermittedException
274
-	 * @throws NotFoundException
275
-	 */
276
-	public function cleanup() {
277
-		$currentFolder = $this->getCacheFolder();
278
-		$folders = $this->getRootFolder()->getDirectoryListing();
279
-		foreach ($folders as $folder) {
280
-			if ($folder->getName() !== 'images' && $folder->getName() !== $currentFolder->getName()) {
281
-				$folder->delete();
282
-			}
283
-		}
284
-	}
285
-
286
-	/**
287
-	 * Check if Imagemagick is enabled and if SVG is supported
288
-	 * otherwise we can't render custom icons
289
-	 *
290
-	 * @return bool
291
-	 */
292
-	public function shouldReplaceIcons() {
293
-		$cache = $this->cacheFactory->createDistributed('theming-' . $this->urlGenerator->getBaseUrl());
294
-		if ($value = $cache->get('shouldReplaceIcons')) {
295
-			return (bool)$value;
296
-		}
297
-		$value = false;
298
-		if (extension_loaded('imagick')) {
299
-			if (count(\Imagick::queryFormats('SVG')) >= 1) {
300
-				$value = true;
301
-			}
302
-		}
303
-		$cache->set('shouldReplaceIcons', $value);
304
-		return $value;
305
-	}
306
-
307
-	private function getRootFolder(): ISimpleFolder {
308
-		try {
309
-			return $this->appData->getFolder('global');
310
-		} catch (NotFoundException $e) {
311
-			return $this->appData->newFolder('global');
312
-		}
313
-	}
48
+    public const SupportedImageKeys = ['background', 'logo', 'logoheader', 'favicon'];
49
+
50
+    /** @var IConfig */
51
+    private $config;
52
+    /** @var IAppData */
53
+    private $appData;
54
+    /** @var IURLGenerator */
55
+    private $urlGenerator;
56
+    /** @var ICacheFactory */
57
+    private $cacheFactory;
58
+    /** @var ILogger */
59
+    private $logger;
60
+    /** @var ITempManager */
61
+    private $tempManager;
62
+
63
+    public function __construct(IConfig $config,
64
+                                IAppData $appData,
65
+                                IURLGenerator $urlGenerator,
66
+                                ICacheFactory $cacheFactory,
67
+                                ILogger $logger,
68
+                                ITempManager $tempManager) {
69
+        $this->config = $config;
70
+        $this->urlGenerator = $urlGenerator;
71
+        $this->cacheFactory = $cacheFactory;
72
+        $this->logger = $logger;
73
+        $this->tempManager = $tempManager;
74
+        $this->appData = $appData;
75
+    }
76
+
77
+    public function getImageUrl(string $key, bool $useSvg = true): string {
78
+        $cacheBusterCounter = $this->config->getAppValue('theming', 'cachebuster', '0');
79
+        if ($this->hasImage($key)) {
80
+            return $this->urlGenerator->linkToRoute('theming.Theming.getImage', [ 'key' => $key ]) . '?v=' . $cacheBusterCounter;
81
+        }
82
+
83
+        switch ($key) {
84
+            case 'logo':
85
+            case 'logoheader':
86
+            case 'favicon':
87
+                return $this->urlGenerator->imagePath('core', 'logo/logo.png') . '?v=' . $cacheBusterCounter;
88
+            case 'background':
89
+                return $this->urlGenerator->imagePath('core', 'background.png') . '?v=' . $cacheBusterCounter;
90
+        }
91
+        return '';
92
+    }
93
+
94
+    public function getImageUrlAbsolute(string $key, bool $useSvg = true): string {
95
+        return $this->urlGenerator->getAbsoluteURL($this->getImageUrl($key, $useSvg));
96
+    }
97
+
98
+    /**
99
+     * @param string $key
100
+     * @param bool $useSvg
101
+     * @return ISimpleFile
102
+     * @throws NotFoundException
103
+     * @throws NotPermittedException
104
+     */
105
+    public function getImage(string $key, bool $useSvg = true): ISimpleFile {
106
+        $logo = $this->config->getAppValue('theming', $key . 'Mime', '');
107
+        $folder = $this->getRootFolder()->getFolder('images');
108
+
109
+        if ($logo === '' || !$folder->fileExists($key)) {
110
+            throw new NotFoundException();
111
+        }
112
+
113
+        if (!$useSvg && $this->shouldReplaceIcons()) {
114
+            if (!$folder->fileExists($key . '.png')) {
115
+                try {
116
+                    $finalIconFile = new \Imagick();
117
+                    $finalIconFile->setBackgroundColor('none');
118
+                    $finalIconFile->readImageBlob($folder->getFile($key)->getContent());
119
+                    $finalIconFile->setImageFormat('png32');
120
+                    $pngFile = $folder->newFile($key . '.png');
121
+                    $pngFile->putContent($finalIconFile->getImageBlob());
122
+                    return $pngFile;
123
+                } catch (\ImagickException $e) {
124
+                    $this->logger->info('The image was requested to be no SVG file, but converting it to PNG failed: ' . $e->getMessage());
125
+                }
126
+            } else {
127
+                return $folder->getFile($key . '.png');
128
+            }
129
+        }
130
+
131
+        return $folder->getFile($key);
132
+    }
133
+
134
+    public function hasImage(string $key): bool {
135
+        $mimeSetting = $this->config->getAppValue('theming', $key . 'Mime', '');
136
+        return $mimeSetting !== '';
137
+    }
138
+
139
+    /**
140
+     * Get folder for current theming files
141
+     *
142
+     * @return ISimpleFolder
143
+     * @throws NotPermittedException
144
+     */
145
+    public function getCacheFolder(): ISimpleFolder {
146
+        $cacheBusterValue = $this->config->getAppValue('theming', 'cachebuster', '0');
147
+        try {
148
+            $folder = $this->getRootFolder()->getFolder($cacheBusterValue);
149
+        } catch (NotFoundException $e) {
150
+            $folder = $this->getRootFolder()->newFolder($cacheBusterValue);
151
+            $this->cleanup();
152
+        }
153
+        return $folder;
154
+    }
155
+
156
+    /**
157
+     * Get a file from AppData
158
+     *
159
+     * @param string $filename
160
+     * @throws NotFoundException
161
+     * @return \OCP\Files\SimpleFS\ISimpleFile
162
+     * @throws NotPermittedException
163
+     */
164
+    public function getCachedImage(string $filename): ISimpleFile {
165
+        $currentFolder = $this->getCacheFolder();
166
+        return $currentFolder->getFile($filename);
167
+    }
168
+
169
+    /**
170
+     * Store a file for theming in AppData
171
+     *
172
+     * @param string $filename
173
+     * @param string $data
174
+     * @return \OCP\Files\SimpleFS\ISimpleFile
175
+     * @throws NotFoundException
176
+     * @throws NotPermittedException
177
+     */
178
+    public function setCachedImage(string $filename, string $data): ISimpleFile {
179
+        $currentFolder = $this->getCacheFolder();
180
+        if ($currentFolder->fileExists($filename)) {
181
+            $file = $currentFolder->getFile($filename);
182
+        } else {
183
+            $file = $currentFolder->newFile($filename);
184
+        }
185
+        $file->putContent($data);
186
+        return $file;
187
+    }
188
+
189
+    public function delete(string $key): void {
190
+        /* ignore exceptions, since we don't want to fail hard if something goes wrong during cleanup */
191
+        try {
192
+            $file = $this->getRootFolder()->getFolder('images')->getFile($key);
193
+            $file->delete();
194
+        } catch (NotFoundException $e) {
195
+        } catch (NotPermittedException $e) {
196
+        }
197
+        try {
198
+            $file = $this->getRootFolder()->getFolder('images')->getFile($key . '.png');
199
+            $file->delete();
200
+        } catch (NotFoundException $e) {
201
+        } catch (NotPermittedException $e) {
202
+        }
203
+    }
204
+
205
+    public function updateImage(string $key, string $tmpFile): string {
206
+        $this->delete($key);
207
+
208
+        try {
209
+            $folder = $this->getRootFolder()->getFolder('images');
210
+        } catch (NotFoundException $e) {
211
+            $folder = $this->getRootFolder()->newFolder('images');
212
+        }
213
+
214
+        $target = $folder->newFile($key);
215
+        $supportedFormats = $this->getSupportedUploadImageFormats($key);
216
+        $detectedMimeType = mime_content_type($tmpFile);
217
+        if (!in_array($detectedMimeType, $supportedFormats, true)) {
218
+            throw new \Exception('Unsupported image type');
219
+        }
220
+
221
+        if ($key === 'background' && strpos($detectedMimeType, 'image/svg') === false && strpos($detectedMimeType, 'image/gif') === false) {
222
+            // Optimize the image since some people may upload images that will be
223
+            // either to big or are not progressive rendering.
224
+            $newImage = @imagecreatefromstring(file_get_contents($tmpFile));
225
+
226
+            // Preserve transparency
227
+            imagesavealpha($newImage, true);
228
+            imagealphablending($newImage, true);
229
+
230
+            $tmpFile = $this->tempManager->getTemporaryFile();
231
+            $newWidth = (int)(imagesx($newImage) < 4096 ? imagesx($newImage) : 4096);
232
+            $newHeight = (int)(imagesy($newImage) / (imagesx($newImage) / $newWidth));
233
+            $outputImage = imagescale($newImage, $newWidth, $newHeight);
234
+
235
+            imageinterlace($outputImage, 1);
236
+            imagepng($outputImage, $tmpFile, 8);
237
+            imagedestroy($outputImage);
238
+
239
+            $target->putContent(file_get_contents($tmpFile));
240
+        } else {
241
+            $target->putContent(file_get_contents($tmpFile));
242
+        }
243
+
244
+        return $detectedMimeType;
245
+    }
246
+
247
+    /**
248
+     * Returns a list of supported mime types for image uploads.
249
+     * "favicon" images are only allowed to be SVG when imagemagick with SVG support is available.
250
+     *
251
+     * @param string $key The image key, e.g. "favicon"
252
+     * @return string[]
253
+     */
254
+    private function getSupportedUploadImageFormats(string $key): array {
255
+        $supportedFormats = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
256
+
257
+        if ($key !== 'favicon' || $this->shouldReplaceIcons() === true) {
258
+            $supportedFormats[] = 'image/svg+xml';
259
+            $supportedFormats[] = 'image/svg';
260
+        }
261
+
262
+        if ($key === 'favicon') {
263
+            $supportedFormats[] = 'image/x-icon';
264
+            $supportedFormats[] = 'image/vnd.microsoft.icon';
265
+        }
266
+
267
+        return $supportedFormats;
268
+    }
269
+
270
+    /**
271
+     * remove cached files that are not required any longer
272
+     *
273
+     * @throws NotPermittedException
274
+     * @throws NotFoundException
275
+     */
276
+    public function cleanup() {
277
+        $currentFolder = $this->getCacheFolder();
278
+        $folders = $this->getRootFolder()->getDirectoryListing();
279
+        foreach ($folders as $folder) {
280
+            if ($folder->getName() !== 'images' && $folder->getName() !== $currentFolder->getName()) {
281
+                $folder->delete();
282
+            }
283
+        }
284
+    }
285
+
286
+    /**
287
+     * Check if Imagemagick is enabled and if SVG is supported
288
+     * otherwise we can't render custom icons
289
+     *
290
+     * @return bool
291
+     */
292
+    public function shouldReplaceIcons() {
293
+        $cache = $this->cacheFactory->createDistributed('theming-' . $this->urlGenerator->getBaseUrl());
294
+        if ($value = $cache->get('shouldReplaceIcons')) {
295
+            return (bool)$value;
296
+        }
297
+        $value = false;
298
+        if (extension_loaded('imagick')) {
299
+            if (count(\Imagick::queryFormats('SVG')) >= 1) {
300
+                $value = true;
301
+            }
302
+        }
303
+        $cache->set('shouldReplaceIcons', $value);
304
+        return $value;
305
+    }
306
+
307
+    private function getRootFolder(): ISimpleFolder {
308
+        try {
309
+            return $this->appData->getFolder('global');
310
+        } catch (NotFoundException $e) {
311
+            return $this->appData->newFolder('global');
312
+        }
313
+    }
314 314
 }
Please login to merge, or discard this patch.
Spacing   +14 added lines, -14 removed lines patch added patch discarded remove patch
@@ -77,16 +77,16 @@  discard block
 block discarded – undo
77 77
 	public function getImageUrl(string $key, bool $useSvg = true): string {
78 78
 		$cacheBusterCounter = $this->config->getAppValue('theming', 'cachebuster', '0');
79 79
 		if ($this->hasImage($key)) {
80
-			return $this->urlGenerator->linkToRoute('theming.Theming.getImage', [ 'key' => $key ]) . '?v=' . $cacheBusterCounter;
80
+			return $this->urlGenerator->linkToRoute('theming.Theming.getImage', ['key' => $key]).'?v='.$cacheBusterCounter;
81 81
 		}
82 82
 
83 83
 		switch ($key) {
84 84
 			case 'logo':
85 85
 			case 'logoheader':
86 86
 			case 'favicon':
87
-				return $this->urlGenerator->imagePath('core', 'logo/logo.png') . '?v=' . $cacheBusterCounter;
87
+				return $this->urlGenerator->imagePath('core', 'logo/logo.png').'?v='.$cacheBusterCounter;
88 88
 			case 'background':
89
-				return $this->urlGenerator->imagePath('core', 'background.png') . '?v=' . $cacheBusterCounter;
89
+				return $this->urlGenerator->imagePath('core', 'background.png').'?v='.$cacheBusterCounter;
90 90
 		}
91 91
 		return '';
92 92
 	}
@@ -103,7 +103,7 @@  discard block
 block discarded – undo
103 103
 	 * @throws NotPermittedException
104 104
 	 */
105 105
 	public function getImage(string $key, bool $useSvg = true): ISimpleFile {
106
-		$logo = $this->config->getAppValue('theming', $key . 'Mime', '');
106
+		$logo = $this->config->getAppValue('theming', $key.'Mime', '');
107 107
 		$folder = $this->getRootFolder()->getFolder('images');
108 108
 
109 109
 		if ($logo === '' || !$folder->fileExists($key)) {
@@ -111,20 +111,20 @@  discard block
 block discarded – undo
111 111
 		}
112 112
 
113 113
 		if (!$useSvg && $this->shouldReplaceIcons()) {
114
-			if (!$folder->fileExists($key . '.png')) {
114
+			if (!$folder->fileExists($key.'.png')) {
115 115
 				try {
116 116
 					$finalIconFile = new \Imagick();
117 117
 					$finalIconFile->setBackgroundColor('none');
118 118
 					$finalIconFile->readImageBlob($folder->getFile($key)->getContent());
119 119
 					$finalIconFile->setImageFormat('png32');
120
-					$pngFile = $folder->newFile($key . '.png');
120
+					$pngFile = $folder->newFile($key.'.png');
121 121
 					$pngFile->putContent($finalIconFile->getImageBlob());
122 122
 					return $pngFile;
123 123
 				} catch (\ImagickException $e) {
124
-					$this->logger->info('The image was requested to be no SVG file, but converting it to PNG failed: ' . $e->getMessage());
124
+					$this->logger->info('The image was requested to be no SVG file, but converting it to PNG failed: '.$e->getMessage());
125 125
 				}
126 126
 			} else {
127
-				return $folder->getFile($key . '.png');
127
+				return $folder->getFile($key.'.png');
128 128
 			}
129 129
 		}
130 130
 
@@ -132,7 +132,7 @@  discard block
 block discarded – undo
132 132
 	}
133 133
 
134 134
 	public function hasImage(string $key): bool {
135
-		$mimeSetting = $this->config->getAppValue('theming', $key . 'Mime', '');
135
+		$mimeSetting = $this->config->getAppValue('theming', $key.'Mime', '');
136 136
 		return $mimeSetting !== '';
137 137
 	}
138 138
 
@@ -195,7 +195,7 @@  discard block
 block discarded – undo
195 195
 		} catch (NotPermittedException $e) {
196 196
 		}
197 197
 		try {
198
-			$file = $this->getRootFolder()->getFolder('images')->getFile($key . '.png');
198
+			$file = $this->getRootFolder()->getFolder('images')->getFile($key.'.png');
199 199
 			$file->delete();
200 200
 		} catch (NotFoundException $e) {
201 201
 		} catch (NotPermittedException $e) {
@@ -228,8 +228,8 @@  discard block
 block discarded – undo
228 228
 			imagealphablending($newImage, true);
229 229
 
230 230
 			$tmpFile = $this->tempManager->getTemporaryFile();
231
-			$newWidth = (int)(imagesx($newImage) < 4096 ? imagesx($newImage) : 4096);
232
-			$newHeight = (int)(imagesy($newImage) / (imagesx($newImage) / $newWidth));
231
+			$newWidth = (int) (imagesx($newImage) < 4096 ? imagesx($newImage) : 4096);
232
+			$newHeight = (int) (imagesy($newImage) / (imagesx($newImage) / $newWidth));
233 233
 			$outputImage = imagescale($newImage, $newWidth, $newHeight);
234 234
 
235 235
 			imageinterlace($outputImage, 1);
@@ -290,9 +290,9 @@  discard block
 block discarded – undo
290 290
 	 * @return bool
291 291
 	 */
292 292
 	public function shouldReplaceIcons() {
293
-		$cache = $this->cacheFactory->createDistributed('theming-' . $this->urlGenerator->getBaseUrl());
293
+		$cache = $this->cacheFactory->createDistributed('theming-'.$this->urlGenerator->getBaseUrl());
294 294
 		if ($value = $cache->get('shouldReplaceIcons')) {
295
-			return (bool)$value;
295
+			return (bool) $value;
296 296
 		}
297 297
 		$value = false;
298 298
 		if (extension_loaded('imagick')) {
Please login to merge, or discard this patch.