Completed
Push — patch_1-0-10 ( 9297c8...b6a303 )
by Emanuele
16s
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
 * Provides ways to add information to your website by linking to and capturing output
5
 * from ElkArte
6
 *
7
 * @name      ElkArte Forum
8
 * @copyright ElkArte Forum contributors
9
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause
10
 *
11
 * This software is a derived product, based on:
12
 *
13
 * Simple Machines Forum (SMF)
14
 * copyright:	2011 Simple Machines (http://www.simplemachines.org)
15
 * license:		BSD, See included LICENSE.TXT for terms and conditions.
16
 *
17
 * @version 1.0.10
18
 *
19
 */
20
21
// Don't do anything if ElkArte is already loaded.
22
if (defined('ELK'))
23
	return true;
24
25
define('ELK', 'SSI');
26
27
// Shortcut for the browser cache stale
28
define('CACHE_STALE', '?109');
29
30
// We're going to want a few globals... these are all set later.
31
global $time_start, $maintenance, $msubject, $mmessage, $mbname, $language;
32
global $boardurl, $webmaster_email, $cookiename;
33
global $db_server, $db_name, $db_user, $db_prefix, $db_persist, $db_error_send, $db_last_error;
34
global $modSettings, $context, $sc, $user_info, $topic, $board, $txt;
35
global $ssi_db_user, $scripturl, $ssi_db_passwd, $db_passwd;
36
global $sourcedir, $boarddir;
37
38
// Remember the current configuration so it can be set back.
39
$ssi_magic_quotes_runtime = function_exists('get_magic_quotes_gpc') && get_magic_quotes_runtime();
40
if (function_exists('set_magic_quotes_runtime'))
41
	@set_magic_quotes_runtime(0);
42
$time_start = microtime(true);
43
44
// Just being safe...
45
foreach (array('db_character_set', 'cachedir') as $variable)
46
	if (isset($GLOBALS[$variable]))
47
		unset($GLOBALS[$variable]);
48
49
// Get the forum's settings for database and file paths.
50
require_once(dirname(__FILE__) . '/Settings.php');
51
52
// Fix for using the current directory as a path.
53
if (substr($sourcedir, 0, 1) == '.' && substr($sourcedir, 1, 1) != '.')
54
	$sourcedir = dirname(__FILE__) . substr($sourcedir, 1);
55
56
// Make sure the paths are correct... at least try to fix them.
57
if (!file_exists($boarddir) && file_exists(dirname(__FILE__) . '/agreement.txt'))
58
	$boarddir = dirname(__FILE__);
59
if (!file_exists($sourcedir) && file_exists($boarddir . '/sources'))
60
	$sourcedir = $boarddir . '/sources';
61
62
// Check that directories which didn't exist in past releases are initialized.
63
if ((empty($cachedir) || !file_exists($cachedir)) && file_exists($boarddir . '/cache'))
64
	$cachedir = $boarddir . '/cache';
65
if ((empty($extdir) || !file_exists($extdir)) && file_exists($sourcedir . '/ext'))
66
	$extdir = $sourcedir . '/ext';
67
if ((empty($languagedir) || !file_exists($languagedir)) && file_exists($boarddir . '/themes/default/languages'))
68
	$languagedir = $boarddir . '/themes/default/languages';
69
70
// Time to forget about variables and go with constants!
71
DEFINE('BOARDDIR', $boarddir);
72
DEFINE('CACHEDIR', $cachedir);
73
DEFINE('EXTDIR', $extdir);
74
DEFINE('LANGUAGEDIR', $languagedir);
75
DEFINE('SOURCEDIR', $sourcedir);
76
DEFINE('ADMINDIR', $sourcedir . '/admin');
77
DEFINE('CONTROLLERDIR', $sourcedir . '/controllers');
78
DEFINE('SUBSDIR', $sourcedir . '/subs');
79
unset($boarddir, $cachedir, $sourcedir, $languagedir, $extdir);
80
81
$ssi_error_reporting = error_reporting(E_ALL | E_STRICT);
82
/**
83
 * Set this to one of three values depending on what you want to happen in the case of a fatal error.
84
 *  - false: Default, will just load the error sub template and die - not putting any theme layers around it.
85
 *  - true: Will load the error sub template AND put the template layers around it (Not useful if on total custom pages).
86
 *  - 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.
87
 */
88
$ssi_on_error_method = false;
89
90
// Don't do john didley if the forum's been shut down competely.
91
if ($maintenance == 2 && (!isset($ssi_maintenance_off) || $ssi_maintenance_off !== true))
92
	die($mmessage);
93
94
// Load the important includes.
95
require_once(SOURCEDIR . '/QueryString.php');
96
require_once(SOURCEDIR . '/Session.php');
97
require_once(SOURCEDIR . '/Subs.php');
98
require_once(SOURCEDIR . '/Errors.php');
99
require_once(SOURCEDIR . '/Logging.php');
100
require_once(SOURCEDIR . '/Load.php');
101
require_once(SUBSDIR . '/Cache.subs.php');
102
require_once(SOURCEDIR . '/Security.php');
103
require_once(SOURCEDIR . '/BrowserDetector.class.php');
104
require_once(SOURCEDIR . '/ErrorContext.class.php');
105
require_once(SUBSDIR . '/Util.class.php');
106
require_once(SUBSDIR . '/TemplateLayers.class.php');
107
require_once(SOURCEDIR . '/Action.controller.php');
108
109
// Clean the request variables.
110
cleanRequest();
111
112
// Initiate the database connection and define some database functions to use.
113
loadDatabase();
114
115
// Load settings from the database.
116
reloadSettings();
117
118
// Seed the random generator?
119
elk_seed_generator();
120
121
// Check on any hacking attempts.
122
if (isset($_REQUEST['GLOBALS']) || isset($_COOKIE['GLOBALS']))
123
	die('Hacking attempt...');
124
elseif (isset($_REQUEST['ssi_theme']) && (int) $_REQUEST['ssi_theme'] == (int) $ssi_theme)
125
	die('Hacking attempt...');
126
elseif (isset($_COOKIE['ssi_theme']) && (int) $_COOKIE['ssi_theme'] == (int) $ssi_theme)
127
	die('Hacking attempt...');
128
elseif (isset($_REQUEST['ssi_layers'], $ssi_layers) && (@get_magic_quotes_gpc() ? stripslashes($_REQUEST['ssi_layers']) : $_REQUEST['ssi_layers']) == $ssi_layers)
129
	die('Hacking attempt...');
130
if (isset($_REQUEST['context']))
131
	die('Hacking attempt...');
132
133
// Gzip output? (because it must be boolean and true, this can't be hacked.)
134
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', '>='))
135
	ob_start('ob_gzhandler');
136
else
137
	$modSettings['enableCompressedOutput'] = '0';
138
139
// Primarily, this is to fix the URLs...
140
ob_start('ob_sessrewrite');
141
142
// Start the session... known to scramble SSI includes in cases...
143
if (!headers_sent())
144
	loadSession();
145
else
146
{
147
	if (isset($_COOKIE[session_name()]) || isset($_REQUEST[session_name()]))
148
	{
149
		// Make a stab at it, but ignore the E_WARNINGs generated because we can't send headers.
150
		$temp = error_reporting(error_reporting() & !E_WARNING);
151
		loadSession();
152
		error_reporting($temp);
153
	}
154
155
	if (!isset($_SESSION['session_value']))
156
	{
157
		$_SESSION['session_var'] = substr(md5(mt_rand() . session_id() . mt_rand()), 0, rand(7, 12));
158
		$_SESSION['session_value'] = md5(session_id() . mt_rand());
159
	}
160
	$sc = $_SESSION['session_value'];
161
	// This is here only to avoid session errors in PHP7
162
	// microtime effectively forces the replacing of the session in the db each
163
	// time the page is loaded
164
	$_SESSION['mictrotime'] = microtime();
165
}
166
167
// Get rid of $board and $topic... do stuff loadBoard would do.
168
unset($board, $topic);
169
$user_info['is_mod'] = false;
170
$context['user']['is_mod'] = &$user_info['is_mod'];
171
$context['linktree'] = array();
172
173
// Load the user and their cookie, as well as their settings.
174
loadUserSettings();
175
176
// Load the current user's permissions....
177
loadPermissions();
178
179
// Load BadBehavior functions
180
loadBadBehavior();
181
182
// Load the current or SSI theme. (just use $ssi_theme = id_theme;)
183
loadTheme(isset($ssi_theme) ? (int) $ssi_theme : 0);
184
185
// @todo: probably not the best place, but somewhere it should be set...
186
if (!headers_sent())
187
	header('Content-Type: text/html; charset=UTF-8');
188
189
// Take care of any banning that needs to be done.
190
if (isset($_REQUEST['ssi_ban']) || (isset($ssi_ban) && $ssi_ban === true))
191
	is_not_banned();
192
193
// Do we allow guests in here?
194
if (empty($ssi_guest_access) && empty($modSettings['allow_guestAccess']) && $user_info['is_guest'] && basename($_SERVER['PHP_SELF']) != 'SSI.php')
195
{
196
	require_once(CONTROLLERDIR . '/Auth.controller.php');
197
	$controller = new Auth_Controller();
198
	$controller->action_kickguest();
199
	obExit(null, true);
200
}
201
202
// Load the stuff like the menu bar, etc.
203
if (isset($ssi_layers))
204
{
205
	$template_layers = Template_Layers::getInstance();
206
	$template_layers->removeAll();
207
	foreach ($ssi_layers as $layer)
208
		$template_layers->addBegin($layer);
209
	template_header();
210
}
211
else
212
	setupThemeContext();
213
214
// We need to set up user agent, and make more checks on the request
215
$req = request();
216
217
// Make sure they didn't muss around with the settings... but only if it's not cli.
218
if (isset($_SERVER['REMOTE_ADDR']) && session_id() == '')
219
	trigger_error($txt['ssi_session_broken'], E_USER_NOTICE);
220
221
// Without visiting the forum this session variable might not be set on submit.
222
if (!isset($_SESSION['USER_AGENT']) && (!isset($_GET['ssi_function']) || $_GET['ssi_function'] !== 'pollVote'))
223
	$_SESSION['USER_AGENT'] = $req->user_agent();
224
225
// Have the ability to easily add functions to SSI.
226
call_integration_hook('integrate_SSI');
227
228
// Call a function passed by GET.
229
if (isset($_GET['ssi_function']) && function_exists('ssi_' . $_GET['ssi_function']) && (!empty($modSettings['allow_guestAccess']) || !$user_info['is_guest']))
230
{
231
	call_user_func('ssi_' . $_GET['ssi_function']);
232
	exit;
233
}
234
235
if (isset($_GET['ssi_function']))
236
	exit;
237
// You shouldn't just access SSI.php directly by URL!!
238
elseif (basename($_SERVER['PHP_SELF']) == 'SSI.php')
239
	die(sprintf($txt['ssi_not_direct'], $user_info['is_admin'] ? '\'' . addslashes(__FILE__) . '\'' : '\'SSI.php\''));
240
241
error_reporting($ssi_error_reporting);
242
if (function_exists('set_magic_quotes_runtime'))
243
	@set_magic_quotes_runtime($ssi_magic_quotes_runtime);
244
245
return true;
246
247
/**
248
 * This shuts down the SSI and shows the footer.
249
 */
250
function ssi_shutdown()
251
{
252
	if (!isset($_GET['ssi_function']) || $_GET['ssi_function'] != 'shutdown')
253
		template_footer();
254
}
255
256
/**
257
 * Display a welcome message, like:
258
 * "Hey, User, you have 0 messages, 0 are new."
259
 *
260
 * @param string $output_method
261
 */
262
function ssi_welcome($output_method = 'echo')
263
{
264
	global $context, $txt, $scripturl;
265
266
	if ($output_method == 'echo')
267
	{
268
		if ($context['user']['is_guest'])
269
			echo replaceBasicActionUrl($txt[$context['can_register'] ? 'welcome_guest_register' : 'welcome_guest']);
270
		else
271
			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'])))) : '';
272
	}
273
	// Don't echo... then do what?!
274
	else
275
		return $context['user'];
276
}
277
278
/**
279
 * Display a menu bar, like is displayed at the top of the forum.
280
 *
281
 * @param string $output_method
282
 */
283
function ssi_menubar($output_method = 'echo')
284
{
285
	global $context;
286
287
	if ($output_method == 'echo')
288
		template_menu();
289
	// What else could this do?
290
	else
291
		return $context['menu_buttons'];
292
}
293
294
/**
295
 * Show a logout link.
296
 *
297
 * @param string $redirect_to
298
 * @param string $output_method = 'echo'
299
 */
300
function ssi_logout($redirect_to = '', $output_method = 'echo')
301
{
302
	global $context, $txt, $scripturl;
303
304
	if ($redirect_to != '')
305
		$_SESSION['logout_url'] = $redirect_to;
306
307
	// Guests can't log out.
308
	if ($context['user']['is_guest'])
309
		return false;
310
311
	$link = '<a href="' . $scripturl . '?action=logout;' . $context['session_var'] . '=' . $context['session_id'] . '">' . $txt['logout'] . '</a>';
312
313
	if ($output_method == 'echo')
314
		echo $link;
315
	else
316
		return $link;
317
}
318
319
/**
320
 * Recent post list:
321
 *  [board] Subject by Poster Date
322
 *
323
 * @todo this may use getLastPosts with some modification
324
 *
325
 * @param int $num_recent
326
 * @param int[]|null $exclude_boards
327
 * @param int[]|null $include_boards
328
 * @param string $output_method
329
 * @param bool $limit_body
330
 */
331
function ssi_recentPosts($num_recent = 8, $exclude_boards = null, $include_boards = null, $output_method = 'echo', $limit_body = true)
332
{
333
	global $modSettings;
334
335
	// Excluding certain boards...
336
	if ($exclude_boards === null && !empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0)
337
		$exclude_boards = array($modSettings['recycle_board']);
338
	else
339
		$exclude_boards = empty($exclude_boards) ? array() : (is_array($exclude_boards) ? $exclude_boards : array($exclude_boards));
340
341
	// What about including certain boards - note we do some protection here as pre-2.0 didn't have this parameter.
342
	if (is_array($include_boards) || (int) $include_boards === $include_boards)
343
	{
344
		$include_boards = is_array($include_boards) ? $include_boards : array($include_boards);
345
	}
346
	elseif ($include_boards != null)
347
	{
348
		$include_boards = array();
349
	}
350
351
	// Let's restrict the query boys (and girls)
352
	$query_where = '
353
		m.id_msg >= {int:min_message_id}
354
		' . (empty($exclude_boards) ? '' : '
355
		AND b.id_board NOT IN ({array_int:exclude_boards})') . '
356
		' . ($include_boards === null ? '' : '
357
		AND b.id_board IN ({array_int:include_boards})') . '
358
		AND {query_wanna_see_board}' . ($modSettings['postmod_active'] ? '
359
		AND m.approved = {int:is_approved}' : '');
360
361
	$query_where_params = array(
362
		'is_approved' => 1,
363
		'include_boards' => $include_boards === null ? '' : $include_boards,
364
		'exclude_boards' => empty($exclude_boards) ? '' : $exclude_boards,
365
		'min_message_id' => $modSettings['maxMsgID'] - 25 * min($num_recent, 5),
366
	);
367
368
	// Past to this simpleton of a function...
369
	return ssi_queryPosts($query_where, $query_where_params, $num_recent, 'm.id_msg DESC', $output_method, $limit_body);
370
}
371
372
/**
373
 * Fetch a post with a particular ID.
374
 * By default will only show if you have permission
375
 *  to the see the board in question - this can be overriden.
376
 *
377
 * @todo this may use getRecentPosts with some modification
378
 *
379
 * @param int[] $post_ids
380
 * @param bool $override_permissions
381
 * @param string $output_method = 'echo'
382
 */
383
function ssi_fetchPosts($post_ids = array(), $override_permissions = false, $output_method = 'echo')
384
{
385
	global $modSettings;
386
387
	if (empty($post_ids))
388
		return;
389
390
	// Allow the user to request more than one - why not?
391
	$post_ids = is_array($post_ids) ? $post_ids : array($post_ids);
392
393
	// Restrict the posts required...
394
	$query_where = '
395
		m.id_msg IN ({array_int:message_list})' . ($override_permissions ? '' : '
396
			AND {query_wanna_see_board}') . ($modSettings['postmod_active'] ? '
397
			AND m.approved = {int:is_approved}' : '');
398
	$query_where_params = array(
399
		'message_list' => $post_ids,
400
		'is_approved' => 1,
401
	);
402
403
	// Then make the query and dump the data.
404
	return ssi_queryPosts($query_where, $query_where_params, '', 'm.id_msg DESC', $output_method, false, $override_permissions);
405
}
406
407
/**
408
 * This removes code duplication in other queries
409
 *  - don't call it direct unless you really know what you're up to.
410
 *
411
 * @todo if ssi_recentPosts and ssi_fetchPosts will use Recent.subs.php this can be removed
412
 *
413
 * @param string $query_where
414
 * @param mixed[] $query_where_params
415
 * @param int $query_limit
416
 * @param string $query_order
417
 * @param string $output_method = 'echo'
418
 * @param bool $limit_body
419
 * @param bool $override_permissions
420
 */
421
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)
422
{
423
	global $scripturl, $txt, $user_info, $modSettings;
424
425
	$db = database();
426
427
	// Find all the posts. Newer ones will have higher IDs.
428
	$request = $db->query('substring', '
429
		SELECT
430
			m.poster_time, m.subject, m.id_topic, m.id_member, m.id_msg, m.id_board, b.name AS board_name,
431
			IFNULL(mem.real_name, m.poster_name) AS poster_name, ' . ($user_info['is_guest'] ? '1 AS is_read, 0 AS new_from' : '
432
			IFNULL(lt.id_msg, IFNULL(lmr.id_msg, 0)) >= m.id_msg_modified AS is_read,
433
			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
434
		FROM {db_prefix}messages AS m
435
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)
436
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)' . (!$user_info['is_guest'] ? '
437
			LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = m.id_topic AND lt.id_member = {int:current_member})
438
			LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = m.id_board AND lmr.id_member = {int:current_member})' : '') . '
439
		WHERE 1=1 ' . ($override_permissions ? '' : '
440
			AND {query_wanna_see_board}') . ($modSettings['postmod_active'] ? '
441
			AND m.approved = {int:is_approved}' : '') . '
442
		' . (empty($query_where) ? '' : 'AND ' . $query_where) . '
443
		ORDER BY ' . $query_order . '
444
		' . ($query_limit == '' ? '' : 'LIMIT {int:query_limit}'),
445
		array_merge($query_where_params, array(
446
			'current_member' => $user_info['id'],
447
			'is_approved' => 1,
448
			'query_limit' => $query_limit,
449
		))
450
	);
451
	$posts = array();
452
	while ($row = $db->fetch_assoc($request))
453
	{
454
		$row['body'] = parse_bbc($row['body'], $row['smileys_enabled'], $row['id_msg']);
455
456
		// Censor it!
457
		censorText($row['subject']);
458
		censorText($row['body']);
459
460
		$preview = strip_tags(strtr($row['body'], array('<br />' => '&#10;')));
461
462
		// Build the array.
463
		$posts[] = array(
464
			'id' => $row['id_msg'],
465
			'board' => array(
466
				'id' => $row['id_board'],
467
				'name' => $row['board_name'],
468
				'href' => $scripturl . '?board=' . $row['id_board'] . '.0',
469
				'link' => '<a href="' . $scripturl . '?board=' . $row['id_board'] . '.0">' . $row['board_name'] . '</a>'
470
			),
471
			'topic' => $row['id_topic'],
472
			'poster' => array(
473
				'id' => $row['id_member'],
474
				'name' => $row['poster_name'],
475
				'href' => empty($row['id_member']) ? '' : $scripturl . '?action=profile;u=' . $row['id_member'],
476
				'link' => empty($row['id_member']) ? $row['poster_name'] : '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['poster_name'] . '</a>'
477
			),
478
			'subject' => $row['subject'],
479
			'short_subject' => Util::shorten_text($row['subject'], !empty($modSettings['ssi_subject_length']) ? $modSettings['ssi_subject_length'] : 24),
480
			'preview' => Util::shorten_text($preview, !empty($modSettings['ssi_preview_length']) ? $modSettings['ssi_preview_length'] : 128),
481
			'body' => $row['body'],
482
			'time' => standardTime($row['poster_time']),
483
			'html_time' => htmlTime($row['poster_time']),
484
			'timestamp' => forum_time(true, $row['poster_time']),
485
			'href' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . ';topicseen#new',
486
			'link' => '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'] . '" rel="nofollow">' . $row['subject'] . '</a>',
487
			'new' => !empty($row['is_read']),
488
			'is_new' => empty($row['is_read']),
489
			'new_from' => $row['new_from'],
490
		);
491
	}
492
	$db->free_result($request);
493
494
	// Just return it.
495
	if ($output_method != 'echo' || empty($posts))
496
		return $posts;
497
498
	echo '
499
		<table class="ssi_table">';
500
	foreach ($posts as $post)
501
		echo '
502
			<tr>
503
				<td class="righttext">
504
					[', $post['board']['link'], ']
505
				</td>
506
				<td class="top">
507
					<a href="', $post['href'], '">', $post['subject'], '</a>
508
					', $txt['by'], ' ', $post['poster']['link'], '
509
					', $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>' : '', '
510
				</td>
511
				<td class="righttext">
512
					', $post['time'], '
513
				</td>
514
			</tr>';
515
	echo '
516
		</table>';
517
}
518
519
/**
520
 * Recent topic list:
521
 *  [board] Subject by Poster Date
522
 *
523
 * @param int $num_recent
524
 * @param int[]|null $exclude_boards
525
 * @param bool|null $include_boards
526
 * @param string $output_method = 'echo'
527
 */
528
function ssi_recentTopics($num_recent = 8, $exclude_boards = null, $include_boards = null, $output_method = 'echo')
529
{
530
	global $settings, $scripturl, $txt, $user_info, $modSettings;
531
532
	$db = database();
533
534
	if ($exclude_boards === null && !empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0)
535
		$exclude_boards = array($modSettings['recycle_board']);
536
	else
537
		$exclude_boards = empty($exclude_boards) ? array() : (is_array($exclude_boards) ? $exclude_boards : array($exclude_boards));
538
539
	// Only some boards?.
540
	if (is_array($include_boards) || (int) $include_boards === $include_boards)
541
		$include_boards = is_array($include_boards) ? $include_boards : array($include_boards);
542
	elseif ($include_boards != null)
543
	{
544
		$output_method = $include_boards;
545
		$include_boards = array();
546
	}
547
548
	require_once(SUBSDIR . '/MessageIndex.subs.php');
549
	$icon_sources = MessageTopicIcons();
550
551
	// Find all the posts in distinct topics. Newer ones will have higher IDs.
552
	$request = $db->query('', '
553
		SELECT
554
			t.id_topic, b.id_board, b.name AS board_name
555
		FROM {db_prefix}topics AS t
556
			INNER JOIN {db_prefix}messages AS ml ON (ml.id_msg = t.id_last_msg)
557
			LEFT JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
558
		WHERE t.id_last_msg >= {int:min_message_id}' . (empty($exclude_boards) ? '' : '
559
			AND b.id_board NOT IN ({array_int:exclude_boards})') . '' . (empty($include_boards) ? '' : '
560
			AND b.id_board IN ({array_int:include_boards})') . '
561
			AND {query_wanna_see_board}' . ($modSettings['postmod_active'] ? '
562
			AND t.approved = {int:is_approved}
563
			AND ml.approved = {int:is_approved}' : '') . '
564
		ORDER BY t.id_last_msg DESC
565
		LIMIT {int:num_recent}',
566
		array(
567
			'include_boards' => empty($include_boards) ? '' : $include_boards,
568
			'exclude_boards' => empty($exclude_boards) ? '' : $exclude_boards,
569
			'min_message_id' => $modSettings['maxMsgID'] - 35 * min($num_recent, 5),
570
			'is_approved' => 1,
571
			'num_recent' => $num_recent,
572
		)
573
	);
574
	$topics = array();
575
	while ($row = $db->fetch_assoc($request))
576
		$topics[$row['id_topic']] = $row;
577
	$db->free_result($request);
578
579
	// Did we find anything? If not, bail.
580
	if (empty($topics))
581
		return array();
582
583
	// Find all the posts in distinct topics. Newer ones will have higher IDs.
584
	$request = $db->query('substring', '
585
		SELECT
586
			ml.poster_time, mf.subject, ml.id_member, ml.id_msg, t.id_topic, t.num_replies, t.num_views, mg.online_color,
587
			IFNULL(mem.real_name, ml.poster_name) AS poster_name, ' . ($user_info['is_guest'] ? '1 AS is_read, 0 AS new_from' : '
588
			IFNULL(lt.id_msg, IFNULL(lmr.id_msg, 0)) >= ml.id_msg_modified AS is_read,
589
			IFNULL(lt.id_msg, IFNULL(lmr.id_msg, -1)) + 1 AS new_from') . ', SUBSTRING(ml.body, 1, 384) AS body, ml.smileys_enabled, ml.icon
590
		FROM {db_prefix}topics AS t
591
			INNER JOIN {db_prefix}messages AS ml ON (ml.id_msg = t.id_last_msg)
592
			INNER JOIN {db_prefix}messages AS mf ON (mf.id_msg = t.id_first_msg)
593
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = ml.id_member)' . (!$user_info['is_guest'] ? '
594
			LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = t.id_topic AND lt.id_member = {int:current_member})
595
			LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = t.id_board AND lmr.id_member = {int:current_member})' : '') . '
596
			LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = mem.id_group)
597
		WHERE t.id_topic IN ({array_int:topic_list})
598
		ORDER BY t.id_last_msg DESC',
599
		array(
600
			'current_member' => $user_info['id'],
601
			'topic_list' => array_keys($topics),
602
		)
603
	);
604
	$posts = array();
605
	while ($row = $db->fetch_assoc($request))
606
	{
607
		$row['body'] = strip_tags(strtr(parse_bbc($row['body'], $row['smileys_enabled'], $row['id_msg']), array('<br />' => '&#10;')));
608
		if (Util::strlen($row['body']) > 128)
609
			$row['body'] = Util::substr($row['body'], 0, 128) . '...';
610
611
		// Censor the subject.
612
		censorText($row['subject']);
613
		censorText($row['body']);
614
615
		if (!empty($modSettings['messageIconChecks_enable']) && !isset($icon_sources[$row['icon']]))
616
			$icon_sources[$row['icon']] = file_exists($settings['theme_dir'] . '/images/post/' . $row['icon'] . '.png') ? 'images_url' : 'default_images_url';
617
618
		// Build the array.
619
		$posts[] = array(
620
			'board' => array(
621
				'id' => $topics[$row['id_topic']]['id_board'],
622
				'name' => $topics[$row['id_topic']]['board_name'],
623
				'href' => $scripturl . '?board=' . $topics[$row['id_topic']]['id_board'] . '.0',
624
				'link' => '<a href="' . $scripturl . '?board=' . $topics[$row['id_topic']]['id_board'] . '.0">' . $topics[$row['id_topic']]['board_name'] . '</a>',
625
			),
626
			'topic' => $row['id_topic'],
627
			'poster' => array(
628
				'id' => $row['id_member'],
629
				'name' => $row['poster_name'],
630
				'href' => empty($row['id_member']) ? '' : $scripturl . '?action=profile;u=' . $row['id_member'],
631
				'link' => empty($row['id_member']) ? $row['poster_name'] : '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['poster_name'] . '</a>'
632
			),
633
			'subject' => $row['subject'],
634
			'replies' => $row['num_replies'],
635
			'views' => $row['num_views'],
636
			'short_subject' => Util::shorten_text($row['subject'], 25),
637
			'preview' => $row['body'],
638
			'time' => standardTime($row['poster_time']),
639
			'html_time' => htmlTime($row['poster_time']),
640
			'timestamp' => forum_time(true, $row['poster_time']),
641
			'href' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . ';topicseen#new',
642
			'link' => '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#new" rel="nofollow">' . $row['subject'] . '</a>',
643
			// Retained for compatibility - is technically incorrect!
644
			'new' => !empty($row['is_read']),
645
			'is_new' => empty($row['is_read']),
646
			'new_from' => $row['new_from'],
647
			'icon' => '<img src="' . $settings[$icon_sources[$row['icon']]] . '/post/' . $row['icon'] . '.png" style="vertical-align: middle;" alt="' . $row['icon'] . '" />',
648
		);
649
	}
650
	$db->free_result($request);
651
652
	// Just return it.
653
	if ($output_method != 'echo' || empty($posts))
654
		return $posts;
655
656
	echo '
657
		<table class="ssi_table">';
658
	foreach ($posts as $post)
659
		echo '
660
			<tr>
661
				<td class="righttext top">
662
					[', $post['board']['link'], ']
663
				</td>
664
				<td class="top">
665
					<a href="', $post['href'], '">', $post['subject'], '</a>
666
					', $txt['by'], ' ', $post['poster']['link'], '
667
					', !$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>', '
668
				</td>
669
				<td class="righttext">
670
					', $post['time'], '
671
				</td>
672
			</tr>';
673
	echo '
674
		</table>';
675
}
676
677
/**
678
 * Show the top poster's name and profile link.
679
 *
680
 * @param int $topNumber
681
 * @param string $output_method = 'echo'
682
 */
683
function ssi_topPoster($topNumber = 1, $output_method = 'echo')
684
{
685
	require_once(SUBSDIR . '/Stats.subs.php');
686
	$top_posters = topPosters($topNumber);
687
688
	// Just return all the top posters.
689
	if ($output_method != 'echo')
690
		return $top_posters;
691
692
	// Make a quick array to list the links in.
693
	$temp_array = array();
694
	foreach ($top_posters as $member)
695
		$temp_array[] = $member['link'];
696
697
	echo implode(', ', $temp_array);
698
}
699
700
/**
701
 * Show boards by activity.
702
 *
703
 * @param int $num_top
704
 * @param string $output_method = 'echo'
705
 */
706
function ssi_topBoards($num_top = 10, $output_method = 'echo')
707
{
708
	global $txt;
709
710
	require_once(SUBSDIR . '/Stats.subs.php');
711
712
	// Find boards with lots of posts.
713
	$boards = topBoards($num_top, true);
714
715
	foreach ($boards as $id => $board)
716
		$boards[$id]['new'] = empty($board['is_read']);
717
718
	// If we shouldn't output or have nothing to output, just jump out.
719
	if ($output_method != 'echo' || empty($boards))
720
		return $boards;
721
722
	echo '
723
		<table class="ssi_table">
724
			<tr>
725
				<th class="lefttext">', $txt['board'], '</th>
726
				<th class="righttext">', $txt['board_topics'], '</th>
727
				<th class="righttext">', $txt['posts'], '</th>
728
			</tr>';
729
	foreach ($boards as $board)
730
		echo '
731
			<tr>
732
				<td>', $board['new'] ? ' <a href="' . $board['href'] . '"><span class="new_posts">' . $txt['new'] . '</span></a> ' : '', $board['link'], '</td>
733
				<td class="righttext">', $board['num_topics'], '</td>
734
				<td class="righttext">', $board['num_posts'], '</td>
735
			</tr>';
736
	echo '
737
		</table>';
738
}
739
740
/**
741
 * Shows the top topics.
742
 *
743
 * @param string $type
744
 * @param 10 $num_topics
745
 * @param string $output_method = 'echo'
746
 */
747
function ssi_topTopics($type = 'replies', $num_topics = 10, $output_method = 'echo')
748
{
749
	global $txt, $scripturl;
750
751
	require_once(SUBSDIR . '/Stats.subs.php');
752
753
	if (function_exists('topTopic' . ucfirst($type)))
754
		$function = 'topTopic' . ucfirst($type);
755
	else
756
		$function = 'topTopicReplies';
757
758
	$topics = $function($num_topics);
759
760
	foreach ($topics as $topic_id => $row)
761
	{
762
		censorText($row['subject']);
763
764
		$topics[$topic_id]['href'] = $scripturl . '?topic=' . $row['id'] . '.0';
765
		$topics[$topic_id]['link'] = '<a href="' . $scripturl . '?topic=' . $row['id'] . '.0">' . $row['subject'] . '</a>';
766
	}
767
768
	if ($output_method != 'echo' || empty($topics))
769
		return $topics;
770
771
	echo '
772
		<table class="top_topic ssi_table">
773
			<tr>
774
				<th class="link"></th>
775
				<th class="views">', $txt['views'], '</th>
776
				<th class="num_replies">', $txt['replies'], '</th>
777
			</tr>';
778
	foreach ($topics as $topic)
779
		echo '
780
			<tr>
781
				<td class="link">
782
					', $topic['link'], '
783
				</td>
784
				<td class="views">', $topic['num_views'], '</td>
785
				<td class="num_replies">', $topic['num_replies'], '</td>
786
			</tr>';
787
	echo '
788
		</table>';
789
}
790
791
/**
792
 * Shows the top topics, by replies.
793
 *
794
 * @param int $num_topics = 10
795
 * @param string $output_method = 'echo'
796
 */
797
function ssi_topTopicsReplies($num_topics = 10, $output_method = 'echo')
798
{
799
	return ssi_topTopics('replies', $num_topics, $output_method);
800
}
801
802
/**
803
 * Shows the top topics, by views.
804
 *
805
 * @param int $num_topics = 10
806
 * @param string $output_method = 'echo'
807
 */
808
function ssi_topTopicsViews($num_topics = 10, $output_method = 'echo')
809
{
810
	return ssi_topTopics('views', $num_topics, $output_method);
811
}
812
813
/**
814
 * Show a link to the latest member:
815
 *  Please welcome, Someone, out latest member.
816
 *
817
 * @param string $output_method = 'echo'
818
 */
819
function ssi_latestMember($output_method = 'echo')
820
{
821
	global $txt, $context;
822
823
	if ($output_method == 'echo')
824
		echo '
825
	', sprintf($txt['welcome_newest_member'], $context['common_stats']['latest_member']['link']), '<br />';
826
	else
827
		return $context['common_stats']['latest_member'];
828
}
829
830
/**
831
 * Fetch a random member - if type set to 'day' will only change once a day!
832
 *
833
 * @param string $random_type = ''
834
 * @param string $output_method = 'echo'
835
 */
836
function ssi_randomMember($random_type = '', $output_method = 'echo')
837
{
838
	global $modSettings;
839
840
	// If we're looking for something to stay the same each day then seed the generator.
841
	if ($random_type == 'day')
842
	{
843
		// Set the seed to change only once per day.
844
		mt_srand(floor(time() / 86400));
845
	}
846
847
	// Get the lowest ID we're interested in.
848
	$member_id = mt_rand(1, $modSettings['latestMember']);
849
850
	$result = ssi_queryMembers('member_greater_equal', $member_id, 1, 'id_member ASC', $output_method);
851
852
	// If we got nothing do the reverse - in case of unactivated members.
853
	if (empty($result))
854
		$result = ssi_queryMembers('member_lesser_equal', $member_id, 1, 'id_member DESC', $output_method);
855
856
	// Just to be sure put the random generator back to something... random.
857
	if ($random_type != '')
858
		mt_srand(time());
859
860
	return $result;
861
}
862
863
/**
864
 * Fetch a specific member.
865
 *
866
 * @param int[] $member_ids = array()
867
 * @param string $output_method = 'echo'
868
 */
869
function ssi_fetchMember($member_ids = array(), $output_method = 'echo')
870
{
871
	if (empty($member_ids))
872
		return;
873
874
	// Can have more than one member if you really want...
875
	$member_ids = is_array($member_ids) ? $member_ids : array($member_ids);
876
877
	// Then make the query and dump the data.
878
	return ssi_queryMembers('members', $member_ids, '', 'id_member', $output_method);
879
}
880
881
/**
882
 * Fetch a specific member.
883
 *
884
 * @param null $group_id
885
 * @param string $output_method = 'echo'
886
 */
887
function ssi_fetchGroupMembers($group_id = null, $output_method = 'echo')
888
{
889
	if ($group_id === null)
890
		return;
891
892
	return ssi_queryMembers('group_list', is_array($group_id) ? $group_id : array($group_id), '', 'real_name', $output_method);
893
}
894
895
/**
896
 * Fetch some member data!
897
 *
898
 * @param string|null $query_where
899
 * @param string|string[] $query_where_params
900
 * @param string $query_limit
901
 * @param string $query_order
902
 * @param string $output_method
903
 */
904
function ssi_queryMembers($query_where = null, $query_where_params = array(), $query_limit = '', $query_order = 'id_member DESC', $output_method = 'echo')
905
{
906
	global $memberContext;
907
908
	if ($query_where === null)
909
		return;
910
911
	require_once(SUBSDIR . '/Members.subs.php');
912
	$members_data = retrieveMemberData(array(
913
		$query_where => $query_where_params,
914
		'limit' => !empty($query_limit) ? (int) $query_limit : 10,
915
		'order_by' => $query_order,
916
		'activated_status' => 1,
917
	));
918
919
	$members = array();
920
	foreach ($members_data['member_info'] as $row)
921
		$members[] = $row['id'];
922
923
	if (empty($members))
924
		return array();
925
926
	// Load the members.
927
	loadMemberData($members);
928
929
	// Draw the table!
930
	if ($output_method == 'echo')
931
		echo '
932
		<table class="ssi_table">';
933
934
	$query_members = array();
935
	foreach ($members as $member)
936
	{
937
		// Load their context data.
938
		if (!loadMemberContext($member))
939
			continue;
940
941
		// Store this member's information.
942
		$query_members[$member] = $memberContext[$member];
943
944
		// Only do something if we're echo'ing.
945
		if ($output_method == 'echo')
946
			echo '
947
			<tr>
948
				<td class="centertext">
949
					', $query_members[$member]['link'], '
950
					<br />', $query_members[$member]['blurb'], '
951
					<br />', $query_members[$member]['avatar']['image'], '
952
				</td>
953
			</tr>';
954
	}
955
956
	// End the table if appropriate.
957
	if ($output_method == 'echo')
958
		echo '
959
		</table>';
960
961
	// Send back the data.
962
	return $query_members;
963
}
964
965
/**
966
 * Show some basic stats:  Total This: XXXX, etc.
967
 *
968
 * @param string $output_method
969
 */
970
function ssi_boardStats($output_method = 'echo')
971
{
972
	global $txt, $scripturl, $modSettings;
973
974
	if (!allowedTo('view_stats'))
975
		return;
976
977
	require_once(SUBSDIR . '/Boards.subs.php');
978
	require_once(SUBSDIR . '/Stats.subs.php');
979
980
	$totals = array(
981
		'members' => $modSettings['totalMembers'],
982
		'posts' => $modSettings['totalMessages'],
983
		'topics' => $modSettings['totalTopics'],
984
		'boards' => countBoards(),
985
		'categories' => numCategories(),
986
	);
987
988
	if ($output_method != 'echo')
989
		return $totals;
990
991
	echo '
992
		', $txt['total_members'], ': <a href="', $scripturl . '?action=memberlist">', comma_format($totals['members']), '</a><br />
993
		', $txt['total_posts'], ': ', comma_format($totals['posts']), '<br />
994
		', $txt['total_topics'], ': ', comma_format($totals['topics']), ' <br />
995
		', $txt['total_cats'], ': ', comma_format($totals['categories']), '<br />
996
		', $txt['total_boards'], ': ', comma_format($totals['boards']);
997
}
998
999
/**
1000
 * Shows a list of online users:
1001
 *  YY Guests, ZZ Users and then a list...
1002
 *
1003
 * @param string $output_method
1004
 */
1005
function ssi_whosOnline($output_method = 'echo')
1006
{
1007
	global $user_info, $txt, $settings;
1008
1009
	require_once(SUBSDIR . '/MembersOnline.subs.php');
1010
	$membersOnlineOptions = array(
1011
		'show_hidden' => allowedTo('moderate_forum'),
1012
	);
1013
	$return = getMembersOnlineStats($membersOnlineOptions);
1014
1015
	// Add some redundancy for backwards compatibility reasons.
1016
	if ($output_method != 'echo')
1017
		return $return + array(
1018
			'users' => $return['users_online'],
1019
			'guests' => $return['num_guests'],
1020
			'hidden' => $return['num_users_hidden'],
1021
			'buddies' => $return['num_buddies'],
1022
			'num_users' => $return['num_users_online'],
1023
			'total_users' => $return['num_users_online'] + $return['num_guests'] + $return['num_spiders'],
1024
		);
1025
1026
	echo '
1027
		', 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'];
1028
1029
	$bracketList = array();
1030
	if (!empty($user_info['buddies']))
1031
		$bracketList[] = comma_format($return['num_buddies']) . ' ' . ($return['num_buddies'] == 1 ? $txt['buddy'] : $txt['buddies']);
1032
	if (!empty($return['num_spiders']))
1033
		$bracketList[] = comma_format($return['num_spiders']) . ' ' . ($return['num_spiders'] == 1 ? $txt['spider'] : $txt['spiders']);
1034
	if (!empty($return['num_users_hidden']))
1035
		$bracketList[] = comma_format($return['num_users_hidden']) . ' ' . $txt['hidden'];
1036
1037
	if (!empty($bracketList))
1038
		echo ' (' . implode(', ', $bracketList) . ')';
1039
1040
	echo '<br />
1041
			', implode(', ', $return['list_users_online']);
1042
1043
	// Showing membergroups?
1044
	if (!empty($settings['show_group_key']) && !empty($return['membergroups']))
1045
		echo '<br />
1046
			[' . implode(']&nbsp;&nbsp;[', $return['membergroups']) . ']';
1047
}
1048
1049
/**
1050
 * Just like whosOnline except it also logs the online presence.
1051
 *
1052
 * @param string $output_method
1053
 */
1054
function ssi_logOnline($output_method = 'echo')
1055
{
1056
	writeLog();
1057
1058
	if ($output_method != 'echo')
1059
		return ssi_whosOnline($output_method);
1060
	else
1061
		ssi_whosOnline($output_method);
1062
}
1063
1064
/**
1065
 * Shows a login box.
1066
 *
1067
 * @param string $redirect_to = ''
1068
 * @param string $output_method = 'echo'
1069
 */
1070
function ssi_login($redirect_to = '', $output_method = 'echo')
1071
{
1072
	global $scripturl, $txt, $user_info, $modSettings, $context, $settings;
1073
1074
	if ($redirect_to != '')
1075
		$_SESSION['login_url'] = $redirect_to;
1076
1077
	if ($output_method != 'echo' || !$user_info['is_guest'])
1078
		return $user_info['is_guest'];
1079
1080
	$context['default_username'] = isset($_POST['user']) ? preg_replace('~&amp;#(\\d{1,7}|x[0-9a-fA-F]{1,6});~', '&#\\1;', htmlspecialchars($_POST['user'], ENT_COMPAT, 'UTF-8')) : '';
1081
1082
	echo '
1083
		<script src="', $settings['default_theme_url'], '/scripts/sha256.js"></script>
1084
1085
		<form action="', $scripturl, '?action=login2" name="frmLogin" id="frmLogin" method="post" accept-charset="UTF-8" ', empty($context['disable_login_hashing']) ? ' onsubmit="hashLoginPassword(this, \'' . $context['session_id'] . '\');"' : '', '>
1086
		<div class="login">
1087
			<div class="roundframe">';
1088
1089
	// Did they make a mistake last time?
1090
	if (!empty($context['login_errors']))
1091
		echo '
1092
			<p class="errorbox">', implode('<br />', $context['login_errors']), '</p><br />';
1093
1094
	// Or perhaps there's some special description for this time?
1095
	if (isset($context['description']))
1096
		echo '
1097
				<p class="description">', $context['description'], '</p>';
1098
1099
	// Now just get the basic information - username, password, etc.
1100
	echo '
1101
				<dl>
1102
					<dt>', $txt['username'], ':</dt>
1103
					<dd><input type="text" name="user" size="20" value="', $context['default_username'], '" class="input_text" autofocus="autofocus" placeholder="', $txt['username'], '" /></dd>
1104
					<dt>', $txt['password'], ':</dt>
1105
					<dd><input type="password" name="passwrd" value="" size="20" class="input_password" placeholder="', $txt['password'], '" /></dd>
1106
				</dl>';
1107
1108
	if (!empty($modSettings['enableOpenID']))
1109
		echo '<p><strong>&mdash;', $txt['or'], '&mdash;</strong></p>
1110
				<dl>
1111
					<dt>', $txt['openid'], ':</dt>
1112
					<dd><input type="text" name="openid_identifier" class="input_text openid_login" size="17" />&nbsp;<a href="', $scripturl, '?action=quickhelp;help=register_openid" onclick="return reqOverlayDiv(this.href);" class="help"><img src="', $settings['images_url'], '/helptopics.png" alt="', $txt['help'], '" class="centericon" /></a></dd>
1113
				</dl>';
1114
1115
	echo '
1116
				<p><input type="submit" value="', $txt['login'], '" class="button_submit" /></p>
1117
				<p class="smalltext"><a href="', $scripturl, '?action=reminder">', $txt['forgot_your_password'], '</a></p>
1118
				<input type="hidden" name="hash_passwrd" value="" />
1119
				<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
1120
				<input type="hidden" name="', $context['login_token_var'], '" value="', $context['login_token'], '" />
1121
			</div>
1122
		</div>
1123
		</form>';
1124
1125
	// Focus on the correct input - username or password.
1126
	echo '
1127
		<script><!-- // --><![CDATA[
1128
			document.forms.frmLogin.', isset($context['default_username']) && $context['default_username'] != '' ? 'passwrd' : 'user', '.focus();
1129
		// ]]></script>';
1130
1131
}
1132
1133
/**
1134
 * Show the most-voted-in poll.
1135
 *
1136
 * @param string $output_method = 'echo'
1137
 */
1138
function ssi_topPoll($output_method = 'echo')
1139
{
1140
	// Just use recentPoll, no need to duplicate code...
1141
	return ssi_recentPoll(true, $output_method);
1142
}
1143
1144
/**
1145
 * Show the most recently posted poll.
1146
 *
1147
 * @param bool $topPollInstead = false
1148
 * @param string $output_method = string
1149
 */
1150
function ssi_recentPoll($topPollInstead = false, $output_method = 'echo')
1151
{
1152
	global $txt, $settings, $boardurl, $user_info, $context, $modSettings;
1153
1154
	$boardsAllowed = array_intersect(boardsAllowedTo('poll_view'), boardsAllowedTo('poll_vote'));
1155
1156
	if (empty($boardsAllowed))
1157
		return array();
1158
1159
	$db = database();
1160
1161
	$request = $db->query('', '
1162
		SELECT p.id_poll, p.question, t.id_topic, p.max_votes, p.guest_vote, p.hide_results, p.expire_time
1163
		FROM {db_prefix}polls AS p
1164
			INNER JOIN {db_prefix}topics AS t ON (t.id_poll = p.id_poll' . ($modSettings['postmod_active'] ? ' AND t.approved = {int:is_approved}' : '') . ')
1165
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)' . ($topPollInstead ? '
1166
			INNER JOIN {db_prefix}poll_choices AS pc ON (pc.id_poll = p.id_poll)' : '') . '
1167
			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})
1168
		WHERE p.voting_locked = {int:voting_opened}
1169
			AND (p.expire_time = {int:no_expiration} OR {int:current_time} < p.expire_time)
1170
			AND ' . ($user_info['is_guest'] ? 'p.guest_vote = {int:guest_vote_allowed}' : 'lp.id_choice IS NULL') . '
1171
			AND {query_wanna_see_board}' . (!in_array(0, $boardsAllowed) ? '
1172
			AND b.id_board IN ({array_int:boards_allowed_list})' : '') . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? '
1173
			AND b.id_board != {int:recycle_enable}' : '') . '
1174
		ORDER BY ' . ($topPollInstead ? 'pc.votes' : 'p.id_poll') . ' DESC
1175
		LIMIT 1',
1176
		array(
1177
			'current_member' => $user_info['id'],
1178
			'boards_allowed_list' => $boardsAllowed,
1179
			'is_approved' => 1,
1180
			'guest_vote_allowed' => 1,
1181
			'no_member' => 0,
1182
			'voting_opened' => 0,
1183
			'no_expiration' => 0,
1184
			'current_time' => time(),
1185
			'recycle_enable' => $modSettings['recycle_board'],
1186
		)
1187
	);
1188
	$row = $db->fetch_assoc($request);
1189
	$db->free_result($request);
1190
1191
	// This user has voted on all the polls.
1192
	if ($row == false)
1193
		return array();
1194
1195
	// If this is a guest who's voted we'll through ourselves to show poll to show the results.
1196
	if ($user_info['is_guest'] && (!$row['guest_vote'] || (isset($_COOKIE['guest_poll_vote']) && in_array($row['id_poll'], explode(',', $_COOKIE['guest_poll_vote'])))))
1197
		return ssi_showPoll($row['id_topic'], $output_method);
1198
1199
	$request = $db->query('', '
1200
		SELECT COUNT(DISTINCT id_member)
1201
		FROM {db_prefix}log_polls
1202
		WHERE id_poll = {int:current_poll}',
1203
		array(
1204
			'current_poll' => $row['id_poll'],
1205
		)
1206
	);
1207
	list ($total) = $db->fetch_row($request);
1208
	$db->free_result($request);
1209
1210
	$request = $db->query('', '
1211
		SELECT id_choice, label, votes
1212
		FROM {db_prefix}poll_choices
1213
		WHERE id_poll = {int:current_poll}',
1214
		array(
1215
			'current_poll' => $row['id_poll'],
1216
		)
1217
	);
1218
	$options = array();
1219
	while ($rowChoice = $db->fetch_assoc($request))
1220
	{
1221
		censorText($rowChoice['label']);
1222
1223
		$options[$rowChoice['id_choice']] = array($rowChoice['label'], $rowChoice['votes']);
1224
	}
1225
	$db->free_result($request);
1226
1227
	// Can they view it?
1228
	$is_expired = !empty($row['expire_time']) && $row['expire_time'] < time();
1229
	$allow_view_results = allowedTo('moderate_board') || $row['hide_results'] == 0 || $is_expired;
1230
1231
	$return = array(
1232
		'id' => $row['id_poll'],
1233
		'image' => 'poll',
1234
		'question' => $row['question'],
1235
		'total_votes' => $total,
1236
		'is_locked' => false,
1237
		'topic' => $row['id_topic'],
1238
		'allow_view_results' => $allow_view_results,
1239
		'options' => array()
1240
	);
1241
1242
	// Calculate the percentages and bar lengths...
1243
	$divisor = $return['total_votes'] == 0 ? 1 : $return['total_votes'];
1244
	foreach ($options as $i => $option)
1245
	{
1246
		$bar = floor(($option[1] * 100) / $divisor);
1247
		$barWide = $bar == 0 ? 1 : floor(($bar * 5) / 3);
1248
		$return['options'][$i] = array(
1249
			'id' => 'options-' . ($topPollInstead ? 'top-' : 'recent-') . $i,
1250
			'percent' => $bar,
1251
			'votes' => $option[1],
1252
			'bar' => '<span style="white-space: nowrap;"><img src="' . $settings['images_url'] . '/poll_' . ($context['right_to_left'] ? 'right' : 'left') . '.png" alt="" /><img src="' . $settings['images_url'] . '/poll_middle.png" style="width:' . $barWide . 'px; height:12px;" alt="-" /><img src="' . $settings['images_url'] . '/poll_' . ($context['right_to_left'] ? 'left' : 'right') . '.png" alt="" /></span>',
1253
			'option' => parse_bbc($option[0]),
1254
			'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') . '" />'
1255
		);
1256
	}
1257
1258
	$return['allowed_warning'] = $row['max_votes'] > 1 ? sprintf($txt['poll_options6'], min(count($options), $row['max_votes'])) : '';
1259
1260
	if ($output_method != 'echo')
1261
		return $return;
1262
1263
	if ($allow_view_results)
1264
	{
1265
		echo '
1266
		<form class="ssi_poll" action="', $boardurl, '/SSI.php?ssi_function=pollVote" method="post" accept-charset="UTF-8">
1267
			<strong>', $return['question'], '</strong><br />
1268
			', !empty($return['allowed_warning']) ? $return['allowed_warning'] . '<br />' : '';
1269
1270
		foreach ($return['options'] as $option)
1271
			echo '
1272
			<label for="', $option['id'], '">', $option['vote_button'], ' ', $option['option'], '</label><br />';
1273
1274
		echo '
1275
			<input type="submit" value="', $txt['poll_vote'], '" class="button_submit" />
1276
			<input type="hidden" name="poll" value="', $return['id'], '" />
1277
			<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
1278
		</form>';
1279
	}
1280
	else
1281
		echo $txt['poll_cannot_see'];
1282
}
1283
1284
/**
1285
 * Show a poll.
1286
 * It is possible to use this function in combination with the template
1287
 * template_display_poll_above from Display.template.php, the only part missing
1288
 * is the definition of the poll moderation button array (see Display.controller.php
1289
 * for details).
1290
 *
1291
 * @param int|null $topicID = null
1292
 * @param string $output_method = 'echo'
1293
 */
1294
function ssi_showPoll($topicID = null, $output_method = 'echo')
1295
{
1296
	global $txt, $user_info, $context, $scripturl;
1297
	global $board;
1298
	static $last_board = null;
1299
1300
	require_once(SUBSDIR . '/Poll.subs.php');
1301
	require_once(SUBSDIR . '/Topic.subs.php');
1302
1303
	if ($topicID === null && isset($_REQUEST['ssi_topic']))
1304
		$topicID = (int) $_REQUEST['ssi_topic'];
1305
	else
1306
		$topicID = (int) $topicID;
1307
1308
	if (empty($topicID))
1309
		return array();
1310
1311
	// Get the topic starter information.
1312
	$topicinfo = getTopicInfo($topicID, 'starter');
1313
1314
	$boards_can_poll = boardsAllowedTo('poll_view');
1315
1316
	// If:
1317
	//  - is not allowed to see poll in any board,
1318
	//  - or:
1319
	//     - is not allowed in the specific board, and
1320
	//     - is not an admin
1321
	// fail
1322
	if (empty($boards_can_poll) || (!in_array($topicinfo['id_board'], $boards_can_poll) && !in_array(0, $boards_can_poll)))
1323
		return array();
1324
1325
	$context['user']['started'] = $user_info['id'] == $topicinfo['id_member'] && !$user_info['is_guest'];
1326
1327
	$poll_id = associatedPoll($topicID);
1328
	loadPollContext($poll_id);
1329
1330
	if (empty($context['poll']))
1331
		return array();
1332
1333
	// For "compatibility" sake
1334
	// @deprecated since 1.0
1335
	$context['poll']['allow_vote'] = $context['allow_vote'];
1336
	$context['poll']['allow_view_results'] = $context['allow_poll_view'];
1337
	$context['poll']['topic'] = $topicID;
1338
1339
	if ($output_method != 'echo')
1340
		return $context['poll'];
1341
1342
	echo '
1343
		<div class="content" id="poll_options">
1344
			<h4 id="pollquestion">
1345
				', $context['poll']['question'], '
1346
			</h4>';
1347
1348
	if ($context['poll']['allow_vote'])
1349
	{
1350
		echo '
1351
			<form action="', $scripturl, '?action=poll;sa=vote;topic=', $context['current_topic'], '.', $context['start'], ';poll=', $context['poll']['id'], '" method="post" accept-charset="UTF-8">';
1352
1353
		// Show a warning if they are allowed more than one option.
1354
		if ($context['poll']['allowed_warning'])
1355
			echo '
1356
				<p>', $context['poll']['allowed_warning'], '</p>';
1357
1358
		echo '
1359
				<ul class="options">';
1360
1361
		// Show each option with its button - a radio likely.
1362
		foreach ($context['poll']['options'] as $option)
1363
			echo '
1364
					<li>', $option['vote_button'], ' <label for="', $option['id'], '">', $option['option'], '</label></li>';
1365
1366
		echo '
1367
				</ul>
1368
				<div class="submitbutton">
1369
					<input type="submit" value="', $txt['poll_vote'], '" class="button_submit" />
1370
					<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
1371
				</div>
1372
			</form>';
1373
		// Is the clock ticking?
1374
		if (!empty($context['poll']['expire_time']))
1375
			echo '
1376
			<p><strong>', ($context['poll']['is_expired'] ? $txt['poll_expired_on'] : $txt['poll_expires_on']), ':</strong> ', $context['poll']['expire_time'], '</p>';
1377
1378
	}
1379
	elseif ($context['poll']['allow_view_results'])
1380
	{
1381
		echo '
1382
			<ul class="options">';
1383
1384
		// Show each option with its corresponding percentage bar.
1385
		foreach ($context['poll']['options'] as $option)
1386
		{
1387
			echo '
1388
				<li', $option['voted_this'] ? ' class="voted"' : '', '>', $option['option'], '
1389
					<div class="results">';
1390
1391
			if ($context['allow_poll_view'])
1392
				echo '
1393
						<div class="statsbar"> ', $option['bar_ndt'], '</div>
1394
						<span class="percentage">', $option['votes'], ' (', $option['percent'], '%)</span>';
1395
1396
			echo '
1397
					</div>
1398
				</li>';
1399
		}
1400
1401
		echo '
1402
			</ul>';
1403
1404
		if ($context['allow_poll_view'])
1405
			echo '
1406
			<p><strong>', $txt['poll_total_voters'], ':</strong> ', $context['poll']['total_votes'], '</p>';
1407
		// Is the clock ticking?
1408
		if (!empty($context['poll']['expire_time']))
1409
			echo '
1410
			<p><strong>', ($context['poll']['is_expired'] ? $txt['poll_expired_on'] : $txt['poll_expires_on']), ':</strong> ', $context['poll']['expire_time'], '</p>';
1411
	}
1412
	// Cannot see it I'm afraid!
1413
	else
1414
		echo $txt['poll_cannot_see'];
1415
1416
	echo '
1417
			</div>';
1418
}
1419
1420
/**
1421
 * Takes care of voting - don't worry, this is done automatically.
1422
 */
1423
function ssi_pollVote()
1424
{
1425
	global $context, $sc, $topic, $board;
1426
1427
	$pollID = isset($_POST['poll']) ? (int) $_POST['poll'] : 0;
1428
1429
	if (empty($pollID) || !isset($_POST[$context['session_var']]) || $_POST[$context['session_var']] != $sc || empty($_POST['options']))
1430
	{
1431
		echo '<!DOCTYPE html>
1432
<html>
1433
<head>
1434
	<script><!-- // --><![CDATA[
1435
		history.go(-1);
1436
	// ]]></script>
1437
</head>
1438
<body>&laquo;</body>
1439
</html>';
1440
		return;
1441
	}
1442
1443
	require_once(CONTROLLERDIR . '/Poll.controller.php');
1444
	require_once(SUBSDIR . '/Poll.subs.php');
1445
	// We have to fake we are in a topic so that we can use the proper controller
1446
	list ($topic, $board) = topicFromPoll($pollID);
1447
	loadBoard();
1448
1449
	$poll_action = new Poll_Controller();
1450
1451
	// The controller takes already care of redirecting properly or fail
1452
	$poll_action->action_vote();
1453
}
1454
1455
/**
1456
 * Show a search box.
1457
 *
1458
 * @param string $output_method = 'echo'
1459
 */
1460
function ssi_quickSearch($output_method = 'echo')
1461
{
1462
	global $scripturl, $txt;
1463
1464
	if (!allowedTo('search_posts'))
1465
		return;
1466
1467
	if ($output_method != 'echo')
1468
		return $scripturl . '?action=search';
1469
1470
	echo '
1471
		<form action="', $scripturl, '?action=search;sa=results" method="post" accept-charset="UTF-8">
1472
			<input type="hidden" name="advanced" value="0" /><input type="text" name="search" size="30" class="input_text" /> <input type="submit" value="', $txt['search'], '" class="button_submit" />
1473
		</form>';
1474
}
1475
1476
/**
1477
 * Show what would be the forum news.
1478
 *
1479
 * @param string $output_method = 'echo'
1480
 */
1481
function ssi_news($output_method = 'echo')
1482
{
1483
	global $context;
1484
1485
	if ($output_method != 'echo')
1486
		return $context['random_news_line'];
1487
1488
	echo $context['random_news_line'];
1489
}
1490
1491
/**
1492
 * Show today's birthdays.
1493
 *
1494
 * @param string $output_method = 'echo'
1495
 */
1496
function ssi_todaysBirthdays($output_method = 'echo')
1497
{
1498
	global $scripturl, $modSettings, $user_info;
1499
1500
	if (empty($modSettings['cal_enabled']) || !allowedTo('calendar_view') || !allowedTo('profile_view_any'))
1501
		return;
1502
1503
	$eventOptions = array(
1504
		'include_birthdays' => true,
1505
		'num_days_shown' => empty($modSettings['cal_days_for_index']) || $modSettings['cal_days_for_index'] < 1 ? 1 : $modSettings['cal_days_for_index'],
1506
	);
1507
	$return = cache_quick_get('calendar_index_offset_' . ($user_info['time_offset'] + $modSettings['time_offset']), 'subs/Calendar.subs.php', 'cache_getRecentEvents', array($eventOptions));
1508
1509
	if ($output_method != 'echo')
1510
		return $return['calendar_birthdays'];
1511
1512
	foreach ($return['calendar_birthdays'] as $member)
1513
		echo '
1514
			<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'] ? ', ' : '');
1515
}
1516
1517
/**
1518
 * Show today's holidays.
1519
 *
1520
 * @param string $output_method = 'echo'
1521
 */
1522
function ssi_todaysHolidays($output_method = 'echo')
1523
{
1524
	global $modSettings, $user_info;
1525
1526
	if (empty($modSettings['cal_enabled']) || !allowedTo('calendar_view'))
1527
		return;
1528
1529
	$eventOptions = array(
1530
		'include_holidays' => true,
1531
		'num_days_shown' => empty($modSettings['cal_days_for_index']) || $modSettings['cal_days_for_index'] < 1 ? 1 : $modSettings['cal_days_for_index'],
1532
	);
1533
	$return = cache_quick_get('calendar_index_offset_' . ($user_info['time_offset'] + $modSettings['time_offset']), 'subs/Calendar.subs.php', 'cache_getRecentEvents', array($eventOptions));
1534
1535
	if ($output_method != 'echo')
1536
		return $return['calendar_holidays'];
1537
1538
	echo '
1539
		', implode(', ', $return['calendar_holidays']);
1540
}
1541
1542
/**
1543
 * Show today's events.
1544
 *
1545
 * @param string $output_method = 'echo'
1546
 */
1547
function ssi_todaysEvents($output_method = 'echo')
1548
{
1549
	global $modSettings, $user_info;
1550
1551
	if (empty($modSettings['cal_enabled']) || !allowedTo('calendar_view'))
1552
		return;
1553
1554
	$eventOptions = array(
1555
		'include_events' => true,
1556
		'num_days_shown' => empty($modSettings['cal_days_for_index']) || $modSettings['cal_days_for_index'] < 1 ? 1 : $modSettings['cal_days_for_index'],
1557
	);
1558
	$return = cache_quick_get('calendar_index_offset_' . ($user_info['time_offset'] + $modSettings['time_offset']), 'subs/Calendar.subs.php', 'cache_getRecentEvents', array($eventOptions));
1559
1560
	if ($output_method != 'echo')
1561
		return $return['calendar_events'];
1562
1563
	foreach ($return['calendar_events'] as $event)
1564
	{
1565
		if ($event['can_edit'])
1566
			echo '
1567
	<a href="' . $event['modify_href'] . '" style="color: #ff0000;">*</a> ';
1568
		echo '
1569
	' . $event['link'] . (!$event['is_last'] ? ', ' : '');
1570
	}
1571
}
1572
1573
/**
1574
 * Show all calendar entires for today. (birthdays, holidays, and events.)
1575
 *
1576
 * @param string $output_method = 'echo'
1577
 */
1578
function ssi_todaysCalendar($output_method = 'echo')
1579
{
1580
	global $modSettings, $txt, $scripturl, $user_info;
1581
1582
	if (empty($modSettings['cal_enabled']) || !allowedTo('calendar_view'))
1583
		return;
1584
1585
	$eventOptions = array(
1586
		'include_birthdays' => allowedTo('profile_view_any'),
1587
		'include_holidays' => true,
1588
		'include_events' => true,
1589
		'num_days_shown' => empty($modSettings['cal_days_for_index']) || $modSettings['cal_days_for_index'] < 1 ? 1 : $modSettings['cal_days_for_index'],
1590
	);
1591
	$return = cache_quick_get('calendar_index_offset_' . ($user_info['time_offset'] + $modSettings['time_offset']), 'subs/Calendar.subs.php', 'cache_getRecentEvents', array($eventOptions));
1592
1593
	if ($output_method != 'echo')
1594
		return $return;
1595
1596
	if (!empty($return['calendar_holidays']))
1597
		echo '
1598
			<span class="holiday">' . $txt['calendar_prompt'] . ' ' . implode(', ', $return['calendar_holidays']) . '<br /></span>';
1599
1600
	if (!empty($return['calendar_birthdays']))
1601
	{
1602
		echo '
1603
			<span class="birthday">' . $txt['birthdays_upcoming'] . '</span> ';
1604
		foreach ($return['calendar_birthdays'] as $member)
1605
			echo '
1606
			<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'] ? ', ' : '';
1607
		echo '
1608
			<br />';
1609
	}
1610
1611
	if (!empty($return['calendar_events']))
1612
	{
1613
		echo '
1614
			<span class="event">' . $txt['events_upcoming'] . '</span> ';
1615
		foreach ($return['calendar_events'] as $event)
1616
		{
1617
			if ($event['can_edit'])
1618
				echo '
1619
			<a href="' . $event['modify_href'] . '" style="color: #ff0000;">*</a> ';
1620
			echo '
1621
			' . $event['link'] . (!$event['is_last'] ? ', ' : '');
1622
		}
1623
	}
1624
}
1625
1626
/**
1627
 * Show the latest news, with a template... by board.
1628
 *
1629
 * @param int|null $board
1630
 * @param int|null $limit
1631
 * @param int|null $start
1632
 * @param int|null $length
1633
 * @param string $preview
1634
 * @param string $output_method = 'echo'
1635
 */
1636
function ssi_boardNews($board = null, $limit = null, $start = null, $length = null, $preview = 'first', $output_method = 'echo')
1637
{
1638
	global $scripturl, $txt, $settings, $modSettings;
1639
1640
	loadLanguage('Stats');
1641
1642
	$db = database();
1643
1644
	// Must be integers....
1645
	if ($limit === null)
1646
		$limit = isset($_GET['limit']) ? (int) $_GET['limit'] : 5;
1647
	else
1648
		$limit = (int) $limit;
1649
1650
	if ($start === null)
1651
		$start = isset($_GET['start']) ? (int) $_GET['start'] : 0;
1652
	else
1653
		$start = (int) $start;
1654
1655
	if ($board !== null)
1656
		$board = (int) $board;
1657
	elseif (isset($_GET['board']))
1658
		$board = (int) $_GET['board'];
1659
1660
	if ($length === null)
1661
		$length = isset($_GET['length']) ? (int) $_GET['length'] : 500;
1662
	else
1663
		$length = (int) $length;
1664
1665
	$limit = max(0, $limit);
1666
	$start = max(0, $start);
1667
1668
	// Make sure guests can see this board.
1669
	$request = $db->query('', '
1670
		SELECT id_board
1671
		FROM {db_prefix}boards
1672
		WHERE ' . ($board === null ? '' : 'id_board = {int:current_board}
1673
			AND ') . 'FIND_IN_SET(-1, member_groups) != 0
1674
		LIMIT 1',
1675
		array(
1676
			'current_board' => $board,
1677
		)
1678
	);
1679
	if ($db->num_rows($request) == 0)
1680
	{
1681
		if ($output_method == 'echo')
1682
			die($txt['ssi_no_guests']);
0 ignored issues
show
Coding Style Compatibility introduced by
The function ssi_boardNews() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1683
		else
1684
			return array();
1685
	}
1686
	list ($board) = $db->fetch_row($request);
1687
	$db->free_result($request);
1688
1689
	// Load the message icons - the usual suspects.
1690
	require_once(SUBSDIR . '/MessageIndex.subs.php');
1691
	$icon_sources = MessageTopicIcons();
1692
1693
	// Find the posts.
1694
	$indexOptions = array(
1695
		'only_approved' => true,
1696
		'include_sticky' => false,
1697
		'ascending' => false,
1698
		'include_avatars' => false,
1699
		'previews' => $length
1700
	);
1701
	$request = messageIndexTopics($board, 0, $start, $limit, 'first_post', 't.id_topic', $indexOptions);
1702
1703
	if (empty($request))
1704
		return;
1705
1706
	$return = array();
1707
	foreach ($request as $row)
1708
	{
1709
		if (!isset($row[$preview . '_body']))
1710
			$preview = 'first';
1711
1712
		$row['body'] = $row[$preview . '_body'];
1713
		$row['subject'] = $row[$preview . '_subject'];
1714
		$row['id_msg'] = $row['id_' . $preview . '_msg'];
1715
		$row['icon'] = $row[$preview . '_icon'];
1716
		$row['id_member'] = $row[$preview . '_id_member'];
1717
		$row['smileys_enabled'] = $row[$preview . '_smileys'];
1718
		$row['poster_time'] = $row[$preview . '_poster_time'];
1719
		$row['poster_name'] = $row[$preview . '_display_name'];
1720
		$row['body'] = parse_bbc($row['body'], $row['smileys_enabled'], $row['id_msg']);
1721
1722
		// Check that this message icon is there...
1723
		if (!empty($modSettings['messageIconChecks_enable']) && !isset($icon_sources[$row['icon']]))
1724
			$icon_sources[$row['icon']] = file_exists($settings['theme_dir'] . '/images/post/' . $row['icon'] . '.png') ? 'images_url' : 'default_images_url';
1725
1726
		censorText($row['subject']);
1727
		censorText($row['body']);
1728
1729
		$return[] = array(
1730
			'id' => $row['id_topic'],
1731
			'message_id' => $row['id_msg'],
1732
			'icon' => '<img src="' . $settings[$icon_sources[$row['icon']]] . '/post/' . $row['icon'] . '.png" alt="' . $row['icon'] . '" />',
1733
			'subject' => $row['subject'],
1734
			'time' => standardTime($row['poster_time']),
1735
			'html_time' => htmlTime($row['poster_time']),
1736
			'timestamp' => forum_time(true, $row['poster_time']),
1737
			'body' => $row['body'],
1738
			'href' => $scripturl . '?topic=' . $row['id_topic'] . '.0',
1739
			'link' => '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.0">' . $row['num_replies'] . ' ' . ($row['num_replies'] == 1 ? $txt['ssi_comment'] : $txt['ssi_comments']) . '</a>',
1740
			'replies' => $row['num_replies'],
1741
			'comment_href' => !empty($row['locked']) ? '' : $scripturl . '?action=post;topic=' . $row['id_topic'] . '.' . $row['num_replies'] . ';last_msg=' . $row['id_last_msg'],
1742
			'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>',
1743
			'new_comment' => !empty($row['locked']) ? '' : '<a href="' . $scripturl . '?action=post;topic=' . $row['id_topic'] . '.' . $row['num_replies'] . '">' . $txt['ssi_write_comment'] . '</a>',
1744
			'poster' => array(
1745
				'id' => $row['id_member'],
1746
				'name' => $row['poster_name'],
1747
				'href' => !empty($row['id_member']) ? $scripturl . '?action=profile;u=' . $row['id_member'] : '',
1748
				'link' => !empty($row['id_member']) ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['poster_name'] . '</a>' : $row['poster_name']
1749
			),
1750
			'locked' => !empty($row['locked']),
1751
			'is_last' => false
1752
		);
1753
	}
1754
1755
	$return[count($return) - 1]['is_last'] = true;
1756
1757
	if ($output_method != 'echo')
1758
		return $return;
1759
1760
	foreach ($return as $news)
1761
	{
1762
		echo '
1763
			<div class="news_item">
1764
				<h3 class="news_header">
1765
					', $news['icon'], '
1766
					<a href="', $news['href'], '">', $news['subject'], '</a>
1767
				</h3>
1768
				<div class="news_timestamp">', $news['time'], ' ', $txt['by'], ' ', $news['poster']['link'], '</div>
1769
				<div class="news_body" style="padding: 2ex 0;">', $news['body'], '</div>
1770
				', $news['link'], $news['locked'] ? '' : ' | ' . $news['comment_link'], '
1771
			</div>';
1772
1773
		if (!$news['is_last'])
1774
			echo '
1775
			<hr />';
1776
	}
1777
}
1778
1779
/**
1780
 * Show the most recent events.
1781
 *
1782
 * @param int $max_events
1783
 * @param string $output_method = 'echo'
1784
 */
1785
function ssi_recentEvents($max_events = 7, $output_method = 'echo')
1786
{
1787
	global $modSettings, $txt;
1788
1789
	if (empty($modSettings['cal_enabled']) || !allowedTo('calendar_view'))
1790
		return;
1791
1792
	require_once(SUBSDIR . '/Calendar.subs.php');
1793
1794
	// Find all events which are happening in the near future that the member can see.
1795
	$date = strftime('%Y-%m-%d', forum_time(false));
1796
	$events = getEventRange($date, $date, true, $max_events);
1797
1798
	$return = array();
1799
	$duplicates = array();
1800
	foreach ($events as $date => $day_events)
1801
	{
1802
		foreach ($day_events as $row)
1803
		{
1804
			// 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.
1805
			if (!empty($duplicates[$row['title'] . $row['id_topic']]))
1806
				continue;
1807
1808
			$return[$date][] = $row;
1809
1810
			// Let's not show this one again, huh?
1811
			$duplicates[$row['title'] . $row['id_topic']] = true;
1812
		}
1813
	}
1814
1815
	foreach ($return as $mday => $array)
1816
		$return[$mday][count($array) - 1]['is_last'] = true;
1817
1818
	if ($output_method != 'echo' || empty($return))
1819
		return $return;
1820
1821
	// Well the output method is echo.
1822
	echo '
1823
			<span class="event">' . $txt['events'] . '</span> ';
1824
	foreach ($return as $mday => $array)
1825
		foreach ($array as $event)
1826
		{
1827
			if ($event['can_edit'])
1828
				echo '
1829
				<a href="' . $event['modify_href'] . '" style="color: #ff0000;">*</a> ';
1830
1831
			echo '
1832
				' . $event['link'] . (!$event['is_last'] ? ', ' : '');
1833
		}
1834
}
1835
1836
/**
1837
 * Check the passed id_member/password.
1838
 *  If $is_username is true, treats $id as a username.
1839
 *
1840
 * @param int|null $id
1841
 * @param string|null $password
1842
 * @param bool $is_username
1843
 */
1844
function ssi_checkPassword($id = null, $password = null, $is_username = false)
1845
{
1846
	// If $id is null, this was most likely called from a query string and should do nothing.
1847
	if ($id === null)
1848
		return;
1849
1850
	require_once(SUBSDIR . '/Auth.subs.php');
1851
1852
	$member = loadExistingMember($id, !$is_username);
1853
1854
	return validateLoginPassword($password, $member['passwd'], $member['member_name']) && $member['is_activated'] == 1;
1855
}
1856
1857
/**
1858
 * We want to show the recent attachments outside of the forum.
1859
 *
1860
 * @param int $num_attachments = 10
1861
 * @param string[] $attachment_ext = array()
1862
 * @param string $output_method = 'echo'
1863
 */
1864
function ssi_recentAttachments($num_attachments = 10, $attachment_ext = array(), $output_method = 'echo')
1865
{
1866
	global $modSettings, $scripturl, $txt, $settings;
1867
1868
	// We want to make sure that we only get attachments for boards that we can see *if* any.
1869
	$attachments_boards = boardsAllowedTo('view_attachments');
1870
1871
	// No boards?  Adios amigo.
1872
	if (empty($attachments_boards))
1873
		return array();
1874
1875
	$db = database();
1876
1877
	// Is it an array?
1878
	if (!is_array($attachment_ext))
1879
		$attachment_ext = array($attachment_ext);
1880
1881
	// Lets build the query.
1882
	$request = $db->query('', '
1883
		SELECT
1884
			att.id_attach, att.id_msg, att.filename, IFNULL(att.size, 0) AS filesize, att.downloads, mem.id_member,
1885
			IFNULL(mem.real_name, m.poster_name) AS poster_name, m.id_topic, m.subject, t.id_board, m.poster_time,
1886
			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') . '
1887
		FROM {db_prefix}attachments AS att
1888
			INNER JOIN {db_prefix}messages AS m ON (m.id_msg = att.id_msg)
1889
			INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic)
1890
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)' . (empty($modSettings['attachmentShowImages']) || empty($modSettings['attachmentThumbnails']) ? '' : '
1891
			LEFT JOIN {db_prefix}attachments AS thumb ON (thumb.id_attach = att.id_thumb)') . '
1892
		WHERE att.attachment_type = 0' . ($attachments_boards === array(0) ? '' : '
1893
			AND m.id_board IN ({array_int:boards_can_see})') . (!empty($attachment_ext) ? '
1894
			AND att.fileext IN ({array_string:attachment_ext})' : '') .
1895
			(!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : '
1896
			AND t.approved = {int:is_approved}
1897
			AND m.approved = {int:is_approved}
1898
			AND att.approved = {int:is_approved}') . '
1899
		ORDER BY att.id_attach DESC
1900
		LIMIT {int:num_attachments}',
1901
		array(
1902
			'boards_can_see' => $attachments_boards,
1903
			'attachment_ext' => $attachment_ext,
1904
			'num_attachments' => $num_attachments,
1905
			'is_approved' => 1,
1906
		)
1907
	);
1908
1909
	// We have something.
1910
	$attachments = array();
1911
	while ($row = $db->fetch_assoc($request))
1912
	{
1913
		$filename = preg_replace('~&amp;#(\\d{1,7}|x[0-9a-fA-F]{1,6});~', '&#\\1;', htmlspecialchars($row['filename'], ENT_COMPAT, 'UTF-8'));
1914
1915
		// Is it an image?
1916
		$attachments[$row['id_attach']] = array(
1917
			'member' => array(
1918
				'id' => $row['id_member'],
1919
				'name' => $row['poster_name'],
1920
				'link' => empty($row['id_member']) ? $row['poster_name'] : '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['poster_name'] . '</a>',
1921
			),
1922
			'file' => array(
1923
				'filename' => $filename,
1924
				'filesize' => round($row['filesize'] / 1024, 2) . $txt['kilobyte'],
1925
				'downloads' => $row['downloads'],
1926
				'href' => $scripturl . '?action=dlattach;topic=' . $row['id_topic'] . '.0;attach=' . $row['id_attach'],
1927
				'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>',
1928
				'is_image' => !empty($row['width']) && !empty($row['height']) && !empty($modSettings['attachmentShowImages']),
1929
			),
1930
			'topic' => array(
1931
				'id' => $row['id_topic'],
1932
				'subject' => $row['subject'],
1933
				'href' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'],
1934
				'link' => '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'] . '">' . $row['subject'] . '</a>',
1935
				'time' => standardTime($row['poster_time']),
1936
				'html_time' => htmlTime($row['poster_time']),
1937
				'timestamp' => forum_time(true, $row['poster_time']),
1938
			),
1939
		);
1940
1941
		// Images.
1942
		if ($attachments[$row['id_attach']]['file']['is_image'])
1943
		{
1944
			$id_thumb = empty($row['id_thumb']) ? $row['id_attach'] : $row['id_thumb'];
1945
			$attachments[$row['id_attach']]['file']['image'] = array(
1946
				'id' => $id_thumb,
1947
				'width' => $row['width'],
1948
				'height' => $row['height'],
1949
				'img' => '<img src="' . $scripturl . '?action=dlattach;topic=' . $row['id_topic'] . '.0;attach=' . $row['id_attach'] . ';image" alt="' . $filename . '" />',
1950
				'thumb' => '<img src="' . $scripturl . '?action=dlattach;topic=' . $row['id_topic'] . '.0;attach=' . $id_thumb . ';image" alt="' . $filename . '" />',
1951
				'href' => $scripturl . '?action=dlattach;topic=' . $row['id_topic'] . '.0;attach=' . $id_thumb . ';image',
1952
				'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>',
1953
			);
1954
		}
1955
	}
1956
	$db->free_result($request);
1957
1958
	// So you just want an array?  Here you can have it.
1959
	if ($output_method == 'array' || empty($attachments))
1960
		return $attachments;
1961
1962
	// Give them the default.
1963
	echo '
1964
		<table class="ssi_downloads" cellpadding="2">
1965
			<tr>
1966
				<th align="left">', $txt['file'], '</th>
1967
				<th align="left">', $txt['posted_by'], '</th>
1968
				<th align="left">', $txt['downloads'], '</th>
1969
				<th align="left">', $txt['filesize'], '</th>
1970
			</tr>';
1971
1972
	foreach ($attachments as $attach)
1973
		echo '
1974
			<tr>
1975
				<td>', $attach['file']['link'], '</td>
1976
				<td>', $attach['member']['link'], '</td>
1977
				<td align="center">', $attach['file']['downloads'], '</td>
1978
				<td>', $attach['file']['filesize'], '</td>
1979
			</tr>';
1980
	echo '
1981
		</table>';
1982
}
1983