Passed
Pull Request — multiproject/db (#703)
by Simon
02:48
created

LogHelper::prepareLogsForTemplate()   C

Complexity

Conditions 14
Paths 33

Size

Total Lines 93
Code Lines 62

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 210

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 62
dl 0
loc 93
ccs 0
cts 73
cp 0
rs 6.2666
c 1
b 0
f 0
cc 14
nc 33
nop 3
crap 210

How to fix   Long Method    Complexity   

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
        $statement = $database->query(<<<SQL
249
SELECT CONCAT('Closed ', id) AS k, CONCAT('closed (',name,')') AS v
250
FROM emailtemplate;
251
SQL
252
        );
253
        foreach ($statement->fetchAll(PDO::FETCH_ASSOC) as $row) {
254
            $lookup["Requests"][$row['k']] = $row['v'];
255
        }
256
257
        return $lookup;
258
    }
259
260
    public static function getObjectTypes()
261
    {
262
        return array(
263
            'Ban'             => 'Ban',
264
            'Comment'         => 'Comment',
265
            'EmailTemplate'   => 'Email template',
266
            'JobQueue'        => 'Job queue item',
267
            'Request'         => 'Request',
268
            'SiteNotice'      => 'Site notice',
269
            'User'            => 'User',
270
            'WelcomeTemplate' => 'Welcome template',
271
            'RequestQueue'    => 'Request queue'
272
        );
273
    }
274
275
    /**
276
     * This returns a HTML
277
     *
278
     * @param string            $objectId
279
     * @param string            $objectType
280
     * @param PdoDatabase       $database
281
     * @param SiteConfiguration $configuration
282
     *
283
     * @return null|string
284
     * @category Security-Critical
285
     */
286
    private static function getObjectDescription(
287
        $objectId,
288
        $objectType,
289
        PdoDatabase $database,
290
        SiteConfiguration $configuration
291
    ) {
292
        if ($objectType == '') {
293
            return null;
294
        }
295
296
        $baseurl = $configuration->getBaseUrl();
297
298
        switch ($objectType) {
299
            case 'Ban':
300
                /** @var Ban $ban */
301
                $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

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

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

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