Completed
Branch newinternal (104de7)
by Simon
10:16
created

LogHelper::getLogActions()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 41
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 41
rs 8.8571
cc 2
eloc 33
nc 2
nop 1
1
<?php
2
/**
3
 * Created by PhpStorm.
4
 * User: stwalkerster
5
 * Date: 26/03/2016
6
 * Time: 02:55
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\PdoDatabase;
22
use Waca\SiteConfiguration;
23
24
class LogHelper
25
{
26
	/**
27
	 * Summary of getRequestLogs
28
	 *
29
	 * @param int         $requestId ID of the request to get logs for
30
	 * @param PdoDatabase $db        Database to use
31
	 *
32
	 * @return array|bool
33
	 */
34
	public static function getRequestLogs($requestId, PdoDatabase $db)
35
	{
36
		$logStatement = $db->prepare(
37
			<<<SQL
38
SELECT * FROM log
39
WHERE objecttype = 'Request' AND objectid = :requestId
40
ORDER BY timestamp DESC
41
SQL
42
		);
43
44
		$result = $logStatement->execute(array(":requestId" => $requestId));
45
		if ($result) {
46
			$data = $logStatement->fetchAll(PDO::FETCH_CLASS, Log::class);
47
48
			/** @var Log $entry */
49
			foreach ($data as $entry) {
50
				$entry->setDatabase($db);
51
			}
52
53
			return $data;
54
		}
55
56
		return false;
57
	}
58
59
	/**
60
	 * Summary of getRequestLogsWithComments
61
	 *
62
	 * @param int         $requestId
63
	 * @param PdoDatabase $db
64
	 *
65
	 * @return array
66
	 */
67
	public static function getRequestLogsWithComments($requestId, PdoDatabase $db)
68
	{
69
		$logs = self::getRequestLogs($requestId, $db);
70
		$comments = Comment::getForRequest($requestId, $db);
71
72
		$items = array_merge($logs, $comments);
73
74
		/**
75
		 * @param DataObject $item
76
		 *
77
		 * @return int
78
		 */
79
		$sortKey = function(DataObject $item) {
80
			if ($item instanceof Log) {
81
				return $item->getTimestamp()->getTimestamp();
82
			}
83
84
			if ($item instanceof Comment) {
85
				return $item->getTime()->getTimestamp();
86
			}
87
88
			return 0;
89
		};
90
91
		do {
92
			$flag = false;
93
94
			$loopLimit = (count($items) - 1);
95
			for ($i = 0; $i < $loopLimit; $i++) {
96
				// are these two items out of order?
97
				if ($sortKey($items[$i]) > $sortKey($items[$i + 1])) {
98
					// swap them
99
					$swap = $items[$i];
100
					$items[$i] = $items[$i + 1];
101
					$items[$i + 1] = $swap;
102
103
					// set a flag to say we've modified the array this time around
104
					$flag = true;
105
				}
106
			}
107
		}
108
		while ($flag);
109
110
		return $items;
111
	}
112
113
	/**
114
	 * Summary of getLogDescription
115
	 *
116
	 * @param Log $entry
117
	 *
118
	 * @return string
119
	 */
120
	public static function getLogDescription(Log $entry)
121
	{
122
		$text = "Deferred to ";
123
		if (substr($entry->getAction(), 0, strlen($text)) == $text) {
124
			// Deferred to a different queue
125
			// This is exactly what we want to display.
126
			return $entry->getAction();
127
		}
128
129
		$text = "Closed custom-n";
130
		if ($entry->getAction() == $text) {
131
			// Custom-closed
132
			return "closed (custom reason - account not created)";
133
		}
134
135
		$text = "Closed custom-y";
136
		if ($entry->getAction() == $text) {
137
			// Custom-closed
138
			return "closed (custom reason - account created)";
139
		}
140
141
		$text = "Closed 0";
142
		if ($entry->getAction() == $text) {
143
			// Dropped the request - short-circuit the lookup
144
			return "dropped request";
145
		}
146
147
		$text = "Closed ";
148
		if (substr($entry->getAction(), 0, strlen($text)) == $text) {
149
			// Closed with a reason - do a lookup here.
150
			$id = substr($entry->getAction(), strlen($text));
151
			/** @var EmailTemplate $template */
152
			$template = EmailTemplate::getById((int)$id, $entry->getDatabase());
153
154
			if ($template != false) {
155
				return "closed (" . $template->getName() . ")";
156
			}
157
		}
158
159
		// Fall back to the basic stuff
160
		$lookup = array(
161
			'Reserved'        => 'reserved',
162
			'Email Confirmed' => 'email-confirmed',
163
			'Unreserved'      => 'unreserved',
164
			'Approved'        => 'approved',
165
			'Suspended'       => 'suspended',
166
			'Banned'          => 'banned',
167
			'Edited'          => 'edited interface message',
168
			'Declined'        => 'declined',
169
			'EditComment-c'   => 'edited a comment',
170
			'EditComment-r'   => 'edited a comment',
171
			'Unbanned'        => 'unbanned',
172
			'Promoted'        => 'promoted to tool admin',
173
			'BreakReserve'    => 'forcibly broke the reservation',
174
			'Prefchange'      => 'changed user preferences',
175
			'Renamed'         => 'renamed',
176
			'Demoted'         => 'demoted from tool admin',
177
			'ReceiveReserved' => 'received the reservation',
178
			'SendReserved'    => 'sent the reservation',
179
			'EditedEmail'     => 'edited email',
180
			'DeletedTemplate' => 'deleted template',
181
			'EditedTemplate'  => 'edited template',
182
			'CreatedEmail'    => 'created email',
183
			'CreatedTemplate' => 'created template',
184
			'SentMail'        => 'sent an email to the requestor',
185
			'Registered'      => 'registered a tool account',
186
		);
187
188
		if (array_key_exists($entry->getAction(), $lookup)) {
189
			return $lookup[$entry->getAction()];
190
		}
191
192
		// OK, I don't know what this is. Fall back to something sane.
193
		return "performed an unknown action ({$entry->getAction()})";
194
	}
195
196
	/**
197
	 * @param PdoDatabase $database
198
	 *
199
	 * @return array
200
	 */
201
	public static function getLogActions(PdoDatabase $database)
202
	{
203
		$lookup = array(
204
			'Reserved'        => 'reserved',
205
			'Email Confirmed' => 'email-confirmed',
206
			'Unreserved'      => 'unreserved',
207
			'Approved'        => 'approved',
208
			'Suspended'       => 'suspended',
209
			'Banned'          => 'banned',
210
			'Edited'          => 'edited interface message',
211
			'Declined'        => 'declined',
212
			'EditComment-c'   => 'edited a comment',
213
			'EditComment-r'   => 'edited a comment',
214
			'Unbanned'        => 'unbanned',
215
			'Promoted'        => 'promoted to tool admin',
216
			'BreakReserve'    => 'forcibly broke the reservation',
217
			'Prefchange'      => 'changed user preferences',
218
			'Renamed'         => 'renamed',
219
			'Demoted'         => 'demoted from tool admin',
220
			'ReceiveReserved' => 'received the reservation',
221
			'SendReserved'    => 'sent the reservation',
222
			'EditedEmail'     => 'edited email',
223
			'DeletedTemplate' => 'deleted template',
224
			'EditedTemplate'  => 'edited template',
225
			'CreatedEmail'    => 'created email',
226
			'CreatedTemplate' => 'created template',
227
			'SentMail'        => 'sent an email to the requestor',
228
			'Registered'      => 'registered a tool account',
229
		);
230
231
		$statement = $database->query(<<<SQL
232
SELECT CONCAT('Closed ', id) AS k, CONCAT('closed (',name,')') AS v
233
FROM emailtemplate;
234
SQL
235
		);
236
		foreach ($statement->fetchAll(PDO::FETCH_ASSOC) as $row) {
237
			$lookup[$row['k']] = $row['v'];
238
		}
239
240
		return $lookup;
241
	}
242
243
	/**
244
	 * Summary of getLogs
245
	 *
246
	 * @param PdoDatabase  $database
247
	 * @param string|null  $userFilter
248
	 * @param string|null  $actionFilter
249
	 * @param string|null  $objectTypeFilter
250
	 * @param integer|null $objectFilter
251
	 * @param integer      $limit
252
	 * @param integer      $offset
253
	 *
254
	 * @return array
255
	 */
256
	public static function getLogs(
257
		PdoDatabase $database,
258
		$userFilter,
259
		$actionFilter,
260
		$objectTypeFilter = null,
261
		$objectFilter = null,
262
		$limit = 100,
263
		$offset = 0
264
	) {
265
		$whereClause = <<<TXT
266
(:userFilter = 0 OR user = :userid)
267
AND (:actionFilter = 0 OR action = :action)
268
AND (:objectFilter = 0 OR objectid = :object)
269
AND (:objectTypeFilter = 0 OR objecttype = :objectType)
270
TXT;
271
		$searchSqlStatement = "SELECT * FROM log WHERE $whereClause ORDER BY timestamp DESC LIMIT :limit OFFSET :offset;";
272
		$countSqlStatement = "SELECT COUNT(1) FROM log WHERE $whereClause;";
273
274
		$searchStatement = $database->prepare($searchSqlStatement);
275
		$countStatement = $database->prepare($countSqlStatement);
276
277
		$searchStatement->bindValue(":limit", $limit, PDO::PARAM_INT);
278
		$searchStatement->bindValue(":offset", $offset, PDO::PARAM_INT);
279
280
		if ($userFilter === null) {
281
			$searchStatement->bindValue(":userFilter", 0, PDO::PARAM_INT);
282
			$countStatement->bindValue(":userFilter", 0, PDO::PARAM_INT);
283
			$searchStatement->bindValue(":userid", 0, PDO::PARAM_INT);
284
			$countStatement->bindValue(":userid", 0, PDO::PARAM_INT);
285
		}
286
		else {
287
			$searchStatement->bindValue(":userFilter", 1, PDO::PARAM_INT);
288
			$countStatement->bindValue(":userFilter", 1, PDO::PARAM_INT);
289
			$searchStatement->bindValue(":userid", User::getByUsername($userFilter, $database)->getId(),
290
				PDO::PARAM_INT);
291
			$countStatement->bindValue(":userid", User::getByUsername($userFilter, $database)->getId(), PDO::PARAM_INT);
292
		}
293
294
		if ($actionFilter === null) {
295
			$searchStatement->bindValue(":actionFilter", 0, PDO::PARAM_INT);
296
			$countStatement->bindValue(":actionFilter", 0, PDO::PARAM_INT);
297
			$searchStatement->bindValue(":action", "", PDO::PARAM_STR);
298
			$countStatement->bindValue(":action", "", PDO::PARAM_STR);
299
		}
300
		else {
301
			$searchStatement->bindValue(":actionFilter", 1, PDO::PARAM_INT);
302
			$countStatement->bindValue(":actionFilter", 1, PDO::PARAM_INT);
303
			$searchStatement->bindValue(":action", $actionFilter, PDO::PARAM_STR);
304
			$countStatement->bindValue(":action", $actionFilter, PDO::PARAM_STR);
305
		}
306
307
		if ($objectTypeFilter === null) {
308
			$searchStatement->bindValue(":objectTypeFilter", 0, PDO::PARAM_INT);
309
			$countStatement->bindValue(":objectTypeFilter", 0, PDO::PARAM_INT);
310
			$searchStatement->bindValue(":objectType", "", PDO::PARAM_STR);
311
			$countStatement->bindValue(":objectType", "", PDO::PARAM_STR);
312
		}
313
		else {
314
			$searchStatement->bindValue(":objectTypeFilter", 1, PDO::PARAM_INT);
315
			$countStatement->bindValue(":objectTypeFilter", 1, PDO::PARAM_INT);
316
			$searchStatement->bindValue(":objectType", $objectTypeFilter, PDO::PARAM_STR);
317
			$countStatement->bindValue(":objectType", $objectTypeFilter, PDO::PARAM_STR);
318
		}
319
320
		if ($objectFilter === null) {
321
			$searchStatement->bindValue(":objectFilter", 0, PDO::PARAM_INT);
322
			$countStatement->bindValue(":objectFilter", 0, PDO::PARAM_INT);
323
			$searchStatement->bindValue(":object", "", PDO::PARAM_STR);
324
			$countStatement->bindValue(":object", "", PDO::PARAM_STR);
325
		}
326
		else {
327
			$searchStatement->bindValue(":objectFilter", 1, PDO::PARAM_INT);
328
			$countStatement->bindValue(":objectFilter", 1, PDO::PARAM_INT);
329
			$searchStatement->bindValue(":object", $objectFilter, PDO::PARAM_INT);
330
			$countStatement->bindValue(":object", $objectFilter, PDO::PARAM_INT);
331
		}
332
333
		if (!$countStatement->execute()) {
334
			return array(false, false);
335
		}
336
337
		$count = $countStatement->fetchColumn(0);
338
		$countStatement->closeCursor();
339
340
		if ($searchStatement->execute()) {
341
			$data = $searchStatement->fetchAll(PDO::FETCH_CLASS, Log::class);
342
343
			/** @var Log $entry */
344
			foreach ($data as $entry) {
345
				$entry->setDatabase($database);
346
			}
347
348
			return array($data, $count);
349
		}
350
351
		return array(false, false);
352
	}
353
354
	/**
355
	 * This returns a HTML
356
	 *
357
	 * @param string            $objectId
358
	 * @param string            $objectType
359
	 * @param PdoDatabase       $database
360
	 * @param SiteConfiguration $configuration
361
	 *
362
	 * @return null|string
363
	 * @category Security-Critical
364
	 */
365
	private static function getObjectDescription($objectId, $objectType, PdoDatabase $database, SiteConfiguration $configuration)
366
	{
367
		if ($objectType == '') {
368
			return null;
369
		}
370
371
		$baseurl = $configuration->getBaseUrl();
372
373
		switch ($objectType) {
374
			case 'Ban':
375
				/** @var Ban $ban */
376
				$ban = Ban::getById($objectId, $database);
377
378
				return 'Ban #' . $objectId . " (" . htmlentities($ban->getTarget()) . ")</a>";
379
			case 'EmailTemplate':
380
				/** @var EmailTemplate $emailTemplate */
381
				$emailTemplate = EmailTemplate::getById($objectId, $database);
382
				$name = htmlentities($emailTemplate->getName(), ENT_COMPAT, 'UTF-8');
383
384
				return <<<HTML
385
<a href="{$baseurl}/internal.php/emailManagement/view?id={$objectId}">Email Template #{$objectId} ({$name})</a>
386
HTML;
387
			case 'SiteNotice':
388
				return "<a href=\"{$baseurl}/internal.php/siteNotice\">the site notice</a>";
389
			case 'Request':
390
				/** @var Request $request */
391
				$request = Request::getById($objectId, $database);
392
				$name = htmlentities($request->getName(), ENT_COMPAT, 'UTF-8');
393
394
				return <<<HTML
395
<a href="{$baseurl}/internal.php/viewRequest?id={$objectId}">Request #{$objectId} ({$name})</a>
396
HTML;
397
			case 'User':
398
				/** @var User $user */
399
				$user = User::getById($objectId, $database);
400
				$username = htmlentities($user->getUsername(), ENT_COMPAT, 'UTF-8');
401
402
				return "<a href=\"{$baseurl}/internal.php/statistics/users/detail?user={$objectId}\">{$username}</a>";
403
			case 'WelcomeTemplate':
404
				/** @var WelcomeTemplate $welcomeTemplate */
405
				$welcomeTemplate = WelcomeTemplate::getById($objectId, $database);
406
				$userCode = htmlentities($welcomeTemplate->getUserCode(), ENT_COMPAT, 'UTF-8');
407
408
				return "<a href=\"{$baseurl}/internal.php/welcomeTemplates/view?id={$objectId}\">{$userCode}</a>";
409
			default:
410
				return '[' . $objectType . " " . $objectId . ']';
411
				break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
412
		}
413
	}
414
415
	/**
416
	 * @param    Log[]          $logs
417
	 * @param     PdoDatabase   $database
418
	 * @param SiteConfiguration $configuration
419
	 *
420
	 * @return array
421
	 * @throws Exception
422
	 */
423
	public static function prepareLogsForTemplate($logs, PdoDatabase $database, SiteConfiguration $configuration)
424
	{
425
		$userIds = array();
426
427
		/** @var Log $logEntry */
428
		foreach ($logs as $logEntry) {
429
			if (!$logEntry instanceof Log) {
430
				// if this happens, we've done something wrong with passing back the log data.
431
				throw new Exception('Log entry is not an instance of a Log, this should never happen.');
432
			}
433
434
			$user = $logEntry->getUser();
435
			if ($user === -1) {
436
				continue;
437
			}
438
439
			if (!array_search($user, $userIds)) {
440
				$userIds[] = $user;
441
			}
442
		}
443
444
		$users = User::getUsernames($userIds, $database);
445
		$users[-1] = User::getCommunity()->getUsername();
446
447
		$logData = array();
448
449
		/** @var Log $logEntry */
450
		foreach ($logs as $logEntry) {
451
			$objectDescription = self::getObjectDescription($logEntry->getObjectId(), $logEntry->getObjectType(),
452
				$database, $configuration);
453
454
			if($logEntry->getAction() === 'Renamed'){
455
				$renameData = unserialize($logEntry->getComment());
456
				$oldName = htmlentities($renameData['old'], ENT_COMPAT, 'UTF-8');
457
				$newName = htmlentities($renameData['new'], ENT_COMPAT, 'UTF-8');
458
				$comment = 'Renamed \'' . $oldName . '\' to \'' . $newName . '\'.';
459
			}
460
			else{
461
				$comment = $logEntry->getComment();
462
			}
463
464
			$logData[] = array(
465
				'timestamp'         => $logEntry->getTimestamp(),
466
				'userid'            => $logEntry->getUser(),
467
				'username'          => $users[$logEntry->getUser()],
468
				'description'       => self::getLogDescription($logEntry),
469
				'objectdescription' => $objectDescription,
470
				'comment'           => $comment,
471
			);
472
		}
473
474
		return array($users, $logData);
475
	}
476
}