Passed
Push — master ( d21310...0b966a )
by John
16:38 queued 12s
created
apps/theming/lib/ImageManager.php 2 patches
Indentation   +336 added lines, -336 removed lines patch added patch discarded remove patch
@@ -47,340 +47,340 @@
 block discarded – undo
47 47
 use OCP\IURLGenerator;
48 48
 
49 49
 class ImageManager {
50
-	public const SUPPORTED_IMAGE_KEYS = ['background', 'logo', 'logoheader', 'favicon'];
51
-
52
-	/** @var IConfig */
53
-	private $config;
54
-	/** @var IAppData */
55
-	private $appData;
56
-	/** @var IURLGenerator */
57
-	private $urlGenerator;
58
-	/** @var ICacheFactory */
59
-	private $cacheFactory;
60
-	/** @var ILogger */
61
-	private $logger;
62
-	/** @var ITempManager */
63
-	private $tempManager;
64
-
65
-	public function __construct(IConfig $config,
66
-								IAppData $appData,
67
-								IURLGenerator $urlGenerator,
68
-								ICacheFactory $cacheFactory,
69
-								ILogger $logger,
70
-								ITempManager $tempManager) {
71
-		$this->config = $config;
72
-		$this->urlGenerator = $urlGenerator;
73
-		$this->cacheFactory = $cacheFactory;
74
-		$this->logger = $logger;
75
-		$this->tempManager = $tempManager;
76
-		$this->appData = $appData;
77
-	}
78
-
79
-	/**
80
-	 * Get a globally defined image (admin theming settings)
81
-	 *
82
-	 * @param string $key the image key
83
-	 * @return string the image url
84
-	 */
85
-	public function getImageUrl(string $key): string {
86
-		$cacheBusterCounter = $this->config->getAppValue(Application::APP_ID, 'cachebuster', '0');
87
-		if ($this->hasImage($key)) {
88
-			return $this->urlGenerator->linkToRoute('theming.Theming.getImage', [ 'key' => $key ]) . '?v=' . $cacheBusterCounter;
89
-		}
90
-
91
-		switch ($key) {
92
-			case 'logo':
93
-			case 'logoheader':
94
-			case 'favicon':
95
-				return $this->urlGenerator->imagePath('core', 'logo/logo.png') . '?v=' . $cacheBusterCounter;
96
-			case 'background':
97
-				return $this->urlGenerator->linkTo(Application::APP_ID, 'img/background/' . BackgroundService::DEFAULT_BACKGROUND_IMAGE);
98
-		}
99
-		return '';
100
-	}
101
-
102
-	/**
103
-	 * Get the absolute url. See getImageUrl
104
-	 */
105
-	public function getImageUrlAbsolute(string $key): string {
106
-		return $this->urlGenerator->getAbsoluteURL($this->getImageUrl($key));
107
-	}
108
-
109
-	/**
110
-	 * @param string $key
111
-	 * @param bool $useSvg
112
-	 * @return ISimpleFile
113
-	 * @throws NotFoundException
114
-	 * @throws NotPermittedException
115
-	 */
116
-	public function getImage(string $key, bool $useSvg = true): ISimpleFile {
117
-		$logo = $this->config->getAppValue('theming', $key . 'Mime', '');
118
-		$folder = $this->getRootFolder()->getFolder('images');
119
-
120
-		if ($logo === '' || !$folder->fileExists($key)) {
121
-			throw new NotFoundException();
122
-		}
123
-
124
-		if (!$useSvg && $this->shouldReplaceIcons()) {
125
-			if (!$folder->fileExists($key . '.png')) {
126
-				try {
127
-					$finalIconFile = new \Imagick();
128
-					$finalIconFile->setBackgroundColor('none');
129
-					$finalIconFile->readImageBlob($folder->getFile($key)->getContent());
130
-					$finalIconFile->setImageFormat('png32');
131
-					$pngFile = $folder->newFile($key . '.png');
132
-					$pngFile->putContent($finalIconFile->getImageBlob());
133
-					return $pngFile;
134
-				} catch (\ImagickException $e) {
135
-					$this->logger->info('The image was requested to be no SVG file, but converting it to PNG failed: ' . $e->getMessage());
136
-				}
137
-			} else {
138
-				return $folder->getFile($key . '.png');
139
-			}
140
-		}
141
-
142
-		return $folder->getFile($key);
143
-	}
144
-
145
-	public function hasImage(string $key): bool {
146
-		$mimeSetting = $this->config->getAppValue('theming', $key . 'Mime', '');
147
-		return $mimeSetting !== '';
148
-	}
149
-
150
-	/**
151
-	 * @return array<string, array{mime: string, url: string}>
152
-	 */
153
-	public function getCustomImages(): array {
154
-		$images = [];
155
-		foreach (self::SUPPORTED_IMAGE_KEYS as $key) {
156
-			$images[$key] = [
157
-				'mime' => $this->config->getAppValue('theming', $key . 'Mime', ''),
158
-				'url' => $this->getImageUrl($key),
159
-			];
160
-		}
161
-		return $images;
162
-	}
163
-
164
-	/**
165
-	 * Get folder for current theming files
166
-	 *
167
-	 * @return ISimpleFolder
168
-	 * @throws NotPermittedException
169
-	 */
170
-	public function getCacheFolder(): ISimpleFolder {
171
-		$cacheBusterValue = $this->config->getAppValue('theming', 'cachebuster', '0');
172
-		try {
173
-			$folder = $this->getRootFolder()->getFolder($cacheBusterValue);
174
-		} catch (NotFoundException $e) {
175
-			$folder = $this->getRootFolder()->newFolder($cacheBusterValue);
176
-			$this->cleanup();
177
-		}
178
-		return $folder;
179
-	}
180
-
181
-	/**
182
-	 * Get a file from AppData
183
-	 *
184
-	 * @param string $filename
185
-	 * @throws NotFoundException
186
-	 * @return \OCP\Files\SimpleFS\ISimpleFile
187
-	 * @throws NotPermittedException
188
-	 */
189
-	public function getCachedImage(string $filename): ISimpleFile {
190
-		$currentFolder = $this->getCacheFolder();
191
-		return $currentFolder->getFile($filename);
192
-	}
193
-
194
-	/**
195
-	 * Store a file for theming in AppData
196
-	 *
197
-	 * @param string $filename
198
-	 * @param string $data
199
-	 * @return \OCP\Files\SimpleFS\ISimpleFile
200
-	 * @throws NotFoundException
201
-	 * @throws NotPermittedException
202
-	 */
203
-	public function setCachedImage(string $filename, string $data): ISimpleFile {
204
-		$currentFolder = $this->getCacheFolder();
205
-		if ($currentFolder->fileExists($filename)) {
206
-			$file = $currentFolder->getFile($filename);
207
-		} else {
208
-			$file = $currentFolder->newFile($filename);
209
-		}
210
-		$file->putContent($data);
211
-		return $file;
212
-	}
213
-
214
-	public function delete(string $key): void {
215
-		/* ignore exceptions, since we don't want to fail hard if something goes wrong during cleanup */
216
-		try {
217
-			$file = $this->getRootFolder()->getFolder('images')->getFile($key);
218
-			$file->delete();
219
-		} catch (NotFoundException $e) {
220
-		} catch (NotPermittedException $e) {
221
-		}
222
-		try {
223
-			$file = $this->getRootFolder()->getFolder('images')->getFile($key . '.png');
224
-			$file->delete();
225
-		} catch (NotFoundException $e) {
226
-		} catch (NotPermittedException $e) {
227
-		}
228
-	}
229
-
230
-	public function updateImage(string $key, string $tmpFile): string {
231
-		$this->delete($key);
232
-
233
-		try {
234
-			$folder = $this->getRootFolder()->getFolder('images');
235
-		} catch (NotFoundException $e) {
236
-			$folder = $this->getRootFolder()->newFolder('images');
237
-		}
238
-
239
-		$target = $folder->newFile($key);
240
-		$supportedFormats = $this->getSupportedUploadImageFormats($key);
241
-		$detectedMimeType = mime_content_type($tmpFile);
242
-		if (!in_array($detectedMimeType, $supportedFormats, true)) {
243
-			throw new \Exception('Unsupported image type');
244
-		}
245
-
246
-		if ($key === 'background' && $this->shouldOptimizeBackgroundImage($detectedMimeType, filesize($tmpFile))) {
247
-			try {
248
-				// Optimize the image since some people may upload images that will be
249
-				// either to big or are not progressive rendering.
250
-				$newImage = @imagecreatefromstring(file_get_contents($tmpFile));
251
-				if ($newImage === false) {
252
-					throw new \Exception('Could not read background image, possibly corrupted.');
253
-				}
254
-
255
-				// Preserve transparency
256
-				imagesavealpha($newImage, true);
257
-				imagealphablending($newImage, true);
258
-
259
-				$newWidth = (int)(imagesx($newImage) < 4096 ? imagesx($newImage) : 4096);
260
-				$newHeight = (int)(imagesy($newImage) / (imagesx($newImage) / $newWidth));
261
-				$outputImage = imagescale($newImage, $newWidth, $newHeight);
262
-				if ($outputImage === false) {
263
-					throw new \Exception('Could not scale uploaded background image.');
264
-				}
265
-
266
-				$newTmpFile = $this->tempManager->getTemporaryFile();
267
-				imageinterlace($outputImage, 1);
268
-				// Keep jpeg images encoded as jpeg
269
-				if (strpos($detectedMimeType, 'image/jpeg') !== false) {
270
-					if (!imagejpeg($outputImage, $newTmpFile, 90)) {
271
-						throw new \Exception('Could not recompress background image as JPEG');
272
-					}
273
-				} else {
274
-					if (!imagepng($outputImage, $newTmpFile, 8)) {
275
-						throw new \Exception('Could not recompress background image as PNG');
276
-					}
277
-				}
278
-				$tmpFile = $newTmpFile;
279
-				imagedestroy($outputImage);
280
-			} catch (\Exception $e) {
281
-				if (is_resource($outputImage) || $outputImage instanceof \GdImage) {
282
-					imagedestroy($outputImage);
283
-				}
284
-
285
-				$this->logger->debug($e->getMessage());
286
-			}
287
-		}
288
-
289
-		$target->putContent(file_get_contents($tmpFile));
290
-
291
-		return $detectedMimeType;
292
-	}
293
-
294
-	/**
295
-	 * Decide whether an image benefits from shrinking and reconverting
296
-	 *
297
-	 * @param string $mimeType the mime type of the image
298
-	 * @param int $contentSize size of the image file
299
-	 * @return bool
300
-	 */
301
-	private function shouldOptimizeBackgroundImage(string $mimeType, int $contentSize): bool {
302
-		// Do not touch SVGs
303
-		if (strpos($mimeType, 'image/svg') !== false) {
304
-			return false;
305
-		}
306
-		// GIF does not benefit from converting
307
-		if (strpos($mimeType, 'image/gif') !== false) {
308
-			return false;
309
-		}
310
-		// WebP also does not benefit from converting
311
-		// We could possibly try to convert to progressive image, but normally webP images are quite small
312
-		if (strpos($mimeType, 'image/webp') !== false) {
313
-			return false;
314
-		}
315
-		// As a rule of thumb background images should be max. 150-300 KiB, small images do not benefit from converting
316
-		return $contentSize > 150000;
317
-	}
318
-
319
-	/**
320
-	 * Returns a list of supported mime types for image uploads.
321
-	 * "favicon" images are only allowed to be SVG when imagemagick with SVG support is available.
322
-	 *
323
-	 * @param string $key The image key, e.g. "favicon"
324
-	 * @return string[]
325
-	 */
326
-	private function getSupportedUploadImageFormats(string $key): array {
327
-		$supportedFormats = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
328
-
329
-		if ($key !== 'favicon' || $this->shouldReplaceIcons() === true) {
330
-			$supportedFormats[] = 'image/svg+xml';
331
-			$supportedFormats[] = 'image/svg';
332
-		}
333
-
334
-		if ($key === 'favicon') {
335
-			$supportedFormats[] = 'image/x-icon';
336
-			$supportedFormats[] = 'image/vnd.microsoft.icon';
337
-		}
338
-
339
-		return $supportedFormats;
340
-	}
341
-
342
-	/**
343
-	 * remove cached files that are not required any longer
344
-	 *
345
-	 * @throws NotPermittedException
346
-	 * @throws NotFoundException
347
-	 */
348
-	public function cleanup() {
349
-		$currentFolder = $this->getCacheFolder();
350
-		$folders = $this->getRootFolder()->getDirectoryListing();
351
-		foreach ($folders as $folder) {
352
-			if ($folder->getName() !== 'images' && $folder->getName() !== $currentFolder->getName()) {
353
-				$folder->delete();
354
-			}
355
-		}
356
-	}
357
-
358
-	/**
359
-	 * Check if Imagemagick is enabled and if SVG is supported
360
-	 * otherwise we can't render custom icons
361
-	 *
362
-	 * @return bool
363
-	 */
364
-	public function shouldReplaceIcons() {
365
-		$cache = $this->cacheFactory->createDistributed('theming-' . $this->urlGenerator->getBaseUrl());
366
-		if ($value = $cache->get('shouldReplaceIcons')) {
367
-			return (bool)$value;
368
-		}
369
-		$value = false;
370
-		if (extension_loaded('imagick')) {
371
-			if (count(\Imagick::queryFormats('SVG')) >= 1) {
372
-				$value = true;
373
-			}
374
-		}
375
-		$cache->set('shouldReplaceIcons', $value);
376
-		return $value;
377
-	}
378
-
379
-	private function getRootFolder(): ISimpleFolder {
380
-		try {
381
-			return $this->appData->getFolder('global');
382
-		} catch (NotFoundException $e) {
383
-			return $this->appData->newFolder('global');
384
-		}
385
-	}
50
+    public const SUPPORTED_IMAGE_KEYS = ['background', 'logo', 'logoheader', 'favicon'];
51
+
52
+    /** @var IConfig */
53
+    private $config;
54
+    /** @var IAppData */
55
+    private $appData;
56
+    /** @var IURLGenerator */
57
+    private $urlGenerator;
58
+    /** @var ICacheFactory */
59
+    private $cacheFactory;
60
+    /** @var ILogger */
61
+    private $logger;
62
+    /** @var ITempManager */
63
+    private $tempManager;
64
+
65
+    public function __construct(IConfig $config,
66
+                                IAppData $appData,
67
+                                IURLGenerator $urlGenerator,
68
+                                ICacheFactory $cacheFactory,
69
+                                ILogger $logger,
70
+                                ITempManager $tempManager) {
71
+        $this->config = $config;
72
+        $this->urlGenerator = $urlGenerator;
73
+        $this->cacheFactory = $cacheFactory;
74
+        $this->logger = $logger;
75
+        $this->tempManager = $tempManager;
76
+        $this->appData = $appData;
77
+    }
78
+
79
+    /**
80
+     * Get a globally defined image (admin theming settings)
81
+     *
82
+     * @param string $key the image key
83
+     * @return string the image url
84
+     */
85
+    public function getImageUrl(string $key): string {
86
+        $cacheBusterCounter = $this->config->getAppValue(Application::APP_ID, 'cachebuster', '0');
87
+        if ($this->hasImage($key)) {
88
+            return $this->urlGenerator->linkToRoute('theming.Theming.getImage', [ 'key' => $key ]) . '?v=' . $cacheBusterCounter;
89
+        }
90
+
91
+        switch ($key) {
92
+            case 'logo':
93
+            case 'logoheader':
94
+            case 'favicon':
95
+                return $this->urlGenerator->imagePath('core', 'logo/logo.png') . '?v=' . $cacheBusterCounter;
96
+            case 'background':
97
+                return $this->urlGenerator->linkTo(Application::APP_ID, 'img/background/' . BackgroundService::DEFAULT_BACKGROUND_IMAGE);
98
+        }
99
+        return '';
100
+    }
101
+
102
+    /**
103
+     * Get the absolute url. See getImageUrl
104
+     */
105
+    public function getImageUrlAbsolute(string $key): string {
106
+        return $this->urlGenerator->getAbsoluteURL($this->getImageUrl($key));
107
+    }
108
+
109
+    /**
110
+     * @param string $key
111
+     * @param bool $useSvg
112
+     * @return ISimpleFile
113
+     * @throws NotFoundException
114
+     * @throws NotPermittedException
115
+     */
116
+    public function getImage(string $key, bool $useSvg = true): ISimpleFile {
117
+        $logo = $this->config->getAppValue('theming', $key . 'Mime', '');
118
+        $folder = $this->getRootFolder()->getFolder('images');
119
+
120
+        if ($logo === '' || !$folder->fileExists($key)) {
121
+            throw new NotFoundException();
122
+        }
123
+
124
+        if (!$useSvg && $this->shouldReplaceIcons()) {
125
+            if (!$folder->fileExists($key . '.png')) {
126
+                try {
127
+                    $finalIconFile = new \Imagick();
128
+                    $finalIconFile->setBackgroundColor('none');
129
+                    $finalIconFile->readImageBlob($folder->getFile($key)->getContent());
130
+                    $finalIconFile->setImageFormat('png32');
131
+                    $pngFile = $folder->newFile($key . '.png');
132
+                    $pngFile->putContent($finalIconFile->getImageBlob());
133
+                    return $pngFile;
134
+                } catch (\ImagickException $e) {
135
+                    $this->logger->info('The image was requested to be no SVG file, but converting it to PNG failed: ' . $e->getMessage());
136
+                }
137
+            } else {
138
+                return $folder->getFile($key . '.png');
139
+            }
140
+        }
141
+
142
+        return $folder->getFile($key);
143
+    }
144
+
145
+    public function hasImage(string $key): bool {
146
+        $mimeSetting = $this->config->getAppValue('theming', $key . 'Mime', '');
147
+        return $mimeSetting !== '';
148
+    }
149
+
150
+    /**
151
+     * @return array<string, array{mime: string, url: string}>
152
+     */
153
+    public function getCustomImages(): array {
154
+        $images = [];
155
+        foreach (self::SUPPORTED_IMAGE_KEYS as $key) {
156
+            $images[$key] = [
157
+                'mime' => $this->config->getAppValue('theming', $key . 'Mime', ''),
158
+                'url' => $this->getImageUrl($key),
159
+            ];
160
+        }
161
+        return $images;
162
+    }
163
+
164
+    /**
165
+     * Get folder for current theming files
166
+     *
167
+     * @return ISimpleFolder
168
+     * @throws NotPermittedException
169
+     */
170
+    public function getCacheFolder(): ISimpleFolder {
171
+        $cacheBusterValue = $this->config->getAppValue('theming', 'cachebuster', '0');
172
+        try {
173
+            $folder = $this->getRootFolder()->getFolder($cacheBusterValue);
174
+        } catch (NotFoundException $e) {
175
+            $folder = $this->getRootFolder()->newFolder($cacheBusterValue);
176
+            $this->cleanup();
177
+        }
178
+        return $folder;
179
+    }
180
+
181
+    /**
182
+     * Get a file from AppData
183
+     *
184
+     * @param string $filename
185
+     * @throws NotFoundException
186
+     * @return \OCP\Files\SimpleFS\ISimpleFile
187
+     * @throws NotPermittedException
188
+     */
189
+    public function getCachedImage(string $filename): ISimpleFile {
190
+        $currentFolder = $this->getCacheFolder();
191
+        return $currentFolder->getFile($filename);
192
+    }
193
+
194
+    /**
195
+     * Store a file for theming in AppData
196
+     *
197
+     * @param string $filename
198
+     * @param string $data
199
+     * @return \OCP\Files\SimpleFS\ISimpleFile
200
+     * @throws NotFoundException
201
+     * @throws NotPermittedException
202
+     */
203
+    public function setCachedImage(string $filename, string $data): ISimpleFile {
204
+        $currentFolder = $this->getCacheFolder();
205
+        if ($currentFolder->fileExists($filename)) {
206
+            $file = $currentFolder->getFile($filename);
207
+        } else {
208
+            $file = $currentFolder->newFile($filename);
209
+        }
210
+        $file->putContent($data);
211
+        return $file;
212
+    }
213
+
214
+    public function delete(string $key): void {
215
+        /* ignore exceptions, since we don't want to fail hard if something goes wrong during cleanup */
216
+        try {
217
+            $file = $this->getRootFolder()->getFolder('images')->getFile($key);
218
+            $file->delete();
219
+        } catch (NotFoundException $e) {
220
+        } catch (NotPermittedException $e) {
221
+        }
222
+        try {
223
+            $file = $this->getRootFolder()->getFolder('images')->getFile($key . '.png');
224
+            $file->delete();
225
+        } catch (NotFoundException $e) {
226
+        } catch (NotPermittedException $e) {
227
+        }
228
+    }
229
+
230
+    public function updateImage(string $key, string $tmpFile): string {
231
+        $this->delete($key);
232
+
233
+        try {
234
+            $folder = $this->getRootFolder()->getFolder('images');
235
+        } catch (NotFoundException $e) {
236
+            $folder = $this->getRootFolder()->newFolder('images');
237
+        }
238
+
239
+        $target = $folder->newFile($key);
240
+        $supportedFormats = $this->getSupportedUploadImageFormats($key);
241
+        $detectedMimeType = mime_content_type($tmpFile);
242
+        if (!in_array($detectedMimeType, $supportedFormats, true)) {
243
+            throw new \Exception('Unsupported image type');
244
+        }
245
+
246
+        if ($key === 'background' && $this->shouldOptimizeBackgroundImage($detectedMimeType, filesize($tmpFile))) {
247
+            try {
248
+                // Optimize the image since some people may upload images that will be
249
+                // either to big or are not progressive rendering.
250
+                $newImage = @imagecreatefromstring(file_get_contents($tmpFile));
251
+                if ($newImage === false) {
252
+                    throw new \Exception('Could not read background image, possibly corrupted.');
253
+                }
254
+
255
+                // Preserve transparency
256
+                imagesavealpha($newImage, true);
257
+                imagealphablending($newImage, true);
258
+
259
+                $newWidth = (int)(imagesx($newImage) < 4096 ? imagesx($newImage) : 4096);
260
+                $newHeight = (int)(imagesy($newImage) / (imagesx($newImage) / $newWidth));
261
+                $outputImage = imagescale($newImage, $newWidth, $newHeight);
262
+                if ($outputImage === false) {
263
+                    throw new \Exception('Could not scale uploaded background image.');
264
+                }
265
+
266
+                $newTmpFile = $this->tempManager->getTemporaryFile();
267
+                imageinterlace($outputImage, 1);
268
+                // Keep jpeg images encoded as jpeg
269
+                if (strpos($detectedMimeType, 'image/jpeg') !== false) {
270
+                    if (!imagejpeg($outputImage, $newTmpFile, 90)) {
271
+                        throw new \Exception('Could not recompress background image as JPEG');
272
+                    }
273
+                } else {
274
+                    if (!imagepng($outputImage, $newTmpFile, 8)) {
275
+                        throw new \Exception('Could not recompress background image as PNG');
276
+                    }
277
+                }
278
+                $tmpFile = $newTmpFile;
279
+                imagedestroy($outputImage);
280
+            } catch (\Exception $e) {
281
+                if (is_resource($outputImage) || $outputImage instanceof \GdImage) {
282
+                    imagedestroy($outputImage);
283
+                }
284
+
285
+                $this->logger->debug($e->getMessage());
286
+            }
287
+        }
288
+
289
+        $target->putContent(file_get_contents($tmpFile));
290
+
291
+        return $detectedMimeType;
292
+    }
293
+
294
+    /**
295
+     * Decide whether an image benefits from shrinking and reconverting
296
+     *
297
+     * @param string $mimeType the mime type of the image
298
+     * @param int $contentSize size of the image file
299
+     * @return bool
300
+     */
301
+    private function shouldOptimizeBackgroundImage(string $mimeType, int $contentSize): bool {
302
+        // Do not touch SVGs
303
+        if (strpos($mimeType, 'image/svg') !== false) {
304
+            return false;
305
+        }
306
+        // GIF does not benefit from converting
307
+        if (strpos($mimeType, 'image/gif') !== false) {
308
+            return false;
309
+        }
310
+        // WebP also does not benefit from converting
311
+        // We could possibly try to convert to progressive image, but normally webP images are quite small
312
+        if (strpos($mimeType, 'image/webp') !== false) {
313
+            return false;
314
+        }
315
+        // As a rule of thumb background images should be max. 150-300 KiB, small images do not benefit from converting
316
+        return $contentSize > 150000;
317
+    }
318
+
319
+    /**
320
+     * Returns a list of supported mime types for image uploads.
321
+     * "favicon" images are only allowed to be SVG when imagemagick with SVG support is available.
322
+     *
323
+     * @param string $key The image key, e.g. "favicon"
324
+     * @return string[]
325
+     */
326
+    private function getSupportedUploadImageFormats(string $key): array {
327
+        $supportedFormats = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
328
+
329
+        if ($key !== 'favicon' || $this->shouldReplaceIcons() === true) {
330
+            $supportedFormats[] = 'image/svg+xml';
331
+            $supportedFormats[] = 'image/svg';
332
+        }
333
+
334
+        if ($key === 'favicon') {
335
+            $supportedFormats[] = 'image/x-icon';
336
+            $supportedFormats[] = 'image/vnd.microsoft.icon';
337
+        }
338
+
339
+        return $supportedFormats;
340
+    }
341
+
342
+    /**
343
+     * remove cached files that are not required any longer
344
+     *
345
+     * @throws NotPermittedException
346
+     * @throws NotFoundException
347
+     */
348
+    public function cleanup() {
349
+        $currentFolder = $this->getCacheFolder();
350
+        $folders = $this->getRootFolder()->getDirectoryListing();
351
+        foreach ($folders as $folder) {
352
+            if ($folder->getName() !== 'images' && $folder->getName() !== $currentFolder->getName()) {
353
+                $folder->delete();
354
+            }
355
+        }
356
+    }
357
+
358
+    /**
359
+     * Check if Imagemagick is enabled and if SVG is supported
360
+     * otherwise we can't render custom icons
361
+     *
362
+     * @return bool
363
+     */
364
+    public function shouldReplaceIcons() {
365
+        $cache = $this->cacheFactory->createDistributed('theming-' . $this->urlGenerator->getBaseUrl());
366
+        if ($value = $cache->get('shouldReplaceIcons')) {
367
+            return (bool)$value;
368
+        }
369
+        $value = false;
370
+        if (extension_loaded('imagick')) {
371
+            if (count(\Imagick::queryFormats('SVG')) >= 1) {
372
+                $value = true;
373
+            }
374
+        }
375
+        $cache->set('shouldReplaceIcons', $value);
376
+        return $value;
377
+    }
378
+
379
+    private function getRootFolder(): ISimpleFolder {
380
+        try {
381
+            return $this->appData->getFolder('global');
382
+        } catch (NotFoundException $e) {
383
+            return $this->appData->newFolder('global');
384
+        }
385
+    }
386 386
 }
Please login to merge, or discard this patch.
Spacing   +15 added lines, -15 removed lines patch added patch discarded remove patch
@@ -85,16 +85,16 @@  discard block
 block discarded – undo
85 85
 	public function getImageUrl(string $key): string {
86 86
 		$cacheBusterCounter = $this->config->getAppValue(Application::APP_ID, 'cachebuster', '0');
87 87
 		if ($this->hasImage($key)) {
88
-			return $this->urlGenerator->linkToRoute('theming.Theming.getImage', [ 'key' => $key ]) . '?v=' . $cacheBusterCounter;
88
+			return $this->urlGenerator->linkToRoute('theming.Theming.getImage', ['key' => $key]).'?v='.$cacheBusterCounter;
89 89
 		}
90 90
 
91 91
 		switch ($key) {
92 92
 			case 'logo':
93 93
 			case 'logoheader':
94 94
 			case 'favicon':
95
-				return $this->urlGenerator->imagePath('core', 'logo/logo.png') . '?v=' . $cacheBusterCounter;
95
+				return $this->urlGenerator->imagePath('core', 'logo/logo.png').'?v='.$cacheBusterCounter;
96 96
 			case 'background':
97
-				return $this->urlGenerator->linkTo(Application::APP_ID, 'img/background/' . BackgroundService::DEFAULT_BACKGROUND_IMAGE);
97
+				return $this->urlGenerator->linkTo(Application::APP_ID, 'img/background/'.BackgroundService::DEFAULT_BACKGROUND_IMAGE);
98 98
 		}
99 99
 		return '';
100 100
 	}
@@ -114,7 +114,7 @@  discard block
 block discarded – undo
114 114
 	 * @throws NotPermittedException
115 115
 	 */
116 116
 	public function getImage(string $key, bool $useSvg = true): ISimpleFile {
117
-		$logo = $this->config->getAppValue('theming', $key . 'Mime', '');
117
+		$logo = $this->config->getAppValue('theming', $key.'Mime', '');
118 118
 		$folder = $this->getRootFolder()->getFolder('images');
119 119
 
120 120
 		if ($logo === '' || !$folder->fileExists($key)) {
@@ -122,20 +122,20 @@  discard block
 block discarded – undo
122 122
 		}
123 123
 
124 124
 		if (!$useSvg && $this->shouldReplaceIcons()) {
125
-			if (!$folder->fileExists($key . '.png')) {
125
+			if (!$folder->fileExists($key.'.png')) {
126 126
 				try {
127 127
 					$finalIconFile = new \Imagick();
128 128
 					$finalIconFile->setBackgroundColor('none');
129 129
 					$finalIconFile->readImageBlob($folder->getFile($key)->getContent());
130 130
 					$finalIconFile->setImageFormat('png32');
131
-					$pngFile = $folder->newFile($key . '.png');
131
+					$pngFile = $folder->newFile($key.'.png');
132 132
 					$pngFile->putContent($finalIconFile->getImageBlob());
133 133
 					return $pngFile;
134 134
 				} catch (\ImagickException $e) {
135
-					$this->logger->info('The image was requested to be no SVG file, but converting it to PNG failed: ' . $e->getMessage());
135
+					$this->logger->info('The image was requested to be no SVG file, but converting it to PNG failed: '.$e->getMessage());
136 136
 				}
137 137
 			} else {
138
-				return $folder->getFile($key . '.png');
138
+				return $folder->getFile($key.'.png');
139 139
 			}
140 140
 		}
141 141
 
@@ -143,7 +143,7 @@  discard block
 block discarded – undo
143 143
 	}
144 144
 
145 145
 	public function hasImage(string $key): bool {
146
-		$mimeSetting = $this->config->getAppValue('theming', $key . 'Mime', '');
146
+		$mimeSetting = $this->config->getAppValue('theming', $key.'Mime', '');
147 147
 		return $mimeSetting !== '';
148 148
 	}
149 149
 
@@ -154,7 +154,7 @@  discard block
 block discarded – undo
154 154
 		$images = [];
155 155
 		foreach (self::SUPPORTED_IMAGE_KEYS as $key) {
156 156
 			$images[$key] = [
157
-				'mime' => $this->config->getAppValue('theming', $key . 'Mime', ''),
157
+				'mime' => $this->config->getAppValue('theming', $key.'Mime', ''),
158 158
 				'url' => $this->getImageUrl($key),
159 159
 			];
160 160
 		}
@@ -220,7 +220,7 @@  discard block
 block discarded – undo
220 220
 		} catch (NotPermittedException $e) {
221 221
 		}
222 222
 		try {
223
-			$file = $this->getRootFolder()->getFolder('images')->getFile($key . '.png');
223
+			$file = $this->getRootFolder()->getFolder('images')->getFile($key.'.png');
224 224
 			$file->delete();
225 225
 		} catch (NotFoundException $e) {
226 226
 		} catch (NotPermittedException $e) {
@@ -256,8 +256,8 @@  discard block
 block discarded – undo
256 256
 				imagesavealpha($newImage, true);
257 257
 				imagealphablending($newImage, true);
258 258
 
259
-				$newWidth = (int)(imagesx($newImage) < 4096 ? imagesx($newImage) : 4096);
260
-				$newHeight = (int)(imagesy($newImage) / (imagesx($newImage) / $newWidth));
259
+				$newWidth = (int) (imagesx($newImage) < 4096 ? imagesx($newImage) : 4096);
260
+				$newHeight = (int) (imagesy($newImage) / (imagesx($newImage) / $newWidth));
261 261
 				$outputImage = imagescale($newImage, $newWidth, $newHeight);
262 262
 				if ($outputImage === false) {
263 263
 					throw new \Exception('Could not scale uploaded background image.');
@@ -362,9 +362,9 @@  discard block
 block discarded – undo
362 362
 	 * @return bool
363 363
 	 */
364 364
 	public function shouldReplaceIcons() {
365
-		$cache = $this->cacheFactory->createDistributed('theming-' . $this->urlGenerator->getBaseUrl());
365
+		$cache = $this->cacheFactory->createDistributed('theming-'.$this->urlGenerator->getBaseUrl());
366 366
 		if ($value = $cache->get('shouldReplaceIcons')) {
367
-			return (bool)$value;
367
+			return (bool) $value;
368 368
 		}
369 369
 		$value = false;
370 370
 		if (extension_loaded('imagick')) {
Please login to merge, or discard this patch.