Failed Conditions
Branch release-2.1 (4e22cf)
by Rick
06:39
created

Profile-View.php ➔ list_getIPMessageCount()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 16
Code Lines 8

Duplication

Lines 16
Ratio 100 %

Importance

Changes 0
Metric Value
cc 1
eloc 8
nc 1
nop 2
dl 16
loc 16
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Simple Machines Forum (SMF)
5
 *
6
 * @package SMF
7
 * @author Simple Machines http://www.simplemachines.org
8
 * @copyright 2017 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
Documentation introduced by
404 is of type integer, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
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_identity_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 View Code Duplication
	elseif (!empty($modSettings['warning_moderate']) && $modSettings['warning_moderate'] <= $context['member']['warning'])
55
		$context['warning_status'] = $txt['profile_warning_is_moderation'];
56 View Code Duplication
	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)
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
	// How about, are they banned?
131
	$context['member']['bans'] = array();
132
	if (allowedTo('moderate_forum'))
133
	{
134
		// Can they edit the ban?
135
		$context['can_edit_ban'] = allowedTo('manage_bans');
136
137
		$ban_query = array();
138
		$ban_query_vars = array(
139
			'time' => time(),
140
		);
141
		$ban_query[] = 'id_member = ' . $context['member']['id'];
142
		$ban_query[] = ' {inet:ip} BETWEEN bi.ip_low and bi.ip_high';
143
		$ban_query_vars['ip'] = $memberContext[$memID]['ip'];
144
		// Do we have a hostname already?
145 View Code Duplication
		if (!empty($context['member']['hostname']))
146
		{
147
			$ban_query[] = '({string:hostname} LIKE hostname)';
148
			$ban_query_vars['hostname'] = $context['member']['hostname'];
149
		}
150
		// Check their email as well...
151 View Code Duplication
		if (strlen($context['member']['email']) != 0)
152
		{
153
			$ban_query[] = '({string:email} LIKE bi.email_address)';
154
			$ban_query_vars['email'] = $context['member']['email'];
155
		}
156
157
		// So... are they banned?  Dying to know!
158
		$request = $smcFunc['db_query']('', '
159
			SELECT bg.id_ban_group, bg.name, bg.cannot_access, bg.cannot_post, bg.cannot_register,
160
				bg.cannot_login, bg.reason
161
			FROM {db_prefix}ban_items AS bi
162
				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}))
163
			WHERE (' . implode(' OR ', $ban_query) . ')',
164
			$ban_query_vars
165
		);
166
		while ($row = $smcFunc['db_fetch_assoc']($request))
167
		{
168
			// Work out what restrictions we actually have.
169
			$ban_restrictions = array();
170
			foreach (array('access', 'register', 'login', 'post') as $type)
171
				if ($row['cannot_' . $type])
172
					$ban_restrictions[] = $txt['ban_type_' . $type];
173
174
			// No actual ban in place?
175
			if (empty($ban_restrictions))
176
				continue;
177
178
			// Prepare the link for context.
179
			$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>');
180
181
			$context['member']['bans'][$row['id_ban_group']] = array(
182
				'reason' => empty($row['reason']) ? '' : '<br><br><strong>' . $txt['ban_reason'] . ':</strong> ' . $row['reason'],
183
				'cannot' => array(
184
					'access' => !empty($row['cannot_access']),
185
					'register' => !empty($row['cannot_register']),
186
					'post' => !empty($row['cannot_post']),
187
					'login' => !empty($row['cannot_login']),
188
				),
189
				'explanation' => $ban_explanation,
190
			);
191
		}
192
		$smcFunc['db_free_result']($request);
193
	}
194
	loadCustomFields($memID);
195
196
	$context['print_custom_fields'] = array();
197
198
	// Any custom profile fields?
199
	if (!empty($context['custom_fields']))
200
		foreach ($context['custom_fields'] as $custom)
201
			$context['print_custom_fields'][$context['cust_profile_fields_placement'][$custom['placement']]][] = $custom;
202
203
}
204
205
/**
206
 * Fetch the alerts a user currently has.
207
 *
208
 * @param int $memID The ID of the member
209
 * @param bool $all Whether to fetch all alerts or just unread ones
210
 * @param int $counter How many alerts to display (0 if displaying all or using pagination)
211
 * @param array $pagination An array containing info for handling pagination. Should have 'start' and 'maxIndex'
212
 * @return array An array of information about the fetched alerts
213
 */
214
function fetch_alerts($memID, $all = false, $counter = 0, $pagination = array())
215
{
216
	global $smcFunc, $txt, $scripturl, $memberContext;
217
218
	$alerts = array();
219
	$request = $smcFunc['db_query']('', '
220
		SELECT id_alert, alert_time, mem.id_member AS sender_id, COALESCE(mem.real_name, ua.member_name) AS sender_name,
221
			content_type, content_id, content_action, is_read, extra
222
		FROM {db_prefix}user_alerts AS ua
223
			LEFT JOIN {db_prefix}members AS mem ON (ua.id_member_started = mem.id_member)
224
		WHERE ua.id_member = {int:id_member}' . (!$all ? '
225
			AND is_read = 0' : '') . '
226
		ORDER BY id_alert DESC' . (!empty($counter) && empty($pagination) ? '
227
		LIMIT {int:counter}' : '') . (!empty($pagination) && empty($counter) ? '
228
		LIMIT {int:start}, {int:maxIndex}' : ''),
229
		array(
230
			'id_member' => $memID,
231
			'counter' => $counter,
232
			'start' => !empty($pagination['start']) ? $pagination['start'] : 0,
233
			'maxIndex' => !empty($pagination['maxIndex']) ? $pagination['maxIndex'] : 0,
234
		)
235
	);
236
237
	$senders = array();
238
	while ($row = $smcFunc['db_fetch_assoc']($request))
239
	{
240
		$id_alert = array_shift($row);
241
		$row['time'] = timeformat($row['alert_time']);
242
		$row['extra'] = !empty($row['extra']) ? $smcFunc['json_decode']($row['extra'], true) : array();
243
		$alerts[$id_alert] = $row;
244
245
		if (!empty($row['sender_id']))
246
			$senders[] = $row['sender_id'];
247
	}
248
	$smcFunc['db_free_result']($request);
249
250
	$senders = loadMemberData($senders);
251
	foreach ($senders as $member)
252
		loadMemberContext($member);
253
254
	// Now go through and actually make with the text.
255
	loadLanguage('Alerts');
256
257
	// Hooks might want to do something snazzy around their own content types - including enforcing permissions if appropriate.
258
	call_integration_hook('integrate_fetch_alerts', array(&$alerts));
259
260
	// For anything that wants us to check board or topic access, let's do that.
261
	$boards = array();
262
	$topics = array();
263
	$msgs = array();
264
	foreach ($alerts as $id_alert => $alert)
265
	{
266
		if (isset($alert['extra']['board']))
267
			$boards[$alert['extra']['board']] = $txt['board_na'];
268
		if (isset($alert['extra']['topic']))
269
			$topics[$alert['extra']['topic']] = $txt['topic_na'];
270
		if ($alert['content_type'] == 'msg')
271
			$msgs[$alert['content_id']] = $txt['topic_na'];
272
	}
273
274
	// 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.
275 View Code Duplication
	if (!empty($boards))
276
	{
277
		$request = $smcFunc['db_query']('', '
278
			SELECT id_board, name
279
			FROM {db_prefix}boards AS b
280
			WHERE {query_see_board}
281
				AND id_board IN ({array_int:boards})',
282
			array(
283
				'boards' => array_keys($boards),
284
			)
285
		);
286
		while ($row = $smcFunc['db_fetch_assoc']($request))
287
			$boards[$row['id_board']] = '<a href="' . $scripturl . '?board=' . $row['id_board'] . '.0">' . $row['name'] . '</a>';
288
	}
289 View Code Duplication
	if (!empty($topics))
290
	{
291
		$request = $smcFunc['db_query']('', '
292
			SELECT t.id_topic, m.subject
293
			FROM {db_prefix}topics AS t
294
				INNER JOIN {db_prefix}messages AS m ON (t.id_first_msg = m.id_msg)
295
				INNER JOIN {db_prefix}boards AS b ON (t.id_board = b.id_board)
296
			WHERE {query_see_board}
297
				AND t.id_topic IN ({array_int:topics})',
298
			array(
299
				'topics' => array_keys($topics),
300
			)
301
		);
302
		while ($row = $smcFunc['db_fetch_assoc']($request))
303
			$topics[$row['id_topic']] = '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.0">' . $row['subject'] . '</a>';
304
	}
305
	if (!empty($msgs))
306
	{
307
		$request = $smcFunc['db_query']('', '
308
			SELECT m.id_msg, t.id_topic, m.subject
309
			FROM {db_prefix}messages AS m
310
				INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic)
311
				INNER JOIN {db_prefix}boards AS b ON (m.id_board = b.id_board)
312
			WHERE {query_see_board}
313
				AND m.id_msg IN ({array_int:msgs})',
314
			array(
315
				'msgs' => array_keys($msgs),
316
			)
317
		);
318 View Code Duplication
		while ($row = $smcFunc['db_fetch_assoc']($request))
319
			$msgs[$row['id_msg']] = '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'] . '">' . $row['subject'] . '</a>';
320
	}
321
322
	// 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)
323
	foreach ($alerts as $id_alert => $alert)
324
	{
325
		if (!empty($alert['text']))
326
			continue;
327 View Code Duplication
		if (isset($alert['extra']['board']))
328
			$alerts[$id_alert]['extra']['board_msg'] = $boards[$alert['extra']['board']];
329 View Code Duplication
		if (isset($alert['extra']['topic']))
330
			$alerts[$id_alert]['extra']['topic_msg'] = $topics[$alert['extra']['topic']];
331
		if ($alert['content_type'] == 'msg')
332
			$alerts[$id_alert]['extra']['msg_msg'] = $msgs[$alert['content_id']];
333
		if ($alert['content_type'] == 'profile')
334
			$alerts[$id_alert]['extra']['profile_msg'] = '<a href="' . $scripturl . '?action=profile;u=' . $alerts[$id_alert]['content_id'] . '">' . $alerts[$id_alert]['extra']['user_name'] . '</a>';
335
336
		if (!empty($memberContext[$alert['sender_id']]))
337
			$alerts[$id_alert]['sender'] = &$memberContext[$alert['sender_id']];
338
339
		$string = 'alert_' . $alert['content_type'] . '_' . $alert['content_action'];
340
		if (isset($txt[$string]))
341
		{
342
			$extra = $alerts[$id_alert]['extra'];
343
			$search = array('{member_link}', '{scripturl}');
344
			$repl = array(!empty($alert['sender_id']) ? '<a href="' . $scripturl . '?action=profile;u=' . $alert['sender_id'] . '">' . $alert['sender_name'] . '</a>' : $alert['sender_name'], $scripturl);
345
			foreach ($extra as $k => $v)
346
			{
347
				$search[] = '{' . $k . '}';
348
				$repl[] = $v;
349
			}
350
			$alerts[$id_alert]['text'] = str_replace($search, $repl, $txt[$string]);
351
		}
352
	}
353
354
	return $alerts;
355
}
356
357
/**
358
 * Shows all alerts for this user
359
 *
360
 * @param int $memID The ID of the member
361
 */
362
function showAlerts($memID)
363
{
364
	global $context, $smcFunc, $txt, $sourcedir, $scripturl;
365
366
	require_once($sourcedir . '/Profile-Modify.php');
367
368
	// Prepare the pagination vars.
369
	$maxIndex = 10;
370
	$start = (int) isset($_REQUEST['start']) ? $_REQUEST['start'] : 0;
371
	$count = alert_count($memID);
372
373
	// Get the alerts.
374
	$context['alerts'] = fetch_alerts($memID, true, false, array('start' => $start, 'maxIndex' => $maxIndex));
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
375
	$toMark = false;
376
	$action = '';
377
378
	// Create the pagination.
379
	$context['pagination'] = constructPageIndex($scripturl . '?action=profile;area=showalerts;u=' . $memID, $start, $count, $maxIndex, false);
380
381
	// Set some JavaScript for checking all alerts at once.
382
	addInlineJavaScript('
383
	$(function(){
384
		$(\'#select_all\').on(\'change\', function() {
385
			var checkboxes = $(\'ul.quickbuttons\').find(\':checkbox\');
386
			if($(this).prop(\'checked\')) {
387
				checkboxes.prop(\'checked\', true);
388
			}
389
			else {
390
				checkboxes.prop(\'checked\', false);
391
			}
392
		});
393
	});', true);
394
395
	// Set a nice message.
396
	if (!empty($_SESSION['update_message']))
397
	{
398
		$context['update_message'] = $txt['profile_updated_own'];
399
		unset($_SESSION['update_message']);
400
	}
401
402
	// Saving multiple changes?
403
	if (isset($_GET['save']) && !empty($_POST['mark']))
404
	{
405
		// Get the values.
406
		$toMark = array_map('intval', $_POST['mark']);
407
408
		// Which action?
409
		$action = !empty($_POST['mark_as']) ? $smcFunc['htmlspecialchars']($smcFunc['htmltrim']($_POST['mark_as'])) : '';
410
	}
411
412
	// A single change.
413
	if (!empty($_GET['do']) && !empty($_GET['aid']))
414
	{
415
		$toMark = (int) $_GET['aid'];
416
		$action = $smcFunc['htmlspecialchars']($smcFunc['htmltrim']($_GET['do']));
417
	}
418
419
	// Save the changes.
420
	if (!empty($toMark) && !empty($action))
421
	{
422
		checkSession('request');
423
424
		// Call it!
425
		if ($action == 'remove')
426
			alert_delete($toMark, $memID);
427
428
		else
429
			alert_mark($memID, $toMark, $action == 'read' ? 1 : 0);
430
431
		// Set a nice update message.
432
		$_SESSION['update_message'] = true;
433
434
		// Redirect.
435
		redirectexit('action=profile;area=showalerts;u=' . $memID);
436
	}
437
}
438
439
/**
440
 * Show all posts by the current user
441
 * @todo This function needs to be split up properly.
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
442
 *
443
 * @param int $memID The ID of the member
444
 */
445
function showPosts($memID)
446
{
447
	global $txt, $user_info, $scripturl, $modSettings;
448
	global $context, $user_profile, $sourcedir, $smcFunc, $board;
449
450
	// Some initial context.
451
	$context['start'] = (int) $_REQUEST['start'];
452
	$context['current_member'] = $memID;
453
454
	// Create the tabs for the template.
455
	$context[$context['profile_menu_name']]['tab_data'] = array(
456
		'title' => $txt['showPosts'],
457
		'description' => $txt['showPosts_help'],
458
		'icon' => 'profile_hd.png',
459
		'tabs' => array(
460
			'messages' => array(
461
			),
462
			'topics' => array(
463
			),
464
			'unwatchedtopics' => array(
465
			),
466
			'attach' => array(
467
			),
468
		),
469
	);
470
471
	// Shortcut used to determine which $txt['show*'] string to use for the title, based on the SA
472
	$title = array(
473
		'attach' => 'Attachments',
474
		'unwatchedtopics' => 'Unwatched',
475
		'topics' => 'Topics'
476
	);
477
478
	// Set the page title
479
	if (isset($_GET['sa']) && array_key_exists($_GET['sa'], $title))
480
		$context['page_title'] = $txt['show' . $title[$_GET['sa']]];
481
	else
482
		$context['page_title'] = $txt['showPosts'];
483
484
	$context['page_title'] .= ' - ' . $user_profile[$memID]['real_name'];
485
486
	// Is the load average too high to allow searching just now?
487 View Code Duplication
	if (!empty($context['load_average']) && !empty($modSettings['loadavg_show_posts']) && $context['load_average'] >= $modSettings['loadavg_show_posts'])
488
		fatal_lang_error('loadavg_show_posts_disabled', false);
489
490
	// If we're specifically dealing with attachments use that function!
491
	if (isset($_GET['sa']) && $_GET['sa'] == 'attach')
492
		return showAttachments($memID);
493
	// Instead, if we're dealing with unwatched topics (and the feature is enabled) use that other function.
494
	elseif (isset($_GET['sa']) && $_GET['sa'] == 'unwatchedtopics')
495
		return showUnwatched($memID);
496
497
	// Are we just viewing topics?
498
	$context['is_topics'] = isset($_GET['sa']) && $_GET['sa'] == 'topics' ? true : false;
499
500
	// If just deleting a message, do it and then redirect back.
501
	if (isset($_GET['delete']) && !$context['is_topics'])
502
	{
503
		checkSession('get');
504
505
		// We need msg info for logging.
506
		$request = $smcFunc['db_query']('', '
507
			SELECT subject, id_member, id_topic, id_board
508
			FROM {db_prefix}messages
509
			WHERE id_msg = {int:id_msg}',
510
			array(
511
				'id_msg' => (int) $_GET['delete'],
512
			)
513
		);
514
		$info = $smcFunc['db_fetch_row']($request);
515
		$smcFunc['db_free_result']($request);
516
517
		// Trying to remove a message that doesn't exist.
518
		if (empty($info))
519
			redirectexit('action=profile;u=' . $memID . ';area=showposts;start=' . $_GET['start']);
520
521
		// We can be lazy, since removeMessage() will check the permissions for us.
522
		require_once($sourcedir . '/RemoveTopic.php');
523
		removeMessage((int) $_GET['delete']);
524
525
		// Add it to the mod log.
526 View Code Duplication
		if (allowedTo('delete_any') && (!allowedTo('delete_own') || $info[1] != $user_info['id']))
527
			logAction('delete', array('topic' => $info[2], 'subject' => $info[0], 'member' => $info[1], 'board' => $info[3]));
528
529
		// Back to... where we are now ;).
530
		redirectexit('action=profile;u=' . $memID . ';area=showposts;start=' . $_GET['start']);
531
	}
532
533
	// Default to 10.
534 View Code Duplication
	if (empty($_REQUEST['viewscount']) || !is_numeric($_REQUEST['viewscount']))
535
		$_REQUEST['viewscount'] = '10';
536
537
	if ($context['is_topics'])
538
		$request = $smcFunc['db_query']('', '
539
			SELECT COUNT(*)
540
			FROM {db_prefix}topics AS t' . ($user_info['query_see_board'] == '1=1' ? '' : '
541
				INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board AND {query_see_board})') . '
542
			WHERE t.id_member_started = {int:current_member}' . (!empty($board) ? '
543
				AND t.id_board = {int:board}' : '') . (!$modSettings['postmod_active'] || $context['user']['is_owner'] ? '' : '
544
				AND t.approved = {int:is_approved}'),
545
			array(
546
				'current_member' => $memID,
547
				'is_approved' => 1,
548
				'board' => $board,
549
			)
550
		);
551
	else
552
		$request = $smcFunc['db_query']('', '
553
			SELECT COUNT(*)
554
			FROM {db_prefix}messages AS m' . ($user_info['query_see_board'] == '1=1' ? '' : '
555
				INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board AND {query_see_board})') . '
556
			WHERE m.id_member = {int:current_member}' . (!empty($board) ? '
557
				AND m.id_board = {int:board}' : '') . (!$modSettings['postmod_active'] || $context['user']['is_owner'] ? '' : '
558
				AND m.approved = {int:is_approved}'),
559
			array(
560
				'current_member' => $memID,
561
				'is_approved' => 1,
562
				'board' => $board,
563
			)
564
		);
565
	list ($msgCount) = $smcFunc['db_fetch_row']($request);
566
	$smcFunc['db_free_result']($request);
567
568
	$request = $smcFunc['db_query']('', '
569
		SELECT MIN(id_msg), MAX(id_msg)
570
		FROM {db_prefix}messages AS m
571
		WHERE m.id_member = {int:current_member}' . (!empty($board) ? '
572
			AND m.id_board = {int:board}' : '') . (!$modSettings['postmod_active'] || $context['user']['is_owner'] ? '' : '
573
			AND m.approved = {int:is_approved}'),
574
		array(
575
			'current_member' => $memID,
576
			'is_approved' => 1,
577
			'board' => $board,
578
		)
579
	);
580
	list ($min_msg_member, $max_msg_member) = $smcFunc['db_fetch_row']($request);
581
	$smcFunc['db_free_result']($request);
582
583
	$range_limit = '';
584
585
	if ($context['is_topics'])
586
		$maxPerPage = empty($modSettings['disableCustomPerPage']) && !empty($options['topics_per_page']) ? $options['topics_per_page'] : $modSettings['defaultMaxTopics'];
0 ignored issues
show
Bug introduced by
The variable $options seems to never exist, and therefore empty should always return true. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() on variables that are yet undefined. These calls will always produce the same result and can be removed.

This is most likely caused by the renaming of a variable or the removal of a function/method parameter.

Loading history...
587
	else
588
		$maxPerPage = empty($modSettings['disableCustomPerPage']) && !empty($options['messages_per_page']) ? $options['messages_per_page'] : $modSettings['defaultMaxMessages'];
589
590
	$maxIndex = $maxPerPage;
591
592
	// Make sure the starting place makes sense and construct our friend the page index.
593
	$context['page_index'] = constructPageIndex($scripturl . '?action=profile;u=' . $memID . ';area=showposts' . ($context['is_topics'] ? ';sa=topics' : '') . (!empty($board) ? ';board=' . $board : ''), $context['start'], $msgCount, $maxIndex);
594
	$context['current_page'] = $context['start'] / $maxIndex;
595
596
	// Reverse the query if we're past 50% of the pages for better performance.
597
	$start = $context['start'];
598
	$reverse = $_REQUEST['start'] > $msgCount / 2;
599 View Code Duplication
	if ($reverse)
600
	{
601
		$maxIndex = $msgCount < $context['start'] + $maxPerPage + 1 && $msgCount > $context['start'] ? $msgCount - $context['start'] : $maxPerPage;
602
		$start = $msgCount < $context['start'] + $maxPerPage + 1 || $msgCount < $context['start'] + $maxPerPage ? 0 : $msgCount - $context['start'] - $maxPerPage;
603
	}
604
605
	// Guess the range of messages to be shown.
606
	if ($msgCount > 1000)
607
	{
608
		$margin = floor(($max_msg_member - $min_msg_member) * (($start + $maxPerPage) / $msgCount) + .1 * ($max_msg_member - $min_msg_member));
609
		// Make a bigger margin for topics only.
610
		if ($context['is_topics'])
611
		{
612
			$margin *= 5;
613
			$range_limit = $reverse ? 't.id_first_msg < ' . ($min_msg_member + $margin) : 't.id_first_msg > ' . ($max_msg_member - $margin);
614
		}
615
		else
616
			$range_limit = $reverse ? 'm.id_msg < ' . ($min_msg_member + $margin) : 'm.id_msg > ' . ($max_msg_member - $margin);
617
	}
618
619
	// Find this user's posts.  The left join on categories somehow makes this faster, weird as it looks.
620
	$looped = false;
621
	while (true)
622
	{
623
		if ($context['is_topics'])
624
		{
625
			$request = $smcFunc['db_query']('', '
626
				SELECT
627
					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,
628
					t.approved, m.body, m.smileys_enabled, m.subject, m.poster_time, m.id_topic, m.id_msg
629
				FROM {db_prefix}topics AS t
630
					INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
631
					LEFT JOIN {db_prefix}categories AS c ON (c.id_cat = b.id_cat)
632
					INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)
633
				WHERE t.id_member_started = {int:current_member}' . (!empty($board) ? '
634
					AND t.id_board = {int:board}' : '') . (empty($range_limit) ? '' : '
635
					AND ' . $range_limit) . '
636
					AND {query_see_board}' . (!$modSettings['postmod_active'] || $context['user']['is_owner'] ? '' : '
637
					AND t.approved = {int:is_approved} AND m.approved = {int:is_approved}') . '
638
				ORDER BY t.id_first_msg ' . ($reverse ? 'ASC' : 'DESC') . '
639
				LIMIT {int:start}, {int:max}',
640
				array(
641
					'current_member' => $memID,
642
					'is_approved' => 1,
643
					'board' => $board,
644
					'start' => $start,
645
					'max' => $maxIndex,
646
				)
647
			);
648
		}
649
		else
650
		{
651
			$request = $smcFunc['db_query']('', '
652
				SELECT
653
					b.id_board, b.name AS bname, c.id_cat, c.name AS cname, m.id_topic, m.id_msg,
654
					t.id_member_started, t.id_first_msg, t.id_last_msg, m.body, m.smileys_enabled,
655
					m.subject, m.poster_time, m.approved
656
				FROM {db_prefix}messages AS m
657
					INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic)
658
					INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
659
					LEFT JOIN {db_prefix}categories AS c ON (c.id_cat = b.id_cat)
660
				WHERE m.id_member = {int:current_member}' . (!empty($board) ? '
661
					AND b.id_board = {int:board}' : '') . (empty($range_limit) ? '' : '
662
					AND ' . $range_limit) . '
663
					AND {query_see_board}' . (!$modSettings['postmod_active'] || $context['user']['is_owner'] ? '' : '
664
					AND t.approved = {int:is_approved} AND m.approved = {int:is_approved}') . '
665
				ORDER BY m.id_msg ' . ($reverse ? 'ASC' : 'DESC') . '
666
				LIMIT {int:start}, {int:max}',
667
				array(
668
					'current_member' => $memID,
669
					'is_approved' => 1,
670
					'board' => $board,
671
					'start' => $start,
672
					'max' => $maxIndex,
673
				)
674
			);
675
		}
676
677
		// Make sure we quit this loop.
678
		if ($smcFunc['db_num_rows']($request) === $maxIndex || $looped)
679
			break;
680
		$looped = true;
681
		$range_limit = '';
682
	}
683
684
	// Start counting at the number of the first message displayed.
685
	$counter = $reverse ? $context['start'] + $maxIndex + 1 : $context['start'];
686
	$context['posts'] = array();
687
	$board_ids = array('own' => array(), 'any' => array());
688
	while ($row = $smcFunc['db_fetch_assoc']($request))
689
	{
690
		// Censor....
691
		censorText($row['body']);
692
		censorText($row['subject']);
693
694
		// Do the code.
695
		$row['body'] = parse_bbc($row['body'], $row['smileys_enabled'], $row['id_msg']);
696
697
		// And the array...
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
698
		$context['posts'][$counter += $reverse ? -1 : 1] = array(
699
			'body' => $row['body'],
700
			'counter' => $counter,
701
			'category' => array(
702
				'name' => $row['cname'],
703
				'id' => $row['id_cat']
704
			),
705
			'board' => array(
706
				'name' => $row['bname'],
707
				'id' => $row['id_board']
708
			),
709
			'topic' => $row['id_topic'],
710
			'subject' => $row['subject'],
711
			'start' => 'msg' . $row['id_msg'],
712
			'time' => timeformat($row['poster_time']),
713
			'timestamp' => forum_time(true, $row['poster_time']),
714
			'id' => $row['id_msg'],
715
			'can_reply' => false,
716
			'can_mark_notify' => !$context['user']['is_guest'],
717
			'can_delete' => false,
718
			'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()),
719
			'approved' => $row['approved'],
720
			'css_class' => $row['approved'] ? 'windowbg' : 'approvebg',
721
		);
722
723
		if ($user_info['id'] == $row['id_member_started'])
724
			$board_ids['own'][$row['id_board']][] = $counter;
725
		$board_ids['any'][$row['id_board']][] = $counter;
726
	}
727
	$smcFunc['db_free_result']($request);
728
729
	// All posts were retrieved in reverse order, get them right again.
730
	if ($reverse)
731
		$context['posts'] = array_reverse($context['posts'], true);
732
733
	// These are all the permissions that are different from board to board..
734
	if ($context['is_topics'])
735
		$permissions = array(
736
			'own' => array(
737
				'post_reply_own' => 'can_reply',
738
			),
739
			'any' => array(
740
				'post_reply_any' => 'can_reply',
741
			)
742
		);
743
	else
744
		$permissions = array(
745
			'own' => array(
746
				'post_reply_own' => 'can_reply',
747
				'delete_own' => 'can_delete',
748
			),
749
			'any' => array(
750
				'post_reply_any' => 'can_reply',
751
				'delete_any' => 'can_delete',
752
			)
753
		);
754
755
	// For every permission in the own/any lists...
756
	foreach ($permissions as $type => $list)
757
	{
758
		foreach ($list as $permission => $allowed)
759
		{
760
			// Get the boards they can do this on...
761
			$boards = boardsAllowedTo($permission);
762
763
			// Hmm, they can do it on all boards, can they?
764 View Code Duplication
			if (!empty($boards) && $boards[0] == 0)
765
				$boards = array_keys($board_ids[$type]);
766
767
			// Now go through each board they can do the permission on.
768
			foreach ($boards as $board_id)
769
			{
770
				// There aren't any posts displayed from this board.
771
				if (!isset($board_ids[$type][$board_id]))
772
					continue;
773
774
				// Set the permission to true ;).
775
				foreach ($board_ids[$type][$board_id] as $counter)
776
					$context['posts'][$counter][$allowed] = true;
777
			}
778
		}
779
	}
780
781
	// Clean up after posts that cannot be deleted and quoted.
782
	$quote_enabled = empty($modSettings['disabledBBC']) || !in_array('quote', explode(',', $modSettings['disabledBBC']));
783 View Code Duplication
	foreach ($context['posts'] as $counter => $dummy)
784
	{
785
		$context['posts'][$counter]['can_delete'] &= $context['posts'][$counter]['delete_possible'];
786
		$context['posts'][$counter]['can_quote'] = $context['posts'][$counter]['can_reply'] && $quote_enabled;
787
	}
788
789
	// Allow last minute changes.
790
	call_integration_hook('integrate_profile_showPosts');
791
}
792
793
/**
794
 * Show all the attachments of a user.
795
 *
796
 * @param int $memID The ID of the member
797
 */
798
function showAttachments($memID)
799
{
800
	global $txt, $scripturl, $modSettings;
801
	global $sourcedir;
802
803
	// OBEY permissions!
804
	$boardsAllowed = boardsAllowedTo('view_attachments');
805
806
	// Make sure we can't actually see anything...
807
	if (empty($boardsAllowed))
808
		$boardsAllowed = array(-1);
809
810
	require_once($sourcedir . '/Subs-List.php');
811
812
	// This is all the information required to list attachments.
813
	$listOptions = array(
814
		'id' => 'attachments',
815
		'width' => '100%',
816
		'items_per_page' => $modSettings['defaultMaxListItems'],
817
		'no_items_label' => $txt['show_attachments_none'],
818
		'base_href' => $scripturl . '?action=profile;area=showposts;sa=attach;u=' . $memID,
819
		'default_sort_col' => 'filename',
820
		'get_items' => array(
821
			'function' => 'list_getAttachments',
822
			'params' => array(
823
				$boardsAllowed,
824
				$memID,
825
			),
826
		),
827
		'get_count' => array(
828
			'function' => 'list_getNumAttachments',
829
			'params' => array(
830
				$boardsAllowed,
831
				$memID,
832
			),
833
		),
834
		'data_check' => array(
835
			'class' => function($data)
836
			{
837
				return $data['approved'] ? '' : 'approvebg';
838
			}
839
		),
840
		'columns' => array(
841
			'filename' => array(
842
				'header' => array(
843
					'value' => $txt['show_attach_filename'],
844
					'class' => 'lefttext',
845
					'style' => 'width: 25%;',
846
				),
847
				'data' => array(
848
					'sprintf' => array(
849
						'format' => '<a href="' . $scripturl . '?action=dlattach;topic=%1$d.0;attach=%2$d">%3$s</a>',
850
						'params' => array(
851
							'topic' => true,
852
							'id' => true,
853
							'filename' => false,
854
						),
855
					),
856
				),
857
				'sort' => array(
858
					'default' => 'a.filename',
859
					'reverse' => 'a.filename DESC',
860
				),
861
			),
862
			'downloads' => array(
863
				'header' => array(
864
					'value' => $txt['show_attach_downloads'],
865
					'style' => 'width: 12%;',
866
				),
867
				'data' => array(
868
					'db' => 'downloads',
869
					'comma_format' => true,
870
				),
871
				'sort' => array(
872
					'default' => 'a.downloads',
873
					'reverse' => 'a.downloads DESC',
874
				),
875
			),
876
			'subject' => array(
877
				'header' => array(
878
					'value' => $txt['message'],
879
					'class' => 'lefttext',
880
					'style' => 'width: 30%;',
881
				),
882
				'data' => array(
883
					'sprintf' => array(
884
						'format' => '<a href="' . $scripturl . '?msg=%1$d">%2$s</a>',
885
						'params' => array(
886
							'msg' => true,
887
							'subject' => false,
888
						),
889
					),
890
				),
891
				'sort' => array(
892
					'default' => 'm.subject',
893
					'reverse' => 'm.subject DESC',
894
				),
895
			),
896
			'posted' => array(
897
				'header' => array(
898
					'value' => $txt['show_attach_posted'],
899
					'class' => 'lefttext',
900
				),
901
				'data' => array(
902
					'db' => 'posted',
903
					'timeformat' => true,
904
				),
905
				'sort' => array(
906
					'default' => 'm.poster_time',
907
					'reverse' => 'm.poster_time DESC',
908
				),
909
			),
910
		),
911
	);
912
913
	// Create the request list.
914
	createList($listOptions);
915
}
916
917
/**
918
 * Get a list of attachments for this user. Callback for the list in showAttachments()
919
 *
920
 * @param int $start Which item to start with (for pagination purposes)
921
 * @param int $items_per_page How many items to show on each page
922
 * @param string $sort A string indicating how to sort the results
923
 * @param array $boardsAllowed An array containing the IDs of the boards they can see
924
 * @param int $memID The ID of the member
925
 * @return array An array of information about the attachments
926
 */
927
function list_getAttachments($start, $items_per_page, $sort, $boardsAllowed, $memID)
928
{
929
	global $smcFunc, $board, $modSettings, $context;
930
931
	// Retrieve some attachments.
932
	$request = $smcFunc['db_query']('', '
933
		SELECT a.id_attach, a.id_msg, a.filename, a.downloads, a.approved, m.id_msg, m.id_topic,
934
			m.id_board, m.poster_time, m.subject, b.name
935
		FROM {db_prefix}attachments AS a
936
			INNER JOIN {db_prefix}messages AS m ON (m.id_msg = a.id_msg)
937
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board AND {query_see_board})
938
		WHERE a.attachment_type = {int:attachment_type}
939
			AND a.id_msg != {int:no_message}
940
			AND m.id_member = {int:current_member}' . (!empty($board) ? '
941
			AND b.id_board = {int:board}' : '') . (!in_array(0, $boardsAllowed) ? '
942
			AND b.id_board IN ({array_int:boards_list})' : '') . (!$modSettings['postmod_active'] || $context['user']['is_owner'] ? '' : '
943
			AND m.approved = {int:is_approved}') . '
944
		ORDER BY {raw:sort}
945
		LIMIT {int:offset}, {int:limit}',
946
		array(
947
			'boards_list' => $boardsAllowed,
948
			'attachment_type' => 0,
949
			'no_message' => 0,
950
			'current_member' => $memID,
951
			'is_approved' => 1,
952
			'board' => $board,
953
			'sort' => $sort,
954
			'offset' => $start,
955
			'limit' => $items_per_page,
956
		)
957
	);
958
	$attachments = array();
959
	while ($row = $smcFunc['db_fetch_assoc']($request))
960
		$attachments[] = array(
961
			'id' => $row['id_attach'],
962
			'filename' => $row['filename'],
963
			'downloads' => $row['downloads'],
964
			'subject' => censorText($row['subject']),
965
			'posted' => $row['poster_time'],
966
			'msg' => $row['id_msg'],
967
			'topic' => $row['id_topic'],
968
			'board' => $row['id_board'],
969
			'board_name' => $row['name'],
970
			'approved' => $row['approved'],
971
		);
972
973
	$smcFunc['db_free_result']($request);
974
975
	return $attachments;
976
}
977
978
/**
979
 * Gets the total number of attachments for the user
980
 *
981
 * @param array $boardsAllowed An array of the IDs of the boards they can see
982
 * @param int $memID The ID of the member
983
 * @return int The number of attachments
984
 */
985
function list_getNumAttachments($boardsAllowed, $memID)
986
{
987
	global $board, $smcFunc, $modSettings, $context;
988
989
	// Get the total number of attachments they have posted.
990
	$request = $smcFunc['db_query']('', '
991
		SELECT COUNT(*)
992
		FROM {db_prefix}attachments AS a
993
			INNER JOIN {db_prefix}messages AS m ON (m.id_msg = a.id_msg)
994
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board AND {query_see_board})
995
		WHERE a.attachment_type = {int:attachment_type}
996
			AND a.id_msg != {int:no_message}
997
			AND m.id_member = {int:current_member}' . (!empty($board) ? '
998
			AND b.id_board = {int:board}' : '') . (!in_array(0, $boardsAllowed) ? '
999
			AND b.id_board IN ({array_int:boards_list})' : '') . (!$modSettings['postmod_active'] || $context['user']['is_owner'] ? '' : '
1000
			AND m.approved = {int:is_approved}'),
1001
		array(
1002
			'boards_list' => $boardsAllowed,
1003
			'attachment_type' => 0,
1004
			'no_message' => 0,
1005
			'current_member' => $memID,
1006
			'is_approved' => 1,
1007
			'board' => $board,
1008
		)
1009
	);
1010
	list ($attachCount) = $smcFunc['db_fetch_row']($request);
1011
	$smcFunc['db_free_result']($request);
1012
1013
	return $attachCount;
1014
}
1015
1016
/**
1017
 * Show all the unwatched topics.
1018
 *
1019
 * @param int $memID The ID of the member
1020
 */
1021
function showUnwatched($memID)
1022
{
1023
	global $txt, $user_info, $scripturl, $modSettings, $context, $sourcedir;
1024
1025
	// Only the owner can see the list (if the function is enabled of course)
1026
	if ($user_info['id'] != $memID)
1027
		return;
1028
1029
	require_once($sourcedir . '/Subs-List.php');
1030
1031
	// And here they are: the topics you don't like
1032
	$listOptions = array(
1033
		'id' => 'unwatched_topics',
1034
		'width' => '100%',
1035
		'items_per_page' => (empty($modSettings['disableCustomPerPage']) && !empty($options['topics_per_page'])) ? $options['topics_per_page'] : $modSettings['defaultMaxTopics'],
0 ignored issues
show
Bug introduced by
The variable $options seems to never exist, and therefore empty should always return true. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() on variables that are yet undefined. These calls will always produce the same result and can be removed.

This is most likely caused by the renaming of a variable or the removal of a function/method parameter.

Loading history...
1036
		'no_items_label' => $txt['unwatched_topics_none'],
1037
		'base_href' => $scripturl . '?action=profile;area=showposts;sa=unwatchedtopics;u=' . $memID,
1038
		'default_sort_col' => 'started_on',
1039
		'get_items' => array(
1040
			'function' => 'list_getUnwatched',
1041
			'params' => array(
1042
				$memID,
1043
			),
1044
		),
1045
		'get_count' => array(
1046
			'function' => 'list_getNumUnwatched',
1047
			'params' => array(
1048
				$memID,
1049
			),
1050
		),
1051
		'columns' => array(
1052
			'subject' => array(
1053
				'header' => array(
1054
					'value' => $txt['subject'],
1055
					'class' => 'lefttext',
1056
					'style' => 'width: 30%;',
1057
				),
1058
				'data' => array(
1059
					'sprintf' => array(
1060
						'format' => '<a href="' . $scripturl . '?topic=%1$d.0">%2$s</a>',
1061
						'params' => array(
1062
							'id_topic' => false,
1063
							'subject' => false,
1064
						),
1065
					),
1066
				),
1067
				'sort' => array(
1068
					'default' => 'm.subject',
1069
					'reverse' => 'm.subject DESC',
1070
				),
1071
			),
1072
			'started_by' => array(
1073
				'header' => array(
1074
					'value' => $txt['started_by'],
1075
					'style' => 'width: 15%;',
1076
				),
1077
				'data' => array(
1078
					'db' => 'started_by',
1079
				),
1080
				'sort' => array(
1081
					'default' => 'mem.real_name',
1082
					'reverse' => 'mem.real_name DESC',
1083
				),
1084
			),
1085
			'started_on' => array(
1086
				'header' => array(
1087
					'value' => $txt['on'],
1088
					'class' => 'lefttext',
1089
					'style' => 'width: 20%;',
1090
				),
1091
				'data' => array(
1092
					'db' => 'started_on',
1093
					'timeformat' => true,
1094
				),
1095
				'sort' => array(
1096
					'default' => 'm.poster_time',
1097
					'reverse' => 'm.poster_time DESC',
1098
				),
1099
			),
1100
			'last_post_by' => array(
1101
				'header' => array(
1102
					'value' => $txt['last_post'],
1103
					'style' => 'width: 15%;',
1104
				),
1105
				'data' => array(
1106
					'db' => 'last_post_by',
1107
				),
1108
				'sort' => array(
1109
					'default' => 'mem.real_name',
1110
					'reverse' => 'mem.real_name DESC',
1111
				),
1112
			),
1113
			'last_post_on' => array(
1114
				'header' => array(
1115
					'value' => $txt['on'],
1116
					'class' => 'lefttext',
1117
					'style' => 'width: 20%;',
1118
				),
1119
				'data' => array(
1120
					'db' => 'last_post_on',
1121
					'timeformat' => true,
1122
				),
1123
				'sort' => array(
1124
					'default' => 'm.poster_time',
1125
					'reverse' => 'm.poster_time DESC',
1126
				),
1127
			),
1128
		),
1129
	);
1130
1131
	// Create the request list.
1132
	createList($listOptions);
1133
1134
	$context['sub_template'] = 'show_list';
1135
	$context['default_list'] = 'unwatched_topics';
1136
}
1137
1138
/**
1139
 * Gets information about unwatched (disregarded) topics. Callback for the list in show_unwatched
1140
 *
1141
 * @param int $start The item to start with (for pagination purposes)
1142
 * @param int $items_per_page How many items to show on each page
1143
 * @param string $sort A string indicating how to sort the results
1144
 * @param int $memID The ID of the member
1145
 * @return array An array of information about the unwatched topics
1146
 */
1147
function list_getUnwatched($start, $items_per_page, $sort, $memID)
1148
{
1149
	global $smcFunc;
1150
1151
	// Get the list of topics we can see
1152
	$request = $smcFunc['db_query']('', '
1153
		SELECT lt.id_topic
1154
		FROM {db_prefix}log_topics as lt
1155
			LEFT JOIN {db_prefix}topics as t ON (lt.id_topic = t.id_topic)
1156
			LEFT JOIN {db_prefix}boards as b ON (t.id_board = b.id_board)
1157
			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')) ? '
1158
			LEFT JOIN {db_prefix}members as mem ON (m.id_member = mem.id_member)' : '') . '
1159
		WHERE lt.id_member = {int:current_member}
1160
			AND unwatched = 1
1161
			AND {query_see_board}
1162
		ORDER BY {raw:sort}
1163
		LIMIT {int:offset}, {int:limit}',
1164
		array(
1165
			'current_member' => $memID,
1166
			'sort' => $sort,
1167
			'offset' => $start,
1168
			'limit' => $items_per_page,
1169
		)
1170
	);
1171
1172
	$topics = array();
1173
	while ($row = $smcFunc['db_fetch_assoc']($request))
1174
		$topics[] = $row['id_topic'];
1175
1176
	$smcFunc['db_free_result']($request);
1177
1178
	// Any topics found?
1179
	$topicsInfo = array();
1180 View Code Duplication
	if (!empty($topics))
1181
	{
1182
		$request = $smcFunc['db_query']('', '
1183
			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
1184
			FROM {db_prefix}topics AS t
1185
				INNER JOIN {db_prefix}messages AS ml ON (ml.id_msg = t.id_last_msg)
1186
				INNER JOIN {db_prefix}messages AS mf ON (mf.id_msg = t.id_first_msg)
1187
				LEFT JOIN {db_prefix}members AS meml ON (meml.id_member = ml.id_member)
1188
				LEFT JOIN {db_prefix}members AS memf ON (memf.id_member = mf.id_member)
1189
			WHERE t.id_topic IN ({array_int:topics})',
1190
			array(
1191
				'topics' => $topics,
1192
			)
1193
		);
1194
		while ($row = $smcFunc['db_fetch_assoc']($request))
1195
			$topicsInfo[] = $row;
1196
		$smcFunc['db_free_result']($request);
1197
	}
1198
1199
	return $topicsInfo;
1200
}
1201
1202
/**
1203
 * Count the number of topics in the unwatched list
1204
 *
1205
 * @param int $memID The ID of the member
1206
 * @return int The number of unwatched topics
1207
 */
1208
function list_getNumUnwatched($memID)
1209
{
1210
	global $smcFunc;
1211
1212
	// Get the total number of attachments they have posted.
1213
	$request = $smcFunc['db_query']('', '
1214
		SELECT COUNT(*)
1215
		FROM {db_prefix}log_topics as lt
1216
		LEFT JOIN {db_prefix}topics as t ON (lt.id_topic = t.id_topic)
1217
		LEFT JOIN {db_prefix}boards as b ON (t.id_board = b.id_board)
1218
		WHERE id_member = {int:current_member}
1219
			AND unwatched = 1
1220
			AND {query_see_board}',
1221
		array(
1222
			'current_member' => $memID,
1223
		)
1224
	);
1225
	list ($unwatchedCount) = $smcFunc['db_fetch_row']($request);
1226
	$smcFunc['db_free_result']($request);
1227
1228
	return $unwatchedCount;
1229
}
1230
1231
/**
1232
 * Gets the user stats for display
1233
 *
1234
 * @param int $memID The ID of the member
1235
 */
1236
function statPanel($memID)
1237
{
1238
	global $txt, $scripturl, $context, $user_profile, $user_info, $modSettings, $smcFunc;
1239
1240
	$context['page_title'] = $txt['statPanel_showStats'] . ' ' . $user_profile[$memID]['real_name'];
1241
1242
	// Is the load average too high to allow searching just now?
1243 View Code Duplication
	if (!empty($context['load_average']) && !empty($modSettings['loadavg_userstats']) && $context['load_average'] >= $modSettings['loadavg_userstats'])
1244
		fatal_lang_error('loadavg_userstats_disabled', false);
1245
1246
	// General user statistics.
1247
	$timeDays = floor($user_profile[$memID]['total_time_logged_in'] / 86400);
1248
	$timeHours = floor(($user_profile[$memID]['total_time_logged_in'] % 86400) / 3600);
1249
	$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'];
1250
	$context['num_posts'] = comma_format($user_profile[$memID]['posts']);
1251
	// Menu tab
1252
	$context[$context['profile_menu_name']]['tab_data'] = array(
1253
		'title' => $txt['statPanel_generalStats'] . ' - ' . $context['member']['name'],
1254
		'icon' => 'stats_info_hd.png'
1255
	);
1256
1257
	// Number of topics started.
1258
	$result = $smcFunc['db_query']('', '
1259
		SELECT COUNT(*)
1260
		FROM {db_prefix}topics
1261
		WHERE id_member_started = {int:current_member}' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? '
1262
			AND id_board != {int:recycle_board}' : ''),
1263
		array(
1264
			'current_member' => $memID,
1265
			'recycle_board' => $modSettings['recycle_board'],
1266
		)
1267
	);
1268
	list ($context['num_topics']) = $smcFunc['db_fetch_row']($result);
1269
	$smcFunc['db_free_result']($result);
1270
1271
	// Number polls started.
1272
	$result = $smcFunc['db_query']('', '
1273
		SELECT COUNT(*)
1274
		FROM {db_prefix}topics
1275
		WHERE id_member_started = {int:current_member}' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? '
1276
			AND id_board != {int:recycle_board}' : '') . '
1277
			AND id_poll != {int:no_poll}',
1278
		array(
1279
			'current_member' => $memID,
1280
			'recycle_board' => $modSettings['recycle_board'],
1281
			'no_poll' => 0,
1282
		)
1283
	);
1284
	list ($context['num_polls']) = $smcFunc['db_fetch_row']($result);
1285
	$smcFunc['db_free_result']($result);
1286
1287
	// Number polls voted in.
1288
	$result = $smcFunc['db_query']('distinct_poll_votes', '
1289
		SELECT COUNT(DISTINCT id_poll)
1290
		FROM {db_prefix}log_polls
1291
		WHERE id_member = {int:current_member}',
1292
		array(
1293
			'current_member' => $memID,
1294
		)
1295
	);
1296
	list ($context['num_votes']) = $smcFunc['db_fetch_row']($result);
1297
	$smcFunc['db_free_result']($result);
1298
1299
	// Format the numbers...
1300
	$context['num_topics'] = comma_format($context['num_topics']);
1301
	$context['num_polls'] = comma_format($context['num_polls']);
1302
	$context['num_votes'] = comma_format($context['num_votes']);
1303
1304
	// Grab the board this member posted in most often.
1305
	$result = $smcFunc['db_query']('', '
1306
		SELECT
1307
			b.id_board, MAX(b.name) AS name, MAX(b.num_posts) AS num_posts, COUNT(*) AS message_count
1308
		FROM {db_prefix}messages AS m
1309
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)
1310
		WHERE m.id_member = {int:current_member}
1311
			AND b.count_posts = {int:count_enabled}
1312
			AND {query_see_board}
1313
		GROUP BY b.id_board
1314
		ORDER BY message_count DESC
1315
		LIMIT 10',
1316
		array(
1317
			'current_member' => $memID,
1318
			'count_enabled' => 0,
1319
		)
1320
	);
1321
	$context['popular_boards'] = array();
1322
	while ($row = $smcFunc['db_fetch_assoc']($result))
1323
	{
1324
		$context['popular_boards'][$row['id_board']] = array(
1325
			'id' => $row['id_board'],
1326
			'posts' => $row['message_count'],
1327
			'href' => $scripturl . '?board=' . $row['id_board'] . '.0',
1328
			'link' => '<a href="' . $scripturl . '?board=' . $row['id_board'] . '.0">' . $row['name'] . '</a>',
1329
			'posts_percent' => $user_profile[$memID]['posts'] == 0 ? 0 : ($row['message_count'] * 100) / $user_profile[$memID]['posts'],
1330
			'total_posts' => $row['num_posts'],
1331
			'total_posts_member' => $user_profile[$memID]['posts'],
1332
		);
1333
	}
1334
	$smcFunc['db_free_result']($result);
1335
1336
	// Now get the 10 boards this user has most often participated in.
1337
	$result = $smcFunc['db_query']('profile_board_stats', '
1338
		SELECT
1339
			b.id_board, MAX(b.name) AS name, b.num_posts, COUNT(*) AS message_count,
1340
			CASE WHEN COUNT(*) > MAX(b.num_posts) THEN 1 ELSE COUNT(*) / MAX(b.num_posts) END * 100 AS percentage
1341
		FROM {db_prefix}messages AS m
1342
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)
1343
		WHERE m.id_member = {int:current_member}
1344
			AND {query_see_board}
1345
		GROUP BY b.id_board, b.num_posts
1346
		ORDER BY percentage DESC
1347
		LIMIT 10',
1348
		array(
1349
			'current_member' => $memID,
1350
		)
1351
	);
1352
	$context['board_activity'] = array();
1353
	while ($row = $smcFunc['db_fetch_assoc']($result))
1354
	{
1355
		$context['board_activity'][$row['id_board']] = array(
1356
			'id' => $row['id_board'],
1357
			'posts' => $row['message_count'],
1358
			'href' => $scripturl . '?board=' . $row['id_board'] . '.0',
1359
			'link' => '<a href="' . $scripturl . '?board=' . $row['id_board'] . '.0">' . $row['name'] . '</a>',
1360
			'percent' => comma_format((float) $row['percentage'], 2),
1361
			'posts_percent' => (float) $row['percentage'],
1362
			'total_posts' => $row['num_posts'],
1363
		);
1364
	}
1365
	$smcFunc['db_free_result']($result);
1366
1367
	// Posting activity by time.
1368
	$result = $smcFunc['db_query']('user_activity_by_time', '
1369
		SELECT
1370
			HOUR(FROM_UNIXTIME(poster_time + {int:time_offset})) AS hour,
1371
			COUNT(*) AS post_count
1372
		FROM {db_prefix}messages
1373
		WHERE id_member = {int:current_member}' . ($modSettings['totalMessages'] > 100000 ? '
1374
			AND id_topic > {int:top_ten_thousand_topics}' : '') . '
1375
		GROUP BY hour',
1376
		array(
1377
			'current_member' => $memID,
1378
			'top_ten_thousand_topics' => $modSettings['totalTopics'] - 10000,
1379
			'time_offset' => (($user_info['time_offset'] + $modSettings['time_offset']) * 3600),
1380
		)
1381
	);
1382
	$maxPosts = $realPosts = 0;
1383
	$context['posts_by_time'] = array();
1384
	while ($row = $smcFunc['db_fetch_assoc']($result))
1385
	{
1386
		// Cast as an integer to remove the leading 0.
1387
		$row['hour'] = (int) $row['hour'];
1388
1389
		$maxPosts = max($row['post_count'], $maxPosts);
1390
		$realPosts += $row['post_count'];
1391
1392
		$context['posts_by_time'][$row['hour']] = array(
1393
			'hour' => $row['hour'],
1394
			'hour_format' => stripos($user_info['time_format'], '%p') === false ? $row['hour'] : date('g a', mktime($row['hour'])),
1395
			'posts' => $row['post_count'],
1396
			'posts_percent' => 0,
1397
			'is_last' => $row['hour'] == 23,
1398
		);
1399
	}
1400
	$smcFunc['db_free_result']($result);
1401
1402
	if ($maxPosts > 0)
1403
		for ($hour = 0; $hour < 24; $hour++)
1404
		{
1405
			if (!isset($context['posts_by_time'][$hour]))
1406
				$context['posts_by_time'][$hour] = array(
1407
					'hour' => $hour,
1408
					'hour_format' => stripos($user_info['time_format'], '%p') === false ? $hour : date('g a', mktime($hour)),
1409
					'posts' => 0,
1410
					'posts_percent' => 0,
1411
					'relative_percent' => 0,
1412
					'is_last' => $hour == 23,
1413
				);
1414
			else
1415
			{
1416
				$context['posts_by_time'][$hour]['posts_percent'] = round(($context['posts_by_time'][$hour]['posts'] * 100) / $realPosts);
1417
				$context['posts_by_time'][$hour]['relative_percent'] = round(($context['posts_by_time'][$hour]['posts'] * 100) / $maxPosts);
1418
			}
1419
		}
1420
1421
	// Put it in the right order.
1422
	ksort($context['posts_by_time']);
1423
1424
	// Custom stats (just add a template_layer to add it to the template!)
1425
 	call_integration_hook('integrate_profile_stats', array($memID));
1426
}
1427
1428
/**
1429
 * Loads up the information for the "track user" section of the profile
1430
 *
1431
 * @param int $memID The ID of the member
1432
 */
1433
function tracking($memID)
1434
{
1435
	global $context, $txt, $modSettings, $user_profile;
1436
1437
	$subActions = array(
1438
		'activity' => array('trackActivity', $txt['trackActivity'], 'moderate_forum'),
1439
		'ip' => array('TrackIP', $txt['trackIP'], 'moderate_forum'),
1440
		'edits' => array('trackEdits', $txt['trackEdits'], 'moderate_forum'),
1441
		'groupreq' => array('trackGroupReq', $txt['trackGroupRequests'], 'approve_group_requests'),
1442
		'logins' => array('TrackLogins', $txt['trackLogins'], 'moderate_forum'),
1443
	);
1444
1445
	foreach ($subActions as $sa => $action)
1446
	{
1447
		if (!allowedTo($action[2]))
1448
			unset($subActions[$sa]);
1449
	}
1450
1451
	// Create the tabs for the template.
1452
	$context[$context['profile_menu_name']]['tab_data'] = array(
1453
		'title' => $txt['tracking'],
1454
		'description' => $txt['tracking_description'],
1455
		'icon' => 'profile_hd.png',
1456
		'tabs' => array(
1457
			'activity' => array(),
1458
			'ip' => array(),
1459
			'edits' => array(),
1460
			'groupreq' => array(),
1461
			'logins' => array(),
1462
		),
1463
	);
1464
1465
	// Moderation must be on to track edits.
1466
	if (empty($modSettings['userlog_enabled']))
1467
		unset($context[$context['profile_menu_name']]['tab_data']['edits'], $subActions['edits']);
1468
1469
	// Group requests must be active to show it...
1470
	if (empty($modSettings['show_group_membership']))
1471
		unset($context[$context['profile_menu_name']]['tab_data']['groupreq'], $subActions['groupreq']);
1472
1473
	if (empty($subActions))
1474
		fatal_lang_error('no_access', false);
1475
1476
	$keys = array_keys($subActions);
1477
	$default = array_shift($keys);
1478
	$context['tracking_area'] = isset($_GET['sa']) && isset($subActions[$_GET['sa']]) ? $_GET['sa'] : $default;
1479
1480
	// Set a page title.
1481
	$context['page_title'] = $txt['trackUser'] . ' - ' . $subActions[$context['tracking_area']][1] . ' - ' . $user_profile[$memID]['real_name'];
1482
1483
	// Pass on to the actual function.
1484
	$context['sub_template'] = $subActions[$context['tracking_area']][0];
1485
	$call = call_helper($subActions[$context['tracking_area']][0], true);
1486
1487
	if (!empty($call))
1488
		call_user_func($call, $memID);
1489
}
1490
1491
/**
1492
 * Handles tracking a user's activity
1493
 *
1494
 * @param int $memID The ID of the member
1495
 */
1496
function trackActivity($memID)
1497
{
1498
	global $scripturl, $txt, $modSettings, $sourcedir;
1499
	global $user_profile, $context, $smcFunc;
1500
1501
	// Verify if the user has sufficient permissions.
1502
	isAllowedTo('moderate_forum');
1503
1504
	$context['last_ip'] = $user_profile[$memID]['member_ip'];
1505
	if ($context['last_ip'] != $user_profile[$memID]['member_ip2'])
1506
		$context['last_ip2'] = $user_profile[$memID]['member_ip2'];
1507
	$context['member']['name'] = $user_profile[$memID]['real_name'];
1508
1509
	// Set the options for the list component.
1510
	$listOptions = array(
1511
		'id' => 'track_user_list',
1512
		'title' => $txt['errors_by'] . ' ' . $context['member']['name'],
1513
		'items_per_page' => $modSettings['defaultMaxListItems'],
1514
		'no_items_label' => $txt['no_errors_from_user'],
1515
		'base_href' => $scripturl . '?action=profile;area=tracking;sa=user;u=' . $memID,
1516
		'default_sort_col' => 'date',
1517
		'get_items' => array(
1518
			'function' => 'list_getUserErrors',
1519
			'params' => array(
1520
				'le.id_member = {int:current_member}',
1521
				array('current_member' => $memID),
1522
			),
1523
		),
1524
		'get_count' => array(
1525
			'function' => 'list_getUserErrorCount',
1526
			'params' => array(
1527
				'id_member = {int:current_member}',
1528
				array('current_member' => $memID),
1529
			),
1530
		),
1531
		'columns' => array(
1532
			'ip_address' => array(
1533
				'header' => array(
1534
					'value' => $txt['ip_address'],
1535
				),
1536
				'data' => array(
1537
					'sprintf' => array(
1538
						'format' => '<a href="' . $scripturl . '?action=profile;area=tracking;sa=ip;searchip=%1$s;u=' . $memID . '">%1$s</a>',
1539
						'params' => array(
1540
							'ip' => false,
1541
						),
1542
					),
1543
				),
1544
				'sort' => array(
1545
					'default' => 'le.ip',
1546
					'reverse' => 'le.ip DESC',
1547
				),
1548
			),
1549
			'message' => array(
1550
				'header' => array(
1551
					'value' => $txt['message'],
1552
				),
1553
				'data' => array(
1554
					'sprintf' => array(
1555
						'format' => '%1$s<br><a href="%2$s">%2$s</a>',
1556
						'params' => array(
1557
							'message' => false,
1558
							'url' => false,
1559
						),
1560
					),
1561
				),
1562
			),
1563
			'date' => array(
1564
				'header' => array(
1565
					'value' => $txt['date'],
1566
				),
1567
				'data' => array(
1568
					'db' => 'time',
1569
				),
1570
				'sort' => array(
1571
					'default' => 'le.id_error DESC',
1572
					'reverse' => 'le.id_error',
1573
				),
1574
			),
1575
		),
1576
		'additional_rows' => array(
1577
			array(
1578
				'position' => 'after_title',
1579
				'value' => $txt['errors_desc'],
1580
			),
1581
		),
1582
	);
1583
1584
	// Create the list for viewing.
1585
	require_once($sourcedir . '/Subs-List.php');
1586
	createList($listOptions);
1587
1588
	// @todo cache this
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
1589
	// If this is a big forum, or a large posting user, let's limit the search.
1590
	if ($modSettings['totalMessages'] > 50000 && $user_profile[$memID]['posts'] > 500)
1591
	{
1592
		$request = $smcFunc['db_query']('', '
1593
			SELECT MAX(id_msg)
1594
			FROM {db_prefix}messages AS m
1595
			WHERE m.id_member = {int:current_member}',
1596
			array(
1597
				'current_member' => $memID,
1598
			)
1599
		);
1600
		list ($max_msg_member) = $smcFunc['db_fetch_row']($request);
1601
		$smcFunc['db_free_result']($request);
1602
1603
		// There's no point worrying ourselves with messages made yonks ago, just get recent ones!
1604
		$min_msg_member = max(0, $max_msg_member - $user_profile[$memID]['posts'] * 3);
1605
	}
1606
1607
	// Default to at least the ones we know about.
1608
	$ips = array(
1609
		$user_profile[$memID]['member_ip'],
1610
		$user_profile[$memID]['member_ip2'],
1611
	);
1612
1613
	// @todo cache this
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
1614
	// Get all IP addresses this user has used for his messages.
1615
	$request = $smcFunc['db_query']('', '
1616
		SELECT poster_ip
1617
		FROM {db_prefix}messages
1618
		WHERE id_member = {int:current_member}
1619
		' . (isset($min_msg_member) ? '
1620
			AND id_msg >= {int:min_msg_member} AND id_msg <= {int:max_msg_member}' : '') . '
1621
		GROUP BY poster_ip',
1622
		array(
1623
			'current_member' => $memID,
1624
			'min_msg_member' => !empty($min_msg_member) ? $min_msg_member : 0,
1625
			'max_msg_member' => !empty($max_msg_member) ? $max_msg_member : 0,
1626
		)
1627
	);
1628
	$context['ips'] = array();
1629 View Code Duplication
	while ($row = $smcFunc['db_fetch_assoc']($request))
1630
	{
1631
		$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>';
1632
		$ips[] = inet_dtop($row['poster_ip']);
1633
	}
1634
	$smcFunc['db_free_result']($request);
1635
1636
	// Now also get the IP addresses from the error messages.
1637
	$request = $smcFunc['db_query']('', '
1638
		SELECT COUNT(*) AS error_count, ip
1639
		FROM {db_prefix}log_errors
1640
		WHERE id_member = {int:current_member}
1641
		GROUP BY ip',
1642
		array(
1643
			'current_member' => $memID,
1644
		)
1645
	);
1646
	$context['error_ips'] = array();
1647 View Code Duplication
	while ($row = $smcFunc['db_fetch_assoc']($request))
1648
	{
1649
		$context['error_ips'][] = '<a href="' . $scripturl . '?action=profile;area=tracking;sa=ip;searchip=' . $row['ip'] . ';u=' . $memID . '">' . $row['ip'] . '</a>';
1650
		$ips[] = inet_dtop($row['ip']);
1651
	}
1652
	$smcFunc['db_free_result']($request);
1653
1654
	// Find other users that might use the same IP.
1655
	$ips = array_unique($ips);
1656
	$context['members_in_range'] = array();
1657
	if (!empty($ips))
1658
	{
1659
		// Get member ID's which are in messages...
1660
		$request = $smcFunc['db_query']('', '
1661
			SELECT mem.id_member
1662
			FROM {db_prefix}messages AS m
1663
				INNER JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
1664
			WHERE m.poster_ip IN ({array_inet:ip_list})
1665
				AND mem.id_member != {int:current_member}',
1666
			array(
1667
				'current_member' => $memID,
1668
				'ip_list' => $ips,
1669
			)
1670
		);
1671
		$message_members = array();
1672
		while ($row = $smcFunc['db_fetch_assoc']($request))
1673
			$message_members[] = $row['id_member'];
1674
		$smcFunc['db_free_result']($request);
1675
1676
		// Fetch their names, cause of the GROUP BY doesn't like giving us that normally.
1677
		if (!empty($message_members))
1678
		{
1679
			$request = $smcFunc['db_query']('', '
1680
				SELECT id_member, real_name
1681
				FROM {db_prefix}members
1682
				WHERE id_member IN ({array_int:message_members})',
1683
				array(
1684
					'message_members' => $message_members,
1685
					'ip_list' => $ips,
1686
				)
1687
			);
1688
			while ($row = $smcFunc['db_fetch_assoc']($request))
1689
				$context['members_in_range'][$row['id_member']] = '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['real_name'] . '</a>';
1690
			$smcFunc['db_free_result']($request);
1691
		}
1692
1693
		$request = $smcFunc['db_query']('', '
1694
			SELECT id_member, real_name
1695
			FROM {db_prefix}members
1696
			WHERE id_member != {int:current_member}
1697
				AND member_ip IN ({array_inet:ip_list})',
1698
			array(
1699
				'current_member' => $memID,
1700
				'ip_list' => $ips,
1701
			)
1702
		);
1703
		while ($row = $smcFunc['db_fetch_assoc']($request))
1704
			$context['members_in_range'][$row['id_member']] = '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['real_name'] . '</a>';
1705
		$smcFunc['db_free_result']($request);
1706
	}
1707
}
1708
1709
/**
1710
 * Get the number of user errors
1711
 *
1712
 * @param string $where A query to limit which errors are counted
1713
 * @param array $where_vars The parameters for $where
1714
 * @return int Number of user errors
1715
 */
1716 View Code Duplication
function list_getUserErrorCount($where, $where_vars = array())
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1717
{
1718
	global $smcFunc;
1719
1720
	$request = $smcFunc['db_query']('', '
1721
		SELECT COUNT(*) AS error_count
1722
		FROM {db_prefix}log_errors
1723
		WHERE ' . $where,
1724
		$where_vars
1725
	);
1726
	list ($count) = $smcFunc['db_fetch_row']($request);
1727
	$smcFunc['db_free_result']($request);
1728
1729
	return (int) $count;
1730
}
1731
1732
/**
1733
 * Gets all of the errors generated by a user's actions. Callback for the list in track_activity
1734
 *
1735
 * @param int $start Which item to start with (for pagination purposes)
1736
 * @param int $items_per_page How many items to show on each page
1737
 * @param string $sort A string indicating how to sort the results
1738
 * @param string $where A query indicating how to filter the results (eg 'id_member={int:id_member}')
1739
 * @param array $where_vars An array of parameters for $where
1740
 * @return array An array of information about the error messages
1741
 */
1742
function list_getUserErrors($start, $items_per_page, $sort, $where, $where_vars = array())
1743
{
1744
	global $smcFunc, $txt, $scripturl;
1745
1746
	// Get a list of error messages from this ip (range).
1747
	$request = $smcFunc['db_query']('', '
1748
		SELECT
1749
			le.log_time, le.ip, le.url, le.message, COALESCE(mem.id_member, 0) AS id_member,
1750
			COALESCE(mem.real_name, {string:guest_title}) AS display_name, mem.member_name
1751
		FROM {db_prefix}log_errors AS le
1752
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = le.id_member)
1753
		WHERE ' . $where . '
1754
		ORDER BY {raw:sort}
1755
		LIMIT {int:start}, {int:max}',
1756
		array_merge($where_vars, array(
1757
			'guest_title' => $txt['guest_title'],
1758
			'sort' => $sort,
1759
			'start' => $start,
1760
			'max' => $items_per_page,
1761
		))
1762
	);
1763
	$error_messages = array();
1764
	while ($row = $smcFunc['db_fetch_assoc']($request))
1765
		$error_messages[] = array(
1766
			'ip' => inet_dtop($row['ip']),
1767
			'member_link' => $row['id_member'] > 0 ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['display_name'] . '</a>' : $row['display_name'],
1768
			'message' => strtr($row['message'], array('&lt;span class=&quot;remove&quot;&gt;' => '', '&lt;/span&gt;' => '')),
1769
			'url' => $row['url'],
1770
			'time' => timeformat($row['log_time']),
1771
			'timestamp' => forum_time(true, $row['log_time']),
1772
		);
1773
	$smcFunc['db_free_result']($request);
1774
1775
	return $error_messages;
1776
}
1777
1778
/**
1779
 * Gets the number of posts made from a particular IP
1780
 *
1781
 * @param string $where A query indicating which posts to count
1782
 * @param array $where_vars The parameters for $where
1783
 * @return int Count of messages matching the IP
1784
 */
1785 View Code Duplication
function list_getIPMessageCount($where, $where_vars = array())
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1786
{
1787
	global $smcFunc;
1788
1789
	$request = $smcFunc['db_query']('', '
1790
		SELECT COUNT(*) AS message_count
1791
		FROM {db_prefix}messages AS m
1792
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)
1793
		WHERE {query_see_board} AND ' . $where,
1794
		$where_vars
1795
	);
1796
	list ($count) = $smcFunc['db_fetch_row']($request);
1797
	$smcFunc['db_free_result']($request);
1798
1799
	return (int) $count;
1800
}
1801
1802
/**
1803
 * Gets all the posts made from a particular IP
1804
 *
1805
 * @param int $start Which item to start with (for pagination purposes)
1806
 * @param int $items_per_page How many items to show on each page
1807
 * @param string $sort A string indicating how to sort the results
1808
 * @param string $where A query to filter which posts are returned
1809
 * @param array $where_vars An array of parameters for $where
1810
 * @return array An array containing information about the posts
1811
 */
1812
function list_getIPMessages($start, $items_per_page, $sort, $where, $where_vars = array())
1813
{
1814
	global $smcFunc, $scripturl;
1815
1816
	// Get all the messages fitting this where clause.
1817
	// @todo SLOW This query is using a filesort.
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
1818
	$request = $smcFunc['db_query']('', '
1819
		SELECT
1820
			m.id_msg, m.poster_ip, COALESCE(mem.real_name, m.poster_name) AS display_name, mem.id_member,
1821
			m.subject, m.poster_time, m.id_topic, m.id_board
1822
		FROM {db_prefix}messages AS m
1823
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)
1824
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
1825
		WHERE {query_see_board} AND ' . $where . '
1826
		ORDER BY {raw:sort}
1827
		LIMIT {int:start}, {int:max}',
1828
		array_merge($where_vars, array(
1829
			'sort' => $sort,
1830
			'start' => $start,
1831
			'max' => $items_per_page,
1832
		))
1833
	);
1834
	$messages = array();
1835
	while ($row = $smcFunc['db_fetch_assoc']($request))
1836
		$messages[] = array(
1837
			'ip' => inet_dtop($row['poster_ip']),
1838
			'member_link' => empty($row['id_member']) ? $row['display_name'] : '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['display_name'] . '</a>',
1839
			'board' => array(
1840
				'id' => $row['id_board'],
1841
				'href' => $scripturl . '?board=' . $row['id_board']
1842
			),
1843
			'topic' => $row['id_topic'],
1844
			'id' => $row['id_msg'],
1845
			'subject' => $row['subject'],
1846
			'time' => timeformat($row['poster_time']),
1847
			'timestamp' => forum_time(true, $row['poster_time'])
1848
		);
1849
	$smcFunc['db_free_result']($request);
1850
1851
	return $messages;
1852
}
1853
1854
/**
1855
 * Handles tracking a particular IP address
1856
 *
1857
 * @param int $memID The ID of a member whose IP we want to track
1858
 */
1859
function TrackIP($memID = 0)
1860
{
1861
	global $user_profile, $scripturl, $txt, $user_info, $modSettings, $sourcedir;
1862
	global $context, $smcFunc;
1863
1864
	// Can the user do this?
1865
	isAllowedTo('moderate_forum');
1866
1867
	if ($memID == 0)
1868
	{
1869
		$context['ip'] = $user_info['ip'];
1870
		loadTemplate('Profile');
1871
		loadLanguage('Profile');
1872
		$context['sub_template'] = 'trackIP';
1873
		$context['page_title'] = $txt['profile'];
1874
		$context['base_url'] = $scripturl . '?action=trackip';
1875
	}
1876
	else
1877
	{
1878
		$context['ip'] = $user_profile[$memID]['member_ip'];
1879
		$context['base_url'] = $scripturl . '?action=profile;area=tracking;sa=ip;u=' . $memID;
1880
	}
1881
1882
	// Searching?
1883
	if (isset($_REQUEST['searchip']))
1884
		$context['ip'] = trim($_REQUEST['searchip']);
1885
1886
	if (isValidIP($context['ip']) === false)
1887
		fatal_lang_error('invalid_tracking_ip', false);
1888
1889
	//mysql didn't support like search with varbinary
1890
	//$ip_var = str_replace('*', '%', $context['ip']);
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1891
	//$ip_string = strpos($ip_var, '%') === false ? '= {inet:ip_address}' : 'LIKE {string:ip_address}';
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1892
	$ip_var = $context['ip'];
1893
	$ip_string = '= {inet:ip_address}';
1894
1895
	if (empty($context['tracking_area']))
1896
		$context['page_title'] = $txt['trackIP'] . ' - ' . $context['ip'];
1897
1898
	$request = $smcFunc['db_query']('', '
1899
		SELECT id_member, real_name AS display_name, member_ip
1900
		FROM {db_prefix}members
1901
		WHERE member_ip ' . $ip_string,
1902
		array(
1903
			'ip_address' => $ip_var,
1904
		)
1905
	);
1906
	$context['ips'] = array();
1907
	while ($row = $smcFunc['db_fetch_assoc']($request))
1908
		$context['ips'][inet_dtop($row['member_ip'])][] = '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['display_name'] . '</a>';
1909
	$smcFunc['db_free_result']($request);
1910
1911
	ksort($context['ips']);
1912
1913
	// For messages we use the "messages per page" option
1914
	$maxPerPage = empty($modSettings['disableCustomPerPage']) && !empty($options['messages_per_page']) ? $options['messages_per_page'] : $modSettings['defaultMaxMessages'];
0 ignored issues
show
Bug introduced by
The variable $options seems to never exist, and therefore empty should always return true. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() on variables that are yet undefined. These calls will always produce the same result and can be removed.

This is most likely caused by the renaming of a variable or the removal of a function/method parameter.

Loading history...
1915
1916
	// Gonna want this for the list.
1917
	require_once($sourcedir . '/Subs-List.php');
1918
1919
	// Start with the user messages.
1920
	$listOptions = array(
1921
		'id' => 'track_message_list',
1922
		'title' => $txt['messages_from_ip'] . ' ' . $context['ip'],
1923
		'start_var_name' => 'messageStart',
1924
		'items_per_page' => $maxPerPage,
1925
		'no_items_label' => $txt['no_messages_from_ip'],
1926
		'base_href' => $context['base_url'] . ';searchip=' . $context['ip'],
1927
		'default_sort_col' => 'date',
1928
		'get_items' => array(
1929
			'function' => 'list_getIPMessages',
1930
			'params' => array(
1931
				'm.poster_ip ' . $ip_string,
1932
				array('ip_address' => $ip_var),
1933
			),
1934
		),
1935
		'get_count' => array(
1936
			'function' => 'list_getIPMessageCount',
1937
			'params' => array(
1938
				'm.poster_ip ' . $ip_string,
1939
				array('ip_address' => $ip_var),
1940
			),
1941
		),
1942
		'columns' => array(
1943
			'ip_address' => array(
1944
				'header' => array(
1945
					'value' => $txt['ip_address'],
1946
				),
1947
				'data' => array(
1948
					'sprintf' => array(
1949
						'format' => '<a href="' . $context['base_url'] . ';searchip=%1$s">%1$s</a>',
1950
						'params' => array(
1951
							'ip' => false,
1952
						),
1953
					),
1954
				),
1955
				'sort' => array(
1956
					'default' => 'm.poster_ip',
1957
					'reverse' => 'm.poster_ip DESC',
1958
				),
1959
			),
1960
			'poster' => array(
1961
				'header' => array(
1962
					'value' => $txt['poster'],
1963
				),
1964
				'data' => array(
1965
					'db' => 'member_link',
1966
				),
1967
			),
1968
			'subject' => array(
1969
				'header' => array(
1970
					'value' => $txt['subject'],
1971
				),
1972
				'data' => array(
1973
					'sprintf' => array(
1974
						'format' => '<a href="' . $scripturl . '?topic=%1$s.msg%2$s#msg%2$s" rel="nofollow">%3$s</a>',
1975
						'params' => array(
1976
							'topic' => false,
1977
							'id' => false,
1978
							'subject' => false,
1979
						),
1980
					),
1981
				),
1982
			),
1983
			'date' => array(
1984
				'header' => array(
1985
					'value' => $txt['date'],
1986
				),
1987
				'data' => array(
1988
					'db' => 'time',
1989
				),
1990
				'sort' => array(
1991
					'default' => 'm.id_msg DESC',
1992
					'reverse' => 'm.id_msg',
1993
				),
1994
			),
1995
		),
1996
		'additional_rows' => array(
1997
			array(
1998
				'position' => 'after_title',
1999
				'value' => $txt['messages_from_ip_desc'],
2000
			),
2001
		),
2002
	);
2003
2004
	// Create the messages list.
2005
	createList($listOptions);
2006
2007
	// Set the options for the error lists.
2008
	$listOptions = array(
2009
		'id' => 'track_user_list',
2010
		'title' => $txt['errors_from_ip'] . ' ' . $context['ip'],
2011
		'start_var_name' => 'errorStart',
2012
		'items_per_page' => $modSettings['defaultMaxListItems'],
2013
		'no_items_label' => $txt['no_errors_from_ip'],
2014
		'base_href' => $context['base_url'] . ';searchip=' . $context['ip'],
2015
		'default_sort_col' => 'date2',
2016
		'get_items' => array(
2017
			'function' => 'list_getUserErrors',
2018
			'params' => array(
2019
				'le.ip ' . $ip_string,
2020
				array('ip_address' => $ip_var),
2021
			),
2022
		),
2023
		'get_count' => array(
2024
			'function' => 'list_getUserErrorCount',
2025
			'params' => array(
2026
				'ip ' . $ip_string,
2027
				array('ip_address' => $ip_var),
2028
			),
2029
		),
2030
		'columns' => array(
2031
			'ip_address2' => array(
2032
				'header' => array(
2033
					'value' => $txt['ip_address'],
2034
				),
2035
				'data' => array(
2036
					'sprintf' => array(
2037
						'format' => '<a href="' . $context['base_url'] . ';searchip=%1$s">%1$s</a>',
2038
						'params' => array(
2039
							'ip' => false,
2040
						),
2041
					),
2042
				),
2043
				'sort' => array(
2044
					'default' => 'le.ip',
2045
					'reverse' => 'le.ip DESC',
2046
				),
2047
			),
2048
			'display_name' => array(
2049
				'header' => array(
2050
					'value' => $txt['display_name'],
2051
				),
2052
				'data' => array(
2053
					'db' => 'member_link',
2054
				),
2055
			),
2056
			'message' => array(
2057
				'header' => array(
2058
					'value' => $txt['message'],
2059
				),
2060
				'data' => array(
2061
					'sprintf' => array(
2062
						'format' => '%1$s<br><a href="%2$s">%2$s</a>',
2063
						'params' => array(
2064
							'message' => false,
2065
							'url' => false,
2066
						),
2067
					),
2068
				),
2069
			),
2070
			'date2' => array(
2071
				'header' => array(
2072
					'value' => $txt['date'],
2073
				),
2074
				'data' => array(
2075
					'db' => 'time',
2076
				),
2077
				'sort' => array(
2078
					'default' => 'le.id_error DESC',
2079
					'reverse' => 'le.id_error',
2080
				),
2081
			),
2082
		),
2083
		'additional_rows' => array(
2084
			array(
2085
				'position' => 'after_title',
2086
				'value' => $txt['errors_from_ip_desc'],
2087
			),
2088
		),
2089
	);
2090
2091
	// Create the error list.
2092
	createList($listOptions);
2093
2094
	// Allow 3rd party integrations to add in their own lists or whatever.
2095
	$context['additional_track_lists'] = array();
2096
	call_integration_hook('integrate_profile_trackip', array($ip_string, $ip_var));
2097
2098
	$context['single_ip'] = strpos($context['ip'], '*') === false;
2099
	if ($context['single_ip'])
2100
	{
2101
		$context['whois_servers'] = array(
2102
			'afrinic' => array(
2103
				'name' => $txt['whois_afrinic'],
2104
				'url' => 'https://www.afrinic.net/cgi-bin/whois?searchtext=' . $context['ip'],
2105
				'range' => array(41, 154, 196),
2106
			),
2107
			'apnic' => array(
2108
				'name' => $txt['whois_apnic'],
2109
				'url' => 'https://wq.apnic.net/apnic-bin/whois.pl?searchtext=' . $context['ip'],
2110
				'range' => array(58, 59, 60, 61, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124,
2111
					125, 126, 133, 150, 153, 163, 171, 202, 203, 210, 211, 218, 219, 220, 221, 222),
2112
			),
2113
			'arin' => array(
2114
				'name' => $txt['whois_arin'],
2115
				'url' => 'https://whois.arin.net/rest/ip/' . $context['ip'],
2116
				'range' => array(7, 24, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 96, 97, 98, 99,
2117
					128, 129, 130, 131, 132, 134, 135, 136, 137, 138, 139, 140, 142, 143, 144, 146, 147, 148, 149,
2118
					152, 155, 156, 157, 158, 159, 160, 161, 162, 164, 165, 166, 167, 168, 169, 170, 172, 173, 174,
2119
					192, 198, 199, 204, 205, 206, 207, 208, 209, 216),
2120
			),
2121
			'lacnic' => array(
2122
				'name' => $txt['whois_lacnic'],
2123
				'url' => 'https://lacnic.net/cgi-bin/lacnic/whois?query=' . $context['ip'],
2124
				'range' => array(186, 187, 189, 190, 191, 200, 201),
2125
			),
2126
			'ripe' => array(
2127
				'name' => $txt['whois_ripe'],
2128
				'url' => 'https://apps.db.ripe.net/search/query.html?searchtext=' . $context['ip'],
2129
				'range' => array(62, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
2130
					141, 145, 151, 188, 193, 194, 195, 212, 213, 217),
2131
			),
2132
		);
2133
2134
		foreach ($context['whois_servers'] as $whois)
2135
		{
2136
			// Strip off the "decimal point" and anything following...
2137
			if (in_array((int) $context['ip'], $whois['range']))
2138
				$context['auto_whois_server'] = $whois;
2139
		}
2140
	}
2141
}
2142
2143
/**
2144
 * Tracks a user's logins.
2145
 *
2146
 * @param int $memID The ID of the member
2147
 */
2148
function TrackLogins($memID = 0)
2149
{
2150
	global $scripturl, $txt, $sourcedir, $context;
2151
2152
	// Gonna want this for the list.
2153
	require_once($sourcedir . '/Subs-List.php');
2154
2155
	if ($memID == 0)
2156
		$context['base_url'] = $scripturl . '?action=trackip';
2157
	else
2158
		$context['base_url'] = $scripturl . '?action=profile;area=tracking;sa=ip;u=' . $memID;
2159
2160
	// Start with the user messages.
2161
	$listOptions = array(
2162
		'id' => 'track_logins_list',
2163
		'title' => $txt['trackLogins'],
2164
		'no_items_label' => $txt['trackLogins_none_found'],
2165
		'base_href' => $context['base_url'],
2166
		'get_items' => array(
2167
			'function' => 'list_getLogins',
2168
			'params' => array(
2169
				'id_member = {int:current_member}',
2170
				array('current_member' => $memID),
2171
			),
2172
		),
2173
		'get_count' => array(
2174
			'function' => 'list_getLoginCount',
2175
			'params' => array(
2176
				'id_member = {int:current_member}',
2177
				array('current_member' => $memID),
2178
			),
2179
		),
2180
		'columns' => array(
2181
			'time' => array(
2182
				'header' => array(
2183
					'value' => $txt['date'],
2184
				),
2185
				'data' => array(
2186
					'db' => 'time',
2187
				),
2188
			),
2189
			'ip' => array(
2190
				'header' => array(
2191
					'value' => $txt['ip_address'],
2192
				),
2193
				'data' => array(
2194
					'sprintf' => array(
2195
						'format' => '<a href="' . $context['base_url'] . ';searchip=%1$s">%1$s</a> (<a href="' . $context['base_url'] . ';searchip=%2$s">%2$s</a>) ',
2196
						'params' => array(
2197
							'ip' => false,
2198
							'ip2' => false
2199
						),
2200
					),
2201
				),
2202
			),
2203
		),
2204
		'additional_rows' => array(
2205
			array(
2206
				'position' => 'after_title',
2207
				'value' => $txt['trackLogins_desc'],
2208
			),
2209
		),
2210
	);
2211
2212
	// Create the messages list.
2213
	createList($listOptions);
2214
2215
	$context['sub_template'] = 'show_list';
2216
	$context['default_list'] = 'track_logins_list';
2217
}
2218
2219
/**
2220
 * Finds the total number of tracked logins for a particular user
2221
 *
2222
 * @param string $where A query to limit which logins are counted
2223
 * @param array $where_vars An array of parameters for $where
2224
 * @return int count of messages matching the IP
2225
 */
2226 View Code Duplication
function list_getLoginCount($where, $where_vars = array())
0 ignored issues
show
Unused Code introduced by
The parameter $where is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2227
{
2228
	global $smcFunc;
2229
2230
	$request = $smcFunc['db_query']('', '
2231
		SELECT COUNT(*) AS message_count
2232
		FROM {db_prefix}member_logins
2233
		WHERE id_member = {int:id_member}',
2234
		array(
2235
			'id_member' => $where_vars['current_member'],
2236
		)
2237
	);
2238
	list ($count) = $smcFunc['db_fetch_row']($request);
2239
	$smcFunc['db_free_result']($request);
2240
2241
	return (int) $count;
2242
}
2243
2244
/**
2245
 * Callback for the list in trackLogins.
2246
 *
2247
 * @param int $start Which item to start with (not used here)
2248
 * @param int $items_per_page How many items to show on each page (not used here)
2249
 * @param string $sort A string indicating
2250
 * @param string $where A query to filter results (not used here)
2251
 * @param array $where_vars An array of parameters for $where. Only 'current_member' (the ID of the member) is used here
2252
 * @return array An array of information about user logins
2253
 */
2254
function list_getLogins($start, $items_per_page, $sort, $where, $where_vars = array())
0 ignored issues
show
Unused Code introduced by
The parameter $start is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $items_per_page is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $sort is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $where is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
2255
{
2256
	global $smcFunc;
2257
2258
	$request = $smcFunc['db_query']('', '
2259
		SELECT time, ip, ip2
2260
		FROM {db_prefix}member_logins
2261
		WHERE id_member = {int:id_member}
2262
		ORDER BY time DESC',
2263
		array(
2264
			'id_member' => $where_vars['current_member'],
2265
		)
2266
	);
2267
	$logins = array();
2268
	while ($row = $smcFunc['db_fetch_assoc']($request))
2269
		$logins[] = array(
2270
			'time' => timeformat($row['time']),
2271
			'ip' => inet_dtop($row['ip']),
2272
			'ip2' => inet_dtop($row['ip2']),
2273
		);
2274
	$smcFunc['db_free_result']($request);
2275
2276
	return $logins;
2277
}
2278
2279
/**
2280
 * Tracks a user's profile edits
2281
 *
2282
 * @param int $memID The ID of the member
2283
 */
2284
function trackEdits($memID)
2285
{
2286
	global $scripturl, $txt, $modSettings, $sourcedir, $context, $smcFunc;
2287
2288
	require_once($sourcedir . '/Subs-List.php');
2289
2290
	// Get the names of any custom fields.
2291
	$request = $smcFunc['db_query']('', '
2292
		SELECT col_name, field_name, bbc
2293
		FROM {db_prefix}custom_fields',
2294
		array(
2295
		)
2296
	);
2297
	$context['custom_field_titles'] = array();
2298
	while ($row = $smcFunc['db_fetch_assoc']($request))
2299
		$context['custom_field_titles']['customfield_' . $row['col_name']] = array(
2300
			'title' => $row['field_name'],
2301
			'parse_bbc' => $row['bbc'],
2302
		);
2303
	$smcFunc['db_free_result']($request);
2304
2305
	// Set the options for the error lists.
2306
	$listOptions = array(
2307
		'id' => 'edit_list',
2308
		'title' => $txt['trackEdits'],
2309
		'items_per_page' => $modSettings['defaultMaxListItems'],
2310
		'no_items_label' => $txt['trackEdit_no_edits'],
2311
		'base_href' => $scripturl . '?action=profile;area=tracking;sa=edits;u=' . $memID,
2312
		'default_sort_col' => 'time',
2313
		'get_items' => array(
2314
			'function' => 'list_getProfileEdits',
2315
			'params' => array(
2316
				$memID,
2317
			),
2318
		),
2319
		'get_count' => array(
2320
			'function' => 'list_getProfileEditCount',
2321
			'params' => array(
2322
				$memID,
2323
			),
2324
		),
2325
		'columns' => array(
2326
			'action' => array(
2327
				'header' => array(
2328
					'value' => $txt['trackEdit_action'],
2329
				),
2330
				'data' => array(
2331
					'db' => 'action_text',
2332
				),
2333
			),
2334
			'before' => array(
2335
				'header' => array(
2336
					'value' => $txt['trackEdit_before'],
2337
				),
2338
				'data' => array(
2339
					'db' => 'before',
2340
				),
2341
			),
2342
			'after' => array(
2343
				'header' => array(
2344
					'value' => $txt['trackEdit_after'],
2345
				),
2346
				'data' => array(
2347
					'db' => 'after',
2348
				),
2349
			),
2350
			'time' => array(
2351
				'header' => array(
2352
					'value' => $txt['date'],
2353
				),
2354
				'data' => array(
2355
					'db' => 'time',
2356
				),
2357
				'sort' => array(
2358
					'default' => 'id_action DESC',
2359
					'reverse' => 'id_action',
2360
				),
2361
			),
2362
			'applicator' => array(
2363
				'header' => array(
2364
					'value' => $txt['trackEdit_applicator'],
2365
				),
2366
				'data' => array(
2367
					'db' => 'member_link',
2368
				),
2369
			),
2370
		),
2371
	);
2372
2373
	// Create the error list.
2374
	createList($listOptions);
2375
2376
	$context['sub_template'] = 'show_list';
2377
	$context['default_list'] = 'edit_list';
2378
}
2379
2380
/**
2381
 * How many edits?
2382
 *
2383
 * @param int $memID The ID of the member
2384
 * @return int The number of profile edits
2385
 */
2386 View Code Duplication
function list_getProfileEditCount($memID)
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2387
{
2388
	global $smcFunc;
2389
2390
	$request = $smcFunc['db_query']('', '
2391
		SELECT COUNT(*) AS edit_count
2392
		FROM {db_prefix}log_actions
2393
		WHERE id_log = {int:log_type}
2394
			AND id_member = {int:owner}',
2395
		array(
2396
			'log_type' => 2,
2397
			'owner' => $memID,
2398
		)
2399
	);
2400
	list ($edit_count) = $smcFunc['db_fetch_row']($request);
2401
	$smcFunc['db_free_result']($request);
2402
2403
	return (int) $edit_count;
2404
}
2405
2406
/**
2407
 * Loads up information about a user's profile edits. Callback for the list in trackEdits()
2408
 *
2409
 * @param int $start Which item to start with (for pagination purposes)
2410
 * @param int $items_per_page How many items to show on each page
2411
 * @param string $sort A string indicating how to sort the results
2412
 * @param int $memID The ID of the member
2413
 * @return array An array of information about the profile edits
2414
 */
2415
function list_getProfileEdits($start, $items_per_page, $sort, $memID)
2416
{
2417
	global $smcFunc, $txt, $scripturl, $context;
2418
2419
	// Get a list of error messages from this ip (range).
2420
	$request = $smcFunc['db_query']('', '
2421
		SELECT
2422
			id_action, id_member, ip, log_time, action, extra
2423
		FROM {db_prefix}log_actions
2424
		WHERE id_log = {int:log_type}
2425
			AND id_member = {int:owner}
2426
		ORDER BY {raw:sort}
2427
		LIMIT {int:start}, {int:max}',
2428
		array(
2429
			'log_type' => 2,
2430
			'owner' => $memID,
2431
			'sort' => $sort,
2432
			'start' => $start,
2433
			'max' => $items_per_page,
2434
		)
2435
	);
2436
	$edits = array();
2437
	$members = array();
2438
	while ($row = $smcFunc['db_fetch_assoc']($request))
2439
	{
2440
		$extra = $smcFunc['json_decode']($row['extra'], true);
2441
		if (!empty($extra['applicator']))
2442
			$members[] = $extra['applicator'];
2443
2444
		// Work out what the name of the action is.
2445
		if (isset($txt['trackEdit_action_' . $row['action']]))
2446
			$action_text = $txt['trackEdit_action_' . $row['action']];
2447
		elseif (isset($txt[$row['action']]))
2448
			$action_text = $txt[$row['action']];
2449
		// Custom field?
2450
		elseif (isset($context['custom_field_titles'][$row['action']]))
2451
			$action_text = $context['custom_field_titles'][$row['action']]['title'];
2452
		else
2453
			$action_text = $row['action'];
2454
2455
		// Parse BBC?
2456
		$parse_bbc = isset($context['custom_field_titles'][$row['action']]) && $context['custom_field_titles'][$row['action']]['parse_bbc'] ? true : false;
2457
2458
		$edits[] = array(
2459
			'id' => $row['id_action'],
2460
			'ip' => inet_dtop($row['ip']),
2461
			'id_member' => !empty($extra['applicator']) ? $extra['applicator'] : 0,
2462
			'member_link' => $txt['trackEdit_deleted_member'],
2463
			'action' => $row['action'],
2464
			'action_text' => $action_text,
2465
			'before' => !empty($extra['previous']) ? ($parse_bbc ? parse_bbc($extra['previous']) : $extra['previous']) : '',
2466
			'after' => !empty($extra['new']) ? ($parse_bbc ? parse_bbc($extra['new']) : $extra['new']) : '',
2467
			'time' => timeformat($row['log_time']),
2468
		);
2469
	}
2470
	$smcFunc['db_free_result']($request);
2471
2472
	// Get any member names.
2473
	if (!empty($members))
2474
	{
2475
		$request = $smcFunc['db_query']('', '
2476
			SELECT
2477
				id_member, real_name
2478
			FROM {db_prefix}members
2479
			WHERE id_member IN ({array_int:members})',
2480
			array(
2481
				'members' => $members,
2482
			)
2483
		);
2484
		$members = array();
2485
		while ($row = $smcFunc['db_fetch_assoc']($request))
2486
			$members[$row['id_member']] = $row['real_name'];
2487
		$smcFunc['db_free_result']($request);
2488
2489
		foreach ($edits as $key => $value)
2490
			if (isset($members[$value['id_member']]))
2491
				$edits[$key]['member_link'] = '<a href="' . $scripturl . '?action=profile;u=' . $value['id_member'] . '">' . $members[$value['id_member']] . '</a>';
2492
	}
2493
2494
	return $edits;
2495
}
2496
2497
/**
2498
 * Display the history of group requests made by the user whose profile we are viewing.
2499
 *
2500
 * @param int $memID The ID of the member
2501
 */
2502
function trackGroupReq($memID)
2503
{
2504
	global $scripturl, $txt, $modSettings, $sourcedir, $context;
2505
2506
	require_once($sourcedir . '/Subs-List.php');
2507
2508
	// Set the options for the error lists.
2509
	$listOptions = array(
2510
		'id' => 'request_list',
2511
		'title' => sprintf($txt['trackGroupRequests_title'], $context['member']['name']),
2512
		'items_per_page' => $modSettings['defaultMaxListItems'],
2513
		'no_items_label' => $txt['requested_none'],
2514
		'base_href' => $scripturl . '?action=profile;area=tracking;sa=groupreq;u=' . $memID,
2515
		'default_sort_col' => 'time_applied',
2516
		'get_items' => array(
2517
			'function' => 'list_getGroupRequests',
2518
			'params' => array(
2519
				$memID,
2520
			),
2521
		),
2522
		'get_count' => array(
2523
			'function' => 'list_getGroupRequestsCount',
2524
			'params' => array(
2525
				$memID,
2526
			),
2527
		),
2528
		'columns' => array(
2529
			'group' => array(
2530
				'header' => array(
2531
					'value' => $txt['requested_group'],
2532
				),
2533
				'data' => array(
2534
					'db' => 'group_name',
2535
				),
2536
			),
2537
			'group_reason' => array(
2538
				'header' => array(
2539
					'value' => $txt['requested_group_reason'],
2540
				),
2541
				'data' => array(
2542
					'db' => 'group_reason',
2543
				),
2544
			),
2545
			'time_applied' => array(
2546
				'header' => array(
2547
					'value' => $txt['requested_group_time'],
2548
				),
2549
				'data' => array(
2550
					'db' => 'time_applied',
2551
					'timeformat' => true,
2552
				),
2553
				'sort' => array(
2554
					'default' => 'time_applied DESC',
2555
					'reverse' => 'time_applied',
2556
				),
2557
			),
2558
			'outcome' => array(
2559
				'header' => array(
2560
					'value' => $txt['requested_group_outcome'],
2561
				),
2562
				'data' => array(
2563
					'db' => 'outcome',
2564
				),
2565
			),
2566
		),
2567
	);
2568
2569
	// Create the error list.
2570
	createList($listOptions);
2571
2572
	$context['sub_template'] = 'show_list';
2573
	$context['default_list'] = 'request_list';
2574
}
2575
2576
/**
2577
 * How many edits?
2578
 *
2579
 * @param int $memID The ID of the member
2580
 * @return int The number of profile edits
2581
 */
2582
function list_getGroupRequestsCount($memID)
2583
{
2584
	global $smcFunc, $user_info;
2585
2586
	$request = $smcFunc['db_query']('', '
2587
		SELECT COUNT(*) AS req_count
2588
		FROM {db_prefix}log_group_requests AS lgr
2589
		WHERE id_member = {int:memID}
2590
			AND ' . ($user_info['mod_cache']['gq'] == '1=1' ? $user_info['mod_cache']['gq'] : 'lgr.' . $user_info['mod_cache']['gq']),
2591
		array(
2592
			'memID' => $memID,
2593
		)
2594
	);
2595
	list ($report_count) = $smcFunc['db_fetch_row']($request);
2596
	$smcFunc['db_free_result']($request);
2597
2598
	return (int) $report_count;
2599
}
2600
2601
/**
2602
 * Loads up information about a user's group requests. Callback for the list in trackGroupReq()
2603
 *
2604
 * @param int $start Which item to start with (for pagination purposes)
2605
 * @param int $items_per_page How many items to show on each page
2606
 * @param string $sort A string indicating how to sort the results
2607
 * @param int $memID The ID of the member
2608
 * @return array An array of information about the user's group requests
2609
 */
2610
function list_getGroupRequests($start, $items_per_page, $sort, $memID)
0 ignored issues
show
Best Practice introduced by
The function list_getGroupRequests() has been defined more than once; this definition is ignored, only the first definition in Sources/Groups.php (L723-775) is considered.

This check looks for functions that have already been defined in other files.

Some Codebases, like WordPress, make a practice of defining functions multiple times. This may lead to problems with the detection of function parameters and types. If you really need to do this, you can mark the duplicate definition with the @ignore annotation.

/**
 * @ignore
 */
function getUser() {

}

function getUser($id, $realm) {

}

See also the PhpDoc documentation for @ignore.

Loading history...
2611
{
2612
	global $smcFunc, $txt, $scripturl, $user_info;
2613
2614
	$groupreq = array();
2615
2616
	$request = $smcFunc['db_query']('', '
2617
		SELECT
2618
			lgr.id_group, mg.group_name, mg.online_color, lgr.time_applied, lgr.reason, lgr.status,
2619
			ma.id_member AS id_member_acted, COALESCE(ma.member_name, lgr.member_name_acted) AS act_name, lgr.time_acted, lgr.act_reason
2620
		FROM {db_prefix}log_group_requests AS lgr
2621
			LEFT JOIN {db_prefix}members AS ma ON (lgr.id_member_acted = ma.id_member)
2622
			INNER JOIN {db_prefix}membergroups AS mg ON (lgr.id_group = mg.id_group)
2623
		WHERE lgr.id_member = {int:memID}
2624
			AND ' . ($user_info['mod_cache']['gq'] == '1=1' ? $user_info['mod_cache']['gq'] : 'lgr.' . $user_info['mod_cache']['gq']) . '
2625
		ORDER BY {raw:sort}
2626
		LIMIT {int:start}, {int:max}',
2627
		array(
2628
			'memID' => $memID,
2629
			'sort' => $sort,
2630
			'start' => $start,
2631
			'max' => $items_per_page,
2632
		)
2633
	);
2634
	while ($row = $smcFunc['db_fetch_assoc']($request))
2635
	{
2636
		$this_req = array(
2637
			'group_name' => empty($row['online_color']) ? $row['group_name'] : '<span style="color:' . $row['online_color'] . '">' . $row['group_name'] . '</span>',
2638
			'group_reason' => $row['reason'],
2639
			'time_applied' => $row['time_applied'],
2640
		);
2641
		switch ($row['status'])
2642
		{
2643
			case 0:
2644
				$this_req['outcome'] = $txt['outcome_pending'];
2645
				break;
2646
			case 1:
2647
				$member_link = empty($row['id_member_acted']) ? $row['act_name'] : '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member_acted'] . '">' . $row['act_name'] . '</a>';
2648
				$this_req['outcome'] = sprintf($txt['outcome_approved'], $member_link, timeformat($row['time_acted']));
2649
				break;
2650
			case 2:
2651
				$member_link = empty($row['id_member_acted']) ? $row['act_name'] : '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member_acted'] . '">' . $row['act_name'] . '</a>';
2652
				$this_req['outcome'] = sprintf(!empty($row['act_reason']) ? $txt['outcome_refused_reason'] : $txt['outcome_refused'], $member_link, timeformat($row['time_acted']), $row['act_reason']);
2653
				break;
2654
		}
2655
2656
		$groupreq[] = $this_req;
2657
	}
2658
	$smcFunc['db_free_result']($request);
2659
2660
	return $groupreq;
2661
}
2662
2663
/**
2664
 * Shows which permissions a user has
2665
 *
2666
 * @param int $memID The ID of the member
2667
 */
2668
function showPermissions($memID)
2669
{
2670
	global $txt, $board;
2671
	global $user_profile, $context, $sourcedir, $smcFunc;
2672
2673
	// Verify if the user has sufficient permissions.
2674
	isAllowedTo('manage_permissions');
2675
2676
	loadLanguage('ManagePermissions');
2677
	loadLanguage('Admin');
2678
	loadTemplate('ManageMembers');
2679
2680
	// Load all the permission profiles.
2681
	require_once($sourcedir . '/ManagePermissions.php');
2682
	loadPermissionProfiles();
2683
2684
	$context['member']['id'] = $memID;
2685
	$context['member']['name'] = $user_profile[$memID]['real_name'];
2686
2687
	$context['page_title'] = $txt['showPermissions'];
2688
	$board = empty($board) ? 0 : (int) $board;
2689
	$context['board'] = $board;
2690
2691
	// Determine which groups this user is in.
2692
	if (empty($user_profile[$memID]['additional_groups']))
2693
		$curGroups = array();
2694
	else
2695
		$curGroups = explode(',', $user_profile[$memID]['additional_groups']);
2696
	$curGroups[] = $user_profile[$memID]['id_group'];
2697
	$curGroups[] = $user_profile[$memID]['id_post_group'];
2698
2699
	// Load a list of boards for the jump box - except the defaults.
2700
	$request = $smcFunc['db_query']('order_by_board_order', '
2701
		SELECT b.id_board, b.name, b.id_profile, b.member_groups, COALESCE(mods.id_member, modgs.id_group, 0) AS is_mod
2702
		FROM {db_prefix}boards AS b
2703
			LEFT JOIN {db_prefix}moderators AS mods ON (mods.id_board = b.id_board AND mods.id_member = {int:current_member})
2704
			LEFT JOIN {db_prefix}moderator_groups AS modgs ON (modgs.id_board = b.id_board AND modgs.id_group IN ({array_int:current_groups}))
2705
		WHERE {query_see_board}',
2706
		array(
2707
			'current_member' => $memID,
2708
			'current_groups' => $curGroups,
2709
		)
2710
	);
2711
	$context['boards'] = array();
2712
	$context['no_access_boards'] = array();
2713
	while ($row = $smcFunc['db_fetch_assoc']($request))
2714
	{
2715
		if (count(array_intersect($curGroups, explode(',', $row['member_groups']))) === 0 && !$row['is_mod'])
2716
			$context['no_access_boards'][] = array(
2717
				'id' => $row['id_board'],
2718
				'name' => $row['name'],
2719
				'is_last' => false,
2720
			);
2721
		elseif ($row['id_profile'] != 1 || $row['is_mod'])
2722
			$context['boards'][$row['id_board']] = array(
2723
				'id' => $row['id_board'],
2724
				'name' => $row['name'],
2725
				'selected' => $board == $row['id_board'],
2726
				'profile' => $row['id_profile'],
2727
				'profile_name' => $context['profiles'][$row['id_profile']]['name'],
2728
			);
2729
	}
2730
	$smcFunc['db_free_result']($request);
2731
2732
	require_once($sourcedir . '/Subs-Boards.php');
2733
	sortBoards($context['boards']);
2734
2735 View Code Duplication
	if (!empty($context['no_access_boards']))
2736
		$context['no_access_boards'][count($context['no_access_boards']) - 1]['is_last'] = true;
2737
2738
	$context['member']['permissions'] = array(
2739
		'general' => array(),
2740
		'board' => array()
2741
	);
2742
2743
	// If you're an admin we know you can do everything, we might as well leave.
2744
	$context['member']['has_all_permissions'] = in_array(1, $curGroups);
2745
	if ($context['member']['has_all_permissions'])
2746
		return;
2747
2748
	$denied = array();
2749
2750
	// Get all general permissions.
2751
	$result = $smcFunc['db_query']('', '
2752
		SELECT p.permission, p.add_deny, mg.group_name, p.id_group
2753
		FROM {db_prefix}permissions AS p
2754
			LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = p.id_group)
2755
		WHERE p.id_group IN ({array_int:group_list})
2756
		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',
2757
		array(
2758
			'group_list' => $curGroups,
2759
			'newbie_group' => 4,
2760
		)
2761
	);
2762
	while ($row = $smcFunc['db_fetch_assoc']($result))
2763
	{
2764
		// We don't know about this permission, it doesn't exist :P.
2765
		if (!isset($txt['permissionname_' . $row['permission']]))
2766
			continue;
2767
2768
		if (empty($row['add_deny']))
2769
			$denied[] = $row['permission'];
2770
2771
		// Permissions that end with _own or _any consist of two parts.
2772 View Code Duplication
		if (in_array(substr($row['permission'], -4), array('_own', '_any')) && isset($txt['permissionname_' . substr($row['permission'], 0, -4)]))
2773
			$name = $txt['permissionname_' . substr($row['permission'], 0, -4)] . ' - ' . $txt['permissionname_' . $row['permission']];
2774
		else
2775
			$name = $txt['permissionname_' . $row['permission']];
2776
2777
		// Add this permission if it doesn't exist yet.
2778 View Code Duplication
		if (!isset($context['member']['permissions']['general'][$row['permission']]))
2779
			$context['member']['permissions']['general'][$row['permission']] = array(
2780
				'id' => $row['permission'],
2781
				'groups' => array(
2782
					'allowed' => array(),
2783
					'denied' => array()
2784
				),
2785
				'name' => $name,
2786
				'is_denied' => false,
2787
				'is_global' => true,
2788
			);
2789
2790
		// Add the membergroup to either the denied or the allowed groups.
2791
		$context['member']['permissions']['general'][$row['permission']]['groups'][empty($row['add_deny']) ? 'denied' : 'allowed'][] = $row['id_group'] == 0 ? $txt['membergroups_members'] : $row['group_name'];
2792
2793
		// Once denied is always denied.
2794
		$context['member']['permissions']['general'][$row['permission']]['is_denied'] |= empty($row['add_deny']);
2795
	}
2796
	$smcFunc['db_free_result']($result);
2797
2798
	$request = $smcFunc['db_query']('', '
2799
		SELECT
2800
			bp.add_deny, bp.permission, bp.id_group, mg.group_name' . (empty($board) ? '' : ',
2801
			b.id_profile, CASE WHEN (mods.id_member IS NULL AND modgs.id_group IS NULL) THEN 0 ELSE 1 END AS is_moderator') . '
2802
		FROM {db_prefix}board_permissions AS bp' . (empty($board) ? '' : '
2803
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = {int:current_board})
2804
			LEFT JOIN {db_prefix}moderators AS mods ON (mods.id_board = b.id_board AND mods.id_member = {int:current_member})
2805
			LEFT JOIN {db_prefix}moderator_groups AS modgs ON (modgs.id_board = b.id_board AND modgs.id_group IN ({array_int:group_list}))') . '
2806
			LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = bp.id_group)
2807
		WHERE bp.id_profile = {raw:current_profile}
2808
			AND bp.id_group IN ({array_int:group_list}' . (empty($board) ? ')' : ', {int:moderator_group})
2809
			AND (mods.id_member IS NOT NULL OR modgs.id_group IS NOT NULL OR bp.id_group != {int:moderator_group})'),
2810
		array(
2811
			'current_board' => $board,
2812
			'group_list' => $curGroups,
2813
			'current_member' => $memID,
2814
			'current_profile' => empty($board) ? '1' : 'b.id_profile',
2815
			'moderator_group' => 3,
2816
		)
2817
	);
2818
2819
	while ($row = $smcFunc['db_fetch_assoc']($request))
2820
	{
2821
		// We don't know about this permission, it doesn't exist :P.
2822
		if (!isset($txt['permissionname_' . $row['permission']]))
2823
			continue;
2824
2825
		// The name of the permission using the format 'permission name' - 'own/any topic/event/etc.'.
2826 View Code Duplication
		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
		// Create the structure for this permission.
2832 View Code Duplication
		if (!isset($context['member']['permissions']['board'][$row['permission']]))
2833
			$context['member']['permissions']['board'][$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' => empty($board),
2842
			);
2843
2844
		$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'];
2845
2846
		$context['member']['permissions']['board'][$row['permission']]['is_denied'] |= empty($row['add_deny']);
2847
	}
2848
	$smcFunc['db_free_result']($request);
2849
}
2850
2851
/**
2852
 * View a member's warnings
2853
 *
2854
 * @param int $memID The ID of the member
2855
 */
2856
function viewWarning($memID)
2857
{
2858
	global $modSettings, $context, $sourcedir, $txt, $scripturl;
2859
2860
	// Firstly, can we actually even be here?
2861
	if (!($context['user']['is_owner'] && allowedTo('view_warning_own')) && !allowedTo('view_warning_any') && !allowedTo('issue_warning') && !allowedTo('moderate_forum'))
2862
		fatal_lang_error('no_access', false);
2863
2864
	// Make sure things which are disabled stay disabled.
2865
	$modSettings['warning_watch'] = !empty($modSettings['warning_watch']) ? $modSettings['warning_watch'] : 110;
2866
	$modSettings['warning_moderate'] = !empty($modSettings['warning_moderate']) && !empty($modSettings['postmod_active']) ? $modSettings['warning_moderate'] : 110;
2867
	$modSettings['warning_mute'] = !empty($modSettings['warning_mute']) ? $modSettings['warning_mute'] : 110;
2868
2869
	// Let's use a generic list to get all the current warnings, and use the issue warnings grab-a-granny thing.
2870
	require_once($sourcedir . '/Subs-List.php');
2871
	require_once($sourcedir . '/Profile-Actions.php');
2872
2873
	$listOptions = array(
2874
		'id' => 'view_warnings',
2875
		'title' => $txt['profile_viewwarning_previous_warnings'],
2876
		'items_per_page' => $modSettings['defaultMaxListItems'],
2877
		'no_items_label' => $txt['profile_viewwarning_no_warnings'],
2878
		'base_href' => $scripturl . '?action=profile;area=viewwarning;sa=user;u=' . $memID,
2879
		'default_sort_col' => 'log_time',
2880
		'get_items' => array(
2881
			'function' => 'list_getUserWarnings',
2882
			'params' => array(
2883
				$memID,
2884
			),
2885
		),
2886
		'get_count' => array(
2887
			'function' => 'list_getUserWarningCount',
2888
			'params' => array(
2889
				$memID,
2890
			),
2891
		),
2892
		'columns' => array(
2893
			'log_time' => array(
2894
				'header' => array(
2895
					'value' => $txt['profile_warning_previous_time'],
2896
				),
2897
				'data' => array(
2898
					'db' => 'time',
2899
				),
2900
				'sort' => array(
2901
					'default' => 'lc.log_time DESC',
2902
					'reverse' => 'lc.log_time',
2903
				),
2904
			),
2905
			'reason' => array(
2906
				'header' => array(
2907
					'value' => $txt['profile_warning_previous_reason'],
2908
					'style' => 'width: 50%;',
2909
				),
2910
				'data' => array(
2911
					'db' => 'reason',
2912
				),
2913
			),
2914
			'level' => array(
2915
				'header' => array(
2916
					'value' => $txt['profile_warning_previous_level'],
2917
				),
2918
				'data' => array(
2919
					'db' => 'counter',
2920
				),
2921
				'sort' => array(
2922
					'default' => 'lc.counter DESC',
2923
					'reverse' => 'lc.counter',
2924
				),
2925
			),
2926
		),
2927
		'additional_rows' => array(
2928
			array(
2929
				'position' => 'after_title',
2930
				'value' => $txt['profile_viewwarning_desc'],
2931
				'class' => 'smalltext',
2932
				'style' => 'padding: 2ex;',
2933
			),
2934
		),
2935
	);
2936
2937
	// Create the list for viewing.
2938
	require_once($sourcedir . '/Subs-List.php');
2939
	createList($listOptions);
2940
2941
	// Create some common text bits for the template.
2942
	$context['level_effects'] = array(
2943
		0 => '',
2944
		$modSettings['warning_watch'] => $txt['profile_warning_effect_own_watched'],
2945
		$modSettings['warning_moderate'] => $txt['profile_warning_effect_own_moderated'],
2946
		$modSettings['warning_mute'] => $txt['profile_warning_effect_own_muted'],
2947
	);
2948
	$context['current_level'] = 0;
2949 View Code Duplication
	foreach ($context['level_effects'] as $limit => $dummy)
2950
		if ($context['member']['warning'] >= $limit)
2951
			$context['current_level'] = $limit;
2952
}
2953
2954
?>