Completed
Pull Request — rbac (#448)
by Simon
05:21 queued 01:52
created

LogHelper::getObjectTypes()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
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 11
ccs 0
cts 11
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
        return array(
224
            'Ban'             => 'Ban',
225
            'Comment'         => 'Comment',
226
            'EmailTemplate'   => 'Email template',
227
            'Request'         => 'Request',
228
            'SiteNotice'      => 'Site notice',
229
            'User'            => 'User',
230
            'WelcomeTemplate' => 'Welcome template',
231
        );
232
    }
233
234
    /**
235
     * This returns a HTML
236
     *
237
     * @param string            $objectId
238
     * @param string            $objectType
239
     * @param PdoDatabase       $database
240
     * @param SiteConfiguration $configuration
241
     *
242
     * @return null|string
243
     * @category Security-Critical
244
     */
245
    private static function getObjectDescription(
246
        $objectId,
247
        $objectType,
248
        PdoDatabase $database,
249
        SiteConfiguration $configuration
250
    ) {
251
        if ($objectType == '') {
252
            return null;
253
        }
254
255
        $baseurl = $configuration->getBaseUrl();
256
257
        switch ($objectType) {
258
            case 'Ban':
259
                /** @var Ban $ban */
260
                $ban = Ban::getById($objectId, $database);
261
262
                if($ban === false) {
263
                    return 'Ban #' . $objectId . "</a>";
264
                }
265
266
                return 'Ban #' . $objectId . " (" . htmlentities($ban->getTarget()) . ")</a>";
267 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...
268
                /** @var EmailTemplate $emailTemplate */
269
                $emailTemplate = EmailTemplate::getById($objectId, $database);
270
                $name = htmlentities($emailTemplate->getName(), ENT_COMPAT, 'UTF-8');
271
272
                return <<<HTML
273
<a href="{$baseurl}/internal.php/emailManagement/view?id={$objectId}">Email Template #{$objectId} ({$name})</a>
274
HTML;
275
            case 'SiteNotice':
276
                return "<a href=\"{$baseurl}/internal.php/siteNotice\">the site notice</a>";
277 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...
278
                /** @var Request $request */
279
                $request = Request::getById($objectId, $database);
280
                $name = htmlentities($request->getName(), ENT_COMPAT, 'UTF-8');
281
282
                return <<<HTML
283
<a href="{$baseurl}/internal.php/viewRequest?id={$objectId}">Request #{$objectId} ({$name})</a>
284
HTML;
285
            case 'User':
286
                /** @var User $user */
287
                $user = User::getById($objectId, $database);
288
                $username = htmlentities($user->getUsername(), ENT_COMPAT, 'UTF-8');
289
290
                return "<a href=\"{$baseurl}/internal.php/statistics/users/detail?user={$objectId}\">{$username}</a>";
291
            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...
292
                /** @var WelcomeTemplate $welcomeTemplate */
293
                $welcomeTemplate = WelcomeTemplate::getById($objectId, $database);
294
295
                // some old templates have been completely deleted and lost to the depths of time.
296
                if ($welcomeTemplate === false) {
297
                    return "Welcome template #{$objectId}";
298
                }
299
                else {
300
                    $userCode = htmlentities($welcomeTemplate->getUserCode(), ENT_COMPAT, 'UTF-8');
301
302
                    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...
303
                }
304
            default:
305
                return '[' . $objectType . " " . $objectId . ']';
306
        }
307
    }
308
309
    /**
310
     * @param    Log[]          $logs
311
     * @param     PdoDatabase   $database
312
     * @param SiteConfiguration $configuration
313
     *
314
     * @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...
315
     * @throws Exception
316
     */
317
    public static function prepareLogsForTemplate($logs, PdoDatabase $database, SiteConfiguration $configuration)
318
    {
319
        $userIds = array();
320
321
        /** @var Log $logEntry */
322
        foreach ($logs as $logEntry) {
323
            if (!$logEntry instanceof Log) {
324
                // if this happens, we've done something wrong with passing back the log data.
325
                throw new Exception('Log entry is not an instance of a Log, this should never happen.');
326
            }
327
328
            $user = $logEntry->getUser();
329
            if ($user === -1) {
330
                continue;
331
            }
332
333
            if (!array_search($user, $userIds)) {
334
                $userIds[] = $user;
335
            }
336
        }
337
338
        $users = UserSearchHelper::get($database)->inIds($userIds)->fetchMap('username');
339
        $users[-1] = User::getCommunity()->getUsername();
340
341
        $logData = array();
342
343
        /** @var Log $logEntry */
344
        foreach ($logs as $logEntry) {
345
            $objectDescription = self::getObjectDescription($logEntry->getObjectId(), $logEntry->getObjectType(),
346
                $database, $configuration);
347
348
            if ($logEntry->getAction() === 'Renamed') {
349
                $renameData = unserialize($logEntry->getComment());
350
                $oldName = htmlentities($renameData['old'], ENT_COMPAT, 'UTF-8');
351
                $newName = htmlentities($renameData['new'], ENT_COMPAT, 'UTF-8');
352
                $comment = 'Renamed \'' . $oldName . '\' to \'' . $newName . '\'.';
353
            }
354
            else if ($logEntry->getAction() === 'RoleChange') {
355
                $roleChangeData = unserialize($logEntry->getComment());
356
357
                $removed = array();
358
                foreach ($roleChangeData['removed'] as $r) {
359
                    $removed[] = htmlentities($r, ENT_COMPAT, 'UTF-8');
360
                }
361
362
                $added = array();
363
                foreach ($roleChangeData['added'] as $r) {
364
                    $added[] = htmlentities($r, ENT_COMPAT, 'UTF-8');
365
                }
366
367
                $reason = htmlentities($roleChangeData['reason'], ENT_COMPAT, 'UTF-8');
368
369
                $roleDelta = 'Removed [' . implode(', ', $removed) . '], Added [' . implode(', ', $added) . ']';
370
                $comment = $roleDelta . ' with comment: ' . $reason;
371
            }
372
            else {
373
                $comment = $logEntry->getComment();
374
            }
375
376
            $logData[] = array(
377
                'timestamp'         => $logEntry->getTimestamp(),
378
                'userid'            => $logEntry->getUser(),
379
                'username'          => $users[$logEntry->getUser()],
380
                'description'       => self::getLogDescription($logEntry),
381
                'objectdescription' => $objectDescription,
382
                'comment'           => $comment,
383
            );
384
        }
385
386
        return array($users, $logData);
387
    }
388
}
389