Issues (1061)

Sources/Who.php (1 issue)

Labels
Severity
1
<?php
2
3
/**
4
 * This file is mainly concerned with the Who's Online list.
5
 * Although, it also handles credits. :P
6
 *
7
 * Simple Machines Forum (SMF)
8
 *
9
 * @package SMF
10
 * @author Simple Machines https://www.simplemachines.org
11
 * @copyright 2020 Simple Machines and individual contributors
12
 * @license https://www.simplemachines.org/about/smf/license.php BSD
13
 *
14
 * @version 2.1 RC2
15
 */
16
17
if (!defined('SMF'))
18
	die('No direct access...');
19
20
/**
21
 * Who's online, and what are they doing?
22
 * This function prepares the who's online data for the Who template.
23
 * It requires the who_view permission.
24
 * It is enabled with the who_enabled setting.
25
 * It is accessed via ?action=who.
26
 *
27
 * Uses Who template, main sub-template
28
 * Uses Who language file.
29
 */
30
function Who()
31
{
32
	global $context, $scripturl, $txt, $modSettings, $memberContext, $smcFunc;
33
34
	// Permissions, permissions, permissions.
35
	isAllowedTo('who_view');
36
37
	// You can't do anything if this is off.
38
	if (empty($modSettings['who_enabled']))
39
		fatal_lang_error('who_off', false);
40
41
	// Load the 'Who' template.
42
	loadTemplate('Who');
43
	loadLanguage('Who');
44
45
	// Sort out... the column sorting.
46
	$sort_methods = array(
47
		'user' => 'mem.real_name',
48
		'time' => 'lo.log_time'
49
	);
50
51
	$show_methods = array(
52
		'members' => '(lo.id_member != 0)',
53
		'guests' => '(lo.id_member = 0)',
54
		'all' => '1=1',
55
	);
56
57
	// Store the sort methods and the show types for use in the template.
58
	$context['sort_methods'] = array(
59
		'user' => $txt['who_user'],
60
		'time' => $txt['who_time'],
61
	);
62
	$context['show_methods'] = array(
63
		'all' => $txt['who_show_all'],
64
		'members' => $txt['who_show_members_only'],
65
		'guests' => $txt['who_show_guests_only'],
66
	);
67
68
	// Can they see spiders too?
69
	if (!empty($modSettings['show_spider_online']) && ($modSettings['show_spider_online'] == 2 || allowedTo('admin_forum')) && !empty($modSettings['spider_name_cache']))
70
	{
71
		$show_methods['spiders'] = '(lo.id_member = 0 AND lo.id_spider > 0)';
72
		$show_methods['guests'] = '(lo.id_member = 0 AND lo.id_spider = 0)';
73
		$context['show_methods']['spiders'] = $txt['who_show_spiders_only'];
74
	}
75
	elseif (empty($modSettings['show_spider_online']) && isset($_SESSION['who_online_filter']) && $_SESSION['who_online_filter'] == 'spiders')
76
		unset($_SESSION['who_online_filter']);
77
78
	// Does the user prefer a different sort direction?
79
	if (isset($_REQUEST['sort']) && isset($sort_methods[$_REQUEST['sort']]))
80
	{
81
		$context['sort_by'] = $_SESSION['who_online_sort_by'] = $_REQUEST['sort'];
82
		$sort_method = $sort_methods[$_REQUEST['sort']];
83
	}
84
	// Did we set a preferred sort order earlier in the session?
85
	elseif (isset($_SESSION['who_online_sort_by']))
86
	{
87
		$context['sort_by'] = $_SESSION['who_online_sort_by'];
88
		$sort_method = $sort_methods[$_SESSION['who_online_sort_by']];
89
	}
90
	// Default to last time online.
91
	else
92
	{
93
		$context['sort_by'] = $_SESSION['who_online_sort_by'] = 'time';
94
		$sort_method = 'lo.log_time';
95
	}
96
97
	$context['sort_direction'] = isset($_REQUEST['asc']) || (isset($_REQUEST['sort_dir']) && $_REQUEST['sort_dir'] == 'asc') ? 'up' : 'down';
98
99
	$conditions = array();
100
	if (!allowedTo('moderate_forum'))
101
		$conditions[] = '(COALESCE(mem.show_online, 1) = 1)';
102
103
	// Fallback to top filter?
104
	if (isset($_REQUEST['submit_top']) && isset($_REQUEST['show_top']))
105
		$_REQUEST['show'] = $_REQUEST['show_top'];
106
	// Does the user wish to apply a filter?
107
	if (isset($_REQUEST['show']) && isset($show_methods[$_REQUEST['show']]))
108
		$context['show_by'] = $_SESSION['who_online_filter'] = $_REQUEST['show'];
109
	// Perhaps we saved a filter earlier in the session?
110
	elseif (isset($_SESSION['who_online_filter']))
111
		$context['show_by'] = $_SESSION['who_online_filter'];
112
	else
113
		$context['show_by'] = 'members';
114
115
	$conditions[] = $show_methods[$context['show_by']];
116
117
	// Get the total amount of members online.
118
	$request = $smcFunc['db_query']('', '
119
		SELECT COUNT(*)
120
		FROM {db_prefix}log_online AS lo
121
			LEFT JOIN {db_prefix}members AS mem ON (lo.id_member = mem.id_member)' . (!empty($conditions) ? '
122
		WHERE ' . implode(' AND ', $conditions) : ''),
123
		array(
124
		)
125
	);
126
	list ($totalMembers) = $smcFunc['db_fetch_row']($request);
127
	$smcFunc['db_free_result']($request);
128
129
	// Prepare some page index variables.
130
	$context['page_index'] = constructPageIndex($scripturl . '?action=who;sort=' . $context['sort_by'] . ($context['sort_direction'] == 'up' ? ';asc' : '') . ';show=' . $context['show_by'], $_REQUEST['start'], $totalMembers, $modSettings['defaultMaxMembers']);
131
	$context['start'] = $_REQUEST['start'];
132
133
	// Look for people online, provided they don't mind if you see they are.
134
	$request = $smcFunc['db_query']('', '
135
		SELECT
136
			lo.log_time, lo.id_member, lo.url, lo.ip AS ip, mem.real_name,
137
			lo.session, mg.online_color, COALESCE(mem.show_online, 1) AS show_online,
138
			lo.id_spider
139
		FROM {db_prefix}log_online AS lo
140
			LEFT JOIN {db_prefix}members AS mem ON (lo.id_member = mem.id_member)
141
			LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = CASE WHEN mem.id_group = {int:regular_member} THEN mem.id_post_group ELSE mem.id_group END)' . (!empty($conditions) ? '
142
		WHERE ' . implode(' AND ', $conditions) : '') . '
143
		ORDER BY {raw:sort_method} {raw:sort_direction}
144
		LIMIT {int:offset}, {int:limit}',
145
		array(
146
			'regular_member' => 0,
147
			'sort_method' => $sort_method,
148
			'sort_direction' => $context['sort_direction'] == 'up' ? 'ASC' : 'DESC',
149
			'offset' => $context['start'],
150
			'limit' => $modSettings['defaultMaxMembers'],
151
		)
152
	);
153
	$context['members'] = array();
154
	$member_ids = array();
155
	$url_data = array();
156
	while ($row = $smcFunc['db_fetch_assoc']($request))
157
	{
158
		$actions = $smcFunc['json_decode']($row['url'], true);
159
		if ($actions === false)
160
			continue;
161
162
		// Send the information to the template.
163
		$context['members'][$row['session']] = array(
164
			'id' => $row['id_member'],
165
			'ip' => allowedTo('moderate_forum') ? inet_dtop($row['ip']) : '',
166
			// It is *going* to be today or yesterday, so why keep that information in there?
167
			'time' => strtr(timeformat($row['log_time']), array($txt['today'] => '', $txt['yesterday'] => '')),
168
			'timestamp' => forum_time(true, $row['log_time']),
169
			'query' => $actions,
170
			'is_hidden' => $row['show_online'] == 0,
171
			'id_spider' => $row['id_spider'],
172
			'color' => empty($row['online_color']) ? '' : $row['online_color']
173
		);
174
175
		$url_data[$row['session']] = array($row['url'], $row['id_member']);
176
		$member_ids[] = $row['id_member'];
177
	}
178
	$smcFunc['db_free_result']($request);
179
180
	// Load the user data for these members.
181
	loadMemberData($member_ids);
182
183
	// Load up the guest user.
184
	$memberContext[0] = array(
185
		'id' => 0,
186
		'name' => $txt['guest_title'],
187
		'group' => $txt['guest_title'],
188
		'href' => '',
189
		'link' => $txt['guest_title'],
190
		'email' => $txt['guest_title'],
191
		'is_guest' => true
192
	);
193
194
	// Are we showing spiders?
195
	$spiderContext = array();
196
	if (!empty($modSettings['show_spider_online']) && ($modSettings['show_spider_online'] == 2 || allowedTo('admin_forum')) && !empty($modSettings['spider_name_cache']))
197
	{
198
		foreach ($smcFunc['json_decode']($modSettings['spider_name_cache'], true) as $id => $name)
199
			$spiderContext[$id] = array(
200
				'id' => 0,
201
				'name' => $name,
202
				'group' => $txt['spiders'],
203
				'href' => '',
204
				'link' => $name,
205
				'email' => $name,
206
				'is_guest' => true
207
			);
208
	}
209
210
	$url_data = determineActions($url_data);
211
212
	// Setup the linktree and page title (do it down here because the language files are now loaded..)
213
	$context['page_title'] = $txt['who_title'];
214
	$context['linktree'][] = array(
215
		'url' => $scripturl . '?action=who',
216
		'name' => $txt['who_title']
217
	);
218
219
	// Put it in the context variables.
220
	foreach ($context['members'] as $i => $member)
221
	{
222
		if ($member['id'] != 0)
223
			$member['id'] = loadMemberContext($member['id']) ? $member['id'] : 0;
224
225
		// Keep the IP that came from the database.
226
		$memberContext[$member['id']]['ip'] = $member['ip'];
227
		$context['members'][$i]['action'] = isset($url_data[$i]) ? $url_data[$i] : array('label' => 'who_hidden', 'class' => 'em');
228
		if ($member['id'] == 0 && isset($spiderContext[$member['id_spider']]))
229
			$context['members'][$i] += $spiderContext[$member['id_spider']];
230
		else
231
			$context['members'][$i] += $memberContext[$member['id']];
232
	}
233
234
	// Some people can't send personal messages...
235
	$context['can_send_pm'] = allowedTo('pm_send');
236
	$context['can_send_email'] = allowedTo('send_email_to_members');
237
238
	// any profile fields disabled?
239
	$context['disabled_fields'] = isset($modSettings['disabled_profile_fields']) ? array_flip(explode(',', $modSettings['disabled_profile_fields'])) : array();
240
241
}
242
243
/**
244
 * This function determines the actions of the members passed in urls.
245
 *
246
 * Adding actions to the Who's Online list:
247
 * Adding actions to this list is actually relatively easy...
248
 *  - for actions anyone should be able to see, just add a string named whoall_ACTION.
249
 *    (where ACTION is the action used in index.php.)
250
 *  - for actions that have a subaction which should be represented differently, use whoall_ACTION_SUBACTION.
251
 *  - for actions that include a topic, and should be restricted, use whotopic_ACTION.
252
 *  - for actions that use a message, by msg or quote, use whopost_ACTION.
253
 *  - for administrator-only actions, use whoadmin_ACTION.
254
 *  - for actions that should be viewable only with certain permissions,
255
 *    use whoallow_ACTION and add a list of possible permissions to the
256
 *    $allowedActions array, using ACTION as the key.
257
 *
258
 * @param mixed $urls a single url (string) or an array of arrays, each inner array being (JSON-encoded request data, id_member)
259
 * @param string|bool $preferred_prefix = false
260
 * @return array, an array of descriptions if you passed an array, otherwise the string describing their current location.
261
 */
262
function determineActions($urls, $preferred_prefix = false)
263
{
264
	global $txt, $user_info, $modSettings, $smcFunc;
265
266
	if (!allowedTo('who_view'))
267
		return array();
268
	loadLanguage('Who');
269
270
	// Actions that require a specific permission level.
271
	$allowedActions = array(
272
		'admin' => array('moderate_forum', 'manage_membergroups', 'manage_bans', 'admin_forum', 'manage_permissions', 'send_mail', 'manage_attachments', 'manage_smileys', 'manage_boards', 'edit_news'),
273
		'ban' => array('manage_bans'),
274
		'boardrecount' => array('admin_forum'),
275
		'calendar' => array('calendar_view'),
276
		'corefeatures' => array('admin_forum'),
277
		'editnews' => array('edit_news'),
278
		'featuresettings' => array('admin_forum'),
279
		'languages' => array('admin_forum'),
280
		'logs' => array('admin_forum'),
281
		'mailing' => array('send_mail'),
282
		'mailqueue' => array('admin_forum'),
283
		'maintain' => array('admin_forum'),
284
		'manageattachments' => array('manage_attachments'),
285
		'manageboards' => array('manage_boards'),
286
		'managecalendar' => array('admin_forum'),
287
		'managesearch' => array('admin_forum'),
288
		'managesmileys' => array('manage_smileys'),
289
		'membergroups' => array('manage_membergroups'),
290
		'mlist' => array('view_mlist'),
291
		'moderate' => array('access_mod_center', 'moderate_forum', 'manage_membergroups'),
292
		'modsettings' => array('admin_forum'),
293
		'news' => array('edit_news', 'send_mail', 'admin_forum'),
294
		'optimizetables' => array('admin_forum'),
295
		'packages' => array('admin_forum'),
296
		'paidsubscribe' => array('admin_forum'),
297
		'permissions' => array('manage_permissions'),
298
		'postsettings' => array('admin_forum'),
299
		'regcenter' => array('admin_forum', 'moderate_forum'),
300
		'repairboards' => array('admin_forum'),
301
		'reports' => array('admin_forum'),
302
		'scheduledtasks' => array('admin_forum'),
303
		'search' => array('search_posts'),
304
		'search2' => array('search_posts'),
305
		'securitysettings' => array('admin_forum'),
306
		'sengines' => array('admin_forum'),
307
		'serversettings' => array('admin_forum'),
308
		'setcensor' => array('moderate_forum'),
309
		'setreserve' => array('moderate_forum'),
310
		'stats' => array('view_stats'),
311
		'theme' => array('admin_forum'),
312
		'viewerrorlog' => array('admin_forum'),
313
		'viewmembers' => array('moderate_forum'),
314
	);
315
	call_integration_hook('who_allowed', array(&$allowedActions));
316
317
	if (!is_array($urls))
318
		$url_list = array(array($urls, $user_info['id']));
319
	else
320
		$url_list = $urls;
321
322
	// These are done to later query these in large chunks. (instead of one by one.)
323
	$topic_ids = array();
324
	$profile_ids = array();
325
	$board_ids = array();
326
327
	$data = array();
328
	foreach ($url_list as $k => $url)
329
	{
330
		// Get the request parameters..
331
		$actions = $smcFunc['json_decode']($url[0], true);
332
		if ($actions === false)
333
			continue;
334
335
		// If it's the admin or moderation center, and there is an area set, use that instead.
336
		if (isset($actions['action']) && ($actions['action'] == 'admin' || $actions['action'] == 'moderate') && isset($actions['area']))
337
			$actions['action'] = $actions['area'];
338
339
		// Check if there was no action or the action is display.
340
		if (!isset($actions['action']) || $actions['action'] == 'display')
341
		{
342
			// It's a topic!  Must be!
343
			if (isset($actions['topic']))
344
			{
345
				// Assume they can't view it, and queue it up for later.
346
				$data[$k] = array('label' => 'who_hidden', 'class' => 'em');
347
				$topic_ids[(int) $actions['topic']][$k] = $txt['who_topic'];
348
			}
349
			// It's a board!
350
			elseif (isset($actions['board']))
351
			{
352
				// Hide first, show later.
353
				$data[$k] = array('label' => 'who_hidden', 'class' => 'em');
354
				$board_ids[$actions['board']][$k] = $txt['who_board'];
355
			}
356
			// It's the board index!!  It must be!
357
			else
358
				$data[$k] = $txt['who_index'];
359
		}
360
		// Probably an error or some goon?
361
		elseif ($actions['action'] == '')
362
			$data[$k] = $txt['who_index'];
363
		// Some other normal action...?
364
		else
365
		{
366
			// Viewing/editing a profile.
367
			if ($actions['action'] == 'profile')
368
			{
369
				// Whose?  Their own?
370
				if (empty($actions['u']))
371
					$actions['u'] = $url[1];
372
373
				$data[$k] = array('label' => 'who_hidden', 'class' => 'em');
374
				$profile_ids[(int) $actions['u']][$k] = $actions['u'] == $url[1] ? $txt['who_viewownprofile'] : $txt['who_viewprofile'];
375
			}
376
			elseif (($actions['action'] == 'post' || $actions['action'] == 'post2') && empty($actions['topic']) && isset($actions['board']))
377
			{
378
				$data[$k] = array('label' => 'who_hidden', 'class' => 'em');
379
				$board_ids[(int) $actions['board']][$k] = isset($actions['poll']) ? $txt['who_poll'] : $txt['who_post'];
380
			}
381
			// A subaction anyone can view... if the language string is there, show it.
382
			elseif (isset($actions['sa']) && isset($txt['whoall_' . $actions['action'] . '_' . $actions['sa']]))
383
				$data[$k] = $preferred_prefix && isset($txt[$preferred_prefix . $actions['action'] . '_' . $actions['sa']]) ? $txt[$preferred_prefix . $actions['action'] . '_' . $actions['sa']] : $txt['whoall_' . $actions['action'] . '_' . $actions['sa']];
0 ignored issues
show
Are you sure $preferred_prefix of type string|true can be used in concatenation? ( Ignorable by Annotation )

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

383
				$data[$k] = $preferred_prefix && isset($txt[/** @scrutinizer ignore-type */ $preferred_prefix . $actions['action'] . '_' . $actions['sa']]) ? $txt[$preferred_prefix . $actions['action'] . '_' . $actions['sa']] : $txt['whoall_' . $actions['action'] . '_' . $actions['sa']];
Loading history...
384
			// An action any old fellow can look at. (if ['whoall_' . $action] exists, we know everyone can see it.)
385
			elseif (isset($txt['whoall_' . $actions['action']]))
386
				$data[$k] = $preferred_prefix && isset($txt[$preferred_prefix . $actions['action']]) ? $txt[$preferred_prefix . $actions['action']] : $txt['whoall_' . $actions['action']];
387
			// Viewable if and only if they can see the board...
388
			elseif (isset($txt['whotopic_' . $actions['action']]))
389
			{
390
				// Find out what topic they are accessing.
391
				$topic = (int) (isset($actions['topic']) ? $actions['topic'] : (isset($actions['from']) ? $actions['from'] : 0));
392
393
				$data[$k] = array('label' => 'who_hidden', 'class' => 'em');
394
				$topic_ids[$topic][$k] = $txt['whotopic_' . $actions['action']];
395
			}
396
			elseif (isset($txt['whopost_' . $actions['action']]))
397
			{
398
				// Find out what message they are accessing.
399
				$msgid = (int) (isset($actions['msg']) ? $actions['msg'] : (isset($actions['quote']) ? $actions['quote'] : 0));
400
401
				$result = $smcFunc['db_query']('', '
402
					SELECT m.id_topic, m.subject
403
					FROM {db_prefix}messages AS m
404
						' . ($modSettings['postmod_active'] ? 'INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic AND t.approved = {int:is_approved})' : '') . '
405
					WHERE m.id_msg = {int:id_msg}
406
						AND {query_see_message_board}' . ($modSettings['postmod_active'] ? '
407
						AND m.approved = {int:is_approved}' : '') . '
408
					LIMIT 1',
409
					array(
410
						'is_approved' => 1,
411
						'id_msg' => $msgid,
412
					)
413
				);
414
				list ($id_topic, $subject) = $smcFunc['db_fetch_row']($result);
415
				$data[$k] = sprintf($txt['whopost_' . $actions['action']], $id_topic, $subject);
416
				$smcFunc['db_free_result']($result);
417
418
				if (empty($id_topic))
419
					$data[$k] = array('label' => 'who_hidden', 'class' => 'em');
420
			}
421
			// Viewable only by administrators.. (if it starts with whoadmin, it's admin only!)
422
			elseif (allowedTo('moderate_forum') && isset($txt['whoadmin_' . $actions['action']]))
423
				$data[$k] = $txt['whoadmin_' . $actions['action']];
424
			// Viewable by permission level.
425
			elseif (isset($allowedActions[$actions['action']]))
426
			{
427
				if (allowedTo($allowedActions[$actions['action']]) && !empty($txt['whoallow_' . $actions['action']]))
428
					$data[$k] = $txt['whoallow_' . $actions['action']];
429
				elseif (in_array('moderate_forum', $allowedActions[$actions['action']]))
430
					$data[$k] = $txt['who_moderate'];
431
				elseif (in_array('admin_forum', $allowedActions[$actions['action']]))
432
					$data[$k] = $txt['who_admin'];
433
				else
434
					$data[$k] = array('label' => 'who_hidden', 'class' => 'em');
435
			}
436
			elseif (!empty($actions['action']))
437
				$data[$k] = $txt['who_generic'] . ' ' . $actions['action'];
438
			else
439
				$data[$k] = array('label' => 'who_unknown', 'class' => 'em');
440
		}
441
442
		if (isset($actions['error']))
443
		{
444
			loadLanguage('Errors');
445
446
			if (isset($txt[$actions['error']]))
447
				$error_message = str_replace('"', '&quot;', empty($actions['who_error_params']) ? $txt[$actions['error']] : vsprintf($txt[$actions['error']], $actions['who_error_params']));
448
			elseif ($actions['error'] == 'guest_login')
449
				$error_message = str_replace('"', '&quot;', $txt['who_guest_login']);
450
			else
451
				$error_message = str_replace('"', '&quot;', $actions['error']);
452
453
			if (!empty($error_message))
454
				$data[$k] .= ' <span class="main_icons error" title="' . $error_message . '"></span>';
455
		}
456
457
		// Maybe the action is integrated into another system?
458
		if (count($integrate_actions = call_integration_hook('integrate_whos_online', array($actions))) > 0)
459
		{
460
			foreach ($integrate_actions as $integrate_action)
461
			{
462
				if (!empty($integrate_action))
463
				{
464
					$data[$k] = $integrate_action;
465
					if (isset($actions['topic']) && isset($topic_ids[(int) $actions['topic']][$k]))
466
						$topic_ids[(int) $actions['topic']][$k] = $integrate_action;
467
					if (isset($actions['board']) && isset($board_ids[(int) $actions['board']][$k]))
468
						$board_ids[(int) $actions['board']][$k] = $integrate_action;
469
					if (isset($actions['u']) && isset($profile_ids[(int) $actions['u']][$k]))
470
						$profile_ids[(int) $actions['u']][$k] = $integrate_action;
471
					break;
472
				}
473
			}
474
		}
475
	}
476
477
	// Load topic names.
478
	if (!empty($topic_ids))
479
	{
480
		$result = $smcFunc['db_query']('', '
481
			SELECT t.id_topic, m.subject
482
			FROM {db_prefix}topics AS t
483
				INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)
484
			WHERE {query_see_topic_board}
485
				AND t.id_topic IN ({array_int:topic_list})' . ($modSettings['postmod_active'] ? '
486
				AND t.approved = {int:is_approved}' : '') . '
487
			LIMIT {int:limit}',
488
			array(
489
				'topic_list' => array_keys($topic_ids),
490
				'is_approved' => 1,
491
				'limit' => count($topic_ids),
492
			)
493
		);
494
		while ($row = $smcFunc['db_fetch_assoc']($result))
495
		{
496
			// Show the topic's subject for each of the actions.
497
			foreach ($topic_ids[$row['id_topic']] as $k => $session_text)
498
				$data[$k] = sprintf($session_text, $row['id_topic'], censorText($row['subject']));
499
		}
500
		$smcFunc['db_free_result']($result);
501
	}
502
503
	// Load board names.
504
	if (!empty($board_ids))
505
	{
506
		$result = $smcFunc['db_query']('', '
507
			SELECT b.id_board, b.name
508
			FROM {db_prefix}boards AS b
509
			WHERE {query_see_board}
510
				AND b.id_board IN ({array_int:board_list})
511
			LIMIT {int:limit}',
512
			array(
513
				'board_list' => array_keys($board_ids),
514
				'limit' => count($board_ids),
515
			)
516
		);
517
		while ($row = $smcFunc['db_fetch_assoc']($result))
518
		{
519
			// Put the board name into the string for each member...
520
			foreach ($board_ids[$row['id_board']] as $k => $session_text)
521
				$data[$k] = sprintf($session_text, $row['id_board'], $row['name']);
522
		}
523
		$smcFunc['db_free_result']($result);
524
	}
525
526
	// Load member names for the profile. (is_not_guest permission for viewing their own profile)
527
	$allow_view_own = allowedTo('is_not_guest');
528
	$allow_view_any = allowedTo('profile_view');
529
	if (!empty($profile_ids) && ($allow_view_any || $allow_view_own))
530
	{
531
		$result = $smcFunc['db_query']('', '
532
			SELECT id_member, real_name
533
			FROM {db_prefix}members
534
			WHERE id_member IN ({array_int:member_list})
535
			LIMIT ' . count($profile_ids),
536
			array(
537
				'member_list' => array_keys($profile_ids),
538
			)
539
		);
540
		while ($row = $smcFunc['db_fetch_assoc']($result))
541
		{
542
			// If they aren't allowed to view this person's profile, skip it.
543
			if (!$allow_view_any && ($user_info['id'] != $row['id_member']))
544
				continue;
545
546
			// Set their action on each - session/text to sprintf.
547
			foreach ($profile_ids[$row['id_member']] as $k => $session_text)
548
				$data[$k] = sprintf($session_text, $row['id_member'], $row['real_name']);
549
		}
550
		$smcFunc['db_free_result']($result);
551
	}
552
553
	call_integration_hook('whos_online_after', array(&$urls, &$data));
554
555
	if (!is_array($urls))
556
		return isset($data[0]) ? $data[0] : false;
557
	else
558
		return $data;
559
}
560
561
/**
562
 * It prepares credit and copyright information for the credits page or the admin page
563
 *
564
 * @param bool $in_admin = false, if parameter is true the it will not load the sub-template nor the template file
565
 */
566
function Credits($in_admin = false)
567
{
568
	global $context, $smcFunc, $forum_copyright, $txt, $user_info;
569
570
	// Don't blink. Don't even blink. Blink and you're dead.
571
	loadLanguage('Who');
572
573
	if ($in_admin)
574
	{
575
		$context[$context['admin_menu_name']]['tab_data'] = array(
576
			'title' => $txt['support_credits_title'],
577
			'help' => '',
578
			'description' => '',
579
		);
580
	}
581
582
	$context['credits'] = array(
583
		array(
584
			'pretext' => $txt['credits_intro'],
585
			'title' => $txt['credits_team'],
586
			'groups' => array(
587
				array(
588
					'title' => $txt['credits_groups_pm'],
589
					'members' => array(
590
						'Michele "Illori" Davis',
591
						// Former Project Managers
592
						'Jessica "Suki" González',
593
						'Will "Kindred" Wagner',
594
					),
595
				),
596
				array(
597
					'title' => $txt['credits_groups_dev'],
598
					'members' => array(
599
						// Lead Developer
600
						'Jon "Sesquipedalian" Stovell',
601
						// Developers
602
						'Jessica "Suki" González',
603
						'John "live627" Rayes',
604
						'Jeremy "SleePy" Darwood',
605
						'Shawn Bulen',
606
607
						// Former Developers
608
						'Aaron van Geffen',
609
						'Antechinus',
610
						'Bjoern "Bloc" Kristiansen',
611
						'Brad "IchBin™" Grow',
612
						'Colin Schoen',
613
						'emanuele',
614
						'Hendrik Jan "Compuart" Visser',
615
						'Juan "JayBachatero" Hernandez',
616
						'Karl "RegularExpression" Benson',
617
						'Matthew "Labradoodle-360" Kerle',
618
						$user_info['is_admin'] ? 'Matt "Grudge" Wolf' : 'Grudge',
619
						'Michael "Oldiesmann" Eshom',
620
						'Michael "Thantos" Miller',
621
						'Norv',
622
						'Peter "Arantor" Spicer',
623
						'Selman "[SiNaN]" Eser',
624
						'Shitiz "Dragooon" Garg',
625
						// 'Spuds', // Doesn't want to be listed here
626
						// 'Steven "Fustrate" Hoffman',
627
						'Theodore "Orstio" Hildebrandt',
628
						'Thorsten "TE" Eurich',
629
						'winrules',
630
					),
631
				),
632
				array(
633
					'title' => $txt['credits_groups_support'],
634
					'members' => array(
635
						// Lead Support Specialist
636
						'Aleksi "Lex" Kilpinen',
637
						// Support Specialists
638
						'br360',
639
						'GigaWatt',
640
						'Will "Kindred" Wagner',
641
						'lurkalot',
642
643
						// Former Support Specialists
644
						'Steve',
645
						'ziycon',
646
						'Adam Tallon',
647
						'Bigguy',
648
						'Bruno "margarett" Alves',
649
						'CapadY',
650
						'ChalkCat',
651
						'Chas Large',
652
						'Duncan85',
653
						'gbsothere',
654
						'JimM',
655
						'Justyne',
656
						'Kat',
657
						'Kevin "greyknight17" Hou',
658
						'Krash',
659
						'Mashby',
660
						'Michael Colin Blaber',
661
						'Michele "Illori" Davis',
662
						'Old Fossil',
663
						'S-Ace',
664
						'Storman™',
665
						'Wade "sησω" Poulsen',
666
						'xenovanis',
667
					),
668
				),
669
				array(
670
					'title' => $txt['credits_groups_customize'],
671
					'members' => array(
672
						// Lead Customizer
673
						'Sami "SychO" Mazouz',
674
						// Customizers
675
						'Gary M. Gadsdon',
676
						'Diego Andrés',
677
						'Jonathan "vbgamer45" Valentin',
678
679
						// Former Customizers
680
						'Brannon "B" Hall',
681
						'Gwenwyfar',
682
						'Jack "akabugeyes" Thorsen',
683
						'Jason "JBlaze" Clemons',
684
						'Jessica "Suki" González',
685
						'Joey "Tyrsson" Smith',
686
						'Kays',
687
						'NanoSector',
688
						'Ricky.',
689
						'Russell "NEND" Najar',
690
						'SA™',
691
					),
692
				),
693
				array(
694
					'title' => $txt['credits_groups_docs'],
695
					'members' => array(
696
						// Doc Coordinator
697
						'Irisado',
698
						// Doc Writers
699
700
						// Former Doc Writers
701
						'AngelinaBelle',
702
						'Chainy',
703
						'Graeme Spence',
704
						'Joshua "groundup" Dickerson',
705
					),
706
				),
707
				array(
708
					'title' => $txt['credits_groups_internationalizers'],
709
					'members' => array(
710
						// Lead Localizer
711
						'Francisco "d3vcho" Domínguez',
712
						// Localizers
713
						'Nikola "Dzonny" Novaković',
714
						// Former Localizers
715
						'Robert Monden',
716
						'Relyana',
717
					),
718
				),
719
				array(
720
					'title' => $txt['credits_groups_marketing'],
721
					'members' => array(
722
						// Marketing Coordinator
723
724
						// Marketing
725
726
						// Former Marketing
727
						'Adish "(F.L.A.M.E.R)" Patel',
728
						'Bryan "Runic" Deakin',
729
						'Marcus "cσσкιє мσηѕтєя" Forsberg',
730
						'Mert "Antes" Alınbay',
731
						'Ralph "[n3rve]" Otowo',
732
					),
733
				),
734
				array(
735
					'title' => $txt['credits_groups_site'],
736
					'members' => array(
737
						'Jeremy "SleePy" Darwood',
738
					),
739
				),
740
				array(
741
					'title' => $txt['credits_groups_servers'],
742
					'members' => array(
743
						'Derek Schwab',
744
						'Michael Johnson',
745
						'Liroy van Hoewijk',
746
					),
747
				),
748
			),
749
		),
750
	);
751
752
	// Give the translators some credit for their hard work.
753
	if (!empty($txt['translation_credits']))
754
		$context['credits'][] = array(
755
			'title' => $txt['credits_groups_translation'],
756
			'groups' => array(
757
				array(
758
					'title' => $txt['credits_groups_translation'],
759
					'members' => $txt['translation_credits'],
760
				),
761
			),
762
		);
763
764
	$context['credits'][] = array(
765
		'title' => $txt['credits_special'],
766
		'posttext' => $txt['credits_anyone'],
767
		'groups' => array(
768
			array(
769
				'title' => $txt['credits_groups_consultants'],
770
				'members' => array(
771
					'albertlast',
772
					'Brett Flannigan',
773
					'Mark Rose',
774
					'René-Gilles "Nao 尚" Deberdt',
775
					'tinoest',
776
					$txt['credits_code_contributors'],
777
				),
778
			),
779
			array(
780
				'title' => $txt['credits_groups_beta'],
781
				'members' => array(
782
					$txt['credits_beta_message'],
783
				),
784
			),
785
			array(
786
				'title' => $txt['credits_groups_translators'],
787
				'members' => array(
788
					$txt['credits_translators_message'],
789
				),
790
			),
791
			array(
792
				'title' => $txt['credits_groups_founder'],
793
				'members' => array(
794
					'Unknown W. "[Unknown]" Brackets',
795
				),
796
			),
797
			array(
798
				'title' => $txt['credits_groups_orignal_pm'],
799
				'members' => array(
800
					'Jeff Lewis',
801
					'Joseph Fung',
802
					'David Recordon',
803
				),
804
			),
805
			array(
806
				'title' => $txt['credits_in_memoriam'],
807
				'members' => array(
808
					'Crip',
809
					'K@',
810
					'metallica48423',
811
					'Paul_Pauline',
812
				),
813
			),
814
		),
815
	);
816
817
	// Give credit to any graphic library's, software library's, plugins etc
818
	$context['credits_software_graphics'] = array(
819
		'graphics' => array(
820
			'<a href="http://p.yusukekamiyamane.com/">Fugue Icons</a> | © 2012 Yusuke Kamiyamane | These icons are licensed under a Creative Commons Attribution 3.0 License',
821
			'<a href="https://techbase.kde.org/Projects/Oxygen/Licensing#Use_on_Websites">Oxygen Icons</a> | These icons are licensed under <a href="http://www.gnu.org/copyleft/lesser.html">GNU LGPLv3</a>',
822
		),
823
		'software' => array(
824
			'<a href="https://jquery.org/">JQuery</a> | © John Resig | Licensed under <a href="https://github.com/jquery/jquery/blob/master/LICENSE.txt">The MIT License (MIT)</a>',
825
			'<a href="https://briancherne.github.io/jquery-hoverIntent/">hoverIntent</a> | © Brian Cherne | Licensed under <a href="https://en.wikipedia.org/wiki/MIT_License">The MIT License (MIT)</a>',
826
			'<a href="https://www.sceditor.com/">SCEditor</a> | © Sam Clarke | Licensed under <a href="https://en.wikipedia.org/wiki/MIT_License">The MIT License (MIT)</a>',
827
			'<a href="http://wayfarerweb.com/jquery/plugins/animadrag/">animaDrag</a> | © Abel Mohler | Licensed under <a href="https://en.wikipedia.org/wiki/MIT_License">The MIT License (MIT)</a>',
828
			'<a href="https://github.com/mzubala/jquery-custom-scrollbar">jQuery Custom Scrollbar</a> | © Maciej Zubala | Licensed under <a href="http://en.wikipedia.org/wiki/MIT_License">The MIT License (MIT)</a>',
829
			'<a href="http://slippry.com/">jQuery Responsive Slider</a> | © booncon ROCKETS | Licensed under <a href="http://en.wikipedia.org/wiki/MIT_License">The MIT License (MIT)</a>',
830
			'<a href="https://github.com/ichord/At.js">At.js</a> | © [email protected] | Licensed under <a href="https://github.com/ichord/At.js/blob/master/LICENSE-MIT">The MIT License (MIT)</a>',
831
			'<a href="https://github.com/ttsvetko/HTML5-Desktop-Notifications">HTML5 Desktop Notifications</a> | © Tsvetan Tsvetkov | Licensed under <a href="https://github.com/ttsvetko/HTML5-Desktop-Notifications/blob/master/License.txt">The Apache License Version 2.0</a>',
832
			'<a href="https://github.com/enygma/gauth">GAuth Code Generator/Validator</a> | © Chris Cornutt | Licensed under <a href="https://github.com/enygma/gauth/blob/master/LICENSE">The MIT License (MIT)</a>',
833
			'<a href="https://github.com/enyo/dropzone">Dropzone.js</a> | © Matias Meno | Licensed under <a href="http://en.wikipedia.org/wiki/MIT_License">The MIT License (MIT)</a>',
834
			'<a href="https://github.com/matthiasmullie/minify">Minify</a> | © Matthias Mullie | Licensed under <a href="http://en.wikipedia.org/wiki/MIT_License">The MIT License (MIT)</a>',
835
			'<a href="https://github.com/true/php-punycode">PHP-Punycode</a> | © True B.V. | Licensed under <a href="http://en.wikipedia.org/wiki/MIT_License">The MIT License (MIT)</a>',
836
		),
837
		'fonts' => array(
838
			'<a href="https://fontlibrary.org/en/font/anonymous-pro"> Anonymous Pro</a> | © 2009 | This font is licensed under the SIL Open Font License, Version 1.1',
839
			'<a href="https://fontlibrary.org/en/font/consolamono"> ConsolaMono</a> | © 2012 | This font is licensed under the SIL Open Font License, Version 1.1',
840
			'<a href="https://fontlibrary.org/en/font/phennig"> Phennig</a> | © 2009-2012 | This font is licensed under the SIL Open Font License, Version 1.1',
841
		),
842
	);
843
844
	// Support for mods that use the <credits> tag via the package manager
845
	$context['credits_modifications'] = array();
846
	if (($mods = cache_get_data('mods_credits', 86400)) === null)
847
	{
848
		$mods = array();
849
		$request = $smcFunc['db_query']('substring', '
850
			SELECT version, name, credits
851
			FROM {db_prefix}log_packages
852
			WHERE install_state = {int:installed_mods}
853
				AND credits != {string:empty}
854
				AND SUBSTRING(filename, 1, 9) != {string:patch_name}',
855
			array(
856
				'installed_mods' => 1,
857
				'patch_name' => 'smf_patch',
858
				'empty' => '',
859
			)
860
		);
861
862
		while ($row = $smcFunc['db_fetch_assoc']($request))
863
		{
864
			$credit_info = $smcFunc['json_decode']($row['credits'], true);
865
866
			$copyright = empty($credit_info['copyright']) ? '' : $txt['credits_copyright'] . ' © ' . $smcFunc['htmlspecialchars']($credit_info['copyright']);
867
			$license = empty($credit_info['license']) ? '' : $txt['credits_license'] . ': ' . (!empty($credit_info['licenseurl']) ? '<a href="' . $smcFunc['htmlspecialchars']($credit_info['licenseurl']) . '">' . $smcFunc['htmlspecialchars']($credit_info['license']) . '</a>' : $smcFunc['htmlspecialchars']($credit_info['license']));
868
			$version = $txt['credits_version'] . ' ' . $row['version'];
869
			$title = (empty($credit_info['title']) ? $row['name'] : $smcFunc['htmlspecialchars']($credit_info['title'])) . ': ' . $version;
870
871
			// build this one out and stash it away
872
			$mod_name = empty($credit_info['url']) ? $title : '<a href="' . $credit_info['url'] . '">' . $title . '</a>';
873
			$mods[] = $mod_name . (!empty($license) ? ' | ' . $license : '') . (!empty($copyright) ? ' | ' . $copyright : '');
874
		}
875
		cache_put_data('mods_credits', $mods, 86400);
876
	}
877
	$context['credits_modifications'] = $mods;
878
879
	$context['copyrights'] = array(
880
		'smf' => sprintf($forum_copyright, SMF_FULL_VERSION, SMF_SOFTWARE_YEAR),
881
		/* Modification Authors:  You may add a copyright statement to this array for your mods.
882
			Copyright statements should be in the form of a value only without a array key.  I.E.:
883
				'Some Mod by Thantos © 2010',
884
				$txt['some_mod_copyright'],
885
		*/
886
		'mods' => array(
887
		),
888
	);
889
890
	// Support for those that want to use a hook as well
891
	call_integration_hook('integrate_credits');
892
893
	if (!$in_admin)
894
	{
895
		loadTemplate('Who');
896
		$context['sub_template'] = 'credits';
897
		$context['robot_no_index'] = true;
898
		$context['page_title'] = $txt['credits'];
899
	}
900
}
901
902
?>