Completed
Branch development (176841)
by Elk
06:59
created

ModLogEntriesReplacement   A

Complexity

Total Complexity 2

Size/Duplication

Total Lines 18
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
eloc 7
dl 0
loc 18
rs 10
c 0
b 0
f 0
wmc 2
ccs 0
cts 6
cp 0

1 Method

Rating   Name   Duplication   Size   Complexity  
A callback() 0 7 2
1
<?php
2
3
/**
4
 * Moderation log helper functions.
5
 *
6
 * @package   ElkArte Forum
7
 * @copyright ElkArte Forum contributors
8
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file)
9
 *
10
 * This file contains code covered by:
11
 * copyright:	2011 Simple Machines (http://www.simplemachines.org)
12
 *
13
 * @version 2.0 dev
14
 *
15
 */
16
17
/**
18
 * Get the number of mod log entries.
19
 * Callback for createList() in Modlog::action_log().
20
 *
21
 * @param string|null $query_string
22
 * @param mixed[] $query_params
23
 * @param int $log_type
24
 *
25
 * @return
26
 */
27
function list_getModLogEntryCount($query_string = '', $query_params = array(), $log_type = 1)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
28
{
29
	$db = database();
30
31
	$modlog_query = allowedTo('admin_forum') || User::$info->mod_cache['bq'] == '1=1' ? '1=1' : (User::$info->mod_cache['bq'] == '0=1' ? 'lm.id_board = 0 AND lm.id_topic = 0' : (strtr(User::$info->mod_cache['bq'], array('id_board' => 'b.id_board')) . ' AND ' . strtr(User::$info->mod_cache['bq'], array('id_board' => 't.id_board'))));
32
33
	$result = $db->query('', '
34
		SELECT COUNT(*)
35
		FROM {db_prefix}log_actions AS lm
36
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lm.id_member)
37
			LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = CASE WHEN mem.id_group = {int:reg_group_id} THEN mem.id_post_group ELSE mem.id_group END)
38
			LEFT JOIN {db_prefix}boards AS b ON (b.id_board = lm.id_board)
39
			LEFT JOIN {db_prefix}topics AS t ON (t.id_topic = lm.id_topic)
40
		WHERE id_log = {int:log_type}
41
			AND {raw:modlog_query}'
42
			. (!empty($query_string) ? '
43
				AND ' . $query_string : ''),
44
		array_merge($query_params, array(
45
			'reg_group_id' => 0,
46
			'log_type' => $log_type,
47
			'modlog_query' => $modlog_query,
48
		))
49
	);
50
	list ($entry_count) = $db->fetch_row($result);
51
	$db->free_result($result);
52
53
	return $entry_count;
54
}
55
56
/**
57
 * Gets the moderation log entries that match the specified parameters.
58
 * Callback for createList() in Modlog::action_log().
59
 *
60
 * @param int $start The item to start with (for pagination purposes)
61
 * @param int $items_per_page The number of items to show per page
62
 * @param string $sort A string indicating how to sort the results
63
 * @param string|null $query_string
64
 * @param mixed[] $query_params
65
 * @param int $log_type
66
 *
67
 * @return array
68
 */
69
function list_getModLogEntries($start, $items_per_page, $sort, $query_string = '', $query_params = array(), $log_type = 1)
70
{
71
	global $context, $scripturl, $txt;
72
73
	$db = database();
74
75
	$modlog_query = allowedTo('admin_forum') || User::$info->mod_cache['bq'] == '1=1' ? '1=1' : (User::$info->mod_cache['bq'] == '0=1' ? 'lm.id_board = 0 AND lm.id_topic = 0' : (strtr(User::$info->mod_cache['bq'], array('id_board' => 'b.id_board')) . ' AND ' . strtr(User::$info->mod_cache['bq'], array('id_board' => 't.id_board'))));
76
77
	// Do a little bit of self protection.
78
	if (!isset($context['hoursdisable']))
79
		$context['hoursdisable'] = 24;
80
81
	// Can they see the IP address?
82
	$seeIP = allowedTo('moderate_forum');
83
84
	// Here we have the query getting the log details.
85
	$result = $db->query('', '
86
		SELECT
87
			lm.id_action, lm.id_member, lm.ip, lm.log_time, lm.action, lm.id_board, lm.id_topic, lm.id_msg, lm.extra,
88
			mem.real_name, mg.group_name
89
		FROM {db_prefix}log_actions AS lm
90
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lm.id_member)
91
			LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = CASE WHEN mem.id_group = {int:reg_group_id} THEN mem.id_post_group ELSE mem.id_group END)
92
			LEFT JOIN {db_prefix}boards AS b ON (b.id_board = lm.id_board)
93
			LEFT JOIN {db_prefix}topics AS t ON (t.id_topic = lm.id_topic)
94
			WHERE id_log = {int:log_type}
95
				AND {raw:modlog_query}'
96
			. (!empty($query_string) ? '
97
				AND ' . $query_string : '') . '
98
		ORDER BY ' . $sort . '
99
		LIMIT ' . $start . ', ' . $items_per_page,
100
		array_merge($query_params, array(
101
			'reg_group_id' => 0,
102
			'log_type' => $log_type,
103
			'modlog_query' => $modlog_query,
104
		))
105
	);
106
107
	// Arrays for decoding objects into.
108
	$topics = array();
109
	$boards = array();
110
	$members = array();
111
	$messages = array();
112
	$entries = array();
113
	while ($row = $db->fetch_assoc($result))
114
	{
115
		$row['extra'] = \ElkArte\Util::unserialize($row['extra']);
116
117
		// Corrupt?
118
		$row['extra'] = is_array($row['extra']) ? $row['extra'] : array();
119
120
		// Add on some of the column stuff info
121
		if (!empty($row['id_board']))
122
		{
123
			if ($row['action'] == 'move')
124
				$row['extra']['board_to'] = $row['id_board'];
125
			else
126
				$row['extra']['board'] = $row['id_board'];
127
		}
128
129
		if (!empty($row['id_topic']))
130
			$row['extra']['topic'] = $row['id_topic'];
131
		if (!empty($row['id_msg']))
132
			$row['extra']['message'] = $row['id_msg'];
133
134
		// Is this associated with a topic?
135 View Code Duplication
		if (isset($row['extra']['topic']))
136
			$topics[(int) $row['extra']['topic']][] = $row['id_action'];
137 View Code Duplication
		if (isset($row['extra']['new_topic']))
138
			$topics[(int) $row['extra']['new_topic']][] = $row['id_action'];
139
140
		// How about a member?
141
		if (isset($row['extra']['member']))
142
		{
143
			// Guests don't have names!
144
			if (empty($row['extra']['member']))
145
				$row['extra']['member'] = $txt['modlog_parameter_guest'];
146
			else
147
			{
148
				// Try to find it...
149
				$members[(int) $row['extra']['member']][] = $row['id_action'];
150
			}
151
		}
152
153
		// Associated with a board?
154 View Code Duplication
		if (isset($row['extra']['board_to']))
155
			$boards[(int) $row['extra']['board_to']][] = $row['id_action'];
156 View Code Duplication
		if (isset($row['extra']['board_from']))
157
			$boards[(int) $row['extra']['board_from']][] = $row['id_action'];
158 View Code Duplication
		if (isset($row['extra']['board']))
159
			$boards[(int) $row['extra']['board']][] = $row['id_action'];
160
161
		// A message?
162
		if (isset($row['extra']['message']))
163
			$messages[(int) $row['extra']['message']][] = $row['id_action'];
164
165
		// IP Info?
166
		if (isset($row['extra']['ip_range']))
167
			if ($seeIP)
168
				$row['extra']['ip_range'] = '<a href="' . $scripturl . '?action=trackip;searchip=' . $row['extra']['ip_range'] . '">' . $row['extra']['ip_range'] . '</a>';
169
			else
170
				$row['extra']['ip_range'] = $txt['logged'];
171
172
		// Email?
173
		if (isset($row['extra']['email']))
174
			$row['extra']['email'] = '<a href="mailto:' . $row['extra']['email'] . '">' . $row['extra']['email'] . '</a>';
175
176
		// Bans are complex.
177
		if ($row['action'] == 'ban')
178
		{
179
			if (!isset($row['extra']['new']) || $row['extra']['new'] == 1)
180
				$row['action_text'] = $txt['modlog_ac_ban'];
181
			elseif ($row['extra']['new'] == 0)
182
				$row['action_text'] = $txt['modlog_ac_ban_update'];
183
			else
184
				$row['action_text'] = $txt['modlog_ac_ban_remove'];
185
186
			foreach (array('member', 'email', 'ip_range', 'hostname') as $type)
187
				if (isset($row['extra'][$type]))
188
					$row['action_text'] .= $txt['modlog_ac_ban_trigger_' . $type];
189
		}
190
191
		// The array to go to the template. Note here that action is set to a "default" value of the action doesn't match anything in the descriptions. Allows easy adding of logging events with basic details.
192
		$entries[$row['id_action']] = array(
193
			'id' => $row['id_action'],
194
			'ip' => $seeIP ? $row['ip'] : $txt['logged'],
195
			'position' => empty($row['real_name']) && empty($row['group_name']) ? $txt['guest'] : $row['group_name'],
196
			'moderator_link' => $row['id_member'] && !empty($row['real_name']) ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['real_name'] . '</a>' : (empty($row['real_name']) ? ($txt['guest'] . (!empty($row['extra']['member_acted']) ? ' (' . $row['extra']['member_acted'] . ')' : '')) : $row['real_name']),
197
			'time' => standardTime($row['log_time']),
198
			'html_time' => htmlTime($row['log_time']),
199
			'timestamp' => forum_time(true, $row['log_time']),
200
			'editable' => time() > $row['log_time'] + $context['hoursdisable'] * 3600,
201
			'extra' => $row['extra'],
202
			'action' => $row['action'],
203
			'action_text' => isset($row['action_text']) ? $row['action_text'] : '',
204
		);
205
	}
206
	$db->free_result($result);
207
208
	if (!empty($boards))
209
	{
210
		require_once(SUBSDIR . '/Boards.subs.php');
211
		$boards_info = fetchBoardsInfo(array('boards' => array_keys($boards)));
212
213
		foreach ($boards_info as $row)
214
		{
215
			foreach ($boards[$row['id_board']] as $action)
216
			{
217
				// Make the board number into a link - dealing with moving too.
218
				if (isset($entries[$action]['extra']['board_to']) && $entries[$action]['extra']['board_to'] == $row['id_board'])
219
					$entries[$action]['extra']['board_to'] = '<a href="' . $scripturl . '?board=' . $row['id_board'] . '.0">' . $row['name'] . '</a>';
220 View Code Duplication
				elseif (isset($entries[$action]['extra']['board_from']) && $entries[$action]['extra']['board_from'] == $row['id_board'])
221
					$entries[$action]['extra']['board_from'] = '<a href="' . $scripturl . '?board=' . $row['id_board'] . '.0">' . $row['name'] . '</a>';
222 View Code Duplication
				elseif (isset($entries[$action]['extra']['board']) && $entries[$action]['extra']['board'] == $row['id_board'])
223
					$entries[$action]['extra']['board'] = '<a href="' . $scripturl . '?board=' . $row['id_board'] . '.0">' . $row['name'] . '</a>';
224
			}
225
		}
226
	}
227
228
	if (!empty($topics))
229
	{
230
		$request = $db->query('', '
231
			SELECT ms.subject, t.id_topic
232
			FROM {db_prefix}topics AS t
233
				INNER JOIN {db_prefix}messages AS ms ON (ms.id_msg = t.id_first_msg)
234
			WHERE t.id_topic IN ({array_int:topic_list})
235
			LIMIT ' . count(array_keys($topics)),
236
			array(
237
				'topic_list' => array_keys($topics),
238
			)
239
		);
240
		while ($row = $db->fetch_assoc($request))
241
		{
242
			foreach ($topics[$row['id_topic']] as $action)
243
			{
244
				$this_action = &$entries[$action];
245
246
				// This isn't used in the current theme.
247
				$this_action['topic'] = array(
248
					'id' => $row['id_topic'],
249
					'subject' => $row['subject'],
250
					'href' => $scripturl . '?topic=' . $row['id_topic'] . '.0',
251
					'link' => '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.0">' . $row['subject'] . '</a>'
252
				);
253
254
				// Make the topic number into a link - dealing with splitting too.
255
				if (isset($this_action['extra']['topic']) && $this_action['extra']['topic'] == $row['id_topic'])
256
					$this_action['extra']['topic'] = '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.' . (isset($this_action['extra']['message']) ? 'msg' . $this_action['extra']['message'] . '#msg' . $this_action['extra']['message'] : '0') . '">' . $row['subject'] . '</a>';
257
				elseif (isset($this_action['extra']['new_topic']) && $this_action['extra']['new_topic'] == $row['id_topic'])
258
					$this_action['extra']['new_topic'] = '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.' . (isset($this_action['extra']['message']) ? 'msg' . $this_action['extra']['message'] . '#msg' . $this_action['extra']['message'] : '0') . '">' . $row['subject'] . '</a>';
259
			}
260
		}
261
		$db->free_result($request);
262
	}
263
264
	if (!empty($messages))
265
	{
266
		$request = $db->query('', '
267
			SELECT id_msg, subject
268
			FROM {db_prefix}messages
269
			WHERE id_msg IN ({array_int:message_list})
270
			LIMIT ' . count(array_keys($messages)),
271
			array(
272
				'message_list' => array_keys($messages),
273
			)
274
		);
275
		while ($row = $db->fetch_assoc($request))
276
		{
277
			foreach ($messages[$row['id_msg']] as $action)
278
			{
279
				$this_action = &$entries[$action];
280
281
				// This isn't used in the current theme.
282
				$this_action['message'] = array(
283
					'id' => $row['id_msg'],
284
					'subject' => $row['subject'],
285
					'href' => $scripturl . '?msg=' . $row['id_msg'],
286
					'link' => '<a href="' . $scripturl . '?msg=' . $row['id_msg'] . '">' . $row['subject'] . '</a>',
287
				);
288
289
				// Make the message number into a link.
290
				if (isset($this_action['extra']['message']) && $this_action['extra']['message'] == $row['id_msg'])
291
					$this_action['extra']['message'] = '<a href="' . $scripturl . '?msg=' . $row['id_msg'] . '">' . $row['subject'] . '</a>';
292
			}
293
		}
294
		$db->free_result($request);
295
	}
296
297
	if (!empty($members))
298
	{
299
		require_once(SUBSDIR . '/Members.subs.php');
300
301
		// Get the latest activated member's display name.
302
		$result = getBasicMemberData(array_keys($members));
303
		foreach ($result as $row)
0 ignored issues
show
Bug introduced by
The expression $result of type array<integer,*>|boolean is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
304
		{
305
			foreach ($members[$row['id_member']] as $action)
306
			{
307
				// Not used currently.
308
				$entries[$action]['member'] = array(
309
					'id' => $row['id_member'],
310
					'name' => $row['real_name'],
311
					'href' => $scripturl . '?action=profile;u=' . $row['id_member'],
312
					'link' => '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['real_name'] . '</a>'
313
				);
314
315
				// Make the member number into a name.
316
				$entries[$action]['extra']['member'] = '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['real_name'] . '</a>';
317
			}
318
		}
319
	}
320
321
	// Do some formatting of the action string.
322
	$callback = new ModLogEntriesReplacement;
323
	$callback->entries = $entries;
324
	foreach ($entries as $k => $entry)
325
	{
326
		// Make any message info links so its easier to go find that message.
327
		if (isset($entry['extra']['message']) && (empty($entry['message']) || empty($entry['message']['id'])))
328
			$entries[$k]['extra']['message'] = '<a href="' . $scripturl . '?msg=' . $entry['extra']['message'] . '">' . $entry['extra']['message'] . '</a>';
329
330
		// Mark up any deleted members, topics and boards.
331
		foreach (array('board', 'board_from', 'board_to', 'member', 'topic', 'new_topic') as $type)
332
			if (!empty($entry['extra'][$type]) && is_numeric($entry['extra'][$type]))
333
				$entries[$k]['extra'][$type] = sprintf($txt['modlog_id'], $entry['extra'][$type]);
334
335
		if (empty($entries[$k]['action_text']))
336
			$entries[$k]['action_text'] = isset($txt['modlog_ac_' . $entry['action']]) ? $txt['modlog_ac_' . $entry['action']] : $entry['action'];
337
338
		$callback->key = $k;
339
		$entries[$k]['action_text'] = preg_replace_callback('~\{([A-Za-z\d_]+)\}~i', array($callback, 'callback'), $entries[$k]['action_text']);
340
	}
341
342
	// Back we go!
343
	return $entries;
344
}
345
346
/**
347
 * Mod Log Replacement Callback.
348
 *
349
 * Our callback that does the actual replacement.
350
 *
351
 */
352
class ModLogEntriesReplacement
353
{
354
	public $entries;
355
	public $key;
356
357
	/**
358
	 * Matching function to return the value in the callback
359
	 *
360
	 * @param string[] $matches
361
	 *
362
	 * @return string
363
	 */
364
	public function callback($matches)
365
	{
366
		if (isset($this->entries[$this->key]['extra'][$matches[1]]))
367
			return $this->entries[$this->key]['extra'][$matches[1]];
368
		else
369
			return '';
370
	}
371
}
372
373
/**
374
 * Delete logged actions.
375
 *
376
 * @param int $id_log
377
 * @param int $time
378
 * @param string[]|null $delete
379
 */
380
function deleteLogAction($id_log, $time, $delete = null)
381
{
382
	$db = database();
383
384
	$db->query('', '
385
		DELETE FROM {db_prefix}log_actions
386
		WHERE id_log = {int:moderate_log}
387
			' . (isset($delete) ? 'AND id_action IN ({array_string:delete_actions})' : '') . '
388
			AND log_time < {int:twenty_four_hours_wait}',
389
		array(
390
			'twenty_four_hours_wait' => time() - $time * 3600,
391
			'delete_actions' => !is_null($delete) ? array_unique($delete) : null,
392
			'moderate_log' => $id_log,
393
		)
394
	);
395
}
396
397
/**
398
 * Checks if an action has been performed "recently"
399
 *
400
 * @param string $action Name of the action
401
 * @param int $time Timeframe since the last time the action has been performed
402
 *
403
 * @return bool
404
 */
405
function recentlyLogged($action, $time = 60)
406
{
407
	$db = database();
408
409
	$request = $db->query('', '
410
		SELECT COUNT(*)
411
		FROM {db_prefix}log_actions
412
		WHERE action = {string:action}
413
			AND log_time >= {int:last_logged}',
414
		array(
415
			'action' => $action,
416
			'last_logged' => time() - $time,
417
		)
418
	);
419
	list ($present) = $db->fetch_row($request);
420
	$db->free_result($request);
421
422
	return !empty($present);
423
}
424