Completed
Push — master ( c0f6e4...f4635c )
by Julius
07:48 queued 11s
created

TemplateManager::isValidTemplateMime()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
dl 0
loc 14
ccs 0
cts 5
cp 0
rs 9.4888
c 0
b 0
f 0
cc 5
nc 5
nop 2
crap 30
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\IAppData;
30
use OCP\Files\IRootFolder;
31
use OCP\Files\Node;
32
use OCP\Files\NotFoundException;
33
use OCP\IConfig;
34
use OCP\IL10N;
35
use OCP\IPreview;
36
use OCP\IURLGenerator;
37
use OC\Files\AppData\Factory;
38
39
class TemplateManager {
40
41
	/** @var string */
42
	protected $appName;
43
44
	/** @var string */
45
	protected $userId;
46
47
	/** @var IConfig */
48
	private $config;
49
50
	/** @var IURLGenerator */
51
	private $urlGenerator;
52
53
	/** @var IRootFolder */
54
	private $rootFolder;
55
56
	/** @var IL10N */
57
	private $l;
58
59
	/** Accepted templates mime types */
60
	const MIMES_DOCUMENTS = [
61
		'application/vnd.oasis.opendocument.text-template',
62
		'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
63
		'application/msword'
64
	];
65
	const MIMES_SHEETS = [
66
		'application/vnd.oasis.opendocument.spreadsheet-template',
67
		'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
68
		'application/vnd.ms-excel'
69
	];
70
	const MIMES_PRESENTATIONS = [
71
		'application/vnd.oasis.opendocument.presentation-template',
72
		'application/vnd.openxmlformats-officedocument.presentationml.template',
73
		'application/vnd.ms-powerpoint'
74
	];
75
76
	/** @var array Template mime types match */
77
	static public $tplTypes = [
78
		'document'     => self::MIMES_DOCUMENTS,
79
		'spreadsheet'  => self::MIMES_SHEETS,
80
		'presentation' => self::MIMES_PRESENTATIONS
81
	];
82
83
	const TYPE_EXTENTION = [
84
		'document'     => 'odt',
85
		'spreadsheet'  => 'ods',
86
		'presentation' => 'odp'
87
	];
88
89
	const TYPE_EXTENSION_OOXML = [
90
		'document'     => 'docx',
91
		'spreadsheet'  => 'xlsx',
92
		'presentation' => 'pptx'
93
	];
94
95
	const EMPTY_TEMPLATE_ID_TYPE = [
96
		-1 => 'document',
97
		-2 => 'spreadsheet',
98
		-3 => 'presentation',
99
	];
100
	const EMPTY_TEMPLATE_TYPE_ID = [
101
		'document'     => -1,
102
		'spreadsheet'  => -2,
103
		'presentation' => -3,
104
	];
105
106
107
	/**
108
	 * Template manager
109
	 *
110
	 * @param string $appName
111
	 * @param string $userId
112
	 * @param IConfig $config
113
	 * @param Factory $appDataFactory
0 ignored issues
show
Documentation introduced by
There is no parameter named $appDataFactory. Did you maybe mean $appData?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
114
	 * @param IURLGenerator $urlGenerator
115
	 * @param IRootFolder $rootFolder
116
	 * @param IL10N $l
117
	 * @throws \OCP\Files\NotPermittedException
118
	 */
119
	public function __construct($appName,
120
								$userId,
121
								IConfig $config,
122
								IAppData $appData,
123
								IURLGenerator $urlGenerator,
124
								IRootFolder $rootFolder,
125
								IL10N $l) {
126
		$this->appName        = $appName;
127
		$this->userId         = $userId;
128
		$this->config         = $config;
129
		$this->rootFolder     = $rootFolder;
130
		$this->urlGenerator   = $urlGenerator;
131
132
133
		$this->appData = $appData;
0 ignored issues
show
Bug introduced by
The property appData does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
134
		$this->createAppDataFolders();
135
136
		$this->l = $l;
137
	}
138
139
	private function createAppDataFolders() {
140
		/*
141
		 * Init the appdata folder
142
		 * We need an actual folder for the fileid and previews.
143
		 * TODO: Fix this at some point
144
		 */
145
		try {
146
			$this->appData->getFolder('templates');
147
		} catch (NotFoundException $e) {
148
			$this->appData->newFolder('templates');
149
		}
150
		try {
151
			$this->appData->getFolder('empty_templates');
152
		} catch (NotFoundException $e) {
153
			$this->appData->newFolder('empty_templates');
154
		}
155
	}
156
157
	public function setUserId($userId) {
158
		$this->userId = $userId;
159
	}
160
161
	/**
162
	 * Get template ISimpleFile|Node
163
	 *
164
	 * @param int $fileId
165
	 * @return File
166
	 */
167
	public function get($fileId) {
168
		// is this a global template ?
169
		$files = $this->getEmptyTemplateDir()->getDirectoryListing();
170
171
		foreach ($files as $file) {
172
			if ($file->getId() === $fileId) {
173
				return $file;
174
			}
175
		}
176
177
		// is this a global template ?
178
		$files = $this->getSystemTemplateDir()->getDirectoryListing();
179
180
		foreach ($files as $file) {
181
			if ($file->getId() === $fileId) {
182
				return $file;
183
			}
184
		}
185
186
		$templateDir = $this->getUserTemplateDir();
187
		// finally get the template file
188
		$files = $templateDir->getById($fileId);
189
		if ($files !== []) {
190
			return $files[0];
191
		}
192
193
		throw new NotFoundException();
194
	}
195
196
	/**
197
	 * @param File[] $templates
198
	 * @return File[]
199
	 */
200
	private function filterTemplates($templates, $type = null) {
201
		return array_filter($templates, function (Node $templateFile) use ($type) {
202
			if (!($templateFile instanceof File)) {
203
				return false;
204
			}
205
206
			return $this->isValidTemplateMime($templateFile->getMimeType(), $type);
207
		});
208
	}
209
210
	private function getEmpty($type = null) {
211
		$folder = $this->getEmptyTemplateDir();
212
213
		$templateFiles = $folder->getDirectoryListing();
214
215
		if ($templateFiles === []) {
216
			// Empty so lets copy over the basic templates
217
			$templates = [
218
				'document.ott',
219
				'spreadsheet.ots',
220
				'presentation.otp',
221
			];
222
223
			foreach ($templates as $template) {
224
				$file = $folder->newFile($template);
225
				$file->putContent(file_get_contents(__DIR__ . '/../assets/' . $template));
226
				$templateFiles[] = $file;
227
			}
228
		}
229
230
		return $this->filterTemplates($templateFiles, $type);
0 ignored issues
show
Documentation introduced by
$templateFiles is of type array<integer,object<OCP\Files\Node>>, but the function expects a array<integer,object<OCP\Files\File>>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
231
	}
232
233
	/**
234
	 * Remove empty_templates in appdata and recreate it from the apps templates
235
	 */
236
	public function updateEmptyTemplates() {
237
		try {
238
			$folder = $this->getEmptyTemplateDir();
239
			$folder->delete();
240
		} catch (NotFoundException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
241
		}
242
		$this->appData->newFolder('empty_templates');
243
		$this->getEmpty();
244
	}
245
246
	/**
247
	 * Get all global templates
248
	 *
249
	 * @return File[]
250
	 */
251
	public function getSystem($type = null) {
252
		$folder = $this->getSystemTemplateDir();
253
254
		$templateFiles = $folder->getDirectoryListing();
255
		return $this->filterTemplates($templateFiles, $type);
0 ignored issues
show
Documentation introduced by
$templateFiles is of type array<integer,object<OCP\Files\Node>>, but the function expects a array<integer,object<OCP\Files\File>>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
256
	}
257
258
	/**
259
	 * @return array
260
	 */
261
	public function getSystemFormatted($type = null) {
262
		$empty = $this->getEmpty($type);
263
		$system = $this->getSystem($type);
264
265
		$emptyFormatted = array_map(function(File $file) {
266
			return $this->formatEmpty($file);
267
		}, $empty);
268
269
		$systemFormatted = array_map(function(File $file) {
270
			return $this->formatNodeReturn($file);
271
		}, $system);
272
273
		return array_merge($emptyFormatted, $systemFormatted);
274
	}
275
276
	/**
277
	 * Get all user templates
278
	 *
279
	 * @return File[]
280
	 */
281
	public function getUser($type = null) {
282
		try {
283
			$templateDir   = $this->getUserTemplateDir();
284
			$templateFiles = $templateDir->getDirectoryListing();
285
286
			return $this->filterTemplates($templateFiles, $type);
0 ignored issues
show
Documentation introduced by
$templateFiles is of type array<integer,object<OCP\Files\Node>>, but the function expects a array<integer,object<OCP\Files\File>>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
287
		} catch(NotFoundException $e) {
288
			return [];
289
		}
290
	}
291
292
	/**
293
	 * @return array
294
	 */
295
	public function getUserFormatted($type) {
296
		$templates = $this->getUser($type);
297
298
		return array_map(function(File $file) {
299
			return $this->formatNodeReturn($file);
300
		}, $templates);
301
	}
302
303
	/**
304
	 * Get all templates
305
	 *
306
	 * @return File[]
307
	 */
308
	public function getAll($type = 'document') {
309
		$system = $this->getSystem();
310
		$user   = $this->getUser();
311
312
		if (!array_key_exists($type, self::$tplTypes)) {
313
			return [];
314
		}
315
316
		return array_values(array_filter(array_merge($user, $system), function (File $template) use ($type) {
317
			foreach (self::$tplTypes[$type] as $mime) {
318
				if ($template->getMimeType() === $mime) {
319
					return true;
320
				}
321
			}
322
			return false;
323
		}));
324
	}
325
326
	public function getAllFormatted($type) {
327
		if (!array_key_exists($type, self::$tplTypes)) {
328
			return [];
329
		}
330
331
		$system = $this->getSystemFormatted($type);
332
		$user   = $this->getUserFormatted($type);
333
334
		return array_merge($system, $user);
335
	}
336
337
	/**
338
	 * Add a template to the global template folder
339
	 *
340
	 * @param string $templateName
341
	 * @param string $templateFile
342
	 * @return array
343
	 */
344
	public function add($templateName, $templateFile) {
345
		$folder = $this->getSystemTemplateDir();
346
347
		try {
348
			$template = $folder->get($templateName);
349
		} catch (NotFoundException $e) {
350
			$template = $folder->newFile($templateName);
351
		}
352
		$template->putContent($templateFile);
0 ignored issues
show
Bug introduced by
The method putContent() does not seem to exist on object<OCP\Files\Node>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
353
354
		return $this->formatNodeReturn($this->get($template->getId()));
355
	}
356
357
	/**
358
	 * Delete a template to the global template folder
359
	 *
360
	 * @param int $fileId
361
	 * @return boolean
362
	 * @throws NotFoundException
363
	 */
364
	public function delete($fileId) {
365
		$files = $this->getSystemTemplateDir()->getDirectoryListing();
366
		foreach ($files as $file) {
367
			if ($file->getId() === $fileId) {
368
				$file->delete();
369
				return true;
370
			}
371
		}
372
373
		throw new NotFoundException();
374
	}
375
376
	/**
377
	 * Flip $tplTypes to retrieve types by mime
378
	 *
379
	 * @return array
380
	 */
381
	private function flipTypes() {
382
		$result = [];
383
		foreach ($this::$tplTypes as $type => $mime) {
384
			$result = array_merge($result, array_fill_keys($mime, $type));
385
		}
386
387
		return $result;
388
	}
389
390
	/**
391
	 * Get the user template directory
392
	 *
393
	 * @return Folder
394
	 * @throws NotFoundException
395
	 */
396
	private function getUserTemplateDir() {
397
		if ($this->userId === null) {
398
			throw new NotFoundException('userId not set');
399
		}
400
401
		// has the user manually set a directory as the default template dir ?
402
		$templateDirPath = $this->config->getUserValue($this->userId, $this->appName, 'templateFolder', false);
403
		$userFolder = $this->rootFolder->getUserFolder($this->userId);
404
405
		if ($templateDirPath !== false) {
406
			$templateDir = $userFolder->get($templateDirPath);
407
		} else {
408
			// fallback to default template dir
409
			try {
410
				$templateDir = $userFolder->get('Templates');
411
			} catch (NotFoundException $e) {
412
				throw new NotFoundException($e->getMessage());
413
			}
414
		}
415
416
		if (!($templateDir instanceof Folder)) {
417
			throw new NotFoundException('Template dir points to a file');
418
		}
419
420
		return $templateDir;
421
	}
422
423
	/**
424
	 * @return Folder
425
	 */
426
	private function getSystemTemplateDir() {
427
		return $this->rootFolder->get('appdata_' . $this->config->getSystemValue('instanceid', null))
0 ignored issues
show
Bug introduced by
The method get() does not seem to exist on object<OCP\Files\Node>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
428
			->get('richdocuments')
429
			->get('templates');
430
	}
431
432
	/**
433
	 * @return Folder
434
	 */
435
	private function getEmptyTemplateDir() {
436
		return $this->rootFolder->get('appdata_' . $this->config->getSystemValue('instanceid', null))
0 ignored issues
show
Bug introduced by
The method get() does not seem to exist on object<OCP\Files\Node>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
437
			->get('richdocuments')
438
			->get('empty_templates');
439
	}
440
441
	/**
442
	 * Format template file for json return object
443
	 *
444
	 * @param File $template
445
	 * @return array
446
	 */
447
	public function formatNodeReturn(File $template) {
448
		$ooxml = $this->config->getAppValue($this->appName, 'doc_format', '') === 'ooxml';
449
		$documentType = $this->flipTypes()[$template->getMimeType()];
450
		return [
451
			'id'        => $template->getId(),
452
			'name'      => $template->getName(),
453
			'preview'   => $this->urlGenerator->linkToRouteAbsolute('richdocuments.templates.getPreview', ['fileId' => $template->getId()]),
454
			'type'      => $this->flipTypes()[$template->getMimeType()],
455
			'delete'    => $this->urlGenerator->linkToRouteAbsolute('richdocuments.templates.delete', ['fileId' => $template->getId()]),
456
			'extension' => $ooxml ? self::TYPE_EXTENSION_OOXML[$documentType] : self::TYPE_EXTENTION[$documentType],
457
		];
458
	}
459
460
	public function isTemplate($fileId) {
461
		$empty = $this->getEmpty();
462
		$system = $this->getSystem();
463
		$user = $this->getUser();
464
		/** @var File[] $all */
465
		$all = array_merge($empty, $system, $user);
466
467
		foreach ($all as $template) {
468
			if ($template->getId() === $fileId) {
469
				return true;
470
			}
471
		}
472
473
		return false;
474
	}
475
476
	public function formatEmpty(File $template) {
477
		$ooxml = $this->config->getAppValue($this->appName, 'doc_format', '') === 'ooxml';
478
		$documentType = $this->flipTypes()[$template->getMimeType()];
479
		return [
480
			'id'        => $template->getId(),
481
			'name'      => $this->l->t('Empty'),
482
			'type'      => $this->flipTypes()[$template->getMimeType()],
483
			'extension' => $ooxml ? self::TYPE_EXTENSION_OOXML[$documentType] : self::TYPE_EXTENTION[$documentType],
484
		];
485
	}
486
487
	public function isValidTemplateMime($mime, $type = null) {
488
		if ($type === null) {
489
			$allMimes = array_merge(self::$tplTypes['document'], self::$tplTypes['spreadsheet'], self::$tplTypes['presentation']);
490
			if (!in_array($mime, $allMimes)) {
491
				return false;
492
			}
493
		}
494
495
		if ($type !== null && !in_array($mime, self::$tplTypes[$type])) {
496
			return false;
497
		}
498
499
		return true;
500
	}
501
}
502