Completed
Push — release-2.1 ( aa21c4...7040ad )
by Mathias
09:20
created

Display.php ➔ Display()   F

Complexity

Conditions 339
Paths 0

Size

Total Lines 1384
Code Lines 743

Duplication

Lines 224
Ratio 16.18 %

Importance

Changes 0
Metric Value
cc 339
eloc 743
nc 0
nop 0
dl 224
loc 1384
rs 2
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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;
33
	global $options, $sourcedir, $user_info, $board_info, $topic, $board;
34
	global $messages_request, $language, $smcFunc;
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)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
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))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
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?
153
	// @todo if we get id_board in this query and cache it, we can save a query on posting
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'])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
236
		$context['total_visible_posts'] = $context['num_replies'] + ($context['topicinfo']['approved'] ? 1 : 0);
237 View Code Duplication
	else
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
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
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
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'])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
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']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
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 allowing for the .START method...
445
	$context['start'] = $_REQUEST['start'];
446
447
	// 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..)
448
	$context['page_info'] = array(
449
		'current_page' => $_REQUEST['start'] / $context['messages_per_page'] + 1,
450
		'num_pages' => floor(($context['total_visible_posts'] - 1) / $context['messages_per_page']) + 1,
451
	);
452
453
	// Figure out all the link to the next/prev/first/last/etc.
454
	if (!($can_show_all && isset($_REQUEST['all'])))
455
	{
456
		$context['links'] = array(
457
			'first' => $_REQUEST['start'] >= $context['messages_per_page'] ? $scripturl . '?topic=' . $topic . '.0' : '',
458
			'prev' => $_REQUEST['start'] >= $context['messages_per_page'] ? $scripturl . '?topic=' . $topic . '.' . ($_REQUEST['start'] - $context['messages_per_page']) : '',
459
			'next' => $_REQUEST['start'] + $context['messages_per_page'] < $context['total_visible_posts'] ? $scripturl . '?topic=' . $topic . '.' . ($_REQUEST['start'] + $context['messages_per_page']) : '',
460
			'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']) : '',
461
			'up' => $scripturl . '?board=' . $board . '.0'
462
		);
463
	}
464
465
	// Build the link tree.
466
	$context['linktree'][] = array(
467
		'url' => $scripturl . '?topic=' . $topic . '.0',
468
		'name' => $context['topicinfo']['subject'],
469
	);
470
471
	// Build a list of this board's moderators.
472
	$context['moderators'] = &$board_info['moderators'];
473
	$context['moderator_groups'] = &$board_info['moderator_groups'];
474
	$context['link_moderators'] = array();
475 View Code Duplication
	if (!empty($board_info['moderators']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
476
	{
477
		// Add a link for each moderator...
478
		foreach ($board_info['moderators'] as $mod)
479
			$context['link_moderators'][] = '<a href="' . $scripturl . '?action=profile;u=' . $mod['id'] . '" title="' . $txt['board_moderator'] . '">' . $mod['name'] . '</a>';
480
	}
481 View Code Duplication
	if (!empty($board_info['moderator_groups']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
482
	{
483
		// Add a link for each moderator group as well...
484
		foreach ($board_info['moderator_groups'] as $mod_group)
485
			$context['link_moderators'][] = '<a href="' . $scripturl . '?action=groups;sa=viewmemberes;group=' . $mod_group['id'] . '" title="' . $txt['board_moderator'] . '">' . $mod_group['name'] . '</a>';
486
	}
487
488 View Code Duplication
	if (!empty($context['link_moderators']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
489
	{
490
		// And show it after the board's name.
491
		$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>';
492
	}
493
494
	// Information about the current topic...
495
	$context['is_locked'] = $context['topicinfo']['locked'];
496
	$context['is_sticky'] = $context['topicinfo']['is_sticky'];
497
	$context['is_approved'] = $context['topicinfo']['approved'];
498
	$context['is_poll'] = $context['topicinfo']['id_poll'] > 0 && $modSettings['pollMode'] == '1' && allowedTo('poll_view');
499
500
	// Did this user start the topic or not?
501
	$context['user']['started'] = $user_info['id'] == $context['topicinfo']['id_member_started'] && !$user_info['is_guest'];
502
	$context['topic_starter_id'] = $context['topicinfo']['id_member_started'];
503
504
	// Set the topic's information for the template.
505
	$context['subject'] = $context['topicinfo']['subject'];
506
	$context['num_views'] = comma_format($context['topicinfo']['num_views']);
507
	$context['num_views_text'] = $context['num_views'] == 1 ? $txt['read_one_time'] : sprintf($txt['read_many_times'], $context['num_views']);
508
	$context['mark_unread_time'] = !empty($virtual_msg) ? $virtual_msg : $context['topicinfo']['new_from'];
509
510
	// Set a canonical URL for this page.
511
	$context['canonical_url'] = $scripturl . '?topic=' . $topic . '.' . ($can_show_all ? '0;all' : $context['start']);
512
513
	// For quick reply we need a response prefix in the default forum language.
514 View Code Duplication
	if (!isset($context['response_prefix']) && !($context['response_prefix'] = cache_get_data('response_prefix', 600)))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
515
	{
516
		if ($language === $user_info['language'])
517
			$context['response_prefix'] = $txt['response_prefix'];
518
		else
519
		{
520
			loadLanguage('index', $language, false);
521
			$context['response_prefix'] = $txt['response_prefix'];
522
			loadLanguage('index');
523
		}
524
		cache_put_data('response_prefix', $context['response_prefix'], 600);
525
	}
526
527
	// If we want to show event information in the topic, prepare the data.
528
	if (allowedTo('calendar_view') && !empty($modSettings['cal_showInTopic']) && !empty($modSettings['cal_enabled']))
529
	{
530
		require_once($sourcedir . '/Subs-Calendar.php');
531
532
		// Any calendar information for this topic?
533
		$request = $smcFunc['db_query']('', '
534
			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
535
			FROM {db_prefix}calendar AS cal
536
				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = cal.id_member)
537
			WHERE cal.id_topic = {int:current_topic}
538
			ORDER BY start_date',
539
			array(
540
				'current_topic' => $topic,
541
			)
542
		);
543
		$context['linked_calendar_events'] = array();
544
		while ($row = $smcFunc['db_fetch_assoc']($request))
545
		{
546
			// Get the various time and date properties for this event
547
			list($start, $end, $allday, $span, $tz, $tz_abbrev) = buildEventDatetimes($row);
548
549
			// Sanity check
550 View Code Duplication
			if (!empty($start['error_count']) || !empty($start['warning_count']) || !empty($end['error_count']) || !empty($end['warning_count']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
551
				continue;
552
553
			$linked_calendar_event = array(
554
				'id' => $row['id_event'],
555
				'title' => $row['title'],
556
				'can_edit' => allowedTo('calendar_edit_any') || ($row['id_member'] == $user_info['id'] && allowedTo('calendar_edit_own')),
557
				'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'],
558
				'can_export' => allowedTo('calendar_edit_any') || ($row['id_member'] == $user_info['id'] && allowedTo('calendar_edit_own')),
559
				'export_href' => $scripturl . '?action=calendar;sa=ical;eventid=' . $row['id_event'] . ';' . $context['session_var'] . '=' . $context['session_id'],
560
				'year' => $start['year'],
561
				'month' => $start['month'],
562
				'day' => $start['day'],
563
				'hour' => !$allday ? $start['hour'] : null,
564
				'minute' => !$allday ? $start['minute'] : null,
565
				'second' => !$allday ? $start['second'] : null,
566
				'start_date' => $row['start_date'],
567
				'start_date_local' => $start['date_local'],
568
				'start_date_orig' => $start['date_orig'],
569
				'start_time' => !$allday ? $row['start_time'] : null,
570
				'start_time_local' => !$allday ? $start['time_local'] : null,
571
				'start_time_orig' => !$allday ? $start['time_orig'] : null,
572
				'start_timestamp' => $start['timestamp'],
573
				'start_iso_gmdate' => $start['iso_gmdate'],
574
				'end_year' => $end['year'],
575
				'end_month' => $end['month'],
576
				'end_day' => $end['day'],
577
				'end_hour' => !$allday ? $end['hour'] : null,
578
				'end_minute' => !$allday ? $end['minute'] : null,
579
				'end_second' => !$allday ? $end['second'] : null,
580
				'end_date' => $row['end_date'],
581
				'end_date_local' => $end['date_local'],
582
				'end_date_orig' => $end['date_orig'],
583
				'end_time' => !$allday ? $row['end_time'] : null,
584
				'end_time_local' => !$allday ? $end['time_local'] : null,
585
				'end_time_orig' => !$allday ? $end['time_orig'] : null,
586
				'end_timestamp' => $end['timestamp'],
587
				'end_iso_gmdate' => $end['iso_gmdate'],
588
				'allday' => $allday,
589
				'tz' => !$allday ? $tz : null,
590
				'tz_abbrev' => !$allday ? $tz_abbrev : null,
591
				'span' => $span,
592
				'location' => $row['location'],
593
				'is_last' => false
594
			);
595
596
			$context['linked_calendar_events'][] = $linked_calendar_event;
597
		}
598
		$smcFunc['db_free_result']($request);
599
600 View Code Duplication
		if (!empty($context['linked_calendar_events']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
601
			$context['linked_calendar_events'][count($context['linked_calendar_events']) - 1]['is_last'] = true;
602
	}
603
604
	// Create the poll info if it exists.
605
	if ($context['is_poll'])
606
	{
607
		// Get the question and if it's locked.
608
		$request = $smcFunc['db_query']('', '
609
			SELECT
610
				p.question, p.voting_locked, p.hide_results, p.expire_time, p.max_votes, p.change_vote,
611
				p.guest_vote, p.id_member, COALESCE(mem.real_name, p.poster_name) AS poster_name, p.num_guest_voters, p.reset_poll
612
			FROM {db_prefix}polls AS p
613
				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = p.id_member)
614
			WHERE p.id_poll = {int:id_poll}
615
			LIMIT 1',
616
			array(
617
				'id_poll' => $context['topicinfo']['id_poll'],
618
			)
619
		);
620
		$pollinfo = $smcFunc['db_fetch_assoc']($request);
621
		$smcFunc['db_free_result']($request);
622
623
		$request = $smcFunc['db_query']('', '
624
			SELECT COUNT(DISTINCT id_member) AS total
625
			FROM {db_prefix}log_polls
626
			WHERE id_poll = {int:id_poll}
627
				AND id_member != {int:not_guest}',
628
			array(
629
				'id_poll' => $context['topicinfo']['id_poll'],
630
				'not_guest' => 0,
631
			)
632
		);
633
		list ($pollinfo['total']) = $smcFunc['db_fetch_row']($request);
634
		$smcFunc['db_free_result']($request);
635
636
		// Total voters needs to include guest voters
637
		$pollinfo['total'] += $pollinfo['num_guest_voters'];
638
639
		// Get all the options, and calculate the total votes.
640
		$request = $smcFunc['db_query']('', '
641
			SELECT pc.id_choice, pc.label, pc.votes, COALESCE(lp.id_choice, -1) AS voted_this
642
			FROM {db_prefix}poll_choices AS pc
643
				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})
644
			WHERE pc.id_poll = {int:id_poll}',
645
			array(
646
				'current_member' => $user_info['id'],
647
				'id_poll' => $context['topicinfo']['id_poll'],
648
				'not_guest' => 0,
649
			)
650
		);
651
		$pollOptions = array();
652
		$realtotal = 0;
653
		$pollinfo['has_voted'] = false;
654 View Code Duplication
		while ($row = $smcFunc['db_fetch_assoc']($request))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
655
		{
656
			censorText($row['label']);
657
			$pollOptions[$row['id_choice']] = $row;
658
			$realtotal += $row['votes'];
659
			$pollinfo['has_voted'] |= $row['voted_this'] != -1;
660
		}
661
		$smcFunc['db_free_result']($request);
662
		
663
		// Got we multi choice?
664
		if ($pollinfo['max_votes'] > 1)
665
			$realtotal = $pollinfo['total'];
666
667
		// If this is a guest we need to do our best to work out if they have voted, and what they voted for.
668 View Code Duplication
		if ($user_info['is_guest'] && $pollinfo['guest_vote'] && allowedTo('poll_vote'))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
669
		{
670
			if (!empty($_COOKIE['guest_poll_vote']) && preg_match('~^[0-9,;]+$~', $_COOKIE['guest_poll_vote']) && strpos($_COOKIE['guest_poll_vote'], ';' . $context['topicinfo']['id_poll'] . ',') !== false)
671
			{
672
				// ;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...
673
				$guestinfo = explode(';', $_COOKIE['guest_poll_vote']);
674
				// Find the poll we're after.
675
				foreach ($guestinfo as $i => $guestvoted)
676
				{
677
					$guestvoted = explode(',', $guestvoted);
678
					if ($guestvoted[0] == $context['topicinfo']['id_poll'])
679
						break;
680
				}
681
				// Has the poll been reset since guest voted?
682
				if ($pollinfo['reset_poll'] > $guestvoted[1])
683
				{
684
					// Remove the poll info from the cookie to allow guest to vote again
685
					unset($guestinfo[$i]);
686
					if (!empty($guestinfo))
687
						$_COOKIE['guest_poll_vote'] = ';' . implode(';', $guestinfo);
688
					else
689
						unset($_COOKIE['guest_poll_vote']);
690
				}
691
				else
692
				{
693
					// What did they vote for?
694
					unset($guestvoted[0], $guestvoted[1]);
695
					foreach ($pollOptions as $choice => $details)
696
					{
697
						$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...
698
						$pollinfo['has_voted'] |= $pollOptions[$choice]['voted_this'] != -1;
699
					}
700
					unset($choice, $details, $guestvoted);
701
				}
702
				unset($guestinfo, $guestvoted, $i);
703
			}
704
		}
705
706
		// Set up the basic poll information.
707
		$context['poll'] = array(
708
			'id' => $context['topicinfo']['id_poll'],
709
			'image' => 'normal_' . (empty($pollinfo['voting_locked']) ? 'poll' : 'locked_poll'),
710
			'question' => parse_bbc($pollinfo['question']),
711
			'total_votes' => $pollinfo['total'],
712
			'change_vote' => !empty($pollinfo['change_vote']),
713
			'is_locked' => !empty($pollinfo['voting_locked']),
714
			'options' => array(),
715
			'lock' => allowedTo('poll_lock_any') || ($context['user']['started'] && allowedTo('poll_lock_own')),
716
			'edit' => allowedTo('poll_edit_any') || ($context['user']['started'] && allowedTo('poll_edit_own')),
717
			'remove' => allowedTo('poll_remove_any') || ($context['user']['started'] && allowedTo('poll_remove_own')),
718
			'allowed_warning' => $pollinfo['max_votes'] > 1 ? sprintf($txt['poll_options6'], min(count($pollOptions), $pollinfo['max_votes'])) : '',
719
			'is_expired' => !empty($pollinfo['expire_time']) && $pollinfo['expire_time'] < time(),
720
			'expire_time' => !empty($pollinfo['expire_time']) ? timeformat($pollinfo['expire_time']) : 0,
721
			'has_voted' => !empty($pollinfo['has_voted']),
722
			'starter' => array(
723
				'id' => $pollinfo['id_member'],
724
				'name' => $row['poster_name'],
725
				'href' => $pollinfo['id_member'] == 0 ? '' : $scripturl . '?action=profile;u=' . $pollinfo['id_member'],
726
				'link' => $pollinfo['id_member'] == 0 ? $row['poster_name'] : '<a href="' . $scripturl . '?action=profile;u=' . $pollinfo['id_member'] . '">' . $row['poster_name'] . '</a>'
727
			)
728
		);
729
730
		// Make the lock, edit and remove permissions defined above more directly accessible.
731
		$context['allow_lock_poll'] = $context['poll']['lock'];
732
		$context['allow_edit_poll'] = $context['poll']['edit'];
733
		$context['can_remove_poll'] = $context['poll']['remove'];
734
735
		// You're allowed to vote if:
736
		// 1. the poll did not expire, and
737
		// 2. you're either not a guest OR guest voting is enabled... and
738
		// 3. you're not trying to view the results, and
739
		// 4. the poll is not locked, and
740
		// 5. you have the proper permissions, and
741
		// 6. you haven't already voted before.
742
		$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'];
743
744
		// You're allowed to view the results if:
745
		// 1. you're just a super-nice-guy, or
746
		// 2. anyone can see them (hide_results == 0), or
747
		// 3. you can see them after you voted (hide_results == 1), or
748
		// 4. you've waited long enough for the poll to expire. (whether hide_results is 1 or 2.)
749
		$context['allow_results_view'] = allowedTo('moderate_board') || $pollinfo['hide_results'] == 0 || ($pollinfo['hide_results'] == 1 && $context['poll']['has_voted']) || $context['poll']['is_expired'];
750
751
		// Show the results if:
752
		// 1. You're allowed to see them (see above), and
753
		// 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...
754
		$context['poll']['show_results'] = $context['allow_results_view'] && (isset($_REQUEST['viewresults']) || isset($_REQUEST['viewResults']));
755
756
		// Show the button if:
757
		// 1. You can vote in the poll (see above), and
758
		// 2. Results are visible to everyone (hidden = 0), and
759
		// 3. You aren't already viewing the results
760
		$context['show_view_results_button'] = $context['allow_vote'] && $context['allow_results_view'] && !$context['poll']['show_results'];
761
762
		// You're allowed to change your vote if:
763
		// 1. the poll did not expire, and
764
		// 2. you're not a guest... and
765
		// 3. the poll is not locked, and
766
		// 4. you have the proper permissions, and
767
		// 5. you have already voted, and
768
		// 6. the poll creator has said you can!
769
		$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'];
770
771
		// You're allowed to return to voting options if:
772
		// 1. you are (still) allowed to vote.
773
		// 2. you are currently seeing the results.
774
		$context['allow_return_vote'] = $context['allow_vote'] && $context['poll']['show_results'];
775
776
		// Calculate the percentages and bar lengths...
777
		$divisor = $realtotal == 0 ? 1 : $realtotal;
778
779
		// Determine if a decimal point is needed in order for the options to add to 100%.
780
		$precision = $realtotal == 100 ? 0 : 1;
781
782
		// Now look through each option, and...
783
		foreach ($pollOptions as $i => $option)
784
		{
785
			// First calculate the percentage, and then the width of the bar...
786
			$bar = round(($option['votes'] * 100) / $divisor, $precision);
787
			$barWide = $bar == 0 ? 1 : floor(($bar * 8) / 3);
788
789
			// Now add it to the poll's contextual theme data.
790
			$context['poll']['options'][$i] = array(
791
				'id' => 'options-' . $i,
792
				'percent' => $bar,
793
				'votes' => $option['votes'],
794
				'voted_this' => $option['voted_this'] != -1,
795
				'bar_ndt' => $bar > 0 ? '<div class="bar" style="width: ' . $bar . '%;"></div>' : '',
796
				'bar_width' => $barWide,
797
				'option' => parse_bbc($option['label']),
798
				'vote_button' => '<input type="' . ($pollinfo['max_votes'] > 1 ? 'checkbox' : 'radio') . '" name="options[]" id="options-' . $i . '" value="' . $i . '" class="input_' . ($pollinfo['max_votes'] > 1 ? 'check' : 'radio') . '">'
799
			);
800
		}
801
802
		// Build the poll moderation button array.
803
		$context['poll_buttons'] = array();
804
805 View Code Duplication
		if ($context['allow_return_vote'])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
806
			$context['poll_buttons']['vote'] = array('text' => 'poll_return_vote', 'image' => 'poll_options.png', 'url' => $scripturl . '?topic=' . $context['current_topic'] . '.' . $context['start']);
807
808 View Code Duplication
		if ($context['show_view_results_button'])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
809
			$context['poll_buttons']['results'] = array('text' => 'poll_results', 'image' => 'poll_results.png', 'url' => $scripturl . '?topic=' . $context['current_topic'] . '.' . $context['start'] . ';viewresults');
810
811 View Code Duplication
		if ($context['allow_change_vote'])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
812
			$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']);
813
814 View Code Duplication
		if ($context['allow_lock_poll'])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
815
			$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']);
816
817 View Code Duplication
		if ($context['allow_edit_poll'])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
818
			$context['poll_buttons']['edit'] = array('text' => 'poll_edit', 'image' => 'poll_edit.png', 'url' => $scripturl . '?action=editpoll;topic=' . $context['current_topic'] . '.' . $context['start']);
819
820
		if ($context['can_remove_poll'])
821
			$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']);
822
823
		// Allow mods to add additional buttons here
824
		call_integration_hook('integrate_poll_buttons');
825
	}
826
	
827
	if(!empty($_REQUEST['page_id']))
828
		$start_char = substr($_REQUEST['page_id'], 0, 1);
829
	else
830
		$start_char = null;
831
832
	if ($start_char === 'M' || $start_char === 'L')
833
		$page_id = substr($_REQUEST['page_id'], 1);
834
835
	$limit = $context['messages_per_page'];
836
 
837
	// Jump to page
838
	if (empty($start_char))
839
	{
840
		// Calculate the fastest way to get the messages!
841
		$ascending = empty($options['view_newest_first']);
842
		$start = $_REQUEST['start'];
843
		$firstIndex = 0;
844
		if ($start >= $context['total_visible_posts'] / 2 && $context['messages_per_page'] != -1)
845
		{
846
			$ascending = !$ascending;
847
			$limit = $context['total_visible_posts'] <= $start + $limit ? $context['total_visible_posts'] - $start : $limit;
848
			$start = $context['total_visible_posts'] <= $start + $limit ? 0 : $context['total_visible_posts'] - $start - $limit;
849
			$firstIndex = $limit - 1;
850
		}
851
852
		// Get each post and poster in this topic.
853
		$request = $smcFunc['db_query']('', '
854
			SELECT id_msg, id_member, approved
855
			FROM {db_prefix}messages
856
			WHERE id_topic = {int:current_topic}' . (!$modSettings['postmod_active'] || $approve_posts ? '' : '
857
			AND (approved = {int:is_approved}' . ($user_info['is_guest'] ? '' : ' OR id_member = {int:current_member}') . ')') . '
858
			ORDER BY id_msg ' . ($ascending ? '' : 'DESC') . ($context['messages_per_page'] == -1 ? '' : '
859
			LIMIT {int:start}, {int:max}'),
860
			array(
861
				'current_member' => $user_info['id'],
862
				'current_topic' => $topic,
863
				'is_approved' => 1,
864
				'blank_id_member' => 0,
865
				'start' => $start,
866
				'max' => $limit,
867
			)
868
		);
869
	}
870
	else //next or before page
871
	{
872
		$firstIndex = 0;
873
		
874
		if ($start_char === 'M')
875
		{
876
			$ascending = true;
877
			$page_operator = '>';
878
		}
879
		else
880
		{
881
			$ascending = false;
882
			$page_operator = '<';
883
		}
884
		
885
		$request = $smcFunc['db_query']('', '
886
			SELECT id_msg, id_member, approved
887
			FROM {db_prefix}messages
888
			WHERE id_topic = {int:current_topic} 
889
			AND id_msg '. $page_operator . ' {int:page_id}'. (!$modSettings['postmod_active'] || $approve_posts ? '' : '
890
			AND (approved = {int:is_approved}' . ($user_info['is_guest'] ? '' : ' OR id_member = {int:current_member}') . ')') . '
891
			ORDER BY id_msg ' . ($ascending ? '' : 'DESC') . ($context['messages_per_page'] == -1 ? '' : '
892
			LIMIT {int:limit}'),
893
			array(
894
				'current_member' => $user_info['id'],
895
				'current_topic' => $topic,
896
				'is_approved' => 1,
897
				'blank_id_member' => 0,
898
				'limit' => $limit,
899
				'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...
900
			)
901
		);
902
	}
903
	
904
905
	$messages = array();
906
	$all_posters = array();
907
	while ($row = $smcFunc['db_fetch_assoc']($request))
908
	{
909
		if (!empty($row['id_member']))
910
			$all_posters[$row['id_msg']] = $row['id_member'];
911
		$messages[] = $row['id_msg'];
912
	}
913
	
914
	// Before Page bring in the right order
915
	if (!empty($start_char) && $start_char === 'L')
916
		krsort($messages);
917
	
918
	// Construct the page index, allowing for the .START method...
919
	$page_options = array(
920
		'low_id' => $messages[0],
921
		'max_id' => end($messages),
922
	);
923
	$context['page_index'] = constructPageIndex($scripturl . '?topic=' . $topic . '.%1$d', $_REQUEST['start'], $context['total_visible_posts'], $context['messages_per_page'], true, true, $page_options);
924
	
925
	// If they are viewing all the posts, show all the posts, otherwise limit the number.
926
	if ($can_show_all)
927
	{
928
		if (isset($_REQUEST['all']))
929
		{
930
			// No limit! (actually, there is a limit, but...)
931
			$context['messages_per_page'] = -1;
932
			$context['page_index'] .= empty($modSettings['compactTopicPagesEnable']) ? '<strong>' . $txt['all'] . '</strong> ' : '[<strong>' . $txt['all'] . '</strong>] ';
933
934
			// Set start back to 0...
935
			$_REQUEST['start'] = 0;
936
		}
937
		// They aren't using it, but the *option* is there, at least.
938
		else
939
			$context['page_index'] .= '&nbsp;<a href="' . $scripturl . '?topic=' . $topic . '.0;all">' . $txt['all'] . '</a> ';
940
	}
941
	
942
	$smcFunc['db_free_result']($request);
943
	$posters = array_unique($all_posters);
944
945
	call_integration_hook('integrate_display_message_list', array(&$messages, &$posters));
946
947
	// Guests can't mark topics read or for notifications, just can't sorry.
948
	if (!$user_info['is_guest'] && !empty($messages))
949
	{
950
		$mark_at_msg = max($messages);
951
		if ($mark_at_msg >= $context['topicinfo']['id_last_msg'])
952
			$mark_at_msg = $modSettings['maxMsgID'];
953
		if ($mark_at_msg >= $context['topicinfo']['new_from'])
954
		{
955
			$smcFunc['db_insert']($context['topicinfo']['new_from'] == 0 ? 'ignore' : 'replace',
956
				'{db_prefix}log_topics',
957
				array(
958
					'id_member' => 'int', 'id_topic' => 'int', 'id_msg' => 'int', 'unwatched' => 'int',
959
				),
960
				array(
961
					$user_info['id'], $topic, $mark_at_msg, $context['topicinfo']['unwatched'],
962
				),
963
				array('id_member', 'id_topic')
964
			);
965
		}
966
967
		// Check for notifications on this topic OR board.
968
		$request = $smcFunc['db_query']('', '
969
			SELECT sent, id_topic
970
			FROM {db_prefix}log_notify
971
			WHERE (id_topic = {int:current_topic} OR id_board = {int:current_board})
972
				AND id_member = {int:current_member}
973
			LIMIT 2',
974
			array(
975
				'current_board' => $board,
976
				'current_member' => $user_info['id'],
977
				'current_topic' => $topic,
978
			)
979
		);
980
		$do_once = true;
981
		while ($row = $smcFunc['db_fetch_assoc']($request))
982
		{
983
			// Find if this topic is marked for notification...
984
			if (!empty($row['id_topic']))
985
				$context['is_marked_notify'] = true;
986
987
			// Only do this once, but mark the notifications as "not sent yet" for next time.
988
			if (!empty($row['sent']) && $do_once)
989
			{
990
				$smcFunc['db_query']('', '
991
					UPDATE {db_prefix}log_notify
992
					SET sent = {int:is_not_sent}
993
					WHERE (id_topic = {int:current_topic} OR id_board = {int:current_board})
994
						AND id_member = {int:current_member}',
995
					array(
996
						'current_board' => $board,
997
						'current_member' => $user_info['id'],
998
						'current_topic' => $topic,
999
						'is_not_sent' => 0,
1000
					)
1001
				);
1002
				$do_once = false;
1003
			}
1004
		}
1005
1006
		// Have we recently cached the number of new topics in this board, and it's still a lot?
1007
		if (isset($_REQUEST['topicseen']) && isset($_SESSION['topicseen_cache'][$board]) && $_SESSION['topicseen_cache'][$board] > 5)
1008
			$_SESSION['topicseen_cache'][$board]--;
1009
		// Mark board as seen if this is the only new topic.
1010
		elseif (isset($_REQUEST['topicseen']))
1011
		{
1012
			// Use the mark read tables... and the last visit to figure out if this should be read or not.
1013
			$request = $smcFunc['db_query']('', '
1014
				SELECT COUNT(*)
1015
				FROM {db_prefix}topics AS t
1016
					LEFT JOIN {db_prefix}log_boards AS lb ON (lb.id_board = {int:current_board} AND lb.id_member = {int:current_member})
1017
					LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = t.id_topic AND lt.id_member = {int:current_member})
1018
				WHERE t.id_board = {int:current_board}
1019
					AND t.id_last_msg > COALESCE(lb.id_msg, 0)
1020
					AND t.id_last_msg > COALESCE(lt.id_msg, 0)' . (empty($_SESSION['id_msg_last_visit']) ? '' : '
1021
					AND t.id_last_msg > {int:id_msg_last_visit}'),
1022
				array(
1023
					'current_board' => $board,
1024
					'current_member' => $user_info['id'],
1025
					'id_msg_last_visit' => (int) $_SESSION['id_msg_last_visit'],
1026
				)
1027
			);
1028
			list ($numNewTopics) = $smcFunc['db_fetch_row']($request);
1029
			$smcFunc['db_free_result']($request);
1030
1031
			// If there're no real new topics in this board, mark the board as seen.
1032
			if (empty($numNewTopics))
1033
				$_REQUEST['boardseen'] = true;
1034
			else
1035
				$_SESSION['topicseen_cache'][$board] = $numNewTopics;
1036
		}
1037
		// Probably one less topic - maybe not, but even if we decrease this too fast it will only make us look more often.
1038
		elseif (isset($_SESSION['topicseen_cache'][$board]))
1039
			$_SESSION['topicseen_cache'][$board]--;
1040
1041
		// Mark board as seen if we came using last post link from BoardIndex. (or other places...)
1042
		if (isset($_REQUEST['boardseen']))
1043
		{
1044
			$smcFunc['db_insert']('replace',
1045
				'{db_prefix}log_boards',
1046
				array('id_msg' => 'int', 'id_member' => 'int', 'id_board' => 'int'),
1047
				array($modSettings['maxMsgID'], $user_info['id'], $board),
1048
				array('id_member', 'id_board')
1049
			);
1050
		}
1051
	}
1052
1053
	// Get notification preferences
1054
	$context['topicinfo']['notify_prefs'] = array();
1055
	if (!empty($user_info['id']))
1056
	{
1057
		require_once($sourcedir . '/Subs-Notify.php');
1058
		$prefs = getNotifyPrefs($user_info['id'], array('topic_notify', 'topic_notify_' . $context['current_topic']), true);
1059
		$pref = !empty($prefs[$user_info['id']]) && $context['is_marked_notify'] ? $prefs[$user_info['id']] : array();
1060
		$context['topicinfo']['notify_prefs'] = array(
1061
			'is_custom' => isset($pref['topic_notify_' . $topic]),
1062
			'pref' => isset($pref['topic_notify_' . $context['current_topic']]) ? $pref['topic_notify_' . $context['current_topic']] : (!empty($pref['topic_notify']) ? $pref['topic_notify'] : 0),
1063
		);
1064
	}
1065
1066
	$context['topic_notification'] = !empty($user_info['id']) ? $context['topicinfo']['notify_prefs'] : array();
1067
	// 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...
1068
	$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;
1069
1070
	$context['loaded_attachments'] = array();
1071
1072
	// If there _are_ messages here... (probably an error otherwise :!)
1073
	if (!empty($messages))
1074
	{
1075
		// Fetch attachments.
1076
		if (!empty($modSettings['attachmentEnable']) && allowedTo('view_attachments'))
1077
		{
1078
			$request = $smcFunc['db_query']('', '
1079
				SELECT
1080
					a.id_attach, a.id_folder, a.id_msg, a.filename, a.file_hash, COALESCE(a.size, 0) AS filesize, a.downloads, a.approved,
1081
					a.width, a.height' . (empty($modSettings['attachmentShowImages']) || empty($modSettings['attachmentThumbnails']) ? '' : ',
1082
					COALESCE(thumb.id_attach, 0) AS id_thumb, thumb.width AS thumb_width, thumb.height AS thumb_height') . '
1083
				FROM {db_prefix}attachments AS a' . (empty($modSettings['attachmentShowImages']) || empty($modSettings['attachmentThumbnails']) ? '' : '
1084
					LEFT JOIN {db_prefix}attachments AS thumb ON (thumb.id_attach = a.id_thumb)') . '
1085
				WHERE a.id_msg IN ({array_int:message_list})
1086
					AND a.attachment_type = {int:attachment_type}',
1087
				array(
1088
					'message_list' => $messages,
1089
					'attachment_type' => 0,
1090
					'is_approved' => 1,
1091
				)
1092
			);
1093
			$temp = array();
1094
			while ($row = $smcFunc['db_fetch_assoc']($request))
1095
			{
1096 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']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1097
					continue;
1098
1099
				$temp[$row['id_attach']] = $row;
1100
				$temp[$row['id_attach']]['topic'] = $topic;
1101
				$temp[$row['id_attach']]['board'] = $board;
1102
1103
				if (!isset($context['loaded_attachments'][$row['id_msg']]))
1104
					$context['loaded_attachments'][$row['id_msg']] = array();
1105
			}
1106
			$smcFunc['db_free_result']($request);
1107
1108
			// This is better than sorting it with the query...
1109
			ksort($temp);
1110
1111
			foreach ($temp as $row)
1112
				$context['loaded_attachments'][$row['id_msg']][] = $row;
1113
		}
1114
1115
		$msg_parameters = array(
1116
			'message_list' => $messages,
1117
			'new_from' => $context['topicinfo']['new_from'],
1118
		);
1119
		$msg_selects = array();
1120
		$msg_tables = array();
1121
		call_integration_hook('integrate_query_message', array(&$msg_selects, &$msg_tables, &$msg_parameters));
1122
1123
		// What?  It's not like it *couldn't* be only guests in this topic...
1124
		loadMemberData($posters);
1125
		$messages_request = $smcFunc['db_query']('', '
1126
			SELECT
1127
				id_msg, icon, subject, poster_time, poster_ip, id_member, modified_time, modified_name, modified_reason, body,
1128
				smileys_enabled, poster_name, poster_email, approved, likes,
1129
				id_msg_modified < {int:new_from} AS is_read
1130
				' . (!empty($msg_selects) ? (', ' . implode(', ', $msg_selects)) : '') . '
1131
			FROM {db_prefix}messages
1132
				' . (!empty($msg_tables) ? implode("\n\t", $msg_tables) : '') . '
1133
			WHERE id_msg IN ({array_int:message_list})
1134
			ORDER BY id_msg' . (empty($options['view_newest_first']) ? '' : ' DESC'),
1135
			$msg_parameters
1136
		);
1137
1138
		// And the likes
1139
		if (!empty($modSettings['enable_likes']))
1140
			$context['my_likes'] = $context['user']['is_guest'] ? array() : prepareLikesContext($topic);
1141
1142
		// Go to the last message if the given time is beyond the time of the last message.
1143 View Code Duplication
		if (isset($context['start_from']) && $context['start_from'] >= $context['topicinfo']['num_replies'])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1144
			$context['start_from'] = $context['topicinfo']['num_replies'];
1145
1146
		// Since the anchor information is needed on the top of the page we load these variables beforehand.
1147
		$context['first_message'] = isset($messages[$firstIndex]) ? $messages[$firstIndex] : $messages[0];
1148
		if (empty($options['view_newest_first']))
1149
			$context['first_new_message'] = isset($context['start_from']) && $_REQUEST['start'] == $context['start_from'];
1150 View Code Duplication
		else
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1151
			$context['first_new_message'] = isset($context['start_from']) && $_REQUEST['start'] == $context['topicinfo']['num_replies'] - $context['start_from'];
1152
	}
1153
	else
1154
	{
1155
		$messages_request = false;
1156
		$context['first_message'] = 0;
1157
		$context['first_new_message'] = false;
1158
1159
		$context['likes'] = array();
1160
	}
1161
1162
	$context['jump_to'] = array(
1163
		'label' => addslashes(un_htmlspecialchars($txt['jump_to'])),
1164
		'board_name' => $smcFunc['htmlspecialchars'](strtr(strip_tags($board_info['name']), array('&amp;' => '&'))),
1165
		'child_level' => $board_info['child_level'],
1166
	);
1167
1168
	// Set the callback.  (do you REALIZE how much memory all the messages would take?!?)
1169
	// This will be called from the template.
1170
	$context['get_message'] = 'prepareDisplayContext';
1171
1172
	// Now set all the wonderful, wonderful permissions... like moderation ones...
1173
	$common_permissions = array(
1174
		'can_approve' => 'approve_posts',
1175
		'can_ban' => 'manage_bans',
1176
		'can_sticky' => 'make_sticky',
1177
		'can_merge' => 'merge_any',
1178
		'can_split' => 'split_any',
1179
		'calendar_post' => 'calendar_post',
1180
		'can_send_pm' => 'pm_send',
1181
		'can_report_moderator' => 'report_any',
1182
		'can_moderate_forum' => 'moderate_forum',
1183
		'can_issue_warning' => 'issue_warning',
1184
		'can_restore_topic' => 'move_any',
1185
		'can_restore_msg' => 'move_any',
1186
		'can_see_likes' => 'likes_view',
1187
		'can_like' => 'likes_like',
1188
	);
1189
	foreach ($common_permissions as $contextual => $perm)
1190
		$context[$contextual] = allowedTo($perm);
1191
1192
	// Permissions with _any/_own versions.  $context[YYY] => ZZZ_any/_own.
1193
	$anyown_permissions = array(
1194
		'can_move' => 'move',
1195
		'can_lock' => 'lock',
1196
		'can_delete' => 'remove',
1197
		'can_add_poll' => 'poll_add',
1198
		'can_remove_poll' => 'poll_remove',
1199
		'can_reply' => 'post_reply',
1200
		'can_reply_unapproved' => 'post_unapproved_replies',
1201
		'can_view_warning' => 'profile_warning',
1202
	);
1203
	foreach ($anyown_permissions as $contextual => $perm)
1204
		$context[$contextual] = allowedTo($perm . '_any') || ($context['user']['started'] && allowedTo($perm . '_own'));
1205
1206
	if (!$user_info['is_admin'] && !$modSettings['topic_move_any'])
1207
	{
1208
		// We'll use this in a minute
1209
		$boards_allowed = array_diff(boardsAllowedTo('post_new'), array($board));
1210
1211
		/* You can't move this unless you have permission
1212
			to start new topics on at least one other board */
1213
		$context['can_move'] &= count($boards_allowed) > 1;
1214
	}
1215
1216
	// If a topic is locked, you can't remove it unless it's yours and you locked it or you can lock_any
1217
	if ($context['topicinfo']['locked'])
1218
	{
1219
		$context['can_delete'] &= (($context['topicinfo']['locked'] == 1 && $context['user']['started']) || allowedTo('lock_any'));
1220
	}
1221
1222
	// Cleanup all the permissions with extra stuff...
1223
	$context['can_mark_notify'] = !$context['user']['is_guest'];
1224
	$context['calendar_post'] &= !empty($modSettings['cal_enabled']);
1225
	$context['can_add_poll'] &= $modSettings['pollMode'] == '1' && $context['topicinfo']['id_poll'] <= 0;
1226
	$context['can_remove_poll'] &= $modSettings['pollMode'] == '1' && $context['topicinfo']['id_poll'] > 0;
1227
	$context['can_reply'] &= empty($context['topicinfo']['locked']) || allowedTo('moderate_board');
1228
	$context['can_reply_unapproved'] &= $modSettings['postmod_active'] && (empty($context['topicinfo']['locked']) || allowedTo('moderate_board'));
1229
	$context['can_issue_warning'] &= $modSettings['warning_settings'][0] == 1;
1230
	// Handle approval flags...
1231
	$context['can_reply_approved'] = $context['can_reply'];
1232
	$context['can_reply'] |= $context['can_reply_unapproved'];
1233
	$context['can_quote'] = $context['can_reply'] && (empty($modSettings['disabledBBC']) || !in_array('quote', explode(',', $modSettings['disabledBBC'])));
1234
	$context['can_mark_unread'] = !$user_info['is_guest'];
1235
	$context['can_unwatch'] = !$user_info['is_guest'];
1236
	$context['can_set_notify'] = !$user_info['is_guest'];
1237
1238
	$context['can_print'] = empty($modSettings['disable_print_topic']);
1239
1240
	// Start this off for quick moderation - it will be or'd for each post.
1241
	$context['can_remove_post'] = allowedTo('delete_any') || (allowedTo('delete_replies') && $context['user']['started']);
1242
1243
	// Can restore topic?  That's if the topic is in the recycle board and has a previous restore state.
1244
	$context['can_restore_topic'] &= !empty($board_info['recycle']) && !empty($context['topicinfo']['id_previous_board']);
1245
	$context['can_restore_msg'] &= !empty($board_info['recycle']) && !empty($context['topicinfo']['id_previous_topic']);
1246
1247
	// Check if the draft functions are enabled and that they have permission to use them (for quick reply.)
1248
	$context['drafts_save'] = !empty($modSettings['drafts_post_enabled']) && allowedTo('post_draft') && $context['can_reply'];
1249
	$context['drafts_autosave'] = !empty($context['drafts_save']) && !empty($modSettings['drafts_autosave_enabled']);
1250
	if (!empty($context['drafts_save']))
1251
		loadLanguage('Drafts');
1252
1253
	// When was the last time this topic was replied to?  Should we warn them about it?
1254
	if (!empty($modSettings['oldTopicDays']) && ($context['can_reply'] || $context['can_reply_unapproved']) && empty($context['topicinfo']['is_sticky']))
1255
	{
1256
		$request = $smcFunc['db_query']('', '
1257
			SELECT poster_time
1258
			FROM {db_prefix}messages
1259
			WHERE id_msg = {int:id_last_msg}
1260
			LIMIT 1',
1261
			array(
1262
				'id_last_msg' => $context['topicinfo']['id_last_msg'],
1263
			)
1264
		);
1265
1266
		list ($lastPostTime) = $smcFunc['db_fetch_row']($request);
1267
		$smcFunc['db_free_result']($request);
1268
1269
		$context['oldTopicError'] = $lastPostTime + $modSettings['oldTopicDays'] * 86400 < time();
1270
	}
1271
1272
	// You can't link an existing topic to the calendar unless you can modify the first post...
1273
	$context['calendar_post'] &= allowedTo('modify_any') || (allowedTo('modify_own') && $context['user']['started']);
1274
1275
	// Load up the "double post" sequencing magic.
1276
	checkSubmitOnce('register');
1277
	$context['name'] = isset($_SESSION['guest_name']) ? $_SESSION['guest_name'] : '';
1278
	$context['email'] = isset($_SESSION['guest_email']) ? $_SESSION['guest_email'] : '';
1279
	// Needed for the editor and message icons.
1280
	require_once($sourcedir . '/Subs-Editor.php');
1281
1282
	// Now create the editor.
1283
	$editorOptions = array(
1284
		'id' => 'quickReply',
1285
		'value' => '',
1286
		'disable_smiley_box' => empty($options['use_editor_quick_reply']),
1287
		'labels' => array(
1288
			'post_button' => $txt['post'],
1289
		),
1290
		// add height and width for the editor
1291
		'height' => '250px',
1292
		'width' => '100%',
1293
		// We do HTML preview here.
1294
		'preview_type' => 1,
1295
		// This is required
1296
		'required' => true,
1297
	);
1298
	create_control_richedit($editorOptions);
1299
1300
	// Store the ID.
1301
	$context['post_box_name'] = $editorOptions['id'];
1302
1303
	// Set a flag so the sub template knows what to do...
1304
	$context['show_bbc'] = !empty($options['use_editor_quick_reply']);
1305
	$modSettings['disable_wysiwyg'] = !empty($options['use_editor_quick_reply']);
1306
	$context['attached'] = '';
1307
	$context['make_poll'] = isset($_REQUEST['poll']);
1308
1309
	// Message icons - customized icons are off?
1310
	$context['icons'] = getMessageIcons($board);
1311
1312 View Code Duplication
	if (!empty($context['icons']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1313
		$context['icons'][count($context['icons']) - 1]['is_last'] = true;
1314
1315
	// Build the normal button array.
1316
	$context['normal_buttons'] = array();
1317
1318
	if ($context['can_reply'])
1319
		$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);
1320
1321 View Code Duplication
	if ($context['can_add_poll'])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1322
		$context['normal_buttons']['add_poll'] = array('text' => 'add_poll', 'image' => 'add_poll.png', 'url' => $scripturl . '?action=editpoll;add;topic=' . $context['current_topic'] . '.' . $context['start']);
1323
1324 View Code Duplication
	if ($context['can_mark_unread'])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1325
		$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']);
1326
1327
	if ($context['can_print'])
1328
		$context['normal_buttons']['print'] = array('text' => 'print', 'image' => 'print.png', 'custom' => 'rel="nofollow"', 'url' => $scripturl . '?action=printpage;topic=' . $context['current_topic'] . '.0');
1329
1330
	if ($context['can_set_notify'])
1331
		$context['normal_buttons']['notify'] = array(
1332
			'text' => 'notify_topic_' . $context['topic_notification_mode'],
1333
			'sub_buttons' => array(
1334
				array(
1335
					'test' => 'can_unwatch',
1336
					'text' => 'notify_topic_0',
1337
					'url' => $scripturl . '?action=notifytopic;topic=' . $context['current_topic'] . ';mode=0;' . $context['session_var'] . '=' . $context['session_id'],
1338
				),
1339
				array(
1340
					'text' => 'notify_topic_1',
1341
					'url' => $scripturl . '?action=notifytopic;topic=' . $context['current_topic'] . ';mode=1;' . $context['session_var'] . '=' . $context['session_id'],
1342
				),
1343
				array(
1344
					'text' => 'notify_topic_2',
1345
					'url' => $scripturl . '?action=notifytopic;topic=' . $context['current_topic'] . ';mode=2;' . $context['session_var'] . '=' . $context['session_id'],
1346
				),
1347
				array(
1348
					'text' => 'notify_topic_3',
1349
					'url' => $scripturl . '?action=notifytopic;topic=' . $context['current_topic'] . ';mode=3;' . $context['session_var'] . '=' . $context['session_id'],
1350
				),
1351
			),
1352
		);
1353
1354
	// Build the mod button array
1355
	$context['mod_buttons'] = array();
1356
1357 View Code Duplication
	if ($context['can_move'])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1358
		$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');
1359
1360 View Code Duplication
	if ($context['can_delete'])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1361
		$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']);
1362
1363 View Code Duplication
	if ($context['can_lock'])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1364
		$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']);
1365
1366 View Code Duplication
	if ($context['can_sticky'])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1367
		$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']);
1368
1369 View Code Duplication
	if ($context['can_merge'])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1370
		$context['mod_buttons']['merge'] = array('text' => 'merge', 'image' => 'merge.png', 'url' => $scripturl . '?action=mergetopics;board=' . $context['current_board'] . '.0;from=' . $context['current_topic']);
1371
1372 View Code Duplication
	if ($context['calendar_post'])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1373
		$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');
1374
1375
	// Restore topic. eh?  No monkey business.
1376 View Code Duplication
	if ($context['can_restore_topic'])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1377
		$context['mod_buttons']['restore_topic'] = array('text' => 'restore_topic', 'image' => '', 'url' => $scripturl . '?action=restoretopic;topics=' . $context['current_topic'] . ';' . $context['session_var'] . '=' . $context['session_id']);
1378
1379
	// Show a message in case a recently posted message became unapproved.
1380
	$context['becomesUnapproved'] = !empty($_SESSION['becomesUnapproved']) ? true : false;
1381
1382
	// Don't want to show this forever...
1383
	if ($context['becomesUnapproved'])
1384
		unset($_SESSION['becomesUnapproved']);
1385
1386
	// Allow adding new mod buttons easily.
1387
	// Note: $context['normal_buttons'] and $context['mod_buttons'] are added for backward compatibility with 2.0, but are deprecated and should not be used
1388
	call_integration_hook('integrate_display_buttons', array(&$context['normal_buttons']));
1389
	// Note: integrate_mod_buttons is no more necessary and deprecated, but is kept for backward compatibility with 2.0
1390
	call_integration_hook('integrate_mod_buttons', array(&$context['mod_buttons']));
1391
1392
	// Load the drafts js file
1393
	if ($context['drafts_autosave'])
1394
		loadJavaScriptFile('drafts.js', array('defer' => false), 'smf_drafts');
1395
1396
	// Spellcheck
1397
	if ($context['show_spellchecking'])
1398
		loadJavaScriptFile('spellcheck.js', array('defer' => false), 'smf_spellcheck');
1399
1400
	// topic.js
1401
	loadJavaScriptFile('topic.js', array('defer' => false), 'smf_topic');
1402
1403
	// quotedText.js
1404
	loadJavaScriptFile('quotedText.js', array('defer' => true), 'smf_quotedText');
1405
1406
	// Mentions
1407 View Code Duplication
	if (!empty($modSettings['enable_mentions']) && allowedTo('mention'))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1408
	{
1409
		loadJavaScriptFile('jquery.atwho.min.js', array('defer' => true), 'smf_atwho');
1410
		loadJavaScriptFile('jquery.caret.min.js', array('defer' => true), 'smf_caret');
1411
		loadJavaScriptFile('mentions.js', array('defer' => true), 'smf_mentions');
1412
	}
1413
}
1414
1415
/**
1416
 * Callback for the message display.
1417
 * It actually gets and prepares the message context.
1418
 * This function will start over from the beginning if reset is set to true, which is
1419
 * useful for showing an index before or after the posts.
1420
 *
1421
 * @param bool $reset Whether or not to reset the db seek pointer
1422
 * @return array A large array of contextual data for the posts
1423
 */
1424
function prepareDisplayContext($reset = false)
1425
{
1426
	global $settings, $txt, $modSettings, $scripturl, $options, $user_info, $smcFunc;
1427
	global $memberContext, $context, $messages_request, $topic, $board_info, $sourcedir;
1428
1429
	static $counter = null;
1430
1431
	// If the query returned false, bail.
1432
	if ($messages_request == false)
1433
		return false;
1434
1435
	// Remember which message this is.  (ie. reply #83)
1436
	if ($counter === null || $reset)
1437
		$counter = empty($options['view_newest_first']) ? $context['start'] : $context['total_visible_posts'] - $context['start'];
1438
1439
	// Start from the beginning...
1440
	if ($reset)
1441
		return @$smcFunc['db_data_seek']($messages_request, 0);
1442
1443
	// Attempt to get the next message.
1444
	$message = $smcFunc['db_fetch_assoc']($messages_request);
1445
	if (!$message)
1446
	{
1447
		$smcFunc['db_free_result']($messages_request);
1448
		return false;
1449
	}
1450
1451
	// $context['icon_sources'] says where each icon should come from - here we set up the ones which will always exist!
1452
	if (empty($context['icon_sources']))
1453
	{
1454
		$context['icon_sources'] = array();
1455
		foreach ($context['stable_icons'] as $icon)
1456
			$context['icon_sources'][$icon] = 'images_url';
1457
	}
1458
1459
	// Message Icon Management... check the images exist.
1460
	if (empty($modSettings['messageIconChecks_disable']))
1461
	{
1462
		// If the current icon isn't known, then we need to do something...
1463 View Code Duplication
		if (!isset($context['icon_sources'][$message['icon']]))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1464
			$context['icon_sources'][$message['icon']] = file_exists($settings['theme_dir'] . '/images/post/' . $message['icon'] . '.png') ? 'images_url' : 'default_images_url';
1465
	}
1466 View Code Duplication
	elseif (!isset($context['icon_sources'][$message['icon']]))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1467
		$context['icon_sources'][$message['icon']] = 'images_url';
1468
1469
	// If you're a lazy bum, you probably didn't give a subject...
1470
	$message['subject'] = $message['subject'] != '' ? $message['subject'] : $txt['no_subject'];
1471
1472
	// Are you allowed to remove at least a single reply?
1473
	$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'];
1474
1475
	// If the topic is locked, you might not be able to delete the post...
1476
	if ($context['is_locked'])
1477
	{
1478
		$context['can_remove_post'] &= ($context['user']['started'] && $context['is_locked'] == 1) || allowedTo('lock_any');
1479
	}
1480
1481
	// If it couldn't load, or the user was a guest.... someday may be done with a guest table.
1482
	if (!loadMemberContext($message['id_member'], true))
1483
	{
1484
		// Notice this information isn't used anywhere else....
1485
		$memberContext[$message['id_member']]['name'] = $message['poster_name'];
1486
		$memberContext[$message['id_member']]['id'] = 0;
1487
		$memberContext[$message['id_member']]['group'] = $txt['guest_title'];
1488
		$memberContext[$message['id_member']]['link'] = $message['poster_name'];
1489
		$memberContext[$message['id_member']]['email'] = $message['poster_email'];
1490
		$memberContext[$message['id_member']]['show_email'] = allowedTo('moderate_forum');
1491
		$memberContext[$message['id_member']]['is_guest'] = true;
1492
	}
1493
	else
1494
	{
1495
		// Define this here to make things a bit more readable
1496
		$can_view_warning = $context['user']['can_mod'] || allowedTo('view_warning_any') || ($message['id_member'] == $user_info['id'] && allowedTo('view_warning_own'));
1497
1498
		$memberContext[$message['id_member']]['can_view_profile'] = allowedTo('profile_view') || ($message['id_member'] == $user_info['id'] && !$user_info['is_guest']);
1499
		$memberContext[$message['id_member']]['is_topic_starter'] = $message['id_member'] == $context['topic_starter_id'];
1500
		$memberContext[$message['id_member']]['can_see_warning'] = !isset($context['disabled_fields']['warning_status']) && $memberContext[$message['id_member']]['warning_status'] && $can_view_warning;
1501
		// Show the email if it's your post...
1502
		$memberContext[$message['id_member']]['show_email'] |= ($message['id_member'] == $user_info['id']);
1503
	}
1504
1505
	$memberContext[$message['id_member']]['ip'] = inet_dtop($message['poster_ip']);
1506
	$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']);
1507
1508
	// Do the censor thang.
1509
	censorText($message['body']);
1510
	censorText($message['subject']);
1511
1512
	// Run BBC interpreter on the message.
1513
	$message['body'] = parse_bbc($message['body'], $message['smileys_enabled'], $message['id_msg']);
1514
1515
	// If it's in the recycle bin we need to override whatever icon we did have.
1516
	if (!empty($board_info['recycle']))
1517
		$message['icon'] = 'recycled';
1518
1519
	require_once($sourcedir . '/Subs-Attachments.php');
1520
1521
	// Compose the memory eat- I mean message array.
1522
	$output = array(
1523
		'attachment' => loadAttachmentContext($message['id_msg'], $context['loaded_attachments']),
1524
		'id' => $message['id_msg'],
1525
		'href' => $scripturl . '?topic=' . $topic . '.msg' . $message['id_msg'] . '#msg' . $message['id_msg'],
1526
		'link' => '<a href="' . $scripturl . '?msg=' . $message['id_msg'] . '" rel="nofollow">' . $message['subject'] . '</a>',
1527
		'member' => &$memberContext[$message['id_member']],
1528
		'icon' => $message['icon'],
1529
		'icon_url' => $settings[$context['icon_sources'][$message['icon']]] . '/post/' . $message['icon'] . '.png',
1530
		'subject' => $message['subject'],
1531
		'time' => timeformat($message['poster_time']),
1532
		'timestamp' => forum_time(true, $message['poster_time']),
1533
		'counter' => $counter,
1534
		'modified' => array(
1535
			'time' => timeformat($message['modified_time']),
1536
			'timestamp' => forum_time(true, $message['modified_time']),
1537
			'name' => $message['modified_name'],
1538
			'reason' => $message['modified_reason']
1539
		),
1540
		'body' => $message['body'],
1541
		'new' => empty($message['is_read']),
1542
		'approved' => $message['approved'],
1543
		'first_new' => isset($context['start_from']) && $context['start_from'] == $counter,
1544
		'is_ignored' => !empty($modSettings['enable_buddylist']) && !empty($options['posts_apply_ignore_list']) && in_array($message['id_member'], $context['user']['ignoreusers']),
1545
		'can_approve' => !$message['approved'] && $context['can_approve'],
1546
		'can_unapprove' => !empty($modSettings['postmod_active']) && $context['can_approve'] && $message['approved'],
1547
		'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()))),
1548
		'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())),
1549
		'can_see_ip' => allowedTo('moderate_forum') || ($message['id_member'] == $user_info['id'] && !empty($user_info['id'])),
1550
		'css_class' => $message['approved'] ? 'windowbg' : 'approvebg',
1551
	);
1552
1553
	// Does the file contains any attachments? if so, change the icon.
1554
	if (!empty($output['attachment']))
1555
	{
1556
		$output['icon'] = 'clip';
1557
		$output['icon_url'] = $settings[$context['icon_sources'][$output['icon']]] . '/post/' . $output['icon'] . '.png';
1558
	}
1559
1560
	// Are likes enable?
1561 View Code Duplication
	if (!empty($modSettings['enable_likes']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1562
		$output['likes'] = array(
1563
			'count' => $message['likes'],
1564
			'you' => in_array($message['id_msg'], $context['my_likes']),
1565
			'can_like' => !$context['user']['is_guest'] && $message['id_member'] != $context['user']['id'] && !empty($context['can_like']),
1566
		);
1567
1568
	// Is this user the message author?
1569
	$output['is_message_author'] = $message['id_member'] == $user_info['id'];
1570 View Code Duplication
	if (!empty($output['modified']['name']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1571
		$output['modified']['last_edit_text'] = sprintf($txt['last_edit_by'], $output['modified']['time'], $output['modified']['name']);
1572
1573
	// Did they give a reason for editing?
1574 View Code Duplication
	if (!empty($output['modified']['name']) && !empty($output['modified']['reason']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1575
		$output['modified']['last_edit_text'] .= '&nbsp;' . sprintf($txt['last_edit_reason'], $output['modified']['reason']);
1576
1577
	// Any custom profile fields?
1578
	if (!empty($memberContext[$message['id_member']]['custom_fields']))
1579
		foreach ($memberContext[$message['id_member']]['custom_fields'] as $custom)
1580
			$output['custom_fields'][$context['cust_profile_fields_placement'][$custom['placement']]][] = $custom;
1581
1582
	if (empty($options['view_newest_first']))
1583
		$counter++;
1584
1585
	else
1586
		$counter--;
1587
1588
	call_integration_hook('integrate_prepare_display_context', array(&$output, &$message, $counter));
1589
1590
	return $output;
1591
}
1592
1593
/**
1594
 * Downloads an attachment, and increments the download count.
1595
 * It requires the view_attachments permission.
1596
 * It disables the session parser, and clears any previous output.
1597
 * It depends on the attachmentUploadDir setting being correct.
1598
 * It is accessed via the query string ?action=dlattach.
1599
 * Views to attachments do not increase hits and are not logged in the "Who's Online" log.
1600
 * Legacy code, all attachments are now handled by ShowAttachments.php
1601
 */
1602
function Download()
1603
{
1604
	global $txt, $modSettings, $user_info, $context, $topic, $smcFunc;
1605
1606
	// Some defaults that we need.
1607
	$context['character_set'] = empty($modSettings['global_character_set']) ? (empty($txt['lang_character_set']) ? 'ISO-8859-1' : $txt['lang_character_set']) : $modSettings['global_character_set'];
1608
	$context['utf8'] = $context['character_set'] === 'UTF-8';
1609
	$context['no_last_modified'] = true;
1610
1611
	// Prevent a preview image from being displayed twice.
1612
	if (isset($_GET['action']) && $_GET['action'] == 'dlattach' && isset($_GET['type']) && ($_GET['type'] == 'avatar' || $_GET['type'] == 'preview'))
1613
		return;
1614
1615
	// Make sure some attachment was requested!
1616
	if (!isset($_REQUEST['attach']) && !isset($_REQUEST['id']))
1617
		fatal_lang_error('no_access', false);
1618
1619
	$_REQUEST['attach'] = isset($_REQUEST['attach']) ? (int) $_REQUEST['attach'] : (int) $_REQUEST['id'];
1620
1621
	// Do we have a hook wanting to use our attachment system? We use $attachRequest to prevent accidental usage of $request.
1622
	$attachRequest = null;
1623
	call_integration_hook('integrate_download_request', array(&$attachRequest));
1624
	if (!is_null($attachRequest) && $smcFunc['db_is_resource']($attachRequest))
1625
		$request = $attachRequest;
1626
	else
1627
	{
1628
		// This checks only the current board for $board/$topic's permissions.
1629
		isAllowedTo('view_attachments');
1630
1631
		// Make sure this attachment is on this board.
1632
		// @todo: We must verify that $topic is the attachment's topic, or else the permission check above is broken.
1633
		$request = $smcFunc['db_query']('', '
1634
			SELECT a.id_folder, a.filename, a.file_hash, a.fileext, a.id_attach, a.attachment_type, a.mime_type, a.approved, m.id_member
1635
			FROM {db_prefix}attachments AS a
1636
				INNER JOIN {db_prefix}messages AS m ON (m.id_msg = a.id_msg AND m.id_topic = {int:current_topic})
1637
				INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board AND {query_see_board})
1638
			WHERE a.id_attach = {int:attach}
1639
			LIMIT 1',
1640
			array(
1641
				'attach' => $_REQUEST['attach'],
1642
				'current_topic' => $topic,
1643
			)
1644
		);
1645
	}
1646
1647
	if ($smcFunc['db_num_rows']($request) == 0)
1648
		fatal_lang_error('no_access', false);
1649
1650
	list ($id_folder, $real_filename, $file_hash, $file_ext, $id_attach, $attachment_type, $mime_type, $is_approved, $id_member) = $smcFunc['db_fetch_row']($request);
1651
	$smcFunc['db_free_result']($request);
1652
1653
	// If it isn't yet approved, do they have permission to view it?
1654
	if (!$is_approved && ($id_member == 0 || $user_info['id'] != $id_member) && ($attachment_type == 0 || $attachment_type == 3))
1655
		isAllowedTo('approve_posts');
1656
1657
	// Update the download counter (unless it's a thumbnail).
1658
	if ($attachment_type != 3)
1659
		$smcFunc['db_query']('attach_download_increase', '
1660
			UPDATE LOW_PRIORITY {db_prefix}attachments
1661
			SET downloads = downloads + 1
1662
			WHERE id_attach = {int:id_attach}',
1663
			array(
1664
				'id_attach' => $id_attach,
1665
			)
1666
		);
1667
1668
	$filename = getAttachmentFilename($real_filename, $_REQUEST['attach'], $id_folder, false, $file_hash);
1669
1670
	// This is done to clear any output that was made before now.
1671
	ob_end_clean();
1672
	if (!empty($modSettings['enableCompressedOutput']) && @filesize($filename) <= 4194304 && in_array($file_ext, array('txt', 'html', 'htm', 'js', 'doc', 'docx', 'rtf', 'css', 'php', 'log', 'xml', 'sql', 'c', 'java')))
1673
		@ob_start('ob_gzhandler');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1674
1675
	else
1676
	{
1677
		ob_start();
1678
		header('Content-Encoding: none');
1679
	}
1680
1681
	// No point in a nicer message, because this is supposed to be an attachment anyway...
1682 View Code Duplication
	if (!file_exists($filename))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1683
	{
1684
		header((preg_match('~HTTP/1\.[01]~i', $_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0') . ' 404 Not Found');
1685
		header('Content-Type: text/plain; charset=' . (empty($context['character_set']) ? 'ISO-8859-1' : $context['character_set']));
1686
1687
		// We need to die like this *before* we send any anti-caching headers as below.
1688
		die('File not found.');
1689
	}
1690
1691
	// If it hasn't been modified since the last time this attachment was retrieved, there's no need to display it again.
1692 View Code Duplication
	if (!empty($_SERVER['HTTP_IF_MODIFIED_SINCE']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1693
	{
1694
		list($modified_since) = explode(';', $_SERVER['HTTP_IF_MODIFIED_SINCE']);
1695
		if (strtotime($modified_since) >= filemtime($filename))
1696
		{
1697
			ob_end_clean();
1698
1699
			// Answer the question - no, it hasn't been modified ;).
1700
			header('HTTP/1.1 304 Not Modified');
1701
			exit;
1702
		}
1703
	}
1704
1705
	// Check whether the ETag was sent back, and cache based on that...
1706
	$eTag = '"' . substr($_REQUEST['attach'] . $real_filename . filemtime($filename), 0, 64) . '"';
1707 View Code Duplication
	if (!empty($_SERVER['HTTP_IF_NONE_MATCH']) && strpos($_SERVER['HTTP_IF_NONE_MATCH'], $eTag) !== false)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1708
	{
1709
		ob_end_clean();
1710
1711
		header('HTTP/1.1 304 Not Modified');
1712
		exit;
1713
	}
1714
1715
	// Send the attachment headers.
1716
	header('Pragma: ');
1717
1718
	if (!isBrowser('gecko'))
1719
		header('Content-Transfer-Encoding: binary');
1720
1721
	header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 525600 * 60) . ' GMT');
1722
	header('Last-Modified: ' . gmdate('D, d M Y H:i:s', filemtime($filename)) . ' GMT');
1723
	header('Accept-Ranges: bytes');
1724
	header('Connection: close');
1725
	header('ETag: ' . $eTag);
1726
1727
	// Make sure the mime type warrants an inline display.
1728
	if (isset($_REQUEST['image']) && !empty($mime_type) && strpos($mime_type, 'image/') !== 0)
1729
		unset($_REQUEST['image']);
1730
1731
	// Does this have a mime type?
1732
	elseif (!empty($mime_type) && (isset($_REQUEST['image']) || !in_array($file_ext, array('jpg', 'gif', 'jpeg', 'x-ms-bmp', 'png', 'psd', 'tiff', 'iff'))))
1733
		header('Content-Type: ' . strtr($mime_type, array('image/bmp' => 'image/x-ms-bmp')));
1734
1735 View Code Duplication
	else
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1736
	{
1737
		header('Content-Type: ' . (isBrowser('ie') || isBrowser('opera') ? 'application/octetstream' : 'application/octet-stream'));
1738
		if (isset($_REQUEST['image']))
1739
			unset($_REQUEST['image']);
1740
	}
1741
1742
	// Convert the file to UTF-8, cuz most browsers dig that.
1743
	$utf8name = !$context['utf8'] && function_exists('iconv') ? iconv($context['character_set'], 'UTF-8', $real_filename) : (!$context['utf8'] && function_exists('mb_convert_encoding') ? mb_convert_encoding($real_filename, 'UTF-8', $context['character_set']) : $real_filename);
1744
	$disposition = !isset($_REQUEST['image']) ? 'attachment' : 'inline';
1745
1746
	// Different browsers like different standards...
1747 View Code Duplication
	if (isBrowser('firefox'))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1748
		header('Content-Disposition: ' . $disposition . '; filename*=UTF-8\'\'' . rawurlencode(preg_replace_callback('~&#(\d{3,8});~', 'fixchar__callback', $utf8name)));
1749
1750
	elseif (isBrowser('opera'))
1751
		header('Content-Disposition: ' . $disposition . '; filename="' . preg_replace_callback('~&#(\d{3,8});~', 'fixchar__callback', $utf8name) . '"');
1752
1753
	elseif (isBrowser('ie'))
1754
		header('Content-Disposition: ' . $disposition . '; filename="' . urlencode(preg_replace_callback('~&#(\d{3,8});~', 'fixchar__callback', $utf8name)) . '"');
1755
1756
	else
1757
		header('Content-Disposition: ' . $disposition . '; filename="' . $utf8name . '"');
1758
1759
	// If this has an "image extension" - but isn't actually an image - then ensure it isn't cached cause of silly IE.
1760 View Code Duplication
	if (!isset($_REQUEST['image']) && in_array($file_ext, array('gif', 'jpg', 'bmp', 'png', 'jpeg', 'tiff')))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1761
		header('Cache-Control: no-cache');
1762
	else
1763
		header('Cache-Control: max-age=' . (525600 * 60) . ', private');
1764
1765
	header('Content-Length: ' . filesize($filename));
1766
1767
	// Try to buy some time...
1768
	@set_time_limit(600);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1769
1770
	// Recode line endings for text files, if enabled.
1771
	if (!empty($modSettings['attachmentRecodeLineEndings']) && !isset($_REQUEST['image']) && in_array($file_ext, array('txt', 'css', 'htm', 'html', 'php', 'xml')))
1772
	{
1773
		if (strpos($_SERVER['HTTP_USER_AGENT'], 'Windows') !== false)
1774
			$callback = function($buffer)
1775
			{
1776
				return preg_replace('~[\r]?\n~', "\r\n", $buffer);
1777
			};
1778
		elseif (strpos($_SERVER['HTTP_USER_AGENT'], 'Mac') !== false)
1779
			$callback = function($buffer)
1780
			{
1781
				return preg_replace('~[\r]?\n~', "\r", $buffer);
1782
			};
1783
		else
1784
			$callback = function($buffer)
1785
			{
1786
				return preg_replace('~[\r]?\n~', "\n", $buffer);
1787
			};
1788
	}
1789
1790
	// Since we don't do output compression for files this large...
1791
	if (filesize($filename) > 4194304)
1792
	{
1793
		// Forcibly end any output buffering going on.
1794
		while (@ob_get_level() > 0)
1795
			@ob_end_clean();
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1796
1797
		$fp = fopen($filename, 'rb');
1798
		while (!feof($fp))
1799
		{
1800
			if (isset($callback))
1801
				echo $callback(fread($fp, 8192));
1802
			else
1803
				echo fread($fp, 8192);
1804
			flush();
1805
		}
1806
		fclose($fp);
1807
	}
1808
	// On some of the less-bright hosts, readfile() is disabled.  It's just a faster, more byte safe, version of what's in the if.
1809
	elseif (isset($callback) || @readfile($filename) === null)
1810
		echo isset($callback) ? $callback(file_get_contents($filename)) : file_get_contents($filename);
1811
1812
	obExit(false);
1813
}
1814
1815
/**
1816
 * A sort function for putting unapproved attachments first.
1817
 * @param array $a An array of info about one attachment
1818
 * @param array $b An array of info about a second attachment
1819
 * @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
1820
 */
1821
function approved_attach_sort($a, $b)
1822
{
1823
	if ($a['is_approved'] == $b['is_approved'])
1824
		return 0;
1825
1826
	return $a['is_approved'] > $b['is_approved'] ? -1 : 1;
1827
}
1828
1829
/**
1830
 * In-topic quick moderation.
1831
 */
1832
function QuickInTopicModeration()
1833
{
1834
	global $sourcedir, $topic, $board, $user_info, $smcFunc, $modSettings, $context;
1835
1836
	// Check the session = get or post.
1837
	checkSession('request');
1838
1839
	require_once($sourcedir . '/RemoveTopic.php');
1840
1841
	if (empty($_REQUEST['msgs']))
1842
		redirectexit('topic=' . $topic . '.' . $_REQUEST['start']);
1843
1844
	$messages = array();
1845
	foreach ($_REQUEST['msgs'] as $dummy)
1846
		$messages[] = (int) $dummy;
1847
1848
	// We are restoring messages. We handle this in another place.
1849 View Code Duplication
	if (isset($_REQUEST['restore_selected']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1850
		redirectexit('action=restoretopic;msgs=' . implode(',', $messages) . ';' . $context['session_var'] . '=' . $context['session_id']);
1851
	if (isset($_REQUEST['split_selection']))
1852
	{
1853
		$request = $smcFunc['db_query']('', '
1854
			SELECT subject
1855
			FROM {db_prefix}messages
1856
			WHERE id_msg = {int:message}
1857
			LIMIT 1',
1858
			array(
1859
				'message' => min($messages),
1860
			)
1861
		);
1862
		list($subname) = $smcFunc['db_fetch_row']($request);
1863
		$smcFunc['db_free_result']($request);
1864
		$_SESSION['split_selection'][$topic] = $messages;
1865
		redirectexit('action=splittopics;sa=selectTopics;topic=' . $topic . '.0;subname_enc=' . urlencode($subname) . ';' . $context['session_var'] . '=' . $context['session_id']);
1866
	}
1867
1868
	// Allowed to delete any message?
1869
	if (allowedTo('delete_any'))
1870
		$allowed_all = true;
1871
	// Allowed to delete replies to their messages?
1872 View Code Duplication
	elseif (allowedTo('delete_replies'))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1873
	{
1874
		$request = $smcFunc['db_query']('', '
1875
			SELECT id_member_started
1876
			FROM {db_prefix}topics
1877
			WHERE id_topic = {int:current_topic}
1878
			LIMIT 1',
1879
			array(
1880
				'current_topic' => $topic,
1881
			)
1882
		);
1883
		list ($starter) = $smcFunc['db_fetch_row']($request);
1884
		$smcFunc['db_free_result']($request);
1885
1886
		$allowed_all = $starter == $user_info['id'];
1887
	}
1888
	else
1889
		$allowed_all = false;
1890
1891
	// Make sure they're allowed to delete their own messages, if not any.
1892
	if (!$allowed_all)
1893
		isAllowedTo('delete_own');
1894
1895
	// Allowed to remove which messages?
1896
	$request = $smcFunc['db_query']('', '
1897
		SELECT id_msg, subject, id_member, poster_time
1898
		FROM {db_prefix}messages
1899
		WHERE id_msg IN ({array_int:message_list})
1900
			AND id_topic = {int:current_topic}' . (!$allowed_all ? '
1901
			AND id_member = {int:current_member}' : '') . '
1902
		LIMIT {int:limit}',
1903
		array(
1904
			'current_member' => $user_info['id'],
1905
			'current_topic' => $topic,
1906
			'message_list' => $messages,
1907
			'limit' => count($messages),
1908
		)
1909
	);
1910
	$messages = array();
1911
	while ($row = $smcFunc['db_fetch_assoc']($request))
1912
	{
1913
		if (!$allowed_all && !empty($modSettings['edit_disable_time']) && $row['poster_time'] + $modSettings['edit_disable_time'] * 60 < time())
1914
			continue;
1915
1916
		$messages[$row['id_msg']] = array($row['subject'], $row['id_member']);
1917
	}
1918
	$smcFunc['db_free_result']($request);
1919
1920
	// Get the first message in the topic - because you can't delete that!
1921
	$request = $smcFunc['db_query']('', '
1922
		SELECT id_first_msg, id_last_msg
1923
		FROM {db_prefix}topics
1924
		WHERE id_topic = {int:current_topic}
1925
		LIMIT 1',
1926
		array(
1927
			'current_topic' => $topic,
1928
		)
1929
	);
1930
	list ($first_message, $last_message) = $smcFunc['db_fetch_row']($request);
1931
	$smcFunc['db_free_result']($request);
1932
1933
	// Delete all the messages we know they can delete. ($messages)
1934
	foreach ($messages as $message => $info)
1935
	{
1936
		// Just skip the first message - if it's not the last.
1937
		if ($message == $first_message && $message != $last_message)
1938
			continue;
1939
		// If the first message is going then don't bother going back to the topic as we're effectively deleting it.
1940
		elseif ($message == $first_message)
1941
			$topicGone = true;
1942
1943
		removeMessage($message);
1944
1945
		// Log this moderation action ;).
1946 View Code Duplication
		if (allowedTo('delete_any') && (!allowedTo('delete_own') || $info[1] != $user_info['id']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1947
			logAction('delete', array('topic' => $topic, 'subject' => $info[0], 'member' => $info[1], 'board' => $board));
1948
	}
1949
1950
	redirectexit(!empty($topicGone) ? 'board=' . $board : 'topic=' . $topic . '.' . $_REQUEST['start']);
1951
}
1952
1953
?>
0 ignored issues
show
Best Practice introduced by
It is not recommended to use PHP's closing tag ?> in files other than templates.

Using a closing tag in PHP files that only contain PHP code is not recommended as you might accidentally add whitespace after the closing tag which would then be output by PHP. This can cause severe problems, for example headers cannot be sent anymore.

A simple precaution is to leave off the closing tag as it is not required, and it also has no negative effects whatsoever.

Loading history...