Total Complexity | 92 |
Total Lines | 505 |
Duplicated Lines | 0 % |
Changes | 1 | ||
Bugs | 0 | Features | 0 |
Complex classes like Provider 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.
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 Provider, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
44 | class Provider implements IProvider { |
||
45 | |||
46 | /** @var IFactory */ |
||
47 | protected $languageFactory; |
||
48 | |||
49 | /** @var IL10N */ |
||
50 | protected $l; |
||
51 | /** @var IL10N */ |
||
52 | protected $activityLang; |
||
53 | |||
54 | /** @var IURLGenerator */ |
||
55 | protected $url; |
||
56 | |||
57 | /** @var IManager */ |
||
58 | protected $activityManager; |
||
59 | |||
60 | /** @var IUserManager */ |
||
61 | protected $userManager; |
||
62 | |||
63 | /** @var IRootFolder */ |
||
64 | protected $rootFolder; |
||
65 | |||
66 | /** @var IEventMerger */ |
||
67 | protected $eventMerger; |
||
68 | |||
69 | /** @var ICloudIdManager */ |
||
70 | protected $cloudIdManager; |
||
71 | |||
72 | /** @var IContactsManager */ |
||
73 | protected $contactsManager; |
||
74 | |||
75 | /** @var string[] cached displayNames - key is the cloud id and value the displayname */ |
||
76 | protected $displayNames = []; |
||
77 | |||
78 | protected $fileIsEncrypted = false; |
||
79 | |||
80 | public function __construct(IFactory $languageFactory, |
||
96 | } |
||
97 | |||
98 | /** |
||
99 | * @param string $language |
||
100 | * @param IEvent $event |
||
101 | * @param IEvent|null $previousEvent |
||
102 | * @return IEvent |
||
103 | * @throws \InvalidArgumentException |
||
104 | * @since 11.0.0 |
||
105 | */ |
||
106 | public function parse($language, IEvent $event, IEvent $previousEvent = null) { |
||
107 | if ($event->getApp() !== 'files') { |
||
108 | throw new \InvalidArgumentException(); |
||
109 | } |
||
110 | |||
111 | $this->l = $this->languageFactory->get('files', $language); |
||
112 | $this->activityLang = $this->languageFactory->get('activity', $language); |
||
113 | |||
114 | if ($this->activityManager->isFormattingFilteredObject()) { |
||
115 | try { |
||
116 | return $this->parseShortVersion($event, $previousEvent); |
||
117 | } catch (\InvalidArgumentException $e) { |
||
118 | // Ignore and simply use the long version... |
||
119 | } |
||
120 | } |
||
121 | |||
122 | return $this->parseLongVersion($event, $previousEvent); |
||
123 | } |
||
124 | |||
125 | protected function setIcon(IEvent $event, string $icon, string $app = 'files') { |
||
126 | if ($this->activityManager->getRequirePNG()) { |
||
127 | $event->setIcon($this->url->getAbsoluteURL($this->url->imagePath($app, $icon . '.png'))); |
||
128 | } else { |
||
129 | $event->setIcon($this->url->getAbsoluteURL($this->url->imagePath($app, $icon . '.svg'))); |
||
130 | } |
||
131 | } |
||
132 | |||
133 | /** |
||
134 | * @param IEvent $event |
||
135 | * @param IEvent|null $previousEvent |
||
136 | * @return IEvent |
||
137 | * @throws \InvalidArgumentException |
||
138 | * @since 11.0.0 |
||
139 | */ |
||
140 | public function parseShortVersion(IEvent $event, IEvent $previousEvent = null) { |
||
141 | $parsedParameters = $this->getParameters($event); |
||
142 | |||
143 | if ($event->getSubject() === 'created_by') { |
||
144 | $subject = $this->l->t('Created by {user}'); |
||
145 | $this->setIcon($event, 'add-color'); |
||
146 | } else if ($event->getSubject() === 'changed_by') { |
||
147 | $subject = $this->l->t('Changed by {user}'); |
||
148 | $this->setIcon($event, 'change'); |
||
149 | } else if ($event->getSubject() === 'deleted_by') { |
||
150 | $subject = $this->l->t('Deleted by {user}'); |
||
151 | $this->setIcon($event, 'delete-color'); |
||
152 | } else if ($event->getSubject() === 'restored_by') { |
||
153 | $subject = $this->l->t('Restored by {user}'); |
||
154 | $this->setIcon($event, 'actions/history', 'core'); |
||
155 | } else if ($event->getSubject() === 'renamed_by') { |
||
156 | $subject = $this->l->t('Renamed by {user}'); |
||
157 | $this->setIcon($event, 'change'); |
||
158 | } else if ($event->getSubject() === 'moved_by') { |
||
159 | $subject = $this->l->t('Moved by {user}'); |
||
160 | $this->setIcon($event, 'change'); |
||
161 | } else { |
||
162 | throw new \InvalidArgumentException(); |
||
163 | } |
||
164 | |||
165 | if (!isset($parsedParameters['user'])) { |
||
166 | // External user via public link share |
||
167 | $subject = str_replace('{user}', $this->activityLang->t('"remote user"'), $subject); |
||
168 | } |
||
169 | |||
170 | $this->setSubjects($event, $subject, $parsedParameters); |
||
171 | |||
172 | return $this->eventMerger->mergeEvents('user', $event, $previousEvent); |
||
173 | } |
||
174 | |||
175 | /** |
||
176 | * @param IEvent $event |
||
177 | * @param IEvent|null $previousEvent |
||
178 | * @return IEvent |
||
179 | * @throws \InvalidArgumentException |
||
180 | * @since 11.0.0 |
||
181 | */ |
||
182 | public function parseLongVersion(IEvent $event, IEvent $previousEvent = null) { |
||
183 | $this->fileIsEncrypted = false; |
||
184 | $parsedParameters = $this->getParameters($event); |
||
185 | |||
186 | if ($event->getSubject() === 'created_self') { |
||
187 | $subject = $this->l->t('You created {file}'); |
||
188 | if ($this->fileIsEncrypted) { |
||
189 | $subject = $this->l->t('You created an encrypted file in {file}'); |
||
190 | } |
||
191 | $this->setIcon($event, 'add-color'); |
||
192 | } else if ($event->getSubject() === 'created_by') { |
||
193 | $subject = $this->l->t('{user} created {file}'); |
||
194 | if ($this->fileIsEncrypted) { |
||
195 | $subject = $this->l->t('{user} created an encrypted file in {file}'); |
||
196 | } |
||
197 | $this->setIcon($event, 'add-color'); |
||
198 | } else if ($event->getSubject() === 'created_public') { |
||
199 | $subject = $this->l->t('{file} was created in a public folder'); |
||
200 | $this->setIcon($event, 'add-color'); |
||
201 | } else if ($event->getSubject() === 'changed_self') { |
||
202 | $subject = $this->l->t('You changed {file}'); |
||
203 | if ($this->fileIsEncrypted) { |
||
204 | $subject = $this->l->t('You changed an encrypted file in {file}'); |
||
205 | } |
||
206 | $this->setIcon($event, 'change'); |
||
207 | } else if ($event->getSubject() === 'changed_by') { |
||
208 | $subject = $this->l->t('{user} changed {file}'); |
||
209 | if ($this->fileIsEncrypted) { |
||
210 | $subject = $this->l->t('{user} changed an encrypted file in {file}'); |
||
211 | } |
||
212 | $this->setIcon($event, 'change'); |
||
213 | } else if ($event->getSubject() === 'deleted_self') { |
||
214 | $subject = $this->l->t('You deleted {file}'); |
||
215 | if ($this->fileIsEncrypted) { |
||
216 | $subject = $this->l->t('You deleted an encrypted file in {file}'); |
||
217 | } |
||
218 | $this->setIcon($event, 'delete-color'); |
||
219 | } else if ($event->getSubject() === 'deleted_by') { |
||
220 | $subject = $this->l->t('{user} deleted {file}'); |
||
221 | if ($this->fileIsEncrypted) { |
||
222 | $subject = $this->l->t('{user} deleted an encrypted file in {file}'); |
||
223 | } |
||
224 | $this->setIcon($event, 'delete-color'); |
||
225 | } else if ($event->getSubject() === 'restored_self') { |
||
226 | $subject = $this->l->t('You restored {file}'); |
||
227 | $this->setIcon($event, 'actions/history', 'core'); |
||
228 | } else if ($event->getSubject() === 'restored_by') { |
||
229 | $subject = $this->l->t('{user} restored {file}'); |
||
230 | $this->setIcon($event, 'actions/history', 'core'); |
||
231 | } else if ($event->getSubject() === 'renamed_self') { |
||
232 | $subject = $this->l->t('You renamed {oldfile} to {newfile}'); |
||
233 | $this->setIcon($event, 'change'); |
||
234 | } else if ($event->getSubject() === 'renamed_by') { |
||
235 | $subject = $this->l->t('{user} renamed {oldfile} to {newfile}'); |
||
236 | $this->setIcon($event, 'change'); |
||
237 | } else if ($event->getSubject() === 'moved_self') { |
||
238 | $subject = $this->l->t('You moved {oldfile} to {newfile}'); |
||
239 | $this->setIcon($event, 'change'); |
||
240 | } else if ($event->getSubject() === 'moved_by') { |
||
241 | $subject = $this->l->t('{user} moved {oldfile} to {newfile}'); |
||
242 | $this->setIcon($event, 'change'); |
||
243 | } else { |
||
244 | throw new \InvalidArgumentException(); |
||
245 | } |
||
246 | |||
247 | if ($this->fileIsEncrypted) { |
||
248 | $event->setSubject($event->getSubject() . '_enc', $event->getSubjectParameters()); |
||
249 | } |
||
250 | |||
251 | if (!isset($parsedParameters['user'])) { |
||
252 | // External user via public link share |
||
253 | $subject = str_replace('{user}', $this->activityLang->t('"remote user"'), $subject); |
||
254 | } |
||
255 | |||
256 | $this->setSubjects($event, $subject, $parsedParameters); |
||
257 | |||
258 | if ($event->getSubject() === 'moved_self' || $event->getSubject() === 'moved_by') { |
||
259 | $event = $this->eventMerger->mergeEvents('oldfile', $event, $previousEvent); |
||
260 | } else { |
||
261 | $event = $this->eventMerger->mergeEvents('file', $event, $previousEvent); |
||
262 | } |
||
263 | |||
264 | if ($event->getChildEvent() === null) { |
||
265 | // Couldn't group by file, maybe we can group by user |
||
266 | $event = $this->eventMerger->mergeEvents('user', $event, $previousEvent); |
||
267 | } |
||
268 | |||
269 | return $event; |
||
270 | } |
||
271 | |||
272 | protected function setSubjects(IEvent $event, $subject, array $parameters) { |
||
285 | } |
||
286 | |||
287 | /** |
||
288 | * @param IEvent $event |
||
289 | * @return array |
||
290 | * @throws \InvalidArgumentException |
||
291 | */ |
||
292 | protected function getParameters(IEvent $event) { |
||
293 | $parameters = $event->getSubjectParameters(); |
||
294 | switch ($event->getSubject()) { |
||
295 | case 'created_self': |
||
296 | case 'created_public': |
||
297 | case 'changed_self': |
||
298 | case 'deleted_self': |
||
299 | case 'restored_self': |
||
300 | return [ |
||
301 | 'file' => $this->getFile($parameters[0], $event), |
||
302 | ]; |
||
303 | case 'created_by': |
||
304 | case 'changed_by': |
||
305 | case 'deleted_by': |
||
306 | case 'restored_by': |
||
307 | if ($parameters[1] === '') { |
||
308 | // External user via public link share |
||
309 | return [ |
||
310 | 'file' => $this->getFile($parameters[0], $event), |
||
311 | ]; |
||
312 | } |
||
313 | return [ |
||
314 | 'file' => $this->getFile($parameters[0], $event), |
||
315 | 'user' => $this->getUser($parameters[1]), |
||
316 | ]; |
||
317 | case 'renamed_self': |
||
318 | case 'moved_self': |
||
319 | return [ |
||
320 | 'newfile' => $this->getFile($parameters[0]), |
||
321 | 'oldfile' => $this->getFile($parameters[1]), |
||
322 | ]; |
||
323 | case 'renamed_by': |
||
324 | case 'moved_by': |
||
325 | if ($parameters[1] === '') { |
||
326 | // External user via public link share |
||
327 | return [ |
||
328 | 'newfile' => $this->getFile($parameters[0]), |
||
329 | 'oldfile' => $this->getFile($parameters[2]), |
||
330 | ]; |
||
331 | } |
||
332 | return [ |
||
333 | 'newfile' => $this->getFile($parameters[0]), |
||
334 | 'user' => $this->getUser($parameters[1]), |
||
335 | 'oldfile' => $this->getFile($parameters[2]), |
||
336 | ]; |
||
337 | } |
||
338 | return []; |
||
339 | } |
||
340 | |||
341 | /** |
||
342 | * @param array|string $parameter |
||
343 | * @param IEvent|null $event |
||
344 | * @return array |
||
345 | * @throws \InvalidArgumentException |
||
346 | */ |
||
347 | protected function getFile($parameter, IEvent $event = null) { |
||
348 | if (is_array($parameter)) { |
||
349 | $path = reset($parameter); |
||
350 | $id = (string) key($parameter); |
||
351 | } else if ($event !== null) { |
||
352 | // Legacy from before ownCloud 8.2 |
||
353 | $path = $parameter; |
||
354 | $id = $event->getObjectId(); |
||
355 | } else { |
||
356 | throw new \InvalidArgumentException('Could not generate file parameter'); |
||
357 | } |
||
358 | |||
359 | $encryptionContainer = $this->getEndToEndEncryptionContainer($id, $path); |
||
360 | if ($encryptionContainer instanceof Folder) { |
||
361 | $this->fileIsEncrypted = true; |
||
362 | try { |
||
363 | $fullPath = rtrim($encryptionContainer->getPath(), '/'); |
||
364 | // Remove /user/files/... |
||
365 | list(,,, $path) = explode('/', $fullPath, 4); |
||
366 | if (!$path) { |
||
367 | throw new InvalidPathException('Path could not be split correctly'); |
||
368 | } |
||
369 | |||
370 | return [ |
||
371 | 'type' => 'file', |
||
372 | 'id' => $encryptionContainer->getId(), |
||
373 | 'name' => $encryptionContainer->getName(), |
||
374 | 'path' => $path, |
||
375 | 'link' => $this->url->linkToRouteAbsolute('files.viewcontroller.showFile', ['fileid' => $encryptionContainer->getId()]), |
||
376 | ]; |
||
377 | } catch (\Exception $e) { |
||
378 | // fall back to the normal one |
||
379 | $this->fileIsEncrypted = false; |
||
380 | } |
||
381 | } |
||
382 | |||
383 | return [ |
||
384 | 'type' => 'file', |
||
385 | 'id' => $id, |
||
386 | 'name' => basename($path), |
||
387 | 'path' => trim($path, '/'), |
||
388 | 'link' => $this->url->linkToRouteAbsolute('files.viewcontroller.showFile', ['fileid' => $id]), |
||
389 | ]; |
||
390 | } |
||
391 | |||
392 | protected $fileEncrypted = []; |
||
393 | |||
394 | /** |
||
395 | * Check if a file is end2end encrypted |
||
396 | * @param int $fileId |
||
397 | * @param string $path |
||
398 | * @return Folder|null |
||
399 | */ |
||
400 | protected function getEndToEndEncryptionContainer($fileId, $path) { |
||
401 | if (isset($this->fileEncrypted[$fileId])) { |
||
402 | return $this->fileEncrypted[$fileId]; |
||
403 | } |
||
404 | |||
405 | $fileName = basename($path); |
||
406 | if (!preg_match('/^[0-9a-fA-F]{32}$/', $fileName)) { |
||
407 | $this->fileEncrypted[$fileId] = false; |
||
408 | return $this->fileEncrypted[$fileId]; |
||
409 | } |
||
410 | |||
411 | $userFolder = $this->rootFolder->getUserFolder($this->activityManager->getCurrentUserId()); |
||
412 | $files = $userFolder->getById($fileId); |
||
413 | if (empty($files)) { |
||
414 | try { |
||
415 | // Deleted, try with parent |
||
416 | $file = $this->findExistingParent($userFolder, dirname($path)); |
||
417 | } catch (NotFoundException $e) { |
||
418 | return null; |
||
419 | } |
||
420 | |||
421 | if (!$file instanceof Folder || !$file->isEncrypted()) { |
||
|
|||
422 | return null; |
||
423 | } |
||
424 | |||
425 | $this->fileEncrypted[$fileId] = $file; |
||
426 | return $file; |
||
427 | } |
||
428 | |||
429 | $file = array_shift($files); |
||
430 | |||
431 | if ($file instanceof Folder && $file->isEncrypted()) { |
||
432 | // If the folder is encrypted, it is the Container, |
||
433 | // but can be the name is just fine. |
||
434 | $this->fileEncrypted[$fileId] = true; |
||
435 | return null; |
||
436 | } |
||
437 | |||
438 | $this->fileEncrypted[$fileId] = $this->getParentEndToEndEncryptionContainer($userFolder, $file); |
||
439 | return $this->fileEncrypted[$fileId]; |
||
440 | } |
||
441 | |||
442 | /** |
||
443 | * @param Folder $userFolder |
||
444 | * @param string $path |
||
445 | * @return Folder |
||
446 | * @throws NotFoundException |
||
447 | */ |
||
448 | protected function findExistingParent(Folder $userFolder, $path) { |
||
460 | } |
||
461 | |||
462 | /** |
||
463 | * Check all parents until the user's root folder if one is encrypted |
||
464 | * |
||
465 | * @param Folder $userFolder |
||
466 | * @param Node $file |
||
467 | * @return Node|null |
||
468 | */ |
||
469 | protected function getParentEndToEndEncryptionContainer(Folder $userFolder, Node $file) { |
||
470 | try { |
||
471 | $parent = $file->getParent(); |
||
472 | |||
473 | if ($userFolder->getId() === $parent->getId()) { |
||
474 | return null; |
||
475 | } |
||
476 | } catch (\Exception $e) { |
||
477 | return null; |
||
478 | } |
||
479 | |||
480 | if ($parent->isEncrypted()) { |
||
481 | return $parent; |
||
482 | } |
||
483 | |||
484 | return $this->getParentEndToEndEncryptionContainer($userFolder, $parent); |
||
485 | } |
||
486 | |||
487 | /** |
||
488 | * @param string $uid |
||
489 | * @return array |
||
490 | */ |
||
491 | protected function getUser($uid) { |
||
492 | // First try local user |
||
493 | $user = $this->userManager->get($uid); |
||
494 | if ($user instanceof IUser) { |
||
495 | return [ |
||
496 | 'type' => 'user', |
||
497 | 'id' => $user->getUID(), |
||
498 | 'name' => $user->getDisplayName(), |
||
499 | ]; |
||
500 | } |
||
501 | |||
502 | // Then a contact from the addressbook |
||
503 | if ($this->cloudIdManager->isValidCloudId($uid)) { |
||
504 | $cloudId = $this->cloudIdManager->resolveCloudId($uid); |
||
505 | return [ |
||
506 | 'type' => 'user', |
||
507 | 'id' => $cloudId->getUser(), |
||
508 | 'name' => $this->getDisplayNameFromAddressBook($cloudId->getDisplayId()), |
||
509 | 'server' => $cloudId->getRemote(), |
||
510 | ]; |
||
511 | } |
||
512 | |||
513 | // Fallback to empty dummy data |
||
514 | return [ |
||
515 | 'type' => 'user', |
||
516 | 'id' => $uid, |
||
517 | 'name' => $uid, |
||
518 | ]; |
||
519 | } |
||
520 | |||
521 | protected function getDisplayNameFromAddressBook(string $search): string { |
||
549 | } |
||
550 | } |
||
551 |