Completed
Push — master ( 743512...045dc7 )
by Robin
41:33 queued 12:31
created
apps/theming/lib/ThemingDefaults.php 2 patches
Indentation   +512 added lines, -512 removed lines patch added patch discarded remove patch
@@ -22,516 +22,516 @@
 block discarded – undo
22 22
 
23 23
 class ThemingDefaults extends \OC_Defaults {
24 24
 
25
-	private string $name;
26
-	private string $title;
27
-	private string $entity;
28
-	private string $productName;
29
-	private string $url;
30
-	private string $backgroundColor;
31
-	private string $primaryColor;
32
-	private string $docBaseUrl;
33
-
34
-	private string $iTunesAppId;
35
-	private string $iOSClientUrl;
36
-	private string $AndroidClientUrl;
37
-	private string $FDroidClientUrl;
38
-
39
-	/**
40
-	 * ThemingDefaults constructor.
41
-	 */
42
-	public function __construct(
43
-		private IConfig $config,
44
-		private IAppConfig $appConfig,
45
-		private IL10N $l,
46
-		private IUserSession $userSession,
47
-		private IURLGenerator $urlGenerator,
48
-		private ICacheFactory $cacheFactory,
49
-		private Util $util,
50
-		private ImageManager $imageManager,
51
-		private IAppManager $appManager,
52
-		private INavigationManager $navigationManager,
53
-		private BackgroundService $backgroundService,
54
-	) {
55
-		parent::__construct();
56
-
57
-		$this->name = parent::getName();
58
-		$this->title = parent::getTitle();
59
-		$this->entity = parent::getEntity();
60
-		$this->productName = parent::getProductName();
61
-		$this->url = parent::getBaseUrl();
62
-		$this->primaryColor = parent::getColorPrimary();
63
-		$this->backgroundColor = parent::getColorBackground();
64
-		$this->iTunesAppId = parent::getiTunesAppId();
65
-		$this->iOSClientUrl = parent::getiOSClientUrl();
66
-		$this->AndroidClientUrl = parent::getAndroidClientUrl();
67
-		$this->FDroidClientUrl = parent::getFDroidClientUrl();
68
-		$this->docBaseUrl = parent::getDocBaseUrl();
69
-	}
70
-
71
-	public function getName() {
72
-		return strip_tags($this->appConfig->getAppValueString(ConfigLexicon::INSTANCE_NAME, $this->name));
73
-	}
74
-
75
-	public function getHTMLName() {
76
-		return $this->appConfig->getAppValueString(ConfigLexicon::INSTANCE_NAME, $this->name);
77
-	}
78
-
79
-	public function getTitle() {
80
-		return strip_tags($this->appConfig->getAppValueString(ConfigLexicon::INSTANCE_NAME, $this->title));
81
-	}
82
-
83
-	public function getEntity() {
84
-		return strip_tags($this->appConfig->getAppValueString(ConfigLexicon::INSTANCE_NAME, $this->entity));
85
-	}
86
-
87
-	public function getProductName() {
88
-		return strip_tags($this->appConfig->getAppValueString(ConfigLexicon::PRODUCT_NAME, $this->productName));
89
-	}
90
-
91
-	public function getBaseUrl() {
92
-		return $this->appConfig->getAppValueString(ConfigLexicon::BASE_URL, $this->url);
93
-	}
94
-
95
-	/**
96
-	 * We pass a string and sanitizeHTML will return a string too in that case
97
-	 * @psalm-suppress InvalidReturnStatement
98
-	 * @psalm-suppress InvalidReturnType
99
-	 */
100
-	public function getSlogan(?string $lang = null): string {
101
-		return \OCP\Util::sanitizeHTML($this->appConfig->getAppValueString(ConfigLexicon::INSTANCE_SLOGAN, parent::getSlogan($lang)));
102
-	}
103
-
104
-	public function getImprintUrl(): string {
105
-		return $this->appConfig->getAppValueString(ConfigLexicon::INSTANCE_IMPRINT_URL, '');
106
-	}
107
-
108
-	public function getPrivacyUrl(): string {
109
-		return $this->appConfig->getAppValueString(ConfigLexicon::INSTANCE_PRIVACY_URL, '');
110
-	}
111
-
112
-	public function getDocBaseUrl(): string {
113
-		return $this->appConfig->getAppValueString(ConfigLexicon::DOC_BASE_URL, $this->docBaseUrl);
114
-	}
115
-
116
-	public function getShortFooter() {
117
-		$slogan = $this->getSlogan();
118
-		$baseUrl = $this->getBaseUrl();
119
-		$entity = $this->getEntity();
120
-		$footer = '';
121
-
122
-		if ($entity !== '') {
123
-			if ($baseUrl !== '') {
124
-				$footer = '<a href="' . $baseUrl . '" target="_blank"'
125
-					. ' rel="noreferrer noopener" class="entity-name">' . $entity . '</a>';
126
-			} else {
127
-				$footer = '<span class="entity-name">' . $entity . '</span>';
128
-			}
129
-		}
130
-		$footer .= ($slogan !== '' ? ' – ' . $slogan : '');
131
-
132
-		$links = [
133
-			[
134
-				'text' => $this->l->t('Legal notice'),
135
-				'url' => $this->getImprintUrl()
136
-			],
137
-			[
138
-				'text' => $this->l->t('Privacy policy'),
139
-				'url' => $this->getPrivacyUrl()
140
-			],
141
-		];
142
-
143
-		$navigation = $this->navigationManager->getAll(INavigationManager::TYPE_GUEST);
144
-		$guestNavigation = array_map(function ($nav) {
145
-			return [
146
-				'text' => $nav['name'],
147
-				'url' => $nav['href']
148
-			];
149
-		}, $navigation);
150
-		$links = array_merge($links, $guestNavigation);
151
-
152
-		$legalLinks = '';
153
-		$divider = '';
154
-		foreach ($links as $link) {
155
-			if ($link['url'] !== ''
156
-				&& filter_var($link['url'], FILTER_VALIDATE_URL)
157
-			) {
158
-				$legalLinks .= $divider . '<a href="' . $link['url'] . '" class="legal" target="_blank"'
159
-					. ' rel="noreferrer noopener">' . $link['text'] . '</a>';
160
-				$divider = ' · ';
161
-			}
162
-		}
163
-		if ($legalLinks !== '') {
164
-			$footer .= '<br/><span class="footer__legal-links">' . $legalLinks . '</span>';
165
-		}
166
-
167
-		return $footer;
168
-	}
169
-
170
-	/**
171
-	 * Color that is used for highlighting elements like important buttons
172
-	 * If user theming is enabled then the user defined value is returned
173
-	 */
174
-	public function getColorPrimary(): string {
175
-		$user = $this->userSession->getUser();
176
-
177
-		// admin-defined primary color
178
-		$defaultColor = $this->getDefaultColorPrimary();
179
-
180
-		if ($this->isUserThemingDisabled()) {
181
-			return $defaultColor;
182
-		}
183
-
184
-		// user-defined primary color
185
-		if (!empty($user)) {
186
-			$userPrimaryColor = $this->config->getUserValue($user->getUID(), Application::APP_ID, 'primary_color', '');
187
-			if (preg_match('/^\#([0-9a-f]{3}|[0-9a-f]{6})$/i', $userPrimaryColor)) {
188
-				return $userPrimaryColor;
189
-			}
190
-		}
191
-
192
-		// Finally, return the system global primary color
193
-		return $defaultColor;
194
-	}
195
-
196
-	/**
197
-	 * Color that is used for the page background (e.g. the header)
198
-	 * If user theming is enabled then the user defined value is returned
199
-	 */
200
-	public function getColorBackground(): string {
201
-		$user = $this->userSession->getUser();
202
-
203
-		// admin-defined background color
204
-		$defaultColor = $this->getDefaultColorBackground();
205
-
206
-		if ($this->isUserThemingDisabled()) {
207
-			return $defaultColor;
208
-		}
209
-
210
-		// user-defined background color
211
-		if (!empty($user)) {
212
-			$userBackgroundColor = $this->config->getUserValue($user->getUID(), Application::APP_ID, 'background_color', '');
213
-			if (preg_match('/^\#([0-9a-f]{3}|[0-9a-f]{6})$/i', $userBackgroundColor)) {
214
-				return $userBackgroundColor;
215
-			}
216
-		}
217
-
218
-		// Finally, return the system global background color
219
-		return $defaultColor;
220
-	}
221
-
222
-	/**
223
-	 * Return the default primary color - only taking admin setting into account
224
-	 */
225
-	public function getDefaultColorPrimary(): string {
226
-		// try admin color
227
-		$defaultColor = $this->appConfig->getAppValueString('primary_color', '');
228
-		if (preg_match('/^\#([0-9a-f]{3}|[0-9a-f]{6})$/i', $defaultColor)) {
229
-			return $defaultColor;
230
-		}
231
-
232
-		// fall back to default primary color
233
-		return $this->primaryColor;
234
-	}
235
-
236
-	/**
237
-	 * Default background color only taking admin setting into account
238
-	 */
239
-	public function getDefaultColorBackground(): string {
240
-		$defaultColor = $this->appConfig->getAppValueString('background_color');
241
-		if (preg_match('/^\#([0-9a-f]{3}|[0-9a-f]{6})$/i', $defaultColor)) {
242
-			return $defaultColor;
243
-		}
244
-
245
-		return $this->backgroundColor;
246
-	}
247
-
248
-	/**
249
-	 * Themed logo url
250
-	 *
251
-	 * @param bool $useSvg Whether to point to the SVG image or a fallback
252
-	 * @return string
253
-	 */
254
-	public function getLogo($useSvg = true): string {
255
-		$logo = $this->appConfig->getAppValueString('logoMime', '');
256
-
257
-		// short cut to avoid setting up the filesystem just to check if the logo is there
258
-		//
259
-		// explanation: if an SVG is requested and the app config value for logoMime is set then the logo is there.
260
-		// otherwise we need to check it and maybe also generate a PNG from the SVG (that's done in getImage() which
261
-		// needs to be called then)
262
-		if ($useSvg === true && $logo !== '') {
263
-			$logoExists = true;
264
-		} else {
265
-			try {
266
-				$this->imageManager->getImage('logo', $useSvg);
267
-				$logoExists = true;
268
-			} catch (\Exception $e) {
269
-				$logoExists = false;
270
-			}
271
-		}
272
-
273
-		$cacheBusterCounter = (string)$this->appConfig->getAppValueInt(ConfigLexicon::CACHE_BUSTER);
274
-		if (!$logo || !$logoExists) {
275
-			if ($useSvg) {
276
-				$logo = $this->urlGenerator->imagePath('core', 'logo/logo.svg');
277
-			} else {
278
-				$logo = $this->urlGenerator->imagePath('core', 'logo/logo.png');
279
-			}
280
-			return $logo . '?v=' . $cacheBusterCounter;
281
-		}
282
-
283
-		return $this->urlGenerator->linkToRoute('theming.Theming.getImage', [ 'key' => 'logo', 'useSvg' => $useSvg, 'v' => $cacheBusterCounter ]);
284
-	}
285
-
286
-	/**
287
-	 * Themed background image url
288
-	 *
289
-	 * @param bool $darkVariant if the dark variant (if available) of the background should be used
290
-	 * @return string
291
-	 */
292
-	public function getBackground(bool $darkVariant = false): string {
293
-		return $this->imageManager->getImageUrl('background' . ($darkVariant ? 'Dark' : ''));
294
-	}
295
-
296
-	/**
297
-	 * @return string
298
-	 */
299
-	public function getiTunesAppId() {
300
-		return $this->appConfig->getAppValueString('iTunesAppId', $this->iTunesAppId);
301
-	}
302
-
303
-	/**
304
-	 * @return string
305
-	 */
306
-	public function getiOSClientUrl() {
307
-		return $this->appConfig->getAppValueString('iOSClientUrl', $this->iOSClientUrl);
308
-	}
309
-
310
-	/**
311
-	 * @return string
312
-	 */
313
-	public function getAndroidClientUrl() {
314
-		return $this->appConfig->getAppValueString('AndroidClientUrl', $this->AndroidClientUrl);
315
-	}
316
-
317
-	/**
318
-	 * @return string
319
-	 */
320
-	public function getFDroidClientUrl() {
321
-		return $this->appConfig->getAppValueString('FDroidClientUrl', $this->FDroidClientUrl);
322
-	}
323
-
324
-	/**
325
-	 * @return array scss variables to overwrite
326
-	 * @deprecated since Nextcloud 22 - https://github.com/nextcloud/server/issues/9940
327
-	 */
328
-	public function getScssVariables() {
329
-		$cacheBuster = $this->appConfig->getAppValueInt(ConfigLexicon::CACHE_BUSTER);
330
-		$cache = $this->cacheFactory->createDistributed('theming-' . (string)$cacheBuster . '-' . $this->urlGenerator->getBaseUrl());
331
-		if ($value = $cache->get('getScssVariables')) {
332
-			return $value;
333
-		}
334
-
335
-		$variables = [
336
-			'theming-cachebuster' => "'" . $cacheBuster . "'",
337
-			'theming-logo-mime' => "'" . $this->appConfig->getAppValueString('logoMime') . "'",
338
-			'theming-background-mime' => "'" . $this->appConfig->getAppValueString('backgroundMime') . "'",
339
-			'theming-logoheader-mime' => "'" . $this->appConfig->getAppValueString('logoheaderMime') . "'",
340
-			'theming-favicon-mime' => "'" . $this->appConfig->getAppValueString('faviconMime') . "'"
341
-		];
342
-
343
-		$variables['image-logo'] = "url('" . $this->imageManager->getImageUrl('logo') . "')";
344
-		$variables['image-logoheader'] = "url('" . $this->imageManager->getImageUrl('logoheader') . "')";
345
-		$variables['image-favicon'] = "url('" . $this->imageManager->getImageUrl('favicon') . "')";
346
-		$variables['image-login-background'] = "url('" . $this->imageManager->getImageUrl('background') . "')";
347
-		$variables['image-login-plain'] = 'false';
348
-
349
-		if ($this->appConfig->getAppValueString('primary_color', '') !== '') {
350
-			$variables['color-primary'] = $this->getColorPrimary();
351
-			$variables['color-primary-text'] = $this->getTextColorPrimary();
352
-			$variables['color-primary-element'] = $this->util->elementColor($this->getColorPrimary());
353
-		}
354
-
355
-		if ($this->appConfig->getAppValueString('backgroundMime', '') === 'backgroundColor') {
356
-			$variables['image-login-plain'] = 'true';
357
-		}
358
-
359
-		$variables['has-legal-links'] = 'false';
360
-		if ($this->getImprintUrl() !== '' || $this->getPrivacyUrl() !== '') {
361
-			$variables['has-legal-links'] = 'true';
362
-		}
363
-
364
-		$cache->set('getScssVariables', $variables);
365
-		return $variables;
366
-	}
367
-
368
-	/**
369
-	 * Check if the image should be replaced by the theming app
370
-	 * and return the new image location then
371
-	 *
372
-	 * @param string $app name of the app
373
-	 * @param string $image filename of the image
374
-	 * @return bool|string false if image should not replaced, otherwise the location of the image
375
-	 */
376
-	public function replaceImagePath($app, $image) {
377
-		if ($app === '' || $app === 'files_sharing') {
378
-			$app = 'core';
379
-		}
380
-
381
-		$route = false;
382
-		if ($image === 'favicon.ico' && ($this->imageManager->shouldReplaceIcons() || $this->getCustomFavicon() !== null)) {
383
-			$route = $this->urlGenerator->linkToRoute('theming.Icon.getFavicon', ['app' => $app]);
384
-		}
385
-		if (($image === 'favicon-touch.png' || $image === 'favicon-fb.png') && ($this->imageManager->shouldReplaceIcons() || $this->getCustomFavicon() !== null)) {
386
-			$route = $this->urlGenerator->linkToRoute('theming.Icon.getTouchIcon', ['app' => $app]);
387
-		}
388
-		if ($image === 'manifest.json') {
389
-			try {
390
-				$appPath = $this->appManager->getAppPath($app);
391
-				if (file_exists($appPath . '/img/manifest.json')) {
392
-					return false;
393
-				}
394
-			} catch (AppPathNotFoundException $e) {
395
-			}
396
-			$route = $this->urlGenerator->linkToRoute('theming.Theming.getManifest', ['app' => $app ]);
397
-		}
398
-		if (str_starts_with($image, 'filetypes/') && file_exists(\OC::$SERVERROOT . '/core/img/' . $image)) {
399
-			$route = $this->urlGenerator->linkToRoute('theming.Icon.getThemedIcon', ['app' => $app, 'image' => $image]);
400
-		}
401
-
402
-		if ($route) {
403
-			return $route . '?v=' . $this->util->getCacheBuster();
404
-		}
405
-
406
-		return false;
407
-	}
408
-
409
-	protected function getCustomFavicon(): ?ISimpleFile {
410
-		try {
411
-			return $this->imageManager->getImage('favicon');
412
-		} catch (NotFoundException $e) {
413
-			return null;
414
-		}
415
-	}
416
-
417
-	/**
418
-	 * Increases the cache buster key
419
-	 */
420
-	public function increaseCacheBuster(): void {
421
-		$cacheBusterKey = $this->appConfig->getAppValueInt(ConfigLexicon::CACHE_BUSTER);
422
-		$this->appConfig->setAppValueInt(ConfigLexicon::CACHE_BUSTER, $cacheBusterKey + 1);
423
-		$this->cacheFactory->createDistributed('theming-')->clear();
424
-		$this->cacheFactory->createDistributed('imagePath')->clear();
425
-	}
426
-
427
-	/**
428
-	 * Update setting in the database
429
-	 *
430
-	 * @param string $setting
431
-	 * @param string $value
432
-	 */
433
-	public function set($setting, $value): void {
434
-		switch ($setting) {
435
-			case ConfigLexicon::CACHE_BUSTER:
436
-				$this->appConfig->setAppValueInt(ConfigLexicon::CACHE_BUSTER, (int)$value);
437
-				break;
438
-			case ConfigLexicon::USER_THEMING_DISABLED:
439
-				$value = in_array($value, ['1', 'true', 'yes', 'on']);
440
-				$this->appConfig->setAppValueBool(ConfigLexicon::USER_THEMING_DISABLED, $value);
441
-				break;
442
-			default:
443
-				$this->appConfig->setAppValueString($setting, $value);
444
-				break;
445
-		}
446
-		$this->increaseCacheBuster();
447
-	}
448
-
449
-	/**
450
-	 * Revert all settings to the default value
451
-	 */
452
-	public function undoAll(): void {
453
-		// Remember the current cachebuster value, as we do not want to reset this value
454
-		// Otherwise this can lead to caching issues as the value might be known to a browser already
455
-		$cacheBusterKey = $this->appConfig->getAppValueInt(ConfigLexicon::CACHE_BUSTER);
456
-		$this->appConfig->deleteAppValues();
457
-		$this->appConfig->setAppValueInt(ConfigLexicon::CACHE_BUSTER, $cacheBusterKey);
458
-		$this->increaseCacheBuster();
459
-	}
460
-
461
-	/**
462
-	 * Revert admin settings to the default value
463
-	 *
464
-	 * @param string $setting setting which should be reverted
465
-	 * @return string default value
466
-	 */
467
-	public function undo($setting): string {
468
-		$this->appConfig->deleteAppValue($setting);
469
-		$this->increaseCacheBuster();
470
-
471
-		$returnValue = '';
472
-		switch ($setting) {
473
-			case 'name':
474
-				$returnValue = $this->getEntity();
475
-				break;
476
-			case 'url':
477
-				$returnValue = $this->getBaseUrl();
478
-				break;
479
-			case 'slogan':
480
-				$returnValue = $this->getSlogan();
481
-				break;
482
-			case 'primary_color':
483
-				$returnValue = BackgroundService::DEFAULT_COLOR;
484
-				break;
485
-			case 'background_color':
486
-				// If a background image is set we revert to the mean image color
487
-				if ($this->imageManager->hasImage('background')) {
488
-					$file = $this->imageManager->getImage('background');
489
-					$returnValue = $this->backgroundService->setGlobalBackground($file->read()) ?? '';
490
-				}
491
-				break;
492
-			case 'logo':
493
-			case 'logoheader':
494
-			case 'background':
495
-			case 'favicon':
496
-				$this->imageManager->delete($setting);
497
-				$this->appConfig->deleteAppValue($setting . 'Mime');
498
-				break;
499
-		}
500
-
501
-		return $returnValue;
502
-	}
503
-
504
-	/**
505
-	 * Color of text in the header menu
506
-	 *
507
-	 * @return string
508
-	 */
509
-	public function getTextColorBackground() {
510
-		return $this->util->invertTextColor($this->getColorBackground()) ? '#000000' : '#ffffff';
511
-	}
512
-
513
-	/**
514
-	 * Color of text on primary buttons and other elements
515
-	 *
516
-	 * @return string
517
-	 */
518
-	public function getTextColorPrimary() {
519
-		return $this->util->invertTextColor($this->getColorPrimary()) ? '#000000' : '#ffffff';
520
-	}
521
-
522
-	/**
523
-	 * Color of text in the header and primary buttons
524
-	 *
525
-	 * @return string
526
-	 */
527
-	public function getDefaultTextColorPrimary() {
528
-		return $this->util->invertTextColor($this->getDefaultColorPrimary()) ? '#000000' : '#ffffff';
529
-	}
530
-
531
-	/**
532
-	 * Has the admin disabled user customization
533
-	 */
534
-	public function isUserThemingDisabled(): bool {
535
-		return $this->appConfig->getAppValueBool(ConfigLexicon::USER_THEMING_DISABLED, false);
536
-	}
25
+    private string $name;
26
+    private string $title;
27
+    private string $entity;
28
+    private string $productName;
29
+    private string $url;
30
+    private string $backgroundColor;
31
+    private string $primaryColor;
32
+    private string $docBaseUrl;
33
+
34
+    private string $iTunesAppId;
35
+    private string $iOSClientUrl;
36
+    private string $AndroidClientUrl;
37
+    private string $FDroidClientUrl;
38
+
39
+    /**
40
+     * ThemingDefaults constructor.
41
+     */
42
+    public function __construct(
43
+        private IConfig $config,
44
+        private IAppConfig $appConfig,
45
+        private IL10N $l,
46
+        private IUserSession $userSession,
47
+        private IURLGenerator $urlGenerator,
48
+        private ICacheFactory $cacheFactory,
49
+        private Util $util,
50
+        private ImageManager $imageManager,
51
+        private IAppManager $appManager,
52
+        private INavigationManager $navigationManager,
53
+        private BackgroundService $backgroundService,
54
+    ) {
55
+        parent::__construct();
56
+
57
+        $this->name = parent::getName();
58
+        $this->title = parent::getTitle();
59
+        $this->entity = parent::getEntity();
60
+        $this->productName = parent::getProductName();
61
+        $this->url = parent::getBaseUrl();
62
+        $this->primaryColor = parent::getColorPrimary();
63
+        $this->backgroundColor = parent::getColorBackground();
64
+        $this->iTunesAppId = parent::getiTunesAppId();
65
+        $this->iOSClientUrl = parent::getiOSClientUrl();
66
+        $this->AndroidClientUrl = parent::getAndroidClientUrl();
67
+        $this->FDroidClientUrl = parent::getFDroidClientUrl();
68
+        $this->docBaseUrl = parent::getDocBaseUrl();
69
+    }
70
+
71
+    public function getName() {
72
+        return strip_tags($this->appConfig->getAppValueString(ConfigLexicon::INSTANCE_NAME, $this->name));
73
+    }
74
+
75
+    public function getHTMLName() {
76
+        return $this->appConfig->getAppValueString(ConfigLexicon::INSTANCE_NAME, $this->name);
77
+    }
78
+
79
+    public function getTitle() {
80
+        return strip_tags($this->appConfig->getAppValueString(ConfigLexicon::INSTANCE_NAME, $this->title));
81
+    }
82
+
83
+    public function getEntity() {
84
+        return strip_tags($this->appConfig->getAppValueString(ConfigLexicon::INSTANCE_NAME, $this->entity));
85
+    }
86
+
87
+    public function getProductName() {
88
+        return strip_tags($this->appConfig->getAppValueString(ConfigLexicon::PRODUCT_NAME, $this->productName));
89
+    }
90
+
91
+    public function getBaseUrl() {
92
+        return $this->appConfig->getAppValueString(ConfigLexicon::BASE_URL, $this->url);
93
+    }
94
+
95
+    /**
96
+     * We pass a string and sanitizeHTML will return a string too in that case
97
+     * @psalm-suppress InvalidReturnStatement
98
+     * @psalm-suppress InvalidReturnType
99
+     */
100
+    public function getSlogan(?string $lang = null): string {
101
+        return \OCP\Util::sanitizeHTML($this->appConfig->getAppValueString(ConfigLexicon::INSTANCE_SLOGAN, parent::getSlogan($lang)));
102
+    }
103
+
104
+    public function getImprintUrl(): string {
105
+        return $this->appConfig->getAppValueString(ConfigLexicon::INSTANCE_IMPRINT_URL, '');
106
+    }
107
+
108
+    public function getPrivacyUrl(): string {
109
+        return $this->appConfig->getAppValueString(ConfigLexicon::INSTANCE_PRIVACY_URL, '');
110
+    }
111
+
112
+    public function getDocBaseUrl(): string {
113
+        return $this->appConfig->getAppValueString(ConfigLexicon::DOC_BASE_URL, $this->docBaseUrl);
114
+    }
115
+
116
+    public function getShortFooter() {
117
+        $slogan = $this->getSlogan();
118
+        $baseUrl = $this->getBaseUrl();
119
+        $entity = $this->getEntity();
120
+        $footer = '';
121
+
122
+        if ($entity !== '') {
123
+            if ($baseUrl !== '') {
124
+                $footer = '<a href="' . $baseUrl . '" target="_blank"'
125
+                    . ' rel="noreferrer noopener" class="entity-name">' . $entity . '</a>';
126
+            } else {
127
+                $footer = '<span class="entity-name">' . $entity . '</span>';
128
+            }
129
+        }
130
+        $footer .= ($slogan !== '' ? ' – ' . $slogan : '');
131
+
132
+        $links = [
133
+            [
134
+                'text' => $this->l->t('Legal notice'),
135
+                'url' => $this->getImprintUrl()
136
+            ],
137
+            [
138
+                'text' => $this->l->t('Privacy policy'),
139
+                'url' => $this->getPrivacyUrl()
140
+            ],
141
+        ];
142
+
143
+        $navigation = $this->navigationManager->getAll(INavigationManager::TYPE_GUEST);
144
+        $guestNavigation = array_map(function ($nav) {
145
+            return [
146
+                'text' => $nav['name'],
147
+                'url' => $nav['href']
148
+            ];
149
+        }, $navigation);
150
+        $links = array_merge($links, $guestNavigation);
151
+
152
+        $legalLinks = '';
153
+        $divider = '';
154
+        foreach ($links as $link) {
155
+            if ($link['url'] !== ''
156
+                && filter_var($link['url'], FILTER_VALIDATE_URL)
157
+            ) {
158
+                $legalLinks .= $divider . '<a href="' . $link['url'] . '" class="legal" target="_blank"'
159
+                    . ' rel="noreferrer noopener">' . $link['text'] . '</a>';
160
+                $divider = ' · ';
161
+            }
162
+        }
163
+        if ($legalLinks !== '') {
164
+            $footer .= '<br/><span class="footer__legal-links">' . $legalLinks . '</span>';
165
+        }
166
+
167
+        return $footer;
168
+    }
169
+
170
+    /**
171
+     * Color that is used for highlighting elements like important buttons
172
+     * If user theming is enabled then the user defined value is returned
173
+     */
174
+    public function getColorPrimary(): string {
175
+        $user = $this->userSession->getUser();
176
+
177
+        // admin-defined primary color
178
+        $defaultColor = $this->getDefaultColorPrimary();
179
+
180
+        if ($this->isUserThemingDisabled()) {
181
+            return $defaultColor;
182
+        }
183
+
184
+        // user-defined primary color
185
+        if (!empty($user)) {
186
+            $userPrimaryColor = $this->config->getUserValue($user->getUID(), Application::APP_ID, 'primary_color', '');
187
+            if (preg_match('/^\#([0-9a-f]{3}|[0-9a-f]{6})$/i', $userPrimaryColor)) {
188
+                return $userPrimaryColor;
189
+            }
190
+        }
191
+
192
+        // Finally, return the system global primary color
193
+        return $defaultColor;
194
+    }
195
+
196
+    /**
197
+     * Color that is used for the page background (e.g. the header)
198
+     * If user theming is enabled then the user defined value is returned
199
+     */
200
+    public function getColorBackground(): string {
201
+        $user = $this->userSession->getUser();
202
+
203
+        // admin-defined background color
204
+        $defaultColor = $this->getDefaultColorBackground();
205
+
206
+        if ($this->isUserThemingDisabled()) {
207
+            return $defaultColor;
208
+        }
209
+
210
+        // user-defined background color
211
+        if (!empty($user)) {
212
+            $userBackgroundColor = $this->config->getUserValue($user->getUID(), Application::APP_ID, 'background_color', '');
213
+            if (preg_match('/^\#([0-9a-f]{3}|[0-9a-f]{6})$/i', $userBackgroundColor)) {
214
+                return $userBackgroundColor;
215
+            }
216
+        }
217
+
218
+        // Finally, return the system global background color
219
+        return $defaultColor;
220
+    }
221
+
222
+    /**
223
+     * Return the default primary color - only taking admin setting into account
224
+     */
225
+    public function getDefaultColorPrimary(): string {
226
+        // try admin color
227
+        $defaultColor = $this->appConfig->getAppValueString('primary_color', '');
228
+        if (preg_match('/^\#([0-9a-f]{3}|[0-9a-f]{6})$/i', $defaultColor)) {
229
+            return $defaultColor;
230
+        }
231
+
232
+        // fall back to default primary color
233
+        return $this->primaryColor;
234
+    }
235
+
236
+    /**
237
+     * Default background color only taking admin setting into account
238
+     */
239
+    public function getDefaultColorBackground(): string {
240
+        $defaultColor = $this->appConfig->getAppValueString('background_color');
241
+        if (preg_match('/^\#([0-9a-f]{3}|[0-9a-f]{6})$/i', $defaultColor)) {
242
+            return $defaultColor;
243
+        }
244
+
245
+        return $this->backgroundColor;
246
+    }
247
+
248
+    /**
249
+     * Themed logo url
250
+     *
251
+     * @param bool $useSvg Whether to point to the SVG image or a fallback
252
+     * @return string
253
+     */
254
+    public function getLogo($useSvg = true): string {
255
+        $logo = $this->appConfig->getAppValueString('logoMime', '');
256
+
257
+        // short cut to avoid setting up the filesystem just to check if the logo is there
258
+        //
259
+        // explanation: if an SVG is requested and the app config value for logoMime is set then the logo is there.
260
+        // otherwise we need to check it and maybe also generate a PNG from the SVG (that's done in getImage() which
261
+        // needs to be called then)
262
+        if ($useSvg === true && $logo !== '') {
263
+            $logoExists = true;
264
+        } else {
265
+            try {
266
+                $this->imageManager->getImage('logo', $useSvg);
267
+                $logoExists = true;
268
+            } catch (\Exception $e) {
269
+                $logoExists = false;
270
+            }
271
+        }
272
+
273
+        $cacheBusterCounter = (string)$this->appConfig->getAppValueInt(ConfigLexicon::CACHE_BUSTER);
274
+        if (!$logo || !$logoExists) {
275
+            if ($useSvg) {
276
+                $logo = $this->urlGenerator->imagePath('core', 'logo/logo.svg');
277
+            } else {
278
+                $logo = $this->urlGenerator->imagePath('core', 'logo/logo.png');
279
+            }
280
+            return $logo . '?v=' . $cacheBusterCounter;
281
+        }
282
+
283
+        return $this->urlGenerator->linkToRoute('theming.Theming.getImage', [ 'key' => 'logo', 'useSvg' => $useSvg, 'v' => $cacheBusterCounter ]);
284
+    }
285
+
286
+    /**
287
+     * Themed background image url
288
+     *
289
+     * @param bool $darkVariant if the dark variant (if available) of the background should be used
290
+     * @return string
291
+     */
292
+    public function getBackground(bool $darkVariant = false): string {
293
+        return $this->imageManager->getImageUrl('background' . ($darkVariant ? 'Dark' : ''));
294
+    }
295
+
296
+    /**
297
+     * @return string
298
+     */
299
+    public function getiTunesAppId() {
300
+        return $this->appConfig->getAppValueString('iTunesAppId', $this->iTunesAppId);
301
+    }
302
+
303
+    /**
304
+     * @return string
305
+     */
306
+    public function getiOSClientUrl() {
307
+        return $this->appConfig->getAppValueString('iOSClientUrl', $this->iOSClientUrl);
308
+    }
309
+
310
+    /**
311
+     * @return string
312
+     */
313
+    public function getAndroidClientUrl() {
314
+        return $this->appConfig->getAppValueString('AndroidClientUrl', $this->AndroidClientUrl);
315
+    }
316
+
317
+    /**
318
+     * @return string
319
+     */
320
+    public function getFDroidClientUrl() {
321
+        return $this->appConfig->getAppValueString('FDroidClientUrl', $this->FDroidClientUrl);
322
+    }
323
+
324
+    /**
325
+     * @return array scss variables to overwrite
326
+     * @deprecated since Nextcloud 22 - https://github.com/nextcloud/server/issues/9940
327
+     */
328
+    public function getScssVariables() {
329
+        $cacheBuster = $this->appConfig->getAppValueInt(ConfigLexicon::CACHE_BUSTER);
330
+        $cache = $this->cacheFactory->createDistributed('theming-' . (string)$cacheBuster . '-' . $this->urlGenerator->getBaseUrl());
331
+        if ($value = $cache->get('getScssVariables')) {
332
+            return $value;
333
+        }
334
+
335
+        $variables = [
336
+            'theming-cachebuster' => "'" . $cacheBuster . "'",
337
+            'theming-logo-mime' => "'" . $this->appConfig->getAppValueString('logoMime') . "'",
338
+            'theming-background-mime' => "'" . $this->appConfig->getAppValueString('backgroundMime') . "'",
339
+            'theming-logoheader-mime' => "'" . $this->appConfig->getAppValueString('logoheaderMime') . "'",
340
+            'theming-favicon-mime' => "'" . $this->appConfig->getAppValueString('faviconMime') . "'"
341
+        ];
342
+
343
+        $variables['image-logo'] = "url('" . $this->imageManager->getImageUrl('logo') . "')";
344
+        $variables['image-logoheader'] = "url('" . $this->imageManager->getImageUrl('logoheader') . "')";
345
+        $variables['image-favicon'] = "url('" . $this->imageManager->getImageUrl('favicon') . "')";
346
+        $variables['image-login-background'] = "url('" . $this->imageManager->getImageUrl('background') . "')";
347
+        $variables['image-login-plain'] = 'false';
348
+
349
+        if ($this->appConfig->getAppValueString('primary_color', '') !== '') {
350
+            $variables['color-primary'] = $this->getColorPrimary();
351
+            $variables['color-primary-text'] = $this->getTextColorPrimary();
352
+            $variables['color-primary-element'] = $this->util->elementColor($this->getColorPrimary());
353
+        }
354
+
355
+        if ($this->appConfig->getAppValueString('backgroundMime', '') === 'backgroundColor') {
356
+            $variables['image-login-plain'] = 'true';
357
+        }
358
+
359
+        $variables['has-legal-links'] = 'false';
360
+        if ($this->getImprintUrl() !== '' || $this->getPrivacyUrl() !== '') {
361
+            $variables['has-legal-links'] = 'true';
362
+        }
363
+
364
+        $cache->set('getScssVariables', $variables);
365
+        return $variables;
366
+    }
367
+
368
+    /**
369
+     * Check if the image should be replaced by the theming app
370
+     * and return the new image location then
371
+     *
372
+     * @param string $app name of the app
373
+     * @param string $image filename of the image
374
+     * @return bool|string false if image should not replaced, otherwise the location of the image
375
+     */
376
+    public function replaceImagePath($app, $image) {
377
+        if ($app === '' || $app === 'files_sharing') {
378
+            $app = 'core';
379
+        }
380
+
381
+        $route = false;
382
+        if ($image === 'favicon.ico' && ($this->imageManager->shouldReplaceIcons() || $this->getCustomFavicon() !== null)) {
383
+            $route = $this->urlGenerator->linkToRoute('theming.Icon.getFavicon', ['app' => $app]);
384
+        }
385
+        if (($image === 'favicon-touch.png' || $image === 'favicon-fb.png') && ($this->imageManager->shouldReplaceIcons() || $this->getCustomFavicon() !== null)) {
386
+            $route = $this->urlGenerator->linkToRoute('theming.Icon.getTouchIcon', ['app' => $app]);
387
+        }
388
+        if ($image === 'manifest.json') {
389
+            try {
390
+                $appPath = $this->appManager->getAppPath($app);
391
+                if (file_exists($appPath . '/img/manifest.json')) {
392
+                    return false;
393
+                }
394
+            } catch (AppPathNotFoundException $e) {
395
+            }
396
+            $route = $this->urlGenerator->linkToRoute('theming.Theming.getManifest', ['app' => $app ]);
397
+        }
398
+        if (str_starts_with($image, 'filetypes/') && file_exists(\OC::$SERVERROOT . '/core/img/' . $image)) {
399
+            $route = $this->urlGenerator->linkToRoute('theming.Icon.getThemedIcon', ['app' => $app, 'image' => $image]);
400
+        }
401
+
402
+        if ($route) {
403
+            return $route . '?v=' . $this->util->getCacheBuster();
404
+        }
405
+
406
+        return false;
407
+    }
408
+
409
+    protected function getCustomFavicon(): ?ISimpleFile {
410
+        try {
411
+            return $this->imageManager->getImage('favicon');
412
+        } catch (NotFoundException $e) {
413
+            return null;
414
+        }
415
+    }
416
+
417
+    /**
418
+     * Increases the cache buster key
419
+     */
420
+    public function increaseCacheBuster(): void {
421
+        $cacheBusterKey = $this->appConfig->getAppValueInt(ConfigLexicon::CACHE_BUSTER);
422
+        $this->appConfig->setAppValueInt(ConfigLexicon::CACHE_BUSTER, $cacheBusterKey + 1);
423
+        $this->cacheFactory->createDistributed('theming-')->clear();
424
+        $this->cacheFactory->createDistributed('imagePath')->clear();
425
+    }
426
+
427
+    /**
428
+     * Update setting in the database
429
+     *
430
+     * @param string $setting
431
+     * @param string $value
432
+     */
433
+    public function set($setting, $value): void {
434
+        switch ($setting) {
435
+            case ConfigLexicon::CACHE_BUSTER:
436
+                $this->appConfig->setAppValueInt(ConfigLexicon::CACHE_BUSTER, (int)$value);
437
+                break;
438
+            case ConfigLexicon::USER_THEMING_DISABLED:
439
+                $value = in_array($value, ['1', 'true', 'yes', 'on']);
440
+                $this->appConfig->setAppValueBool(ConfigLexicon::USER_THEMING_DISABLED, $value);
441
+                break;
442
+            default:
443
+                $this->appConfig->setAppValueString($setting, $value);
444
+                break;
445
+        }
446
+        $this->increaseCacheBuster();
447
+    }
448
+
449
+    /**
450
+     * Revert all settings to the default value
451
+     */
452
+    public function undoAll(): void {
453
+        // Remember the current cachebuster value, as we do not want to reset this value
454
+        // Otherwise this can lead to caching issues as the value might be known to a browser already
455
+        $cacheBusterKey = $this->appConfig->getAppValueInt(ConfigLexicon::CACHE_BUSTER);
456
+        $this->appConfig->deleteAppValues();
457
+        $this->appConfig->setAppValueInt(ConfigLexicon::CACHE_BUSTER, $cacheBusterKey);
458
+        $this->increaseCacheBuster();
459
+    }
460
+
461
+    /**
462
+     * Revert admin settings to the default value
463
+     *
464
+     * @param string $setting setting which should be reverted
465
+     * @return string default value
466
+     */
467
+    public function undo($setting): string {
468
+        $this->appConfig->deleteAppValue($setting);
469
+        $this->increaseCacheBuster();
470
+
471
+        $returnValue = '';
472
+        switch ($setting) {
473
+            case 'name':
474
+                $returnValue = $this->getEntity();
475
+                break;
476
+            case 'url':
477
+                $returnValue = $this->getBaseUrl();
478
+                break;
479
+            case 'slogan':
480
+                $returnValue = $this->getSlogan();
481
+                break;
482
+            case 'primary_color':
483
+                $returnValue = BackgroundService::DEFAULT_COLOR;
484
+                break;
485
+            case 'background_color':
486
+                // If a background image is set we revert to the mean image color
487
+                if ($this->imageManager->hasImage('background')) {
488
+                    $file = $this->imageManager->getImage('background');
489
+                    $returnValue = $this->backgroundService->setGlobalBackground($file->read()) ?? '';
490
+                }
491
+                break;
492
+            case 'logo':
493
+            case 'logoheader':
494
+            case 'background':
495
+            case 'favicon':
496
+                $this->imageManager->delete($setting);
497
+                $this->appConfig->deleteAppValue($setting . 'Mime');
498
+                break;
499
+        }
500
+
501
+        return $returnValue;
502
+    }
503
+
504
+    /**
505
+     * Color of text in the header menu
506
+     *
507
+     * @return string
508
+     */
509
+    public function getTextColorBackground() {
510
+        return $this->util->invertTextColor($this->getColorBackground()) ? '#000000' : '#ffffff';
511
+    }
512
+
513
+    /**
514
+     * Color of text on primary buttons and other elements
515
+     *
516
+     * @return string
517
+     */
518
+    public function getTextColorPrimary() {
519
+        return $this->util->invertTextColor($this->getColorPrimary()) ? '#000000' : '#ffffff';
520
+    }
521
+
522
+    /**
523
+     * Color of text in the header and primary buttons
524
+     *
525
+     * @return string
526
+     */
527
+    public function getDefaultTextColorPrimary() {
528
+        return $this->util->invertTextColor($this->getDefaultColorPrimary()) ? '#000000' : '#ffffff';
529
+    }
530
+
531
+    /**
532
+     * Has the admin disabled user customization
533
+     */
534
+    public function isUserThemingDisabled(): bool {
535
+        return $this->appConfig->getAppValueBool(ConfigLexicon::USER_THEMING_DISABLED, false);
536
+    }
537 537
 }
Please login to merge, or discard this patch.
Spacing   +28 added lines, -28 removed lines patch added patch discarded remove patch
@@ -121,13 +121,13 @@  discard block
 block discarded – undo
121 121
 
122 122
 		if ($entity !== '') {
123 123
 			if ($baseUrl !== '') {
124
-				$footer = '<a href="' . $baseUrl . '" target="_blank"'
125
-					. ' rel="noreferrer noopener" class="entity-name">' . $entity . '</a>';
124
+				$footer = '<a href="'.$baseUrl.'" target="_blank"'
125
+					. ' rel="noreferrer noopener" class="entity-name">'.$entity.'</a>';
126 126
 			} else {
127
-				$footer = '<span class="entity-name">' . $entity . '</span>';
127
+				$footer = '<span class="entity-name">'.$entity.'</span>';
128 128
 			}
129 129
 		}
130
-		$footer .= ($slogan !== '' ? ' – ' . $slogan : '');
130
+		$footer .= ($slogan !== '' ? ' – '.$slogan : '');
131 131
 
132 132
 		$links = [
133 133
 			[
@@ -141,7 +141,7 @@  discard block
 block discarded – undo
141 141
 		];
142 142
 
143 143
 		$navigation = $this->navigationManager->getAll(INavigationManager::TYPE_GUEST);
144
-		$guestNavigation = array_map(function ($nav) {
144
+		$guestNavigation = array_map(function($nav) {
145 145
 			return [
146 146
 				'text' => $nav['name'],
147 147
 				'url' => $nav['href']
@@ -155,13 +155,13 @@  discard block
 block discarded – undo
155 155
 			if ($link['url'] !== ''
156 156
 				&& filter_var($link['url'], FILTER_VALIDATE_URL)
157 157
 			) {
158
-				$legalLinks .= $divider . '<a href="' . $link['url'] . '" class="legal" target="_blank"'
159
-					. ' rel="noreferrer noopener">' . $link['text'] . '</a>';
158
+				$legalLinks .= $divider.'<a href="'.$link['url'].'" class="legal" target="_blank"'
159
+					. ' rel="noreferrer noopener">'.$link['text'].'</a>';
160 160
 				$divider = ' · ';
161 161
 			}
162 162
 		}
163 163
 		if ($legalLinks !== '') {
164
-			$footer .= '<br/><span class="footer__legal-links">' . $legalLinks . '</span>';
164
+			$footer .= '<br/><span class="footer__legal-links">'.$legalLinks.'</span>';
165 165
 		}
166 166
 
167 167
 		return $footer;
@@ -270,17 +270,17 @@  discard block
 block discarded – undo
270 270
 			}
271 271
 		}
272 272
 
273
-		$cacheBusterCounter = (string)$this->appConfig->getAppValueInt(ConfigLexicon::CACHE_BUSTER);
273
+		$cacheBusterCounter = (string) $this->appConfig->getAppValueInt(ConfigLexicon::CACHE_BUSTER);
274 274
 		if (!$logo || !$logoExists) {
275 275
 			if ($useSvg) {
276 276
 				$logo = $this->urlGenerator->imagePath('core', 'logo/logo.svg');
277 277
 			} else {
278 278
 				$logo = $this->urlGenerator->imagePath('core', 'logo/logo.png');
279 279
 			}
280
-			return $logo . '?v=' . $cacheBusterCounter;
280
+			return $logo.'?v='.$cacheBusterCounter;
281 281
 		}
282 282
 
283
-		return $this->urlGenerator->linkToRoute('theming.Theming.getImage', [ 'key' => 'logo', 'useSvg' => $useSvg, 'v' => $cacheBusterCounter ]);
283
+		return $this->urlGenerator->linkToRoute('theming.Theming.getImage', ['key' => 'logo', 'useSvg' => $useSvg, 'v' => $cacheBusterCounter]);
284 284
 	}
285 285
 
286 286
 	/**
@@ -290,7 +290,7 @@  discard block
 block discarded – undo
290 290
 	 * @return string
291 291
 	 */
292 292
 	public function getBackground(bool $darkVariant = false): string {
293
-		return $this->imageManager->getImageUrl('background' . ($darkVariant ? 'Dark' : ''));
293
+		return $this->imageManager->getImageUrl('background'.($darkVariant ? 'Dark' : ''));
294 294
 	}
295 295
 
296 296
 	/**
@@ -327,23 +327,23 @@  discard block
 block discarded – undo
327 327
 	 */
328 328
 	public function getScssVariables() {
329 329
 		$cacheBuster = $this->appConfig->getAppValueInt(ConfigLexicon::CACHE_BUSTER);
330
-		$cache = $this->cacheFactory->createDistributed('theming-' . (string)$cacheBuster . '-' . $this->urlGenerator->getBaseUrl());
330
+		$cache = $this->cacheFactory->createDistributed('theming-'.(string) $cacheBuster.'-'.$this->urlGenerator->getBaseUrl());
331 331
 		if ($value = $cache->get('getScssVariables')) {
332 332
 			return $value;
333 333
 		}
334 334
 
335 335
 		$variables = [
336
-			'theming-cachebuster' => "'" . $cacheBuster . "'",
337
-			'theming-logo-mime' => "'" . $this->appConfig->getAppValueString('logoMime') . "'",
338
-			'theming-background-mime' => "'" . $this->appConfig->getAppValueString('backgroundMime') . "'",
339
-			'theming-logoheader-mime' => "'" . $this->appConfig->getAppValueString('logoheaderMime') . "'",
340
-			'theming-favicon-mime' => "'" . $this->appConfig->getAppValueString('faviconMime') . "'"
336
+			'theming-cachebuster' => "'".$cacheBuster."'",
337
+			'theming-logo-mime' => "'".$this->appConfig->getAppValueString('logoMime')."'",
338
+			'theming-background-mime' => "'".$this->appConfig->getAppValueString('backgroundMime')."'",
339
+			'theming-logoheader-mime' => "'".$this->appConfig->getAppValueString('logoheaderMime')."'",
340
+			'theming-favicon-mime' => "'".$this->appConfig->getAppValueString('faviconMime')."'"
341 341
 		];
342 342
 
343
-		$variables['image-logo'] = "url('" . $this->imageManager->getImageUrl('logo') . "')";
344
-		$variables['image-logoheader'] = "url('" . $this->imageManager->getImageUrl('logoheader') . "')";
345
-		$variables['image-favicon'] = "url('" . $this->imageManager->getImageUrl('favicon') . "')";
346
-		$variables['image-login-background'] = "url('" . $this->imageManager->getImageUrl('background') . "')";
343
+		$variables['image-logo'] = "url('".$this->imageManager->getImageUrl('logo')."')";
344
+		$variables['image-logoheader'] = "url('".$this->imageManager->getImageUrl('logoheader')."')";
345
+		$variables['image-favicon'] = "url('".$this->imageManager->getImageUrl('favicon')."')";
346
+		$variables['image-login-background'] = "url('".$this->imageManager->getImageUrl('background')."')";
347 347
 		$variables['image-login-plain'] = 'false';
348 348
 
349 349
 		if ($this->appConfig->getAppValueString('primary_color', '') !== '') {
@@ -388,19 +388,19 @@  discard block
 block discarded – undo
388 388
 		if ($image === 'manifest.json') {
389 389
 			try {
390 390
 				$appPath = $this->appManager->getAppPath($app);
391
-				if (file_exists($appPath . '/img/manifest.json')) {
391
+				if (file_exists($appPath.'/img/manifest.json')) {
392 392
 					return false;
393 393
 				}
394 394
 			} catch (AppPathNotFoundException $e) {
395 395
 			}
396
-			$route = $this->urlGenerator->linkToRoute('theming.Theming.getManifest', ['app' => $app ]);
396
+			$route = $this->urlGenerator->linkToRoute('theming.Theming.getManifest', ['app' => $app]);
397 397
 		}
398
-		if (str_starts_with($image, 'filetypes/') && file_exists(\OC::$SERVERROOT . '/core/img/' . $image)) {
398
+		if (str_starts_with($image, 'filetypes/') && file_exists(\OC::$SERVERROOT.'/core/img/'.$image)) {
399 399
 			$route = $this->urlGenerator->linkToRoute('theming.Icon.getThemedIcon', ['app' => $app, 'image' => $image]);
400 400
 		}
401 401
 
402 402
 		if ($route) {
403
-			return $route . '?v=' . $this->util->getCacheBuster();
403
+			return $route.'?v='.$this->util->getCacheBuster();
404 404
 		}
405 405
 
406 406
 		return false;
@@ -433,7 +433,7 @@  discard block
 block discarded – undo
433 433
 	public function set($setting, $value): void {
434 434
 		switch ($setting) {
435 435
 			case ConfigLexicon::CACHE_BUSTER:
436
-				$this->appConfig->setAppValueInt(ConfigLexicon::CACHE_BUSTER, (int)$value);
436
+				$this->appConfig->setAppValueInt(ConfigLexicon::CACHE_BUSTER, (int) $value);
437 437
 				break;
438 438
 			case ConfigLexicon::USER_THEMING_DISABLED:
439 439
 				$value = in_array($value, ['1', 'true', 'yes', 'on']);
@@ -494,7 +494,7 @@  discard block
 block discarded – undo
494 494
 			case 'background':
495 495
 			case 'favicon':
496 496
 				$this->imageManager->delete($setting);
497
-				$this->appConfig->deleteAppValue($setting . 'Mime');
497
+				$this->appConfig->deleteAppValue($setting.'Mime');
498 498
 				break;
499 499
 		}
500 500
 
Please login to merge, or discard this patch.
apps/theming/tests/ThemingDefaultsTest.php 2 patches
Indentation   +830 added lines, -830 removed lines patch added patch discarded remove patch
@@ -27,834 +27,834 @@
 block discarded – undo
27 27
 use Test\TestCase;
28 28
 
29 29
 class ThemingDefaultsTest extends TestCase {
30
-	private IAppConfig&MockObject $appConfig;
31
-	private IConfig&MockObject $config;
32
-	private IL10N&MockObject $l10n;
33
-	private IUserSession&MockObject $userSession;
34
-	private IURLGenerator&MockObject $urlGenerator;
35
-	private ICacheFactory&MockObject $cacheFactory;
36
-	private Util&MockObject $util;
37
-	private ICache&MockObject $cache;
38
-	private IAppManager&MockObject $appManager;
39
-	private ImageManager&MockObject $imageManager;
40
-	private INavigationManager&MockObject $navigationManager;
41
-	private BackgroundService&MockObject $backgroundService;
42
-
43
-	private \OC_Defaults $defaults;
44
-	private ThemingDefaults $template;
45
-
46
-	protected function setUp(): void {
47
-		parent::setUp();
48
-		$this->appConfig = $this->createMock(IAppConfig::class);
49
-		$this->config = $this->createMock(IConfig::class);
50
-		$this->l10n = $this->createMock(IL10N::class);
51
-		$this->userSession = $this->createMock(IUserSession::class);
52
-		$this->urlGenerator = $this->createMock(IURLGenerator::class);
53
-		$this->cacheFactory = $this->createMock(ICacheFactory::class);
54
-		$this->cache = $this->createMock(ICache::class);
55
-		$this->util = $this->createMock(Util::class);
56
-		$this->imageManager = $this->createMock(ImageManager::class);
57
-		$this->appManager = $this->createMock(IAppManager::class);
58
-		$this->navigationManager = $this->createMock(INavigationManager::class);
59
-		$this->backgroundService = $this->createMock(BackgroundService::class);
60
-		$this->defaults = new \OC_Defaults();
61
-		$this->urlGenerator
62
-			->expects($this->any())
63
-			->method('getBaseUrl')
64
-			->willReturn('');
65
-		$this->template = new ThemingDefaults(
66
-			$this->config,
67
-			$this->appConfig,
68
-			$this->l10n,
69
-			$this->userSession,
70
-			$this->urlGenerator,
71
-			$this->cacheFactory,
72
-			$this->util,
73
-			$this->imageManager,
74
-			$this->appManager,
75
-			$this->navigationManager,
76
-			$this->backgroundService,
77
-		);
78
-	}
79
-
80
-	public function testGetNameWithDefault(): void {
81
-		$this->appConfig
82
-			->expects($this->once())
83
-			->method('getAppValueString')
84
-			->with('name', 'Nextcloud')
85
-			->willReturn('Nextcloud');
86
-
87
-		$this->assertEquals('Nextcloud', $this->template->getName());
88
-	}
89
-
90
-	public function testGetNameWithCustom(): void {
91
-		$this->appConfig
92
-			->expects($this->once())
93
-			->method('getAppValueString')
94
-			->with('name', 'Nextcloud')
95
-			->willReturn('MyCustomCloud');
96
-
97
-		$this->assertEquals('MyCustomCloud', $this->template->getName());
98
-	}
99
-
100
-	public function testGetHTMLNameWithDefault(): void {
101
-		$this->appConfig
102
-			->expects($this->once())
103
-			->method('getAppValueString')
104
-			->with('name', 'Nextcloud')
105
-			->willReturn('Nextcloud');
106
-
107
-		$this->assertEquals('Nextcloud', $this->template->getHTMLName());
108
-	}
109
-
110
-	public function testGetHTMLNameWithCustom(): void {
111
-		$this->appConfig
112
-			->expects($this->once())
113
-			->method('getAppValueString')
114
-			->with('name', 'Nextcloud')
115
-			->willReturn('MyCustomCloud');
116
-
117
-		$this->assertEquals('MyCustomCloud', $this->template->getHTMLName());
118
-	}
119
-
120
-	public function testGetTitleWithDefault(): void {
121
-		$this->appConfig
122
-			->expects($this->once())
123
-			->method('getAppValueString')
124
-			->with('name', 'Nextcloud')
125
-			->willReturn('Nextcloud');
126
-
127
-		$this->assertEquals('Nextcloud', $this->template->getTitle());
128
-	}
129
-
130
-	public function testGetTitleWithCustom(): void {
131
-		$this->appConfig
132
-			->expects($this->once())
133
-			->method('getAppValueString')
134
-			->with('name', 'Nextcloud')
135
-			->willReturn('MyCustomCloud');
136
-
137
-		$this->assertEquals('MyCustomCloud', $this->template->getTitle());
138
-	}
139
-
140
-
141
-	public function testGetEntityWithDefault(): void {
142
-		$this->appConfig
143
-			->expects($this->once())
144
-			->method('getAppValueString')
145
-			->with('name', 'Nextcloud')
146
-			->willReturn('Nextcloud');
147
-
148
-		$this->assertEquals('Nextcloud', $this->template->getEntity());
149
-	}
150
-
151
-	public function testGetEntityWithCustom(): void {
152
-		$this->appConfig
153
-			->expects($this->once())
154
-			->method('getAppValueString')
155
-			->with('name', 'Nextcloud')
156
-			->willReturn('MyCustomCloud');
157
-
158
-		$this->assertEquals('MyCustomCloud', $this->template->getEntity());
159
-	}
160
-
161
-	public function testGetBaseUrlWithDefault(): void {
162
-		$this->appConfig
163
-			->expects($this->once())
164
-			->method('getAppValueString')
165
-			->with('url', $this->defaults->getBaseUrl())
166
-			->willReturn($this->defaults->getBaseUrl());
167
-
168
-		$this->assertEquals($this->defaults->getBaseUrl(), $this->template->getBaseUrl());
169
-	}
170
-
171
-	public function testGetBaseUrlWithCustom(): void {
172
-		$this->appConfig
173
-			->expects($this->once())
174
-			->method('getAppValueString')
175
-			->with('url', $this->defaults->getBaseUrl())
176
-			->willReturn('https://example.com/');
177
-
178
-		$this->assertEquals('https://example.com/', $this->template->getBaseUrl());
179
-	}
180
-
181
-	public static function legalUrlProvider(): array {
182
-		return [
183
-			[''],
184
-			['https://example.com/legal.html'],
185
-		];
186
-	}
187
-
188
-	#[\PHPUnit\Framework\Attributes\DataProvider('legalUrlProvider')]
189
-	public function testGetImprintURL(string $imprintUrl): void {
190
-		$this->appConfig
191
-			->expects($this->once())
192
-			->method('getAppValueString')
193
-			->with('imprintUrl', '')
194
-			->willReturn($imprintUrl);
195
-
196
-		$this->assertEquals($imprintUrl, $this->template->getImprintUrl());
197
-	}
198
-
199
-	#[\PHPUnit\Framework\Attributes\DataProvider('legalUrlProvider')]
200
-	public function testGetPrivacyURL(string $privacyUrl): void {
201
-		$this->appConfig
202
-			->expects($this->once())
203
-			->method('getAppValueString')
204
-			->with('privacyUrl', '')
205
-			->willReturn($privacyUrl);
206
-
207
-		$this->assertEquals($privacyUrl, $this->template->getPrivacyUrl());
208
-	}
209
-
210
-	public function testGetSloganWithDefault(): void {
211
-		$this->appConfig
212
-			->expects($this->once())
213
-			->method('getAppValueString')
214
-			->with('slogan', $this->defaults->getSlogan())
215
-			->willReturn($this->defaults->getSlogan());
216
-
217
-		$this->assertEquals($this->defaults->getSlogan(), $this->template->getSlogan());
218
-	}
219
-
220
-	public function testGetSloganWithCustom(): void {
221
-		$this->appConfig
222
-			->expects($this->once())
223
-			->method('getAppValueString')
224
-			->with('slogan', $this->defaults->getSlogan())
225
-			->willReturn('My custom Slogan');
226
-
227
-		$this->assertEquals('My custom Slogan', $this->template->getSlogan());
228
-	}
229
-
230
-	public function testGetShortFooter(): void {
231
-		$this->appConfig
232
-			->expects($this->exactly(5))
233
-			->method('getAppValueString')
234
-			->willReturnMap([
235
-				['url', $this->defaults->getBaseUrl(), 'url'],
236
-				['name', 'Nextcloud', 'Name'],
237
-				['slogan', $this->defaults->getSlogan(), 'Slogan'],
238
-				['imprintUrl', '', ''],
239
-				['privacyUrl', '', ''],
240
-			]);
241
-
242
-		$this->assertEquals('<a href="url" target="_blank" rel="noreferrer noopener" class="entity-name">Name</a> – Slogan', $this->template->getShortFooter());
243
-	}
244
-
245
-	public function testGetShortFooterEmptyUrl(): void {
246
-		$this->navigationManager->expects($this->once())->method('getAll')->with(INavigationManager::TYPE_GUEST)->willReturn([]);
247
-		$this->appConfig
248
-			->expects($this->exactly(5))
249
-			->method('getAppValueString')
250
-			->willReturnMap([
251
-				['url', $this->defaults->getBaseUrl(), ''],
252
-				['name', 'Nextcloud', 'Name'],
253
-				['slogan', $this->defaults->getSlogan(), 'Slogan'],
254
-				['imprintUrl', '', ''],
255
-				['privacyUrl', '', ''],
256
-			]);
257
-
258
-		$this->assertEquals('<span class="entity-name">Name</span> – Slogan', $this->template->getShortFooter());
259
-	}
260
-
261
-	public function testGetShortFooterEmptySlogan(): void {
262
-		$this->navigationManager->expects($this->once())->method('getAll')->with(INavigationManager::TYPE_GUEST)->willReturn([]);
263
-		$this->appConfig
264
-			->expects($this->exactly(5))
265
-			->method('getAppValueString')
266
-			->willReturnMap([
267
-				['url', $this->defaults->getBaseUrl(), 'url'],
268
-				['name', 'Nextcloud', 'Name'],
269
-				['slogan', $this->defaults->getSlogan(), ''],
270
-				['imprintUrl', '', ''],
271
-				['privacyUrl', '', ''],
272
-			]);
273
-
274
-		$this->assertEquals('<a href="url" target="_blank" rel="noreferrer noopener" class="entity-name">Name</a>', $this->template->getShortFooter());
275
-	}
276
-
277
-	public function testGetShortFooterImprint(): void {
278
-		$this->navigationManager->expects($this->once())->method('getAll')->with(INavigationManager::TYPE_GUEST)->willReturn([]);
279
-		$this->appConfig
280
-			->expects($this->exactly(5))
281
-			->method('getAppValueString')
282
-			->willReturnMap([
283
-				['url', $this->defaults->getBaseUrl(), 'url'],
284
-				['name', 'Nextcloud', 'Name'],
285
-				['slogan', $this->defaults->getSlogan(), 'Slogan'],
286
-				['imprintUrl', '', 'https://example.com/imprint'],
287
-				['privacyUrl', '', ''],
288
-			]);
289
-
290
-		$this->l10n
291
-			->expects($this->any())
292
-			->method('t')
293
-			->willReturnArgument(0);
294
-
295
-		$this->assertEquals('<a href="url" target="_blank" rel="noreferrer noopener" class="entity-name">Name</a> – Slogan<br/><span class="footer__legal-links"><a href="https://example.com/imprint" class="legal" target="_blank" rel="noreferrer noopener">Legal notice</a></span>', $this->template->getShortFooter());
296
-	}
297
-
298
-	public function testGetShortFooterPrivacy(): void {
299
-		$this->navigationManager->expects($this->once())->method('getAll')->with(INavigationManager::TYPE_GUEST)->willReturn([]);
300
-		$this->appConfig
301
-			->expects($this->exactly(5))
302
-			->method('getAppValueString')
303
-			->willReturnMap([
304
-				['url', $this->defaults->getBaseUrl(), 'url'],
305
-				['name', 'Nextcloud', 'Name'],
306
-				['slogan', $this->defaults->getSlogan(), 'Slogan'],
307
-				['imprintUrl', '', ''],
308
-				['privacyUrl', '', 'https://example.com/privacy'],
309
-			]);
310
-
311
-		$this->l10n
312
-			->expects($this->any())
313
-			->method('t')
314
-			->willReturnArgument(0);
315
-
316
-		$this->assertEquals('<a href="url" target="_blank" rel="noreferrer noopener" class="entity-name">Name</a> – Slogan<br/><span class="footer__legal-links"><a href="https://example.com/privacy" class="legal" target="_blank" rel="noreferrer noopener">Privacy policy</a></span>', $this->template->getShortFooter());
317
-	}
318
-
319
-	public function testGetShortFooterAllLegalLinks(): void {
320
-		$this->navigationManager->expects($this->once())->method('getAll')->with(INavigationManager::TYPE_GUEST)->willReturn([]);
321
-		$this->appConfig
322
-			->expects($this->exactly(5))
323
-			->method('getAppValueString')
324
-			->willReturnMap([
325
-				['url', $this->defaults->getBaseUrl(), 'url'],
326
-				['name', 'Nextcloud', 'Name'],
327
-				['slogan', $this->defaults->getSlogan(), 'Slogan'],
328
-				['imprintUrl', '', 'https://example.com/imprint'],
329
-				['privacyUrl', '', 'https://example.com/privacy'],
330
-			]);
331
-
332
-		$this->l10n
333
-			->expects($this->any())
334
-			->method('t')
335
-			->willReturnArgument(0);
336
-
337
-		$this->assertEquals('<a href="url" target="_blank" rel="noreferrer noopener" class="entity-name">Name</a> – Slogan<br/><span class="footer__legal-links"><a href="https://example.com/imprint" class="legal" target="_blank" rel="noreferrer noopener">Legal notice</a> · <a href="https://example.com/privacy" class="legal" target="_blank" rel="noreferrer noopener">Privacy policy</a></span>', $this->template->getShortFooter());
338
-	}
339
-
340
-	public static function invalidLegalUrlProvider(): array {
341
-		return [
342
-			['example.com/legal'],  # missing scheme
343
-			['https:///legal'],     # missing host
344
-		];
345
-	}
346
-
347
-	#[\PHPUnit\Framework\Attributes\DataProvider('invalidLegalUrlProvider')]
348
-	public function testGetShortFooterInvalidImprint(string $invalidImprintUrl): void {
349
-		$this->navigationManager->expects($this->once())->method('getAll')->with(INavigationManager::TYPE_GUEST)->willReturn([]);
350
-		$this->appConfig
351
-			->expects($this->exactly(5))
352
-			->method('getAppValueString')
353
-			->willReturnMap([
354
-				['url', $this->defaults->getBaseUrl(), 'url'],
355
-				['name', 'Nextcloud', 'Name'],
356
-				['slogan', $this->defaults->getSlogan(), 'Slogan'],
357
-				['imprintUrl', '', $invalidImprintUrl],
358
-				['privacyUrl', '', ''],
359
-			]);
360
-
361
-		$this->assertEquals('<a href="url" target="_blank" rel="noreferrer noopener" class="entity-name">Name</a> – Slogan', $this->template->getShortFooter());
362
-	}
363
-
364
-	#[\PHPUnit\Framework\Attributes\DataProvider('invalidLegalUrlProvider')]
365
-	public function testGetShortFooterInvalidPrivacy(string $invalidPrivacyUrl): void {
366
-		$this->navigationManager->expects($this->once())->method('getAll')->with(INavigationManager::TYPE_GUEST)->willReturn([]);
367
-		$this->appConfig
368
-			->expects($this->exactly(5))
369
-			->method('getAppValueString')
370
-			->willReturnMap([
371
-				['url', $this->defaults->getBaseUrl(), 'url'],
372
-				['name', 'Nextcloud', 'Name'],
373
-				['slogan', $this->defaults->getSlogan(), 'Slogan'],
374
-				['imprintUrl', '', ''],
375
-				['privacyUrl', '', $invalidPrivacyUrl],
376
-			]);
377
-
378
-		$this->assertEquals('<a href="url" target="_blank" rel="noreferrer noopener" class="entity-name">Name</a> – Slogan', $this->template->getShortFooter());
379
-	}
380
-
381
-	public function testGetColorPrimaryWithDefault(): void {
382
-		$this->appConfig
383
-			->expects(self::once())
384
-			->method('getAppValueBool')
385
-			->with('disable-user-theming')
386
-			->willReturn(false);
387
-		$this->appConfig
388
-			->expects(self::once())
389
-			->method('getAppValueString')
390
-			->with('primary_color', '')
391
-			->willReturn($this->defaults->getColorPrimary());
392
-
393
-		$this->assertEquals($this->defaults->getColorPrimary(), $this->template->getColorPrimary());
394
-	}
395
-
396
-	public function testGetColorPrimaryWithCustom(): void {
397
-		$this->appConfig
398
-			->expects(self::once())
399
-			->method('getAppValueBool')
400
-			->with('disable-user-theming')
401
-			->willReturn(false);
402
-		$this->appConfig
403
-			->expects(self::once())
404
-			->method('getAppValueString')
405
-			->with('primary_color', '')
406
-			->willReturn('#fff');
407
-
408
-		$this->assertEquals('#fff', $this->template->getColorPrimary());
409
-	}
410
-
411
-	public static function dataGetColorPrimary(): array {
412
-		return [
413
-			'with fallback default' => [
414
-				'disableTheming' => false,
415
-				'primaryColor' => '',
416
-				'userPrimaryColor' => '',
417
-				'expected' => BackgroundService::DEFAULT_COLOR,
418
-			],
419
-			'with custom admin primary' => [
420
-				'disableTheming' => false,
421
-				'primaryColor' => '#aaa',
422
-				'userPrimaryColor' => '',
423
-				'expected' => '#aaa',
424
-			],
425
-			'with custom invalid admin primary' => [
426
-				'disableTheming' => false,
427
-				'primaryColor' => 'invalid',
428
-				'userPrimaryColor' => '',
429
-				'expected' => BackgroundService::DEFAULT_COLOR,
430
-			],
431
-			'with custom invalid user primary' => [
432
-				'disableTheming' => false,
433
-				'primaryColor' => '',
434
-				'userPrimaryColor' => 'invalid-name',
435
-				'expected' => BackgroundService::DEFAULT_COLOR,
436
-			],
437
-			'with custom user primary' => [
438
-				'disableTheming' => false,
439
-				'primaryColor' => '',
440
-				'userPrimaryColor' => '#bbb',
441
-				'expected' => '#bbb',
442
-			],
443
-			'with disabled user theming primary' => [
444
-				'disableTheming' => true,
445
-				'primaryColor' => '#aaa',
446
-				'userPrimaryColor' => '#bbb',
447
-				'expected' => '#aaa',
448
-			],
449
-		];
450
-	}
451
-
452
-	#[\PHPUnit\Framework\Attributes\DataProvider('dataGetColorPrimary')]
453
-	public function testGetColorPrimary(bool $disableTheming, string $primaryColor, string $userPrimaryColor, string $expected): void {
454
-		$user = $this->createMock(IUser::class);
455
-		$this->userSession->expects($this->any())
456
-			->method('getUser')
457
-			->willReturn($user);
458
-		$user->expects($this->any())
459
-			->method('getUID')
460
-			->willReturn('user');
461
-		$this->appConfig
462
-			->expects(self::any())
463
-			->method('getAppValueBool')
464
-			->with('disable-user-theming')
465
-			->willReturn($disableTheming);
466
-		$this->appConfig
467
-			->expects(self::any())
468
-			->method('getAppValueString')
469
-			->with('primary_color', '')
470
-			->willReturn($primaryColor);
471
-		$this->config
472
-			->expects($this->any())
473
-			->method('getUserValue')
474
-			->with('user', 'theming', 'primary_color', '')
475
-			->willReturn($userPrimaryColor);
476
-
477
-		$this->assertEquals($expected, $this->template->getColorPrimary());
478
-	}
479
-
480
-	public function testSet(): void {
481
-		$this->appConfig
482
-			->expects($this->once())
483
-			->method('setAppValueInt')
484
-			->with('cachebuster', 16);
485
-		$this->appConfig
486
-			->expects($this->once())
487
-			->method('setAppValueString')
488
-			->with('MySetting', 'MyValue');
489
-		$this->appConfig
490
-			->expects($this->once())
491
-			->method('getAppValueInt')
492
-			->with('cachebuster')
493
-			->willReturn(15);
494
-		$this->cacheFactory
495
-			->expects($this->exactly(2))
496
-			->method('createDistributed')
497
-			->willReturnMap([
498
-				['theming-', $this->cache],
499
-				['imagePath', $this->cache],
500
-			]);
501
-		$this->cache
502
-			->expects($this->any())
503
-			->method('clear')
504
-			->with('');
505
-		$this->template->set('MySetting', 'MyValue');
506
-	}
507
-
508
-	public function testUndoName(): void {
509
-		$this->appConfig
510
-			->expects($this->once())
511
-			->method('deleteAppValue')
512
-			->with('name');
513
-		$this->appConfig
514
-			->expects($this->once())
515
-			->method('getAppValueInt')
516
-			->with('cachebuster')
517
-			->willReturn(15);
518
-		$this->appConfig
519
-			->expects($this->once())
520
-			->method('getAppValueString')
521
-			->with('name', 'Nextcloud')
522
-			->willReturn('Nextcloud');
523
-		$this->appConfig
524
-			->expects($this->once())
525
-			->method('setAppValueInt')
526
-			->with('cachebuster', 16);
527
-
528
-		$this->assertSame('Nextcloud', $this->template->undo('name'));
529
-	}
530
-
531
-	public function testUndoBaseUrl(): void {
532
-		$this->appConfig
533
-			->expects($this->once())
534
-			->method('deleteAppValue')
535
-			->with('url');
536
-		$this->appConfig
537
-			->expects($this->once())
538
-			->method('getAppValueInt')
539
-			->with('cachebuster')
540
-			->willReturn(15);
541
-		$this->appConfig
542
-			->expects($this->once())
543
-			->method('getAppValueString')
544
-			->with('url', $this->defaults->getBaseUrl())
545
-			->willReturn($this->defaults->getBaseUrl());
546
-		$this->appConfig
547
-			->expects($this->once())
548
-			->method('setAppValueInt')
549
-			->with('cachebuster', 16);
550
-
551
-		$this->assertSame($this->defaults->getBaseUrl(), $this->template->undo('url'));
552
-	}
553
-
554
-	public function testUndoSlogan(): void {
555
-		$this->appConfig
556
-			->expects($this->once())
557
-			->method('deleteAppValue')
558
-			->with('slogan');
559
-		$this->appConfig
560
-			->expects($this->once())
561
-			->method('getAppValueInt')
562
-			->with('cachebuster')
563
-			->willReturn(15);
564
-		$this->appConfig
565
-			->expects($this->once())
566
-			->method('getAppValueString')
567
-			->with('slogan', $this->defaults->getSlogan())
568
-			->willReturn($this->defaults->getSlogan());
569
-		$this->appConfig
570
-			->expects($this->once())
571
-			->method('setAppValueInt')
572
-			->with('cachebuster', 16);
573
-
574
-		$this->assertSame($this->defaults->getSlogan(), $this->template->undo('slogan'));
575
-	}
576
-
577
-	public function testUndoPrimaryColor(): void {
578
-		$this->appConfig
579
-			->expects($this->once())
580
-			->method('deleteAppValue')
581
-			->with('primary_color');
582
-		$this->appConfig
583
-			->expects($this->once())
584
-			->method('getAppValueInt')
585
-			->with('cachebuster')
586
-			->willReturn(15);
587
-		$this->appConfig
588
-			->expects($this->once())
589
-			->method('setAppValueInt')
590
-			->with('cachebuster', 16);
591
-
592
-		$this->assertSame($this->defaults->getColorPrimary(), $this->template->undo('primary_color'));
593
-	}
594
-
595
-	public function testUndoDefaultAction(): void {
596
-		$this->appConfig
597
-			->expects($this->once())
598
-			->method('deleteAppValue')
599
-			->with('defaultitem');
600
-		$this->appConfig
601
-			->expects($this->once())
602
-			->method('getAppValueInt')
603
-			->with('cachebuster', '0')
604
-			->willReturn(15);
605
-		$this->appConfig
606
-			->expects($this->once())
607
-			->method('setAppValueInt')
608
-			->with('cachebuster', 16);
609
-
610
-		$this->assertSame('', $this->template->undo('defaultitem'));
611
-	}
612
-
613
-	public function testGetBackground(): void {
614
-		$this->imageManager
615
-			->expects($this->once())
616
-			->method('getImageUrl')
617
-			->with('background')
618
-			->willReturn('custom-background?v=0');
619
-		$this->assertEquals('custom-background?v=0', $this->template->getBackground());
620
-	}
621
-
622
-	private function getLogoHelper($withName, $useSvg) {
623
-		$this->imageManager->expects($this->any())
624
-			->method('getImage')
625
-			->with('logo')
626
-			->willThrowException(new NotFoundException());
627
-		$this->appConfig
628
-			->expects($this->once())
629
-			->method('getAppValueString')
630
-			->with('logoMime', '')
631
-			->willReturn('');
632
-		$this->appConfig
633
-			->expects($this->once())
634
-			->method('getAppValueInt')
635
-			->with('cachebuster')
636
-			->willReturn(0);
637
-		$this->urlGenerator->expects($this->once())
638
-			->method('imagePath')
639
-			->with('core', $withName)
640
-			->willReturn('core-logo');
641
-		$this->assertEquals('core-logo?v=0', $this->template->getLogo($useSvg));
642
-	}
643
-
644
-	public function testGetLogoDefaultWithSvg(): void {
645
-		$this->getLogoHelper('logo/logo.svg', true);
646
-	}
647
-
648
-	public function testGetLogoDefaultWithoutSvg(): void {
649
-		$this->getLogoHelper('logo/logo.png', false);
650
-	}
651
-
652
-	public function testGetLogoCustom(): void {
653
-		$this->appConfig
654
-			->expects($this->once())
655
-			->method('getAppValueString')
656
-			->with('logoMime', '')
657
-			->willReturn('image/svg+xml');
658
-		$this->appConfig
659
-			->expects($this->once())
660
-			->method('getAppValueInt')
661
-			->with('cachebuster')
662
-			->willReturn(0);
663
-		$this->urlGenerator->expects($this->once())
664
-			->method('linkToRoute')
665
-			->with('theming.Theming.getImage')
666
-			->willReturn('custom-logo?v=0');
667
-		$this->assertEquals('custom-logo' . '?v=0', $this->template->getLogo());
668
-	}
669
-
670
-	public function testGetScssVariablesCached(): void {
671
-		$this->appConfig->expects($this->any())
672
-			->method('getAppValueInt')
673
-			->with('cachebuster')
674
-			->willReturn(1);
675
-		$this->cacheFactory->expects($this->once())
676
-			->method('createDistributed')
677
-			->with('theming-1-')
678
-			->willReturn($this->cache);
679
-		$this->cache->expects($this->once())->method('get')->with('getScssVariables')->willReturn(['foo' => 'bar']);
680
-		$this->assertEquals(['foo' => 'bar'], $this->template->getScssVariables());
681
-	}
682
-
683
-	public function testGetScssVariables(): void {
684
-		$this->appConfig->expects($this->any())
685
-			->method('getAppValueInt')
686
-			->with('cachebuster')
687
-			->willReturn(0);
688
-		$this->appConfig
689
-			->expects($this->any())
690
-			->method('getAppValueString')
691
-			->willReturnMap([
692
-				['imprintUrl', '', ''],
693
-				['privacyUrl', '', ''],
694
-				['logoMime', '', 'jpeg'],
695
-				['backgroundMime', '', 'jpeg'],
696
-				['logoheaderMime', '', 'jpeg'],
697
-				['faviconMime', '', 'jpeg'],
698
-				['primary_color', '', false, $this->defaults->getColorPrimary()],
699
-				['primary_color', $this->defaults->getColorPrimary(), false, $this->defaults->getColorPrimary()],
700
-			]);
701
-
702
-		$this->util->expects($this->any())->method('invertTextColor')->with($this->defaults->getColorPrimary())->willReturn(false);
703
-		$this->util->expects($this->any())->method('elementColor')->with($this->defaults->getColorPrimary())->willReturn('#aaaaaa');
704
-		$this->cacheFactory->expects($this->once())
705
-			->method('createDistributed')
706
-			->with('theming-0-')
707
-			->willReturn($this->cache);
708
-		$this->cache->expects($this->once())->method('get')->with('getScssVariables')->willReturn(null);
709
-		$this->imageManager->expects($this->exactly(4))
710
-			->method('getImageUrl')
711
-			->willReturnMap([
712
-				['logo', 'custom-logo?v=0'],
713
-				['logoheader', 'custom-logoheader?v=0'],
714
-				['favicon', 'custom-favicon?v=0'],
715
-				['background', 'custom-background?v=0'],
716
-			]);
717
-
718
-		$expected = [
719
-			'theming-cachebuster' => '\'0\'',
720
-			'theming-logo-mime' => '\'jpeg\'',
721
-			'theming-background-mime' => '\'jpeg\'',
722
-			'image-logo' => "url('custom-logo?v=0')",
723
-			'image-login-background' => "url('custom-background?v=0')",
724
-			'color-primary' => $this->defaults->getColorPrimary(),
725
-			'color-primary-text' => '#ffffff',
726
-			'image-login-plain' => 'false',
727
-			'color-primary-element' => '#aaaaaa',
728
-			'theming-logoheader-mime' => '\'jpeg\'',
729
-			'theming-favicon-mime' => '\'jpeg\'',
730
-			'image-logoheader' => "url('custom-logoheader?v=0')",
731
-			'image-favicon' => "url('custom-favicon?v=0')",
732
-			'has-legal-links' => 'false',
733
-		];
734
-		$this->assertEquals($expected, $this->template->getScssVariables());
735
-	}
736
-
737
-	public function testGetDefaultAndroidURL(): void {
738
-		$this->appConfig
739
-			->expects($this->once())
740
-			->method('getAppValueString')
741
-			->with('AndroidClientUrl', 'https://play.google.com/store/apps/details?id=com.nextcloud.client')
742
-			->willReturn('https://play.google.com/store/apps/details?id=com.nextcloud.client');
743
-
744
-		$this->assertEquals('https://play.google.com/store/apps/details?id=com.nextcloud.client', $this->template->getAndroidClientUrl());
745
-	}
746
-
747
-	public function testGetCustomAndroidURL(): void {
748
-		$this->appConfig
749
-			->expects($this->once())
750
-			->method('getAppValueString')
751
-			->with('AndroidClientUrl', 'https://play.google.com/store/apps/details?id=com.nextcloud.client')
752
-			->willReturn('https://play.google.com/store/apps/details?id=com.mycloud.client');
753
-
754
-		$this->assertEquals('https://play.google.com/store/apps/details?id=com.mycloud.client', $this->template->getAndroidClientUrl());
755
-	}
756
-
757
-	public function testGetDefaultiOSURL(): void {
758
-		$this->appConfig
759
-			->expects($this->once())
760
-			->method('getAppValueString')
761
-			->with('iOSClientUrl', 'https://geo.itunes.apple.com/us/app/nextcloud/id1125420102?mt=8')
762
-			->willReturn('https://geo.itunes.apple.com/us/app/nextcloud/id1125420102?mt=8');
763
-
764
-		$this->assertEquals('https://geo.itunes.apple.com/us/app/nextcloud/id1125420102?mt=8', $this->template->getiOSClientUrl());
765
-	}
766
-
767
-	public function testGetCustomiOSURL(): void {
768
-		$this->appConfig
769
-			->expects($this->once())
770
-			->method('getAppValueString')
771
-			->with('iOSClientUrl', 'https://geo.itunes.apple.com/us/app/nextcloud/id1125420102?mt=8')
772
-			->willReturn('https://geo.itunes.apple.com/us/app/nextcloud/id1234567890?mt=8');
773
-
774
-		$this->assertEquals('https://geo.itunes.apple.com/us/app/nextcloud/id1234567890?mt=8', $this->template->getiOSClientUrl());
775
-	}
776
-
777
-	public function testGetDefaultiTunesAppId(): void {
778
-		$this->appConfig
779
-			->expects($this->once())
780
-			->method('getAppValueString')
781
-			->with('iTunesAppId', '1125420102')
782
-			->willReturn('1125420102');
783
-
784
-		$this->assertEquals('1125420102', $this->template->getiTunesAppId());
785
-	}
786
-
787
-	public function testGetCustomiTunesAppId(): void {
788
-		$this->appConfig
789
-			->expects($this->once())
790
-			->method('getAppValueString')
791
-			->with('iTunesAppId', '1125420102')
792
-			->willReturn('1234567890');
793
-
794
-		$this->assertEquals('1234567890', $this->template->getiTunesAppId());
795
-	}
796
-
797
-	public static function dataReplaceImagePath(): array {
798
-		return [
799
-			['core', 'test.png', false],
800
-			['core', 'manifest.json'],
801
-			['core', 'favicon.ico'],
802
-			['core', 'favicon-touch.png'],
803
-		];
804
-	}
805
-
806
-	#[\PHPUnit\Framework\Attributes\DataProvider('dataReplaceImagePath')]
807
-	public function testReplaceImagePath(string $app, string $image, string|bool $result = 'themingRoute?v=1234abcd'): void {
808
-		$this->cache->expects($this->any())
809
-			->method('get')
810
-			->with('shouldReplaceIcons')
811
-			->willReturn(true);
812
-		$this->appConfig
813
-			->expects($this->any())
814
-			->method('getAppValueInt')
815
-			->with('cachebuster')
816
-			->willReturn(0);
817
-		$this->urlGenerator
818
-			->expects($this->any())
819
-			->method('linkToRoute')
820
-			->willReturn('themingRoute');
821
-		if ($result) {
822
-			$this->util
823
-				->expects($this->once())
824
-				->method('getCacheBuster')
825
-				->willReturn('1234abcd');
826
-		}
827
-		$this->assertEquals($result, $this->template->replaceImagePath($app, $image));
828
-	}
829
-
830
-	public static function setTypesProvider(): array {
831
-		return [
832
-			[ConfigLexicon::BASE_URL, 'example.com', 'example.com'],
833
-			[ConfigLexicon::USER_THEMING_DISABLED, 'no', false],
834
-			[ConfigLexicon::USER_THEMING_DISABLED, 'true', true],
835
-		];
836
-	}
837
-
838
-	#[\PHPUnit\Framework\Attributes\DataProvider('setTypesProvider')]
839
-	public function testSetTypes(string $setting, string $value, mixed $expected): void {
840
-		$setValue = null;
841
-		$cb = function ($setting, $value) use (&$setValue) {
842
-			if ($setting !== ConfigLexicon::CACHE_BUSTER) {
843
-				$setValue = $value;
844
-			}
845
-			return true;
846
-		};
847
-		$this->appConfig
848
-			->method('setAppValueBool')
849
-			->willReturnCallback($cb);
850
-		$this->appConfig
851
-			->method('setAppValueString')
852
-			->willReturnCallback($cb);
853
-		$this->appConfig
854
-			->method('setAppValueInt')
855
-			->willReturnCallback($cb);
856
-
857
-		$this->template->set($setting, $value);
858
-		$this->assertEquals($expected, $setValue);
859
-	}
30
+    private IAppConfig&MockObject $appConfig;
31
+    private IConfig&MockObject $config;
32
+    private IL10N&MockObject $l10n;
33
+    private IUserSession&MockObject $userSession;
34
+    private IURLGenerator&MockObject $urlGenerator;
35
+    private ICacheFactory&MockObject $cacheFactory;
36
+    private Util&MockObject $util;
37
+    private ICache&MockObject $cache;
38
+    private IAppManager&MockObject $appManager;
39
+    private ImageManager&MockObject $imageManager;
40
+    private INavigationManager&MockObject $navigationManager;
41
+    private BackgroundService&MockObject $backgroundService;
42
+
43
+    private \OC_Defaults $defaults;
44
+    private ThemingDefaults $template;
45
+
46
+    protected function setUp(): void {
47
+        parent::setUp();
48
+        $this->appConfig = $this->createMock(IAppConfig::class);
49
+        $this->config = $this->createMock(IConfig::class);
50
+        $this->l10n = $this->createMock(IL10N::class);
51
+        $this->userSession = $this->createMock(IUserSession::class);
52
+        $this->urlGenerator = $this->createMock(IURLGenerator::class);
53
+        $this->cacheFactory = $this->createMock(ICacheFactory::class);
54
+        $this->cache = $this->createMock(ICache::class);
55
+        $this->util = $this->createMock(Util::class);
56
+        $this->imageManager = $this->createMock(ImageManager::class);
57
+        $this->appManager = $this->createMock(IAppManager::class);
58
+        $this->navigationManager = $this->createMock(INavigationManager::class);
59
+        $this->backgroundService = $this->createMock(BackgroundService::class);
60
+        $this->defaults = new \OC_Defaults();
61
+        $this->urlGenerator
62
+            ->expects($this->any())
63
+            ->method('getBaseUrl')
64
+            ->willReturn('');
65
+        $this->template = new ThemingDefaults(
66
+            $this->config,
67
+            $this->appConfig,
68
+            $this->l10n,
69
+            $this->userSession,
70
+            $this->urlGenerator,
71
+            $this->cacheFactory,
72
+            $this->util,
73
+            $this->imageManager,
74
+            $this->appManager,
75
+            $this->navigationManager,
76
+            $this->backgroundService,
77
+        );
78
+    }
79
+
80
+    public function testGetNameWithDefault(): void {
81
+        $this->appConfig
82
+            ->expects($this->once())
83
+            ->method('getAppValueString')
84
+            ->with('name', 'Nextcloud')
85
+            ->willReturn('Nextcloud');
86
+
87
+        $this->assertEquals('Nextcloud', $this->template->getName());
88
+    }
89
+
90
+    public function testGetNameWithCustom(): void {
91
+        $this->appConfig
92
+            ->expects($this->once())
93
+            ->method('getAppValueString')
94
+            ->with('name', 'Nextcloud')
95
+            ->willReturn('MyCustomCloud');
96
+
97
+        $this->assertEquals('MyCustomCloud', $this->template->getName());
98
+    }
99
+
100
+    public function testGetHTMLNameWithDefault(): void {
101
+        $this->appConfig
102
+            ->expects($this->once())
103
+            ->method('getAppValueString')
104
+            ->with('name', 'Nextcloud')
105
+            ->willReturn('Nextcloud');
106
+
107
+        $this->assertEquals('Nextcloud', $this->template->getHTMLName());
108
+    }
109
+
110
+    public function testGetHTMLNameWithCustom(): void {
111
+        $this->appConfig
112
+            ->expects($this->once())
113
+            ->method('getAppValueString')
114
+            ->with('name', 'Nextcloud')
115
+            ->willReturn('MyCustomCloud');
116
+
117
+        $this->assertEquals('MyCustomCloud', $this->template->getHTMLName());
118
+    }
119
+
120
+    public function testGetTitleWithDefault(): void {
121
+        $this->appConfig
122
+            ->expects($this->once())
123
+            ->method('getAppValueString')
124
+            ->with('name', 'Nextcloud')
125
+            ->willReturn('Nextcloud');
126
+
127
+        $this->assertEquals('Nextcloud', $this->template->getTitle());
128
+    }
129
+
130
+    public function testGetTitleWithCustom(): void {
131
+        $this->appConfig
132
+            ->expects($this->once())
133
+            ->method('getAppValueString')
134
+            ->with('name', 'Nextcloud')
135
+            ->willReturn('MyCustomCloud');
136
+
137
+        $this->assertEquals('MyCustomCloud', $this->template->getTitle());
138
+    }
139
+
140
+
141
+    public function testGetEntityWithDefault(): void {
142
+        $this->appConfig
143
+            ->expects($this->once())
144
+            ->method('getAppValueString')
145
+            ->with('name', 'Nextcloud')
146
+            ->willReturn('Nextcloud');
147
+
148
+        $this->assertEquals('Nextcloud', $this->template->getEntity());
149
+    }
150
+
151
+    public function testGetEntityWithCustom(): void {
152
+        $this->appConfig
153
+            ->expects($this->once())
154
+            ->method('getAppValueString')
155
+            ->with('name', 'Nextcloud')
156
+            ->willReturn('MyCustomCloud');
157
+
158
+        $this->assertEquals('MyCustomCloud', $this->template->getEntity());
159
+    }
160
+
161
+    public function testGetBaseUrlWithDefault(): void {
162
+        $this->appConfig
163
+            ->expects($this->once())
164
+            ->method('getAppValueString')
165
+            ->with('url', $this->defaults->getBaseUrl())
166
+            ->willReturn($this->defaults->getBaseUrl());
167
+
168
+        $this->assertEquals($this->defaults->getBaseUrl(), $this->template->getBaseUrl());
169
+    }
170
+
171
+    public function testGetBaseUrlWithCustom(): void {
172
+        $this->appConfig
173
+            ->expects($this->once())
174
+            ->method('getAppValueString')
175
+            ->with('url', $this->defaults->getBaseUrl())
176
+            ->willReturn('https://example.com/');
177
+
178
+        $this->assertEquals('https://example.com/', $this->template->getBaseUrl());
179
+    }
180
+
181
+    public static function legalUrlProvider(): array {
182
+        return [
183
+            [''],
184
+            ['https://example.com/legal.html'],
185
+        ];
186
+    }
187
+
188
+    #[\PHPUnit\Framework\Attributes\DataProvider('legalUrlProvider')]
189
+    public function testGetImprintURL(string $imprintUrl): void {
190
+        $this->appConfig
191
+            ->expects($this->once())
192
+            ->method('getAppValueString')
193
+            ->with('imprintUrl', '')
194
+            ->willReturn($imprintUrl);
195
+
196
+        $this->assertEquals($imprintUrl, $this->template->getImprintUrl());
197
+    }
198
+
199
+    #[\PHPUnit\Framework\Attributes\DataProvider('legalUrlProvider')]
200
+    public function testGetPrivacyURL(string $privacyUrl): void {
201
+        $this->appConfig
202
+            ->expects($this->once())
203
+            ->method('getAppValueString')
204
+            ->with('privacyUrl', '')
205
+            ->willReturn($privacyUrl);
206
+
207
+        $this->assertEquals($privacyUrl, $this->template->getPrivacyUrl());
208
+    }
209
+
210
+    public function testGetSloganWithDefault(): void {
211
+        $this->appConfig
212
+            ->expects($this->once())
213
+            ->method('getAppValueString')
214
+            ->with('slogan', $this->defaults->getSlogan())
215
+            ->willReturn($this->defaults->getSlogan());
216
+
217
+        $this->assertEquals($this->defaults->getSlogan(), $this->template->getSlogan());
218
+    }
219
+
220
+    public function testGetSloganWithCustom(): void {
221
+        $this->appConfig
222
+            ->expects($this->once())
223
+            ->method('getAppValueString')
224
+            ->with('slogan', $this->defaults->getSlogan())
225
+            ->willReturn('My custom Slogan');
226
+
227
+        $this->assertEquals('My custom Slogan', $this->template->getSlogan());
228
+    }
229
+
230
+    public function testGetShortFooter(): void {
231
+        $this->appConfig
232
+            ->expects($this->exactly(5))
233
+            ->method('getAppValueString')
234
+            ->willReturnMap([
235
+                ['url', $this->defaults->getBaseUrl(), 'url'],
236
+                ['name', 'Nextcloud', 'Name'],
237
+                ['slogan', $this->defaults->getSlogan(), 'Slogan'],
238
+                ['imprintUrl', '', ''],
239
+                ['privacyUrl', '', ''],
240
+            ]);
241
+
242
+        $this->assertEquals('<a href="url" target="_blank" rel="noreferrer noopener" class="entity-name">Name</a> – Slogan', $this->template->getShortFooter());
243
+    }
244
+
245
+    public function testGetShortFooterEmptyUrl(): void {
246
+        $this->navigationManager->expects($this->once())->method('getAll')->with(INavigationManager::TYPE_GUEST)->willReturn([]);
247
+        $this->appConfig
248
+            ->expects($this->exactly(5))
249
+            ->method('getAppValueString')
250
+            ->willReturnMap([
251
+                ['url', $this->defaults->getBaseUrl(), ''],
252
+                ['name', 'Nextcloud', 'Name'],
253
+                ['slogan', $this->defaults->getSlogan(), 'Slogan'],
254
+                ['imprintUrl', '', ''],
255
+                ['privacyUrl', '', ''],
256
+            ]);
257
+
258
+        $this->assertEquals('<span class="entity-name">Name</span> – Slogan', $this->template->getShortFooter());
259
+    }
260
+
261
+    public function testGetShortFooterEmptySlogan(): void {
262
+        $this->navigationManager->expects($this->once())->method('getAll')->with(INavigationManager::TYPE_GUEST)->willReturn([]);
263
+        $this->appConfig
264
+            ->expects($this->exactly(5))
265
+            ->method('getAppValueString')
266
+            ->willReturnMap([
267
+                ['url', $this->defaults->getBaseUrl(), 'url'],
268
+                ['name', 'Nextcloud', 'Name'],
269
+                ['slogan', $this->defaults->getSlogan(), ''],
270
+                ['imprintUrl', '', ''],
271
+                ['privacyUrl', '', ''],
272
+            ]);
273
+
274
+        $this->assertEquals('<a href="url" target="_blank" rel="noreferrer noopener" class="entity-name">Name</a>', $this->template->getShortFooter());
275
+    }
276
+
277
+    public function testGetShortFooterImprint(): void {
278
+        $this->navigationManager->expects($this->once())->method('getAll')->with(INavigationManager::TYPE_GUEST)->willReturn([]);
279
+        $this->appConfig
280
+            ->expects($this->exactly(5))
281
+            ->method('getAppValueString')
282
+            ->willReturnMap([
283
+                ['url', $this->defaults->getBaseUrl(), 'url'],
284
+                ['name', 'Nextcloud', 'Name'],
285
+                ['slogan', $this->defaults->getSlogan(), 'Slogan'],
286
+                ['imprintUrl', '', 'https://example.com/imprint'],
287
+                ['privacyUrl', '', ''],
288
+            ]);
289
+
290
+        $this->l10n
291
+            ->expects($this->any())
292
+            ->method('t')
293
+            ->willReturnArgument(0);
294
+
295
+        $this->assertEquals('<a href="url" target="_blank" rel="noreferrer noopener" class="entity-name">Name</a> – Slogan<br/><span class="footer__legal-links"><a href="https://example.com/imprint" class="legal" target="_blank" rel="noreferrer noopener">Legal notice</a></span>', $this->template->getShortFooter());
296
+    }
297
+
298
+    public function testGetShortFooterPrivacy(): void {
299
+        $this->navigationManager->expects($this->once())->method('getAll')->with(INavigationManager::TYPE_GUEST)->willReturn([]);
300
+        $this->appConfig
301
+            ->expects($this->exactly(5))
302
+            ->method('getAppValueString')
303
+            ->willReturnMap([
304
+                ['url', $this->defaults->getBaseUrl(), 'url'],
305
+                ['name', 'Nextcloud', 'Name'],
306
+                ['slogan', $this->defaults->getSlogan(), 'Slogan'],
307
+                ['imprintUrl', '', ''],
308
+                ['privacyUrl', '', 'https://example.com/privacy'],
309
+            ]);
310
+
311
+        $this->l10n
312
+            ->expects($this->any())
313
+            ->method('t')
314
+            ->willReturnArgument(0);
315
+
316
+        $this->assertEquals('<a href="url" target="_blank" rel="noreferrer noopener" class="entity-name">Name</a> – Slogan<br/><span class="footer__legal-links"><a href="https://example.com/privacy" class="legal" target="_blank" rel="noreferrer noopener">Privacy policy</a></span>', $this->template->getShortFooter());
317
+    }
318
+
319
+    public function testGetShortFooterAllLegalLinks(): void {
320
+        $this->navigationManager->expects($this->once())->method('getAll')->with(INavigationManager::TYPE_GUEST)->willReturn([]);
321
+        $this->appConfig
322
+            ->expects($this->exactly(5))
323
+            ->method('getAppValueString')
324
+            ->willReturnMap([
325
+                ['url', $this->defaults->getBaseUrl(), 'url'],
326
+                ['name', 'Nextcloud', 'Name'],
327
+                ['slogan', $this->defaults->getSlogan(), 'Slogan'],
328
+                ['imprintUrl', '', 'https://example.com/imprint'],
329
+                ['privacyUrl', '', 'https://example.com/privacy'],
330
+            ]);
331
+
332
+        $this->l10n
333
+            ->expects($this->any())
334
+            ->method('t')
335
+            ->willReturnArgument(0);
336
+
337
+        $this->assertEquals('<a href="url" target="_blank" rel="noreferrer noopener" class="entity-name">Name</a> – Slogan<br/><span class="footer__legal-links"><a href="https://example.com/imprint" class="legal" target="_blank" rel="noreferrer noopener">Legal notice</a> · <a href="https://example.com/privacy" class="legal" target="_blank" rel="noreferrer noopener">Privacy policy</a></span>', $this->template->getShortFooter());
338
+    }
339
+
340
+    public static function invalidLegalUrlProvider(): array {
341
+        return [
342
+            ['example.com/legal'],  # missing scheme
343
+            ['https:///legal'],     # missing host
344
+        ];
345
+    }
346
+
347
+    #[\PHPUnit\Framework\Attributes\DataProvider('invalidLegalUrlProvider')]
348
+    public function testGetShortFooterInvalidImprint(string $invalidImprintUrl): void {
349
+        $this->navigationManager->expects($this->once())->method('getAll')->with(INavigationManager::TYPE_GUEST)->willReturn([]);
350
+        $this->appConfig
351
+            ->expects($this->exactly(5))
352
+            ->method('getAppValueString')
353
+            ->willReturnMap([
354
+                ['url', $this->defaults->getBaseUrl(), 'url'],
355
+                ['name', 'Nextcloud', 'Name'],
356
+                ['slogan', $this->defaults->getSlogan(), 'Slogan'],
357
+                ['imprintUrl', '', $invalidImprintUrl],
358
+                ['privacyUrl', '', ''],
359
+            ]);
360
+
361
+        $this->assertEquals('<a href="url" target="_blank" rel="noreferrer noopener" class="entity-name">Name</a> – Slogan', $this->template->getShortFooter());
362
+    }
363
+
364
+    #[\PHPUnit\Framework\Attributes\DataProvider('invalidLegalUrlProvider')]
365
+    public function testGetShortFooterInvalidPrivacy(string $invalidPrivacyUrl): void {
366
+        $this->navigationManager->expects($this->once())->method('getAll')->with(INavigationManager::TYPE_GUEST)->willReturn([]);
367
+        $this->appConfig
368
+            ->expects($this->exactly(5))
369
+            ->method('getAppValueString')
370
+            ->willReturnMap([
371
+                ['url', $this->defaults->getBaseUrl(), 'url'],
372
+                ['name', 'Nextcloud', 'Name'],
373
+                ['slogan', $this->defaults->getSlogan(), 'Slogan'],
374
+                ['imprintUrl', '', ''],
375
+                ['privacyUrl', '', $invalidPrivacyUrl],
376
+            ]);
377
+
378
+        $this->assertEquals('<a href="url" target="_blank" rel="noreferrer noopener" class="entity-name">Name</a> – Slogan', $this->template->getShortFooter());
379
+    }
380
+
381
+    public function testGetColorPrimaryWithDefault(): void {
382
+        $this->appConfig
383
+            ->expects(self::once())
384
+            ->method('getAppValueBool')
385
+            ->with('disable-user-theming')
386
+            ->willReturn(false);
387
+        $this->appConfig
388
+            ->expects(self::once())
389
+            ->method('getAppValueString')
390
+            ->with('primary_color', '')
391
+            ->willReturn($this->defaults->getColorPrimary());
392
+
393
+        $this->assertEquals($this->defaults->getColorPrimary(), $this->template->getColorPrimary());
394
+    }
395
+
396
+    public function testGetColorPrimaryWithCustom(): void {
397
+        $this->appConfig
398
+            ->expects(self::once())
399
+            ->method('getAppValueBool')
400
+            ->with('disable-user-theming')
401
+            ->willReturn(false);
402
+        $this->appConfig
403
+            ->expects(self::once())
404
+            ->method('getAppValueString')
405
+            ->with('primary_color', '')
406
+            ->willReturn('#fff');
407
+
408
+        $this->assertEquals('#fff', $this->template->getColorPrimary());
409
+    }
410
+
411
+    public static function dataGetColorPrimary(): array {
412
+        return [
413
+            'with fallback default' => [
414
+                'disableTheming' => false,
415
+                'primaryColor' => '',
416
+                'userPrimaryColor' => '',
417
+                'expected' => BackgroundService::DEFAULT_COLOR,
418
+            ],
419
+            'with custom admin primary' => [
420
+                'disableTheming' => false,
421
+                'primaryColor' => '#aaa',
422
+                'userPrimaryColor' => '',
423
+                'expected' => '#aaa',
424
+            ],
425
+            'with custom invalid admin primary' => [
426
+                'disableTheming' => false,
427
+                'primaryColor' => 'invalid',
428
+                'userPrimaryColor' => '',
429
+                'expected' => BackgroundService::DEFAULT_COLOR,
430
+            ],
431
+            'with custom invalid user primary' => [
432
+                'disableTheming' => false,
433
+                'primaryColor' => '',
434
+                'userPrimaryColor' => 'invalid-name',
435
+                'expected' => BackgroundService::DEFAULT_COLOR,
436
+            ],
437
+            'with custom user primary' => [
438
+                'disableTheming' => false,
439
+                'primaryColor' => '',
440
+                'userPrimaryColor' => '#bbb',
441
+                'expected' => '#bbb',
442
+            ],
443
+            'with disabled user theming primary' => [
444
+                'disableTheming' => true,
445
+                'primaryColor' => '#aaa',
446
+                'userPrimaryColor' => '#bbb',
447
+                'expected' => '#aaa',
448
+            ],
449
+        ];
450
+    }
451
+
452
+    #[\PHPUnit\Framework\Attributes\DataProvider('dataGetColorPrimary')]
453
+    public function testGetColorPrimary(bool $disableTheming, string $primaryColor, string $userPrimaryColor, string $expected): void {
454
+        $user = $this->createMock(IUser::class);
455
+        $this->userSession->expects($this->any())
456
+            ->method('getUser')
457
+            ->willReturn($user);
458
+        $user->expects($this->any())
459
+            ->method('getUID')
460
+            ->willReturn('user');
461
+        $this->appConfig
462
+            ->expects(self::any())
463
+            ->method('getAppValueBool')
464
+            ->with('disable-user-theming')
465
+            ->willReturn($disableTheming);
466
+        $this->appConfig
467
+            ->expects(self::any())
468
+            ->method('getAppValueString')
469
+            ->with('primary_color', '')
470
+            ->willReturn($primaryColor);
471
+        $this->config
472
+            ->expects($this->any())
473
+            ->method('getUserValue')
474
+            ->with('user', 'theming', 'primary_color', '')
475
+            ->willReturn($userPrimaryColor);
476
+
477
+        $this->assertEquals($expected, $this->template->getColorPrimary());
478
+    }
479
+
480
+    public function testSet(): void {
481
+        $this->appConfig
482
+            ->expects($this->once())
483
+            ->method('setAppValueInt')
484
+            ->with('cachebuster', 16);
485
+        $this->appConfig
486
+            ->expects($this->once())
487
+            ->method('setAppValueString')
488
+            ->with('MySetting', 'MyValue');
489
+        $this->appConfig
490
+            ->expects($this->once())
491
+            ->method('getAppValueInt')
492
+            ->with('cachebuster')
493
+            ->willReturn(15);
494
+        $this->cacheFactory
495
+            ->expects($this->exactly(2))
496
+            ->method('createDistributed')
497
+            ->willReturnMap([
498
+                ['theming-', $this->cache],
499
+                ['imagePath', $this->cache],
500
+            ]);
501
+        $this->cache
502
+            ->expects($this->any())
503
+            ->method('clear')
504
+            ->with('');
505
+        $this->template->set('MySetting', 'MyValue');
506
+    }
507
+
508
+    public function testUndoName(): void {
509
+        $this->appConfig
510
+            ->expects($this->once())
511
+            ->method('deleteAppValue')
512
+            ->with('name');
513
+        $this->appConfig
514
+            ->expects($this->once())
515
+            ->method('getAppValueInt')
516
+            ->with('cachebuster')
517
+            ->willReturn(15);
518
+        $this->appConfig
519
+            ->expects($this->once())
520
+            ->method('getAppValueString')
521
+            ->with('name', 'Nextcloud')
522
+            ->willReturn('Nextcloud');
523
+        $this->appConfig
524
+            ->expects($this->once())
525
+            ->method('setAppValueInt')
526
+            ->with('cachebuster', 16);
527
+
528
+        $this->assertSame('Nextcloud', $this->template->undo('name'));
529
+    }
530
+
531
+    public function testUndoBaseUrl(): void {
532
+        $this->appConfig
533
+            ->expects($this->once())
534
+            ->method('deleteAppValue')
535
+            ->with('url');
536
+        $this->appConfig
537
+            ->expects($this->once())
538
+            ->method('getAppValueInt')
539
+            ->with('cachebuster')
540
+            ->willReturn(15);
541
+        $this->appConfig
542
+            ->expects($this->once())
543
+            ->method('getAppValueString')
544
+            ->with('url', $this->defaults->getBaseUrl())
545
+            ->willReturn($this->defaults->getBaseUrl());
546
+        $this->appConfig
547
+            ->expects($this->once())
548
+            ->method('setAppValueInt')
549
+            ->with('cachebuster', 16);
550
+
551
+        $this->assertSame($this->defaults->getBaseUrl(), $this->template->undo('url'));
552
+    }
553
+
554
+    public function testUndoSlogan(): void {
555
+        $this->appConfig
556
+            ->expects($this->once())
557
+            ->method('deleteAppValue')
558
+            ->with('slogan');
559
+        $this->appConfig
560
+            ->expects($this->once())
561
+            ->method('getAppValueInt')
562
+            ->with('cachebuster')
563
+            ->willReturn(15);
564
+        $this->appConfig
565
+            ->expects($this->once())
566
+            ->method('getAppValueString')
567
+            ->with('slogan', $this->defaults->getSlogan())
568
+            ->willReturn($this->defaults->getSlogan());
569
+        $this->appConfig
570
+            ->expects($this->once())
571
+            ->method('setAppValueInt')
572
+            ->with('cachebuster', 16);
573
+
574
+        $this->assertSame($this->defaults->getSlogan(), $this->template->undo('slogan'));
575
+    }
576
+
577
+    public function testUndoPrimaryColor(): void {
578
+        $this->appConfig
579
+            ->expects($this->once())
580
+            ->method('deleteAppValue')
581
+            ->with('primary_color');
582
+        $this->appConfig
583
+            ->expects($this->once())
584
+            ->method('getAppValueInt')
585
+            ->with('cachebuster')
586
+            ->willReturn(15);
587
+        $this->appConfig
588
+            ->expects($this->once())
589
+            ->method('setAppValueInt')
590
+            ->with('cachebuster', 16);
591
+
592
+        $this->assertSame($this->defaults->getColorPrimary(), $this->template->undo('primary_color'));
593
+    }
594
+
595
+    public function testUndoDefaultAction(): void {
596
+        $this->appConfig
597
+            ->expects($this->once())
598
+            ->method('deleteAppValue')
599
+            ->with('defaultitem');
600
+        $this->appConfig
601
+            ->expects($this->once())
602
+            ->method('getAppValueInt')
603
+            ->with('cachebuster', '0')
604
+            ->willReturn(15);
605
+        $this->appConfig
606
+            ->expects($this->once())
607
+            ->method('setAppValueInt')
608
+            ->with('cachebuster', 16);
609
+
610
+        $this->assertSame('', $this->template->undo('defaultitem'));
611
+    }
612
+
613
+    public function testGetBackground(): void {
614
+        $this->imageManager
615
+            ->expects($this->once())
616
+            ->method('getImageUrl')
617
+            ->with('background')
618
+            ->willReturn('custom-background?v=0');
619
+        $this->assertEquals('custom-background?v=0', $this->template->getBackground());
620
+    }
621
+
622
+    private function getLogoHelper($withName, $useSvg) {
623
+        $this->imageManager->expects($this->any())
624
+            ->method('getImage')
625
+            ->with('logo')
626
+            ->willThrowException(new NotFoundException());
627
+        $this->appConfig
628
+            ->expects($this->once())
629
+            ->method('getAppValueString')
630
+            ->with('logoMime', '')
631
+            ->willReturn('');
632
+        $this->appConfig
633
+            ->expects($this->once())
634
+            ->method('getAppValueInt')
635
+            ->with('cachebuster')
636
+            ->willReturn(0);
637
+        $this->urlGenerator->expects($this->once())
638
+            ->method('imagePath')
639
+            ->with('core', $withName)
640
+            ->willReturn('core-logo');
641
+        $this->assertEquals('core-logo?v=0', $this->template->getLogo($useSvg));
642
+    }
643
+
644
+    public function testGetLogoDefaultWithSvg(): void {
645
+        $this->getLogoHelper('logo/logo.svg', true);
646
+    }
647
+
648
+    public function testGetLogoDefaultWithoutSvg(): void {
649
+        $this->getLogoHelper('logo/logo.png', false);
650
+    }
651
+
652
+    public function testGetLogoCustom(): void {
653
+        $this->appConfig
654
+            ->expects($this->once())
655
+            ->method('getAppValueString')
656
+            ->with('logoMime', '')
657
+            ->willReturn('image/svg+xml');
658
+        $this->appConfig
659
+            ->expects($this->once())
660
+            ->method('getAppValueInt')
661
+            ->with('cachebuster')
662
+            ->willReturn(0);
663
+        $this->urlGenerator->expects($this->once())
664
+            ->method('linkToRoute')
665
+            ->with('theming.Theming.getImage')
666
+            ->willReturn('custom-logo?v=0');
667
+        $this->assertEquals('custom-logo' . '?v=0', $this->template->getLogo());
668
+    }
669
+
670
+    public function testGetScssVariablesCached(): void {
671
+        $this->appConfig->expects($this->any())
672
+            ->method('getAppValueInt')
673
+            ->with('cachebuster')
674
+            ->willReturn(1);
675
+        $this->cacheFactory->expects($this->once())
676
+            ->method('createDistributed')
677
+            ->with('theming-1-')
678
+            ->willReturn($this->cache);
679
+        $this->cache->expects($this->once())->method('get')->with('getScssVariables')->willReturn(['foo' => 'bar']);
680
+        $this->assertEquals(['foo' => 'bar'], $this->template->getScssVariables());
681
+    }
682
+
683
+    public function testGetScssVariables(): void {
684
+        $this->appConfig->expects($this->any())
685
+            ->method('getAppValueInt')
686
+            ->with('cachebuster')
687
+            ->willReturn(0);
688
+        $this->appConfig
689
+            ->expects($this->any())
690
+            ->method('getAppValueString')
691
+            ->willReturnMap([
692
+                ['imprintUrl', '', ''],
693
+                ['privacyUrl', '', ''],
694
+                ['logoMime', '', 'jpeg'],
695
+                ['backgroundMime', '', 'jpeg'],
696
+                ['logoheaderMime', '', 'jpeg'],
697
+                ['faviconMime', '', 'jpeg'],
698
+                ['primary_color', '', false, $this->defaults->getColorPrimary()],
699
+                ['primary_color', $this->defaults->getColorPrimary(), false, $this->defaults->getColorPrimary()],
700
+            ]);
701
+
702
+        $this->util->expects($this->any())->method('invertTextColor')->with($this->defaults->getColorPrimary())->willReturn(false);
703
+        $this->util->expects($this->any())->method('elementColor')->with($this->defaults->getColorPrimary())->willReturn('#aaaaaa');
704
+        $this->cacheFactory->expects($this->once())
705
+            ->method('createDistributed')
706
+            ->with('theming-0-')
707
+            ->willReturn($this->cache);
708
+        $this->cache->expects($this->once())->method('get')->with('getScssVariables')->willReturn(null);
709
+        $this->imageManager->expects($this->exactly(4))
710
+            ->method('getImageUrl')
711
+            ->willReturnMap([
712
+                ['logo', 'custom-logo?v=0'],
713
+                ['logoheader', 'custom-logoheader?v=0'],
714
+                ['favicon', 'custom-favicon?v=0'],
715
+                ['background', 'custom-background?v=0'],
716
+            ]);
717
+
718
+        $expected = [
719
+            'theming-cachebuster' => '\'0\'',
720
+            'theming-logo-mime' => '\'jpeg\'',
721
+            'theming-background-mime' => '\'jpeg\'',
722
+            'image-logo' => "url('custom-logo?v=0')",
723
+            'image-login-background' => "url('custom-background?v=0')",
724
+            'color-primary' => $this->defaults->getColorPrimary(),
725
+            'color-primary-text' => '#ffffff',
726
+            'image-login-plain' => 'false',
727
+            'color-primary-element' => '#aaaaaa',
728
+            'theming-logoheader-mime' => '\'jpeg\'',
729
+            'theming-favicon-mime' => '\'jpeg\'',
730
+            'image-logoheader' => "url('custom-logoheader?v=0')",
731
+            'image-favicon' => "url('custom-favicon?v=0')",
732
+            'has-legal-links' => 'false',
733
+        ];
734
+        $this->assertEquals($expected, $this->template->getScssVariables());
735
+    }
736
+
737
+    public function testGetDefaultAndroidURL(): void {
738
+        $this->appConfig
739
+            ->expects($this->once())
740
+            ->method('getAppValueString')
741
+            ->with('AndroidClientUrl', 'https://play.google.com/store/apps/details?id=com.nextcloud.client')
742
+            ->willReturn('https://play.google.com/store/apps/details?id=com.nextcloud.client');
743
+
744
+        $this->assertEquals('https://play.google.com/store/apps/details?id=com.nextcloud.client', $this->template->getAndroidClientUrl());
745
+    }
746
+
747
+    public function testGetCustomAndroidURL(): void {
748
+        $this->appConfig
749
+            ->expects($this->once())
750
+            ->method('getAppValueString')
751
+            ->with('AndroidClientUrl', 'https://play.google.com/store/apps/details?id=com.nextcloud.client')
752
+            ->willReturn('https://play.google.com/store/apps/details?id=com.mycloud.client');
753
+
754
+        $this->assertEquals('https://play.google.com/store/apps/details?id=com.mycloud.client', $this->template->getAndroidClientUrl());
755
+    }
756
+
757
+    public function testGetDefaultiOSURL(): void {
758
+        $this->appConfig
759
+            ->expects($this->once())
760
+            ->method('getAppValueString')
761
+            ->with('iOSClientUrl', 'https://geo.itunes.apple.com/us/app/nextcloud/id1125420102?mt=8')
762
+            ->willReturn('https://geo.itunes.apple.com/us/app/nextcloud/id1125420102?mt=8');
763
+
764
+        $this->assertEquals('https://geo.itunes.apple.com/us/app/nextcloud/id1125420102?mt=8', $this->template->getiOSClientUrl());
765
+    }
766
+
767
+    public function testGetCustomiOSURL(): void {
768
+        $this->appConfig
769
+            ->expects($this->once())
770
+            ->method('getAppValueString')
771
+            ->with('iOSClientUrl', 'https://geo.itunes.apple.com/us/app/nextcloud/id1125420102?mt=8')
772
+            ->willReturn('https://geo.itunes.apple.com/us/app/nextcloud/id1234567890?mt=8');
773
+
774
+        $this->assertEquals('https://geo.itunes.apple.com/us/app/nextcloud/id1234567890?mt=8', $this->template->getiOSClientUrl());
775
+    }
776
+
777
+    public function testGetDefaultiTunesAppId(): void {
778
+        $this->appConfig
779
+            ->expects($this->once())
780
+            ->method('getAppValueString')
781
+            ->with('iTunesAppId', '1125420102')
782
+            ->willReturn('1125420102');
783
+
784
+        $this->assertEquals('1125420102', $this->template->getiTunesAppId());
785
+    }
786
+
787
+    public function testGetCustomiTunesAppId(): void {
788
+        $this->appConfig
789
+            ->expects($this->once())
790
+            ->method('getAppValueString')
791
+            ->with('iTunesAppId', '1125420102')
792
+            ->willReturn('1234567890');
793
+
794
+        $this->assertEquals('1234567890', $this->template->getiTunesAppId());
795
+    }
796
+
797
+    public static function dataReplaceImagePath(): array {
798
+        return [
799
+            ['core', 'test.png', false],
800
+            ['core', 'manifest.json'],
801
+            ['core', 'favicon.ico'],
802
+            ['core', 'favicon-touch.png'],
803
+        ];
804
+    }
805
+
806
+    #[\PHPUnit\Framework\Attributes\DataProvider('dataReplaceImagePath')]
807
+    public function testReplaceImagePath(string $app, string $image, string|bool $result = 'themingRoute?v=1234abcd'): void {
808
+        $this->cache->expects($this->any())
809
+            ->method('get')
810
+            ->with('shouldReplaceIcons')
811
+            ->willReturn(true);
812
+        $this->appConfig
813
+            ->expects($this->any())
814
+            ->method('getAppValueInt')
815
+            ->with('cachebuster')
816
+            ->willReturn(0);
817
+        $this->urlGenerator
818
+            ->expects($this->any())
819
+            ->method('linkToRoute')
820
+            ->willReturn('themingRoute');
821
+        if ($result) {
822
+            $this->util
823
+                ->expects($this->once())
824
+                ->method('getCacheBuster')
825
+                ->willReturn('1234abcd');
826
+        }
827
+        $this->assertEquals($result, $this->template->replaceImagePath($app, $image));
828
+    }
829
+
830
+    public static function setTypesProvider(): array {
831
+        return [
832
+            [ConfigLexicon::BASE_URL, 'example.com', 'example.com'],
833
+            [ConfigLexicon::USER_THEMING_DISABLED, 'no', false],
834
+            [ConfigLexicon::USER_THEMING_DISABLED, 'true', true],
835
+        ];
836
+    }
837
+
838
+    #[\PHPUnit\Framework\Attributes\DataProvider('setTypesProvider')]
839
+    public function testSetTypes(string $setting, string $value, mixed $expected): void {
840
+        $setValue = null;
841
+        $cb = function ($setting, $value) use (&$setValue) {
842
+            if ($setting !== ConfigLexicon::CACHE_BUSTER) {
843
+                $setValue = $value;
844
+            }
845
+            return true;
846
+        };
847
+        $this->appConfig
848
+            ->method('setAppValueBool')
849
+            ->willReturnCallback($cb);
850
+        $this->appConfig
851
+            ->method('setAppValueString')
852
+            ->willReturnCallback($cb);
853
+        $this->appConfig
854
+            ->method('setAppValueInt')
855
+            ->willReturnCallback($cb);
856
+
857
+        $this->template->set($setting, $value);
858
+        $this->assertEquals($expected, $setValue);
859
+    }
860 860
 }
Please login to merge, or discard this patch.
Spacing   +5 added lines, -5 removed lines patch added patch discarded remove patch
@@ -339,8 +339,8 @@  discard block
 block discarded – undo
339 339
 
340 340
 	public static function invalidLegalUrlProvider(): array {
341 341
 		return [
342
-			['example.com/legal'],  # missing scheme
343
-			['https:///legal'],     # missing host
342
+			['example.com/legal'], # missing scheme
343
+			['https:///legal'], # missing host
344 344
 		];
345 345
 	}
346 346
 
@@ -664,7 +664,7 @@  discard block
 block discarded – undo
664 664
 			->method('linkToRoute')
665 665
 			->with('theming.Theming.getImage')
666 666
 			->willReturn('custom-logo?v=0');
667
-		$this->assertEquals('custom-logo' . '?v=0', $this->template->getLogo());
667
+		$this->assertEquals('custom-logo'.'?v=0', $this->template->getLogo());
668 668
 	}
669 669
 
670 670
 	public function testGetScssVariablesCached(): void {
@@ -804,7 +804,7 @@  discard block
 block discarded – undo
804 804
 	}
805 805
 
806 806
 	#[\PHPUnit\Framework\Attributes\DataProvider('dataReplaceImagePath')]
807
-	public function testReplaceImagePath(string $app, string $image, string|bool $result = 'themingRoute?v=1234abcd'): void {
807
+	public function testReplaceImagePath(string $app, string $image, string | bool $result = 'themingRoute?v=1234abcd'): void {
808 808
 		$this->cache->expects($this->any())
809 809
 			->method('get')
810 810
 			->with('shouldReplaceIcons')
@@ -838,7 +838,7 @@  discard block
 block discarded – undo
838 838
 	#[\PHPUnit\Framework\Attributes\DataProvider('setTypesProvider')]
839 839
 	public function testSetTypes(string $setting, string $value, mixed $expected): void {
840 840
 		$setValue = null;
841
-		$cb = function ($setting, $value) use (&$setValue) {
841
+		$cb = function($setting, $value) use (&$setValue) {
842 842
 			if ($setting !== ConfigLexicon::CACHE_BUSTER) {
843 843
 				$setValue = $value;
844 844
 			}
Please login to merge, or discard this patch.