Completed
Push — release-2.1 ( b61572...dc1632 )
by Colin
07:33
created

Display.php ➔ Download()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 0
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * This is perhaps the most important and probably most accessed file in all
5
 * of SMF.  This file controls topic, message, and attachment display.
6
 *
7
 * Simple Machines Forum (SMF)
8
 *
9
 * @package SMF
10
 * @author Simple Machines http://www.simplemachines.org
11
 * @copyright 2017 Simple Machines and individual contributors
12
 * @license http://www.simplemachines.org/about/smf/license.php BSD
13
 *
14
 * @version 2.1 Beta 4
15
 */
16
17
if (!defined('SMF'))
18
	die('No direct access...');
19
20
/**
21
 * The central part of the board - topic display.
22
 * This function loads the posts in a topic up so they can be displayed.
23
 * It uses the main sub template of the Display template.
24
 * It requires a topic, and can go to the previous or next topic from it.
25
 * It jumps to the correct post depending on a number/time/IS_MSG passed.
26
 * It depends on the messages_per_page, defaultMaxMessages and enableAllMessages settings.
27
 * It is accessed by ?topic=id_topic.START.
28
 * @return void
29
 */
30
function Display()
31
{
32
	global $scripturl, $txt, $modSettings, $context, $settings;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
33
	global $options, $sourcedir, $user_info, $board_info, $topic, $board;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
34
	global $messages_request, $language, $smcFunc;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
35
36
	// What are you gonna display if these are empty?!
37
	if (empty($topic))
38
		fatal_lang_error('no_board', false);
39
40
	// Load the proper template.
41
	loadTemplate('Display');
42
43
	// Not only does a prefetch make things slower for the server, but it makes it impossible to know if they read it.
44
	if (isset($_SERVER['HTTP_X_MOZ']) && $_SERVER['HTTP_X_MOZ'] == 'prefetch')
45
	{
46
		ob_end_clean();
47
		header('HTTP/1.1 403 Prefetch Forbidden');
48
		die;
49
	}
50
51
	// How much are we sticking on each page?
52
	$context['messages_per_page'] = empty($modSettings['disableCustomPerPage']) && !empty($options['messages_per_page']) ? $options['messages_per_page'] : $modSettings['defaultMaxMessages'];
53
54
	// Let's do some work on what to search index.
55 View Code Duplication
	if (count($_GET) > 2)
56
		foreach ($_GET as $k => $v)
57
		{
58
			if (!in_array($k, array('topic', 'board', 'start', session_name())))
59
				$context['robot_no_index'] = true;
60
		}
61
62 View Code Duplication
	if (!empty($_REQUEST['start']) && (!is_numeric($_REQUEST['start']) || $_REQUEST['start'] % $context['messages_per_page'] != 0))
63
		$context['robot_no_index'] = true;
64
65
	// Find the previous or next topic.  Make a fuss if there are no more.
66
	if (isset($_REQUEST['prev_next']) && ($_REQUEST['prev_next'] == 'prev' || $_REQUEST['prev_next'] == 'next'))
67
	{
68
		// No use in calculating the next topic if there's only one.
69
		if ($board_info['num_topics'] > 1)
70
		{
71
			// Just prepare some variables that are used in the query.
72
			$gt_lt = $_REQUEST['prev_next'] == 'prev' ? '>' : '<';
73
			$order = $_REQUEST['prev_next'] == 'prev' ? '' : ' DESC';
74
75
			$request = $smcFunc['db_query']('', '
76
				SELECT t2.id_topic
77
				FROM {db_prefix}topics AS t
78
					INNER JOIN {db_prefix}topics AS t2 ON (
79
					(t2.id_last_msg ' . $gt_lt . ' t.id_last_msg AND t2.is_sticky ' . $gt_lt . '= t.is_sticky) OR t2.is_sticky ' . $gt_lt . ' t.is_sticky)
80
				WHERE t.id_topic = {int:current_topic}
81
					AND t2.id_board = {int:current_board}' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : '
82
					AND (t2.approved = {int:is_approved} OR (t2.id_member_started != {int:id_member_started} AND t2.id_member_started = {int:current_member}))') . '
83
				ORDER BY t2.is_sticky' . $order . ', t2.id_last_msg' . $order . '
84
				LIMIT 1',
85
				array(
86
					'current_board' => $board,
87
					'current_member' => $user_info['id'],
88
					'current_topic' => $topic,
89
					'is_approved' => 1,
90
					'id_member_started' => 0,
91
				)
92
			);
93
94
			// No more left.
95
			if ($smcFunc['db_num_rows']($request) == 0)
96
			{
97
				$smcFunc['db_free_result']($request);
98
99
				// Roll over - if we're going prev, get the last - otherwise the first.
100
				$request = $smcFunc['db_query']('', '
101
					SELECT id_topic
102
					FROM {db_prefix}topics
103
					WHERE id_board = {int:current_board}' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : '
104
						AND (approved = {int:is_approved} OR (id_member_started != {int:id_member_started} AND id_member_started = {int:current_member}))') . '
105
					ORDER BY is_sticky' . $order . ', id_last_msg' . $order . '
106
					LIMIT 1',
107
					array(
108
						'current_board' => $board,
109
						'current_member' => $user_info['id'],
110
						'is_approved' => 1,
111
						'id_member_started' => 0,
112
					)
113
				);
114
			}
115
116
			// Now you can be sure $topic is the id_topic to view.
117
			list ($topic) = $smcFunc['db_fetch_row']($request);
118
			$smcFunc['db_free_result']($request);
119
120
			$context['current_topic'] = $topic;
121
		}
122
123
		// Go to the newest message on this topic.
124
		$_REQUEST['start'] = 'new';
125
	}
126
127
	// Add 1 to the number of views of this topic (except for robots).
128
	if (!$user_info['possibly_robot'] && (empty($_SESSION['last_read_topic']) || $_SESSION['last_read_topic'] != $topic))
129
	{
130
		$smcFunc['db_query']('', '
131
			UPDATE {db_prefix}topics
132
			SET num_views = num_views + 1
133
			WHERE id_topic = {int:current_topic}',
134
			array(
135
				'current_topic' => $topic,
136
			)
137
		);
138
139
		$_SESSION['last_read_topic'] = $topic;
140
	}
141
142
	$topic_parameters = array(
143
		'current_member' => $user_info['id'],
144
		'current_topic' => $topic,
145
		'current_board' => $board,
146
	);
147
	$topic_selects = array();
148
	$topic_tables = array();
149
	$context['topicinfo'] = array();
150
	call_integration_hook('integrate_display_topic', array(&$topic_selects, &$topic_tables, &$topic_parameters));
151
152
	// @todo Why isn't this cached?
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
153
	// @todo if we get id_board in this query and cache it, we can save a query on posting
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
154
	// Get all the important topic info.
155
	$request = $smcFunc['db_query']('', '
156
		SELECT
157
			t.num_replies, t.num_views, t.locked, ms.subject, t.is_sticky, t.id_poll,
158
			t.id_member_started, t.id_first_msg, t.id_last_msg, t.approved, t.unapproved_posts, t.id_redirect_topic,
159
			COALESCE(mem.real_name, ms.poster_name) AS topic_started_name, ms.poster_time AS topic_started_time,
160
			' . ($user_info['is_guest'] ? 't.id_last_msg + 1' : 'COALESCE(lt.id_msg, lmr.id_msg, -1) + 1') . ' AS new_from
161
			' . (!empty($board_info['recycle']) ? ', id_previous_board, id_previous_topic' : '') . '
162
			' . (!empty($topic_selects) ? (', ' . implode(', ', $topic_selects)) : '') . '
163
			' . (!$user_info['is_guest'] ? ', COALESCE(lt.unwatched, 0) as unwatched' : '') . '
164
		FROM {db_prefix}topics AS t
165
			INNER JOIN {db_prefix}messages AS ms ON (ms.id_msg = t.id_first_msg)
166
			LEFT JOIN {db_prefix}members AS mem on (mem.id_member = t.id_member_started)' . ($user_info['is_guest'] ? '' : '
167
			LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = {int:current_topic} AND lt.id_member = {int:current_member})
168
			LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = {int:current_board} AND lmr.id_member = {int:current_member})') . '
169
			' . (!empty($topic_tables) ? implode("\n\t", $topic_tables) : '') . '
170
		WHERE t.id_topic = {int:current_topic}
171
		LIMIT 1',
172
			$topic_parameters
173
	);
174
175
	if ($smcFunc['db_num_rows']($request) == 0)
176
		fatal_lang_error('not_a_topic', false, 404);
0 ignored issues
show
Documentation introduced by
404 is of type integer, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
177
	$context['topicinfo'] = $smcFunc['db_fetch_assoc']($request);
178
	$smcFunc['db_free_result']($request);
179
180
	// Is this a moved or merged topic that we are redirecting to?
181
	if (!empty($context['topicinfo']['id_redirect_topic']))
182
	{
183
		// Mark this as read...
184
		if (!$user_info['is_guest'] && $context['topicinfo']['new_from'] != $context['topicinfo']['id_first_msg'])
185
		{
186
			// Mark this as read first
187
			$smcFunc['db_insert']($context['topicinfo']['new_from'] == 0 ? 'ignore' : 'replace',
188
				'{db_prefix}log_topics',
189
				array(
190
					'id_member' => 'int', 'id_topic' => 'int', 'id_msg' => 'int', 'unwatched' => 'int',
191
				),
192
				array(
193
					$user_info['id'], $topic, $context['topicinfo']['id_first_msg'], $context['topicinfo']['unwatched'],
194
				),
195
				array('id_member', 'id_topic')
196
			);
197
		}
198
		redirectexit('topic=' . $context['topicinfo']['id_redirect_topic'] . '.0', false, true);
199
	}
200
201
	// Short-cut to know if this user can see unapproved messages.
202
	$approve_posts = (allowedTo('approve_posts') || $context['topicinfo']['id_member_started'] == $user_info['id']);
203
204
	$context['real_num_replies'] = $context['num_replies'] = $context['topicinfo']['num_replies'];
205
	$context['topic_started_time'] = timeformat($context['topicinfo']['topic_started_time']);
206
	$context['topic_started_timestamp'] = $context['topicinfo']['topic_started_time'];
207
	$context['topic_poster_name'] = $context['topicinfo']['topic_started_name'];
208
	$context['topic_first_message'] = $context['topicinfo']['id_first_msg'];
209
	$context['topic_last_message'] = $context['topicinfo']['id_last_msg'];
210
	$context['topic_unwatched'] = isset($context['topicinfo']['unwatched']) ? $context['topicinfo']['unwatched'] : 0;
211
212
	// Add up unapproved replies to get real number of replies...
213
	if ($modSettings['postmod_active'] && $approve_posts)
214
		$context['real_num_replies'] += $context['topicinfo']['unapproved_posts'] - ($context['topicinfo']['approved'] ? 0 : 1);
215
216
	// If this topic has unapproved posts, we need to work out how many posts the user can see, for page indexing.
217
	if ($modSettings['postmod_active'] && $context['topicinfo']['unapproved_posts'] && !$user_info['is_guest'] && !$approve_posts)
218
	{
219
		$request = $smcFunc['db_query']('', '
220
			SELECT COUNT(id_member) AS my_unapproved_posts
221
			FROM {db_prefix}messages
222
			WHERE id_topic = {int:current_topic}
223
				AND id_member = {int:current_member}
224
				AND approved = 0',
225
			array(
226
				'current_topic' => $topic,
227
				'current_member' => $user_info['id'],
228
			)
229
		);
230
		list ($myUnapprovedPosts) = $smcFunc['db_fetch_row']($request);
231
		$smcFunc['db_free_result']($request);
232
233
		$context['total_visible_posts'] = $context['num_replies'] + $myUnapprovedPosts + ($context['topicinfo']['approved'] ? 1 : 0);
234
	}
235 View Code Duplication
	elseif ($user_info['is_guest'])
236
		$context['total_visible_posts'] = $context['num_replies'] + ($context['topicinfo']['approved'] ? 1 : 0);
237 View Code Duplication
	else
238
		$context['total_visible_posts'] = $context['num_replies'] + $context['topicinfo']['unapproved_posts'] + ($context['topicinfo']['approved'] ? 1 : 0);
239
240
	// The start isn't a number; it's information about what to do, where to go.
241
	if (!is_numeric($_REQUEST['start']))
242
	{
243
		// Redirect to the page and post with new messages, originally by Omar Bazavilvazo.
244
		if ($_REQUEST['start'] == 'new')
245
		{
246
			// Guests automatically go to the last post.
247
			if ($user_info['is_guest'])
248
			{
249
				$context['start_from'] = $context['total_visible_posts'] - 1;
250
				$_REQUEST['start'] = empty($options['view_newest_first']) ? $context['start_from'] : 0;
251
			}
252 View Code Duplication
			else
253
			{
254
				// Find the earliest unread message in the topic. (the use of topics here is just for both tables.)
255
				$request = $smcFunc['db_query']('', '
256
					SELECT COALESCE(lt.id_msg, lmr.id_msg, -1) + 1 AS new_from
257
					FROM {db_prefix}topics AS t
258
						LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = {int:current_topic} AND lt.id_member = {int:current_member})
259
						LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = {int:current_board} AND lmr.id_member = {int:current_member})
260
					WHERE t.id_topic = {int:current_topic}
261
					LIMIT 1',
262
					array(
263
						'current_board' => $board,
264
						'current_member' => $user_info['id'],
265
						'current_topic' => $topic,
266
					)
267
				);
268
				list ($new_from) = $smcFunc['db_fetch_row']($request);
269
				$smcFunc['db_free_result']($request);
270
271
				// Fall through to the next if statement.
272
				$_REQUEST['start'] = 'msg' . $new_from;
273
			}
274
		}
275
276
		// Start from a certain time index, not a message.
277
		if (substr($_REQUEST['start'], 0, 4) == 'from')
278
		{
279
			$timestamp = (int) substr($_REQUEST['start'], 4);
280
			if ($timestamp === 0)
281
				$_REQUEST['start'] = 0;
282
			else
283
			{
284
				// Find the number of messages posted before said time...
285
				$request = $smcFunc['db_query']('', '
286
					SELECT COUNT(*)
287
					FROM {db_prefix}messages
288
					WHERE poster_time < {int:timestamp}
289
						AND id_topic = {int:current_topic}' . ($modSettings['postmod_active'] && $context['topicinfo']['unapproved_posts'] && !allowedTo('approve_posts') ? '
290
						AND (approved = {int:is_approved}' . ($user_info['is_guest'] ? '' : ' OR id_member = {int:current_member}') . ')' : ''),
291
					array(
292
						'current_topic' => $topic,
293
						'current_member' => $user_info['id'],
294
						'is_approved' => 1,
295
						'timestamp' => $timestamp,
296
					)
297
				);
298
				list ($context['start_from']) = $smcFunc['db_fetch_row']($request);
299
				$smcFunc['db_free_result']($request);
300
301
				// Handle view_newest_first options, and get the correct start value.
302
				$_REQUEST['start'] = empty($options['view_newest_first']) ? $context['start_from'] : $context['total_visible_posts'] - $context['start_from'] - 1;
303
			}
304
		}
305
306
		// Link to a message...
307
		elseif (substr($_REQUEST['start'], 0, 3) == 'msg')
308
		{
309
			$virtual_msg = (int) substr($_REQUEST['start'], 3);
310
			if (!$context['topicinfo']['unapproved_posts'] && $virtual_msg >= $context['topicinfo']['id_last_msg'])
311
				$context['start_from'] = $context['total_visible_posts'] - 1;
312
			elseif (!$context['topicinfo']['unapproved_posts'] && $virtual_msg <= $context['topicinfo']['id_first_msg'])
313
				$context['start_from'] = 0;
314
			else
315
			{
316
				// Find the start value for that message......
317
				$request = $smcFunc['db_query']('', '
318
					SELECT COUNT(*)
319
					FROM {db_prefix}messages
320
					WHERE id_msg < {int:virtual_msg}
321
						AND id_topic = {int:current_topic}' . ($modSettings['postmod_active'] && $context['topicinfo']['unapproved_posts'] && !allowedTo('approve_posts') ? '
322
						AND (approved = {int:is_approved}' . ($user_info['is_guest'] ? '' : ' OR id_member = {int:current_member}') . ')' : ''),
323
					array(
324
						'current_member' => $user_info['id'],
325
						'current_topic' => $topic,
326
						'virtual_msg' => $virtual_msg,
327
						'is_approved' => 1,
328
						'no_member' => 0,
329
					)
330
				);
331
				list ($context['start_from']) = $smcFunc['db_fetch_row']($request);
332
				$smcFunc['db_free_result']($request);
333
			}
334
335
			// We need to reverse the start as well in this case.
336
			$_REQUEST['start'] = empty($options['view_newest_first']) ? $context['start_from'] : $context['total_visible_posts'] - $context['start_from'] - 1;
337
		}
338
	}
339
340
	// Create a previous next string if the selected theme has it as a selected option.
341
	$context['previous_next'] = $modSettings['enablePreviousNext'] ? '<a href="' . $scripturl . '?topic=' . $topic . '.0;prev_next=prev#new">' . $txt['previous_next_back'] . '</a> - <a href="' . $scripturl . '?topic=' . $topic . '.0;prev_next=next#new">' . $txt['previous_next_forward'] . '</a>' : '';
342
343
	// Check if spellchecking is both enabled and actually working. (for quick reply.)
344
	$context['show_spellchecking'] = !empty($modSettings['enableSpellChecking']) && (function_exists('pspell_new') || (function_exists('enchant_broker_init') && ($txt['lang_charset'] == 'UTF-8' || function_exists('iconv'))));
345
346
	// Do we need to show the visual verification image?
347
	$context['require_verification'] = !$user_info['is_mod'] && !$user_info['is_admin'] && !empty($modSettings['posts_require_captcha']) && ($user_info['posts'] < $modSettings['posts_require_captcha'] || ($user_info['is_guest'] && $modSettings['posts_require_captcha'] == -1));
348 View Code Duplication
	if ($context['require_verification'])
349
	{
350
		require_once($sourcedir . '/Subs-Editor.php');
351
		$verificationOptions = array(
352
			'id' => 'post',
353
		);
354
		$context['require_verification'] = create_control_verification($verificationOptions);
355
		$context['visual_verification_id'] = $verificationOptions['id'];
356
	}
357
358
	// Are we showing signatures - or disabled fields?
359
	$context['signature_enabled'] = substr($modSettings['signature_settings'], 0, 1) == 1;
360
	$context['disabled_fields'] = isset($modSettings['disabled_profile_fields']) ? array_flip(explode(',', $modSettings['disabled_profile_fields'])) : array();
361
362
	// Censor the title...
363
	censorText($context['topicinfo']['subject']);
364
	$context['page_title'] = $context['topicinfo']['subject'];
365
366
	// Default this topic to not marked for notifications... of course...
367
	$context['is_marked_notify'] = false;
368
369
	// Did we report a post to a moderator just now?
370
	$context['report_sent'] = isset($_GET['reportsent']);
371
372
	// Let's get nosey, who is viewing this topic?
373 View Code Duplication
	if (!empty($settings['display_who_viewing']))
374
	{
375
		// Start out with no one at all viewing it.
376
		$context['view_members'] = array();
377
		$context['view_members_list'] = array();
378
		$context['view_num_hidden'] = 0;
379
380
		// Search for members who have this topic set in their GET data.
381
		$request = $smcFunc['db_query']('', '
382
			SELECT
383
				lo.id_member, lo.log_time, mem.real_name, mem.member_name, mem.show_online,
384
				mg.online_color, mg.id_group, mg.group_name
385
			FROM {db_prefix}log_online AS lo
386
				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lo.id_member)
387
				LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = CASE WHEN mem.id_group = {int:reg_id_group} THEN mem.id_post_group ELSE mem.id_group END)
388
			WHERE INSTR(lo.url, {string:in_url_string}) > 0 OR lo.session = {string:session}',
389
			array(
390
				'reg_id_group' => 0,
391
				'in_url_string' => '"topic":' . $topic,
392
				'session' => $user_info['is_guest'] ? 'ip' . $user_info['ip'] : session_id(),
393
			)
394
		);
395
		while ($row = $smcFunc['db_fetch_assoc']($request))
396
		{
397
			if (empty($row['id_member']))
398
				continue;
399
400
			if (!empty($row['online_color']))
401
				$link = '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '" style="color: ' . $row['online_color'] . ';">' . $row['real_name'] . '</a>';
402
			else
403
				$link = '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['real_name'] . '</a>';
404
405
			$is_buddy = in_array($row['id_member'], $user_info['buddies']);
406
			if ($is_buddy)
407
				$link = '<strong>' . $link . '</strong>';
408
409
			// Add them both to the list and to the more detailed list.
410
			if (!empty($row['show_online']) || allowedTo('moderate_forum'))
411
				$context['view_members_list'][$row['log_time'] . $row['member_name']] = empty($row['show_online']) ? '<em>' . $link . '</em>' : $link;
412
			$context['view_members'][$row['log_time'] . $row['member_name']] = array(
413
				'id' => $row['id_member'],
414
				'username' => $row['member_name'],
415
				'name' => $row['real_name'],
416
				'group' => $row['id_group'],
417
				'href' => $scripturl . '?action=profile;u=' . $row['id_member'],
418
				'link' => $link,
419
				'is_buddy' => $is_buddy,
420
				'hidden' => empty($row['show_online']),
421
			);
422
423
			if (empty($row['show_online']))
424
				$context['view_num_hidden']++;
425
		}
426
427
		// The number of guests is equal to the rows minus the ones we actually used ;).
428
		$context['view_num_guests'] = $smcFunc['db_num_rows']($request) - count($context['view_members']);
429
		$smcFunc['db_free_result']($request);
430
431
		// Sort the list.
432
		krsort($context['view_members']);
433
		krsort($context['view_members_list']);
434
	}
435
436
	// If all is set, but not allowed... just unset it.
437
	$can_show_all = !empty($modSettings['enableAllMessages']) && $context['total_visible_posts'] > $context['messages_per_page'] && $context['total_visible_posts'] < $modSettings['enableAllMessages'];
438
	if (isset($_REQUEST['all']) && !$can_show_all)
439
		unset($_REQUEST['all']);
440
	// Otherwise, it must be allowed... so pretend start was -1.
441
	elseif (isset($_REQUEST['all']))
442
		$_REQUEST['start'] = -1;
443
444
	// Construct the page index, allowing for the .START method...
445
	$context['page_index'] = constructPageIndex($scripturl . '?topic=' . $topic . '.%1$d', $_REQUEST['start'], $context['total_visible_posts'], $context['messages_per_page'], true);
446
	$context['start'] = $_REQUEST['start'];
447
448
	// This is information about which page is current, and which page we're on - in case you don't like the constructed page index. (again, wireles..)
449
	$context['page_info'] = array(
450
		'current_page' => $_REQUEST['start'] / $context['messages_per_page'] + 1,
451
		'num_pages' => floor(($context['total_visible_posts'] - 1) / $context['messages_per_page']) + 1,
452
	);
453
454
	// Figure out all the link to the next/prev/first/last/etc.
455
	if (!($can_show_all && isset($_REQUEST['all'])))
456
	{
457
		$context['links'] = array(
458
			'first' => $_REQUEST['start'] >= $context['messages_per_page'] ? $scripturl . '?topic=' . $topic . '.0' : '',
459
			'prev' => $_REQUEST['start'] >= $context['messages_per_page'] ? $scripturl . '?topic=' . $topic . '.' . ($_REQUEST['start'] - $context['messages_per_page']) : '',
460
			'next' => $_REQUEST['start'] + $context['messages_per_page'] < $context['total_visible_posts'] ? $scripturl . '?topic=' . $topic . '.' . ($_REQUEST['start'] + $context['messages_per_page']) : '',
461
			'last' => $_REQUEST['start'] + $context['messages_per_page'] < $context['total_visible_posts'] ? $scripturl . '?topic=' . $topic . '.' . (floor($context['total_visible_posts'] / $context['messages_per_page']) * $context['messages_per_page']) : '',
462
			'up' => $scripturl . '?board=' . $board . '.0'
463
		);
464
	}
465
466
	// If they are viewing all the posts, show all the posts, otherwise limit the number.
467
	if ($can_show_all)
468
	{
469
		if (isset($_REQUEST['all']))
470
		{
471
			// No limit! (actually, there is a limit, but...)
472
			$context['messages_per_page'] = -1;
473
			$context['page_index'] .= empty($modSettings['compactTopicPagesEnable']) ? '<strong>' . $txt['all'] . '</strong> ' : '[<strong>' . $txt['all'] . '</strong>] ';
474
475
			// Set start back to 0...
476
			$_REQUEST['start'] = 0;
477
		}
478
		// They aren't using it, but the *option* is there, at least.
479
		else
480
			$context['page_index'] .= '&nbsp;<a href="' . $scripturl . '?topic=' . $topic . '.0;all">' . $txt['all'] . '</a> ';
481
	}
482
483
	// Build the link tree.
484
	$context['linktree'][] = array(
485
		'url' => $scripturl . '?topic=' . $topic . '.0',
486
		'name' => $context['topicinfo']['subject'],
487
	);
488
489
	// Build a list of this board's moderators.
490
	$context['moderators'] = &$board_info['moderators'];
491
	$context['moderator_groups'] = &$board_info['moderator_groups'];
492
	$context['link_moderators'] = array();
493 View Code Duplication
	if (!empty($board_info['moderators']))
494
	{
495
		// Add a link for each moderator...
496
		foreach ($board_info['moderators'] as $mod)
497
			$context['link_moderators'][] = '<a href="' . $scripturl . '?action=profile;u=' . $mod['id'] . '" title="' . $txt['board_moderator'] . '">' . $mod['name'] . '</a>';
498
	}
499 View Code Duplication
	if (!empty($board_info['moderator_groups']))
500
	{
501
		// Add a link for each moderator group as well...
502
		foreach ($board_info['moderator_groups'] as $mod_group)
503
			$context['link_moderators'][] = '<a href="' . $scripturl . '?action=groups;sa=viewmemberes;group=' . $mod_group['id'] . '" title="' . $txt['board_moderator'] . '">' . $mod_group['name'] . '</a>';
504
	}
505
506 View Code Duplication
	if (!empty($context['link_moderators']))
507
	{
508
		// And show it after the board's name.
509
		$context['linktree'][count($context['linktree']) - 2]['extra_after'] = '<span class="board_moderators">(' . (count($context['link_moderators']) == 1 ? $txt['moderator'] : $txt['moderators']) . ': ' . implode(', ', $context['link_moderators']) . ')</span>';
510
	}
511
512
	// Information about the current topic...
513
	$context['is_locked'] = $context['topicinfo']['locked'];
514
	$context['is_sticky'] = $context['topicinfo']['is_sticky'];
515
	$context['is_approved'] = $context['topicinfo']['approved'];
516
	$context['is_poll'] = $context['topicinfo']['id_poll'] > 0 && $modSettings['pollMode'] == '1' && allowedTo('poll_view');
517
518
	// Did this user start the topic or not?
519
	$context['user']['started'] = $user_info['id'] == $context['topicinfo']['id_member_started'] && !$user_info['is_guest'];
520
	$context['topic_starter_id'] = $context['topicinfo']['id_member_started'];
521
522
	// Set the topic's information for the template.
523
	$context['subject'] = $context['topicinfo']['subject'];
524
	$context['num_views'] = comma_format($context['topicinfo']['num_views']);
525
	$context['num_views_text'] = $context['num_views'] == 1 ? $txt['read_one_time'] : sprintf($txt['read_many_times'], $context['num_views']);
526
	$context['mark_unread_time'] = !empty($virtual_msg) ? $virtual_msg : $context['topicinfo']['new_from'];
527
528
	// Set a canonical URL for this page.
529
	$context['canonical_url'] = $scripturl . '?topic=' . $topic . '.' . ($can_show_all ? '0;all' : $context['start']);
530
531
	// For quick reply we need a response prefix in the default forum language.
532 View Code Duplication
	if (!isset($context['response_prefix']) && !($context['response_prefix'] = cache_get_data('response_prefix', 600)))
533
	{
534
		if ($language === $user_info['language'])
535
			$context['response_prefix'] = $txt['response_prefix'];
536
		else
537
		{
538
			loadLanguage('index', $language, false);
539
			$context['response_prefix'] = $txt['response_prefix'];
540
			loadLanguage('index');
541
		}
542
		cache_put_data('response_prefix', $context['response_prefix'], 600);
543
	}
544
545
	// If we want to show event information in the topic, prepare the data.
546
	if (allowedTo('calendar_view') && !empty($modSettings['cal_showInTopic']) && !empty($modSettings['cal_enabled']))
547
	{
548
		require_once($sourcedir . '/Subs-Calendar.php');
549
550
		// Any calendar information for this topic?
551
		$request = $smcFunc['db_query']('', '
552
			SELECT cal.id_event, cal.start_date, cal.end_date, cal.title, cal.id_member, mem.real_name, cal.start_time, cal.end_time, cal.timezone, cal.location
553
			FROM {db_prefix}calendar AS cal
554
				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = cal.id_member)
555
			WHERE cal.id_topic = {int:current_topic}
556
			ORDER BY start_date',
557
			array(
558
				'current_topic' => $topic,
559
			)
560
		);
561
		$context['linked_calendar_events'] = array();
562
		while ($row = $smcFunc['db_fetch_assoc']($request))
563
		{
564
			// Get the various time and date properties for this event
565
			list($start, $end, $allday, $span, $tz, $tz_abbrev) = buildEventDatetimes($row);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $tz. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
566
567
			// Sanity check
568 View Code Duplication
			if (!empty($start['error_count']) || !empty($start['warning_count']) || !empty($end['error_count']) || !empty($end['warning_count']))
569
				continue;
570
571
			$linked_calendar_event = array(
572
				'id' => $row['id_event'],
573
				'title' => $row['title'],
574
				'can_edit' => allowedTo('calendar_edit_any') || ($row['id_member'] == $user_info['id'] && allowedTo('calendar_edit_own')),
575
				'modify_href' => $scripturl . '?action=post;msg=' . $context['topicinfo']['id_first_msg'] . ';topic=' . $topic . '.0;calendar;eventid=' . $row['id_event'] . ';' . $context['session_var'] . '=' . $context['session_id'],
576
				'can_export' => allowedTo('calendar_edit_any') || ($row['id_member'] == $user_info['id'] && allowedTo('calendar_edit_own')),
577
				'export_href' => $scripturl . '?action=calendar;sa=ical;eventid=' . $row['id_event'] . ';' . $context['session_var'] . '=' . $context['session_id'],
578
				'year' => $start['year'],
579
				'month' => $start['month'],
580
				'day' => $start['day'],
581
				'hour' => !$allday ? $start['hour'] : null,
582
				'minute' => !$allday ? $start['minute'] : null,
583
				'second' => !$allday ? $start['second'] : null,
584
				'start_date' => $row['start_date'],
585
				'start_date_local' => $start['date_local'],
586
				'start_date_orig' => $start['date_orig'],
587
				'start_time' => !$allday ? $row['start_time'] : null,
588
				'start_time_local' => !$allday ? $start['time_local'] : null,
589
				'start_time_orig' => !$allday ? $start['time_orig'] : null,
590
				'start_timestamp' => $start['timestamp'],
591
				'start_iso_gmdate' => $start['iso_gmdate'],
592
				'end_year' => $end['year'],
593
				'end_month' => $end['month'],
594
				'end_day' => $end['day'],
595
				'end_hour' => !$allday ? $end['hour'] : null,
596
				'end_minute' => !$allday ? $end['minute'] : null,
597
				'end_second' => !$allday ? $end['second'] : null,
598
				'end_date' => $row['end_date'],
599
				'end_date_local' => $end['date_local'],
600
				'end_date_orig' => $end['date_orig'],
601
				'end_time' => !$allday ? $row['end_time'] : null,
602
				'end_time_local' => !$allday ? $end['time_local'] : null,
603
				'end_time_orig' => !$allday ? $end['time_orig'] : null,
604
				'end_timestamp' => $end['timestamp'],
605
				'end_iso_gmdate' => $end['iso_gmdate'],
606
				'allday' => $allday,
607
				'tz' => !$allday ? $tz : null,
608
				'tz_abbrev' => !$allday ? $tz_abbrev : null,
609
				'span' => $span,
610
				'location' => $row['location'],
611
				'is_last' => false
612
			);
613
614
			$context['linked_calendar_events'][] = $linked_calendar_event;
615
		}
616
		$smcFunc['db_free_result']($request);
617
618 View Code Duplication
		if (!empty($context['linked_calendar_events']))
619
			$context['linked_calendar_events'][count($context['linked_calendar_events']) - 1]['is_last'] = true;
620
	}
621
622
	// Create the poll info if it exists.
623
	if ($context['is_poll'])
624
	{
625
		// Get the question and if it's locked.
626
		$request = $smcFunc['db_query']('', '
627
			SELECT
628
				p.question, p.voting_locked, p.hide_results, p.expire_time, p.max_votes, p.change_vote,
629
				p.guest_vote, p.id_member, COALESCE(mem.real_name, p.poster_name) AS poster_name, p.num_guest_voters, p.reset_poll
630
			FROM {db_prefix}polls AS p
631
				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = p.id_member)
632
			WHERE p.id_poll = {int:id_poll}
633
			LIMIT 1',
634
			array(
635
				'id_poll' => $context['topicinfo']['id_poll'],
636
			)
637
		);
638
		$pollinfo = $smcFunc['db_fetch_assoc']($request);
639
		$smcFunc['db_free_result']($request);
640
641
		$request = $smcFunc['db_query']('', '
642
			SELECT COUNT(DISTINCT id_member) AS total
643
			FROM {db_prefix}log_polls
644
			WHERE id_poll = {int:id_poll}
645
				AND id_member != {int:not_guest}',
646
			array(
647
				'id_poll' => $context['topicinfo']['id_poll'],
648
				'not_guest' => 0,
649
			)
650
		);
651
		list ($pollinfo['total']) = $smcFunc['db_fetch_row']($request);
652
		$smcFunc['db_free_result']($request);
653
654
		// Total voters needs to include guest voters
655
		$pollinfo['total'] += $pollinfo['num_guest_voters'];
656
657
		// Get all the options, and calculate the total votes.
658
		$request = $smcFunc['db_query']('', '
659
			SELECT pc.id_choice, pc.label, pc.votes, COALESCE(lp.id_choice, -1) AS voted_this
660
			FROM {db_prefix}poll_choices AS pc
661
				LEFT JOIN {db_prefix}log_polls AS lp ON (lp.id_choice = pc.id_choice AND lp.id_poll = {int:id_poll} AND lp.id_member = {int:current_member} AND lp.id_member != {int:not_guest})
662
			WHERE pc.id_poll = {int:id_poll}',
663
			array(
664
				'current_member' => $user_info['id'],
665
				'id_poll' => $context['topicinfo']['id_poll'],
666
				'not_guest' => 0,
667
			)
668
		);
669
		$pollOptions = array();
670
		$realtotal = 0;
671
		$pollinfo['has_voted'] = false;
672 View Code Duplication
		while ($row = $smcFunc['db_fetch_assoc']($request))
673
		{
674
			censorText($row['label']);
675
			$pollOptions[$row['id_choice']] = $row;
676
			$realtotal += $row['votes'];
677
			$pollinfo['has_voted'] |= $row['voted_this'] != -1;
678
		}
679
		$smcFunc['db_free_result']($request);
680
681
		// Got we multi choice?
682
		if ($pollinfo['max_votes'] > 1)
683
			$realtotal = $pollinfo['total'];
684
685
		// If this is a guest we need to do our best to work out if they have voted, and what they voted for.
686 View Code Duplication
		if ($user_info['is_guest'] && $pollinfo['guest_vote'] && allowedTo('poll_vote'))
687
		{
688
			if (!empty($_COOKIE['guest_poll_vote']) && preg_match('~^[0-9,;]+$~', $_COOKIE['guest_poll_vote']) && strpos($_COOKIE['guest_poll_vote'], ';' . $context['topicinfo']['id_poll'] . ',') !== false)
689
			{
690
				// ;id,timestamp,[vote,vote...]; etc
0 ignored issues
show
Unused Code Comprehensibility introduced by
54% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
691
				$guestinfo = explode(';', $_COOKIE['guest_poll_vote']);
692
				// Find the poll we're after.
693
				foreach ($guestinfo as $i => $guestvoted)
694
				{
695
					$guestvoted = explode(',', $guestvoted);
696
					if ($guestvoted[0] == $context['topicinfo']['id_poll'])
697
						break;
698
				}
699
				// Has the poll been reset since guest voted?
700
				if ($pollinfo['reset_poll'] > $guestvoted[1])
701
				{
702
					// Remove the poll info from the cookie to allow guest to vote again
703
					unset($guestinfo[$i]);
704
					if (!empty($guestinfo))
705
						$_COOKIE['guest_poll_vote'] = ';' . implode(';', $guestinfo);
706
					else
707
						unset($_COOKIE['guest_poll_vote']);
708
				}
709
				else
710
				{
711
					// What did they vote for?
712
					unset($guestvoted[0], $guestvoted[1]);
713
					foreach ($pollOptions as $choice => $details)
714
					{
715
						$pollOptions[$choice]['voted_this'] = in_array($choice, $guestvoted) ? 1 : -1;
0 ignored issues
show
Bug introduced by
The variable $guestvoted does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
716
						$pollinfo['has_voted'] |= $pollOptions[$choice]['voted_this'] != -1;
717
					}
718
					unset($choice, $details, $guestvoted);
719
				}
720
				unset($guestinfo, $guestvoted, $i);
721
			}
722
		}
723
724
		// Set up the basic poll information.
725
		$context['poll'] = array(
726
			'id' => $context['topicinfo']['id_poll'],
727
			'image' => 'normal_' . (empty($pollinfo['voting_locked']) ? 'poll' : 'locked_poll'),
728
			'question' => parse_bbc($pollinfo['question']),
729
			'total_votes' => $pollinfo['total'],
730
			'change_vote' => !empty($pollinfo['change_vote']),
731
			'is_locked' => !empty($pollinfo['voting_locked']),
732
			'options' => array(),
733
			'lock' => allowedTo('poll_lock_any') || ($context['user']['started'] && allowedTo('poll_lock_own')),
734
			'edit' => allowedTo('poll_edit_any') || ($context['user']['started'] && allowedTo('poll_edit_own')),
735
			'remove' => allowedTo('poll_remove_any') || ($context['user']['started'] && allowedTo('poll_remove_own')),
736
			'allowed_warning' => $pollinfo['max_votes'] > 1 ? sprintf($txt['poll_options6'], min(count($pollOptions), $pollinfo['max_votes'])) : '',
737
			'is_expired' => !empty($pollinfo['expire_time']) && $pollinfo['expire_time'] < time(),
738
			'expire_time' => !empty($pollinfo['expire_time']) ? timeformat($pollinfo['expire_time']) : 0,
739
			'has_voted' => !empty($pollinfo['has_voted']),
740
			'starter' => array(
741
				'id' => $pollinfo['id_member'],
742
				'name' => $row['poster_name'],
743
				'href' => $pollinfo['id_member'] == 0 ? '' : $scripturl . '?action=profile;u=' . $pollinfo['id_member'],
744
				'link' => $pollinfo['id_member'] == 0 ? $row['poster_name'] : '<a href="' . $scripturl . '?action=profile;u=' . $pollinfo['id_member'] . '">' . $row['poster_name'] . '</a>'
745
			)
746
		);
747
748
		// Make the lock, edit and remove permissions defined above more directly accessible.
749
		$context['allow_lock_poll'] = $context['poll']['lock'];
750
		$context['allow_edit_poll'] = $context['poll']['edit'];
751
		$context['can_remove_poll'] = $context['poll']['remove'];
752
753
		// You're allowed to vote if:
754
		// 1. the poll did not expire, and
755
		// 2. you're either not a guest OR guest voting is enabled... and
756
		// 3. you're not trying to view the results, and
757
		// 4. the poll is not locked, and
758
		// 5. you have the proper permissions, and
759
		// 6. you haven't already voted before.
760
		$context['allow_vote'] = !$context['poll']['is_expired'] && (!$user_info['is_guest'] || ($pollinfo['guest_vote'] && allowedTo('poll_vote'))) && empty($pollinfo['voting_locked']) && allowedTo('poll_vote') && !$context['poll']['has_voted'];
761
762
		// You're allowed to view the results if:
763
		// 1. you're just a super-nice-guy, or
764
		// 2. anyone can see them (hide_results == 0), or
765
		// 3. you can see them after you voted (hide_results == 1), or
766
		// 4. you've waited long enough for the poll to expire. (whether hide_results is 1 or 2.)
767
		$context['allow_results_view'] = allowedTo('moderate_board') || $pollinfo['hide_results'] == 0 || ($pollinfo['hide_results'] == 1 && $context['poll']['has_voted']) || $context['poll']['is_expired'];
768
769
		// Show the results if:
770
		// 1. You're allowed to see them (see above), and
771
		// 2. $_REQUEST['viewresults'] or $_REQUEST['viewResults'] is set
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
772
		$context['poll']['show_results'] = $context['allow_results_view'] && (isset($_REQUEST['viewresults']) || isset($_REQUEST['viewResults']));
773
774
		// Show the button if:
775
		// 1. You can vote in the poll (see above), and
776
		// 2. Results are visible to everyone (hidden = 0), and
777
		// 3. You aren't already viewing the results
778
		$context['show_view_results_button'] = $context['allow_vote'] && $context['allow_results_view'] && !$context['poll']['show_results'];
779
780
		// You're allowed to change your vote if:
781
		// 1. the poll did not expire, and
782
		// 2. you're not a guest... and
783
		// 3. the poll is not locked, and
784
		// 4. you have the proper permissions, and
785
		// 5. you have already voted, and
786
		// 6. the poll creator has said you can!
787
		$context['allow_change_vote'] = !$context['poll']['is_expired'] && !$user_info['is_guest'] && empty($pollinfo['voting_locked']) && allowedTo('poll_vote') && $context['poll']['has_voted'] && $context['poll']['change_vote'];
788
789
		// You're allowed to return to voting options if:
790
		// 1. you are (still) allowed to vote.
791
		// 2. you are currently seeing the results.
792
		$context['allow_return_vote'] = $context['allow_vote'] && $context['poll']['show_results'];
793
794
		// Calculate the percentages and bar lengths...
795
		$divisor = $realtotal == 0 ? 1 : $realtotal;
796
797
		// Determine if a decimal point is needed in order for the options to add to 100%.
798
		$precision = $realtotal == 100 ? 0 : 1;
799
800
		// Now look through each option, and...
801
		foreach ($pollOptions as $i => $option)
802
		{
803
			// First calculate the percentage, and then the width of the bar...
804
			$bar = round(($option['votes'] * 100) / $divisor, $precision);
805
			$barWide = $bar == 0 ? 1 : floor(($bar * 8) / 3);
806
807
			// Now add it to the poll's contextual theme data.
808
			$context['poll']['options'][$i] = array(
809
				'id' => 'options-' . $i,
810
				'percent' => $bar,
811
				'votes' => $option['votes'],
812
				'voted_this' => $option['voted_this'] != -1,
813
				'bar_ndt' => $bar > 0 ? '<div class="bar" style="width: ' . $bar . '%;"></div>' : '',
814
				'bar_width' => $barWide,
815
				'option' => parse_bbc($option['label']),
816
				'vote_button' => '<input type="' . ($pollinfo['max_votes'] > 1 ? 'checkbox' : 'radio') . '" name="options[]" id="options-' . $i . '" value="' . $i . '">'
817
			);
818
		}
819
820
		// Build the poll moderation button array.
821
		$context['poll_buttons'] = array();
822
823 View Code Duplication
		if ($context['allow_return_vote'])
824
			$context['poll_buttons']['vote'] = array('text' => 'poll_return_vote', 'image' => 'poll_options.png', 'url' => $scripturl . '?topic=' . $context['current_topic'] . '.' . $context['start']);
825
826 View Code Duplication
		if ($context['show_view_results_button'])
827
			$context['poll_buttons']['results'] = array('text' => 'poll_results', 'image' => 'poll_results.png', 'url' => $scripturl . '?topic=' . $context['current_topic'] . '.' . $context['start'] . ';viewresults');
828
829 View Code Duplication
		if ($context['allow_change_vote'])
830
			$context['poll_buttons']['change_vote'] = array('text' => 'poll_change_vote', 'image' => 'poll_change_vote.png', 'url' => $scripturl . '?action=vote;topic=' . $context['current_topic'] . '.' . $context['start'] . ';poll=' . $context['poll']['id'] . ';' . $context['session_var'] . '=' . $context['session_id']);
831
832 View Code Duplication
		if ($context['allow_lock_poll'])
833
			$context['poll_buttons']['lock'] = array('text' => (!$context['poll']['is_locked'] ? 'poll_lock' : 'poll_unlock'), 'image' => 'poll_lock.png', 'url' => $scripturl . '?action=lockvoting;topic=' . $context['current_topic'] . '.' . $context['start'] . ';' . $context['session_var'] . '=' . $context['session_id']);
834
835 View Code Duplication
		if ($context['allow_edit_poll'])
836
			$context['poll_buttons']['edit'] = array('text' => 'poll_edit', 'image' => 'poll_edit.png', 'url' => $scripturl . '?action=editpoll;topic=' . $context['current_topic'] . '.' . $context['start']);
837
838
		if ($context['can_remove_poll'])
839
			$context['poll_buttons']['remove_poll'] = array('text' => 'poll_remove', 'image' => 'admin_remove_poll.png', 'custom' => 'data-confirm="' . $txt['poll_remove_warn'] . '"', 'class' => 'you_sure', 'url' => $scripturl . '?action=removepoll;topic=' . $context['current_topic'] . '.' . $context['start'] . ';' . $context['session_var'] . '=' . $context['session_id']);
840
841
		// Allow mods to add additional buttons here
842
		call_integration_hook('integrate_poll_buttons');
843
	}
844
845
	$start = $_REQUEST['start'];
846
847
	// Check if we can use the seek method to speed things up
848
	if (isset($_SESSION['page_topic']) && $_SESSION['page_topic'] == $topic)
849
	{
850
		// User moved to the next page
851
		if (isset($_SESSION['page_next_start']) && $_SESSION['page_next_start'] == $start)
852
		{
853
			$start_char = 'M';
854
			$page_id = $_SESSION['page_last_id'];
855
		}
856
		// User moved to the previous page
857
		elseif (isset($_SESSION['page_before_start']) && $_SESSION['page_before_start'] == $start)
858
		{
859
			$start_char = 'L';
860
			$page_id = $_SESSION['page_first_id'];
861
		}
862
		// User refreshed the current page
863
		elseif (isset($_SESSION['page_current_start']) && $_SESSION['page_current_start'] == $start)
864
		{
865
			$start_char = 'C';
866
			$page_id = $_SESSION['page_first_id'];
867
		}
868
	}
869
	// Special case start page
870
	elseif ($start == 0)
871
	{
872
		$start_char = 'C';
873
		$page_id = $context['topicinfo']['id_first_msg'];
874
	}
875
	else
876
		$start_char = null;
877
878
	$limit = $context['messages_per_page'];
879
880
	$messages = array();
881
	$all_posters = array();
882
883
	if (isset($start_char))
884
	{
885
		$firstIndex = 0;
886
887
		if ($start_char === 'M' or $start_char === 'C')
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
888
		{
889
			$ascending = true;
890
			$page_operator = '>=';
891
		}
892
		else
893
		{
894
			$ascending = false;
895
			$page_operator = '<=';
896
		}
897
898
		if ($start_char === 'C')
899
			$limit_seek = $limit;
900
		else
901
			$limit_seek  = $limit + 1;
902
903
		$request = $smcFunc['db_query']('', '
904
			SELECT id_msg, id_member, approved
905
			FROM {db_prefix}messages
906
			WHERE id_topic = {int:current_topic}
907
			AND id_msg '. $page_operator . ' {int:page_id}'. (!$modSettings['postmod_active'] || $approve_posts ? '' : '
908
			AND (approved = {int:is_approved}' . ($user_info['is_guest'] ? '' : ' OR id_member = {int:current_member}') . ')') . '
909
			ORDER BY id_msg ' . ($ascending ? '' : 'DESC') . ($context['messages_per_page'] == -1 ? '' : '
910
			LIMIT {int:limit}'),
911
			array(
912
				'current_member' => $user_info['id'],
913
				'current_topic' => $topic,
914
				'is_approved' => 1,
915
				'blank_id_member' => 0,
916
				'limit' => $limit_seek,
917
				'page_id' => $page_id,
0 ignored issues
show
Bug introduced by
The variable $page_id does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
918
			)
919
		);
920
921
		$found_msg = false;
922
923
		// Fallback
924
		if ($smcFunc['db_num_rows']($request) < 1)
925
			unset($start_char);
926
		else
927
		{
928
			while ($row = $smcFunc['db_fetch_assoc']($request))
929
			{
930
				// Check if the start msg is in our result
931
				if ($row['id_msg'] == $page_id)
932
					$found_msg = true;
933
934
				// Skip the the start msg if we not in mode C
935
				if ($start_char === 'C' || $row['id_msg'] != $page_id)
936
				{
937
					if (!empty($row['id_member']))
938
						$all_posters[$row['id_msg']] = $row['id_member'];
939
940
					$messages[] = $row['id_msg'];
941
				}
942
			}
943
944
			// page_id not found? -> fallback
945
			if (!$found_msg)
946
			{
947
				$messages = array();
948
				$all_posters = array();
949
				unset($start_char);
950
			}
951
		}
952
953
		// Before Page bring in the right order
954
		if (!empty($start_char) && $start_char === 'L')
955
			krsort($messages);
956
	}
957
958
	// Jump to page
959
	if (empty($start_char))
960
	{
961
		// Calculate the fastest way to get the messages!
962
		$ascending = empty($options['view_newest_first']);
963
		$firstIndex = 0;
964
		if ($start >= $context['total_visible_posts'] / 2 && $context['messages_per_page'] != -1)
965
		{
966
			$ascending = !$ascending;
967
			$limit = $context['total_visible_posts'] <= $start + $limit ? $context['total_visible_posts'] - $start : $limit;
968
			$start = $context['total_visible_posts'] <= $start + $limit ? 0 : $context['total_visible_posts'] - $start - $limit;
969
			$firstIndex = $limit - 1;
970
		}
971
972
		// Get each post and poster in this topic.
973
		$request = $smcFunc['db_query']('', '
974
			SELECT id_msg, id_member, approved
975
			FROM {db_prefix}messages
976
			WHERE id_topic = {int:current_topic}' . (!$modSettings['postmod_active'] || $approve_posts ? '' : '
977
			AND (approved = {int:is_approved}' . ($user_info['is_guest'] ? '' : ' OR id_member = {int:current_member}') . ')') . '
978
			ORDER BY id_msg ' . ($ascending ? '' : 'DESC') . ($context['messages_per_page'] == -1 ? '' : '
979
			LIMIT {int:start}, {int:max}'),
980
			array(
981
				'current_member' => $user_info['id'],
982
				'current_topic' => $topic,
983
				'is_approved' => 1,
984
				'blank_id_member' => 0,
985
				'start' => $start,
986
				'max' => $limit,
987
			)
988
		);
989
990
		while ($row = $smcFunc['db_fetch_assoc']($request))
991
		{
992
			if (!empty($row['id_member']))
993
				$all_posters[$row['id_msg']] = $row['id_member'];
994
			$messages[] = $row['id_msg'];
995
		}
996
997
		// Sort the messages into the correct display order
998
		if (!$ascending)
999
			sort($messages);
1000
	}
1001
1002
	// Remember the paging data for next time
1003
	$_SESSION['page_first_id'] = $messages[0];
1004
	$_SESSION['page_before_start'] = $_REQUEST['start'] - $limit;
1005
	$_SESSION['page_last_id'] = end($messages);
1006
	$_SESSION['page_next_start'] = $_REQUEST['start'] + $limit;
1007
	$_SESSION['page_current_start'] = $_REQUEST['start'];
1008
	$_SESSION['page_topic'] = $topic;
1009
1010
	$smcFunc['db_free_result']($request);
1011
	$posters = array_unique($all_posters);
1012
1013
	call_integration_hook('integrate_display_message_list', array(&$messages, &$posters));
1014
1015
	// Guests can't mark topics read or for notifications, just can't sorry.
1016
	if (!$user_info['is_guest'] && !empty($messages))
1017
	{
1018
		$mark_at_msg = max($messages);
1019
		if ($mark_at_msg >= $context['topicinfo']['id_last_msg'])
1020
			$mark_at_msg = $modSettings['maxMsgID'];
1021
		if ($mark_at_msg >= $context['topicinfo']['new_from'])
1022
		{
1023
			$smcFunc['db_insert']($context['topicinfo']['new_from'] == 0 ? 'ignore' : 'replace',
1024
				'{db_prefix}log_topics',
1025
				array(
1026
					'id_member' => 'int', 'id_topic' => 'int', 'id_msg' => 'int', 'unwatched' => 'int',
1027
				),
1028
				array(
1029
					$user_info['id'], $topic, $mark_at_msg, $context['topicinfo']['unwatched'],
1030
				),
1031
				array('id_member', 'id_topic')
1032
			);
1033
		}
1034
1035
		// Check for notifications on this topic OR board.
1036
		$request = $smcFunc['db_query']('', '
1037
			SELECT sent, id_topic
1038
			FROM {db_prefix}log_notify
1039
			WHERE (id_topic = {int:current_topic} OR id_board = {int:current_board})
1040
				AND id_member = {int:current_member}
1041
			LIMIT 2',
1042
			array(
1043
				'current_board' => $board,
1044
				'current_member' => $user_info['id'],
1045
				'current_topic' => $topic,
1046
			)
1047
		);
1048
		$do_once = true;
1049
		while ($row = $smcFunc['db_fetch_assoc']($request))
1050
		{
1051
			// Find if this topic is marked for notification...
1052
			if (!empty($row['id_topic']))
1053
				$context['is_marked_notify'] = true;
1054
1055
			// Only do this once, but mark the notifications as "not sent yet" for next time.
1056
			if (!empty($row['sent']) && $do_once)
1057
			{
1058
				$smcFunc['db_query']('', '
1059
					UPDATE {db_prefix}log_notify
1060
					SET sent = {int:is_not_sent}
1061
					WHERE (id_topic = {int:current_topic} OR id_board = {int:current_board})
1062
						AND id_member = {int:current_member}',
1063
					array(
1064
						'current_board' => $board,
1065
						'current_member' => $user_info['id'],
1066
						'current_topic' => $topic,
1067
						'is_not_sent' => 0,
1068
					)
1069
				);
1070
				$do_once = false;
1071
			}
1072
		}
1073
1074
		// Have we recently cached the number of new topics in this board, and it's still a lot?
1075
		if (isset($_REQUEST['topicseen']) && isset($_SESSION['topicseen_cache'][$board]) && $_SESSION['topicseen_cache'][$board] > 5)
1076
			$_SESSION['topicseen_cache'][$board]--;
1077
		// Mark board as seen if this is the only new topic.
1078
		elseif (isset($_REQUEST['topicseen']))
1079
		{
1080
			// Use the mark read tables... and the last visit to figure out if this should be read or not.
1081
			$request = $smcFunc['db_query']('', '
1082
				SELECT COUNT(*)
1083
				FROM {db_prefix}topics AS t
1084
					LEFT JOIN {db_prefix}log_boards AS lb ON (lb.id_board = {int:current_board} AND lb.id_member = {int:current_member})
1085
					LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = t.id_topic AND lt.id_member = {int:current_member})
1086
				WHERE t.id_board = {int:current_board}
1087
					AND t.id_last_msg > COALESCE(lb.id_msg, 0)
1088
					AND t.id_last_msg > COALESCE(lt.id_msg, 0)' . (empty($_SESSION['id_msg_last_visit']) ? '' : '
1089
					AND t.id_last_msg > {int:id_msg_last_visit}'),
1090
				array(
1091
					'current_board' => $board,
1092
					'current_member' => $user_info['id'],
1093
					'id_msg_last_visit' => (int) $_SESSION['id_msg_last_visit'],
1094
				)
1095
			);
1096
			list ($numNewTopics) = $smcFunc['db_fetch_row']($request);
1097
			$smcFunc['db_free_result']($request);
1098
1099
			// If there're no real new topics in this board, mark the board as seen.
1100
			if (empty($numNewTopics))
1101
				$_REQUEST['boardseen'] = true;
1102
			else
1103
				$_SESSION['topicseen_cache'][$board] = $numNewTopics;
1104
		}
1105
		// Probably one less topic - maybe not, but even if we decrease this too fast it will only make us look more often.
1106
		elseif (isset($_SESSION['topicseen_cache'][$board]))
1107
			$_SESSION['topicseen_cache'][$board]--;
1108
1109
		// Mark board as seen if we came using last post link from BoardIndex. (or other places...)
1110
		if (isset($_REQUEST['boardseen']))
1111
		{
1112
			$smcFunc['db_insert']('replace',
1113
				'{db_prefix}log_boards',
1114
				array('id_msg' => 'int', 'id_member' => 'int', 'id_board' => 'int'),
1115
				array($modSettings['maxMsgID'], $user_info['id'], $board),
1116
				array('id_member', 'id_board')
1117
			);
1118
		}
1119
	}
1120
1121
	// Get notification preferences
1122
	$context['topicinfo']['notify_prefs'] = array();
1123
	if (!empty($user_info['id']))
1124
	{
1125
		require_once($sourcedir . '/Subs-Notify.php');
1126
		$prefs = getNotifyPrefs($user_info['id'], array('topic_notify', 'topic_notify_' . $context['current_topic']), true);
1127
		$pref = !empty($prefs[$user_info['id']]) && $context['is_marked_notify'] ? $prefs[$user_info['id']] : array();
1128
		$context['topicinfo']['notify_prefs'] = array(
1129
			'is_custom' => isset($pref['topic_notify_' . $topic]),
1130
			'pref' => isset($pref['topic_notify_' . $context['current_topic']]) ? $pref['topic_notify_' . $context['current_topic']] : (!empty($pref['topic_notify']) ? $pref['topic_notify'] : 0),
1131
		);
1132
	}
1133
1134
	$context['topic_notification'] = !empty($user_info['id']) ? $context['topicinfo']['notify_prefs'] : array();
1135
	// 0 => unwatched, 1 => normal, 2 => receive alerts, 3 => receive emails
0 ignored issues
show
Unused Code Comprehensibility introduced by
36% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1136
	$context['topic_notification_mode'] = !$user_info['is_guest'] ? ($context['topic_unwatched'] ? 0 : ($context['topicinfo']['notify_prefs']['pref'] & 0x02 ? 3 : ($context['topicinfo']['notify_prefs']['pref'] & 0x01 ? 2 : 1))) : 0;
1137
1138
	$context['loaded_attachments'] = array();
1139
1140
	// If there _are_ messages here... (probably an error otherwise :!)
1141
	if (!empty($messages))
1142
	{
1143
		// Fetch attachments.
1144
		if (!empty($modSettings['attachmentEnable']) && allowedTo('view_attachments'))
1145
		{
1146
			$request = $smcFunc['db_query']('', '
1147
				SELECT
1148
					a.id_attach, a.id_folder, a.id_msg, a.filename, a.file_hash, COALESCE(a.size, 0) AS filesize, a.downloads, a.approved,
1149
					a.width, a.height' . (empty($modSettings['attachmentShowImages']) || empty($modSettings['attachmentThumbnails']) ? '' : ',
1150
					COALESCE(thumb.id_attach, 0) AS id_thumb, thumb.width AS thumb_width, thumb.height AS thumb_height') . '
1151
				FROM {db_prefix}attachments AS a' . (empty($modSettings['attachmentShowImages']) || empty($modSettings['attachmentThumbnails']) ? '' : '
1152
					LEFT JOIN {db_prefix}attachments AS thumb ON (thumb.id_attach = a.id_thumb)') . '
1153
				WHERE a.id_msg IN ({array_int:message_list})
1154
					AND a.attachment_type = {int:attachment_type}',
1155
				array(
1156
					'message_list' => $messages,
1157
					'attachment_type' => 0,
1158
					'is_approved' => 1,
1159
				)
1160
			);
1161
			$temp = array();
1162
			while ($row = $smcFunc['db_fetch_assoc']($request))
1163
			{
1164 View Code Duplication
				if (!$row['approved'] && $modSettings['postmod_active'] && !allowedTo('approve_posts') && (!isset($all_posters[$row['id_msg']]) || $all_posters[$row['id_msg']] != $user_info['id']))
1165
					continue;
1166
1167
				$temp[$row['id_attach']] = $row;
1168
				$temp[$row['id_attach']]['topic'] = $topic;
1169
				$temp[$row['id_attach']]['board'] = $board;
1170
1171
				if (!isset($context['loaded_attachments'][$row['id_msg']]))
1172
					$context['loaded_attachments'][$row['id_msg']] = array();
1173
			}
1174
			$smcFunc['db_free_result']($request);
1175
1176
			// This is better than sorting it with the query...
1177
			ksort($temp);
1178
1179
			foreach ($temp as $row)
1180
				$context['loaded_attachments'][$row['id_msg']][] = $row;
1181
		}
1182
1183
		$msg_parameters = array(
1184
			'message_list' => $messages,
1185
			'new_from' => $context['topicinfo']['new_from'],
1186
		);
1187
		$msg_selects = array();
1188
		$msg_tables = array();
1189
		call_integration_hook('integrate_query_message', array(&$msg_selects, &$msg_tables, &$msg_parameters));
1190
1191
		// What?  It's not like it *couldn't* be only guests in this topic...
1192
		loadMemberData($posters);
1193
		$messages_request = $smcFunc['db_query']('', '
1194
			SELECT
1195
				id_msg, icon, subject, poster_time, poster_ip, id_member, modified_time, modified_name, modified_reason, body,
1196
				smileys_enabled, poster_name, poster_email, approved, likes,
1197
				id_msg_modified < {int:new_from} AS is_read
1198
				' . (!empty($msg_selects) ? (', ' . implode(', ', $msg_selects)) : '') . '
1199
			FROM {db_prefix}messages
1200
				' . (!empty($msg_tables) ? implode("\n\t", $msg_tables) : '') . '
1201
			WHERE id_msg IN ({array_int:message_list})
1202
			ORDER BY id_msg' . (empty($options['view_newest_first']) ? '' : ' DESC'),
1203
			$msg_parameters
1204
		);
1205
1206
		// And the likes
1207
		if (!empty($modSettings['enable_likes']))
1208
			$context['my_likes'] = $context['user']['is_guest'] ? array() : prepareLikesContext($topic);
1209
1210
		// Go to the last message if the given time is beyond the time of the last message.
1211 View Code Duplication
		if (isset($context['start_from']) && $context['start_from'] >= $context['topicinfo']['num_replies'])
1212
			$context['start_from'] = $context['topicinfo']['num_replies'];
1213
1214
		// Since the anchor information is needed on the top of the page we load these variables beforehand.
1215
		$context['first_message'] = isset($messages[$firstIndex]) ? $messages[$firstIndex] : $messages[0];
0 ignored issues
show
Bug introduced by
The variable $firstIndex does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1216
		if (empty($options['view_newest_first']))
1217
			$context['first_new_message'] = isset($context['start_from']) && $_REQUEST['start'] == $context['start_from'];
1218 View Code Duplication
		else
1219
			$context['first_new_message'] = isset($context['start_from']) && $_REQUEST['start'] == $context['topicinfo']['num_replies'] - $context['start_from'];
1220
	}
1221
	else
1222
	{
1223
		$messages_request = false;
1224
		$context['first_message'] = 0;
1225
		$context['first_new_message'] = false;
1226
1227
		$context['likes'] = array();
1228
	}
1229
1230
	$context['jump_to'] = array(
1231
		'label' => addslashes(un_htmlspecialchars($txt['jump_to'])),
1232
		'board_name' => $smcFunc['htmlspecialchars'](strtr(strip_tags($board_info['name']), array('&amp;' => '&'))),
1233
		'child_level' => $board_info['child_level'],
1234
	);
1235
1236
	// Set the callback.  (do you REALIZE how much memory all the messages would take?!?)
1237
	// This will be called from the template.
1238
	$context['get_message'] = 'prepareDisplayContext';
1239
1240
	// Now set all the wonderful, wonderful permissions... like moderation ones...
1241
	$common_permissions = array(
1242
		'can_approve' => 'approve_posts',
1243
		'can_ban' => 'manage_bans',
1244
		'can_sticky' => 'make_sticky',
1245
		'can_merge' => 'merge_any',
1246
		'can_split' => 'split_any',
1247
		'calendar_post' => 'calendar_post',
1248
		'can_send_pm' => 'pm_send',
1249
		'can_report_moderator' => 'report_any',
1250
		'can_moderate_forum' => 'moderate_forum',
1251
		'can_issue_warning' => 'issue_warning',
1252
		'can_restore_topic' => 'move_any',
1253
		'can_restore_msg' => 'move_any',
1254
		'can_like' => 'likes_like',
1255
	);
1256
	foreach ($common_permissions as $contextual => $perm)
1257
		$context[$contextual] = allowedTo($perm);
1258
1259
	// Permissions with _any/_own versions.  $context[YYY] => ZZZ_any/_own.
1260
	$anyown_permissions = array(
1261
		'can_move' => 'move',
1262
		'can_lock' => 'lock',
1263
		'can_delete' => 'remove',
1264
		'can_add_poll' => 'poll_add',
1265
		'can_remove_poll' => 'poll_remove',
1266
		'can_reply' => 'post_reply',
1267
		'can_reply_unapproved' => 'post_unapproved_replies',
1268
		'can_view_warning' => 'profile_warning',
1269
	);
1270
	foreach ($anyown_permissions as $contextual => $perm)
1271
		$context[$contextual] = allowedTo($perm . '_any') || ($context['user']['started'] && allowedTo($perm . '_own'));
1272
1273
	if (!$user_info['is_admin'] && !$modSettings['topic_move_any'])
1274
	{
1275
		// We'll use this in a minute
1276
		$boards_allowed = array_diff(boardsAllowedTo('post_new'), array($board));
1277
1278
		/* You can't move this unless you have permission
1279
			to start new topics on at least one other board */
1280
		$context['can_move'] &= count($boards_allowed) > 1;
1281
	}
1282
1283
	// If a topic is locked, you can't remove it unless it's yours and you locked it or you can lock_any
1284
	if ($context['topicinfo']['locked'])
1285
	{
1286
		$context['can_delete'] &= (($context['topicinfo']['locked'] == 1 && $context['user']['started']) || allowedTo('lock_any'));
1287
	}
1288
1289
	// Cleanup all the permissions with extra stuff...
1290
	$context['can_mark_notify'] = !$context['user']['is_guest'];
1291
	$context['calendar_post'] &= !empty($modSettings['cal_enabled']);
1292
	$context['can_add_poll'] &= $modSettings['pollMode'] == '1' && $context['topicinfo']['id_poll'] <= 0;
1293
	$context['can_remove_poll'] &= $modSettings['pollMode'] == '1' && $context['topicinfo']['id_poll'] > 0;
1294
	$context['can_reply'] &= empty($context['topicinfo']['locked']) || allowedTo('moderate_board');
1295
	$context['can_reply_unapproved'] &= $modSettings['postmod_active'] && (empty($context['topicinfo']['locked']) || allowedTo('moderate_board'));
1296
	$context['can_issue_warning'] &= $modSettings['warning_settings'][0] == 1;
1297
	// Handle approval flags...
1298
	$context['can_reply_approved'] = $context['can_reply'];
1299
	$context['can_reply'] |= $context['can_reply_unapproved'];
1300
	$context['can_quote'] = $context['can_reply'] && (empty($modSettings['disabledBBC']) || !in_array('quote', explode(',', $modSettings['disabledBBC'])));
1301
	$context['can_mark_unread'] = !$user_info['is_guest'];
1302
	$context['can_unwatch'] = !$user_info['is_guest'];
1303
	$context['can_set_notify'] = !$user_info['is_guest'];
1304
1305
	$context['can_print'] = empty($modSettings['disable_print_topic']);
1306
1307
	// Start this off for quick moderation - it will be or'd for each post.
1308
	$context['can_remove_post'] = allowedTo('delete_any') || (allowedTo('delete_replies') && $context['user']['started']);
1309
1310
	// Can restore topic?  That's if the topic is in the recycle board and has a previous restore state.
1311
	$context['can_restore_topic'] &= !empty($board_info['recycle']) && !empty($context['topicinfo']['id_previous_board']);
1312
	$context['can_restore_msg'] &= !empty($board_info['recycle']) && !empty($context['topicinfo']['id_previous_topic']);
1313
1314
	// Check if the draft functions are enabled and that they have permission to use them (for quick reply.)
1315
	$context['drafts_save'] = !empty($modSettings['drafts_post_enabled']) && allowedTo('post_draft') && $context['can_reply'];
1316
	$context['drafts_autosave'] = !empty($context['drafts_save']) && !empty($modSettings['drafts_autosave_enabled']);
1317
	if (!empty($context['drafts_save']))
1318
		loadLanguage('Drafts');
1319
1320
	// When was the last time this topic was replied to?  Should we warn them about it?
1321
	if (!empty($modSettings['oldTopicDays']) && ($context['can_reply'] || $context['can_reply_unapproved']) && empty($context['topicinfo']['is_sticky']))
1322
	{
1323
		$request = $smcFunc['db_query']('', '
1324
			SELECT poster_time
1325
			FROM {db_prefix}messages
1326
			WHERE id_msg = {int:id_last_msg}
1327
			LIMIT 1',
1328
			array(
1329
				'id_last_msg' => $context['topicinfo']['id_last_msg'],
1330
			)
1331
		);
1332
1333
		list ($lastPostTime) = $smcFunc['db_fetch_row']($request);
1334
		$smcFunc['db_free_result']($request);
1335
1336
		$context['oldTopicError'] = $lastPostTime + $modSettings['oldTopicDays'] * 86400 < time();
1337
	}
1338
1339
	// You can't link an existing topic to the calendar unless you can modify the first post...
1340
	$context['calendar_post'] &= allowedTo('modify_any') || (allowedTo('modify_own') && $context['user']['started']);
1341
1342
	// Load up the "double post" sequencing magic.
1343
	checkSubmitOnce('register');
1344
	$context['name'] = isset($_SESSION['guest_name']) ? $_SESSION['guest_name'] : '';
1345
	$context['email'] = isset($_SESSION['guest_email']) ? $_SESSION['guest_email'] : '';
1346
	// Needed for the editor and message icons.
1347
	require_once($sourcedir . '/Subs-Editor.php');
1348
1349
	// Now create the editor.
1350
	$editorOptions = array(
1351
		'id' => 'quickReply',
1352
		'value' => '',
1353
		'disable_smiley_box' => empty($options['use_editor_quick_reply']),
1354
		'labels' => array(
1355
			'post_button' => $txt['post'],
1356
		),
1357
		// add height and width for the editor
1358
		'height' => '250px',
1359
		'width' => '100%',
1360
		// We do HTML preview here.
1361
		'preview_type' => 1,
1362
		// This is required
1363
		'required' => true,
1364
	);
1365
	create_control_richedit($editorOptions);
1366
1367
	// Store the ID.
1368
	$context['post_box_name'] = $editorOptions['id'];
1369
1370
	// Set a flag so the sub template knows what to do...
1371
	$context['show_bbc'] = !empty($options['use_editor_quick_reply']);
1372
	$modSettings['disable_wysiwyg'] = !empty($options['use_editor_quick_reply']);
1373
	$context['attached'] = '';
1374
	$context['make_poll'] = isset($_REQUEST['poll']);
1375
1376
	// Message icons - customized icons are off?
1377
	$context['icons'] = getMessageIcons($board);
1378
1379 View Code Duplication
	if (!empty($context['icons']))
1380
		$context['icons'][count($context['icons']) - 1]['is_last'] = true;
1381
1382
	// Build the normal button array.
1383
	$context['normal_buttons'] = array();
1384
1385
	if ($context['can_reply'])
1386
		$context['normal_buttons']['reply'] = array('text' => 'reply', 'image' => 'reply.png', 'url' => $scripturl . '?action=post;topic=' . $context['current_topic'] . '.' . $context['start'] . ';last_msg=' . $context['topic_last_message'], 'active' => true);
1387
1388 View Code Duplication
	if ($context['can_add_poll'])
1389
		$context['normal_buttons']['add_poll'] = array('text' => 'add_poll', 'image' => 'add_poll.png', 'url' => $scripturl . '?action=editpoll;add;topic=' . $context['current_topic'] . '.' . $context['start']);
1390
1391 View Code Duplication
	if ($context['can_mark_unread'])
1392
		$context['normal_buttons']['mark_unread'] = array('text' => 'mark_unread', 'image' => 'markunread.png', 'url' => $scripturl . '?action=markasread;sa=topic;t=' . $context['mark_unread_time'] . ';topic=' . $context['current_topic'] . '.' . $context['start'] . ';' . $context['session_var'] . '=' . $context['session_id']);
1393
1394
	if ($context['can_print'])
1395
		$context['normal_buttons']['print'] = array('text' => 'print', 'image' => 'print.png', 'custom' => 'rel="nofollow"', 'url' => $scripturl . '?action=printpage;topic=' . $context['current_topic'] . '.0');
1396
1397
	if ($context['can_set_notify'])
1398
		$context['normal_buttons']['notify'] = array(
1399
			'text' => 'notify_topic_' . $context['topic_notification_mode'],
1400
			'sub_buttons' => array(
1401
				array(
1402
					'test' => 'can_unwatch',
1403
					'text' => 'notify_topic_0',
1404
					'url' => $scripturl . '?action=notifytopic;topic=' . $context['current_topic'] . ';mode=0;' . $context['session_var'] . '=' . $context['session_id'],
1405
				),
1406
				array(
1407
					'text' => 'notify_topic_1',
1408
					'url' => $scripturl . '?action=notifytopic;topic=' . $context['current_topic'] . ';mode=1;' . $context['session_var'] . '=' . $context['session_id'],
1409
				),
1410
				array(
1411
					'text' => 'notify_topic_2',
1412
					'url' => $scripturl . '?action=notifytopic;topic=' . $context['current_topic'] . ';mode=2;' . $context['session_var'] . '=' . $context['session_id'],
1413
				),
1414
				array(
1415
					'text' => 'notify_topic_3',
1416
					'url' => $scripturl . '?action=notifytopic;topic=' . $context['current_topic'] . ';mode=3;' . $context['session_var'] . '=' . $context['session_id'],
1417
				),
1418
			),
1419
		);
1420
1421
	// Build the mod button array
1422
	$context['mod_buttons'] = array();
1423
1424 View Code Duplication
	if ($context['can_move'])
1425
		$context['mod_buttons']['move'] = array('text' => 'move_topic', 'image' => 'admin_move.png', 'url' => $scripturl . '?action=movetopic;current_board=' . $context['current_board'] . ';topic=' . $context['current_topic'] . '.0');
1426
1427 View Code Duplication
	if ($context['can_delete'])
1428
		$context['mod_buttons']['delete'] = array('text' => 'remove_topic', 'image' => 'admin_rem.png', 'custom' => 'data-confirm="' . $txt['are_sure_remove_topic'] . '"', 'class' => 'you_sure', 'url' => $scripturl . '?action=removetopic2;topic=' . $context['current_topic'] . '.0;' . $context['session_var'] . '=' . $context['session_id']);
1429
1430 View Code Duplication
	if ($context['can_lock'])
1431
		$context['mod_buttons']['lock'] = array('text' => empty($context['is_locked']) ? 'set_lock' : 'set_unlock', 'image' => 'admin_lock.png', 'url' => $scripturl . '?action=lock;topic=' . $context['current_topic'] . '.' . $context['start'] . ';' . $context['session_var'] . '=' . $context['session_id']);
1432
1433 View Code Duplication
	if ($context['can_sticky'])
1434
		$context['mod_buttons']['sticky'] = array('text' => empty($context['is_sticky']) ? 'set_sticky' : 'set_nonsticky', 'image' => 'admin_sticky.png', 'url' => $scripturl . '?action=sticky;topic=' . $context['current_topic'] . '.' . $context['start'] . ';' . $context['session_var'] . '=' . $context['session_id']);
1435
1436 View Code Duplication
	if ($context['can_merge'])
1437
		$context['mod_buttons']['merge'] = array('text' => 'merge', 'image' => 'merge.png', 'url' => $scripturl . '?action=mergetopics;board=' . $context['current_board'] . '.0;from=' . $context['current_topic']);
1438
1439 View Code Duplication
	if ($context['calendar_post'])
1440
		$context['mod_buttons']['calendar'] = array('text' => 'calendar_link', 'image' => 'linktocal.png', 'url' => $scripturl . '?action=post;calendar;msg=' . $context['topic_first_message'] . ';topic=' . $context['current_topic'] . '.0');
1441
1442
	// Restore topic. eh?  No monkey business.
1443 View Code Duplication
	if ($context['can_restore_topic'])
1444
		$context['mod_buttons']['restore_topic'] = array('text' => 'restore_topic', 'image' => '', 'url' => $scripturl . '?action=restoretopic;topics=' . $context['current_topic'] . ';' . $context['session_var'] . '=' . $context['session_id']);
1445
1446
	// Show a message in case a recently posted message became unapproved.
1447
	$context['becomesUnapproved'] = !empty($_SESSION['becomesUnapproved']) ? true : false;
1448
1449
	// Don't want to show this forever...
1450
	if ($context['becomesUnapproved'])
1451
		unset($_SESSION['becomesUnapproved']);
1452
1453
	// Allow adding new mod buttons easily.
1454
	// Note: $context['normal_buttons'] and $context['mod_buttons'] are added for backward compatibility with 2.0, but are deprecated and should not be used
1455
	call_integration_hook('integrate_display_buttons', array(&$context['normal_buttons']));
1456
	// Note: integrate_mod_buttons is no more necessary and deprecated, but is kept for backward compatibility with 2.0
1457
	call_integration_hook('integrate_mod_buttons', array(&$context['mod_buttons']));
1458
1459
	// Load the drafts js file
1460
	if ($context['drafts_autosave'])
1461
		loadJavaScriptFile('drafts.js', array('defer' => false), 'smf_drafts');
1462
1463
	// Spellcheck
1464
	if ($context['show_spellchecking'])
1465
		loadJavaScriptFile('spellcheck.js', array('defer' => false), 'smf_spellcheck');
1466
1467
	// topic.js
1468
	loadJavaScriptFile('topic.js', array('defer' => false), 'smf_topic');
1469
1470
	// quotedText.js
1471
	loadJavaScriptFile('quotedText.js', array('defer' => true), 'smf_quotedText');
1472
1473
	// Mentions
1474 View Code Duplication
	if (!empty($modSettings['enable_mentions']) && allowedTo('mention'))
1475
	{
1476
		loadJavaScriptFile('jquery.atwho.min.js', array('defer' => true), 'smf_atwho');
1477
		loadJavaScriptFile('jquery.caret.min.js', array('defer' => true), 'smf_caret');
1478
		loadJavaScriptFile('mentions.js', array('defer' => true), 'smf_mentions');
1479
	}
1480
}
1481
1482
/**
1483
 * Callback for the message display.
1484
 * It actually gets and prepares the message context.
1485
 * This function will start over from the beginning if reset is set to true, which is
1486
 * useful for showing an index before or after the posts.
1487
 *
1488
 * @param bool $reset Whether or not to reset the db seek pointer
1489
 * @return array A large array of contextual data for the posts
1490
 */
1491
function prepareDisplayContext($reset = false)
1492
{
1493
	global $settings, $txt, $modSettings, $scripturl, $options, $user_info, $smcFunc;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1494
	global $memberContext, $context, $messages_request, $topic, $board_info, $sourcedir;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1495
1496
	static $counter = null;
1497
1498
	// If the query returned false, bail.
1499
	if ($messages_request == false)
1500
		return false;
1501
1502
	// Remember which message this is.  (ie. reply #83)
1503
	if ($counter === null || $reset)
1504
		$counter = empty($options['view_newest_first']) ? $context['start'] : $context['total_visible_posts'] - $context['start'];
1505
1506
	// Start from the beginning...
1507
	if ($reset)
1508
		return @$smcFunc['db_data_seek']($messages_request, 0);
1509
1510
	// Attempt to get the next message.
1511
	$message = $smcFunc['db_fetch_assoc']($messages_request);
1512
	if (!$message)
1513
	{
1514
		$smcFunc['db_free_result']($messages_request);
1515
		return false;
1516
	}
1517
1518
	// $context['icon_sources'] says where each icon should come from - here we set up the ones which will always exist!
1519
	if (empty($context['icon_sources']))
1520
	{
1521
		$context['icon_sources'] = array();
1522
		foreach ($context['stable_icons'] as $icon)
1523
			$context['icon_sources'][$icon] = 'images_url';
1524
	}
1525
1526
	// Message Icon Management... check the images exist.
1527
	if (empty($modSettings['messageIconChecks_disable']))
1528
	{
1529
		// If the current icon isn't known, then we need to do something...
1530 View Code Duplication
		if (!isset($context['icon_sources'][$message['icon']]))
1531
			$context['icon_sources'][$message['icon']] = file_exists($settings['theme_dir'] . '/images/post/' . $message['icon'] . '.png') ? 'images_url' : 'default_images_url';
1532
	}
1533 View Code Duplication
	elseif (!isset($context['icon_sources'][$message['icon']]))
1534
		$context['icon_sources'][$message['icon']] = 'images_url';
1535
1536
	// If you're a lazy bum, you probably didn't give a subject...
1537
	$message['subject'] = $message['subject'] != '' ? $message['subject'] : $txt['no_subject'];
1538
1539
	// Are you allowed to remove at least a single reply?
1540
	$context['can_remove_post'] |= allowedTo('delete_own') && (empty($modSettings['edit_disable_time']) || $message['poster_time'] + $modSettings['edit_disable_time'] * 60 >= time()) && $message['id_member'] == $user_info['id'];
1541
1542
	// If the topic is locked, you might not be able to delete the post...
1543
	if ($context['is_locked'])
1544
	{
1545
		$context['can_remove_post'] &= ($context['user']['started'] && $context['is_locked'] == 1) || allowedTo('lock_any');
1546
	}
1547
1548
	// If it couldn't load, or the user was a guest.... someday may be done with a guest table.
1549
	if (!loadMemberContext($message['id_member'], true))
1550
	{
1551
		// Notice this information isn't used anywhere else....
1552
		$memberContext[$message['id_member']]['name'] = $message['poster_name'];
1553
		$memberContext[$message['id_member']]['id'] = 0;
1554
		$memberContext[$message['id_member']]['group'] = $txt['guest_title'];
1555
		$memberContext[$message['id_member']]['link'] = $message['poster_name'];
1556
		$memberContext[$message['id_member']]['email'] = $message['poster_email'];
1557
		$memberContext[$message['id_member']]['show_email'] = allowedTo('moderate_forum');
1558
		$memberContext[$message['id_member']]['is_guest'] = true;
1559
	}
1560
	else
1561
	{
1562
		// Define this here to make things a bit more readable
1563
		$can_view_warning = $context['user']['can_mod'] || allowedTo('view_warning_any') || ($message['id_member'] == $user_info['id'] && allowedTo('view_warning_own'));
1564
1565
		$memberContext[$message['id_member']]['can_view_profile'] = allowedTo('profile_view') || ($message['id_member'] == $user_info['id'] && !$user_info['is_guest']);
1566
		$memberContext[$message['id_member']]['is_topic_starter'] = $message['id_member'] == $context['topic_starter_id'];
1567
		$memberContext[$message['id_member']]['can_see_warning'] = !isset($context['disabled_fields']['warning_status']) && $memberContext[$message['id_member']]['warning_status'] && $can_view_warning;
1568
		// Show the email if it's your post...
1569
		$memberContext[$message['id_member']]['show_email'] |= ($message['id_member'] == $user_info['id']);
1570
	}
1571
1572
	$memberContext[$message['id_member']]['ip'] = inet_dtop($message['poster_ip']);
1573
	$memberContext[$message['id_member']]['show_profile_buttons'] = !empty($modSettings['show_profile_buttons']) && (!empty($memberContext[$message['id_member']]['can_view_profile']) || (!empty($memberContext[$message['id_member']]['website']['url']) && !isset($context['disabled_fields']['website'])) || $memberContext[$message['id_member']]['show_email'] || $context['can_send_pm']);
1574
1575
	// Do the censor thang.
1576
	censorText($message['body']);
1577
	censorText($message['subject']);
1578
1579
	// Run BBC interpreter on the message.
1580
	$message['body'] = parse_bbc($message['body'], $message['smileys_enabled'], $message['id_msg']);
1581
1582
	// If it's in the recycle bin we need to override whatever icon we did have.
1583
	if (!empty($board_info['recycle']))
1584
		$message['icon'] = 'recycled';
1585
1586
	require_once($sourcedir . '/Subs-Attachments.php');
1587
1588
	// Compose the memory eat- I mean message array.
1589
	$output = array(
1590
		'attachment' => loadAttachmentContext($message['id_msg'], $context['loaded_attachments']),
1591
		'id' => $message['id_msg'],
1592
		'href' => $scripturl . '?topic=' . $topic . '.msg' . $message['id_msg'] . '#msg' . $message['id_msg'],
1593
		'link' => '<a href="' . $scripturl . '?msg=' . $message['id_msg'] . '" rel="nofollow">' . $message['subject'] . '</a>',
1594
		'member' => &$memberContext[$message['id_member']],
1595
		'icon' => $message['icon'],
1596
		'icon_url' => $settings[$context['icon_sources'][$message['icon']]] . '/post/' . $message['icon'] . '.png',
1597
		'subject' => $message['subject'],
1598
		'time' => timeformat($message['poster_time']),
1599
		'timestamp' => forum_time(true, $message['poster_time']),
1600
		'counter' => $counter,
1601
		'modified' => array(
1602
			'time' => timeformat($message['modified_time']),
1603
			'timestamp' => forum_time(true, $message['modified_time']),
1604
			'name' => $message['modified_name'],
1605
			'reason' => $message['modified_reason']
1606
		),
1607
		'body' => $message['body'],
1608
		'new' => empty($message['is_read']),
1609
		'approved' => $message['approved'],
1610
		'first_new' => isset($context['start_from']) && $context['start_from'] == $counter,
1611
		'is_ignored' => !empty($modSettings['enable_buddylist']) && !empty($options['posts_apply_ignore_list']) && in_array($message['id_member'], $context['user']['ignoreusers']),
1612
		'can_approve' => !$message['approved'] && $context['can_approve'],
1613
		'can_unapprove' => !empty($modSettings['postmod_active']) && $context['can_approve'] && $message['approved'],
1614
		'can_modify' => (!$context['is_locked'] || allowedTo('moderate_board')) && (allowedTo('modify_any') || (allowedTo('modify_replies') && $context['user']['started']) || (allowedTo('modify_own') && $message['id_member'] == $user_info['id'] && (empty($modSettings['edit_disable_time']) || !$message['approved'] || $message['poster_time'] + $modSettings['edit_disable_time'] * 60 > time()))),
1615
		'can_remove' => allowedTo('delete_any') || (allowedTo('delete_replies') && $context['user']['started']) || (allowedTo('delete_own') && $message['id_member'] == $user_info['id'] && (empty($modSettings['edit_disable_time']) || $message['poster_time'] + $modSettings['edit_disable_time'] * 60 > time())),
1616
		'can_see_ip' => allowedTo('moderate_forum') || ($message['id_member'] == $user_info['id'] && !empty($user_info['id'])),
1617
		'css_class' => $message['approved'] ? 'windowbg' : 'approvebg',
1618
	);
1619
1620
	// Does the file contains any attachments? if so, change the icon.
1621
	if (!empty($output['attachment']))
1622
	{
1623
		$output['icon'] = 'clip';
1624
		$output['icon_url'] = $settings[$context['icon_sources'][$output['icon']]] . '/post/' . $output['icon'] . '.png';
1625
	}
1626
1627
	// Are likes enable?
1628 View Code Duplication
	if (!empty($modSettings['enable_likes']))
1629
		$output['likes'] = array(
1630
			'count' => $message['likes'],
1631
			'you' => in_array($message['id_msg'], $context['my_likes']),
1632
			'can_like' => !$context['user']['is_guest'] && $message['id_member'] != $context['user']['id'] && !empty($context['can_like']),
1633
		);
1634
1635
	// Is this user the message author?
1636
	$output['is_message_author'] = $message['id_member'] == $user_info['id'];
1637 View Code Duplication
	if (!empty($output['modified']['name']))
1638
		$output['modified']['last_edit_text'] = sprintf($txt['last_edit_by'], $output['modified']['time'], $output['modified']['name']);
1639
1640
	// Did they give a reason for editing?
1641 View Code Duplication
	if (!empty($output['modified']['name']) && !empty($output['modified']['reason']))
1642
		$output['modified']['last_edit_text'] .= '&nbsp;' . sprintf($txt['last_edit_reason'], $output['modified']['reason']);
1643
1644
	// Any custom profile fields?
1645
	if (!empty($memberContext[$message['id_member']]['custom_fields']))
1646
		foreach ($memberContext[$message['id_member']]['custom_fields'] as $custom)
1647
			$output['custom_fields'][$context['cust_profile_fields_placement'][$custom['placement']]][] = $custom;
1648
1649
	if (empty($options['view_newest_first']))
1650
		$counter++;
1651
1652
	else
1653
		$counter--;
1654
1655
	call_integration_hook('integrate_prepare_display_context', array(&$output, &$message, $counter));
1656
1657
	return $output;
1658
}
1659
1660
/**
1661
 * Once upon a time, this function handled downloading attachments.
1662
 * Now it's just an alias retained for the sake of backwards compatibility.
1663
 */
1664
function Download()
1665
{
1666
	global $sourcedir;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1667
	require_once($sourcedir . '/ShowAttachments.php');
1668
	showAttachment();
1669
}
1670
1671
/**
1672
 * A sort function for putting unapproved attachments first.
1673
 * @param array $a An array of info about one attachment
1674
 * @param array $b An array of info about a second attachment
1675
 * @return int -1 if $a is approved but $b isn't, 0 if both are approved/unapproved, 1 if $b is approved but a isn't
1676
 */
1677
function approved_attach_sort($a, $b)
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $a. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
Comprehensibility introduced by
Avoid variables with short names like $b. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
1678
{
1679
	if ($a['is_approved'] == $b['is_approved'])
1680
		return 0;
1681
1682
	return $a['is_approved'] > $b['is_approved'] ? -1 : 1;
1683
}
1684
1685
/**
1686
 * In-topic quick moderation.
1687
 */
1688
function QuickInTopicModeration()
1689
{
1690
	global $sourcedir, $topic, $board, $user_info, $smcFunc, $modSettings, $context;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1691
1692
	// Check the session = get or post.
1693
	checkSession('request');
1694
1695
	require_once($sourcedir . '/RemoveTopic.php');
1696
1697
	if (empty($_REQUEST['msgs']))
1698
		redirectexit('topic=' . $topic . '.' . $_REQUEST['start']);
1699
1700
	$messages = array();
1701
	foreach ($_REQUEST['msgs'] as $dummy)
1702
		$messages[] = (int) $dummy;
1703
1704
	// We are restoring messages. We handle this in another place.
1705 View Code Duplication
	if (isset($_REQUEST['restore_selected']))
1706
		redirectexit('action=restoretopic;msgs=' . implode(',', $messages) . ';' . $context['session_var'] . '=' . $context['session_id']);
1707
	if (isset($_REQUEST['split_selection']))
1708
	{
1709
		$request = $smcFunc['db_query']('', '
1710
			SELECT subject
1711
			FROM {db_prefix}messages
1712
			WHERE id_msg = {int:message}
1713
			LIMIT 1',
1714
			array(
1715
				'message' => min($messages),
1716
			)
1717
		);
1718
		list($subname) = $smcFunc['db_fetch_row']($request);
1719
		$smcFunc['db_free_result']($request);
1720
		$_SESSION['split_selection'][$topic] = $messages;
1721
		redirectexit('action=splittopics;sa=selectTopics;topic=' . $topic . '.0;subname_enc=' . urlencode($subname) . ';' . $context['session_var'] . '=' . $context['session_id']);
1722
	}
1723
1724
	// Allowed to delete any message?
1725
	if (allowedTo('delete_any'))
1726
		$allowed_all = true;
1727
	// Allowed to delete replies to their messages?
1728 View Code Duplication
	elseif (allowedTo('delete_replies'))
1729
	{
1730
		$request = $smcFunc['db_query']('', '
1731
			SELECT id_member_started
1732
			FROM {db_prefix}topics
1733
			WHERE id_topic = {int:current_topic}
1734
			LIMIT 1',
1735
			array(
1736
				'current_topic' => $topic,
1737
			)
1738
		);
1739
		list ($starter) = $smcFunc['db_fetch_row']($request);
1740
		$smcFunc['db_free_result']($request);
1741
1742
		$allowed_all = $starter == $user_info['id'];
1743
	}
1744
	else
1745
		$allowed_all = false;
1746
1747
	// Make sure they're allowed to delete their own messages, if not any.
1748
	if (!$allowed_all)
1749
		isAllowedTo('delete_own');
1750
1751
	// Allowed to remove which messages?
1752
	$request = $smcFunc['db_query']('', '
1753
		SELECT id_msg, subject, id_member, poster_time
1754
		FROM {db_prefix}messages
1755
		WHERE id_msg IN ({array_int:message_list})
1756
			AND id_topic = {int:current_topic}' . (!$allowed_all ? '
1757
			AND id_member = {int:current_member}' : '') . '
1758
		LIMIT {int:limit}',
1759
		array(
1760
			'current_member' => $user_info['id'],
1761
			'current_topic' => $topic,
1762
			'message_list' => $messages,
1763
			'limit' => count($messages),
1764
		)
1765
	);
1766
	$messages = array();
1767
	while ($row = $smcFunc['db_fetch_assoc']($request))
1768
	{
1769
		if (!$allowed_all && !empty($modSettings['edit_disable_time']) && $row['poster_time'] + $modSettings['edit_disable_time'] * 60 < time())
1770
			continue;
1771
1772
		$messages[$row['id_msg']] = array($row['subject'], $row['id_member']);
1773
	}
1774
	$smcFunc['db_free_result']($request);
1775
1776
	// Get the first message in the topic - because you can't delete that!
1777
	$request = $smcFunc['db_query']('', '
1778
		SELECT id_first_msg, id_last_msg
1779
		FROM {db_prefix}topics
1780
		WHERE id_topic = {int:current_topic}
1781
		LIMIT 1',
1782
		array(
1783
			'current_topic' => $topic,
1784
		)
1785
	);
1786
	list ($first_message, $last_message) = $smcFunc['db_fetch_row']($request);
1787
	$smcFunc['db_free_result']($request);
1788
1789
	// Delete all the messages we know they can delete. ($messages)
1790
	foreach ($messages as $message => $info)
1791
	{
1792
		// Just skip the first message - if it's not the last.
1793
		if ($message == $first_message && $message != $last_message)
1794
			continue;
1795
		// If the first message is going then don't bother going back to the topic as we're effectively deleting it.
1796
		elseif ($message == $first_message)
1797
			$topicGone = true;
1798
1799
		removeMessage($message);
1800
1801
		// Log this moderation action ;).
1802 View Code Duplication
		if (allowedTo('delete_any') && (!allowedTo('delete_own') || $info[1] != $user_info['id']))
1803
			logAction('delete', array('topic' => $topic, 'subject' => $info[0], 'member' => $info[1], 'board' => $board));
1804
	}
1805
1806
	redirectexit(!empty($topicGone) ? 'board=' . $board : 'topic=' . $topic . '.' . $_REQUEST['start']);
1807
}
1808
1809
?>