| 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 |