Failed Conditions
Push — newinternal-releasecandidate ( 327c61...a30d14 )
by Simon
15:28 queued 05:26
created

PageUserManagement::main()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 62
Code Lines 41

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 41
c 3
b 0
f 0
dl 0
loc 62
rs 9.264
cc 4
nc 5
nop 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/******************************************************************************
3
 * Wikipedia Account Creation Assistance tool                                 *
4
 *                                                                            *
5
 * All code in this file is released into the public domain by the ACC        *
6
 * Development Team. Please see team.json for a list of contributors.         *
7
 ******************************************************************************/
8
9
namespace Waca\Pages;
10
11
use Waca\DataObjects\User;
12
use Waca\DataObjects\UserRole;
13
use Waca\Exceptions\ApplicationLogicException;
14
use Waca\Helpers\Logger;
15
use Waca\Helpers\OAuthUserHelper;
16
use Waca\Helpers\SearchHelpers\UserSearchHelper;
17
use Waca\SessionAlert;
18
use Waca\Tasks\InternalPageBase;
19
use Waca\WebRequest;
20
21
/**
22
 * Class PageUserManagement
23
 * @package Waca\Pages
24
 */
25
class PageUserManagement extends InternalPageBase
26
{
27
    /** @var string */
28
    private $adminMailingList = '[email protected]';
29
30
    /**
31
     * Main function for this page, when no specific actions are called.
32
     */
33
    protected function main()
34
    {
35
        $this->setHtmlTitle('User Management');
36
37
        $database = $this->getDatabase();
38
        $currentUser = User::getCurrent($database);
39
40
        $userSearchRequest = WebRequest::getString('usersearch');
41
        if ($userSearchRequest !== null) {
42
            $searchedUser = User::getByUsername($userSearchRequest, $database);
43
            if($searchedUser !== false) {
44
                $this->redirect('statistics/users', 'detail', ['user' => $searchedUser->getId()]);
45
                return;
46
            }
47
        }
48
49
        // A bit hacky, but it's better than my last solution of creating an object for each user and passing that to
50
        // the template. I still don't have a particularly good way of handling this.
51
        OAuthUserHelper::prepareTokenCountStatement($database);
52
53
        if (WebRequest::getBoolean("showAll")) {
54
            $this->assign("showAll", true);
55
56
            $suspendedUsers = UserSearchHelper::get($database)->byStatus(User::STATUS_SUSPENDED)->fetch();
57
            $this->assign("suspendedUsers", $suspendedUsers);
58
59
            $declinedUsers = UserSearchHelper::get($database)->byStatus(User::STATUS_DECLINED)->fetch();
60
            $this->assign("declinedUsers", $declinedUsers);
61
62
            UserSearchHelper::get($database)->getRoleMap($roleMap);
63
        }
64
        else {
65
            $this->assign("showAll", false);
66
            $this->assign("suspendedUsers", array());
67
            $this->assign("declinedUsers", array());
68
69
            UserSearchHelper::get($database)->statusIn(array('New', 'Active'))->getRoleMap($roleMap);
70
        }
71
72
        $newUsers = UserSearchHelper::get($database)->byStatus(User::STATUS_NEW)->fetch();
73
        $normalUsers = UserSearchHelper::get($database)->byStatus(User::STATUS_ACTIVE)->byRole('user')->fetch();
74
        $adminUsers = UserSearchHelper::get($database)->byStatus(User::STATUS_ACTIVE)->byRole('admin')->fetch();
75
        $checkUsers = UserSearchHelper::get($database)->byStatus(User::STATUS_ACTIVE)->byRole('checkuser')->fetch();
76
        $toolRoots = UserSearchHelper::get($database)->byStatus(User::STATUS_ACTIVE)->byRole('toolRoot')->fetch();
77
        $this->assign('newUsers', $newUsers);
78
        $this->assign('normalUsers', $normalUsers);
79
        $this->assign('adminUsers', $adminUsers);
80
        $this->assign('checkUsers', $checkUsers);
81
        $this->assign('toolRoots', $toolRoots);
82
83
        $this->assign('roles', $roleMap);
84
85
        $this->addJs("/api.php?action=users&all=true&targetVariable=typeaheaddata");
86
87
        $this->assign('canApprove', $this->barrierTest('approve', $currentUser));
88
        $this->assign('canDecline', $this->barrierTest('decline', $currentUser));
89
        $this->assign('canRename', $this->barrierTest('rename', $currentUser));
90
        $this->assign('canEditUser', $this->barrierTest('editUser', $currentUser));
91
        $this->assign('canSuspend', $this->barrierTest('suspend', $currentUser));
92
        $this->assign('canEditRoles', $this->barrierTest('editRoles', $currentUser));
93
94
        $this->setTemplate("usermanagement/main.tpl");
95
    }
96
97
    #region Access control
98
99
    /**
100
     * Action target for editing the roles assigned to a user
101
     */
102
    protected function editRoles()
103
    {
104
        $this->setHtmlTitle('User Management');
105
        $database = $this->getDatabase();
106
        $userId = WebRequest::getInt('user');
107
108
        /** @var User $user */
109
        $user = User::getById($userId, $database);
110
111
        if ($user === false) {
0 ignored issues
show
introduced by
The condition $user === false is always false.
Loading history...
112
            throw new ApplicationLogicException('Sorry, the user you are trying to edit could not be found.');
113
        }
114
115
        $roleData = $this->getRoleData(UserRole::getForUser($user->getId(), $database));
116
117
        // Dual-mode action
118
        if (WebRequest::wasPosted()) {
119
            $this->validateCSRFToken();
120
121
            $reason = WebRequest::postString('reason');
122
            if ($reason === false || trim($reason) === '') {
123
                throw new ApplicationLogicException('No reason specified for roles change');
124
            }
125
126
            /** @var UserRole[] $delete */
127
            $delete = array();
128
            /** @var string[] $delete */
129
            $add = array();
130
131
            foreach ($roleData as $name => $r) {
132
                if ($r['allowEdit'] !== 1) {
133
                    // not allowed, to touch this, so ignore it
134
                    continue;
135
                }
136
137
                $newValue = WebRequest::postBoolean('role-' . $name) ? 1 : 0;
138
                if ($newValue !== $r['active']) {
139
                    if ($newValue === 0) {
140
                        $delete[] = $r['object'];
141
                    }
142
143
                    if ($newValue === 1) {
144
                        $add[] = $name;
145
                    }
146
                }
147
            }
148
149
            // Check there's something to do
150
            if ((count($add) + count($delete)) === 0) {
151
                $this->redirect('statistics/users', 'detail', array('user' => $user->getId()));
152
                SessionAlert::warning('No changes made to roles.');
153
154
                return;
155
            }
156
157
            $removed = array();
158
159
            /** @var UserRole $d */
160
            foreach ($delete as $d) {
161
                $removed[] = $d->getRole();
162
                $d->delete();
163
            }
164
165
            foreach ($add as $x) {
166
                $a = new UserRole();
167
                $a->setUser($user->getId());
168
                $a->setRole($x);
169
                $a->setDatabase($database);
170
                $a->save();
171
            }
172
173
            Logger::userRolesEdited($database, $user, $reason, $add, $removed);
174
175
            // dummy save for optimistic locking. If this fails, the entire txn will roll back.
176
            $user->setUpdateVersion(WebRequest::postInt('updateversion'));
177
            $user->save();
178
179
            $this->getNotificationHelper()->userRolesEdited($user, $reason);
180
            SessionAlert::quick('Roles changed for user ' . htmlentities($user->getUsername(), ENT_COMPAT, 'UTF-8'));
181
182
            $this->redirect('statistics/users', 'detail', array('user' => $user->getId()));
183
184
            return;
185
        }
186
        else {
187
            $this->assignCSRFToken();
188
            $this->setTemplate('usermanagement/roleedit.tpl');
189
            $this->assign('user', $user);
190
            $this->assign('roleData', $roleData);
191
        }
192
    }
193
194
    /**
195
     * Action target for suspending users
196
     *
197
     * @throws ApplicationLogicException
198
     */
199
    protected function suspend()
200
    {
201
        $this->setHtmlTitle('User Management');
202
203
        $database = $this->getDatabase();
204
205
        $userId = WebRequest::getInt('user');
206
207
        /** @var User $user */
208
        $user = User::getById($userId, $database);
209
210
        if ($user === false) {
0 ignored issues
show
introduced by
The condition $user === false is always false.
Loading history...
211
            throw new ApplicationLogicException('Sorry, the user you are trying to suspend could not be found.');
212
        }
213
214
        if ($user->isSuspended()) {
215
            throw new ApplicationLogicException('Sorry, the user you are trying to suspend is already suspended.');
216
        }
217
218
        // Dual-mode action
219
        if (WebRequest::wasPosted()) {
220
            $this->validateCSRFToken();
221
            $reason = WebRequest::postString('reason');
222
223
            if ($reason === null || trim($reason) === "") {
224
                throw new ApplicationLogicException('No reason provided');
225
            }
226
227
            $user->setStatus(User::STATUS_SUSPENDED);
228
            $user->setUpdateVersion(WebRequest::postInt('updateversion'));
229
            $user->save();
230
            Logger::suspendedUser($database, $user, $reason);
231
232
            $this->getNotificationHelper()->userSuspended($user, $reason);
233
            SessionAlert::quick('Suspended user ' . htmlentities($user->getUsername(), ENT_COMPAT, 'UTF-8'));
234
235
            // send email
236
            $this->sendStatusChangeEmail(
237
                'Your WP:ACC account has been suspended',
238
                'usermanagement/emails/suspended.tpl',
239
                $reason,
240
                $user,
241
                User::getCurrent($database)->getUsername()
242
            );
243
244
            $this->redirect('userManagement');
245
246
            return;
247
        }
248
        else {
249
            $this->assignCSRFToken();
250
            $this->setTemplate('usermanagement/changelevel-reason.tpl');
251
            $this->assign('user', $user);
252
            $this->assign('status', 'Suspended');
253
            $this->assign("showReason", true);
254
255
            if (WebRequest::getString('preload')) {
256
                $this->assign('preload', WebRequest::getString('preload'));
257
            }
258
        }
259
    }
260
261
    /**
262
     * Entry point for the decline action
263
     *
264
     * @throws ApplicationLogicException
265
     */
266
    protected function decline()
267
    {
268
        $this->setHtmlTitle('User Management');
269
270
        $database = $this->getDatabase();
271
272
        $userId = WebRequest::getInt('user');
273
        $user = User::getById($userId, $database);
274
275
        if ($user === false) {
0 ignored issues
show
introduced by
The condition $user === false is always false.
Loading history...
276
            throw new ApplicationLogicException('Sorry, the user you are trying to decline could not be found.');
277
        }
278
279
        if (!$user->isNewUser()) {
280
            throw new ApplicationLogicException('Sorry, the user you are trying to decline is not new.');
281
        }
282
283
        // Dual-mode action
284
        if (WebRequest::wasPosted()) {
285
            $this->validateCSRFToken();
286
            $reason = WebRequest::postString('reason');
287
288
            if ($reason === null || trim($reason) === "") {
289
                throw new ApplicationLogicException('No reason provided');
290
            }
291
292
            $user->setStatus(User::STATUS_DECLINED);
293
            $user->setUpdateVersion(WebRequest::postInt('updateversion'));
294
            $user->save();
295
            Logger::declinedUser($database, $user, $reason);
296
297
            $this->getNotificationHelper()->userDeclined($user, $reason);
298
            SessionAlert::quick('Declined user ' . htmlentities($user->getUsername(), ENT_COMPAT, 'UTF-8'));
299
300
            // send email
301
            $this->sendStatusChangeEmail(
302
                'Your WP:ACC account has been declined',
303
                'usermanagement/emails/declined.tpl',
304
                $reason,
305
                $user,
306
                User::getCurrent($database)->getUsername()
307
            );
308
309
            $this->redirect('userManagement');
310
311
            return;
312
        }
313
        else {
314
            $this->assignCSRFToken();
315
            $this->setTemplate('usermanagement/changelevel-reason.tpl');
316
            $this->assign('user', $user);
317
            $this->assign('status', 'Declined');
318
            $this->assign("showReason", true);
319
        }
320
    }
321
322
    /**
323
     * Entry point for the approve action
324
     *
325
     * @throws ApplicationLogicException
326
     */
327
    protected function approve()
328
    {
329
        $this->setHtmlTitle('User Management');
330
331
        $database = $this->getDatabase();
332
333
        $userId = WebRequest::getInt('user');
334
        $user = User::getById($userId, $database);
335
336
        if ($user === false) {
0 ignored issues
show
introduced by
The condition $user === false is always false.
Loading history...
337
            throw new ApplicationLogicException('Sorry, the user you are trying to approve could not be found.');
338
        }
339
340
        if ($user->isActive()) {
341
            throw new ApplicationLogicException('Sorry, the user you are trying to approve is already an active user.');
342
        }
343
344
        // Dual-mode action
345
        if (WebRequest::wasPosted()) {
346
            $this->validateCSRFToken();
347
            $user->setStatus(User::STATUS_ACTIVE);
348
            $user->setUpdateVersion(WebRequest::postInt('updateversion'));
349
            $user->save();
350
            Logger::approvedUser($database, $user);
351
352
            $this->getNotificationHelper()->userApproved($user);
353
            SessionAlert::quick('Approved user ' . htmlentities($user->getUsername(), ENT_COMPAT, 'UTF-8'));
354
355
            // send email
356
            $this->sendStatusChangeEmail(
357
                'Your WP:ACC account has been approved',
358
                'usermanagement/emails/approved.tpl',
359
                null,
360
                $user,
361
                User::getCurrent($database)->getUsername()
362
            );
363
364
            $this->redirect("userManagement");
365
366
            return;
367
        }
368
        else {
369
            $this->assignCSRFToken();
370
            $this->setTemplate("usermanagement/changelevel-reason.tpl");
371
            $this->assign("user", $user);
372
            $this->assign("status", "Active");
373
            $this->assign("showReason", false);
374
        }
375
    }
376
377
    #endregion
378
379
    #region Renaming / Editing
380
381
    /**
382
     * Entry point for the rename action
383
     *
384
     * @throws ApplicationLogicException
385
     */
386
    protected function rename()
387
    {
388
        $this->setHtmlTitle('User Management');
389
390
        $database = $this->getDatabase();
391
392
        $userId = WebRequest::getInt('user');
393
        $user = User::getById($userId, $database);
394
395
        if ($user === false) {
0 ignored issues
show
introduced by
The condition $user === false is always false.
Loading history...
396
            throw new ApplicationLogicException('Sorry, the user you are trying to rename could not be found.');
397
        }
398
399
        // Dual-mode action
400
        if (WebRequest::wasPosted()) {
401
            $this->validateCSRFToken();
402
            $newUsername = WebRequest::postString('newname');
403
404
            if ($newUsername === null || trim($newUsername) === "") {
405
                throw new ApplicationLogicException('The new username cannot be empty');
406
            }
407
408
            if (User::getByUsername($newUsername, $database) != false) {
409
                throw new ApplicationLogicException('The new username already exists');
410
            }
411
412
            $oldUsername = $user->getUsername();
413
            $user->setUsername($newUsername);
414
            $user->setUpdateVersion(WebRequest::postInt('updateversion'));
415
416
            $user->save();
417
418
            $logEntryData = serialize(array(
419
                'old' => $oldUsername,
420
                'new' => $newUsername,
421
            ));
422
423
            Logger::renamedUser($database, $user, $logEntryData);
424
425
            SessionAlert::quick("Changed User "
426
                . htmlentities($oldUsername, ENT_COMPAT, 'UTF-8')
427
                . " name to "
428
                . htmlentities($newUsername, ENT_COMPAT, 'UTF-8'));
429
430
            $this->getNotificationHelper()->userRenamed($user, $oldUsername);
431
432
            // send an email to the user.
433
            $this->assign('targetUsername', $user->getUsername());
434
            $this->assign('toolAdmin', User::getCurrent($database)->getUsername());
435
            $this->assign('oldUsername', $oldUsername);
436
            $this->assign('mailingList', $this->adminMailingList);
437
438
            $this->getEmailHelper()->sendMail(
439
                $user->getEmail(),
440
                'Your username on WP:ACC has been changed',
441
                $this->fetchTemplate('usermanagement/emails/renamed.tpl'),
442
                array('Reply-To' => $this->adminMailingList)
443
            );
444
445
            $this->redirect("userManagement");
446
447
            return;
448
        }
449
        else {
450
            $this->assignCSRFToken();
451
            $this->setTemplate('usermanagement/renameuser.tpl');
452
            $this->assign('user', $user);
453
        }
454
    }
455
456
    /**
457
     * Entry point for the edit action
458
     *
459
     * @throws ApplicationLogicException
460
     */
461
    protected function editUser()
462
    {
463
        $this->setHtmlTitle('User Management');
464
465
        $database = $this->getDatabase();
466
467
        $userId = WebRequest::getInt('user');
468
        $user = User::getById($userId, $database);
469
        $oauth = new OAuthUserHelper($user, $database, $this->getOAuthProtocolHelper(), $this->getSiteConfiguration());
470
471
        if ($user === false) {
0 ignored issues
show
introduced by
The condition $user === false is always false.
Loading history...
472
            throw new ApplicationLogicException('Sorry, the user you are trying to edit could not be found.');
473
        }
474
475
        // Dual-mode action
476
        if (WebRequest::wasPosted()) {
477
            $this->validateCSRFToken();
478
            $newEmail = WebRequest::postEmail('user_email');
479
            $newOnWikiName = WebRequest::postString('user_onwikiname');
480
481
            if ($newEmail === null) {
482
                throw new ApplicationLogicException('Invalid email address');
483
            }
484
485
            if (!$oauth->isFullyLinked()) {
486
                if (trim($newOnWikiName) == "") {
487
                    throw new ApplicationLogicException('New on-wiki username cannot be blank');
488
                }
489
490
                $user->setOnWikiName($newOnWikiName);
491
            }
492
493
            $user->setEmail($newEmail);
494
495
            $user->setUpdateVersion(WebRequest::postInt('updateversion'));
496
497
            $user->save();
498
499
            Logger::userPreferencesChange($database, $user);
500
            $this->getNotificationHelper()->userPrefChange($user);
501
            SessionAlert::quick('Changes to user\'s preferences have been saved');
502
503
            $this->redirect("userManagement");
504
505
            return;
506
        }
507
        else {
508
            $this->assignCSRFToken();
509
            $oauth = new OAuthUserHelper($user, $database, $this->getOAuthProtocolHelper(),
510
                $this->getSiteConfiguration());
511
            $this->setTemplate('usermanagement/edituser.tpl');
512
            $this->assign('user', $user);
513
            $this->assign('oauth', $oauth);
514
        }
515
    }
516
517
    #endregion
518
519
    /**
520
     * Sends a status change email to the user.
521
     *
522
     * @param string      $subject           The subject of the email
523
     * @param string      $template          The smarty template to use
524
     * @param string|null $reason            The reason for performing the status change
525
     * @param User        $user              The user affected
526
     * @param string      $toolAdminUsername The tool admin's username who is making the edit
527
     */
528
    private function sendStatusChangeEmail($subject, $template, $reason, $user, $toolAdminUsername)
529
    {
530
        $this->assign('targetUsername', $user->getUsername());
531
        $this->assign('toolAdmin', $toolAdminUsername);
532
        $this->assign('actionReason', $reason);
533
        $this->assign('mailingList', $this->adminMailingList);
534
535
        $this->getEmailHelper()->sendMail(
536
            $user->getEmail(),
537
            $subject,
538
            $this->fetchTemplate($template),
539
            array('Reply-To' => $this->adminMailingList)
540
        );
541
    }
542
543
    /**
544
     * @param UserRole[] $activeRoles
545
     *
546
     * @return array
547
     */
548
    private function getRoleData($activeRoles)
549
    {
550
        $availableRoles = $this->getSecurityManager()->getRoleConfiguration()->getAvailableRoles();
551
552
        $currentUser = User::getCurrent($this->getDatabase());
553
        $this->getSecurityManager()->getActiveRoles($currentUser, $userRoles, $inactiveRoles);
554
555
        $initialValue = array('active' => 0, 'allowEdit' => 0, 'description' => '???', 'object' => null);
556
557
        $roleData = array();
558
        foreach ($availableRoles as $role => $data) {
559
            $intersection = array_intersect($data['editableBy'], $userRoles);
560
561
            $roleData[$role] = $initialValue;
562
            $roleData[$role]['allowEdit'] = count($intersection) > 0 ? 1 : 0;
563
            $roleData[$role]['description'] = $data['description'];
564
        }
565
566
        foreach ($activeRoles as $role) {
567
            if (!isset($roleData[$role->getRole()])) {
568
                // This value is no longer available in the configuration, allow changing (aka removing) it.
569
                $roleData[$role->getRole()] = $initialValue;
570
                $roleData[$role->getRole()]['allowEdit'] = 1;
571
            }
572
573
            $roleData[$role->getRole()]['object'] = $role;
574
            $roleData[$role->getRole()]['active'] = 1;
575
        }
576
577
        return $roleData;
578
    }
579
}
580