Passed
Push — master ( a1ed1d...263a69 )
by John
16:05 queued 13s
created

Util::isBrightColor()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 1
dl 0
loc 6
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016 Julius Härtl <[email protected]>
4
 *
5
 * @author Christoph Wurst <[email protected]>
6
 * @author Daniel Kesselberg <[email protected]>
7
 * @author Joas Schilling <[email protected]>
8
 * @author Julien Veyssier <[email protected]>
9
 * @author Julius Haertl <[email protected]>
10
 * @author Julius Härtl <[email protected]>
11
 * @author Michael Weimann <[email protected]>
12
 *
13
 * @license GNU AGPL version 3 or any later version
14
 *
15
 * This program is free software: you can redistribute it and/or modify
16
 * it under the terms of the GNU Affero General Public License as
17
 * published by the Free Software Foundation, either version 3 of the
18
 * License, or (at your option) any later version.
19
 *
20
 * This program is distributed in the hope that it will be useful,
21
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23
 * GNU Affero General Public License for more details.
24
 *
25
 * You should have received a copy of the GNU Affero General Public License
26
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
27
 *
28
 */
29
namespace OCA\Theming;
30
31
use OCP\App\AppPathNotFoundException;
32
use OCP\App\IAppManager;
33
use OCP\Files\IAppData;
34
use OCP\Files\NotFoundException;
35
use OCP\Files\SimpleFS\ISimpleFile;
36
use OCP\IConfig;
37
use OCP\IUserSession;
38
use Mexitek\PHPColors\Color;
39
40
class Util {
41
42
	private IConfig $config;
43
	private IAppManager $appManager;
44
	private IAppData $appData;
45
	private ImageManager $imageManager;
46
47
	public function __construct(IConfig $config, IAppManager $appManager, IAppData $appData, ImageManager $imageManager) {
48
		$this->config = $config;
49
		$this->appManager = $appManager;
50
		$this->appData = $appData;
51
		$this->imageManager = $imageManager;
52
	}
53
54
	/**
55
	 * Should we invert the text on this background color?
56
	 * @param string $color rgb color value
57
	 * @return bool
58
	 */
59
	public function invertTextColor(string $color): bool {
60
		return $this->isBrightColor($color);
61
	}
62
63
	/**
64
	 * Is this color too bright ?
65
	 * @param string $color rgb color value
66
	 * @return bool
67
	 */
68
	public function isBrightColor(string $color): bool {
69
		$l = $this->calculateLuma($color);
70
		if ($l > 0.6) {
71
			return true;
72
		} else {
73
			return false;
74
		}
75
	}
76
77
	/**
78
	 * get color for on-page elements:
79
	 * theme color by default, grey if theme color is to bright
80
	 * @param string $color
81
	 * @param bool $brightBackground
82
	 * @return string
83
	 */
84
	public function elementColor($color, bool $brightBackground = true) {
85
		$luminance = $this->calculateLuminance($color);
86
87
		if ($brightBackground && $luminance > 0.8) {
88
			// If the color is too bright in bright mode, we fall back to a darker gray
89
			return '#aaaaaa';
90
		}
91
92
		if (!$brightBackground && $luminance < 0.2) {
93
			// If the color is too dark in dark mode, we fall back to a brighter gray
94
			return '#8c8c8c';
95
		}
96
97
		return $color;
98
	}
99
100
	public function mix(string $color1, string $color2, int $factor): string {
101
		$color = new Color($color1);
102
		return '#' . $color->mix($color2, $factor);
103
	}
104
105
	public function lighten(string $color, int $factor): string {
106
		$color = new Color($color);
107
		return '#' . $color->lighten($factor);
108
	}
109
110
	public function darken(string $color, int $factor): string {
111
		$color = new Color($color);
112
		return '#' . $color->darken($factor);
113
	}
114
115
	/**
116
	 * Convert RGB to HSL
117
	 *
118
	 * Copied from cssphp, copyright Leaf Corcoran, licensed under MIT
119
	 *
120
	 * @param int $red
121
	 * @param int $green
122
	 * @param int $blue
123
	 *
124
	 * @return float[]
125
	 */
126
	public function toHSL(int $red, int $green, int $blue): array {
127
		$color = new Color(Color::rgbToHex(['R' => $red, 'G' => $green, 'B' => $blue]));
128
		return array_values($color->getHsl());
129
	}
130
131
	/**
132
	 * @param string $color rgb color value
133
	 * @return float
134
	 */
135
	public function calculateLuminance(string $color): float {
136
		[$red, $green, $blue] = $this->hexToRGB($color);
137
		$hsl = $this->toHSL($red, $green, $blue);
138
		return $hsl[2];
139
	}
140
141
	/**
142
	 * @param string $color rgb color value
143
	 * @return float
144
	 */
145
	public function calculateLuma(string $color): float {
146
		[$red, $green, $blue] = $this->hexToRGB($color);
147
		return (0.2126 * $red + 0.7152 * $green + 0.0722 * $blue) / 255;
148
	}
149
150
	/**
151
	 * @param string $color rgb color value
152
	 * @return int[]
153
	 * @psalm-return array{0: int, 1: int, 2: int}
154
	 */
155
	public function hexToRGB(string $color): array {
156
		$color = new Color($color);
157
		return array_values($color->getRgb());
158
	}
159
160
	/**
161
	 * @param $color
162
	 * @return string base64 encoded radio button svg
163
	 */
164
	public function generateRadioButton($color) {
165
		$radioButtonIcon = '<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16">' .
166
			'<path d="M8 1a7 7 0 0 0-7 7 7 7 0 0 0 7 7 7 7 0 0 0 7-7 7 7 0 0 0-7-7zm0 1a6 6 0 0 1 6 6 6 6 0 0 1-6 6 6 6 0 0 1-6-6 6 6 0 0 1 6-6zm0 2a4 4 0 1 0 0 8 4 4 0 0 0 0-8z" fill="'.$color.'"/></svg>';
167
		return base64_encode($radioButtonIcon);
168
	}
169
170
171
	/**
172
	 * @param $app string app name
173
	 * @return string|ISimpleFile path to app icon / file of logo
174
	 */
175
	public function getAppIcon($app) {
176
		$app = str_replace(['\0', '/', '\\', '..'], '', $app);
177
		try {
178
			$appPath = $this->appManager->getAppPath($app);
179
			$icon = $appPath . '/img/' . $app . '.svg';
180
			if (file_exists($icon)) {
181
				return $icon;
182
			}
183
			$icon = $appPath . '/img/app.svg';
184
			if (file_exists($icon)) {
185
				return $icon;
186
			}
187
		} catch (AppPathNotFoundException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
188
		}
189
190
		if ($this->config->getAppValue('theming', 'logoMime', '') !== '') {
191
			$logoFile = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $logoFile is dead and can be removed.
Loading history...
192
			try {
193
				$folder = $this->appData->getFolder('images');
194
				return $folder->getFile('logo');
195
			} catch (NotFoundException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
196
			}
197
		}
198
		return \OC::$SERVERROOT . '/core/img/logo/logo.svg';
199
	}
200
201
	/**
202
	 * @param $app string app name
203
	 * @param $image string relative path to image in app folder
204
	 * @return string|false absolute path to image
205
	 */
206
	public function getAppImage($app, $image) {
207
		$app = str_replace(['\0', '/', '\\', '..'], '', $app);
208
		$image = str_replace(['\0', '\\', '..'], '', $image);
209
		if ($app === "core") {
210
			$icon = \OC::$SERVERROOT . '/core/img/' . $image;
211
			if (file_exists($icon)) {
212
				return $icon;
213
			}
214
		}
215
216
		try {
217
			$appPath = $this->appManager->getAppPath($app);
218
		} catch (AppPathNotFoundException $e) {
219
			return false;
220
		}
221
222
		$icon = $appPath . '/img/' . $image;
223
		if (file_exists($icon)) {
224
			return $icon;
225
		}
226
		$icon = $appPath . '/img/' . $image . '.svg';
227
		if (file_exists($icon)) {
228
			return $icon;
229
		}
230
		$icon = $appPath . '/img/' . $image . '.png';
231
		if (file_exists($icon)) {
232
			return $icon;
233
		}
234
		$icon = $appPath . '/img/' . $image . '.gif';
235
		if (file_exists($icon)) {
236
			return $icon;
237
		}
238
		$icon = $appPath . '/img/' . $image . '.jpg';
239
		if (file_exists($icon)) {
240
			return $icon;
241
		}
242
243
		return false;
244
	}
245
246
	/**
247
	 * replace default color with a custom one
248
	 *
249
	 * @param $svg string content of a svg file
250
	 * @param $color string color to match
251
	 * @return string
252
	 */
253
	public function colorizeSvg($svg, $color) {
254
		$svg = preg_replace('/#0082c9/i', $color, $svg);
255
		return $svg;
256
	}
257
258
	/**
259
	 * Check if a custom theme is set in the server configuration
260
	 *
261
	 * @return bool
262
	 */
263
	public function isAlreadyThemed() {
264
		$theme = $this->config->getSystemValue('theme', '');
265
		if ($theme !== '') {
266
			return true;
267
		}
268
		return false;
269
	}
270
271
	public function isBackgroundThemed() {
272
		$backgroundLogo = $this->config->getAppValue('theming', 'backgroundMime', '');
273
		return $backgroundLogo !== '' && $backgroundLogo !== 'backgroundColor';
274
	}
275
276
	public function isLogoThemed() {
277
		return $this->imageManager->hasImage('logo')
278
			|| $this->imageManager->hasImage('logoheader');
279
	}
280
281
	public function getCacheBuster(): string {
282
		$userSession = \OC::$server->get(IUserSession::class);
283
		$userId = '';
284
		$user = $userSession->getUser();
285
		if (!is_null($user)) {
286
			$userId = $user->getUID();
287
		}
288
		$userCacheBuster = '';
289
		if ($userId) {
290
			$userCacheBusterValue = (int)$this->config->getUserValue($userId, 'theming', 'userCacheBuster', '0');
291
			$userCacheBuster = $userId . '_' . $userCacheBusterValue;
292
		}
293
		$systemCacheBuster = $this->config->getAppValue('theming', 'cachebuster', '0');
294
		return substr(sha1($userCacheBuster . $systemCacheBuster), 0, 8);
295
	}
296
}
297