<commit_before><?php

/**
 * @name      ElkArte Forum
 * @copyright ElkArte Forum contributors
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause
 *
 * This software is a derived product, based on:
 *
 * Simple Machines Forum (SMF)
 * copyright:	2011 Simple Machines (http://www.simplemachines.org)
 * license:  	BSD, See included LICENSE.TXT for terms and conditions.
 *
 * @version 1.0 Beta
 *
 * The job of this file is to handle everything related to posting replies,
 * new topics, quotes, and modifications to existing posts.  It also handles
 * quoting posts by way of javascript.
 *
 */

if (!defined('ELK'))
	die('No access...');

/**
 * Post Controller
 */
class Post_Controller extends Action_Controller
{
	/**
	 * Dispatch to the right action method for the request.
	 *
	 * @see Action_Controller::action_index()
	 */
	public function action_index()
	{
		// figure out the right action to do.
		// hint: I'm post controller. :P
		$this->action_post();
	}

	/**
	 * Handles showing the post screen, loading the post to be modified, and loading any post quoted.
	 *
	 * - additionally handles previews of posts.
	 * - @uses the Post template and language file, main sub template.
	 * - allows wireless access using the protocol_post sub template.
	 * - requires different permissions depending on the actions, but most notably post_new, post_reply_own, and post_reply_any.
	 * - shows options for the editing and posting of calendar events and attachments, as well as the posting of polls.
	 * - accessed from ?action=post.
	 *
	 *  @param array $post_errors holds any errors found tyring to post
	 */
	function action_post()
	{
		global $txt, $scripturl, $topic, $modSettings, $board;
		global $user_info, $context, $options, $language;

		$db = database();

		loadLanguage('Post');

		// You can't reply with a poll... hacker.
		if (isset($_REQUEST['poll']) && !empty($topic) && !isset($_REQUEST['msg']))
			unset($_REQUEST['poll']);

		$post_errors = Error_Context::context('post', 1);
		$attach_errors = attachment_Error_Context::context('attachment', 1);
		$attach_errors->activate();
		$first_subject = '';

		// Posting an event?
		$context['make_event'] = isset($_REQUEST['calendar']);
		$context['robot_no_index'] = true;
		$template_layers = Template_Layers::getInstance();
		$template_layers->add('postarea');

		// You must be posting to *some* board.
		if (empty($board) && !$context['make_event'])
			fatal_lang_error('no_board', false);

		if ($context['make_event'])
			$template_layers->add('make_event');

		// All those wonderful modifiers and attachments
		$template_layers->add('additional_options', 200);

		require_once(SUBSDIR . '/Post.subs.php');
		require_once(SUBSDIR . '/Messages.subs.php');

		if (isset($_REQUEST['xml']))
		{
			$context['sub_template'] = 'post';

			// Just in case of an earlier error...
			$context['preview_message'] = '';
			$context['preview_subject'] = '';
		}

		if (!empty($modSettings['mentions_enabled']) && !empty($_REQUEST['uid']))
			$context['member_ids'] = array_unique(array_map('intval', $_REQUEST['uid']));

		// No message is complete without a topic.
		if (empty($topic) && !empty($_REQUEST['msg']))
		{
			$topic = associatedTopic((int) $_REQUEST['msg']);
			if (empty($topic))
				unset($_REQUEST['msg'], $_POST['msg'], $_GET['msg']);
		}

		// Check if it's locked. It isn't locked if no topic is specified.
		if (!empty($topic))
		{
			$request = $db->query('', '
				SELECT
					t.locked, IFNULL(ln.id_topic, 0) AS notify, t.is_sticky, t.id_poll, t.id_last_msg, mf.id_member,
					t.id_first_msg, mf.subject,
					CASE WHEN ml.poster_time > ml.modified_time THEN ml.poster_time ELSE ml.modified_time END AS last_post_time
				FROM {db_prefix}topics AS t
					LEFT JOIN {db_prefix}log_notify AS ln ON (ln.id_topic = t.id_topic AND ln.id_member = {int:current_member})
					LEFT JOIN {db_prefix}messages AS mf ON (mf.id_msg = t.id_first_msg)
					LEFT JOIN {db_prefix}messages AS ml ON (ml.id_msg = t.id_last_msg)
				WHERE t.id_topic = {int:current_topic}
				LIMIT 1',
				array(
					'current_member' => $user_info['id'],
					'current_topic' => $topic,
				)
			);
			list ($locked, $context['notify'], $sticky, $pollID, $context['topic_last_message'], $id_member_poster, $id_first_msg, $first_subject, $lastPostTime) = $db->fetch_row($request);
			$db->free_result($request);

			// If this topic already has a poll, they sure can't add another.
			if (isset($_REQUEST['poll']) && $pollID > 0)
				unset($_REQUEST['poll']);

			if (empty($_REQUEST['msg']))
			{
				if ($user_info['is_guest'] && !allowedTo('post_reply_any') && (!$modSettings['postmod_active'] || !allowedTo('post_unapproved_replies_any')))
					is_not_guest();

				// By default the reply will be approved...
				$context['becomes_approved'] = true;
				if ($id_member_poster != $user_info['id'])
				{
					if ($modSettings['postmod_active'] && allowedTo('post_unapproved_replies_any') && !allowedTo('post_reply_any'))
						$context['becomes_approved'] = false;
					else
						isAllowedTo('post_reply_any');
				}
				elseif (!allowedTo('post_reply_any'))
				{
					if ($modSettings['postmod_active'] && allowedTo('post_unapproved_replies_own') && !allowedTo('post_reply_own'))
						$context['becomes_approved'] = false;
					else
						isAllowedTo('post_reply_own');
				}
			}
			else
				$context['becomes_approved'] = true;

			$context['can_lock'] = allowedTo('lock_any') || ($user_info['id'] == $id_member_poster && allowedTo('lock_own'));
			$context['can_sticky'] = allowedTo('make_sticky') && !empty($modSettings['enableStickyTopics']);

			$context['notify'] = !empty($context['notify']);
			$context['sticky'] = isset($_REQUEST['sticky']) ? !empty($_REQUEST['sticky']) : $sticky;
		}
		else
		{
			$context['becomes_approved'] = true;
			if ((!$context['make_event'] || !empty($board)))
			{
				if ($modSettings['postmod_active'] && !allowedTo('post_new') && allowedTo('post_unapproved_topics'))
					$context['becomes_approved'] = false;
				else
					isAllowedTo('post_new');
			}

			$locked = 0;

			// @todo These won't work if you're making an event.
			$context['can_lock'] = allowedTo(array('lock_any', 'lock_own'));
			$context['can_sticky'] = allowedTo('make_sticky') && !empty($modSettings['enableStickyTopics']);

			$context['notify'] = !empty($context['notify']);
			$context['sticky'] = !empty($_REQUEST['sticky']);
		}

		// @todo These won't work if you're posting an event!
		$context['can_notify'] = allowedTo('mark_any_notify');
		$context['can_move'] = allowedTo('move_any');
		$context['move'] = !empty($_REQUEST['move']);
		$context['announce'] = !empty($_REQUEST['announce']);

		// You can only announce topics that will get approved...
		$context['can_announce'] = allowedTo('announce_topic') && $context['becomes_approved'];
		$context['locked'] = !empty($locked) || !empty($_REQUEST['lock']);
		$context['can_quote'] = empty($modSettings['disabledBBC']) || !in_array('quote', explode(',', $modSettings['disabledBBC']));

		// Generally don't show the approval box... (Assume we want things approved)
		$context['show_approval'] = allowedTo('approve_posts') && $context['becomes_approved'] ? 2 : (allowedTo('approve_posts') ? 1 : 0);

		// An array to hold all the attachments for this topic.
		$context['current_attachments'] = array();

		// Don't allow a post if it's locked and you aren't all powerful.
		if ($locked && !allowedTo('moderate_board'))
			fatal_lang_error('topic_locked', false);

		// Check the users permissions - is the user allowed to add or post a poll?
		if (isset($_REQUEST['poll']) && $modSettings['pollMode'] == '1')
		{
			// New topic, new poll.
			if (empty($topic))
				isAllowedTo('poll_post');
			// This is an old topic - but it is yours!  Can you add to it?
			elseif ($user_info['id'] == $id_member_poster && !allowedTo('poll_add_any'))
				isAllowedTo('poll_add_own');
			// If you're not the owner, can you add to any poll?
			else
				isAllowedTo('poll_add_any');
			$context['can_moderate_poll'] = true;

			require_once(SUBSDIR . '/Members.subs.php');
			$allowedVoteGroups = groupsAllowedTo('poll_vote', $board);

			// Set up the poll options.
			$context['poll'] = array(
				'max_votes' => empty($_POST['poll_max_votes']) ? '1' : max(1, $_POST['poll_max_votes']),
				'hide_results' => empty($_POST['poll_hide']) ? 0 : $_POST['poll_hide'],
				'expiration' => !isset($_POST['poll_expire']) ? '' : $_POST['poll_expire'],
				'change_vote' => isset($_POST['poll_change_vote']),
				'guest_vote' => isset($_POST['poll_guest_vote']),
				'guest_vote_allowed' => in_array(-1, $allowedVoteGroups['allowed']),
			);

			// Make all five poll choices empty.
			$context['choices'] = array(
				array('id' => 0, 'number' => 1, 'label' => '', 'is_last' => false),
				array('id' => 1, 'number' => 2, 'label' => '', 'is_last' => false),
				array('id' => 2, 'number' => 3, 'label' => '', 'is_last' => false),
				array('id' => 3, 'number' => 4, 'label' => '', 'is_last' => false),
				array('id' => 4, 'number' => 5, 'label' => '', 'is_last' => true)
			);
			$context['last_choice_id'] = 4;
		}

		if ($context['make_event'])
		{
			// They might want to pick a board.
			if (!isset($context['current_board']))
				$context['current_board'] = 0;

			// Start loading up the event info.
			$context['event'] = array();
			$context['event']['title'] = isset($_REQUEST['evtitle']) ? htmlspecialchars(stripslashes($_REQUEST['evtitle']), ENT_COMPAT, 'UTF-8') : '';
			$context['event']['id'] = isset($_REQUEST['eventid']) ? (int) $_REQUEST['eventid'] : -1;
			$context['event']['new'] = $context['event']['id'] == -1;

			// Permissions check!
			isAllowedTo('calendar_post');

			// Editing an event?  (but NOT previewing!?)
			if (empty($context['event']['new']) && !isset($_REQUEST['subject']))
			{
				// If the user doesn't have permission to edit the post in this topic, redirect them.
				if ((empty($id_member_poster) || $id_member_poster != $user_info['id'] || !allowedTo('modify_own')) && !allowedTo('modify_any'))
				{
					require_once(CONTROLLERDIR . '/Calendar.controller.php');
					$controller = new Calendar_Controller();
					return $controller->action_post();
				}

				// Get the current event information.
				require_once(SUBSDIR . '/Calendar.subs.php');
				$event_info = getEventProperties($context['event']['id']);

				// Make sure the user is allowed to edit this event.
				if ($event_info['member'] != $user_info['id'])
					isAllowedTo('calendar_edit_any');
				elseif (!allowedTo('calendar_edit_any'))
					isAllowedTo('calendar_edit_own');

				$context['event']['month'] = $event_info['month'];
				$context['event']['day'] = $event_info['day'];
				$context['event']['year'] = $event_info['year'];
				$context['event']['title'] = $event_info['title'];
				$context['event']['span'] = $event_info['span'];
			}
			else
			{
				// Posting a new event? (or preview...)
				$today = getdate();

				// You must have a month and year specified!
				if (!isset($_REQUEST['month']))
					$_REQUEST['month'] = $today['mon'];

				if (!isset($_REQUEST['year']))
					$_REQUEST['year'] = $today['year'];

				$context['event']['month'] = (int) $_REQUEST['month'];
				$context['event']['year'] = (int) $_REQUEST['year'];
				$context['event']['day'] = isset($_REQUEST['day']) ? $_REQUEST['day'] : ($_REQUEST['month'] == $today['mon'] ? $today['mday'] : 0);
				$context['event']['span'] = isset($_REQUEST['span']) ? $_REQUEST['span'] : 1;

				// Make sure the year and month are in the valid range.
				if ($context['event']['month'] < 1 || $context['event']['month'] > 12)
					fatal_lang_error('invalid_month', false);

				if ($context['event']['year'] < $modSettings['cal_minyear'] || $context['event']['year'] > $modSettings['cal_maxyear'])
					fatal_lang_error('invalid_year', false);

				// Get a list of boards they can post in.
				require_once(SUBSDIR . '/Boards.subs.php');

				$boards = boardsAllowedTo('post_new');
				if (empty($boards))
					fatal_lang_error('cannot_post_new', 'user');

				// Load a list of boards for this event in the context.
				$boardListOptions = array(
					'included_boards' => in_array(0, $boards) ? null : $boards,
					'not_redirection' => true,
					'use_permissions' => true,
					'selected_board' => empty($context['current_board']) ? $modSettings['cal_defaultboard'] : $context['current_board'],
				);
				$context += getBoardList($boardListOptions);
			}

			// Find the last day of the month.
			$context['event']['last_day'] = (int) strftime('%d', mktime(0, 0, 0, $context['event']['month'] == 12 ? 1 : $context['event']['month'] + 1, 0, $context['event']['month'] == 12 ? $context['event']['year'] + 1 : $context['event']['year']));

			$context['event']['board'] = !empty($board) ? $board : $modSettings['cal_defaultboard'];
		}

		// See if any new replies have come along.
		// Huh, $_REQUEST['msg'] is set upon submit, so this doesn't get executed at submit
		// only at preview
		if (empty($_REQUEST['msg']) && !empty($topic))
		{
			if (empty($options['no_new_reply_warning']) && isset($_REQUEST['last_msg']) && $context['topic_last_message'] > $_REQUEST['last_msg'])
			{
				require_once(SUBSDIR . '/Topic.subs.php');
				$context['new_replies'] = countMessagesSince($topic, (int) $_REQUEST['last_msg'], false, $modSettings['postmod_active'] && !allowedTo('approve_posts'));

				if (!empty($context['new_replies']))
				{
					if ($context['new_replies'] == 1)
						$txt['error_new_replies'] = isset($_GET['last_msg']) ? $txt['error_new_reply_reading'] : $txt['error_new_reply'];
					else
						$txt['error_new_replies'] = sprintf(isset($_GET['last_msg']) ? $txt['error_new_replies_reading'] : $txt['error_new_replies'], $context['new_replies']);

					$post_errors->addError('new_replies', 0);

					$modSettings['topicSummaryPosts'] = $context['new_replies'] > $modSettings['topicSummaryPosts'] ? max($modSettings['topicSummaryPosts'], 5) : $modSettings['topicSummaryPosts'];
				}
			}
		}

		// Get a response prefix (like 'Re:') in the default forum language.
		if (!isset($context['response_prefix']) && !($context['response_prefix'] = cache_get_data('response_prefix')))
		{
			if ($language === $user_info['language'])
				$context['response_prefix'] = $txt['response_prefix'];
			else
			{
				loadLanguage('index', $language, false);
				$context['response_prefix'] = $txt['response_prefix'];
				loadLanguage('index');
			}
			cache_put_data('response_prefix', $context['response_prefix'], 600);
		}

		// Previewing, modifying, or posting?
		// Do we have a body, but an error happened.
		if (isset($_REQUEST['message']) || $post_errors->hasErrors() || $attach_errors->hasErrors())
		{
			// Validate inputs.
			if (!$post_errors->hasErrors() && !$attach_errors->hasErrors())
			{
				// This means they didn't click Post and get an error.
				$really_previewing = true;
			}
			else
			{
				if (!isset($_REQUEST['subject']))
					$_REQUEST['subject'] = '';

				if (!isset($_REQUEST['message']))
					$_REQUEST['message'] = '';

				if (!isset($_REQUEST['icon']))
					$_REQUEST['icon'] = 'xx';

				// They are previewing if they asked to preview (i.e. came from quick reply).
				$really_previewing = !empty($_POST['preview']);
			}

			// In order to keep the approval status flowing through, we have to pass it through the form...
			$context['becomes_approved'] = empty($_REQUEST['not_approved']);
			$context['show_approval'] = isset($_REQUEST['approve']) ? ($_REQUEST['approve'] ? 2 : 1) : 0;
			$context['can_announce'] &= $context['becomes_approved'];

			// Set up the inputs for the form.
			$form_subject = strtr(Util::htmlspecialchars($_REQUEST['subject']), array("\r" => '', "\n" => '', "\t" => ''));
			$form_message = Util::htmlspecialchars($_REQUEST['message'], ENT_QUOTES);

			// Make sure the subject isn't too long - taking into account special characters.
			if (Util::strlen($form_subject) > 100)
				$form_subject = Util::substr($form_subject, 0, 100);

			if (isset($_REQUEST['poll']))
			{
				$context['poll']['question'] = isset($_REQUEST['question']) ? Util::htmlspecialchars(trim($_REQUEST['question'])) : '';

				$context['choices'] = array();
				$choice_id = 0;

				$_POST['options'] = empty($_POST['options']) ? array() : htmlspecialchars__recursive($_POST['options']);
				foreach ($_POST['options'] as $option)
				{
					if (trim($option) == '')
						continue;

					$context['choices'][] = array(
						'id' => $choice_id++,
						'number' => $choice_id,
						'label' => $option,
						'is_last' => false
					);
				}

				// One empty option for those with js disabled...I know are few... :P
				$context['choices'][] = array(
					'id' => $choice_id++,
					'number' => $choice_id,
					'label' => '',
					'is_last' => false
				);

				if (count($context['choices']) < 2)
				{
					$context['choices'][] = array(
						'id' => $choice_id++,
						'number' => $choice_id,
						'label' => '',
						'is_last' => false
					);
				}
				$context['last_choice_id'] = $choice_id;
				$context['choices'][count($context['choices']) - 1]['is_last'] = true;
			}

			// Are you... a guest?
			if ($user_info['is_guest'])
			{
				$context['name'] = !isset($_REQUEST['guestname']) ? '' : Util::htmlspecialchars(trim($_REQUEST['guestname']));
				$context['email'] = !isset($_REQUEST['email']) ? '' : Util::htmlspecialchars(trim($_REQUEST['email']));
				$user_info['name'] = $context['name'];
			}

			// Only show the preview stuff if they hit Preview.
			if (($really_previewing === true || isset($_REQUEST['xml'])) && !isset($_REQUEST['save_draft']))
			{
				// Set up the preview message and subject
				$context['preview_message'] = $form_message;
				preparsecode($form_message, true);

				// Do all bulletin board code thing on the message
				preparsecode($context['preview_message']);
				$context['preview_message'] = parse_bbc($context['preview_message'], isset($_REQUEST['ns']) ? 0 : 1);
				censorText($context['preview_message']);

				// Don't forget the subject
				$context['preview_subject'] = $form_subject;
				censorText($context['preview_subject']);

				// Any errors we should tell them about?
				if ($form_subject === '')
				{
					$post_errors->addError('no_subject', 0);
					$context['preview_subject'] = '<em>' . $txt['no_subject'] . '</em>';
				}

				if ($context['preview_message'] === '')
					$post_errors->addError('no_message');
				elseif (!empty($modSettings['max_messageLength']) && Util::strlen($form_message) > $modSettings['max_messageLength'])
					$post_errors->addError(array('long_message', array($modSettings['max_messageLength'])));

				// Protect any CDATA blocks.
				if (isset($_REQUEST['xml']))
					$context['preview_message'] = strtr($context['preview_message'], array(']]>' => ']]]]><![CDATA[>'));
			}

			// Set up the checkboxes.
			$context['notify'] = !empty($_REQUEST['notify']);
			$context['use_smileys'] = !isset($_REQUEST['ns']);

			$context['icon'] = isset($_REQUEST['icon']) ? preg_replace('~[\./\\\\*\':"<>]~', '', $_REQUEST['icon']) : 'xx';

			// Set the destination action for submission.
			$context['destination'] = 'post2;start=' . $_REQUEST['start'] . (isset($_REQUEST['msg']) ? ';msg=' . $_REQUEST['msg'] . ';' . $context['session_var'] . '=' . $context['session_id'] : '') . (isset($_REQUEST['poll']) ? ';poll' : '');
			$context['submit_label'] = isset($_REQUEST['msg']) ? $txt['save'] : $txt['post'];

			// Previewing an edit?
			if (isset($_REQUEST['msg']) && !empty($topic))
			{
				require_once(SUBSDIR . '/Messages.subs.php');

				// Get the existing message.
				$message = messageDetails((int) $_REQUEST['msg'], $topic);

				// The message they were trying to edit was most likely deleted.
				// @todo Change this error message?
				if ($message === false)
					fatal_lang_error('no_board', false);

				$errors = checkMessagePermissions($message['message']);
				if (!empty($errors))
					foreach ($errors as $error)
						$post_errors->addError($error);

				prepareMessageContext($message);
			}

			// No check is needed, since nothing is really posted.
			checkSubmitOnce('free');
		}
		// Editing a message...
		elseif (isset($_REQUEST['msg']) && !empty($topic))
		{
			$_REQUEST['msg'] = (int) $_REQUEST['msg'];

			$message = getFormMsgSubject(true, $topic);
			if (!empty($message['errors']))
				foreach ($errors as $error)
					$post_errors->addError($error);

			// Get the stuff ready for the form.
			$form_subject = $message['message']['subject'];
			$form_message = un_preparsecode($message['message']['body']);

			censorText($form_message);
			censorText($form_subject);

			// Check the boxes that should be checked.
			$context['use_smileys'] = !empty($message['message']['smileys_enabled']);
			$context['icon'] = $message['message']['icon'];

			// Set the destination.
			$context['destination'] = 'post2;start=' . $_REQUEST['start'] . ';msg=' . $_REQUEST['msg'] . ';' . $context['session_var'] . '=' . $context['session_id'] . (isset($_REQUEST['poll']) ? ';poll' : '');
			$context['submit_label'] = $txt['save'];
		}
		// Posting...
		else
		{
			// By default....
			$context['use_smileys'] = true;
			$context['icon'] = 'xx';

			if ($user_info['is_guest'])
			{
				$context['name'] = isset($_SESSION['guest_name']) ? $_SESSION['guest_name'] : '';
				$context['email'] = isset($_SESSION['guest_email']) ? $_SESSION['guest_email'] : '';
			}
			$context['destination'] = 'post2;start=' . $_REQUEST['start'] . (isset($_REQUEST['poll']) ? ';poll' : '');

			$context['submit_label'] = $txt['post'];

			list ($form_subject, $form_message) = getFormMsgSubject(false, $topic, $first_subject);
		}

		// Check whether this is a really old post being bumped...
		if (!empty($topic) && !empty($modSettings['oldTopicDays']) && $lastPostTime + $modSettings['oldTopicDays'] * 86400 < time() && empty($sticky) && !isset($_REQUEST['subject']))
			$post_errors->addError(array('old_topic', array($modSettings['oldTopicDays'])), 0);

		// Are we moving a discussion to its own topic?
		if (!empty($modSettings['enableFollowup']) && !empty($_REQUEST['followup']))
		{
			$context['original_post'] = isset($_REQUEST['quote']) ? (int) $_REQUEST['quote'] : (int) $_REQUEST['followup'];
			$context['show_boards_dropdown'] = true;
			require_once(SUBSDIR . '/Boards.subs.php');
			$context += getBoardList(array('use_permissions' => true, 'not_redirection' => true, 'allowed_to' => 'post_new'));
			$context['boards_current_disabled'] = false;
			if (!empty($board))
			{
				foreach ($context['categories'] as $id => $values)
					if (isset($values['boards'][$board]))
					{
						$context['categories'][$id]['boards'][$board]['selected'] = true;
						break;
					}
			}
		}

		$context['can_post_attachment'] = !empty($modSettings['attachmentEnable']) && $modSettings['attachmentEnable'] == 1 && (allowedTo('post_attachment') || ($modSettings['postmod_active'] && allowedTo('post_unapproved_attachments')));
		if ($context['can_post_attachment'])
		{
			// If there are attachments, calculate the total size and how many.
			$context['attachments']['total_size'] = 0;
			$context['attachments']['quantity'] = 0;

			// If this isn't a new post, check the current attachments.
			if (isset($_REQUEST['msg']))
			{
				$context['attachments']['quantity'] = count($context['current_attachments']);
				foreach ($context['current_attachments'] as $attachment)
					$context['attachments']['total_size'] += $attachment['size'];
			}

			// A bit of house keeping first.
			if (!empty($_SESSION['temp_attachments']) && count($_SESSION['temp_attachments']) == 1)
				unset($_SESSION['temp_attachments']);

			if (!empty($_SESSION['temp_attachments']))
			{
				// Is this a request to delete them?
				if (isset($_GET['delete_temp']))
				{
					foreach ($_SESSION['temp_attachments'] as $attachID => $attachment)
					{
						if (strpos($attachID, 'post_tmp_' . $user_info['id']) !== false)
							if (file_exists($attachment['tmp_name']))
								unlink($attachment['tmp_name']);
					}
					$attach_errors->addError('temp_attachments_gone');
					$_SESSION['temp_attachments'] = array();
				}
				// Hmm, coming in fresh and there are files in session.
				elseif ($context['current_action'] != 'post2' || !empty($_POST['from_qr']))
				{
					// Let's be nice and see if they belong here first.
					if ((empty($_REQUEST['msg']) && empty($_SESSION['temp_attachments']['post']['msg']) && $_SESSION['temp_attachments']['post']['board'] == $board) || (!empty($_REQUEST['msg']) && $_SESSION['temp_attachments']['post']['msg'] == $_REQUEST['msg']))
					{
						// See if any files still exist before showing the warning message and the files attached.
						foreach ($_SESSION['temp_attachments'] as $attachID => $attachment)
						{
							if (strpos($attachID, 'post_tmp_' . $user_info['id']) === false)
								continue;

							if (file_exists($attachment['tmp_name']))
							{
								$attach_errors->addError('temp_attachments_new');
								$context['files_in_session_warning'] = $txt['attached_files_in_session'];
								unset($_SESSION['temp_attachments']['post']['files']);
								break;
							}
						}
					}
					else
					{
						// Since, they don't belong here. Let's inform the user that they exist..
						if (!empty($topic))
							$delete_link = '<a href="' . $scripturl . '?action=post' .(!empty($_REQUEST['msg']) ? (';msg=' . $_REQUEST['msg']) : '') . (!empty($_REQUEST['last_msg']) ? (';last_msg=' . $_REQUEST['last_msg']) : '') . ';topic=' . $topic . ';delete_temp">' . $txt['here'] . '</a>';
						else
							$delete_link = '<a href="' . $scripturl . '?action=post;board=' . $board . ';delete_temp">' . $txt['here'] . '</a>';

						// Compile a list of the files to show the user.
						$file_list = array();
						foreach ($_SESSION['temp_attachments'] as $attachID => $attachment)
							if (strpos($attachID, 'post_tmp_' . $user_info['id']) !== false)
								$file_list[] = $attachment['name'];

						$_SESSION['temp_attachments']['post']['files'] = $file_list;
						$file_list = '<div class="attachments">' . implode('<br />', $file_list) . '</div>';

						if (!empty($_SESSION['temp_attachments']['post']['msg']))
						{
							// We have a message id, so we can link back to the old topic they were trying to edit..
							$goback_link = '<a href="' . $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">' . $txt['here'] . '</a>';

							$attach_errors->addError(array('temp_attachments_found', array($delete_link, $goback_link, $file_list)));
							$context['ignore_temp_attachments'] = true;
						}
						else
						{
							$attach_errors->addError(array('temp_attachments_lost', array($delete_link, $file_list)));
							$context['ignore_temp_attachments'] = true;
						}
					}
				}

				foreach ($_SESSION['temp_attachments'] as $attachID => $attachment)
				{
					if (isset($context['ignore_temp_attachments']) || isset($_SESSION['temp_attachments']['post']['files']))
						break;

					if ($attachID != 'initial_error' && strpos($attachID, 'post_tmp_' . $user_info['id']) === false)
						continue;

					if ($attachID == 'initial_error')
					{
						$txt['error_attach_initial_error'] = $txt['attach_no_upload'] . '<div style="padding: 0 1em;">' . (is_array($attachment) ? vsprintf($txt[$attachment[0]], $attachment[1]) : $txt[$attachment]) . '</div>';
						$attach_errors->addError('attach_initial_error');
						unset($_SESSION['temp_attachments']);
						break;
					}

					// Show any errors which might have occurred.
					if (!empty($attachment['errors']))
					{
						$txt['error_attach_errors'] = empty($txt['error_attach_errors']) ? '<br />' : '';
						$txt['error_attach_errors'] .= vsprintf($txt['attach_warning'], $attachment['name']) . '<div style="padding: 0 1em;">';
						foreach ($attachment['errors'] as $error)
							$txt['error_attach_errors'] .= (is_array($error) ? vsprintf($txt[$error[0]], $error[1]) : $txt[$error]) . '<br  />';
						$txt['error_attach_errors'] .= '</div>';
						$attach_errors->addError('attach_errors');

						// Take out the trash.
						unset($_SESSION['temp_attachments'][$attachID]);
						if (file_exists($attachment['tmp_name']))
							unlink($attachment['tmp_name']);
						continue;
					}

					// More house keeping.
					if (!file_exists($attachment['tmp_name']))
					{
						unset($_SESSION['temp_attachments'][$attachID]);
						continue;
					}

					$context['attachments']['quantity']++;
					$context['attachments']['total_size'] += $attachment['size'];

					if (!isset($context['files_in_session_warning']))
						$context['files_in_session_warning'] = $txt['attached_files_in_session'];

					$context['current_attachments'][] = array(
						'name' => '<u>' . htmlspecialchars($attachment['name'], ENT_COMPAT, 'UTF-8') . '</u>',
						'size' => $attachment['size'],
						'id' => $attachID,
						'unchecked' => false,
						'approved' => 1,
					);
				}
			}
		}

		// Do we need to show the visual verification image?
		$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));
		if ($context['require_verification'])
		{
			require_once(SUBSDIR . '/VerificationControls.class.php');
			$verificationOptions = array(
				'id' => 'post',
			);
			$context['require_verification'] = create_control_verification($verificationOptions);
			$context['visual_verification_id'] = $verificationOptions['id'];
		}

		// If they came from quick reply, and have to enter verification details, give them some notice.
		if (!empty($_REQUEST['from_qr']) && !empty($context['require_verification']))
			$post_errors->addError('need_qr_verification', 0);

		// Any errors occurred?
		$context['post_error'] = array(
			'errors' => $post_errors->prepareErrors(),
			'type' => $post_errors->getErrorType() == 0 ? 'minor' : 'serious',
			'title' => $txt['error_while_submitting'],
		);

		// If there are attachment errors. Let's show a list to the user.
		if ($attach_errors->hasErrors())
		{
			loadTemplate('Errors');

			$errors = $attach_errors->prepareErrors();

			foreach ($errors as $key => $error)
			{
				$context['attachment_error_keys'][] = $key . '_error';
				$context[$key . '_error'] = $error;
			}
		}

		// What are you doing? Posting a poll, modifying, previewing, new post, or reply...
		if (isset($_REQUEST['poll']))
			$context['page_title'] = $txt['new_poll'];
		elseif ($context['make_event'])
			$context['page_title'] = $context['event']['id'] == -1 ? $txt['calendar_post_event'] : $txt['calendar_edit'];
		elseif (isset($_REQUEST['msg']))
			$context['page_title'] = $txt['modify_msg'];
		elseif (isset($_REQUEST['subject'], $context['preview_subject']))
			$context['page_title'] = $txt['preview'] . ' - ' . strip_tags($context['preview_subject']);
		elseif (empty($topic))
			$context['page_title'] = $txt['start_new_topic'];
		else
			$context['page_title'] = $txt['post_reply'];

		// Update the topic summary, needed to show new posts in a preview
		if (!empty($topic) && !empty($modSettings['topicSummaryPosts']))
		{
			require_once(SUBSDIR . '/Topic.subs.php');
			$only_approved = $modSettings['postmod_active'] && !allowedTo('approve_posts');

			if (isset($_REQUEST['xml']))
				$limit = empty($context['new_replies']) ? 0 : (int) $context['new_replies'];
			else
				$limit = empty($modSettings['topicSummaryPosts']) ? 0 : (int) $modSettings['topicSummaryPosts'];

			$before = isset($_REQUEST['msg']) ? array('before' => (int) $_REQUEST['msg']) : array();

			$counter = 0;
			$context['previous_posts'] = selectMessages($topic, 0, $limit, $before, $only_approved);
			foreach ($context['previous_posts'] as &$post)
			{
				$post['is_new'] = !empty($context['new_replies']);
				$post['counter'] = $counter++;
				$post['is_ignored'] = !empty($modSettings['enable_buddylist']) && in_array($post['id_poster'], $user_info['ignoreusers']);

				if (!empty($context['new_replies']))
					$context['new_replies']--;
			}
		}

		// Just ajax previewing then lets stop now
		if (isset($_REQUEST['xml']))
			obExit();

		// Build the link tree.
		if (empty($topic))
		{
			$context['linktree'][] = array(
				'name' => '<em>' . $txt['start_new_topic'] . '</em>'
			);
		}
		else
		{
			$context['linktree'][] = array(
				'url' => $scripturl . '?topic=' . $topic . '.' . $_REQUEST['start'],
				'name' => $form_subject,
				'extra_before' => '<span><strong class="nav">' . $context['page_title'] . ' ( </strong></span>',
				'extra_after' => '<span><strong class="nav"> )</strong></span>'
			);
		}

		$context['subject'] = addcslashes($form_subject, '"');
		$context['message'] = str_replace(array('"', '<', '>', '&nbsp;'), array('&quot;', '&lt;', '&gt;', ' '), $form_message);

		// Are post drafts enabled?
		$context['drafts_save'] = !empty($modSettings['drafts_enabled']) && !empty($modSettings['drafts_post_enabled']) && allowedTo('post_draft');
		$context['drafts_autosave'] = !empty($context['drafts_save']) && !empty($modSettings['drafts_autosave_enabled']) && allowedTo('post_autosave_draft');

		if (!empty($modSettings['mentions_enabled']))
		{
			$context['mentions_enabled'] = true;
			loadJavascriptFile(array('jquery.atwho.js', 'jquery.caret.js'));
			loadCSSFile('jquery.atwho.css');

			addInlineJavascript('
			$(document).ready(function () {
				for (var i = 0, count = all_elk_mentions.length; i < count; i++)
					all_elk_mentions[i].oMention = new elk_mentions(all_elk_mentions[i].oOptions);
			});');
		}

		// Build a list of drafts that they can load into the editor<commit_msg>! the editor functions loads these JS dependences, no need to do it twice<commit_after><?php

/**
 * @name      ElkArte Forum
 * @copyright ElkArte Forum contributors
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause
 *
 * This software is a derived product, based on:
 *
 * Simple Machines Forum (SMF)
 * copyright:	2011 Simple Machines (http://www.simplemachines.org)
 * license:  	BSD, See included LICENSE.TXT for terms and conditions.
 *
 * @version 1.0 Beta
 *
 * The job of this file is to handle everything related to posting replies,
 * new topics, quotes, and modifications to existing posts.  It also handles
 * quoting posts by way of javascript.
 *
 */

if (!defined('ELK'))
	die('No access...');

/**
 * Post Controller
 */
class Post_Controller extends Action_Controller
{
	/**
	 * Dispatch to the right action method for the request.
	 *
	 * @see Action_Controller::action_index()
	 */
	public function action_index()
	{
		// figure out the right action to do.
		// hint: I'm post controller. :P
		$this->action_post();
	}

	/**
	 * Handles showing the post screen, loading the post to be modified, and loading any post quoted.
	 *
	 * - additionally handles previews of posts.
	 * - @uses the Post template and language file, main sub template.
	 * - allows wireless access using the protocol_post sub template.
	 * - requires different permissions depending on the actions, but most notably post_new, post_reply_own, and post_reply_any.
	 * - shows options for the editing and posting of calendar events and attachments, as well as the posting of polls.
	 * - accessed from ?action=post.
	 *
	 *  @param array $post_errors holds any errors found tyring to post
	 */
	function action_post()
	{
		global $txt, $scripturl, $topic, $modSettings, $board;
		global $user_info, $context, $options, $language;

		$db = database();

		loadLanguage('Post');

		// You can't reply with a poll... hacker.
		if (isset($_REQUEST['poll']) && !empty($topic) && !isset($_REQUEST['msg']))
			unset($_REQUEST['poll']);

		$post_errors = Error_Context::context('post', 1);
		$attach_errors = attachment_Error_Context::context('attachment', 1);
		$attach_errors->activate();
		$first_subject = '';

		// Posting an event?
		$context['make_event'] = isset($_REQUEST['calendar']);
		$context['robot_no_index'] = true;
		$template_layers = Template_Layers::getInstance();
		$template_layers->add('postarea');

		// You must be posting to *some* board.
		if (empty($board) && !$context['make_event'])
			fatal_lang_error('no_board', false);

		if ($context['make_event'])
			$template_layers->add('make_event');

		// All those wonderful modifiers and attachments
		$template_layers->add('additional_options', 200);

		require_once(SUBSDIR . '/Post.subs.php');
		require_once(SUBSDIR . '/Messages.subs.php');

		if (isset($_REQUEST['xml']))
		{
			$context['sub_template'] = 'post';

			// Just in case of an earlier error...
			$context['preview_message'] = '';
			$context['preview_subject'] = '';
		}

		if (!empty($modSettings['mentions_enabled']) && !empty($_REQUEST['uid']))
			$context['member_ids'] = array_unique(array_map('intval', $_REQUEST['uid']));

		// No message is complete without a topic.
		if (empty($topic) && !empty($_REQUEST['msg']))
		{
			$topic = associatedTopic((int) $_REQUEST['msg']);
			if (empty($topic))
				unset($_REQUEST['msg'], $_POST['msg'], $_GET['msg']);
		}

		// Check if it's locked. It isn't locked if no topic is specified.
		if (!empty($topic))
		{
			$request = $db->query('', '
				SELECT
					t.locked, IFNULL(ln.id_topic, 0) AS notify, t.is_sticky, t.id_poll, t.id_last_msg, mf.id_member,
					t.id_first_msg, mf.subject,
					CASE WHEN ml.poster_time > ml.modified_time THEN ml.poster_time ELSE ml.modified_time END AS last_post_time
				FROM {db_prefix}topics AS t
					LEFT JOIN {db_prefix}log_notify AS ln ON (ln.id_topic = t.id_topic AND ln.id_member = {int:current_member})
					LEFT JOIN {db_prefix}messages AS mf ON (mf.id_msg = t.id_first_msg)
					LEFT JOIN {db_prefix}messages AS ml ON (ml.id_msg = t.id_last_msg)
				WHERE t.id_topic = {int:current_topic}
				LIMIT 1',
				array(
					'current_member' => $user_info['id'],
					'current_topic' => $topic,
				)
			);
			list ($locked, $context['notify'], $sticky, $pollID, $context['topic_last_message'], $id_member_poster, $id_first_msg, $first_subject, $lastPostTime) = $db->fetch_row($request);
			$db->free_result($request);

			// If this topic already has a poll, they sure can't add another.
			if (isset($_REQUEST['poll']) && $pollID > 0)
				unset($_REQUEST['poll']);

			if (empty($_REQUEST['msg']))
			{
				if ($user_info['is_guest'] && !allowedTo('post_reply_any') && (!$modSettings['postmod_active'] || !allowedTo('post_unapproved_replies_any')))
					is_not_guest();

				// By default the reply will be approved...
				$context['becomes_approved'] = true;
				if ($id_member_poster != $user_info['id'])
				{
					if ($modSettings['postmod_active'] && allowedTo('post_unapproved_replies_any') && !allowedTo('post_reply_any'))
						$context['becomes_approved'] = false;
					else
						isAllowedTo('post_reply_any');
				}
				elseif (!allowedTo('post_reply_any'))
				{
					if ($modSettings['postmod_active'] && allowedTo('post_unapproved_replies_own') && !allowedTo('post_reply_own'))
						$context['becomes_approved'] = false;
					else
						isAllowedTo('post_reply_own');
				}
			}
			else
				$context['becomes_approved'] = true;

			$context['can_lock'] = allowedTo('lock_any') || ($user_info['id'] == $id_member_poster && allowedTo('lock_own'));
			$context['can_sticky'] = allowedTo('make_sticky') && !empty($modSettings['enableStickyTopics']);

			$context['notify'] = !empty($context['notify']);
			$context['sticky'] = isset($_REQUEST['sticky']) ? !empty($_REQUEST['sticky']) : $sticky;
		}
		else
		{
			$context['becomes_approved'] = true;
			if ((!$context['make_event'] || !empty($board)))
			{
				if ($modSettings['postmod_active'] && !allowedTo('post_new') && allowedTo('post_unapproved_topics'))
					$context['becomes_approved'] = false;
				else
					isAllowedTo('post_new');
			}

			$locked = 0;

			// @todo These won't work if you're making an event.
			$context['can_lock'] = allowedTo(array('lock_any', 'lock_own'));
			$context['can_sticky'] = allowedTo('make_sticky') && !empty($modSettings['enableStickyTopics']);

			$context['notify'] = !empty($context['notify']);
			$context['sticky'] = !empty($_REQUEST['sticky']);
		}

		// @todo These won't work if you're posting an event!
		$context['can_notify'] = allowedTo('mark_any_notify');
		$context['can_move'] = allowedTo('move_any');
		$context['move'] = !empty($_REQUEST['move']);
		$context['announce'] = !empty($_REQUEST['announce']);

		// You can only announce topics that will get approved...
		$context['can_announce'] = allowedTo('announce_topic') && $context['becomes_approved'];
		$context['locked'] = !empty($locked) || !empty($_REQUEST['lock']);
		$context['can_quote'] = empty($modSettings['disabledBBC']) || !in_array('quote', explode(',', $modSettings['disabledBBC']));

		// Generally don't show the approval box... (Assume we want things approved)
		$context['show_approval'] = allowedTo('approve_posts') && $context['becomes_approved'] ? 2 : (allowedTo('approve_posts') ? 1 : 0);

		// An array to hold all the attachments for this topic.
		$context['current_attachments'] = array();

		// Don't allow a post if it's locked and you aren't all powerful.
		if ($locked && !allowedTo('moderate_board'))
			fatal_lang_error('topic_locked', false);

		// Check the users permissions - is the user allowed to add or post a poll?
		if (isset($_REQUEST['poll']) && $modSettings['pollMode'] == '1')
		{
			// New topic, new poll.
			if (empty($topic))
				isAllowedTo('poll_post');
			// This is an old topic - but it is yours!  Can you add to it?
			elseif ($user_info['id'] == $id_member_poster && !allowedTo('poll_add_any'))
				isAllowedTo('poll_add_own');
			// If you're not the owner, can you add to any poll?
			else
				isAllowedTo('poll_add_any');
			$context['can_moderate_poll'] = true;

			require_once(SUBSDIR . '/Members.subs.php');
			$allowedVoteGroups = groupsAllowedTo('poll_vote', $board);

			// Set up the poll options.
			$context['poll'] = array(
				'max_votes' => empty($_POST['poll_max_votes']) ? '1' : max(1, $_POST['poll_max_votes']),
				'hide_results' => empty($_POST['poll_hide']) ? 0 : $_POST['poll_hide'],
				'expiration' => !isset($_POST['poll_expire']) ? '' : $_POST['poll_expire'],
				'change_vote' => isset($_POST['poll_change_vote']),
				'guest_vote' => isset($_POST['poll_guest_vote']),
				'guest_vote_allowed' => in_array(-1, $allowedVoteGroups['allowed']),
			);

			// Make all five poll choices empty.
			$context['choices'] = array(
				array('id' => 0, 'number' => 1, 'label' => '', 'is_last' => false),
				array('id' => 1, 'number' => 2, 'label' => '', 'is_last' => false),
				array('id' => 2, 'number' => 3, 'label' => '', 'is_last' => false),
				array('id' => 3, 'number' => 4, 'label' => '', 'is_last' => false),
				array('id' => 4, 'number' => 5, 'label' => '', 'is_last' => true)
			);
			$context['last_choice_id'] = 4;
		}

		if ($context['make_event'])
		{
			// They might want to pick a board.
			if (!isset($context['current_board']))
				$context['current_board'] = 0;

			// Start loading up the event info.
			$context['event'] = array();
			$context['event']['title'] = isset($_REQUEST['evtitle']) ? htmlspecialchars(stripslashes($_REQUEST['evtitle']), ENT_COMPAT, 'UTF-8') : '';
			$context['event']['id'] = isset($_REQUEST['eventid']) ? (int) $_REQUEST['eventid'] : -1;
			$context['event']['new'] = $context['event']['id'] == -1;

			// Permissions check!
			isAllowedTo('calendar_post');

			// Editing an event?  (but NOT previewing!?)
			if (empty($context['event']['new']) && !isset($_REQUEST['subject']))
			{
				// If the user doesn't have permission to edit the post in this topic, redirect them.
				if ((empty($id_member_poster) || $id_member_poster != $user_info['id'] || !allowedTo('modify_own')) && !allowedTo('modify_any'))
				{
					require_once(CONTROLLERDIR . '/Calendar.controller.php');
					$controller = new Calendar_Controller();
					return $controller->action_post();
				}

				// Get the current event information.
				require_once(SUBSDIR . '/Calendar.subs.php');
				$event_info = getEventProperties($context['event']['id']);

				// Make sure the user is allowed to edit this event.
				if ($event_info['member'] != $user_info['id'])
					isAllowedTo('calendar_edit_any');
				elseif (!allowedTo('calendar_edit_any'))
					isAllowedTo('calendar_edit_own');

				$context['event']['month'] = $event_info['month'];
				$context['event']['day'] = $event_info['day'];
				$context['event']['year'] = $event_info['year'];
				$context['event']['title'] = $event_info['title'];
				$context['event']['span'] = $event_info['span'];
			}
			else
			{
				// Posting a new event? (or preview...)
				$today = getdate();

				// You must have a month and year specified!
				if (!isset($_REQUEST['month']))
					$_REQUEST['month'] = $today['mon'];

				if (!isset($_REQUEST['year']))
					$_REQUEST['year'] = $today['year'];

				$context['event']['month'] = (int) $_REQUEST['month'];
				$context['event']['year'] = (int) $_REQUEST['year'];
				$context['event']['day'] = isset($_REQUEST['day']) ? $_REQUEST['day'] : ($_REQUEST['month'] == $today['mon'] ? $today['mday'] : 0);
				$context['event']['span'] = isset($_REQUEST['span']) ? $_REQUEST['span'] : 1;

				// Make sure the year and month are in the valid range.
				if ($context['event']['month'] < 1 || $context['event']['month'] > 12)
					fatal_lang_error('invalid_month', false);

				if ($context['event']['year'] < $modSettings['cal_minyear'] || $context['event']['year'] > $modSettings['cal_maxyear'])
					fatal_lang_error('invalid_year', false);

				// Get a list of boards they can post in.
				require_once(SUBSDIR . '/Boards.subs.php');

				$boards = boardsAllowedTo('post_new');
				if (empty($boards))
					fatal_lang_error('cannot_post_new', 'user');

				// Load a list of boards for this event in the context.
				$boardListOptions = array(
					'included_boards' => in_array(0, $boards) ? null : $boards,
					'not_redirection' => true,
					'use_permissions' => true,
					'selected_board' => empty($context['current_board']) ? $modSettings['cal_defaultboard'] : $context['current_board'],
				);
				$context += getBoardList($boardListOptions);
			}

			// Find the last day of the month.
			$context['event']['last_day'] = (int) strftime('%d', mktime(0, 0, 0, $context['event']['month'] == 12 ? 1 : $context['event']['month'] + 1, 0, $context['event']['month'] == 12 ? $context['event']['year'] + 1 : $context['event']['year']));

			$context['event']['board'] = !empty($board) ? $board : $modSettings['cal_defaultboard'];
		}

		// See if any new replies have come along.
		// Huh, $_REQUEST['msg'] is set upon submit, so this doesn't get executed at submit
		// only at preview
		if (empty($_REQUEST['msg']) && !empty($topic))
		{
			if (empty($options['no_new_reply_warning']) && isset($_REQUEST['last_msg']) && $context['topic_last_message'] > $_REQUEST['last_msg'])
			{
				require_once(SUBSDIR . '/Topic.subs.php');
				$context['new_replies'] = countMessagesSince($topic, (int) $_REQUEST['last_msg'], false, $modSettings['postmod_active'] && !allowedTo('approve_posts'));

				if (!empty($context['new_replies']))
				{
					if ($context['new_replies'] == 1)
						$txt['error_new_replies'] = isset($_GET['last_msg']) ? $txt['error_new_reply_reading'] : $txt['error_new_reply'];
					else
						$txt['error_new_replies'] = sprintf(isset($_GET['last_msg']) ? $txt['error_new_replies_reading'] : $txt['error_new_replies'], $context['new_replies']);

					$post_errors->addError('new_replies', 0);

					$modSettings['topicSummaryPosts'] = $context['new_replies'] > $modSettings['topicSummaryPosts'] ? max($modSettings['topicSummaryPosts'], 5) : $modSettings['topicSummaryPosts'];
				}
			}
		}

		// Get a response prefix (like 'Re:') in the default forum language.
		if (!isset($context['response_prefix']) && !($context['response_prefix'] = cache_get_data('response_prefix')))
		{
			if ($language === $user_info['language'])
				$context['response_prefix'] = $txt['response_prefix'];
			else
			{
				loadLanguage('index', $language, false);
				$context['response_prefix'] = $txt['response_prefix'];
				loadLanguage('index');
			}
			cache_put_data('response_prefix', $context['response_prefix'], 600);
		}

		// Previewing, modifying, or posting?
		// Do we have a body, but an error happened.
		if (isset($_REQUEST['message']) || $post_errors->hasErrors() || $attach_errors->hasErrors())
		{
			// Validate inputs.
			if (!$post_errors->hasErrors() && !$attach_errors->hasErrors())
			{
				// This means they didn't click Post and get an error.
				$really_previewing = true;
			}
			else
			{
				if (!isset($_REQUEST['subject']))
					$_REQUEST['subject'] = '';

				if (!isset($_REQUEST['message']))
					$_REQUEST['message'] = '';

				if (!isset($_REQUEST['icon']))
					$_REQUEST['icon'] = 'xx';

				// They are previewing if they asked to preview (i.e. came from quick reply).
				$really_previewing = !empty($_POST['preview']);
			}

			// In order to keep the approval status flowing through, we have to pass it through the form...
			$context['becomes_approved'] = empty($_REQUEST['not_approved']);
			$context['show_approval'] = isset($_REQUEST['approve']) ? ($_REQUEST['approve'] ? 2 : 1) : 0;
			$context['can_announce'] &= $context['becomes_approved'];

			// Set up the inputs for the form.
			$form_subject = strtr(Util::htmlspecialchars($_REQUEST['subject']), array("\r" => '', "\n" => '', "\t" => ''));
			$form_message = Util::htmlspecialchars($_REQUEST['message'], ENT_QUOTES);

			// Make sure the subject isn't too long - taking into account special characters.
			if (Util::strlen($form_subject) > 100)
				$form_subject = Util::substr($form_subject, 0, 100);

			if (isset($_REQUEST['poll']))
			{
				$context['poll']['question'] = isset($_REQUEST['question']) ? Util::htmlspecialchars(trim($_REQUEST['question'])) : '';

				$context['choices'] = array();
				$choice_id = 0;

				$_POST['options'] = empty($_POST['options']) ? array() : htmlspecialchars__recursive($_POST['options']);
				foreach ($_POST['options'] as $option)
				{
					if (trim($option) == '')
						continue;

					$context['choices'][] = array(
						'id' => $choice_id++,
						'number' => $choice_id,
						'label' => $option,
						'is_last' => false
					);
				}

				// One empty option for those with js disabled...I know are few... :P
				$context['choices'][] = array(
					'id' => $choice_id++,
					'number' => $choice_id,
					'label' => '',
					'is_last' => false
				);

				if (count($context['choices']) < 2)
				{
					$context['choices'][] = array(
						'id' => $choice_id++,
						'number' => $choice_id,
						'label' => '',
						'is_last' => false
					);
				}
				$context['last_choice_id'] = $choice_id;
				$context['choices'][count($context['choices']) - 1]['is_last'] = true;
			}

			// Are you... a guest?
			if ($user_info['is_guest'])
			{
				$context['name'] = !isset($_REQUEST['guestname']) ? '' : Util::htmlspecialchars(trim($_REQUEST['guestname']));
				$context['email'] = !isset($_REQUEST['email']) ? '' : Util::htmlspecialchars(trim($_REQUEST['email']));
				$user_info['name'] = $context['name'];
			}

			// Only show the preview stuff if they hit Preview.
			if (($really_previewing === true || isset($_REQUEST['xml'])) && !isset($_REQUEST['save_draft']))
			{
				// Set up the preview message and subject
				$context['preview_message'] = $form_message;
				preparsecode($form_message, true);

				// Do all bulletin board code thing on the message
				preparsecode($context['preview_message']);
				$context['preview_message'] = parse_bbc($context['preview_message'], isset($_REQUEST['ns']) ? 0 : 1);
				censorText($context['preview_message']);

				// Don't forget the subject
				$context['preview_subject'] = $form_subject;
				censorText($context['preview_subject']);

				// Any errors we should tell them about?
				if ($form_subject === '')
				{
					$post_errors->addError('no_subject', 0);
					$context['preview_subject'] = '<em>' . $txt['no_subject'] . '</em>';
				}

				if ($context['preview_message'] === '')
					$post_errors->addError('no_message');
				elseif (!empty($modSettings['max_messageLength']) && Util::strlen($form_message) > $modSettings['max_messageLength'])
					$post_errors->addError(array('long_message', array($modSettings['max_messageLength'])));

				// Protect any CDATA blocks.
				if (isset($_REQUEST['xml']))
					$context['preview_message'] = strtr($context['preview_message'], array(']]>' => ']]]]><![CDATA[>'));
			}

			// Set up the checkboxes.
			$context['notify'] = !empty($_REQUEST['notify']);
			$context['use_smileys'] = !isset($_REQUEST['ns']);

			$context['icon'] = isset($_REQUEST['icon']) ? preg_replace('~[\./\\\\*\':"<>]~', '', $_REQUEST['icon']) : 'xx';

			// Set the destination action for submission.
			$context['destination'] = 'post2;start=' . $_REQUEST['start'] . (isset($_REQUEST['msg']) ? ';msg=' . $_REQUEST['msg'] . ';' . $context['session_var'] . '=' . $context['session_id'] : '') . (isset($_REQUEST['poll']) ? ';poll' : '');
			$context['submit_label'] = isset($_REQUEST['msg']) ? $txt['save'] : $txt['post'];

			// Previewing an edit?
			if (isset($_REQUEST['msg']) && !empty($topic))
			{
				require_once(SUBSDIR . '/Messages.subs.php');

				// Get the existing message.
				$message = messageDetails((int) $_REQUEST['msg'], $topic);

				// The message they were trying to edit was most likely deleted.
				// @todo Change this error message?
				if ($message === false)
					fatal_lang_error('no_board', false);

				$errors = checkMessagePermissions($message['message']);
				if (!empty($errors))
					foreach ($errors as $error)
						$post_errors->addError($error);

				prepareMessageContext($message);
			}

			// No check is needed, since nothing is really posted.
			checkSubmitOnce('free');
		}
		// Editing a message...
		elseif (isset($_REQUEST['msg']) && !empty($topic))
		{
			$_REQUEST['msg'] = (int) $_REQUEST['msg'];

			$message = getFormMsgSubject(true, $topic);
			if (!empty($message['errors']))
				foreach ($errors as $error)
					$post_errors->addError($error);

			// Get the stuff ready for the form.
			$form_subject = $message['message']['subject'];
			$form_message = un_preparsecode($message['message']['body']);

			censorText($form_message);
			censorText($form_subject);

			// Check the boxes that should be checked.
			$context['use_smileys'] = !empty($message['message']['smileys_enabled']);
			$context['icon'] = $message['message']['icon'];

			// Set the destination.
			$context['destination'] = 'post2;start=' . $_REQUEST['start'] . ';msg=' . $_REQUEST['msg'] . ';' . $context['session_var'] . '=' . $context['session_id'] . (isset($_REQUEST['poll']) ? ';poll' : '');
			$context['submit_label'] = $txt['save'];
		}
		// Posting...
		else
		{
			// By default....
			$context['use_smileys'] = true;
			$context['icon'] = 'xx';

			if ($user_info['is_guest'])
			{
				$context['name'] = isset($_SESSION['guest_name']) ? $_SESSION['guest_name'] : '';
				$context['email'] = isset($_SESSION['guest_email']) ? $_SESSION['guest_email'] : '';
			}
			$context['destination'] = 'post2;start=' . $_REQUEST['start'] . (isset($_REQUEST['poll']) ? ';poll' : '');

			$context['submit_label'] = $txt['post'];

			list ($form_subject, $form_message) = getFormMsgSubject(false, $topic, $first_subject);
		}

		// Check whether this is a really old post being bumped...
		if (!empty($topic) && !empty($modSettings['oldTopicDays']) && $lastPostTime + $modSettings['oldTopicDays'] * 86400 < time() && empty($sticky) && !isset($_REQUEST['subject']))
			$post_errors->addError(array('old_topic', array($modSettings['oldTopicDays'])), 0);

		// Are we moving a discussion to its own topic?
		if (!empty($modSettings['enableFollowup']) && !empty($_REQUEST['followup']))
		{
			$context['original_post'] = isset($_REQUEST['quote']) ? (int) $_REQUEST['quote'] : (int) $_REQUEST['followup'];
			$context['show_boards_dropdown'] = true;
			require_once(SUBSDIR . '/Boards.subs.php');
			$context += getBoardList(array('use_permissions' => true, 'not_redirection' => true, 'allowed_to' => 'post_new'));
			$context['boards_current_disabled'] = false;
			if (!empty($board))
			{
				foreach ($context['categories'] as $id => $values)
					if (isset($values['boards'][$board]))
					{
						$context['categories'][$id]['boards'][$board]['selected'] = true;
						break;
					}
			}
		}

		$context['can_post_attachment'] = !empty($modSettings['attachmentEnable']) && $modSettings['attachmentEnable'] == 1 && (allowedTo('post_attachment') || ($modSettings['postmod_active'] && allowedTo('post_unapproved_attachments')));
		if ($context['can_post_attachment'])
		{
			// If there are attachments, calculate the total size and how many.
			$context['attachments']['total_size'] = 0;
			$context['attachments']['quantity'] = 0;

			// If this isn't a new post, check the current attachments.
			if (isset($_REQUEST['msg']))
			{
				$context['attachments']['quantity'] = count($context['current_attachments']);
				foreach ($context['current_attachments'] as $attachment)
					$context['attachments']['total_size'] += $attachment['size'];
			}

			// A bit of house keeping first.
			if (!empty($_SESSION['temp_attachments']) && count($_SESSION['temp_attachments']) == 1)
				unset($_SESSION['temp_attachments']);

			if (!empty($_SESSION['temp_attachments']))
			{
				// Is this a request to delete them?
				if (isset($_GET['delete_temp']))
				{
					foreach ($_SESSION['temp_attachments'] as $attachID => $attachment)
					{
						if (strpos($attachID, 'post_tmp_' . $user_info['id']) !== false)
							if (file_exists($attachment['tmp_name']))
								unlink($attachment['tmp_name']);
					}
					$attach_errors->addError('temp_attachments_gone');
					$_SESSION['temp_attachments'] = array();
				}
				// Hmm, coming in fresh and there are files in session.
				elseif ($context['current_action'] != 'post2' || !empty($_POST['from_qr']))
				{
					// Let's be nice and see if they belong here first.
					if ((empty($_REQUEST['msg']) && empty($_SESSION['temp_attachments']['post']['msg']) && $_SESSION['temp_attachments']['post']['board'] == $board) || (!empty($_REQUEST['msg']) && $_SESSION['temp_attachments']['post']['msg'] == $_REQUEST['msg']))
					{
						// See if any files still exist before showing the warning message and the files attached.
						foreach ($_SESSION['temp_attachments'] as $attachID => $attachment)
						{
							if (strpos($attachID, 'post_tmp_' . $user_info['id']) === false)
								continue;

							if (file_exists($attachment['tmp_name']))
							{
								$attach_errors->addError('temp_attachments_new');
								$context['files_in_session_warning'] = $txt['attached_files_in_session'];
								unset($_SESSION['temp_attachments']['post']['files']);
								break;
							}
						}
					}
					else
					{
						// Since, they don't belong here. Let's inform the user that they exist..
						if (!empty($topic))
							$delete_link = '<a href="' . $scripturl . '?action=post' .(!empty($_REQUEST['msg']) ? (';msg=' . $_REQUEST['msg']) : '') . (!empty($_REQUEST['last_msg']) ? (';last_msg=' . $_REQUEST['last_msg']) : '') . ';topic=' . $topic . ';delete_temp">' . $txt['here'] . '</a>';
						else
							$delete_link = '<a href="' . $scripturl . '?action=post;board=' . $board . ';delete_temp">' . $txt['here'] . '</a>';

						// Compile a list of the files to show the user.
						$file_list = array();
						foreach ($_SESSION['temp_attachments'] as $attachID => $attachment)
							if (strpos($attachID, 'post_tmp_' . $user_info['id']) !== false)
								$file_list[] = $attachment['name'];

						$_SESSION['temp_attachments']['post']['files'] = $file_list;
						$file_list = '<div class="attachments">' . implode('<br />', $file_list) . '</div>';

						if (!empty($_SESSION['temp_attachments']['post']['msg']))
						{
							// We have a message id, so we can link back to the old topic they were trying to edit..
							$goback_link = '<a href="' . $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">' . $txt['here'] . '</a>';

							$attach_errors->addError(array('temp_attachments_found', array($delete_link, $goback_link, $file_list)));
							$context['ignore_temp_attachments'] = true;
						}
						else
						{
							$attach_errors->addError(array('temp_attachments_lost', array($delete_link, $file_list)));
							$context['ignore_temp_attachments'] = true;
						}
					}
				}

				foreach ($_SESSION['temp_attachments'] as $attachID => $attachment)
				{
					if (isset($context['ignore_temp_attachments']) || isset($_SESSION['temp_attachments']['post']['files']))
						break;

					if ($attachID != 'initial_error' && strpos($attachID, 'post_tmp_' . $user_info['id']) === false)
						continue;

					if ($attachID == 'initial_error')
					{
						$txt['error_attach_initial_error'] = $txt['attach_no_upload'] . '<div style="padding: 0 1em;">' . (is_array($attachment) ? vsprintf($txt[$attachment[0]], $attachment[1]) : $txt[$attachment]) . '</div>';
						$attach_errors->addError('attach_initial_error');
						unset($_SESSION['temp_attachments']);
						break;
					}

					// Show any errors which might have occurred.
					if (!empty($attachment['errors']))
					{
						$txt['error_attach_errors'] = empty($txt['error_attach_errors']) ? '<br />' : '';
						$txt['error_attach_errors'] .= vsprintf($txt['attach_warning'], $attachment['name']) . '<div style="padding: 0 1em;">';
						foreach ($attachment['errors'] as $error)
							$txt['error_attach_errors'] .= (is_array($error) ? vsprintf($txt[$error[0]], $error[1]) : $txt[$error]) . '<br  />';
						$txt['error_attach_errors'] .= '</div>';
						$attach_errors->addError('attach_errors');

						// Take out the trash.
						unset($_SESSION['temp_attachments'][$attachID]);
						if (file_exists($attachment['tmp_name']))
							unlink($attachment['tmp_name']);
						continue;
					}

					// More house keeping.
					if (!file_exists($attachment['tmp_name']))
					{
						unset($_SESSION['temp_attachments'][$attachID]);
						continue;
					}

					$context['attachments']['quantity']++;
					$context['attachments']['total_size'] += $attachment['size'];

					if (!isset($context['files_in_session_warning']))
						$context['files_in_session_warning'] = $txt['attached_files_in_session'];

					$context['current_attachments'][] = array(
						'name' => '<u>' . htmlspecialchars($attachment['name'], ENT_COMPAT, 'UTF-8') . '</u>',
						'size' => $attachment['size'],
						'id' => $attachID,
						'unchecked' => false,
						'approved' => 1,
					);
				}
			}
		}

		// Do we need to show the visual verification image?
		$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));
		if ($context['require_verification'])
		{
			require_once(SUBSDIR . '/VerificationControls.class.php');
			$verificationOptions = array(
				'id' => 'post',
			);
			$context['require_verification'] = create_control_verification($verificationOptions);
			$context['visual_verification_id'] = $verificationOptions['id'];
		}

		// If they came from quick reply, and have to enter verification details, give them some notice.
		if (!empty($_REQUEST['from_qr']) && !empty($context['require_verification']))
			$post_errors->addError('need_qr_verification', 0);

		// Any errors occurred?
		$context['post_error'] = array(
			'errors' => $post_errors->prepareErrors(),
			'type' => $post_errors->getErrorType() == 0 ? 'minor' : 'serious',
			'title' => $txt['error_while_submitting'],
		);

		// If there are attachment errors. Let's show a list to the user.
		if ($attach_errors->hasErrors())
		{
			loadTemplate('Errors');

			$errors = $attach_errors->prepareErrors();

			foreach ($errors as $key => $error)
			{
				$context['attachment_error_keys'][] = $key . '_error';
				$context[$key . '_error'] = $error;
			}
		}

		// What are you doing? Posting a poll, modifying, previewing, new post, or reply...
		if (isset($_REQUEST['poll']))
			$context['page_title'] = $txt['new_poll'];
		elseif ($context['make_event'])
			$context['page_title'] = $context['event']['id'] == -1 ? $txt['calendar_post_event'] : $txt['calendar_edit'];
		elseif (isset($_REQUEST['msg']))
			$context['page_title'] = $txt['modify_msg'];
		elseif (isset($_REQUEST['subject'], $context['preview_subject']))
			$context['page_title'] = $txt['preview'] . ' - ' . strip_tags($context['preview_subject']);
		elseif (empty($topic))
			$context['page_title'] = $txt['start_new_topic'];
		else
			$context['page_title'] = $txt['post_reply'];

		// Update the topic summary, needed to show new posts in a preview
		if (!empty($topic) && !empty($modSettings['topicSummaryPosts']))
		{
			require_once(SUBSDIR . '/Topic.subs.php');
			$only_approved = $modSettings['postmod_active'] && !allowedTo('approve_posts');

			if (isset($_REQUEST['xml']))
				$limit = empty($context['new_replies']) ? 0 : (int) $context['new_replies'];
			else
				$limit = empty($modSettings['topicSummaryPosts']) ? 0 : (int) $modSettings['topicSummaryPosts'];

			$before = isset($_REQUEST['msg']) ? array('before' => (int) $_REQUEST['msg']) : array();

			$counter = 0;
			$context['previous_posts'] = selectMessages($topic, 0, $limit, $before, $only_approved);
			foreach ($context['previous_posts'] as &$post)
			{
				$post['is_new'] = !empty($context['new_replies']);
				$post['counter'] = $counter++;
				$post['is_ignored'] = !empty($modSettings['enable_buddylist']) && in_array($post['id_poster'], $user_info['ignoreusers']);

				if (!empty($context['new_replies']))
					$context['new_replies']--;
			}
		}

		// Just ajax previewing then lets stop now
		if (isset($_REQUEST['xml']))
			obExit();

		// Build the link tree.
		if (empty($topic))
		{
			$context['linktree'][] = array(
				'name' => '<em>' . $txt['start_new_topic'] . '</em>'
			);
		}
		else
		{
			$context['linktree'][] = array(
				'url' => $scripturl . '?topic=' . $topic . '.' . $_REQUEST['start'],
				'name' => $form_subject,
				'extra_before' => '<span><strong class="nav">' . $context['page_title'] . ' ( </strong></span>',
				'extra_after' => '<span><strong class="nav"> )</strong></span>'
			);
		}

		$context['subject'] = addcslashes($form_subject, '"');
		$context['message'] = str_replace(array('"', '<', '>', '&nbsp;'), array('&quot;', '&lt;', '&gt;', ' '), $form_message);

		// Are post drafts enabled?
		$context['drafts_save'] = !empty($modSettings['drafts_enabled']) && !empty($modSettings['drafts_post_enabled']) && allowedTo('post_draft');
		$context['drafts_autosave'] = !empty($context['drafts_save']) && !empty($modSettings['drafts_autosave_enabled']) && allowedTo('post_autosave_draft');

		if (!empty($modSettings['mentions_enabled']))
		{
			$context['mentions_enabled'] = true;
			loadCSSFile('jquery.atwho.css');

			addInlineJavascript('
			$(document).ready(function () {
				for (var i = 0, count = all_elk_mentions.length; i < count; i++)
					all_elk_mentions[i].oMention = new elk_mentions(all_elk_mentions[i].oOptions);
			});');
		}

		// Build a list of drafts that they can load into the editor
{'fraction_non_alphanumeric': 0.20120895566184455, 'fraction_numerical': 0.0032571378268141673, 'mean_word_length': 4.296371124407491, 'pattern_counts': {'":': 0, '<': 81, '<?xml version=': 0, '>': 293, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 10}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1199976', 'n_tokens_mistral': 25787, 'n_tokens_neox': 21529, 'n_words': 4520}