Passed
Push — master ( e1cb1b...9a76f0 )
by John
15:33 queued 17s
created

ThemingController::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 24
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 10
c 0
b 0
f 0
nc 1
nop 11
dl 0
loc 24
rs 9.9332

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
/**
3
 * @copyright Copyright (c) 2016 Bjoern Schiessle <[email protected]>
4
 * @copyright Copyright (c) 2016 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 Calviño Sánchez <[email protected]>
10
 * @author Jan-Christoph Borchardt <[email protected]>
11
 * @author Joas Schilling <[email protected]>
12
 * @author Julius Haertl <[email protected]>
13
 * @author Julius Härtl <[email protected]>
14
 * @author Kyle Fazzari <[email protected]>
15
 * @author Lukas Reschke <[email protected]>
16
 * @author nhirokinet <[email protected]>
17
 * @author rakekniven <[email protected]>
18
 * @author Robin Appelman <[email protected]>
19
 * @author Roeland Jago Douma <[email protected]>
20
 * @author Thomas Citharel <[email protected]>
21
 *
22
 * @license GNU AGPL version 3 or any later version
23
 *
24
 * This program is free software: you can redistribute it and/or modify
25
 * it under the terms of the GNU Affero General Public License as
26
 * published by the Free Software Foundation, either version 3 of the
27
 * License, or (at your option) any later version.
28
 *
29
 * This program is distributed in the hope that it will be useful,
30
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
31
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32
 * GNU Affero General Public License for more details.
33
 *
34
 * You should have received a copy of the GNU Affero General Public License
35
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
36
 *
37
 */
38
namespace OCA\Theming\Controller;
39
40
use OCA\Theming\ImageManager;
41
use OCA\Theming\Service\ThemesService;
42
use OCA\Theming\ThemingDefaults;
43
use OCP\App\IAppManager;
44
use OCP\AppFramework\Controller;
45
use OCP\AppFramework\Http;
46
use OCP\AppFramework\Http\DataDisplayResponse;
47
use OCP\AppFramework\Http\DataResponse;
48
use OCP\AppFramework\Http\FileDisplayResponse;
49
use OCP\AppFramework\Http\NotFoundResponse;
50
use OCP\Files\IAppData;
51
use OCP\Files\NotFoundException;
52
use OCP\Files\NotPermittedException;
53
use OCP\IConfig;
54
use OCP\IL10N;
55
use OCP\IRequest;
56
use OCP\ITempManager;
57
use OCP\IURLGenerator;
58
use ScssPhp\ScssPhp\Compiler;
59
60
/**
61
 * Class ThemingController
62
 *
63
 * handle ajax requests to update the theme
64
 *
65
 * @package OCA\Theming\Controller
66
 */
67
class ThemingController extends Controller {
68
	private ThemingDefaults $themingDefaults;
69
	private IL10N $l10n;
70
	private IConfig $config;
71
	private ITempManager $tempManager;
72
	private IAppData $appData;
73
	private IURLGenerator $urlGenerator;
74
	private IAppManager $appManager;
75
	private ImageManager $imageManager;
76
	private ThemesService $themesService;
77
78
	public function __construct(
79
		$appName,
80
		IRequest $request,
81
		IConfig $config,
82
		ThemingDefaults $themingDefaults,
83
		IL10N $l,
84
		ITempManager $tempManager,
85
		IAppData $appData,
86
		IURLGenerator $urlGenerator,
87
		IAppManager $appManager,
88
		ImageManager $imageManager,
89
		ThemesService $themesService
90
	) {
91
		parent::__construct($appName, $request);
92
93
		$this->themingDefaults = $themingDefaults;
94
		$this->l10n = $l;
95
		$this->config = $config;
96
		$this->tempManager = $tempManager;
97
		$this->appData = $appData;
98
		$this->urlGenerator = $urlGenerator;
99
		$this->appManager = $appManager;
100
		$this->imageManager = $imageManager;
101
		$this->themesService = $themesService;
102
	}
103
104
	/**
105
	 * @AuthorizedAdminSetting(settings=OCA\Theming\Settings\Admin)
106
	 * @param string $setting
107
	 * @param string $value
108
	 * @return DataResponse
109
	 * @throws NotPermittedException
110
	 */
111
	public function updateStylesheet($setting, $value) {
112
		$value = trim($value);
113
		$error = null;
114
		switch ($setting) {
115
			case 'name':
116
				if (strlen($value) > 250) {
117
					$error = $this->l10n->t('The given name is too long');
118
				}
119
				break;
120
			case 'url':
121
				if (strlen($value) > 500) {
122
					$error = $this->l10n->t('The given web address is too long');
123
				}
124
				if (!$this->isValidUrl($value)) {
125
					$error = $this->l10n->t('The given web address is not a valid URL');
126
				}
127
				break;
128
			case 'imprintUrl':
129
				if (strlen($value) > 500) {
130
					$error = $this->l10n->t('The given legal notice address is too long');
131
				}
132
				if (!$this->isValidUrl($value)) {
133
					$error = $this->l10n->t('The given legal notice address is not a valid URL');
134
				}
135
				break;
136
			case 'privacyUrl':
137
				if (strlen($value) > 500) {
138
					$error = $this->l10n->t('The given privacy policy address is too long');
139
				}
140
				if (!$this->isValidUrl($value)) {
141
					$error = $this->l10n->t('The given privacy policy address is not a valid URL');
142
				}
143
				break;
144
			case 'slogan':
145
				if (strlen($value) > 500) {
146
					$error = $this->l10n->t('The given slogan is too long');
147
				}
148
				break;
149
			case 'color':
150
				if (!preg_match('/^\#([0-9a-f]{3}|[0-9a-f]{6})$/i', $value)) {
151
					$error = $this->l10n->t('The given color is invalid');
152
				}
153
				break;
154
		}
155
		if ($error !== null) {
156
			return new DataResponse([
157
				'data' => [
158
					'message' => $error,
159
				],
160
				'status' => 'error'
161
			], Http::STATUS_BAD_REQUEST);
162
		}
163
164
		$this->themingDefaults->set($setting, $value);
165
166
		return new DataResponse([
167
			'data' => [
168
				'message' => $this->l10n->t('Saved'),
169
			],
170
			'status' => 'success'
171
		]);
172
	}
173
174
	/**
175
	 * Check that a string is a valid http/https url
176
	 */
177
	private function isValidUrl(string $url): bool {
178
		return ((strpos($url, 'http://') === 0 || strpos($url, 'https://') === 0) &&
179
			filter_var($url, FILTER_VALIDATE_URL) !== false);
180
	}
181
182
	/**
183
	 * @AuthorizedAdminSetting(settings=OCA\Theming\Settings\Admin)
184
	 * @return DataResponse
185
	 * @throws NotPermittedException
186
	 */
187
	public function uploadImage(): DataResponse {
188
		$key = $this->request->getParam('key');
189
		$image = $this->request->getUploadedFile('image');
190
		$error = null;
191
		$phpFileUploadErrors = [
192
			UPLOAD_ERR_OK => $this->l10n->t('The file was uploaded'),
193
			UPLOAD_ERR_INI_SIZE => $this->l10n->t('The uploaded file exceeds the upload_max_filesize directive in php.ini'),
194
			UPLOAD_ERR_FORM_SIZE => $this->l10n->t('The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form'),
195
			UPLOAD_ERR_PARTIAL => $this->l10n->t('The file was only partially uploaded'),
196
			UPLOAD_ERR_NO_FILE => $this->l10n->t('No file was uploaded'),
197
			UPLOAD_ERR_NO_TMP_DIR => $this->l10n->t('Missing a temporary folder'),
198
			UPLOAD_ERR_CANT_WRITE => $this->l10n->t('Could not write file to disk'),
199
			UPLOAD_ERR_EXTENSION => $this->l10n->t('A PHP extension stopped the file upload'),
200
		];
201
		if (empty($image)) {
202
			$error = $this->l10n->t('No file uploaded');
203
		}
204
		if (!empty($image) && array_key_exists('error', $image) && $image['error'] !== UPLOAD_ERR_OK) {
205
			$error = $phpFileUploadErrors[$image['error']];
206
		}
207
208
		if ($error !== null) {
209
			return new DataResponse(
210
				[
211
					'data' => [
212
						'message' => $error
213
					],
214
					'status' => 'failure',
215
				],
216
				Http::STATUS_UNPROCESSABLE_ENTITY
217
			);
218
		}
219
220
		try {
221
			$mime = $this->imageManager->updateImage($key, $image['tmp_name']);
222
			$this->themingDefaults->set($key . 'Mime', $mime);
223
		} catch (\Exception $e) {
224
			return new DataResponse(
225
				[
226
					'data' => [
227
						'message' => $e->getMessage()
228
					],
229
					'status' => 'failure',
230
				],
231
				Http::STATUS_UNPROCESSABLE_ENTITY
232
			);
233
		}
234
235
		$name = $image['name'];
236
237
		return new DataResponse(
238
			[
239
				'data' =>
240
					[
241
						'name' => $name,
242
						'url' => $this->imageManager->getImageUrl($key),
243
						'message' => $this->l10n->t('Saved'),
244
					],
245
				'status' => 'success'
246
			]
247
		);
248
	}
249
250
	/**
251
	 * Revert setting to default value
252
	 * @AuthorizedAdminSetting(settings=OCA\Theming\Settings\Admin)
253
	 *
254
	 * @param string $setting setting which should be reverted
255
	 * @return DataResponse
256
	 * @throws NotPermittedException
257
	 */
258
	public function undo(string $setting): DataResponse {
259
		$value = $this->themingDefaults->undo($setting);
260
261
		return new DataResponse(
262
			[
263
				'data' =>
264
					[
265
						'value' => $value,
266
						'message' => $this->l10n->t('Saved'),
267
					],
268
				'status' => 'success'
269
			]
270
		);
271
	}
272
273
	/**
274
	 * @PublicPage
275
	 * @NoCSRFRequired
276
	 * @NoSameSiteCookieRequired
277
	 *
278
	 * @param string $key
279
	 * @param bool $useSvg
280
	 * @return FileDisplayResponse|NotFoundResponse
281
	 * @throws NotPermittedException
282
	 */
283
	public function getImage(string $key, bool $useSvg = true) {
284
		try {
285
			$file = $this->imageManager->getImage($key, $useSvg);
286
		} catch (NotFoundException $e) {
287
			return new NotFoundResponse();
288
		}
289
290
		$response = new FileDisplayResponse($file);
291
		$csp = new Http\ContentSecurityPolicy();
292
		$csp->allowInlineStyle();
293
		$response->setContentSecurityPolicy($csp);
294
		$response->cacheFor(3600);
295
		$response->addHeader('Content-Type', $this->config->getAppValue($this->appName, $key . 'Mime', ''));
296
		$response->addHeader('Content-Disposition', 'attachment; filename="' . $key . '"');
297
		if (!$useSvg) {
298
			$response->addHeader('Content-Type', 'image/png');
299
		} else {
300
			$response->addHeader('Content-Type', $this->config->getAppValue($this->appName, $key . 'Mime', ''));
301
		}
302
		return $response;
303
	}
304
305
	/**
306
	 * @NoCSRFRequired
307
	 * @PublicPage
308
	 * @NoSameSiteCookieRequired
309
	 * @NoTwoFactorRequired
310
	 *
311
	 * @return DataDisplayResponse|NotFoundResponse
312
	 */
313
	public function getThemeStylesheet(string $themeId, bool $plain = false, bool $withCustomCss = false) {
0 ignored issues
show
Unused Code introduced by
The parameter $withCustomCss is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

313
	public function getThemeStylesheet(string $themeId, bool $plain = false, /** @scrutinizer ignore-unused */ bool $withCustomCss = false) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
314
		$themes = $this->themesService->getThemes();
315
		if (!in_array($themeId, array_keys($themes))) {
316
			return new NotFoundResponse();
317
		}
318
319
		$theme = $themes[$themeId];
320
		$customCss  = $theme->getCustomCss();
321
322
		// Generate variables
323
		$variables = '';
324
		foreach ($theme->getCSSVariables() as $variable => $value) {
325
			$variables .= "$variable:$value; ";
326
		};
327
328
		// If plain is set, the browser decides of the css priority
329
		if ($plain) {
330
			$css = ":root { $variables } " . $customCss;
331
		} else { 
332
			// If not set, we'll rely on the body class
333
			$compiler = new Compiler();
334
			$compiledCss = $compiler->compileString("body[data-theme-$themeId] { $variables $customCss }");
335
			$css = $compiledCss->getCss();;
336
		}
337
338
		try {
339
			$response = new DataDisplayResponse($css, Http::STATUS_OK, ['Content-Type' => 'text/css']);
340
			$response->cacheFor(86400);
341
			return $response;
342
		} catch (NotFoundException $e) {
343
			return new NotFoundResponse();
344
		}
345
	}
346
347
	/**
348
	 * @NoCSRFRequired
349
	 * @PublicPage
350
	 *
351
	 * @return Http\JSONResponse
352
	 */
353
	public function getManifest($app) {
354
		$cacheBusterValue = $this->config->getAppValue('theming', 'cachebuster', '0');
355
		if ($app === 'core' || $app === 'settings') {
356
			$name = $this->themingDefaults->getName();
357
			$shortName = $this->themingDefaults->getName();
358
			$startUrl = $this->urlGenerator->getBaseUrl();
359
			$description = $this->themingDefaults->getSlogan();
360
		} else {
361
			$info = $this->appManager->getAppInfo($app, false, $this->l10n->getLanguageCode());
362
			$name = $info['name'] . ' - ' . $this->themingDefaults->getName();
363
			$shortName = $info['name'];
364
			if (strpos($this->request->getRequestUri(), '/index.php/') !== false) {
365
				$startUrl = $this->urlGenerator->getBaseUrl() . '/index.php/apps/' . $app . '/';
366
			} else {
367
				$startUrl = $this->urlGenerator->getBaseUrl() . '/apps/' . $app . '/';
368
			}
369
			$description = $info['summary'] ?? '';
370
		}
371
		$responseJS = [
372
			'name' => $name,
373
			'short_name' => $shortName,
374
			'start_url' => $startUrl,
375
			'theme_color' => $this->themingDefaults->getColorPrimary(),
376
			'background_color' => $this->themingDefaults->getColorPrimary(),
377
			'description' => $description,
378
			'icons' =>
379
				[
380
					[
381
						'src' => $this->urlGenerator->linkToRoute('theming.Icon.getTouchIcon',
382
								['app' => $app]) . '?v=' . $cacheBusterValue,
383
						'type' => 'image/png',
384
						'sizes' => '512x512'
385
					],
386
					[
387
						'src' => $this->urlGenerator->linkToRoute('theming.Icon.getFavicon',
388
								['app' => $app]) . '?v=' . $cacheBusterValue,
389
						'type' => 'image/svg+xml',
390
						'sizes' => '16x16'
391
					]
392
				],
393
			'display' => 'standalone'
394
		];
395
		$response = new Http\JSONResponse($responseJS);
396
		$response->cacheFor(3600);
397
		return $response;
398
	}
399
}
400