Passed
Push — master ( a884f3...785682 )
by
unknown
14:57 queued 14s
created

ThemingDefaults::getColorPrimary()   A

Complexity

Conditions 5
Paths 6

Size

Total Lines 26
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 11
nc 6
nop 0
dl 0
loc 26
rs 9.6111
c 0
b 0
f 0
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016 Bjoern Schiessle <[email protected]>
4
 * @copyright Copyright (c) 2017 Lukas Reschke <[email protected]>
5
 *
6
 * @author Arthur Schiwon <[email protected]>
7
 * @author Bjoern Schiessle <[email protected]>
8
 * @author Christoph Wurst <[email protected]>
9
 * @author Daniel Kesselberg <[email protected]>
10
 * @author Guillaume COMPAGNON <[email protected]>
11
 * @author Jan-Christoph Borchardt <[email protected]>
12
 * @author Joachim Bauch <[email protected]>
13
 * @author Joas Schilling <[email protected]>
14
 * @author John Molakvoæ <[email protected]>
15
 * @author Julien Veyssier <[email protected]>
16
 * @author Julius Haertl <[email protected]>
17
 * @author Julius Härtl <[email protected]>
18
 * @author Lukas Reschke <[email protected]>
19
 * @author Michael Weimann <[email protected]>
20
 * @author Morris Jobke <[email protected]>
21
 * @author Patrik Kernstock <[email protected]>
22
 * @author Robin Appelman <[email protected]>
23
 * @author Roeland Jago Douma <[email protected]>
24
 *
25
 * @license GNU AGPL version 3 or any later version
26
 *
27
 * This program is free software: you can redistribute it and/or modify
28
 * it under the terms of the GNU Affero General Public License as
29
 * published by the Free Software Foundation, either version 3 of the
30
 * License, or (at your option) any later version.
31
 *
32
 * This program is distributed in the hope that it will be useful,
33
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
34
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
35
 * GNU Affero General Public License for more details.
36
 *
37
 * You should have received a copy of the GNU Affero General Public License
38
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
39
 *
40
 */
41
namespace OCA\Theming;
42
43
use OCA\Theming\AppInfo\Application;
44
use OCA\Theming\Service\BackgroundService;
45
use OCP\App\AppPathNotFoundException;
46
use OCP\App\IAppManager;
47
use OCP\Files\NotFoundException;
48
use OCP\Files\SimpleFS\ISimpleFile;
49
use OCP\ICacheFactory;
50
use OCP\IConfig;
51
use OCP\IL10N;
52
use OCP\INavigationManager;
53
use OCP\IURLGenerator;
54
use OCP\IUserSession;
55
56
class ThemingDefaults extends \OC_Defaults {
57
58
	private IConfig $config;
59
	private IL10N $l;
60
	private ImageManager $imageManager;
61
	private IUserSession $userSession;
62
	private IURLGenerator $urlGenerator;
63
	private ICacheFactory $cacheFactory;
64
	private Util $util;
65
	private IAppManager $appManager;
66
	private INavigationManager $navigationManager;
67
68
	private string $name;
69
	private string $title;
70
	private string $entity;
71
	private string $productName;
72
	private string $url;
73
	private string $color;
74
75
	private string $iTunesAppId;
76
	private string $iOSClientUrl;
77
	private string $AndroidClientUrl;
78
	private string $FDroidClientUrl;
79
80
	/**
81
	 * ThemingDefaults constructor.
82
	 *
83
	 * @param IConfig $config
84
	 * @param IL10N $l
85
	 * @param ImageManager $imageManager
86
	 * @param IUserSession $userSession
87
	 * @param IURLGenerator $urlGenerator
88
	 * @param ICacheFactory $cacheFactory
89
	 * @param Util $util
90
	 * @param IAppManager $appManager
91
	 */
92
	public function __construct(IConfig $config,
93
								IL10N $l,
94
								IUserSession $userSession,
95
								IURLGenerator $urlGenerator,
96
								ICacheFactory $cacheFactory,
97
								Util $util,
98
								ImageManager $imageManager,
99
								IAppManager $appManager,
100
								INavigationManager $navigationManager
101
	) {
102
		parent::__construct();
103
		$this->config = $config;
104
		$this->l = $l;
105
		$this->imageManager = $imageManager;
106
		$this->userSession = $userSession;
107
		$this->urlGenerator = $urlGenerator;
108
		$this->cacheFactory = $cacheFactory;
109
		$this->util = $util;
110
		$this->appManager = $appManager;
111
		$this->navigationManager = $navigationManager;
112
113
		$this->name = parent::getName();
114
		$this->title = parent::getTitle();
115
		$this->entity = parent::getEntity();
116
		$this->productName = parent::getProductName();
117
		$this->url = parent::getBaseUrl();
118
		$this->color = parent::getColorPrimary();
119
		$this->iTunesAppId = parent::getiTunesAppId();
120
		$this->iOSClientUrl = parent::getiOSClientUrl();
121
		$this->AndroidClientUrl = parent::getAndroidClientUrl();
122
		$this->FDroidClientUrl = parent::getFDroidClientUrl();
123
	}
124
125
	public function getName() {
126
		return strip_tags($this->config->getAppValue('theming', 'name', $this->name));
127
	}
128
129
	public function getHTMLName() {
130
		return $this->config->getAppValue('theming', 'name', $this->name);
131
	}
132
133
	public function getTitle() {
134
		return strip_tags($this->config->getAppValue('theming', 'name', $this->title));
135
	}
136
137
	public function getEntity() {
138
		return strip_tags($this->config->getAppValue('theming', 'name', $this->entity));
139
	}
140
141
	public function getProductName() {
142
		return strip_tags($this->config->getAppValue('theming', 'productName', $this->productName));
143
	}
144
145
	public function getBaseUrl() {
146
		return $this->config->getAppValue('theming', 'url', $this->url);
147
	}
148
149
	/**
150
	 * We pass a string and sanitizeHTML will return a string too in that case
151
	 * @psalm-suppress InvalidReturnStatement
152
	 * @psalm-suppress InvalidReturnType
153
	 */
154
	public function getSlogan(?string $lang = null) {
155
		return \OCP\Util::sanitizeHTML($this->config->getAppValue('theming', 'slogan', parent::getSlogan($lang)));
156
	}
157
158
	public function getImprintUrl() {
159
		return (string)$this->config->getAppValue('theming', 'imprintUrl', '');
160
	}
161
162
	public function getPrivacyUrl() {
163
		return (string)$this->config->getAppValue('theming', 'privacyUrl', '');
164
	}
165
166
	public function getShortFooter() {
167
		$slogan = $this->getSlogan();
168
		$baseUrl = $this->getBaseUrl();
169
		if ($baseUrl !== '') {
170
			$footer = '<a href="' . $baseUrl . '" target="_blank"' .
171
				' rel="noreferrer noopener" class="entity-name">' . $this->getEntity() . '</a>';
172
		} else {
173
			$footer = '<span class="entity-name">' .$this->getEntity() . '</span>';
174
		}
175
		$footer .= ($slogan !== '' ? ' – ' . $slogan : '');
176
177
		$links = [
178
			[
179
				'text' => $this->l->t('Legal notice'),
180
				'url' => (string)$this->getImprintUrl()
181
			],
182
			[
183
				'text' => $this->l->t('Privacy policy'),
184
				'url' => (string)$this->getPrivacyUrl()
185
			],
186
		];
187
188
		$navigation = $this->navigationManager->getAll(INavigationManager::TYPE_GUEST);
189
		$guestNavigation = array_map(function ($nav) {
190
			return [
191
				'text' => $nav['name'],
192
				'url' => $nav['href']
193
			];
194
		}, $navigation);
195
		$links = array_merge($links, $guestNavigation);
196
197
		$legalLinks = '';
198
		$divider = '';
199
		foreach ($links as $link) {
200
			if ($link['url'] !== ''
201
				&& filter_var($link['url'], FILTER_VALIDATE_URL)
202
			) {
203
				$legalLinks .= $divider . '<a href="' . $link['url'] . '" class="legal" target="_blank"' .
204
					' rel="noreferrer noopener">' . $link['text'] . '</a>';
205
				$divider = ' · ';
206
			}
207
		}
208
		if ($legalLinks !== '') {
209
			$footer .= '<br/>' . $legalLinks;
210
		}
211
212
		return $footer;
213
	}
214
215
	/**
216
	 * Color that is used for the header as well as for mail headers
217
	 */
218
	public function getColorPrimary(): string {
219
		$user = $this->userSession->getUser();
220
221
		// admin-defined primary color
222
		$defaultColor = $this->getDefaultColorPrimary();
223
224
		if ($this->isUserThemingDisabled()) {
225
			return $defaultColor;
226
		}
227
228
		// user-defined primary color
229
		if (!empty($user)) {
230
			$themingBackgroundColor = $this->config->getUserValue($user->getUID(), Application::APP_ID, 'background_color', '');
231
			// If the user selected a specific colour
232
			if (preg_match('/^\#([0-9a-f]{3}|[0-9a-f]{6})$/i', $themingBackgroundColor)) {
233
				return $themingBackgroundColor;
234
			}
235
		}
236
237
		// If the default color is not valid, return the default background one
238
		if (!preg_match('/^\#([0-9a-f]{3}|[0-9a-f]{6})$/i', $defaultColor)) {
239
			return BackgroundService::DEFAULT_COLOR;
240
		}
241
242
		// Finally, return the system global primary color
243
		return $defaultColor;
244
	}
245
246
	/**
247
	 * Return the default color primary
248
	 */
249
	public function getDefaultColorPrimary(): string {
250
		$color = $this->config->getAppValue(Application::APP_ID, 'color', '');
251
		if (!preg_match('/^\#([0-9a-f]{3}|[0-9a-f]{6})$/i', $color)) {
252
			$color = '#0082c9';
253
		}
254
		return $color;
255
	}
256
257
	/**
258
	 * Themed logo url
259
	 *
260
	 * @param bool $useSvg Whether to point to the SVG image or a fallback
261
	 * @return string
262
	 */
263
	public function getLogo($useSvg = true): string {
264
		$logo = $this->config->getAppValue('theming', 'logoMime', '');
265
266
		// short cut to avoid setting up the filesystem just to check if the logo is there
267
		//
268
		// explanation: if an SVG is requested and the app config value for logoMime is set then the logo is there.
269
		// otherwise we need to check it and maybe also generate a PNG from the SVG (that's done in getImage() which
270
		// needs to be called then)
271
		if ($useSvg === true && $logo !== false) {
272
			$logoExists = true;
273
		} else {
274
			try {
275
				$this->imageManager->getImage('logo', $useSvg);
276
				$logoExists = true;
277
			} catch (\Exception $e) {
278
				$logoExists = false;
279
			}
280
		}
281
282
		$cacheBusterCounter = $this->config->getAppValue('theming', 'cachebuster', '0');
283
284
		if (!$logo || !$logoExists) {
285
			if ($useSvg) {
286
				$logo = $this->urlGenerator->imagePath('core', 'logo/logo.svg');
287
			} else {
288
				$logo = $this->urlGenerator->imagePath('core', 'logo/logo.png');
289
			}
290
			return $logo . '?v=' . $cacheBusterCounter;
291
		}
292
293
		return $this->urlGenerator->linkToRoute('theming.Theming.getImage', [ 'key' => 'logo', 'useSvg' => $useSvg, 'v' => $cacheBusterCounter ]);
294
	}
295
296
	/**
297
	 * Themed background image url
298
	 *
299
	 * @return string
300
	 */
301
	public function getBackground(): string {
302
		return $this->imageManager->getImageUrl('background');
303
	}
304
305
	/**
306
	 * @return string
307
	 */
308
	public function getiTunesAppId() {
309
		return $this->config->getAppValue('theming', 'iTunesAppId', $this->iTunesAppId);
310
	}
311
312
	/**
313
	 * @return string
314
	 */
315
	public function getiOSClientUrl() {
316
		return $this->config->getAppValue('theming', 'iOSClientUrl', $this->iOSClientUrl);
317
	}
318
319
	/**
320
	 * @return string
321
	 */
322
	public function getAndroidClientUrl() {
323
		return $this->config->getAppValue('theming', 'AndroidClientUrl', $this->AndroidClientUrl);
324
	}
325
326
	/**
327
	 * @return string
328
	 */
329
	public function getFDroidClientUrl() {
330
		return $this->config->getAppValue('theming', 'FDroidClientUrl', $this->FDroidClientUrl);
331
	}
332
333
	/**
334
	 * @return array scss variables to overwrite
335
	 */
336
	public function getScssVariables() {
337
		$cacheBuster = $this->config->getAppValue('theming', 'cachebuster', '0');
338
		$cache = $this->cacheFactory->createDistributed('theming-' . $cacheBuster . '-' . $this->urlGenerator->getBaseUrl());
339
		if ($value = $cache->get('getScssVariables')) {
340
			return $value;
341
		}
342
343
		$variables = [
344
			'theming-cachebuster' => "'" . $cacheBuster . "'",
345
			'theming-logo-mime' => "'" . $this->config->getAppValue('theming', 'logoMime') . "'",
346
			'theming-background-mime' => "'" . $this->config->getAppValue('theming', 'backgroundMime') . "'",
347
			'theming-logoheader-mime' => "'" . $this->config->getAppValue('theming', 'logoheaderMime') . "'",
348
			'theming-favicon-mime' => "'" . $this->config->getAppValue('theming', 'faviconMime') . "'"
349
		];
350
351
		$variables['image-logo'] = "url('".$this->imageManager->getImageUrl('logo')."')";
352
		$variables['image-logoheader'] = "url('".$this->imageManager->getImageUrl('logoheader')."')";
353
		$variables['image-favicon'] = "url('".$this->imageManager->getImageUrl('favicon')."')";
354
		$variables['image-login-background'] = "url('".$this->imageManager->getImageUrl('background')."')";
355
		$variables['image-login-plain'] = 'false';
356
357
		if ($this->config->getAppValue('theming', 'color', '') !== '') {
358
			$variables['color-primary'] = $this->getColorPrimary();
359
			$variables['color-primary-text'] = $this->getTextColorPrimary();
360
			$variables['color-primary-element'] = $this->util->elementColor($this->getColorPrimary());
361
		}
362
363
		if ($this->config->getAppValue('theming', 'backgroundMime', '') === 'backgroundColor') {
364
			$variables['image-login-plain'] = 'true';
365
		}
366
367
		$variables['has-legal-links'] = 'false';
368
		if ($this->getImprintUrl() !== '' || $this->getPrivacyUrl() !== '') {
369
			$variables['has-legal-links'] = 'true';
370
		}
371
372
		$cache->set('getScssVariables', $variables);
373
		return $variables;
374
	}
375
376
	/**
377
	 * Check if the image should be replaced by the theming app
378
	 * and return the new image location then
379
	 *
380
	 * @param string $app name of the app
381
	 * @param string $image filename of the image
382
	 * @return bool|string false if image should not replaced, otherwise the location of the image
383
	 */
384
	public function replaceImagePath($app, $image) {
385
		if ($app === '' || $app === 'files_sharing') {
386
			$app = 'core';
387
		}
388
		$cacheBusterValue = $this->config->getAppValue('theming', 'cachebuster', '0');
0 ignored issues
show
Unused Code introduced by
The assignment to $cacheBusterValue is dead and can be removed.
Loading history...
389
390
		$route = false;
391
		if ($image === 'favicon.ico' && ($this->imageManager->shouldReplaceIcons() || $this->getCustomFavicon() !== null)) {
392
			$route = $this->urlGenerator->linkToRoute('theming.Icon.getFavicon', ['app' => $app]);
393
		}
394
		if (($image === 'favicon-touch.png' || $image === 'favicon-fb.png') && ($this->imageManager->shouldReplaceIcons() || $this->getCustomFavicon() !== null)) {
395
			$route = $this->urlGenerator->linkToRoute('theming.Icon.getTouchIcon', ['app' => $app]);
396
		}
397
		if ($image === 'manifest.json') {
398
			try {
399
				$appPath = $this->appManager->getAppPath($app);
400
				if (file_exists($appPath . '/img/manifest.json')) {
401
					return false;
402
				}
403
			} catch (AppPathNotFoundException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
404
			}
405
			$route = $this->urlGenerator->linkToRoute('theming.Theming.getManifest', ['app' => $app ]);
406
		}
407
		if (strpos($image, 'filetypes/') === 0 && file_exists(\OC::$SERVERROOT . '/core/img/' . $image)) {
408
			$route = $this->urlGenerator->linkToRoute('theming.Icon.getThemedIcon', ['app' => $app, 'image' => $image]);
409
		}
410
411
		if ($route) {
412
			return $route . '?v=' . $this->util->getCacheBuster();
413
		}
414
415
		return false;
416
	}
417
418
	protected function getCustomFavicon(): ?ISimpleFile {
419
		try {
420
			return $this->imageManager->getImage('favicon');
421
		} catch (NotFoundException $e) {
422
			return null;
423
		}
424
	}
425
426
	/**
427
	 * Increases the cache buster key
428
	 */
429
	public function increaseCacheBuster(): void {
430
		$cacheBusterKey = (int)$this->config->getAppValue('theming', 'cachebuster', '0');
431
		$this->config->setAppValue('theming', 'cachebuster', (string)($cacheBusterKey + 1));
432
		$this->cacheFactory->createDistributed('theming-')->clear();
433
		$this->cacheFactory->createDistributed('imagePath')->clear();
434
	}
435
436
	/**
437
	 * Update setting in the database
438
	 *
439
	 * @param string $setting
440
	 * @param string $value
441
	 */
442
	public function set($setting, $value) {
443
		$this->config->setAppValue('theming', $setting, $value);
444
		$this->increaseCacheBuster();
445
	}
446
447
	/**
448
	 * Revert settings to the default value
449
	 *
450
	 * @param string $setting setting which should be reverted
451
	 * @return string default value
452
	 */
453
	public function undo($setting) {
454
		$this->config->deleteAppValue('theming', $setting);
455
		$this->increaseCacheBuster();
456
457
		$returnValue = '';
458
		switch ($setting) {
459
			case 'name':
460
				$returnValue = $this->getEntity();
461
				break;
462
			case 'url':
463
				$returnValue = $this->getBaseUrl();
464
				break;
465
			case 'slogan':
466
				$returnValue = $this->getSlogan();
467
				break;
468
			case 'color':
469
				$returnValue = $this->getDefaultColorPrimary();
470
				break;
471
			case 'logo':
472
			case 'logoheader':
473
			case 'background':
474
			case 'favicon':
475
				$this->imageManager->delete($setting);
476
				break;
477
		}
478
479
		return $returnValue;
480
	}
481
482
	/**
483
	 * Color of text in the header and primary buttons
484
	 *
485
	 * @return string
486
	 */
487
	public function getTextColorPrimary() {
488
		return $this->util->invertTextColor($this->getColorPrimary()) ? '#000000' : '#ffffff';
489
	}
490
491
	/**
492
	 * Has the admin disabled user customization
493
	 */
494
	public function isUserThemingDisabled(): bool {
495
		return $this->config->getAppValue('theming', 'disable-user-theming', 'no') === 'yes';
496
	}
497
}
498