Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
1 | <?php |
||
26 | class TransferRequestManager implements INotifier { |
||
27 | |||
28 | /** @var IRootFolder */ |
||
29 | protected $rootFolder; |
||
30 | /** @var IManager */ |
||
31 | protected $notificationManager; |
||
32 | /** @var IUserManager */ |
||
33 | protected $userManager; |
||
34 | /** @var ITimeFactory */ |
||
35 | protected $timeFactory; |
||
36 | /** @var TransferRequestMapper */ |
||
37 | protected $requestMapper; |
||
38 | /** @var IURLGenerator */ |
||
39 | protected $urlGenerator; |
||
40 | /** @var IFactory */ |
||
41 | protected $factory; |
||
42 | /** @var IJobList */ |
||
43 | protected $jobList; |
||
44 | |||
45 | public function __construct( |
||
46 | IRootFolder $rootFolder, |
||
47 | IManager $notificationManager, |
||
48 | IUserManager $userManager, |
||
49 | ITimeFactory $timeFactory, |
||
50 | TransferRequestMapper $requestMapper, |
||
51 | IURLGenerator $urlGenerator, |
||
52 | IFactory $factory, |
||
53 | IJobList $jobList) { |
||
54 | $this->rootFolder = $rootFolder; |
||
55 | $this->notificationManager = $notificationManager; |
||
56 | $this->userManager = $userManager; |
||
57 | $this->timeFactory = $timeFactory; |
||
58 | $this->requestMapper = $requestMapper; |
||
59 | $this->urlGenerator = $urlGenerator; |
||
60 | $this->factory = $factory; |
||
61 | $this->jobList = $jobList; |
||
62 | } |
||
63 | |||
64 | /** |
||
65 | * @param IUser $sourceUser |
||
66 | * @param IUser $destinationUser |
||
67 | * @param $fileId |
||
68 | * @throws NotFoundException |
||
69 | * @throws \Exception |
||
70 | */ |
||
71 | public function newTransferRequest(IUser $sourceUser, IUser $destinationUser, $fileId) { |
||
72 | // Cannot give to self |
||
73 | if ($sourceUser->getUID() === $destinationUser->getUID()) { |
||
74 | throw new \Exception('Cannot transfer to self'); |
||
75 | } |
||
76 | // Check node exists |
||
77 | $sourceFolder = $this->rootFolder->getById($fileId)[0]; |
||
78 | // Check source user owns the node |
||
79 | if ($sourceFolder->getOwner()->getUID() !== $sourceUser->getUID()) { |
||
80 | throw new NotPermittedException('Cannot move a file you dont own'); |
||
81 | } |
||
82 | // Check the folder is on persistent lockable storage otherwise we can't do this in the background |
||
83 | if (!$sourceFolder->getStorage() instanceof IPersistentLockingStorage) { |
||
84 | throw new \Exception('Source folder storage not lockable'); |
||
85 | } |
||
86 | // Check therer is no request with the same signature |
||
87 | if (count($this->requestMapper->findRequestWithSameSignature($sourceUser->getUID(), $destinationUser->getUID(), $fileId)) > 0) { |
||
88 | // There is |
||
89 | throw new \Exception('There is already a request to transfer this file/folder'); |
||
90 | } |
||
91 | // Check we are not trying to request a transfer for a folder that is inside a current request |
||
92 | $folder = $sourceFolder; |
||
93 | $fileids = [$folder->getId()]; |
||
94 | while($folder->getPath() !== '/') { |
||
95 | $folder = $folder->getParent(); |
||
96 | $fileids[] = $folder->getId(); |
||
97 | } |
||
98 | if (count($this->requestMapper->findOpenRequestForGivenFiles($fileids)) > 0) { |
||
99 | throw new \Exception('This file/folder is already pending an existing transfer'); |
||
100 | } |
||
101 | |||
102 | // Create the transfer request object |
||
103 | $request = new TransferRequest(); |
||
104 | $request->setRequestedTime($this->timeFactory->getTime()); |
||
105 | $request->setSourceUserId($sourceUser->getUID()); |
||
106 | $request->setDestinationUserId($destinationUser->getUID()); |
||
107 | $request->setFileId($fileId); |
||
108 | $request = $this->requestMapper->insert($request); |
||
109 | |||
110 | /** @var IPersistentLockingStorage $storage */ |
||
111 | $storage = $sourceFolder->getStorage(); |
||
112 | try { |
||
113 | $storage->lockNodePersistent($sourceFolder->getInternalPath(), [ |
||
114 | 'depth' => ILock::LOCK_DEPTH_INFINITE, |
||
115 | 'token' => $this->getLockTokenFromRequest($request), |
||
|
|||
116 | 'timeout' => 60*60*2 // 2 hours to allow a cron run |
||
117 | ]); |
||
118 | } catch (\Exception $e) { |
||
119 | // Cleanup transfer request and fail |
||
120 | $this->requestMapper->delete($request); |
||
121 | throw $e; |
||
122 | } |
||
123 | |||
124 | $this->sendRequestNotification($request); |
||
125 | } |
||
126 | |||
127 | public function acceptRequest(TransferRequest $request) { |
||
128 | View Code Duplication | if ($request->getAcceptedTime() !== null || $request->getActionedTime() !== null || $request->getRejectedTime() !== null) { |
|
129 | throw new \Exception('Already actioned, accepted or rejected'); |
||
130 | } |
||
131 | // Create a background job, update accepted time |
||
132 | $request->setAcceptedTime($this->timeFactory->getTime()); |
||
133 | $this->requestMapper->update($request); |
||
134 | $sourcePath = $this->rootFolder->getUserFolder( |
||
135 | $request->getSourceUserId())->getById($request->getFileId())[0]->getInternalPath(); |
||
136 | $this->jobList->add(TransferOwnership::class, json_encode([ |
||
137 | 'sourcePath' => $sourcePath, |
||
138 | 'sourceUser' => $request->getSourceUserId(), |
||
139 | 'destinationUser' => $request->getDestinationUserId() |
||
140 | ])); |
||
141 | $notification = $this->notificationManager->createNotification(); |
||
142 | $notification->setApp('files') |
||
143 | ->setUser($request->getDestinationUserId()) |
||
144 | ->setObject('transfer_request', $request->getId()); |
||
145 | $this->notificationManager->markProcessed($notification); |
||
146 | } |
||
147 | |||
148 | public function rejectRequest(TransferRequest $request) { |
||
149 | // Cleanup the lock, save reject timestamp |
||
150 | View Code Duplication | if ($request->getAcceptedTime() !== null || $request->getActionedTime() !== null || $request->getRejectedTime() !== null) { |
|
151 | throw new \Exception('Already actioned, accepted or rejected'); |
||
152 | } |
||
153 | // Create a background job, update accepted time |
||
154 | $request->setRejectedTime($this->timeFactory->getTime()); |
||
155 | $this->requestMapper->update($request); |
||
156 | $notification = $this->notificationManager->createNotification(); |
||
157 | $notification->setApp('files') |
||
158 | ->setUser($request->getDestinationUserId()) |
||
159 | ->setObject('transfer_request', $request->getId()); |
||
160 | $this->notificationManager->markProcessed($notification); |
||
161 | $file = $this->rootFolder->getById($request->getFileId())[0]; |
||
162 | /** @var IPersistentLockingStorage $storage */ |
||
163 | $storage = $file->getStorage(); |
||
164 | $storage->unlockNodePersistent($file->getInternalPath(), ['token' => $this->getLockTokenFromRequest($request)]); |
||
165 | } |
||
166 | |||
167 | public function deleteRequest(TransferRequest $request) { |
||
168 | // Cleanup the lock and the notification |
||
169 | $this->requestMapper->delete($request); |
||
170 | $notification = $this->notificationManager->createNotification(); |
||
171 | $notification->setApp('files') |
||
172 | ->setUser($request->getDestinationUserId()) |
||
173 | ->setObject('transfer_request', $request->getId()); |
||
174 | $this->notificationManager->markProcessed($notification); |
||
175 | $file = $this->rootFolder->getById($request->getFileId())[0]; |
||
176 | /** @var IPersistentLockingStorage $storage */ |
||
177 | $storage = $file->getStorage(); |
||
178 | $storage->unlockNodePersistent($file->getInternalPath(), ['token' => $this->getLockTokenFromRequest($request)]); |
||
179 | } |
||
180 | |||
181 | |||
182 | /** |
||
183 | * @param TransferRequest $request the request object |
||
184 | */ |
||
185 | protected function sendRequestNotification(TransferRequest $request) { |
||
186 | $time = new \DateTime(); |
||
187 | $time->setTimestamp($this->timeFactory->getTime()); |
||
188 | $notification = $this->notificationManager->createNotification(); |
||
189 | $notification->setApp('files') |
||
190 | ->setUser($request->getDestinationUserId()) |
||
191 | ->setDateTime($time) |
||
192 | ->setObject('transfer_request', $request->getId()); |
||
193 | |||
194 | $notification->setIcon( |
||
195 | $this->urlGenerator->imagePath('core', 'actions/give.svg') |
||
196 | ); |
||
197 | |||
198 | $sourceUser = $this->userManager->get($request->getSourceUserId()); |
||
199 | $folder = $this->rootFolder->getById($request->getFileId())[0]; |
||
200 | $notification->setSubject("new_transfer_request"); |
||
201 | $notification->setMessage("new_transfer_request", [$sourceUser->getDisplayName(), $folder->getName(), Util::humanFileSize($folder->getSize())]); |
||
202 | |||
203 | $endpoint = $this->urlGenerator->linkToRouteAbsolute( |
||
204 | 'files.Transfer.accept', |
||
205 | ['requestId' => $request->getId()] |
||
206 | ); |
||
207 | $declineAction = $notification->createAction(); |
||
208 | $declineAction->setLabel('reject'); |
||
209 | $declineAction->setLink($endpoint, 'DELETE'); |
||
210 | $notification->addAction($declineAction); |
||
211 | |||
212 | $acceptAction = $notification->createAction(); |
||
213 | $acceptAction->setLabel('accept'); |
||
214 | $acceptAction->setLink($endpoint, 'POST'); |
||
215 | $acceptAction->setPrimary(true); |
||
216 | $notification->addAction($acceptAction); |
||
217 | |||
218 | $this->notificationManager->notify($notification); |
||
219 | } |
||
220 | |||
221 | public function prepare(INotification $notification, $languageCode) { |
||
244 | |||
245 | protected function formatNotification(INotification $notification, IL10N $l) { |
||
246 | switch($notification->getObjectType()) { |
||
247 | case 'transfer_request': |
||
248 | $notification->setParsedSubject((string) $l->t('A user would like to transfer a folder to you')); |
||
249 | $notification->setParsedMessage( |
||
250 | (string) $l->t( |
||
285 | |||
286 | public function actionRequest(TransferRequest $request) { |
||
292 | |||
293 | /** |
||
294 | * Tell the source user that the trasnfer has happened |
||
295 | * @param TransferRequest $request |
||
296 | * @throws NotFoundException |
||
297 | * @throws \OCP\Files\InvalidPathException |
||
298 | */ |
||
299 | public function notifyActioned(TransferRequest $request) { |
||
319 | |||
320 | public function cleanup() { |
||
344 | |||
345 | protected function getLockTokenFromRequest(TransferRequest $request) { |
||
348 | |||
349 | public function getRequestById($requestId) { |
||
352 | |||
353 | |||
354 | } |
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.