Completed
Pull Request — rbac (#448)
by Simon
07:19 queued 03:56
created

LogHelper::getObjectTypes()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
304
                }
305
            default:
306
                return '[' . $objectType . " " . $objectId . ']';
307
        }
308
    }
309
310
    /**
311
     * @param    Log[]          $logs
312
     * @param     PdoDatabase   $database
313
     * @param SiteConfiguration $configuration
314
     *
315
     * @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...
316
     * @throws Exception
317
     */
318
    public static function prepareLogsForTemplate($logs, PdoDatabase $database, SiteConfiguration $configuration)
319
    {
320
        $userIds = array();
321
322
        /** @var Log $logEntry */
323
        foreach ($logs as $logEntry) {
324
            if (!$logEntry instanceof Log) {
325
                // if this happens, we've done something wrong with passing back the log data.
326
                throw new Exception('Log entry is not an instance of a Log, this should never happen.');
327
            }
328
329
            $user = $logEntry->getUser();
330
            if ($user === -1) {
331
                continue;
332
            }
333
334
            if (!array_search($user, $userIds)) {
335
                $userIds[] = $user;
336
            }
337
        }
338
339
        $users = UserSearchHelper::get($database)->inIds($userIds)->fetchMap('username');
340
        $users[-1] = User::getCommunity()->getUsername();
341
342
        $logData = array();
343
344
        /** @var Log $logEntry */
345
        foreach ($logs as $logEntry) {
346
            $objectDescription = self::getObjectDescription($logEntry->getObjectId(), $logEntry->getObjectType(),
347
                $database, $configuration);
348
349
            switch ($logEntry->getAction()) {
350
                case 'Renamed':
351
                    $renameData = unserialize($logEntry->getComment());
352
                    $oldName = htmlentities($renameData['old'], ENT_COMPAT, 'UTF-8');
353
                    $newName = htmlentities($renameData['new'], ENT_COMPAT, 'UTF-8');
354
                    $comment = 'Renamed \'' . $oldName . '\' to \'' . $newName . '\'.';
355
                    break;
356
                case 'RoleChange':
357
                    $roleChangeData = unserialize($logEntry->getComment());
358
359
                    $removed = array();
360
                    foreach ($roleChangeData['removed'] as $r) {
361
                        $removed[] = htmlentities($r, ENT_COMPAT, 'UTF-8');
362
                    }
363
364
                    $added = array();
365
                    foreach ($roleChangeData['added'] as $r) {
366
                        $added[] = htmlentities($r, ENT_COMPAT, 'UTF-8');
367
                    }
368
369
                    $reason = htmlentities($roleChangeData['reason'], ENT_COMPAT, 'UTF-8');
370
371
                    $roleDelta = 'Removed [' . implode(', ', $removed) . '], Added [' . implode(', ', $added) . ']';
372
                    $comment = $roleDelta . ' with comment: ' . $reason;
373
                    break;
374
                default:
375
                    $comment = $logEntry->getComment();
376
                    break;
377
            }
378
379
            $logData[] = array(
380
                'timestamp'         => $logEntry->getTimestamp(),
381
                'userid'            => $logEntry->getUser(),
382
                'username'          => $users[$logEntry->getUser()],
383
                'description'       => self::getLogDescription($logEntry),
384
                'objectdescription' => $objectDescription,
385
                'comment'           => $comment,
386
            );
387
        }
388
389
        return array($users, $logData);
390
    }
391
}
392