Failed Conditions
Pull Request — multiproject/requestqueue (#704)
by Simon
02:29
created

LogHelper::getLogActions()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 70
Code Lines 55

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 55
c 1
b 0
f 0
dl 0
loc 70
ccs 0
cts 68
cp 0
rs 8.9818
cc 2
nc 2
nop 1
crap 6

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\Helpers;
10
11
use Exception;
12
use PDO;
13
use Waca\DataObject;
14
use Waca\DataObjects\Ban;
15
use Waca\DataObjects\Comment;
16
use Waca\DataObjects\EmailTemplate;
17
use Waca\DataObjects\JobQueue;
18
use Waca\DataObjects\Log;
19
use Waca\DataObjects\Request;
20
use Waca\DataObjects\RequestQueue;
21
use Waca\DataObjects\User;
22
use Waca\DataObjects\WelcomeTemplate;
23
use Waca\Helpers\SearchHelpers\LogSearchHelper;
24
use Waca\Helpers\SearchHelpers\UserSearchHelper;
25
use Waca\PdoDatabase;
26
use Waca\Security\SecurityManager;
27
use Waca\SiteConfiguration;
28
29
class LogHelper
30
{
31
    /**
32
     * Summary of getRequestLogsWithComments
33
     *
34
     * @param int             $requestId
35
     * @param PdoDatabase     $db
36
     * @param SecurityManager $securityManager
37
     *
38
     * @return DataObject[]
39
     */
40
    public static function getRequestLogsWithComments($requestId, PdoDatabase $db, SecurityManager $securityManager)
41
    {
42
        $logs = LogSearchHelper::get($db)->byObjectType('Request')->byObjectId($requestId)->fetch();
43
44
        $currentUser = User::getCurrent($db);
45
        $showRestrictedComments = $securityManager->allows('RequestData', 'seeRestrictedComments', $currentUser) === SecurityManager::ALLOWED;
46
        $showCheckuserComments = $securityManager->allows('RequestData', 'seeCheckuserComments', $currentUser) === SecurityManager::ALLOWED;
47
48
        $comments = Comment::getForRequest($requestId, $db, $showRestrictedComments, $showCheckuserComments, $currentUser->getId());
49
50
        $items = array_merge($logs, $comments);
51
52
        /**
53
         * @param DataObject $item
54
         *
55
         * @return int
56
         */
57
        $sortKey = function(DataObject $item) {
58
            if ($item instanceof Log) {
59
                return $item->getTimestamp()->getTimestamp();
60
            }
61
62
            if ($item instanceof Comment) {
63
                return $item->getTime()->getTimestamp();
64
            }
65
66
            return 0;
67
        };
68
69
        do {
70
            $flag = false;
71
72
            $loopLimit = (count($items) - 1);
73
            for ($i = 0; $i < $loopLimit; $i++) {
74
                // are these two items out of order?
75
                if ($sortKey($items[$i]) > $sortKey($items[$i + 1])) {
76
                    // swap them
77
                    $swap = $items[$i];
78
                    $items[$i] = $items[$i + 1];
79
                    $items[$i + 1] = $swap;
80
81
                    // set a flag to say we've modified the array this time around
82
                    $flag = true;
83
                }
84
            }
85
        }
86
        while ($flag);
87
88
        return $items;
89
    }
90
91
    /**
92
     * Summary of getLogDescription
93
     *
94
     * @param Log $entry
95
     *
96
     * @return string
97
     */
98
    public static function getLogDescription(Log $entry)
99
    {
100
        $text = "Deferred to ";
101
        if (substr($entry->getAction(), 0, strlen($text)) == $text) {
102
            // Deferred to a different queue
103
            // This is exactly what we want to display.
104
            return $entry->getAction();
105
        }
106
107
        $text = "Closed custom-n";
108
        if ($entry->getAction() == $text) {
109
            // Custom-closed
110
            return "closed (custom reason - account not created)";
111
        }
112
113
        $text = "Closed custom-y";
114
        if ($entry->getAction() == $text) {
115
            // Custom-closed
116
            return "closed (custom reason - account created)";
117
        }
118
119
        $text = "Closed 0";
120
        if ($entry->getAction() == $text) {
121
            // Dropped the request - short-circuit the lookup
122
            return "dropped request";
123
        }
124
125
        $text = "Closed ";
126
        if (substr($entry->getAction(), 0, strlen($text)) == $text) {
127
            // Closed with a reason - do a lookup here.
128
            $id = substr($entry->getAction(), strlen($text));
129
            /** @var EmailTemplate $template */
130
            $template = EmailTemplate::getById((int)$id, $entry->getDatabase());
131
132
            if ($template != false) {
0 ignored issues
show
introduced by
The condition $template != false is always true.
Loading history...
133
                return "closed (" . $template->getName() . ")";
134
            }
135
        }
136
137
        // Fall back to the basic stuff
138
        $lookup = array(
139
            'Reserved'            => 'reserved',
140
            'Email Confirmed'     => 'email-confirmed',
141
            'Unreserved'          => 'unreserved',
142
            'Approved'            => 'approved',
143
            'Suspended'           => 'suspended',
144
            'RoleChange'          => 'changed roles',
145
            'Banned'              => 'banned',
146
            'Edited'              => 'edited interface message',
147
            'Declined'            => 'declined',
148
            'EditComment-c'       => 'edited a comment',
149
            'EditComment-r'       => 'edited a comment',
150
            'Unbanned'            => 'unbanned',
151
            'Promoted'            => 'promoted to tool admin',
152
            'BreakReserve'        => 'forcibly broke the reservation',
153
            'Prefchange'          => 'changed user preferences',
154
            'Renamed'             => 'renamed',
155
            'Demoted'             => 'demoted from tool admin',
156
            'ReceiveReserved'     => 'received the reservation',
157
            'SendReserved'        => 'sent the reservation',
158
            'EditedEmail'         => 'edited email',
159
            'DeletedTemplate'     => 'deleted template',
160
            'EditedTemplate'      => 'edited template',
161
            'CreatedEmail'        => 'created email',
162
            'CreatedTemplate'     => 'created template',
163
            'SentMail'            => 'sent an email to the requester',
164
            'Registered'          => 'registered a tool account',
165
            'JobIssue'            => 'ran a background job unsuccessfully',
166
            'JobCompleted'        => 'completed a background job',
167
            'JobAcknowledged'     => 'acknowledged a job failure',
168
            'JobRequeued'         => 'requeued a job for re-execution',
169
            'JobCancelled'        => 'cancelled execution of a job',
170
            'EnqueuedJobQueue'    => 'scheduled for creation',
171
            'Hospitalised'        => 'sent to the hospital',
172
            'QueueCreated'        => 'created a request queue',
173
            'QueueEdited'         => 'edited a request queue',
174
        );
175
176
        if (array_key_exists($entry->getAction(), $lookup)) {
177
            return $lookup[$entry->getAction()];
178
        }
179
180
        // OK, I don't know what this is. Fall back to something sane.
181
        return "performed an unknown action ({$entry->getAction()})";
182
    }
183
184
    /**
185
     * @param PdoDatabase $database
186
     *
187
     * @return array
188
     */
189
    public static function getLogActions(PdoDatabase $database)
190
    {
191
        $lookup = array(
192
            "Requests" => [
193
                'Reserved'            => 'reserved',
194
                'Email Confirmed'     => 'email-confirmed',
195
                'Unreserved'          => 'unreserved',
196
                'EditComment-c'       => 'edited a comment (by comment ID)',
197
                'EditComment-r'       => 'edited a comment (by request)',
198
                'BreakReserve'        => 'forcibly broke the reservation',
199
                'ReceiveReserved'     => 'received the reservation',
200
                'SendReserved'        => 'sent the reservation',
201
                'SentMail'            => 'sent an email to the requester',
202
                'Closed 0'            => 'dropped request',
203
                'Closed custom-y'     => 'closed (custom reason - account created)',
204
                'Closed custom-n'     => 'closed (custom reason - account not created)',
205
            ],
206
            'Users' => [
207
                'Approved'            => 'approved',
208
                'Suspended'           => 'suspended',
209
                'RoleChange'          => 'changed roles',
210
                'Declined'            => 'declined',
211
                'Prefchange'          => 'changed user preferences',
212
                'Renamed'             => 'renamed',
213
                'Promoted'            => 'promoted to tool admin',
214
                'Demoted'             => 'demoted from tool admin',
215
                'Registered'          => 'registered a tool account',
216
            ],
217
            "Bans" => [
218
                'Banned'              => 'banned',
219
                'Unbanned'            => 'unbanned',
220
            ],
221
            "Site notice" => [
222
                'Edited'              => 'edited interface message',
223
            ],
224
            "Email close templates" => [
225
                'EditedEmail'         => 'edited email',
226
                'CreatedEmail'        => 'created email',
227
            ],
228
            "Welcome templates" => [
229
                'DeletedTemplate'     => 'deleted template',
230
                'EditedTemplate'      => 'edited template',
231
                'CreatedTemplate'     => 'created template',
232
            ],
233
            "Job queue" => [
234
                'JobIssue'            => 'ran a background job unsuccessfully',
235
                'JobCompleted'        => 'completed a background job',
236
                'JobAcknowledged'     => 'acknowledged a job failure',
237
                'JobRequeued'         => 'requeued a job for re-execution',
238
                'JobCancelled'        => 'cancelled execution of a job',
239
                'EnqueuedJobQueue'    => 'scheduled for creation',
240
                'Hospitalised'        => 'sent to the hospital',
241
            ],
242
            "Request queues" => [
243
                'QueueCreated'        => 'created a request queue',
244
                'QueueEdited'         => 'edited a request queue',
245
            ],
246
        );
247
248
        $databaseDrivenLogKeys = $database->query(<<<SQL
249
SELECT CONCAT('Closed ', id) AS k, CONCAT('closed (',name,')') AS v FROM emailtemplate
250
UNION ALL
251
SELECT CONCAT('Deferred to ', logname) AS k, CONCAT('deferred to ', displayname) AS v FROM requestqueue;
252
SQL
253
        );
254
        foreach ($databaseDrivenLogKeys->fetchAll(PDO::FETCH_ASSOC) as $row) {
255
            $lookup["Requests"][$row['k']] = $row['v'];
256
        }
257
258
        return $lookup;
259
    }
260
261
    public static function getObjectTypes()
262
    {
263
        return array(
264
            'Ban'             => 'Ban',
265
            'Comment'         => 'Comment',
266
            'EmailTemplate'   => 'Email template',
267
            'JobQueue'        => 'Job queue item',
268
            'Request'         => 'Request',
269
            'SiteNotice'      => 'Site notice',
270
            'User'            => 'User',
271
            'WelcomeTemplate' => 'Welcome template',
272
            'RequestQueue'    => 'Request queue'
273
        );
274
    }
275
276
    /**
277
     * This returns a HTML
278
     *
279
     * @param string            $objectId
280
     * @param string            $objectType
281
     * @param PdoDatabase       $database
282
     * @param SiteConfiguration $configuration
283
     *
284
     * @return null|string
285
     * @category Security-Critical
286
     */
287
    private static function getObjectDescription(
288
        $objectId,
289
        $objectType,
290
        PdoDatabase $database,
291
        SiteConfiguration $configuration
292
    ) {
293
        if ($objectType == '') {
294
            return null;
295
        }
296
297
        $baseurl = $configuration->getBaseUrl();
298
299
        switch ($objectType) {
300
            case 'Ban':
301
                /** @var Ban $ban */
302
                $ban = Ban::getById($objectId, $database);
0 ignored issues
show
Bug introduced by
$objectId of type string is incompatible with the type integer expected by parameter $id of Waca\DataObject::getById(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

302
                $ban = Ban::getById(/** @scrutinizer ignore-type */ $objectId, $database);
Loading history...
303
304
                if ($ban === false) {
0 ignored issues
show
introduced by
The condition $ban === false is always false.
Loading history...
305
                    return 'Ban #' . $objectId;
306
                }
307
308
                return <<<HTML
309
<a href="{$baseurl}/internal.php/bans/show?id={$objectId}">Ban #{$objectId}</a>
310
HTML;
311
            case 'EmailTemplate':
312
                /** @var EmailTemplate $emailTemplate */
313
                $emailTemplate = EmailTemplate::getById($objectId, $database);
314
315
                if ($emailTemplate === false) {
0 ignored issues
show
introduced by
The condition $emailTemplate === false is always false.
Loading history...
316
                    return 'Email Template #' . $objectId;
317
                }
318
319
                $name = htmlentities($emailTemplate->getName(), ENT_COMPAT, 'UTF-8');
320
321
                return <<<HTML
322
<a href="{$baseurl}/internal.php/emailManagement/view?id={$objectId}">Email Template #{$objectId} ({$name})</a>
323
HTML;
324
            case 'SiteNotice':
325
                return "<a href=\"{$baseurl}/internal.php/siteNotice\">the site notice</a>";
326
            case 'Request':
327
                /** @var Request $request */
328
                $request = Request::getById($objectId, $database);
329
330
                if ($request === false) {
0 ignored issues
show
introduced by
The condition $request === false is always false.
Loading history...
331
                    return 'Request #' . $objectId;
332
                }
333
334
                $name = htmlentities($request->getName(), ENT_COMPAT, 'UTF-8');
335
336
                return <<<HTML
337
<a href="{$baseurl}/internal.php/viewRequest?id={$objectId}">Request #{$objectId} ({$name})</a>
338
HTML;
339
            case 'User':
340
                /** @var User $user */
341
                $user = User::getById($objectId, $database);
0 ignored issues
show
Bug introduced by
$objectId of type string is incompatible with the type integer|null expected by parameter $id of Waca\DataObjects\User::getById(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

341
                $user = User::getById(/** @scrutinizer ignore-type */ $objectId, $database);
Loading history...
342
343
                // Some users were merged out of existence
344
                if ($user === false) {
0 ignored issues
show
introduced by
The condition $user === false is always false.
Loading history...
345
                    return 'User #' . $objectId;
346
                }
347
348
                $username = htmlentities($user->getUsername(), ENT_COMPAT, 'UTF-8');
349
350
                return "<a href=\"{$baseurl}/internal.php/statistics/users/detail?user={$objectId}\">{$username}</a>";
351
            case 'WelcomeTemplate':
352
                /** @var WelcomeTemplate $welcomeTemplate */
353
                $welcomeTemplate = WelcomeTemplate::getById($objectId, $database);
354
355
                // some old templates have been completely deleted and lost to the depths of time.
356
                if ($welcomeTemplate === false) {
0 ignored issues
show
introduced by
The condition $welcomeTemplate === false is always false.
Loading history...
357
                    return "Welcome template #{$objectId}";
358
                }
359
                else {
360
                    $userCode = htmlentities($welcomeTemplate->getUserCode(), ENT_COMPAT, 'UTF-8');
361
362
                    return "<a href=\"{$baseurl}/internal.php/welcomeTemplates/view?template={$objectId}\">{$userCode}</a>";
363
                }
364
            case 'JobQueue':
365
                /** @var JobQueue $job */
366
                $job = JobQueue::getById($objectId, $database);
367
368
                $taskDescriptions = JobQueue::getTaskDescriptions();
369
370
                if ($job === false) {
0 ignored issues
show
introduced by
The condition $job === false is always false.
Loading history...
371
                    return 'Job Queue Task #' . $objectId;
372
                }
373
374
                $task = $job->getTask();
375
                if (isset($taskDescriptions[$task])) {
376
                    $description = $taskDescriptions[$task];
377
                }
378
                else {
379
                    $description = 'Unknown task';
380
                }
381
382
                return "<a href=\"{$baseurl}/internal.php/jobQueue/view?id={$objectId}\">Job #{$job->getId()} ({$description})</a>";
383
            case 'RequestQueue':
384
                /** @var RequestQueue $queue */
385
                $queue = RequestQueue::getById($objectId, $database);
386
387
                if ($queue === false) {
0 ignored issues
show
introduced by
The condition $queue === false is always false.
Loading history...
388
                    return "Request Queue #{$objectId}";
389
                }
390
391
                $queueHeader = htmlentities($queue->getHeader(), ENT_COMPAT, 'UTF-8');
392
393
                return "<a href=\"{$baseurl}/internal.php/queueManagement/edit?queue={$objectId}\">{$queueHeader}</a>";
394
            default:
395
                return '[' . $objectType . " " . $objectId . ']';
396
        }
397
    }
398
399
    /**
400
     * @param Log[]             $logs
401
     * @param PdoDatabase       $database
402
     * @param SiteConfiguration $configuration
403
     *
404
     * @return array
405
     * @throws Exception
406
     */
407
    public static function prepareLogsForTemplate($logs, PdoDatabase $database, SiteConfiguration $configuration)
408
    {
409
        $userIds = array();
410
411
        foreach ($logs as $logEntry) {
412
            if (!$logEntry instanceof Log) {
413
                // if this happens, we've done something wrong with passing back the log data.
414
                throw new Exception('Log entry is not an instance of a Log, this should never happen.');
415
            }
416
417
            $user = $logEntry->getUser();
418
            if ($user === -1) {
419
                continue;
420
            }
421
422
            if (!array_search($user, $userIds)) {
423
                $userIds[] = $user;
424
            }
425
        }
426
427
        $users = UserSearchHelper::get($database)->inIds($userIds)->fetchMap('username');
428
        $users[-1] = User::getCommunity()->getUsername();
429
430
        $logData = array();
431
432
        foreach ($logs as $logEntry) {
433
            $objectDescription = self::getObjectDescription($logEntry->getObjectId(), $logEntry->getObjectType(),
434
                $database, $configuration);
435
436
            // initialise to sane default
437
            $comment = null;
438
439
            switch ($logEntry->getAction()) {
440
                case 'Renamed':
441
                    $renameData = unserialize($logEntry->getComment());
0 ignored issues
show
Bug introduced by
It seems like $logEntry->getComment() can also be of type null; however, parameter $data of unserialize() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

441
                    $renameData = unserialize(/** @scrutinizer ignore-type */ $logEntry->getComment());
Loading history...
442
                    $oldName = htmlentities($renameData['old'], ENT_COMPAT, 'UTF-8');
443
                    $newName = htmlentities($renameData['new'], ENT_COMPAT, 'UTF-8');
444
                    $comment = 'Renamed \'' . $oldName . '\' to \'' . $newName . '\'.';
445
                    break;
446
                case 'RoleChange':
447
                    $roleChangeData = unserialize($logEntry->getComment());
448
449
                    $removed = array();
450
                    foreach ($roleChangeData['removed'] as $r) {
451
                        $removed[] = htmlentities($r, ENT_COMPAT, 'UTF-8');
452
                    }
453
454
                    $added = array();
455
                    foreach ($roleChangeData['added'] as $r) {
456
                        $added[] = htmlentities($r, ENT_COMPAT, 'UTF-8');
457
                    }
458
459
                    $reason = htmlentities($roleChangeData['reason'], ENT_COMPAT, 'UTF-8');
460
461
                    $roleDelta = 'Removed [' . implode(', ', $removed) . '], Added [' . implode(', ', $added) . ']';
462
                    $comment = $roleDelta . ' with comment: ' . $reason;
463
                    break;
464
                case 'JobIssue':
465
                    $jobIssueData = unserialize($logEntry->getComment());
466
                    $errorMessage = $jobIssueData['error'];
467
                    $status = $jobIssueData['status'];
468
469
                    $comment = 'Job ' . htmlentities($status, ENT_COMPAT, 'UTF-8') . ': ';
470
                    $comment .= htmlentities($errorMessage, ENT_COMPAT, 'UTF-8');
471
                    break;
472
                case 'JobIssueRequest':
473
                case 'JobCompletedRequest':
474
                    $jobData = unserialize($logEntry->getComment());
475
476
                    /** @var JobQueue $job */
477
                    $job = JobQueue::getById($jobData['job'], $database);
478
                    $descs = JobQueue::getTaskDescriptions();
479
                    $comment = htmlentities($descs[$job->getTask()], ENT_COMPAT, 'UTF-8');
480
                    break;
481
482
                case 'JobCompleted':
483
                    break;
484
                default:
485
                    $comment = $logEntry->getComment();
486
                    break;
487
            }
488
489
            $logData[] = array(
490
                'timestamp'         => $logEntry->getTimestamp(),
491
                'userid'            => $logEntry->getUser(),
492
                'username'          => $users[$logEntry->getUser()],
493
                'description'       => self::getLogDescription($logEntry),
494
                'objectdescription' => $objectDescription,
495
                'comment'           => $comment,
496
            );
497
        }
498
499
        return array($users, $logData);
500
    }
501
}
502