Passed
Pull Request — release-2.1 (#5074)
by John
05:59
created

showPermissions()   F

Complexity

Conditions 29
Paths 19616

Size

Total Lines 181
Code Lines 108

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 29
eloc 108
c 0
b 0
f 0
nop 1
dl 0
loc 181
rs 0
nc 19616

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * Simple Machines Forum (SMF)
5
 *
6
 * @package SMF
7
 * @author Simple Machines http://www.simplemachines.org
8
 * @copyright 2018 Simple Machines and individual contributors
9
 * @license http://www.simplemachines.org/about/smf/license.php BSD
10
 *
11
 * @version 2.1 Beta 4
12
 */
13
14
if (!defined('SMF'))
15
	die('No direct access...');
16
17
/**
18
 * View a summary.
19
 * @param int $memID The ID of the member
20
 */
21
function summary($memID)
22
{
23
	global $context, $memberContext, $txt, $modSettings, $user_profile, $sourcedir, $scripturl, $smcFunc;
24
25
	// Attempt to load the member's profile data.
26
	if (!loadMemberContext($memID) || !isset($memberContext[$memID]))
27
		fatal_lang_error('not_a_user', false, 404);
0 ignored issues
show
Bug introduced by
404 of type integer is incompatible with the type array expected by parameter $sprintf of fatal_lang_error(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

27
		fatal_lang_error('not_a_user', false, /** @scrutinizer ignore-type */ 404);
Loading history...
28
29
	// Set up the stuff and load the user.
30
	$context += array(
31
		'page_title' => sprintf($txt['profile_of_username'], $memberContext[$memID]['name']),
32
		'can_send_pm' => allowedTo('pm_send'),
33
		'can_have_buddy' => allowedTo('profile_extra_own') && !empty($modSettings['enable_buddylist']),
34
		'can_issue_warning' => allowedTo('issue_warning') && $modSettings['warning_settings'][0] == 1,
35
		'can_view_warning' => (allowedTo('moderate_forum') || allowedTo('issue_warning') || allowedTo('view_warning_any') || ($context['user']['is_owner'] && allowedTo('view_warning_own')) && $modSettings['warning_settings'][0] === 1)
36
	);
37
	$context['member'] = &$memberContext[$memID];
38
39
	// Set a canonical URL for this page.
40
	$context['canonical_url'] = $scripturl . '?action=profile;u=' . $memID;
41
42
	// Are there things we don't show?
43
	$context['disabled_fields'] = isset($modSettings['disabled_profile_fields']) ? array_flip(explode(',', $modSettings['disabled_profile_fields'])) : array();
44
	// Menu tab
45
	$context[$context['profile_menu_name']]['tab_data'] = array(
46
		'title' => $txt['summary'],
47
		'icon' => 'profile_hd.png'
48
	);
49
50
	// See if they have broken any warning levels...
51
	list ($modSettings['warning_enable'], $modSettings['user_limit']) = explode(',', $modSettings['warning_settings']);
52
	if (!empty($modSettings['warning_mute']) && $modSettings['warning_mute'] <= $context['member']['warning'])
53
		$context['warning_status'] = $txt['profile_warning_is_muted'];
54
	elseif (!empty($modSettings['warning_moderate']) && $modSettings['warning_moderate'] <= $context['member']['warning'])
55
		$context['warning_status'] = $txt['profile_warning_is_moderation'];
56
	elseif (!empty($modSettings['warning_watch']) && $modSettings['warning_watch'] <= $context['member']['warning'])
57
		$context['warning_status'] = $txt['profile_warning_is_watch'];
58
59
	// They haven't even been registered for a full day!?
60
	$days_registered = (int) ((time() - $user_profile[$memID]['date_registered']) / (3600 * 24));
61
	if (empty($user_profile[$memID]['date_registered']) || $days_registered < 1)
62
		$context['member']['posts_per_day'] = $txt['not_applicable'];
63
	else
64
		$context['member']['posts_per_day'] = comma_format($context['member']['real_posts'] / $days_registered, 3);
65
66
	// Set the age...
67
	if (empty($context['member']['birth_date']) || substr($context['member']['birth_date'], 0, 4) < 1002)
68
	{
69
		$context['member'] += array(
70
			'age' => $txt['not_applicable'],
71
			'today_is_birthday' => false
72
		);
73
	}
74
	else
75
	{
76
		list ($birth_year, $birth_month, $birth_day) = sscanf($context['member']['birth_date'], '%d-%d-%d');
77
		$datearray = getdate(forum_time());
78
		$context['member'] += array(
79
			'age' => $birth_year <= 1004 ? $txt['not_applicable'] : $datearray['year'] - $birth_year - (($datearray['mon'] > $birth_month || ($datearray['mon'] == $birth_month && $datearray['mday'] >= $birth_day)) ? 0 : 1),
80
			'today_is_birthday' => $datearray['mon'] == $birth_month && $datearray['mday'] == $birth_day
81
		);
82
	}
83
84
	if (allowedTo('moderate_forum'))
85
	{
86
		// Make sure it's a valid ip address; otherwise, don't bother...
87
		if (preg_match('/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/', $memberContext[$memID]['ip']) == 1 && empty($modSettings['disableHostnameLookup']))
88
			$context['member']['hostname'] = host_from_ip($memberContext[$memID]['ip']);
89
		else
90
			$context['member']['hostname'] = '';
91
92
		$context['can_see_ip'] = true;
93
	}
94
	else
95
		$context['can_see_ip'] = false;
96
97
	// Are they hidden?
98
	$context['member']['is_hidden'] = empty($user_profile[$memID]['show_online']);
99
	$context['member']['show_last_login'] = allowedTo('admin_forum') || !$context['member']['is_hidden'];
100
101
	if (!empty($modSettings['who_enabled']) && $context['member']['show_last_login'])
102
	{
103
		include_once($sourcedir . '/Who.php');
104
		$action = determineActions($user_profile[$memID]['url']);
105
106
		if ($action !== false)
0 ignored issues
show
introduced by
The condition $action !== false is always true.
Loading history...
107
			$context['member']['action'] = $action;
108
	}
109
110
	// If the user is awaiting activation, and the viewer has permission - setup some activation context messages.
111
	if ($context['member']['is_activated'] % 10 != 1 && allowedTo('moderate_forum'))
112
	{
113
		$context['activate_type'] = $context['member']['is_activated'];
114
		// What should the link text be?
115
		$context['activate_link_text'] = in_array($context['member']['is_activated'], array(3, 4, 5, 13, 14, 15)) ? $txt['account_approve'] : $txt['account_activate'];
116
117
		// Should we show a custom message?
118
		$context['activate_message'] = isset($txt['account_activate_method_' . $context['member']['is_activated'] % 10]) ? $txt['account_activate_method_' . $context['member']['is_activated'] % 10] : $txt['account_not_activated'];
119
120
		// If they can be approved, we need to set up a token for them.
121
		$context['token_check'] = 'profile-aa' . $memID;
122
		createToken($context['token_check'], 'get');
123
124
		$context['activate_link'] = $scripturl . '?action=profile;save;area=activateaccount;u=' . $context['id_member'] . ';' . $context['session_var'] . '=' . $context['session_id'] . ';' . $context[$context['token_check'] . '_token_var'] . '=' . $context[$context['token_check'] . '_token'];
125
	}
126
127
	// Is the signature even enabled on this forum?
128
	$context['signature_enabled'] = substr($modSettings['signature_settings'], 0, 1) == 1;
129
130
	// Prevent signature images from going outside the box.
131
	if ($context['signature_enabled'])
132
	{
133
		list ($sig_limits, $sig_bbc) = explode(':', $modSettings['signature_settings']);
134
		$sig_limits = explode(',', $sig_limits);
135
136
		if (!empty($sig_limits[5]) || !empty($sig_limits[6]))
137
			addInlineCss('
138
	.signature img { ' . (!empty($sig_limits[5]) ? 'max-width: ' . (int) $sig_limits[5] . 'px; ' : '') . (!empty($sig_limits[6]) ? 'max-height: ' . (int) $sig_limits[6] . 'px; ' : '') . '}');
139
	}
140
141
	// How about, are they banned?
142
	$context['member']['bans'] = array();
143
	if (allowedTo('moderate_forum'))
144
	{
145
		// Can they edit the ban?
146
		$context['can_edit_ban'] = allowedTo('manage_bans');
147
148
		$ban_query = array();
149
		$ban_query_vars = array(
150
			'time' => time(),
151
		);
152
		$ban_query[] = 'id_member = ' . $context['member']['id'];
153
		$ban_query[] = ' {inet:ip} BETWEEN bi.ip_low and bi.ip_high';
154
		$ban_query_vars['ip'] = $memberContext[$memID]['ip'];
155
		// Do we have a hostname already?
156
		if (!empty($context['member']['hostname']))
157
		{
158
			$ban_query[] = '({string:hostname} LIKE hostname)';
159
			$ban_query_vars['hostname'] = $context['member']['hostname'];
160
		}
161
		// Check their email as well...
162
		if (strlen($context['member']['email']) != 0)
163
		{
164
			$ban_query[] = '({string:email} LIKE bi.email_address)';
165
			$ban_query_vars['email'] = $context['member']['email'];
166
		}
167
168
		// So... are they banned?  Dying to know!
169
		$request = $smcFunc['db_query']('', '
170
			SELECT bg.id_ban_group, bg.name, bg.cannot_access, bg.cannot_post,
171
				bg.cannot_login, bg.reason
172
			FROM {db_prefix}ban_items AS bi
173
				INNER JOIN {db_prefix}ban_groups AS bg ON (bg.id_ban_group = bi.id_ban_group AND (bg.expire_time IS NULL OR bg.expire_time > {int:time}))
174
			WHERE (' . implode(' OR ', $ban_query) . ')',
175
			$ban_query_vars
176
		);
177
		while ($row = $smcFunc['db_fetch_assoc']($request))
178
		{
179
			// Work out what restrictions we actually have.
180
			$ban_restrictions = array();
181
			foreach (array('access', 'login', 'post') as $type)
182
				if ($row['cannot_' . $type])
183
					$ban_restrictions[] = $txt['ban_type_' . $type];
184
185
			// No actual ban in place?
186
			if (empty($ban_restrictions))
187
				continue;
188
189
			// Prepare the link for context.
190
			$ban_explanation = sprintf($txt['user_cannot_due_to'], implode(', ', $ban_restrictions), '<a href="' . $scripturl . '?action=admin;area=ban;sa=edit;bg=' . $row['id_ban_group'] . '">' . $row['name'] . '</a>');
191
192
			$context['member']['bans'][$row['id_ban_group']] = array(
193
				'reason' => empty($row['reason']) ? '' : '<br><br><strong>' . $txt['ban_reason'] . ':</strong> ' . $row['reason'],
194
				'cannot' => array(
195
					'access' => !empty($row['cannot_access']),
196
					'post' => !empty($row['cannot_post']),
197
					'login' => !empty($row['cannot_login']),
198
				),
199
				'explanation' => $ban_explanation,
200
			);
201
		}
202
		$smcFunc['db_free_result']($request);
203
	}
204
	loadCustomFields($memID);
205
206
	$context['print_custom_fields'] = array();
207
208
	// Any custom profile fields?
209
	if (!empty($context['custom_fields']))
210
		foreach ($context['custom_fields'] as $custom)
211
			$context['print_custom_fields'][$context['cust_profile_fields_placement'][$custom['placement']]][] = $custom;
212
213
}
214
215
/**
216
 * Fetch the alerts a user currently has.
217
 *
218
 * @param int $memID The ID of the member
219
 * @param bool $all Whether to fetch all alerts or just unread ones
220
 * @param int $counter How many alerts to display (0 if displaying all or using pagination)
221
 * @param array $pagination An array containing info for handling pagination. Should have 'start' and 'maxIndex'
222
 * @param bool $withSender With $memberContext from sender
223
 * @return array An array of information about the fetched alerts
224
 */
225
function fetch_alerts($memID, $all = false, $counter = 0, $pagination = array(), $withSender = true)
226
{
227
	global $smcFunc, $txt, $scripturl, $memberContext, $user_info, $user_profile;
228
229
	$query_see_board = build_query_board($memID);
230
	$query_see_board = $query_see_board['query_see_board'];
231
232
	$user_old = $user_info;
233
	// are we someone else?
234
	if (empty($user_info) || $user_info['id'] != $memID)
235
	{
236
		if (empty($user_profile[$memID]))
237
			loadMemberData($memID, false, 'profile');
238
		$user_info = $user_profile[$memID];
239
	}
240
241
	$alerts = array();
242
	$request = $smcFunc['db_query']('', '
243
		SELECT id_alert, alert_time, mem.id_member AS sender_id, COALESCE(mem.real_name, ua.member_name) AS sender_name,
244
			content_type, content_id, content_action, is_read, extra
245
		FROM {db_prefix}user_alerts AS ua
246
			LEFT JOIN {db_prefix}members AS mem ON (ua.id_member_started = mem.id_member)
247
		WHERE ua.id_member = {int:id_member}' . (!$all ? '
248
			AND is_read = 0' : '') . '
249
		ORDER BY id_alert DESC' . (!empty($counter) && empty($pagination) ? '
250
		LIMIT {int:counter}' : '') . (!empty($pagination) && empty($counter) ? '
251
		LIMIT {int:start}, {int:maxIndex}' : ''),
252
		array(
253
			'id_member' => $memID,
254
			'counter' => $counter,
255
			'start' => !empty($pagination['start']) ? $pagination['start'] : 0,
256
			'maxIndex' => !empty($pagination['maxIndex']) ? $pagination['maxIndex'] : 0,
257
		)
258
	);
259
260
	$senders = array();
261
	while ($row = $smcFunc['db_fetch_assoc']($request))
262
	{
263
		$id_alert = array_shift($row);
264
		$row['time'] = timeformat($row['alert_time']);
265
		$row['extra'] = !empty($row['extra']) ? $smcFunc['json_decode']($row['extra'], true) : array();
266
		$alerts[$id_alert] = $row;
267
268
		if (!empty($row['sender_id']))
269
			$senders[] = $row['sender_id'];
270
	}
271
	$smcFunc['db_free_result']($request);
272
273
	if($withSender)
274
	{
275
		$senders = loadMemberData($senders);
276
		foreach ($senders as $member)
277
			loadMemberContext($member);
278
	}
279
280
	// Now go through and actually make with the text.
281
	loadLanguage('Alerts');
282
283
	// Hooks might want to do something snazzy around their own content types - including enforcing permissions if appropriate.
284
	call_integration_hook('integrate_fetch_alerts', array(&$alerts));
285
286
	// For anything that wants us to check board or topic access, let's do that.
287
	$boards = array();
288
	$topics = array();
289
	$msgs = array();
290
	foreach ($alerts as $id_alert => $alert)
291
	{
292
		if (isset($alert['extra']['board']))
293
			$boards[$alert['extra']['board']] = $txt['board_na'];
294
		if (isset($alert['extra']['topic']))
295
			$topics[$alert['extra']['topic']] = $txt['topic_na'];
296
		if ($alert['content_type'] == 'msg')
297
			$msgs[$alert['content_id']] = $txt['topic_na'];
298
	}
299
300
	// Having figured out what boards etc. there are, let's now get the names of them if we can see them. If not, there's already a fallback set up.
301
	if (!empty($boards))
302
	{
303
		$request = $smcFunc['db_query']('', '
304
			SELECT id_board, name
305
			FROM {db_prefix}boards AS b
306
			WHERE ' . $query_see_board . '
307
				AND id_board IN ({array_int:boards})',
308
			array(
309
				'boards' => array_keys($boards),
310
			)
311
		);
312
		while ($row = $smcFunc['db_fetch_assoc']($request))
313
			$boards[$row['id_board']] = '<a href="' . $scripturl . '?board=' . $row['id_board'] . '.0">' . $row['name'] . '</a>';
314
	}
315
	if (!empty($topics))
316
	{
317
		$request = $smcFunc['db_query']('', '
318
			SELECT t.id_topic, m.subject
319
			FROM {db_prefix}topics AS t
320
				INNER JOIN {db_prefix}messages AS m ON (t.id_first_msg = m.id_msg)
321
				INNER JOIN {db_prefix}boards AS b ON (t.id_board = b.id_board)
322
			WHERE ' . $query_see_board . '
323
				AND t.id_topic IN ({array_int:topics})',
324
			array(
325
				'topics' => array_keys($topics),
326
			)
327
		);
328
		while ($row = $smcFunc['db_fetch_assoc']($request))
329
			$topics[$row['id_topic']] = '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.0">' . $row['subject'] . '</a>';
330
	}
331
	if (!empty($msgs))
332
	{
333
		$request = $smcFunc['db_query']('', '
334
			SELECT m.id_msg, t.id_topic, m.subject
335
			FROM {db_prefix}messages AS m
336
				INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic)
337
				INNER JOIN {db_prefix}boards AS b ON (m.id_board = b.id_board)
338
			WHERE ' . $query_see_board . '
339
				AND m.id_msg IN ({array_int:msgs})',
340
			array(
341
				'msgs' => array_keys($msgs),
342
			)
343
		);
344
		while ($row = $smcFunc['db_fetch_assoc']($request))
345
			$msgs[$row['id_msg']] = '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'] . '">' . $row['subject'] . '</a>';
346
	}
347
348
	// Now to go back through the alerts, reattach this extra information and then try to build the string out of it (if a hook didn't already)
349
	foreach ($alerts as $id_alert => $alert)
350
	{
351
		if (!empty($alert['text']))
352
			continue;
353
		if (isset($alert['extra']['board']))
354
			if ($boards[$alert['extra']['board']] == $txt['board_na'])
355
			{
356
				unset($alerts[$id_alert]);
357
				continue;
358
			}
359
			else
360
				$alerts[$id_alert]['extra']['board_msg'] = $boards[$alert['extra']['board']];
361
		if (isset($alert['extra']['topic']))
362
			if ($alert['extra']['topic'] == $txt['topic_na'])
363
			{
364
				unset($alerts[$id_alert]);
365
				continue;
366
			}
367
			else
368
				$alerts[$id_alert]['extra']['topic_msg'] = $topics[$alert['extra']['topic']];
369
		if ($alert['content_type'] == 'msg')
370
			if ($msgs[$alert['content_id']] == $txt['topic_na'])
371
			{
372
				unset($alerts[$id_alert]);
373
				continue;
374
			}
375
			else
376
				$alerts[$id_alert]['extra']['msg_msg'] = $msgs[$alert['content_id']];
377
		if ($alert['content_type'] == 'profile')
378
			$alerts[$id_alert]['extra']['profile_msg'] = '<a href="' . $scripturl . '?action=profile;u=' . $alerts[$id_alert]['content_id'] . '">' . $alerts[$id_alert]['extra']['user_name'] . '</a>';
379
380
		if (!empty($memberContext[$alert['sender_id']]))
381
			$alerts[$id_alert]['sender'] = &$memberContext[$alert['sender_id']];
382
383
		$string = 'alert_' . $alert['content_type'] . '_' . $alert['content_action'];
384
		if (isset($txt[$string]))
385
		{
386
			$extra = $alerts[$id_alert]['extra'];
387
			$search = array('{member_link}', '{scripturl}');
388
			$repl = array(!empty($alert['sender_id']) ? '<a href="' . $scripturl . '?action=profile;u=' . $alert['sender_id'] . '">' . $alert['sender_name'] . '</a>' : $alert['sender_name'], $scripturl);
389
			foreach ($extra as $k => $v)
390
			{
391
				$search[] = '{' . $k . '}';
392
				$repl[] = $v;
393
			}
394
			$alerts[$id_alert]['text'] = str_replace($search, $repl, $txt[$string]);
395
		}
396
	}
397
398
	$user_info = $user_old;
399
400
	return $alerts;
401
}
402
403
/**
404
 * Shows all alerts for this user
405
 *
406
 * @param int $memID The ID of the member
407
 */
408
function showAlerts($memID)
409
{
410
	global $context, $smcFunc, $txt, $sourcedir, $scripturl, $options;
411
412
	require_once($sourcedir . '/Profile-Modify.php');
413
414
	// Prepare the pagination vars.
415
	$maxIndex = 10;
416
	$start = (int) isset($_REQUEST['start']) ? $_REQUEST['start'] : 0;
417
	$count = alert_count($memID);
418
419
	// Get the alerts.
420
	$context['alerts'] = fetch_alerts($memID, true, false, array('start' => $start, 'maxIndex' => $maxIndex));
0 ignored issues
show
Bug introduced by
false of type false is incompatible with the type integer expected by parameter $counter of fetch_alerts(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

420
	$context['alerts'] = fetch_alerts($memID, true, /** @scrutinizer ignore-type */ false, array('start' => $start, 'maxIndex' => $maxIndex));
Loading history...
421
	$toMark = false;
422
	$action = '';
423
424
	//  Are we using checkboxes?
425
	$context['showCheckboxes'] = !empty($options['display_quick_mod']) && $options['display_quick_mod'] == 1;
426
427
	// Create the pagination.
428
	$context['pagination'] = constructPageIndex($scripturl . '?action=profile;area=showalerts;u=' . $memID, $start, $count, $maxIndex, false);
429
430
	// Set some JavaScript for checking all alerts at once.
431
	if ($context['showCheckboxes'])
432
		addInlineJavaScript('
433
		$(function(){
434
			$(\'#select_all\').on(\'change\', function() {
435
				var checkboxes = $(\'ul.quickbuttons\').find(\':checkbox\');
436
				if($(this).prop(\'checked\')) {
437
					checkboxes.prop(\'checked\', true);
438
				}
439
				else {
440
					checkboxes.prop(\'checked\', false);
441
				}
442
			});
443
		});', true);
444
445
	// Set a nice message.
446
	if (!empty($_SESSION['update_message']))
447
	{
448
		$context['update_message'] = $txt['profile_updated_own'];
449
		unset($_SESSION['update_message']);
450
	}
451
452
	// Saving multiple changes?
453
	if (isset($_GET['save']) && !empty($_POST['mark']))
454
	{
455
		// Get the values.
456
		$toMark = array_map('intval', $_POST['mark']);
457
458
		// Which action?
459
		$action = !empty($_POST['mark_as']) ? $smcFunc['htmlspecialchars']($smcFunc['htmltrim']($_POST['mark_as'])) : '';
460
	}
461
462
	// A single change.
463
	if (!empty($_GET['do']) && !empty($_GET['aid']))
464
	{
465
		$toMark = (int) $_GET['aid'];
466
		$action = $smcFunc['htmlspecialchars']($smcFunc['htmltrim']($_GET['do']));
467
	}
468
469
	// Save the changes.
470
	if (!empty($toMark) && !empty($action))
471
	{
472
		checkSession('request');
473
474
		// Call it!
475
		if ($action == 'remove')
476
			alert_delete($toMark, $memID);
477
478
		else
479
			alert_mark($memID, $toMark, $action == 'read' ? 1 : 0);
480
481
		// Set a nice update message.
482
		$_SESSION['update_message'] = true;
483
484
		// Redirect.
485
		redirectexit('action=profile;area=showalerts;u=' . $memID);
486
	}
487
}
488
489
/**
490
 * Show all posts by the current user
491
 * @todo This function needs to be split up properly.
492
 *
493
 * @param int $memID The ID of the member
494
 */
495
function showPosts($memID)
496
{
497
	global $txt, $user_info, $scripturl, $modSettings;
498
	global $context, $user_profile, $sourcedir, $smcFunc, $board;
499
500
	// Some initial context.
501
	$context['start'] = (int) $_REQUEST['start'];
502
	$context['current_member'] = $memID;
503
504
	// Create the tabs for the template.
505
	$context[$context['profile_menu_name']]['tab_data'] = array(
506
		'title' => $txt['showPosts'],
507
		'description' => $txt['showPosts_help'],
508
		'icon' => 'profile_hd.png',
509
		'tabs' => array(
510
			'messages' => array(
511
			),
512
			'topics' => array(
513
			),
514
			'unwatchedtopics' => array(
515
			),
516
			'attach' => array(
517
			),
518
		),
519
	);
520
521
	// Shortcut used to determine which $txt['show*'] string to use for the title, based on the SA
522
	$title = array(
523
		'attach' => 'Attachments',
524
		'unwatchedtopics' => 'Unwatched',
525
		'topics' => 'Topics'
526
	);
527
528
	// Set the page title
529
	if (isset($_GET['sa']) && array_key_exists($_GET['sa'], $title))
530
		$context['page_title'] = $txt['show' . $title[$_GET['sa']]];
531
	else
532
		$context['page_title'] = $txt['showPosts'];
533
534
	$context['page_title'] .= ' - ' . $user_profile[$memID]['real_name'];
535
536
	// Is the load average too high to allow searching just now?
537
	if (!empty($context['load_average']) && !empty($modSettings['loadavg_show_posts']) && $context['load_average'] >= $modSettings['loadavg_show_posts'])
538
		fatal_lang_error('loadavg_show_posts_disabled', false);
539
540
	// If we're specifically dealing with attachments use that function!
541
	if (isset($_GET['sa']) && $_GET['sa'] == 'attach')
542
		return showAttachments($memID);
543
	// Instead, if we're dealing with unwatched topics (and the feature is enabled) use that other function.
544
	elseif (isset($_GET['sa']) && $_GET['sa'] == 'unwatchedtopics')
545
		return showUnwatched($memID);
0 ignored issues
show
Bug introduced by
Are you sure the usage of showUnwatched($memID) is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
546
547
	// Are we just viewing topics?
548
	$context['is_topics'] = isset($_GET['sa']) && $_GET['sa'] == 'topics' ? true : false;
549
550
	// If just deleting a message, do it and then redirect back.
551
	if (isset($_GET['delete']) && !$context['is_topics'])
552
	{
553
		checkSession('get');
554
555
		// We need msg info for logging.
556
		$request = $smcFunc['db_query']('', '
557
			SELECT subject, id_member, id_topic, id_board
558
			FROM {db_prefix}messages
559
			WHERE id_msg = {int:id_msg}',
560
			array(
561
				'id_msg' => (int) $_GET['delete'],
562
			)
563
		);
564
		$info = $smcFunc['db_fetch_row']($request);
565
		$smcFunc['db_free_result']($request);
566
567
		// Trying to remove a message that doesn't exist.
568
		if (empty($info))
569
			redirectexit('action=profile;u=' . $memID . ';area=showposts;start=' . $_GET['start']);
570
571
		// We can be lazy, since removeMessage() will check the permissions for us.
572
		require_once($sourcedir . '/RemoveTopic.php');
573
		removeMessage((int) $_GET['delete']);
574
575
		// Add it to the mod log.
576
		if (allowedTo('delete_any') && (!allowedTo('delete_own') || $info[1] != $user_info['id']))
577
			logAction('delete', array('topic' => $info[2], 'subject' => $info[0], 'member' => $info[1], 'board' => $info[3]));
578
579
		// Back to... where we are now ;).
580
		redirectexit('action=profile;u=' . $memID . ';area=showposts;start=' . $_GET['start']);
581
	}
582
583
	// Default to 10.
584
	if (empty($_REQUEST['viewscount']) || !is_numeric($_REQUEST['viewscount']))
585
		$_REQUEST['viewscount'] = '10';
586
587
	if ($context['is_topics'])
588
		$request = $smcFunc['db_query']('', '
589
			SELECT COUNT(*)
590
			FROM {db_prefix}topics AS t' . ($user_info['query_see_board'] == '1=1' ? '' : '
591
				INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board AND {query_see_board})') . '
592
			WHERE t.id_member_started = {int:current_member}' . (!empty($board) ? '
593
				AND t.id_board = {int:board}' : '') . (!$modSettings['postmod_active'] || $context['user']['is_owner'] ? '' : '
594
				AND t.approved = {int:is_approved}'),
595
			array(
596
				'current_member' => $memID,
597
				'is_approved' => 1,
598
				'board' => $board,
599
			)
600
		);
601
	else
602
		$request = $smcFunc['db_query']('', '
603
			SELECT COUNT(*)
604
			FROM {db_prefix}messages AS m' . ($user_info['query_see_board'] == '1=1' ? '' : '
605
				INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board AND {query_see_board})') . '
606
			WHERE m.id_member = {int:current_member}' . (!empty($board) ? '
607
				AND m.id_board = {int:board}' : '') . (!$modSettings['postmod_active'] || $context['user']['is_owner'] ? '' : '
608
				AND m.approved = {int:is_approved}'),
609
			array(
610
				'current_member' => $memID,
611
				'is_approved' => 1,
612
				'board' => $board,
613
			)
614
		);
615
	list ($msgCount) = $smcFunc['db_fetch_row']($request);
616
	$smcFunc['db_free_result']($request);
617
618
	$request = $smcFunc['db_query']('', '
619
		SELECT MIN(id_msg), MAX(id_msg)
620
		FROM {db_prefix}messages AS m
621
		WHERE m.id_member = {int:current_member}' . (!empty($board) ? '
622
			AND m.id_board = {int:board}' : '') . (!$modSettings['postmod_active'] || $context['user']['is_owner'] ? '' : '
623
			AND m.approved = {int:is_approved}'),
624
		array(
625
			'current_member' => $memID,
626
			'is_approved' => 1,
627
			'board' => $board,
628
		)
629
	);
630
	list ($min_msg_member, $max_msg_member) = $smcFunc['db_fetch_row']($request);
631
	$smcFunc['db_free_result']($request);
632
633
	$range_limit = '';
634
635
	if ($context['is_topics'])
636
		$maxPerPage = empty($modSettings['disableCustomPerPage']) && !empty($options['topics_per_page']) ? $options['topics_per_page'] : $modSettings['defaultMaxTopics'];
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $options seems to never exist and therefore empty should always be true.
Loading history...
637
	else
638
		$maxPerPage = empty($modSettings['disableCustomPerPage']) && !empty($options['messages_per_page']) ? $options['messages_per_page'] : $modSettings['defaultMaxMessages'];
639
640
	$maxIndex = $maxPerPage;
641
642
	// Make sure the starting place makes sense and construct our friend the page index.
643
	$context['page_index'] = constructPageIndex($scripturl . '?action=profile;u=' . $memID . ';area=showposts' . ($context['is_topics'] ? ';sa=topics' : '') . (!empty($board) ? ';board=' . $board : ''), $context['start'], $msgCount, $maxIndex);
644
	$context['current_page'] = $context['start'] / $maxIndex;
645
646
	// Reverse the query if we're past 50% of the pages for better performance.
647
	$start = $context['start'];
648
	$reverse = $_REQUEST['start'] > $msgCount / 2;
649
	if ($reverse)
650
	{
651
		$maxIndex = $msgCount < $context['start'] + $maxPerPage + 1 && $msgCount > $context['start'] ? $msgCount - $context['start'] : $maxPerPage;
652
		$start = $msgCount < $context['start'] + $maxPerPage + 1 || $msgCount < $context['start'] + $maxPerPage ? 0 : $msgCount - $context['start'] - $maxPerPage;
653
	}
654
655
	// Guess the range of messages to be shown.
656
	if ($msgCount > 1000)
657
	{
658
		$margin = floor(($max_msg_member - $min_msg_member) * (($start + $maxPerPage) / $msgCount) + .1 * ($max_msg_member - $min_msg_member));
659
		// Make a bigger margin for topics only.
660
		if ($context['is_topics'])
661
		{
662
			$margin *= 5;
663
			$range_limit = $reverse ? 't.id_first_msg < ' . ($min_msg_member + $margin) : 't.id_first_msg > ' . ($max_msg_member - $margin);
664
		}
665
		else
666
			$range_limit = $reverse ? 'm.id_msg < ' . ($min_msg_member + $margin) : 'm.id_msg > ' . ($max_msg_member - $margin);
667
	}
668
669
	// Find this user's posts.  The left join on categories somehow makes this faster, weird as it looks.
670
	$looped = false;
671
	while (true)
672
	{
673
		if ($context['is_topics'])
674
		{
675
			$request = $smcFunc['db_query']('', '
676
				SELECT
677
					b.id_board, b.name AS bname, c.id_cat, c.name AS cname, t.id_member_started, t.id_first_msg, t.id_last_msg,
678
					t.approved, m.body, m.smileys_enabled, m.subject, m.poster_time, m.id_topic, m.id_msg
679
				FROM {db_prefix}topics AS t
680
					INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
681
					LEFT JOIN {db_prefix}categories AS c ON (c.id_cat = b.id_cat)
682
					INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)
683
				WHERE t.id_member_started = {int:current_member}' . (!empty($board) ? '
684
					AND t.id_board = {int:board}' : '') . (empty($range_limit) ? '' : '
685
					AND ' . $range_limit) . '
686
					AND {query_see_board}' . (!$modSettings['postmod_active'] || $context['user']['is_owner'] ? '' : '
687
					AND t.approved = {int:is_approved} AND m.approved = {int:is_approved}') . '
688
				ORDER BY t.id_first_msg ' . ($reverse ? 'ASC' : 'DESC') . '
689
				LIMIT {int:start}, {int:max}',
690
				array(
691
					'current_member' => $memID,
692
					'is_approved' => 1,
693
					'board' => $board,
694
					'start' => $start,
695
					'max' => $maxIndex,
696
				)
697
			);
698
		}
699
		else
700
		{
701
			$request = $smcFunc['db_query']('', '
702
				SELECT
703
					b.id_board, b.name AS bname, c.id_cat, c.name AS cname, m.id_topic, m.id_msg,
704
					t.id_member_started, t.id_first_msg, t.id_last_msg, m.body, m.smileys_enabled,
705
					m.subject, m.poster_time, m.approved
706
				FROM {db_prefix}messages AS m
707
					INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic)
708
					INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
709
					LEFT JOIN {db_prefix}categories AS c ON (c.id_cat = b.id_cat)
710
				WHERE m.id_member = {int:current_member}' . (!empty($board) ? '
711
					AND b.id_board = {int:board}' : '') . (empty($range_limit) ? '' : '
712
					AND ' . $range_limit) . '
713
					AND {query_see_board}' . (!$modSettings['postmod_active'] || $context['user']['is_owner'] ? '' : '
714
					AND t.approved = {int:is_approved} AND m.approved = {int:is_approved}') . '
715
				ORDER BY m.id_msg ' . ($reverse ? 'ASC' : 'DESC') . '
716
				LIMIT {int:start}, {int:max}',
717
				array(
718
					'current_member' => $memID,
719
					'is_approved' => 1,
720
					'board' => $board,
721
					'start' => $start,
722
					'max' => $maxIndex,
723
				)
724
			);
725
		}
726
727
		// Make sure we quit this loop.
728
		if ($smcFunc['db_num_rows']($request) === $maxIndex || $looped || $range_limit == '')
729
			break;
730
		$looped = true;
731
		$range_limit = '';
732
	}
733
734
	// Start counting at the number of the first message displayed.
735
	$counter = $reverse ? $context['start'] + $maxIndex + 1 : $context['start'];
736
	$context['posts'] = array();
737
	$board_ids = array('own' => array(), 'any' => array());
738
	while ($row = $smcFunc['db_fetch_assoc']($request))
739
	{
740
		// Censor....
741
		censorText($row['body']);
742
		censorText($row['subject']);
743
744
		// Do the code.
745
		$row['body'] = parse_bbc($row['body'], $row['smileys_enabled'], $row['id_msg']);
746
747
		// And the array...
748
		$context['posts'][$counter += $reverse ? -1 : 1] = array(
749
			'body' => $row['body'],
750
			'counter' => $counter,
751
			'category' => array(
752
				'name' => $row['cname'],
753
				'id' => $row['id_cat']
754
			),
755
			'board' => array(
756
				'name' => $row['bname'],
757
				'id' => $row['id_board']
758
			),
759
			'topic' => $row['id_topic'],
760
			'subject' => $row['subject'],
761
			'start' => 'msg' . $row['id_msg'],
762
			'time' => timeformat($row['poster_time']),
763
			'timestamp' => forum_time(true, $row['poster_time']),
764
			'id' => $row['id_msg'],
765
			'can_reply' => false,
766
			'can_mark_notify' => !$context['user']['is_guest'],
767
			'can_delete' => false,
768
			'delete_possible' => ($row['id_first_msg'] != $row['id_msg'] || $row['id_last_msg'] == $row['id_msg']) && (empty($modSettings['edit_disable_time']) || $row['poster_time'] + $modSettings['edit_disable_time'] * 60 >= time()),
769
			'approved' => $row['approved'],
770
			'css_class' => $row['approved'] ? 'windowbg' : 'approvebg',
771
		);
772
773
		if ($user_info['id'] == $row['id_member_started'])
774
			$board_ids['own'][$row['id_board']][] = $counter;
775
		$board_ids['any'][$row['id_board']][] = $counter;
776
	}
777
	$smcFunc['db_free_result']($request);
778
779
	// All posts were retrieved in reverse order, get them right again.
780
	if ($reverse)
781
		$context['posts'] = array_reverse($context['posts'], true);
782
783
	// These are all the permissions that are different from board to board..
784
	if ($context['is_topics'])
785
		$permissions = array(
786
			'own' => array(
787
				'post_reply_own' => 'can_reply',
788
			),
789
			'any' => array(
790
				'post_reply_any' => 'can_reply',
791
			)
792
		);
793
	else
794
		$permissions = array(
795
			'own' => array(
796
				'post_reply_own' => 'can_reply',
797
				'delete_own' => 'can_delete',
798
			),
799
			'any' => array(
800
				'post_reply_any' => 'can_reply',
801
				'delete_any' => 'can_delete',
802
			)
803
		);
804
805
	// For every permission in the own/any lists...
806
	foreach ($permissions as $type => $list)
807
	{
808
		foreach ($list as $permission => $allowed)
809
		{
810
			// Get the boards they can do this on...
811
			$boards = boardsAllowedTo($permission);
812
813
			// Hmm, they can do it on all boards, can they?
814
			if (!empty($boards) && $boards[0] == 0)
815
				$boards = array_keys($board_ids[$type]);
816
817
			// Now go through each board they can do the permission on.
818
			foreach ($boards as $board_id)
819
			{
820
				// There aren't any posts displayed from this board.
821
				if (!isset($board_ids[$type][$board_id]))
822
					continue;
823
824
				// Set the permission to true ;).
825
				foreach ($board_ids[$type][$board_id] as $counter)
826
					$context['posts'][$counter][$allowed] = true;
827
			}
828
		}
829
	}
830
831
	// Clean up after posts that cannot be deleted and quoted.
832
	$quote_enabled = empty($modSettings['disabledBBC']) || !in_array('quote', explode(',', $modSettings['disabledBBC']));
833
	foreach ($context['posts'] as $counter => $dummy)
834
	{
835
		$context['posts'][$counter]['can_delete'] &= $context['posts'][$counter]['delete_possible'];
836
		$context['posts'][$counter]['can_quote'] = $context['posts'][$counter]['can_reply'] && $quote_enabled;
837
	}
838
839
	// Allow last minute changes.
840
	call_integration_hook('integrate_profile_showPosts');
841
}
842
843
/**
844
 * Show all the attachments of a user.
845
 *
846
 * @param int $memID The ID of the member
847
 */
848
function showAttachments($memID)
849
{
850
	global $txt, $scripturl, $modSettings;
851
	global $sourcedir;
852
853
	// OBEY permissions!
854
	$boardsAllowed = boardsAllowedTo('view_attachments');
855
856
	// Make sure we can't actually see anything...
857
	if (empty($boardsAllowed))
858
		$boardsAllowed = array(-1);
859
860
	require_once($sourcedir . '/Subs-List.php');
861
862
	// This is all the information required to list attachments.
863
	$listOptions = array(
864
		'id' => 'attachments',
865
		'width' => '100%',
866
		'items_per_page' => $modSettings['defaultMaxListItems'],
867
		'no_items_label' => $txt['show_attachments_none'],
868
		'base_href' => $scripturl . '?action=profile;area=showposts;sa=attach;u=' . $memID,
869
		'default_sort_col' => 'filename',
870
		'get_items' => array(
871
			'function' => 'list_getAttachments',
872
			'params' => array(
873
				$boardsAllowed,
874
				$memID,
875
			),
876
		),
877
		'get_count' => array(
878
			'function' => 'list_getNumAttachments',
879
			'params' => array(
880
				$boardsAllowed,
881
				$memID,
882
			),
883
		),
884
		'data_check' => array(
885
			'class' => function($data)
886
			{
887
				return $data['approved'] ? '' : 'approvebg';
888
			}
889
		),
890
		'columns' => array(
891
			'filename' => array(
892
				'header' => array(
893
					'value' => $txt['show_attach_filename'],
894
					'class' => 'lefttext',
895
					'style' => 'width: 25%;',
896
				),
897
				'data' => array(
898
					'sprintf' => array(
899
						'format' => '<a href="' . $scripturl . '?action=dlattach;topic=%1$d.0;attach=%2$d">%3$s</a>',
900
						'params' => array(
901
							'topic' => true,
902
							'id' => true,
903
							'filename' => false,
904
						),
905
					),
906
				),
907
				'sort' => array(
908
					'default' => 'a.filename',
909
					'reverse' => 'a.filename DESC',
910
				),
911
			),
912
			'downloads' => array(
913
				'header' => array(
914
					'value' => $txt['show_attach_downloads'],
915
					'style' => 'width: 12%;',
916
				),
917
				'data' => array(
918
					'db' => 'downloads',
919
					'comma_format' => true,
920
				),
921
				'sort' => array(
922
					'default' => 'a.downloads',
923
					'reverse' => 'a.downloads DESC',
924
				),
925
			),
926
			'subject' => array(
927
				'header' => array(
928
					'value' => $txt['message'],
929
					'class' => 'lefttext',
930
					'style' => 'width: 30%;',
931
				),
932
				'data' => array(
933
					'sprintf' => array(
934
						'format' => '<a href="' . $scripturl . '?msg=%1$d">%2$s</a>',
935
						'params' => array(
936
							'msg' => true,
937
							'subject' => false,
938
						),
939
					),
940
				),
941
				'sort' => array(
942
					'default' => 'm.subject',
943
					'reverse' => 'm.subject DESC',
944
				),
945
			),
946
			'posted' => array(
947
				'header' => array(
948
					'value' => $txt['show_attach_posted'],
949
					'class' => 'lefttext',
950
				),
951
				'data' => array(
952
					'db' => 'posted',
953
					'timeformat' => true,
954
				),
955
				'sort' => array(
956
					'default' => 'm.poster_time',
957
					'reverse' => 'm.poster_time DESC',
958
				),
959
			),
960
		),
961
	);
962
963
	// Create the request list.
964
	createList($listOptions);
965
}
966
967
/**
968
 * Get a list of attachments for this user. Callback for the list in showAttachments()
969
 *
970
 * @param int $start Which item to start with (for pagination purposes)
971
 * @param int $items_per_page How many items to show on each page
972
 * @param string $sort A string indicating how to sort the results
973
 * @param array $boardsAllowed An array containing the IDs of the boards they can see
974
 * @param int $memID The ID of the member
975
 * @return array An array of information about the attachments
976
 */
977
function list_getAttachments($start, $items_per_page, $sort, $boardsAllowed, $memID)
978
{
979
	global $smcFunc, $board, $modSettings, $context;
980
981
	// Retrieve some attachments.
982
	$request = $smcFunc['db_query']('', '
983
		SELECT a.id_attach, a.id_msg, a.filename, a.downloads, a.approved, m.id_msg, m.id_topic,
984
			m.id_board, m.poster_time, m.subject, b.name
985
		FROM {db_prefix}attachments AS a
986
			INNER JOIN {db_prefix}messages AS m ON (m.id_msg = a.id_msg)
987
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board AND {query_see_board})
988
		WHERE a.attachment_type = {int:attachment_type}
989
			AND a.id_msg != {int:no_message}
990
			AND m.id_member = {int:current_member}' . (!empty($board) ? '
991
			AND b.id_board = {int:board}' : '') . (!in_array(0, $boardsAllowed) ? '
992
			AND b.id_board IN ({array_int:boards_list})' : '') . (!$modSettings['postmod_active'] || $context['user']['is_owner'] ? '' : '
993
			AND m.approved = {int:is_approved}') . '
994
		ORDER BY {raw:sort}
995
		LIMIT {int:offset}, {int:limit}',
996
		array(
997
			'boards_list' => $boardsAllowed,
998
			'attachment_type' => 0,
999
			'no_message' => 0,
1000
			'current_member' => $memID,
1001
			'is_approved' => 1,
1002
			'board' => $board,
1003
			'sort' => $sort,
1004
			'offset' => $start,
1005
			'limit' => $items_per_page,
1006
		)
1007
	);
1008
	$attachments = array();
1009
	while ($row = $smcFunc['db_fetch_assoc']($request))
1010
		$attachments[] = array(
1011
			'id' => $row['id_attach'],
1012
			'filename' => $row['filename'],
1013
			'downloads' => $row['downloads'],
1014
			'subject' => censorText($row['subject']),
1015
			'posted' => $row['poster_time'],
1016
			'msg' => $row['id_msg'],
1017
			'topic' => $row['id_topic'],
1018
			'board' => $row['id_board'],
1019
			'board_name' => $row['name'],
1020
			'approved' => $row['approved'],
1021
		);
1022
1023
	$smcFunc['db_free_result']($request);
1024
1025
	return $attachments;
1026
}
1027
1028
/**
1029
 * Gets the total number of attachments for the user
1030
 *
1031
 * @param array $boardsAllowed An array of the IDs of the boards they can see
1032
 * @param int $memID The ID of the member
1033
 * @return int The number of attachments
1034
 */
1035
function list_getNumAttachments($boardsAllowed, $memID)
1036
{
1037
	global $board, $smcFunc, $modSettings, $context;
1038
1039
	// Get the total number of attachments they have posted.
1040
	$request = $smcFunc['db_query']('', '
1041
		SELECT COUNT(*)
1042
		FROM {db_prefix}attachments AS a
1043
			INNER JOIN {db_prefix}messages AS m ON (m.id_msg = a.id_msg)
1044
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board AND {query_see_board})
1045
		WHERE a.attachment_type = {int:attachment_type}
1046
			AND a.id_msg != {int:no_message}
1047
			AND m.id_member = {int:current_member}' . (!empty($board) ? '
1048
			AND b.id_board = {int:board}' : '') . (!in_array(0, $boardsAllowed) ? '
1049
			AND b.id_board IN ({array_int:boards_list})' : '') . (!$modSettings['postmod_active'] || $context['user']['is_owner'] ? '' : '
1050
			AND m.approved = {int:is_approved}'),
1051
		array(
1052
			'boards_list' => $boardsAllowed,
1053
			'attachment_type' => 0,
1054
			'no_message' => 0,
1055
			'current_member' => $memID,
1056
			'is_approved' => 1,
1057
			'board' => $board,
1058
		)
1059
	);
1060
	list ($attachCount) = $smcFunc['db_fetch_row']($request);
1061
	$smcFunc['db_free_result']($request);
1062
1063
	return $attachCount;
1064
}
1065
1066
/**
1067
 * Show all the unwatched topics.
1068
 *
1069
 * @param int $memID The ID of the member
1070
 */
1071
function showUnwatched($memID)
1072
{
1073
	global $txt, $user_info, $scripturl, $modSettings, $context, $sourcedir;
1074
1075
	// Only the owner can see the list (if the function is enabled of course)
1076
	if ($user_info['id'] != $memID)
1077
		return;
1078
1079
	require_once($sourcedir . '/Subs-List.php');
1080
1081
	// And here they are: the topics you don't like
1082
	$listOptions = array(
1083
		'id' => 'unwatched_topics',
1084
		'width' => '100%',
1085
		'items_per_page' => (empty($modSettings['disableCustomPerPage']) && !empty($options['topics_per_page'])) ? $options['topics_per_page'] : $modSettings['defaultMaxTopics'],
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $options seems to never exist and therefore empty should always be true.
Loading history...
1086
		'no_items_label' => $txt['unwatched_topics_none'],
1087
		'base_href' => $scripturl . '?action=profile;area=showposts;sa=unwatchedtopics;u=' . $memID,
1088
		'default_sort_col' => 'started_on',
1089
		'get_items' => array(
1090
			'function' => 'list_getUnwatched',
1091
			'params' => array(
1092
				$memID,
1093
			),
1094
		),
1095
		'get_count' => array(
1096
			'function' => 'list_getNumUnwatched',
1097
			'params' => array(
1098
				$memID,
1099
			),
1100
		),
1101
		'columns' => array(
1102
			'subject' => array(
1103
				'header' => array(
1104
					'value' => $txt['subject'],
1105
					'class' => 'lefttext',
1106
					'style' => 'width: 30%;',
1107
				),
1108
				'data' => array(
1109
					'sprintf' => array(
1110
						'format' => '<a href="' . $scripturl . '?topic=%1$d.0">%2$s</a>',
1111
						'params' => array(
1112
							'id_topic' => false,
1113
							'subject' => false,
1114
						),
1115
					),
1116
				),
1117
				'sort' => array(
1118
					'default' => 'm.subject',
1119
					'reverse' => 'm.subject DESC',
1120
				),
1121
			),
1122
			'started_by' => array(
1123
				'header' => array(
1124
					'value' => $txt['started_by'],
1125
					'style' => 'width: 15%;',
1126
				),
1127
				'data' => array(
1128
					'db' => 'started_by',
1129
				),
1130
				'sort' => array(
1131
					'default' => 'mem.real_name',
1132
					'reverse' => 'mem.real_name DESC',
1133
				),
1134
			),
1135
			'started_on' => array(
1136
				'header' => array(
1137
					'value' => $txt['on'],
1138
					'class' => 'lefttext',
1139
					'style' => 'width: 20%;',
1140
				),
1141
				'data' => array(
1142
					'db' => 'started_on',
1143
					'timeformat' => true,
1144
				),
1145
				'sort' => array(
1146
					'default' => 'm.poster_time',
1147
					'reverse' => 'm.poster_time DESC',
1148
				),
1149
			),
1150
			'last_post_by' => array(
1151
				'header' => array(
1152
					'value' => $txt['last_post'],
1153
					'style' => 'width: 15%;',
1154
				),
1155
				'data' => array(
1156
					'db' => 'last_post_by',
1157
				),
1158
				'sort' => array(
1159
					'default' => 'mem.real_name',
1160
					'reverse' => 'mem.real_name DESC',
1161
				),
1162
			),
1163
			'last_post_on' => array(
1164
				'header' => array(
1165
					'value' => $txt['on'],
1166
					'class' => 'lefttext',
1167
					'style' => 'width: 20%;',
1168
				),
1169
				'data' => array(
1170
					'db' => 'last_post_on',
1171
					'timeformat' => true,
1172
				),
1173
				'sort' => array(
1174
					'default' => 'm.poster_time',
1175
					'reverse' => 'm.poster_time DESC',
1176
				),
1177
			),
1178
		),
1179
	);
1180
1181
	// Create the request list.
1182
	createList($listOptions);
1183
1184
	$context['sub_template'] = 'show_list';
1185
	$context['default_list'] = 'unwatched_topics';
1186
}
1187
1188
/**
1189
 * Gets information about unwatched (disregarded) topics. Callback for the list in show_unwatched
1190
 *
1191
 * @param int $start The item to start with (for pagination purposes)
1192
 * @param int $items_per_page How many items to show on each page
1193
 * @param string $sort A string indicating how to sort the results
1194
 * @param int $memID The ID of the member
1195
 * @return array An array of information about the unwatched topics
1196
 */
1197
function list_getUnwatched($start, $items_per_page, $sort, $memID)
1198
{
1199
	global $smcFunc;
1200
1201
	// Get the list of topics we can see
1202
	$request = $smcFunc['db_query']('', '
1203
		SELECT lt.id_topic
1204
		FROM {db_prefix}log_topics as lt
1205
			LEFT JOIN {db_prefix}topics as t ON (lt.id_topic = t.id_topic)
1206
			LEFT JOIN {db_prefix}boards as b ON (t.id_board = b.id_board)
1207
			LEFT JOIN {db_prefix}messages as m ON (t.id_first_msg = m.id_msg)' . (in_array($sort, array('mem.real_name', 'mem.real_name DESC', 'mem.poster_time', 'mem.poster_time DESC')) ? '
1208
			LEFT JOIN {db_prefix}members as mem ON (m.id_member = mem.id_member)' : '') . '
1209
		WHERE lt.id_member = {int:current_member}
1210
			AND unwatched = 1
1211
			AND {query_see_board}
1212
		ORDER BY {raw:sort}
1213
		LIMIT {int:offset}, {int:limit}',
1214
		array(
1215
			'current_member' => $memID,
1216
			'sort' => $sort,
1217
			'offset' => $start,
1218
			'limit' => $items_per_page,
1219
		)
1220
	);
1221
1222
	$topics = array();
1223
	while ($row = $smcFunc['db_fetch_assoc']($request))
1224
		$topics[] = $row['id_topic'];
1225
1226
	$smcFunc['db_free_result']($request);
1227
1228
	// Any topics found?
1229
	$topicsInfo = array();
1230
	if (!empty($topics))
1231
	{
1232
		$request = $smcFunc['db_query']('', '
1233
			SELECT mf.subject, mf.poster_time as started_on, COALESCE(memf.real_name, mf.poster_name) as started_by, ml.poster_time as last_post_on, COALESCE(meml.real_name, ml.poster_name) as last_post_by, t.id_topic
1234
			FROM {db_prefix}topics AS t
1235
				INNER JOIN {db_prefix}messages AS ml ON (ml.id_msg = t.id_last_msg)
1236
				INNER JOIN {db_prefix}messages AS mf ON (mf.id_msg = t.id_first_msg)
1237
				LEFT JOIN {db_prefix}members AS meml ON (meml.id_member = ml.id_member)
1238
				LEFT JOIN {db_prefix}members AS memf ON (memf.id_member = mf.id_member)
1239
			WHERE t.id_topic IN ({array_int:topics})',
1240
			array(
1241
				'topics' => $topics,
1242
			)
1243
		);
1244
		while ($row = $smcFunc['db_fetch_assoc']($request))
1245
			$topicsInfo[] = $row;
1246
		$smcFunc['db_free_result']($request);
1247
	}
1248
1249
	return $topicsInfo;
1250
}
1251
1252
/**
1253
 * Count the number of topics in the unwatched list
1254
 *
1255
 * @param int $memID The ID of the member
1256
 * @return int The number of unwatched topics
1257
 */
1258
function list_getNumUnwatched($memID)
1259
{
1260
	global $smcFunc;
1261
1262
	// Get the total number of attachments they have posted.
1263
	$request = $smcFunc['db_query']('', '
1264
		SELECT COUNT(*)
1265
		FROM {db_prefix}log_topics as lt
1266
		LEFT JOIN {db_prefix}topics as t ON (lt.id_topic = t.id_topic)
1267
		LEFT JOIN {db_prefix}boards as b ON (t.id_board = b.id_board)
1268
		WHERE id_member = {int:current_member}
1269
			AND unwatched = 1
1270
			AND {query_see_board}',
1271
		array(
1272
			'current_member' => $memID,
1273
		)
1274
	);
1275
	list ($unwatchedCount) = $smcFunc['db_fetch_row']($request);
1276
	$smcFunc['db_free_result']($request);
1277
1278
	return $unwatchedCount;
1279
}
1280
1281
/**
1282
 * Gets the user stats for display
1283
 *
1284
 * @param int $memID The ID of the member
1285
 */
1286
function statPanel($memID)
1287
{
1288
	global $txt, $scripturl, $context, $user_profile, $user_info, $modSettings, $smcFunc;
1289
1290
	$context['page_title'] = $txt['statPanel_showStats'] . ' ' . $user_profile[$memID]['real_name'];
1291
1292
	// Is the load average too high to allow searching just now?
1293
	if (!empty($context['load_average']) && !empty($modSettings['loadavg_userstats']) && $context['load_average'] >= $modSettings['loadavg_userstats'])
1294
		fatal_lang_error('loadavg_userstats_disabled', false);
1295
1296
	// General user statistics.
1297
	$timeDays = floor($user_profile[$memID]['total_time_logged_in'] / 86400);
1298
	$timeHours = floor(($user_profile[$memID]['total_time_logged_in'] % 86400) / 3600);
1299
	$context['time_logged_in'] = ($timeDays > 0 ? $timeDays . $txt['totalTimeLogged2'] : '') . ($timeHours > 0 ? $timeHours . $txt['totalTimeLogged3'] : '') . floor(($user_profile[$memID]['total_time_logged_in'] % 3600) / 60) . $txt['totalTimeLogged4'];
1300
	$context['num_posts'] = comma_format($user_profile[$memID]['posts']);
1301
	// Menu tab
1302
	$context[$context['profile_menu_name']]['tab_data'] = array(
1303
		'title' => $txt['statPanel_generalStats'] . ' - ' . $context['member']['name'],
1304
		'icon' => 'stats_info.png'
1305
	);
1306
1307
	// Number of topics started and Number polls started
1308
	$result = $smcFunc['db_query']('', '
1309
		SELECT COUNT(*), COUNT( CASE WHEN id_poll != {int:no_poll} THEN 1 ELSE NULL END )
1310
		FROM {db_prefix}topics
1311
		WHERE id_member_started = {int:current_member}' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? '
1312
			AND id_board != {int:recycle_board}' : ''),
1313
		array(
1314
			'current_member' => $memID,
1315
			'recycle_board' => $modSettings['recycle_board'],
1316
			'no_poll' => 0,
1317
		)
1318
	);
1319
	list ($context['num_topics'], $context['num_polls']) = $smcFunc['db_fetch_row']($result);
1320
	$smcFunc['db_free_result']($result);
1321
1322
	// Number polls voted in.
1323
	$result = $smcFunc['db_query']('distinct_poll_votes', '
1324
		SELECT COUNT(DISTINCT id_poll)
1325
		FROM {db_prefix}log_polls
1326
		WHERE id_member = {int:current_member}',
1327
		array(
1328
			'current_member' => $memID,
1329
		)
1330
	);
1331
	list ($context['num_votes']) = $smcFunc['db_fetch_row']($result);
1332
	$smcFunc['db_free_result']($result);
1333
1334
	// Format the numbers...
1335
	$context['num_topics'] = comma_format($context['num_topics']);
1336
	$context['num_polls'] = comma_format($context['num_polls']);
1337
	$context['num_votes'] = comma_format($context['num_votes']);
1338
1339
	// Grab the board this member posted in most often.
1340
	$result = $smcFunc['db_query']('', '
1341
		SELECT
1342
			b.id_board, MAX(b.name) AS name, MAX(b.num_posts) AS num_posts, COUNT(*) AS message_count
1343
		FROM {db_prefix}messages AS m
1344
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)
1345
		WHERE m.id_member = {int:current_member}
1346
			AND b.count_posts = {int:count_enabled}
1347
			AND {query_see_board}
1348
		GROUP BY b.id_board
1349
		ORDER BY message_count DESC
1350
		LIMIT 10',
1351
		array(
1352
			'current_member' => $memID,
1353
			'count_enabled' => 0,
1354
		)
1355
	);
1356
	$context['popular_boards'] = array();
1357
	while ($row = $smcFunc['db_fetch_assoc']($result))
1358
	{
1359
		$context['popular_boards'][$row['id_board']] = array(
1360
			'id' => $row['id_board'],
1361
			'posts' => $row['message_count'],
1362
			'href' => $scripturl . '?board=' . $row['id_board'] . '.0',
1363
			'link' => '<a href="' . $scripturl . '?board=' . $row['id_board'] . '.0">' . $row['name'] . '</a>',
1364
			'posts_percent' => $user_profile[$memID]['posts'] == 0 ? 0 : ($row['message_count'] * 100) / $user_profile[$memID]['posts'],
1365
			'total_posts' => $row['num_posts'],
1366
			'total_posts_member' => $user_profile[$memID]['posts'],
1367
		);
1368
	}
1369
	$smcFunc['db_free_result']($result);
1370
1371
	// Now get the 10 boards this user has most often participated in.
1372
	$result = $smcFunc['db_query']('profile_board_stats', '
1373
		SELECT
1374
			b.id_board, MAX(b.name) AS name, b.num_posts, COUNT(*) AS message_count,
1375
			CASE WHEN COUNT(*) > MAX(b.num_posts) THEN 1 ELSE COUNT(*) / MAX(b.num_posts) END * 100 AS percentage
1376
		FROM {db_prefix}messages AS m
1377
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)
1378
		WHERE m.id_member = {int:current_member}
1379
			AND {query_see_board}
1380
		GROUP BY b.id_board, b.num_posts
1381
		ORDER BY percentage DESC
1382
		LIMIT 10',
1383
		array(
1384
			'current_member' => $memID,
1385
		)
1386
	);
1387
	$context['board_activity'] = array();
1388
	while ($row = $smcFunc['db_fetch_assoc']($result))
1389
	{
1390
		$context['board_activity'][$row['id_board']] = array(
1391
			'id' => $row['id_board'],
1392
			'posts' => $row['message_count'],
1393
			'href' => $scripturl . '?board=' . $row['id_board'] . '.0',
1394
			'link' => '<a href="' . $scripturl . '?board=' . $row['id_board'] . '.0">' . $row['name'] . '</a>',
1395
			'percent' => comma_format((float) $row['percentage'], 2),
1396
			'posts_percent' => (float) $row['percentage'],
1397
			'total_posts' => $row['num_posts'],
1398
		);
1399
	}
1400
	$smcFunc['db_free_result']($result);
1401
1402
	// Posting activity by time.
1403
	$result = $smcFunc['db_query']('user_activity_by_time', '
1404
		SELECT
1405
			HOUR(FROM_UNIXTIME(poster_time + {int:time_offset})) AS hour,
1406
			COUNT(*) AS post_count
1407
		FROM {db_prefix}messages
1408
		WHERE id_member = {int:current_member}' . ($modSettings['totalMessages'] > 100000 ? '
1409
			AND id_topic > {int:top_ten_thousand_topics}' : '') . '
1410
		GROUP BY hour',
1411
		array(
1412
			'current_member' => $memID,
1413
			'top_ten_thousand_topics' => $modSettings['totalTopics'] - 10000,
1414
			'time_offset' => (($user_info['time_offset'] + $modSettings['time_offset']) * 3600),
1415
		)
1416
	);
1417
	$maxPosts = $realPosts = 0;
1418
	$context['posts_by_time'] = array();
1419
	while ($row = $smcFunc['db_fetch_assoc']($result))
1420
	{
1421
		// Cast as an integer to remove the leading 0.
1422
		$row['hour'] = (int) $row['hour'];
1423
1424
		$maxPosts = max($row['post_count'], $maxPosts);
1425
		$realPosts += $row['post_count'];
1426
1427
		$context['posts_by_time'][$row['hour']] = array(
1428
			'hour' => $row['hour'],
1429
			'hour_format' => stripos($user_info['time_format'], '%p') === false ? $row['hour'] : date('g a', mktime($row['hour'])),
1430
			'posts' => $row['post_count'],
1431
			'posts_percent' => 0,
1432
			'is_last' => $row['hour'] == 23,
1433
		);
1434
	}
1435
	$smcFunc['db_free_result']($result);
1436
1437
	if ($maxPosts > 0)
1438
		for ($hour = 0; $hour < 24; $hour++)
1439
		{
1440
			if (!isset($context['posts_by_time'][$hour]))
1441
				$context['posts_by_time'][$hour] = array(
1442
					'hour' => $hour,
1443
					'hour_format' => stripos($user_info['time_format'], '%p') === false ? $hour : date('g a', mktime($hour)),
1444
					'posts' => 0,
1445
					'posts_percent' => 0,
1446
					'relative_percent' => 0,
1447
					'is_last' => $hour == 23,
1448
				);
1449
			else
1450
			{
1451
				$context['posts_by_time'][$hour]['posts_percent'] = round(($context['posts_by_time'][$hour]['posts'] * 100) / $realPosts);
1452
				$context['posts_by_time'][$hour]['relative_percent'] = round(($context['posts_by_time'][$hour]['posts'] * 100) / $maxPosts);
1453
			}
1454
		}
1455
1456
	// Put it in the right order.
1457
	ksort($context['posts_by_time']);
1458
1459
	/**
1460
	 * Adding new entries:
1461
	 * 'key' => array(
1462
	 * 		'text' => string, // The text that will be shown next to the entry.
1463
	 * 		'url' => string, // OPTIONAL: The entry will be a url
1464
	 * ),
1465
	 *
1466
	 * 'key' will be used to look up the language string as $txt['statPanel_' . $key].
1467
	 * Make sure to add a new entry when writing your mod!
1468
	 */
1469
	$context['text_stats'] = array(
1470
		'total_time_online' => array(
1471
			'text' => $context['time_logged_in'],
1472
		),
1473
		'total_posts' => array(
1474
			'text' => $context['num_posts'] . ' ' . $txt['statPanel_posts'],
1475
			'url' => $scripturl . '?action=profile;area=showposts;sa=messages;u=' . $memID
1476
		),
1477
		'total_topics' => array(
1478
			'text' => $context['num_topics'] . ' ' . $txt['statPanel_topics'],
1479
			'url' => $scripturl . '?action=profile;area=showposts;sa=topics;u=' . $memID
1480
		),
1481
		'users_polls' => array(
1482
			'text' => $context['num_polls'] . ' ' . $txt['statPanel_polls'],
1483
		),
1484
		'users_votes' => array(
1485
			'text' => $context['num_votes'] . ' ' . $txt['statPanel_votes']
1486
		)
1487
	);
1488
1489
	// Custom stats (just add a template_layer to add it to the template!)
1490
 	call_integration_hook('integrate_profile_stats', array($memID, &$context['text_stats']));
1491
}
1492
1493
/**
1494
 * Loads up the information for the "track user" section of the profile
1495
 *
1496
 * @param int $memID The ID of the member
1497
 */
1498
function tracking($memID)
1499
{
1500
	global $context, $txt, $modSettings, $user_profile;
1501
1502
	$subActions = array(
1503
		'activity' => array('trackActivity', $txt['trackActivity'], 'moderate_forum'),
1504
		'ip' => array('TrackIP', $txt['trackIP'], 'moderate_forum'),
1505
		'edits' => array('trackEdits', $txt['trackEdits'], 'moderate_forum'),
1506
		'groupreq' => array('trackGroupReq', $txt['trackGroupRequests'], 'approve_group_requests'),
1507
		'logins' => array('TrackLogins', $txt['trackLogins'], 'moderate_forum'),
1508
	);
1509
1510
	foreach ($subActions as $sa => $action)
1511
	{
1512
		if (!allowedTo($action[2]))
1513
			unset($subActions[$sa]);
1514
	}
1515
1516
	// Create the tabs for the template.
1517
	$context[$context['profile_menu_name']]['tab_data'] = array(
1518
		'title' => $txt['tracking'],
1519
		'description' => $txt['tracking_description'],
1520
		'icon' => 'profile_hd.png',
1521
		'tabs' => array(
1522
			'activity' => array(),
1523
			'ip' => array(),
1524
			'edits' => array(),
1525
			'groupreq' => array(),
1526
			'logins' => array(),
1527
		),
1528
	);
1529
1530
	// Moderation must be on to track edits.
1531
	if (empty($modSettings['userlog_enabled']))
1532
		unset($context[$context['profile_menu_name']]['tab_data']['edits'], $subActions['edits']);
1533
1534
	// Group requests must be active to show it...
1535
	if (empty($modSettings['show_group_membership']))
1536
		unset($context[$context['profile_menu_name']]['tab_data']['groupreq'], $subActions['groupreq']);
1537
1538
	if (empty($subActions))
1539
		fatal_lang_error('no_access', false);
1540
1541
	$keys = array_keys($subActions);
1542
	$default = array_shift($keys);
1543
	$context['tracking_area'] = isset($_GET['sa']) && isset($subActions[$_GET['sa']]) ? $_GET['sa'] : $default;
1544
1545
	// Set a page title.
1546
	$context['page_title'] = $txt['trackUser'] . ' - ' . $subActions[$context['tracking_area']][1] . ' - ' . $user_profile[$memID]['real_name'];
1547
1548
	// Pass on to the actual function.
1549
	$context['sub_template'] = $subActions[$context['tracking_area']][0];
1550
	$call = call_helper($subActions[$context['tracking_area']][0], true);
1551
1552
	if (!empty($call))
1553
		call_user_func($call, $memID);
0 ignored issues
show
Bug introduced by
It seems like $call can also be of type boolean; however, parameter $function of call_user_func() does only seem to accept callable, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1553
		call_user_func(/** @scrutinizer ignore-type */ $call, $memID);
Loading history...
1554
}
1555
1556
/**
1557
 * Handles tracking a user's activity
1558
 *
1559
 * @param int $memID The ID of the member
1560
 */
1561
function trackActivity($memID)
1562
{
1563
	global $scripturl, $txt, $modSettings, $sourcedir;
1564
	global $user_profile, $context, $smcFunc;
1565
1566
	// Verify if the user has sufficient permissions.
1567
	isAllowedTo('moderate_forum');
1568
1569
	$context['last_ip'] = $user_profile[$memID]['member_ip'];
1570
	if ($context['last_ip'] != $user_profile[$memID]['member_ip2'])
1571
		$context['last_ip2'] = $user_profile[$memID]['member_ip2'];
1572
	$context['member']['name'] = $user_profile[$memID]['real_name'];
1573
1574
	// Set the options for the list component.
1575
	$listOptions = array(
1576
		'id' => 'track_user_list',
1577
		'title' => $txt['errors_by'] . ' ' . $context['member']['name'],
1578
		'items_per_page' => $modSettings['defaultMaxListItems'],
1579
		'no_items_label' => $txt['no_errors_from_user'],
1580
		'base_href' => $scripturl . '?action=profile;area=tracking;sa=user;u=' . $memID,
1581
		'default_sort_col' => 'date',
1582
		'get_items' => array(
1583
			'function' => 'list_getUserErrors',
1584
			'params' => array(
1585
				'le.id_member = {int:current_member}',
1586
				array('current_member' => $memID),
1587
			),
1588
		),
1589
		'get_count' => array(
1590
			'function' => 'list_getUserErrorCount',
1591
			'params' => array(
1592
				'id_member = {int:current_member}',
1593
				array('current_member' => $memID),
1594
			),
1595
		),
1596
		'columns' => array(
1597
			'ip_address' => array(
1598
				'header' => array(
1599
					'value' => $txt['ip_address'],
1600
				),
1601
				'data' => array(
1602
					'sprintf' => array(
1603
						'format' => '<a href="' . $scripturl . '?action=profile;area=tracking;sa=ip;searchip=%1$s;u=' . $memID . '">%1$s</a>',
1604
						'params' => array(
1605
							'ip' => false,
1606
						),
1607
					),
1608
				),
1609
				'sort' => array(
1610
					'default' => 'le.ip',
1611
					'reverse' => 'le.ip DESC',
1612
				),
1613
			),
1614
			'message' => array(
1615
				'header' => array(
1616
					'value' => $txt['message'],
1617
				),
1618
				'data' => array(
1619
					'sprintf' => array(
1620
						'format' => '%1$s<br><a href="%2$s">%2$s</a>',
1621
						'params' => array(
1622
							'message' => false,
1623
							'url' => false,
1624
						),
1625
					),
1626
				),
1627
			),
1628
			'date' => array(
1629
				'header' => array(
1630
					'value' => $txt['date'],
1631
				),
1632
				'data' => array(
1633
					'db' => 'time',
1634
				),
1635
				'sort' => array(
1636
					'default' => 'le.id_error DESC',
1637
					'reverse' => 'le.id_error',
1638
				),
1639
			),
1640
		),
1641
		'additional_rows' => array(
1642
			array(
1643
				'position' => 'after_title',
1644
				'value' => $txt['errors_desc'],
1645
			),
1646
		),
1647
	);
1648
1649
	// Create the list for viewing.
1650
	require_once($sourcedir . '/Subs-List.php');
1651
	createList($listOptions);
1652
1653
	// @todo cache this
1654
	// If this is a big forum, or a large posting user, let's limit the search.
1655
	if ($modSettings['totalMessages'] > 50000 && $user_profile[$memID]['posts'] > 500)
1656
	{
1657
		$request = $smcFunc['db_query']('', '
1658
			SELECT MAX(id_msg)
1659
			FROM {db_prefix}messages AS m
1660
			WHERE m.id_member = {int:current_member}',
1661
			array(
1662
				'current_member' => $memID,
1663
			)
1664
		);
1665
		list ($max_msg_member) = $smcFunc['db_fetch_row']($request);
1666
		$smcFunc['db_free_result']($request);
1667
1668
		// There's no point worrying ourselves with messages made yonks ago, just get recent ones!
1669
		$min_msg_member = max(0, $max_msg_member - $user_profile[$memID]['posts'] * 3);
1670
	}
1671
1672
	// Default to at least the ones we know about.
1673
	$ips = array(
1674
		$user_profile[$memID]['member_ip'],
1675
		$user_profile[$memID]['member_ip2'],
1676
	);
1677
1678
	// @todo cache this
1679
	// Get all IP addresses this user has used for his messages.
1680
	$request = $smcFunc['db_query']('', '
1681
		SELECT poster_ip
1682
		FROM {db_prefix}messages
1683
		WHERE id_member = {int:current_member}
1684
		' . (isset($min_msg_member) ? '
1685
			AND id_msg >= {int:min_msg_member} AND id_msg <= {int:max_msg_member}' : '') . '
1686
		GROUP BY poster_ip',
1687
		array(
1688
			'current_member' => $memID,
1689
			'min_msg_member' => !empty($min_msg_member) ? $min_msg_member : 0,
1690
			'max_msg_member' => !empty($max_msg_member) ? $max_msg_member : 0,
1691
		)
1692
	);
1693
	$context['ips'] = array();
1694
	while ($row = $smcFunc['db_fetch_assoc']($request))
1695
	{
1696
		$context['ips'][] = '<a href="' . $scripturl . '?action=profile;area=tracking;sa=ip;searchip=' . inet_dtop($row['poster_ip']) . ';u=' . $memID . '">' . inet_dtop($row['poster_ip']) . '</a>';
1697
		$ips[] = inet_dtop($row['poster_ip']);
1698
	}
1699
	$smcFunc['db_free_result']($request);
1700
1701
	// Now also get the IP addresses from the error messages.
1702
	$request = $smcFunc['db_query']('', '
1703
		SELECT COUNT(*) AS error_count, ip
1704
		FROM {db_prefix}log_errors
1705
		WHERE id_member = {int:current_member}
1706
		GROUP BY ip',
1707
		array(
1708
			'current_member' => $memID,
1709
		)
1710
	);
1711
	$context['error_ips'] = array();
1712
	while ($row = $smcFunc['db_fetch_assoc']($request))
1713
	{
1714
		$row['ip'] = inet_dtop($row['ip']);
1715
		$context['error_ips'][] = '<a href="' . $scripturl . '?action=profile;area=tracking;sa=ip;searchip=' . $row['ip'] . ';u=' . $memID . '">' . $row['ip'] . '</a>';
1716
		$ips[] = $row['ip'];
1717
	}
1718
	$smcFunc['db_free_result']($request);
1719
1720
	// Find other users that might use the same IP.
1721
	$ips = array_unique($ips);
1722
	$context['members_in_range'] = array();
1723
	if (!empty($ips))
1724
	{
1725
		// Get member ID's which are in messages...
1726
		$request = $smcFunc['db_query']('', '
1727
			SELECT DISTINCT mem.id_member
1728
			FROM {db_prefix}messages AS m
1729
				INNER JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
1730
			WHERE m.poster_ip IN ({array_inet:ip_list})
1731
				AND mem.id_member != {int:current_member}',
1732
			array(
1733
				'current_member' => $memID,
1734
				'ip_list' => $ips,
1735
			)
1736
		);
1737
		$message_members = array();
1738
		while ($row = $smcFunc['db_fetch_assoc']($request))
1739
			$message_members[] = $row['id_member'];
1740
		$smcFunc['db_free_result']($request);
1741
1742
		// Fetch their names, cause of the GROUP BY doesn't like giving us that normally.
1743
		if (!empty($message_members))
1744
		{
1745
			$request = $smcFunc['db_query']('', '
1746
				SELECT id_member, real_name
1747
				FROM {db_prefix}members
1748
				WHERE id_member IN ({array_int:message_members})',
1749
				array(
1750
					'message_members' => $message_members,
1751
					'ip_list' => $ips,
1752
				)
1753
			);
1754
			while ($row = $smcFunc['db_fetch_assoc']($request))
1755
				$context['members_in_range'][$row['id_member']] = '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['real_name'] . '</a>';
1756
			$smcFunc['db_free_result']($request);
1757
		}
1758
1759
		$request = $smcFunc['db_query']('', '
1760
			SELECT id_member, real_name
1761
			FROM {db_prefix}members
1762
			WHERE id_member != {int:current_member}
1763
				AND member_ip IN ({array_inet:ip_list})',
1764
			array(
1765
				'current_member' => $memID,
1766
				'ip_list' => $ips,
1767
			)
1768
		);
1769
		while ($row = $smcFunc['db_fetch_assoc']($request))
1770
			$context['members_in_range'][$row['id_member']] = '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['real_name'] . '</a>';
1771
		$smcFunc['db_free_result']($request);
1772
	}
1773
}
1774
1775
/**
1776
 * Get the number of user errors
1777
 *
1778
 * @param string $where A query to limit which errors are counted
1779
 * @param array $where_vars The parameters for $where
1780
 * @return int Number of user errors
1781
 */
1782
function list_getUserErrorCount($where, $where_vars = array())
1783
{
1784
	global $smcFunc;
1785
1786
	$request = $smcFunc['db_query']('', '
1787
		SELECT COUNT(*) AS error_count
1788
		FROM {db_prefix}log_errors
1789
		WHERE ' . $where,
1790
		$where_vars
1791
	);
1792
	list ($count) = $smcFunc['db_fetch_row']($request);
1793
	$smcFunc['db_free_result']($request);
1794
1795
	return (int) $count;
1796
}
1797
1798
/**
1799
 * Gets all of the errors generated by a user's actions. Callback for the list in track_activity
1800
 *
1801
 * @param int $start Which item to start with (for pagination purposes)
1802
 * @param int $items_per_page How many items to show on each page
1803
 * @param string $sort A string indicating how to sort the results
1804
 * @param string $where A query indicating how to filter the results (eg 'id_member={int:id_member}')
1805
 * @param array $where_vars An array of parameters for $where
1806
 * @return array An array of information about the error messages
1807
 */
1808
function list_getUserErrors($start, $items_per_page, $sort, $where, $where_vars = array())
1809
{
1810
	global $smcFunc, $txt, $scripturl;
1811
1812
	// Get a list of error messages from this ip (range).
1813
	$request = $smcFunc['db_query']('', '
1814
		SELECT
1815
			le.log_time, le.ip, le.url, le.message, COALESCE(mem.id_member, 0) AS id_member,
1816
			COALESCE(mem.real_name, {string:guest_title}) AS display_name, mem.member_name
1817
		FROM {db_prefix}log_errors AS le
1818
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = le.id_member)
1819
		WHERE ' . $where . '
1820
		ORDER BY {raw:sort}
1821
		LIMIT {int:start}, {int:max}',
1822
		array_merge($where_vars, array(
1823
			'guest_title' => $txt['guest_title'],
1824
			'sort' => $sort,
1825
			'start' => $start,
1826
			'max' => $items_per_page,
1827
		))
1828
	);
1829
	$error_messages = array();
1830
	while ($row = $smcFunc['db_fetch_assoc']($request))
1831
		$error_messages[] = array(
1832
			'ip' => inet_dtop($row['ip']),
1833
			'member_link' => $row['id_member'] > 0 ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['display_name'] . '</a>' : $row['display_name'],
1834
			'message' => strtr($row['message'], array('&lt;span class=&quot;remove&quot;&gt;' => '', '&lt;/span&gt;' => '')),
1835
			'url' => $row['url'],
1836
			'time' => timeformat($row['log_time']),
1837
			'timestamp' => forum_time(true, $row['log_time']),
1838
		);
1839
	$smcFunc['db_free_result']($request);
1840
1841
	return $error_messages;
1842
}
1843
1844
/**
1845
 * Gets the number of posts made from a particular IP
1846
 *
1847
 * @param string $where A query indicating which posts to count
1848
 * @param array $where_vars The parameters for $where
1849
 * @return int Count of messages matching the IP
1850
 */
1851
function list_getIPMessageCount($where, $where_vars = array())
1852
{
1853
	global $smcFunc;
1854
1855
	$request = $smcFunc['db_query']('', '
1856
		SELECT COUNT(*) AS message_count
1857
		FROM {db_prefix}messages AS m
1858
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)
1859
		WHERE {query_see_board} AND ' . $where,
1860
		$where_vars
1861
	);
1862
	list ($count) = $smcFunc['db_fetch_row']($request);
1863
	$smcFunc['db_free_result']($request);
1864
1865
	return (int) $count;
1866
}
1867
1868
/**
1869
 * Gets all the posts made from a particular IP
1870
 *
1871
 * @param int $start Which item to start with (for pagination purposes)
1872
 * @param int $items_per_page How many items to show on each page
1873
 * @param string $sort A string indicating how to sort the results
1874
 * @param string $where A query to filter which posts are returned
1875
 * @param array $where_vars An array of parameters for $where
1876
 * @return array An array containing information about the posts
1877
 */
1878
function list_getIPMessages($start, $items_per_page, $sort, $where, $where_vars = array())
1879
{
1880
	global $smcFunc, $scripturl;
1881
1882
	// Get all the messages fitting this where clause.
1883
	// @todo SLOW This query is using a filesort.
1884
	$request = $smcFunc['db_query']('', '
1885
		SELECT
1886
			m.id_msg, m.poster_ip, COALESCE(mem.real_name, m.poster_name) AS display_name, mem.id_member,
1887
			m.subject, m.poster_time, m.id_topic, m.id_board
1888
		FROM {db_prefix}messages AS m
1889
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)
1890
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
1891
		WHERE {query_see_board} AND ' . $where . '
1892
		ORDER BY {raw:sort}
1893
		LIMIT {int:start}, {int:max}',
1894
		array_merge($where_vars, array(
1895
			'sort' => $sort,
1896
			'start' => $start,
1897
			'max' => $items_per_page,
1898
		))
1899
	);
1900
	$messages = array();
1901
	while ($row = $smcFunc['db_fetch_assoc']($request))
1902
		$messages[] = array(
1903
			'ip' => inet_dtop($row['poster_ip']),
1904
			'member_link' => empty($row['id_member']) ? $row['display_name'] : '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['display_name'] . '</a>',
1905
			'board' => array(
1906
				'id' => $row['id_board'],
1907
				'href' => $scripturl . '?board=' . $row['id_board']
1908
			),
1909
			'topic' => $row['id_topic'],
1910
			'id' => $row['id_msg'],
1911
			'subject' => $row['subject'],
1912
			'time' => timeformat($row['poster_time']),
1913
			'timestamp' => forum_time(true, $row['poster_time'])
1914
		);
1915
	$smcFunc['db_free_result']($request);
1916
1917
	return $messages;
1918
}
1919
1920
/**
1921
 * Handles tracking a particular IP address
1922
 *
1923
 * @param int $memID The ID of a member whose IP we want to track
1924
 */
1925
function TrackIP($memID = 0)
1926
{
1927
	global $user_profile, $scripturl, $txt, $user_info, $modSettings, $sourcedir;
1928
	global $context, $smcFunc;
1929
1930
	// Can the user do this?
1931
	isAllowedTo('moderate_forum');
1932
1933
	if ($memID == 0)
1934
	{
1935
		$context['ip'] = ip2range($user_info['ip']);
1936
		loadTemplate('Profile');
1937
		loadLanguage('Profile');
1938
		$context['sub_template'] = 'trackIP';
1939
		$context['page_title'] = $txt['profile'];
1940
		$context['base_url'] = $scripturl . '?action=trackip';
1941
	}
1942
	else
1943
	{
1944
		$context['ip'] = ip2range($user_profile[$memID]['member_ip']);
1945
		$context['base_url'] = $scripturl . '?action=profile;area=tracking;sa=ip;u=' . $memID;
1946
	}
1947
1948
	// Searching?
1949
	if (isset($_REQUEST['searchip']))
1950
		$context['ip'] = ip2range(trim($_REQUEST['searchip']));
1951
1952
	if (count($context['ip']) !== 2)
1953
		fatal_lang_error('invalid_tracking_ip', false);
1954
1955
	$ip_string = array('{inet:ip_address_low}','{inet:ip_address_high}');
1956
	$fields = array(
1957
			'ip_address_low' => $context['ip']['low'],
1958
			'ip_address_high' => $context['ip']['high'],
1959
		);
1960
1961
	$ip_var = $context['ip'];
1962
1963
	if ($context['ip']['low'] !==  $context['ip']['high'])
1964
		$context['ip'] = $context['ip']['low'] . ' - ' . $context['ip']['high'];
1965
	else
1966
		$context['ip'] = $context['ip']['low'];
1967
1968
	if (empty($context['tracking_area']))
1969
		$context['page_title'] = $txt['trackIP'] . ' - ' . $context['ip'];
1970
1971
	$request = $smcFunc['db_query']('', '
1972
		SELECT id_member, real_name AS display_name, member_ip
1973
		FROM {db_prefix}members
1974
		WHERE member_ip >= ' . $ip_string[0] . ' and member_ip <= ' . $ip_string[1],
1975
		$fields
1976
	);
1977
	$context['ips'] = array();
1978
	while ($row = $smcFunc['db_fetch_assoc']($request))
1979
		$context['ips'][inet_dtop($row['member_ip'])][] = '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['display_name'] . '</a>';
1980
	$smcFunc['db_free_result']($request);
1981
1982
	ksort($context['ips']);
1983
1984
	// For messages we use the "messages per page" option
1985
	$maxPerPage = empty($modSettings['disableCustomPerPage']) && !empty($options['messages_per_page']) ? $options['messages_per_page'] : $modSettings['defaultMaxMessages'];
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $options seems to never exist and therefore empty should always be true.
Loading history...
1986
1987
	// Gonna want this for the list.
1988
	require_once($sourcedir . '/Subs-List.php');
1989
1990
	// Start with the user messages.
1991
	$listOptions = array(
1992
		'id' => 'track_message_list',
1993
		'title' => $txt['messages_from_ip'] . ' ' . $context['ip'],
1994
		'start_var_name' => 'messageStart',
1995
		'items_per_page' => $maxPerPage,
1996
		'no_items_label' => $txt['no_messages_from_ip'],
1997
		'base_href' => $context['base_url'] . ';searchip=' . $context['ip'],
1998
		'default_sort_col' => 'date',
1999
		'get_items' => array(
2000
			'function' => 'list_getIPMessages',
2001
			'params' => array(
2002
				'm.poster_ip >= ' . $ip_string[0] . ' and m.poster_ip <= ' . $ip_string[1],
2003
				$fields,
2004
			),
2005
		),
2006
		'get_count' => array(
2007
			'function' => 'list_getIPMessageCount',
2008
			'params' => array(
2009
				'm.poster_ip >= ' . $ip_string[0] . ' and m.poster_ip <= ' . $ip_string[1],
2010
				$fields,
2011
			),
2012
		),
2013
		'columns' => array(
2014
			'ip_address' => array(
2015
				'header' => array(
2016
					'value' => $txt['ip_address'],
2017
				),
2018
				'data' => array(
2019
					'sprintf' => array(
2020
						'format' => '<a href="' . $context['base_url'] . ';searchip=%1$s">%1$s</a>',
2021
						'params' => array(
2022
							'ip' => false,
2023
						),
2024
					),
2025
				),
2026
				'sort' => array(
2027
					'default' => 'm.poster_ip',
2028
					'reverse' => 'm.poster_ip DESC',
2029
				),
2030
			),
2031
			'poster' => array(
2032
				'header' => array(
2033
					'value' => $txt['poster'],
2034
				),
2035
				'data' => array(
2036
					'db' => 'member_link',
2037
				),
2038
			),
2039
			'subject' => array(
2040
				'header' => array(
2041
					'value' => $txt['subject'],
2042
				),
2043
				'data' => array(
2044
					'sprintf' => array(
2045
						'format' => '<a href="' . $scripturl . '?topic=%1$s.msg%2$s#msg%2$s" rel="nofollow">%3$s</a>',
2046
						'params' => array(
2047
							'topic' => false,
2048
							'id' => false,
2049
							'subject' => false,
2050
						),
2051
					),
2052
				),
2053
			),
2054
			'date' => array(
2055
				'header' => array(
2056
					'value' => $txt['date'],
2057
				),
2058
				'data' => array(
2059
					'db' => 'time',
2060
				),
2061
				'sort' => array(
2062
					'default' => 'm.id_msg DESC',
2063
					'reverse' => 'm.id_msg',
2064
				),
2065
			),
2066
		),
2067
		'additional_rows' => array(
2068
			array(
2069
				'position' => 'after_title',
2070
				'value' => $txt['messages_from_ip_desc'],
2071
			),
2072
		),
2073
	);
2074
2075
	// Create the messages list.
2076
	createList($listOptions);
2077
2078
	// Set the options for the error lists.
2079
	$listOptions = array(
2080
		'id' => 'track_user_list',
2081
		'title' => $txt['errors_from_ip'] . ' ' . $context['ip'],
2082
		'start_var_name' => 'errorStart',
2083
		'items_per_page' => $modSettings['defaultMaxListItems'],
2084
		'no_items_label' => $txt['no_errors_from_ip'],
2085
		'base_href' => $context['base_url'] . ';searchip=' . $context['ip'],
2086
		'default_sort_col' => 'date2',
2087
		'get_items' => array(
2088
			'function' => 'list_getUserErrors',
2089
			'params' => array(
2090
				'le.ip >= ' . $ip_string[0] . ' and le.ip <= ' . $ip_string[1],
2091
				$fields,
2092
			),
2093
		),
2094
		'get_count' => array(
2095
			'function' => 'list_getUserErrorCount',
2096
			'params' => array(
2097
				'ip >= ' . $ip_string[0] . ' and ip <= ' . $ip_string[1],
2098
				$fields,
2099
			),
2100
		),
2101
		'columns' => array(
2102
			'ip_address2' => array(
2103
				'header' => array(
2104
					'value' => $txt['ip_address'],
2105
				),
2106
				'data' => array(
2107
					'sprintf' => array(
2108
						'format' => '<a href="' . $context['base_url'] . ';searchip=%1$s">%1$s</a>',
2109
						'params' => array(
2110
							'ip' => false,
2111
						),
2112
					),
2113
				),
2114
				'sort' => array(
2115
					'default' => 'le.ip',
2116
					'reverse' => 'le.ip DESC',
2117
				),
2118
			),
2119
			'display_name' => array(
2120
				'header' => array(
2121
					'value' => $txt['display_name'],
2122
				),
2123
				'data' => array(
2124
					'db' => 'member_link',
2125
				),
2126
			),
2127
			'message' => array(
2128
				'header' => array(
2129
					'value' => $txt['message'],
2130
				),
2131
				'data' => array(
2132
					'sprintf' => array(
2133
						'format' => '%1$s<br><a href="%2$s">%2$s</a>',
2134
						'params' => array(
2135
							'message' => false,
2136
							'url' => false,
2137
						),
2138
					),
2139
				),
2140
			),
2141
			'date2' => array(
2142
				'header' => array(
2143
					'value' => $txt['date'],
2144
				),
2145
				'data' => array(
2146
					'db' => 'time',
2147
				),
2148
				'sort' => array(
2149
					'default' => 'le.id_error DESC',
2150
					'reverse' => 'le.id_error',
2151
				),
2152
			),
2153
		),
2154
		'additional_rows' => array(
2155
			array(
2156
				'position' => 'after_title',
2157
				'value' => $txt['errors_from_ip_desc'],
2158
			),
2159
		),
2160
	);
2161
2162
	// Create the error list.
2163
	createList($listOptions);
2164
2165
	// Allow 3rd party integrations to add in their own lists or whatever.
2166
	$context['additional_track_lists'] = array();
2167
	call_integration_hook('integrate_profile_trackip', array($ip_string, $ip_var));
2168
2169
	$context['single_ip'] = ($ip_var['low'] === $ip_var['high']);
2170
	if ($context['single_ip'])
2171
	{
2172
		$context['whois_servers'] = array(
2173
			'afrinic' => array(
2174
				'name' => $txt['whois_afrinic'],
2175
				'url' => 'https://www.afrinic.net/cgi-bin/whois?searchtext=' . $context['ip'],
2176
			),
2177
			'apnic' => array(
2178
				'name' => $txt['whois_apnic'],
2179
				'url' => 'https://wq.apnic.net/apnic-bin/whois.pl?searchtext=' . $context['ip'],
2180
			),
2181
			'arin' => array(
2182
				'name' => $txt['whois_arin'],
2183
				'url' => 'https://whois.arin.net/rest/ip/' . $context['ip'],
2184
			),
2185
			'lacnic' => array(
2186
				'name' => $txt['whois_lacnic'],
2187
				'url' => 'https://lacnic.net/cgi-bin/lacnic/whois?query=' . $context['ip'],
2188
			),
2189
			'ripe' => array(
2190
				'name' => $txt['whois_ripe'],
2191
				'url' => 'https://apps.db.ripe.net/search/query.html?searchtext=' . $context['ip'],
2192
			),
2193
		);
2194
	}
2195
}
2196
2197
/**
2198
 * Tracks a user's logins.
2199
 *
2200
 * @param int $memID The ID of the member
2201
 */
2202
function TrackLogins($memID = 0)
2203
{
2204
	global $scripturl, $txt, $sourcedir, $context;
2205
2206
	// Gonna want this for the list.
2207
	require_once($sourcedir . '/Subs-List.php');
2208
2209
	if ($memID == 0)
2210
		$context['base_url'] = $scripturl . '?action=trackip';
2211
	else
2212
		$context['base_url'] = $scripturl . '?action=profile;area=tracking;sa=ip;u=' . $memID;
2213
2214
	// Start with the user messages.
2215
	$listOptions = array(
2216
		'id' => 'track_logins_list',
2217
		'title' => $txt['trackLogins'],
2218
		'no_items_label' => $txt['trackLogins_none_found'],
2219
		'base_href' => $context['base_url'],
2220
		'get_items' => array(
2221
			'function' => 'list_getLogins',
2222
			'params' => array(
2223
				'id_member = {int:current_member}',
2224
				array('current_member' => $memID),
2225
			),
2226
		),
2227
		'get_count' => array(
2228
			'function' => 'list_getLoginCount',
2229
			'params' => array(
2230
				'id_member = {int:current_member}',
2231
				array('current_member' => $memID),
2232
			),
2233
		),
2234
		'columns' => array(
2235
			'time' => array(
2236
				'header' => array(
2237
					'value' => $txt['date'],
2238
				),
2239
				'data' => array(
2240
					'db' => 'time',
2241
				),
2242
			),
2243
			'ip' => array(
2244
				'header' => array(
2245
					'value' => $txt['ip_address'],
2246
				),
2247
				'data' => array(
2248
					'sprintf' => array(
2249
						'format' => '<a href="' . $context['base_url'] . ';searchip=%1$s">%1$s</a> (<a href="' . $context['base_url'] . ';searchip=%2$s">%2$s</a>) ',
2250
						'params' => array(
2251
							'ip' => false,
2252
							'ip2' => false
2253
						),
2254
					),
2255
				),
2256
			),
2257
		),
2258
		'additional_rows' => array(
2259
			array(
2260
				'position' => 'after_title',
2261
				'value' => $txt['trackLogins_desc'],
2262
			),
2263
		),
2264
	);
2265
2266
	// Create the messages list.
2267
	createList($listOptions);
2268
2269
	$context['sub_template'] = 'show_list';
2270
	$context['default_list'] = 'track_logins_list';
2271
}
2272
2273
/**
2274
 * Finds the total number of tracked logins for a particular user
2275
 *
2276
 * @param string $where A query to limit which logins are counted
2277
 * @param array $where_vars An array of parameters for $where
2278
 * @return int count of messages matching the IP
2279
 */
2280
function list_getLoginCount($where, $where_vars = array())
2281
{
2282
	global $smcFunc;
2283
2284
	$request = $smcFunc['db_query']('', '
2285
		SELECT COUNT(*) AS message_count
2286
		FROM {db_prefix}member_logins
2287
		WHERE id_member = {int:id_member}',
2288
		array(
2289
			'id_member' => $where_vars['current_member'],
2290
		)
2291
	);
2292
	list ($count) = $smcFunc['db_fetch_row']($request);
2293
	$smcFunc['db_free_result']($request);
2294
2295
	return (int) $count;
2296
}
2297
2298
/**
2299
 * Callback for the list in trackLogins.
2300
 *
2301
 * @param int $start Which item to start with (not used here)
2302
 * @param int $items_per_page How many items to show on each page (not used here)
2303
 * @param string $sort A string indicating
2304
 * @param string $where A query to filter results (not used here)
2305
 * @param array $where_vars An array of parameters for $where. Only 'current_member' (the ID of the member) is used here
2306
 * @return array An array of information about user logins
2307
 */
2308
function list_getLogins($start, $items_per_page, $sort, $where, $where_vars = array())
2309
{
2310
	global $smcFunc;
2311
2312
	$request = $smcFunc['db_query']('', '
2313
		SELECT time, ip, ip2
2314
		FROM {db_prefix}member_logins
2315
		WHERE id_member = {int:id_member}
2316
		ORDER BY time DESC',
2317
		array(
2318
			'id_member' => $where_vars['current_member'],
2319
		)
2320
	);
2321
	$logins = array();
2322
	while ($row = $smcFunc['db_fetch_assoc']($request))
2323
		$logins[] = array(
2324
			'time' => timeformat($row['time']),
2325
			'ip' => inet_dtop($row['ip']),
2326
			'ip2' => inet_dtop($row['ip2']),
2327
		);
2328
	$smcFunc['db_free_result']($request);
2329
2330
	return $logins;
2331
}
2332
2333
/**
2334
 * Tracks a user's profile edits
2335
 *
2336
 * @param int $memID The ID of the member
2337
 */
2338
function trackEdits($memID)
2339
{
2340
	global $scripturl, $txt, $modSettings, $sourcedir, $context, $smcFunc;
2341
2342
	require_once($sourcedir . '/Subs-List.php');
2343
2344
	// Get the names of any custom fields.
2345
	$request = $smcFunc['db_query']('', '
2346
		SELECT col_name, field_name, bbc
2347
		FROM {db_prefix}custom_fields',
2348
		array(
2349
		)
2350
	);
2351
	$context['custom_field_titles'] = array();
2352
	while ($row = $smcFunc['db_fetch_assoc']($request))
2353
		$context['custom_field_titles']['customfield_' . $row['col_name']] = array(
2354
			'title' => $row['field_name'],
2355
			'parse_bbc' => $row['bbc'],
2356
		);
2357
	$smcFunc['db_free_result']($request);
2358
2359
	// Set the options for the error lists.
2360
	$listOptions = array(
2361
		'id' => 'edit_list',
2362
		'title' => $txt['trackEdits'],
2363
		'items_per_page' => $modSettings['defaultMaxListItems'],
2364
		'no_items_label' => $txt['trackEdit_no_edits'],
2365
		'base_href' => $scripturl . '?action=profile;area=tracking;sa=edits;u=' . $memID,
2366
		'default_sort_col' => 'time',
2367
		'get_items' => array(
2368
			'function' => 'list_getProfileEdits',
2369
			'params' => array(
2370
				$memID,
2371
			),
2372
		),
2373
		'get_count' => array(
2374
			'function' => 'list_getProfileEditCount',
2375
			'params' => array(
2376
				$memID,
2377
			),
2378
		),
2379
		'columns' => array(
2380
			'action' => array(
2381
				'header' => array(
2382
					'value' => $txt['trackEdit_action'],
2383
				),
2384
				'data' => array(
2385
					'db' => 'action_text',
2386
				),
2387
			),
2388
			'before' => array(
2389
				'header' => array(
2390
					'value' => $txt['trackEdit_before'],
2391
				),
2392
				'data' => array(
2393
					'db' => 'before',
2394
				),
2395
			),
2396
			'after' => array(
2397
				'header' => array(
2398
					'value' => $txt['trackEdit_after'],
2399
				),
2400
				'data' => array(
2401
					'db' => 'after',
2402
				),
2403
			),
2404
			'time' => array(
2405
				'header' => array(
2406
					'value' => $txt['date'],
2407
				),
2408
				'data' => array(
2409
					'db' => 'time',
2410
				),
2411
				'sort' => array(
2412
					'default' => 'id_action DESC',
2413
					'reverse' => 'id_action',
2414
				),
2415
			),
2416
			'applicator' => array(
2417
				'header' => array(
2418
					'value' => $txt['trackEdit_applicator'],
2419
				),
2420
				'data' => array(
2421
					'db' => 'member_link',
2422
				),
2423
			),
2424
		),
2425
	);
2426
2427
	// Create the error list.
2428
	createList($listOptions);
2429
2430
	$context['sub_template'] = 'show_list';
2431
	$context['default_list'] = 'edit_list';
2432
}
2433
2434
/**
2435
 * How many edits?
2436
 *
2437
 * @param int $memID The ID of the member
2438
 * @return int The number of profile edits
2439
 */
2440
function list_getProfileEditCount($memID)
2441
{
2442
	global $smcFunc;
2443
2444
	$request = $smcFunc['db_query']('', '
2445
		SELECT COUNT(*) AS edit_count
2446
		FROM {db_prefix}log_actions
2447
		WHERE id_log = {int:log_type}
2448
			AND id_member = {int:owner}',
2449
		array(
2450
			'log_type' => 2,
2451
			'owner' => $memID,
2452
		)
2453
	);
2454
	list ($edit_count) = $smcFunc['db_fetch_row']($request);
2455
	$smcFunc['db_free_result']($request);
2456
2457
	return (int) $edit_count;
2458
}
2459
2460
/**
2461
 * Loads up information about a user's profile edits. Callback for the list in trackEdits()
2462
 *
2463
 * @param int $start Which item to start with (for pagination purposes)
2464
 * @param int $items_per_page How many items to show on each page
2465
 * @param string $sort A string indicating how to sort the results
2466
 * @param int $memID The ID of the member
2467
 * @return array An array of information about the profile edits
2468
 */
2469
function list_getProfileEdits($start, $items_per_page, $sort, $memID)
2470
{
2471
	global $smcFunc, $txt, $scripturl, $context;
2472
2473
	// Get a list of error messages from this ip (range).
2474
	$request = $smcFunc['db_query']('', '
2475
		SELECT
2476
			id_action, id_member, ip, log_time, action, extra
2477
		FROM {db_prefix}log_actions
2478
		WHERE id_log = {int:log_type}
2479
			AND id_member = {int:owner}
2480
		ORDER BY {raw:sort}
2481
		LIMIT {int:start}, {int:max}',
2482
		array(
2483
			'log_type' => 2,
2484
			'owner' => $memID,
2485
			'sort' => $sort,
2486
			'start' => $start,
2487
			'max' => $items_per_page,
2488
		)
2489
	);
2490
	$edits = array();
2491
	$members = array();
2492
	while ($row = $smcFunc['db_fetch_assoc']($request))
2493
	{
2494
		$extra = $smcFunc['json_decode']($row['extra'], true);
2495
		if (!empty($extra['applicator']))
2496
			$members[] = $extra['applicator'];
2497
2498
		// Work out what the name of the action is.
2499
		if (isset($txt['trackEdit_action_' . $row['action']]))
2500
			$action_text = $txt['trackEdit_action_' . $row['action']];
2501
		elseif (isset($txt[$row['action']]))
2502
			$action_text = $txt[$row['action']];
2503
		// Custom field?
2504
		elseif (isset($context['custom_field_titles'][$row['action']]))
2505
			$action_text = $context['custom_field_titles'][$row['action']]['title'];
2506
		else
2507
			$action_text = $row['action'];
2508
2509
		// Parse BBC?
2510
		$parse_bbc = isset($context['custom_field_titles'][$row['action']]) && $context['custom_field_titles'][$row['action']]['parse_bbc'] ? true : false;
2511
2512
		$edits[] = array(
2513
			'id' => $row['id_action'],
2514
			'ip' => inet_dtop($row['ip']),
2515
			'id_member' => !empty($extra['applicator']) ? $extra['applicator'] : 0,
2516
			'member_link' => $txt['trackEdit_deleted_member'],
2517
			'action' => $row['action'],
2518
			'action_text' => $action_text,
2519
			'before' => !empty($extra['previous']) ? ($parse_bbc ? parse_bbc($extra['previous']) : $extra['previous']) : '',
2520
			'after' => !empty($extra['new']) ? ($parse_bbc ? parse_bbc($extra['new']) : $extra['new']) : '',
2521
			'time' => timeformat($row['log_time']),
2522
		);
2523
	}
2524
	$smcFunc['db_free_result']($request);
2525
2526
	// Get any member names.
2527
	if (!empty($members))
2528
	{
2529
		$request = $smcFunc['db_query']('', '
2530
			SELECT
2531
				id_member, real_name
2532
			FROM {db_prefix}members
2533
			WHERE id_member IN ({array_int:members})',
2534
			array(
2535
				'members' => $members,
2536
			)
2537
		);
2538
		$members = array();
2539
		while ($row = $smcFunc['db_fetch_assoc']($request))
2540
			$members[$row['id_member']] = $row['real_name'];
2541
		$smcFunc['db_free_result']($request);
2542
2543
		foreach ($edits as $key => $value)
2544
			if (isset($members[$value['id_member']]))
2545
				$edits[$key]['member_link'] = '<a href="' . $scripturl . '?action=profile;u=' . $value['id_member'] . '">' . $members[$value['id_member']] . '</a>';
2546
	}
2547
2548
	return $edits;
2549
}
2550
2551
/**
2552
 * Display the history of group requests made by the user whose profile we are viewing.
2553
 *
2554
 * @param int $memID The ID of the member
2555
 */
2556
function trackGroupReq($memID)
2557
{
2558
	global $scripturl, $txt, $modSettings, $sourcedir, $context;
2559
2560
	require_once($sourcedir . '/Subs-List.php');
2561
2562
	// Set the options for the error lists.
2563
	$listOptions = array(
2564
		'id' => 'request_list',
2565
		'title' => sprintf($txt['trackGroupRequests_title'], $context['member']['name']),
2566
		'items_per_page' => $modSettings['defaultMaxListItems'],
2567
		'no_items_label' => $txt['requested_none'],
2568
		'base_href' => $scripturl . '?action=profile;area=tracking;sa=groupreq;u=' . $memID,
2569
		'default_sort_col' => 'time_applied',
2570
		'get_items' => array(
2571
			'function' => 'list_getGroupRequests',
2572
			'params' => array(
2573
				$memID,
2574
			),
2575
		),
2576
		'get_count' => array(
2577
			'function' => 'list_getGroupRequestsCount',
2578
			'params' => array(
2579
				$memID,
2580
			),
2581
		),
2582
		'columns' => array(
2583
			'group' => array(
2584
				'header' => array(
2585
					'value' => $txt['requested_group'],
2586
				),
2587
				'data' => array(
2588
					'db' => 'group_name',
2589
				),
2590
			),
2591
			'group_reason' => array(
2592
				'header' => array(
2593
					'value' => $txt['requested_group_reason'],
2594
				),
2595
				'data' => array(
2596
					'db' => 'group_reason',
2597
				),
2598
			),
2599
			'time_applied' => array(
2600
				'header' => array(
2601
					'value' => $txt['requested_group_time'],
2602
				),
2603
				'data' => array(
2604
					'db' => 'time_applied',
2605
					'timeformat' => true,
2606
				),
2607
				'sort' => array(
2608
					'default' => 'time_applied DESC',
2609
					'reverse' => 'time_applied',
2610
				),
2611
			),
2612
			'outcome' => array(
2613
				'header' => array(
2614
					'value' => $txt['requested_group_outcome'],
2615
				),
2616
				'data' => array(
2617
					'db' => 'outcome',
2618
				),
2619
			),
2620
		),
2621
	);
2622
2623
	// Create the error list.
2624
	createList($listOptions);
2625
2626
	$context['sub_template'] = 'show_list';
2627
	$context['default_list'] = 'request_list';
2628
}
2629
2630
/**
2631
 * How many edits?
2632
 *
2633
 * @param int $memID The ID of the member
2634
 * @return int The number of profile edits
2635
 */
2636
function list_getGroupRequestsCount($memID)
2637
{
2638
	global $smcFunc, $user_info;
2639
2640
	$request = $smcFunc['db_query']('', '
2641
		SELECT COUNT(*) AS req_count
2642
		FROM {db_prefix}log_group_requests AS lgr
2643
		WHERE id_member = {int:memID}
2644
			AND ' . ($user_info['mod_cache']['gq'] == '1=1' ? $user_info['mod_cache']['gq'] : 'lgr.' . $user_info['mod_cache']['gq']),
2645
		array(
2646
			'memID' => $memID,
2647
		)
2648
	);
2649
	list ($report_count) = $smcFunc['db_fetch_row']($request);
2650
	$smcFunc['db_free_result']($request);
2651
2652
	return (int) $report_count;
2653
}
2654
2655
/**
2656
 * Loads up information about a user's group requests. Callback for the list in trackGroupReq()
2657
 *
2658
 * @param int $start Which item to start with (for pagination purposes)
2659
 * @param int $items_per_page How many items to show on each page
2660
 * @param string $sort A string indicating how to sort the results
2661
 * @param int $memID The ID of the member
2662
 * @return array An array of information about the user's group requests
2663
 */
2664
function list_getGroupRequests($start, $items_per_page, $sort, $memID)
2665
{
2666
	global $smcFunc, $txt, $scripturl, $user_info;
2667
2668
	$groupreq = array();
2669
2670
	$request = $smcFunc['db_query']('', '
2671
		SELECT
2672
			lgr.id_group, mg.group_name, mg.online_color, lgr.time_applied, lgr.reason, lgr.status,
2673
			ma.id_member AS id_member_acted, COALESCE(ma.member_name, lgr.member_name_acted) AS act_name, lgr.time_acted, lgr.act_reason
2674
		FROM {db_prefix}log_group_requests AS lgr
2675
			LEFT JOIN {db_prefix}members AS ma ON (lgr.id_member_acted = ma.id_member)
2676
			INNER JOIN {db_prefix}membergroups AS mg ON (lgr.id_group = mg.id_group)
2677
		WHERE lgr.id_member = {int:memID}
2678
			AND ' . ($user_info['mod_cache']['gq'] == '1=1' ? $user_info['mod_cache']['gq'] : 'lgr.' . $user_info['mod_cache']['gq']) . '
2679
		ORDER BY {raw:sort}
2680
		LIMIT {int:start}, {int:max}',
2681
		array(
2682
			'memID' => $memID,
2683
			'sort' => $sort,
2684
			'start' => $start,
2685
			'max' => $items_per_page,
2686
		)
2687
	);
2688
	while ($row = $smcFunc['db_fetch_assoc']($request))
2689
	{
2690
		$this_req = array(
2691
			'group_name' => empty($row['online_color']) ? $row['group_name'] : '<span style="color:' . $row['online_color'] . '">' . $row['group_name'] . '</span>',
2692
			'group_reason' => $row['reason'],
2693
			'time_applied' => $row['time_applied'],
2694
		);
2695
		switch ($row['status'])
2696
		{
2697
			case 0:
2698
				$this_req['outcome'] = $txt['outcome_pending'];
2699
				break;
2700
			case 1:
2701
				$member_link = empty($row['id_member_acted']) ? $row['act_name'] : '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member_acted'] . '">' . $row['act_name'] . '</a>';
2702
				$this_req['outcome'] = sprintf($txt['outcome_approved'], $member_link, timeformat($row['time_acted']));
2703
				break;
2704
			case 2:
2705
				$member_link = empty($row['id_member_acted']) ? $row['act_name'] : '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member_acted'] . '">' . $row['act_name'] . '</a>';
2706
				$this_req['outcome'] = sprintf(!empty($row['act_reason']) ? $txt['outcome_refused_reason'] : $txt['outcome_refused'], $member_link, timeformat($row['time_acted']), $row['act_reason']);
2707
				break;
2708
		}
2709
2710
		$groupreq[] = $this_req;
2711
	}
2712
	$smcFunc['db_free_result']($request);
2713
2714
	return $groupreq;
2715
}
2716
2717
/**
2718
 * Shows which permissions a user has
2719
 *
2720
 * @param int $memID The ID of the member
2721
 */
2722
function showPermissions($memID)
2723
{
2724
	global $txt, $board;
2725
	global $user_profile, $context, $sourcedir, $smcFunc;
2726
2727
	// Verify if the user has sufficient permissions.
2728
	isAllowedTo('manage_permissions');
2729
2730
	loadLanguage('ManagePermissions');
2731
	loadLanguage('Admin');
2732
	loadTemplate('ManageMembers');
2733
2734
	// Load all the permission profiles.
2735
	require_once($sourcedir . '/ManagePermissions.php');
2736
	loadPermissionProfiles();
2737
2738
	$context['member']['id'] = $memID;
2739
	$context['member']['name'] = $user_profile[$memID]['real_name'];
2740
2741
	$context['page_title'] = $txt['showPermissions'];
2742
	$board = empty($board) ? 0 : (int) $board;
2743
	$context['board'] = $board;
2744
2745
	// Determine which groups this user is in.
2746
	if (empty($user_profile[$memID]['additional_groups']))
2747
		$curGroups = array();
2748
	else
2749
		$curGroups = explode(',', $user_profile[$memID]['additional_groups']);
2750
	$curGroups[] = $user_profile[$memID]['id_group'];
2751
	$curGroups[] = $user_profile[$memID]['id_post_group'];
2752
2753
	// Load a list of boards for the jump box - except the defaults.
2754
	$request = $smcFunc['db_query']('order_by_board_order', '
2755
		SELECT b.id_board, b.name, b.id_profile, b.member_groups, COALESCE(mods.id_member, modgs.id_group, 0) AS is_mod
2756
		FROM {db_prefix}boards AS b
2757
			LEFT JOIN {db_prefix}moderators AS mods ON (mods.id_board = b.id_board AND mods.id_member = {int:current_member})
2758
			LEFT JOIN {db_prefix}moderator_groups AS modgs ON (modgs.id_board = b.id_board AND modgs.id_group IN ({array_int:current_groups}))
2759
		WHERE {query_see_board}',
2760
		array(
2761
			'current_member' => $memID,
2762
			'current_groups' => $curGroups,
2763
		)
2764
	);
2765
	$context['boards'] = array();
2766
	$context['no_access_boards'] = array();
2767
	while ($row = $smcFunc['db_fetch_assoc']($request))
2768
	{
2769
		if (count(array_intersect($curGroups, explode(',', $row['member_groups']))) === 0 && !$row['is_mod'])
2770
			$context['no_access_boards'][] = array(
2771
				'id' => $row['id_board'],
2772
				'name' => $row['name'],
2773
				'is_last' => false,
2774
			);
2775
		elseif ($row['id_profile'] != 1 || $row['is_mod'])
2776
			$context['boards'][$row['id_board']] = array(
2777
				'id' => $row['id_board'],
2778
				'name' => $row['name'],
2779
				'selected' => $board == $row['id_board'],
2780
				'profile' => $row['id_profile'],
2781
				'profile_name' => $context['profiles'][$row['id_profile']]['name'],
2782
			);
2783
	}
2784
	$smcFunc['db_free_result']($request);
2785
2786
	require_once($sourcedir . '/Subs-Boards.php');
2787
	sortBoards($context['boards']);
2788
2789
	if (!empty($context['no_access_boards']))
2790
		$context['no_access_boards'][count($context['no_access_boards']) - 1]['is_last'] = true;
2791
2792
	$context['member']['permissions'] = array(
2793
		'general' => array(),
2794
		'board' => array()
2795
	);
2796
2797
	// If you're an admin we know you can do everything, we might as well leave.
2798
	$context['member']['has_all_permissions'] = in_array(1, $curGroups);
2799
	if ($context['member']['has_all_permissions'])
2800
		return;
2801
2802
	$denied = array();
2803
2804
	// Get all general permissions.
2805
	$result = $smcFunc['db_query']('', '
2806
		SELECT p.permission, p.add_deny, mg.group_name, p.id_group
2807
		FROM {db_prefix}permissions AS p
2808
			LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = p.id_group)
2809
		WHERE p.id_group IN ({array_int:group_list})
2810
		ORDER BY p.add_deny DESC, p.permission, mg.min_posts, CASE WHEN mg.id_group < {int:newbie_group} THEN mg.id_group ELSE 4 END, mg.group_name',
2811
		array(
2812
			'group_list' => $curGroups,
2813
			'newbie_group' => 4,
2814
		)
2815
	);
2816
	while ($row = $smcFunc['db_fetch_assoc']($result))
2817
	{
2818
		// We don't know about this permission, it doesn't exist :P.
2819
		if (!isset($txt['permissionname_' . $row['permission']]))
2820
			continue;
2821
2822
		if (empty($row['add_deny']))
2823
			$denied[] = $row['permission'];
2824
2825
		// Permissions that end with _own or _any consist of two parts.
2826
		if (in_array(substr($row['permission'], -4), array('_own', '_any')) && isset($txt['permissionname_' . substr($row['permission'], 0, -4)]))
2827
			$name = $txt['permissionname_' . substr($row['permission'], 0, -4)] . ' - ' . $txt['permissionname_' . $row['permission']];
2828
		else
2829
			$name = $txt['permissionname_' . $row['permission']];
2830
2831
		// Add this permission if it doesn't exist yet.
2832
		if (!isset($context['member']['permissions']['general'][$row['permission']]))
2833
			$context['member']['permissions']['general'][$row['permission']] = array(
2834
				'id' => $row['permission'],
2835
				'groups' => array(
2836
					'allowed' => array(),
2837
					'denied' => array()
2838
				),
2839
				'name' => $name,
2840
				'is_denied' => false,
2841
				'is_global' => true,
2842
			);
2843
2844
		// Add the membergroup to either the denied or the allowed groups.
2845
		$context['member']['permissions']['general'][$row['permission']]['groups'][empty($row['add_deny']) ? 'denied' : 'allowed'][] = $row['id_group'] == 0 ? $txt['membergroups_members'] : $row['group_name'];
2846
2847
		// Once denied is always denied.
2848
		$context['member']['permissions']['general'][$row['permission']]['is_denied'] |= empty($row['add_deny']);
2849
	}
2850
	$smcFunc['db_free_result']($result);
2851
2852
	$request = $smcFunc['db_query']('', '
2853
		SELECT
2854
			bp.add_deny, bp.permission, bp.id_group, mg.group_name' . (empty($board) ? '' : ',
2855
			b.id_profile, CASE WHEN (mods.id_member IS NULL AND modgs.id_group IS NULL) THEN 0 ELSE 1 END AS is_moderator') . '
2856
		FROM {db_prefix}board_permissions AS bp' . (empty($board) ? '' : '
2857
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = {int:current_board})
2858
			LEFT JOIN {db_prefix}moderators AS mods ON (mods.id_board = b.id_board AND mods.id_member = {int:current_member})
2859
			LEFT JOIN {db_prefix}moderator_groups AS modgs ON (modgs.id_board = b.id_board AND modgs.id_group IN ({array_int:group_list}))') . '
2860
			LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = bp.id_group)
2861
		WHERE bp.id_profile = {raw:current_profile}
2862
			AND bp.id_group IN ({array_int:group_list}' . (empty($board) ? ')' : ', {int:moderator_group})
2863
			AND (mods.id_member IS NOT NULL OR modgs.id_group IS NOT NULL OR bp.id_group != {int:moderator_group})'),
2864
		array(
2865
			'current_board' => $board,
2866
			'group_list' => $curGroups,
2867
			'current_member' => $memID,
2868
			'current_profile' => empty($board) ? '1' : 'b.id_profile',
2869
			'moderator_group' => 3,
2870
		)
2871
	);
2872
2873
	while ($row = $smcFunc['db_fetch_assoc']($request))
2874
	{
2875
		// We don't know about this permission, it doesn't exist :P.
2876
		if (!isset($txt['permissionname_' . $row['permission']]))
2877
			continue;
2878
2879
		// The name of the permission using the format 'permission name' - 'own/any topic/event/etc.'.
2880
		if (in_array(substr($row['permission'], -4), array('_own', '_any')) && isset($txt['permissionname_' . substr($row['permission'], 0, -4)]))
2881
			$name = $txt['permissionname_' . substr($row['permission'], 0, -4)] . ' - ' . $txt['permissionname_' . $row['permission']];
2882
		else
2883
			$name = $txt['permissionname_' . $row['permission']];
2884
2885
		// Create the structure for this permission.
2886
		if (!isset($context['member']['permissions']['board'][$row['permission']]))
2887
			$context['member']['permissions']['board'][$row['permission']] = array(
2888
				'id' => $row['permission'],
2889
				'groups' => array(
2890
					'allowed' => array(),
2891
					'denied' => array()
2892
				),
2893
				'name' => $name,
2894
				'is_denied' => false,
2895
				'is_global' => empty($board),
2896
			);
2897
2898
		$context['member']['permissions']['board'][$row['permission']]['groups'][empty($row['add_deny']) ? 'denied' : 'allowed'][$row['id_group']] = $row['id_group'] == 0 ? $txt['membergroups_members'] : $row['group_name'];
2899
2900
		$context['member']['permissions']['board'][$row['permission']]['is_denied'] |= empty($row['add_deny']);
2901
	}
2902
	$smcFunc['db_free_result']($request);
2903
}
2904
2905
/**
2906
 * View a member's warnings
2907
 *
2908
 * @param int $memID The ID of the member
2909
 */
2910
function viewWarning($memID)
2911
{
2912
	global $modSettings, $context, $sourcedir, $txt, $scripturl;
2913
2914
	// Firstly, can we actually even be here?
2915
	if (!($context['user']['is_owner'] && allowedTo('view_warning_own')) && !allowedTo('view_warning_any') && !allowedTo('issue_warning') && !allowedTo('moderate_forum'))
2916
		fatal_lang_error('no_access', false);
2917
2918
	// Make sure things which are disabled stay disabled.
2919
	$modSettings['warning_watch'] = !empty($modSettings['warning_watch']) ? $modSettings['warning_watch'] : 110;
2920
	$modSettings['warning_moderate'] = !empty($modSettings['warning_moderate']) && !empty($modSettings['postmod_active']) ? $modSettings['warning_moderate'] : 110;
2921
	$modSettings['warning_mute'] = !empty($modSettings['warning_mute']) ? $modSettings['warning_mute'] : 110;
2922
2923
	// Let's use a generic list to get all the current warnings, and use the issue warnings grab-a-granny thing.
2924
	require_once($sourcedir . '/Subs-List.php');
2925
	require_once($sourcedir . '/Profile-Actions.php');
2926
2927
	$listOptions = array(
2928
		'id' => 'view_warnings',
2929
		'title' => $txt['profile_viewwarning_previous_warnings'],
2930
		'items_per_page' => $modSettings['defaultMaxListItems'],
2931
		'no_items_label' => $txt['profile_viewwarning_no_warnings'],
2932
		'base_href' => $scripturl . '?action=profile;area=viewwarning;sa=user;u=' . $memID,
2933
		'default_sort_col' => 'log_time',
2934
		'get_items' => array(
2935
			'function' => 'list_getUserWarnings',
2936
			'params' => array(
2937
				$memID,
2938
			),
2939
		),
2940
		'get_count' => array(
2941
			'function' => 'list_getUserWarningCount',
2942
			'params' => array(
2943
				$memID,
2944
			),
2945
		),
2946
		'columns' => array(
2947
			'log_time' => array(
2948
				'header' => array(
2949
					'value' => $txt['profile_warning_previous_time'],
2950
				),
2951
				'data' => array(
2952
					'db' => 'time',
2953
				),
2954
				'sort' => array(
2955
					'default' => 'lc.log_time DESC',
2956
					'reverse' => 'lc.log_time',
2957
				),
2958
			),
2959
			'reason' => array(
2960
				'header' => array(
2961
					'value' => $txt['profile_warning_previous_reason'],
2962
					'style' => 'width: 50%;',
2963
				),
2964
				'data' => array(
2965
					'db' => 'reason',
2966
				),
2967
			),
2968
			'level' => array(
2969
				'header' => array(
2970
					'value' => $txt['profile_warning_previous_level'],
2971
				),
2972
				'data' => array(
2973
					'db' => 'counter',
2974
				),
2975
				'sort' => array(
2976
					'default' => 'lc.counter DESC',
2977
					'reverse' => 'lc.counter',
2978
				),
2979
			),
2980
		),
2981
		'additional_rows' => array(
2982
			array(
2983
				'position' => 'after_title',
2984
				'value' => $txt['profile_viewwarning_desc'],
2985
				'class' => 'smalltext',
2986
				'style' => 'padding: 2ex;',
2987
			),
2988
		),
2989
	);
2990
2991
	// Create the list for viewing.
2992
	require_once($sourcedir . '/Subs-List.php');
2993
	createList($listOptions);
2994
2995
	// Create some common text bits for the template.
2996
	$context['level_effects'] = array(
2997
		0 => '',
2998
		$modSettings['warning_watch'] => $txt['profile_warning_effect_own_watched'],
2999
		$modSettings['warning_moderate'] => $txt['profile_warning_effect_own_moderated'],
3000
		$modSettings['warning_mute'] => $txt['profile_warning_effect_own_muted'],
3001
	);
3002
	$context['current_level'] = 0;
3003
	foreach ($context['level_effects'] as $limit => $dummy)
3004
		if ($context['member']['warning'] >= $limit)
3005
			$context['current_level'] = $limit;
3006
}
3007
3008
?>