AttachmentService::restore()   A
last analyzed

Complexity

Conditions 5
Paths 10

Size

Total Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 27
rs 9.1768
c 0
b 0
f 0
cc 5
nc 10
nop 2
1
<?php
2
/**
3
 * @copyright Copyright (c) 2018 Julius Härtl <[email protected]>
4
 *
5
 * @author Julius Härtl <[email protected]>
6
 *
7
 * @license GNU AGPL version 3 or any later version
8
 *
9
 * This program is free software: you can redistribute it and/or modify
10
 * it under the terms of the GNU Affero General Public License as
11
 * published by the Free Software Foundation, either version 3 of the
12
 * License, or (at your option) any later version.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU Affero General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU Affero General Public License
20
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21
 *
22
 */
23
24
namespace OCA\Deck\Service;
25
26
27
use OCA\Deck\Activity\ActivityManager;
28
use OCA\Deck\AppInfo\Application;
29
use OCA\Deck\BadRequestException;
30
use OCA\Deck\Db\Acl;
31
use OCA\Deck\Db\Attachment;
32
use OCA\Deck\Db\AttachmentMapper;
33
use OCA\Deck\Db\CardMapper;
34
use OCA\Deck\Db\ChangeHelper;
35
use OCA\Deck\InvalidAttachmentType;
36
use OCA\Deck\NoPermissionException;
37
use OCA\Deck\NotFoundException;
38
use OCA\Deck\StatusException;
39
use OCP\AppFramework\Http\Response;
40
use OCP\ICache;
41
use OCP\ICacheFactory;
42
use OCP\IL10N;
43
44
class AttachmentService {
45
46
	private $attachmentMapper;
47
	private $cardMapper;
48
	private $permissionService;
49
	private $userId;
50
51
	/** @var IAttachmentService[] */
52
	private $services = [];
53
	private $application;
54
	/** @var ICache */
55
	private $cache;
56
	/** @var IL10N */
57
	private $l10n;
58
	/** @var ActivityManager */
59
	private $activityManager;
60
	/** @var ChangeHelper */
61
	private $changeHelper;
62
63
	/**
64
	 * AttachmentService constructor.
65
	 *
66
	 * @param AttachmentMapper $attachmentMapper
67
	 * @param CardMapper $cardMapper
68
	 * @param PermissionService $permissionService
69
	 * @param Application $application
70
	 * @param ICacheFactory $cacheFactory
71
	 * @param $userId
72
	 * @param IL10N $l10n
73
	 * @throws \OCP\AppFramework\QueryException
74
	 */
75
	public function __construct(AttachmentMapper $attachmentMapper, CardMapper $cardMapper, ChangeHelper $changeHelper, PermissionService $permissionService, Application $application, ICacheFactory $cacheFactory, $userId, IL10N $l10n, ActivityManager $activityManager) {
76
		$this->attachmentMapper = $attachmentMapper;
77
		$this->cardMapper = $cardMapper;
78
		$this->permissionService = $permissionService;
79
		$this->userId = $userId;
80
		$this->application = $application;
81
		$this->cache = $cacheFactory->createDistributed('deck-card-attachments-');
82
		$this->l10n = $l10n;
83
		$this->activityManager = $activityManager;
84
		$this->changeHelper = $changeHelper;
85
86
		// Register shipped attachment services
87
		// TODO: move this to a plugin based approach once we have different types of attachments
88
		$this->registerAttachmentService('deck_file', FileService::class);
89
	}
90
91
	/**
92
	 * @param string $type
93
	 * @param string $class
94
	 * @throws \OCP\AppFramework\QueryException
95
	 */
96
	public function registerAttachmentService($type, $class) {
97
		$this->services[$type] = $this->application->getContainer()->query($class);
98
	}
99
100
	/**
101
	 * @param string $type
102
	 * @return IAttachmentService
103
	 * @throws InvalidAttachmentType
104
	 */
105
	public function getService($type) {
106
		if (isset($this->services[$type])) {
107
			return $this->services[$type];
108
		}
109
		throw new InvalidAttachmentType($type);
110
	}
111
112
	/**
113
	 * @param $cardId
114
	 * @return array
115
	 * @throws \OCA\Deck\NoPermissionException
116
	 * @throws BadRequestException
117
	 */
118
	public function findAll($cardId, $withDeleted = false) {
119
120
		if (is_numeric($cardId) === false) {
121
			throw new BadRequestException('card id must be a number');
122
		}
123
124
		$this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_READ);
125
126
		$attachments = $this->attachmentMapper->findAll($cardId);
127
		if ($withDeleted) {
128
			$attachments = array_merge($attachments, $this->attachmentMapper->findToDelete($cardId, false));
129
		}
130
		foreach ($attachments as &$attachment) {
131
			try {
132
				$service = $this->getService($attachment->getType());
133
				$service->extendData($attachment);
134
			} catch (InvalidAttachmentType $e) {
135
				// Ingore invalid attachment types when extending the data
136
			}
137
		}
138
		return $attachments;
139
	}
140
141
	/**
142
	 * @param $cardId
143
	 * @return int|mixed
144
	 * @throws BadRequestException
145
	 */
146
	public function count($cardId) {
147
148
		if (is_numeric($cardId) === false) {
149
			throw new BadRequestException('card id must be a number');
150
		}
151
152
		$count = $this->cache->get('card-' . $cardId);
153
		if (!$count) {
154
			$count = count($this->attachmentMapper->findAll($cardId));
155
			$this->cache->set('card-' . $cardId, $count);
156
		}
157
		return $count;
158
	}
159
160
	/**
161
	 * @param $cardId
162
	 * @param $type
163
	 * @param $data
164
	 * @return Attachment|\OCP\AppFramework\Db\Entity
165
	 * @throws NoPermissionException
166
	 * @throws StatusException
167
	 * @throws BadRequestException
168
	 */
169
	public function create($cardId, $type, $data) {
170
171
		if (is_numeric($cardId) === false) {
172
			throw new BadRequestException('card id must be a number');
173
		}
174
175
		if ($type === false || $type === null) {
176
			throw new BadRequestException('type must be provided');
177
		}
178
179
		if ($data === false || $data === null) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
180
			//throw new BadRequestException('data must be provided');
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
181
		}
182
183
		$this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_EDIT);
184
185
		$this->cache->clear('card-' . $cardId);
186
		$attachment = new Attachment();
187
		$attachment->setCardId($cardId);
188
		$attachment->setType($type);
189
		$attachment->setData($data);
190
		$attachment->setCreatedBy($this->userId);
191
		$attachment->setLastModified(time());
192
		$attachment->setCreatedAt(time());
193
194
		try {
195
			$service = $this->getService($attachment->getType());
196
			$service->create($attachment);
197
		} catch (InvalidAttachmentType $e) {
198
			// just store the data
199
		}
200
		if ($attachment->getData() === null) {
201
			throw new StatusException($this->l10n->t('No data was provided to create an attachment.'));
202
		}
203
		$attachment = $this->attachmentMapper->insert($attachment);
0 ignored issues
show
Deprecated Code introduced by
The method OCP\AppFramework\Db\Mapper::insert() has been deprecated with message: 14.0.0 Move over to QBMapper

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
204
205
		// extend data so the frontend can use it properly after creating
206
		try {
207
			$service = $this->getService($attachment->getType());
208
			$service->extendData($attachment);
209
		} catch (InvalidAttachmentType $e) {
210
			// just store the data
211
		}
212
		$this->changeHelper->cardChanged($attachment->getCardId());
213
		$this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_CARD, $attachment, ActivityManager::SUBJECT_ATTACHMENT_CREATE);
214
		return $attachment;
215
	}
216
217
218
	/**
219
	 * Display the attachment
220
	 *
221
	 * @param $cardId
222
	 * @param $attachmentId
223
	 * @return Response
224
	 * @throws BadRequestException
225
	 * @throws NoPermissionException
226
	 * @throws NotFoundException
227
	 * @throws \OCP\AppFramework\Db\DoesNotExistException
228
	 * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
229
	 */
230
	public function display($cardId, $attachmentId) {
231
232
		if (is_numeric($cardId) === false) {
233
			throw new BadRequestException('card id must be a number');
234
		}
235
236
		if (is_numeric($attachmentId) === false) {
237
			throw new BadRequestException('attachment id must be a number');
238
		}
239
240
		$this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_READ);
241
		$attachment = $this->attachmentMapper->find($attachmentId);
242
243
		try {
244
			$service = $this->getService($attachment->getType());
245
			return $service->display($attachment);
246
		} catch (InvalidAttachmentType $e) {
247
			throw new NotFoundException();
248
		}
249
	}
250
251
	/**
252
	 * Update an attachment with custom data
253
	 *
254
	 * @param $cardId
255
	 * @param $attachmentId
256
	 * @param $request
257
	 * @return mixed
258
	 * @throws \OCA\Deck\NoPermissionException
259
	 * @throws \OCP\AppFramework\Db\DoesNotExistException
260
	 * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
261
	 * @throws BadRequestException
262
	 */
263
	public function update($cardId, $attachmentId, $data) {
264
265
		if (is_numeric($cardId) === false) {
266
			throw new BadRequestException('card id must be a number');
267
		}
268
269
		if (is_numeric($attachmentId) === false) {
270
			throw new BadRequestException('attachment id must be a number');
271
		}
272
273
		if ($data === false || $data === null) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
274
			//throw new BadRequestException('data must be provided');
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
275
		}
276
277
		$this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_EDIT);
278
		$this->cache->clear('card-' . $cardId);
279
280
		$attachment = $this->attachmentMapper->find($attachmentId);
281
		$attachment->setData($data);
282
		try {
283
			$service = $this->getService($attachment->getType());
284
			$service->update($attachment);
285
		} catch (InvalidAttachmentType $e) {
286
			// just update without further action
287
		}
288
		$attachment->setLastModified(time());
289
		$this->attachmentMapper->update($attachment);
0 ignored issues
show
Deprecated Code introduced by
The method OCP\AppFramework\Db\Mapper::update() has been deprecated with message: 14.0.0 Move over to QBMapper

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
290
		// extend data so the frontend can use it properly after creating
291
		try {
292
			$service = $this->getService($attachment->getType());
293
			$service->extendData($attachment);
294
		} catch (InvalidAttachmentType $e) {
295
			// just store the data
296
		}
297
		$this->changeHelper->cardChanged($attachment->getCardId());
298
		$this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_CARD, $attachment, ActivityManager::SUBJECT_ATTACHMENT_UPDATE);
299
		return $attachment;
300
	}
301
302
	/**
303
	 * Either mark an attachment as deleted for later removal or just remove it depending
304
	 * on the IAttachmentService implementation
305
	 *
306
	 * @param $cardId
307
	 * @param $attachmentId
308
	 * @return \OCP\AppFramework\Db\Entity
309
	 * @throws \OCA\Deck\NoPermissionException
310
	 * @throws \OCP\AppFramework\Db\DoesNotExistException
311
	 * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
312
	 * @throws BadRequestException
313
	 */
314
	public function delete($cardId, $attachmentId) {
315
316
		if (is_numeric($cardId) === false) {
317
			throw new BadRequestException('card id must be a number');
318
		}
319
320
		if (is_numeric($attachmentId) === false) {
321
			throw new BadRequestException('attachment id must be a number');
322
		}
323
324
		$this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_EDIT);
325
		$this->cache->clear('card-' . $cardId);
326
327
		$attachment = $this->attachmentMapper->find($attachmentId);
328
		try {
329
			$service = $this->getService($attachment->getType());
330
			if ($service->allowUndo()) {
331
				$service->markAsDeleted($attachment);
332
				$this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_CARD, $attachment, ActivityManager::SUBJECT_ATTACHMENT_DELETE);
333
				return $this->attachmentMapper->update($attachment);
0 ignored issues
show
Deprecated Code introduced by
The method OCP\AppFramework\Db\Mapper::update() has been deprecated with message: 14.0.0 Move over to QBMapper

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
334
			}
335
			$service->delete($attachment);
336
		} catch (InvalidAttachmentType $e) {
337
			// just delete without further action
338
		}
339
		$attachment = $this->attachmentMapper->delete($attachment);
0 ignored issues
show
Deprecated Code introduced by
The method OCP\AppFramework\Db\Mapper::delete() has been deprecated with message: 14.0.0 Move over to QBMapper

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
340
		$this->changeHelper->cardChanged($attachment->getCardId());
341
		$this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_CARD, $attachment, ActivityManager::SUBJECT_ATTACHMENT_DELETE);
342
		return $attachment;
343
	}
344
345
	public function restore($cardId, $attachmentId) {
346
347
		if (is_numeric($cardId) === false) {
348
			throw new BadRequestException('card id must be a number');
349
		}
350
351
		if (is_numeric($attachmentId) === false) {
352
			throw new BadRequestException('attachment id must be a number');
353
		}
354
355
		$this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_EDIT);
356
		$this->cache->clear('card-' . $cardId);
357
358
		$attachment = $this->attachmentMapper->find($attachmentId);
359
		try {
360
			$service = $this->getService($attachment->getType());
361
			if ($service->allowUndo()) {
362
				$attachment->setDeletedAt(0);
363
				$attachment = $this->attachmentMapper->update($attachment);
0 ignored issues
show
Deprecated Code introduced by
The method OCP\AppFramework\Db\Mapper::update() has been deprecated with message: 14.0.0 Move over to QBMapper

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
364
				$this->changeHelper->cardChanged($attachment->getCardId());
365
				$this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_CARD, $attachment, ActivityManager::SUBJECT_ATTACHMENT_RESTORE);
366
				return $attachment;
367
			}
368
		} catch (InvalidAttachmentType $e) {
369
		}
370
		throw new NoPermissionException('Restore is not allowed.');
371
	}
372
}
373