Completed
Pull Request — master (#27558)
by Victor
48:36
created

PreviewManager   B

Complexity

Total Complexity 48

Size/Duplication

Total Lines 341
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 48
c 2
b 0
f 0
lcom 1
cbo 7
dl 0
loc 341
rs 8.4864

11 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
A registerProvider() 0 11 3
A getProviders() 0 14 3
A hasProviders() 0 4 1
A createPreview() 0 4 1
B isMimeSupported() 0 20 5
A getSupportedMimes() 0 10 2
D isAvailable() 0 32 10
B getEnabledDefaultProvider() 0 25 3
A registerCoreProvider() 0 7 2
F registerCoreProviders() 0 93 17

How to fix   Complexity   

Complex Class

Complex classes like PreviewManager often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use PreviewManager, and based on these observations, apply Extract Interface, too.

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) 2017, 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 OCP\IPreview;
30
use OCP\Preview\IProvider;
31
32
class PreviewManager implements IPreview {
33
	/** @var \OCP\IConfig */
34
	protected $config;
35
36
	/** @var bool */
37
	protected $providerListDirty = false;
38
39
	/** @var bool */
40
	protected $registeredCoreProviders = false;
41
42
	/** @var array */
43
	protected $providers = [];
44
45
	/** @var array mime type => support status */
46
	protected $mimeTypeSupportMap = [];
47
48
	/** @var array */
49
	protected $defaultProviders;
50
51
	/**
52
	 * Constructor
53
	 *
54
	 * @param \OCP\IConfig $config
55
	 */
56
	public function __construct(\OCP\IConfig $config) {
57
		$this->config = $config;
58
	}
59
60
	/**
61
	 * In order to improve lazy loading a closure can be registered which will be
62
	 * called in case preview providers are actually requested
63
	 *
64
	 * $callable has to return an instance of \OCP\Preview\IProvider
65
	 *
66
	 * @param string $mimeTypeRegex Regex with the mime types that are supported by this provider
67
	 * @param \Closure $callable
68
	 * @return void
69
	 */
70
	public function registerProvider($mimeTypeRegex, \Closure $callable) {
71
		if (!$this->config->getSystemValue('enable_previews', true)) {
72
			return;
73
		}
74
75
		if (!isset($this->providers[$mimeTypeRegex])) {
76
			$this->providers[$mimeTypeRegex] = [];
77
		}
78
		$this->providers[$mimeTypeRegex][] = $callable;
79
		$this->providerListDirty = true;
80
	}
81
82
	/**
83
	 * Get all providers
84
	 * @return array
85
	 */
86
	public function getProviders() {
87
		if (!$this->config->getSystemValue('enable_previews', true)) {
88
			return [];
89
		}
90
91
		$this->registerCoreProviders();
92
		if ($this->providerListDirty) {
93
			$keys = array_map('strlen', array_keys($this->providers));
94
			array_multisort($keys, SORT_DESC, $this->providers);
95
			$this->providerListDirty = false;
96
		}
97
98
		return $this->providers;
99
	}
100
101
	/**
102
	 * Does the manager have any providers
103
	 * @return bool
104
	 */
105
	public function hasProviders() {
106
		$this->registerCoreProviders();
107
		return !empty($this->providers);
108
	}
109
110
	/**
111
	 * return a preview of a file
112
	 *
113
	 * @param string $file The path to the file where you want a thumbnail from
114
	 * @param int $maxX The maximum X size of the thumbnail. It can be smaller depending on the shape of the image
115
	 * @param int $maxY The maximum Y size of the thumbnail. It can be smaller depending on the shape of the image
116
	 * @param boolean $scaleUp Scale smaller images up to the thumbnail size or not. Might look ugly
117
	 * @return \OCP\IImage
118
	 */
119
	public function createPreview($file, $maxX = 100, $maxY = 75, $scaleUp = false) {
120
		$preview = new \OC\Preview('', '/', $file, $maxX, $maxY, $scaleUp);
121
		return $preview->getPreview();
122
	}
123
124
	/**
125
	 * returns true if the passed mime type is supported
126
	 *
127
	 * @param string $mimeType
128
	 * @return boolean
129
	 */
130
	public function isMimeSupported($mimeType = '*') {
131
		if (!$this->config->getSystemValue('enable_previews', true)) {
132
			return false;
133
		}
134
135
		if (isset($this->mimeTypeSupportMap[$mimeType])) {
136
			return $this->mimeTypeSupportMap[$mimeType];
137
		}
138
139
		$this->registerCoreProviders();
140
		$providerMimeTypes = array_keys($this->providers);
141
		foreach ($providerMimeTypes as $supportedMimeType) {
142
			if (preg_match($supportedMimeType, $mimeType)) {
143
				$this->mimeTypeSupportMap[$mimeType] = true;
144
				return true;
145
			}
146
		}
147
		$this->mimeTypeSupportMap[$mimeType] = false;
148
		return false;
149
	}
150
	
151
	/**
152
	 * Returns all registered MimeTypes as an array
153
	 *
154
	 * @return string[]
155
	 */
156
	public function getSupportedMimes(){
157
		$supportedMimes = [];
158
		$this->registerCoreProviders();
159
		$mimeRegexArray = array_keys($this->providers);
160
		// Now trim start/stop regexp delimiters
161
		foreach ($mimeRegexArray as $mimeRegex){
162
			$supportedMimes[] = trim($mimeRegex, '/');
163
		}
164
		return $supportedMimes;
165
	}
166
167
	/**
168
	 * Check if a preview can be generated for a file
169
	 *
170
	 * @param \OCP\Files\FileInfo $file
171
	 * @return bool
172
	 */
173
	public function isAvailable(\OCP\Files\FileInfo $file) {
174
		if (!$this->config->getSystemValue('enable_previews', true)) {
175
			return false;
176
		}
177
178
		$this->registerCoreProviders();
179
		if (!$this->isMimeSupported($file->getMimetype())) {
180
			return false;
181
		}
182
183
		$mount = $file->getMountPoint();
184
		if ($mount and !$mount->getOption('previews', true)){
185
			return false;
186
		}
187
188
		foreach ($this->providers as $supportedMimeType => $providers) {
189
			if (preg_match($supportedMimeType, $file->getMimetype())) {
190
				foreach ($providers as $closure) {
191
					$provider = $closure();
192
					if (!($provider instanceof IProvider)) {
193
						continue;
194
					}
195
196
					/** @var $provider IProvider */
197
					if ($provider->isAvailable($file)) {
198
						return true;
199
					}
200
				}
201
			}
202
		}
203
		return false;
204
	}
205
206
	/**
207
	 * List of enabled default providers
208
	 *
209
	 * The following providers are enabled by default:
210
	 *  - OC\Preview\PNG
211
	 *  - OC\Preview\JPEG
212
	 *  - OC\Preview\GIF
213
	 *  - OC\Preview\BMP
214
	 *  - OC\Preview\XBitmap
215
	 *  - OC\Preview\MarkDown
216
	 *  - OC\Preview\MP3
217
	 *  - OC\Preview\TXT
218
	 *
219
	 * The following providers are disabled by default due to performance or privacy concerns:
220
	 *  - OC\Preview\Font
221
	 *  - OC\Preview\Illustrator
222
	 *  - OC\Preview\Movie
223
	 *  - OC\Preview\MSOfficeDoc
224
	 *  - OC\Preview\MSOffice2003
225
	 *  - OC\Preview\MSOffice2007
226
	 *  - OC\Preview\OpenDocument
227
	 *  - OC\Preview\PDF
228
	 *  - OC\Preview\Photoshop
229
	 *  - OC\Preview\Postscript
230
	 *  - OC\Preview\StarOffice
231
	 *  - OC\Preview\SVG
232
	 *  - OC\Preview\TIFF
233
	 *
234
	 * @return array
235
	 */
236
	protected function getEnabledDefaultProvider() {
237
		if ($this->defaultProviders !== null) {
238
			return $this->defaultProviders;
239
		}
240
241
		$imageProviders = [
242
			'OC\Preview\PNG',
243
			'OC\Preview\JPEG',
244
			'OC\Preview\GIF',
245
			'OC\Preview\BMP',
246
			'OC\Preview\XBitmap'
247
		];
248
249
		$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...
250
			'OC\Preview\MarkDown',
251
			'OC\Preview\MP3',
252
			'OC\Preview\TXT',
253
		], $imageProviders));
254
255
		if (in_array('OC\Preview\Image', $this->defaultProviders)) {
256
			$this->defaultProviders = array_merge($this->defaultProviders, $imageProviders);
257
		}
258
		$this->defaultProviders = array_unique($this->defaultProviders);
259
		return $this->defaultProviders;
260
	}
261
262
	/**
263
	 * Register the default providers (if enabled)
264
	 *
265
	 * @param string $class
266
	 * @param string $mimeType
267
	 */
268
	protected function registerCoreProvider($class, $mimeType, $options = []) {
269
		if (in_array(trim($class, '\\'), $this->getEnabledDefaultProvider())) {
270
			$this->registerProvider($mimeType, function () use ($class, $options) {
271
				return new $class($options);
272
			});
273
		}
274
	}
275
276
	/**
277
	 * Register the default providers (if enabled)
278
	 */
279
	protected function registerCoreProviders() {
280
		if ($this->registeredCoreProviders) {
281
			return;
282
		}
283
		$this->registeredCoreProviders = true;
284
285
		$this->registerCoreProvider('OC\Preview\TXT', '/text\/plain/');
286
		$this->registerCoreProvider('OC\Preview\MarkDown', '/text\/(x-)?markdown/');
287
		$this->registerCoreProvider('OC\Preview\PNG', '/image\/png/');
288
		$this->registerCoreProvider('OC\Preview\JPEG', '/image\/jpeg/');
289
		$this->registerCoreProvider('OC\Preview\GIF', '/image\/gif/');
290
		$this->registerCoreProvider('OC\Preview\BMP', '/image\/bmp/');
291
		$this->registerCoreProvider('OC\Preview\XBitmap', '/image\/x-xbitmap/');
292
		$this->registerCoreProvider('OC\Preview\MP3', '/audio\/mpeg/');
293
294
		// SVG, Office and Bitmap require imagick
295
		if (extension_loaded('imagick')) {
296
			$checkImagick = new \Imagick();
297
298
			$imagickProviders = [
299
				'SVG'	=> ['mimetype' => '/image\/svg\+xml/', 'class' => '\OC\Preview\SVG'],
300
				'TIFF'	=> ['mimetype' => '/image\/tiff/', 'class' => '\OC\Preview\TIFF'],
301
				'PDF'	=> ['mimetype' => '/application\/pdf/', 'class' => '\OC\Preview\PDF'],
302
				'AI'	=> ['mimetype' => '/application\/illustrator/', 'class' => '\OC\Preview\Illustrator'],
303
				'PSD'	=> ['mimetype' => '/application\/x-photoshop/', 'class' => '\OC\Preview\Photoshop'],
304
				'EPS'	=> ['mimetype' => '/application\/postscript/', 'class' => '\OC\Preview\Postscript'],
305
				'TTF'	=> ['mimetype' => '/application\/(?:font-sfnt|x-font$)/', 'class' => '\OC\Preview\Font'],
306
			];
307
308
			foreach ($imagickProviders as $queryFormat => $provider) {
309
				$class = $provider['class'];
310
				if (!in_array(trim($class, '\\'), $this->getEnabledDefaultProvider())) {
311
					continue;
312
				}
313
314
				if (count($checkImagick->queryFormats($queryFormat)) === 1) {
315
					$this->registerCoreProvider($class, $provider['mimetype']);
316
				}
317
			}
318
319
			if (count($checkImagick->queryFormats('PDF')) === 1) {
320
				// Office previews are currently not supported on Windows
321
				if (\OC_Helper::is_function_enabled('shell_exec')) {
322
					$officeFound = is_string($this->config->getSystemValue('preview_libreoffice_path', null));
323
324
					if (!$officeFound) {
325
						//let's see if there is libreoffice or openoffice on this machine
326
						$whichLibreOffice = shell_exec('command -v libreoffice');
327
						$officeFound = !empty($whichLibreOffice);
328
						if (!$officeFound) {
329
							$whichOpenOffice = shell_exec('command -v openoffice');
330
							$officeFound = !empty($whichOpenOffice);
331
						}
332
					}
333
334
					if ($officeFound) {
335
						$this->registerCoreProvider('\OC\Preview\MSOfficeDoc', '/application\/msword/');
336
						$this->registerCoreProvider('\OC\Preview\MSOffice2003', '/application\/vnd.ms-.*/');
337
						$this->registerCoreProvider('\OC\Preview\MSOffice2007', '/application\/vnd.openxmlformats-officedocument.*/');
338
						$this->registerCoreProvider('\OC\Preview\OpenDocument', '/application\/vnd.oasis.opendocument.*/');
339
						$this->registerCoreProvider('\OC\Preview\StarOffice', '/application\/vnd.sun.xml.*/');
340
					}
341
				}
342
			}
343
		}
344
345
		// Video requires avconv or ffmpeg and is therefor
346
		// currently not supported on Windows.
347
		if (in_array('OC\Preview\Movie', $this->getEnabledDefaultProvider())) {
348
		// AtomicParsley would actually work under Windows.
349
			$avconvBinary = \OC_Helper::findBinaryPath('avconv');
350
			$ffmpegBinary = ($avconvBinary) ? null : \OC_Helper::findBinaryPath('ffmpeg');
351
			$atomicParsleyBinary = \OC_Helper::findBinaryPath('AtomicParsley');
352
353
			// FIXME // a bit hacky but didn't want to use subclasses
354
			$registerProvider = false;
355
			if (null !== $avconvBinary) {
356
				\OC\Preview\Movie::$avconvBinary = $avconvBinary;
357
				$registerProvider = true;
358
			}
359
			if (null !== $ffmpegBinary) {
360
				\OC\Preview\Movie::$ffmpegBinary = $ffmpegBinary;
361
				$registerProvider = true;
362
			}
363
			if (null !== $atomicParsleyBinary) {
364
				\OC\Preview\Movie::$atomicParsleyBinary = $atomicParsleyBinary;
365
				$registerProvider = true;
366
			}
367
			if (true === $registerProvider) {
368
				$this->registerCoreProvider('\OC\Preview\Movie', '/video\/.*/');
369
			}
370
		}
371
	}
372
}
373