Completed
Pull Request — master (#32545)
by Tom
09:21 queued 17s
created

TransferRequestManager::formatNotification()   B

Complexity

Conditions 9
Paths 9

Size

Total Lines 67

Duplication

Lines 51
Ratio 76.12 %

Importance

Changes 0
Metric Value
cc 9
nc 9
nop 2
dl 51
loc 67
rs 7.1644
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * @author Tom Needham <[email protected]>
4
 *
5
 * @copyright Copyright (c) 2018, ownCloud GmbH
6
 * @license AGPL-3.0
7
 *
8
 * This code is free software: you can redistribute it and/or modify
9
 * it under the terms of the GNU Affero General Public License, version 3,
10
 * as published by the Free Software Foundation.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
 * GNU Affero General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Affero General Public License, version 3,
18
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
19
 *
20
 */
21
22
namespace OCA\Files\Service\TransferOwnership;
23
24
use OCA\Files\BackgroundJob\TransferOwnership;
25
use OCP\AppFramework\Db\DoesNotExistException;
26
use OCP\AppFramework\Utility\ITimeFactory;
27
use OCP\BackgroundJob\IJobList;
28
use OCP\Files\IRootFolder;
29
use OCP\Files\NotFoundException;
30
use OCP\Files\NotPermittedException;
31
use OCP\Files\Storage\IPersistentLockingStorage;
32
use OCP\IL10N;
33
use OCP\IURLGenerator;
34
use OCP\IUser;
35
use OCP\IUserManager;
36
use OCP\L10N\IFactory;
37
use OCP\Lock\Persistent\ILock;
38
use OCP\Notification\IManager;
39
use OCP\Notification\INotification;
40
use OCP\Notification\INotifier;
41
use OCP\Util;
42
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
43
use Symfony\Component\EventDispatcher\GenericEvent;
44
45
class TransferRequestManager implements INotifier {
46
47
	/** @var IRootFolder */
48
	protected $rootFolder;
49
	/** @var IManager */
50
	protected $notificationManager;
51
	/** @var IUserManager  */
52
	protected $userManager;
53
	/** @var ITimeFactory */
54
	protected $timeFactory;
55
	/** @var TransferRequestMapper  */
56
	protected $requestMapper;
57
	/** @var IURLGenerator  */
58
	protected $urlGenerator;
59
	/** @var IFactory */
60
	protected $factory;
61
	/** @var IJobList */
62
	protected $jobList;
63
	/** @var EventDispatcherInterface  */
64
	protected $eventDispatcher;
65
66
	public function __construct(
67
		IRootFolder $rootFolder,
68
		IManager $notificationManager,
69
		IUserManager $userManager,
70
		ITimeFactory $timeFactory,
71
		TransferRequestMapper $requestMapper,
72
		IURLGenerator $urlGenerator,
73
		IFactory $factory,
74
		IJobList $jobList,
75
		EventDispatcherInterface $eventDispatcher) {
76
		$this->rootFolder = $rootFolder;
77
		$this->notificationManager = $notificationManager;
78
		$this->userManager = $userManager;
79
		$this->timeFactory = $timeFactory;
80
		$this->requestMapper = $requestMapper;
81
		$this->urlGenerator = $urlGenerator;
82
		$this->factory = $factory;
83
		$this->jobList = $jobList;
84
		$this->eventDispatcher = $eventDispatcher;
85
	}
86
87
	/**
88
	 * @param IUser $sourceUser
89
	 * @param IUser $destinationUser
90
	 * @param $fileId
91
	 * @throws NotFoundException
92
	 * @throws \Exception
93
	 */
94
	public function newTransferRequest(IUser $sourceUser, IUser $destinationUser, $fileId) {
95
		// Cannot give to self
96
		if ($sourceUser->getUID() === $destinationUser->getUID()) {
97
			throw new \Exception('Cannot transfer to self');
98
		}
99
		// Check node exists
100
		$sourceFolder = $this->rootFolder->getById($fileId)[0];
101
		// Check source user owns the node
102
		if ($sourceFolder->getOwner()->getUID() !== $sourceUser->getUID()) {
103
			throw new NotPermittedException('Cannot move a file you dont own');
104
		}
105
		// Check the folder is on persistent lockable storage otherwise we can't do this in the background
106
		if (!$sourceFolder->getStorage() instanceof IPersistentLockingStorage) {
107
			throw new \Exception('Source folder storage not lockable');
108
		}
109
		// Check therer is no request with the same signature
110
		if (count($this->requestMapper->findRequestWithSameSignature($sourceUser->getUID(), $destinationUser->getUID(), $fileId)) > 0) {
111
			// There is
112
			throw new \Exception('There is already a request to transfer this file/folder');
113
		}
114
		// Check we are not trying to request a transfer for a folder that is inside a current request
115
		$folder = $sourceFolder;
116
		$fileids = [$folder->getId()];
117
		while($folder->getPath() !== '/') {
118
			$folder = $folder->getParent();
119
			$fileids[] = $folder->getId();
120
		}
121
		if (count($this->requestMapper->findOpenRequestForGivenFiles($fileids)) > 0) {
122
			throw new \Exception('This file/folder is already pending an existing transfer');
123
		}
124
125
		// Create the transfer request object
126
		$request = new TransferRequest();
127
		$request->setRequestedTime($this->timeFactory->getTime());
128
		$request->setSourceUserId($sourceUser->getUID());
129
		$request->setDestinationUserId($destinationUser->getUID());
130
		$request->setFileId($fileId);
131
		$request = $this->requestMapper->insert($request);
132
133
		/** @var IPersistentLockingStorage $storage */
134
		$storage = $sourceFolder->getStorage();
135
		try {
136
			$storage->lockNodePersistent($sourceFolder->getInternalPath(), [
137
				'depth' => ILock::LOCK_DEPTH_INFINITE,
138
				'token' => $this->getLockTokenFromRequest($request),
0 ignored issues
show
Compatibility introduced by
$request of type object<OCP\AppFramework\Db\Entity> is not a sub-type of object<OCA\Files\Service...ership\TransferRequest>. It seems like you assume a child class of the class OCP\AppFramework\Db\Entity to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
139
				'timeout' => 60*60*24 // 24 hours to allow a cron run and acceptance
140
			]);
141
		} catch (\Exception $e) {
142
			// Cleanup transfer request and fail
143
			$this->requestMapper->delete($request);
144
			throw $e;
145
		}
146
147
		$this->sendRequestNotification($request);
0 ignored issues
show
Compatibility introduced by
$request of type object<OCP\AppFramework\Db\Entity> is not a sub-type of object<OCA\Files\Service...ership\TransferRequest>. It seems like you assume a child class of the class OCP\AppFramework\Db\Entity to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
148
	}
149
150
	public function acceptRequest(TransferRequest $request) {
151 View Code Duplication
		if ($request->getAcceptedTime() !== null || $request->getActionedTime() !== null || $request->getRejectedTime() !== null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
152
			throw new \Exception('Already actioned, accepted or rejected');
153
		}
154
		// Create a background job, update accepted time
155
		$request->setAcceptedTime($this->timeFactory->getTime());
156
		$this->requestMapper->update($request);
157
		$sourcePath = $this->rootFolder->getUserFolder(
158
			$request->getSourceUserId())->getById($request->getFileId())[0]->getInternalPath();
159
		$this->jobList->add(TransferOwnership::class, json_encode([
160
			'requestId' => $request->getId(),
161
		]));
162
		$notification = $this->notificationManager->createNotification();
163
		$notification->setApp('files')
164
			->setUser($request->getDestinationUserId())
165
			->setObject('transfer_request', $request->getId());
166
		$this->notificationManager->markProcessed($notification);
167
	}
168
169
	public function rejectRequest(TransferRequest $request) {
170
		// Cleanup the lock, save reject timestamp
171 View Code Duplication
		if ($request->getAcceptedTime() !== null || $request->getActionedTime() !== null || $request->getRejectedTime() !== null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
172
			throw new \Exception('Already actioned, accepted or rejected');
173
		}
174
		$request->setRejectedTime($this->timeFactory->getTime());
175
		$this->requestMapper->update($request);
176
		$notification = $this->notificationManager->createNotification();
177
		$notification->setApp('files')
178
			->setUser($request->getDestinationUserId())
179
			->setObject('transfer_request', $request->getId());
180
		$this->notificationManager->markProcessed($notification);
181
		$file = $this->rootFolder->getUserFolder($request->getSourceUserId())->getById($request->getFileId())[0];
182
		/** @var IPersistentLockingStorage $storage */
183
		$storage = $file->getStorage();
184
		$storage->unlockNodePersistent($file->getInternalPath(), ['token' => $this->getLockTokenFromRequest($request)]);
185
	}
186
187
	public function deleteRequest(TransferRequest $request) {
188
		// Cleanup the lock and the notification
189
		$this->requestMapper->delete($request);
190
		$notification = $this->notificationManager->createNotification();
191
		$notification->setApp('files')
192
			->setUser($request->getDestinationUserId())
193
			->setObject('transfer_request', $request->getId());
194
		$this->notificationManager->markProcessed($notification);
195
		$file = $this->rootFolder->getById($request->getFileId())[0];
196
		/** @var IPersistentLockingStorage $storage */
197
		$storage = $file->getStorage();
198
		$storage->unlockNodePersistent($file->getInternalPath(), ['token' => $this->getLockTokenFromRequest($request)]);
199
	}
200
201
202
	/**
203
	 * @param TransferRequest $request the request object
204
	 */
205
	protected function sendRequestNotification(TransferRequest $request) {
206
		$time = new \DateTime();
207
		$time->setTimestamp($this->timeFactory->getTime());
208
		$notification = $this->notificationManager->createNotification();
209
		$notification->setApp('files')
210
			->setUser($request->getDestinationUserId())
211
			->setDateTime($time)
212
			->setObject('transfer_request', $request->getId());
213
214
		$notification->setIcon(
215
			$this->urlGenerator->imagePath('core', 'actions/give.svg')
216
		);
217
218
		$sourceUser = $this->userManager->get($request->getSourceUserId());
219
		$sourceUserFolder = $this->rootFolder->getUserFolder($sourceUser->getUID());
220
		$folder = $sourceUserFolder->getById($request->getFileId())[0];
221
		$notification->setSubject("new_transfer_request");
222
		$notification->setMessage("new_transfer_request", [$sourceUser->getDisplayName(), $folder->getName(), Util::humanFileSize($folder->getSize())]);
223
224
		$endpoint = $this->urlGenerator->linkToRouteAbsolute(
225
			'files.Transfer.accept',
226
			['requestId' => $request->getId()]
227
		);
228
		$declineAction = $notification->createAction();
229
		$declineAction->setLabel('reject');
230
		$declineAction->setLink($endpoint, 'DELETE');
231
		$notification->addAction($declineAction);
232
233
		$acceptAction = $notification->createAction();
234
		$acceptAction->setLabel('accept');
235
		$acceptAction->setLink($endpoint, 'POST');
236
		$acceptAction->setPrimary(true);
237
		$notification->addAction($acceptAction);
238
239
		$this->notificationManager->notify($notification);
240
	}
241
242
	public function prepare(INotification $notification, $languageCode) {
243
		if ($notification->getApp() !== 'files') {
244
			throw new \InvalidArgumentException();
245
		}
246
247
		// Read the language from the notification
248
		$l = $this->factory->get('files', $languageCode);
249
250
		switch ($notification->getObjectType()) {
251
			case 'transfer_request':
252
				$requestId = $notification->getObjectId();
253
				try {
254
					$this->requestMapper->findById($requestId);
255
				} catch (DoesNotExistException $ex) {
256
					$this->notificationManager->markProcessed($notification);
257
					throw new \InvalidArgumentException();
258
				}
259
				return $this->formatNotification($notification, $l);
260
261
			default:
262
				throw new \InvalidArgumentException();
263
		}
264
	}
265
266
	protected function formatNotification(INotification $notification, IL10N $l) {
267
		switch($notification->getSubject()) {
268
			case 'new_transfer_request':
269
				$notification->setParsedSubject((string) $l->t('A user would like to transfer a folder to you'));
270
				$notification->setParsedMessage(
271
					(string) $l->t(
272
						'"%1$s" requested to transfer "%2$s" to you (%3$s)"',
273
						$notification->getMessageParameters())
274
				);
275 View Code Duplication
				foreach ($notification->getActions() as $action) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
276
					switch ($action->getLabel()) {
277
						case 'accept':
278
							$action->setParsedLabel(
279
								(string) $l->t('Accept')
280
							);
281
							break;
282
						case 'reject':
283
							$action->setParsedLabel(
284
								(string) $l->t('Decline')
285
							);
286
							break;
287
					}
288
					$notification->addParsedAction($action);
289
				}
290
				return $notification;
291
				break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
292 View Code Duplication
			case 'transfer_request_actioned_source':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
293
				$notification->setParsedSubject((string) $l->t('Transfer completed'));
294
				$notification->setParsedMessage(
295
					(string) $l->t(
296
						'"%1$s" accepted your transfer of "%2$s" and it was completed',
297
						$notification->getMessageParameters())
298
				);
299
				return $notification;
300
				break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
301 View Code Duplication
			case 'transfer_request_actioned_destination':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
302
				$notification->setParsedSubject((string) $l->t('Transfer completed'));
303
				$notification->setParsedMessage(
304
					(string) $l->t(
305
						'"%1$s" was transferred to you from "%2$s"',
306
						$notification->getMessageParameters())
307
				);
308
				return $notification;
309
				break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
310 View Code Duplication
			case 'transfer_request_failed_destination':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
311
				$notification->setParsedSubject((string) $l->t('Transfer failed'));
312
				$notification->setParsedMessage(
313
					(string) $l->t(
314
						'The transfer of "%1$s" from "%2$s failed. Ask the sender to try again."',
315
						$notification->getMessageParameters())
316
				);
317
				return $notification;
318
			break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
319 View Code Duplication
			case 'transfer_request_failed_source':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
320
				$notification->setParsedSubject((string) $l->t('Transfer failed'));
321
				$notification->setParsedMessage(
322
					(string) $l->t(
323
						'The transfer of "%1$s" to "%2$s" failed with message: "%3$s"',
324
						$notification->getMessageParameters())
325
				);
326
				return $notification;
327
				break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
328
			default:
329
				throw new \InvalidArgumentException('Not a notifcation that can be formatted by this class');
330
		}
331
332
	}
333
334
	public function actionRequest(TransferRequest $request) {
335
		$request->setActionedTime($this->timeFactory->getTime());
336
		$this->requestMapper->update($request);
337
		// Notify the source user it was accepted
338
		$this->notifyActioned($request);
339
		$this->eventDispatcher->dispatch('files:user-transfer-complete', new GenericEvent([
340
			'requestId' => $request,
341
			'sourceUser' => $request->getSourceUserId(),
342
			'destinationUser' => $request->getDestinationUserId(),
343
			'sourceNode' => $request->getFileId()
344
		]));
345
	}
346
347
	/**
348
	 * Cleanup and notify
349
	 * @param TransferRequest $request
350
	 * @throws NotFoundException
351
	 * @throws \OCP\Files\InvalidPathException
352
	 */
353
	public function actionRequestFailure(TransferRequest $request, $message = null) {
354
		// Notify the users that it failed
355
		$this->notifyActionedFailure($request, $message);
356
		$this->cleanupRequest($request);
357
358
	}
359
360
	/**
361
	 * Tell the source user and destination user that the transfer has happened
362
	 * @param TransferRequest $request
363
	 * @throws NotFoundException
364
	 * @throws \OCP\Files\InvalidPathException
365
	 */
366
	public function notifyActioned(TransferRequest $request) {
367
		// Set to now
368
		$time = new \DateTime();
369
		$time->setTimestamp($request->getActionedTime());
370
		$notification = $this->notificationManager->createNotification();
371
		$notification->setApp('files')
372
			->setUser($request->getSourceUserId())
373
			->setDateTime($time)
374
			->setObject('transfer_request', $request->getId());
375
376
		$notification->setIcon(
377
			$this->urlGenerator->imagePath('core', 'actions/give.svg')
378
		);
379
380
		$destinationUser = $this->userManager->get($request->getDestinationUserId());
381
		$folder = $this->rootFolder->getById($request->getFileId())[0];
382
		$notification->setSubject("transfer_request_actioned_source");
383
		$notification->setMessage("transfer_request_actioned_source", [$destinationUser->getDisplayName(), $folder->getName()]);
384
		$this->notificationManager->notify($notification);
385
386
		// Set to now
387
		$time = new \DateTime();
388
		$time->setTimestamp($request->getActionedTime());
389
		$notification = $this->notificationManager->createNotification();
390
		$notification->setApp('files')
391
			->setUser($request->getDestinationUserId())
392
			->setDateTime($time)
393
			->setObject('transfer_request', $request->getId());
394
395
		$notification->setIcon(
396
			$this->urlGenerator->imagePath('core', 'actions/give.svg')
397
		);
398
399
		$sourceUser = $this->userManager->get($request->getSourceUserId());
400
		$folder = $this->rootFolder->getById($request->getFileId())[0];
401
		$notification->setSubject("transfer_request_actioned_destination");
402
		$notification->setMessage("transfer_request_actioned_destination", [$folder->getName(), $sourceUser->getDisplayName()]);
403
		$notification->setLink($this->urlGenerator->getAbsoluteURL('/f/'.$folder->getId()));
404
		$this->notificationManager->notify($notification);
405
	}
406
407
	/**
408
	 * Tell the source user and destination user that the transfer failed
409
	 * @param TransferRequest $request
410
	 * @throws NotFoundException
411
	 * @throws \OCP\Files\InvalidPathException
412
	 */
413
	public function notifyActionedFailure(TransferRequest $request, $message = null) {
414
		// Set to now
415
		$time = new \DateTime();
416
		$time->setTimestamp($this->timeFactory->getTime());
417
		$notification = $this->notificationManager->createNotification();
418
		$notification->setApp('files')
419
			->setUser($request->getSourceUserId())
420
			->setDateTime($time)
421
			->setObject('transfer_request', $request->getId());
422
423
		$notification->setIcon(
424
			$this->urlGenerator->imagePath('core', 'actions/give.svg')
425
		);
426
427
		$destinationUser = $this->userManager->get($request->getDestinationUserId());
428
		$folder = $this->rootFolder->getById($request->getFileId())[0];
429
		$notification->setSubject("transfer_request_failed_source");
430
		$notification->setMessage("transfer_request_failed_source", [$folder->getName(), $destinationUser->getDisplayName(), $message]);
431
		$this->notificationManager->notify($notification);
432
433
		// Set to now
434
		$time = new \DateTime();
435
		$time->setTimestamp($this->timeFactory->getTime());
436
		$notification = $this->notificationManager->createNotification();
437
		$notification->setApp('files')
438
			->setUser($request->getDestinationUserId())
439
			->setDateTime($time)
440
			->setObject('transfer_request', $request->getId());
441
442
		$notification->setIcon(
443
			$this->urlGenerator->imagePath('core', 'actions/give.svg')
444
		);
445
446
		$sourceUser = $this->userManager->get($request->getSourceUserId());
447
		$folder = $this->rootFolder->getById($request->getFileId())[0];
448
		$notification->setSubject("transfer_request_failed_destination");
449
		$notification->setMessage("transfer_request_failed_destination", [$folder->getName(), $sourceUser->getDisplayName()]);
450
		$this->notificationManager->notify($notification);
451
	}
452
453
	/**
454
	 * Background job for cleaning up old requests, removes notifications, request and locks
455
	 */
456
	public function cleanup() {
457
		// Delete request that are older than 24 hours
458
		$oldRequests = $this->requestMapper->findOpenRequestsOlderThan(1);
459
		/** @var TransferRequest $request */
460
		foreach ($oldRequests as $request) {
461
			$this->cleanupRequest($request);
462
		}
463
	}
464
465
	/**
466
	 * Delete request, delete lock, and delete requested notification
467
	 * @param TransferRequest $request
468
	 */
469
	protected function cleanupRequest(TransferRequest $request) {
470
		// Remove the lock
471
		try {
472
			$file = $this->rootFolder->getById($request->getFileId())[0];
473
			/** @var IPersistentLockingStorage $storage */
474
			$storage = $file->getStorage();
475
			$storage->unlockNodePersistent($file->getInternalPath(), ['token' => $this->getLockTokenFromRequest($request)]);
476
		} catch (\Exception $e) {
477
			\OC::$server->getLogger()->logException($e, ['app' => 'files']);
478
		}
479
		// Now remove the request
480
		$this->requestMapper->delete($request);
481
		// And lets remove the notification to save confusion
482
		$notification = $this->notificationManager->createNotification();
483
		$notification->setApp('files')
484
			->setUser($request->getDestinationUserId())
485
			->setObject('transfer_request', $request->getId());
486
		$this->notificationManager->markProcessed($notification);
487
	}
488
489
	/**
490
	 * Helper to get the lock token id associated with a request
491
	 * @param TransferRequest $request
492
	 * @return string
493
	 */
494
	protected function getLockTokenFromRequest(TransferRequest $request) {
495
		return 'transfer-request-'.$request->getId();
496
	}
497
498
	/**
499
	 * Helper to get a request object from the mapper without another dep injection
500
	 * @param $requestId
501
	 * @return TransferRequest|\OCP\AppFramework\Db\Entity
502
	 * @throws DoesNotExistException
503
	 * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
504
	 */
505
	public function getRequestById($requestId) {
506
		return $this->requestMapper->findById($requestId);
507
	}
508
509
510
}