Passed
Branch development (176841)
by Elk
01:20
created
1
<?php
2
3
/**
4
 * Provides ways to add information to your website by linking to and capturing output
5
 * from ElkArte
6
 *
7
 * @package   ElkArte Forum
8
 * @copyright ElkArte Forum contributors
9
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file)
10
 *
11
 * This file contains code covered by:
12
 * copyright:    2011 Simple Machines (http://www.simplemachines.org)
13
 *
14
 * @version 2.0 dev
15
 *
16
 */
17
18
/**
19
 * Set this to one of three values depending on what you want to happen in the case of a fatal error.
20
 *  - false: Default, will just load the error sub template and die - not putting any theme layers around it.
21
 *  - true: Will load the error sub template AND put the template layers around it (Not useful if on total custom pages).
22
 *  - 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.
23
 */
24
$ssi_on_error_method = false;
25
26
/**
27
 * Don't do john didley if the forum's been shut down completely.
28
 */
29
// $ssi_maintenance_off = false;
30
31
/**
32
 * Define a theme for SSI (integer)
33
 */
34
// $ssi_theme = 0;
35
36
/**
37
 * An array of layers to use.
38
 */
39
// $ssi_layers = array();
40
41
/**
42
 * Gzip output? (because it must be boolean and true, this can't be hacked.)
43
 */
44
// $ssi_gzip = false;
45
46
/**
47
 * Should we ban from SSI as well?
48
 */
49
// $ssi_ban = false;
50
51
/**
52
 * Do we allow guests in here?
53
 */
54
// $ssi_guest_access = false;
55
56
// We are in Elk, but from the side-entrance.
57
if (!defined('ELKBOOT'))
58
{
59
	define('ELK', 'SSI');
60
61
	require_once(dirname(__FILE__) . '/bootstrap.php');
62
	$bootstrap = new Bootstrap(true);
63
}
64
65
// The globals that were created during the bootstrap process
66
global $time_start, $maintenance, $msubject, $mmessage, $mbname, $language;
67
global $boardurl, $webmaster_email, $cookiename;
68
global $db_type, $db_server, $db_name, $db_user, $db_prefix, $db_persist, $db_error_send;
69
global $modSettings, $context, $user_info, $topic, $board, $txt;
70
global $ssi_db_user, $scripturl, $ssi_db_passwd, $db_passwd;
71
global $boarddir, $sourcedir, $db_show_debug, $ssi_error_reporting;
72
73
// Have the ability to easily add functions to SSI.
74
call_integration_hook('integrate_SSI');
75
76
// Call a function passed by GET.
77
if (isset($_GET['ssi_function']) && function_exists('ssi_' . $_GET['ssi_function']) && (!empty($modSettings['allow_guestAccess']) || !$user_info['is_guest']))
78
{
79
	call_user_func('ssi_' . $_GET['ssi_function']);
80
	exit;
81
}
82
83
if (isset($_GET['ssi_function']))
84
{
85
	exit;
86
}
87
// You shouldn't just access SSI.php directly by URL!!
88
elseif (basename($_SERVER['PHP_SELF']) === 'SSI.php')
89
{
90
	die(sprintf($txt['ssi_not_direct'], $user_info['is_admin'] ? '\'' . addslashes(__FILE__) . '\'' : '\'SSI.php\''));
91
}
92
93
error_reporting($ssi_error_reporting);
94
95
return true;
96
97
/**
98
 * This shuts down the SSI and shows the footer.
99
 */
100
function ssi_shutdown()
101
{
102
	if (!isset($_GET['ssi_function']) || $_GET['ssi_function'] !== 'shutdown')
103
	{
104
		template_footer();
105
	}
106
}
107
108
/**
109
 * Display a welcome message, like:
110
 * "Hey, User, you have 0 messages, 0 are new."
111
 *
112
 * @param string $output_method The output method. If 'echo', will display
113
 * everything. Otherwise returns an array of user info.
114
 */
115
function ssi_welcome($output_method = 'echo')
116
{
117
	global $context, $txt, $scripturl;
118
119
	if ($output_method === 'echo')
120
	{
121
		if ($context['user']['is_guest'])
122
		{
123
			echo replaceBasicActionUrl($txt[$context['can_register'] ? 'welcome_guest_register' : 'welcome_guest']);
124
		}
125
		else
126
		{
127
			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'])))) : '';
128
		}
129
	}
130
	// Don't echo... then do what?!
131
	else
132
	{
133
		return $context['user'];
134
	}
135
}
136
137
/**
138
 * Display a menu bar, like is displayed at the top of the forum.
139
 *
140
 * @param string $output_method The output method. If 'echo', will display
141
 * everything. Otherwise returns an array of menu data.
142
 */
143
function ssi_menubar($output_method = 'echo')
144
{
145
	global $context;
146
147
	if ($output_method === 'echo')
148
	{
149
		template_menu();
150
	}
151
	// What else could this do?
152
	else
153
	{
154
		return $context['menu_buttons'];
155
	}
156
}
157
158
/**
159
 * Show a logout link.
160
 *
161
 * @param string $redirect_to A URL to redirect the user to after they log out.
162
 * @param string $output_method = 'echo; The output method. If 'echo', shows a logout link,
163
 * otherwise returns HTML.
164
 */
165
function ssi_logout($redirect_to = '', $output_method = 'echo')
166
{
167
	global $context, $txt, $scripturl;
168
169
	if ($redirect_to !== '')
170
	{
171
		$_SESSION['logout_url'] = $redirect_to;
172
	}
173
174
	// Guests can't log out.
175
	if ($context['user']['is_guest'])
176
	{
177
		return false;
178
	}
179
180
	$link = '<a class="linkbutton" href="' . $scripturl . '?action=logout;' . $context['session_var'] . '=' . $context['session_id'] . '">' . $txt['logout'] . '</a>';
181
182
	if ($output_method === 'echo')
183
	{
184
		echo $link;
185
	}
186
	else
187
	{
188
		return $link;
189
	}
190
}
191
192
/**
193
 * Recent post list:
194
 *  [board] Subject by Poster Date
195
 *
196
 * @todo this may use getLastPosts with some modification
197
 *
198
 * @param int $num_recent How many recent posts to display
199
 * @param int[]|null $exclude_boards If set, doesn't show posts from the specified boards
200
 * @param int[]|null $include_boards If set, only includes posts from the specified boards
201
 * @param string $output_method The output method. If 'echo', displays the posts, otherwise
202
 * returns an array of information about them.
203
 * @param bool $limit_body Whether or not to only show the first 384 characters of each post
204
 */
205
function ssi_recentPosts($num_recent = 8, $exclude_boards = null, $include_boards = null, $output_method = 'echo', $limit_body = true)
206
{
207
	global $modSettings;
208
209
	// Excluding certain boards...
210
	if ($exclude_boards === null && !empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0)
211
	{
212
		$exclude_boards = array($modSettings['recycle_board']);
213
	}
214
	else
215
	{
216
		$exclude_boards = empty($exclude_boards) ? array() : (is_array($exclude_boards) ? $exclude_boards : array($exclude_boards));
0 ignored issues
show
The condition is_array($exclude_boards) is always true.
Loading history...
217
	}
218
219
	// What about including certain boards - note we do some protection here as pre-2.0 didn't have this parameter.
220
	if (is_array($include_boards) || (int) $include_boards === $include_boards)
221
	{
222
		$include_boards = is_array($include_boards) ? $include_boards : array($include_boards);
0 ignored issues
show
The condition is_array($include_boards) is always true.
Loading history...
223
	}
224
	elseif ($include_boards !== null)
0 ignored issues
show
The condition $include_boards !== null is always false.
Loading history...
225
	{
226
		$include_boards = array();
227
	}
228
229
	// Let's restrict the query boys (and girls)
230
	$query_where = '
231
		m.id_msg >= {int:min_message_id}
232
		' . (empty($exclude_boards) ? '' : '
233
		AND b.id_board NOT IN ({array_int:exclude_boards})') . '
234
		' . ($include_boards === null ? '' : '
235
		AND b.id_board IN ({array_int:include_boards})') . '
236
		AND {query_wanna_see_board}' . ($modSettings['postmod_active'] ? '
237
		AND m.approved = {int:is_approved}' : '');
238
239
	$query_where_params = array(
240
		'is_approved' => 1,
241
		'include_boards' => $include_boards === null ? '' : $include_boards,
242
		'exclude_boards' => empty($exclude_boards) ? '' : $exclude_boards,
243
		'min_message_id' => $modSettings['maxMsgID'] - 25 * min($num_recent, 5),
244
	);
245
246
	// Past to this simpleton of a function...
247
	return ssi_queryPosts($query_where, $query_where_params, $num_recent, 'm.id_msg DESC', $output_method, $limit_body);
248
}
249
250
/**
251
 * Fetch a post with a particular ID.
252
 *
253
 * - By default will only show if you have permission to the see the board
254
 * in question - this can be overridden.
255
 *
256
 * @todo this may use getRecentPosts with some modification
257
 *
258
 * @param int[] $post_ids An array containing the IDs of the posts to show
259
 * @param bool $override_permissions Whether to ignore permissions. If true, will show posts even
260
 * if the user doesn't have permission to see them.
261
 * @param string $output_method = 'echo; The output method. If 'echo', displays the posts,
262
 * otherwise returns an array of info about them
263
 */
264
function ssi_fetchPosts($post_ids = array(), $override_permissions = false, $output_method = 'echo')
265
{
266
	global $modSettings;
267
268
	if (empty($post_ids))
269
	{
270
		return '';
271
	}
272
273
	// Allow the user to request more than one - why not?
274
	$post_ids = is_array($post_ids) ? $post_ids : array($post_ids);
0 ignored issues
show
The condition is_array($post_ids) is always true.
Loading history...
275
276
	// Restrict the posts required...
277
	$query_where = '
278
		m.id_msg IN ({array_int:message_list})' . ($override_permissions ? '' : '
279
			AND {query_wanna_see_board}') . ($modSettings['postmod_active'] ? '
280
			AND m.approved = {int:is_approved}' : '');
281
	$query_where_params = array(
282
		'message_list' => $post_ids,
283
		'is_approved' => 1,
284
	);
285
286
	// Then make the query and dump the data.
287
	return ssi_queryPosts($query_where, $query_where_params, '', 'm.id_msg DESC', $output_method, false, $override_permissions);
0 ignored issues
show
'' of type string is incompatible with the type integer expected by parameter $query_limit of ssi_queryPosts(). ( Ignorable by Annotation )

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

287
	return ssi_queryPosts($query_where, $query_where_params, /** @scrutinizer ignore-type */ '', 'm.id_msg DESC', $output_method, false, $override_permissions);
Loading history...
288
}
289
290
/**
291
 * This handles actually pulling post info
292
 *
293
 * - removes code duplication in other queries
294
 * - don't call it direct unless you really know what you're up to.
295
 *
296
 * @todo if ssi_recentPosts and ssi_fetchPosts will use Recent.subs.php this can be removed
297
 *
298
 * @param string $query_where The WHERE clause for the query
299
 * @param mixed[] $query_where_params An array of parameters for the WHERE clause
300
 * @param int $query_limit The maximum number of rows to return
301
 * @param string $query_order The ORDER BY clause for the query
302
 * @param string $output_method = 'echo; The output method. If 'echo', displays the posts,
303
 * otherwise returns an array of info about them.
304
 * @param bool $limit_body If true, will only show the first 384 characters of the post
305
 * rather than all of it
306
 * @param bool $override_permissions Whether or not to ignore permissions. If true, will
307
 * show all posts regardless of whether the user can actually see them
308
 */
309
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)
310
{
311
	global $scripturl, $txt, $user_info, $modSettings;
312
313
	$db = database();
314
315
	// Find all the posts. Newer ones will have higher IDs.
316
	$request = $db->query('substring', '
317
		SELECT
318
			m.poster_time, m.subject, m.id_topic, m.id_member, m.id_msg, m.id_board, b.name AS board_name,
319
			COALESCE(mem.real_name, m.poster_name) AS poster_name, ' . ($user_info['is_guest'] ? '1 AS is_read, 0 AS new_from' : '
320
			COALESCE(lt.id_msg, lmr.id_msg, 0) >= m.id_msg_modified AS is_read,
321
			COALESCE(lt.id_msg, lmr.id_msg, -1) + 1 AS new_from') . ', ' . ($limit_body ? 'SUBSTRING(m.body, 1, 384) AS body' : 'm.body') . ', m.smileys_enabled
322
		FROM {db_prefix}messages AS m
323
			JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)
324
			JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic)
325
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)' . (!$user_info['is_guest'] ? '
326
			LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = m.id_topic AND lt.id_member = {int:current_member})
327
			LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = m.id_board AND lmr.id_member = {int:current_member})' : '') . '
328
		WHERE 1=1 ' . ($override_permissions ? '' : '
329
			AND {query_wanna_see_board}') . ($modSettings['postmod_active'] ? '
330
			AND m.approved = {int:is_approved}
331
			AND t.approved = {int:is_approved}' : '') . '
332
		' . (empty($query_where) ? '' : 'AND ' . $query_where) . '
333
		ORDER BY ' . $query_order . '
334
		' . (empty($query_limit) ? '' : 'LIMIT {int:query_limit}'),
335
		array_merge($query_where_params, array(
336
			'current_member' => $user_info['id'],
337
			'is_approved' => 1,
338
			'query_limit' => $query_limit,
339
		))
340
	);
341
342
	$bbc_parser = \BBC\ParserWrapper::instance();
343
344
	$posts = array();
345
	while ($row = $db->fetch_assoc($request))
0 ignored issues
show
The method fetch_assoc() does not exist on ElkArte\Database\QueryInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to ElkArte\Database\QueryInterface. ( Ignorable by Annotation )

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

345
	while ($row = $db->/** @scrutinizer ignore-call */ fetch_assoc($request))
Loading history...
346
	{
347
		$row['body'] = $bbc_parser->parseMessage($row['body'], $row['smileys_enabled']);
348
349
		// Censor it!
350
		$row['subject'] = censor($row['subject']);
351
		$row['body'] = censor($row['body']);
352
353
		$preview = strip_tags(strtr($row['body'], array('<br />' => '&#10;')));
354
355
		// Build the array.
356
		$posts[] = array(
357
			'id' => $row['id_msg'],
358
			'board' => array(
359
				'id' => $row['id_board'],
360
				'name' => $row['board_name'],
361
				'href' => $scripturl . '?board=' . $row['id_board'] . '.0',
362
				'link' => '<a href="' . $scripturl . '?board=' . $row['id_board'] . '.0">' . $row['board_name'] . '</a>'
363
			),
364
			'topic' => $row['id_topic'],
365
			'poster' => array(
366
				'id' => $row['id_member'],
367
				'name' => $row['poster_name'],
368
				'href' => empty($row['id_member']) ? '' : $scripturl . '?action=profile;u=' . $row['id_member'],
369
				'link' => empty($row['id_member']) ? $row['poster_name'] : '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['poster_name'] . '</a>'
370
			),
371
			'subject' => $row['subject'],
372
			'short_subject' => \ElkArte\Util::shorten_text($row['subject'], !empty($modSettings['ssi_subject_length']) ? $modSettings['ssi_subject_length'] : 24),
373
			'preview' => \ElkArte\Util::shorten_text($preview, !empty($modSettings['ssi_preview_length']) ? $modSettings['ssi_preview_length'] : 128),
374
			'body' => $row['body'],
375
			'time' => standardTime($row['poster_time']),
376
			'html_time' => htmlTime($row['poster_time']),
377
			'timestamp' => forum_time(true, $row['poster_time']),
378
			'href' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . ';topicseen#new',
379
			'link' => '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'] . '" rel="nofollow">' . $row['subject'] . '</a>',
380
			'new' => !empty($row['is_read']),
381
			'is_new' => empty($row['is_read']),
382
			'new_from' => $row['new_from'],
383
		);
384
	}
385
	$db->free_result($request);
0 ignored issues
show
The method free_result() does not exist on ElkArte\Database\QueryInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to ElkArte\Database\QueryInterface. ( Ignorable by Annotation )

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

385
	$db->/** @scrutinizer ignore-call */ 
386
      free_result($request);
Loading history...
386
387
	// Just return it.
388
	if ($output_method !== 'echo' || empty($posts))
389
	{
390
		return $posts;
391
	}
392
393
	echo '
394
		<table class="ssi_table">';
395
396
	foreach ($posts as $post)
397
	{
398
		echo '
399
			<tr>
400
				<td class="righttext">
401
					[', $post['board']['link'], ']
402
				</td>
403
				<td class="top">
404
					<a href="', $post['href'], '">', $post['subject'], '</a>
405
					', $txt['by'], ' ', $post['poster']['link'], '
406
					', $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>' : '', '
407
				</td>
408
				<td class="righttext">
409
					', $post['time'], '
410
				</td>
411
			</tr>';
412
	}
413
414
	echo '
415
		</table>';
416
}
417
418
/**
419
 * Generates a recent topic list
420
 *
421
 * - [board] Subject by Poster Date
422
 *
423
 * @param int $num_recent How many recent topics to show
424
 * @param int[]|null $exclude_boards If set, exclude topics from the specified board(s)
425
 * @param bool|null $include_boards If set, only include topics from the specified board(s)
426
 * @param string $output_method = 'echo' The output method. If 'echo', displays a list of topics,
427
 * otherwise returns an array of info about them
428
 */
429
function ssi_recentTopics($num_recent = 8, $exclude_boards = null, $include_boards = null, $output_method = 'echo')
430
{
431
	global $settings, $scripturl, $txt, $user_info, $modSettings;
432
433
	$db = database();
434
435
	if ($exclude_boards === null && !empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0)
436
	{
437
		$exclude_boards = array($modSettings['recycle_board']);
438
	}
439
	else
440
	{
441
		$exclude_boards = empty($exclude_boards) ? array() : (is_array($exclude_boards) ? $exclude_boards : array($exclude_boards));
0 ignored issues
show
The condition is_array($exclude_boards) is always true.
Loading history...
442
	}
443
444
	// Only some boards?.
445
	if (is_array($include_boards) || (int) $include_boards === $include_boards)
0 ignored issues
show
The condition (int)$include_boards === $include_boards is always false.
Loading history...
446
	{
447
		$include_boards = is_array($include_boards) ? $include_boards : array($include_boards);
448
	}
449
	elseif ($include_boards !== null)
450
	{
451
		$output_method = $include_boards;
452
		$include_boards = array();
453
	}
454
455
	$icon_sources = new MessageTopicIcons(!empty($modSettings['messageIconChecks_enable']), $settings['theme_dir']);
0 ignored issues
show
The type MessageTopicIcons was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
456
457
	// Find all the posts in distinct topics. Newer ones will have higher IDs.
458
	$request = $db->query('', '
459
		SELECT
460
			t.id_topic, b.id_board, b.name AS board_name
461
		FROM {db_prefix}topics AS t
462
			INNER JOIN {db_prefix}messages AS ml ON (ml.id_msg = t.id_last_msg)
463
			LEFT JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
464
		WHERE t.id_last_msg >= {int:min_message_id}' . (empty($exclude_boards) ? '' : '
465
			AND b.id_board NOT IN ({array_int:exclude_boards})') . '' . (empty($include_boards) ? '' : '
466
			AND b.id_board IN ({array_int:include_boards})') . '
467
			AND {query_wanna_see_board}' . ($modSettings['postmod_active'] ? '
468
			AND t.approved = {int:is_approved}
469
			AND ml.approved = {int:is_approved}' : '') . '
470
		ORDER BY t.id_last_msg DESC
471
		LIMIT {int:num_recent}',
472
		array(
473
			'include_boards' => empty($include_boards) ? '' : $include_boards,
474
			'exclude_boards' => empty($exclude_boards) ? '' : $exclude_boards,
475
			'min_message_id' => $modSettings['maxMsgID'] - 35 * min($num_recent, 5),
476
			'is_approved' => 1,
477
			'num_recent' => $num_recent,
478
		)
479
	);
480
	$topics = array();
481
	while ($row = $db->fetch_assoc($request))
482
	{
483
		$topics[$row['id_topic']] = $row;
484
	}
485
	$db->free_result($request);
486
487
	// Did we find anything? If not, bail.
488
	if (empty($topics))
489
	{
490
		return array();
491
	}
492
493
	$topic_list = array_keys($topics);
494
495
	// Count number of new posts per topic.
496
	if (!$user_info['is_guest'])
497
	{
498
		$request = $db->query('', '
499
			SELECT
500
				m.id_topic, COALESCE(lt.id_msg, lmr.id_msg, -2) + 1 AS new_from
501
			FROM {db_prefix}messages AS m
502
				LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = m.id_topic AND lt.id_member = {int:current_member})
503
				LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = m.id_board AND lmr.id_member = {int:current_member})
504
			WHERE
505
				m.id_topic IN ({array_int:topic_list})
506
				AND (m.id_msg > COALESCE(lt.id_msg, lmr.id_msg, 0))
507
			GROUP BY m.id_topic',
508
			array(
509
				'current_member' => $user_info['id'],
510
				'topic_list' => $topic_list
511
			)
512
		);
513
		while ($row = $db->fetch_assoc($request))
514
		{
515
			$topics[$row['id_topic']] += $row;
516
		}
517
		$db->free_result($request);
518
	}
519
520
	// Find all the posts in distinct topics. Newer ones will have higher IDs.
521
	$request = $db->query('substring', '
522
		SELECT
523
			ml.poster_time, ml.id_member, ml.id_msg, ml.smileys_enabled, ml.icon,
524
			mf.subject, mf.id_member AS id_op_member, 
525
			t.id_topic, t.num_replies, t.num_views, t.id_last_msg, t.id_first_msg,
526
			mg.online_color,
527
			COALESCE(mem.real_name, ml.poster_name) AS poster_name,
528
			COALESCE(memop.real_name, mf.poster_name) AS op_name,
529
			SUBSTRING(ml.body, 1, 384) AS body
530
		FROM {db_prefix}topics AS t
531
			INNER JOIN {db_prefix}messages AS ml ON (ml.id_msg = t.id_last_msg)
532
			INNER JOIN {db_prefix}messages AS mf ON (mf.id_msg = t.id_first_msg)
533
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = ml.id_member)
534
			LEFT JOIN {db_prefix}members AS memop ON (memop.id_member = mf.id_member)
535
			LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = mem.id_group)
536
		WHERE t.id_topic IN ({array_int:topic_list})',
537
		array(
538
			'topic_list' => $topic_list
539
		)
540
	);
541
	$bbc_parser = \BBC\ParserWrapper::instance();
542
	$posts = array();
543
	while ($row = $db->fetch_assoc($request))
544
	{
545
		$row['body'] = strip_tags(strtr($bbc_parser->parseMessage($row['body'], $row['smileys_enabled']), array('<br />' => '&#10;')));
546
547
		// Censor the subject and body.
548
		$row['subject'] = censor($row['subject']);
549
		$row['body'] = censor($row['body']);
550
551
		$row['body'] = \ElkArte\Util::shorten_text($row['body'], 128);
552
553
		// Build the array.
554
		$posts[$row['id_last_msg']] = array(
555
			'board' => array(
556
				'id' => $topics[$row['id_topic']]['id_board'],
557
				'name' => $topics[$row['id_topic']]['board_name'],
558
				'href' => $scripturl . '?board=' . $topics[$row['id_topic']]['id_board'] . '.0',
559
				'link' => '<a href="' . $scripturl . '?board=' . $topics[$row['id_topic']]['id_board'] . '.0">' . $topics[$row['id_topic']]['board_name'] . '</a>',
560
			),
561
			'topic' => $row['id_topic'],
562
			'poster' => array(
563
				'id' => $row['id_member'],
564
				'name' => $row['poster_name'],
565
				'href' => empty($row['id_member']) ? '' : $scripturl . '?action=profile;u=' . $row['id_member'],
566
				'link' => empty($row['id_member']) ? $row['poster_name'] : '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['poster_name'] . '</a>'
567
			),
568
			'original_poster' => array(
569
				'id' => $row['id_op_member'],
570
				'name' => $row['op_name'],
571
				'href' => empty($row['id_op_member']) ? '' : $scripturl . '?action=profile;u=' . $row['id_op_member'],
572
				'link' => empty($row['id_op_member']) ? $row['op_name'] : '<a href="' . $scripturl . '?action=profile;u=' . $row['id_op_member'] . '">' . $row['op_name'] . '</a>'
573
			),
574
			'subject' => $row['subject'],
575
			'replies' => $row['num_replies'],
576
			'views' => $row['num_views'],
577
			'short_subject' => \ElkArte\Util::shorten_text($row['subject'], 25),
578
			'preview' => $row['body'],
579
			'time' => standardTime($row['poster_time']),
580
			'html_time' => htmlTime($row['poster_time']),
581
			'timestamp' => forum_time(true, $row['poster_time']),
582
			'href' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . ';topicseen#new',
583
			'link' => '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#new" rel="nofollow">' . $row['subject'] . '</a>',
584
			'is_new' => !empty($topics[$row['id_topic']]['new_from']),
585
			'new_from' => empty($topics[$row['id_topic']]['new_from']) ? 0 : $topics[$row['id_topic']]['new_from'],
586
			'icon' => '<img src="' . $icon_sources->{$row['icon']} . '" class="centericon" alt="' . $row['icon'] . '" />',
587
		);
588
	}
589
	$db->free_result($request);
590
	krsort($posts);
591
592
	// Just return it.
593
	if ($output_method !== 'echo' || empty($posts))
594
	{
595
		return $posts;
596
	}
597
598
	echo '
599
		<table class="ssi_table">';
600
601
	foreach ($posts as $post)
602
	{
603
		echo '
604
			<tr>
605
				<td class="righttext top">
606
					[', $post['board']['link'], ']
607
				</td>
608
				<td class="top">
609
					<a href="', $post['href'], '">', $post['subject'], '</a>
610
					', $txt['by'], ' ', $post['poster']['link'], '
611
					', !$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>', '
612
				</td>
613
				<td class="righttext">
614
					', $post['time'], '
615
				</td>
616
			</tr>';
617
	}
618
619
	echo '
620
		</table>';
621
}
622
623
/**
624
 * Show the top poster's name and profile link.
625
 *
626
 * @param int $topNumber How many top posters to list
627
 * @param string $output_method = 'echo' The output method. If 'echo', will display a list of
628
 * users, otherwise returns an array of info about them.
629
 */
630
function ssi_topPoster($topNumber = 1, $output_method = 'echo')
631
{
632
	require_once(SUBSDIR . '/Stats.subs.php');
633
	$top_posters = topPosters($topNumber);
634
635
	// Just return all the top posters.
636
	if ($output_method !== 'echo')
637
	{
638
		return $top_posters;
639
	}
640
641
	// Make a quick array to list the links in.
642
	$temp_array = array();
643
	foreach ($top_posters as $member)
644
	{
645
		$temp_array[] = $member['link'];
646
	}
647
648
	echo implode(', ', $temp_array);
649
}
650
651
/**
652
 * Show boards by activity.
653
 *
654
 * @param int $num_top How many boards to display
655
 * @param string $output_method = 'echo' The output method. If 'echo', displays a list of
656
 * boards, otherwise returns an array of info about them.
657
 */
658
function ssi_topBoards($num_top = 10, $output_method = 'echo')
659
{
660
	global $txt;
661
662
	require_once(SUBSDIR . '/Stats.subs.php');
663
664
	// Find boards with lots of posts.
665
	$boards = topBoards($num_top, true);
666
667
	foreach ($boards as $id => $board)
668
	{
669
		$boards[$id]['new'] = empty($board['is_read']);
670
	}
671
672
	// If we shouldn't output or have nothing to output, just jump out.
673
	if ($output_method !== 'echo' || empty($boards))
674
	{
675
		return $boards;
676
	}
677
678
	echo '
679
		<table class="ssi_table">
680
			<tr>
681
				<th>', $txt['board'], '</th>
682
				<th class="centertext">', $txt['board_topics'], '</th>
683
				<th class="centertext">', $txt['posts'], '</th>
684
			</tr>';
685
686
	foreach ($boards as $board)
687
	{
688
		echo '
689
			<tr>
690
				<td>', $board['new'] ? ' <a href="' . $board['href'] . '"><span class="new_posts">' . $txt['new'] . '</span></a> ' : '', $board['link'], '</td>
691
				<td class="centertext">', $board['num_topics'], '</td>
692
				<td class="centertext">', $board['num_posts'], '</td>
693
			</tr>';
694
	}
695
696
	echo '
697
		</table>';
698
}
699
700
/**
701
 * Shows the top topics.
702
 *
703
 * @param string $type Can be one of type or replies
704
 * @param int $num_topics = 10, How many topics to display
705
 * @param string $output_method = 'echo' The output method. If 'echo', displays a
706
 * list of topics, otherwise returns an array of info about them.
707
 */
708
function ssi_topTopics($type = 'replies', $num_topics = 10, $output_method = 'echo')
709
{
710
	global $txt, $scripturl;
711
712
	require_once(SUBSDIR . '/Stats.subs.php');
713
714
	if (function_exists('topTopic' . ucfirst($type)))
715
	{
716
		$function = 'topTopic' . ucfirst($type);
717
	}
718
	else
719
	{
720
		$function = 'topTopicReplies';
721
	}
722
723
	$topics = $function($num_topics);
724
725
	foreach ($topics as $topic_id => $row)
726
	{
727
		$row['subject'] = censor($row['subject']);
728
729
		$topics[$topic_id]['href'] = $scripturl . '?topic=' . $row['id'] . '.0';
730
		$topics[$topic_id]['link'] = '<a href="' . $scripturl . '?topic=' . $row['id'] . '.0">' . $row['subject'] . '</a>';
731
	}
732
733
	if ($output_method !== 'echo' || empty($topics))
734
	{
735
		return $topics;
736
	}
737
738
	echo '
739
		<table class="top_topic ssi_table">
740
			<tr>
741
				<th class="link"></th>
742
				<th class="centertext views">', $txt['views'], '</th>
743
				<th class="centertext num_replies">', $txt['replies'], '</th>
744
			</tr>';
745
746
	foreach ($topics as $topic)
747
	{
748
		echo '
749
			<tr>
750
				<td class="link">
751
					', $topic['link'], '
752
				</td>
753
				<td class="centertext views">', $topic['num_views'], '</td>
754
				<td class="centertext num_replies">', $topic['num_replies'], '</td>
755
			</tr>';
756
	}
757
758
	echo '
759
		</table>';
760
}
761
762
/**
763
 * Shows the top topics, by replies.
764
 *
765
 * @param int $num_topics = 10, How many topics to show
766
 * @param string $output_method = 'echo'  The output method. If 'echo', displays a list of topics,
767
 * otherwise returns an array of info about them
768
 */
769
function ssi_topTopicsReplies($num_topics = 10, $output_method = 'echo')
770
{
771
	return ssi_topTopics('replies', $num_topics, $output_method);
772
}
773
774
/**
775
 * Shows the top topics, by views.
776
 *
777
 * @param int $num_topics = 10, How many topics to show
778
 * @param string $output_method = 'echo' The output method. If 'echo', displays a list of topics,
779
 * otherwise returns an array of info about them
780
 */
781
function ssi_topTopicsViews($num_topics = 10, $output_method = 'echo')
782
{
783
	return ssi_topTopics('views', $num_topics, $output_method);
784
}
785
786
/**
787
 * Show a link to the latest member:
788
 *
789
 * - Please welcome, Someone, our latest member.
790
 *
791
 * @param string $output_method = 'echo' The output method. If 'echo', returns a string
792
 * with a link to the latest member's profile, otherwise returns an array of info about them.
793
 */
794
function ssi_latestMember($output_method = 'echo')
795
{
796
	global $txt, $context;
797
798
	if ($output_method === 'echo')
799
		echo '
800
		', sprintf($txt['welcome_newest_member'], $context['common_stats']['latest_member']['link']), '<br />';
801
	else
802
	{
803
		return $context['common_stats']['latest_member'];
804
	}
805
}
806
807
/**
808
 * Fetch a random member
809
 *
810
 * @param string $random_type = '', if type set to 'day' will only change once a day!
811
 * @param string $output_method = 'echo' The output method. If 'echo', displays a link to the member's
812
 * profile, otherwise returns an array of info about them.
813
 */
814
function ssi_randomMember($random_type = '', $output_method = 'echo')
815
{
816
	global $modSettings;
817
818
	// If we're looking for something to stay the same each day then seed the generator.
819
	if ($random_type === 'day')
820
	{
821
		// Set the seed to change only once per day.
822
		mt_srand(floor(time() / 86400));
0 ignored issues
show
floor(time() / 86400) of type double is incompatible with the type integer expected by parameter $seed of mt_srand(). ( Ignorable by Annotation )

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

822
		mt_srand(/** @scrutinizer ignore-type */ floor(time() / 86400));
Loading history...
823
	}
824
825
	// Get the lowest ID we're interested in.
826
	$member_id = mt_rand(1, $modSettings['latestMember']);
827
828
	$result = ssi_queryMembers('member_greater_equal', $member_id, 1, 'id_member ASC', $output_method);
829
830
	// If we got nothing do the reverse - in case of unactivated members.
831
	if (empty($result))
832
	{
833
		$result = ssi_queryMembers('member_lesser_equal', $member_id, 1, 'id_member DESC', $output_method);
834
	}
835
836
	// Just to be sure put the random generator back to something... random.
837
	if ($random_type !== '')
838
	{
839
		mt_srand(time());
840
	}
841
842
	return $result;
843
}
844
845
/**
846
 * Fetch a specific member.
847
 *
848
 * @param int[] $member_ids = array() The IDs of the members to fetch
849
 * @param string $output_method = 'echo' The output method. If 'echo', displays a
850
 * list of links to the members' profiles, otherwise returns an array of info about them.
851
 */
852
function ssi_fetchMember($member_ids = array(), $output_method = 'echo')
853
{
854
	if (empty($member_ids))
855
	{
856
		return '';
857
	}
858
859
	// Can have more than one member if you really want...
860
	$member_ids = is_array($member_ids) ? $member_ids : array($member_ids);
0 ignored issues
show
The condition is_array($member_ids) is always true.
Loading history...
861
862
	// Then make the query and dump the data.
863
	return ssi_queryMembers('members', $member_ids, '', 'id_member', $output_method);
864
}
865
866
/**
867
 * Fetch all members in the specified group
868
 *
869
 * @param int|null $group_id The ID of the group to get members from
870
 * @param string $output_method = 'echo' The output method. If 'echo', returns a list of
871
 * group members, otherwise returns an array of info about them.
872
 */
873
function ssi_fetchGroupMembers($group_id = null, $output_method = 'echo')
874
{
875
	if ($group_id === null)
876
	{
877
		return false;
878
	}
879
880
	return ssi_queryMembers('group_list', is_array($group_id) ? $group_id : array($group_id), '', 'real_name', $output_method);
0 ignored issues
show
The condition is_array($group_id) is always false.
Loading history...
881
}
882
883
/**
884
 * Fetch some member data!
885
 *
886
 * - Gathers info about members based on the specified parameters.
887
 * - Used by other functions to eliminate duplication.
888
 *
889
 * @param string|null $query_where The info for the WHERE clause of the query
890
 * @param string|string[] $query_where_params The parameters for the WHERE clause
891
 * @param string|int $query_limit The number of rows to return or an empty string to return all
892
 * @param string $query_order The info for the ORDER BY clause of the query
893
 * @param string $output_method The output method. If 'echo', displays a list of members,
894
 * otherwise returns an array of info about them
895
 */
896
function ssi_queryMembers($query_where = null, $query_where_params = array(), $query_limit = '', $query_order = 'id_member DESC', $output_method = 'echo')
897
{
898
	if ($query_where === null)
899
	{
900
		return false;
901
	}
902
903
	require_once(SUBSDIR . '/Members.subs.php');
904
	$members_data = retrieveMemberData(array(
905
		$query_where => $query_where_params,
906
		'limit' => !empty($query_limit) ? (int) $query_limit : 10,
907
		'order_by' => $query_order,
908
		'activated_status' => 1,
909
	));
910
911
	$members = array();
912
	foreach ($members_data['member_info'] as $row)
913
	{
914
		$members[] = $row['id'];
915
	}
916
917
	if (empty($members))
918
	{
919
		return array();
920
	}
921
922
	// Load the members.
923
	\ElkArte\MembersList::load($members);
924
925
	// Draw the table!
926
	if ($output_method === 'echo')
927
	{
928
		echo '
929
		<table class="ssi_table">';
930
	}
931
932
	$query_members = array();
933
	foreach ($members as $id)
934
	{
935
		$member = \ElkArte\MembersList::get($id);
936
		// Load their context data.
937
		if ($member->isEmpty())
938
		{
939
			continue;
940
		}
941
		$member->loadContext();
942
943
		// Store this member's information.
944
		$query_members[$id] = $member;
945
946
		// Only do something if we're echo'ing.
947
		if ($output_method === 'echo')
948
		{
949
			echo '
950
			<tr>
951
				<td class="centertext">
952
					', $query_members[$member]['link'], '
953
					<br />', $query_members[$member]['avatar']['image'], '
954
				</td>
955
			</tr>';
956
		}
957
	}
958
959
	// End the table if appropriate.
960
	if ($output_method === 'echo')
961
	{
962
		echo '
963
		</table>';
964
	}
965
966
	// Send back the data.
967
	return $query_members;
968
}
969
970
/**
971
 * Show some basic stats:
972
 *
973
 * - Total This: XXXX, etc.
974
 *
975
 * @param string $output_method The output method. If 'echo', displays the stats,
976
 * otherwise returns an array of info about them
977
 */
978
function ssi_boardStats($output_method = 'echo')
979
{
980
	global $txt, $scripturl, $modSettings;
981
982
	if (!allowedTo('view_stats'))
983
	{
984
		return false;
985
	}
986
987
	require_once(SUBSDIR . '/Boards.subs.php');
988
	require_once(SUBSDIR . '/Stats.subs.php');
989
990
	$totals = array(
991
		'members' => $modSettings['totalMembers'],
992
		'posts' => $modSettings['totalMessages'],
993
		'topics' => $modSettings['totalTopics'],
994
		'boards' => countBoards(),
995
		'categories' => numCategories(),
996
	);
997
998
	if ($output_method !== 'echo')
999
	{
1000
		return $totals;
1001
	}
1002
1003
	echo '
1004
		', $txt['total_members'], ': <a href="', $scripturl . '?action=memberlist">', comma_format($totals['members']), '</a><br />
1005
		', $txt['total_posts'], ': ', comma_format($totals['posts']), '<br />
1006
		', $txt['total_topics'], ': ', comma_format($totals['topics']), ' <br />
1007
		', $txt['total_cats'], ': ', comma_format($totals['categories']), '<br />
1008
		', $txt['total_boards'], ': ', comma_format($totals['boards']);
1009
}
1010
1011
/**
1012
 * Shows a list of online users:
1013
 *
1014
 * - YY Guests, ZZ Users and then a list...
1015
 *
1016
 * @param string $output_method The output method. If 'echo', displays the stats,
1017
 * otherwise returns an array of info about them
1018
 */
1019
function ssi_whosOnline($output_method = 'echo')
1020
{
1021
	global $user_info, $txt, $settings;
1022
1023
	require_once(SUBSDIR . '/MembersOnline.subs.php');
1024
	$membersOnlineOptions = array(
1025
		'show_hidden' => allowedTo('moderate_forum'),
1026
	);
1027
	$return = getMembersOnlineStats($membersOnlineOptions);
1028
1029
	// Add some redundancy for backwards compatibility reasons.
1030
	if ($output_method !== 'echo')
1031
	{
1032
		return $return + array(
1033
			'users' => $return['users_online'],
1034
			'guests' => $return['num_guests'],
1035
			'hidden' => $return['num_users_hidden'],
1036
			'buddies' => $return['num_buddies'],
1037
			'num_users' => $return['num_users_online'],
1038
			'total_users' => $return['num_users_online'] + $return['num_guests'],
1039
		);
1040
	}
1041
1042
	echo '
1043
		', 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'];
1044
1045
	$bracketList = array();
1046
	if (!empty($user_info['buddies']))
1047
	{
1048
		$bracketList[] = comma_format($return['num_buddies']) . ' ' . ($return['num_buddies'] == 1 ? $txt['buddy'] : $txt['buddies']);
1049
	}
1050
1051
	if (!empty($return['num_spiders']))
1052
	{
1053
		$bracketList[] = comma_format($return['num_spiders']) . ' ' . ($return['num_spiders'] == 1 ? $txt['spider'] : $txt['spiders']);
1054
	}
1055
1056
	if (!empty($return['num_users_hidden']))
1057
	{
1058
		$bracketList[] = comma_format($return['num_users_hidden']) . ' ' . $txt['hidden'];
1059
	}
1060
1061
	if (!empty($bracketList))
1062
	{
1063
		echo ' (' . implode(', ', $bracketList) . ')';
1064
	}
1065
1066
	echo '<br />
1067
			', implode(', ', $return['list_users_online']);
1068
1069
	// Showing membergroups?
1070
	if (!empty($settings['show_group_key']) && !empty($return['membergroups']))
1071
	{
1072
		echo '<br />
1073
			[' . implode(']&nbsp;&nbsp;[', $return['membergroups']) . ']';
1074
	}
1075
}
1076
1077
/**
1078
 * Just like whosOnline except it also logs the online presence.
1079
 *
1080
 * @param string $output_method The output method. If 'echo', displays the stats,
1081
 * otherwise returns an array of info about them
1082
 */
1083
function ssi_logOnline($output_method = 'echo')
1084
{
1085
	writeLog();
1086
1087
	if ($output_method !== 'echo')
1088
	{
1089
		return ssi_whosOnline($output_method);
1090
	}
1091
	else
1092
	{
1093
		ssi_whosOnline($output_method);
1094
	}
1095
}
1096
1097
/**
1098
 * Shows a login box.
1099
 *
1100
 * @param string $redirect_to = '' The URL to redirect the user to after they login
1101
 * @param string $output_method = 'echo' The output method. If 'echo' and the user is a guest, displays a
1102
 * login box, otherwise returns whether the user is a guest
1103
 */
1104
function ssi_login($redirect_to = '', $output_method = 'echo')
1105
{
1106
	global $scripturl, $txt, $user_info, $modSettings, $context, $settings;
1107
1108
	if ($redirect_to !== '')
1109
	{
1110
		$_SESSION['login_url'] = $redirect_to;
1111
	}
1112
1113
	if ($output_method !== 'echo' || !$user_info['is_guest'])
1114
	{
1115
		return $user_info['is_guest'];
1116
	}
1117
1118
	$context['default_username'] = isset($_POST['user']) ? preg_replace('~&amp;#(\\d{1,7}|x[0-9a-fA-F]{1,6});~', '&#\\1;', htmlspecialchars($_POST['user'], ENT_COMPAT, 'UTF-8')) : '';
1119
1120
	echo '
1121
		<script src="', $settings['default_theme_url'], '/scripts/sha256.js"></script>';
1122
1123
	echo '
1124
		<form action="', $scripturl, '?action=login2" name="frmLogin" id="frmLogin" method="post" accept-charset="UTF-8" ', empty($context['disable_login_hashing']) ? ' onsubmit="hashLoginPassword(this, \'' . $context['session_id'] . '\');"' : '', '>
1125
		<div class="login centertext">
1126
			<div class="well">';
1127
1128
	// Did they make a mistake last time?
1129
	if (!empty($context['login_errors']))
1130
	{
1131
		echo '
1132
			<p class="errorbox">', implode('<br />', $context['login_errors']), '</p><br />';
1133
	}
1134
1135
	// Or perhaps there's some special description for this time?
1136
	if (isset($context['description']))
1137
	{
1138
		echo '
1139
				<p class="description">', $context['description'], '</p>';
1140
	}
1141
1142
	// Now just get the basic information - username, password, etc.
1143
	echo '
1144
				<dl>
1145
					<dt>', $txt['username'], ':</dt>
1146
					<dd>
1147
						<input type="text" name="user" size="20" value="', $context['default_username'], '" class="input_text" autofocus="autofocus" placeholder="', $txt['username'], '" />
1148
					</dd>
1149
					<dt>', $txt['password'], ':</dt>
1150
					<dd>
1151
						<input type="password" name="passwrd" value="" size="20" class="input_password" placeholder="', $txt['password'], '" />
1152
					</dd>
1153
				</dl>';
1154
1155
	if (!empty($modSettings['enableOpenID']))
1156
	{
1157
		echo '<p><strong>&mdash;', $txt['or'], '&mdash;</strong></p>
1158
				<dl>
1159
					<dt>', $txt['openid'], ':</dt>
1160
					<dd>
1161
						<input type="text" name="openid_identifier" class="input_text openid_login" size="17" />
1162
						&nbsp;<a href="', $scripturl, '?action=quickhelp;help=register_openid" onclick="return reqOverlayDiv(this.href);" class="helpicon i-help"><s>', $txt['help'], '</s></a>
1163
					</dd>
1164
				</dl>';
1165
	}
1166
1167
	echo '
1168
				<input type="submit" value="', $txt['login'], '" />
1169
				<p class="smalltext">
1170
					<a href="', $scripturl, '?action=reminder">', $txt['forgot_your_password'], '</a>
1171
				</p>
1172
				<input type="hidden" name="hash_passwrd" value="" />
1173
				<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
1174
				<input type="hidden" name="', $context['login_token_var'], '" value="', $context['login_token'], '" />
1175
			</div>
1176
		</div>
1177
		</form>';
1178
1179
	// Focus on the correct input - username or password.
1180
	echo '
1181
		<script>
1182
			document.forms.frmLogin.', isset($context['default_username']) && $context['default_username'] !== '' ? 'passwrd' : 'user', '.focus();
1183
		</script>';
1184
}
1185
1186
/**
1187
 * Show the most-voted-in poll.
1188
 *
1189
 * @param string $output_method = 'echo; The output method. If 'echo', displays the poll,
1190
 * otherwise returns an array of info about it
1191
 */
1192
function ssi_topPoll($output_method = 'echo')
1193
{
1194
	// Just use recentPoll, no need to duplicate code...
1195
	return ssi_recentPoll(true, $output_method);
1196
}
1197
1198
/**
1199
 * Show the most recently posted poll.
1200
 *
1201
 * @param bool $topPollInstead = false Show the top poll (based on votes) instead of the most recent one
1202
 * @param string $output_method = string The output method. If 'echo', displays the poll,
1203
 * otherwise returns an array of info about it.
1204
 */
1205
function ssi_recentPoll($topPollInstead = false, $output_method = 'echo')
1206
{
1207
	global $txt, $boardurl, $user_info, $context, $modSettings;
1208
1209
	$boardsAllowed = array_intersect(boardsAllowedTo('poll_view'), boardsAllowedTo('poll_vote'));
1210
1211
	if (empty($boardsAllowed))
1212
	{
1213
		return array();
1214
	}
1215
1216
	$db = database();
1217
1218
	$request = $db->query('', '
1219
		SELECT p.id_poll, p.question, t.id_topic, p.max_votes, p.guest_vote, p.hide_results, p.expire_time
1220
		FROM {db_prefix}polls AS p
1221
			INNER JOIN {db_prefix}topics AS t ON (t.id_poll = p.id_poll' . ($modSettings['postmod_active'] ? ' AND t.approved = {int:is_approved}' : '') . ')
1222
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)' . ($topPollInstead ? '
1223
			INNER JOIN {db_prefix}poll_choices AS pc ON (pc.id_poll = p.id_poll)' : '') . '
1224
			LEFT JOIN {db_prefix}log_polls AS lp ON (lp.id_poll = p.id_poll 
1225
				AND lp.id_member > {int:no_member} 
1226
				AND lp.id_member = {int:current_member})
1227
		WHERE p.voting_locked = {int:voting_opened}
1228
			AND (p.expire_time = {int:no_expiration} OR {int:current_time} < p.expire_time)
1229
			AND ' . ($user_info['is_guest'] ? 'p.guest_vote = {int:guest_vote_allowed}' : 'lp.id_choice IS NULL') . '
1230
			AND {query_wanna_see_board}' . (!in_array(0, $boardsAllowed) ? '
1231
			AND b.id_board IN ({array_int:boards_allowed_list})' : '') . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? '
1232
			AND b.id_board != {int:recycle_enable}' : '') . '
1233
		ORDER BY ' . ($topPollInstead ? 'pc.votes' : 'p.id_poll') . ' DESC
1234
		LIMIT 1',
1235
		array(
1236
			'current_member' => $user_info['id'],
1237
			'boards_allowed_list' => $boardsAllowed,
1238
			'is_approved' => 1,
1239
			'guest_vote_allowed' => 1,
1240
			'no_member' => 0,
1241
			'voting_opened' => 0,
1242
			'no_expiration' => 0,
1243
			'current_time' => time(),
1244
			'recycle_enable' => $modSettings['recycle_board'],
1245
		)
1246
	);
1247
	$row = $db->fetch_assoc($request);
1248
	$db->free_result($request);
1249
1250
	// This user has voted on all the polls.
1251
	if (empty($row))
1252
	{
1253
		return array();
1254
	}
1255
1256
	// If this is a guest who's voted we'll through ourselves to show poll to show the results.
1257
	if ($user_info['is_guest'] && (!$row['guest_vote'] || (isset($_COOKIE['guest_poll_vote']) && in_array($row['id_poll'], explode(',', $_COOKIE['guest_poll_vote'])))))
1258
	{
1259
		return ssi_showPoll($row['id_topic'], $output_method);
1260
	}
1261
1262
	$request = $db->query('', '
1263
		SELECT COUNT(DISTINCT id_member)
1264
		FROM {db_prefix}log_polls
1265
		WHERE id_poll = {int:current_poll}',
1266
		array(
1267
			'current_poll' => $row['id_poll'],
1268
		)
1269
	);
1270
	list ($total) = $db->fetch_row($request);
0 ignored issues
show
The method fetch_row() does not exist on ElkArte\Database\QueryInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to ElkArte\Database\QueryInterface. ( Ignorable by Annotation )

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

1270
	/** @scrutinizer ignore-call */ 
1271
 list ($total) = $db->fetch_row($request);
Loading history...
1271
	$db->free_result($request);
1272
1273
	$request = $db->query('', '
1274
		SELECT id_choice, label, votes
1275
		FROM {db_prefix}poll_choices
1276
		WHERE id_poll = {int:current_poll}',
1277
		array(
1278
			'current_poll' => $row['id_poll'],
1279
		)
1280
	);
1281
	$options = array();
1282
	while ($rowChoice = $db->fetch_assoc($request))
1283
	{
1284
		$rowChoice['label'] = censor($rowChoice['label']);
1285
1286
		$options[$rowChoice['id_choice']] = array($rowChoice['label'], $rowChoice['votes']);
1287
	}
1288
	$db->free_result($request);
1289
1290
	// Can they view it?
1291
	$is_expired = !empty($row['expire_time']) && $row['expire_time'] < time();
1292
	$allow_view_results = allowedTo('moderate_board') || $row['hide_results'] == 0 || $is_expired;
1293
1294
	$return = array(
1295
		'id' => $row['id_poll'],
1296
		'image' => 'poll',
1297
		'question' => $row['question'],
1298
		'total_votes' => $total,
1299
		'is_locked' => false,
1300
		'topic' => $row['id_topic'],
1301
		'allow_view_results' => $allow_view_results,
1302
		'options' => array()
1303
	);
1304
1305
	$bbc_parser = \BBC\ParserWrapper::instance();
1306
1307
	// Calculate the percentages and bar lengths...
1308
	$divisor = $return['total_votes'] == 0 ? 1 : $return['total_votes'];
1309
	foreach ($options as $i => $option)
1310
	{
1311
		$bar = floor(($option[1] * 100) / $divisor);
1312
		$barWide = $bar == 0 ? 1 : floor(($bar * 5) / 3);
1313
		$return['options'][$i] = array(
1314
			'id' => 'options-' . ($topPollInstead ? 'top-' : 'recent-') . $i,
1315
			'percent' => $bar,
1316
			'votes' => $option[1], /* Todo: gradient div will need some visual tweaking. Or replace with the current bar_ndt code from Poll.subs */
1317
			'bar' => '<div class="poll_gradient" style="width: ' . $barWide . 'px;"></div>',
1318
			'option' => $bbc_parser->parsePoll($option[0]),
1319
			'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') . '" />'
1320
		);
1321
	}
1322
1323
	$return['allowed_warning'] = $row['max_votes'] > 1 ? sprintf($txt['poll_options6'], min(count($options), $row['max_votes'])) : '';
1324
1325
	if ($output_method !== 'echo')
1326
	{
1327
		return $return;
1328
	}
1329
1330
	if ($allow_view_results)
1331
	{
1332
		echo '
1333
		<form class="ssi_poll" action="', $boardurl, '/SSI.php?ssi_function=pollVote" method="post" accept-charset="UTF-8">
1334
			<strong>', $return['question'], '</strong><br />
1335
			', !empty($return['allowed_warning']) ? $return['allowed_warning'] . '<br />' : '';
1336
1337
		foreach ($return['options'] as $option)
1338
		{
1339
			echo '
1340
			<label for="', $option['id'], '">', $option['vote_button'], ' ', $option['option'], '</label><br />';
1341
		}
1342
1343
		echo '
1344
			<input type="submit" value="', $txt['poll_vote'], '" />
1345
			<input type="hidden" name="poll" value="', $return['id'], '" />
1346
			<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
1347
		</form>';
1348
	}
1349
	else
1350
	{
1351
		echo $txt['poll_cannot_see'];
1352
	}
1353
}
1354
1355
/**
1356
 * Show a poll.
1357
 *
1358
 * - It is possible to use this function in combination with the template
1359
 * template_display_poll_above from Display.template.php, the only part missing
1360
 * is the definition of the poll moderation button array (see Display.controller.php
1361
 * for details).
1362
 *
1363
 * @param int|null $topicID = null The topic to show the poll from. If null, $_REQUEST['ssi_topic'] will
1364
 * be used instead.
1365
 * @param string $output_method = 'echo' The output method. If 'echo', displays the poll, otherwise
1366
 * returns an array of info about it.
1367
 */
1368
function ssi_showPoll($topicID = null, $output_method = 'echo')
1369
{
1370
	global $txt, $user_info, $context, $scripturl;
1371
	static $last_board = null;
1372
1373
	require_once(SUBSDIR . '/Poll.subs.php');
1374
	require_once(SUBSDIR . '/Topic.subs.php');
1375
1376
	if ($topicID === null && isset($_REQUEST['ssi_topic']))
1377
	{
1378
		$topicID = (int) $_REQUEST['ssi_topic'];
1379
	}
1380
	else
1381
	{
1382
		$topicID = (int) $topicID;
1383
	}
1384
1385
	if (empty($topicID))
1386
	{
1387
		return array();
1388
	}
1389
1390
	// Get the topic starter information.
1391
	$topicinfo = getTopicInfo($topicID, 'starter');
1392
1393
	$boards_can_poll = boardsAllowedTo('poll_view');
1394
1395
	// If:
1396
	//  - is not allowed to see poll in any board,
1397
	//  - or:
1398
	//     - is not allowed in the specific board, and
1399
	//     - is not an admin
1400
	// fail
1401
	if (empty($boards_can_poll) || (!in_array($topicinfo['id_board'], $boards_can_poll) && !in_array(0, $boards_can_poll)))
1402
	{
1403
		return array();
1404
	}
1405
1406
	$context['user']['started'] = $user_info['id'] == $topicinfo['id_member'] && !$user_info['is_guest'];
1407
1408
	$poll_id = associatedPoll($topicID);
1409
	loadPollContext($poll_id);
1410
1411
	if (empty($context['poll']))
1412
	{
1413
		return array();
1414
	}
1415
1416
	if ($output_method !== 'echo')
1417
	{
1418
		return $context['poll'];
1419
	}
1420
1421
	echo '
1422
		<div class="content" id="poll_options">
1423
			<h4 id="pollquestion">
1424
				', $context['poll']['question'], '
1425
			</h4>';
1426
1427
	if ($context['poll']['allow_vote'])
1428
	{
1429
		echo '
1430
			<form action="', $scripturl, '?action=poll;sa=vote;topic=', $context['current_topic'], '.', $context['start'], ';poll=', $context['poll']['id'], '" method="post" accept-charset="UTF-8">';
1431
1432
		// Show a warning if they are allowed more than one option.
1433
		if ($context['poll']['allowed_warning'])
1434
		{
1435
			echo '
1436
				<p>', $context['poll']['allowed_warning'], '</p>';
1437
		}
1438
1439
		echo '
1440
				<ul class="options">';
1441
1442
		// Show each option with its button - a radio likely.
1443
		foreach ($context['poll']['options'] as $option)
1444
		{
1445
			echo '
1446
					<li>', $option['vote_button'], ' <label for="', $option['id'], '">', $option['option'], '</label></li>';
1447
		}
1448
1449
		echo '
1450
				</ul>
1451
				<div class="submitbutton">
1452
					<input type="submit" value="', $txt['poll_vote'], '" />
1453
					<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
1454
				</div>
1455
			</form>';
1456
1457
		// Is the clock ticking?
1458
		if (!empty($context['poll']['expire_time']))
1459
		{
1460
			echo '
1461
			<p><strong>', ($context['poll']['is_expired'] ? $txt['poll_expired_on'] : $txt['poll_expires_on']), ':</strong> ', $context['poll']['expire_time'], '</p>';
1462
		}
1463
1464
	}
1465
	elseif ($context['poll']['allow_view_results'])
1466
	{
1467
		echo '
1468
			<ul class="options">';
1469
1470
		// Show each option with its corresponding percentage bar.
1471
		foreach ($context['poll']['options'] as $option)
1472
		{
1473
			echo '
1474
				<li', $option['voted_this'] ? ' class="voted"' : '', '>', $option['option'], '
1475
					<div class="results">';
1476
1477
			if ($context['allow_poll_view'])
1478
			{
1479
				echo '
1480
						<div class="statsbar"> ', $option['bar_ndt'], '</div>
1481
						<span class="percentage">', $option['votes'], ' (', $option['percent'], '%)</span>';
1482
			}
1483
1484
			echo '
1485
					</div>
1486
				</li>';
1487
		}
1488
1489
		echo '
1490
			</ul>';
1491
1492
		if ($context['allow_poll_view'])
1493
		{
1494
			echo '
1495
			<p><strong>', $txt['poll_total_voters'], ':</strong> ', $context['poll']['total_votes'], '</p>';
1496
		}
1497
1498
		// Is the clock ticking?
1499
		if (!empty($context['poll']['expire_time']))
1500
		{
1501
			echo '
1502
			<p><strong>', ($context['poll']['is_expired'] ? $txt['poll_expired_on'] : $txt['poll_expires_on']), ':</strong> ', $context['poll']['expire_time'], '</p>';
1503
		}
1504
	}
1505
	// Cannot see it I'm afraid!
1506
	else
1507
	{
1508
		echo $txt['poll_cannot_see'];
1509
	}
1510
1511
	echo '
1512
			</div>';
1513
}
1514
1515
/**
1516
 * Takes care of voting - don't worry, this is done automatically.
1517
 */
1518
function ssi_pollVote()
1519
{
1520
	global $context, $topic, $board;
1521
1522
	$pollID = isset($_POST['poll']) ? (int) $_POST['poll'] : 0;
1523
1524
	if (empty($pollID) || !isset($_POST[$context['session_var']]) || $_POST[$context['session_var']] != $_SESSION['session_value'] || empty($_POST['options']))
1525
	{
1526
		echo '<!DOCTYPE html>
1527
<html>
1528
<head>
1529
	<script>
1530
		history.go(-1);
1531
	</script>
1532
</head>
1533
<body>&laquo;</body>
1534
</html>';
1535
1536
		return;
1537
	}
1538
1539
	require_once(SUBSDIR . '/Poll.subs.php');
1540
1541
	// We have to fake we are in a topic so that we can use the proper controller
1542
	list ($topic, $board) = topicFromPoll($pollID);
1543
	loadBoard();
1544
1545
	$poll_action = new \ElkArte\Controller\Poll(new \ElkArte\EventManager());
1546
	$poll_action->setUser(\ElkArte\User::$info);
1547
	$poll_action->pre_dispatch();
1548
1549
	// The controller takes already care of redirecting properly or fail
1550
	$poll_action->action_vote();
1551
}
1552
1553
/**
1554
 * Show a search box.
1555
 *
1556
 * @param string $output_method = 'echo'  The output method. If 'echo', displays a search box,
1557
 * otherwise returns the URL of the search page.
1558
 */
1559
function ssi_quickSearch($output_method = 'echo')
1560
{
1561
	global $scripturl, $txt;
1562
1563
	if (!allowedTo('search_posts'))
1564
	{
1565
		return '';
1566
	}
1567
1568
	if ($output_method !== 'echo')
1569
	{
1570
		return $scripturl . '?action=search';
1571
	}
1572
1573
	echo '
1574
		<form action="', $scripturl, '?action=search;sa=results" method="post" accept-charset="UTF-8">
1575
			<input type="hidden" name="advanced" value="0" />
1576
			<input type="text" name="search" size="30" class="input_text" />
1577
			<input type="submit" value="', $txt['search'], '" />
1578
		</form>';
1579
}
1580
1581
/**
1582
 * Show what would be the forum news.
1583
 *
1584
 * @param string $output_method = 'echo' The output method. If 'echo', shows the news item, otherwise returns it.
1585
 */
1586
function ssi_news($output_method = 'echo')
1587
{
1588
	global $context;
1589
1590
	if ($output_method !== 'echo')
1591
	{
1592
		return $context['random_news_line'];
1593
	}
1594
1595
	echo $context['random_news_line'];
1596
}
1597
1598
/**
1599
 * Show today's birthdays.
1600
 *
1601
 * @param string $output_method = 'echo' The output method. If 'echo', displays a list of users,
1602
 * otherwise returns an array of info about them.
1603
 */
1604
function ssi_todaysBirthdays($output_method = 'echo')
1605
{
1606
	global $scripturl, $modSettings, $user_info;
1607
1608
	if (empty($modSettings['cal_enabled']) || !allowedTo('calendar_view') || !allowedTo('profile_view_any'))
1609
	{
1610
		return '';
1611
	}
1612
1613
	$eventOptions = array(
1614
		'include_birthdays' => true,
1615
		'num_days_shown' => empty($modSettings['cal_days_for_index']) || $modSettings['cal_days_for_index'] < 1 ? 1 : $modSettings['cal_days_for_index'],
1616
	);
1617
	$return = \ElkArte\Cache\Cache::instance()->quick_get('calendar_index_offset_' . ($user_info['time_offset'] + $modSettings['time_offset']), 'subs/Calendar.subs.php', 'cache_getRecentEvents', array($eventOptions));
1618
1619
	if ($output_method !== 'echo')
1620
	{
1621
		return $return['calendar_birthdays'];
1622
	}
1623
1624
	foreach ($return['calendar_birthdays'] as $member)
1625
	{
1626
		echo '
1627
			<a href="', $scripturl, '?action=profile;u=', $member['id'], '">' . $member['name'] . (isset($member['age']) ? ' (' . $member['age'] . ')' : '') . '</a>' . (!$member['is_last'] ? ', ' : '');
1628
	}
1629
}
1630
1631
/**
1632
 * Show today's holidays.
1633
 *
1634
 * @param string $output_method = 'echo' The output method. If 'echo', displays a list of holidays,
1635
 * otherwise returns an array of info about them.
1636
 */
1637
function ssi_todaysHolidays($output_method = 'echo')
1638
{
1639
	global $modSettings, $user_info;
1640
1641
	if (empty($modSettings['cal_enabled']) || !allowedTo('calendar_view'))
1642
	{
1643
		return false;
1644
	}
1645
1646
	$eventOptions = array(
1647
		'include_holidays' => true,
1648
		'num_days_shown' => empty($modSettings['cal_days_for_index']) || $modSettings['cal_days_for_index'] < 1 ? 1 : $modSettings['cal_days_for_index'],
1649
	);
1650
	$return = \ElkArte\Cache\Cache::instance()->quick_get('calendar_index_offset_' . ($user_info['time_offset'] + $modSettings['time_offset']), 'subs/Calendar.subs.php', 'cache_getRecentEvents', array($eventOptions));
1651
1652
	if ($output_method !== 'echo')
1653
	{
1654
		return $return['calendar_holidays'];
1655
	}
1656
1657
	echo '
1658
		', implode(', ', $return['calendar_holidays']);
1659
}
1660
1661
/**
1662
 * Show today's events.
1663
 *
1664
 * @param string $output_method = 'echo' The output method. If 'echo', displays a list of events,
1665
 * otherwise returns an array of info about them.
1666
 */
1667
function ssi_todaysEvents($output_method = 'echo')
1668
{
1669
	global $modSettings, $user_info;
1670
1671
	if (empty($modSettings['cal_enabled']) || !allowedTo('calendar_view'))
1672
	{
1673
		return false;
1674
	}
1675
1676
	$eventOptions = array(
1677
		'include_events' => true,
1678
		'num_days_shown' => empty($modSettings['cal_days_for_index']) || $modSettings['cal_days_for_index'] < 1 ? 1 : $modSettings['cal_days_for_index'],
1679
	);
1680
	$return = \ElkArte\Cache\Cache::instance()->quick_get('calendar_index_offset_' . ($user_info['time_offset'] + $modSettings['time_offset']), 'subs/Calendar.subs.php', 'cache_getRecentEvents', array($eventOptions));
1681
1682
	if ($output_method !== 'echo')
1683
	{
1684
		return $return['calendar_events'];
1685
	}
1686
1687
	foreach ($return['calendar_events'] as $event)
1688
	{
1689
		if ($event['can_edit'])
1690
		{
1691
			echo '
1692
	<a href="' . $event['modify_href'] . '" class="moderation_link">*</a> ';
1693
		}
1694
1695
		echo '
1696
	' . $event['link'] . (!$event['is_last'] ? ', ' : '');
1697
	}
1698
}
1699
1700
/**
1701
 * Show all calendar entries for today. (birthdays, holidays, and events.)
1702
 *
1703
 * @param string $output_method = 'echo' The output method. If 'echo', displays a list of calendar
1704
 * items, otherwise returns an array of info about them.
1705
 */
1706
function ssi_todaysCalendar($output_method = 'echo')
1707
{
1708
	global $modSettings, $txt, $scripturl, $user_info;
1709
1710
	if (empty($modSettings['cal_enabled']) || !allowedTo('calendar_view'))
1711
	{
1712
		return '';
1713
	}
1714
1715
	$eventOptions = array(
1716
		'include_birthdays' => allowedTo('profile_view_any'),
1717
		'include_holidays' => true,
1718
		'include_events' => true,
1719
		'num_days_shown' => empty($modSettings['cal_days_for_index']) || $modSettings['cal_days_for_index'] < 1 ? 1 : $modSettings['cal_days_for_index'],
1720
	);
1721
1722
	$return = \ElkArte\Cache\Cache::instance()->quick_get('calendar_index_offset_' . ($user_info['time_offset'] + $modSettings['time_offset']), 'subs/Calendar.subs.php', 'cache_getRecentEvents', array($eventOptions));
1723
1724
	if ($output_method !== 'echo')
1725
	{
1726
		return $return;
1727
	}
1728
1729
	if (!empty($return['calendar_holidays']))
1730
	{
1731
		echo '
1732
			<span class="holiday">' . $txt['calendar_prompt'] . ' ' . implode(', ', $return['calendar_holidays']) . '<br /></span>';
1733
	}
1734
1735
	if (!empty($return['calendar_birthdays']))
1736
	{
1737
		echo '
1738
			<span class="birthday">' . $txt['birthdays_upcoming'] . '</span> ';
1739
1740
		foreach ($return['calendar_birthdays'] as $member)
1741
		{
1742
			echo '
1743
			<a href="', $scripturl, '?action=profile;u=', $member['id'], '">', $member['name'], isset($member['age']) ? ' (' . $member['age'] . ')' : '', '</a>', !$member['is_last'] ? ', ' : '';
1744
		}
1745
1746
		echo '
1747
			<br />';
1748
	}
1749
1750
	if (!empty($return['calendar_events']))
1751
	{
1752
		echo '
1753
			<span class="event">' . $txt['events_upcoming'] . '</span> ';
1754
1755
		foreach ($return['calendar_events'] as $event)
1756
		{
1757
			if ($event['can_edit'])
1758
			{
1759
				echo '
1760
			<a href="' . $event['modify_href'] . '" class="moderation_link">*</a> ';
1761
			}
1762
1763
			echo '
1764
			' . $event['link'] . (!$event['is_last'] ? ', ' : '');
1765
		}
1766
	}
1767
}
1768
1769
/**
1770
 * Show the latest news, with a template... by board.
1771
 *
1772
 * @param int|null $board The ID of the board to get the info from. Defaults to $board or
1773
 * $_GET['board'] if not set.
1774
 * @param int|null $limit How many items to show. Defaults to $_GET['limit'] or 5 if not set.
1775
 * @param int|null $start Start with the specified item. Defaults to $_GET['start'] or 0 if not set.
1776
 * @param int|null $length How many characters to show from each post. Defaults to $_GET['length']
1777
 * or 0 (no limit) if not set.
1778
 * @param string $preview = 'first' item to fetch, first or last
1779
 * @param string $output_method = 'echo' The output method. If 'echo', displays the news items,
1780
 * otherwise returns an array of info about them.
1781
 */
1782
function ssi_boardNews($board = null, $limit = null, $start = null, $length = null, $preview = 'first', $output_method = 'echo')
1783
{
1784
	global $scripturl, $txt, $settings, $modSettings;
1785
1786
	theme()->getTemplates()->loadLanguageFile('Stats');
1787
1788
	$db = database();
1789
1790
	// Must be integers....
1791
	if ($limit === null)
1792
	{
1793
		$limit = isset($_GET['limit']) ? (int) $_GET['limit'] : 5;
1794
	}
1795
	else
1796
	{
1797
		$limit = (int) $limit;
1798
	}
1799
1800
	if ($start === null)
1801
	{
1802
		$start = isset($_GET['start']) ? (int) $_GET['start'] : 0;
1803
	}
1804
	else
1805
	{
1806
		$start = (int) $start;
1807
	}
1808
1809
	if ($board !== null)
1810
	{
1811
		$board = (int) $board;
1812
	}
1813
	elseif (isset($_GET['board']))
1814
	{
1815
		$board = (int) $_GET['board'];
1816
	}
1817
1818
	if ($length === null)
1819
	{
1820
		$length = isset($_GET['length']) ? (int) $_GET['length'] : 500;
1821
	}
1822
	else
1823
	{
1824
		$length = (int) $length;
1825
	}
1826
1827
	$limit = max(0, $limit);
1828
	$start = max(0, $start);
1829
1830
	// Make sure guests can see this board.
1831
	$request = $db->query('', '
1832
		SELECT id_board
1833
		FROM {db_prefix}boards
1834
		WHERE ' . ($board === null ? '' : 'id_board = {int:current_board}
1835
			AND ') . 'FIND_IN_SET(-1, member_groups) != 0
1836
			AND redirect = {string:blank_redirect}
1837
		LIMIT 1',
1838
		array(
1839
			'current_board' => $board,
1840
			'blank_redirect' => '',
1841
		)
1842
	);
1843
	if ($db->num_rows($request) == 0)
0 ignored issues
show
The method num_rows() does not exist on ElkArte\Database\QueryInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to ElkArte\Database\QueryInterface. ( Ignorable by Annotation )

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

1843
	if ($db->/** @scrutinizer ignore-call */ num_rows($request) == 0)
Loading history...
1844
	{
1845
		if ($output_method === 'echo')
1846
		{
1847
			die($txt['ssi_no_guests']);
0 ignored issues
show
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1848
		}
1849
		else
1850
		{
1851
			return array();
1852
		}
1853
	}
1854
	list ($board) = $db->fetch_row($request);
1855
	$db->free_result($request);
1856
1857
	// Load the message icons - the usual suspects.
1858
	$icon_sources = new MessageTopicIcons(!empty($modSettings['messageIconChecks_enable']), $settings['theme_dir']);
1859
1860
	// Find the posts.
1861
	$indexOptions = array(
1862
		'only_approved' => true,
1863
		'include_sticky' => false,
1864
		'ascending' => false,
1865
		'include_avatars' => false,
1866
		'previews' => $length
1867
	);
1868
1869
	require_once(SUBSDIR . '/MessageIndex.subs.php');
1870
	$request = messageIndexTopics($board, 0, $start, $limit, 'first_post', 't.id_topic', $indexOptions);
1871
1872
	if ($request->hasResults() === false)
1873
	{
1874
		return false;
1875
	}
1876
1877
	$bbc_parser = \BBC\ParserWrapper::instance();
1878
1879
	$return = array();
1880
	foreach ($request as $row)
1881
	{
1882
		if (!isset($row[$preview . '_body']))
1883
		{
1884
			$preview = 'first';
1885
		}
1886
1887
		$row['body'] = $row[$preview . '_body'];
1888
		$row['subject'] = $row[$preview . '_subject'];
1889
		$row['id_msg'] = $row['id_' . $preview . '_msg'];
1890
		$row['icon'] = $row[$preview . '_icon'];
1891
		$row['id_member'] = $row[$preview . '_id_member'];
1892
		$row['smileys_enabled'] = $row[$preview . '_smileys'];
1893
		$row['poster_time'] = $row[$preview . '_poster_time'];
1894
		$row['poster_name'] = $row[$preview . '_display_name'];
1895
		$row['body'] = $bbc_parser->parseMessage($row['body'], $row['smileys_enabled']);
1896
1897
		$row['subject'] = censor($row['subject']);
1898
		$row['body'] = censor($row['body']);
1899
1900
		$return[] = array(
1901
			'id' => $row['id_topic'],
1902
			'message_id' => $row['id_msg'],
1903
			'icon' => '<img src="' . $icon_sources->{$row['icon']} . '" alt="' . $row['icon'] . '" />',
1904
			'subject' => $row['subject'],
1905
			'time' => standardTime($row['poster_time']),
1906
			'html_time' => htmlTime($row['poster_time']),
1907
			'timestamp' => forum_time(true, $row['poster_time']),
1908
			'body' => $row['body'],
1909
			'href' => $scripturl . '?topic=' . $row['id_topic'] . '.0',
1910
			'link' => '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.0">' . $row['num_replies'] . ' ' . ($row['num_replies'] == 1 ? $txt['ssi_comment'] : $txt['ssi_comments']) . '</a>',
1911
			'replies' => $row['num_replies'],
1912
			'comment_href' => !empty($row['locked']) ? '' : $scripturl . '?action=post;topic=' . $row['id_topic'] . '.' . $row['num_replies'] . ';last_msg=' . $row['id_last_msg'],
1913
			'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>',
1914
			'new_comment' => !empty($row['locked']) ? '' : '<a href="' . $scripturl . '?action=post;topic=' . $row['id_topic'] . '.' . $row['num_replies'] . '">' . $txt['ssi_write_comment'] . '</a>',
1915
			'poster' => array(
1916
				'id' => $row['id_member'],
1917
				'name' => $row['poster_name'],
1918
				'href' => !empty($row['id_member']) ? $scripturl . '?action=profile;u=' . $row['id_member'] : '',
1919
				'link' => !empty($row['id_member']) ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['poster_name'] . '</a>' : $row['poster_name']
1920
			),
1921
			'locked' => !empty($row['locked']),
1922
			'is_last' => false
1923
		);
1924
	}
1925
1926
	$return[count($return) - 1]['is_last'] = true;
1927
1928
	if ($output_method !== 'echo')
1929
	{
1930
		return $return;
1931
	}
1932
1933
	foreach ($return as $news)
1934
	{
1935
		echo '
1936
			<div class="news_item">
1937
				<h3 class="news_header">
1938
					', $news['icon'], '
1939
					<a href="', $news['href'], '">', $news['subject'], '</a>
1940
				</h3>
1941
				<div class="news_timestamp">', $news['time'], ' ', $txt['by'], ' ', $news['poster']['link'], '</div>
1942
				<div class="news_body">', $news['body'], '</div>
1943
				', $news['link'], $news['locked'] ? '' : ' | ' . $news['comment_link'], '
1944
			</div>';
1945
1946
		if (!$news['is_last'])
1947
		{
1948
			echo '
1949
			<hr />';
1950
		}
1951
	}
1952
}
1953
1954
/**
1955
 * Show the most recent events.
1956
 *
1957
 * @param int $max_events The maximum number of events to return
1958
 * @param string $output_method = 'echo' The output method. If 'echo', displays the events,
1959
 * otherwise returns an array of info about them.
1960
 */
1961
function ssi_recentEvents($max_events = 7, $output_method = 'echo')
1962
{
1963
	global $modSettings, $txt;
1964
1965
	if (empty($modSettings['cal_enabled']) || !allowedTo('calendar_view'))
1966
	{
1967
		return false;
1968
	}
1969
1970
	require_once(SUBSDIR . '/Calendar.subs.php');
1971
1972
	// Find all events which are happening in the near future that the member can see.
1973
	$date = strftime('%Y-%m-%d', forum_time(false));
1974
	$events = getEventRange($date, $date, true, $max_events);
1975
1976
	$return = array();
1977
	$duplicates = array();
1978
	foreach ($events as $date => $day_events)
1979
	{
1980
		foreach ($day_events as $row)
1981
		{
1982
			// 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.
1983
			if (!empty($duplicates[$row['title'] . $row['id_topic']]))
1984
			{
1985
				continue;
1986
			}
1987
1988
			$return[$date][] = $row;
1989
1990
			// Let's not show this one again, huh?
1991
			$duplicates[$row['title'] . $row['id_topic']] = true;
1992
		}
1993
	}
1994
1995
	foreach ($return as $mday => $array)
1996
	{
1997
		$return[$mday][count($array) - 1]['is_last'] = true;
1998
	}
1999
2000
	if ($output_method !== 'echo' || empty($return))
2001
	{
2002
		return $return;
2003
	}
2004
2005
	// Well the output method is echo.
2006
	echo '
2007
			<span class="event">' . $txt['events'] . '</span> ';
2008
2009
	foreach ($return as $mday => $array)
2010
	{
2011
		foreach ($array as $event)
2012
		{
2013
			if ($event['can_edit'])
2014
			{
2015
				echo '
2016
				<a href="' . $event['modify_href'] . '" class="moderation_link">*</a> ';
2017
			}
2018
2019
			echo '
2020
				' . $event['link'] . (!$event['is_last'] ? ', ' : '');
2021
		}
2022
	}
2023
}
2024
2025
/**
2026
 * Check the passed id_member/password.
2027
 *
2028
 *  If $is_username is true, treats $id as a username.
2029
 *
2030
 * @param int|string|null $id The ID or username of a user
2031
 * @param string|null $password The password to check
2032
 * @param bool $is_username If true, treats $id as a username rather than a user ID
2033
 */
2034
function ssi_checkPassword($id = null, $password = null, $is_username = false)
2035
{
2036
	// If $id is null, this was most likely called from a query string and should do nothing.
2037
	if ($id === null)
2038
	{
2039
		return false;
2040
	}
2041
2042
	require_once(SUBSDIR . '/Auth.subs.php');
2043
2044
	$member = loadExistingMember($id, !$is_username);
2045
2046
	return validateLoginPassword($password, $member['passwd'], $member['member_name']) && $member['is_activated'] == 1;
2047
}
2048
2049
/**
2050
 * We want to show the recent attachments outside of the forum.
2051
 *
2052
 * @param int $num_attachments = 10 How many to return
2053
 * @param string[] $attachment_ext = array() Only show attachments with the specified extensions
2054
 * @param string $output_method = 'echo'  The output method. If 'echo', displays a table with links/info,
2055
 * otherwise returns an array with information about the attachments
2056
 */
2057
function ssi_recentAttachments($num_attachments = 10, $attachment_ext = array(), $output_method = 'echo')
2058
{
2059
	global $modSettings, $scripturl, $txt;
2060
2061
	// We want to make sure that we only get attachments for boards that we can see *if* any.
2062
	$attachments_boards = boardsAllowedTo('view_attachments');
2063
2064
	// No boards?  Adios amigo.
2065
	if (empty($attachments_boards))
2066
	{
2067
		return array();
2068
	}
2069
2070
	$db = database();
2071
2072
	// Is it an array?
2073
	if (!is_array($attachment_ext))
0 ignored issues
show
The condition is_array($attachment_ext) is always true.
Loading history...
2074
	{
2075
		$attachment_ext = array($attachment_ext);
2076
	}
2077
2078
	// Lets build the query.
2079
	$request = $db->query('', '
2080
		SELECT
2081
			att.id_attach, att.id_msg, att.filename, COALESCE(att.size, 0) AS filesize, att.downloads, mem.id_member,
2082
			COALESCE(mem.real_name, m.poster_name) AS poster_name, m.id_topic, m.subject, t.id_board, m.poster_time,
2083
			att.width, att.height' . (empty($modSettings['attachmentShowImages']) || empty($modSettings['attachmentThumbnails']) ? '' : ', COALESCE(thumb.id_attach, 0) AS id_thumb, thumb.width AS thumb_width, thumb.height AS thumb_height') . '
2084
		FROM {db_prefix}attachments AS att
2085
			INNER JOIN {db_prefix}messages AS m ON (m.id_msg = att.id_msg)
2086
			INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic)
2087
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)' . (empty($modSettings['attachmentShowImages']) || empty($modSettings['attachmentThumbnails']) ? '' : '
2088
			LEFT JOIN {db_prefix}attachments AS thumb ON (thumb.id_attach = att.id_thumb)') . '
2089
		WHERE att.attachment_type = 0' . ($attachments_boards === array(0) ? '' : '
2090
			AND m.id_board IN ({array_int:boards_can_see})') . (!empty($attachment_ext) ? '
2091
			AND att.fileext IN ({array_string:attachment_ext})' : '') .
2092
			(!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : '
2093
			AND t.approved = {int:is_approved}
2094
			AND m.approved = {int:is_approved}
2095
			AND att.approved = {int:is_approved}') . '
2096
		ORDER BY att.id_attach DESC
2097
		LIMIT {int:num_attachments}',
2098
		array(
2099
			'boards_can_see' => $attachments_boards,
2100
			'attachment_ext' => $attachment_ext,
2101
			'num_attachments' => $num_attachments,
2102
			'is_approved' => 1,
2103
		)
2104
	);
2105
2106
	// We have something.
2107
	$attachments = array();
2108
	while ($row = $db->fetch_assoc($request))
2109
	{
2110
		$filename = preg_replace('~&amp;#(\\d{1,7}|x[0-9a-fA-F]{1,6});~', '&#\\1;', htmlspecialchars($row['filename'], ENT_COMPAT, 'UTF-8'));
2111
2112
		// Is it an image?
2113
		$attachments[$row['id_attach']] = array(
2114
			'member' => array(
2115
				'id' => $row['id_member'],
2116
				'name' => $row['poster_name'],
2117
				'link' => empty($row['id_member']) ? $row['poster_name'] : '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['poster_name'] . '</a>',
2118
			),
2119
			'file' => array(
2120
				'filename' => $filename,
2121
				'filesize' => byte_format($row['filesize']),
2122
				'downloads' => $row['downloads'],
2123
				'href' => $scripturl . '?action=dlattach;topic=' . $row['id_topic'] . '.0;attach=' . $row['id_attach'],
2124
				'link' => '<a href="' . $scripturl . '?action=dlattach;topic=' . $row['id_topic'] . '.0;attach=' . $row['id_attach'] . '"><i class="icon i-paperclip"><s>Attachement:</s></i> ' . $filename . '</a>',
2125
				'is_image' => !empty($row['width']) && !empty($row['height']) && !empty($modSettings['attachmentShowImages']),
2126
			),
2127
			'topic' => array(
2128
				'id' => $row['id_topic'],
2129
				'subject' => $row['subject'],
2130
				'href' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'],
2131
				'link' => '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'] . '">' . $row['subject'] . '</a>',
2132
				'time' => standardTime($row['poster_time']),
2133
				'html_time' => htmlTime($row['poster_time']),
2134
				'timestamp' => forum_time(true, $row['poster_time']),
2135
			),
2136
		);
2137
2138
		// Images.
2139
		if ($attachments[$row['id_attach']]['file']['is_image'])
2140
		{
2141
			$id_thumb = empty($row['id_thumb']) ? $row['id_attach'] : $row['id_thumb'];
2142
			$attachments[$row['id_attach']]['file']['image'] = array(
2143
				'id' => $id_thumb,
2144
				'width' => $row['width'],
2145
				'height' => $row['height'],
2146
				'img' => '<img src="' . $scripturl . '?action=dlattach;topic=' . $row['id_topic'] . '.0;attach=' . $row['id_attach'] . ';image" alt="' . $filename . '" />',
2147
				'thumb' => '<img src="' . $scripturl . '?action=dlattach;topic=' . $row['id_topic'] . '.0;attach=' . $id_thumb . ';image" alt="' . $filename . '" />',
2148
				'href' => $scripturl . '?action=dlattach;topic=' . $row['id_topic'] . '.0;attach=' . $id_thumb . ';image',
2149
				'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>',
2150
			);
2151
		}
2152
	}
2153
	$db->free_result($request);
2154
2155
	// So you just want an array?  Here you can have it.
2156
	if ($output_method === 'array' || empty($attachments))
2157
	{
2158
		return $attachments;
2159
	}
2160
2161
	// Give them the default.
2162
	echo '
2163
		<table class="ssi_table">
2164
			<tr>
2165
				<th>', $txt['file'], '</th>
2166
				<th>', $txt['posted_by'], '</th>
2167
				<th class="centertext">', $txt['downloads'], '</th>
2168
				<th>', $txt['filesize'], '</th>
2169
			</tr>';
2170
2171
	foreach ($attachments as $attach)
2172
	{
2173
		echo '
2174
			<tr>
2175
				<td>', $attach['file']['link'], '</td>
2176
				<td>', $attach['member']['link'], '</td>
2177
				<td class="centertext">', $attach['file']['downloads'], '</td>
2178
				<td>', $attach['file']['filesize'], '</td>
2179
			</tr>';
2180
	}
2181
2182
	echo '
2183
		</table>';
2184
}
2185