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:
Complex classes like UsersController 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
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 UsersController, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 54 | class UsersController extends OCSController { |
||
| 55 | |||
| 56 | /** @var IUserManager */ |
||
| 57 | private $userManager; |
||
| 58 | /** @var IConfig */ |
||
| 59 | private $config; |
||
| 60 | /** @var IAppManager */ |
||
| 61 | private $appManager; |
||
| 62 | /** @var IGroupManager|\OC\Group\Manager */ // FIXME Requires a method that is not on the interface |
||
| 63 | private $groupManager; |
||
| 64 | /** @var IUserSession */ |
||
| 65 | private $userSession; |
||
| 66 | /** @var AccountManager */ |
||
| 67 | private $accountManager; |
||
| 68 | /** @var ILogger */ |
||
| 69 | private $logger; |
||
| 70 | /** @var IFactory */ |
||
| 71 | private $l10nFactory; |
||
| 72 | /** @var NewUserMailHelper */ |
||
| 73 | private $newUserMailHelper; |
||
| 74 | /** @var FederatedFileSharingFactory */ |
||
| 75 | private $federatedFileSharingFactory; |
||
| 76 | |||
| 77 | /** |
||
| 78 | * @param string $appName |
||
| 79 | * @param IRequest $request |
||
| 80 | * @param IUserManager $userManager |
||
| 81 | * @param IConfig $config |
||
| 82 | * @param IAppManager $appManager |
||
| 83 | * @param IGroupManager $groupManager |
||
| 84 | * @param IUserSession $userSession |
||
| 85 | * @param AccountManager $accountManager |
||
| 86 | * @param ILogger $logger |
||
| 87 | * @param IFactory $l10nFactory |
||
| 88 | * @param NewUserMailHelper $newUserMailHelper |
||
| 89 | * @param FederatedFileSharingFactory $federatedFileSharingFactory |
||
| 90 | */ |
||
| 91 | public function __construct(string $appName, |
||
| 116 | |||
| 117 | /** |
||
| 118 | * @NoAdminRequired |
||
| 119 | * |
||
| 120 | * returns a list of users |
||
| 121 | * |
||
| 122 | * @param string $search |
||
| 123 | * @param int $limit |
||
| 124 | * @param int $offset |
||
| 125 | * @return DataResponse |
||
| 126 | */ |
||
| 127 | public function getUsers(string $search = '', $limit = null, $offset = 0): DataResponse { |
||
| 154 | |||
| 155 | /** |
||
| 156 | * @NoAdminRequired |
||
| 157 | * |
||
| 158 | * returns a list of users and their data |
||
| 159 | */ |
||
| 160 | public function getUsersDetails(string $search = '', $limit = null, $offset = 0): DataResponse { |
||
| 195 | |||
| 196 | /** |
||
| 197 | * @PasswordConfirmationRequired |
||
| 198 | * @NoAdminRequired |
||
| 199 | * |
||
| 200 | * @param string $userid |
||
| 201 | * @param string $password |
||
| 202 | * @param array $groups |
||
| 203 | * @return DataResponse |
||
| 204 | * @throws OCSException |
||
| 205 | */ |
||
| 206 | public function addUser(string $userid, string $password, array $groups = []): DataResponse { |
||
| 257 | |||
| 258 | /** |
||
| 259 | * @NoAdminRequired |
||
| 260 | * @NoSubAdminRequired |
||
| 261 | * |
||
| 262 | * gets user info |
||
| 263 | * |
||
| 264 | * @param string $userId |
||
| 265 | * @return DataResponse |
||
| 266 | * @throws OCSException |
||
| 267 | */ |
||
| 268 | public function getUser(string $userId): DataResponse { |
||
| 276 | |||
| 277 | /** |
||
| 278 | * @NoAdminRequired |
||
| 279 | * @NoSubAdminRequired |
||
| 280 | * |
||
| 281 | * gets user info from the currently logged in user |
||
| 282 | * |
||
| 283 | * @return DataResponse |
||
| 284 | * @throws OCSException |
||
| 285 | */ |
||
| 286 | public function getCurrentUser(): DataResponse { |
||
| 300 | |||
| 301 | /** |
||
| 302 | * creates a array with all user data |
||
| 303 | * |
||
| 304 | * @param $userId |
||
| 305 | * @return array |
||
| 306 | * @throws OCSException |
||
| 307 | */ |
||
| 308 | protected function getUserData(string $userId): array { |
||
| 356 | |||
| 357 | /** |
||
| 358 | * @NoAdminRequired |
||
| 359 | * @NoSubAdminRequired |
||
| 360 | */ |
||
| 361 | public function getEditableFields(): DataResponse { |
||
| 383 | |||
| 384 | /** |
||
| 385 | * @NoAdminRequired |
||
| 386 | * @NoSubAdminRequired |
||
| 387 | * @PasswordConfirmationRequired |
||
| 388 | * |
||
| 389 | * edit users |
||
| 390 | * |
||
| 391 | * @param string $userId |
||
| 392 | * @param string $key |
||
| 393 | * @param string $value |
||
| 394 | * @return DataResponse |
||
| 395 | * @throws OCSException |
||
| 396 | */ |
||
| 397 | public function editUser(string $userId, string $key, string $value): DataResponse { |
||
| 398 | $currentLoggedInUser = $this->userSession->getUser(); |
||
| 399 | |||
| 400 | $targetUser = $this->userManager->get($userId); |
||
| 401 | if($targetUser === null) { |
||
| 402 | throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED); |
||
| 403 | } |
||
| 404 | |||
| 405 | $permittedFields = []; |
||
| 406 | if($targetUser->getUID() === $currentLoggedInUser->getUID()) { |
||
| 407 | // Editing self (display, email) |
||
| 408 | View Code Duplication | if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) { |
|
| 409 | $permittedFields[] = 'display'; |
||
| 410 | $permittedFields[] = AccountManager::PROPERTY_DISPLAYNAME; |
||
| 411 | $permittedFields[] = AccountManager::PROPERTY_EMAIL; |
||
| 412 | } |
||
| 413 | |||
| 414 | $permittedFields[] = 'password'; |
||
| 415 | if ($this->config->getSystemValue('force_language', false) === false || |
||
| 416 | $this->groupManager->isAdmin($currentLoggedInUser->getUID())) { |
||
| 417 | $permittedFields[] = 'language'; |
||
| 418 | } |
||
| 419 | |||
| 420 | View Code Duplication | if ($this->appManager->isEnabledForUser('federatedfilesharing')) { |
|
| 421 | $federatedFileSharing = new \OCA\FederatedFileSharing\AppInfo\Application(); |
||
| 422 | $shareProvider = $federatedFileSharing->getFederatedShareProvider(); |
||
| 423 | if ($shareProvider->isLookupServerUploadEnabled()) { |
||
| 424 | $permittedFields[] = AccountManager::PROPERTY_PHONE; |
||
| 425 | $permittedFields[] = AccountManager::PROPERTY_ADDRESS; |
||
| 426 | $permittedFields[] = AccountManager::PROPERTY_WEBSITE; |
||
| 427 | $permittedFields[] = AccountManager::PROPERTY_TWITTER; |
||
| 428 | } |
||
| 429 | } |
||
| 430 | |||
| 431 | // If admin they can edit their own quota |
||
| 432 | if($this->groupManager->isAdmin($currentLoggedInUser->getUID())) { |
||
| 433 | $permittedFields[] = 'quota'; |
||
| 434 | } |
||
| 435 | } else { |
||
| 436 | // Check if admin / subadmin |
||
| 437 | $subAdminManager = $this->groupManager->getSubAdmin(); |
||
| 438 | if($subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser) |
||
| 439 | || $this->groupManager->isAdmin($currentLoggedInUser->getUID())) { |
||
| 440 | // They have permissions over the user |
||
| 441 | $permittedFields[] = 'display'; |
||
| 442 | $permittedFields[] = AccountManager::PROPERTY_DISPLAYNAME; |
||
| 443 | $permittedFields[] = AccountManager::PROPERTY_EMAIL; |
||
| 444 | $permittedFields[] = 'password'; |
||
| 445 | $permittedFields[] = 'language'; |
||
| 446 | $permittedFields[] = AccountManager::PROPERTY_PHONE; |
||
| 447 | $permittedFields[] = AccountManager::PROPERTY_ADDRESS; |
||
| 448 | $permittedFields[] = AccountManager::PROPERTY_WEBSITE; |
||
| 449 | $permittedFields[] = AccountManager::PROPERTY_TWITTER; |
||
| 450 | $permittedFields[] = 'quota'; |
||
| 451 | } else { |
||
| 452 | // No rights |
||
| 453 | throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED); |
||
| 454 | } |
||
| 455 | } |
||
| 456 | // Check if permitted to edit this field |
||
| 457 | if(!in_array($key, $permittedFields)) { |
||
| 458 | throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED); |
||
| 459 | } |
||
| 460 | // Process the edit |
||
| 461 | switch($key) { |
||
| 462 | case 'display': |
||
| 463 | case AccountManager::PROPERTY_DISPLAYNAME: |
||
| 464 | $targetUser->setDisplayName($value); |
||
| 465 | break; |
||
| 466 | case 'quota': |
||
| 467 | $quota = $value; |
||
| 468 | if($quota !== 'none' && $quota !== 'default') { |
||
| 469 | if (is_numeric($quota)) { |
||
| 470 | $quota = (float) $quota; |
||
| 471 | } else { |
||
| 472 | $quota = \OCP\Util::computerFileSize($quota); |
||
| 473 | } |
||
| 474 | if ($quota === false) { |
||
| 475 | throw new OCSException('Invalid quota value '.$value, 103); |
||
| 476 | } |
||
| 477 | if($quota === 0) { |
||
| 478 | $quota = 'default'; |
||
| 479 | }else if($quota === -1) { |
||
| 480 | $quota = 'none'; |
||
| 481 | } else { |
||
| 482 | $quota = \OCP\Util::humanFileSize($quota); |
||
| 483 | } |
||
| 484 | } |
||
| 485 | $targetUser->setQuota($quota); |
||
| 486 | break; |
||
| 487 | case 'password': |
||
| 488 | $targetUser->setPassword($value); |
||
| 489 | break; |
||
| 490 | case 'language': |
||
| 491 | $languagesCodes = $this->l10nFactory->findAvailableLanguages(); |
||
| 492 | if (!in_array($value, $languagesCodes, true) && $value !== 'en') { |
||
| 493 | throw new OCSException('Invalid language', 102); |
||
| 494 | } |
||
| 495 | $this->config->setUserValue($targetUser->getUID(), 'core', 'lang', $value); |
||
| 496 | break; |
||
| 497 | case AccountManager::PROPERTY_EMAIL: |
||
| 498 | if(filter_var($value, FILTER_VALIDATE_EMAIL) || $value === '') { |
||
| 499 | $targetUser->setEMailAddress($value); |
||
| 500 | } else { |
||
| 501 | throw new OCSException('', 102); |
||
| 502 | } |
||
| 503 | break; |
||
| 504 | case AccountManager::PROPERTY_PHONE: |
||
| 505 | case AccountManager::PROPERTY_ADDRESS: |
||
| 506 | case AccountManager::PROPERTY_WEBSITE: |
||
| 507 | case AccountManager::PROPERTY_TWITTER: |
||
| 508 | $userAccount = $this->accountManager->getUser($targetUser); |
||
| 509 | if ($userAccount[$key]['value'] !== $value) { |
||
| 510 | $userAccount[$key]['value'] = $value; |
||
| 511 | $this->accountManager->updateUser($targetUser, $userAccount); |
||
| 512 | } |
||
| 513 | break; |
||
| 514 | default: |
||
| 515 | throw new OCSException('', 103); |
||
| 516 | } |
||
| 517 | return new DataResponse(); |
||
| 518 | } |
||
| 519 | |||
| 520 | /** |
||
| 521 | * @PasswordConfirmationRequired |
||
| 522 | * @NoAdminRequired |
||
| 523 | * |
||
| 524 | * @param string $userId |
||
| 525 | * @return DataResponse |
||
| 526 | * @throws OCSException |
||
| 527 | */ |
||
| 528 | public function deleteUser(string $userId): DataResponse { |
||
| 550 | |||
| 551 | /** |
||
| 552 | * @PasswordConfirmationRequired |
||
| 553 | * @NoAdminRequired |
||
| 554 | * |
||
| 555 | * @param string $userId |
||
| 556 | * @return DataResponse |
||
| 557 | * @throws OCSException |
||
| 558 | * @throws OCSForbiddenException |
||
| 559 | */ |
||
| 560 | public function disableUser(string $userId): DataResponse { |
||
| 563 | |||
| 564 | /** |
||
| 565 | * @PasswordConfirmationRequired |
||
| 566 | * @NoAdminRequired |
||
| 567 | * |
||
| 568 | * @param string $userId |
||
| 569 | * @return DataResponse |
||
| 570 | * @throws OCSException |
||
| 571 | * @throws OCSForbiddenException |
||
| 572 | */ |
||
| 573 | public function enableUser(string $userId): DataResponse { |
||
| 576 | |||
| 577 | /** |
||
| 578 | * @param string $userId |
||
| 579 | * @param bool $value |
||
| 580 | * @return DataResponse |
||
| 581 | * @throws OCSException |
||
| 582 | */ |
||
| 583 | private function setEnabled(string $userId, bool $value): DataResponse { |
||
| 601 | |||
| 602 | /** |
||
| 603 | * @NoAdminRequired |
||
| 604 | * @NoSubAdminRequired |
||
| 605 | * |
||
| 606 | * @param string $userId |
||
| 607 | * @return DataResponse |
||
| 608 | * @throws OCSException |
||
| 609 | */ |
||
| 610 | public function getUsersGroups(string $userId): DataResponse { |
||
| 646 | |||
| 647 | /** |
||
| 648 | * @PasswordConfirmationRequired |
||
| 649 | * @NoAdminRequired |
||
| 650 | * |
||
| 651 | * @param string $userId |
||
| 652 | * @param string $groupid |
||
| 653 | * @return DataResponse |
||
| 654 | * @throws OCSException |
||
| 655 | */ |
||
| 656 | public function addToGroup(string $userId, string $groupid = ''): DataResponse { |
||
| 681 | |||
| 682 | /** |
||
| 683 | * @PasswordConfirmationRequired |
||
| 684 | * @NoAdminRequired |
||
| 685 | * |
||
| 686 | * @param string $userId |
||
| 687 | * @param string $groupid |
||
| 688 | * @return DataResponse |
||
| 689 | * @throws OCSException |
||
| 690 | */ |
||
| 691 | public function removeFromGroup(string $userId, string $groupid): DataResponse { |
||
| 744 | |||
| 745 | /** |
||
| 746 | * Creates a subadmin |
||
| 747 | * |
||
| 748 | * @PasswordConfirmationRequired |
||
| 749 | * |
||
| 750 | * @param string $userId |
||
| 751 | * @param string $groupid |
||
| 752 | * @return DataResponse |
||
| 753 | * @throws OCSException |
||
| 754 | */ |
||
| 755 | View Code Duplication | public function addSubAdmin(string $userId, string $groupid): DataResponse { |
|
| 785 | |||
| 786 | /** |
||
| 787 | * Removes a subadmin from a group |
||
| 788 | * |
||
| 789 | * @PasswordConfirmationRequired |
||
| 790 | * |
||
| 791 | * @param string $userId |
||
| 792 | * @param string $groupid |
||
| 793 | * @return DataResponse |
||
| 794 | * @throws OCSException |
||
| 795 | */ |
||
| 796 | View Code Duplication | public function removeSubAdmin(string $userId, string $groupid): DataResponse { |
|
| 821 | |||
| 822 | /** |
||
| 823 | * Get the groups a user is a subadmin of |
||
| 824 | * |
||
| 825 | * @param string $userId |
||
| 826 | * @return array |
||
| 827 | * @throws OCSException |
||
| 828 | */ |
||
| 829 | View Code Duplication | protected function getUserSubAdminGroupsData(string $userId): array { |
|
| 845 | |||
| 846 | /** |
||
| 847 | * Get the groups a user is a subadmin of |
||
| 848 | * |
||
| 849 | * @param string $userId |
||
| 850 | * @return DataResponse |
||
| 851 | * @throws OCSException |
||
| 852 | */ |
||
| 853 | public function getUserSubAdminGroups(string $userId): DataResponse { |
||
| 857 | |||
| 858 | /** |
||
| 859 | * @param string $userId |
||
| 860 | * @return array |
||
| 861 | * @throws \OCP\Files\NotFoundException |
||
| 862 | */ |
||
| 863 | protected function fillStorageInfo(string $userId): array { |
||
| 880 | |||
| 881 | /** |
||
| 882 | * @NoAdminRequired |
||
| 883 | * @PasswordConfirmationRequired |
||
| 884 | * |
||
| 885 | * resend welcome message |
||
| 886 | * |
||
| 887 | * @param string $userId |
||
| 888 | * @return DataResponse |
||
| 889 | * @throws OCSException |
||
| 890 | */ |
||
| 891 | public function resendWelcomeMessage(string $userId): DataResponse { |
||
| 934 | } |
||
| 935 |
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.