Completed
Push — master ( f2b8d7...c7becd )
by Roeland
11:44 queued 10:17
created

TemplateManager   B

Complexity

Total Complexity 47

Size/Duplication

Total Lines 419
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 47
lcom 1
cbo 0
dl 0
loc 419
ccs 0
cts 216
cp 0
rs 8.64
c 0
b 0
f 0

20 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 32 3
A setUserId() 0 3 1
B get() 0 28 6
A filterTemplates() 0 15 4
A getEmpty() 0 22 3
A getSystem() 0 6 1
A getSystemFormatted() 0 14 1
A getUser() 0 10 2
A getUserFormatted() 0 7 1
A getAll() 0 17 4
A getAllFormatted() 0 10 2
A add() 0 12 2
A delete() 0 11 3
A flipTypes() 0 8 2
A getUserTemplateDir() 0 26 5
A getSystemTemplateDir() 0 5 1
A getEmptyTemplateDir() 0 5 1
A formatNodeReturn() 0 10 1
A isTemplate() 0 15 3
A formatEmpty() 0 8 1

How to fix   Complexity   

Complex Class

Complex classes like TemplateManager 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 TemplateManager, and based on these observations, apply Extract Interface, too.

1
<?php
2
declare (strict_types = 1);
3
/**
4
 * @copyright Copyright (c) 2018 John Molakvoæ <[email protected]>
5
 *
6
 * @author John Molakvoæ <[email protected]>
7
 *
8
 * @license GNU AGPL version 3 or any later version
9
 *
10
 * This program is free software: you can redistribute it and/or modify
11
 * it under the terms of the GNU Affero General Public License as
12
 * published by the Free Software Foundation, either version 3 of the
13
 * License, or (at your option) any later version.
14
 *
15
 * This program is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 * GNU Affero General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU Affero General Public License
21
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22
 *
23
 */
24
25
namespace OCA\Richdocuments;
26
27
use OCP\Files\File;
28
use OCP\Files\Folder;
29
use OCP\Files\IRootFolder;
30
use OCP\Files\Node;
31
use OCP\Files\NotFoundException;
32
use OCP\IConfig;
33
use OCP\IL10N;
34
use OCP\IPreview;
35
use OCP\IURLGenerator;
36
use OC\Files\AppData\Factory;
37
38
class TemplateManager {
39
40
	/** @var string */
41
	protected $appName;
42
43
	/** @var string */
44
	protected $userId;
45
46
	/** @var IConfig */
47
	private $config;
48
49
	/** @var IURLGenerator */
50
	private $urlGenerator;
51
52
	/** @var IRootFolder */
53
	private $rootFolder;
54
55
	/** @var IL10N */
56
	private $l;
57
58
	/** Accepted templates mime types */
59
	const MIMES_DOCUMENTS = [
60
		'application/vnd.oasis.opendocument.text-template'
61
	];
62
	const MIMES_SHEETS = [
63
		'application/vnd.oasis.opendocument.spreadsheet-template'
64
	];
65
	const MIMES_PRESENTATIONS = [
66
		'application/vnd.oasis.opendocument.presentation-template'
67
	];
68
69
	/** @var array Template mime types match */
70
	static public $tplTypes = [
71
		'document'     => self::MIMES_DOCUMENTS,
72
		'spreadsheet'  => self::MIMES_SHEETS,
73
		'presentation' => self::MIMES_PRESENTATIONS
74
	];
75
76
	const EMPTY_TEMPLATE_ID_TYPE = [
77
		-1 => 'document',
78
		-2 => 'spreadsheet',
79
		-3 => 'presentation',
80
	];
81
	const EMPTY_TEMPLATE_TYPE_ID = [
82
		'document'     => -1,
83
		'spreadsheet'  => -2,
84
		'presentation' => -3,
85
	];
86
	const TYPE_EXTENTION = [
87
		'document'     => 'odt',
88
		'spreadsheet'  => 'ods',
89
		'presentation' => 'odp',
90
	];
91
92
93
	/**
94
	 * Template manager
95
	 *
96
	 * @param string $appName
97
	 * @param string $userId
98
	 * @param IConfig $config
99
	 * @param Factory $appDataFactory
100
	 * @param IURLGenerator $urlGenerator
101
	 * @param IRootFolder $rootFolder
102
	 * @param IL10N $l
103
	 * @throws \OCP\Files\NotPermittedException
104
	 */
105
	public function __construct($appName,
106
								$userId,
107
								IConfig $config,
108
								Factory $appDataFactory,
109
								IURLGenerator $urlGenerator,
110
								IRootFolder $rootFolder,
111
								IL10N $l) {
112
		$this->appName        = $appName;
113
		$this->userId         = $userId;
114
		$this->config         = $config;
115
		$this->rootFolder     = $rootFolder;
116
		$this->urlGenerator   = $urlGenerator;
117
118
		/*
119
		 * Init the appdata folder
120
		 * We need an actual folder for the fileid and previews.
121
		 * TODO: Fix this at some point
122
		 */
123
		$appData = $appDataFactory->get($appName);
124
		try {
125
			$appData->getFolder('templates');
126
		} catch (NotFoundException $e) {
0 ignored issues
show
Bug introduced by
The class OCP\Files\NotFoundException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
127
			$appData->newFolder('templates');
128
		}
129
		try {
130
			$appData->getFolder('empty_templates');
131
		} catch (NotFoundException $e) {
0 ignored issues
show
Bug introduced by
The class OCP\Files\NotFoundException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
132
			$appData->newFolder('empty_templates');
133
		}
134
135
		$this->l = $l;
136
	}
137
138
	public function setUserId($userId) {
139
		$this->userId = $userId;
140
	}
141
142
	/**
143
	 * Get template ISimpleFile|Node
144
	 *
145
	 * @param int $fileId
146
	 * @return File
147
	 */
148
	public function get($fileId) {
149
		// is this a global template ?
150
		$files = $this->getEmptyTemplateDir()->getDirectoryListing();
151
152
		foreach ($files as $file) {
153
			if ($file->getId() === $fileId) {
154
				return $file;
155
			}
156
		}
157
158
		// is this a global template ?
159
		$files = $this->getSystemTemplateDir()->getDirectoryListing();
160
161
		foreach ($files as $file) {
162
			if ($file->getId() === $fileId) {
163
				return $file;
164
			}
165
		}
166
167
		$templateDir = $this->getUserTemplateDir();
168
		// finally get the template file
169
		$files = $templateDir->getById($fileId);
170
		if ($files !== []) {
171
			return $files[0];
172
		}
173
174
		throw new NotFoundException();
175
	}
176
177
	/**
178
	 * @param File[] $templates
179
	 * @return File[]
180
	 */
181
	private function filterTemplates($templates, $type = null) {
182
		return array_filter($templates, function (Node $templateFile) use ($type) {
183
			if (!($templateFile instanceof File)) {
0 ignored issues
show
Bug introduced by
The class OCP\Files\File does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
184
				return false;
185
			}
186
187
			if ($type !== null && !in_array($templateFile->getMimeType(), self::$tplTypes[$type])) {
188
				return false;
189
			}
190
191
			//Todo validate mimetypes etc
192
193
			return true;
194
		});
195
	}
196
197
	private function getEmpty($type = null) {
198
		$folder = $this->getEmptyTemplateDir();
199
200
		$templateFiles = $folder->getDirectoryListing();
201
202
		if ($templateFiles === []) {
203
			// Empty so lets copy over the basic templates
204
			$templates = [
205
				'document.ott',
206
				'spreadsheet.ots',
207
				'presentation.otp',
208
			];
209
210
			foreach ($templates as $template) {
211
				$file = $folder->newFile($template);
212
				$file->putContent(file_get_contents(__DIR__ . '/../assets/' . $template));
213
				$templateFiles[] = $file;
214
			}
215
		}
216
217
		return $this->filterTemplates($templateFiles, $type);
218
	}
219
220
	/**
221
	 * Get all global templates
222
	 *
223
	 * @return File[]
224
	 */
225
	public function getSystem($type = null) {
226
		$folder = $this->getSystemTemplateDir();
227
228
		$templateFiles = $folder->getDirectoryListing();
229
		return $this->filterTemplates($templateFiles, $type);
230
	}
231
232
	/**
233
	 * @return array
234
	 */
235
	public function getSystemFormatted($type = null) {
236
		$empty = $this->getEmpty($type);
237
		$system = $this->getSystem($type);
238
239
		$emptyFormatted = array_map(function(File $file) {
240
			return $this->formatEmpty($file);
241
		}, $empty);
242
243
		$systemFormatted = array_map(function(File $file) {
244
			return $this->formatNodeReturn($file);
245
		}, $system);
246
247
		return array_merge($emptyFormatted, $systemFormatted);
248
	}
249
250
	/**
251
	 * Get all user templates
252
	 *
253
	 * @return File[]
254
	 */
255
	public function getUser($type = null) {
256
		try {
257
			$templateDir   = $this->getUserTemplateDir();
258
			$templateFiles = $templateDir->getDirectoryListing();
259
260
			return $this->filterTemplates($templateFiles, $type);
261
		} catch(NotFoundException $e) {
0 ignored issues
show
Bug introduced by
The class OCP\Files\NotFoundException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
262
			return [];
263
		}
264
	}
265
266
	/**
267
	 * @return array
268
	 */
269
	public function getUserFormatted($type) {
270
		$templates = $this->getUser($type);
271
272
		return array_map(function(File $file) {
273
			return $this->formatNodeReturn($file);
274
		}, $templates);
275
	}
276
277
	/**
278
	 * Get all templates
279
	 *
280
	 * @return File[]
281
	 */
282
	public function getAll($type = 'document') {
283
		$system = $this->getSystem();
284
		$user   = $this->getUser();
285
286
		if (!array_key_exists($type, self::$tplTypes)) {
287
			return [];
288
		}
289
290
		return array_values(array_filter(array_merge($user, $system), function (File $template) use ($type) {
291
			foreach (self::$tplTypes[$type] as $mime) {
292
				if ($template->getMimeType() === $mime) {
293
					return true;
294
				}
295
			}
296
			return false;
297
		}));
298
	}
299
300
	public function getAllFormatted($type) {
301
		if (!array_key_exists($type, self::$tplTypes)) {
302
			return [];
303
		}
304
305
		$system = $this->getSystemFormatted($type);
306
		$user   = $this->getUserFormatted($type);
307
308
		return array_merge($system, $user);
309
	}
310
311
	/**
312
	 * Add a template to the global template folder
313
	 *
314
	 * @param string $templateName
315
	 * @param string $templateFile
316
	 * @return array
317
	 */
318
	public function add($templateName, $templateFile) {
319
		$folder = $this->getSystemTemplateDir();
320
321
		try {
322
			$template = $folder->get($templateName);
323
		} catch (NotFoundException $e) {
0 ignored issues
show
Bug introduced by
The class OCP\Files\NotFoundException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
324
			$template = $folder->newFile($templateName);
325
		}
326
		$template->putContent($templateFile);
327
328
		return $this->formatNodeReturn($this->get($template->getId()));
329
	}
330
331
	/**
332
	 * Delete a template to the global template folder
333
	 *
334
	 * @param int $fileId
335
	 * @return boolean
336
	 * @throws NotFoundException
337
	 */
338
	public function delete($fileId) {
339
		$files = $this->getSystemTemplateDir()->getDirectoryListing();
340
		foreach ($files as $file) {
341
			if ($file->getId() === $fileId) {
342
				$file->delete();
343
				return true;
344
			}
345
		}
346
347
		throw new NotFoundException();
348
	}
349
350
	/**
351
	 * Flip $tplTypes to retrieve types by mime
352
	 *
353
	 * @return array
354
	 */
355
	private function flipTypes() {
356
		$result = [];
357
		foreach ($this::$tplTypes as $type => $mime) {
358
			$result = array_merge($result, array_fill_keys($mime, $type));
359
		}
360
361
		return $result;
362
	}
363
364
	/**
365
	 * Get the user template directory
366
	 *
367
	 * @return Folder
368
	 * @throws NotFoundException
369
	 */
370
	private function getUserTemplateDir() {
371
		if ($this->userId === null) {
372
			throw new NotFoundException('userId not set');
373
		}
374
375
		// has the user manually set a directory as the default template dir ?
376
		$templateDirPath = $this->config->getUserValue($this->userId, $this->appName, 'templateFolder', false);
377
		$userFolder = $this->rootFolder->getUserFolder($this->userId);
378
379
		if ($templateDirPath !== false) {
380
			$templateDir = $userFolder->get($templateDirPath);
381
		} else {
382
			// fallback to default template dir
383
			try {
384
				$templateDir = $userFolder->get('Templates');
385
			} catch (NotFoundException $e) {
0 ignored issues
show
Bug introduced by
The class OCP\Files\NotFoundException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
386
				throw new NotFoundException($e->getMessage());
387
			}
388
		}
389
390
		if (!($templateDir instanceof Folder)) {
0 ignored issues
show
Bug introduced by
The class OCP\Files\Folder does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
391
			throw new NotFoundException('Template dir points to a file');
392
		}
393
394
		return $templateDir;
395
	}
396
397
	/**
398
	 * @return Folder
399
	 */
400
	private function getSystemTemplateDir() {
401
		return $this->rootFolder->get('appdata_' . $this->config->getSystemValue('instanceid', null))
402
			->get('richdocuments')
403
			->get('templates');
404
	}
405
406
	/**
407
	 * @return Folder
408
	 */
409
	private function getEmptyTemplateDir() {
410
		return $this->rootFolder->get('appdata_' . $this->config->getSystemValue('instanceid', null))
411
			->get('richdocuments')
412
			->get('empty_templates');
413
	}
414
415
	/**
416
	 * Format template file for json return object
417
	 *
418
	 * @param File $template
419
	 * @return array
420
	 */
421
	public function formatNodeReturn(File $template) {
422
		return [
423
			'id'        => $template->getId(),
424
			'name'      => $template->getName(),
425
			'preview'   => $this->urlGenerator->linkToRouteAbsolute('richdocuments.templates.getPreview', ['fileId' => $template->getId()]),
426
			'type'      => $this->flipTypes()[$template->getMimeType()],
427
			'delete'    => $this->urlGenerator->linkToRouteAbsolute('richdocuments.templates.delete', ['fileId' => $template->getId()]),
428
			'extension' => self::TYPE_EXTENTION[$this->flipTypes()[$template->getMimeType()]],
429
		];
430
	}
431
432
	public function isTemplate($fileId) {
433
		$empty = $this->getEmpty();
434
		$system = $this->getSystem();
435
		$user = $this->getUser();
436
		/** @var File[] $all */
437
		$all = array_merge($empty, $system, $user);
438
439
		foreach ($all as $template) {
440
			if ($template->getId() === $fileId) {
441
				return true;
442
			}
443
		}
444
445
		return false;
446
	}
447
448
	public function formatEmpty(File $template) {
449
		return [
450
			'id'        => $template->getId(),
451
			'name'      => $this->l->t('Empty'),
452
			'type'      => $this->flipTypes()[$template->getMimeType()],
453
			'extension' => self::TYPE_EXTENTION[$this->flipTypes()[$template->getMimeType()]],
454
		];
455
	}
456
}
457