Completed
Push — master ( 99edba...50a6a7 )
by Roeland
11:29
created

ThemingController::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 27

Duplication

Lines 27
Ratio 100 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 12
dl 27
loc 27
rs 9.488
c 0
b 0
f 0

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 Daniel Calviño Sánchez <[email protected]>
9
 * @author Jan-Christoph Borchardt <[email protected]>
10
 * @author Joas Schilling <[email protected]>
11
 * @author Julius Haertl <[email protected]>
12
 * @author Julius Härtl <[email protected]>
13
 * @author Lukas Reschke <[email protected]>
14
 * @author Robin Appelman <[email protected]>
15
 *
16
 * @license GNU AGPL version 3 or any later version
17
 *
18
 * This program is free software: you can redistribute it and/or modify
19
 * it under the terms of the GNU Affero General Public License as
20
 * published by the Free Software Foundation, either version 3 of the
21
 * License, or (at your option) any later version.
22
 *
23
 * This program is distributed in the hope that it will be useful,
24
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
26
 * GNU Affero General Public License for more details.
27
 *
28
 * You should have received a copy of the GNU Affero General Public License
29
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
30
 *
31
 */
32
33
namespace OCA\Theming\Controller;
34
35
use OC\Template\SCSSCacher;
36
use OCA\Theming\ImageManager;
37
use OCA\Theming\ThemingDefaults;
38
use OCP\AppFramework\Controller;
39
use OCP\AppFramework\Http;
40
use OCP\AppFramework\Http\DataDownloadResponse;
41
use OCP\AppFramework\Http\FileDisplayResponse;
42
use OCP\AppFramework\Http\DataResponse;
43
use OCP\AppFramework\Http\NotFoundResponse;
44
use OCP\AppFramework\Utility\ITimeFactory;
45
use OCP\Files\File;
46
use OCP\Files\IAppData;
47
use OCP\Files\NotFoundException;
48
use OCP\Files\NotPermittedException;
49
use OCP\IConfig;
50
use OCP\IL10N;
51
use OCP\IRequest;
52
use OCA\Theming\Util;
53
use OCP\ITempManager;
54
use OCP\IURLGenerator;
55
use OCP\App\IAppManager;
56
57
/**
58
 * Class ThemingController
59
 *
60
 * handle ajax requests to update the theme
61
 *
62
 * @package OCA\Theming\Controller
63
 */
64
class ThemingController extends Controller {
65
	/** @var ThemingDefaults */
66
	private $themingDefaults;
67
	/** @var Util */
68
	private $util;
69
	/** @var IL10N */
70
	private $l10n;
71
	/** @var IConfig */
72
	private $config;
73
	/** @var ITempManager */
74
	private $tempManager;
75
	/** @var IAppData */
76
	private $appData;
77
	/** @var SCSSCacher */
78
	private $scssCacher;
79
	/** @var IURLGenerator */
80
	private $urlGenerator;
81
	/** @var IAppManager */
82
	private $appManager;
83
	/** @var ImageManager */
84
	private $imageManager;
85
86
	/**
87
	 * ThemingController constructor.
88
	 *
89
	 * @param string $appName
90
	 * @param IRequest $request
91
	 * @param IConfig $config
92
	 * @param ThemingDefaults $themingDefaults
93
	 * @param Util $util
94
	 * @param IL10N $l
95
	 * @param ITempManager $tempManager
96
	 * @param IAppData $appData
97
	 * @param SCSSCacher $scssCacher
98
	 * @param IURLGenerator $urlGenerator
99
	 * @param IAppManager $appManager
100
	 * @param ImageManager $imageManager
101
	 */
102 View Code Duplication
	public function __construct(
103
		$appName,
104
		IRequest $request,
105
		IConfig $config,
106
		ThemingDefaults $themingDefaults,
107
		Util $util,
108
		IL10N $l,
109
		ITempManager $tempManager,
110
		IAppData $appData,
111
		SCSSCacher $scssCacher,
112
		IURLGenerator $urlGenerator,
113
		IAppManager $appManager,
114
		ImageManager $imageManager
115
	) {
116
		parent::__construct($appName, $request);
117
118
		$this->themingDefaults = $themingDefaults;
119
		$this->util = $util;
120
		$this->l10n = $l;
121
		$this->config = $config;
122
		$this->tempManager = $tempManager;
123
		$this->appData = $appData;
124
		$this->scssCacher = $scssCacher;
125
		$this->urlGenerator = $urlGenerator;
126
		$this->appManager = $appManager;
127
		$this->imageManager = $imageManager;
128
	}
129
130
	/**
131
	 * @param string $setting
132
	 * @param string $value
133
	 * @return DataResponse
134
	 * @throws NotPermittedException
135
	 */
136
	public function updateStylesheet($setting, $value) {
137
		$value = trim($value);
138
		switch ($setting) {
139 View Code Duplication
			case 'name':
140
				if (strlen($value) > 250) {
141
					return new DataResponse([
142
						'data' => [
143
							'message' => $this->l10n->t('The given name is too long'),
144
						],
145
						'status' => 'error'
146
					]);
147
				}
148
				break;
149 View Code Duplication
			case 'url':
150
				if (strlen($value) > 500) {
151
					return new DataResponse([
152
						'data' => [
153
							'message' => $this->l10n->t('The given web address is too long'),
154
						],
155
						'status' => 'error'
156
					]);
157
				}
158
				break;
159 View Code Duplication
			case 'imprintUrl':
160
				if (strlen($value) > 500) {
161
					return new DataResponse([
162
						'data' => [
163
							'message' => $this->l10n->t('The given legal notice address is too long'),
164
						],
165
						'status' => 'error'
166
					]);
167
				}
168
				break;
169 View Code Duplication
			case 'privacyUrl':
170
				if (strlen($value) > 500) {
171
					return new DataResponse([
172
						'data' => [
173
							'message' => $this->l10n->t('The given privacy policy address is too long'),
174
						],
175
						'status' => 'error'
176
					]);
177
				}
178
				break;
179 View Code Duplication
			case 'slogan':
180
				if (strlen($value) > 500) {
181
					return new DataResponse([
182
						'data' => [
183
							'message' => $this->l10n->t('The given slogan is too long'),
184
						],
185
						'status' => 'error'
186
					]);
187
				}
188
				break;
189 View Code Duplication
			case 'color':
190
				if (!preg_match('/^\#([0-9a-f]{3}|[0-9a-f]{6})$/i', $value)) {
191
					return new DataResponse([
192
						'data' => [
193
							'message' => $this->l10n->t('The given color is invalid'),
194
						],
195
						'status' => 'error'
196
					]);
197
				}
198
				break;
199
		}
200
201
		$this->themingDefaults->set($setting, $value);
202
203
		// reprocess server scss for preview
204
		$cssCached = $this->scssCacher->process(\OC::$SERVERROOT, 'core/css/css-variables.scss', 'core');
0 ignored issues
show
Unused Code introduced by
$cssCached is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
205
206
		return new DataResponse(
207
			[
208
				'data' =>
209
					[
210
						'message' => $this->l10n->t('Saved'),
211
						'serverCssUrl' => $this->urlGenerator->linkTo('', $this->scssCacher->getCachedSCSS('core', '/core/css/css-variables.scss'))
212
					],
213
				'status' => 'success'
214
			]
215
		);
216
	}
217
218
	/**
219
	 * @return DataResponse
220
	 * @throws NotPermittedException
221
	 */
222
	public function uploadImage(): DataResponse {
223
		// logo / background
224
		// new: favicon logo-header
225
		//
226
		$key = $this->request->getParam('key');
227
		$image = $this->request->getUploadedFile('image');
228
		$error = null;
229
		$phpFileUploadErrors = [
230
			UPLOAD_ERR_OK => $this->l10n->t('The file was uploaded'),
231
			UPLOAD_ERR_INI_SIZE => $this->l10n->t('The uploaded file exceeds the upload_max_filesize directive in php.ini'),
232
			UPLOAD_ERR_FORM_SIZE => $this->l10n->t('The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form'),
233
			UPLOAD_ERR_PARTIAL => $this->l10n->t('The file was only partially uploaded'),
234
			UPLOAD_ERR_NO_FILE => $this->l10n->t('No file was uploaded'),
235
			UPLOAD_ERR_NO_TMP_DIR => $this->l10n->t('Missing a temporary folder'),
236
			UPLOAD_ERR_CANT_WRITE => $this->l10n->t('Could not write file to disk'),
237
			UPLOAD_ERR_EXTENSION => $this->l10n->t('A PHP extension stopped the file upload'),
238
		];
239
		if (empty($image)) {
240
			$error = $this->l10n->t('No file uploaded');
241
		}
242
		if (!empty($image) && array_key_exists('error', $image) && $image['error'] !== UPLOAD_ERR_OK) {
243
			$error = $phpFileUploadErrors[$image['error']];
244
		}
245
246
		if ($error !== null) {
247
			return new DataResponse(
248
				[
249
					'data' => [
250
						'message' => $error
251
					],
252
					'status' => 'failure',
253
				],
254
				Http::STATUS_UNPROCESSABLE_ENTITY
255
			);
256
		}
257
258
		$name = '';
0 ignored issues
show
Unused Code introduced by
$name is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
259
		try {
260
			$folder = $this->appData->getFolder('images');
261
		} catch (NotFoundException $e) {
262
			$folder = $this->appData->newFolder('images');
263
		}
264
265
		$this->imageManager->delete($key);
266
267
		$target = $folder->newFile($key);
268
		$supportedFormats = $this->getSupportedUploadImageFormats($key);
269
		$detectedMimeType = mime_content_type($image['tmp_name']);
270
		if (!in_array($image['type'], $supportedFormats) || !in_array($detectedMimeType, $supportedFormats)) {
271
			return new DataResponse(
272
				[
273
					'data' => [
274
						'message' => $this->l10n->t('Unsupported image type'),
275
					],
276
					'status' => 'failure',
277
				],
278
				Http::STATUS_UNPROCESSABLE_ENTITY
279
			);
280
		}
281
282
		$resizeKeys = ['background'];
283
		if (in_array($key, $resizeKeys, true)) {
284
			// Optimize the image since some people may upload images that will be
285
			// either to big or are not progressive rendering.
286
			$newImage = @imagecreatefromstring(file_get_contents($image['tmp_name'], 'r'));
287
288
			$tmpFile = $this->tempManager->getTemporaryFile();
289
			$newWidth = imagesx($newImage) < 4096 ? imagesx($newImage) : 4096;
290
			$newHeight = imagesy($newImage) / (imagesx($newImage) / $newWidth);
291
			$outputImage = imagescale($newImage, $newWidth, $newHeight);
292
293
			imageinterlace($outputImage, 1);
294
			imagejpeg($outputImage, $tmpFile, 75);
295
			imagedestroy($outputImage);
296
297
			$target->putContent(file_get_contents($tmpFile, 'r'));
298
		} else {
299
			$target->putContent(file_get_contents($image['tmp_name'], 'r'));
300
		}
301
		$name = $image['name'];
302
303
		$this->themingDefaults->set($key.'Mime', $image['type']);
304
305
		$cssCached = $this->scssCacher->process(\OC::$SERVERROOT, 'core/css/css-variables.scss', 'core');
0 ignored issues
show
Unused Code introduced by
$cssCached is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
306
307
		return new DataResponse(
308
			[
309
				'data' =>
310
					[
311
						'name' => $name,
312
						'url' => $this->imageManager->getImageUrl($key),
313
						'message' => $this->l10n->t('Saved'),
314
						'serverCssUrl' => $this->urlGenerator->linkTo('', $this->scssCacher->getCachedSCSS('core', '/core/css/css-variables.scss'))
315
					],
316
				'status' => 'success'
317
			]
318
		);
319
	}
320
321
	/**
322
	 * Returns a list of supported mime types for image uploads.
323
	 * "favicon" images are only allowed to be SVG when imagemagick with SVG support is available.
324
	 *
325
	 * @param string $key The image key, e.g. "favicon"
326
	 * @return array
327
	 */
328
	private function getSupportedUploadImageFormats(string $key): array {
329
		$supportedFormats = ['image/jpeg', 'image/png', 'image/gif',];
330
331
		if ($key !== 'favicon' || $this->imageManager->shouldReplaceIcons() === true) {
332
			$supportedFormats[] = 'image/svg+xml';
333
			$supportedFormats[] = 'image/svg';
334
		}
335
336
		return $supportedFormats;
337
	}
338
339
	/**
340
	 * Revert setting to default value
341
	 *
342
	 * @param string $setting setting which should be reverted
343
	 * @return DataResponse
344
	 * @throws NotPermittedException
345
	 */
346
	public function undo(string $setting): DataResponse {
347
		$value = $this->themingDefaults->undo($setting);
348
		// reprocess server scss for preview
349
		$cssCached = $this->scssCacher->process(\OC::$SERVERROOT, 'core/css/css-variables.scss', 'core');
0 ignored issues
show
Unused Code introduced by
$cssCached is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
350
351
		if (strpos($setting, 'Mime') !== -1) {
352
			$imageKey = str_replace('Mime', '', $setting);
353
			$this->imageManager->delete($imageKey);
354
		}
355
356
		return new DataResponse(
357
			[
358
				'data' =>
359
					[
360
						'value' => $value,
361
						'message' => $this->l10n->t('Saved'),
362
						'serverCssUrl' => $this->urlGenerator->linkTo('', $this->scssCacher->getCachedSCSS('core', '/core/css/css-variables.scss'))
363
					],
364
				'status' => 'success'
365
			]
366
		);
367
	}
368
369
	/**
370
	 * @PublicPage
371
	 * @NoCSRFRequired
372
	 *
373
	 * @param string $key
374
	 * @param bool $useSvg
375
	 * @return FileDisplayResponse|NotFoundResponse
376
	 * @throws NotPermittedException
377
	 */
378
	public function getImage(string $key, bool $useSvg = true) {
379
		try {
380
			$file = $this->imageManager->getImage($key, $useSvg);
381
		} catch (NotFoundException $e) {
382
			return new NotFoundResponse();
383
		}
384
385
		$response = new FileDisplayResponse($file);
386
		$response->cacheFor(3600);
387
		$response->addHeader('Content-Type', $this->config->getAppValue($this->appName, $key . 'Mime', ''));
388
		$response->addHeader('Content-Disposition', 'attachment; filename="' . $key . '"');
389
		if (!$useSvg) {
390
			$response->addHeader('Content-Type', 'image/png');
391
		} else {
392
			$response->addHeader('Content-Type', $this->config->getAppValue($this->appName, $key . 'Mime', ''));
393
		}
394
		return $response;
395
	}
396
397
	/**
398
	 * @NoCSRFRequired
399
	 * @PublicPage
400
	 *
401
	 * @return FileDisplayResponse|NotFoundResponse
402
	 * @throws NotPermittedException
403
	 * @throws \Exception
404
	 * @throws \OCP\App\AppPathNotFoundException
405
	 */
406
	public function getStylesheet() {
407
		$appPath = $this->appManager->getAppPath('theming');
408
409
		/* SCSSCacher is required here
410
		 * We cannot rely on automatic caching done by \OC_Util::addStyle,
411
		 * since we need to add the cacheBuster value to the url
412
		 */
413
		$cssCached = $this->scssCacher->process($appPath, 'css/theming.scss', 'theming');
414
		if(!$cssCached) {
415
			return new NotFoundResponse();
416
		}
417
418
		try {
419
			$cssFile = $this->scssCacher->getCachedCSS('theming', 'theming.css');
420
			$response = new FileDisplayResponse($cssFile, Http::STATUS_OK, ['Content-Type' => 'text/css']);
421
			$response->cacheFor(86400);
422
			return $response;
423
		} catch (NotFoundException $e) {
424
			return new NotFoundResponse();
425
		}
426
	}
427
428
	/**
429
	 * @NoCSRFRequired
430
	 * @PublicPage
431
	 *
432
	 * @return DataDownloadResponse
433
	 */
434
	public function getJavascript() {
435
		$cacheBusterValue = $this->config->getAppValue('theming', 'cachebuster', '0');
436
		$responseJS = '(function() {
437
	OCA.Theming = {
438
		name: ' . json_encode($this->themingDefaults->getName()) . ',
439
		url: ' . json_encode($this->themingDefaults->getBaseUrl()) . ',
440
		slogan: ' . json_encode($this->themingDefaults->getSlogan()) . ',
441
		color: ' . json_encode($this->themingDefaults->getColorPrimary()) . ',
442
		imprintUrl: ' . json_encode($this->themingDefaults->getImprintUrl()) . ',
443
		privacyUrl: ' . json_encode($this->themingDefaults->getPrivacyUrl()) . ',
444
		inverted: ' . json_encode($this->util->invertTextColor($this->themingDefaults->getColorPrimary())) . ',
445
		cacheBuster: ' . json_encode($cacheBusterValue) . '
446
	};
447
})();';
448
		$response = new DataDownloadResponse($responseJS, 'javascript', 'text/javascript');
449
		$response->cacheFor(3600);
450
		return $response;
451
	}
452
453
	/**
454
	 * @NoCSRFRequired
455
	 * @PublicPage
456
	 *
457
	 * @return Http\JSONResponse
458
	 */
459
	public function getManifest($app) {
460
		$cacheBusterValue = $this->config->getAppValue('theming', 'cachebuster', '0');
461
		$responseJS = [
462
			'name' => $this->themingDefaults->getName(),
463
			'start_url' => $this->urlGenerator->getBaseUrl(),
464
			'icons' =>
465
				[
466
					[
467
						'src' => $this->urlGenerator->linkToRoute('theming.Icon.getTouchIcon',
468
								['app' => $app]) . '?v=' . $cacheBusterValue,
469
						'type'=> 'image/png',
470
						'sizes'=> '128x128'
471
					],
472
					[
473
						'src' => $this->urlGenerator->linkToRoute('theming.Icon.getFavicon',
474
								['app' => $app]) . '?v=' . $cacheBusterValue,
475
						'type' => 'image/svg+xml',
476
						'sizes' => '16x16'
477
					]
478
				],
479
			'display' => 'standalone'
480
		];
481
		$response = new Http\JSONResponse($responseJS);
482
		$response->cacheFor(3600);
483
		return $response;
484
	}
485
}
486