Completed
Push — master ( db7984...ab328d )
by Thomas
38:41 queued 22:29
created

PreviewManager::getEnabledDefaultProvider()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 26
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 19
nc 3
nop 0
dl 0
loc 26
rs 8.8571
c 0
b 0
f 0
1
<?php
2
/**
3
 * @author Joas Schilling <[email protected]>
4
 * @author Morris Jobke <[email protected]>
5
 * @author Olivier Paroz <[email protected]>
6
 * @author Robin Appelman <[email protected]>
7
 * @author Thomas Müller <[email protected]>
8
 * @author Vincent Petry <[email protected]>
9
 * @author Lorenzo Perone <[email protected]>
10
 *
11
 * @copyright Copyright (c) 2018, ownCloud GmbH
12
 * @license AGPL-3.0
13
 *
14
 * This code is free software: you can redistribute it and/or modify
15
 * it under the terms of the GNU Affero General Public License, version 3,
16
 * as published by the Free Software Foundation.
17
 *
18
 * This program is distributed in the hope that it will be useful,
19
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21
 * GNU Affero General Public License for more details.
22
 *
23
 * You should have received a copy of the GNU Affero General Public License, version 3,
24
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
25
 *
26
 */
27
namespace OC;
28
29
use OC\AppFramework\Middleware\Security\Exceptions\NotLoggedInException;
30
use OCP\Files\IRootFolder;
31
use OCP\IConfig;
32
use OCP\IImage;
33
use OCP\IPreview;
34
use OCP\IUserSession;
35
use OCP\Preview\IProvider;
36
use OCP\Preview\IProvider2;
37
38
class PreviewManager implements IPreview {
39
	/** @var IConfig */
40
	protected $config;
41
42
	/** @var bool */
43
	protected $providerListDirty = false;
44
45
	/** @var bool */
46
	protected $registeredCoreProviders = false;
47
48
	/** @var array */
49
	protected $providers = [];
50
51
	/** @var array mime type => support status */
52
	protected $mimeTypeSupportMap = [];
53
54
	/** @var array */
55
	protected $defaultProviders;
56
57
	/** @var IRootFolder */
58
	private $rootFolder;
59
60
	/** @var IUserSession */
61
	private $userSession;
62
63
	/**
64
	 * Constructor
65
	 *
66
	 * @param IConfig $config
67
	 * @param IRootFolder $rootFolder
68
	 * @param IUserSession $userSession
69
	 */
70
	public function __construct(IConfig $config, IRootFolder $rootFolder, IUserSession $userSession) {
71
		$this->config = $config;
72
		$this->rootFolder = $rootFolder;
73
		$this->userSession = $userSession;
74
	}
75
76
	/**
77
	 * In order to improve lazy loading a closure can be registered which will be
78
	 * called in case preview providers are actually requested
79
	 *
80
	 * $callable has to return an instance of \OCP\Preview\IProvider
81
	 *
82
	 * @param string $mimeTypeRegex Regex with the mime types that are supported by this provider
83
	 * @param \Closure $callable
84
	 * @return void
85
	 */
86
	public function registerProvider($mimeTypeRegex, \Closure $callable) {
87
		if (!$this->config->getSystemValue('enable_previews', true)) {
88
			return;
89
		}
90
91
		if (!isset($this->providers[$mimeTypeRegex])) {
92
			$this->providers[$mimeTypeRegex] = [];
93
		}
94
		$this->providers[$mimeTypeRegex][] = $callable;
95
		$this->providerListDirty = true;
96
	}
97
98
	/**
99
	 * Get all providers
100
	 * @return array
101
	 */
102
	public function getProviders() {
103
		if (!$this->config->getSystemValue('enable_previews', true)) {
104
			return [];
105
		}
106
107
		$this->registerCoreProviders();
108
		if ($this->providerListDirty) {
109
			$keys = \array_map('strlen', \array_keys($this->providers));
110
			\array_multisort($keys, SORT_DESC, $this->providers);
111
			$this->providerListDirty = false;
112
		}
113
114
		return $this->providers;
115
	}
116
117
	/**
118
	 * Does the manager have any providers
119
	 * @return bool
120
	 */
121
	public function hasProviders() {
122
		$this->registerCoreProviders();
123
		return !empty($this->providers);
124
	}
125
126
	/**
127
	 * return a preview of a file
128
	 *
129
	 * @param string $file The path to the file where you want a thumbnail from
130
	 * @param int $maxX The maximum X size of the thumbnail. It can be smaller depending on the shape of the image
131
	 * @param int $maxY The maximum Y size of the thumbnail. It can be smaller depending on the shape of the image
132
	 * @param boolean $scaleUp Scale smaller images up to the thumbnail size or not. Might look ugly
133
	 * @return IImage
134
	 * @throws NotLoggedInException
135
	 * @throws \OCP\Files\NotFoundException
136
	 * @throws \Exception
137
	 */
138
	public function createPreview($file, $maxX = 100, $maxY = 75, $scaleUp = false) {
139
		$user = $this->userSession->getUser();
140
		if ($user === null) {
141
			throw new NotLoggedInException();
142
		}
143
		$file = $this->rootFolder->getUserFolder($user->getUID())->getParent()->get($file);
144
		$preview = new Preview('', '/', $file, $maxX, $maxY, $scaleUp);
145
		return $preview->getPreview();
146
	}
147
148
	/**
149
	 * returns true if the passed mime type is supported
150
	 *
151
	 * @param string $mimeType
152
	 * @return boolean
153
	 */
154
	public function isMimeSupported($mimeType = '*') {
155
		if (!$this->config->getSystemValue('enable_previews', true)) {
156
			return false;
157
		}
158
159
		if (isset($this->mimeTypeSupportMap[$mimeType])) {
160
			return $this->mimeTypeSupportMap[$mimeType];
161
		}
162
163
		$this->registerCoreProviders();
164
		$providerMimeTypes = \array_keys($this->providers);
165
		foreach ($providerMimeTypes as $supportedMimeType) {
166
			if (\preg_match($supportedMimeType, $mimeType)) {
167
				$this->mimeTypeSupportMap[$mimeType] = true;
168
				return true;
169
			}
170
		}
171
		$this->mimeTypeSupportMap[$mimeType] = false;
172
		return false;
173
	}
174
	
175
	/**
176
	 * Returns all registered MimeTypes as an array
177
	 *
178
	 * @return string[]
179
	 */
180
	public function getSupportedMimes() {
181
		$supportedMimes = [];
182
		$this->registerCoreProviders();
183
		$mimeRegexArray = \array_keys($this->providers);
184
		// Now trim start/stop regexp delimiters
185
		foreach ($mimeRegexArray as $mimeRegex) {
186
			$supportedMimes[] = \trim($mimeRegex, '/');
187
		}
188
		return $supportedMimes;
189
	}
190
191
	/**
192
	 * Check if a preview can be generated for a file
193
	 *
194
	 * @param \OCP\Files\FileInfo $file
195
	 * @return bool
196
	 */
197
	public function isAvailable(\OCP\Files\FileInfo $file) {
198
		if (!$this->config->getSystemValue('enable_previews', true)) {
199
			return false;
200
		}
201
202
		$this->registerCoreProviders();
203
		if (!$this->isMimeSupported($file->getMimetype())) {
204
			return false;
205
		}
206
207
		$mount = $file->getMountPoint();
208
		if ($mount and !$mount->getOption('previews', true)) {
209
			return false;
210
		}
211
212
		foreach ($this->providers as $supportedMimeType => $providers) {
213
			if (\preg_match($supportedMimeType, $file->getMimetype())) {
214
				foreach ($providers as $closure) {
215
					$provider = $closure();
216
					if (!($provider instanceof IProvider) && !($provider instanceof IProvider2)) {
217
						continue;
218
					}
219
220
					/** @var $provider IProvider */
221
					if ($provider->isAvailable($file)) {
222
						return true;
223
					}
224
				}
225
			}
226
		}
227
		return false;
228
	}
229
230
	/**
231
	 * List of enabled default providers
232
	 *
233
	 * The following providers are enabled by default:
234
	 *  - OC\Preview\PNG
235
	 *  - OC\Preview\JPEG
236
	 *  - OC\Preview\GIF
237
	 *  - OC\Preview\BMP
238
	 *  - OC\Preview\XBitmap
239
	 *  - OC\Preview\MarkDown
240
	 *  - OC\Preview\MP3
241
	 *  - OC\Preview\TXT
242
	 *
243
	 * The following providers are disabled by default due to performance or privacy concerns:
244
	 *  - OC\Preview\Font
245
	 *  - OC\Preview\Illustrator
246
	 *  - OC\Preview\Movie
247
	 *  - OC\Preview\MSOfficeDoc
248
	 *  - OC\Preview\MSOffice2003
249
	 *  - OC\Preview\MSOffice2007
250
	 *  - OC\Preview\OpenDocument
251
	 *  - OC\Preview\PDF
252
	 *  - OC\Preview\Photoshop
253
	 *  - OC\Preview\Postscript
254
	 *  - OC\Preview\StarOffice
255
	 *  - OC\Preview\SVG
256
	 *  - OC\Preview\TIFF
257
	 *
258
	 * @return array
259
	 */
260
	protected function getEnabledDefaultProvider() {
261
		if ($this->defaultProviders !== null) {
262
			return $this->defaultProviders;
263
		}
264
265
		$imageProviders = [
266
			'OC\Preview\PNG',
267
			'OC\Preview\JPEG',
268
			'OC\Preview\GIF',
269
			'OC\Preview\BMP',
270
			'OC\Preview\Heic',
271
			'OC\Preview\XBitmap'
272
		];
273
274
		$this->defaultProviders = $this->config->getSystemValue('enabledPreviewProviders', \array_merge([
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->config->getSystem...XT'), $imageProviders)) of type * is incompatible with the declared type array of property $defaultProviders.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
275
			'OC\Preview\MarkDown',
276
			'OC\Preview\MP3',
277
			'OC\Preview\TXT',
278
		], $imageProviders));
279
280
		if (\in_array('OC\Preview\Image', $this->defaultProviders)) {
281
			$this->defaultProviders = \array_merge($this->defaultProviders, $imageProviders);
282
		}
283
		$this->defaultProviders = \array_unique($this->defaultProviders);
284
		return $this->defaultProviders;
285
	}
286
287
	/**
288
	 * Register the default providers (if enabled)
289
	 *
290
	 * @param string $class
291
	 * @param string $mimeType
292
	 */
293
	protected function registerCoreProvider($class, $mimeType, $options = []) {
294
		if (\in_array(\trim($class, '\\'), $this->getEnabledDefaultProvider())) {
295
			$this->registerProvider($mimeType, function () use ($class, $options) {
296
				return new $class($options);
297
			});
298
		}
299
	}
300
301
	/**
302
	 * Register the default providers (if enabled)
303
	 */
304
	protected function registerCoreProviders() {
305
		if ($this->registeredCoreProviders) {
306
			return;
307
		}
308
		$this->registeredCoreProviders = true;
309
310
		$this->registerCoreProvider('OC\Preview\TXT', '/text\/plain/');
311
		$this->registerCoreProvider('OC\Preview\MarkDown', '/text\/(x-)?markdown/');
312
		$this->registerCoreProvider('OC\Preview\PNG', '/image\/png/');
313
		$this->registerCoreProvider('OC\Preview\JPEG', '/image\/jpeg/');
314
		$this->registerCoreProvider('OC\Preview\GIF', '/image\/gif/');
315
		$this->registerCoreProvider('OC\Preview\BMP', '/image\/bmp/');
316
		$this->registerCoreProvider('OC\Preview\XBitmap', '/image\/x-xbitmap/');
317
		$this->registerCoreProvider('OC\Preview\MP3', '/audio\/mpeg/');
318
319
		// SVG, Office and Bitmap require imagick
320
		if (\extension_loaded('imagick')) {
321
			$checkImagick = new \Imagick();
322
323
			$imagickProviders = [
324
				'SVG'	=> ['mimetype' => '/image\/svg\+xml/', 'class' => '\OC\Preview\SVG'],
325
				'TIFF'	=> ['mimetype' => '/image\/tiff/', 'class' => '\OC\Preview\TIFF'],
326
				'PDF'	=> ['mimetype' => '/application\/pdf/', 'class' => '\OC\Preview\PDF'],
327
				'AI'	=> ['mimetype' => '/application\/illustrator/', 'class' => '\OC\Preview\Illustrator'],
328
				'PSD'	=> ['mimetype' => '/application\/x-photoshop/', 'class' => '\OC\Preview\Photoshop'],
329
				'EPS'	=> ['mimetype' => '/application\/postscript/', 'class' => '\OC\Preview\Postscript'],
330
				'TTF'	=> ['mimetype' => '/application\/(?:font-sfnt|x-font$)/', 'class' => '\OC\Preview\Font'],
331
				'HEIC'	=> ['mimetype' => '/image\/hei(f|c)/', 'class' => '\OC\Preview\Heic'],
332
			];
333
334
			foreach ($imagickProviders as $queryFormat => $provider) {
335
				$class = $provider['class'];
336
				if (!\in_array(\trim($class, '\\'), $this->getEnabledDefaultProvider())) {
337
					continue;
338
				}
339
340
				if (\count($checkImagick->queryFormats($queryFormat)) === 1) {
341
					$this->registerCoreProvider($class, $provider['mimetype']);
342
				}
343
			}
344
345
			if (\count($checkImagick->queryFormats('PDF')) === 1) {
346
				// Office previews are currently not supported on Windows
347
				if (\OC_Helper::is_function_enabled('shell_exec')) {
348
					$officeFound = \is_string($this->config->getSystemValue('preview_libreoffice_path', null));
349
350
					if (!$officeFound) {
351
						//let's see if there is libreoffice or openoffice on this machine
352
						$whichLibreOffice = \shell_exec('command -v libreoffice');
353
						$officeFound = !empty($whichLibreOffice);
354
						if (!$officeFound) {
355
							$whichOpenOffice = \shell_exec('command -v openoffice');
356
							$officeFound = !empty($whichOpenOffice);
357
						}
358
					}
359
360
					if ($officeFound) {
361
						$this->registerCoreProvider('\OC\Preview\MSOfficeDoc', '/application\/msword/');
362
						$this->registerCoreProvider('\OC\Preview\MSOffice2003', '/application\/vnd.ms-.*/');
363
						$this->registerCoreProvider('\OC\Preview\MSOffice2007', '/application\/vnd.openxmlformats-officedocument.*/');
364
						$this->registerCoreProvider('\OC\Preview\OpenDocument', '/application\/vnd.oasis.opendocument.*/');
365
						$this->registerCoreProvider('\OC\Preview\StarOffice', '/application\/vnd.sun.xml.*/');
366
					}
367
				}
368
			}
369
		}
370
371
		// Video requires avconv or ffmpeg and is therefor
372
		// currently not supported on Windows.
373
		if (\in_array('OC\Preview\Movie', $this->getEnabledDefaultProvider())) {
374
			// AtomicParsley would actually work under Windows.
375
			$avconvBinary = \OC_Helper::findBinaryPath('avconv');
376
			$ffmpegBinary = ($avconvBinary) ? null : \OC_Helper::findBinaryPath('ffmpeg');
377
			$atomicParsleyBinary = \OC_Helper::findBinaryPath('AtomicParsley');
378
379
			// FIXME // a bit hacky but didn't want to use subclasses
380
			$registerProvider = false;
381
			if ($avconvBinary !== null) {
382
				\OC\Preview\Movie::$avconvBinary = $avconvBinary;
383
				$registerProvider = true;
384
			}
385
			if ($ffmpegBinary !== null) {
386
				\OC\Preview\Movie::$ffmpegBinary = $ffmpegBinary;
387
				$registerProvider = true;
388
			}
389
			if ($atomicParsleyBinary !== null) {
390
				\OC\Preview\Movie::$atomicParsleyBinary = $atomicParsleyBinary;
391
				$registerProvider = true;
392
			}
393
			if ($registerProvider === true) {
394
				$this->registerCoreProvider('\OC\Preview\Movie', '/video\/.*/');
395
			}
396
		}
397
	}
398
}
399