Failed Conditions
Push — newinternal ( b66232...216d62 )
by Simon
16:33 queued 06:35
created

LogHelper::getObjectTypes()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
283
                /** @var EmailTemplate $emailTemplate */
284
                $emailTemplate = EmailTemplate::getById($objectId, $database);
285
                $name = htmlentities($emailTemplate->getName(), ENT_COMPAT, 'UTF-8');
286
287
                return <<<HTML
288
<a href="{$baseurl}/internal.php/emailManagement/view?id={$objectId}">Email Template #{$objectId} ({$name})</a>
289
HTML;
290
            case 'SiteNotice':
291
                return "<a href=\"{$baseurl}/internal.php/siteNotice\">the site notice</a>";
292 View Code Duplication
            case 'Request':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
293
                /** @var Request $request */
294
                $request = Request::getById($objectId, $database);
295
                $name = htmlentities($request->getName(), ENT_COMPAT, 'UTF-8');
296
297
                return <<<HTML
298
<a href="{$baseurl}/internal.php/viewRequest?id={$objectId}">Request #{$objectId} ({$name})</a>
299
HTML;
300
            case 'User':
301
                /** @var User $user */
302
                $user = User::getById($objectId, $database);
303
                $username = htmlentities($user->getUsername(), ENT_COMPAT, 'UTF-8');
304
305
                return "<a href=\"{$baseurl}/internal.php/statistics/users/detail?user={$objectId}\">{$username}</a>";
306
            case 'WelcomeTemplate':
307
                /** @var WelcomeTemplate $welcomeTemplate */
308
                $welcomeTemplate = WelcomeTemplate::getById($objectId, $database);
309
310
                // some old templates have been completely deleted and lost to the depths of time.
311
                if ($welcomeTemplate === false) {
312
                    return "Welcome template #{$objectId}";
313
                }
314
                else {
315
                    $userCode = htmlentities($welcomeTemplate->getUserCode(), ENT_COMPAT, 'UTF-8');
316
317
                    return "<a href=\"{$baseurl}/internal.php/welcomeTemplates/view?template={$objectId}\">{$userCode}</a>";
318
                }
319
            case 'JobQueue':
320
                /** @var JobQueue $job */
321
                $job = JobQueue::getById($objectId, $database);
322
323
                $taskDescriptions = JobQueue::getTaskDescriptions();
324
325
                $task = $job->getTask();
326
                if(isset($taskDescriptions[$task])){
327
                    $description = $taskDescriptions[$task];
328
                } else {
329
                    $description = 'Unknown task';
330
                }
331
332
                return "<a href=\"{$baseurl}/internal.php/jobQueue/view?id={$objectId}\">Job #{$job->getId()} ({$description})</a>";
333
            default:
334
                return '[' . $objectType . " " . $objectId . ']';
335
        }
336
    }
337
338
    /**
339
     * @param Log[]             $logs
340
     * @param PdoDatabase       $database
341
     * @param SiteConfiguration $configuration
342
     *
343
     * @return array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array[].

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
344
     * @throws Exception
345
     */
346
    public static function prepareLogsForTemplate($logs, PdoDatabase $database, SiteConfiguration $configuration)
347
    {
348
        $userIds = array();
349
350
        /** @var Log $logEntry */
351
        foreach ($logs as $logEntry) {
352
            if (!$logEntry instanceof Log) {
353
                // if this happens, we've done something wrong with passing back the log data.
354
                throw new Exception('Log entry is not an instance of a Log, this should never happen.');
355
            }
356
357
            $user = $logEntry->getUser();
358
            if ($user === -1) {
359
                continue;
360
            }
361
362
            if (!array_search($user, $userIds)) {
363
                $userIds[] = $user;
364
            }
365
        }
366
367
        $users = UserSearchHelper::get($database)->inIds($userIds)->fetchMap('username');
368
        $users[-1] = User::getCommunity()->getUsername();
369
370
        $logData = array();
371
372
        /** @var Log $logEntry */
373
        foreach ($logs as $logEntry) {
374
            $objectDescription = self::getObjectDescription($logEntry->getObjectId(), $logEntry->getObjectType(),
375
                $database, $configuration);
376
377
            switch ($logEntry->getAction()) {
378 View Code Duplication
                case 'Renamed':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
379
                    $renameData = unserialize($logEntry->getComment());
380
                    $oldName = htmlentities($renameData['old'], ENT_COMPAT, 'UTF-8');
381
                    $newName = htmlentities($renameData['new'], ENT_COMPAT, 'UTF-8');
382
                    $comment = 'Renamed \'' . $oldName . '\' to \'' . $newName . '\'.';
383
                    break;
384
                case 'RoleChange':
385
                    $roleChangeData = unserialize($logEntry->getComment());
386
387
                    $removed = array();
388
                    foreach ($roleChangeData['removed'] as $r) {
389
                        $removed[] = htmlentities($r, ENT_COMPAT, 'UTF-8');
390
                    }
391
392
                    $added = array();
393
                    foreach ($roleChangeData['added'] as $r) {
394
                        $added[] = htmlentities($r, ENT_COMPAT, 'UTF-8');
395
                    }
396
397
                    $reason = htmlentities($roleChangeData['reason'], ENT_COMPAT, 'UTF-8');
398
399
                    $roleDelta = 'Removed [' . implode(', ', $removed) . '], Added [' . implode(', ', $added) . ']';
400
                    $comment = $roleDelta . ' with comment: ' . $reason;
401
                    break;
402 View Code Duplication
                case 'JobIssue':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
403
                    $jobIssueData = unserialize($logEntry->getComment());
404
                    $errorMessage = $jobIssueData['error'];
405
                    $status = $jobIssueData['status'];
406
407
                    $comment = 'Job ' . htmlentities($status, ENT_COMPAT, 'UTF-8') . ': ';
408
                    $comment .= htmlentities($errorMessage, ENT_COMPAT, 'UTF-8');
409
                    break;
410
                case 'JobIssueRequest':
411
                case 'JobCompletedRequest':
412
                    $jobData = unserialize($logEntry->getComment());
413
414
                    /** @var JobQueue $job */
415
                    $job = JobQueue::getById($jobData['job'], $database);
416
                    $descs = JobQueue::getTaskDescriptions();
417
                    $comment = htmlentities($descs[$job->getTask()], ENT_COMPAT, 'UTF-8');
418
                    break;
419
420
                case 'JobCompleted':
421
                    break;
422
                default:
423
                    $comment = $logEntry->getComment();
424
                    break;
425
            }
426
427
            $logData[] = array(
428
                'timestamp'         => $logEntry->getTimestamp(),
429
                'userid'            => $logEntry->getUser(),
430
                'username'          => $users[$logEntry->getUser()],
431
                'description'       => self::getLogDescription($logEntry),
432
                'objectdescription' => $objectDescription,
433
                'comment'           => $comment,
0 ignored issues
show
Bug introduced by
The variable $comment does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
434
            );
435
        }
436
437
        return array($users, $logData);
438
    }
439
}
440