Issues (1065)

Sources/Post.php (2 issues)

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 https://www.simplemachines.org
11
 * @copyright 2025 Simple Machines and individual contributors
12
 * @license https://www.simplemachines.org/about/smf/license.php BSD
13
 *
14
 * @version 2.1.5
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, $options;
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
49
	call_integration_hook('integrate_post_start');
50
51
	// Get notification preferences for later
52
	require_once($sourcedir . '/Subs-Notify.php');
53
	// use $temp to get around "Only variables should be passed by reference"
54
	$temp = getNotifyPrefs($user_info['id']);
55
	$context['notify_prefs'] = (array) array_pop($temp);
56
	$context['auto_notify'] = !empty($context['notify_prefs']['msg_auto_notify']);
57
58
	// Not in a board? Fine, but we'll make them pick one eventually.
59
	if (empty($board) || $context['make_event'])
60
	{
61
		// Get ids of all the boards they can post in.
62
		$post_permissions = array('post_new');
63
		if ($modSettings['postmod_active'])
64
			$post_permissions[] = 'post_unapproved_topics';
65
66
		$boards = boardsAllowedTo($post_permissions);
67
		if (empty($boards))
68
			fatal_lang_error('cannot_post_new', false);
69
70
		// Get a list of boards for the select menu
71
		require_once($sourcedir . '/Subs-MessageIndex.php');
72
		$boardListOptions = array(
73
			'included_boards' => in_array(0, $boards) ? null : $boards,
74
			'not_redirection' => true,
75
			'use_permissions' => true,
76
			'selected_board' => !empty($board) ? $board : ($context['make_event'] && !empty($modSettings['cal_defaultboard']) ? $modSettings['cal_defaultboard'] : $boards[0]),
77
		);
78
		$board_list = getBoardList($boardListOptions);
79
	}
80
	// Let's keep things simple for ourselves below
81
	else
82
		$boards = array($board);
83
84
	require_once($sourcedir . '/Subs-Post.php');
85
86
	if (isset($_REQUEST['xml']))
87
	{
88
		$context['sub_template'] = 'post';
89
90
		// Just in case of an earlier error...
91
		$context['preview_message'] = '';
92
		$context['preview_subject'] = '';
93
	}
94
95
	// No message is complete without a topic.
96
	if (empty($topic) && !empty($_REQUEST['msg']))
97
	{
98
		$request = $smcFunc['db_query']('', '
99
			SELECT id_topic
100
			FROM {db_prefix}messages
101
			WHERE id_msg = {int:msg}',
102
			array(
103
				'msg' => (int) $_REQUEST['msg'],
104
			)
105
		);
106
		if ($smcFunc['db_num_rows']($request) != 1)
107
			unset($_REQUEST['msg'], $_POST['msg'], $_GET['msg']);
108
		else
109
			list ($topic) = $smcFunc['db_fetch_row']($request);
110
		$smcFunc['db_free_result']($request);
111
	}
112
113
	// Check if it's locked. It isn't locked if no topic is specified.
114
	if (!empty($topic))
115
	{
116
		$request = $smcFunc['db_query']('', '
117
			SELECT
118
				t.locked, t.approved, COALESCE(ln.id_topic, 0) AS notify, t.is_sticky, t.id_poll, t.id_last_msg, mf.id_member,
119
				t.id_first_msg, mf.subject, ml.modified_reason,
120
				CASE WHEN ml.poster_time > ml.modified_time THEN ml.poster_time ELSE ml.modified_time END AS last_post_time
121
			FROM {db_prefix}topics AS t
122
				LEFT JOIN {db_prefix}log_notify AS ln ON (ln.id_topic = t.id_topic AND ln.id_member = {int:current_member})
123
				LEFT JOIN {db_prefix}messages AS mf ON (mf.id_msg = t.id_first_msg)
124
				LEFT JOIN {db_prefix}messages AS ml ON (ml.id_msg = t.id_last_msg)
125
			WHERE t.id_topic = {int:current_topic}
126
			LIMIT 1',
127
			array(
128
				'current_member' => $user_info['id'],
129
				'current_topic' => $topic,
130
			)
131
		);
132
		list ($locked, $topic_approved, $context['notify'], $sticky, $pollID, $context['topic_last_message'], $id_member_poster, $id_first_msg, $first_subject, $editReason, $lastPostTime) = $smcFunc['db_fetch_row']($request);
133
		$smcFunc['db_free_result']($request);
134
135
		// If this topic already has a poll, they sure can't add another.
136
		if (isset($_REQUEST['poll']) && $pollID > 0)
137
			unset($_REQUEST['poll']);
138
139
		if (empty($_REQUEST['msg']))
140
		{
141
			if ($user_info['is_guest'] && !allowedTo('post_reply_any') && (!$modSettings['postmod_active'] || !allowedTo('post_unapproved_replies_any')))
142
				is_not_guest();
143
144
			// By default the reply will be approved...
145
			$context['becomes_approved'] = true;
146
			if ($id_member_poster != $user_info['id'] || $user_info['is_guest'])
147
			{
148
				if ($modSettings['postmod_active'] && allowedTo('post_unapproved_replies_any') && !allowedTo('post_reply_any'))
149
					$context['becomes_approved'] = false;
150
				else
151
					isAllowedTo('post_reply_any');
152
			}
153
			elseif (!allowedTo('post_reply_any'))
154
			{
155
				if ($modSettings['postmod_active'] && ((allowedTo('post_unapproved_replies_own') && !allowedTo('post_reply_own')) || allowedTo('post_unapproved_replies_any')))
156
					$context['becomes_approved'] = false;
157
				else
158
					isAllowedTo('post_reply_own');
159
			}
160
		}
161
		else
162
			$context['becomes_approved'] = true;
163
164
		$context['can_lock'] = allowedTo('lock_any') || ($user_info['id'] == $id_member_poster && allowedTo('lock_own'));
165
		$context['can_sticky'] = allowedTo('make_sticky');
166
		$context['can_move'] = allowedTo('move_any');
167
		// You can only announce topics that will get approved...
168
		$context['can_announce'] = allowedTo('announce_topic') && $context['becomes_approved'];
169
		$context['show_approval'] = !allowedTo('approve_posts') ? 0 : ($context['becomes_approved'] ? 2 : 1);
170
171
		// We don't always want the request vars to override what's in the db...
172
		$context['already_locked'] = $locked;
173
		$context['already_sticky'] = $sticky;
174
		$context['sticky'] = isset($_REQUEST['sticky']) ? !empty($_REQUEST['sticky']) : $sticky;
175
176
		// Check whether this is a really old post being bumped...
177
		if (!empty($modSettings['oldTopicDays']) && $lastPostTime + $modSettings['oldTopicDays'] * 86400 < time() && empty($sticky) && !isset($_REQUEST['subject']))
178
			$post_errors[] = array('old_topic', array($modSettings['oldTopicDays']));
179
	}
180
	else
181
	{
182
		// @todo Should use JavaScript to hide and show the warning based on the selection in the board select menu
183
		$context['becomes_approved'] = true;
184
		if ($modSettings['postmod_active'] && !allowedTo('post_new', $boards, true) && allowedTo('post_unapproved_topics', $boards, true))
185
			$context['becomes_approved'] = false;
186
		else
187
			isAllowedTo('post_new', $boards, true);
188
189
		$locked = 0;
190
		$context['already_locked'] = 0;
191
		$context['already_sticky'] = 0;
192
		$context['sticky'] = !empty($_REQUEST['sticky']);
193
194
		// What options should we show?
195
		$context['can_lock'] = allowedTo(array('lock_any', 'lock_own'), $boards, true);
196
		$context['can_sticky'] = allowedTo('make_sticky', $boards, true);
197
		$context['can_move'] = allowedTo('move_any', $boards, true);
198
		$context['can_announce'] = allowedTo('announce_topic', $boards, true) && $context['becomes_approved'];
199
		$context['show_approval'] = !allowedTo('approve_posts', $boards, true) ? 0 : ($context['becomes_approved'] ? 2 : 1);
200
	}
201
202
	$context['notify'] = !empty($context['notify']);
203
204
	$context['can_notify'] = !$context['user']['is_guest'];
205
	$context['move'] = !empty($_REQUEST['move']);
206
	$context['announce'] = !empty($_REQUEST['announce']);
207
	$context['locked'] = !empty($locked) || !empty($_REQUEST['lock']);
208
	$context['can_quote'] = empty($modSettings['disabledBBC']) || !in_array('quote', explode(',', $modSettings['disabledBBC']));
209
210
	// An array to hold all the attachments for this topic.
211
	$context['current_attachments'] = array();
212
213
	// Clear out prior attachment activity when starting afresh
214
	if (empty($_REQUEST['message']) && empty($_REQUEST['preview']) && !empty($_SESSION['already_attached']))
215
	{
216
		require_once($sourcedir . '/ManageAttachments.php');
217
		foreach ($_SESSION['already_attached'] as $attachID => $attachment)
218
			removeAttachments(array('id_attach' => $attachID));
219
220
		unset($_SESSION['already_attached']);
221
	}
222
223
	// Don't allow a post if it's locked and you aren't all powerful.
224
	if ($locked && !allowedTo('moderate_board'))
225
		fatal_lang_error('topic_locked', false);
226
	// Check the users permissions - is the user allowed to add or post a poll?
227
	if (isset($_REQUEST['poll']) && $modSettings['pollMode'] == '1')
228
	{
229
		// New topic, new poll.
230
		if (empty($topic))
231
			isAllowedTo('poll_post');
232
		// This is an old topic - but it is yours!  Can you add to it?
233
		elseif ($user_info['id'] == $id_member_poster && !allowedTo('poll_add_any'))
234
			isAllowedTo('poll_add_own');
235
		// If you're not the owner, can you add to any poll?
236
		else
237
			isAllowedTo('poll_add_any');
238
239
		if (!empty($board))
240
		{
241
			require_once($sourcedir . '/Subs-Members.php');
242
			$allowedVoteGroups = groupsAllowedTo('poll_vote', $board);
243
			$guest_vote_enabled = in_array(-1, $allowedVoteGroups['allowed']);
244
		}
245
		// No board, so we'll have to check this again in Post2
246
		else
247
			$guest_vote_enabled = true;
248
249
		// Set up the poll options.
250
		$context['poll_options'] = array(
251
			'max_votes' => empty($_POST['poll_max_votes']) ? '1' : max(1, $_POST['poll_max_votes']),
252
			'hide' => empty($_POST['poll_hide']) ? 0 : $_POST['poll_hide'],
253
			'expire' => !isset($_POST['poll_expire']) ? '' : $_POST['poll_expire'],
254
			'change_vote' => isset($_POST['poll_change_vote']),
255
			'guest_vote' => isset($_POST['poll_guest_vote']),
256
			'guest_vote_enabled' => $guest_vote_enabled,
257
		);
258
259
		// Make all five poll choices empty.
260
		$context['choices'] = array(
261
			array('id' => 0, 'number' => 1, 'label' => '', 'is_last' => false),
262
			array('id' => 1, 'number' => 2, 'label' => '', 'is_last' => false),
263
			array('id' => 2, 'number' => 3, 'label' => '', 'is_last' => false),
264
			array('id' => 3, 'number' => 4, 'label' => '', 'is_last' => false),
265
			array('id' => 4, 'number' => 5, 'label' => '', 'is_last' => true)
266
		);
267
		$context['last_choice_id'] = 4;
268
	}
269
270
	if ($context['make_event'])
271
	{
272
		// They might want to pick a board.
273
		if (!isset($context['current_board']))
274
			$context['current_board'] = 0;
275
276
		// Start loading up the event info.
277
		$context['event'] = array();
278
		$context['event']['title'] = isset($_REQUEST['evtitle']) ? $smcFunc['htmlspecialchars'](stripslashes($_REQUEST['evtitle'])) : '';
279
		$context['event']['location'] = isset($_REQUEST['event_location']) ? $smcFunc['htmlspecialchars'](stripslashes($_REQUEST['event_location'])) : '';
280
281
		$context['event']['id'] = isset($_REQUEST['eventid']) ? (int) $_REQUEST['eventid'] : -1;
282
		$context['event']['new'] = $context['event']['id'] == -1;
283
284
		// Permissions check!
285
		isAllowedTo('calendar_post');
286
287
		require_once($sourcedir . '/Subs-Calendar.php');
288
289
		// We want a fairly compact version of the time, but as close as possible to the user's settings.
290
		$time_string = strtr(get_date_or_time_format('time'), array(
291
			'%I' => '%l',
292
			'%H' => '%k',
293
			'%S' => '',
294
			'%r' => '%l:%M %p',
295
			'%R' => '%k:%M',
296
			'%T' => '%l:%M',
297
		));
298
299
		$time_string = preg_replace('~:(?=\s|$|%[pPzZ])~', '', $time_string);
300
301
		// Editing an event?  (but NOT previewing!?)
302
		if (empty($context['event']['new']) && !isset($_REQUEST['subject']))
303
		{
304
			// If the user doesn't have permission to edit the post in this topic, redirect them.
305
			if ((empty($id_member_poster) || $id_member_poster != $user_info['id'] || !allowedTo('modify_own')) && !allowedTo('modify_any'))
306
			{
307
				require_once($sourcedir . '/Calendar.php');
308
				return CalendarPost();
309
			}
310
311
			// Get the current event information.
312
			$eventProperties = getEventProperties($context['event']['id']);
313
			$context['event'] = array_merge($context['event'], $eventProperties);
314
		}
315
		else
316
		{
317
			// Get the current event information.
318
			$eventProperties = getNewEventDatetimes();
319
			$context['event'] = array_merge($context['event'], $eventProperties);
320
321
			// Make sure the year and month are in the valid range.
322
			if ($context['event']['month'] < 1 || $context['event']['month'] > 12)
323
				fatal_lang_error('invalid_month', false);
324
			if ($context['event']['year'] < $modSettings['cal_minyear'] || $context['event']['year'] > $modSettings['cal_maxyear'])
325
				fatal_lang_error('invalid_year', false);
326
327
			$context['event']['categories'] = $board_list;
328
		}
329
330
		// Find the last day of the month.
331
		$context['event']['last_day'] = (int) smf_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']));
332
333
		// An all day event? Set up some nice defaults in case the user wants to change that
334
		if ($context['event']['allday'] == true)
335
		{
336
			$context['event']['tz'] = getUserTimezone();
337
			$context['event']['start_time'] = timeformat(time(), $time_string);
338
			$context['event']['end_time'] = timeformat(time() + 3600, $time_string);
339
		}
340
		// Otherwise, just adjust these to look nice on the input form
341
		else
342
		{
343
			$context['event']['start_time'] = $context['event']['start_time_orig'];
344
			$context['event']['end_time'] = $context['event']['end_time_orig'];
345
		}
346
347
		// Need this so the user can select a timezone for the event.
348
		$context['all_timezones'] = smf_list_timezones($context['event']['start_date']);
349
350
		// If the event's timezone is not in SMF's standard list of time zones, try to fix it.
351
		if (!isset($context['all_timezones'][$context['event']['tz']]))
352
		{
353
			$later = strtotime('@' . $context['event']['start_timestamp'] . ' + 1 year');
354
			$tzinfo = timezone_transitions_get(timezone_open($context['event']['tz']), $context['event']['start_timestamp'], $later);
355
356
			$found = false;
357
			foreach ($context['all_timezones'] as $possible_tzid => $dummy)
358
			{
359
				$possible_tzinfo = timezone_transitions_get(timezone_open($possible_tzid), $context['event']['start_timestamp'], $later);
360
361
				if ($tzinfo === $possible_tzinfo)
362
				{
363
					$context['event']['tz'] = $possible_tzid;
364
					$found = true;
365
					break;
366
				}
367
			}
368
369
			// Hm. That's weird. Well, just prepend it to the list and let the user deal with it.
370
			if (!$found)
371
			{
372
				$d = date_create($context['event']['start_datetime'] . ' ' . $context['event']['tz']);
373
				$context['all_timezones'] = array($context['event']['tz'] => '[UTC' . date_format($d, 'P') . '] - ' . $context['event']['tz']) + $context['all_timezones'];
374
			}
375
		}
376
377
		loadDatePicker('#event_time_input .date_input');
378
		loadTimePicker('#event_time_input .time_input', $time_string);
379
		loadDatePair('#event_time_input', 'date_input', 'time_input');
380
		addInlineJavaScript('
381
	$("#allday").click(function(){
382
		$("#start_time").attr("disabled", this.checked);
383
		$("#end_time").attr("disabled", this.checked);
384
		$("#tz").attr("disabled", this.checked);
385
	});	', true);
386
387
		$context['event']['board'] = !empty($board) ? $board : $modSettings['cal_defaultboard'];
388
		$context['event']['topic'] = !empty($topic) ? $topic : 0;
389
	}
390
391
	// See if any new replies have come along.
392
	// Huh, $_REQUEST['msg'] is set upon submit, so this doesn't get executed at submit
393
	// only at preview
394
	if (empty($_REQUEST['msg']) && !empty($topic))
395
	{
396
		if (empty($options['no_new_reply_warning']) && isset($_REQUEST['last_msg']) && $context['topic_last_message'] > $_REQUEST['last_msg'])
397
		{
398
			$request = $smcFunc['db_query']('', '
399
				SELECT COUNT(*)
400
				FROM {db_prefix}messages
401
				WHERE id_topic = {int:current_topic}
402
					AND id_msg > {int:last_msg}' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : '
403
					AND approved = {int:approved}') . '
404
				LIMIT 1',
405
				array(
406
					'current_topic' => $topic,
407
					'last_msg' => (int) $_REQUEST['last_msg'],
408
					'approved' => 1,
409
				)
410
			);
411
			list ($context['new_replies']) = $smcFunc['db_fetch_row']($request);
412
			$smcFunc['db_free_result']($request);
413
414
			if (!empty($context['new_replies']))
415
			{
416
				if ($context['new_replies'] == 1)
417
					$txt['error_new_replies'] = isset($_GET['last_msg']) ? $txt['error_new_reply_reading'] : $txt['error_new_reply'];
418
				else
419
					$txt['error_new_replies'] = sprintf(isset($_GET['last_msg']) ? $txt['error_new_replies_reading'] : $txt['error_new_replies'], $context['new_replies']);
420
421
				$post_errors[] = 'new_replies';
422
423
				$modSettings['topicSummaryPosts'] = $context['new_replies'] > $modSettings['topicSummaryPosts'] ? max($modSettings['topicSummaryPosts'], 5) : $modSettings['topicSummaryPosts'];
424
			}
425
		}
426
	}
427
428
	// Get a response prefix (like 'Re:') in the default forum language.
429
	if (!isset($context['response_prefix']) && !($context['response_prefix'] = cache_get_data('response_prefix')))
430
	{
431
		if ($language === $user_info['language'])
432
			$context['response_prefix'] = $txt['response_prefix'];
433
		else
434
		{
435
			loadLanguage('index', $language, false);
436
			$context['response_prefix'] = $txt['response_prefix'];
437
			loadLanguage('index');
438
		}
439
		cache_put_data('response_prefix', $context['response_prefix'], 600);
440
	}
441
442
	// Previewing, modifying, or posting?
443
	// Do we have a body, but an error happened.
444
	if (isset($_REQUEST['message']) || isset($_REQUEST['quickReply']) || !empty($context['post_error']))
445
	{
446
		if (isset($_REQUEST['quickReply']))
447
			$_REQUEST['message'] = $_REQUEST['quickReply'];
448
449
		// Validate inputs.
450
		if (empty($context['post_error']))
451
		{
452
			// This means they didn't click Post and get an error.
453
			$really_previewing = true;
454
		}
455
		else
456
		{
457
			if (!isset($_REQUEST['subject']))
458
				$_REQUEST['subject'] = '';
459
			if (!isset($_REQUEST['message']))
460
				$_REQUEST['message'] = '';
461
			if (!isset($_REQUEST['icon']))
462
				$_REQUEST['icon'] = 'xx';
463
464
			// They are previewing if they asked to preview (i.e. came from quick reply).
465
			$really_previewing = !empty($_POST['preview']);
466
		}
467
468
		// In order to keep the approval status flowing through, we have to pass it through the form...
469
		$context['becomes_approved'] = empty($_REQUEST['not_approved']);
470
		$context['show_approval'] = isset($_REQUEST['approve']) ? ($_REQUEST['approve'] ? 2 : 1) : (allowedTo('approve_posts') ? 2 : 0);
471
		$context['can_announce'] &= $context['becomes_approved'];
472
473
		// Set up the inputs for the form.
474
		$form_subject = strtr($smcFunc['htmlspecialchars']($_REQUEST['subject']), array("\r" => '', "\n" => '', "\t" => ''));
475
		$form_message = $smcFunc['htmlspecialchars']($_REQUEST['message'], ENT_QUOTES);
476
477
		// Make sure the subject isn't too long - taking into account special characters.
478
		if ($smcFunc['strlen']($form_subject) > 100)
479
			$form_subject = $smcFunc['substr']($form_subject, 0, 100);
480
481
		if (isset($_REQUEST['poll']))
482
		{
483
			$context['question'] = isset($_REQUEST['question']) ? $smcFunc['htmlspecialchars'](trim($_REQUEST['question'])) : '';
484
485
			$context['choices'] = array();
486
			$choice_id = 0;
487
488
			$_POST['options'] = empty($_POST['options']) ? array() : htmlspecialchars__recursive($_POST['options']);
489
			foreach ($_POST['options'] as $option)
490
			{
491
				if (trim($option) == '')
492
					continue;
493
494
				$context['choices'][] = array(
495
					'id' => $choice_id++,
496
					'number' => $choice_id,
497
					'label' => $option,
498
					'is_last' => false
499
				);
500
			}
501
502
			// One empty option for those with js disabled...I know are few... :P
503
			$context['choices'][] = array(
504
				'id' => $choice_id++,
505
				'number' => $choice_id,
506
				'label' => '',
507
				'is_last' => false
508
			);
509
510
			if (count($context['choices']) < 2)
511
			{
512
				$context['choices'][] = array(
513
					'id' => $choice_id++,
514
					'number' => $choice_id,
515
					'label' => '',
516
					'is_last' => false
517
				);
518
			}
519
			$context['last_choice_id'] = $choice_id;
520
			$context['choices'][count($context['choices']) - 1]['is_last'] = true;
521
		}
522
523
		// Are you... a guest?
524
		if ($user_info['is_guest'])
525
		{
526
			$_REQUEST['guestname'] = !isset($_REQUEST['guestname']) ? '' : trim($_REQUEST['guestname']);
527
			$_REQUEST['email'] = !isset($_REQUEST['email']) ? '' : trim($_REQUEST['email']);
528
529
			$_REQUEST['guestname'] = $smcFunc['htmlspecialchars']($_REQUEST['guestname']);
530
			$context['name'] = $_REQUEST['guestname'];
531
			$_REQUEST['email'] = $smcFunc['htmlspecialchars']($_REQUEST['email']);
532
			$context['email'] = $_REQUEST['email'];
533
534
			$user_info['name'] = $_REQUEST['guestname'];
535
		}
536
537
		// Only show the preview stuff if they hit Preview.
538
		if (($really_previewing == true || isset($_REQUEST['xml'])) && !isset($_REQUEST['save_draft']))
539
		{
540
			// Set up the preview message and subject and censor them...
541
			$context['preview_message'] = $form_message;
542
			preparsecode($form_message, true);
543
			preparsecode($context['preview_message']);
544
545
			// Do all bulletin board code tags, with or without smileys.
546
			$context['preview_message'] = parse_bbc($context['preview_message'], isset($_REQUEST['ns']) ? 0 : 1);
547
			censorText($context['preview_message']);
548
549
			if ($form_subject != '')
550
			{
551
				$context['preview_subject'] = $form_subject;
552
553
				censorText($context['preview_subject']);
554
			}
555
			else
556
				$context['preview_subject'] = '<em>' . $txt['no_subject'] . '</em>';
557
558
			call_integration_hook('integrate_preview_post', array(&$form_message, &$form_subject));
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, t.id_first_msg
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);
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
			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
			if ($context['can_announce'] && !empty($row['id_action']) && $row['id_first_msg'] == $_REQUEST['msg'])
626
			{
627
				loadLanguage('Errors');
628
				$context['post_error']['already_announced'] = $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
				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, t.id_first_msg
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
		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
		if ($context['can_announce'] && !empty($row['id_action']) && $row['id_first_msg'] == $_REQUEST['msg'])
743
		{
744
			loadLanguage('Errors');
745
			$context['post_error']['already_announced'] = $txt['error_topic_already_announced'];
746
		}
747
748
		// When was it last modified?
749
		if (!empty($row['modified_time']))
750
		{
751
			$modified_reason = $row['modified_reason'];
752
			$context['last_modified'] = timeformat($row['modified_time']);
753
			$context['last_modified_reason'] = censorText($row['modified_reason']);
754
			$context['last_modified_name'] = $row['modified_name'];
755
			$context['last_modified_text'] = sprintf($txt['last_edit_by'], $context['last_modified'], $row['modified_name']) . (empty($row['modified_reason']) ? '' : ' ' . sprintf($txt['last_edit_reason'], $row['modified_reason']));
756
		}
757
758
		// Get the stuff ready for the form.
759
		$form_subject = $row['subject'];
760
		$form_message = un_preparsecode($row['body']);
761
		censorText($form_message);
762
		censorText($form_subject);
763
764
		// Check the boxes that should be checked.
765
		$context['use_smileys'] = !empty($row['smileys_enabled']);
766
		$context['icon'] = $row['icon'];
767
768
		// Leave the approval checkbox unchecked by default for unapproved messages.
769
		if (!$row['approved'] && !empty($context['show_approval']))
770
			$context['show_approval'] = 1;
771
772
		// Sort the attachments so they are in the order saved
773
		$temp = array();
774
		foreach ($attachment_stuff as $attachment)
775
		{
776
			if ($attachment['filesize'] >= 0 && !empty($modSettings['attachmentEnable']))
777
				$temp[$attachment['id_attach']] = $attachment;
778
		}
779
		ksort($temp);
780
781
		// Load up 'em attachments!
782
		foreach ($temp as $attachment)
783
		{
784
			$context['current_attachments'][$attachment['id_attach']] = array(
785
				'name' => $smcFunc['htmlspecialchars']($attachment['filename']),
786
				'size' => $attachment['filesize'],
787
				'attachID' => $attachment['id_attach'],
788
				'href' => $scripturl . '?action=dlattach;attach=' . $attachment['id_attach'],
789
				'approved' => $attachment['attachment_approved'],
790
				'mime_type' => $attachment['mime_type'],
791
				'thumb' => $attachment['id_thumb'],
792
			);
793
		}
794
795
		// Allow moderators to change names....
796
		if (allowedTo('moderate_forum') && empty($row['id_member']))
797
		{
798
			$context['name'] = $smcFunc['htmlspecialchars']($row['poster_name']);
799
			$context['email'] = $smcFunc['htmlspecialchars']($row['poster_email']);
800
		}
801
802
		// Set the destination.
803
		$context['destination'] = 'post2;start=' . $_REQUEST['start'] . ';msg=' . $_REQUEST['msg'] . ';' . $context['session_var'] . '=' . $context['session_id'] . (isset($_REQUEST['poll']) ? ';poll' : '');
804
		$context['submit_label'] = $txt['save'];
805
	}
806
	// Posting...
807
	else
808
	{
809
		// By default....
810
		$context['use_smileys'] = true;
811
		$context['icon'] = 'xx';
812
813
		if ($user_info['is_guest'])
814
		{
815
			$context['name'] = isset($_SESSION['guest_name']) ? $_SESSION['guest_name'] : '';
816
			$context['email'] = isset($_SESSION['guest_email']) ? $_SESSION['guest_email'] : '';
817
		}
818
		$context['destination'] = 'post2;start=' . $_REQUEST['start'] . (isset($_REQUEST['poll']) ? ';poll' : '');
819
820
		$context['submit_label'] = $txt['post'];
821
822
		// Posting a quoted reply?
823
		if (!empty($topic) && !empty($_REQUEST['quote']))
824
		{
825
			// Make sure they _can_ quote this post, and if so get it.
826
			$request = $smcFunc['db_query']('', '
827
				SELECT m.subject, COALESCE(mem.real_name, m.poster_name) AS poster_name, m.poster_time, m.body
828
				FROM {db_prefix}messages AS m
829
					LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : '
830
					INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic)') . '
831
				WHERE {query_see_message_board}
832
					AND m.id_msg = {int:id_msg}' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : '
833
					AND m.approved = {int:is_approved}
834
					AND t.approved = {int:is_approved}') . '
835
				LIMIT 1',
836
				array(
837
					'id_msg' => (int) $_REQUEST['quote'],
838
					'is_approved' => 1,
839
				)
840
			);
841
			if ($smcFunc['db_num_rows']($request) == 0)
842
				fatal_lang_error('quoted_post_deleted', false);
843
			list ($form_subject, $mname, $mdate, $form_message) = $smcFunc['db_fetch_row']($request);
844
			$smcFunc['db_free_result']($request);
845
846
			// Add 'Re: ' to the front of the quoted subject.
847
			if (trim($context['response_prefix']) != '' && $smcFunc['strpos']($form_subject, trim($context['response_prefix'])) !== 0)
848
				$form_subject = $context['response_prefix'] . $form_subject;
849
850
			// Censor the message and subject.
851
			censorText($form_message);
852
			censorText($form_subject);
853
854
			// But if it's in HTML world, turn them into htmlspecialchar's so they can be edited!
855
			if (strpos($form_message, '[html]') !== false)
856
			{
857
				$parts = preg_split('~(\[/code\]|\[code(?:=[^\]]+)?\])~i', $form_message, -1, PREG_SPLIT_DELIM_CAPTURE);
858
				for ($i = 0, $n = count($parts); $i < $n; $i++)
859
				{
860
					// It goes 0 = outside, 1 = begin tag, 2 = inside, 3 = close tag, repeat.
861
					if ($i % 4 == 0)
862
						$parts[$i] = preg_replace_callback(
863
							'~\[html\](.+?)\[/html\]~is',
864
							function($m)
865
							{
866
								return '[html]' . preg_replace('~<br\s?/?' . '>~i', '&lt;br /&gt;<br>', "$m[1]") . '[/html]';
867
							},
868
							$parts[$i]
869
						);
870
				}
871
				$form_message = implode('', $parts);
872
			}
873
874
			$form_message = preg_replace('~<br ?/?' . '>~i', "\n", $form_message);
875
876
			// Remove any nested quotes, if necessary.
877
			if (!empty($modSettings['removeNestedQuotes']))
878
				$form_message = preg_replace(array('~\n?\[quote.*?\].+?\[/quote\]\n?~is', '~^\n~', '~\[/quote\]~'), '', $form_message);
879
880
			// Add a quote string on the front and end.
881
			$form_message = '[quote author=' . $mname . ' link=msg=' . (int) $_REQUEST['quote'] . ' date=' . $mdate . ']' . "\n" . rtrim($form_message) . "\n" . '[/quote]';
882
		}
883
		// Posting a reply without a quote?
884
		elseif (!empty($topic) && empty($_REQUEST['quote']))
885
		{
886
			// Get the first message's subject.
887
			$form_subject = $first_subject;
888
889
			// Add 'Re: ' to the front of the subject.
890
			if (trim($context['response_prefix']) != '' && $form_subject != '' && $smcFunc['strpos']($form_subject, trim($context['response_prefix'])) !== 0)
891
				$form_subject = $context['response_prefix'] . $form_subject;
892
893
			// Censor the subject.
894
			censorText($form_subject);
895
896
			$form_message = '';
897
		}
898
		else
899
		{
900
			$form_subject = isset($_GET['subject']) ? $_GET['subject'] : '';
901
			$form_message = '';
902
		}
903
	}
904
905
	$context['can_post_attachment'] = !empty($modSettings['attachmentEnable']) && $modSettings['attachmentEnable'] == 1 && (allowedTo('post_attachment', $boards, true) || ($modSettings['postmod_active'] && allowedTo('post_unapproved_attachments', $boards, true)));
906
907
	if ($context['can_post_attachment'])
908
	{
909
		// If there are attachments, calculate the total size and how many.
910
		$context['attachments']['total_size'] = 0;
911
		$context['attachments']['quantity'] = 0;
912
913
		// If this isn't a new post, check the current attachments.
914
		if (isset($_REQUEST['msg']))
915
		{
916
			$context['attachments']['quantity'] = count($context['current_attachments']);
917
			foreach ($context['current_attachments'] as $attachment)
918
				$context['attachments']['total_size'] += $attachment['size'];
919
		}
920
921
		// A bit of house keeping first.
922
		if (!empty($_SESSION['temp_attachments']) && count($_SESSION['temp_attachments']) == 1)
923
			unset($_SESSION['temp_attachments']);
924
925
		if (!empty($_SESSION['temp_attachments']))
926
		{
927
			// Is this a request to delete them?
928
			if (isset($_GET['delete_temp']))
929
			{
930
				foreach ($_SESSION['temp_attachments'] as $attachID => $attachment)
931
				{
932
					if (strpos($attachID, 'post_tmp_' . $user_info['id']) !== false)
933
						if (file_exists($attachment['tmp_name']))
934
							unlink($attachment['tmp_name']);
935
				}
936
				$post_errors[] = 'temp_attachments_gone';
937
				$_SESSION['temp_attachments'] = array();
938
			}
939
			// Hmm, coming in fresh and there are files in session.
940
			elseif ($context['current_action'] != 'post2' || !empty($_POST['from_qr']))
941
			{
942
				// Let's be nice and see if they belong here first.
943
				if ((empty($_REQUEST['msg']) && empty($_SESSION['temp_attachments']['post']['msg']) && $_SESSION['temp_attachments']['post']['board'] == (!empty($board) ? $board : 0)) || (!empty($_REQUEST['msg']) && $_SESSION['temp_attachments']['post']['msg'] == $_REQUEST['msg']))
944
				{
945
					// See if any files still exist before showing the warning message and the files attached.
946
					foreach ($_SESSION['temp_attachments'] as $attachID => $attachment)
947
					{
948
						if (strpos($attachID, 'post_tmp_' . $user_info['id']) === false)
949
							continue;
950
951
						if (file_exists($attachment['tmp_name']))
952
						{
953
							$post_errors[] = 'temp_attachments_new';
954
							$context['files_in_session_warning'] = $txt['attached_files_in_session'];
955
							unset($_SESSION['temp_attachments']['post']['files']);
956
							break;
957
						}
958
					}
959
				}
960
				else
961
				{
962
					// Since, they don't belong here. Let's inform the user that they exist..
963
					if (!empty($topic))
964
						$delete_url = $scripturl . '?action=post' . (!empty($_REQUEST['msg']) ? (';msg=' . $_REQUEST['msg']) : '') . (!empty($_REQUEST['last_msg']) ? (';last_msg=' . $_REQUEST['last_msg']) : '') . ';topic=' . $topic . ';delete_temp';
965
					else
966
						$delete_url = $scripturl . '?action=post' . (!empty($board) ? ';board=' . $board : '') . ';delete_temp';
967
968
					// Compile a list of the files to show the user.
969
					$file_list = array();
970
					foreach ($_SESSION['temp_attachments'] as $attachID => $attachment)
971
						if (strpos($attachID, 'post_tmp_' . $user_info['id']) !== false)
972
							$file_list[] = $attachment['name'];
973
974
					$_SESSION['temp_attachments']['post']['files'] = $file_list;
975
					$file_list = '<div class="attachments">' . implode('<br>', $file_list) . '</div>';
976
977
					if (!empty($_SESSION['temp_attachments']['post']['msg']))
978
					{
979
						// We have a message id, so we can link back to the old topic they were trying to edit..
980
						$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';
981
982
						$post_errors[] = array('temp_attachments_found', array($delete_url, $goback_url, $file_list));
983
						$context['ignore_temp_attachments'] = true;
984
					}
985
					else
986
					{
987
						$post_errors[] = array('temp_attachments_lost', array($delete_url, $file_list));
988
						$context['ignore_temp_attachments'] = true;
989
					}
990
				}
991
			}
992
993
			if (!empty($context['we_are_history']))
994
				$post_errors[] = $context['we_are_history'];
995
996
			foreach ($_SESSION['temp_attachments'] as $attachID => $attachment)
997
			{
998
				if (isset($context['ignore_temp_attachments']) || isset($_SESSION['temp_attachments']['post']['files']))
999
					break;
1000
1001
				if ($attachID != 'initial_error' && strpos($attachID, 'post_tmp_' . $user_info['id']) === false)
1002
					continue;
1003
1004
				if ($attachID == 'initial_error')
1005
				{
1006
					$txt['error_attach_initial_error'] = $txt['attach_no_upload'] . '<div style="padding: 0 1em;">' . (is_array($attachment) ? vsprintf($txt[$attachment[0]], (array) $attachment[1]) : $txt[$attachment]) . '</div>';
1007
					$post_errors[] = 'attach_initial_error';
1008
					unset($_SESSION['temp_attachments']);
1009
					break;
1010
				}
1011
1012
				// Show any errors which might have occurred.
1013
				if (!empty($attachment['errors']))
1014
				{
1015
					$txt['error_attach_errors'] = empty($txt['error_attach_errors']) ? '<br>' : '';
1016
					$txt['error_attach_errors'] .= sprintf($txt['attach_warning'], $attachment['name']) . '<div style="padding: 0 1em;">';
1017
					foreach ($attachment['errors'] as $error)
1018
						$txt['error_attach_errors'] .= (is_array($error) ? vsprintf($txt[$error[0]], (array) $error[1]) : $txt[$error]) . '<br >';
1019
					$txt['error_attach_errors'] .= '</div>';
1020
					$post_errors[] = 'attach_errors';
1021
1022
					// Take out the trash.
1023
					unset($_SESSION['temp_attachments'][$attachID]);
1024
					if (file_exists($attachment['tmp_name']))
1025
						unlink($attachment['tmp_name']);
1026
					continue;
1027
				}
1028
1029
				// More house keeping.
1030
				if (!file_exists($attachment['tmp_name']))
1031
				{
1032
					unset($_SESSION['temp_attachments'][$attachID]);
1033
					continue;
1034
				}
1035
1036
				$context['attachments']['quantity']++;
1037
				$context['attachments']['total_size'] += $attachment['size'];
1038
				if (!isset($context['files_in_session_warning']))
1039
					$context['files_in_session_warning'] = $txt['attached_files_in_session'];
1040
1041
				$context['current_attachments'][$attachID] = array(
1042
					'name' => $smcFunc['htmlspecialchars']($attachment['name']),
1043
					'size' => $attachment['size'],
1044
					'attachID' => $attachID,
1045
					'unchecked' => false,
1046
					'approved' => 1,
1047
					'mime_type' => '',
1048
					'thumb' => 0,
1049
				);
1050
			}
1051
		}
1052
	}
1053
1054
	// Allow user to see previews for all of this post's attachments, even if the post hasn't been submitted yet.
1055
	if (!isset($_SESSION['attachments_can_preview']))
1056
		$_SESSION['attachments_can_preview'] = array();
1057
1058
	if (!empty($_SESSION['already_attached']))
1059
		$_SESSION['attachments_can_preview'] += array_fill_keys(array_keys($_SESSION['already_attached']), true);
1060
1061
	foreach ($context['current_attachments'] as $attachID => $attachment)
1062
	{
1063
		$_SESSION['attachments_can_preview'][$attachID] = true;
1064
1065
		if (!empty($attachment['thumb']))
1066
			$_SESSION['attachments_can_preview'][$attachment['thumb']] = true;
1067
	}
1068
1069
	// Previously uploaded attachments have 2 flavors:
1070
	// - Existing post - at this point, now in $context['current_attachments']
1071
	// - Just added, current session only - at this point, now in $_SESSION['already_attached']
1072
	// We need to make sure *all* of these are in $context['current_attachments'], otherwise they won't show in dropzone during edits.
1073
	if (!empty($_SESSION['already_attached']))
1074
	{
1075
		$request = $smcFunc['db_query']('', '
1076
			SELECT
1077
				a.id_attach, a.filename, COALESCE(a.size, 0) AS filesize, a.approved, a.mime_type, a.id_thumb
1078
			FROM {db_prefix}attachments AS a
1079
			WHERE a.attachment_type = {int:attachment_type}
1080
				AND a.id_attach IN ({array_int:just_uploaded})',
1081
			array(
1082
				'attachment_type' => 0,
1083
				'just_uploaded' => $_SESSION['already_attached']
1084
			)
1085
		);
1086
1087
		while ($row = $smcFunc['db_fetch_assoc']($request))
1088
		{
1089
			$context['current_attachments'][$row['id_attach']] = array(
1090
				'name' => $smcFunc['htmlspecialchars']($row['filename']),
1091
				'size' => $row['filesize'],
1092
				'attachID' => $row['id_attach'],
1093
				'approved' => $row['approved'],
1094
				'mime_type' => $row['mime_type'],
1095
				'thumb' => $row['id_thumb'],
1096
			);
1097
		}
1098
		$smcFunc['db_free_result']($request);
1099
	}
1100
1101
	// Do we need to show the visual verification image?
1102
	$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));
1103
	if ($context['require_verification'])
1104
	{
1105
		require_once($sourcedir . '/Subs-Editor.php');
1106
		$verificationOptions = array(
1107
			'id' => 'post',
1108
		);
1109
		$context['require_verification'] = create_control_verification($verificationOptions);
1110
		$context['visual_verification_id'] = $verificationOptions['id'];
1111
	}
1112
1113
	// If they came from quick reply, and have to enter verification details, give them some notice.
1114
	if (!empty($_REQUEST['from_qr']) && !empty($context['require_verification']))
1115
		$post_errors[] = 'need_qr_verification';
1116
1117
	/*
1118
	 * There are two error types: serious and minor. Serious errors
1119
	 * actually tell the user that a real error has occurred, while minor
1120
	 * errors are like warnings that let them know that something with
1121
	 * their post isn't right.
1122
	 */
1123
	$minor_errors = array('not_approved', 'new_replies', 'old_topic', 'need_qr_verification', 'no_subject', 'topic_locked', 'topic_unlocked', 'topic_stickied', 'topic_unstickied', 'cannot_post_attachment');
1124
1125
	call_integration_hook('integrate_post_errors', array(&$post_errors, &$minor_errors, $form_message, $form_subject));
1126
1127
	// Any errors occurred?
1128
	if (!empty($post_errors))
1129
	{
1130
		loadLanguage('Errors');
1131
		$context['error_type'] = 'minor';
1132
		foreach ($post_errors as $post_error)
1133
			if (is_array($post_error))
1134
			{
1135
				$post_error_id = $post_error[0];
1136
				$context['post_error'][$post_error_id] = vsprintf($txt['error_' . $post_error_id], (array) $post_error[1]);
1137
1138
				// If it's not a minor error flag it as such.
1139
				if (!in_array($post_error_id, $minor_errors))
1140
					$context['error_type'] = 'serious';
1141
			}
1142
			else
1143
			{
1144
				$context['post_error'][$post_error] = $txt['error_' . $post_error];
1145
1146
				// If it's not a minor error flag it as such.
1147
				if (!in_array($post_error, $minor_errors))
1148
					$context['error_type'] = 'serious';
1149
			}
1150
	}
1151
1152
	// What are you doing? Posting a poll, modifying, previewing, new post, or reply...
1153
	if (isset($_REQUEST['poll']))
1154
		$context['page_title'] = $txt['new_poll'];
1155
	elseif ($context['make_event'])
1156
		$context['page_title'] = $context['event']['id'] == -1 ? $txt['calendar_post_event'] : $txt['calendar_edit'];
1157
	elseif (isset($_REQUEST['msg']))
1158
		$context['page_title'] = $txt['modify_msg'];
1159
	elseif (isset($_REQUEST['subject'], $context['preview_subject']))
1160
		$context['page_title'] = $txt['preview'] . ' - ' . strip_tags($context['preview_subject']);
1161
	elseif (empty($topic))
1162
		$context['page_title'] = $txt['start_new_topic'];
1163
	else
1164
		$context['page_title'] = $txt['post_reply'];
1165
1166
	// Build the link tree.
1167
	if (empty($topic))
1168
		$context['linktree'][] = array(
1169
			'name' => '<em>' . $txt['start_new_topic'] . '</em>'
1170
		);
1171
	else
1172
		$context['linktree'][] = array(
1173
			'url' => $scripturl . '?topic=' . $topic . '.' . $_REQUEST['start'],
1174
			'name' => $form_subject,
1175
			'extra_before' => '<span><strong class="nav">' . $context['page_title'] . ' (</strong></span>',
1176
			'extra_after' => '<span><strong class="nav">)</strong></span>'
1177
		);
1178
1179
	$context['subject'] = addcslashes($form_subject, '"');
1180
	$context['message'] = str_replace(array('"', '<', '>', '&nbsp;'), array('&quot;', '&lt;', '&gt;', ' '), $form_message);
1181
1182
	// Are post drafts enabled?
1183
	$context['drafts_save'] = !empty($modSettings['drafts_post_enabled']) && allowedTo('post_draft');
1184
	$context['drafts_autosave'] = !empty($context['drafts_save']) && !empty($modSettings['drafts_autosave_enabled']) && allowedTo('post_autosave_draft') && !empty($options['drafts_autosave_enabled']);
1185
1186
	// Build a list of drafts that they can load in to the editor
1187
	if (!empty($context['drafts_save']))
1188
	{
1189
		require_once($sourcedir . '/Drafts.php');
1190
		ShowDrafts($user_info['id'], $topic);
1191
	}
1192
1193
	// Needed for the editor and message icons.
1194
	require_once($sourcedir . '/Subs-Editor.php');
1195
1196
	// Now create the editor.
1197
	$editorOptions = array(
1198
		'id' => 'message',
1199
		'value' => $context['message'],
1200
		'labels' => array(
1201
			'post_button' => $context['submit_label'],
1202
		),
1203
		// add height and width for the editor
1204
		'height' => '175px',
1205
		'width' => '100%',
1206
		// We do XML preview here.
1207
		'preview_type' => 2,
1208
		'required' => true,
1209
	);
1210
	create_control_richedit($editorOptions);
1211
1212
	// Store the ID.
1213
	$context['post_box_name'] = $editorOptions['id'];
1214
1215
	$context['attached'] = '';
1216
	$context['make_poll'] = isset($_REQUEST['poll']);
1217
1218
	// Message icons - customized icons are off?
1219
	$context['icons'] = getMessageIcons(!empty($board) ? $board : 0);
1220
1221
	if (!empty($context['icons']))
1222
		$context['icons'][count($context['icons']) - 1]['is_last'] = true;
1223
1224
	// Are we starting a poll? if set the poll icon as selected if its available
1225
	if (isset($_REQUEST['poll']))
1226
	{
1227
		foreach ($context['icons'] as $icons)
1228
		{
1229
			if (isset($icons['value']) && $icons['value'] == 'poll')
1230
			{
1231
				// if found we are done
1232
				$context['icon'] = 'poll';
1233
				break;
1234
			}
1235
		}
1236
	}
1237
1238
	$context['icon_url'] = '';
1239
	for ($i = 0, $n = count($context['icons']); $i < $n; $i++)
1240
	{
1241
		$context['icons'][$i]['selected'] = $context['icon'] == $context['icons'][$i]['value'];
1242
		if ($context['icons'][$i]['selected'])
1243
			$context['icon_url'] = $context['icons'][$i]['url'];
1244
	}
1245
	if (empty($context['icon_url']))
1246
	{
1247
		$context['icon_url'] = $settings[file_exists($settings['theme_dir'] . '/images/post/' . $context['icon'] . '.png') ? 'images_url' : 'default_images_url'] . '/post/' . $context['icon'] . '.png';
1248
		array_unshift($context['icons'], array(
1249
			'value' => $context['icon'],
1250
			'name' => $txt['current_icon'],
1251
			'url' => $context['icon_url'],
1252
			'is_last' => empty($context['icons']),
1253
			'selected' => true,
1254
		));
1255
	}
1256
1257
	if (!empty($topic) && !empty($modSettings['topicSummaryPosts']))
1258
		getTopic();
1259
1260
	// If the user can post attachments prepare the warning labels.
1261
	if ($context['can_post_attachment'])
1262
	{
1263
		// 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.
1264
		$context['num_allowed_attachments'] = empty($modSettings['attachmentNumPerPostLimit']) ? PHP_INT_MAX : $modSettings['attachmentNumPerPostLimit'];
1265
		$context['can_post_attachment_unapproved'] = allowedTo('post_attachment');
1266
		$context['attachment_restrictions'] = array();
1267
		$context['allowed_extensions'] = !empty($modSettings['attachmentCheckExtensions']) ? (strtr(strtolower($modSettings['attachmentExtensions']), array(',' => ', '))) : '';
1268
		$attachmentRestrictionTypes = array('attachmentNumPerPostLimit', 'attachmentPostLimit', 'attachmentSizeLimit');
1269
		foreach ($attachmentRestrictionTypes as $type)
1270
			if (!empty($modSettings[$type]))
1271
			{
1272
				$context['attachment_restrictions'][$type] = sprintf($txt['attach_restrict_' . $type . ($modSettings[$type] >= 1024 ? '_MB' : '')], comma_format($modSettings[$type] >= 1024 ? $modSettings[$type] / 1024 : $modSettings[$type], 2));
1273
1274
				// Show the max number of attachments if not 0.
1275
				if ($type == 'attachmentNumPerPostLimit')
1276
				{
1277
					$context['attachment_restrictions'][$type] .= ' (' . sprintf($txt['attach_remaining'], max($modSettings['attachmentNumPerPostLimit'] - $context['attachments']['quantity'], 0)) . ')';
1278
				}
1279
				elseif ($type == 'attachmentPostLimit' && $context['attachments']['total_size'] > 0)
1280
				{
1281
 					$context['attachment_restrictions'][$type] .= '<span class="attach_available"> (' . sprintf($txt['attach_available'], round(max($modSettings['attachmentPostLimit'] - ($context['attachments']['total_size'] / 1024), 0), 2)) . ')</span>';
1282
				}
1283
1284
			}
1285
	}
1286
1287
	$context['back_to_topic'] = isset($_REQUEST['goback']) || (isset($_REQUEST['msg']) && !isset($_REQUEST['subject']));
1288
	$context['show_additional_options'] = !empty($_POST['additional_options']) || isset($_SESSION['temp_attachments']['post']) || isset($_GET['additionalOptions']);
1289
1290
	$context['is_new_topic'] = empty($topic);
1291
	$context['is_new_post'] = !isset($_REQUEST['msg']);
1292
	$context['is_first_post'] = $context['is_new_topic'] || (isset($_REQUEST['msg']) && $_REQUEST['msg'] == $id_first_msg);
1293
1294
	// Register this form in the session variables.
1295
	checkSubmitOnce('register');
1296
1297
	// Mentions
1298
	if (!empty($modSettings['enable_mentions']) && allowedTo('mention'))
1299
	{
1300
		loadJavaScriptFile('jquery.caret.min.js', array('defer' => true), 'smf_caret');
1301
		loadJavaScriptFile('jquery.atwho.min.js', array('defer' => true), 'smf_atwho');
1302
		loadJavaScriptFile('mentions.js', array('defer' => true, 'minimize' => true), 'smf_mentions');
1303
	}
1304
1305
	// Load the drafts js file
1306
	if ($context['drafts_autosave'])
1307
		loadJavaScriptFile('drafts.js', array('defer' => false, 'minimize' => true), 'smf_drafts');
1308
1309
	// quotedText.js
1310
	loadJavaScriptFile('quotedText.js', array('defer' => true, 'minimize' => true), 'smf_quotedText');
1311
1312
	addInlineJavaScript('
1313
	var current_attachments = [];');
1314
1315
	if (!empty($context['current_attachments']))
1316
	{
1317
		// Mock files to show already attached files.
1318
		foreach ($context['current_attachments'] as $key => $mock)
1319
			addInlineJavaScript('
1320
	current_attachments.push({
1321
		name: ' . JavaScriptEscape($mock['name']) . ',
1322
		size: ' . $mock['size'] . ',
1323
		attachID: ' . $mock['attachID'] . ',
1324
		approved: ' . $mock['approved'] . ',
1325
		type: ' . JavaScriptEscape(!empty($mock['mime_type']) ? $mock['mime_type'] : '') . ',
1326
		thumbID: ' . (!empty($mock['thumb']) ? $mock['thumb'] : 0) . '
1327
	});');
1328
	}
1329
1330
	// File Upload.
1331
	if ($context['can_post_attachment'])
1332
	{
1333
		$acceptedFiles = empty($context['allowed_extensions']) ? '' : implode(',', array_map(
1334
			function ($val) use ($smcFunc)
1335
			{
1336
				return !empty($val) ? ('.' . $smcFunc['htmltrim']($val)) : '';
1337
			},
1338
			explode(',', $context['allowed_extensions'])
1339
		));
1340
1341
		loadJavaScriptFile('dropzone.min.js', array('defer' => true), 'smf_dropzone');
1342
		loadJavaScriptFile('smf_fileUpload.js', array('defer' => true, 'minimize' => true), 'smf_fileUpload');
1343
		addInlineJavaScript('
1344
	$(function() {
1345
		smf_fileUpload({
1346
			dictDefaultMessage : ' . JavaScriptEscape($txt['attach_drop_zone']) . ',
1347
			dictFallbackMessage : ' . JavaScriptEscape($txt['attach_drop_zone_no']) . ',
1348
			dictCancelUpload : ' . JavaScriptEscape($txt['modify_cancel']) . ',
1349
			genericError: ' . JavaScriptEscape($txt['attach_php_error']) . ',
1350
			text_attachDropzoneLabel: ' . JavaScriptEscape($txt['attach_drop_zone']) . ',
1351
			text_attachLimitNag: ' . JavaScriptEscape($txt['attach_limit_nag']) . ',
1352
			text_attachLeft: ' . JavaScriptEscape($txt['attachments_left']) . ',
1353
			text_deleteAttach: ' . JavaScriptEscape($txt['attached_file_delete']) . ',
1354
			text_attachDeleted: ' . JavaScriptEscape($txt['attached_file_deleted']) . ',
1355
			text_insertBBC: ' . JavaScriptEscape($txt['attached_insert_bbc']) . ',
1356
			text_attachUploaded: ' . JavaScriptEscape($txt['attached_file_uploaded']) . ',
1357
			text_attach_unlimited: ' . JavaScriptEscape($txt['attach_drop_unlimited']) . ',
1358
			text_totalMaxSize: ' . JavaScriptEscape($txt['attach_max_total_file_size_current']) . ',
1359
			text_max_size_progress: ' . JavaScriptEscape('{currentRemain} ' . ($modSettings['attachmentPostLimit'] >= 1024 ? $txt['megabyte'] : $txt['kilobyte']) . ' / {currentTotal} ' . ($modSettings['attachmentPostLimit'] >= 1024 ? $txt['megabyte'] : $txt['kilobyte'])) . ',
1360
			dictMaxFilesExceeded: ' . JavaScriptEscape($txt['more_attachments_error']) . ',
1361
			dictInvalidFileType: ' . JavaScriptEscape(sprintf($txt['cant_upload_type'], $context['allowed_extensions'])) . ',
1362
			dictFileTooBig: ' . JavaScriptEscape(sprintf($txt['file_too_big'], comma_format($modSettings['attachmentSizeLimit'], 0))) . ',
1363
			acceptedFiles: ' . JavaScriptEscape($acceptedFiles) . ',
1364
			thumbnailWidth: ' . (!empty($modSettings['attachmentThumbWidth']) ? $modSettings['attachmentThumbWidth'] : 'null') . ',
1365
			thumbnailHeight: ' . (!empty($modSettings['attachmentThumbHeight']) ? $modSettings['attachmentThumbHeight'] : 'null') . ',
1366
			limitMultiFileUploadSize:' . round(max($modSettings['attachmentPostLimit'] - ($context['attachments']['total_size'] / 1024), 0)) * 1024 . ',
1367
			maxFileAmount: ' . (!empty($context['num_allowed_attachments']) ? $context['num_allowed_attachments'] : 'null') . ',
1368
			maxTotalSize: ' . (!empty($modSettings['attachmentPostLimit']) ? $modSettings['attachmentPostLimit'] : '0') . ',
1369
			maxFilesize: ' . (!empty($modSettings['attachmentSizeLimit']) ? $modSettings['attachmentSizeLimit'] : '0') . ',
1370
		});
1371
	});', true);
1372
	}
1373
1374
	// Knowing the current board ID might be handy.
1375
	addInlineJavaScript('
1376
	var current_board = ' . (empty($context['current_board']) ? 'null' : $context['current_board']) . ';', false);
1377
1378
	/* Now let's set up the fields for the posting form header...
1379
1380
		Each item in $context['posting_fields'] is an array similar to one of
1381
		the following:
1382
1383
		$context['posting_fields']['foo'] = array(
1384
			'label' => array(
1385
				'text' => $txt['foo'], // required
1386
				'class' => 'foo', // optional
1387
			),
1388
			'input' => array(
1389
				'type' => 'text', // required
1390
				'attributes' => array(
1391
					'name' => 'foo', // optional, defaults to posting field's key
1392
					'value' => $foo,
1393
					'size' => 80,
1394
				),
1395
			),
1396
		);
1397
1398
		$context['posting_fields']['bar'] = array(
1399
			'label' => array(
1400
				'text' => $txt['bar'], // required
1401
				'class' => 'bar', // optional
1402
			),
1403
			'input' => array(
1404
				'type' => 'select', // required
1405
				'attributes' => array(
1406
					'name' => 'bar', // optional, defaults to posting field's key
1407
				),
1408
				'options' => array(
1409
					'option_1' => array(
1410
						'label' => $txt['option_1'],
1411
						'value' => '1',
1412
						'selected' => true,
1413
					),
1414
					'option_2' => array(
1415
						'label' => $txt['option_2'],
1416
						'value' => '2',
1417
						'selected' => false,
1418
					),
1419
					'opt_group_1' => array(
1420
						'label' => $txt['opt_group_1'],
1421
						'options' => array(
1422
							'option_3' => array(
1423
								'label' => $txt['option_3'],
1424
								'value' => '3',
1425
								'selected' => false,
1426
							),
1427
							'option_4' => array(
1428
								'label' => $txt['option_4'],
1429
								'value' => '4',
1430
								'selected' => false,
1431
							),
1432
						),
1433
					),
1434
				),
1435
			),
1436
		);
1437
1438
		$context['posting_fields']['baz'] = array(
1439
			'label' => array(
1440
				'text' => $txt['baz'], // required
1441
				'class' => 'baz', // optional
1442
			),
1443
			'input' => array(
1444
				'type' => 'radio_select', // required
1445
				'attributes' => array(
1446
					'name' => 'baz', // optional, defaults to posting field's key
1447
				),
1448
				'options' => array(
1449
					'option_1' => array(
1450
						'label' => $txt['option_1'],
1451
						'value' => '1',
1452
						'selected' => true,
1453
					),
1454
					'option_2' => array(
1455
						'label' => $txt['option_2'],
1456
						'value' => '2',
1457
						'selected' => false,
1458
					),
1459
				),
1460
			),
1461
		);
1462
1463
		The label and input elements are required. The label text and input
1464
		type are also required. Other elements may be required or optional
1465
		depending on the situation.
1466
1467
		The input type can be one of the following:
1468
1469
		- text, password, color, date, datetime-local, email, month, number,
1470
		  range, tel, time, url, or week
1471
		- textarea
1472
		- checkbox
1473
		- select
1474
		- radio_select
1475
1476
		When the input type is text (etc.), textarea, or checkbox, the
1477
		'attributes' element is used to specify the initial value and any
1478
		other HTML attributes that might be necessary for the input field.
1479
1480
		When the input type is select or radio_select, the options element
1481
		is required in order to list the options that the user can select.
1482
		For the select type, these will be used to generate a typical select
1483
		menu. For the radio_select type, they will be used to make a div with
1484
		some radio buttons in it.
1485
1486
		Each option in the options array is itself an array of attributes. If
1487
		an option contains a sub-array of more options, then it will be
1488
		turned into an optgroup in the generated select menu. Note that the
1489
		radio_select type only supports simple options, not grouped ones.
1490
1491
		Both the label and the input can have a 'before' and/or 'after'
1492
		element. If used, these define literal HTML strings to be inserted
1493
		before or after the rest of the content of the label or input.
1494
1495
		Finally, it is possible to define an 'html' element for the label
1496
		and/or the input. If used, this will override the HTML that would
1497
		normally be generated in the template file using the other
1498
		information in the array. This should be avoided if at all possible.
1499
	*/
1500
	$context['posting_fields'] = array();
1501
1502
	// Guests must supply their name and email.
1503
	if (isset($context['name']) && isset($context['email']))
1504
	{
1505
		$context['posting_fields']['guestname'] = array(
1506
			'label' => array(
1507
				'text' => $txt['name'],
1508
				'class' => isset($context['post_error']['long_name']) || isset($context['post_error']['no_name']) || isset($context['post_error']['bad_name']) ? 'error' : '',
1509
			),
1510
			'input' => array(
1511
				'type' => 'text',
1512
				'attributes' => array(
1513
					'size' => 25,
1514
					'maxlength' => 25,
1515
					'value' => $context['name'],
1516
					'required' => true,
1517
				),
1518
			),
1519
		);
1520
1521
		if (empty($modSettings['guest_post_no_email']))
1522
		{
1523
			$context['posting_fields']['email'] = array(
1524
				'label' => array(
1525
					'text' => $txt['email'],
1526
					'class' => isset($context['post_error']['no_email']) || isset($context['post_error']['bad_email']) ? 'error' : '',
1527
				),
1528
				'input' => array(
1529
					'type' => 'email',
1530
					'attributes' => array(
1531
						'size' => 25,
1532
						'value' => $context['email'],
1533
						'required' => true,
1534
					),
1535
				),
1536
			);
1537
		}
1538
	}
1539
1540
	// Gotta post it somewhere.
1541
	if (empty($board))
1542
	{
1543
		$context['posting_fields']['board'] = array(
1544
			'label' => array(
1545
				'text' => $txt['calendar_post_in'],
1546
			),
1547
			'input' => array(
1548
				'type' => 'select',
1549
				'options' => array(),
1550
			),
1551
		);
1552
		foreach ($board_list as $category)
1553
		{
1554
			$context['posting_fields']['board']['input']['options'][$category['name']] = array('options' => array());
1555
1556
			foreach ($category['boards'] as $brd)
1557
				$context['posting_fields']['board']['input']['options'][$category['name']]['options'][$brd['name']] = array(
1558
					'value' => $brd['id'],
1559
					'selected' => (bool) $brd['selected'],
1560
					'label' => ($brd['child_level'] > 0 ? str_repeat('==', $brd['child_level'] - 1) . '=&gt;' : '') . ' ' . $brd['name'],
1561
				);
1562
		}
1563
	}
1564
1565
	// Gotta have a subject.
1566
	$context['posting_fields']['subject'] = array(
1567
		'label' => array(
1568
			'text' => $txt['subject'],
1569
			'class' => isset($context['post_error']['no_subject']) ? 'error' : '',
1570
		),
1571
		'input' => array(
1572
			'type' => 'text',
1573
			'attributes' => array(
1574
				'size' => 80,
1575
				'maxlength' => 80 + (!empty($topic) ? $smcFunc['strlen']($context['response_prefix']) : 0),
1576
				'value' => $context['subject'],
1577
				'required' => true,
1578
			),
1579
		),
1580
	);
1581
1582
	// Icons are fun.
1583
	$context['posting_fields']['icon'] = array(
1584
		'label' => array(
1585
			'text' => $txt['message_icon'],
1586
		),
1587
		'input' => array(
1588
			'type' => 'select',
1589
			'attributes' => array(
1590
				'id' => 'icon',
1591
				'onchange' => 'showimage();',
1592
			),
1593
			'options' => array(),
1594
			'after' => ' <img id="icons" src="' . $context['icon_url'] . '">',
1595
		),
1596
	);
1597
	foreach ($context['icons'] as $icon)
1598
	{
1599
		$context['posting_fields']['icon']['input']['options'][$icon['name']] = array(
1600
			'value' => $icon['value'],
1601
			'selected' => $icon['value'] == $context['icon'],
1602
		);
1603
	}
1604
1605
	// If we're editing and displaying edit details, show a box where they can say why.
1606
	if (isset($context['editing']) && $modSettings['show_modify'])
1607
	{
1608
		$context['posting_fields']['modify_reason'] = array(
1609
			'label' => array(
1610
				'text' => $txt['reason_for_edit'],
1611
			),
1612
			'input' => array(
1613
				'type' => 'text',
1614
				'attributes' => array(
1615
					'size' => 80,
1616
					'maxlength' => 80,
1617
					// If same user is editing again, keep the previous edit reason by default.
1618
					'value' => isset($modified_reason) && isset($context['last_modified_name']) && $context['last_modified_name'] === $user_info['name'] ? $modified_reason : '',
1619
				),
1620
				// If message has been edited before, show info about that.
1621
				'after' => empty($context['last_modified_text']) ? '' : '<div class="smalltext em">' . $context['last_modified_text'] . '</div>',
1622
			),
1623
		);
1624
1625
		// Prior to 2.1.4, the edit reason was not handled as a posting field,
1626
		// but instead using a hardcoded input in the template file. We've fixed
1627
		// that in the default theme, but to support any custom themes based on
1628
		// the old verison, we do this to fix it for them.
1629
		addInlineCss("\n\t" . '#caption_edit_reason, dl:not(#post_header) input[name="modify_reason"] { display: none; }');
1630
		addInlineJavaScript("\n\t" . '$("#caption_edit_reason").remove(); $("dl:not(#post_header) input[name=\"modify_reason\"]").remove();', true);
1631
	}
1632
1633
	// Finally, load the template.
1634
	if (!isset($_REQUEST['xml']))
1635
	{
1636
		loadTemplate('Post');
1637
1638
		// These two lines are for the revamped attachments UI add in 2.1.4.
1639
		loadCSSFile('attachments.css', array('minimize' => true, 'order_pos' => 450), 'smf_attachments');
1640
		addInlineJavaScript("\n\t" . '$("#post_attachments_area #postAttachment").remove();', true);
1641
	}
1642
1643
	call_integration_hook('integrate_post_end');
1644
}
1645
1646
/**
1647
 * Posts or saves the message composed with Post().
1648
 *
1649
 * requires various permissions depending on the action.
1650
 * handles attachment, post, and calendar saving.
1651
 * sends off notifications, and allows for announcements and moderation.
1652
 * accessed from ?action=post2.
1653
 */
1654
function Post2()
1655
{
1656
	global $board, $topic, $txt, $modSettings, $sourcedir, $context;
1657
	global $user_info, $board_info, $options, $smcFunc, $settings;
1658
1659
	// Sneaking off, are we?
1660
	if (empty($_POST) && empty($topic))
1661
	{
1662
		if (empty($_SERVER['CONTENT_LENGTH']))
1663
			redirectexit('action=post;board=' . $board . '.0');
1664
		else
1665
			fatal_lang_error('post_upload_error', false);
1666
	}
1667
	elseif (empty($_POST) && !empty($topic))
1668
		redirectexit('action=post;topic=' . $topic . '.0');
1669
1670
	// No need!
1671
	$context['robot_no_index'] = true;
1672
1673
	// Prevent double submission of this form.
1674
	checkSubmitOnce('check');
1675
1676
	// No errors as yet.
1677
	$post_errors = array();
1678
1679
	// If the session has timed out, let the user re-submit their form.
1680
	if (checkSession('post', '', false) != '')
1681
		$post_errors[] = 'session_timeout';
1682
1683
	// Wrong verification code?
1684
	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)))
1685
	{
1686
		require_once($sourcedir . '/Subs-Editor.php');
1687
		$verificationOptions = array(
1688
			'id' => 'post',
1689
		);
1690
		$context['require_verification'] = create_control_verification($verificationOptions, true);
1691
		if (is_array($context['require_verification']))
1692
			$post_errors = array_merge($post_errors, $context['require_verification']);
1693
	}
1694
1695
	require_once($sourcedir . '/Subs-Post.php');
1696
	loadLanguage('Post');
1697
1698
	call_integration_hook('integrate_post2_start', array(&$post_errors));
1699
1700
	// Drafts enabled and needed?
1701
	if (!empty($modSettings['drafts_post_enabled']) && (isset($_POST['save_draft']) || isset($_POST['id_draft'])))
1702
		require_once($sourcedir . '/Drafts.php');
1703
1704
	// First check to see if they are trying to delete any current attachments.
1705
	if (isset($_POST['attach_del']))
1706
	{
1707
		$keep_temp = array();
1708
		$keep_ids = array();
1709
		foreach ($_POST['attach_del'] as $dummy)
1710
			if (strpos($dummy, 'post_tmp_' . $user_info['id']) !== false)
1711
				$keep_temp[] = $dummy;
1712
			else
1713
				$keep_ids[] = (int) $dummy;
1714
1715
		if (isset($_SESSION['temp_attachments']))
1716
			foreach ($_SESSION['temp_attachments'] as $attachID => $attachment)
1717
			{
1718
				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)
1719
					continue;
1720
1721
				unset($_SESSION['temp_attachments'][$attachID]);
1722
				unlink($attachment['tmp_name']);
1723
			}
1724
1725
		if (!empty($_REQUEST['msg']))
1726
		{
1727
			require_once($sourcedir . '/ManageAttachments.php');
1728
			$attachmentQuery = array(
1729
				'attachment_type' => 0,
1730
				'id_msg' => (int) $_REQUEST['msg'],
1731
				'not_id_attach' => $keep_ids,
1732
			);
1733
			removeAttachments($attachmentQuery);
1734
		}
1735
	}
1736
1737
	// Then try to upload any attachments.
1738
	$context['can_post_attachment'] = !empty($modSettings['attachmentEnable']) && $modSettings['attachmentEnable'] == 1 && (allowedTo('post_attachment') || ($modSettings['postmod_active'] && allowedTo('post_unapproved_attachments')));
1739
	if ($context['can_post_attachment'] && empty($_POST['from_qr']))
1740
	{
1741
		require_once($sourcedir . '/Subs-Attachments.php');
1742
		processAttachments();
1743
	}
1744
1745
	// They've already uploaded some attachments, but they don't have permission to post them
1746
	// This can sometimes happen when they came from ?action=calendar;sa=post
1747
	if (!$context['can_post_attachment'] && !empty($_SESSION['already_attached']))
1748
	{
1749
		require_once($sourcedir . '/ManageAttachments.php');
1750
1751
		foreach ($_SESSION['already_attached'] as $attachID => $attachment)
1752
			removeAttachments(array('id_attach' => $attachID));
1753
1754
		unset($_SESSION['already_attached']);
1755
1756
		$post_errors[] = array('cannot_post_attachment', array($board_info['name']));
1757
	}
1758
1759
	$can_approve = allowedTo('approve_posts');
1760
1761
	// If this isn't a new topic load the topic info that we need.
1762
	if (!empty($topic))
1763
	{
1764
		$request = $smcFunc['db_query']('', '
1765
			SELECT locked, is_sticky, id_poll, approved, id_first_msg, id_last_msg, id_member_started, id_board
1766
			FROM {db_prefix}topics
1767
			WHERE id_topic = {int:current_topic}
1768
			LIMIT 1',
1769
			array(
1770
				'current_topic' => $topic,
1771
			)
1772
		);
1773
		$topic_info = $smcFunc['db_fetch_assoc']($request);
1774
		$smcFunc['db_free_result']($request);
1775
1776
		// Though the topic should be there, it might have vanished.
1777
		if (!is_array($topic_info))
1778
			fatal_lang_error('topic_doesnt_exist', 404);
1779
1780
		// Did this topic suddenly move? Just checking...
1781
		if ($topic_info['id_board'] != $board)
1782
			fatal_lang_error('not_a_topic');
1783
1784
		// Do the permissions and approval stuff...
1785
		$becomesApproved = true;
1786
1787
		// Replies to unapproved topics are unapproved by default (but not for moderators)
1788
		if (empty($topic_info['approved']) && !$can_approve)
1789
		{
1790
			$becomesApproved = false;
1791
1792
			// Set a nice session var...
1793
			$_SESSION['becomesUnapproved'] = true;
1794
		}
1795
	}
1796
1797
	// Replying to a topic?
1798
	if (!empty($topic) && !isset($_REQUEST['msg']))
1799
	{
1800
		// Don't allow a post if it's locked.
1801
		if ($topic_info['locked'] != 0 && !allowedTo('moderate_board'))
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $topic_info does not seem to be defined for all execution paths leading up to this point.
Loading history...
1802
			fatal_lang_error('topic_locked', false);
1803
1804
		// Sorry, multiple polls aren't allowed... yet.  You should stop giving me ideas :P.
1805
		if (isset($_REQUEST['poll']) && $topic_info['id_poll'] > 0)
1806
			unset($_REQUEST['poll']);
1807
1808
		elseif ($topic_info['id_member_started'] != $user_info['id'])
1809
		{
1810
			if ($modSettings['postmod_active'] && allowedTo('post_unapproved_replies_any') && !allowedTo('post_reply_any'))
1811
				$becomesApproved = false;
1812
1813
			else
1814
				isAllowedTo('post_reply_any');
1815
		}
1816
		elseif (!allowedTo('post_reply_any'))
1817
		{
1818
			if ($modSettings['postmod_active'] && allowedTo('post_unapproved_replies_own') && !allowedTo('post_reply_own'))
1819
				$becomesApproved = false;
1820
1821
			else
1822
				isAllowedTo('post_reply_own');
1823
		}
1824
1825
		if (isset($_POST['lock']))
1826
		{
1827
			// Nothing is changed to the lock.
1828
			if (empty($topic_info['locked']) == empty($_POST['lock']))
1829
				unset($_POST['lock']);
1830
1831
			// You're have no permission to lock this topic.
1832
			elseif (!allowedTo(array('lock_any', 'lock_own')) || (!allowedTo('lock_any') && $user_info['id'] != $topic_info['id_member_started']))
1833
				unset($_POST['lock']);
1834
1835
			// You are allowed to (un)lock your own topic only.
1836
			elseif (!allowedTo('lock_any'))
1837
			{
1838
				// You cannot override a moderator lock.
1839
				if ($topic_info['locked'] == 1)
1840
					unset($_POST['lock']);
1841
1842
				else
1843
					$_POST['lock'] = empty($_POST['lock']) ? 0 : 2;
1844
			}
1845
			// Hail mighty moderator, (un)lock this topic immediately.
1846
			else
1847
			{
1848
				$_POST['lock'] = empty($_POST['lock']) ? 0 : 1;
1849
1850
				// Did someone (un)lock this while you were posting?
1851
				if (isset($_POST['already_locked']) && $_POST['already_locked'] != $topic_info['locked'])
1852
					$post_errors[] = 'topic_' . (empty($topic_info['locked']) ? 'un' : '') . 'locked';
1853
			}
1854
		}
1855
1856
		// So you wanna (un)sticky this...let's see.
1857
		if (isset($_POST['sticky']) && ($_POST['sticky'] == $topic_info['is_sticky'] || !allowedTo('make_sticky')))
1858
			unset($_POST['sticky']);
1859
		elseif (isset($_POST['sticky']))
1860
		{
1861
			// Did someone (un)sticky this while you were posting?
1862
			if (isset($_POST['already_sticky']) && $_POST['already_sticky'] != $topic_info['is_sticky'])
1863
				$post_errors[] = 'topic_' . (empty($topic_info['is_sticky']) ? 'un' : '') . 'sticky';
1864
		}
1865
1866
		// If drafts are enabled, then pass this off
1867
		if (!empty($modSettings['drafts_post_enabled']) && isset($_POST['save_draft']))
1868
		{
1869
			SaveDraft($post_errors);
1870
			return Post();
1871
		}
1872
1873
		// If the number of replies has changed, if the setting is enabled, go back to Post() - which handles the error.
1874
		if (empty($options['no_new_reply_warning']) && isset($_POST['last_msg']) && $topic_info['id_last_msg'] > $_POST['last_msg'])
1875
		{
1876
			$_REQUEST['preview'] = true;
1877
			return Post();
1878
		}
1879
1880
		$posterIsGuest = $user_info['is_guest'];
1881
		$context['is_own_post'] = true;
1882
		$context['poster_id'] = $user_info['id'];
1883
	}
1884
	// Posting a new topic.
1885
	elseif (empty($topic))
1886
	{
1887
		// Now don't be silly, new topics will get their own id_msg soon enough.
1888
		unset($_REQUEST['msg'], $_POST['msg'], $_GET['msg']);
1889
1890
		// Do like, the permissions, for safety and stuff...
1891
		$becomesApproved = true;
1892
		if ($modSettings['postmod_active'] && !allowedTo('post_new') && allowedTo('post_unapproved_topics'))
1893
			$becomesApproved = false;
1894
		else
1895
			isAllowedTo('post_new');
1896
1897
		if (isset($_POST['lock']))
1898
		{
1899
			// New topics are by default not locked.
1900
			if (empty($_POST['lock']))
1901
				unset($_POST['lock']);
1902
			// Besides, you need permission.
1903
			elseif (!allowedTo(array('lock_any', 'lock_own')))
1904
				unset($_POST['lock']);
1905
			// A moderator-lock (1) can override a user-lock (2).
1906
			else
1907
				$_POST['lock'] = allowedTo('lock_any') ? 1 : 2;
1908
		}
1909
1910
		if (isset($_POST['sticky']) && (empty($_POST['sticky']) || !allowedTo('make_sticky')))
1911
			unset($_POST['sticky']);
1912
1913
		// Saving your new topic as a draft first?
1914
		if (!empty($modSettings['drafts_post_enabled']) && isset($_POST['save_draft']))
1915
		{
1916
			SaveDraft($post_errors);
1917
			return Post();
1918
		}
1919
1920
		$posterIsGuest = $user_info['is_guest'];
1921
		$context['is_own_post'] = true;
1922
		$context['poster_id'] = $user_info['id'];
1923
	}
1924
	// Modifying an existing message?
1925
	elseif (isset($_REQUEST['msg']) && !empty($topic))
1926
	{
1927
		$_REQUEST['msg'] = (int) $_REQUEST['msg'];
1928
1929
		$request = $smcFunc['db_query']('', '
1930
			SELECT id_member, poster_name, poster_email, poster_time, approved
1931
			FROM {db_prefix}messages
1932
			WHERE id_msg = {int:id_msg}
1933
			LIMIT 1',
1934
			array(
1935
				'id_msg' => $_REQUEST['msg'],
1936
			)
1937
		);
1938
		if ($smcFunc['db_num_rows']($request) == 0)
1939
			fatal_lang_error('cant_find_messages', false);
1940
		$row = $smcFunc['db_fetch_assoc']($request);
1941
		$smcFunc['db_free_result']($request);
1942
1943
		if (!empty($topic_info['locked']) && !allowedTo('moderate_board'))
1944
			fatal_lang_error('topic_locked', false);
1945
1946
		if (isset($_POST['lock']))
1947
		{
1948
			// Nothing changes to the lock status.
1949
			if ((empty($_POST['lock']) && empty($topic_info['locked'])) || (!empty($_POST['lock']) && !empty($topic_info['locked'])))
1950
				unset($_POST['lock']);
1951
			// You're simply not allowed to (un)lock this.
1952
			elseif (!allowedTo(array('lock_any', 'lock_own')) || (!allowedTo('lock_any') && $user_info['id'] != $topic_info['id_member_started']))
1953
				unset($_POST['lock']);
1954
			// You're only allowed to lock your own topics.
1955
			elseif (!allowedTo('lock_any'))
1956
			{
1957
				// You're not allowed to break a moderator's lock.
1958
				if ($topic_info['locked'] == 1)
1959
					unset($_POST['lock']);
1960
				// Lock it with a soft lock or unlock it.
1961
				else
1962
					$_POST['lock'] = empty($_POST['lock']) ? 0 : 2;
1963
			}
1964
			// You must be the moderator.
1965
			else
1966
			{
1967
				$_POST['lock'] = empty($_POST['lock']) ? 0 : 1;
1968
1969
				// Did someone (un)lock this while you were posting?
1970
				if (isset($_POST['already_locked']) && $_POST['already_locked'] != $topic_info['locked'])
1971
					$post_errors[] = 'topic_' . (empty($topic_info['locked']) ? 'un' : '') . 'locked';
1972
			}
1973
		}
1974
1975
		// Change the sticky status of this topic?
1976
		if (isset($_POST['sticky']) && (!allowedTo('make_sticky') || $_POST['sticky'] == $topic_info['is_sticky']))
1977
			unset($_POST['sticky']);
1978
		elseif (isset($_POST['sticky']))
1979
		{
1980
			// Did someone (un)sticky this while you were posting?
1981
			if (isset($_POST['already_sticky']) && $_POST['already_sticky'] != $topic_info['is_sticky'])
1982
				$post_errors[] = 'topic_' . (empty($topic_info['locked']) ? 'un' : '') . 'stickied';
1983
		}
1984
1985
		if ($row['id_member'] == $user_info['id'] && !allowedTo('modify_any'))
1986
		{
1987
			if ((!$modSettings['postmod_active'] || $row['approved']) && !empty($modSettings['edit_disable_time']) && $row['poster_time'] + ($modSettings['edit_disable_time'] + 5) * 60 < time())
1988
				fatal_lang_error('modify_post_time_passed', false);
1989
			elseif ($topic_info['id_member_started'] == $user_info['id'] && !allowedTo('modify_own'))
1990
				isAllowedTo('modify_replies');
1991
			else
1992
				isAllowedTo('modify_own');
1993
		}
1994
		elseif ($topic_info['id_member_started'] == $user_info['id'] && !allowedTo('modify_any'))
1995
		{
1996
			isAllowedTo('modify_replies');
1997
1998
			// If you're modifying a reply, I say it better be logged...
1999
			$moderationAction = true;
2000
		}
2001
		else
2002
		{
2003
			isAllowedTo('modify_any');
2004
2005
			// Log it, assuming you're not modifying your own post.
2006
			if ($row['id_member'] != $user_info['id'])
2007
				$moderationAction = true;
2008
		}
2009
2010
		// If drafts are enabled, then lets send this off to save
2011
		if (!empty($modSettings['drafts_post_enabled']) && isset($_POST['save_draft']))
2012
		{
2013
			SaveDraft($post_errors);
2014
			return Post();
2015
		}
2016
2017
		$posterIsGuest = empty($row['id_member']);
2018
		$context['is_own_post'] = $user_info['id'] === (int) $row['id_member'];
2019
		$context['poster_id'] = (int) $row['id_member'];
2020
2021
		// Can they approve it?
2022
		$approve_checked = (!empty($REQUEST['approve']) ? 1 : 0);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $REQUEST does not exist. Did you maybe mean $_REQUEST?
Loading history...
2023
		$becomesApproved = $modSettings['postmod_active'] ? ($can_approve && !$row['approved'] ? $approve_checked : $row['approved']) : 1;
2024
		$approve_has_changed = $row['approved'] != $becomesApproved;
2025
2026
		if (!allowedTo('moderate_forum') || !$posterIsGuest)
2027
		{
2028
			$_POST['guestname'] = $row['poster_name'];
2029
			$_POST['email'] = $row['poster_email'];
2030
		}
2031
2032
		// Update search api
2033
		require_once($sourcedir . '/Search.php');
2034
		$searchAPI = findSearchAPI();
2035
		if ($searchAPI->supportsMethod('postRemoved'))
2036
			$searchAPI->postRemoved($_REQUEST['msg']);
2037
	}
2038
2039
	// In case we have approval permissions and want to override.
2040
	if ($can_approve && $modSettings['postmod_active'])
2041
	{
2042
		$becomesApproved = isset($_POST['quickReply']) || !empty($_REQUEST['approve']) ? 1 : 0;
2043
		$approve_has_changed = isset($row['approved']) ? $row['approved'] != $becomesApproved : false;
2044
	}
2045
2046
	// If the poster is a guest evaluate the legality of name and email.
2047
	if ($posterIsGuest)
2048
	{
2049
		$_POST['guestname'] = !isset($_POST['guestname']) ? '' : trim(normalize_spaces(sanitize_chars($_POST['guestname'], 1, ' '), true, true, array('no_breaks' => true, 'replace_tabs' => true, 'collapse_hspace' => true)));
2050
		$_POST['email'] = !isset($_POST['email']) ? '' : trim($_POST['email']);
2051
2052
		if ($_POST['guestname'] == '' || $_POST['guestname'] == '_')
2053
			$post_errors[] = 'no_name';
2054
		if ($smcFunc['strlen']($_POST['guestname']) > 25)
2055
			$post_errors[] = 'long_name';
2056
2057
		if (empty($modSettings['guest_post_no_email']))
2058
		{
2059
			// Only check if they changed it!
2060
			if (!isset($row) || $row['poster_email'] != $_POST['email'])
2061
			{
2062
				if (!allowedTo('moderate_forum') && (!isset($_POST['email']) || $_POST['email'] == ''))
2063
					$post_errors[] = 'no_email';
2064
				if (!allowedTo('moderate_forum') && !filter_var($_POST['email'], FILTER_VALIDATE_EMAIL))
2065
					$post_errors[] = 'bad_email';
2066
			}
2067
2068
			// Now make sure this email address is not banned from posting.
2069
			isBannedEmail($_POST['email'], 'cannot_post', sprintf($txt['you_are_post_banned'], $txt['guest_title']));
2070
		}
2071
2072
		// In case they are making multiple posts this visit, help them along by storing their name.
2073
		if (empty($post_errors))
2074
		{
2075
			$_SESSION['guest_name'] = $_POST['guestname'];
2076
			$_SESSION['guest_email'] = $_POST['email'];
2077
		}
2078
	}
2079
2080
	// Coming from the quickReply?
2081
	if (isset($_POST['quickReply']))
2082
		$_POST['message'] = $_POST['quickReply'];
2083
2084
	// Check the subject and message.
2085
	if (!isset($_POST['subject']) || $smcFunc['htmltrim']($smcFunc['htmlspecialchars']($_POST['subject'])) === '')
2086
		$post_errors[] = 'no_subject';
2087
	if (!isset($_POST['message']) || $smcFunc['htmltrim']($smcFunc['htmlspecialchars']($_POST['message']), ENT_QUOTES) === '')
2088
		$post_errors[] = 'no_message';
2089
	elseif (!empty($modSettings['max_messageLength']) && $smcFunc['strlen']($_POST['message']) > $modSettings['max_messageLength'])
2090
		$post_errors[] = array('long_message', array($modSettings['max_messageLength']));
2091
	else
2092
	{
2093
		// Prepare the message a bit for some additional testing.
2094
		$_POST['message'] = $smcFunc['htmlspecialchars']($_POST['message'], ENT_QUOTES);
2095
2096
		// Preparse code. (Zef)
2097
		if ($user_info['is_guest'])
2098
			$user_info['name'] = $_POST['guestname'];
2099
		preparsecode($_POST['message']);
2100
2101
		// Let's see if there's still some content left without the tags.
2102
		if ($smcFunc['htmltrim'](strip_tags(parse_bbc($_POST['message'], false), implode('', $context['allowed_html_tags']))) === '' && (!allowedTo('bbc_html') || strpos($_POST['message'], '[html]') === false))
2103
			$post_errors[] = 'no_message';
2104
2105
	}
2106
	if (isset($_POST['calendar']) && !isset($_REQUEST['deleteevent']) && $smcFunc['htmltrim']($_POST['evtitle']) === '')
2107
		$post_errors[] = 'no_event';
2108
	// You are not!
2109
	if (isset($_POST['message']) && strtolower($_POST['message']) == 'i am the administrator.' && !$user_info['is_admin'])
2110
		fatal_error('Knave! Masquerader! Charlatan!', false);
2111
2112
	// Validate the poll...
2113
	if (isset($_REQUEST['poll']) && $modSettings['pollMode'] == '1')
2114
	{
2115
		if (!empty($topic) && !isset($_REQUEST['msg']))
2116
			fatal_lang_error('no_access', false);
2117
2118
		// This is a new topic... so it's a new poll.
2119
		if (empty($topic))
2120
			isAllowedTo('poll_post');
2121
		// Can you add to your own topics?
2122
		elseif ($user_info['id'] == $topic_info['id_member_started'] && !allowedTo('poll_add_any'))
2123
			isAllowedTo('poll_add_own');
2124
		// Can you add polls to any topic, then?
2125
		else
2126
			isAllowedTo('poll_add_any');
2127
2128
		if (!isset($_POST['question']) || trim($_POST['question']) == '')
2129
			$post_errors[] = 'no_question';
2130
2131
		$_POST['options'] = empty($_POST['options']) ? array() : htmltrim__recursive($_POST['options']);
2132
2133
		// Get rid of empty ones.
2134
		foreach ($_POST['options'] as $k => $option)
2135
			if ($option == '')
2136
				unset($_POST['options'][$k], $_POST['options'][$k]);
2137
2138
		// What are you going to vote between with one choice?!?
2139
		if (count($_POST['options']) < 2)
2140
			$post_errors[] = 'poll_few';
2141
		elseif (count($_POST['options']) > 256)
2142
			$post_errors[] = 'poll_many';
2143
	}
2144
2145
	if ($posterIsGuest)
2146
	{
2147
		// If user is a guest, make sure the chosen name isn't taken.
2148
		require_once($sourcedir . '/Subs-Members.php');
2149
		if (isReservedName($_POST['guestname'], 0, true, false) && (!isset($row['poster_name']) || $_POST['guestname'] != $row['poster_name']))
2150
			$post_errors[] = 'bad_name';
2151
	}
2152
	// If the user isn't a guest, get his or her name and email.
2153
	elseif (!isset($_REQUEST['msg']))
2154
	{
2155
		$_POST['guestname'] = $user_info['name'];
2156
		$_POST['email'] = $user_info['email'];
2157
	}
2158
2159
 	call_integration_hook('integrate_post2_pre', array(&$post_errors));
2160
2161
	// Any mistakes?
2162
	if (!empty($post_errors))
2163
	{
2164
		// Previewing.
2165
		$_REQUEST['preview'] = true;
2166
2167
		return Post($post_errors);
2168
	}
2169
2170
	// Previewing? Go back to start.
2171
	if (isset($_REQUEST['preview']))
2172
	{
2173
		if (checkSession('post', '', false) != '')
2174
		{
2175
			loadLanguage('Errors');
2176
			$post_errors[] = 'session_timeout';
2177
			unset ($_POST['preview'], $_REQUEST['xml']); // just in case
2178
		}
2179
		return Post($post_errors);
2180
	}
2181
2182
	// Make sure the user isn't spamming the board.
2183
	if (!isset($_REQUEST['msg']))
2184
		spamProtection('post');
2185
2186
	// At about this point, we're posting and that's that.
2187
	ignore_user_abort(true);
2188
	@set_time_limit(300);
2189
2190
	// Add special html entities to the subject, name, and email.
2191
	$_POST['subject'] = strtr($smcFunc['htmlspecialchars']($_POST['subject']), array("\r" => '', "\n" => '', "\t" => ''));
2192
	$_POST['guestname'] = $smcFunc['htmlspecialchars']($_POST['guestname']);
2193
	$_POST['email'] = $smcFunc['htmlspecialchars']($_POST['email']);
2194
	$_POST['modify_reason'] = empty($_POST['modify_reason']) ? '' : strtr($smcFunc['htmlspecialchars']($_POST['modify_reason']), array("\r" => '', "\n" => '', "\t" => ''));
2195
2196
	// At this point, we want to make sure the subject isn't too long.
2197
	if ($smcFunc['strlen']($_POST['subject']) > 100)
2198
		$_POST['subject'] = $smcFunc['substr']($_POST['subject'], 0, 100);
2199
2200
	// Same with the "why did you edit this" text.
2201
	if ($smcFunc['strlen']($_POST['modify_reason']) > 100)
2202
		$_POST['modify_reason'] = $smcFunc['substr']($_POST['modify_reason'], 0, 100);
2203
2204
	// Make the poll...
2205
	if (isset($_REQUEST['poll']))
2206
	{
2207
		// Make sure that the user has not entered a ridiculous number of options..
2208
		if (empty($_POST['poll_max_votes']) || $_POST['poll_max_votes'] <= 0)
2209
			$_POST['poll_max_votes'] = 1;
2210
		elseif ($_POST['poll_max_votes'] > count($_POST['options']))
2211
			$_POST['poll_max_votes'] = count($_POST['options']);
2212
		else
2213
			$_POST['poll_max_votes'] = (int) $_POST['poll_max_votes'];
2214
2215
		$_POST['poll_expire'] = (int) $_POST['poll_expire'];
2216
		$_POST['poll_expire'] = $_POST['poll_expire'] > 9999 ? 9999 : ($_POST['poll_expire'] < 0 ? 0 : $_POST['poll_expire']);
2217
2218
		// Just set it to zero if it's not there..
2219
		if (!isset($_POST['poll_hide']))
2220
			$_POST['poll_hide'] = 0;
2221
		else
2222
			$_POST['poll_hide'] = (int) $_POST['poll_hide'];
2223
		$_POST['poll_change_vote'] = isset($_POST['poll_change_vote']) ? 1 : 0;
2224
2225
		$_POST['poll_guest_vote'] = isset($_POST['poll_guest_vote']) ? 1 : 0;
2226
		// Make sure guests are actually allowed to vote generally.
2227
		if ($_POST['poll_guest_vote'])
2228
		{
2229
			require_once($sourcedir . '/Subs-Members.php');
2230
			$allowedVoteGroups = groupsAllowedTo('poll_vote', $board);
2231
			if (!in_array(-1, $allowedVoteGroups['allowed']))
2232
				$_POST['poll_guest_vote'] = 0;
2233
		}
2234
2235
		// If the user tries to set the poll too far in advance, don't let them.
2236
		if (!empty($_POST['poll_expire']) && $_POST['poll_expire'] < 1)
2237
			fatal_lang_error('poll_range_error', false);
2238
		// Don't allow them to select option 2 for hidden results if it's not time limited.
2239
		elseif (empty($_POST['poll_expire']) && $_POST['poll_hide'] == 2)
2240
			$_POST['poll_hide'] = 1;
2241
2242
		// Clean up the question and answers.
2243
		$_POST['question'] = $smcFunc['htmlspecialchars']($_POST['question']);
2244
		$_POST['question'] = $smcFunc['truncate']($_POST['question'], 255);
2245
		$_POST['question'] = preg_replace('~&amp;#(\d{4,5}|[2-9]\d{2,4}|1[2-9]\d);~', '&#$1;', $_POST['question']);
2246
		$_POST['options'] = htmlspecialchars__recursive($_POST['options']);
2247
	}
2248
2249
	// ...or attach a new file...
2250
	if ($context['can_post_attachment'] && !empty($_SESSION['temp_attachments']) && empty($_POST['from_qr']))
2251
	{
2252
		$attachIDs = array();
2253
		$attach_errors = array();
2254
		if (!empty($context['we_are_history']))
2255
			$attach_errors[] = '<dd>' . $txt['error_temp_attachments_flushed'] . '<br><br></dd>';
2256
2257
		foreach ($_SESSION['temp_attachments'] as $attachID => $attachment)
2258
		{
2259
			if ($attachID != 'initial_error' && strpos($attachID, 'post_tmp_' . $user_info['id']) === false)
2260
				continue;
2261
2262
			// If there was an initial error just show that message.
2263
			if ($attachID == 'initial_error')
2264
			{
2265
				$attach_errors[] = '<dt>' . $txt['attach_no_upload'] . '</dt>';
2266
				$attach_errors[] = '<dd>' . (is_array($attachment) ? vsprintf($txt[$attachment[0]], (array) $attachment[1]) : $txt[$attachment]) . '</dd>';
2267
2268
				unset($_SESSION['temp_attachments']);
2269
				break;
2270
			}
2271
2272
			$attachmentOptions = array(
2273
				'post' => isset($_REQUEST['msg']) ? $_REQUEST['msg'] : 0,
2274
				'poster' => $user_info['id'],
2275
				'name' => $attachment['name'],
2276
				'tmp_name' => $attachment['tmp_name'],
2277
				'size' => isset($attachment['size']) ? $attachment['size'] : 0,
2278
				'mime_type' => isset($attachment['type']) ? $attachment['type'] : '',
2279
				'id_folder' => isset($attachment['id_folder']) ? $attachment['id_folder'] : $modSettings['currentAttachmentUploadDir'],
2280
				'approved' => !$modSettings['postmod_active'] || allowedTo('post_attachment'),
2281
				'errors' => $attachment['errors'],
2282
			);
2283
2284
			if (empty($attachment['errors']))
2285
			{
2286
				if (createAttachment($attachmentOptions))
2287
				{
2288
					$attachIDs[] = $attachmentOptions['id'];
2289
					if (!empty($attachmentOptions['thumb']))
2290
						$attachIDs[] = $attachmentOptions['thumb'];
2291
				}
2292
			}
2293
			else
2294
				$attach_errors[] = '<dt>&nbsp;</dt>';
2295
2296
			if (!empty($attachmentOptions['errors']))
2297
			{
2298
				// Sort out the errors for display and delete any associated files.
2299
				$attach_errors[] = '<dt>' . sprintf($txt['attach_warning'], $attachment['name']) . '</dt>';
2300
				$log_these = array('attachments_no_create', 'attachments_no_write', 'attach_timeout', 'ran_out_of_space', 'cant_access_upload_path', 'attach_0_byte_file');
2301
				foreach ($attachmentOptions['errors'] as $error)
2302
				{
2303
					if (!is_array($error))
2304
					{
2305
						$attach_errors[] = '<dd>' . $txt[$error] . '</dd>';
2306
						if (in_array($error, $log_these))
2307
							log_error($attachment['name'] . ': ' . $txt[$error], 'critical');
2308
					}
2309
					else
2310
						$attach_errors[] = '<dd>' . vsprintf($txt[$error[0]], (array) $error[1]) . '</dd>';
2311
				}
2312
				if (file_exists($attachment['tmp_name']))
2313
					unlink($attachment['tmp_name']);
2314
			}
2315
		}
2316
	}
2317
	unset($_SESSION['temp_attachments']);
2318
2319
	// Make the poll...
2320
	if (isset($_REQUEST['poll']))
2321
	{
2322
		// Create the poll.
2323
		$id_poll = $smcFunc['db_insert']('',
2324
			'{db_prefix}polls',
2325
			array(
2326
				'question' => 'string-255', 'hide_results' => 'int', 'max_votes' => 'int', 'expire_time' => 'int', 'id_member' => 'int',
2327
				'poster_name' => 'string-255', 'change_vote' => 'int', 'guest_vote' => 'int'
2328
			),
2329
			array(
2330
				$_POST['question'], $_POST['poll_hide'], $_POST['poll_max_votes'], (empty($_POST['poll_expire']) ? 0 : time() + $_POST['poll_expire'] * 3600 * 24), $user_info['id'],
2331
				$_POST['guestname'], $_POST['poll_change_vote'], $_POST['poll_guest_vote'],
2332
			),
2333
			array('id_poll'),
2334
			1
2335
		);
2336
2337
		// Create each answer choice.
2338
		$i = 0;
2339
		$pollOptions = array();
2340
		foreach ($_POST['options'] as $option)
2341
		{
2342
			$pollOptions[] = array($id_poll, $i, $option);
2343
			$i++;
2344
		}
2345
2346
		$smcFunc['db_insert']('insert',
2347
			'{db_prefix}poll_choices',
2348
			array('id_poll' => 'int', 'id_choice' => 'int', 'label' => 'string-255'),
2349
			$pollOptions,
2350
			array('id_poll', 'id_choice')
2351
		);
2352
2353
		call_integration_hook('integrate_poll_add_edit', array($id_poll, false));
2354
	}
2355
	else
2356
		$id_poll = 0;
2357
2358
	// Creating a new topic?
2359
	$newTopic = empty($_REQUEST['msg']) && empty($topic);
2360
2361
	// Check the icon.
2362
	if (!isset($_POST['icon']))
2363
		$_POST['icon'] = 'xx';
2364
2365
	else
2366
	{
2367
		$_POST['icon'] = $smcFunc['htmlspecialchars']($_POST['icon']);
2368
2369
		// Need to figure it out if this is a valid icon name.
2370
		if ((!file_exists($settings['theme_dir'] . '/images/post/' . $_POST['icon'] . '.png')) && (!file_exists($settings['default_theme_dir'] . '/images/post/' . $_POST['icon'] . '.png')))
2371
			$_POST['icon'] = 'xx';
2372
	}
2373
2374
	// Collect all parameters for the creation or modification of a post.
2375
	$msgOptions = array(
2376
		'id' => empty($_REQUEST['msg']) ? 0 : (int) $_REQUEST['msg'],
2377
		'subject' => $_POST['subject'],
2378
		'body' => $_POST['message'],
2379
		'icon' => preg_replace('~[\./\\\\*:"\'<>]~', '', $_POST['icon']),
2380
		'smileys_enabled' => !isset($_POST['ns']),
2381
		'attachments' => empty($attachIDs) ? array() : $attachIDs,
2382
		'approved' => $becomesApproved,
2383
	);
2384
	$topicOptions = array(
2385
		'id' => empty($topic) ? 0 : $topic,
2386
		'board' => $board,
2387
		'poll' => isset($_REQUEST['poll']) ? $id_poll : null,
2388
		'lock_mode' => isset($_POST['lock']) ? (int) $_POST['lock'] : null,
2389
		'sticky_mode' => isset($_POST['sticky']) ? (int) $_POST['sticky'] : null,
2390
		'mark_as_read' => true,
2391
		'is_approved' => !$modSettings['postmod_active'] || empty($topic) || !empty($board_info['cur_topic_approved']),
2392
		'first_msg' => empty($topic_info['id_first_msg']) ? null : $topic_info['id_first_msg'],
2393
		'last_msg' => empty($topic_info['id_last_msg']) ? null : $topic_info['id_last_msg'],
2394
	);
2395
	$posterOptions = array(
2396
		'id' => $user_info['id'],
2397
		'name' => $_POST['guestname'],
2398
		'email' => $_POST['email'],
2399
		'update_post_count' => !$user_info['is_guest'] && !isset($_REQUEST['msg']) && $board_info['posts_count'],
2400
	);
2401
2402
	// This is an already existing message. Edit it.
2403
	if (!empty($_REQUEST['msg']))
2404
	{
2405
		// Have admins allowed people to hide their screwups?
2406
		if (time() - $row['poster_time'] > $modSettings['edit_wait_time'] || $user_info['id'] != $row['id_member'])
2407
		{
2408
			$msgOptions['modify_time'] = time();
2409
			$msgOptions['modify_name'] = $user_info['name'];
2410
			$msgOptions['modify_reason'] = $_POST['modify_reason'];
2411
			$msgOptions['poster_time'] = $row['poster_time'];
2412
		}
2413
2414
		modifyPost($msgOptions, $topicOptions, $posterOptions);
2415
	}
2416
	// This is a new topic or an already existing one. Save it.
2417
	else
2418
	{
2419
		createPost($msgOptions, $topicOptions, $posterOptions);
2420
2421
		if (isset($topicOptions['id']))
2422
			$topic = $topicOptions['id'];
2423
	}
2424
2425
	// Are there attachments already uploaded and waiting to be assigned?
2426
	if (!empty($msgOptions['id']) && !empty($_SESSION['already_attached']))
2427
	{
2428
		require_once($sourcedir . '/Subs-Attachments.php');
2429
		assignAttachments($_SESSION['already_attached'], $msgOptions['id']);
2430
		unset($_SESSION['already_attached']);
2431
	}
2432
2433
	// If we had a draft for this, its time to remove it since it was just posted
2434
	if (!empty($modSettings['drafts_post_enabled']) && !empty($_POST['id_draft']))
2435
		DeleteDraft($_POST['id_draft']);
2436
2437
	// Editing or posting an event?
2438
	if (isset($_POST['calendar']) && (!isset($_REQUEST['eventid']) || $_REQUEST['eventid'] == -1))
2439
	{
2440
		require_once($sourcedir . '/Subs-Calendar.php');
2441
2442
		// Make sure they can link an event to this post.
2443
		canLinkEvent();
2444
2445
		// Insert the event.
2446
		$eventOptions = array(
2447
			'board' => $board,
2448
			'topic' => $topic,
2449
			'title' => $_POST['evtitle'],
2450
			'location' => $_POST['event_location'],
2451
			'member' => $user_info['id'],
2452
		);
2453
		insertEvent($eventOptions);
2454
	}
2455
	elseif (isset($_POST['calendar']))
2456
	{
2457
		$_REQUEST['eventid'] = (int) $_REQUEST['eventid'];
2458
2459
		// Validate the post...
2460
		require_once($sourcedir . '/Subs-Calendar.php');
2461
		validateEventPost();
2462
2463
		// If you're not allowed to edit any events, you have to be the poster.
2464
		if (!allowedTo('calendar_edit_any'))
2465
		{
2466
			// Get the event's poster.
2467
			$request = $smcFunc['db_query']('', '
2468
				SELECT id_member
2469
				FROM {db_prefix}calendar
2470
				WHERE id_event = {int:id_event}',
2471
				array(
2472
					'id_event' => $_REQUEST['eventid'],
2473
				)
2474
			);
2475
			$row2 = $smcFunc['db_fetch_assoc']($request);
2476
			$smcFunc['db_free_result']($request);
2477
2478
			// Silly hacker, Trix are for kids. ...probably trademarked somewhere, this is FAIR USE! (parody...)
2479
			isAllowedTo('calendar_edit_' . ($row2['id_member'] == $user_info['id'] ? 'own' : 'any'));
2480
		}
2481
2482
		// Delete it?
2483
		if (isset($_REQUEST['deleteevent']))
2484
			$smcFunc['db_query']('', '
2485
				DELETE FROM {db_prefix}calendar
2486
				WHERE id_event = {int:id_event}',
2487
				array(
2488
					'id_event' => $_REQUEST['eventid'],
2489
				)
2490
			);
2491
		// ... or just update it?
2492
		else
2493
		{
2494
			// Set up our options
2495
			$eventOptions = array(
2496
				'board' => $board,
2497
				'topic' => $topic,
2498
				'title' => $_POST['evtitle'],
2499
				'location' => $_POST['event_location'],
2500
				'member' => $user_info['id'],
2501
			);
2502
			modifyEvent($_REQUEST['eventid'], $eventOptions);
2503
		}
2504
	}
2505
2506
	// Marking read should be done even for editing messages....
2507
	// Mark all the parents read.  (since you just posted and they will be unread.)
2508
	if (!$user_info['is_guest'] && !empty($board_info['parent_boards']))
2509
	{
2510
		$smcFunc['db_query']('', '
2511
			UPDATE {db_prefix}log_boards
2512
			SET id_msg = {int:id_msg}
2513
			WHERE id_member = {int:current_member}
2514
				AND id_board IN ({array_int:board_list})',
2515
			array(
2516
				'current_member' => $user_info['id'],
2517
				'board_list' => array_keys($board_info['parent_boards']),
2518
				'id_msg' => $modSettings['maxMsgID'],
2519
			)
2520
		);
2521
	}
2522
2523
	// Turn notification on or off.  (note this just blows smoke if it's already on or off.)
2524
	if (!empty($_POST['notify']) && !$context['user']['is_guest'])
2525
	{
2526
		$smcFunc['db_insert']('ignore',
2527
			'{db_prefix}log_notify',
2528
			array('id_member' => 'int', 'id_topic' => 'int', 'id_board' => 'int'),
2529
			array($user_info['id'], $topic, 0),
2530
			array('id_member', 'id_topic', 'id_board')
2531
		);
2532
	}
2533
	elseif (!$newTopic)
2534
		$smcFunc['db_query']('', '
2535
			DELETE FROM {db_prefix}log_notify
2536
			WHERE id_member = {int:current_member}
2537
				AND id_topic = {int:current_topic}',
2538
			array(
2539
				'current_member' => $user_info['id'],
2540
				'current_topic' => $topic,
2541
			)
2542
		);
2543
2544
	// Log an act of moderation - modifying.
2545
	if (!empty($moderationAction))
2546
		logAction('modify', array('topic' => $topic, 'message' => (int) $_REQUEST['msg'], 'member' => $row['id_member'], 'board' => $board));
2547
2548
	if (isset($_POST['lock']) && $_POST['lock'] != 2)
2549
		logAction(empty($_POST['lock']) ? 'unlock' : 'lock', array('topic' => $topicOptions['id'], 'board' => $topicOptions['board']));
2550
2551
	if (isset($_POST['sticky']))
2552
		logAction(empty($_POST['sticky']) ? 'unsticky' : 'sticky', array('topic' => $topicOptions['id'], 'board' => $topicOptions['board']));
2553
2554
	// Returning to the topic?
2555
	if (!empty($_REQUEST['goback']))
2556
	{
2557
		// Mark the board as read.... because it might get confusing otherwise.
2558
		$smcFunc['db_query']('', '
2559
			UPDATE {db_prefix}log_boards
2560
			SET id_msg = {int:maxMsgID}
2561
			WHERE id_member = {int:current_member}
2562
				AND id_board = {int:current_board}',
2563
			array(
2564
				'current_board' => $board,
2565
				'current_member' => $user_info['id'],
2566
				'maxMsgID' => $modSettings['maxMsgID'],
2567
			)
2568
		);
2569
	}
2570
2571
	if ($board_info['num_topics'] == 0)
2572
		cache_put_data('board-' . $board, null, 120);
2573
2574
	call_integration_hook('integrate_post2_end');
2575
2576
	if (!empty($_POST['announce_topic']) && allowedTo('announce_topic'))
2577
		redirectexit('action=announce;sa=selectgroup;topic=' . $topic . (!empty($_POST['move']) && allowedTo('move_any') ? ';move' : '') . (empty($_REQUEST['goback']) ? '' : ';goback'));
2578
2579
	if (!empty($_POST['move']) && allowedTo('move_any'))
2580
		redirectexit('action=movetopic;topic=' . $topic . '.0' . (empty($_REQUEST['goback']) ? '' : ';goback'));
2581
2582
	// Return to post if the mod is on.
2583
	if (isset($_REQUEST['msg']) && !empty($_REQUEST['goback']))
2584
		redirectexit('topic=' . $topic . '.msg' . $_REQUEST['msg'] . '#msg' . $_REQUEST['msg'], isBrowser('ie'));
2585
	elseif (!empty($_REQUEST['goback']))
2586
		redirectexit('topic=' . $topic . '.new#new', isBrowser('ie'));
2587
	// Dut-dut-duh-duh-DUH-duh-dut-duh-duh!  *dances to the Final Fantasy Fanfare...*
2588
	else
2589
		redirectexit('board=' . $board . '.0');
2590
}
2591
2592
/**
2593
 * Handle the announce topic function (action=announce).
2594
 *
2595
 * checks the topic announcement permissions and loads the announcement template.
2596
 * requires the announce_topic permission.
2597
 * uses the ManageMembers template and Post language file.
2598
 * call the right function based on the sub-action.
2599
 */
2600
function AnnounceTopic()
2601
{
2602
	global $context, $txt, $topic;
2603
2604
	isAllowedTo('announce_topic');
2605
2606
	validateSession();
2607
2608
	if (empty($topic))
2609
		fatal_lang_error('topic_gone', false);
2610
2611
	loadLanguage('Post');
2612
	loadTemplate('Post');
2613
2614
	$subActions = array(
2615
		'selectgroup' => 'AnnouncementSelectMembergroup',
2616
		'send' => 'AnnouncementSend',
2617
	);
2618
2619
	$context['page_title'] = $txt['announce_topic'];
2620
2621
	// Call the function based on the sub-action.
2622
	$call = isset($_REQUEST['sa']) && isset($subActions[$_REQUEST['sa']]) ? $_REQUEST['sa'] : 'selectgroup';
2623
	call_helper($subActions[$call]);
2624
}
2625
2626
/**
2627
 * Allow a user to chose the membergroups to send the announcement to.
2628
 *
2629
 * lets the user select the membergroups that will receive the topic announcement.
2630
 */
2631
function AnnouncementSelectMembergroup()
2632
{
2633
	global $txt, $context, $topic, $board_info, $smcFunc;
2634
2635
	$groups = array_merge($board_info['groups'], array(1));
2636
	foreach ($groups as $id => $group)
2637
		$groups[$id] = (int) $group;
2638
2639
	$context['groups'] = array();
2640
	if (in_array(0, $groups))
2641
	{
2642
		$context['groups'][0] = array(
2643
			'id' => 0,
2644
			'name' => $txt['announce_regular_members'],
2645
			'member_count' => 'n/a',
2646
		);
2647
	}
2648
2649
	// Get all membergroups that have access to the board the announcement was made on.
2650
	$request = $smcFunc['db_query']('', '
2651
		SELECT mg.id_group, COUNT(mem.id_member) AS num_members
2652
		FROM {db_prefix}membergroups AS mg
2653
			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)
2654
		WHERE mg.id_group IN ({array_int:group_list})
2655
		GROUP BY mg.id_group',
2656
		array(
2657
			'group_list' => $groups,
2658
			'newbie_id_group' => 4,
2659
		)
2660
	);
2661
	while ($row = $smcFunc['db_fetch_assoc']($request))
2662
	{
2663
		$context['groups'][$row['id_group']] = array(
2664
			'id' => $row['id_group'],
2665
			'name' => '',
2666
			'member_count' => $row['num_members'],
2667
		);
2668
	}
2669
	$smcFunc['db_free_result']($request);
2670
2671
	// Now get the membergroup names.
2672
	$request = $smcFunc['db_query']('', '
2673
		SELECT id_group, group_name
2674
		FROM {db_prefix}membergroups
2675
		WHERE id_group IN ({array_int:group_list})',
2676
		array(
2677
			'group_list' => $groups,
2678
		)
2679
	);
2680
	while ($row = $smcFunc['db_fetch_assoc']($request))
2681
		$context['groups'][$row['id_group']]['name'] = $row['group_name'];
2682
	$smcFunc['db_free_result']($request);
2683
2684
	// Get the subject of the topic we're about to announce.
2685
	$request = $smcFunc['db_query']('', '
2686
		SELECT m.subject
2687
		FROM {db_prefix}topics AS t
2688
			INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)
2689
		WHERE t.id_topic = {int:current_topic}',
2690
		array(
2691
			'current_topic' => $topic,
2692
		)
2693
	);
2694
	list ($context['topic_subject']) = $smcFunc['db_fetch_row']($request);
2695
	$smcFunc['db_free_result']($request);
2696
2697
	censorText($context['announce_topic']['subject']);
2698
2699
	$context['move'] = isset($_REQUEST['move']) ? 1 : 0;
2700
	$context['go_back'] = isset($_REQUEST['goback']) ? 1 : 0;
2701
2702
	$context['sub_template'] = 'announce';
2703
}
2704
2705
/**
2706
 * Send the announcement in chunks.
2707
 *
2708
 * splits the members to be sent a topic announcement into chunks.
2709
 * composes notification messages in all languages needed.
2710
 * does the actual sending of the topic announcements in chunks.
2711
 * calculates a rough estimate of the percentage items sent.
2712
 */
2713
function AnnouncementSend()
2714
{
2715
	global $topic, $board, $board_info, $context, $modSettings;
2716
	global $language, $scripturl, $sourcedir, $smcFunc;
2717
2718
	checkSession();
2719
2720
	$context['start'] = empty($_REQUEST['start']) ? 0 : (int) $_REQUEST['start'];
2721
	$groups = array_merge($board_info['groups'], array(1));
2722
2723
	if (isset($_POST['membergroups']))
2724
		$_POST['who'] = explode(',', $_POST['membergroups']);
2725
2726
	// Check whether at least one membergroup was selected.
2727
	if (empty($_POST['who']))
2728
		fatal_lang_error('no_membergroup_selected');
2729
2730
	// Make sure all membergroups are integers and can access the board of the announcement.
2731
	foreach ($_POST['who'] as $id => $mg)
2732
		$_POST['who'][$id] = in_array((int) $mg, $groups) ? (int) $mg : 0;
2733
2734
	// Get the topic subject and censor it.
2735
	$request = $smcFunc['db_query']('', '
2736
		SELECT m.id_msg, m.subject, m.body
2737
		FROM {db_prefix}topics AS t
2738
			INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)
2739
		WHERE t.id_topic = {int:current_topic}',
2740
		array(
2741
			'current_topic' => $topic,
2742
		)
2743
	);
2744
	list ($id_msg, $context['topic_subject'], $message) = $smcFunc['db_fetch_row']($request);
2745
	$smcFunc['db_free_result']($request);
2746
2747
	censorText($context['topic_subject']);
2748
	censorText($message);
2749
2750
	$message = trim(un_htmlspecialchars(strip_tags(strtr(parse_bbc($message, false, $id_msg), array('<br>' => "\n", '</div>' => "\n", '</li>' => "\n", '&#91;' => '[', '&#93;' => ']')))));
2751
2752
	// We need this in order to be able send emails.
2753
	require_once($sourcedir . '/Subs-Post.php');
2754
2755
	// Select the email addresses for this batch.
2756
	$request = $smcFunc['db_query']('', '
2757
		SELECT mem.id_member, mem.email_address, mem.lngfile
2758
		FROM {db_prefix}members AS mem
2759
		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)
2760
			AND mem.is_activated = {int:is_activated}
2761
			AND mem.id_member > {int:start}
2762
		ORDER BY mem.id_member
2763
		LIMIT {int:chunk_size}',
2764
		array(
2765
			'group_list' => $_POST['who'],
2766
			'is_activated' => 1,
2767
			'start' => $context['start'],
2768
			'additional_group_list' => implode(', mem.additional_groups) != 0 OR FIND_IN_SET(', $_POST['who']),
2769
			// @todo Might need an interface?
2770
			'chunk_size' => 500,
2771
		)
2772
	);
2773
2774
	// All members have received a mail. Go to the next screen.
2775
	if ($smcFunc['db_num_rows']($request) == 0)
2776
	{
2777
		logAction('announce_topic', array('topic' => $topic), 'user');
2778
		if (!empty($_REQUEST['move']) && allowedTo('move_any'))
2779
			redirectexit('action=movetopic;topic=' . $topic . '.0' . (empty($_REQUEST['goback']) ? '' : ';goback'));
2780
		elseif (!empty($_REQUEST['goback']))
2781
			redirectexit('topic=' . $topic . '.new;boardseen#new', isBrowser('ie'));
2782
		else
2783
			redirectexit('board=' . $board . '.0');
2784
	}
2785
2786
	$announcements = array();
2787
	// Loop through all members that'll receive an announcement in this batch.
2788
	$rows = array();
2789
	while ($row = $smcFunc['db_fetch_assoc']($request))
2790
	{
2791
		$rows[$row['id_member']] = $row;
2792
	}
2793
	$smcFunc['db_free_result']($request);
2794
2795
	// Load their alert preferences
2796
	require_once($sourcedir . '/Subs-Notify.php');
2797
	$prefs = getNotifyPrefs(array_keys($rows), 'announcements', true);
2798
2799
	foreach ($rows as $row)
2800
	{
2801
		$context['start'] = $row['id_member'];
2802
		// Force them to have it?
2803
		if (empty($prefs[$row['id_member']]['announcements']))
2804
			continue;
2805
2806
		$cur_language = empty($row['lngfile']) || empty($modSettings['userLanguage']) ? $language : $row['lngfile'];
2807
2808
		// If the language wasn't defined yet, load it and compose a notification message.
2809
		if (!isset($announcements[$cur_language]))
2810
		{
2811
			$replacements = array(
2812
				'TOPICSUBJECT' => $context['topic_subject'],
2813
				'MESSAGE' => $message,
2814
				'TOPICLINK' => $scripturl . '?topic=' . $topic . '.0',
2815
				'UNSUBSCRIBELINK' => $scripturl . '?action=notifyannouncements;u={UNSUBSCRIBE_ID};token={UNSUBSCRIBE_TOKEN}',
2816
			);
2817
2818
			$emaildata = loadEmailTemplate('new_announcement', $replacements, $cur_language);
2819
2820
			$announcements[$cur_language] = array(
2821
				'subject' => $emaildata['subject'],
2822
				'body' => $emaildata['body'],
2823
				'is_html' => $emaildata['is_html'],
2824
				'recipients' => array(),
2825
			);
2826
		}
2827
2828
		$announcements[$cur_language]['recipients'][$row['id_member']] = $row['email_address'];
2829
	}
2830
2831
	// For each language send a different mail - low priority...
2832
	foreach ($announcements as $lang => $mail)
2833
	{
2834
		foreach ($mail['recipients'] as $member_id => $member_email)
2835
		{
2836
			$token = createUnsubscribeToken($member_id, $member_email, 'announcements');
2837
2838
			$body = str_replace(array('{UNSUBSCRIBE_ID}', '{UNSUBSCRIBE_TOKEN}'), array($member_id, $token), $mail['body']);
2839
2840
			sendmail($member_email, $mail['subject'], $body, null, null, false, 5);
2841
		}
2842
2843
	}
2844
2845
	$context['percentage_done'] = round(100 * $context['start'] / $modSettings['latestMember'], 1);
2846
2847
	$context['move'] = empty($_REQUEST['move']) ? 0 : 1;
2848
	$context['go_back'] = empty($_REQUEST['goback']) ? 0 : 1;
2849
	$context['membergroups'] = implode(',', $_POST['who']);
2850
	$context['sub_template'] = 'announcement_send';
2851
2852
	// Go back to the correct language for the user ;).
2853
	if (!empty($modSettings['userLanguage']))
2854
		loadLanguage('Post');
2855
}
2856
2857
/**
2858
 * Get the topic for display purposes.
2859
 *
2860
 * gets a summary of the most recent posts in a topic.
2861
 * depends on the topicSummaryPosts setting.
2862
 * if you are editing a post, only shows posts previous to that post.
2863
 */
2864
function getTopic()
2865
{
2866
	global $topic, $modSettings, $context, $smcFunc, $counter, $options;
2867
2868
	if (isset($_REQUEST['xml']))
2869
		$limit = '
2870
		LIMIT ' . (empty($context['new_replies']) ? '0' : $context['new_replies']);
2871
	else
2872
		$limit = empty($modSettings['topicSummaryPosts']) ? '' : '
2873
		LIMIT ' . (int) $modSettings['topicSummaryPosts'];
2874
2875
	// If you're modifying, get only those posts before the current one. (otherwise get all.)
2876
	$request = $smcFunc['db_query']('', '
2877
		SELECT
2878
			COALESCE(mem.real_name, m.poster_name) AS poster_name, m.poster_time,
2879
			m.body, m.smileys_enabled, m.id_msg, m.id_member
2880
		FROM {db_prefix}messages AS m
2881
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
2882
		WHERE m.id_topic = {int:current_topic}' . (isset($_REQUEST['msg']) ? '
2883
			AND m.id_msg < {int:id_msg}' : '') . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : '
2884
			AND m.approved = {int:approved}') . '
2885
		ORDER BY m.id_msg DESC' . $limit,
2886
		array(
2887
			'current_topic' => $topic,
2888
			'id_msg' => isset($_REQUEST['msg']) ? (int) $_REQUEST['msg'] : 0,
2889
			'approved' => 1,
2890
		)
2891
	);
2892
	$context['previous_posts'] = array();
2893
	while ($row = $smcFunc['db_fetch_assoc']($request))
2894
	{
2895
		// Censor, BBC, ...
2896
		censorText($row['body']);
2897
		$row['body'] = parse_bbc($row['body'], $row['smileys_enabled'], $row['id_msg']);
2898
2899
	 	call_integration_hook('integrate_getTopic_previous_post', array(&$row));
2900
2901
		// ...and store.
2902
		$context['previous_posts'][] = array(
2903
			'counter' => $counter++,
2904
			'poster' => $row['poster_name'],
2905
			'message' => $row['body'],
2906
			'time' => timeformat($row['poster_time']),
2907
			'timestamp' => $row['poster_time'],
2908
			'id' => $row['id_msg'],
2909
			'is_new' => !empty($context['new_replies']),
2910
			'is_ignored' => !empty($modSettings['enable_buddylist']) && !empty($options['posts_apply_ignore_list']) && in_array($row['id_member'], $context['user']['ignoreusers']),
2911
		);
2912
2913
		if (!empty($context['new_replies']))
2914
			$context['new_replies']--;
2915
	}
2916
	$smcFunc['db_free_result']($request);
2917
}
2918
2919
/**
2920
 * Loads a post an inserts it into the current editing text box.
2921
 * uses the Post language file.
2922
 * uses special (sadly browser dependent) javascript to parse entities for internationalization reasons.
2923
 * accessed with ?action=quotefast.
2924
 */
2925
function QuoteFast()
2926
{
2927
	global $modSettings, $user_info, $context;
2928
	global $sourcedir, $smcFunc;
2929
2930
	loadLanguage('Post');
2931
	if (!isset($_REQUEST['xml']))
2932
		loadTemplate('Post');
2933
2934
	include_once($sourcedir . '/Subs-Post.php');
2935
2936
	$moderate_boards = boardsAllowedTo('moderate_board');
2937
2938
	$request = $smcFunc['db_query']('', '
2939
		SELECT COALESCE(mem.real_name, m.poster_name) AS poster_name, m.poster_time, m.body, m.id_topic, m.subject,
2940
			m.id_board, m.id_member, m.approved, m.modified_time, m.modified_name, m.modified_reason
2941
		FROM {db_prefix}messages AS m
2942
			INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic)
2943
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
2944
		WHERE {query_see_message_board}
2945
			AND m.id_msg = {int:id_msg}' . (isset($_REQUEST['modify']) || (!empty($moderate_boards) && $moderate_boards[0] == 0) ? '' : '
2946
			AND (t.locked = {int:not_locked}' . (empty($moderate_boards) ? '' : ' OR m.id_board IN ({array_int:moderation_board_list})') . ')') . '
2947
		LIMIT 1',
2948
		array(
2949
			'current_member' => $user_info['id'],
2950
			'moderation_board_list' => $moderate_boards,
2951
			'id_msg' => (int) $_REQUEST['quote'],
2952
			'not_locked' => 0,
2953
		)
2954
	);
2955
	$context['close_window'] = $smcFunc['db_num_rows']($request) == 0;
2956
	$row = $smcFunc['db_fetch_assoc']($request);
2957
	$smcFunc['db_free_result']($request);
2958
2959
	$context['sub_template'] = 'quotefast';
2960
	if (!empty($row))
2961
		$can_view_post = $row['approved'] || ($row['id_member'] != 0 && $row['id_member'] == $user_info['id']) || allowedTo('approve_posts', $row['id_board']);
2962
2963
	if (!empty($can_view_post))
2964
	{
2965
		// Remove special formatting we don't want anymore.
2966
		$row['body'] = un_preparsecode($row['body']);
2967
2968
		// Censor the message!
2969
		censorText($row['body']);
2970
2971
		// Want to modify a single message by double clicking it?
2972
		if (isset($_REQUEST['modify']))
2973
		{
2974
			censorText($row['subject']);
2975
2976
			$context['sub_template'] = 'modifyfast';
2977
			$context['message'] = array(
2978
				'id' => $_REQUEST['quote'],
2979
				'body' => $row['body'],
2980
				'subject' => addcslashes($row['subject'], '"'),
2981
				'reason' => array(
2982
					'name' => $row['modified_name'],
2983
					'text' => $row['modified_reason'],
2984
					'time' => $row['modified_time'],
2985
				),
2986
			);
2987
2988
			return;
2989
		}
2990
2991
		// Remove any nested quotes.
2992
		if (!empty($modSettings['removeNestedQuotes']))
2993
			$row['body'] = preg_replace(array('~\n?\[quote.*?\].+?\[/quote\]\n?~is', '~^\n~', '~\[/quote\]~'), '', $row['body']);
2994
2995
		$lb = "\n";
2996
2997
		// Add a quote string on the front and end.
2998
		$context['quote']['xml'] = '[quote author=' . $row['poster_name'] . ' link=msg=' . (int) $_REQUEST['quote'] . ' date=' . $row['poster_time'] . ']' . $lb . $row['body'] . $lb . '[/quote]';
2999
		$context['quote']['text'] = strtr(un_htmlspecialchars($context['quote']['xml']), array('\'' => '\\\'', '\\' => '\\\\', "\n" => '\\n', '</script>' => '</\' + \'script>'));
3000
		$context['quote']['xml'] = strtr($context['quote']['xml'], array('&nbsp;' => '&#160;', '<' => '&lt;', '>' => '&gt;'));
3001
3002
		$context['quote']['mozilla'] = strtr($smcFunc['htmlspecialchars']($context['quote']['text']), array('&quot;' => '"'));
3003
	}
3004
	//@todo Needs a nicer interface.
3005
	// In case our message has been removed in the meantime.
3006
	elseif (isset($_REQUEST['modify']))
3007
	{
3008
		$context['sub_template'] = 'modifyfast';
3009
		$context['message'] = array(
3010
			'id' => 0,
3011
			'body' => '',
3012
			'subject' => '',
3013
			'reason' => array(
3014
				'name' => '',
3015
				'text' => '',
3016
				'time' => '',
3017
			),
3018
		);
3019
	}
3020
	else
3021
		$context['quote'] = array(
3022
			'xml' => '',
3023
			'mozilla' => '',
3024
			'text' => '',
3025
		);
3026
}
3027
3028
/**
3029
 * Used to edit the body or subject of a message inline
3030
 * called from action=jsmodify from script and topic js
3031
 */
3032
function JavaScriptModify()
3033
{
3034
	global $sourcedir, $modSettings, $board, $topic, $txt;
3035
	global $user_info, $context, $smcFunc, $language, $board_info;
3036
3037
	// We have to have a topic!
3038
	if (empty($topic))
3039
		obExit(false);
3040
3041
	checkSession('get');
3042
	require_once($sourcedir . '/Subs-Post.php');
3043
3044
	// Assume the first message if no message ID was given.
3045
	$request = $smcFunc['db_query']('', '
3046
		SELECT
3047
			t.locked, t.num_replies, t.id_member_started, t.id_first_msg,
3048
			m.id_msg, m.id_member, m.poster_time, m.subject, m.smileys_enabled, m.body, m.icon,
3049
			m.modified_time, m.modified_name, m.modified_reason, m.approved,
3050
			m.poster_name, m.poster_email
3051
		FROM {db_prefix}messages AS m
3052
			INNER JOIN {db_prefix}topics AS t ON (t.id_topic = {int:current_topic})
3053
		WHERE m.id_msg = {raw:id_msg}
3054
			AND m.id_topic = {int:current_topic}' . (allowedTo('modify_any') || allowedTo('approve_posts') ? '' : (!$modSettings['postmod_active'] ? '
3055
			AND (m.id_member != {int:guest_id} AND m.id_member = {int:current_member})' : '
3056
			AND (m.approved = {int:is_approved} OR (m.id_member != {int:guest_id} AND m.id_member = {int:current_member}))')),
3057
		array(
3058
			'current_member' => $user_info['id'],
3059
			'current_topic' => $topic,
3060
			'id_msg' => empty($_REQUEST['msg']) ? 't.id_first_msg' : (int) $_REQUEST['msg'],
3061
			'is_approved' => 1,
3062
			'guest_id' => 0,
3063
		)
3064
	);
3065
	if ($smcFunc['db_num_rows']($request) == 0)
3066
		fatal_lang_error('no_board', false);
3067
	$row = $smcFunc['db_fetch_assoc']($request);
3068
	$smcFunc['db_free_result']($request);
3069
3070
	// Change either body or subject requires permissions to modify messages.
3071
	if (isset($_POST['message']) || isset($_POST['subject']) || isset($_REQUEST['icon']))
3072
	{
3073
		if (!empty($row['locked']))
3074
			isAllowedTo('moderate_board');
3075
3076
		if ($row['id_member'] == $user_info['id'] && !allowedTo('modify_any'))
3077
		{
3078
			if ((!$modSettings['postmod_active'] || $row['approved']) && !empty($modSettings['edit_disable_time']) && $row['poster_time'] + ($modSettings['edit_disable_time'] + 5) * 60 < time())
3079
				fatal_lang_error('modify_post_time_passed', false);
3080
			elseif ($row['id_member_started'] == $user_info['id'] && !allowedTo('modify_own'))
3081
				isAllowedTo('modify_replies');
3082
			else
3083
				isAllowedTo('modify_own');
3084
		}
3085
		// Otherwise, they're locked out; someone who can modify the replies is needed.
3086
		elseif ($row['id_member_started'] == $user_info['id'] && !allowedTo('modify_any'))
3087
			isAllowedTo('modify_replies');
3088
		else
3089
			isAllowedTo('modify_any');
3090
3091
		// Only log this action if it wasn't your message.
3092
		$moderationAction = $row['id_member'] != $user_info['id'];
3093
	}
3094
3095
	$post_errors = array();
3096
	if (isset($_POST['subject']) && $smcFunc['htmltrim']($smcFunc['htmlspecialchars']($_POST['subject'])) !== '')
3097
	{
3098
		$_POST['subject'] = strtr($smcFunc['htmlspecialchars']($_POST['subject']), array("\r" => '', "\n" => '', "\t" => ''));
3099
3100
		// Maximum number of characters.
3101
		if ($smcFunc['strlen']($_POST['subject']) > 100)
3102
			$_POST['subject'] = $smcFunc['substr']($_POST['subject'], 0, 100);
3103
	}
3104
	elseif (isset($_POST['subject']))
3105
	{
3106
		$post_errors[] = 'no_subject';
3107
		unset($_POST['subject']);
3108
	}
3109
3110
	if (isset($_POST['message']))
3111
	{
3112
		if ($smcFunc['htmltrim']($smcFunc['htmlspecialchars']($_POST['message'])) === '')
3113
		{
3114
			$post_errors[] = 'no_message';
3115
			unset($_POST['message']);
3116
		}
3117
		elseif (!empty($modSettings['max_messageLength']) && $smcFunc['strlen']($_POST['message']) > $modSettings['max_messageLength'])
3118
		{
3119
			$post_errors[] = 'long_message';
3120
			unset($_POST['message']);
3121
		}
3122
		else
3123
		{
3124
			$_POST['message'] = $smcFunc['htmlspecialchars']($_POST['message'], ENT_QUOTES);
3125
3126
			preparsecode($_POST['message']);
3127
3128
			if ($smcFunc['htmltrim'](strip_tags(parse_bbc($_POST['message'], false), implode('', $context['allowed_html_tags']))) === '')
3129
			{
3130
				$post_errors[] = 'no_message';
3131
				unset($_POST['message']);
3132
			}
3133
		}
3134
	}
3135
3136
 	call_integration_hook('integrate_post_JavascriptModify', array(&$post_errors, $row));
3137
3138
	if (isset($_POST['lock']))
3139
	{
3140
		if (!allowedTo(array('lock_any', 'lock_own')) || (!allowedTo('lock_any') && $user_info['id'] != $row['id_member']))
3141
			unset($_POST['lock']);
3142
		elseif (!allowedTo('lock_any'))
3143
		{
3144
			if ($row['locked'] == 1)
3145
				unset($_POST['lock']);
3146
			else
3147
				$_POST['lock'] = empty($_POST['lock']) ? 0 : 2;
3148
		}
3149
		elseif (!empty($row['locked']) && !empty($_POST['lock']) || $_POST['lock'] == $row['locked'])
3150
			unset($_POST['lock']);
3151
		else
3152
			$_POST['lock'] = empty($_POST['lock']) ? 0 : 1;
3153
	}
3154
3155
	if (isset($_POST['sticky']) && !allowedTo('make_sticky'))
3156
		unset($_POST['sticky']);
3157
3158
	if (isset($_POST['modify_reason']))
3159
	{
3160
		$_POST['modify_reason'] = strtr($smcFunc['htmlspecialchars']($_POST['modify_reason']), array("\r" => '', "\n" => '', "\t" => ''));
3161
3162
		// Maximum number of characters.
3163
		if ($smcFunc['strlen']($_POST['modify_reason']) > 100)
3164
			$_POST['modify_reason'] = $smcFunc['substr']($_POST['modify_reason'], 0, 100);
3165
	}
3166
3167
	if (empty($post_errors))
3168
	{
3169
		$msgOptions = array(
3170
			'id' => $row['id_msg'],
3171
			'subject' => isset($_POST['subject']) ? $_POST['subject'] : null,
3172
			'body' => isset($_POST['message']) ? $_POST['message'] : null,
3173
			'icon' => isset($_REQUEST['icon']) ? preg_replace('~[\./\\\\*\':"<>]~', '', $_REQUEST['icon']) : null,
3174
			'approved' => (isset($row['approved']) ? $row['approved'] : null),
3175
		);
3176
		$topicOptions = array(
3177
			'id' => $topic,
3178
			'board' => $board,
3179
			'lock_mode' => isset($_POST['lock']) ? (int) $_POST['lock'] : null,
3180
			'sticky_mode' => isset($_POST['sticky']) ? (int) $_POST['sticky'] : null,
3181
			'mark_as_read' => true,
3182
		);
3183
		$posterOptions = array(
3184
			'id' => $user_info['id'],
3185
			'name' => $row['poster_name'],
3186
			'email' => $row['poster_email'],
3187
			'update_post_count' => !$user_info['is_guest'] && !isset($_REQUEST['msg']) && $board_info['posts_count'],
3188
		);
3189
3190
		// Only consider marking as editing if they have edited the subject, modify reason, message or icon.
3191
		if ((isset($_POST['subject']) && $_POST['subject'] != $row['subject']) || (isset($_POST['message']) && $_POST['message'] != $row['body']) || (isset($_REQUEST['icon']) && $_REQUEST['icon'] != $row['icon']) || (isset($_POST['modify_reason']) && $_POST['modify_reason'] != $row['modified_reason']))
3192
		{
3193
			// And even then only if the time has passed...
3194
			if (time() - $row['poster_time'] > $modSettings['edit_wait_time'] || $user_info['id'] != $row['id_member'])
3195
			{
3196
				$msgOptions['modify_time'] = time();
3197
				$msgOptions['modify_name'] = $user_info['name'];
3198
				$msgOptions['modify_reason'] = (isset($_POST['modify_reason']) ? $_POST['modify_reason'] : '');
3199
			}
3200
		}
3201
		// If nothing was changed there's no need to add an entry to the moderation log.
3202
		else
3203
			$moderationAction = false;
3204
3205
		modifyPost($msgOptions, $topicOptions, $posterOptions);
3206
3207
		// If we didn't change anything this time but had before put back the old info.
3208
		if (!isset($msgOptions['modify_time']) && !empty($row['modified_time']))
3209
		{
3210
			$msgOptions['modify_time'] = $row['modified_time'];
3211
			$msgOptions['modify_name'] = $row['modified_name'];
3212
			$msgOptions['modify_reason'] = $row['modified_reason'];
3213
		}
3214
3215
		// Changing the first subject updates other subjects to 'Re: new_subject'.
3216
		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'))))
3217
		{
3218
			// Get the proper (default language) response prefix first.
3219
			if (!isset($context['response_prefix']) && !($context['response_prefix'] = cache_get_data('response_prefix')))
3220
			{
3221
				if ($language === $user_info['language'])
3222
					$context['response_prefix'] = $txt['response_prefix'];
3223
				else
3224
				{
3225
					loadLanguage('index', $language, false);
3226
					$context['response_prefix'] = $txt['response_prefix'];
3227
					loadLanguage('index');
3228
				}
3229
				cache_put_data('response_prefix', $context['response_prefix'], 600);
3230
			}
3231
3232
			$smcFunc['db_query']('', '
3233
				UPDATE {db_prefix}messages
3234
				SET subject = {string:subject}
3235
				WHERE id_topic = {int:current_topic}
3236
					AND id_msg != {int:id_first_msg}',
3237
				array(
3238
					'current_topic' => $topic,
3239
					'id_first_msg' => $row['id_first_msg'],
3240
					'subject' => $context['response_prefix'] . $_POST['subject'],
3241
				)
3242
			);
3243
		}
3244
3245
		if (!empty($moderationAction))
3246
			logAction('modify', array('topic' => $topic, 'message' => $row['id_msg'], 'member' => $row['id_member'], 'board' => $board));
3247
	}
3248
3249
	if (isset($_REQUEST['xml']))
3250
	{
3251
		$context['sub_template'] = 'modifydone';
3252
		if (empty($post_errors) && isset($msgOptions['subject']) && isset($msgOptions['body']))
3253
		{
3254
			$context['message'] = array(
3255
				'id' => $row['id_msg'],
3256
				'modified' => array(
3257
					'time' => isset($msgOptions['modify_time']) ? timeformat($msgOptions['modify_time']) : '',
3258
					'timestamp' => isset($msgOptions['modify_time']) ? $msgOptions['modify_time'] : 0,
3259
					'name' => isset($msgOptions['modify_time']) ? $msgOptions['modify_name'] : '',
3260
					'reason' => isset($msgOptions['modify_reason']) ? $msgOptions['modify_reason'] : '',
3261
				),
3262
				'subject' => $msgOptions['subject'],
3263
				'first_in_topic' => $row['id_msg'] == $row['id_first_msg'],
3264
				'body' => strtr($msgOptions['body'], array(']]>' => ']]]]><![CDATA[>')),
3265
			);
3266
3267
			censorText($context['message']['subject']);
3268
			censorText($context['message']['body']);
3269
3270
			$context['message']['body'] = parse_bbc($context['message']['body'], $row['smileys_enabled'], $row['id_msg']);
3271
		}
3272
		// Topic?
3273
		elseif (empty($post_errors))
3274
		{
3275
			$context['sub_template'] = 'modifytopicdone';
3276
			$context['message'] = array(
3277
				'id' => $row['id_msg'],
3278
				'modified' => array(
3279
					'time' => isset($msgOptions['modify_time']) ? timeformat($msgOptions['modify_time']) : '',
3280
					'timestamp' => isset($msgOptions['modify_time']) ? $msgOptions['modify_time'] : 0,
3281
					'name' => isset($msgOptions['modify_time']) ? $msgOptions['modify_name'] : '',
3282
				),
3283
				'subject' => isset($msgOptions['subject']) ? $msgOptions['subject'] : '',
3284
			);
3285
3286
			censorText($context['message']['subject']);
3287
		}
3288
		else
3289
		{
3290
			$context['message'] = array(
3291
				'id' => $row['id_msg'],
3292
				'errors' => array(),
3293
				'error_in_subject' => in_array('no_subject', $post_errors),
3294
				'error_in_body' => in_array('no_message', $post_errors) || in_array('long_message', $post_errors),
3295
			);
3296
3297
			loadLanguage('Errors');
3298
			foreach ($post_errors as $post_error)
3299
			{
3300
				if ($post_error == 'long_message')
3301
					$context['message']['errors'][] = sprintf($txt['error_' . $post_error], $modSettings['max_messageLength']);
3302
				else
3303
					$context['message']['errors'][] = $txt['error_' . $post_error];
3304
			}
3305
		}
3306
3307
		// Allow mods to do something with $context before we return.
3308
		call_integration_hook('integrate_jsmodify_xml');
3309
	}
3310
	else
3311
		obExit(false);
3312
}
3313
3314
?>