Completed
Push — release-2.1 ( e6c696...22bfba )
by Mathias
07:04
created

Sources/Post.php (9 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/**
4
 * The job of this file is to handle everything related to posting replies,
5
 * new topics, quotes, and modifications to existing posts.  It also handles
6
 * quoting posts by way of javascript.
7
 * Simple Machines Forum (SMF)
8
 *
9
 * @package SMF
10
 * @author Simple Machines http://www.simplemachines.org
11
 * @copyright 2016 Simple Machines and individual contributors
12
 * @license http://www.simplemachines.org/about/smf/license.php BSD
13
 *
14
 * @version 2.1 Beta 3
15
 */
16
17
if (!defined('SMF'))
18
	die('No direct access...');
19
20
/**
21
 * Handles showing the post screen, loading the post to be modified, and loading any post quoted.
22
 *
23
 * - additionally handles previews of posts.
24
 * - @uses the Post template and language file, main sub template.
25
 * - requires different permissions depending on the actions, but most notably post_new, post_reply_own, and post_reply_any.
26
 * - shows options for the editing and posting of calendar events and attachments, as well as the posting of polls.
27
 * - accessed from ?action=post.
28
 *
29
 *  @param array $post_errors Holds any errors found while tyring to post
30
 */
31
function Post($post_errors = array())
32
{
33
	global $txt, $scripturl, $topic, $modSettings, $board;
34
	global $user_info, $context, $settings;
35
	global $sourcedir, $smcFunc, $language;
36
37
	loadLanguage('Post');
38
	if (!empty($modSettings['drafts_post_enabled']))
39
		loadLanguage('Drafts');
40
41
	// You can't reply with a poll... hacker.
42
	if (isset($_REQUEST['poll']) && !empty($topic) && !isset($_REQUEST['msg']))
43
		unset($_REQUEST['poll']);
44
45
	// Posting an event?
46
	$context['make_event'] = isset($_REQUEST['calendar']);
47
	$context['robot_no_index'] = true;
48
	$context['posting_fields'] = array();
49
50
	// Get notification preferences for later
51
	require_once($sourcedir . '/Subs-Notify.php');
52
	// use $temp to get around "Only variables should be passed by reference"
53
	$temp = getNotifyPrefs($user_info['id']);
54
	$context['notify_prefs'] = (array) array_pop($temp);
55
	$context['auto_notify'] = !empty($context['notify_prefs']['msg_auto_notify']);
56
57
	// You must be posting to *some* board.
58
	if (empty($board) && !$context['make_event'])
59
		fatal_lang_error('no_board', false);
60
61
	require_once($sourcedir . '/Subs-Post.php');
62
63
	if (isset($_REQUEST['xml']))
64
	{
65
		$context['sub_template'] = 'post';
66
67
		// Just in case of an earlier error...
68
		$context['preview_message'] = '';
69
		$context['preview_subject'] = '';
70
	}
71
72
	// No message is complete without a topic.
73 View Code Duplication
	if (empty($topic) && !empty($_REQUEST['msg']))
74
	{
75
		$request = $smcFunc['db_query']('', '
76
			SELECT id_topic
77
			FROM {db_prefix}messages
78
			WHERE id_msg = {int:msg}',
79
			array(
80
				'msg' => (int) $_REQUEST['msg'],
81
		));
82
		if ($smcFunc['db_num_rows']($request) != 1)
83
			unset($_REQUEST['msg'], $_POST['msg'], $_GET['msg']);
84
		else
85
			list ($topic) = $smcFunc['db_fetch_row']($request);
86
		$smcFunc['db_free_result']($request);
87
	}
88
89
	// Check if it's locked. It isn't locked if no topic is specified.
90
	if (!empty($topic))
91
	{
92
		$request = $smcFunc['db_query']('', '
93
			SELECT
94
				t.locked, COALESCE(ln.id_topic, 0) AS notify, t.is_sticky, t.id_poll, t.id_last_msg, mf.id_member,
95
				t.id_first_msg, mf.subject, ml.modified_reason,
96
				CASE WHEN ml.poster_time > ml.modified_time THEN ml.poster_time ELSE ml.modified_time END AS last_post_time
97
			FROM {db_prefix}topics AS t
98
				LEFT JOIN {db_prefix}log_notify AS ln ON (ln.id_topic = t.id_topic AND ln.id_member = {int:current_member})
99
				LEFT JOIN {db_prefix}messages AS mf ON (mf.id_msg = t.id_first_msg)
100
				LEFT JOIN {db_prefix}messages AS ml ON (ml.id_msg = t.id_last_msg)
101
			WHERE t.id_topic = {int:current_topic}
102
			LIMIT 1',
103
			array(
104
				'current_member' => $user_info['id'],
105
				'current_topic' => $topic,
106
			)
107
		);
108
		list ($locked, $context['notify'], $sticky, $pollID, $context['topic_last_message'], $id_member_poster, $id_first_msg, $first_subject, $editReason, $lastPostTime) = $smcFunc['db_fetch_row']($request);
109
		$smcFunc['db_free_result']($request);
110
111
		// If this topic already has a poll, they sure can't add another.
112
		if (isset($_REQUEST['poll']) && $pollID > 0)
113
			unset($_REQUEST['poll']);
114
115
		if (empty($_REQUEST['msg']))
116
		{
117
			if ($user_info['is_guest'] && !allowedTo('post_reply_any') && (!$modSettings['postmod_active'] || !allowedTo('post_unapproved_replies_any')))
118
				is_not_guest();
119
120
			// By default the reply will be approved...
121
			$context['becomes_approved'] = true;
122
			if ($id_member_poster != $user_info['id'] || $user_info['is_guest'])
123
			{
124
				if ($modSettings['postmod_active'] && allowedTo('post_unapproved_replies_any') && !allowedTo('post_reply_any'))
125
					$context['becomes_approved'] = false;
126
				else
127
					isAllowedTo('post_reply_any');
128
			}
129
			elseif (!allowedTo('post_reply_any'))
130
			{
131
				if ($modSettings['postmod_active'] && ((allowedTo('post_unapproved_replies_own') && !allowedTo('post_reply_own')) || allowedTo('post_unapproved_replies_any')))
132
					$context['becomes_approved'] = false;
133
				else
134
					isAllowedTo('post_reply_own');
135
			}
136
		}
137
		else
138
			$context['becomes_approved'] = true;
139
140
		$context['can_lock'] = allowedTo('lock_any') || ($user_info['id'] == $id_member_poster && allowedTo('lock_own'));
141
		$context['can_sticky'] = allowedTo('make_sticky');
142
143
		// We don't always want the request vars to override what's in the db...
144
		$context['already_locked'] = $locked;
145
		$context['already_sticky'] = $sticky;
146
		$context['notify'] = !empty($context['notify']);
147
		$context['sticky'] = isset($_REQUEST['sticky']) ? !empty($_REQUEST['sticky']) : $sticky;
148
149
		// Check whether this is a really old post being bumped...
150
		if (!empty($modSettings['oldTopicDays']) && $lastPostTime + $modSettings['oldTopicDays'] * 86400 < time() && empty($sticky) && !isset($_REQUEST['subject']))
151
			$post_errors[] = array('old_topic', array($modSettings['oldTopicDays']));
152
	}
153
	else
154
	{
155
		$context['becomes_approved'] = true;
156
		if ((!$context['make_event'] || !empty($board)))
157
		{
158
			if ($modSettings['postmod_active'] && !allowedTo('post_new') && allowedTo('post_unapproved_topics'))
159
				$context['becomes_approved'] = false;
160
			else
161
				isAllowedTo('post_new');
162
		}
163
164
		$locked = 0;
165
166
		// @todo These won't work if you're making an event.
167
		$context['can_lock'] = allowedTo(array('lock_any', 'lock_own'));
168
		$context['can_sticky'] = allowedTo('make_sticky');
169
		$context['already_sticky'] = 0;
170
		$context['already_locked'] = 0;
171
		$context['notify'] = !empty($context['notify']);
172
		$context['sticky'] = !empty($_REQUEST['sticky']);
173
	}
174
175
	// @todo These won't work if you're posting an event!
176
	$context['can_notify'] = !$context['user']['is_guest'];
177
	$context['can_move'] = allowedTo('move_any');
178
	$context['move'] = !empty($_REQUEST['move']);
179
	$context['announce'] = !empty($_REQUEST['announce']);
180
	// You can only announce topics that will get approved...
181
	$context['can_announce'] = allowedTo('announce_topic') && $context['becomes_approved'];
182
	$context['locked'] = !empty($locked) || !empty($_REQUEST['lock']);
183
	$context['can_quote'] = empty($modSettings['disabledBBC']) || !in_array('quote', explode(',', $modSettings['disabledBBC']));
184
185
	// Generally don't show the approval box... (Assume we want things approved)
186
	$context['show_approval'] = allowedTo('approve_posts') && $context['becomes_approved'] ? 2 : (allowedTo('approve_posts') ? 1 : 0);
187
188
	// An array to hold all the attachments for this topic.
189
	$context['current_attachments'] = array();
190
191
	// If there are attachments already uploaded, pass them to the current attachments array.
192
	if (!empty($_SESSION['already_attached']))
193
	{
194
		require_once($sourcedir . '/Subs-Attachments.php');
195
		$context['current_attachments'] = getRawAttachInfo($_SESSION['already_attached']);
196
	}
197
198
	// Don't allow a post if it's locked and you aren't all powerful.
199
	if ($locked && !allowedTo('moderate_board'))
200
		fatal_lang_error('topic_locked', false);
201
	// Check the users permissions - is the user allowed to add or post a poll?
202
	if (isset($_REQUEST['poll']) && $modSettings['pollMode'] == '1')
203
	{
204
		// New topic, new poll.
205 View Code Duplication
		if (empty($topic))
206
			isAllowedTo('poll_post');
207
		// This is an old topic - but it is yours!  Can you add to it?
208
		elseif ($user_info['id'] == $id_member_poster && !allowedTo('poll_add_any'))
209
			isAllowedTo('poll_add_own');
210
		// If you're not the owner, can you add to any poll?
211
		else
212
			isAllowedTo('poll_add_any');
213
214
		require_once($sourcedir . '/Subs-Members.php');
215
		$allowedVoteGroups = groupsAllowedTo('poll_vote', $board);
216
217
		// Set up the poll options.
218
		$context['poll_options'] = array(
219
			'max_votes' => empty($_POST['poll_max_votes']) ? '1' : max(1, $_POST['poll_max_votes']),
220
			'hide' => empty($_POST['poll_hide']) ? 0 : $_POST['poll_hide'],
221
			'expire' => !isset($_POST['poll_expire']) ? '' : $_POST['poll_expire'],
222
			'change_vote' => isset($_POST['poll_change_vote']),
223
			'guest_vote' => isset($_POST['poll_guest_vote']),
224
			'guest_vote_enabled' => in_array(-1, $allowedVoteGroups['allowed']),
225
		);
226
227
		// Make all five poll choices empty.
228
		$context['choices'] = array(
229
			array('id' => 0, 'number' => 1, 'label' => '', 'is_last' => false),
230
			array('id' => 1, 'number' => 2, 'label' => '', 'is_last' => false),
231
			array('id' => 2, 'number' => 3, 'label' => '', 'is_last' => false),
232
			array('id' => 3, 'number' => 4, 'label' => '', 'is_last' => false),
233
			array('id' => 4, 'number' => 5, 'label' => '', 'is_last' => true)
234
		);
235
		$context['last_choice_id'] = 4;
236
	}
237
238
	if ($context['make_event'])
239
	{
240
		// They might want to pick a board.
241
		if (!isset($context['current_board']))
242
			$context['current_board'] = 0;
243
244
		// Start loading up the event info.
245
		$context['event'] = array();
246
		$context['event']['title'] = isset($_REQUEST['evtitle']) ? $smcFunc['htmlspecialchars'](stripslashes($_REQUEST['evtitle'])) : '';
247
		$context['event']['location'] = isset($_REQUEST['event_location']) ? $smcFunc['htmlspecialchars'](stripslashes($_REQUEST['event_location'])) : '';
248
249
		$context['event']['id'] = isset($_REQUEST['eventid']) ? (int) $_REQUEST['eventid'] : -1;
250
		$context['event']['new'] = $context['event']['id'] == -1;
251
252
		// Permissions check!
253
		isAllowedTo('calendar_post');
254
255
		// We want a fairly compact version of the time, but as close as possible to the user's settings.
256 View Code Duplication
		if (preg_match('~%[HkIlMpPrRSTX](?:[^%]*%[HkIlMpPrRSTX])*~', $user_info['time_format'], $matches) == 0 || empty($matches[0]))
0 ignored issues
show
This code seems to be duplicated across your project.

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

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

Loading history...
257
			$time_string = '%k:%M';
258
		else
259
			$time_string = str_replace(array('%I', '%H', '%S', '%r', '%R', '%T'), array('%l', '%k', '', '%l:%M %p', '%k:%M', '%l:%M'), $matches[0]);
260
261
		$js_time_string = str_replace(
262
			array('%H', '%k', '%I', '%l', '%M', '%p', '%P', '%r',      '%R',  '%S', '%T',    '%X'),
263
			array('H',  'G',  'h',  'g',  'i',  'A',  'a',  'h:i:s A', 'H:i', 's',  'H:i:s', 'H:i:s'),
264
			$time_string
265
		);
266
267
		// Editing an event?  (but NOT previewing!?)
268
		if (empty($context['event']['new']) && !isset($_REQUEST['subject']))
269
		{
270
			// If the user doesn't have permission to edit the post in this topic, redirect them.
271
			if ((empty($id_member_poster) || $id_member_poster != $user_info['id'] || !allowedTo('modify_own')) && !allowedTo('modify_any'))
272
			{
273
				require_once($sourcedir . '/Calendar.php');
274
				return CalendarPost();
275
			}
276
277
			// Get the current event information.
278
			require_once($sourcedir . '/Subs-Calendar.php');
279
			$eventProperties = getEventProperties($context['event']['id']);
280
			$context['event'] = array_merge($context['event'], $eventProperties);
281
		}
282
		else
283
		{
284
			// Get the current event information.
285
			require_once($sourcedir . '/Subs-Calendar.php');
286
			$eventProperties = getNewEventDatetimes();
287
			$context['event'] = array_merge($context['event'], $eventProperties);
288
289
			// Make sure the year and month are in the valid range.
290
			if ($context['event']['month'] < 1 || $context['event']['month'] > 12)
291
				fatal_lang_error('invalid_month', false);
292 View Code Duplication
			if ($context['event']['year'] < $modSettings['cal_minyear'] || $context['event']['year'] > $modSettings['cal_maxyear'])
293
				fatal_lang_error('invalid_year', false);
294
295
			// Get a list of boards they can post in.
296
			$boards = boardsAllowedTo('post_new');
297
			if (empty($boards))
298
				fatal_lang_error('cannot_post_new', 'user');
299
300
			// Load a list of boards for this event in the context.
301
			require_once($sourcedir . '/Subs-MessageIndex.php');
302
			$boardListOptions = array(
303
				'included_boards' => in_array(0, $boards) ? null : $boards,
304
				'not_redirection' => true,
305
				'use_permissions' => true,
306
				'selected_board' => empty($context['current_board']) ? $modSettings['cal_defaultboard'] : $context['current_board'],
307
			);
308
			$context['event']['categories'] = getBoardList($boardListOptions);
309
		}
310
311
		// Find the last day of the month.
312
		$context['event']['last_day'] = (int) strftime('%d', mktime(0, 0, 0, $context['event']['month'] == 12 ? 1 : $context['event']['month'] + 1, 0, $context['event']['month'] == 12 ? $context['event']['year'] + 1 : $context['event']['year']));
313
314
		// An all day event? Set up some nice defaults in case the user wants to change that
315 View Code Duplication
		if ($context['event']['allday'] == true)
0 ignored issues
show
This code seems to be duplicated across your project.

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

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

Loading history...
316
		{
317
			$context['event']['tz'] = getUserTimezone();
318
			$context['event']['start_time'] = timeformat(time(), $time_string);
0 ignored issues
show
$time_string is of type string, but the function expects a boolean.

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...
319
			$context['event']['end_time'] = timeformat(time() + 3600, $time_string);
0 ignored issues
show
$time_string is of type string, but the function expects a boolean.

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...
320
		}
321
		// Otherwise, just adjust these to look nice on the input form
322
		else
323
		{
324
			$context['event']['start_time'] = timeformat(strtotime($context['event']['start_iso_gmdate']), $time_string);
0 ignored issues
show
$time_string is of type string, but the function expects a boolean.

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...
325
			$context['event']['end_time'] = timeformat(strtotime($context['event']['end_iso_gmdate']), $time_string);
0 ignored issues
show
$time_string is of type string, but the function expects a boolean.

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...
326
		}
327
328
		// Need this so the user can select a timezone for the event.
329
		$context['all_timezones'] = smf_list_timezones($context['event']['start_date']);
330
		unset($context['all_timezones']['']);
331
332
		// If the event's timezone is not in SMF's standard list of time zones, prepend it to the list
333 View Code Duplication
		if (!in_array($context['event']['tz'], array_keys($context['all_timezones'])))
0 ignored issues
show
This code seems to be duplicated across your project.

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

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

Loading history...
334
		{
335
			$d = date_create($context['event']['tz']);
336
			$context['all_timezones'] = array($context['event']['tz'] => date_format($d, 'T') . ' - ' . $context['event']['tz'] . ' [UTC' . date_format($d, 'P') . ']') + $context['all_timezones'];
337
		}
338
339
		loadCSSFile('jquery-ui.datepicker.css', array('defer' => false), 'smf_datepicker');
340
		loadCSSFile('jquery.timepicker.css', array('defer' => false), 'smf_timepicker');
341
		loadJavaScriptFile('jquery-ui.datepicker.min.js', array('defer' => true), 'smf_datepicker');
342
		loadJavaScriptFile('jquery.timepicker.min.js', array('defer' => true), 'smf_timepicker');
343
		loadJavaScriptFile('datepair.min.js', array('defer' => true), 'smf_datepair');
344
		addInlineJavaScript('
345
	$("#allday").click(function(){
346
		$("#start_time").attr("disabled", this.checked);
347
		$("#end_time").attr("disabled", this.checked);
348
		$("#tz").attr("disabled", this.checked);
349
	});
350
	$("#event_time_input .date_input").datepicker({
351
		dateFormat: "yy-mm-dd",
352
		autoSize: true,
353
		isRTL: ' . ($context['right_to_left'] ? 'true' : 'false') . ',
354
		constrainInput: true,
355
		showAnim: "",
356
		showButtonPanel: false,
357
		minDate: "' . $modSettings['cal_minyear'] . '-01-01",
358
		maxDate: "' . $modSettings['cal_maxyear'] . '-12-31",
359
		yearRange: "' . $modSettings['cal_minyear'] . ':' . $modSettings['cal_maxyear'] . '",
360
		hideIfNoPrevNext: true,
361
		monthNames: ["' . implode('", "', $txt['months_titles']) . '"],
362
		monthNamesShort: ["' . implode('", "', $txt['months_short']) . '"],
363
		dayNames: ["' . implode('", "', $txt['days']) . '"],
364
		dayNamesShort: ["' . implode('", "', $txt['days_short']) . '"],
365
		dayNamesMin: ["' . implode('", "', $txt['days_short']) . '"],
366
		prevText: "' . $txt['prev_month'] . '",
367
		nextText: "' . $txt['next_month'] . '",
368
	});
369
	$(".time_input").timepicker({
370
		timeFormat: "' . $js_time_string . '",
371
		showDuration: true,
372
		maxTime: "23:59:59",
373
	});
374
	var date_entry = document.getElementById("event_time_input");
375
	var date_entry_pair = new Datepair(date_entry, {
376
		timeClass: "time_input",
377
		dateClass: "date_input",
378
		parseDate: function (el) {
379
		    var utc = new Date($(el).datepicker("getDate"));
380
		    return utc && new Date(utc.getTime() + (utc.getTimezoneOffset() * 60000));
381
		},
382
		updateDate: function (el, v) {
383
		    $(el).datepicker("setDate", new Date(v.getTime() - (v.getTimezoneOffset() * 60000)));
384
		}
385
	});
386
	', true);
387
388
		$context['event']['board'] = !empty($board) ? $board : $modSettings['cal_defaultboard'];
389
		$context['event']['topic'] = !empty($topic) ? $topic : 0;
390
	}
391
392
	// See if any new replies have come along.
393
	// Huh, $_REQUEST['msg'] is set upon submit, so this doesn't get executed at submit
394
	// only at preview
395
	if (empty($_REQUEST['msg']) && !empty($topic))
396
	{
397
		if (isset($_REQUEST['last_msg']) && $context['topic_last_message'] > $_REQUEST['last_msg'])
398
		{
399
			$request = $smcFunc['db_query']('', '
400
				SELECT COUNT(*)
401
				FROM {db_prefix}messages
402
				WHERE id_topic = {int:current_topic}
403
					AND id_msg > {int:last_msg}' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : '
404
					AND approved = {int:approved}') . '
405
				LIMIT 1',
406
				array(
407
					'current_topic' => $topic,
408
					'last_msg' => (int) $_REQUEST['last_msg'],
409
					'approved' => 1,
410
				)
411
			);
412
			list ($context['new_replies']) = $smcFunc['db_fetch_row']($request);
413
			$smcFunc['db_free_result']($request);
414
415
			if (!empty($context['new_replies']))
416
			{
417
				if ($context['new_replies'] == 1)
418
					$txt['error_new_replies'] = isset($_GET['last_msg']) ? $txt['error_new_reply_reading'] : $txt['error_new_reply'];
419
				else
420
					$txt['error_new_replies'] = sprintf(isset($_GET['last_msg']) ? $txt['error_new_replies_reading'] : $txt['error_new_replies'], $context['new_replies']);
421
422
				$post_errors[] = 'new_replies';
423
424
				$modSettings['topicSummaryPosts'] = $context['new_replies'] > $modSettings['topicSummaryPosts'] ? max($modSettings['topicSummaryPosts'], 5) : $modSettings['topicSummaryPosts'];
425
			}
426
		}
427
	}
428
429
	// Get a response prefix (like 'Re:') in the default forum language.
430 View Code Duplication
	if (!isset($context['response_prefix']) && !($context['response_prefix'] = cache_get_data('response_prefix')))
431
	{
432
		if ($language === $user_info['language'])
433
			$context['response_prefix'] = $txt['response_prefix'];
434
		else
435
		{
436
			loadLanguage('index', $language, false);
437
			$context['response_prefix'] = $txt['response_prefix'];
438
			loadLanguage('index');
439
		}
440
		cache_put_data('response_prefix', $context['response_prefix'], 600);
441
	}
442
443
	// Previewing, modifying, or posting?
444
	// Do we have a body, but an error happened.
445
	if (isset($_REQUEST['message']) || isset($_REQUEST['quickReply']) || !empty($context['post_error']))
446
	{
447
		if (isset($_REQUEST['quickReply']))
448
			$_REQUEST['message'] = $_REQUEST['quickReply'];
449
450
		// Validate inputs.
451
		if (empty($context['post_error']))
452
		{
453
			// This means they didn't click Post and get an error.
454
			$really_previewing = true;
455
456
		}
457
		else
458
		{
459
			if (!isset($_REQUEST['subject']))
460
				$_REQUEST['subject'] = '';
461
			if (!isset($_REQUEST['message']))
462
				$_REQUEST['message'] = '';
463
			if (!isset($_REQUEST['icon']))
464
				$_REQUEST['icon'] = 'xx';
465
466
			// They are previewing if they asked to preview (i.e. came from quick reply).
467
			$really_previewing = !empty($_POST['preview']);
468
		}
469
470
		// In order to keep the approval status flowing through, we have to pass it through the form...
471
		$context['becomes_approved'] = empty($_REQUEST['not_approved']);
472
		$context['show_approval'] = isset($_REQUEST['approve']) ? ($_REQUEST['approve'] ? 2 : 1) : 0;
473
		$context['can_announce'] &= $context['becomes_approved'];
474
475
		// Set up the inputs for the form.
476
		$form_subject = strtr($smcFunc['htmlspecialchars']($_REQUEST['subject']), array("\r" => '', "\n" => '', "\t" => ''));
477
		$form_message = $smcFunc['htmlspecialchars']($_REQUEST['message'], ENT_QUOTES);
478
479
		// Make sure the subject isn't too long - taking into account special characters.
480
		if ($smcFunc['strlen']($form_subject) > 100)
481
			$form_subject = $smcFunc['substr']($form_subject, 0, 100);
482
483
		if (isset($_REQUEST['poll']))
484
		{
485
			$context['question'] = isset($_REQUEST['question']) ? $smcFunc['htmlspecialchars'](trim($_REQUEST['question'])) : '';
486
487
			$context['choices'] = array();
488
			$choice_id = 0;
489
490
			$_POST['options'] = empty($_POST['options']) ? array() : htmlspecialchars__recursive($_POST['options']);
491
			foreach ($_POST['options'] as $option)
492
			{
493
				if (trim($option) == '')
494
					continue;
495
496
				$context['choices'][] = array(
497
					'id' => $choice_id++,
498
					'number' => $choice_id,
499
					'label' => $option,
500
					'is_last' => false
501
				);
502
			}
503
504
			// One empty option for those with js disabled...I know are few... :P
505
			$context['choices'][] = array(
506
				'id' => $choice_id++,
507
				'number' => $choice_id,
508
				'label' => '',
509
				'is_last' => false
510
			);
511
512 View Code Duplication
			if (count($context['choices']) < 2)
513
			{
514
				$context['choices'][] = array(
515
					'id' => $choice_id++,
516
					'number' => $choice_id,
517
					'label' => '',
518
					'is_last' => false
519
				);
520
			}
521
			$context['last_choice_id'] = $choice_id;
522
			$context['choices'][count($context['choices']) - 1]['is_last'] = true;
523
		}
524
525
		// Are you... a guest?
526
		if ($user_info['is_guest'])
527
		{
528
			$_REQUEST['guestname'] = !isset($_REQUEST['guestname']) ? '' : trim($_REQUEST['guestname']);
529
			$_REQUEST['email'] = !isset($_REQUEST['email']) ? '' : trim($_REQUEST['email']);
530
531
			$_REQUEST['guestname'] = $smcFunc['htmlspecialchars']($_REQUEST['guestname']);
532
			$context['name'] = $_REQUEST['guestname'];
533
			$_REQUEST['email'] = $smcFunc['htmlspecialchars']($_REQUEST['email']);
534
			$context['email'] = $_REQUEST['email'];
535
536
			$user_info['name'] = $_REQUEST['guestname'];
537
		}
538
539
		// Only show the preview stuff if they hit Preview.
540
		if (($really_previewing == true || isset($_REQUEST['xml'])) && !isset($_REQUEST['save_draft']))
541
		{
542
			// Set up the preview message and subject and censor them...
543
			$context['preview_message'] = $form_message;
544
			preparsecode($form_message, true);
545
			preparsecode($context['preview_message']);
546
547
			// Do all bulletin board code tags, with or without smileys.
548
			$context['preview_message'] = parse_bbc($context['preview_message'], isset($_REQUEST['ns']) ? 0 : 1);
549
			censorText($context['preview_message']);
550
551
			if ($form_subject != '')
552
			{
553
				$context['preview_subject'] = $form_subject;
554
555
				censorText($context['preview_subject']);
556
			}
557
			else
558
				$context['preview_subject'] = '<em>' . $txt['no_subject'] . '</em>';
559
560
			// Protect any CDATA blocks.
561
			if (isset($_REQUEST['xml']))
562
				$context['preview_message'] = strtr($context['preview_message'], array(']]>' => ']]]]><![CDATA[>'));
563
		}
564
565
		// Set up the checkboxes.
566
		$context['notify'] = !empty($_REQUEST['notify']);
567
		$context['use_smileys'] = !isset($_REQUEST['ns']);
568
569
		$context['icon'] = isset($_REQUEST['icon']) ? preg_replace('~[\./\\\\*\':"<>]~', '', $_REQUEST['icon']) : 'xx';
570
571
		// Set the destination action for submission.
572
		$context['destination'] = 'post2;start=' . $_REQUEST['start'] . (isset($_REQUEST['msg']) ? ';msg=' . $_REQUEST['msg'] . ';' . $context['session_var'] . '=' . $context['session_id'] : '') . (isset($_REQUEST['poll']) ? ';poll' : '');
573
		$context['submit_label'] = isset($_REQUEST['msg']) ? $txt['save'] : $txt['post'];
574
575
		// Previewing an edit?
576
		if (isset($_REQUEST['msg']) && !empty($topic))
577
		{
578
			// Get the existing message. Previewing.
579
			$request = $smcFunc['db_query']('', '
580
				SELECT
581
					m.id_member, m.modified_time, m.smileys_enabled, m.body,
582
					m.poster_name, m.poster_email, m.subject, m.icon, m.approved,
583
					COALESCE(a.size, -1) AS filesize, a.filename, a.id_attach,
584
					a.approved AS attachment_approved, t.id_member_started AS id_member_poster,
585
					m.poster_time, log.id_action
586
				FROM {db_prefix}messages AS m
587
					INNER JOIN {db_prefix}topics AS t ON (t.id_topic = {int:current_topic})
588
					LEFT JOIN {db_prefix}attachments AS a ON (a.id_msg = m.id_msg AND a.attachment_type = {int:attachment_type})
589
					LEFT JOIN {db_prefix}log_actions AS log ON (m.id_topic = log.id_topic AND log.action = {string:announce_action})
590
				WHERE m.id_msg = {int:id_msg}
591
					AND m.id_topic = {int:current_topic}',
592
				array(
593
					'current_topic' => $topic,
594
					'attachment_type' => 0,
595
					'id_msg' => $_REQUEST['msg'],
596
					'announce_action' => 'announce_topic',
597
				)
598
			);
599
			// The message they were trying to edit was most likely deleted.
600
			// @todo Change this error message?
601
			if ($smcFunc['db_num_rows']($request) == 0)
602
				fatal_lang_error('no_board', false);
0 ignored issues
show
false is of type boolean, but the function expects a string.

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...
603
			$row = $smcFunc['db_fetch_assoc']($request);
604
605
			$attachment_stuff = array($row);
606
			while ($row2 = $smcFunc['db_fetch_assoc']($request))
607
				$attachment_stuff[] = $row2;
608
			$smcFunc['db_free_result']($request);
609
610 View Code Duplication
			if ($row['id_member'] == $user_info['id'] && !allowedTo('modify_any'))
611
			{
612
				// Give an extra five minutes over the disable time threshold, so they can type - assuming the post is public.
613
				if ($row['approved'] && !empty($modSettings['edit_disable_time']) && $row['poster_time'] + ($modSettings['edit_disable_time'] + 5) * 60 < time())
614
					fatal_lang_error('modify_post_time_passed', false);
615
				elseif ($row['id_member_poster'] == $user_info['id'] && !allowedTo('modify_own'))
616
					isAllowedTo('modify_replies');
617
				else
618
					isAllowedTo('modify_own');
619
			}
620
			elseif ($row['id_member_poster'] == $user_info['id'] && !allowedTo('modify_any'))
621
				isAllowedTo('modify_replies');
622
			else
623
				isAllowedTo('modify_any');
624
625 View Code Duplication
			if ($context['can_announce'] && !empty($row['id_action']))
626
			{
627
				loadLanguage('Errors');
628
				$context['post_error']['messages'][] = $txt['error_topic_already_announced'];
629
			}
630
631
			if (!empty($modSettings['attachmentEnable']))
632
			{
633
				$request = $smcFunc['db_query']('', '
634
					SELECT COALESCE(size, -1) AS filesize, filename, id_attach, approved, mime_type, id_thumb
635
					FROM {db_prefix}attachments
636
					WHERE id_msg = {int:id_msg}
637
						AND attachment_type = {int:attachment_type}
638
					ORDER BY id_attach',
639
					array(
640
						'id_msg' => (int) $_REQUEST['msg'],
641
						'attachment_type' => 0,
642
					)
643
				);
644
645
				while ($row = $smcFunc['db_fetch_assoc']($request))
646
				{
647
					if ($row['filesize'] <= 0)
648
						continue;
649
					$context['current_attachments'][$row['id_attach']] = array(
650
						'name' => $smcFunc['htmlspecialchars']($row['filename']),
651
						'size' => $row['filesize'],
652
						'attachID' => $row['id_attach'],
653
						'approved' => $row['approved'],
654
						'mime_type' => $row['mime_type'],
655
						'thumb' => $row['id_thumb'],
656
					);
657
				}
658
				$smcFunc['db_free_result']($request);
659
			}
660
661
			// Allow moderators to change names....
662
			if (allowedTo('moderate_forum') && !empty($topic))
663
			{
664
				$request = $smcFunc['db_query']('', '
665
					SELECT id_member, poster_name, poster_email
666
					FROM {db_prefix}messages
667
					WHERE id_msg = {int:id_msg}
668
						AND id_topic = {int:current_topic}
669
					LIMIT 1',
670
					array(
671
						'current_topic' => $topic,
672
						'id_msg' => (int) $_REQUEST['msg'],
673
					)
674
				);
675
				$row = $smcFunc['db_fetch_assoc']($request);
676
				$smcFunc['db_free_result']($request);
677
678 View Code Duplication
				if (empty($row['id_member']))
679
				{
680
					$context['name'] = $smcFunc['htmlspecialchars']($row['poster_name']);
681
					$context['email'] = $smcFunc['htmlspecialchars']($row['poster_email']);
682
				}
683
			}
684
		}
685
686
		// No check is needed, since nothing is really posted.
687
		checkSubmitOnce('free');
688
	}
689
	// Editing a message...
690
	elseif (isset($_REQUEST['msg']) && !empty($topic))
691
	{
692
		$context['editing'] = true;
693
694
		$_REQUEST['msg'] = (int) $_REQUEST['msg'];
695
696
		// Get the existing message. Editing.
697
		$request = $smcFunc['db_query']('', '
698
			SELECT
699
				m.id_member, m.modified_time, m.modified_name, m.modified_reason, m.smileys_enabled, m.body,
700
				m.poster_name, m.poster_email, m.subject, m.icon, m.approved,
701
				COALESCE(a.size, -1) AS filesize, a.filename, a.id_attach, a.mime_type, a.id_thumb,
702
				a.approved AS attachment_approved, t.id_member_started AS id_member_poster,
703
				m.poster_time, log.id_action
704
			FROM {db_prefix}messages AS m
705
				INNER JOIN {db_prefix}topics AS t ON (t.id_topic = {int:current_topic})
706
				LEFT JOIN {db_prefix}attachments AS a ON (a.id_msg = m.id_msg AND a.attachment_type = {int:attachment_type})
707
					LEFT JOIN {db_prefix}log_actions AS log ON (m.id_topic = log.id_topic AND log.action = {string:announce_action})
708
			WHERE m.id_msg = {int:id_msg}
709
				AND m.id_topic = {int:current_topic}',
710
			array(
711
				'current_topic' => $topic,
712
				'attachment_type' => 0,
713
				'id_msg' => $_REQUEST['msg'],
714
				'announce_action' => 'announce_topic',
715
			)
716
		);
717
		// The message they were trying to edit was most likely deleted.
718
		if ($smcFunc['db_num_rows']($request) == 0)
719
			fatal_lang_error('no_message', false);
720
		$row = $smcFunc['db_fetch_assoc']($request);
721
722
		$attachment_stuff = array($row);
723
		while ($row2 = $smcFunc['db_fetch_assoc']($request))
724
			$attachment_stuff[] = $row2;
725
		$smcFunc['db_free_result']($request);
726
727 View Code Duplication
		if ($row['id_member'] == $user_info['id'] && !allowedTo('modify_any'))
728
		{
729
			// Give an extra five minutes over the disable time threshold, so they can type - assuming the post is public.
730
			if ($row['approved'] && !empty($modSettings['edit_disable_time']) && $row['poster_time'] + ($modSettings['edit_disable_time'] + 5) * 60 < time())
731
				fatal_lang_error('modify_post_time_passed', false);
732
			elseif ($row['id_member_poster'] == $user_info['id'] && !allowedTo('modify_own'))
733
				isAllowedTo('modify_replies');
734
			else
735
				isAllowedTo('modify_own');
736
		}
737
		elseif ($row['id_member_poster'] == $user_info['id'] && !allowedTo('modify_any'))
738
			isAllowedTo('modify_replies');
739
		else
740
			isAllowedTo('modify_any');
741
742 View Code Duplication
		if ($context['can_announce'] && !empty($row['id_action']))
743
		{
744
			loadLanguage('Errors');
745
			$context['post_error']['messages'][] = $txt['error_topic_already_announced'];
746
		}
747
748
		// When was it last modified?
749
		if (!empty($row['modified_time']))
750
		{
751
			$context['last_modified'] = timeformat($row['modified_time']);
752
			$context['last_modified_reason'] = censorText($row['modified_reason']);
753
			$context['last_modified_text'] = sprintf($txt['last_edit_by'], $context['last_modified'], $row['modified_name']) . empty($row['modified_reason']) ? '' : '&nbsp;' . $txt['last_edit_reason'] . ':&nbsp;' . $row['modified_reason'];
754
		}
755
756
		// Get the stuff ready for the form.
757
		$form_subject = $row['subject'];
758
		$form_message = un_preparsecode($row['body']);
759
		censorText($form_message);
760
		censorText($form_subject);
761
762
		// Check the boxes that should be checked.
763
		$context['use_smileys'] = !empty($row['smileys_enabled']);
764
		$context['icon'] = $row['icon'];
765
766
		// Show an "approve" box if the user can approve it, and the message isn't approved.
767
		if (!$row['approved'] && !$context['show_approval'])
768
			$context['show_approval'] = allowedTo('approve_posts');
769
770
		// Sort the attachments so they are in the order saved
771
		$temp = array();
772
		foreach ($attachment_stuff as $attachment)
773
		{
774
			if ($attachment['filesize'] >= 0 && !empty($modSettings['attachmentEnable']))
775
				$temp[$attachment['id_attach']] = $attachment;
776
777
		}
778
		ksort($temp);
779
780
		// Load up 'em attachments!
781
		foreach ($temp as $attachment)
782
		{
783
			$context['current_attachments'][$attachment['id_attach']] = array(
784
				'name' => $smcFunc['htmlspecialchars']($attachment['filename']),
785
				'size' => $attachment['filesize'],
786
				'attachID' => $attachment['id_attach'],
787
				'approved' => $attachment['attachment_approved'],
788
				'mime_type' => $attachment['mime_type'],
789
				'thumb' => $attachment['id_thumb'],
790
			);
791
		}
792
793
		// Allow moderators to change names....
794 View Code Duplication
		if (allowedTo('moderate_forum') && empty($row['id_member']))
795
		{
796
			$context['name'] = $smcFunc['htmlspecialchars']($row['poster_name']);
797
			$context['email'] = $smcFunc['htmlspecialchars']($row['poster_email']);
798
		}
799
800
		// Set the destination.
801
		$context['destination'] = 'post2;start=' . $_REQUEST['start'] . ';msg=' . $_REQUEST['msg'] . ';' . $context['session_var'] . '=' . $context['session_id'] . (isset($_REQUEST['poll']) ? ';poll' : '');
802
		$context['submit_label'] = $txt['save'];
803
	}
804
	// Posting...
805
	else
806
	{
807
		// By default....
808
		$context['use_smileys'] = true;
809
		$context['icon'] = 'xx';
810
811
		if ($user_info['is_guest'])
812
		{
813
			$context['name'] = isset($_SESSION['guest_name']) ? $_SESSION['guest_name'] : '';
814
			$context['email'] = isset($_SESSION['guest_email']) ? $_SESSION['guest_email'] : '';
815
		}
816
		$context['destination'] = 'post2;start=' . $_REQUEST['start'] . (isset($_REQUEST['poll']) ? ';poll' : '');
817
818
		$context['submit_label'] = $txt['post'];
819
820
		// Posting a quoted reply?
821
		if (!empty($topic) && !empty($_REQUEST['quote']))
822
		{
823
			// Make sure they _can_ quote this post, and if so get it.
824
			$request = $smcFunc['db_query']('', '
825
				SELECT m.subject, COALESCE(mem.real_name, m.poster_name) AS poster_name, m.poster_time, m.body
826
				FROM {db_prefix}messages AS m
827
					INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board AND {query_see_board})
828
					LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
829
				WHERE m.id_msg = {int:id_msg}' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : '
830
					AND m.approved = {int:is_approved}') . '
831
				LIMIT 1',
832
				array(
833
					'id_msg' => (int) $_REQUEST['quote'],
834
					'is_approved' => 1,
835
				)
836
			);
837
			if ($smcFunc['db_num_rows']($request) == 0)
838
				fatal_lang_error('quoted_post_deleted', false);
839
			list ($form_subject, $mname, $mdate, $form_message) = $smcFunc['db_fetch_row']($request);
840
			$smcFunc['db_free_result']($request);
841
842
			// Add 'Re: ' to the front of the quoted subject.
843
			if (trim($context['response_prefix']) != '' && $smcFunc['strpos']($form_subject, trim($context['response_prefix'])) !== 0)
844
				$form_subject = $context['response_prefix'] . $form_subject;
845
846
			// Censor the message and subject.
847
			censorText($form_message);
848
			censorText($form_subject);
849
850
			// But if it's in HTML world, turn them into htmlspecialchar's so they can be edited!
851
			if (strpos($form_message, '[html]') !== false)
852
			{
853
				$parts = preg_split('~(\[/code\]|\[code(?:=[^\]]+)?\])~i', $form_message, -1, PREG_SPLIT_DELIM_CAPTURE);
854
				for ($i = 0, $n = count($parts); $i < $n; $i++)
855
				{
856
					// It goes 0 = outside, 1 = begin tag, 2 = inside, 3 = close tag, repeat.
857
					if ($i % 4 == 0)
858
						$parts[$i] = preg_replace_callback('~\[html\](.+?)\[/html\]~is', function ($m)
859
						{
860
							return '[html]' . preg_replace('~<br\s?/?' . '>~i', '&lt;br /&gt;<br>', "$m[1]") . '[/html]';
861
						}, $parts[$i]);
862
				}
863
				$form_message = implode('', $parts);
864
			}
865
866
			$form_message = preg_replace('~<br ?/?' . '>~i', "\n", $form_message);
867
868
			// Remove any nested quotes, if necessary.
869 View Code Duplication
			if (!empty($modSettings['removeNestedQuotes']))
870
				$form_message = preg_replace(array('~\n?\[quote.*?\].+?\[/quote\]\n?~is', '~^\n~', '~\[/quote\]~'), '', $form_message);
871
872
			// Add a quote string on the front and end.
873
			$form_message = '[quote author=' . $mname . ' link=msg=' . (int) $_REQUEST['quote'] . ' date=' . $mdate . ']' . "\n" . rtrim($form_message) . "\n" . '[/quote]';
874
		}
875
		// Posting a reply without a quote?
876
		elseif (!empty($topic) && empty($_REQUEST['quote']))
877
		{
878
			// Get the first message's subject.
879
			$form_subject = $first_subject;
880
881
			// Add 'Re: ' to the front of the subject.
882 View Code Duplication
			if (trim($context['response_prefix']) != '' && $form_subject != '' && $smcFunc['strpos']($form_subject, trim($context['response_prefix'])) !== 0)
883
				$form_subject = $context['response_prefix'] . $form_subject;
884
885
			// Censor the subject.
886
			censorText($form_subject);
887
888
			$form_message = '';
889
		}
890
		else
891
		{
892
			$form_subject = isset($_GET['subject']) ? $_GET['subject'] : '';
893
			$form_message = '';
894
		}
895
	}
896
897
	$context['can_post_attachment'] = !empty($modSettings['attachmentEnable']) && $modSettings['attachmentEnable'] == 1 && (allowedTo('post_attachment') || ($modSettings['postmod_active'] && allowedTo('post_unapproved_attachments')));
898
	if ($context['can_post_attachment'])
899
	{
900
		// If there are attachments, calculate the total size and how many.
901
		$context['attachments']['total_size'] = 0;
902
		$context['attachments']['quantity'] = 0;
903
904
		// If this isn't a new post, check the current attachments.
905 View Code Duplication
		if (isset($_REQUEST['msg']))
906
		{
907
			$context['attachments']['quantity'] = count($context['current_attachments']);
908
			foreach ($context['current_attachments'] as $attachment)
909
				$context['attachments']['total_size'] += $attachment['size'];
910
		}
911
912
		// A bit of house keeping first.
913 View Code Duplication
		if (!empty($_SESSION['temp_attachments']) && count($_SESSION['temp_attachments']) == 1)
914
			unset($_SESSION['temp_attachments']);
915
916
		if (!empty($_SESSION['temp_attachments']))
917
		{
918
			// Is this a request to delete them?
919
			if (isset($_GET['delete_temp']))
920
			{
921 View Code Duplication
				foreach ($_SESSION['temp_attachments'] as $attachID => $attachment)
922
				{
923
					if (strpos($attachID, 'post_tmp_' . $user_info['id']) !== false)
924
						if (file_exists($attachment['tmp_name']))
925
							unlink($attachment['tmp_name']);
926
				}
927
				$post_errors[] = 'temp_attachments_gone';
928
				$_SESSION['temp_attachments'] = array();
929
			}
930
			// Hmm, coming in fresh and there are files in session.
931
			elseif ($context['current_action'] != 'post2' || !empty($_POST['from_qr']))
932
			{
933
				// Let's be nice and see if they belong here first.
934
				if ((empty($_REQUEST['msg']) && empty($_SESSION['temp_attachments']['post']['msg']) && $_SESSION['temp_attachments']['post']['board'] == $board) || (!empty($_REQUEST['msg']) && $_SESSION['temp_attachments']['post']['msg'] == $_REQUEST['msg']))
935
				{
936
					// See if any files still exist before showing the warning message and the files attached.
937
					foreach ($_SESSION['temp_attachments'] as $attachID => $attachment)
938
					{
939
						if (strpos($attachID, 'post_tmp_' . $user_info['id']) === false)
940
							continue;
941
942
						if (file_exists($attachment['tmp_name']))
943
						{
944
							$post_errors[] = 'temp_attachments_new';
945
							$context['files_in_session_warning'] = $txt['attached_files_in_session'];
946
							unset($_SESSION['temp_attachments']['post']['files']);
947
							break;
948
						}
949
					}
950
				}
951
				else
952
				{
953
					// Since, they don't belong here. Let's inform the user that they exist..
954
					if (!empty($topic))
955
						$delete_url = $scripturl . '?action=post' .(!empty($_REQUEST['msg']) ? (';msg=' . $_REQUEST['msg']) : '') . (!empty($_REQUEST['last_msg']) ? (';last_msg=' . $_REQUEST['last_msg']) : '') . ';topic=' . $topic . ';delete_temp';
956
					else
957
						$delete_url = $scripturl . '?action=post;board=' . $board . ';delete_temp';
958
959
					// Compile a list of the files to show the user.
960
					$file_list = array();
961
					foreach ($_SESSION['temp_attachments'] as $attachID => $attachment)
962
						if (strpos($attachID, 'post_tmp_' . $user_info['id']) !== false)
963
							$file_list[] =  $attachment['name'];
964
965
					$_SESSION['temp_attachments']['post']['files'] = $file_list;
966
					$file_list = '<div class="attachments">' . implode('<br>', $file_list) . '</div>';
967
968
					if (!empty($_SESSION['temp_attachments']['post']['msg']))
969
					{
970
						// We have a message id, so we can link back to the old topic they were trying to edit..
971
						$goback_url = $scripturl . '?action=post' .(!empty($_SESSION['temp_attachments']['post']['msg']) ? (';msg=' . $_SESSION['temp_attachments']['post']['msg']) : '') . (!empty($_SESSION['temp_attachments']['post']['last_msg']) ? (';last_msg=' . $_SESSION['temp_attachments']['post']['last_msg']) : '') . ';topic=' . $_SESSION['temp_attachments']['post']['topic'] . ';additionalOptions';
972
973
						$post_errors[] = array('temp_attachments_found', array($delete_url, $goback_url, $file_list));
974
						$context['ignore_temp_attachments'] = true;
975
					}
976
					else
977
					{
978
						$post_errors[] = array('temp_attachments_lost', array($delete_url, $file_list));
979
						$context['ignore_temp_attachments'] = true;
980
					}
981
				}
982
			}
983
984
			if (!empty($context['we_are_history']))
985
				$post_errors[] = $context['we_are_history'];
986
987
			foreach ($_SESSION['temp_attachments'] as $attachID => $attachment)
988
			{
989
				if (isset($context['ignore_temp_attachments']) || isset($_SESSION['temp_attachments']['post']['files']))
990
					break;
991
992 View Code Duplication
				if ($attachID != 'initial_error' && strpos($attachID, 'post_tmp_' . $user_info['id']) === false)
993
					continue;
994
995 View Code Duplication
				if ($attachID == 'initial_error')
996
				{
997
					$txt['error_attach_initial_error'] = $txt['attach_no_upload'] . '<div style="padding: 0 1em;">' . (is_array($attachment) ? vsprintf($txt[$attachment[0]], $attachment[1]) : $txt[$attachment]) . '</div>';
998
					$post_errors[] = 'attach_initial_error';
999
					unset($_SESSION['temp_attachments']);
1000
					break;
1001
				}
1002
1003
				// Show any errors which might have occured.
1004
				if (!empty($attachment['errors']))
1005
				{
1006
					$txt['error_attach_errors'] = empty($txt['error_attach_errors']) ? '<br>' : '';
1007
					$txt['error_attach_errors'] .= vsprintf($txt['attach_warning'], $attachment['name']) . '<div style="padding: 0 1em;">';
1008
					foreach ($attachment['errors'] as $error)
1009
						$txt['error_attach_errors'] .= (is_array($error) ? vsprintf($txt[$error[0]], $error[1]) : $txt[$error]) . '<br >';
1010
					$txt['error_attach_errors'] .= '</div>';
1011
					$post_errors[] = 'attach_errors';
1012
1013
					// Take out the trash.
1014
					unset($_SESSION['temp_attachments'][$attachID]);
1015
					if (file_exists($attachment['tmp_name']))
1016
						unlink($attachment['tmp_name']);
1017
					continue;
1018
				}
1019
1020
				// More house keeping.
1021
				if (!file_exists($attachment['tmp_name']))
1022
				{
1023
					unset($_SESSION['temp_attachments'][$attachID]);
1024
					continue;
1025
				}
1026
1027
				$context['attachments']['quantity']++;
1028
				$context['attachments']['total_size'] += $attachment['size'];
1029
				if (!isset($context['files_in_session_warning']))
1030
					$context['files_in_session_warning'] = $txt['attached_files_in_session'];
1031
1032
				$context['current_attachments'][$attachID] = array(
1033
					'name' => '<u>' . $smcFunc['htmlspecialchars']($attachment['name']) . '</u>',
1034
					'size' => $attachment['size'],
1035
					'attachID' => $attachID,
1036
					'unchecked' => false,
1037
					'approved' => 1,
1038
					'mime_type' => '',
1039
					'thumb' => 0,
1040
				);
1041
			}
1042
		}
1043
	}
1044
1045
	// Do we need to show the visual verification image?
1046
	$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));
1047 View Code Duplication
	if ($context['require_verification'])
1048
	{
1049
		require_once($sourcedir . '/Subs-Editor.php');
1050
		$verificationOptions = array(
1051
			'id' => 'post',
1052
		);
1053
		$context['require_verification'] = create_control_verification($verificationOptions);
1054
		$context['visual_verification_id'] = $verificationOptions['id'];
1055
	}
1056
1057
	// If they came from quick reply, and have to enter verification details, give them some notice.
1058
	if (!empty($_REQUEST['from_qr']) && !empty($context['require_verification']))
1059
		$post_errors[] = 'need_qr_verification';
1060
1061
	/*
1062
	 * There are two error types: serious and minor. Serious errors
1063
	 * actually tell the user that a real error has occurred, while minor
1064
	 * errors are like warnings that let them know that something with
1065
	 * their post isn't right.
1066
	 */
1067
	$minor_errors = array('not_approved', 'new_replies', 'old_topic', 'need_qr_verification', 'no_subject', 'topic_locked', 'topic_unlocked', 'topic_stickied', 'topic_unstickied');
1068
1069
	call_integration_hook('integrate_post_errors', array(&$post_errors, &$minor_errors));
1070
1071
	// Any errors occurred?
1072
	if (!empty($post_errors))
1073
	{
1074
		loadLanguage('Errors');
1075
		$context['error_type'] = 'minor';
1076
		foreach ($post_errors as $post_error)
1077
			if (is_array($post_error))
1078
			{
1079
				$post_error_id = $post_error[0];
1080
				$context['post_error'][$post_error_id] = vsprintf($txt['error_' . $post_error_id], $post_error[1]);
1081
1082
				// If it's not a minor error flag it as such.
1083
				if (!in_array($post_error_id, $minor_errors))
1084
					$context['error_type'] = 'serious';
1085
			}
1086
			else
1087
			{
1088
				$context['post_error'][$post_error] = $txt['error_' . $post_error];
1089
1090
				// If it's not a minor error flag it as such.
1091
				if (!in_array($post_error, $minor_errors))
1092
					$context['error_type'] = 'serious';
1093
			}
1094
	}
1095
1096
	// What are you doing? Posting a poll, modifying, previewing, new post, or reply...
1097
	if (isset($_REQUEST['poll']))
1098
		$context['page_title'] = $txt['new_poll'];
1099
	elseif ($context['make_event'])
1100
		$context['page_title'] = $context['event']['id'] == -1 ? $txt['calendar_post_event'] : $txt['calendar_edit'];
1101
	elseif (isset($_REQUEST['msg']))
1102
		$context['page_title'] = $txt['modify_msg'];
1103
	elseif (isset($_REQUEST['subject'], $context['preview_subject']))
1104
		$context['page_title'] = $txt['preview'] . ' - ' . strip_tags($context['preview_subject']);
1105
	elseif (empty($topic))
1106
		$context['page_title'] = $txt['start_new_topic'];
1107
	else
1108
		$context['page_title'] = $txt['post_reply'];
1109
1110
	// Build the link tree.
1111
	if (empty($topic))
1112
		$context['linktree'][] = array(
1113
			'name' => '<em>' . $txt['start_new_topic'] . '</em>'
1114
		);
1115
	else
1116
		$context['linktree'][] = array(
1117
			'url' => $scripturl . '?topic=' . $topic . '.' . $_REQUEST['start'],
1118
			'name' => $form_subject,
1119
			'extra_before' => '<span><strong class="nav">' . $context['page_title'] . ' (</strong></span>',
1120
			'extra_after' => '<span><strong class="nav">)</strong></span>'
1121
		);
1122
1123
	$context['subject'] = addcslashes($form_subject, '"');
1124
	$context['message'] = str_replace(array('"', '<', '>', '&nbsp;'), array('&quot;', '&lt;', '&gt;', ' '), $form_message);
1125
1126
	// Are post drafts enabled?
1127
	$context['drafts_save'] = !empty($modSettings['drafts_post_enabled']) && allowedTo('post_draft');
1128
	$context['drafts_autosave'] = !empty($context['drafts_save']) && !empty($modSettings['drafts_autosave_enabled']) && allowedTo('post_autosave_draft');
1129
1130
	// Build a list of drafts that they can load in to the editor
1131
	if (!empty($context['drafts_save']))
1132
	{
1133
		require_once($sourcedir . '/Drafts.php');
1134
		ShowDrafts($user_info['id'], $topic);
1135
	}
1136
1137
	// Needed for the editor and message icons.
1138
	require_once($sourcedir . '/Subs-Editor.php');
1139
1140
	// Now create the editor.
1141
	$editorOptions = array(
1142
		'id' => 'message',
1143
		'value' => $context['message'],
1144
		'labels' => array(
1145
			'post_button' => $context['submit_label'],
1146
		),
1147
		// add height and width for the editor
1148
		'height' => '275px',
1149
		'width' => '100%',
1150
		// We do XML preview here.
1151
		'preview_type' => 2,
1152
		'required' => true,
1153
	);
1154
	create_control_richedit($editorOptions);
1155
1156
	// Store the ID.
1157
	$context['post_box_name'] = $editorOptions['id'];
1158
1159
	$context['attached'] = '';
1160
	$context['make_poll'] = isset($_REQUEST['poll']);
1161
1162
	// Message icons - customized icons are off?
1163
	$context['icons'] = getMessageIcons($board);
1164
1165 View Code Duplication
	if (!empty($context['icons']))
1166
		$context['icons'][count($context['icons']) - 1]['is_last'] = true;
1167
1168
	// Are we starting a poll? if set the poll icon as selected if its available
1169
	if (isset($_REQUEST['poll']))
1170
	{
1171
		foreach ($context['icons'] as $icons)
1172
		{
1173
			if (isset($icons['value']) && $icons['value'] == 'poll')
1174
			{
1175
				// if found we are done
1176
				$context['icon'] = 'poll';
1177
				break;
1178
			}
1179
		}
1180
	}
1181
1182
	$context['icon_url'] = '';
1183
	for ($i = 0, $n = count($context['icons']); $i < $n; $i++)
1184
	{
1185
		$context['icons'][$i]['selected'] = $context['icon'] == $context['icons'][$i]['value'];
1186
		if ($context['icons'][$i]['selected'])
1187
			$context['icon_url'] = $context['icons'][$i]['url'];
1188
	}
1189
	if (empty($context['icon_url']))
1190
	{
1191
		$context['icon_url'] = $settings[file_exists($settings['theme_dir'] . '/images/post/' . $context['icon'] . '.png') ? 'images_url' : 'default_images_url'] . '/post/' . $context['icon'] . '.png';
1192
		array_unshift($context['icons'], array(
1193
			'value' => $context['icon'],
1194
			'name' => $txt['current_icon'],
1195
			'url' => $context['icon_url'],
1196
			'is_last' => empty($context['icons']),
1197
			'selected' => true,
1198
		));
1199
	}
1200
1201
	if (!empty($topic) && !empty($modSettings['topicSummaryPosts']))
1202
		getTopic();
1203
1204
	// If the user can post attachments prepare the warning labels.
1205
	if ($context['can_post_attachment'])
1206
	{
1207
		// If they've unchecked an attachment, they may still want to attach that many more files, but don't allow more than num_allowed_attachments.
1208
		$context['num_allowed_attachments'] = empty($modSettings['attachmentNumPerPostLimit']) ? 50 : min($modSettings['attachmentNumPerPostLimit'] - count($context['current_attachments']), $modSettings['attachmentNumPerPostLimit']);
1209
		$context['can_post_attachment_unapproved'] = allowedTo('post_attachment');
1210
		$context['attachment_restrictions'] = array();
1211
		$context['allowed_extensions'] = strtr(strtolower($modSettings['attachmentExtensions']), array(',' => ', '));
1212
		$attachmentRestrictionTypes = array('attachmentNumPerPostLimit', 'attachmentPostLimit', 'attachmentSizeLimit');
1213
		foreach ($attachmentRestrictionTypes as $type)
1214
			if (!empty($modSettings[$type]))
1215
			{
1216
				$context['attachment_restrictions'][] = sprintf($txt['attach_restrict_' . $type . ($modSettings[$type] >= 1024 ? '_MB' : '')], comma_format($modSettings[$type], 0));
1217
				// Show some numbers. If they exist.
1218
				if ($type == 'attachmentNumPerPostLimit' && $context['attachments']['quantity'] > 0)
1219
					$context['attachment_restrictions'][] = sprintf($txt['attach_remaining'], $modSettings['attachmentNumPerPostLimit'] - $context['attachments']['quantity']);
1220
				elseif ($type == 'attachmentPostLimit' && $context['attachments']['total_size'] > 0)
1221
					$context['attachment_restrictions'][] = sprintf($txt['attach_available'], comma_format(round(max($modSettings['attachmentPostLimit'] - ($context['attachments']['total_size'] / 1024), 0)), 0));
1222
			}
1223
	}
1224
1225
	$context['back_to_topic'] = isset($_REQUEST['goback']) || (isset($_REQUEST['msg']) && !isset($_REQUEST['subject']));
1226
	$context['show_additional_options'] = !empty($_POST['additional_options']) || isset($_SESSION['temp_attachments']['post']) || isset($_GET['additionalOptions']);
1227
1228
	$context['is_new_topic'] = empty($topic);
1229
	$context['is_new_post'] = !isset($_REQUEST['msg']);
1230
	$context['is_first_post'] = $context['is_new_topic'] || (isset($_REQUEST['msg']) && $_REQUEST['msg'] == $id_first_msg);
1231
1232
	// WYSIWYG only works if BBC is enabled
1233
	$modSettings['disable_wysiwyg'] = !empty($modSettings['disable_wysiwyg']) || empty($modSettings['enableBBC']);
1234
1235
	// Register this form in the session variables.
1236
	checkSubmitOnce('register');
1237
1238
	// Mentions
1239 View Code Duplication
	if (!empty($modSettings['enable_mentions']) && allowedTo('mention'))
1240
	{
1241
		loadJavaScriptFile('jquery.caret.min.js', array('defer' => true), 'smf_caret');
1242
		loadJavaScriptFile('jquery.atwho.min.js', array('defer' => true), 'smf_atwho');
1243
		loadJavaScriptFile('mentions.js', array('defer' => true), 'smf_mentions');
1244
	}
1245
1246
	// quotedText.js
1247
	loadJavaScriptFile('quotedText.js', array('defer' => true), 'smf_quotedText');
1248
1249
	// Mock files to show already attached files.
1250
	addInlineJavaScript('
1251
	var current_attachments = [];', true);
1252
1253
	if (!empty($context['current_attachments']))
1254
	{
1255
		foreach ($context['current_attachments'] as $key => $mock)
1256
			addInlineJavaScript('
1257
	current_attachments.push({
1258
		name: '. JavaScriptEscape($mock['name']) .',
1259
		size: '. $mock['size'] .',
1260
		attachID: '. $mock['attachID'] .',
1261
		approved: '. $mock['approved'] .',
1262
		type: '. JavaScriptEscape(!empty($mock['mime_type']) ? $mock['mime_type'] : '') .',
1263
		thumbID: '. (!empty($mock['thumb']) ? $mock['thumb'] : 0) .'
1264
	});', true);
1265
	}
1266
1267
	// File Upload.
1268
	if ($context['can_post_attachment'])
1269
	{
1270
		$acceptedFiles = implode(',', array_map(function($val) use($smcFunc) { return '.'. $smcFunc['htmltrim']($val);} , explode(',', $context['allowed_extensions'])));
1271
1272
		loadJavaScriptFile('dropzone.min.js', array('defer' => true), 'smf_dropzone');
1273
		loadJavaScriptFile('smf_fileUpload.js', array('defer' => true), 'smf_fileUpload');
1274
		addInlineJavaScript('
1275
	$(function() {
1276
		smf_fileUpload({
1277
			dictDefaultMessage : '. JavaScriptEscape($txt['attach_drop_zone']) .',
1278
			dictFallbackMessage : '. JavaScriptEscape($txt['attach_drop_zone_no']) .',
1279
			dictCancelUpload : '. JavaScriptEscape($txt['modify_cancel']) .',
1280
			genericError: '. JavaScriptEscape($txt['attach_php_error']) .',
1281
			text_attachLeft: '. JavaScriptEscape($txt['attached_attachedLeft']) .',
1282
			text_deleteAttach: '. JavaScriptEscape($txt['attached_file_delete']) .',
1283
			text_attachDeleted: '. JavaScriptEscape($txt['attached_file_deleted']) .',
1284
			text_insertBBC: '. JavaScriptEscape($txt['attached_insertBBC']) .',
1285
			text_attachUploaded: '. JavaScriptEscape($txt['attached_file_uploaded']) .',
1286
			text_attach_unlimited: '. JavaScriptEscape($txt['attach_drop_unlimited']) .',
1287
			dictMaxFilesExceeded: '. JavaScriptEscape($txt['more_attachments_error']) .',
1288
			dictInvalidFileType: '. JavaScriptEscape(sprintf($txt['cant_upload_type'], $context['allowed_extensions'])) .',
1289
			dictFileTooBig: '. JavaScriptEscape(sprintf($txt['file_too_big'], comma_format($modSettings['attachmentSizeLimit'], 0))) .',
1290
			maxTotalSize: '. JavaScriptEscape($txt['attach_max_total_file_size_current']) .',
1291
			acceptedFiles: '. JavaScriptEscape($acceptedFiles) .',
1292
			maxFilesize: '. (!empty($modSettings['attachmentSizeLimit']) ? $modSettings['attachmentSizeLimit'] : 'null') .',
1293
			thumbnailWidth: '.(!empty($modSettings['attachmentThumbWidth']) ? $modSettings['attachmentThumbWidth'] : 'null') .',
1294
			thumbnailHeight: '.(!empty($modSettings['attachmentThumbHeight']) ? $modSettings['attachmentThumbHeight'] : 'null') .',
1295
			maxFiles: '. (!empty($context['num_allowed_attachments']) ? $context['num_allowed_attachments'] : 'null') .',
1296
			text_totalMaxSize: '. JavaScriptEscape($txt['attach_max_total_file_size_current']) .',
1297
			text_max_size_progress: '. JavaScriptEscape($txt['attach_max_size_progress']) .',
1298
			limitMultiFileUploadSize:'. round(max($modSettings['attachmentPostLimit'] - ($context['attachments']['total_size'] / 1024), 0)) * 1024 .',
1299
			maxLimitReferenceUploadSize: '. $modSettings['attachmentPostLimit'] * 1024 .',
1300
		});
1301
	});', true);
1302
	}
1303
1304
	// Knowing the current board ID might be handy.
1305
	addInlineJavaScript('
1306
	var current_board = '. (empty($context['current_board']) ? 'null' : $context['current_board']) .';', false);
1307
1308
	// Finally, load the template.
1309
	if (!isset($_REQUEST['xml']))
1310
		loadTemplate('Post');
1311
1312
	call_integration_hook('integrate_post_end');
1313
}
1314
1315
/**
1316
 * Posts or saves the message composed with Post().
1317
 *
1318
 * requires various permissions depending on the action.
1319
 * handles attachment, post, and calendar saving.
1320
 * sends off notifications, and allows for announcements and moderation.
1321
 * accessed from ?action=post2.
1322
 */
1323
function Post2()
1324
{
1325
	global $board, $topic, $txt, $modSettings, $sourcedir, $context;
1326
	global $user_info, $board_info, $smcFunc, $settings;
1327
1328
	// Sneaking off, are we?
1329
	if (empty($_POST) && empty($topic))
1330
	{
1331
		if (empty($_SERVER['CONTENT_LENGTH']))
1332
			redirectexit('action=post;board=' . $board . '.0');
1333
		else
1334
			fatal_lang_error('post_upload_error', false);
1335
	}
1336
	elseif (empty($_POST) && !empty($topic))
1337
		redirectexit('action=post;topic=' . $topic . '.0');
1338
1339
	// No need!
1340
	$context['robot_no_index'] = true;
1341
1342
	// Prevent double submission of this form.
1343
	checkSubmitOnce('check');
1344
1345
	// No errors as yet.
1346
	$post_errors = array();
1347
1348
	// If the session has timed out, let the user re-submit their form.
1349
	if (checkSession('post', '', false) != '')
1350
		$post_errors[] = 'session_timeout';
1351
1352
	// Wrong verification code?
1353
	if (!$user_info['is_admin'] && !$user_info['is_mod'] && !empty($modSettings['posts_require_captcha']) && ($user_info['posts'] < $modSettings['posts_require_captcha'] || ($user_info['is_guest'] && $modSettings['posts_require_captcha'] == -1)))
1354
	{
1355
		require_once($sourcedir . '/Subs-Editor.php');
1356
		$verificationOptions = array(
1357
			'id' => 'post',
1358
		);
1359
		$context['require_verification'] = create_control_verification($verificationOptions, true);
1360
		if (is_array($context['require_verification']))
1361
			$post_errors = array_merge($post_errors, $context['require_verification']);
1362
	}
1363
1364
	require_once($sourcedir . '/Subs-Post.php');
1365
	loadLanguage('Post');
1366
1367
	// Drafts enabled and needed?
1368
	if (!empty($modSettings['drafts_post_enabled']) && (isset($_POST['save_draft']) || isset($_POST['id_draft'])))
1369
		require_once($sourcedir . '/Drafts.php');
1370
1371
	// First check to see if they are trying to delete any current attachments.
1372
	if (isset($_POST['attach_del']))
1373
	{
1374
		$keep_temp = array();
1375
		$keep_ids = array();
1376
		foreach ($_POST['attach_del'] as $dummy)
1377
			if (strpos($dummy, 'post_tmp_' . $user_info['id']) !== false)
1378
				$keep_temp[] = $dummy;
1379
			else
1380
				$keep_ids[] = (int) $dummy;
1381
1382
		if (isset($_SESSION['temp_attachments']))
1383
			foreach ($_SESSION['temp_attachments'] as $attachID => $attachment)
1384
			{
1385
				if ((isset($_SESSION['temp_attachments']['post']['files'], $attachment['name']) && in_array($attachment['name'], $_SESSION['temp_attachments']['post']['files'])) || in_array($attachID, $keep_temp) || strpos($attachID, 'post_tmp_' . $user_info['id']) === false)
1386
					continue;
1387
1388
				unset($_SESSION['temp_attachments'][$attachID]);
1389
				unlink($attachment['tmp_name']);
1390
			}
1391
1392
		if (!empty($_REQUEST['msg']))
1393
		{
1394
			require_once($sourcedir . '/ManageAttachments.php');
1395
			$attachmentQuery = array(
1396
				'attachment_type' => 0,
1397
				'id_msg' => (int) $_REQUEST['msg'],
1398
				'not_id_attach' => $keep_ids,
1399
			);
1400
			removeAttachments($attachmentQuery);
1401
		}
1402
	}
1403
1404
	// Then try to upload any attachments.
1405
	$context['can_post_attachment'] = !empty($modSettings['attachmentEnable']) && $modSettings['attachmentEnable'] == 1 && (allowedTo('post_attachment') || ($modSettings['postmod_active'] && allowedTo('post_unapproved_attachments')));
1406
	if ($context['can_post_attachment'] && empty($_POST['from_qr']))
1407
	{
1408
		require_once($sourcedir . '/Subs-Attachments.php');
1409
		processAttachments();
1410
	}
1411
1412
	// If this isn't a new topic load the topic info that we need.
1413
	if (!empty($topic))
1414
	{
1415
		$request = $smcFunc['db_query']('', '
1416
			SELECT locked, is_sticky, id_poll, approved, id_first_msg, id_last_msg, id_member_started, id_board
1417
			FROM {db_prefix}topics
1418
			WHERE id_topic = {int:current_topic}
1419
			LIMIT 1',
1420
			array(
1421
				'current_topic' => $topic,
1422
			)
1423
		);
1424
		$topic_info = $smcFunc['db_fetch_assoc']($request);
1425
		$smcFunc['db_free_result']($request);
1426
1427
		// Though the topic should be there, it might have vanished.
1428
		if (!is_array($topic_info))
1429
			fatal_lang_error('topic_doesnt_exist', 404);
1430
1431
		// Did this topic suddenly move? Just checking...
1432
		if ($topic_info['id_board'] != $board)
1433
			fatal_lang_error('not_a_topic');
1434
	}
1435
1436
	// Replying to a topic?
1437
	if (!empty($topic) && !isset($_REQUEST['msg']))
1438
	{
1439
		// Don't allow a post if it's locked.
1440 View Code Duplication
		if ($topic_info['locked'] != 0 && !allowedTo('moderate_board'))
1441
			fatal_lang_error('topic_locked', false);
1442
1443
		// Sorry, multiple polls aren't allowed... yet.  You should stop giving me ideas :P.
1444
		if (isset($_REQUEST['poll']) && $topic_info['id_poll'] > 0)
1445
			unset($_REQUEST['poll']);
1446
1447
		// Do the permissions and approval stuff...
1448
		$becomesApproved = true;
1449
		$topicAndMessageBothUnapproved = false;
1450
1451
		// If the topic is unapproved the message automatically becomes unapproved too.
1452
		if (empty($topic_info['approved']))
1453
		{
1454
			$becomesApproved = false;
1455
1456
			// camelCase fan much? :P
1457
			$topicAndMessageBothUnapproved = true;
1458
1459
			// Set a nice session var...
1460
			$_SESSION['becomesUnapproved'] = true;
1461
		}
1462
1463
		elseif ($topic_info['id_member_started'] != $user_info['id'])
1464
		{
1465
			if ($modSettings['postmod_active'] && allowedTo('post_unapproved_replies_any') && !allowedTo('post_reply_any'))
1466
				$becomesApproved = false;
1467
1468
			else
1469
				isAllowedTo('post_reply_any');
1470
		}
1471
		elseif (!allowedTo('post_reply_any'))
1472
		{
1473
			if ($modSettings['postmod_active'] && allowedTo('post_unapproved_replies_own') && !allowedTo('post_reply_own'))
1474
				$becomesApproved = false;
1475
1476
			else
1477
				isAllowedTo('post_reply_own');
1478
		}
1479
1480 View Code Duplication
		if (isset($_POST['lock']))
1481
		{
1482
			// Nothing is changed to the lock.
1483
			if ((empty($topic_info['locked']) && empty($_POST['lock'])) || (!empty($_POST['lock']) && !empty($topic_info['locked'])))
1484
				unset($_POST['lock']);
1485
1486
			// You're have no permission to lock this topic.
1487
			elseif (!allowedTo(array('lock_any', 'lock_own')) || (!allowedTo('lock_any') && $user_info['id'] != $topic_info['id_member_started']))
1488
				unset($_POST['lock']);
1489
1490
			// You are allowed to (un)lock your own topic only.
1491
			elseif (!allowedTo('lock_any'))
1492
			{
1493
				// You cannot override a moderator lock.
1494
				if ($topic_info['locked'] == 1)
1495
					unset($_POST['lock']);
1496
1497
				else
1498
					$_POST['lock'] = empty($_POST['lock']) ? 0 : 2;
1499
			}
1500
			// Hail mighty moderator, (un)lock this topic immediately.
1501
			else
1502
			{
1503
				$_POST['lock'] = empty($_POST['lock']) ? 0 : 1;
1504
1505
				// Did someone (un)lock this while you were posting?
1506
				if (isset($_POST['already_locked']) && $_POST['already_locked'] != $topic_info['locked'])
1507
					$post_errors[] = 'topic_' . (empty($topic_info['locked']) ? 'un' : '') . 'locked';
1508
			}
1509
		}
1510
1511
		// So you wanna (un)sticky this...let's see.
1512 View Code Duplication
		if (isset($_POST['sticky']) && ($_POST['sticky'] == $topic_info['is_sticky'] || !allowedTo('make_sticky')))
1513
			unset($_POST['sticky']);
1514
		elseif (isset($_POST['sticky']))
1515
		{
1516
			// Did someone (un)sticky this while you were posting?
1517
			if (isset($_POST['already_sticky']) && $_POST['already_sticky'] != $topic_info['is_sticky'])
1518
				$post_errors[] = 'topic_' . (empty($topic_info['is_sticky']) ? 'un' : '') . 'sticky';
1519
		}
1520
1521
		// If drafts are enabled, then pass this off
1522 View Code Duplication
		if (!empty($modSettings['drafts_post_enabled']) && isset($_POST['save_draft']))
1523
		{
1524
			SaveDraft($post_errors);
1525
			return Post();
1526
		}
1527
1528
		// If the number of replies has changed, if the setting is enabled, go back to Post() - which handles the error.
1529
		if (isset($_POST['last_msg']) && $topic_info['id_last_msg'] > $_POST['last_msg'])
1530
		{
1531
			$_REQUEST['preview'] = true;
1532
			return Post();
1533
		}
1534
1535
		$posterIsGuest = $user_info['is_guest'];
1536
	}
1537
	// Posting a new topic.
1538
	elseif (empty($topic))
1539
	{
1540
		// Now don't be silly, new topics will get their own id_msg soon enough.
1541
		unset($_REQUEST['msg'], $_POST['msg'], $_GET['msg']);
1542
1543
		// Do like, the permissions, for safety and stuff...
1544
		$becomesApproved = true;
1545
		if ($modSettings['postmod_active'] && !allowedTo('post_new') && allowedTo('post_unapproved_topics'))
1546
			$becomesApproved = false;
1547
		else
1548
			isAllowedTo('post_new');
1549
1550
		if (isset($_POST['lock']))
1551
		{
1552
			// New topics are by default not locked.
1553
			if (empty($_POST['lock']))
1554
				unset($_POST['lock']);
1555
			// Besides, you need permission.
1556
			elseif (!allowedTo(array('lock_any', 'lock_own')))
1557
				unset($_POST['lock']);
1558
			// A moderator-lock (1) can override a user-lock (2).
1559
			else
1560
				$_POST['lock'] = allowedTo('lock_any') ? 1 : 2;
1561
		}
1562
1563
		if (isset($_POST['sticky']) && (empty($_POST['sticky']) || !allowedTo('make_sticky')))
1564
			unset($_POST['sticky']);
1565
1566
		// Saving your new topic as a draft first?
1567 View Code Duplication
		if (!empty($modSettings['drafts_post_enabled']) && isset($_POST['save_draft']))
1568
		{
1569
			SaveDraft($post_errors);
1570
			return Post();
1571
		}
1572
1573
		$posterIsGuest = $user_info['is_guest'];
1574
	}
1575
	// Modifying an existing message?
1576
	elseif (isset($_REQUEST['msg']) && !empty($topic))
1577
	{
1578
		$_REQUEST['msg'] = (int) $_REQUEST['msg'];
1579
1580
		$request = $smcFunc['db_query']('', '
1581
			SELECT id_member, poster_name, poster_email, poster_time, approved
1582
			FROM {db_prefix}messages
1583
			WHERE id_msg = {int:id_msg}
1584
			LIMIT 1',
1585
			array(
1586
				'id_msg' => $_REQUEST['msg'],
1587
			)
1588
		);
1589
		if ($smcFunc['db_num_rows']($request) == 0)
1590
			fatal_lang_error('cant_find_messages', false);
1591
		$row = $smcFunc['db_fetch_assoc']($request);
1592
		$smcFunc['db_free_result']($request);
1593
1594 View Code Duplication
		if (!empty($topic_info['locked']) && !allowedTo('moderate_board'))
1595
			fatal_lang_error('topic_locked', false);
1596
1597 View Code Duplication
		if (isset($_POST['lock']))
1598
		{
1599
			// Nothing changes to the lock status.
1600
			if ((empty($_POST['lock']) && empty($topic_info['locked'])) || (!empty($_POST['lock']) && !empty($topic_info['locked'])))
1601
				unset($_POST['lock']);
1602
			// You're simply not allowed to (un)lock this.
1603
			elseif (!allowedTo(array('lock_any', 'lock_own')) || (!allowedTo('lock_any') && $user_info['id'] != $topic_info['id_member_started']))
1604
				unset($_POST['lock']);
1605
			// You're only allowed to lock your own topics.
1606
			elseif (!allowedTo('lock_any'))
1607
			{
1608
				// You're not allowed to break a moderator's lock.
1609
				if ($topic_info['locked'] == 1)
1610
					unset($_POST['lock']);
1611
				// Lock it with a soft lock or unlock it.
1612
				else
1613
					$_POST['lock'] = empty($_POST['lock']) ? 0 : 2;
1614
			}
1615
			// You must be the moderator.
1616
			else
1617
			{
1618
				$_POST['lock'] = empty($_POST['lock']) ? 0 : 1;
1619
1620
				// Did someone (un)lock this while you were posting?
1621
				if (isset($_POST['already_locked']) && $_POST['already_locked'] != $topic_info['locked'])
1622
					$post_errors[] = 'topic_' . (empty($topic_info['locked']) ? 'un' : '') . 'locked';
1623
			}
1624
		}
1625
1626
		// Change the sticky status of this topic?
1627 View Code Duplication
		if (isset($_POST['sticky']) && (!allowedTo('make_sticky') || $_POST['sticky'] == $topic_info['is_sticky']))
1628
			unset($_POST['sticky']);
1629
		elseif (isset($_POST['sticky']))
1630
		{
1631
			// Did someone (un)sticky this while you were posting?
1632
			if (isset($_POST['already_sticky']) && $_POST['already_sticky'] != $topic_info['is_sticky'])
1633
				$post_errors[] = 'topic_' . (empty($topic_info['locked']) ? 'un' : '') . 'stickied';
1634
		}
1635
1636
		if ($row['id_member'] == $user_info['id'] && !allowedTo('modify_any'))
1637
		{
1638
			if ((!$modSettings['postmod_active'] || $row['approved']) && !empty($modSettings['edit_disable_time']) && $row['poster_time'] + ($modSettings['edit_disable_time'] + 5) * 60 < time())
1639
				fatal_lang_error('modify_post_time_passed', false);
1640
			elseif ($topic_info['id_member_started'] == $user_info['id'] && !allowedTo('modify_own'))
1641
				isAllowedTo('modify_replies');
1642
			else
1643
				isAllowedTo('modify_own');
1644
		}
1645
		elseif ($topic_info['id_member_started'] == $user_info['id'] && !allowedTo('modify_any'))
1646
		{
1647
			isAllowedTo('modify_replies');
1648
1649
			// If you're modifying a reply, I say it better be logged...
1650
			$moderationAction = true;
1651
		}
1652
		else
1653
		{
1654
			isAllowedTo('modify_any');
1655
1656
			// Log it, assuming you're not modifying your own post.
1657
			if ($row['id_member'] != $user_info['id'])
1658
				$moderationAction = true;
1659
		}
1660
1661
		// If drafts are enabled, then lets send this off to save
1662 View Code Duplication
		if (!empty($modSettings['drafts_post_enabled']) && isset($_POST['save_draft']))
1663
		{
1664
			SaveDraft($post_errors);
1665
			return Post();
1666
		}
1667
1668
		$posterIsGuest = empty($row['id_member']);
1669
1670
		// Can they approve it?
1671
		$can_approve = allowedTo('approve_posts');
1672
		$becomesApproved = $modSettings['postmod_active'] ? ($can_approve && !$row['approved'] ? (!empty($_REQUEST['approve']) ? 1 : 0) : $row['approved']) : 1;
1673
		$approve_has_changed = $row['approved'] != $becomesApproved;
1674
1675
		if (!allowedTo('moderate_forum') || !$posterIsGuest)
1676
		{
1677
			$_POST['guestname'] = $row['poster_name'];
1678
			$_POST['email'] = $row['poster_email'];
1679
		}
1680
	}
1681
1682
	// In case we want to override but still respect the unapproved topic rule.
1683
	if (allowedTo('approve_posts') && empty($topicAndMessageBothUnapproved))
1684
	{
1685
		$becomesApproved = !isset($_REQUEST['approve']) || !empty($_REQUEST['approve']) ? 1 : 0;
1686
		$approve_has_changed = isset($row['approved']) ? $row['approved'] != $becomesApproved : false;
1687
	}
1688
1689
	// If the poster is a guest evaluate the legality of name and email.
1690
	if ($posterIsGuest)
1691
	{
1692
		$_POST['guestname'] = !isset($_POST['guestname']) ? '' : trim($_POST['guestname']);
1693
		$_POST['email'] = !isset($_POST['email']) ? '' : trim($_POST['email']);
1694
1695
		if ($_POST['guestname'] == '' || $_POST['guestname'] == '_')
1696
			$post_errors[] = 'no_name';
1697
		if ($smcFunc['strlen']($_POST['guestname']) > 25)
1698
			$post_errors[] = 'long_name';
1699
1700
		if (empty($modSettings['guest_post_no_email']))
1701
		{
1702
			// Only check if they changed it!
1703
			if (!isset($row) || $row['poster_email'] != $_POST['email'])
1704
			{
1705
				if (!allowedTo('moderate_forum') && (!isset($_POST['email']) || $_POST['email'] == ''))
1706
					$post_errors[] = 'no_email';
1707
				if (!allowedTo('moderate_forum') && !filter_var($_POST['email'], FILTER_VALIDATE_EMAIL))
1708
					$post_errors[] = 'bad_email';
1709
			}
1710
1711
			// Now make sure this email address is not banned from posting.
1712
			isBannedEmail($_POST['email'], 'cannot_post', sprintf($txt['you_are_post_banned'], $txt['guest_title']));
1713
		}
1714
1715
		// In case they are making multiple posts this visit, help them along by storing their name.
1716
		if (empty($post_errors))
1717
		{
1718
			$_SESSION['guest_name'] = $_POST['guestname'];
1719
			$_SESSION['guest_email'] = $_POST['email'];
1720
		}
1721
	}
1722
1723
	// Coming from the quickReply?
1724
	if(isset($_POST['quickReply']))
1725
		$_POST['message'] = $_POST['quickReply'];
1726
1727
	// Check the subject and message.
1728
	if (!isset($_POST['subject']) || $smcFunc['htmltrim']($smcFunc['htmlspecialchars']($_POST['subject'])) === '')
1729
		$post_errors[] = 'no_subject';
1730
	if (!isset($_POST['message']) || $smcFunc['htmltrim']($smcFunc['htmlspecialchars']($_POST['message']), ENT_QUOTES) === '')
1731
		$post_errors[] = 'no_message';
1732
	elseif (!empty($modSettings['max_messageLength']) && $smcFunc['strlen']($_POST['message']) > $modSettings['max_messageLength'])
1733
		$post_errors[] = array('long_message', array($modSettings['max_messageLength']));
1734
	else
1735
	{
1736
		// Prepare the message a bit for some additional testing.
1737
		$_POST['message'] = $smcFunc['htmlspecialchars']($_POST['message'], ENT_QUOTES);
1738
1739
		// Preparse code. (Zef)
1740
		if ($user_info['is_guest'])
1741
			$user_info['name'] = $_POST['guestname'];
1742
		preparsecode($_POST['message']);
1743
1744
		// Let's see if there's still some content left without the tags.
1745 View Code Duplication
		if ($smcFunc['htmltrim'](strip_tags(parse_bbc($_POST['message'], false), implode('', $context['allowed_html_tags']))) === '' && (!allowedTo('admin_forum') || strpos($_POST['message'], '[html]') === false))
1746
			$post_errors[] = 'no_message';
1747
	}
1748
	if (isset($_POST['calendar']) && !isset($_REQUEST['deleteevent']) && $smcFunc['htmltrim']($_POST['evtitle']) === '')
1749
		$post_errors[] = 'no_event';
1750
	// You are not!
1751
	if (isset($_POST['message']) && strtolower($_POST['message']) == 'i am the administrator.' && !$user_info['is_admin'])
1752
		fatal_error('Knave! Masquerader! Charlatan!', false);
1753
1754
	// Validate the poll...
1755
	if (isset($_REQUEST['poll']) && $modSettings['pollMode'] == '1')
1756
	{
1757
		if (!empty($topic) && !isset($_REQUEST['msg']))
1758
			fatal_lang_error('no_access', false);
1759
1760
		// This is a new topic... so it's a new poll.
1761 View Code Duplication
		if (empty($topic))
1762
			isAllowedTo('poll_post');
1763
		// Can you add to your own topics?
1764
		elseif ($user_info['id'] == $topic_info['id_member_started'] && !allowedTo('poll_add_any'))
1765
			isAllowedTo('poll_add_own');
1766
		// Can you add polls to any topic, then?
1767
		else
1768
			isAllowedTo('poll_add_any');
1769
1770
		if (!isset($_POST['question']) || trim($_POST['question']) == '')
1771
			$post_errors[] = 'no_question';
1772
1773
		$_POST['options'] = empty($_POST['options']) ? array() : htmltrim__recursive($_POST['options']);
1774
1775
		// Get rid of empty ones.
1776
		foreach ($_POST['options'] as $k => $option)
1777
			if ($option == '')
1778
				unset($_POST['options'][$k], $_POST['options'][$k]);
1779
1780
		// What are you going to vote between with one choice?!?
1781
		if (count($_POST['options']) < 2)
1782
			$post_errors[] = 'poll_few';
1783
		elseif (count($_POST['options']) > 256)
1784
			$post_errors[] = 'poll_many';
1785
	}
1786
1787
	if ($posterIsGuest)
1788
	{
1789
		// If user is a guest, make sure the chosen name isn't taken.
1790
		require_once($sourcedir . '/Subs-Members.php');
1791
		if (isReservedName($_POST['guestname'], 0, true, false) && (!isset($row['poster_name']) || $_POST['guestname'] != $row['poster_name']))
1792
			$post_errors[] = 'bad_name';
1793
	}
1794
	// If the user isn't a guest, get his or her name and email.
1795
	elseif (!isset($_REQUEST['msg']))
1796
	{
1797
		$_POST['guestname'] = $user_info['username'];
1798
		$_POST['email'] = $user_info['email'];
1799
	}
1800
1801
	// Any mistakes?
1802
	if (!empty($post_errors))
1803
	{
1804
		// Previewing.
1805
		$_REQUEST['preview'] = true;
1806
1807
		return Post($post_errors);
1808
	}
1809
1810
	// Previewing? Go back to start.
1811
	if (isset($_REQUEST['preview']))
1812
	{
1813
		if (checkSession('post', '', false) != '')
1814
		{
1815
			loadLanguage('Errors');
1816
			$post_errors[] = 'session_timeout';
1817
			unset ($_POST['preview'], $_REQUEST['xml']); // just in case
1818
		}
1819
		return Post($post_errors);
1820
	}
1821
1822
	// Make sure the user isn't spamming the board.
1823
	if (!isset($_REQUEST['msg']))
1824
		spamProtection('post');
1825
1826
	// At about this point, we're posting and that's that.
1827
	ignore_user_abort(true);
1828
	@set_time_limit(300);
1829
1830
	// Add special html entities to the subject, name, and email.
1831
	$_POST['subject'] = strtr($smcFunc['htmlspecialchars']($_POST['subject']), array("\r" => '', "\n" => '', "\t" => ''));
1832
	$_POST['guestname'] = $smcFunc['htmlspecialchars']($_POST['guestname']);
1833
	$_POST['email'] = $smcFunc['htmlspecialchars']($_POST['email']);
1834
	$_POST['modify_reason'] = empty($_POST['modify_reason']) ? '' : strtr($smcFunc['htmlspecialchars']($_POST['modify_reason']), array("\r" => '', "\n" => '', "\t" => ''));
1835
1836
	// At this point, we want to make sure the subject isn't too long.
1837 View Code Duplication
	if ($smcFunc['strlen']($_POST['subject']) > 100)
1838
		$_POST['subject'] = $smcFunc['substr']($_POST['subject'], 0, 100);
1839
1840
	// Same with the "why did you edit this" text.
1841 View Code Duplication
	if ($smcFunc['strlen']($_POST['modify_reason']) > 100)
1842
		$_POST['modify_reason'] = $smcFunc['substr']($_POST['modify_reason'], 0, 100);
1843
1844
	// Make the poll...
1845
	if (isset($_REQUEST['poll']))
1846
	{
1847
		// Make sure that the user has not entered a ridiculous number of options..
1848
		if (empty($_POST['poll_max_votes']) || $_POST['poll_max_votes'] <= 0)
1849
			$_POST['poll_max_votes'] = 1;
1850
		elseif ($_POST['poll_max_votes'] > count($_POST['options']))
1851
			$_POST['poll_max_votes'] = count($_POST['options']);
1852
		else
1853
			$_POST['poll_max_votes'] = (int) $_POST['poll_max_votes'];
1854
1855
		$_POST['poll_expire'] = (int) $_POST['poll_expire'];
1856
		$_POST['poll_expire'] = $_POST['poll_expire'] > 9999 ? 9999 : ($_POST['poll_expire'] < 0 ? 0 : $_POST['poll_expire']);
1857
1858
		// Just set it to zero if it's not there..
1859
		if (!isset($_POST['poll_hide']))
1860
			$_POST['poll_hide'] = 0;
1861
		else
1862
			$_POST['poll_hide'] = (int) $_POST['poll_hide'];
1863
		$_POST['poll_change_vote'] = isset($_POST['poll_change_vote']) ? 1 : 0;
1864
1865
		$_POST['poll_guest_vote'] = isset($_POST['poll_guest_vote']) ? 1 : 0;
1866
		// Make sure guests are actually allowed to vote generally.
1867 View Code Duplication
		if ($_POST['poll_guest_vote'])
1868
		{
1869
			require_once($sourcedir . '/Subs-Members.php');
1870
			$allowedVoteGroups = groupsAllowedTo('poll_vote', $board);
1871
			if (!in_array(-1, $allowedVoteGroups['allowed']))
1872
				$_POST['poll_guest_vote'] = 0;
1873
		}
1874
1875
		// If the user tries to set the poll too far in advance, don't let them.
1876
		if (!empty($_POST['poll_expire']) && $_POST['poll_expire'] < 1)
1877
			fatal_lang_error('poll_range_error', false);
1878
		// Don't allow them to select option 2 for hidden results if it's not time limited.
1879
		elseif (empty($_POST['poll_expire']) && $_POST['poll_hide'] == 2)
1880
			$_POST['poll_hide'] = 1;
1881
1882
		// Clean up the question and answers.
1883
		$_POST['question'] = $smcFunc['htmlspecialchars']($_POST['question']);
1884
		$_POST['question'] = $smcFunc['truncate']($_POST['question'], 255);
1885
		$_POST['question'] = preg_replace('~&amp;#(\d{4,5}|[2-9]\d{2,4}|1[2-9]\d);~', '&#$1;', $_POST['question']);
1886
		$_POST['options'] = htmlspecialchars__recursive($_POST['options']);
1887
	}
1888
1889
	// ...or attach a new file...
1890
	if (empty($ignore_temp) && $context['can_post_attachment'] && !empty($_SESSION['temp_attachments']) && empty($_POST['from_qr']))
1891
	{
1892
		$attachIDs = array();
1893
		$attach_errors = array();
1894
		if (!empty($context['we_are_history']))
1895
			$attach_errors[] = '<dd>' . $txt['error_temp_attachments_flushed'] . '<br><br></dd>';
1896
1897
		foreach ($_SESSION['temp_attachments'] as  $attachID => $attachment)
1898
		{
1899 View Code Duplication
			if ($attachID != 'initial_error' && strpos($attachID, 'post_tmp_' . $user_info['id']) === false)
1900
				continue;
1901
1902
			// If there was an initial error just show that message.
1903 View Code Duplication
			if ($attachID == 'initial_error')
1904
			{
1905
				$attach_errors[] = '<dt>' . $txt['attach_no_upload'] . '</dt>';
1906
				$attach_errors[] = '<dd>' . (is_array($attachment) ? vsprintf($txt[$attachment[0]], $attachment[1]) : $txt[$attachment]) . '</dd>';
1907
1908
				unset($_SESSION['temp_attachments']);
1909
				break;
1910
			}
1911
1912
			$attachmentOptions = array(
1913
				'post' => isset($_REQUEST['msg']) ? $_REQUEST['msg'] : 0,
1914
				'poster' => $user_info['id'],
1915
				'name' => $attachment['name'],
1916
				'tmp_name' => $attachment['tmp_name'],
1917
				'size' => isset($attachment['size']) ? $attachment['size'] : 0,
1918
				'mime_type' => isset($attachment['type']) ? $attachment['type'] : '',
1919
				'id_folder' => isset($attachment['id_folder']) ? $attachment['id_folder'] : $modSettings['currentAttachmentUploadDir'],
1920
				'approved' => !$modSettings['postmod_active'] || allowedTo('post_attachment'),
1921
				'errors' => $attachment['errors'],
1922
			);
1923
1924
			if (empty($attachment['errors']))
1925
			{
1926
				if (createAttachment($attachmentOptions))
1927
				{
1928
					$attachIDs[] = $attachmentOptions['id'];
1929
					if (!empty($attachmentOptions['thumb']))
1930
						$attachIDs[] = $attachmentOptions['thumb'];
1931
				}
1932
			}
1933
			else
1934
				$attach_errors[] = '<dt>&nbsp;</dt>';
1935
1936
			if (!empty($attachmentOptions['errors']))
1937
			{
1938
				// Sort out the errors for display and delete any associated files.
1939
				$attach_errors[] = '<dt>' . vsprintf($txt['attach_warning'], $attachment['name']) . '</dt>';
1940
				$log_these = array('attachments_no_create', 'attachments_no_write', 'attach_timeout', 'ran_out_of_space', 'cant_access_upload_path', 'attach_0_byte_file');
1941
				foreach ($attachmentOptions['errors'] as $error)
1942
				{
1943
					if (!is_array($error))
1944
					{
1945
						$attach_errors[] = '<dd>' . $txt[$error] . '</dd>';
1946 View Code Duplication
						if (in_array($error, $log_these))
1947
							log_error($attachment['name'] . ': ' . $txt[$error], 'critical');
1948
					}
1949
					else
1950
						$attach_errors[] = '<dd>' . vsprintf($txt[$error[0]], $error[1]) . '</dd>';
1951
				}
1952
				if (file_exists($attachment['tmp_name']))
1953
					unlink($attachment['tmp_name']);
1954
			}
1955
		}
1956
		unset($_SESSION['temp_attachments']);
1957
	}
1958
1959
	// Make the poll...
1960
	if (isset($_REQUEST['poll']))
1961
	{
1962
		// Create the poll.
1963
		$smcFunc['db_insert']('',
1964
			'{db_prefix}polls',
1965
			array(
1966
				'question' => 'string-255', 'hide_results' => 'int', 'max_votes' => 'int', 'expire_time' => 'int', 'id_member' => 'int',
1967
				'poster_name' => 'string-255', 'change_vote' => 'int', 'guest_vote' => 'int'
1968
			),
1969
			array(
1970
				$_POST['question'], $_POST['poll_hide'], $_POST['poll_max_votes'], (empty($_POST['poll_expire']) ? 0 : time() + $_POST['poll_expire'] * 3600 * 24), $user_info['id'],
1971
				$_POST['guestname'], $_POST['poll_change_vote'], $_POST['poll_guest_vote'],
1972
			),
1973
			array('id_poll')
1974
		);
1975
		$id_poll = $smcFunc['db_insert_id']('{db_prefix}polls', 'id_poll');
1976
1977
		// Create each answer choice.
1978
		$i = 0;
1979
		$pollOptions = array();
1980
		foreach ($_POST['options'] as $option)
1981
		{
1982
			$pollOptions[] = array($id_poll, $i, $option);
1983
			$i++;
1984
		}
1985
1986
		$smcFunc['db_insert']('insert',
1987
			'{db_prefix}poll_choices',
1988
			array('id_poll' => 'int', 'id_choice' => 'int', 'label' => 'string-255'),
1989
			$pollOptions,
1990
			array('id_poll', 'id_choice')
1991
		);
1992
1993
		call_integration_hook('integrate_poll_add_edit', array($id_poll, false));
1994
	}
1995
	else
1996
		$id_poll = 0;
1997
1998
	// Creating a new topic?
1999
	$newTopic = empty($_REQUEST['msg']) && empty($topic);
2000
2001
	// Check the icon.
2002
	if (!isset($_POST['icon']))
2003
		$_POST['icon'] = 'xx';
2004
2005
	else
2006
	{
2007
		$_POST['icon'] = $smcFunc['htmlspecialchars']($_POST['icon']);
2008
2009
		// Need to figure it out if this is a valid icon name.
2010
		if ((!file_exists($settings['theme_dir'] . '/images/post/' . $_POST['icon'] . '.png')) && (!file_exists($settings['default_theme_dir'] . '/images/post/' . $_POST['icon'] . '.png')))
2011
			$_POST['icon'] = 'xx';
2012
	}
2013
2014
	// Collect all parameters for the creation or modification of a post.
2015
	$msgOptions = array(
2016
		'id' => empty($_REQUEST['msg']) ? 0 : (int) $_REQUEST['msg'],
2017
		'subject' => $_POST['subject'],
2018
		'body' => $_POST['message'],
2019
		'icon' => preg_replace('~[\./\\\\*:"\'<>]~', '', $_POST['icon']),
2020
		'smileys_enabled' => !isset($_POST['ns']),
2021
		'attachments' => empty($attachIDs) ? array() : $attachIDs,
2022
		'approved' => $becomesApproved,
2023
	);
2024
	$topicOptions = array(
2025
		'id' => empty($topic) ? 0 : $topic,
2026
		'board' => $board,
2027
		'poll' => isset($_REQUEST['poll']) ? $id_poll : null,
2028
		'lock_mode' => isset($_POST['lock']) ? (int) $_POST['lock'] : null,
2029
		'sticky_mode' => isset($_POST['sticky']) ? (int) $_POST['sticky'] : null,
2030
		'mark_as_read' => true,
2031
		'is_approved' => !$modSettings['postmod_active'] || empty($topic) || !empty($board_info['cur_topic_approved']),
2032
	);
2033
	$posterOptions = array(
2034
		'id' => $user_info['id'],
2035
		'name' => $_POST['guestname'],
2036
		'email' => $_POST['email'],
2037
		'update_post_count' => !$user_info['is_guest'] && !isset($_REQUEST['msg']) && $board_info['posts_count'],
2038
	);
2039
2040
	// This is an already existing message. Edit it.
2041
	if (!empty($_REQUEST['msg']))
2042
	{
2043
		// Have admins allowed people to hide their screwups?
2044
		if (time() - $row['poster_time'] > $modSettings['edit_wait_time'] || $user_info['id'] != $row['id_member'])
2045
		{
2046
			$msgOptions['modify_time'] = time();
2047
			$msgOptions['modify_name'] = $user_info['name'];
2048
			$msgOptions['modify_reason'] = $_POST['modify_reason'];
2049
		}
2050
2051
		// This will save some time...
2052
		if (empty($approve_has_changed))
2053
			unset($msgOptions['approved']);
2054
2055
		modifyPost($msgOptions, $topicOptions, $posterOptions);
2056
	}
2057
	// This is a new topic or an already existing one. Save it.
2058
	else
2059
	{
2060
		createPost($msgOptions, $topicOptions, $posterOptions);
2061
2062
		if (isset($topicOptions['id']))
2063
			$topic = $topicOptions['id'];
2064
	}
2065
2066
	// Assign the previously uploaded attachments to the brand new message.
2067
	if (!empty($msgOptions['id']) && !empty($_SESSION['already_attached']))
2068
	{
2069
		require_once($sourcedir . '/Subs-Attachments.php');
2070
		assignAttachments($_SESSION['already_attached'], $msgOptions['id']);
2071
		unset($_SESSION['already_attached']);
2072
	}
2073
2074
	// If we had a draft for this, its time to remove it since it was just posted
2075
	if (!empty($modSettings['drafts_post_enabled']) && !empty($_POST['id_draft']))
2076
		DeleteDraft($_POST['id_draft']);
2077
2078
	// Editing or posting an event?
2079
	if (isset($_POST['calendar']) && (!isset($_REQUEST['eventid']) || $_REQUEST['eventid'] == -1))
2080
	{
2081
		require_once($sourcedir . '/Subs-Calendar.php');
2082
2083
		// Make sure they can link an event to this post.
2084
		canLinkEvent();
2085
2086
		// Insert the event.
2087
		$eventOptions = array(
2088
			'board' => $board,
2089
			'topic' => $topic,
2090
			'title' => $_POST['evtitle'],
2091
			'location' => $_POST['event_location'],
2092
			'member' => $user_info['id'],
2093
		);
2094
		insertEvent($eventOptions);
2095
	}
2096
	elseif (isset($_POST['calendar']))
2097
	{
2098
		$_REQUEST['eventid'] = (int) $_REQUEST['eventid'];
2099
2100
		// Validate the post...
2101
		require_once($sourcedir . '/Subs-Calendar.php');
2102
		validateEventPost();
2103
2104
		// If you're not allowed to edit any events, you have to be the poster.
2105
		if (!allowedTo('calendar_edit_any'))
2106
		{
2107
			// Get the event's poster.
2108
			$request = $smcFunc['db_query']('', '
2109
				SELECT id_member
2110
				FROM {db_prefix}calendar
2111
				WHERE id_event = {int:id_event}',
2112
				array(
2113
					'id_event' => $_REQUEST['eventid'],
2114
				)
2115
			);
2116
			$row2 = $smcFunc['db_fetch_assoc']($request);
2117
			$smcFunc['db_free_result']($request);
2118
2119
			// Silly hacker, Trix are for kids. ...probably trademarked somewhere, this is FAIR USE! (parody...)
2120
			isAllowedTo('calendar_edit_' . ($row2['id_member'] == $user_info['id'] ? 'own' : 'any'));
2121
		}
2122
2123
		// Delete it?
2124
		if (isset($_REQUEST['deleteevent']))
2125
			$smcFunc['db_query']('', '
2126
				DELETE FROM {db_prefix}calendar
2127
				WHERE id_event = {int:id_event}',
2128
				array(
2129
					'id_event' => $_REQUEST['eventid'],
2130
				)
2131
			);
2132
		// ... or just update it?
2133
		else
2134
		{
2135
			// Set up our options
2136
			$eventOptions = array(
2137
				'board' => $board,
2138
				'topic' => $topic,
2139
				'title' => $_POST['evtitle'],
2140
				'location' => $_POST['event_location'],
2141
				'member' => $user_info['id'],
2142
			);
2143
			modifyEvent($_REQUEST['eventid'], $eventOptions);
2144
		}
2145
	}
2146
2147
	// Marking read should be done even for editing messages....
2148
	// Mark all the parents read.  (since you just posted and they will be unread.)
2149
	if (!$user_info['is_guest'] && !empty($board_info['parent_boards']))
2150
	{
2151
		$smcFunc['db_query']('', '
2152
			UPDATE {db_prefix}log_boards
2153
			SET id_msg = {int:id_msg}
2154
			WHERE id_member = {int:current_member}
2155
				AND id_board IN ({array_int:board_list})',
2156
			array(
2157
				'current_member' => $user_info['id'],
2158
				'board_list' => array_keys($board_info['parent_boards']),
2159
				'id_msg' => $modSettings['maxMsgID'],
2160
			)
2161
		);
2162
	}
2163
2164
	// Turn notification on or off.  (note this just blows smoke if it's already on or off.)
2165
	if (!empty($_POST['notify']) && !$context['user']['is_guest'])
2166
	{
2167
		$smcFunc['db_insert']('ignore',
2168
			'{db_prefix}log_notify',
2169
			array('id_member' => 'int', 'id_topic' => 'int', 'id_board' => 'int'),
2170
			array($user_info['id'], $topic, 0),
2171
			array('id_member', 'id_topic', 'id_board')
2172
		);
2173
	}
2174
	elseif (!$newTopic)
2175
		$smcFunc['db_query']('', '
2176
			DELETE FROM {db_prefix}log_notify
2177
			WHERE id_member = {int:current_member}
2178
				AND id_topic = {int:current_topic}',
2179
			array(
2180
				'current_member' => $user_info['id'],
2181
				'current_topic' => $topic,
2182
			)
2183
		);
2184
2185
	// Log an act of moderation - modifying.
2186 View Code Duplication
	if (!empty($moderationAction))
2187
		logAction('modify', array('topic' => $topic, 'message' => (int) $_REQUEST['msg'], 'member' => $row['id_member'], 'board' => $board));
2188
2189 View Code Duplication
	if (isset($_POST['lock']) && $_POST['lock'] != 2)
2190
		logAction(empty($_POST['lock']) ? 'unlock' : 'lock', array('topic' => $topicOptions['id'], 'board' => $topicOptions['board']));
2191
2192 View Code Duplication
	if (isset($_POST['sticky']))
2193
		logAction(empty($_POST['sticky']) ? 'unsticky' : 'sticky', array('topic' => $topicOptions['id'], 'board' => $topicOptions['board']));
2194
2195
	// Returning to the topic?
2196 View Code Duplication
	if (!empty($_REQUEST['goback']))
2197
	{
2198
		// Mark the board as read.... because it might get confusing otherwise.
2199
		$smcFunc['db_query']('', '
2200
			UPDATE {db_prefix}log_boards
2201
			SET id_msg = {int:maxMsgID}
2202
			WHERE id_member = {int:current_member}
2203
				AND id_board = {int:current_board}',
2204
			array(
2205
				'current_board' => $board,
2206
				'current_member' => $user_info['id'],
2207
				'maxMsgID' => $modSettings['maxMsgID'],
2208
			)
2209
		);
2210
	}
2211
2212
	if ($board_info['num_topics'] == 0)
2213
		cache_put_data('board-' . $board, null, 120);
2214
2215
	call_integration_hook('integrate_post2_end');
2216
2217 View Code Duplication
	if (!empty($_POST['announce_topic']))
2218
		redirectexit('action=announce;sa=selectgroup;topic=' . $topic . (!empty($_POST['move']) && allowedTo('move_any') ? ';move' : '') . (empty($_REQUEST['goback']) ? '' : ';goback'));
2219
2220 View Code Duplication
	if (!empty($_POST['move']) && allowedTo('move_any'))
2221
		redirectexit('action=movetopic;topic=' . $topic . '.0' . (empty($_REQUEST['goback']) ? '' : ';goback'));
2222
2223
	// Return to post if the mod is on.
2224
	if (isset($_REQUEST['msg']) && !empty($_REQUEST['goback']))
2225
		redirectexit('topic=' . $topic . '.msg' . $_REQUEST['msg'] . '#msg' . $_REQUEST['msg'], isBrowser('ie'));
2226 View Code Duplication
	elseif (!empty($_REQUEST['goback']))
2227
		redirectexit('topic=' . $topic . '.new#new', isBrowser('ie'));
2228
	// Dut-dut-duh-duh-DUH-duh-dut-duh-duh!  *dances to the Final Fantasy Fanfare...*
2229
	else
2230
		redirectexit('board=' . $board . '.0');
2231
}
2232
2233
/**
2234
 * Handle the announce topic function (action=announce).
2235
 *
2236
 * checks the topic announcement permissions and loads the announcement template.
2237
 * requires the announce_topic permission.
2238
 * uses the ManageMembers template and Post language file.
2239
 * call the right function based on the sub-action.
2240
 */
2241
function AnnounceTopic()
2242
{
2243
	global $context, $txt, $topic;
2244
2245
	isAllowedTo('announce_topic');
2246
2247
	validateSession();
2248
2249
	if (empty($topic))
2250
		fatal_lang_error('topic_gone', false);
2251
2252
	loadLanguage('Post');
2253
	loadTemplate('Post');
2254
2255
	$subActions = array(
2256
		'selectgroup' => 'AnnouncementSelectMembergroup',
2257
		'send' => 'AnnouncementSend',
2258
	);
2259
2260
	$context['page_title'] = $txt['announce_topic'];
2261
2262
	// Call the function based on the sub-action.
2263
	$call = isset($_REQUEST['sa']) && isset($subActions[$_REQUEST['sa']]) ? $_REQUEST['sa'] : 'selectgroup';
2264
	call_helper($subActions[$call]);
2265
}
2266
2267
/**
2268
 * Allow a user to chose the membergroups to send the announcement to.
2269
 *
2270
 * lets the user select the membergroups that will receive the topic announcement.
2271
 */
2272
function AnnouncementSelectMembergroup()
2273
{
2274
	global $txt, $context, $topic, $board, $board_info, $smcFunc;
2275
2276
	$groups = array_merge($board_info['groups'], array(1));
2277
	foreach ($groups as $id => $group)
2278
		$groups[$id] = (int) $group;
2279
2280
	$context['groups'] = array();
2281
	if (in_array(0, $groups))
2282
	{
2283
		$context['groups'][0] = array(
2284
			'id' => 0,
2285
			'name' => $txt['announce_regular_members'],
2286
			'member_count' => 'n/a',
2287
		);
2288
	}
2289
2290
	// Get all membergroups that have access to the board the announcement was made on.
2291
	$request = $smcFunc['db_query']('', '
2292
		SELECT mg.id_group, COUNT(mem.id_member) AS num_members
2293
		FROM {db_prefix}membergroups AS mg
2294
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_group = mg.id_group OR FIND_IN_SET(mg.id_group, mem.additional_groups) != 0 OR mg.id_group = mem.id_post_group)
2295
		WHERE mg.id_group IN ({array_int:group_list})
2296
		GROUP BY mg.id_group',
2297
		array(
2298
			'group_list' => $groups,
2299
			'newbie_id_group' => 4,
2300
		)
2301
	);
2302
	while ($row = $smcFunc['db_fetch_assoc']($request))
2303
	{
2304
		$context['groups'][$row['id_group']] = array(
2305
			'id' => $row['id_group'],
2306
			'name' => '',
2307
			'member_count' => $row['num_members'],
2308
		);
2309
	}
2310
	$smcFunc['db_free_result']($request);
2311
2312
	// Now get the membergroup names.
2313
	$request = $smcFunc['db_query']('', '
2314
		SELECT id_group, group_name
2315
		FROM {db_prefix}membergroups
2316
		WHERE id_group IN ({array_int:group_list})',
2317
		array(
2318
			'group_list' => $groups,
2319
		)
2320
	);
2321 View Code Duplication
	while ($row = $smcFunc['db_fetch_assoc']($request))
2322
		$context['groups'][$row['id_group']]['name'] = $row['group_name'];
2323
	$smcFunc['db_free_result']($request);
2324
2325
	// Get the subject of the topic we're about to announce.
2326
	$request = $smcFunc['db_query']('', '
2327
		SELECT m.subject
2328
		FROM {db_prefix}topics AS t
2329
			INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)
2330
		WHERE t.id_topic = {int:current_topic}',
2331
		array(
2332
			'current_topic' => $topic,
2333
		)
2334
	);
2335
	list ($context['topic_subject']) = $smcFunc['db_fetch_row']($request);
2336
	$smcFunc['db_free_result']($request);
2337
2338
	censorText($context['announce_topic']['subject']);
2339
2340
	$context['move'] = isset($_REQUEST['move']) ? 1 : 0;
2341
	$context['go_back'] = isset($_REQUEST['goback']) ? 1 : 0;
2342
2343
	$context['sub_template'] = 'announce';
2344
}
2345
2346
/**
2347
 * Send the announcement in chunks.
2348
 *
2349
 * splits the members to be sent a topic announcement into chunks.
2350
 * composes notification messages in all languages needed.
2351
 * does the actual sending of the topic announcements in chunks.
2352
 * calculates a rough estimate of the percentage items sent.
2353
 */
2354
function AnnouncementSend()
2355
{
2356
	global $topic, $board, $board_info, $context, $modSettings;
2357
	global $language, $scripturl, $sourcedir, $smcFunc;
2358
2359
	checkSession();
2360
2361
	$context['start'] = empty($_REQUEST['start']) ? 0 : (int) $_REQUEST['start'];
2362
	$groups = array_merge($board_info['groups'], array(1));
2363
2364
	if (isset($_POST['membergroups']))
2365
		$_POST['who'] = explode(',', $_POST['membergroups']);
2366
2367
	// Check whether at least one membergroup was selected.
2368
	if (empty($_POST['who']))
2369
		fatal_lang_error('no_membergroup_selected');
2370
2371
	// Make sure all membergroups are integers and can access the board of the announcement.
2372
	foreach ($_POST['who'] as $id => $mg)
2373
		$_POST['who'][$id] = in_array((int) $mg, $groups) ? (int) $mg : 0;
2374
2375
	// Get the topic subject and censor it.
2376
	$request = $smcFunc['db_query']('', '
2377
		SELECT m.id_msg, m.subject, m.body
2378
		FROM {db_prefix}topics AS t
2379
			INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)
2380
		WHERE t.id_topic = {int:current_topic}',
2381
		array(
2382
			'current_topic' => $topic,
2383
		)
2384
	);
2385
	list ($id_msg, $context['topic_subject'], $message) = $smcFunc['db_fetch_row']($request);
2386
	$smcFunc['db_free_result']($request);
2387
2388
	censorText($context['topic_subject']);
2389
	censorText($message);
2390
2391
	$message = trim(un_htmlspecialchars(strip_tags(strtr(parse_bbc($message, false, $id_msg), array('<br>' => "\n", '</div>' => "\n", '</li>' => "\n", '&#91;' => '[', '&#93;' => ']')))));
2392
2393
	// We need this in order to be able send emails.
2394
	require_once($sourcedir . '/Subs-Post.php');
2395
2396
	// Select the email addresses for this batch.
2397
	$request = $smcFunc['db_query']('', '
2398
		SELECT mem.id_member, mem.email_address, mem.lngfile
2399
		FROM {db_prefix}members AS mem
2400
		WHERE (mem.id_group IN ({array_int:group_list}) OR mem.id_post_group IN ({array_int:group_list}) OR FIND_IN_SET({raw:additional_group_list}, mem.additional_groups) != 0)
2401
			AND mem.is_activated = {int:is_activated}
2402
			AND mem.id_member > {int:start}
2403
		ORDER BY mem.id_member
2404
		LIMIT {int:chunk_size}',
2405
		array(
2406
			'group_list' => $_POST['who'],
2407
			'is_activated' => 1,
2408
			'start' => $context['start'],
2409
			'additional_group_list' => implode(', mem.additional_groups) != 0 OR FIND_IN_SET(', $_POST['who']),
2410
			// @todo Might need an interface?
2411
			'chunk_size' => 500,
2412
		)
2413
	);
2414
2415
	// All members have received a mail. Go to the next screen.
2416
	if ($smcFunc['db_num_rows']($request) == 0)
2417
	{
2418
		logAction('announce_topic', array('topic' => $topic), 'user');
2419
		if (!empty($_REQUEST['move']) && allowedTo('move_any'))
2420
			redirectexit('action=movetopic;topic=' . $topic . '.0' . (empty($_REQUEST['goback']) ? '' : ';goback'));
2421 View Code Duplication
		elseif (!empty($_REQUEST['goback']))
2422
			redirectexit('topic=' . $topic . '.new;boardseen#new', isBrowser('ie'));
2423
		else
2424
			redirectexit('board=' . $board . '.0');
2425
	}
2426
2427
	$announcements = array();
2428
	// Loop through all members that'll receive an announcement in this batch.
2429
	$rows = array();
2430
	while ($row = $smcFunc['db_fetch_assoc']($request))
2431
	{
2432
		$rows[$row['id_member']] = $row;
2433
	}
2434
	$smcFunc['db_free_result']($request);
2435
2436
	// Load their alert preferences
2437
	require_once($sourcedir . '/Subs-Notify.php');
2438
	$prefs = getNotifyPrefs(array_keys($rows), 'announcements', true);
2439
2440
	foreach ($rows as $row)
2441
	{
2442
		// Force them to have it?
2443
		if (empty($prefs[$row['id_member']]['announcements']))
2444
			continue;
2445
2446
		$cur_language = empty($row['lngfile']) || empty($modSettings['userLanguage']) ? $language : $row['lngfile'];
2447
2448
		// If the language wasn't defined yet, load it and compose a notification message.
2449
		if (!isset($announcements[$cur_language]))
2450
		{
2451
			$replacements = array(
2452
				'TOPICSUBJECT' => $context['topic_subject'],
2453
				'MESSAGE' => $message,
2454
				'TOPICLINK' => $scripturl . '?topic=' . $topic . '.0',
2455
			);
2456
2457
			$emaildata = loadEmailTemplate('new_announcement', $replacements, $cur_language);
2458
2459
			$announcements[$cur_language] = array(
2460
				'subject' => $emaildata['subject'],
2461
				'body' => $emaildata['body'],
2462
				'is_html' => $emaildata['is_html'],
2463
				'recipients' => array(),
2464
			);
2465
		}
2466
2467
		$announcements[$cur_language]['recipients'][$row['id_member']] = $row['email_address'];
2468
		$context['start'] = $row['id_member'];
2469
	}
2470
2471
	// For each language send a different mail - low priority...
2472
	foreach ($announcements as $lang => $mail)
2473
		sendmail($mail['recipients'], $mail['subject'], $mail['body'], null, 'ann-' . $lang, $mail['is_html'], 5);
2474
2475
	$context['percentage_done'] = round(100 * $context['start'] / $modSettings['latestMember'], 1);
2476
2477
	$context['move'] = empty($_REQUEST['move']) ? 0 : 1;
2478
	$context['go_back'] = empty($_REQUEST['goback']) ? 0 : 1;
2479
	$context['membergroups'] = implode(',', $_POST['who']);
2480
	$context['sub_template'] = 'announcement_send';
2481
2482
	// Go back to the correct language for the user ;).
2483
	if (!empty($modSettings['userLanguage']))
2484
		loadLanguage('Post');
2485
}
2486
2487
/**
2488
 * Get the topic for display purposes.
2489
 *
2490
 * gets a summary of the most recent posts in a topic.
2491
 * depends on the topicSummaryPosts setting.
2492
 * if you are editing a post, only shows posts previous to that post.
2493
 */
2494
function getTopic()
2495
{
2496
	global $topic, $modSettings, $context, $smcFunc, $counter, $options;
2497
2498
	if (isset($_REQUEST['xml']))
2499
		$limit = '
2500
		LIMIT ' . (empty($context['new_replies']) ? '0' : $context['new_replies']);
2501
	else
2502
		$limit = empty($modSettings['topicSummaryPosts']) ? '' : '
2503
		LIMIT ' . (int) $modSettings['topicSummaryPosts'];
2504
2505
	// If you're modifying, get only those posts before the current one. (otherwise get all.)
2506
	$request = $smcFunc['db_query']('', '
2507
		SELECT
2508
			COALESCE(mem.real_name, m.poster_name) AS poster_name, m.poster_time,
2509
			m.body, m.smileys_enabled, m.id_msg, m.id_member
2510
		FROM {db_prefix}messages AS m
2511
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
2512
		WHERE m.id_topic = {int:current_topic}' . (isset($_REQUEST['msg']) ? '
2513
			AND m.id_msg < {int:id_msg}' : '') .(!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : '
2514
			AND m.approved = {int:approved}') . '
2515
		ORDER BY m.id_msg DESC' . $limit,
2516
		array(
2517
			'current_topic' => $topic,
2518
			'id_msg' => isset($_REQUEST['msg']) ? (int) $_REQUEST['msg'] : 0,
2519
			'approved' => 1,
2520
		)
2521
	);
2522
	$context['previous_posts'] = array();
2523
	while ($row = $smcFunc['db_fetch_assoc']($request))
2524
	{
2525
		// Censor, BBC, ...
2526
		censorText($row['body']);
2527
		$row['body'] = parse_bbc($row['body'], $row['smileys_enabled'], $row['id_msg']);
2528
2529
		// ...and store.
2530
		$context['previous_posts'][] = array(
2531
			'counter' => $counter++,
2532
			'poster' => $row['poster_name'],
2533
			'message' => $row['body'],
2534
			'time' => timeformat($row['poster_time']),
2535
			'timestamp' => forum_time(true, $row['poster_time']),
2536
			'id' => $row['id_msg'],
2537
			'is_new' => !empty($context['new_replies']),
2538
			'is_ignored' => !empty($modSettings['enable_buddylist']) && !empty($options['posts_apply_ignore_list']) && in_array($row['id_member'], $context['user']['ignoreusers']),
2539
		);
2540
2541
		if (!empty($context['new_replies']))
2542
			$context['new_replies']--;
2543
	}
2544
	$smcFunc['db_free_result']($request);
2545
}
2546
2547
/**
2548
 * Loads a post an inserts it into the current editing text box.
2549
 * uses the Post language file.
2550
 * uses special (sadly browser dependent) javascript to parse entities for internationalization reasons.
2551
 * accessed with ?action=quotefast.
2552
 */
2553
function QuoteFast()
2554
{
2555
	global $modSettings, $user_info, $context;
2556
	global $sourcedir, $smcFunc;
2557
2558
	loadLanguage('Post');
2559
	if (!isset($_REQUEST['xml']))
2560
		loadTemplate('Post');
2561
2562
	include_once($sourcedir . '/Subs-Post.php');
2563
2564
	$moderate_boards = boardsAllowedTo('moderate_board');
2565
2566
	// Where we going if we need to?
2567
	$context['post_box_name'] = isset($_GET['pb']) ? $_GET['pb'] : '';
2568
2569
	$request = $smcFunc['db_query']('', '
2570
		SELECT COALESCE(mem.real_name, m.poster_name) AS poster_name, m.poster_time, m.body, m.id_topic, m.subject,
2571
			m.id_board, m.id_member, m.approved, m.modified_time, m.modified_name, m.modified_reason
2572
		FROM {db_prefix}messages AS m
2573
			INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic)
2574
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board AND {query_see_board})
2575
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
2576
		WHERE m.id_msg = {int:id_msg}' . (isset($_REQUEST['modify']) || (!empty($moderate_boards) && $moderate_boards[0] == 0) ? '' : '
2577
			AND (t.locked = {int:not_locked}' . (empty($moderate_boards) ? '' : ' OR b.id_board IN ({array_int:moderation_board_list})') . ')') . '
2578
		LIMIT 1',
2579
		array(
2580
			'current_member' => $user_info['id'],
2581
			'moderation_board_list' => $moderate_boards,
2582
			'id_msg' => (int) $_REQUEST['quote'],
2583
			'not_locked' => 0,
2584
		)
2585
	);
2586
	$context['close_window'] = $smcFunc['db_num_rows']($request) == 0;
2587
	$row = $smcFunc['db_fetch_assoc']($request);
2588
	$smcFunc['db_free_result']($request);
2589
2590
	$context['sub_template'] = 'quotefast';
2591
	if (!empty($row))
2592
		$can_view_post = $row['approved'] || ($row['id_member'] != 0 && $row['id_member'] == $user_info['id']) || allowedTo('approve_posts', $row['id_board']);
2593
2594
	if (!empty($can_view_post))
2595
	{
2596
		// Remove special formatting we don't want anymore.
2597
		$row['body'] = un_preparsecode($row['body']);
2598
2599
		// Censor the message!
2600
		censorText($row['body']);
2601
2602
		$row['body'] = preg_replace('~<br ?/?' . '>~i', "\n", $row['body']);
2603
2604
		// Want to modify a single message by double clicking it?
2605
		if (isset($_REQUEST['modify']))
2606
		{
2607
			censorText($row['subject']);
2608
2609
			$context['sub_template'] = 'modifyfast';
2610
			$context['message'] = array(
2611
				'id' => $_REQUEST['quote'],
2612
				'body' => $row['body'],
2613
				'subject' => addcslashes($row['subject'], '"'),
2614
				'reason' => array(
2615
					'name' => $row['modified_name'],
2616
					'text' => $row['modified_reason'],
2617
					'time' => $row['modified_time'],
2618
				),
2619
			);
2620
2621
			return;
2622
		}
2623
2624
		// Remove any nested quotes.
2625 View Code Duplication
		if (!empty($modSettings['removeNestedQuotes']))
2626
			$row['body'] = preg_replace(array('~\n?\[quote.*?\].+?\[/quote\]\n?~is', '~^\n~', '~\[/quote\]~'), '', $row['body']);
2627
2628
		$lb = "\n";
2629
2630
		// Add a quote string on the front and end.
2631
		$context['quote']['xml'] = '[quote author=' . $row['poster_name'] . ' link=msg=' . (int) $_REQUEST['quote'] . ' date=' . $row['poster_time'] . ']' . $lb . $row['body'] . $lb . '[/quote]';
2632
		$context['quote']['text'] = strtr(un_htmlspecialchars($context['quote']['xml']), array('\'' => '\\\'', '\\' => '\\\\', "\n" => '\\n', '</script>' => '</\' + \'script>'));
2633
		$context['quote']['xml'] = strtr($context['quote']['xml'], array('&nbsp;' => '&#160;', '<' => '&lt;', '>' => '&gt;'));
2634
2635
		$context['quote']['mozilla'] = strtr($smcFunc['htmlspecialchars']($context['quote']['text']), array('&quot;' => '"'));
2636
	}
2637
	//@todo Needs a nicer interface.
2638
	// In case our message has been removed in the meantime.
2639
	elseif (isset($_REQUEST['modify']))
2640
	{
2641
		$context['sub_template'] = 'modifyfast';
2642
		$context['message'] = array(
2643
			'id' => 0,
2644
			'body' => '',
2645
			'subject' => '',
2646
			'reason' => array(
2647
				'name' => '',
2648
				'text' => '',
2649
				'time' => '',
2650
			),
2651
		);
2652
	}
2653
	else
2654
		$context['quote'] = array(
2655
			'xml' => '',
2656
			'mozilla' => '',
2657
			'text' => '',
2658
		);
2659
}
2660
2661
/**
2662
 * Used to edit the body or subject of a message inline
2663
 * called from action=jsmodify from script and topic js
2664
 */
2665
function JavaScriptModify()
2666
{
2667
	global $sourcedir, $modSettings, $board, $topic, $txt;
2668
	global $user_info, $context, $smcFunc, $language, $board_info;
2669
2670
	// We have to have a topic!
2671
	if (empty($topic))
2672
		obExit(false);
2673
2674
	checkSession('get');
2675
	require_once($sourcedir . '/Subs-Post.php');
2676
2677
	// Assume the first message if no message ID was given.
2678
	$request = $smcFunc['db_query']('', '
2679
		SELECT
2680
			t.locked, t.num_replies, t.id_member_started, t.id_first_msg,
2681
			m.id_msg, m.id_member, m.poster_time, m.subject, m.smileys_enabled, m.body, m.icon,
2682
			m.modified_time, m.modified_name, m.modified_reason, m.approved,
2683
			m.poster_name, m.poster_email
2684
		FROM {db_prefix}messages AS m
2685
			INNER JOIN {db_prefix}topics AS t ON (t.id_topic = {int:current_topic})
2686
		WHERE m.id_msg = {raw:id_msg}
2687
			AND m.id_topic = {int:current_topic}' . (allowedTo('modify_any') || allowedTo('approve_posts') ? '' : (!$modSettings['postmod_active'] ? '
2688
			AND (m.id_member != {int:guest_id} AND m.id_member = {int:current_member})' : '
2689
			AND (m.approved = {int:is_approved} OR (m.id_member != {int:guest_id} AND m.id_member = {int:current_member}))')),
2690
		array(
2691
			'current_member' => $user_info['id'],
2692
			'current_topic' => $topic,
2693
			'id_msg' => empty($_REQUEST['msg']) ? 't.id_first_msg' : (int) $_REQUEST['msg'],
2694
			'is_approved' => 1,
2695
			'guest_id' => 0,
2696
		)
2697
	);
2698
	if ($smcFunc['db_num_rows']($request) == 0)
2699
		fatal_lang_error('no_board', false);
0 ignored issues
show
false is of type boolean, but the function expects a string.

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...
2700
	$row = $smcFunc['db_fetch_assoc']($request);
2701
	$smcFunc['db_free_result']($request);
2702
2703
	// Change either body or subject requires permissions to modify messages.
2704
	if (isset($_POST['message']) || isset($_POST['subject']) || isset($_REQUEST['icon']))
2705
	{
2706
		if (!empty($row['locked']))
2707
			isAllowedTo('moderate_board');
2708
2709 View Code Duplication
		if ($row['id_member'] == $user_info['id'] && !allowedTo('modify_any'))
2710
		{
2711
			if ((!$modSettings['postmod_active'] || $row['approved']) && !empty($modSettings['edit_disable_time']) && $row['poster_time'] + ($modSettings['edit_disable_time'] + 5) * 60 < time())
2712
				fatal_lang_error('modify_post_time_passed', false);
2713
			elseif ($row['id_member_started'] == $user_info['id'] && !allowedTo('modify_own'))
2714
				isAllowedTo('modify_replies');
2715
			else
2716
				isAllowedTo('modify_own');
2717
		}
2718
		// Otherwise, they're locked out; someone who can modify the replies is needed.
2719
		elseif ($row['id_member_started'] == $user_info['id'] && !allowedTo('modify_any'))
2720
			isAllowedTo('modify_replies');
2721
		else
2722
			isAllowedTo('modify_any');
2723
2724
		// Only log this action if it wasn't your message.
2725
		$moderationAction = $row['id_member'] != $user_info['id'];
2726
	}
2727
2728
	$post_errors = array();
2729
	if (isset($_POST['subject']) && $smcFunc['htmltrim']($smcFunc['htmlspecialchars']($_POST['subject'])) !== '')
2730
	{
2731
		$_POST['subject'] = strtr($smcFunc['htmlspecialchars']($_POST['subject']), array("\r" => '', "\n" => '', "\t" => ''));
2732
2733
		// Maximum number of characters.
2734 View Code Duplication
		if ($smcFunc['strlen']($_POST['subject']) > 100)
2735
			$_POST['subject'] = $smcFunc['substr']($_POST['subject'], 0, 100);
2736
	}
2737
	elseif (isset($_POST['subject']))
2738
	{
2739
		$post_errors[] = 'no_subject';
2740
		unset($_POST['subject']);
2741
	}
2742
2743
	if (isset($_POST['message']))
2744
	{
2745
		if ($smcFunc['htmltrim']($smcFunc['htmlspecialchars']($_POST['message'])) === '')
2746
		{
2747
			$post_errors[] = 'no_message';
2748
			unset($_POST['message']);
2749
		}
2750 View Code Duplication
		elseif (!empty($modSettings['max_messageLength']) && $smcFunc['strlen']($_POST['message']) > $modSettings['max_messageLength'])
2751
		{
2752
			$post_errors[] = 'long_message';
2753
			unset($_POST['message']);
2754
		}
2755
		else
2756
		{
2757
			$_POST['message'] = $smcFunc['htmlspecialchars']($_POST['message'], ENT_QUOTES);
2758
2759
			preparsecode($_POST['message']);
2760
2761
			if ($smcFunc['htmltrim'](strip_tags(parse_bbc($_POST['message'], false), implode('', $context['allowed_html_tags']))) === '')
2762
			{
2763
				$post_errors[] = 'no_message';
2764
				unset($_POST['message']);
2765
			}
2766
		}
2767
	}
2768
2769
	if (isset($_POST['lock']))
2770
	{
2771
		if (!allowedTo(array('lock_any', 'lock_own')) || (!allowedTo('lock_any') && $user_info['id'] != $row['id_member']))
2772
			unset($_POST['lock']);
2773
		elseif (!allowedTo('lock_any'))
2774
		{
2775
			if ($row['locked'] == 1)
2776
				unset($_POST['lock']);
2777
			else
2778
				$_POST['lock'] = empty($_POST['lock']) ? 0 : 2;
2779
		}
2780
		elseif (!empty($row['locked']) && !empty($_POST['lock']) || $_POST['lock'] == $row['locked'])
2781
			unset($_POST['lock']);
2782
		else
2783
			$_POST['lock'] = empty($_POST['lock']) ? 0 : 1;
2784
	}
2785
2786
	if (isset($_POST['sticky']) && !allowedTo('make_sticky'))
2787
		unset($_POST['sticky']);
2788
2789
	if (isset($_POST['modify_reason']))
2790
	{
2791
		$_POST['modify_reason'] = strtr($smcFunc['htmlspecialchars']($_POST['modify_reason']), array("\r" => '', "\n" => '', "\t" => ''));
2792
2793
		// Maximum number of characters.
2794 View Code Duplication
		if ($smcFunc['strlen']($_POST['modify_reason']) > 100)
2795
			$_POST['modify_reason'] = $smcFunc['substr']($_POST['modify_reason'], 0, 100);
2796
	}
2797
2798
	if (empty($post_errors))
2799
	{
2800
		$msgOptions = array(
2801
			'id' => $row['id_msg'],
2802
			'subject' => isset($_POST['subject']) ? $_POST['subject'] : null,
2803
			'body' => isset($_POST['message']) ? $_POST['message'] : null,
2804
			'icon' => isset($_REQUEST['icon']) ? preg_replace('~[\./\\\\*\':"<>]~', '', $_REQUEST['icon']) : null,
2805
			'modify_reason' => (isset($_POST['modify_reason']) ? $_POST['modify_reason'] : ''),
2806
		);
2807
		$topicOptions = array(
2808
			'id' => $topic,
2809
			'board' => $board,
2810
			'lock_mode' => isset($_POST['lock']) ? (int) $_POST['lock'] : null,
2811
			'sticky_mode' => isset($_POST['sticky']) ? (int) $_POST['sticky'] : null,
2812
			'mark_as_read' => true,
2813
		);
2814
		$posterOptions = array(
2815
			'id' => $user_info['id'],
2816
			'name' => $row['poster_name'],
2817
			'email' => $row['poster_email'],
2818
			'update_post_count' => !$user_info['is_guest'] && !isset($_REQUEST['msg']) && $board_info['posts_count'],
2819
		);
2820
2821
		// Only consider marking as editing if they have edited the subject, message or icon.
2822
		if ((isset($_POST['subject']) && $_POST['subject'] != $row['subject']) || (isset($_POST['message']) && $_POST['message'] != $row['body']) || (isset($_REQUEST['icon']) && $_REQUEST['icon'] != $row['icon']))
2823
		{
2824
			// And even then only if the time has passed...
2825
			if (time() - $row['poster_time'] > $modSettings['edit_wait_time'] || $user_info['id'] != $row['id_member'])
2826
			{
2827
				$msgOptions['modify_time'] = time();
2828
				$msgOptions['modify_name'] = $user_info['name'];
2829
			}
2830
		}
2831
		// If nothing was changed there's no need to add an entry to the moderation log.
2832
		else
2833
			$moderationAction = false;
2834
2835
		modifyPost($msgOptions, $topicOptions, $posterOptions);
2836
2837
		// If we didn't change anything this time but had before put back the old info.
2838 View Code Duplication
		if (!isset($msgOptions['modify_time']) && !empty($row['modified_time']))
2839
		{
2840
			$msgOptions['modify_time'] = $row['modified_time'];
2841
			$msgOptions['modify_name'] = $row['modified_name'];
2842
			$msgOptions['modify_reason'] = $row['modified_reason'];
2843
		}
2844
2845
		// Changing the first subject updates other subjects to 'Re: new_subject'.
2846
		if (isset($_POST['subject']) && isset($_REQUEST['change_all_subjects']) && $row['id_first_msg'] == $row['id_msg'] && !empty($row['num_replies']) && (allowedTo('modify_any') || ($row['id_member_started'] == $user_info['id'] && allowedTo('modify_replies'))))
2847
		{
2848
			// Get the proper (default language) response prefix first.
2849 View Code Duplication
			if (!isset($context['response_prefix']) && !($context['response_prefix'] = cache_get_data('response_prefix')))
2850
			{
2851
				if ($language === $user_info['language'])
2852
					$context['response_prefix'] = $txt['response_prefix'];
2853
				else
2854
				{
2855
					loadLanguage('index', $language, false);
2856
					$context['response_prefix'] = $txt['response_prefix'];
2857
					loadLanguage('index');
2858
				}
2859
				cache_put_data('response_prefix', $context['response_prefix'], 600);
2860
			}
2861
2862
			$smcFunc['db_query']('', '
2863
				UPDATE {db_prefix}messages
2864
				SET subject = {string:subject}
2865
				WHERE id_topic = {int:current_topic}
2866
					AND id_msg != {int:id_first_msg}',
2867
				array(
2868
					'current_topic' => $topic,
2869
					'id_first_msg' => $row['id_first_msg'],
2870
					'subject' => $context['response_prefix'] . $_POST['subject'],
2871
				)
2872
			);
2873
		}
2874
2875 View Code Duplication
		if (!empty($moderationAction))
2876
			logAction('modify', array('topic' => $topic, 'message' => $row['id_msg'], 'member' => $row['id_member'], 'board' => $board));
2877
	}
2878
2879
	if (isset($_REQUEST['xml']))
2880
	{
2881
		$context['sub_template'] = 'modifydone';
2882
		if (empty($post_errors) && isset($msgOptions['subject']) && isset($msgOptions['body']))
2883
		{
2884
			$context['message'] = array(
2885
				'id' => $row['id_msg'],
2886
				'modified' => array(
2887
					'time' => isset($msgOptions['modify_time']) ? timeformat($msgOptions['modify_time']) : '',
2888
					'timestamp' => isset($msgOptions['modify_time']) ? forum_time(true, $msgOptions['modify_time']) : 0,
2889
					'name' => isset($msgOptions['modify_time']) ? $msgOptions['modify_name'] : '',
2890
					'reason' => $msgOptions['modify_reason'],
2891
				),
2892
				'subject' => $msgOptions['subject'],
2893
				'first_in_topic' => $row['id_msg'] == $row['id_first_msg'],
2894
				'body' => strtr($msgOptions['body'], array(']]>' => ']]]]><![CDATA[>')),
2895
			);
2896
2897
			censorText($context['message']['subject']);
2898
			censorText($context['message']['body']);
2899
2900
			$context['message']['body'] = parse_bbc($context['message']['body'], $row['smileys_enabled'], $row['id_msg']);
2901
		}
2902
		// Topic?
2903
		elseif (empty($post_errors))
2904
		{
2905
			$context['sub_template'] = 'modifytopicdone';
2906
			$context['message'] = array(
2907
				'id' => $row['id_msg'],
2908
				'modified' => array(
2909
					'time' => isset($msgOptions['modify_time']) ? timeformat($msgOptions['modify_time']) : '',
2910
					'timestamp' => isset($msgOptions['modify_time']) ? forum_time(true, $msgOptions['modify_time']) : 0,
2911
					'name' => isset($msgOptions['modify_time']) ? $msgOptions['modify_name'] : '',
2912
				),
2913
				'subject' => isset($msgOptions['subject']) ? $msgOptions['subject'] : '',
2914
			);
2915
2916
			censorText($context['message']['subject']);
2917
		}
2918
		else
2919
		{
2920
			$context['message'] = array(
2921
				'id' => $row['id_msg'],
2922
				'errors' => array(),
2923
				'error_in_subject' => in_array('no_subject', $post_errors),
2924
				'error_in_body' => in_array('no_message', $post_errors) || in_array('long_message', $post_errors),
2925
			);
2926
2927
			loadLanguage('Errors');
2928
			foreach ($post_errors as $post_error)
2929
			{
2930
				if ($post_error == 'long_message')
2931
					$context['message']['errors'][] = sprintf($txt['error_' . $post_error], $modSettings['max_messageLength']);
2932
				else
2933
					$context['message']['errors'][] = $txt['error_' . $post_error];
2934
			}
2935
		}
2936
	}
2937
	else
2938
		obExit(false);
2939
}
2940
2941
?>