Issues (1014)

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