Completed
Pull Request — release-2.1 (#4090)
by Rick
08:20
created

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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 3
12
 */
13
14
// Don't do anything if SMF is already loaded.
15
if (defined('SMF'))
16
	return true;
17
18
define('SMF', 'SSI');
19
20
// We're going to want a few globals... these are all set later.
21
global $time_start, $maintenance, $msubject, $mmessage, $mbname, $language;
22
global $boardurl, $boarddir, $sourcedir, $webmaster_email, $cookiename;
23
global $db_type, $db_server, $db_name, $db_user, $db_prefix, $db_persist, $db_error_send, $db_last_error;
24
global $db_connection, $modSettings, $context, $sc, $user_info, $topic, $board, $txt;
25
global $smcFunc, $ssi_db_user, $scripturl, $ssi_db_passwd, $db_passwd, $cachedir;
26
27
// Remember the current configuration so it can be set back.
28
$ssi_magic_quotes_runtime = function_exists('get_magic_quotes_gpc') && get_magic_quotes_runtime();
29
if (function_exists('set_magic_quotes_runtime'))
30
	@set_magic_quotes_runtime(0);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
31
$time_start = microtime();
32
33
// Just being safe...
34
foreach (array('db_character_set', 'cachedir') as $variable)
35
	if (isset($GLOBALS[$variable]))
36
		unset($GLOBALS[$variable]);
37
38
// Get the forum's settings for database and file paths.
39
require_once(dirname(__FILE__) . '/Settings.php');
40
41
// Make absolutely sure the cache directory is defined.
42 View Code Duplication
if ((empty($cachedir) || !file_exists($cachedir)) && file_exists($boarddir . '/cache'))
43
	$cachedir = $boarddir . '/cache';
44
45
$ssi_error_reporting = error_reporting(defined('E_STRICT') ? E_ALL | E_STRICT : E_ALL);
46
/* Set this to one of three values depending on what you want to happen in the case of a fatal error.
47
	false:	Default, will just load the error sub template and die - not putting any theme layers around it.
48
	true:	Will load the error sub template AND put the SMF layers around it (Not useful if on total custom pages).
49
	string:	Name of a callback function to call in the event of an error to allow you to define your own methods. Will die after function returns.
50
*/
51
$ssi_on_error_method = false;
52
53
// Don't do john didley if the forum's been shut down completely.
54
if ($maintenance == 2 && (!isset($ssi_maintenance_off) || $ssi_maintenance_off !== true))
55
	die($mmessage);
56
57
// Fix for using the current directory as a path.
58 View Code Duplication
if (substr($sourcedir, 0, 1) == '.' && substr($sourcedir, 1, 1) != '.')
59
	$sourcedir = dirname(__FILE__) . substr($sourcedir, 1);
60
61
// Load the important includes.
62
require_once($sourcedir . '/QueryString.php');
63
require_once($sourcedir . '/Session.php');
64
require_once($sourcedir . '/Subs.php');
65
require_once($sourcedir . '/Errors.php');
66
require_once($sourcedir . '/Logging.php');
67
require_once($sourcedir . '/Load.php');
68
require_once($sourcedir . '/Security.php');
69
require_once($sourcedir . '/Class-BrowserDetect.php');
70
require_once($sourcedir . '/Subs-Auth.php');
71
72
// Create a variable to store some SMF specific functions in.
73
$smcFunc = array();
74
75
// Initiate the database connection and define some database functions to use.
76
loadDatabase();
77
78
// Load installed 'Mods' settings.
79
reloadSettings();
80
// Clean the request variables.
81
cleanRequest();
82
83
// Seed the random generator?
84
if (empty($modSettings['rand_seed']) || mt_rand(1, 250) == 69)
85
	smf_seed_generator();
86
87
// Check on any hacking attempts.
88
if (isset($_REQUEST['GLOBALS']) || isset($_COOKIE['GLOBALS']))
89
	die('No direct access...');
90
elseif (isset($_REQUEST['ssi_theme']) && (int) $_REQUEST['ssi_theme'] == (int) $ssi_theme)
91
	die('No direct access...');
92
elseif (isset($_COOKIE['ssi_theme']) && (int) $_COOKIE['ssi_theme'] == (int) $ssi_theme)
93
	die('No direct access...');
94
elseif (isset($_REQUEST['ssi_layers'], $ssi_layers) && (@get_magic_quotes_gpc() ? stripslashes($_REQUEST['ssi_layers']) : $_REQUEST['ssi_layers']) == $ssi_layers)
95
	die('No direct access...');
96
if (isset($_REQUEST['context']))
97
	die('No direct access...');
98
99
// Gzip output? (because it must be boolean and true, this can't be hacked.)
100
if (isset($ssi_gzip) && $ssi_gzip === true && ini_get('zlib.output_compression') != '1' && ini_get('output_handler') != 'ob_gzhandler' && version_compare(PHP_VERSION, '4.2.0', '>='))
101
	ob_start('ob_gzhandler');
102
else
103
	$modSettings['enableCompressedOutput'] = '0';
104
105
// Primarily, this is to fix the URLs...
106
ob_start('ob_sessrewrite');
107
108
// Start the session... known to scramble SSI includes in cases...
109
if (!headers_sent())
110
	loadSession();
111
else
112
{
113
	if (isset($_COOKIE[session_name()]) || isset($_REQUEST[session_name()]))
114
	{
115
		// Make a stab at it, but ignore the E_WARNINGs generated because we can't send headers.
116
		$temp = error_reporting(error_reporting() & !E_WARNING);
117
		loadSession();
118
		error_reporting($temp);
119
	}
120
121 View Code Duplication
	if (!isset($_SESSION['session_value']))
122
	{
123
		$_SESSION['session_var'] = substr(md5(mt_rand() . session_id() . mt_rand()), 0, rand(7, 12));
124
		$_SESSION['session_value'] = md5(session_id() . mt_rand());
125
	}
126
	$sc = $_SESSION['session_value'];
127
}
128
129
// Get rid of $board and $topic... do stuff loadBoard would do.
130
unset($board, $topic);
131
$user_info['is_mod'] = false;
132
$context['user']['is_mod'] = &$user_info['is_mod'];
133
$context['linktree'] = array();
134
135
// Load the user and their cookie, as well as their settings.
136
loadUserSettings();
137
138
// Load the current user's permissions....
139
loadPermissions();
140
141
// Load the current or SSI theme. (just use $ssi_theme = id_theme;)
142
loadTheme(isset($ssi_theme) ? (int) $ssi_theme : 0);
143
144
// @todo: probably not the best place, but somewhere it should be set...
145
if (!headers_sent())
146
	header('Content-Type: text/html; charset=' . (empty($modSettings['global_character_set']) ? (empty($txt['lang_character_set']) ? 'ISO-8859-1' : $txt['lang_character_set']) : $modSettings['global_character_set']));
147
148
// Take care of any banning that needs to be done.
149
if (isset($_REQUEST['ssi_ban']) || (isset($ssi_ban) && $ssi_ban === true))
150
	is_not_banned();
151
152
// Do we allow guests in here?
153
if (empty($ssi_guest_access) && empty($modSettings['allow_guestAccess']) && $user_info['is_guest'] && basename($_SERVER['PHP_SELF']) != 'SSI.php')
154
{
155
	require_once($sourcedir . '/Subs-Auth.php');
156
	KickGuest();
157
	obExit(null, true);
158
}
159
160
// Load the stuff like the menu bar, etc.
161
if (isset($ssi_layers))
162
{
163
	$context['template_layers'] = $ssi_layers;
164
	template_header();
165
}
166
else
167
	setupThemeContext();
168
169
// Make sure they didn't muss around with the settings... but only if it's not cli.
170
if (isset($_SERVER['REMOTE_ADDR']) && !isset($_SERVER['is_cli']) && session_id() == '')
171
	trigger_error($txt['ssi_session_broken'], E_USER_NOTICE);
172
173
// Without visiting the forum this session variable might not be set on submit.
174
if (!isset($_SESSION['USER_AGENT']) && (!isset($_GET['ssi_function']) || $_GET['ssi_function'] !== 'pollVote'))
175
	$_SESSION['USER_AGENT'] = $_SERVER['HTTP_USER_AGENT'];
176
177
// Have the ability to easily add functions to SSI.
178
call_integration_hook('integrate_SSI');
179
180
// Ignore a call to ssi_* functions if we are not accessing SSI.php directly.
181
if (basename($_SERVER['PHP_SELF']) == 'SSI.php')
182
{
183
	// You shouldn't just access SSI.php directly by URL!!
184
	if (!isset($_GET['ssi_function']))
185
		die(sprintf($txt['ssi_not_direct'], $user_info['is_admin'] ? '\'' . addslashes(__FILE__) . '\'' : '\'SSI.php\''));
186
	// Call a function passed by GET.
187
	if (function_exists('ssi_' . $_GET['ssi_function']) && (!empty($modSettings['allow_guestAccess']) || !$user_info['is_guest']))
188
		call_user_func('ssi_' . $_GET['ssi_function']);
189
	exit;
190
}
191
192
// To avoid side effects later on.
193
unset($_GET['ssi_function']);
194
195
error_reporting($ssi_error_reporting);
196
if (function_exists('set_magic_quotes_runtime'))
197
	@set_magic_quotes_runtime($ssi_magic_quotes_runtime);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
198
199
return true;
200
201
/**
202
 * This shuts down the SSI and shows the footer.
203
 * @return void
204
 */
205
function ssi_shutdown()
206
{
207
	if (!isset($_GET['ssi_function']) || $_GET['ssi_function'] != 'shutdown')
208
		template_footer();
209
}
210
211
/**
212
 * Display a welcome message, like: Hey, User, you have 0 messages, 0 are new.
213
 * @param string $output_method The output method. If 'echo', will display everything. Otherwise returns an array of user info.
214
 * @return void|array Displays a welcome message or returns an array of user data depending on output_method.
215
 */
216
function ssi_welcome($output_method = 'echo')
217
{
218
	global $context, $txt, $scripturl;
219
220
	if ($output_method == 'echo')
221
	{
222
		if ($context['user']['is_guest'])
223
			echo sprintf($txt[$context['can_register'] ? 'welcome_guest_register' : 'welcome_guest'], $txt['guest_title'], $context['forum_name_html_safe'], $scripturl . '?action=login', 'return reqOverlayDiv(this.href, ' . JavaScriptEscape($txt['login']) . ');', $scripturl . '?action=signup');
224
		else
225
			echo $txt['hello_member'], ' <strong>', $context['user']['name'], '</strong>', allowedTo('pm_read') ? ', ' . (empty($context['user']['messages']) ? $txt['msg_alert_no_messages'] : (($context['user']['messages'] == 1 ? sprintf($txt['msg_alert_one_message'], $scripturl . '?action=pm') : sprintf($txt['msg_alert_many_message'], $scripturl . '?action=pm', $context['user']['messages'])) . ', ' . ($context['user']['unread_messages'] == 1 ? $txt['msg_alert_one_new'] : sprintf($txt['msg_alert_many_new'], $context['user']['unread_messages'])))) : '';
226
	}
227
	// Don't echo... then do what?!
228
	else
229
		return $context['user'];
230
}
231
232
/**
233
 * Display a menu bar, like is displayed at the top of the forum.
234
 * @param string $output_method The output method. If 'echo', will display the menu, otherwise returns an array of menu data.
235
 * @return void|array Displays the menu or returns an array of menu data depending on output_method.
236
 */
237
function ssi_menubar($output_method = 'echo')
238
{
239
	global $context;
240
241
	if ($output_method == 'echo')
242
		template_menu();
243
	// What else could this do?
244
	else
245
		return $context['menu_buttons'];
246
}
247
248
/**
249
 * Show a logout link.
250
 * @param string $redirect_to A URL to redirect the user to after they log out.
251
 * @param string $output_method The output method. If 'echo', shows a logout link, otherwise returns the HTML for it.
252
 * @return void|string Displays a logout link or returns its HTML depending on output_method.
253
 */
254
function ssi_logout($redirect_to = '', $output_method = 'echo')
255
{
256
	global $context, $txt, $scripturl;
257
258
	if ($redirect_to != '')
259
		$_SESSION['logout_url'] = $redirect_to;
260
261
	// Guests can't log out.
262
	if ($context['user']['is_guest'])
263
		return false;
264
265
	$link = '<a href="' . $scripturl . '?action=logout;' . $context['session_var'] . '=' . $context['session_id'] . '">' . $txt['logout'] . '</a>';
266
267
	if ($output_method == 'echo')
268
		echo $link;
269
	else
270
		return $link;
271
}
272
273
/**
274
 * Recent post list:   [board] Subject by Poster    Date
275
 * @param int $num_recent How many recent posts to display
276
 * @param null|array $exclude_boards If set, doesn't show posts from the specified boards
277
 * @param null|array $include_boards If set, only includes posts from the specified boards
278
 * @param string $output_method The output method. If 'echo', displays the posts, otherwise returns an array of information about them.
279
 * @param bool $limit_body Whether or not to only show the first 384 characters of each post
280
 * @return void|array Displays a list of recent posts or returns an array of information about them depending on output_method.
281
 */
282
function ssi_recentPosts($num_recent = 8, $exclude_boards = null, $include_boards = null, $output_method = 'echo', $limit_body = true)
283
{
284
	global $modSettings, $context;
285
286
	// Excluding certain boards...
287 View Code Duplication
	if ($exclude_boards === null && !empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0)
288
		$exclude_boards = array($modSettings['recycle_board']);
289
	else
290
		$exclude_boards = empty($exclude_boards) ? array() : (is_array($exclude_boards) ? $exclude_boards : array($exclude_boards));
291
292
	// What about including certain boards - note we do some protection here as pre-2.0 didn't have this parameter.
293 View Code Duplication
	if (is_array($include_boards) || (int) $include_boards === $include_boards)
294
	{
295
		$include_boards = is_array($include_boards) ? $include_boards : array($include_boards);
296
	}
297
	elseif ($include_boards != null)
298
	{
299
		$include_boards = array();
300
	}
301
302
	// Let's restrict the query boys (and girls)
303
	$query_where = '
304
		m.id_msg >= {int:min_message_id}
305
		' . (empty($exclude_boards) ? '' : '
306
		AND b.id_board NOT IN ({array_int:exclude_boards})') . '
307
		' . ($include_boards === null ? '' : '
308
		AND b.id_board IN ({array_int:include_boards})') . '
309
		AND {query_wanna_see_board}' . ($modSettings['postmod_active'] ? '
310
		AND m.approved = {int:is_approved}' : '');
311
312
	$query_where_params = array(
313
		'is_approved' => 1,
314
		'include_boards' => $include_boards === null ? '' : $include_boards,
315
		'exclude_boards' => empty($exclude_boards) ? '' : $exclude_boards,
316
		'min_message_id' => $modSettings['maxMsgID'] - (!empty($context['min_message_posts']) ? $context['min_message_posts'] : 25) * min($num_recent, 5),
317
	);
318
319
	// Past to this simpleton of a function...
320
	return ssi_queryPosts($query_where, $query_where_params, $num_recent, 'm.id_msg DESC', $output_method, $limit_body);
321
}
322
323
/**
324
 * Fetches one or more posts by ID.
325
 * @param array $post_ids An array containing the IDs of the posts to show
326
 * @param bool $override_permissions Whether to ignore permissions. If true, will show posts even if the user doesn't have permission to see them.
327
 * @param string $output_method The output method. If 'echo', displays the posts, otherwise returns an array of info about them
328
 * @return void|array Displays the specified posts or returns an array of info about them, depending on output_method.
329
 */
330
function ssi_fetchPosts($post_ids = array(), $override_permissions = false, $output_method = 'echo')
331
{
332
	global $modSettings;
333
334
	if (empty($post_ids))
335
		return;
336
337
	// Allow the user to request more than one - why not?
338
	$post_ids = is_array($post_ids) ? $post_ids : array($post_ids);
339
340
	// Restrict the posts required...
341
	$query_where = '
342
		m.id_msg IN ({array_int:message_list})' . ($override_permissions ? '' : '
343
			AND {query_wanna_see_board}') . ($modSettings['postmod_active'] ? '
344
			AND m.approved = {int:is_approved}' : '');
345
	$query_where_params = array(
346
		'message_list' => $post_ids,
347
		'is_approved' => 1,
348
	);
349
350
	// Then make the query and dump the data.
351
	return ssi_queryPosts($query_where, $query_where_params, '', 'm.id_msg DESC', $output_method, false, $override_permissions);
352
}
353
354
/**
355
 * This handles actually pulling post info. Called from other functions to eliminate duplication.
356
 * @param string $query_where The WHERE clause for the query
357
 * @param array $query_where_params An array of parameters for the WHERE clause
358
 * @param int $query_limit The maximum number of rows to return
359
 * @param string $query_order The ORDER BY clause for the query
360
 * @param string $output_method The output method. If 'echo', displays the posts, otherwise returns an array of info about them.
361
 * @param bool $limit_body If true, will only show the first 384 characters of the post rather than all of it
362
 * @param bool|false $override_permissions Whether or not to ignore permissions. If true, will show all posts regardless of whether the user can actually see them
363
 * @return void|array Displays the posts or returns an array of info about them, depending on output_method
364
 */
365
function ssi_queryPosts($query_where = '', $query_where_params = array(), $query_limit = 10, $query_order = 'm.id_msg DESC', $output_method = 'echo', $limit_body = false, $override_permissions = false)
366
{
367
	global $scripturl, $txt, $user_info;
368
	global $modSettings, $smcFunc, $context;
369
370
	if (!empty($modSettings['enable_likes']))
371
		$context['can_like'] = allowedTo('likes_like');
372
373
	// Find all the posts. Newer ones will have higher IDs.
374
	$request = $smcFunc['db_query']('substring', '
375
		SELECT
376
			m.poster_time, m.subject, m.id_topic, m.id_member, m.id_msg, m.id_board, m.likes, b.name AS board_name,
377
			IFNULL(mem.real_name, m.poster_name) AS poster_name, ' . ($user_info['is_guest'] ? '1 AS is_read, 0 AS new_from' : '
378
			IFNULL(lt.id_msg, IFNULL(lmr.id_msg, 0)) >= m.id_msg_modified AS is_read,
379
			IFNULL(lt.id_msg, IFNULL(lmr.id_msg, -1)) + 1 AS new_from') . ', ' . ($limit_body ? 'SUBSTRING(m.body, 1, 384) AS body' : 'm.body') . ', m.smileys_enabled
380
		FROM {db_prefix}messages AS m
381
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)
382
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)' . (!$user_info['is_guest'] ? '
383
			LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = m.id_topic AND lt.id_member = {int:current_member})
384
			LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = m.id_board AND lmr.id_member = {int:current_member})' : '') . '
385
		WHERE 1=1 ' . ($override_permissions ? '' : '
386
			AND {query_wanna_see_board}') . ($modSettings['postmod_active'] ? '
387
			AND m.approved = {int:is_approved}' : '') . '
388
		' . (empty($query_where) ? '' : 'AND ' . $query_where) . '
389
		ORDER BY ' . $query_order . '
390
		' . ($query_limit == '' ? '' : 'LIMIT ' . $query_limit),
391
		array_merge($query_where_params, array(
392
			'current_member' => $user_info['id'],
393
			'is_approved' => 1,
394
		))
395
	);
396
	$posts = array();
397
	while ($row = $smcFunc['db_fetch_assoc']($request))
398
	{
399
		$row['body'] = parse_bbc($row['body'], $row['smileys_enabled'], $row['id_msg']);
400
401
		// Censor it!
402
		censorText($row['subject']);
403
		censorText($row['body']);
404
405
		$preview = strip_tags(strtr($row['body'], array('<br>' => '&#10;')));
406
407
		// Build the array.
408
		$posts[$row['id_msg']] = array(
409
			'id' => $row['id_msg'],
410
			'board' => array(
411
				'id' => $row['id_board'],
412
				'name' => $row['board_name'],
413
				'href' => $scripturl . '?board=' . $row['id_board'] . '.0',
414
				'link' => '<a href="' . $scripturl . '?board=' . $row['id_board'] . '.0">' . $row['board_name'] . '</a>'
415
			),
416
			'topic' => $row['id_topic'],
417
			'poster' => array(
418
				'id' => $row['id_member'],
419
				'name' => $row['poster_name'],
420
				'href' => empty($row['id_member']) ? '' : $scripturl . '?action=profile;u=' . $row['id_member'],
421
				'link' => empty($row['id_member']) ? $row['poster_name'] : '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['poster_name'] . '</a>'
422
			),
423
			'subject' => $row['subject'],
424
			'short_subject' => shorten_subject($row['subject'], 25),
425
			'preview' => $smcFunc['strlen']($preview) > 128 ? $smcFunc['substr']($preview, 0, 128) . '...' : $preview,
426
			'body' => $row['body'],
427
			'time' => timeformat($row['poster_time']),
428
			'timestamp' => forum_time(true, $row['poster_time']),
429
			'href' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . ';topicseen#new',
430
			'link' => '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'] . '" rel="nofollow">' . $row['subject'] . '</a>',
431
			'new' => !empty($row['is_read']),
432
			'is_new' => empty($row['is_read']),
433
			'new_from' => $row['new_from'],
434
		);
435
436
		// Get the likes for each message.
437 View Code Duplication
		if (!empty($modSettings['enable_likes']))
438
			$posts[$row['id_msg']]['likes'] = array(
439
				'count' => $row['likes'],
440
				'you' => in_array($row['id_msg'], prepareLikesContext($row['id_topic'])),
441
				'can_like' => !$context['user']['is_guest'] && $row['id_member'] != $context['user']['id'] && !empty($context['can_like']),
442
			);
443
	}
444
	$smcFunc['db_free_result']($request);
445
446
	// If mods want to do somthing with this list of posts, let them do that now.
447
	call_integration_hook('integrate_ssi_queryPosts', array(&$posts));
448
449
	// Just return it.
450
	if ($output_method != 'echo' || empty($posts))
451
		return $posts;
452
453
	echo '
454
		<table style="border: none" class="ssi_table">';
455 View Code Duplication
	foreach ($posts as $post)
456
		echo '
457
			<tr>
458
				<td style="text-align: right; vertical-align: top; white-space: nowrap">
459
					[', $post['board']['link'], ']
460
				</td>
461
				<td style="vertical-align: top">
462
					<a href="', $post['href'], '">', $post['subject'], '</a>
463
					', $txt['by'], ' ', $post['poster']['link'], '
464
					', $post['is_new'] ? '<a href="' . $scripturl . '?topic=' . $post['topic'] . '.msg' . $post['new_from'] . ';topicseen#new" rel="nofollow"><span class="new_posts">' . $txt['new'] . '</span></a>' : '', '
465
				</td>
466
				<td style="text-align: right; white-space: nowrap">
467
					', $post['time'], '
468
				</td>
469
			</tr>';
470
	echo '
471
		</table>';
472
}
473
474
/**
475
 * Recent topic list:   [board] Subject by Poster   Date
476
 * @param int $num_recent How many recent topics to show
477
 * @param null|array $exclude_boards If set, exclude topics from the specified board(s)
478
 * @param null|array $include_boards If set, only include topics from the specified board(s)
479
 * @param string $output_method The output method. If 'echo', displays a list of topics, otherwise returns an array of info about them
480
 * @return void|array Either displays a list of topics or returns an array of info about them, depending on output_method.
481
 */
482
function ssi_recentTopics($num_recent = 8, $exclude_boards = null, $include_boards = null, $output_method = 'echo')
483
{
484
	global $settings, $scripturl, $txt, $user_info;
485
	global $modSettings, $smcFunc, $context;
486
487 View Code Duplication
	if ($exclude_boards === null && !empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0)
488
		$exclude_boards = array($modSettings['recycle_board']);
489
	else
490
		$exclude_boards = empty($exclude_boards) ? array() : (is_array($exclude_boards) ? $exclude_boards : array($exclude_boards));
491
492
	// Only some boards?.
493 View Code Duplication
	if (is_array($include_boards) || (int) $include_boards === $include_boards)
494
	{
495
		$include_boards = is_array($include_boards) ? $include_boards : array($include_boards);
496
	}
497
	elseif ($include_boards != null)
498
	{
499
		$output_method = $include_boards;
500
		$include_boards = array();
501
	}
502
503
	$icon_sources = array();
504
	foreach ($context['stable_icons'] as $icon)
505
		$icon_sources[$icon] = 'images_url';
506
507
	// Find all the posts in distinct topics.  Newer ones will have higher IDs.
508
	$request = $smcFunc['db_query']('substring', '
509
		SELECT
510
			t.id_topic, b.id_board, b.name AS board_name
511
		FROM {db_prefix}topics AS t
512
			INNER JOIN {db_prefix}messages AS ml ON (ml.id_msg = t.id_last_msg)
513
			LEFT JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
514
		WHERE t.id_last_msg >= {int:min_message_id}' . (empty($exclude_boards) ? '' : '
515
			AND b.id_board NOT IN ({array_int:exclude_boards})') . '' . (empty($include_boards) ? '' : '
516
			AND b.id_board IN ({array_int:include_boards})') . '
517
			AND {query_wanna_see_board}' . ($modSettings['postmod_active'] ? '
518
			AND t.approved = {int:is_approved}
519
			AND ml.approved = {int:is_approved}' : '') . '
520
		ORDER BY t.id_last_msg DESC
521
		LIMIT ' . $num_recent,
522
		array(
523
			'include_boards' => empty($include_boards) ? '' : $include_boards,
524
			'exclude_boards' => empty($exclude_boards) ? '' : $exclude_boards,
525
			'min_message_id' => $modSettings['maxMsgID'] - (!empty($context['min_message_topics']) ? $context['min_message_topics'] : 35) * min($num_recent, 5),
526
			'is_approved' => 1,
527
		)
528
	);
529
	$topics = array();
530
	while ($row = $smcFunc['db_fetch_assoc']($request))
531
		$topics[$row['id_topic']] = $row;
532
	$smcFunc['db_free_result']($request);
533
534
	// Did we find anything? If not, bail.
535
	if (empty($topics))
536
		return array();
537
538
	$recycle_board = !empty($modSettings['recycle_enable']) && !empty($modSettings['recycle_board']) ? (int) $modSettings['recycle_board'] : 0;
539
540
	// Find all the posts in distinct topics.  Newer ones will have higher IDs.
541
	$request = $smcFunc['db_query']('substring', '
542
		SELECT
543
			mf.poster_time, mf.subject, ml.id_topic, mf.id_member, ml.id_msg, t.num_replies, t.num_views, mg.online_color,
544
			IFNULL(mem.real_name, mf.poster_name) AS poster_name, ' . ($user_info['is_guest'] ? '1 AS is_read, 0 AS new_from' : '
545
			IFNULL(lt.id_msg, IFNULL(lmr.id_msg, 0)) >= ml.id_msg_modified AS is_read,
546
			IFNULL(lt.id_msg, IFNULL(lmr.id_msg, -1)) + 1 AS new_from') . ', SUBSTRING(mf.body, 1, 384) AS body, mf.smileys_enabled, mf.icon
547
		FROM {db_prefix}topics AS t
548
			INNER JOIN {db_prefix}messages AS ml ON (ml.id_msg = t.id_last_msg)
549
			INNER JOIN {db_prefix}messages AS mf ON (mf.id_msg = t.id_last_msg)
550
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = mf.id_member)' . (!$user_info['is_guest'] ? '
551
			LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = t.id_topic AND lt.id_member = {int:current_member})
552
			LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = t.id_board AND lmr.id_member = {int:current_member})' : '') . '
553
			LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = mem.id_group)
554
		WHERE t.id_topic IN ({array_int:topic_list})',
555
		array(
556
			'current_member' => $user_info['id'],
557
			'topic_list' => array_keys($topics),
558
		)
559
	);
560
	$posts = array();
561
	while ($row = $smcFunc['db_fetch_assoc']($request))
562
	{
563
		$row['body'] = strip_tags(strtr(parse_bbc($row['body'], $row['smileys_enabled'], $row['id_msg']), array('<br>' => '&#10;')));
564 View Code Duplication
		if ($smcFunc['strlen']($row['body']) > 128)
565
			$row['body'] = $smcFunc['substr']($row['body'], 0, 128) . '...';
566
567
		// Censor the subject.
568
		censorText($row['subject']);
569
		censorText($row['body']);
570
571
		// Recycled icon
572
		if (!empty($recycle_board) && $topics[$row['id_topic']]['id_board'])
573
			$row['icon'] = 'recycled';
574
575 View Code Duplication
		if (!empty($modSettings['messageIconChecks_enable']) && !isset($icon_sources[$row['icon']]))
576
			$icon_sources[$row['icon']] = file_exists($settings['theme_dir'] . '/images/post/' . $row['icon'] . '.png') ? 'images_url' : 'default_images_url';
577
578
		// Build the array.
579
		$posts[] = array(
580
			'board' => array(
581
				'id' => $topics[$row['id_topic']]['id_board'],
582
				'name' => $topics[$row['id_topic']]['board_name'],
583
				'href' => $scripturl . '?board=' . $topics[$row['id_topic']]['id_board'] . '.0',
584
				'link' => '<a href="' . $scripturl . '?board=' . $topics[$row['id_topic']]['id_board'] . '.0">' . $topics[$row['id_topic']]['board_name'] . '</a>',
585
			),
586
			'topic' => $row['id_topic'],
587
			'poster' => array(
588
				'id' => $row['id_member'],
589
				'name' => $row['poster_name'],
590
				'href' => empty($row['id_member']) ? '' : $scripturl . '?action=profile;u=' . $row['id_member'],
591
				'link' => empty($row['id_member']) ? $row['poster_name'] : '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['poster_name'] . '</a>'
592
			),
593
			'subject' => $row['subject'],
594
			'replies' => $row['num_replies'],
595
			'views' => $row['num_views'],
596
			'short_subject' => shorten_subject($row['subject'], 25),
597
			'preview' => $row['body'],
598
			'time' => timeformat($row['poster_time']),
599
			'timestamp' => forum_time(true, $row['poster_time']),
600
			'href' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . ';topicseen#new',
601
			'link' => '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#new" rel="nofollow">' . $row['subject'] . '</a>',
602
			// Retained for compatibility - is technically incorrect!
603
			'new' => !empty($row['is_read']),
604
			'is_new' => empty($row['is_read']),
605
			'new_from' => $row['new_from'],
606
			'icon' => '<img src="' . $settings[$icon_sources[$row['icon']]] . '/post/' . $row['icon'] . '.png" style="vertical-align:middle;" alt="' . $row['icon'] . '">',
607
		);
608
	}
609
	$smcFunc['db_free_result']($request);
610
611
	// If mods want to do somthing with this list of topics, let them do that now.
612
	call_integration_hook('integrate_ssi_recentTopics', array(&$posts));
613
614
	// Just return it.
615
	if ($output_method != 'echo' || empty($posts))
616
		return $posts;
617
618
	echo '
619
		<table style="border: none" class="ssi_table">';
620 View Code Duplication
	foreach ($posts as $post)
621
		echo '
622
			<tr>
623
				<td style="text-align: right; vertical-align: top; white-space: nowrap">
624
					[', $post['board']['link'], ']
625
				</td>
626
				<td style="vertical-align: top">
627
					<a href="', $post['href'], '">', $post['subject'], '</a>
628
					', $txt['by'], ' ', $post['poster']['link'], '
629
					', !$post['is_new'] ? '' : '<a href="' . $scripturl . '?topic=' . $post['topic'] . '.msg' . $post['new_from'] . ';topicseen#new" rel="nofollow"><span class="new_posts">' . $txt['new'] . '</span></a>', '
630
				</td>
631
				<td style="text-align: right; white-space: nowrap">
632
					', $post['time'], '
633
				</td>
634
			</tr>';
635
	echo '
636
		</table>';
637
}
638
639
/**
640
 * Shows a list of top posters
641
 * @param int $topNumber How many top posters to list
642
 * @param string $output_method The output method. If 'echo', will display a list of users, otherwise returns an array of info about them.
643
 * @return void|array Either displays a list of users or returns an array of info about them, depending on output_method.
644
 */
645
function ssi_topPoster($topNumber = 1, $output_method = 'echo')
646
{
647
	global $scripturl, $smcFunc;
648
649
	// Find the latest poster.
650
	$request = $smcFunc['db_query']('', '
651
		SELECT id_member, real_name, posts
652
		FROM {db_prefix}members
653
		ORDER BY posts DESC
654
		LIMIT ' . $topNumber,
655
		array(
656
		)
657
	);
658
	$return = array();
659
	while ($row = $smcFunc['db_fetch_assoc']($request))
660
		$return[] = array(
661
			'id' => $row['id_member'],
662
			'name' => $row['real_name'],
663
			'href' => $scripturl . '?action=profile;u=' . $row['id_member'],
664
			'link' => '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['real_name'] . '</a>',
665
			'posts' => $row['posts']
666
		);
667
	$smcFunc['db_free_result']($request);
668
669
	// If mods want to do somthing with this list of members, let them do that now.
670
	call_integration_hook('integrate_ssi_topPoster', array(&$return));
671
672
	// Just return all the top posters.
673
	if ($output_method != 'echo')
674
		return $return;
675
676
	// Make a quick array to list the links in.
677
	$temp_array = array();
678
	foreach ($return as $member)
679
		$temp_array[] = $member['link'];
680
681
	echo implode(', ', $temp_array);
682
}
683
684
/**
685
 * Shows a list of top boards based on activity
686
 * @param int $num_top How many boards to display
687
 * @param string $output_method The output method. If 'echo', displays a list of boards, otherwise returns an array of info about them.
688
 * @return void|array Displays a list of the top boards or returns an array of info about them, depending on output_method.
689
 */
690
function ssi_topBoards($num_top = 10, $output_method = 'echo')
691
{
692
	global $txt, $scripturl, $user_info, $modSettings, $smcFunc;
693
694
	// Find boards with lots of posts.
695
	$request = $smcFunc['db_query']('', '
696
		SELECT
697
			b.name, b.num_topics, b.num_posts, b.id_board,' . (!$user_info['is_guest'] ? ' 1 AS is_read' : '
698
			(IFNULL(lb.id_msg, 0) >= b.id_last_msg) AS is_read') . '
699
		FROM {db_prefix}boards AS b
700
			LEFT JOIN {db_prefix}log_boards AS lb ON (lb.id_board = b.id_board AND lb.id_member = {int:current_member})
701
		WHERE {query_wanna_see_board}' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? '
702
			AND b.id_board != {int:recycle_board}' : '') . '
703
		ORDER BY b.num_posts DESC
704
		LIMIT ' . $num_top,
705
		array(
706
			'current_member' => $user_info['id'],
707
			'recycle_board' => (int) $modSettings['recycle_board'],
708
		)
709
	);
710
	$boards = array();
711
	while ($row = $smcFunc['db_fetch_assoc']($request))
712
		$boards[] = array(
713
			'id' => $row['id_board'],
714
			'num_posts' => $row['num_posts'],
715
			'num_topics' => $row['num_topics'],
716
			'name' => $row['name'],
717
			'new' => empty($row['is_read']),
718
			'href' => $scripturl . '?board=' . $row['id_board'] . '.0',
719
			'link' => '<a href="' . $scripturl . '?board=' . $row['id_board'] . '.0">' . $row['name'] . '</a>'
720
		);
721
	$smcFunc['db_free_result']($request);
722
723
	// If mods want to do somthing with this list of boards, let them do that now.
724
	call_integration_hook('integrate_ssi_topBoards', array(&$boards));
725
726
	// If we shouldn't output or have nothing to output, just jump out.
727
	if ($output_method != 'echo' || empty($boards))
728
		return $boards;
729
730
	echo '
731
		<table class="ssi_table">
732
			<tr>
733
				<th style="text-align: left">', $txt['board'], '</th>
734
				<th style="text-align: left">', $txt['board_topics'], '</th>
735
				<th style="text-align: left">', $txt['posts'], '</th>
736
			</tr>';
737
	foreach ($boards as $sBoard)
738
		echo '
739
			<tr>
740
				<td>', $sBoard['link'], $sBoard['new'] ? ' <a href="' . $sBoard['href'] . '"><span class="new_posts">' . $txt['new'] . '</span></a>' : '', '</td>
741
				<td style="text-align: right">', comma_format($sBoard['num_topics']), '</td>
742
				<td style="text-align: right">', comma_format($sBoard['num_posts']), '</td>
743
			</tr>';
744
	echo '
745
		</table>';
746
}
747
748
// Shows the top topics.
749
/**
750
 * Shows a list of top topics based on views or replies
751
 * @param string $type Can be either replies or views
752
 * @param int $num_topics How many topics to display
753
 * @param string $output_method The output method. If 'echo', displays a list of topics, otherwise returns an array of info about them.
754
 * @return void|array Either displays a list of topics or returns an array of info about them, depending on output_method.
755
 */
756
function ssi_topTopics($type = 'replies', $num_topics = 10, $output_method = 'echo')
757
{
758
	global $txt, $scripturl, $modSettings, $smcFunc;
759
760
	if ($modSettings['totalMessages'] > 100000)
761
	{
762
		// @todo Why don't we use {query(_wanna)_see_board}?
763
		$request = $smcFunc['db_query']('', '
764
			SELECT id_topic
765
			FROM {db_prefix}topics
766
			WHERE num_' . ($type != 'replies' ? 'views' : 'replies') . ' != 0' . ($modSettings['postmod_active'] ? '
767
				AND approved = {int:is_approved}' : '') . '
768
			ORDER BY num_' . ($type != 'replies' ? 'views' : 'replies') . ' DESC
769
			LIMIT {int:limit}',
770
			array(
771
				'is_approved' => 1,
772
				'limit' => $num_topics > 100 ? ($num_topics + ($num_topics / 2)) : 100,
773
			)
774
		);
775
		$topic_ids = array();
776
		while ($row = $smcFunc['db_fetch_assoc']($request))
777
			$topic_ids[] = $row['id_topic'];
778
		$smcFunc['db_free_result']($request);
779
	}
780
	else
781
		$topic_ids = array();
782
783
	$request = $smcFunc['db_query']('', '
784
		SELECT m.subject, m.id_topic, t.num_views, t.num_replies
785
		FROM {db_prefix}topics AS t
786
			INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)
787
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
788
		WHERE {query_wanna_see_board}' . ($modSettings['postmod_active'] ? '
789
			AND t.approved = {int:is_approved}' : '') . (!empty($topic_ids) ? '
790
			AND t.id_topic IN ({array_int:topic_list})' : '') . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? '
791
			AND b.id_board != {int:recycle_enable}' : '') . '
792
		ORDER BY t.num_' . ($type != 'replies' ? 'views' : 'replies') . ' DESC
793
		LIMIT {int:limit}',
794
		array(
795
			'topic_list' => $topic_ids,
796
			'is_approved' => 1,
797
			'recycle_enable' => $modSettings['recycle_board'],
798
			'limit' => $num_topics,
799
		)
800
	);
801
	$topics = array();
802
	while ($row = $smcFunc['db_fetch_assoc']($request))
803
	{
804
		censorText($row['subject']);
805
806
		$topics[] = array(
807
			'id' => $row['id_topic'],
808
			'subject' => $row['subject'],
809
			'num_replies' => $row['num_replies'],
810
			'num_views' => $row['num_views'],
811
			'href' => $scripturl . '?topic=' . $row['id_topic'] . '.0',
812
			'link' => '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.0">' . $row['subject'] . '</a>',
813
		);
814
	}
815
	$smcFunc['db_free_result']($request);
816
817
	// If mods want to do somthing with this list of topics, let them do that now.
818
	call_integration_hook('integrate_ssi_topTopics', array(&$topics, $type));
819
820
	if ($output_method != 'echo' || empty($topics))
821
		return $topics;
822
823
	echo '
824
		<table class="ssi_table">
825
			<tr>
826
				<th style="text-align: left"></th>
827
				<th style="text-align: left">', $txt['views'], '</th>
828
				<th style="text-align: left">', $txt['replies'], '</th>
829
			</tr>';
830
	foreach ($topics as $sTopic)
831
		echo '
832
			<tr>
833
				<td style="text-align: left">
834
					', $sTopic['link'], '
835
				</td>
836
				<td style="text-align: right">', comma_format($sTopic['num_views']), '</td>
837
				<td style="text-align: right">', comma_format($sTopic['num_replies']), '</td>
838
			</tr>';
839
	echo '
840
		</table>';
841
}
842
843
/**
844
 * Top topics based on replies
845
 * @param int $num_topics How many topics to show
846
 * @param string $output_method The output method. If 'echo', displays a list of topics, otherwise returns an array of info about them
847
 * @return void|array Either displays a list of top topics or returns an array of info about them, depending on output_method.
848
 */
849
function ssi_topTopicsReplies($num_topics = 10, $output_method = 'echo')
850
{
851
	return ssi_topTopics('replies', $num_topics, $output_method);
852
}
853
854
/**
855
 * Top topics based on views
856
 * @param int $num_topics How many topics to show
857
 * @param string $output_method The output method. If 'echo', displays a list of topics, otherwise returns an array of info about them
858
 * @return void|array Either displays a list of top topics or returns an array of info about them, depending on output_method.
859
 */
860
function ssi_topTopicsViews($num_topics = 10, $output_method = 'echo')
861
{
862
	return ssi_topTopics('views', $num_topics, $output_method);
863
}
864
865
/**
866
 * Show a link to the latest member: Please welcome, Someone, our latest member.
867
 * @param string $output_method The output method. If 'echo', returns a string with a link to the latest member's profile, otherwise returns an array of info about them.
868
 * @return void|array Displays a "welcome" message for the latest member or returns an array of info about them, depending on output_method.
869
 */
870
function ssi_latestMember($output_method = 'echo')
871
{
872
	global $txt, $context;
873
874
	if ($output_method == 'echo')
875
		echo '
876
	', sprintf($txt['welcome_newest_member'], $context['common_stats']['latest_member']['link']), '<br>';
877
	else
878
		return $context['common_stats']['latest_member'];
879
}
880
881
/**
882
 * Fetches a random member.
883
 * @param string $random_type If 'day', only fetches a new random member once a day.
884
 * @param string $output_method The output method. If 'echo', displays a link to the member's profile, otherwise returns an array of info about them.
885
 * @return void|array Displays a link to a random member's profile or returns an array of info about them depending on output_method.
886
 */
887
function ssi_randomMember($random_type = '', $output_method = 'echo')
888
{
889
	global $modSettings;
890
891
	// If we're looking for something to stay the same each day then seed the generator.
892
	if ($random_type == 'day')
893
	{
894
		// Set the seed to change only once per day.
895
		mt_srand(floor(time() / 86400));
896
	}
897
898
	// Get the lowest ID we're interested in.
899
	$member_id = mt_rand(1, $modSettings['latestMember']);
900
901
	$where_query = '
902
		id_member >= {int:selected_member}
903
		AND is_activated = {int:is_activated}';
904
905
	$query_where_params = array(
906
		'selected_member' => $member_id,
907
		'is_activated' => 1,
908
	);
909
910
	$result = ssi_queryMembers($where_query, $query_where_params, 1, 'id_member ASC', $output_method);
911
912
	// If we got nothing do the reverse - in case of unactivated members.
913
	if (empty($result))
914
	{
915
		$where_query = '
916
			id_member <= {int:selected_member}
917
			AND is_activated = {int:is_activated}';
918
919
		$query_where_params = array(
920
			'selected_member' => $member_id,
921
			'is_activated' => 1,
922
		);
923
924
		$result = ssi_queryMembers($where_query, $query_where_params, 1, 'id_member DESC', $output_method);
925
	}
926
927
	// Just to be sure put the random generator back to something... random.
928
	if ($random_type != '')
929
		mt_srand(time());
930
931
	return $result;
932
}
933
934
/**
935
 * Fetch specific members
936
 * @param array $member_ids The IDs of the members to fetch
937
 * @param string $output_method The output method. If 'echo', displays a list of links to the members' profiles, otherwise returns an array of info about them.
938
 * @return void|array Displays links to the specified members' profiles or returns an array of info about them, depending on output_method.
939
 */
940
function ssi_fetchMember($member_ids = array(), $output_method = 'echo')
941
{
942
	if (empty($member_ids))
943
		return;
944
945
	// Can have more than one member if you really want...
946
	$member_ids = is_array($member_ids) ? $member_ids : array($member_ids);
947
948
	// Restrict it right!
949
	$query_where = '
950
		id_member IN ({array_int:member_list})';
951
952
	$query_where_params = array(
953
		'member_list' => $member_ids,
954
	);
955
956
	// Then make the query and dump the data.
957
	return ssi_queryMembers($query_where, $query_where_params, '', 'id_member', $output_method);
958
}
959
960
/**
961
 * Get al members in the specified group
962
 * @param int $group_id The ID of the group to get members from
963
 * @param string $output_method The output method. If 'echo', returns a list of group members, otherwise returns an array of info about them.
964
 * @return void|array Displays a list of group members or returns an array of info about them, depending on output_method.
965
 */
966
function ssi_fetchGroupMembers($group_id = null, $output_method = 'echo')
967
{
968
	if ($group_id === null)
969
		return;
970
971
	$query_where = '
972
		id_group = {int:id_group}
973
		OR id_post_group = {int:id_group}
974
		OR FIND_IN_SET({int:id_group}, additional_groups) != 0';
975
976
	$query_where_params = array(
977
		'id_group' => $group_id,
978
	);
979
980
	return ssi_queryMembers($query_where, $query_where_params, '', 'real_name', $output_method);
981
}
982
983
/**
984
 * Pulls info about members based on the specified parameters. Used by other functions to eliminate duplication.
985
 * @param string $query_where The info for the WHERE clause of the query
986
 * @param array $query_where_params The parameters for the WHERE clause
987
 * @param string|int $query_limit The number of rows to return or an empty string to return all
988
 * @param string $query_order The info for the ORDER BY clause of the query
989
 * @param string $output_method The output method. If 'echo', displays a list of members, otherwise returns an array of info about them
990
 * @return void|array Displays a list of members or returns an array of info about them, depending on output_method.
991
 */
992
function ssi_queryMembers($query_where = null, $query_where_params = array(), $query_limit = '', $query_order = 'id_member DESC', $output_method = 'echo')
993
{
994
	global $smcFunc, $memberContext;
995
996
	if ($query_where === null)
997
		return;
998
999
	// Fetch the members in question.
1000
	$request = $smcFunc['db_query']('', '
1001
		SELECT id_member
1002
		FROM {db_prefix}members
1003
		WHERE ' . $query_where . '
1004
		ORDER BY ' . $query_order . '
1005
		' . ($query_limit == '' ? '' : 'LIMIT ' . $query_limit),
1006
		array_merge($query_where_params, array(
1007
		))
1008
	);
1009
	$members = array();
1010
	while ($row = $smcFunc['db_fetch_assoc']($request))
1011
		$members[] = $row['id_member'];
1012
	$smcFunc['db_free_result']($request);
1013
1014
	if (empty($members))
1015
		return array();
1016
1017
	// If mods want to do somthing with this list of members, let them do that now.
1018
	call_integration_hook('integrate_ssi_queryMembers', array(&$members));
1019
1020
	// Load the members.
1021
	loadMemberData($members);
1022
1023
	// Draw the table!
1024
	if ($output_method == 'echo')
1025
		echo '
1026
		<table style="border: none" class="ssi_table">';
1027
1028
	$query_members = array();
1029
	foreach ($members as $member)
1030
	{
1031
		// Load their context data.
1032
		if (!loadMemberContext($member))
1033
			continue;
1034
1035
		// Store this member's information.
1036
		$query_members[$member] = $memberContext[$member];
1037
1038
		// Only do something if we're echo'ing.
1039
		if ($output_method == 'echo')
1040
			echo '
1041
			<tr>
1042
				<td style="text-align: right; vertical-align: top; white-space: nowrap">
1043
					', $query_members[$member]['link'], '
1044
					<br>', $query_members[$member]['blurb'], '
1045
					<br>', $query_members[$member]['avatar']['image'], '
1046
				</td>
1047
			</tr>';
1048
	}
1049
1050
	// End the table if appropriate.
1051
	if ($output_method == 'echo')
1052
		echo '
1053
		</table>';
1054
1055
	// Send back the data.
1056
	return $query_members;
1057
}
1058
1059
/**
1060
 * Show some basic stats:   Total This: XXXX, etc.
1061
 * @param string $output_method The output method. If 'echo', displays the stats, otherwise returns an array of info about them
1062
 * @return void|array Doesn't return anything if the user can't view stats. Otherwise either displays the stats or returns an array of info about them, depending on output_method.
1063
 */
1064
function ssi_boardStats($output_method = 'echo')
1065
{
1066
	global $txt, $scripturl, $modSettings, $smcFunc;
1067
1068
	if (!allowedTo('view_stats'))
1069
		return;
1070
1071
	$totals = array(
1072
		'members' => $modSettings['totalMembers'],
1073
		'posts' => $modSettings['totalMessages'],
1074
		'topics' => $modSettings['totalTopics']
1075
	);
1076
1077
	$result = $smcFunc['db_query']('', '
1078
		SELECT COUNT(*)
1079
		FROM {db_prefix}boards',
1080
		array(
1081
		)
1082
	);
1083
	list ($totals['boards']) = $smcFunc['db_fetch_row']($result);
1084
	$smcFunc['db_free_result']($result);
1085
1086
	$result = $smcFunc['db_query']('', '
1087
		SELECT COUNT(*)
1088
		FROM {db_prefix}categories',
1089
		array(
1090
		)
1091
	);
1092
	list ($totals['categories']) = $smcFunc['db_fetch_row']($result);
1093
	$smcFunc['db_free_result']($result);
1094
1095
	// If mods want to do somthing with the board stats, let them do that now.
1096
	call_integration_hook('integrate_ssi_boardStats', array(&$totals));
1097
1098
	if ($output_method != 'echo')
1099
		return $totals;
1100
1101
	echo '
1102
		', $txt['total_members'], ': <a href="', $scripturl . '?action=mlist">', comma_format($totals['members']), '</a><br>
1103
		', $txt['total_posts'], ': ', comma_format($totals['posts']), '<br>
1104
		', $txt['total_topics'], ': ', comma_format($totals['topics']), ' <br>
1105
		', $txt['total_cats'], ': ', comma_format($totals['categories']), '<br>
1106
		', $txt['total_boards'], ': ', comma_format($totals['boards']);
1107
}
1108
1109
/**
1110
 * Shows a list of online users:  YY Guests, ZZ Users and then a list...
1111
 * @param string $output_method The output method. If 'echo', displays a list, otherwise returns an array of info about the online users.
1112
 * @return void|array Either displays a list of online users or returns an array of info about them, depending on output_method.
1113
 */
1114
function ssi_whosOnline($output_method = 'echo')
1115
{
1116
	global $user_info, $txt, $sourcedir, $settings;
1117
1118
	require_once($sourcedir . '/Subs-MembersOnline.php');
1119
	$membersOnlineOptions = array(
1120
		'show_hidden' => allowedTo('moderate_forum'),
1121
	);
1122
	$return = getMembersOnlineStats($membersOnlineOptions);
1123
1124
	// If mods want to do somthing with the list of who is online, let them do that now.
1125
	call_integration_hook('integrate_ssi_whosOnline', array(&$return));
1126
1127
	// Add some redundancy for backwards compatibility reasons.
1128
	if ($output_method != 'echo')
1129
		return $return + array(
1130
			'users' => $return['users_online'],
1131
			'guests' => $return['num_guests'],
1132
			'hidden' => $return['num_users_hidden'],
1133
			'buddies' => $return['num_buddies'],
1134
			'num_users' => $return['num_users_online'],
1135
			'total_users' => $return['num_users_online'] + $return['num_guests'],
1136
		);
1137
1138
	echo '
1139
		', comma_format($return['num_guests']), ' ', $return['num_guests'] == 1 ? $txt['guest'] : $txt['guests'], ', ', comma_format($return['num_users_online']), ' ', $return['num_users_online'] == 1 ? $txt['user'] : $txt['users'];
1140
1141
	$bracketList = array();
1142 View Code Duplication
	if (!empty($user_info['buddies']))
1143
		$bracketList[] = comma_format($return['num_buddies']) . ' ' . ($return['num_buddies'] == 1 ? $txt['buddy'] : $txt['buddies']);
1144 View Code Duplication
	if (!empty($return['num_spiders']))
1145
		$bracketList[] = comma_format($return['num_spiders']) . ' ' . ($return['num_spiders'] == 1 ? $txt['spider'] : $txt['spiders']);
1146
	if (!empty($return['num_users_hidden']))
1147
		$bracketList[] = comma_format($return['num_users_hidden']) . ' ' . $txt['hidden'];
1148
1149
	if (!empty($bracketList))
1150
		echo ' (' . implode(', ', $bracketList) . ')';
1151
1152
	echo '<br>
1153
			', implode(', ', $return['list_users_online']);
1154
1155
	// Showing membergroups?
1156 View Code Duplication
	if (!empty($settings['show_group_key']) && !empty($return['membergroups']))
1157
		echo '<br>
1158
			[' . implode(']&nbsp;&nbsp;[', $return['membergroups']) . ']';
1159
}
1160
1161
/**
1162
 * Just like whosOnline except it also logs the online presence.
1163
 * @param string $output_method The output method. If 'echo', displays a list, otherwise returns an array of info about the online users.
1164
 * @return void|array Either displays a list of online users or returns an aray of info about them, depending on output_method.
1165
 */
1166
function ssi_logOnline($output_method = 'echo')
1167
{
1168
	writeLog();
1169
1170
	if ($output_method != 'echo')
1171
		return ssi_whosOnline($output_method);
1172
	else
1173
		ssi_whosOnline($output_method);
1174
}
1175
1176
// Shows a login box.
1177
/**
1178
 * Shows a login box
1179
 * @param string $redirect_to The URL to redirect the user to after they login
1180
 * @param string $output_method The output method. If 'echo' and the user is a guest, displays a login box, otherwise returns whether the user is a guest
1181
 * @return void|bool Either displays a login box or returns whether the user is a guest, depending on whether the user is logged in and output_method.
1182
 */
1183
function ssi_login($redirect_to = '', $output_method = 'echo')
1184
{
1185
	global $scripturl, $txt, $user_info, $context;
1186
1187
	if ($redirect_to != '')
1188
		$_SESSION['login_url'] = $redirect_to;
1189
1190
	if ($output_method != 'echo' || !$user_info['is_guest'])
1191
		return $user_info['is_guest'];
1192
1193
	// Create a login token
1194
	createToken('login');
1195
1196
	echo '
1197
		<form action="', $scripturl, '?action=login2" method="post" accept-charset="', $context['character_set'], '">
1198
			<table style="border: none" class="ssi_table">
1199
				<tr>
1200
					<td style="text-align: right; border-spacing: 1"><label for="user">', $txt['username'], ':</label>&nbsp;</td>
1201
					<td><input type="text" id="user" name="user" size="9" value="', $user_info['username'], '" class="input_text"></td>
1202
				</tr><tr>
1203
					<td style="text-align: right; border-spacing: 1"><label for="passwrd">', $txt['password'], ':</label>&nbsp;</td>
1204
					<td><input type="password" name="passwrd" id="passwrd" size="9" class="input_password"></td>
1205
				</tr>
1206
				<tr>
1207
					<td>
1208
						<input type="hidden" name="cookielength" value="-1">
1209
						<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
1210
						<input type="hidden" name="', $context['login_token_var'], '" value="', $context['login_token'], '">
1211
					</td>
1212
					<td><input type="submit" value="', $txt['login'], '" class="button_submit"></td>
1213
				</tr>
1214
			</table>
1215
		</form>';
1216
1217
}
1218
1219
/**
1220
 * Show the top poll based on votes
1221
 * @param string $output_method The output method. If 'echo', displays the poll, otherwise returns an array of info about it
1222
 * @return void|array Either shows the top poll or returns an array of info about it, depending on output_method.
1223
 */
1224
function ssi_topPoll($output_method = 'echo')
1225
{
1226
	// Just use recentPoll, no need to duplicate code...
1227
	return ssi_recentPoll(true, $output_method);
1228
}
1229
1230
// Show the most recently posted poll.
1231
/**
1232
 * Shows the most recent poll
1233
 * @param bool $topPollInstead Whether to show the top poll (based on votes) instead of the most recent one
1234
 * @param string $output_method The output method. If 'echo', displays the poll, otherwise returns an array of info about it.
1235
 * @return void|array Either shows the poll or returns an array of info about it, depending on output_method.
1236
 */
1237
function ssi_recentPoll($topPollInstead = false, $output_method = 'echo')
1238
{
1239
	global $txt, $boardurl, $user_info, $context, $smcFunc, $modSettings;
1240
1241
	$boardsAllowed = array_intersect(boardsAllowedTo('poll_view'), boardsAllowedTo('poll_vote'));
1242
1243
	if (empty($boardsAllowed))
1244
		return array();
1245
1246
	$request = $smcFunc['db_query']('', '
1247
		SELECT p.id_poll, p.question, t.id_topic, p.max_votes, p.guest_vote, p.hide_results, p.expire_time
1248
		FROM {db_prefix}polls AS p
1249
			INNER JOIN {db_prefix}topics AS t ON (t.id_poll = p.id_poll' . ($modSettings['postmod_active'] ? ' AND t.approved = {int:is_approved}' : '') . ')
1250
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)' . ($topPollInstead ? '
1251
			INNER JOIN {db_prefix}poll_choices AS pc ON (pc.id_poll = p.id_poll)' : '') . '
1252
			LEFT JOIN {db_prefix}log_polls AS lp ON (lp.id_poll = p.id_poll AND lp.id_member > {int:no_member} AND lp.id_member = {int:current_member})
1253
		WHERE p.voting_locked = {int:voting_opened}
1254
			AND (p.expire_time = {int:no_expiration} OR {int:current_time} < p.expire_time)
1255
			AND ' . ($user_info['is_guest'] ? 'p.guest_vote = {int:guest_vote_allowed}' : 'lp.id_choice IS NULL') . '
1256
			AND {query_wanna_see_board}' . (!in_array(0, $boardsAllowed) ? '
1257
			AND b.id_board IN ({array_int:boards_allowed_list})' : '') . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? '
1258
			AND b.id_board != {int:recycle_enable}' : '') . '
1259
		ORDER BY ' . ($topPollInstead ? 'pc.votes' : 'p.id_poll') . ' DESC
1260
		LIMIT 1',
1261
		array(
1262
			'current_member' => $user_info['id'],
1263
			'boards_allowed_list' => $boardsAllowed,
1264
			'is_approved' => 1,
1265
			'guest_vote_allowed' => 1,
1266
			'no_member' => 0,
1267
			'voting_opened' => 0,
1268
			'no_expiration' => 0,
1269
			'current_time' => time(),
1270
			'recycle_enable' => $modSettings['recycle_board'],
1271
		)
1272
	);
1273
	$row = $smcFunc['db_fetch_assoc']($request);
1274
	$smcFunc['db_free_result']($request);
1275
1276
	// This user has voted on all the polls.
1277
	if (empty($row) || !is_array($row))
1278
		return array();
1279
1280
	// If this is a guest who's voted we'll through ourselves to show poll to show the results.
1281
	if ($user_info['is_guest'] && (!$row['guest_vote'] || (isset($_COOKIE['guest_poll_vote']) && in_array($row['id_poll'], explode(',', $_COOKIE['guest_poll_vote'])))))
1282
		return ssi_showPoll($row['id_topic'], $output_method);
1283
1284
	$request = $smcFunc['db_query']('', '
1285
		SELECT COUNT(DISTINCT id_member)
1286
		FROM {db_prefix}log_polls
1287
		WHERE id_poll = {int:current_poll}',
1288
		array(
1289
			'current_poll' => $row['id_poll'],
1290
		)
1291
	);
1292
	list ($total) = $smcFunc['db_fetch_row']($request);
1293
	$smcFunc['db_free_result']($request);
1294
1295
	$request = $smcFunc['db_query']('', '
1296
		SELECT id_choice, label, votes
1297
		FROM {db_prefix}poll_choices
1298
		WHERE id_poll = {int:current_poll}',
1299
		array(
1300
			'current_poll' => $row['id_poll'],
1301
		)
1302
	);
1303
	$sOptions = array();
1304 View Code Duplication
	while ($rowChoice = $smcFunc['db_fetch_assoc']($request))
1305
	{
1306
		censorText($rowChoice['label']);
1307
1308
		$sOptions[$rowChoice['id_choice']] = array($rowChoice['label'], $rowChoice['votes']);
1309
	}
1310
	$smcFunc['db_free_result']($request);
1311
1312
	// Can they view it?
1313
	$is_expired = !empty($row['expire_time']) && $row['expire_time'] < time();
1314
	$allow_view_results = allowedTo('moderate_board') || $row['hide_results'] == 0 || $is_expired;
1315
1316
	$return = array(
1317
		'id' => $row['id_poll'],
1318
		'image' => 'poll',
1319
		'question' => $row['question'],
1320
		'total_votes' => $total,
1321
		'is_locked' => false,
1322
		'topic' => $row['id_topic'],
1323
		'allow_view_results' => $allow_view_results,
1324
		'options' => array()
1325
	);
1326
1327
	// Calculate the percentages and bar lengths...
1328
	$divisor = $return['total_votes'] == 0 ? 1 : $return['total_votes'];
1329
	foreach ($sOptions as $i => $option)
1330
	{
1331
		$bar = floor(($option[1] * 100) / $divisor);
1332
		$return['options'][$i] = array(
1333
			'id' => 'options-' . ($topPollInstead ? 'top-' : 'recent-') . $i,
1334
			'percent' => $bar,
1335
			'votes' => $option[1],
1336
			'option' => parse_bbc($option[0]),
1337
			'vote_button' => '<input type="' . ($row['max_votes'] > 1 ? 'checkbox' : 'radio') . '" name="options[]" id="options-' . ($topPollInstead ? 'top-' : 'recent-') . $i . '" value="' . $i . '" class="input_' . ($row['max_votes'] > 1 ? 'check' : 'radio') . '">'
1338
		);
1339
	}
1340
1341
	$return['allowed_warning'] = $row['max_votes'] > 1 ? sprintf($txt['poll_options6'], min(count($sOptions), $row['max_votes'])) : '';
1342
1343
	// If mods want to do somthing with this list of polls, let them do that now.
1344
	call_integration_hook('integrate_ssi_recentPoll', array(&$return, $topPollInstead));
1345
1346
	if ($output_method != 'echo')
1347
		return $return;
1348
1349
	if ($allow_view_results)
1350
	{
1351
		echo '
1352
		<form class="ssi_poll" action="', $boardurl, '/SSI.php?ssi_function=pollVote" method="post" accept-charset="', $context['character_set'], '">
1353
			<strong>', $return['question'], '</strong><br>
1354
			', !empty($return['allowed_warning']) ? $return['allowed_warning'] . '<br>' : '';
1355
1356 View Code Duplication
		foreach ($return['options'] as $option)
1357
			echo '
1358
			<label for="', $option['id'], '">', $option['vote_button'], ' ', $option['option'], '</label><br>';
1359
1360
		echo '
1361
			<input type="submit" value="', $txt['poll_vote'], '" class="button_submit">
1362
			<input type="hidden" name="poll" value="', $return['id'], '">
1363
			<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '">
1364
		</form>';
1365
	}
1366
	else
1367
		echo $txt['poll_cannot_see'];
1368
}
1369
1370
/**
1371
 * Shows the poll from the specified topic
1372
 * @param null|int $topic The topic to show the poll from. If null, $_REQUEST['ssi_topic'] will be used instead.
1373
 * @param string $output_method The output method. If 'echo', displays the poll, otherwise returns an array of info about it.
1374
 * @return void|array Either displays the poll or returns an array of info about it, depending on output_method.
1375
 */
1376
function ssi_showPoll($topic = null, $output_method = 'echo')
1377
{
1378
	global $txt, $boardurl, $user_info, $context, $smcFunc, $modSettings;
1379
1380
	$boardsAllowed = boardsAllowedTo('poll_view');
1381
1382
	if (empty($boardsAllowed))
1383
		return array();
1384
1385
	if ($topic === null && isset($_REQUEST['ssi_topic']))
1386
		$topic = (int) $_REQUEST['ssi_topic'];
1387
	else
1388
		$topic = (int) $topic;
1389
1390
	$request = $smcFunc['db_query']('', '
1391
		SELECT
1392
			p.id_poll, p.question, p.voting_locked, p.hide_results, p.expire_time, p.max_votes, p.guest_vote, b.id_board
1393
		FROM {db_prefix}topics AS t
1394
			INNER JOIN {db_prefix}polls AS p ON (p.id_poll = t.id_poll)
1395
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
1396
		WHERE t.id_topic = {int:current_topic}
1397
			AND {query_see_board}' . (!in_array(0, $boardsAllowed) ? '
1398
			AND b.id_board IN ({array_int:boards_allowed_see})' : '') . ($modSettings['postmod_active'] ? '
1399
			AND t.approved = {int:is_approved}' : '') . '
1400
		LIMIT 1',
1401
		array(
1402
			'current_topic' => $topic,
1403
			'boards_allowed_see' => $boardsAllowed,
1404
			'is_approved' => 1,
1405
		)
1406
	);
1407
1408
	// Either this topic has no poll, or the user cannot view it.
1409
	if ($smcFunc['db_num_rows']($request) == 0)
1410
		return array();
1411
1412
	$row = $smcFunc['db_fetch_assoc']($request);
1413
	$smcFunc['db_free_result']($request);
1414
1415
	// Check if they can vote.
1416
	$already_voted = false;
1417
	if (!empty($row['expire_time']) && $row['expire_time'] < time())
1418
		$allow_vote = false;
1419
	elseif ($user_info['is_guest'])
1420
	{
1421
		// There's a difference between "allowed to vote" and "already voted"...
1422
		$allow_vote = $row['guest_vote'];
1423
1424
		// Did you already vote?
1425
		if (isset($_COOKIE['guest_poll_vote']) && in_array($row['id_poll'], explode(',', $_COOKIE['guest_poll_vote'])))
1426
		{
1427
			$already_voted = true;
1428
		}
1429
	}
1430
	elseif (!empty($row['voting_locked']) || !allowedTo('poll_vote', $row['id_board']))
1431
		$allow_vote = false;
1432
	else
1433
	{
1434
		$request = $smcFunc['db_query']('', '
1435
			SELECT id_member
1436
			FROM {db_prefix}log_polls
1437
			WHERE id_poll = {int:current_poll}
1438
				AND id_member = {int:current_member}
1439
			LIMIT 1',
1440
			array(
1441
				'current_member' => $user_info['id'],
1442
				'current_poll' => $row['id_poll'],
1443
			)
1444
		);
1445
		$allow_vote = $smcFunc['db_num_rows']($request) == 0;
1446
		$already_voted = $allow_vote;
1447
		$smcFunc['db_free_result']($request);
1448
	}
1449
1450
	// Can they view?
1451
	$is_expired = !empty($row['expire_time']) && $row['expire_time'] < time();
1452
	$allow_view_results = allowedTo('moderate_board') || $row['hide_results'] == 0 || ($row['hide_results'] == 1 && $already_voted) || $is_expired;
1453
1454
	$request = $smcFunc['db_query']('', '
1455
		SELECT COUNT(DISTINCT id_member)
1456
		FROM {db_prefix}log_polls
1457
		WHERE id_poll = {int:current_poll}',
1458
		array(
1459
			'current_poll' => $row['id_poll'],
1460
		)
1461
	);
1462
	list ($total) = $smcFunc['db_fetch_row']($request);
1463
	$smcFunc['db_free_result']($request);
1464
1465
	$request = $smcFunc['db_query']('', '
1466
		SELECT id_choice, label, votes
1467
		FROM {db_prefix}poll_choices
1468
		WHERE id_poll = {int:current_poll}',
1469
		array(
1470
			'current_poll' => $row['id_poll'],
1471
		)
1472
	);
1473
	$sOptions = array();
1474
	$total_votes = 0;
1475 View Code Duplication
	while ($rowChoice = $smcFunc['db_fetch_assoc']($request))
1476
	{
1477
		censorText($rowChoice['label']);
1478
1479
		$sOptions[$rowChoice['id_choice']] = array($rowChoice['label'], $rowChoice['votes']);
1480
		$total_votes += $rowChoice['votes'];
1481
	}
1482
	$smcFunc['db_free_result']($request);
1483
1484
	$return = array(
1485
		'id' => $row['id_poll'],
1486
		'image' => empty($row['voting_locked']) ? 'poll' : 'locked_poll',
1487
		'question' => $row['question'],
1488
		'total_votes' => $total,
1489
		'is_locked' => !empty($row['voting_locked']),
1490
		'allow_vote' => $allow_vote,
1491
		'allow_view_results' => $allow_view_results,
1492
		'topic' => $topic
1493
	);
1494
1495
	// Calculate the percentages and bar lengths...
1496
	$divisor = $total_votes == 0 ? 1 : $total_votes;
1497
	foreach ($sOptions as $i => $option)
1498
	{
1499
		$bar = floor(($option[1] * 100) / $divisor);
1500
		$return['options'][$i] = array(
1501
			'id' => 'options-' . $i,
1502
			'percent' => $bar,
1503
			'votes' => $option[1],
1504
			'option' => parse_bbc($option[0]),
1505
			'vote_button' => '<input type="' . ($row['max_votes'] > 1 ? 'checkbox' : 'radio') . '" name="options[]" id="options-' . $i . '" value="' . $i . '" class="input_' . ($row['max_votes'] > 1 ? 'check' : 'radio') . '">'
1506
		);
1507
	}
1508
1509
	$return['allowed_warning'] = $row['max_votes'] > 1 ? sprintf($txt['poll_options6'], min(count($sOptions), $row['max_votes'])) : '';
1510
1511
	// If mods want to do somthing with this poll, let them do that now.
1512
	call_integration_hook('integrate_ssi_showPoll', array(&$return));
1513
1514
	if ($output_method != 'echo')
1515
		return $return;
1516
1517
	if ($return['allow_vote'])
1518
	{
1519
		echo '
1520
			<form class="ssi_poll" action="', $boardurl, '/SSI.php?ssi_function=pollVote" method="post" accept-charset="', $context['character_set'], '">
1521
				<strong>', $return['question'], '</strong><br>
1522
				', !empty($return['allowed_warning']) ? $return['allowed_warning'] . '<br>' : '';
1523
1524 View Code Duplication
		foreach ($return['options'] as $option)
1525
			echo '
1526
				<label for="', $option['id'], '">', $option['vote_button'], ' ', $option['option'], '</label><br>';
1527
1528
		echo '
1529
				<input type="submit" value="', $txt['poll_vote'], '" class="button_submit">
1530
				<input type="hidden" name="poll" value="', $return['id'], '">
1531
				<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '">
1532
			</form>';
1533
	}
1534
	else
1535
	{
1536
		echo '
1537
			<div class="ssi_poll">
1538
				<strong>', $return['question'], '</strong>
1539
				<dl>';
1540
1541
		foreach ($return['options'] as $option)
1542
		{
1543
			echo '
1544
					<dt>', $option['option'], '</dt>
1545
					<dd>';
1546
1547
			if ($return['allow_view_results'])
1548
			{
1549
				echo '
1550
						<div class="ssi_poll_bar" style="border: 1px solid #666; height: 1em">
1551
							<div class="ssi_poll_bar_fill" style="background: #ccf; height: 1em; width: ', $option['percent'], '%;">
1552
							</div>
1553
						</div>
1554
						', $option['votes'], ' (', $option['percent'], '%)';
1555
			}
1556
1557
			echo '
1558
					</dd>';
1559
		}
1560
1561
		echo '
1562
				</dl>', ($return['allow_view_results'] ? '
1563
				<strong>'. $txt['poll_total_voters'] . ': ' . $return['total_votes'] . '</strong>' : ''), '
1564
			</div>';
1565
	}
1566
}
1567
1568
/**
1569
 * Handles voting in a poll (done automatically)
1570
 */
1571
function ssi_pollVote()
1572
{
1573
	global $context, $db_prefix, $user_info, $sc, $smcFunc, $sourcedir, $modSettings;
1574
1575
	if (!isset($_POST[$context['session_var']]) || $_POST[$context['session_var']] != $sc || empty($_POST['options']) || !isset($_POST['poll']))
1576
	{
1577
		echo '<!DOCTYPE html>
1578
<html>
1579
<head>
1580
	<script>
1581
		history.go(-1);
1582
	</script>
1583
</head>
1584
<body>&laquo;</body>
1585
</html>';
1586
		return;
1587
	}
1588
1589
	// This can cause weird errors! (ie. copyright missing.)
1590
	checkSession();
1591
1592
	$_POST['poll'] = (int) $_POST['poll'];
1593
1594
	// Check if they have already voted, or voting is locked.
1595
	$request = $smcFunc['db_query']('', '
1596
		SELECT
1597
			p.id_poll, p.voting_locked, p.expire_time, p.max_votes, p.guest_vote,
1598
			t.id_topic,
1599
			IFNULL(lp.id_choice, -1) AS selected
1600
		FROM {db_prefix}polls AS p
1601
			INNER JOIN {db_prefix}topics AS t ON (t.id_poll = {int:current_poll})
1602
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
1603
			LEFT JOIN {db_prefix}log_polls AS lp ON (lp.id_poll = p.id_poll AND lp.id_member = {int:current_member})
1604
		WHERE p.id_poll = {int:current_poll}
1605
			AND {query_see_board}' . ($modSettings['postmod_active'] ? '
1606
			AND t.approved = {int:is_approved}' : '') . '
1607
		LIMIT 1',
1608
		array(
1609
			'current_member' => $user_info['id'],
1610
			'current_poll' => $_POST['poll'],
1611
			'is_approved' => 1,
1612
		)
1613
	);
1614
	if ($smcFunc['db_num_rows']($request) == 0)
1615
		die;
1616
	$row = $smcFunc['db_fetch_assoc']($request);
1617
	$smcFunc['db_free_result']($request);
1618
1619
	if (!empty($row['voting_locked']) || ($row['selected'] != -1 && !$user_info['is_guest']) || (!empty($row['expire_time']) && time() > $row['expire_time']))
1620
		redirectexit('topic=' . $row['id_topic'] . '.0');
1621
1622
	// Too many options checked?
1623
	if (count($_REQUEST['options']) > $row['max_votes'])
1624
		redirectexit('topic=' . $row['id_topic'] . '.0');
1625
1626
	// It's a guest who has already voted?
1627
	if ($user_info['is_guest'])
1628
	{
1629
		// Guest voting disabled?
1630
		if (!$row['guest_vote'])
1631
			redirectexit('topic=' . $row['id_topic'] . '.0');
1632
		// Already voted?
1633
		elseif (isset($_COOKIE['guest_poll_vote']) && in_array($row['id_poll'], explode(',', $_COOKIE['guest_poll_vote'])))
1634
			redirectexit('topic=' . $row['id_topic'] . '.0');
1635
	}
1636
1637
	$sOptions = array();
1638
	$inserts = array();
1639 View Code Duplication
	foreach ($_REQUEST['options'] as $id)
1640
	{
1641
		$id = (int) $id;
1642
1643
		$sOptions[] = $id;
1644
		$inserts[] = array($_POST['poll'], $user_info['id'], $id);
1645
	}
1646
1647
	// Add their vote in to the tally.
1648
	$smcFunc['db_insert']('insert',
1649
		$db_prefix . 'log_polls',
1650
		array('id_poll' => 'int', 'id_member' => 'int', 'id_choice' => 'int'),
1651
		$inserts,
1652
		array('id_poll', 'id_member', 'id_choice')
1653
	);
1654
	$smcFunc['db_query']('', '
1655
		UPDATE {db_prefix}poll_choices
1656
		SET votes = votes + 1
1657
		WHERE id_poll = {int:current_poll}
1658
			AND id_choice IN ({array_int:option_list})',
1659
		array(
1660
			'option_list' => $sOptions,
1661
			'current_poll' => $_POST['poll'],
1662
		)
1663
	);
1664
1665
	// Track the vote if a guest.
1666
	if ($user_info['is_guest'])
1667
	{
1668
		$_COOKIE['guest_poll_vote'] = !empty($_COOKIE['guest_poll_vote']) ? ($_COOKIE['guest_poll_vote'] . ',' . $row['id_poll']) : $row['id_poll'];
1669
1670
		require_once($sourcedir . '/Subs-Auth.php');
1671
		$cookie_url = url_parts(!empty($modSettings['localCookies']), !empty($modSettings['globalCookies']));
1672
		smf_setcookie('guest_poll_vote', $_COOKIE['guest_poll_vote'], time() + 2500000, $cookie_url[1], $cookie_url[0], false, false);
1673
	}
1674
1675
	redirectexit('topic=' . $row['id_topic'] . '.0');
1676
}
1677
1678
// Show a search box.
1679
/**
1680
 * Shows a search box
1681
 * @param string $output_method The output method. If 'echo', displays a search box, otherwise returns the URL of the search page.
1682
 * @return void|string Displays a search box or returns the URL to the search page depending on output_method. If you don't have permission to search, the function won't return anything.
1683
 */
1684
function ssi_quickSearch($output_method = 'echo')
1685
{
1686
	global $scripturl, $txt, $context;
1687
1688
	if (!allowedTo('search_posts'))
1689
		return;
1690
1691
	if ($output_method != 'echo')
1692
		return $scripturl . '?action=search';
1693
1694
	echo '
1695
		<form action="', $scripturl, '?action=search2" method="post" accept-charset="', $context['character_set'], '">
1696
			<input type="hidden" name="advanced" value="0"><input type="text" name="ssi_search" size="30" class="input_text"> <input type="submit" value="', $txt['search'], '" class="button_submit">
1697
		</form>';
1698
}
1699
1700
/**
1701
 * Show a random forum news item
1702
 * @param string $output_method The output method. If 'echo', shows the news item, otherwise returns it.
1703
 * @return void|string Shows or returns a random forum news item, depending on output_method.
1704
 */
1705
function ssi_news($output_method = 'echo')
1706
{
1707
	global $context;
1708
1709
	$context['random_news_line'] = !empty($context['news_lines']) ? $context['news_lines'][mt_rand(0, count($context['news_lines']) - 1)] : '';
1710
1711
	// If mods want to do somthing with the news, let them do that now. Don't need to pass the news line itself, since it is already in $context.
1712
	call_integration_hook('integrate_ssi_news');
1713
1714
	if ($output_method != 'echo')
1715
		return $context['random_news_line'];
1716
1717
	echo $context['random_news_line'];
1718
}
1719
1720
/**
1721
 * Show today's birthdays.
1722
 * @param string $output_method The output method. If 'echo', displays a list of users, otherwise returns an array of info about them.
1723
 * @return void|array Displays a list of users or returns an array of info about them depending on output_method.
1724
 */
1725
function ssi_todaysBirthdays($output_method = 'echo')
1726
{
1727
	global $scripturl, $modSettings, $user_info;
1728
1729
	if (empty($modSettings['cal_enabled']) || !allowedTo('calendar_view') || !allowedTo('profile_view'))
1730
		return;
1731
1732
	$eventOptions = array(
1733
		'include_birthdays' => true,
1734
		'num_days_shown' => empty($modSettings['cal_days_for_index']) || $modSettings['cal_days_for_index'] < 1 ? 1 : $modSettings['cal_days_for_index'],
1735
	);
1736
	$return = cache_quick_get('calendar_index_offset_' . ($user_info['time_offset'] + $modSettings['time_offset']), 'Subs-Calendar.php', 'cache_getRecentEvents', array($eventOptions));
1737
1738
	// The ssi_todaysCalendar variants all use the same hook and just pass on $eventOptions so the hooked code can distinguish different cases if necessary
1739
	call_integration_hook('integrate_ssi_calendar', array(&$return, $eventOptions));
1740
1741
	if ($output_method != 'echo')
1742
		return $return['calendar_birthdays'];
1743
1744
	foreach ($return['calendar_birthdays'] as $member)
1745
		echo '
1746
			<a href="', $scripturl, '?action=profile;u=', $member['id'], '"><span class="fix_rtl_names">' . $member['name'] . '</span>' . (isset($member['age']) ? ' (' . $member['age'] . ')' : '') . '</a>' . (!$member['is_last'] ? ', ' : '');
1747
}
1748
1749
/**
1750
 * Shows today's holidays.
1751
 * @param string $output_method The output method. If 'echo', displays a list of holidays, otherwise returns an array of info about them.
1752
 * @return void|array Displays a list of holidays or returns an array of info about them depending on output_method
1753
 */
1754
function ssi_todaysHolidays($output_method = 'echo')
1755
{
1756
	global $modSettings, $user_info;
1757
1758
	if (empty($modSettings['cal_enabled']) || !allowedTo('calendar_view'))
1759
		return;
1760
1761
	$eventOptions = array(
1762
		'include_holidays' => true,
1763
		'num_days_shown' => empty($modSettings['cal_days_for_index']) || $modSettings['cal_days_for_index'] < 1 ? 1 : $modSettings['cal_days_for_index'],
1764
	);
1765
	$return = cache_quick_get('calendar_index_offset_' . ($user_info['time_offset'] + $modSettings['time_offset']), 'Subs-Calendar.php', 'cache_getRecentEvents', array($eventOptions));
1766
1767
	// The ssi_todaysCalendar variants all use the same hook and just pass on $eventOptions so the hooked code can distinguish different cases if necessary
1768
	call_integration_hook('integrate_ssi_calendar', array(&$return, $eventOptions));
1769
1770
	if ($output_method != 'echo')
1771
		return $return['calendar_holidays'];
1772
1773
	echo '
1774
		', implode(', ', $return['calendar_holidays']);
1775
}
1776
1777
/**
1778
 * @param string $output_method The output method. If 'echo', displays a list of events, otherwise returns an array of info about them.
1779
 * @return void|array Displays a list of events or returns an array of info about them depending on output_method
1780
 */
1781
function ssi_todaysEvents($output_method = 'echo')
1782
{
1783
	global $modSettings, $user_info;
1784
1785
	if (empty($modSettings['cal_enabled']) || !allowedTo('calendar_view'))
1786
		return;
1787
1788
	$eventOptions = array(
1789
		'include_events' => true,
1790
		'num_days_shown' => empty($modSettings['cal_days_for_index']) || $modSettings['cal_days_for_index'] < 1 ? 1 : $modSettings['cal_days_for_index'],
1791
	);
1792
	$return = cache_quick_get('calendar_index_offset_' . ($user_info['time_offset'] + $modSettings['time_offset']), 'Subs-Calendar.php', 'cache_getRecentEvents', array($eventOptions));
1793
1794
	// The ssi_todaysCalendar variants all use the same hook and just pass on $eventOptions so the hooked code can distinguish different cases if necessary
1795
	call_integration_hook('integrate_ssi_calendar', array(&$return, $eventOptions));
1796
1797
	if ($output_method != 'echo')
1798
		return $return['calendar_events'];
1799
1800 View Code Duplication
	foreach ($return['calendar_events'] as $event)
1801
	{
1802
		if ($event['can_edit'])
1803
			echo '
1804
	<a href="' . $event['modify_href'] . '" style="color: #ff0000;">*</a> ';
1805
		echo '
1806
	' . $event['link'] . (!$event['is_last'] ? ', ' : '');
1807
	}
1808
}
1809
1810
/**
1811
 * Shows today's calendar items (events, birthdays and holidays)
1812
 * @param string $output_method The output method. If 'echo', displays a list of calendar items, otherwise returns an array of info about them.
1813
 * @return void|array Displays a list of calendar items or returns an array of info about them depending on output_method
1814
 */
1815
function ssi_todaysCalendar($output_method = 'echo')
1816
{
1817
	global $modSettings, $txt, $scripturl, $user_info;
1818
1819
	if (empty($modSettings['cal_enabled']) || !allowedTo('calendar_view'))
1820
		return;
1821
1822
	$eventOptions = array(
1823
		'include_birthdays' => allowedTo('profile_view'),
1824
		'include_holidays' => true,
1825
		'include_events' => true,
1826
		'num_days_shown' => empty($modSettings['cal_days_for_index']) || $modSettings['cal_days_for_index'] < 1 ? 1 : $modSettings['cal_days_for_index'],
1827
	);
1828
	$return = cache_quick_get('calendar_index_offset_' . ($user_info['time_offset'] + $modSettings['time_offset']), 'Subs-Calendar.php', 'cache_getRecentEvents', array($eventOptions));
1829
1830
	// The ssi_todaysCalendar variants all use the same hook and just pass on $eventOptions so the hooked code can distinguish different cases if necessary
1831
	call_integration_hook('integrate_ssi_calendar', array(&$return, $eventOptions));
1832
1833
	if ($output_method != 'echo')
1834
		return $return;
1835
1836
	if (!empty($return['calendar_holidays']))
1837
		echo '
1838
			<span class="holiday">' . $txt['calendar_prompt'] . ' ' . implode(', ', $return['calendar_holidays']) . '<br></span>';
1839
	if (!empty($return['calendar_birthdays']))
1840
	{
1841
		echo '
1842
			<span class="birthday">' . $txt['birthdays_upcoming'] . '</span> ';
1843 View Code Duplication
		foreach ($return['calendar_birthdays'] as $member)
1844
			echo '
1845
			<a href="', $scripturl, '?action=profile;u=', $member['id'], '"><span class="fix_rtl_names">', $member['name'], '</span>', isset($member['age']) ? ' (' . $member['age'] . ')' : '', '</a>', !$member['is_last'] ? ', ' : '';
1846
		echo '
1847
			<br>';
1848
	}
1849
	if (!empty($return['calendar_events']))
1850
	{
1851
		echo '
1852
			<span class="event">' . $txt['events_upcoming'] . '</span> ';
1853 View Code Duplication
		foreach ($return['calendar_events'] as $event)
1854
		{
1855
			if ($event['can_edit'])
1856
				echo '
1857
			<a href="' . $event['modify_href'] . '" style="color: #ff0000;">*</a> ';
1858
			echo '
1859
			' . $event['link'] . (!$event['is_last'] ? ', ' : '');
1860
		}
1861
	}
1862
}
1863
1864
/**
1865
 * Show the latest news, with a template... by board.
1866
 * @param null|int $board The ID of the board to get the info from. Defaults to $board or $_GET['board'] if not set.
1867
 * @param null|int $limit How many items to show. Defaults to $_GET['limit'] or 5 if not set.
1868
 * @param null|int $start Start with the specified item. Defaults to $_GET['start'] or 0 if not set.
1869
 * @param null|int $length How many characters to show from each post. Defaults to $_GET['length'] or 0 (no limit) if not set.
1870
 * @param string $output_method The output method. If 'echo', displays the news items, otherwise returns an array of info about them.
1871
 * @return void|array Displays the news items or returns an array of info about them, depending on output_method.
1872
 */
1873
function ssi_boardNews($board = null, $limit = null, $start = null, $length = null, $output_method = 'echo')
1874
{
1875
	global $scripturl, $txt, $settings, $modSettings, $context;
1876
	global $smcFunc;
1877
1878
	loadLanguage('Stats');
1879
1880
	// Must be integers....
1881
	if ($limit === null)
1882
		$limit = isset($_GET['limit']) ? (int) $_GET['limit'] : 5;
1883
	else
1884
		$limit = (int) $limit;
1885
1886
	if ($start === null)
1887
		$start = isset($_GET['start']) ? (int) $_GET['start'] : 0;
1888
	else
1889
		$start = (int) $start;
1890
1891
	if ($board !== null)
1892
		$board = (int) $board;
1893
	elseif (isset($_GET['board']))
1894
		$board = (int) $_GET['board'];
1895
1896
	if ($length === null)
1897
		$length = isset($_GET['length']) ? (int) $_GET['length'] : 0;
1898
	else
1899
		$length = (int) $length;
1900
1901
	$limit = max(0, $limit);
1902
	$start = max(0, $start);
1903
1904
	// Make sure guests can see this board.
1905
	$request = $smcFunc['db_query']('', '
1906
		SELECT id_board
1907
		FROM {db_prefix}boards
1908
		WHERE ' . ($board === null ? '' : 'id_board = {int:current_board}
1909
			AND ') . 'FIND_IN_SET(-1, member_groups) != 0
1910
		LIMIT 1',
1911
		array(
1912
			'current_board' => $board,
1913
		)
1914
	);
1915
	if ($smcFunc['db_num_rows']($request) == 0)
1916
	{
1917
		if ($output_method == 'echo')
1918
			die($txt['ssi_no_guests']);
1919
		else
1920
			return array();
1921
	}
1922
	list ($board) = $smcFunc['db_fetch_row']($request);
1923
	$smcFunc['db_free_result']($request);
1924
1925
	$icon_sources = array();
1926
	foreach ($context['stable_icons'] as $icon)
1927
		$icon_sources[$icon] = 'images_url';
1928
1929
	if (!empty($modSettings['enable_likes']))
1930
	{
1931
		$context['can_like'] = allowedTo('likes_like');
1932
		$context['can_see_likes'] = allowedTo('likes_view');
1933
	}
1934
1935
	// Find the post ids.
1936
	$request = $smcFunc['db_query']('', '
1937
		SELECT t.id_first_msg
1938
		FROM {db_prefix}topics as t
1939
		LEFT JOIN {db_prefix}boards as b ON (b.id_board = t.id_board)
1940
		WHERE t.id_board = {int:current_board}' . ($modSettings['postmod_active'] ? '
1941
			AND t.approved = {int:is_approved}' : '') . '
1942
			AND {query_see_board}
1943
		ORDER BY t.id_first_msg DESC
1944
		LIMIT ' . $start . ', ' . $limit,
1945
		array(
1946
			'current_board' => $board,
1947
			'is_approved' => 1,
1948
		)
1949
	);
1950
	$posts = array();
1951
	while ($row = $smcFunc['db_fetch_assoc']($request))
1952
		$posts[] = $row['id_first_msg'];
1953
	$smcFunc['db_free_result']($request);
1954
1955
	if (empty($posts))
1956
		return array();
1957
1958
	// Find the posts.
1959
	$request = $smcFunc['db_query']('', '
1960
		SELECT
1961
			m.icon, m.subject, m.body, IFNULL(mem.real_name, m.poster_name) AS poster_name, m.poster_time, m.likes,
1962
			t.num_replies, t.id_topic, m.id_member, m.smileys_enabled, m.id_msg, t.locked, t.id_last_msg, m.id_board
1963
		FROM {db_prefix}topics AS t
1964
			INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)
1965
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
1966
		WHERE t.id_first_msg IN ({array_int:post_list})
1967
		ORDER BY t.id_first_msg DESC
1968
		LIMIT ' . count($posts),
1969
		array(
1970
			'post_list' => $posts,
1971
		)
1972
	);
1973
	$return = array();
1974
	$recycle_board = !empty($modSettings['recycle_enable']) && !empty($modSettings['recycle_board']) ? (int) $modSettings['recycle_board'] : 0;
1975
	while ($row = $smcFunc['db_fetch_assoc']($request))
1976
	{
1977
		// If we want to limit the length of the post.
1978
		if (!empty($length) && $smcFunc['strlen']($row['body']) > $length)
1979
		{
1980
			$row['body'] = $smcFunc['substr']($row['body'], 0, $length);
1981
			$cutoff = false;
1982
1983
			$last_space = strrpos($row['body'], ' ');
1984
			$last_open = strrpos($row['body'], '<');
1985
			$last_close = strrpos($row['body'], '>');
1986
			if (empty($last_space) || ($last_space == $last_open + 3 && (empty($last_close) || (!empty($last_close) && $last_close < $last_open))) || $last_space < $last_open || $last_open == $length - 6)
1987
				$cutoff = $last_open;
1988
			elseif (empty($last_close) || $last_close < $last_open)
1989
				$cutoff = $last_space;
1990
1991
			if ($cutoff !== false)
1992
				$row['body'] = $smcFunc['substr']($row['body'], 0, $cutoff);
1993
			$row['body'] .= '...';
1994
		}
1995
1996
		$row['body'] = parse_bbc($row['body'], $row['smileys_enabled'], $row['id_msg']);
1997
1998
		if (!empty($recycle_board) && $row['id_board'] == $recycle_board)
1999
			$row['icon'] = 'recycled';
2000
2001
		// Check that this message icon is there...
2002 View Code Duplication
		if (!empty($modSettings['messageIconChecks_enable']) && !isset($icon_sources[$row['icon']]))
2003
			$icon_sources[$row['icon']] = file_exists($settings['theme_dir'] . '/images/post/' . $row['icon'] . '.png') ? 'images_url' : 'default_images_url';
2004
2005
		censorText($row['subject']);
2006
		censorText($row['body']);
2007
2008
		$return[] = array(
2009
			'id' => $row['id_topic'],
2010
			'message_id' => $row['id_msg'],
2011
			'icon' => '<img src="' . $settings[$icon_sources[$row['icon']]] . '/post/' . $row['icon'] . '.png" alt="' . $row['icon'] . '">',
2012
			'subject' => $row['subject'],
2013
			'time' => timeformat($row['poster_time']),
2014
			'timestamp' => forum_time(true, $row['poster_time']),
2015
			'body' => $row['body'],
2016
			'href' => $scripturl . '?topic=' . $row['id_topic'] . '.0',
2017
			'link' => '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.0">' . $row['num_replies'] . ' ' . ($row['num_replies'] == 1 ? $txt['ssi_comment'] : $txt['ssi_comments']) . '</a>',
2018
			'replies' => $row['num_replies'],
2019
			'comment_href' => !empty($row['locked']) ? '' : $scripturl . '?action=post;topic=' . $row['id_topic'] . '.' . $row['num_replies'] . ';last_msg=' . $row['id_last_msg'],
2020
			'comment_link' => !empty($row['locked']) ? '' : '<a href="' . $scripturl . '?action=post;topic=' . $row['id_topic'] . '.' . $row['num_replies'] . ';last_msg=' . $row['id_last_msg'] . '">' . $txt['ssi_write_comment'] . '</a>',
2021
			'new_comment' => !empty($row['locked']) ? '' : '<a href="' . $scripturl . '?action=post;topic=' . $row['id_topic'] . '.' . $row['num_replies'] . '">' . $txt['ssi_write_comment'] . '</a>',
2022
			'poster' => array(
2023
				'id' => $row['id_member'],
2024
				'name' => $row['poster_name'],
2025
				'href' => !empty($row['id_member']) ? $scripturl . '?action=profile;u=' . $row['id_member'] : '',
2026
				'link' => !empty($row['id_member']) ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['poster_name'] . '</a>' : $row['poster_name']
2027
			),
2028
			'locked' => !empty($row['locked']),
2029
			'is_last' => false,
2030
			// Nasty ternary for likes not messing around the "is_last" check.
2031
			'likes' => !empty($modSettings['enable_likes']) ? array(
2032
				'count' => $row['likes'],
2033
				'you' => in_array($row['id_msg'], prepareLikesContext((int) $row['id_topic'])),
2034
				'can_like' => !$context['user']['is_guest'] && $row['id_member'] != $context['user']['id'] && !empty($context['can_like']),
2035
			) : array(),
2036
		);
2037
	}
2038
	$smcFunc['db_free_result']($request);
2039
2040
	if (empty($return))
2041
		return $return;
2042
2043
	$return[count($return) - 1]['is_last'] = true;
2044
2045
	// If mods want to do somthing with this list of posts, let them do that now.
2046
	call_integration_hook('integrate_ssi_boardNews', array(&$return));
2047
2048
	if ($output_method != 'echo')
2049
		return $return;
2050
2051
	foreach ($return as $news)
2052
	{
2053
		echo '
2054
			<div class="news_item">
2055
				<h3 class="news_header">
2056
					', $news['icon'], '
2057
					<a href="', $news['href'], '">', $news['subject'], '</a>
2058
				</h3>
2059
				<div class="news_timestamp">', $news['time'], ' ', $txt['by'], ' ', $news['poster']['link'], '</div>
2060
				<div class="news_body" style="padding: 2ex 0;">', $news['body'], '</div>
2061
				', $news['link'], $news['locked'] ? '' : ' | ' . $news['comment_link'], '';
2062
2063
2064
		// Is there any likes to show?
2065
		if (!empty($modSettings['enable_likes']))
2066
		{
2067
			echo '
2068
					<ul>';
2069
2070
			if (!empty($news['likes']['can_like']))
2071
			{
2072
				echo '
2073
						<li class="like_button" id="msg_', $news['message_id'], '_likes"><a href="', $scripturl, '?action=likes;ltype=msg;sa=like;like=', $news['message_id'], ';', $context['session_var'], '=', $context['session_id'], '" class="msg_like"><span class="', $news['likes']['you'] ? 'unlike' : 'like', '"></span>', $news['likes']['you'] ? $txt['unlike'] : $txt['like'], '</a></li>';
2074
			}
2075
2076 View Code Duplication
			if (!empty($news['likes']['count']) && !empty($context['can_see_likes']))
2077
			{
2078
				$context['some_likes'] = true;
2079
				$count = $news['likes']['count'];
2080
				$base = 'likes_';
2081
				if ($news['likes']['you'])
2082
				{
2083
					$base = 'you_' . $base;
2084
					$count--;
2085
				}
2086
				$base .= (isset($txt[$base . $count])) ? $count : 'n';
2087
2088
				echo '
2089
						<li class="like_count smalltext">', sprintf($txt[$base], $scripturl . '?action=likes;sa=view;ltype=msg;like=' . $news['message_id'] . ';' . $context['session_var'] . '=' . $context['session_id'], comma_format($count)), '</li>';
2090
			}
2091
2092
			echo '
2093
					</ul>';
2094
		}
2095
2096
2097
		// Close the main div.
2098
		echo '
2099
			</div>';
2100
2101
		if (!$news['is_last'])
2102
			echo '
2103
			<hr>';
2104
	}
2105
}
2106
2107
/**
2108
 * Show the most recent events
2109
 * @param int $max_events The maximum number of events to show
2110
 * @param string $output_method The output method. If 'echo', displays the events, otherwise returns an array of info about them.
2111
 * @return void|array Displays the events or returns an array of info about them, depending on output_method.
2112
 */
2113
function ssi_recentEvents($max_events = 7, $output_method = 'echo')
2114
{
2115
	global $user_info, $scripturl, $modSettings, $txt, $context, $smcFunc;
2116
2117
	if (empty($modSettings['cal_enabled']) || !allowedTo('calendar_view'))
2118
		return;
2119
2120
	// Find all events which are happening in the near future that the member can see.
2121
	$request = $smcFunc['db_query']('', '
2122
		SELECT
2123
			cal.id_event, cal.start_date, cal.end_date, cal.title, cal.id_member, cal.id_topic,
2124
			cal.start_time, cal.end_time, cal.timezone, cal.location,
2125
			cal.id_board, t.id_first_msg, t.approved
2126
		FROM {db_prefix}calendar AS cal
2127
			LEFT JOIN {db_prefix}boards AS b ON (b.id_board = cal.id_board)
2128
			LEFT JOIN {db_prefix}topics AS t ON (t.id_topic = cal.id_topic)
2129
		WHERE cal.start_date <= {date:current_date}
2130
			AND cal.end_date >= {date:current_date}
2131
			AND (cal.id_board = {int:no_board} OR {query_wanna_see_board})
2132
		ORDER BY cal.start_date DESC
2133
		LIMIT ' . $max_events,
2134
		array(
2135
			'current_date' => strftime('%Y-%m-%d', forum_time(false)),
2136
			'no_board' => 0,
2137
		)
2138
	);
2139
	$return = array();
2140
	$duplicates = array();
2141
	while ($row = $smcFunc['db_fetch_assoc']($request))
2142
	{
2143
		// Check if we've already come by an event linked to this same topic with the same title... and don't display it if we have.
2144
		if (!empty($duplicates[$row['title'] . $row['id_topic']]))
2145
			continue;
2146
2147
		// Censor the title.
2148
		censorText($row['title']);
2149
2150
		if ($row['start_date'] < strftime('%Y-%m-%d', forum_time(false)))
2151
			$date = strftime('%Y-%m-%d', forum_time(false));
2152
		else
2153
			$date = $row['start_date'];
2154
2155
		// If the topic it is attached to is not approved then don't link it.
2156
		if (!empty($row['id_first_msg']) && !$row['approved'])
2157
			$row['id_board'] = $row['id_topic'] = $row['id_first_msg'] = 0;
2158
2159
		$allday = (empty($row['start_time']) || empty($row['end_time']) || empty($row['timezone']) || !in_array($row['timezone'], timezone_identifiers_list(DateTimeZone::ALL_WITH_BC))) ? true : false;
2160
2161
		$return[$date][] = array(
2162
			'id' => $row['id_event'],
2163
			'title' => $row['title'],
2164
			'location' => $row['location'],
2165
			'can_edit' => allowedTo('calendar_edit_any') || ($row['id_member'] == $user_info['id'] && allowedTo('calendar_edit_own')),
2166
			'modify_href' => $scripturl . '?action=' . ($row['id_board'] == 0 ? 'calendar;sa=post;' : 'post;msg=' . $row['id_first_msg'] . ';topic=' . $row['id_topic'] . '.0;calendar;') . 'eventid=' . $row['id_event'] . ';' . $context['session_var'] . '=' . $context['session_id'],
2167
			'href' => $row['id_board'] == 0 ? '' : $scripturl . '?topic=' . $row['id_topic'] . '.0',
2168
			'link' => $row['id_board'] == 0 ? $row['title'] : '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.0">' . $row['title'] . '</a>',
2169
			'start_date' => $row['start_date'],
2170
			'end_date' => $row['end_date'],
2171
			'start_time' => !$allday ? $row['start_time'] : null,
2172
			'end_time' => !$allday ? $row['end_time'] : null,
2173
			'tz' => !$allday ? $row['timezone'] : null,
2174
			'allday' => $allday,
2175
			'is_last' => false
2176
		);
2177
2178
		// Let's not show this one again, huh?
2179
		$duplicates[$row['title'] . $row['id_topic']] = true;
2180
	}
2181
	$smcFunc['db_free_result']($request);
2182
2183
	foreach ($return as $mday => $array)
2184
		$return[$mday][count($array) - 1]['is_last'] = true;
2185
2186
	// If mods want to do somthing with this list of events, let them do that now.
2187
	call_integration_hook('integrate_ssi_recentEvents', array(&$return));
2188
2189
	if ($output_method != 'echo' || empty($return))
2190
		return $return;
2191
2192
	// Well the output method is echo.
2193
	echo '
2194
			<span class="event">' . $txt['events'] . '</span> ';
2195
	foreach ($return as $mday => $array)
2196
		foreach ($array as $event)
2197
		{
2198
			if ($event['can_edit'])
2199
				echo '
2200
				<a href="' . $event['modify_href'] . '" style="color: #ff0000;">*</a> ';
2201
2202
			echo '
2203
				' . $event['link'] . (!$event['is_last'] ? ', ' : '');
2204
		}
2205
}
2206
2207
/**
2208
 * Checks whether the specified password is correct for the specified user.
2209
 * @param int|string $id The ID or username of a user
2210
 * @param string $password The password to check
2211
 * @param bool $is_username If true, treats $id as a username rather than a user ID
2212
 * @return bool Whether or not the password is correct.
2213
 */
2214
function ssi_checkPassword($id = null, $password = null, $is_username = false)
2215
{
2216
	global $smcFunc;
2217
2218
	// If $id is null, this was most likely called from a query string and should do nothing.
2219
	if ($id === null)
2220
		return;
2221
2222
	$request = $smcFunc['db_query']('', '
2223
		SELECT passwd, member_name, is_activated
2224
		FROM {db_prefix}members
2225
		WHERE ' . ($is_username ? 'member_name' : 'id_member') . ' = {string:id}
2226
		LIMIT 1',
2227
		array(
2228
			'id' => $id,
2229
		)
2230
	);
2231
	list ($pass, $user, $active) = $smcFunc['db_fetch_row']($request);
2232
	$smcFunc['db_free_result']($request);
2233
2234
	return hash_verify_password($user, $password, $pass) && $active == 1;
2235
}
2236
2237
/**
2238
 * Shows the most recent attachments that the user can see
2239
 * @param int $num_attachments How many to show
2240
 * @param array $attachment_ext Only shows attachments with the specified extensions ('jpg', 'gif', etc.) if set
2241
 * @param string $output_method The output method. If 'echo', displays a table with links/info, otherwise returns an array with information about the attachments
2242
 * @return void|array Displays a table of attachment info or returns an array containing info about the attachments, depending on output_method.
2243
 */
2244
function ssi_recentAttachments($num_attachments = 10, $attachment_ext = array(), $output_method = 'echo')
2245
{
2246
	global $smcFunc, $modSettings, $scripturl, $txt, $settings;
2247
2248
	// We want to make sure that we only get attachments for boards that we can see *if* any.
2249
	$attachments_boards = boardsAllowedTo('view_attachments');
2250
2251
	// No boards?  Adios amigo.
2252
	if (empty($attachments_boards))
2253
		return array();
2254
2255
	// Is it an array?
2256
	$attachment_ext = (array) $attachment_ext;
2257
2258
	// Lets build the query.
2259
	$request = $smcFunc['db_query']('', '
2260
		SELECT
2261
			att.id_attach, att.id_msg, att.filename, IFNULL(att.size, 0) AS filesize, att.downloads, mem.id_member,
2262
			IFNULL(mem.real_name, m.poster_name) AS poster_name, m.id_topic, m.subject, t.id_board, m.poster_time,
2263
			att.width, att.height' . (empty($modSettings['attachmentShowImages']) || empty($modSettings['attachmentThumbnails']) ? '' : ', IFNULL(thumb.id_attach, 0) AS id_thumb, thumb.width AS thumb_width, thumb.height AS thumb_height') . '
2264
		FROM {db_prefix}attachments AS att
2265
			INNER JOIN {db_prefix}messages AS m ON (m.id_msg = att.id_msg)
2266
			INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic)
2267
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)' . (empty($modSettings['attachmentShowImages']) || empty($modSettings['attachmentThumbnails']) ? '' : '
2268
			LEFT JOIN {db_prefix}attachments AS thumb ON (thumb.id_attach = att.id_thumb)') . '
2269
		WHERE att.attachment_type = 0' . ($attachments_boards === array(0) ? '' : '
2270
			AND m.id_board IN ({array_int:boards_can_see})') . (!empty($attachment_ext) ? '
2271
			AND att.fileext IN ({array_string:attachment_ext})' : '') .
2272
			(!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : '
2273
			AND t.approved = {int:is_approved}
2274
			AND m.approved = {int:is_approved}
2275
			AND att.approved = {int:is_approved}') . '
2276
		ORDER BY att.id_attach DESC
2277
		LIMIT {int:num_attachments}',
2278
		array(
2279
			'boards_can_see' => $attachments_boards,
2280
			'attachment_ext' => $attachment_ext,
2281
			'num_attachments' => $num_attachments,
2282
			'is_approved' => 1,
2283
		)
2284
	);
2285
2286
	// We have something.
2287
	$attachments = array();
2288
	while ($row = $smcFunc['db_fetch_assoc']($request))
2289
	{
2290
		$filename = preg_replace('~&amp;#(\\d{1,7}|x[0-9a-fA-F]{1,6});~', '&#\\1;', htmlspecialchars($row['filename']));
2291
2292
		// Is it an image?
2293
		$attachments[$row['id_attach']] = array(
2294
			'member' => array(
2295
				'id' => $row['id_member'],
2296
				'name' => $row['poster_name'],
2297
				'link' => empty($row['id_member']) ? $row['poster_name'] : '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['poster_name'] . '</a>',
2298
			),
2299
			'file' => array(
2300
				'filename' => $filename,
2301
				'filesize' => round($row['filesize'] / 1024, 2) . $txt['kilobyte'],
2302
				'downloads' => $row['downloads'],
2303
				'href' => $scripturl . '?action=dlattach;topic=' . $row['id_topic'] . '.0;attach=' . $row['id_attach'],
2304
				'link' => '<img src="' . $settings['images_url'] . '/icons/clip.png" alt=""> <a href="' . $scripturl . '?action=dlattach;topic=' . $row['id_topic'] . '.0;attach=' . $row['id_attach'] . '">' . $filename . '</a>',
2305
				'is_image' => !empty($row['width']) && !empty($row['height']) && !empty($modSettings['attachmentShowImages']),
2306
			),
2307
			'topic' => array(
2308
				'id' => $row['id_topic'],
2309
				'subject' => $row['subject'],
2310
				'href' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'],
2311
				'link' => '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'] . '">' . $row['subject'] . '</a>',
2312
				'time' => timeformat($row['poster_time']),
2313
			),
2314
		);
2315
2316
		// Images.
2317
		if ($attachments[$row['id_attach']]['file']['is_image'])
2318
		{
2319
			$id_thumb = empty($row['id_thumb']) ? $row['id_attach'] : $row['id_thumb'];
2320
			$attachments[$row['id_attach']]['file']['image'] = array(
2321
				'id' => $id_thumb,
2322
				'width' => $row['width'],
2323
				'height' => $row['height'],
2324
				'img' => '<img src="' . $scripturl . '?action=dlattach;topic=' . $row['id_topic'] . '.0;attach=' . $row['id_attach'] . ';image" alt="' . $filename . '">',
2325
				'thumb' => '<img src="' . $scripturl . '?action=dlattach;topic=' . $row['id_topic'] . '.0;attach=' . $id_thumb . ';image" alt="' . $filename . '">',
2326
				'href' => $scripturl . '?action=dlattach;topic=' . $row['id_topic'] . '.0;attach=' . $id_thumb . ';image',
2327
				'link' => '<a href="' . $scripturl . '?action=dlattach;topic=' . $row['id_topic'] . '.0;attach=' . $row['id_attach'] . ';image"><img src="' . $scripturl . '?action=dlattach;topic=' . $row['id_topic'] . '.0;attach=' . $id_thumb . ';image" alt="' . $filename . '"></a>',
2328
			);
2329
		}
2330
	}
2331
	$smcFunc['db_free_result']($request);
2332
2333
	// If mods want to do somthing with this list of attachments, let them do that now.
2334
	call_integration_hook('integrate_ssi_recentAttachments', array(&$attachments));
2335
2336
	// So you just want an array?  Here you can have it.
2337
	if ($output_method == 'array' || empty($attachments))
2338
		return $attachments;
2339
2340
	// Give them the default.
2341
	echo '
2342
		<table class="ssi_downloads">
2343
			<tr>
2344
				<th style="text-align: left; padding: 2">', $txt['file'], '</th>
2345
				<th style="text-align: left; padding: 2">', $txt['posted_by'], '</th>
2346
				<th style="text-align: left; padding: 2">', $txt['downloads'], '</th>
2347
				<th style="text-align: left; padding: 2">', $txt['filesize'], '</th>
2348
			</tr>';
2349
	foreach ($attachments as $attach)
2350
		echo '
2351
			<tr>
2352
				<td>', $attach['file']['link'], '</td>
2353
				<td>', $attach['member']['link'], '</td>
2354
				<td style="text-align: center">', $attach['file']['downloads'], '</td>
2355
				<td>', $attach['file']['filesize'], '</td>
2356
			</tr>';
2357
	echo '
2358
		</table>';
2359
}
2360
2361
?>