diff --git a/src/Drupal/Commands/core/UserCommands.php b/src/Drupal/Commands/core/UserCommands.php index 9a76c02721..ac8fda4b15 100644 --- a/src/Drupal/Commands/core/UserCommands.php +++ b/src/Drupal/Commands/core/UserCommands.php @@ -119,22 +119,19 @@ public function renderRolesCell($key, $cellData, FormatterOptions $options) * @command user:block * * @param string $names A comma delimited list of user names. + * @option $uid A comma delimited list of user ids to lookup (an alternative to names). + * @option $mail A comma delimited list of emails to lookup (an alternative to names). * @aliases ublk,user-block * @usage drush user:block user3 * Block the users whose name is user3 */ - public function block($names) + public function block($names = '', $options = ['uid' => self::REQ, 'mail' => self::REQ]) { - if ($names = StringUtils::csvToArray($names)) { - foreach ($names as $name) { - if ($account = user_load_by_name($name)) { - $account->block(); - $account->save(); - $this->logger->success(dt('Blocked user(s): !user', ['!user' => $name])); - } else { - $this->logger->warning(dt('Unable to load user: !user', ['!user' => $name])); - } - } + $accounts = $this->getAccounts($names, $options); + foreach ($accounts as $id => $account) { + $account->block(); + $account->save(); + $this->logger->success(dt('Blocked user(s): !user', ['!user' => $account->getAccountName()])); } } @@ -144,22 +141,19 @@ public function block($names) * @command user:unblock * * @param string $names A comma delimited list of user names. + * @option $uid A comma delimited list of user ids to lookup (an alternative to names). + * @option $mail A comma delimited list of emails to lookup (an alternative to names). * @aliases uublk,user-unblock * @usage drush user:unblock user3 * Unblock the users with name user3 */ - public function unblock($names) + public function unblock($names = '', $options = ['uid' => self::REQ, 'mail' => self::REQ]) { - if ($names = StringUtils::csvToArray($names)) { - foreach ($names as $name) { - if ($account = user_load_by_name($name)) { - $account->activate(); - $account->save(); - $this->logger->success(dt('Unblocked user(s): !user', ['!user' => $name])); - } else { - $this->logger->warning(dt('Unable to load user: !user', ['!user' => $name])); - } - } + $accounts = $this->getAccounts($names, $options); + foreach ($accounts as $id => $account) { + $account->activate(); + $account->save(); + $this->logger->success(dt('Unblocked user(s): !user', ['!user' => $account->getAccountName()])); } } @@ -171,25 +165,22 @@ public function unblock($names) * @validate-entity-load user_role role * @param string $role The machine name of the role to add. * @param string $names A comma delimited list of user names. + * @option $uid A comma delimited list of user ids to lookup (an alternative to names). + * @option $mail A comma delimited list of emails to lookup (an alternative to names). * @aliases urol,user-add-role * @usage drush user-add-role "editor" user3 * Add the editor role to user3 */ - public function addRole($role, $names) + public function addRole($role, $names = '', $options = ['uid' => self::REQ, 'mail' => self::REQ]) { - if ($names = StringUtils::csvToArray($names)) { - foreach ($names as $name) { - if ($account = user_load_by_name($name)) { - $account->addRole($role); - $account->save(); - $this->logger->success(dt('Added !role role to !user', [ - '!role' => $role, - '!user' => $name, - ])); - } else { - $this->logger->warning(dt('Unable to load user: !user', ['!user' => $name])); - } - } + $accounts = $this->getAccounts($names, $options); + foreach ($accounts as $id => $account) { + $account->addRole($role); + $account->save(); + $this->logger->success(dt('Added !role role to !user', [ + '!role' => $role, + '!user' => $account->getAccountName(), + ])); } } @@ -201,25 +192,22 @@ public function addRole($role, $names) * @validate-entity-load user_role role * @param string $role The name of the role to add * @param string $names A comma delimited list of user names. + * @option $uid A comma delimited list of user ids to lookup (an alternative to names). + * @option $mail A comma delimited list of emails to lookup (an alternative to names). * @aliases urrol,user-remove-role * @usage drush user:remove-role "power user" user3 * Remove the "power user" role from user3 */ - public function removeRole($role, $names) + public function removeRole($role, $names = '', $options = ['uid' => self::REQ, 'mail' => self::REQ]) { - if ($names = StringUtils::csvToArray($names)) { - foreach ($names as $name) { - if ($account = user_load_by_name($name)) { - $account->removeRole($role); - $account->save(); - $this->logger->success(dt('Removed !role role from !user', [ - '!role' => $role, - '!user' => $name, - ])); - } else { - $this->logger->warning(dt('Unable to load user: !user', ['!user' => $name])); - } - } + $accounts = $this->getAccounts($names, $options); + foreach ($accounts as $id => $account) { + $account->removeRole($role); + $account->save(); + $this->logger->success(dt('Removed !role role from !user', [ + '!role' => $role, + '!user' => $account->getAccountName(), + ])); } } @@ -279,29 +267,26 @@ public function createValidate(CommandData $commandData) * * @param string $names A comma delimited list of user names. * @option delete-content Delete the user, and all content created by the user + * @option $uid A comma delimited list of user ids to lookup (an alternative to names). + * @option $mail A comma delimited list of emails to lookup (an alternative to names). * @aliases ucan,user-cancel * @usage drush user:cancel username * Cancel the user account with the name username and anonymize all content created by that user. * @usage drush user:cancel --delete-content username * Delete the user account with the name username and delete all content created by that user. */ - public function cancel($names, $options = ['delete-content' => false]) + public function cancel($names, $options = ['delete-content' => false, 'uid' => self::REQ, 'mail' => self::REQ]) { - if ($names = StringUtils::csvToArray($names)) { - foreach ($names as $name) { - if ($account = user_load_by_name($name)) { - if ($options['delete-content']) { - $this->logger()->warning(dt('All content created by !name will be deleted.', ['!name' => $account->getAccountName()])); - } - if ($this->io()->confirm('Cancel user account?: ')) { - $method = $options['delete-content'] ? 'user_cancel_delete' : 'user_cancel_block'; - user_cancel([], $account->id(), $method); - drush_backend_batch_process(); - // Drupal logs a message for us. - } - } else { - $this->logger()->warning(dt('Unable to load user: !user', ['!user' => $name])); - } + $accounts = $this->getAccounts($names, $options); + foreach ($accounts as $id => $account) { + if ($options['delete-content']) { + $this->logger()->warning(dt('All content created by !name will be deleted.', ['!name' => $account->getAccountName()])); + } + if ($this->io()->confirm('Cancel user account?: ')) { + $method = $options['delete-content'] ? 'user_cancel_delete' : 'user_cancel_block'; + user_cancel([], $account->id(), $method); + drush_backend_batch_process(); + // Drupal logs a message for us. } } } @@ -357,4 +342,51 @@ public function infoArray($account) 'uuid' => $account->uuid->value, ]; } + + /** + * Get accounts from name variables or uid & mail options. + * + * @param string $names + * @param array $options + * + * @return array + * A array of loaded accounts. + * @throws \Exception + */ + protected function getAccounts($names = '', $options = []) + { + $accounts = []; + if ($mails = StringUtils::csvToArray($options['mail'])) { + foreach ($mails as $mail) { + if ($account = user_load_by_mail($mail)) { + $accounts[$account->id()] = $account; + } else { + $this->logger->warning(dt('Unable to load user: !mail', ['!mail' => $mail])); + } + } + } + if ($uids = StringUtils::csvToArray($options['uid'])) { + foreach ($uids as $uid) { + if ($account = User::load($uid)) { + $accounts[$account->id()] = $account; + } else { + $this->logger->warning(dt('Unable to load user: !uid', ['!uid' => $uid])); + } + } + } + if ($names = StringUtils::csvToArray($names)) { + foreach ($names as $name) { + if ($account = user_load_by_name($name)) { + $accounts[$account->id()] = $account; + } else { + $this->logger->warning(dt('Unable to load user: !user', ['!user' => $name])); + } + } + } + if (empty($accounts)) { + throw new \Exception(dt('Unable to find any matching user')); + } + + return $accounts; + } } diff --git a/tests/functional/UserTest.php b/tests/functional/UserTest.php index 988c3b7176..1e992dd37d 100644 --- a/tests/functional/UserTest.php +++ b/tests/functional/UserTest.php @@ -12,6 +12,7 @@ class UserCase extends CommandUnishTestCase { const NAME = 'example'; + const MAIL = 'example@example.com'; public function setup(): void { @@ -23,9 +24,10 @@ public function setup(): void public function testBlockUnblock() { + $uid = 2; + $this->drush('user-block', [self::NAME]); $this->drush('user-information', [self::NAME], ['format' => 'json']); - $uid = 2; $output = $this->getOutputFromJSON($uid); $this->assertEquals(0, $output['user_status'], 'User is blocked.'); @@ -34,15 +36,38 @@ public function testBlockUnblock() $this->drush('user-information', [self::NAME], ['format' => 'json']); $output = $this->getOutputFromJSON($uid); $this->assertEquals(1, $output['user_status'], 'User is unblocked.'); + + // user-block user by uid. + $this->drush('user-block', [], ['uid' => $uid]); + $this->drush('user-information', [self::NAME], ['format' => 'json']); + $output = $this->getOutputFromJSON($uid); + $this->assertEquals(0, $output['user_status'], 'User (id) is blocked.'); + + $this->drush('user-unblock', [], ['uid' => $uid]); + $this->drush('user-information', [self::NAME], ['format' => 'json']); + $output = $this->getOutputFromJSON($uid); + $this->assertEquals(1, $output['user_status'], 'User (id) is unblocked.'); + + + // user-block user by mail. + $this->drush('user-block', [], ['mail' => self::MAIL]); + $this->drush('user-information', [self::NAME], ['format' => 'json']); + $output = $this->getOutputFromJSON($uid); + $this->assertEquals(0, $output['user_status'], 'User (mail) is blocked.'); + + $this->drush('user-unblock', [], ['uid' => $uid]); + $this->drush('user-information', [self::NAME], ['format' => 'json']); + $output = $this->getOutputFromJSON($uid); + $this->assertEquals(1, $output['user_status'], 'User (mail) is unblocked.'); } public function testUserRole() { + $uid = 2; // First, create the role since we use testing install profile. $this->drush('role-create', ['test role']); $this->drush('user-add-role', ['test role', self::NAME]); $this->drush('user-information', [self::NAME], ['format' => 'json']); - $uid = 2; $output = $this->getOutputFromJSON($uid); $expected = ['authenticated', 'test role']; $this->assertEquals($expected, array_values($output['roles']), 'User has test role.'); @@ -53,6 +78,34 @@ public function testUserRole() $output = $this->getOutputFromJSON($uid); $expected = ['authenticated']; $this->assertEquals($expected, array_values($output['roles']), 'User removed test role.'); + + // user-add-role by uid. + $this->drush('user-add-role', ['test role'], ['uid' => $uid]); + $this->drush('user-information', [self::NAME], ['format' => 'json']); + $output = $this->getOutputFromJSON($uid); + $expected = ['authenticated', 'test role']; + $this->assertEquals($expected, array_values($output['roles']), 'User (id) has test role.'); + + // user-remove-role by uid + $this->drush('user-remove-role', ['test role'], ['uid' => $uid]); + $this->drush('user-information', [self::NAME], ['format' => 'json']); + $output = $this->getOutputFromJSON($uid); + $expected = ['authenticated']; + $this->assertEquals($expected, array_values($output['roles']), 'User (id) removed test role.'); + + // user-add-role by mail. + $this->drush('user-add-role', ['test role'], ['mail' => self::MAIL]); + $this->drush('user-information', [self::NAME], ['format' => 'json']); + $output = $this->getOutputFromJSON($uid); + $expected = ['authenticated', 'test role']; + $this->assertEquals($expected, array_values($output['roles']), 'User (mail) has test role.'); + + // user-remove-role by mail. + $this->drush('user-remove-role', ['test role'], ['mail' => self::MAIL]); + $this->drush('user-information', [self::NAME], ['format' => 'json']); + $output = $this->getOutputFromJSON($uid); + $expected = ['authenticated']; + $this->assertEquals($expected, array_values($output['roles']), 'User (mail) removed test role.'); } public function testUserPassword() @@ -106,7 +159,7 @@ public function testUserLogin() $this->assertStringContainsString('/user/reset/' . $uid, $url['path'], 'Login with uid option returned a valid reset URL'); // Test specific user by mail. $uid = 2; - $mail = 'example@example.com'; + $mail = self::MAIL; $this->drush('user-login', [], $user_login_options + ['mail' => $mail]); $output = $this->getOutput(); $url = parse_url($output); @@ -138,11 +191,11 @@ public function testUserCancel() public function userCreate() { - $this->drush('user-create', [self::NAME], ['password' => 'password', 'mail' => "example@example.com"]); + $this->drush('user-create', [self::NAME], ['password' => 'password', 'mail' => self::MAIL]); $this->drush('user-information', [self::NAME], ['format' => 'json']); $uid = 2; $output = $this->getOutputFromJSON($uid); - $this->assertEquals('example@example.com', $output['mail']); + $this->assertEquals(self::MAIL, $output['mail']); $this->assertEquals(self::NAME, $output['name']); $this->assertEquals(1, $output['user_status'], 'Newly created user is Active.'); $expected = ['authenticated'];