Completed
Push — release-2.1 ( 286127...e6c696 )
by Mathias
11s
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 2016 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);
31
$time_start = microtime();
32
33
// Just being safe...
34 View Code Duplication
foreach (array('db_character_set', 'cachedir') as $variable)
1 ignored issue
show
This code seems to be duplicated across your project.

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

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

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