forked from richardbporter/drush-users-commands
-
Notifications
You must be signed in to change notification settings - Fork 0
/
UsersCommands.php
312 lines (271 loc) · 9.48 KB
/
UsersCommands.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
<?php
namespace Drush\Commands\UsersCommands;
use Consolidation\OutputFormatters\Options\FormatterOptions;
use Consolidation\OutputFormatters\StructuredData\RowsOfFields;
use Consolidation\AnnotatedCommand\CommandData;
use Consolidation\SiteAlias\SiteAliasManagerAwareTrait;
use Drupal\user\Entity\User;
use Drush\Commands\DrushCommands;
use Drush\Drush;
use Drush\Exceptions\UserAbortException;
use Drush\SiteAlias\SiteAliasManagerAwareInterface;
use Symfony\Component\Console\Input\InputOption;
/**
* Users command class.
*/
class UsersCommands extends DrushCommands implements SiteAliasManagerAwareInterface {
use SiteAliasManagerAwareTrait;
/**
* Display a list of Drupal users.
*
* @param array $options
* An associative array of options.
*
* @command users:list
*
* @option status Filter by status of the account. Can be active or blocked.
* @option roles Filter by accounts having a role. Use a comma-separated list for more than one.
* @option no-roles Filter by accounts not having a role. Use a comma-separated list for more than one.
* @option last-login Filter by last login date. Can be relative.
* @usage users:list
* Display all users on the site.
* @usage users:list --status=blocked
* Displays a list of blocked users.
* @usage users:list --roles=admin
* Displays a list of users with the admin role.
* @usage users:list --last-login="1 year ago"
* Displays a list of users who have logged in within a year.
* @aliases ulist, user-list, list-users
* @bootstrap full
* @field-labels
* uid: User ID
* name: Username
* pass: Password
* mail: User mail
* theme: User theme
* signature: Signature
* signature_format: Signature format
* user_created: User created
* created: Created
* user_access: User last access
* access: Last access
* user_login: User last login
* login: Last login
* user_status: User status
* status: Status
* timezone: Time zone
* picture: User picture
* init: Initial user mail
* roles: User roles
* group_audience: Group Audience
* langcode: Language code
* uuid: Uuid
* @table-style default
* @default-fields uid,name,mail,roles,status,login
*
* @throws \Exception
*
* @return \Consolidation\OutputFormatters\StructuredData\RowsOfFields
* The users as a RowsOfFields.
*/
public function listAll(array $options = [
'status' => InputOption::VALUE_REQUIRED,
'roles' => InputOption::VALUE_REQUIRED,
'no-roles' => InputOption::VALUE_REQUIRED,
'last-login' => InputOption::VALUE_REQUIRED,
]) {
// Use an entityQuery to dynamically set property conditions.
$query = \Drupal::entityQuery('user')
->accessCheck(FALSE)
->condition('uid', 0, '!=');
if (isset($options['status'])) {
$query->condition('status', $options['status'], '=');
}
if (isset($options['roles'])) {
$query->condition('roles', $options['roles'], 'IN');
}
if (isset($options['no-roles'])) {
$query->condition('roles', $options['no-roles'], 'NOT IN');
}
if (isset($options['last-login'])) {
$timestamp = strtotime($options['last-login']);
$query->condition('login', 0, '!=');
$query->condition('login', $timestamp, '>=');
}
$ids = $query->execute();
if ($users = User::loadMultiple($ids)) {
$rows = [];
foreach ($users as $id => $user) {
$rows[$id] = $this->infoArray($user);
}
$result = new RowsOfFields($rows);
$result->addRendererFunction(function ($key, $cellData, FormatterOptions $options) {
if (is_array($cellData)) {
return implode("\n", $cellData);
}
return $cellData;
});
return $result;
}
else {
throw new \Exception(dt('No users found.'));
}
}
/**
* Validate the users:list command.
*
* @param \Consolidation\AnnotatedCommand\CommandData $commandData
* The command data.
*
* @hook validate users:list
*
* @throws \Exception
*/
public function validateList(CommandData $commandData) {
$input = $commandData->input();
$options = [
'blocked',
'active',
];
if ($status = $input->getOption('status')) {
if (!in_array($status, $options)) {
throw new \Exception(dt('Unknown status @status. Status must be one of @options.', [
'@status' => $status,
'@options' => implode(', ', $options),
]));
}
// Set the status to the key of the options array.
$input->setOption('status', array_search($status, $options));
}
// Set the (no-)roles options to an array but validate each one exists.
$actual = user_roles(TRUE);
foreach (['roles', 'no-roles'] as $option) {
if ($roles = $input->getOption($option)) {
$roles = explode(',', $roles);
// Throw an exception for non-existent roles.
foreach ($roles as $role) {
if (!isset($actual[$role])) {
throw new \Exception(dt('Role @role does not exist.', [
'@role' => $role,
]));
}
}
$input->setOption($option, $roles);
}
}
// Validate the last-login option.
if ($last = $input->getOption('last-login')) {
if (strtotime($last) === FALSE) {
throw new \Exception(dt('Unable to convert @last to a timestamp.', [
'@last' => $last,
]));
}
}
}
/**
* Block and unblock users while keeping track of previous state.
*
* @command users:toggle
* @usage users:toggle
* Block/unblock all users on the site. Based on previous state.
* @aliases utog
* @bootstrap full
*/
public function toggle() {
// Get all users.
$ids = \Drupal::entityQuery('user')
->accessCheck(FALSE)
->condition('uid', 0, '!=')
->execute();
if ($users = User::loadMultiple($ids)) {
// The toggle status is determined by the last command run.
$status = \Drupal::state()->get('utog_status', 'unblocked');
$previous = \Drupal::state()->get('utog_previous', []);
$this->logger()->notice(dt('Toggle status: @status', [
'@status' => $status,
]));
if ($status == 'unblocked') {
if (\Drupal::configFactory()->getEditable('user.settings')->get('notify.status_blocked')) {
$this->logger()->warning(dt('Account blocked email notifications are currently enabled.'));
}
$block = [];
foreach ($users as $user) {
$name = $user->getAccountName();
if ($user->isActive() == FALSE) {
$previous[] = $name;
}
else {
$block[] = $name;
}
}
$block_list = implode(', ', $block);
if (!$this->io()->confirm(dt(
'You will block @names. Are you sure?',
['@names' => $block_list]
))) {
throw new UserAbortException();
}
if (Drush::drush($this->siteAliasManager()->getSelf(), 'user:block', [$block_list])->mustRun()) {
\Drupal::state()->set('utog_previous', $previous);
\Drupal::state()->set('utog_status', 'blocked');
}
}
else {
if (\Drupal::configFactory()->getEditable('user.settings')->get('notify.status_activated')) {
$this->logger()->warning(dt('Account activation email notifications are currently enabled.'));
}
if (empty($previous)) {
$this->logger()->notice(dt('No previously-blocked users.'));
}
else {
$this->logger()->notice(dt('Previously blocked users: @names.', ['@names' => implode(', ', $previous)]));
}
$unblock = [];
foreach ($users as $user) {
if (!in_array($user->getAccountName(), $previous)) {
$unblock[] = $user->getAccountName();
}
}
$unblock_list = implode(', ', $unblock);
if (!$this->io()->confirm(dt('You will unblock @unblock. Are you sure?', ['@unblock' => $unblock_list]))) {
throw new UserAbortException();
}
if (Drush::drush($this->siteAliasManager()->getSelf(), 'user:unblock', [$unblock_list])->mustRun()) {
\Drupal::state()->set('utog_previous', []);
\Drupal::state()->set('utog_status', 'unblocked');
}
}
}
}
/**
* A flatter and simpler array presentation of a Drupal $user object.
*
* @param \Drupal\user\Entity\User $account
* A user account object.
*
* @return array
* An array of user information.
*/
protected function infoArray(User $account) {
/** @var \Drupal\Core\Datetime\DateFormatter $date_formatter */
$date_formatter = \Drupal::service('date.formatter');
return [
'uid' => $account->id(),
'name' => $account->getAccountName(),
'pass' => $account->getPassword(),
'mail' => $account->getEmail(),
'user_created' => $account->getCreatedTime(),
'created' => $date_formatter->format($account->getCreatedTime()),
'user_access' => $account->getLastAccessedTime(),
'access' => $date_formatter->format($account->getLastAccessedTime()),
'user_login' => $account->getLastLoginTime(),
'login' => $date_formatter->format($account->getLastLoginTime()),
'user_status' => $account->get('status')->value,
'status' => $account->isActive() ? 'active' : 'blocked',
'timezone' => $account->getTimeZone(),
'roles' => $account->getRoles(),
'langcode' => $account->getPreferredLangcode(),
'uuid' => $account->uuid->value,
];
}
}