<?php
/*
	Question2Answer by Gideon Greenspan and contributors
	http://www.question2answer.org/

	Description: Common functions for question page form submission, either regular or via Ajax


	This program is free software; you can redistribute it and/or
	modify it under the terms of the GNU General Public License
	as published by the Free Software Foundation; either version 2
	of the License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	More about this license: http://www.question2answer.org/license.php
*/

if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser
	header('Location: ../../');
	exit;
}


require_once QA_INCLUDE_DIR . 'app/post-create.php';
require_once QA_INCLUDE_DIR . 'app/post-update.php';


/**
 * Checks for a POSTed click on $question by the current user and returns true if it was permitted and processed. Pass
 * in the question's $answers, all $commentsfollows from it or its answers, and its closing $closepost (or null if
 * none). If there is an error to display, it will be passed out in $error.
 * @param $question
 * @param $answers
 * @param $commentsfollows
 * @param $closepost
 * @param $error
 * @return bool
 */
function qa_page_q_single_click_q($question, $answers, $commentsfollows, $closepost, &$error)
{
	require_once QA_INCLUDE_DIR . 'app/post-update.php';
	require_once QA_INCLUDE_DIR . 'app/limits.php';

	$userid = qa_get_logged_in_userid();
	$handle = qa_get_logged_in_handle();
	$cookieid = qa_cookie_get();

	if (qa_clicked('q_doreopen') && $question['reopenable'] && qa_page_q_click_check_form_code($question, $error)) {
		qa_question_close_clear($question, $closepost, $userid, $handle, $cookieid);
		return true;
	}

	if ((qa_clicked('q_dohide') && $question['hideable']) || (qa_clicked('q_doreject') && $question['moderatable'])) {
		if (qa_page_q_click_check_form_code($question, $error)) {
			qa_question_set_status($question, QA_POST_STATUS_HIDDEN, $userid, $handle, $cookieid, $answers, $commentsfollows, $closepost);
			return true;
		}
	}

	if ((qa_clicked('q_doreshow') && $question['reshowable']) || (qa_clicked('q_doapprove') && $question['moderatable'])) {
		if (qa_page_q_click_check_form_code($question, $error)) {
			if ($question['moderatable'] || $question['reshowimmed']) {
				$status = QA_POST_STATUS_NORMAL;

			} else {
				$in = qa_page_q_prepare_post_for_filters($question);
				$filtermodules = qa_load_modules_with('filter', 'filter_question'); // run through filters but only for queued status

				foreach ($filtermodules as $filtermodule) {
					$tempin = $in; // always pass original question in because we aren't modifying anything else
					$filtermodule->filter_question($tempin, $temperrors, $question);
					$in['queued'] = $tempin['queued']; // only preserve queued status in loop
				}

				$status = $in['queued'] ? QA_POST_STATUS_QUEUED : QA_POST_STATUS_NORMAL;
			}

			qa_question_set_status($question, $status, $userid, $handle, $cookieid, $answers, $commentsfollows, $closepost);
			return true;
		}
	}

	if (qa_clicked('q_doclaim') && $question['claimable'] && qa_page_q_click_check_form_code($question, $error)) {
		if (qa_user_limits_remaining(QA_LIMIT_QUESTIONS)) { // already checked 'permit_post_q'
			qa_question_set_userid($question, $userid, $handle, $cookieid);
			return true;

		} else
			$error = qa_lang_html('question/ask_limit');
	}

	if (qa_clicked('q_doflag') && $question['flagbutton'] && qa_page_q_click_check_form_code($question, $error)) {
		require_once QA_INCLUDE_DIR . 'app/votes.php';

		$error = qa_flag_error_html($question, $userid, qa_request());
		if (!$error) {
			if (qa_flag_set_tohide($question, $userid, $handle, $cookieid, $question))
				qa_question_set_status($question, QA_POST_STATUS_HIDDEN, null, null, null, $answers, $commentsfollows, $closepost); // hiding not really by this user so pass nulls
			return true;
		}
	}

	if (qa_clicked('q_dounflag') && $question['unflaggable'] && qa_page_q_click_check_form_code($question, $error)) {
		require_once QA_INCLUDE_DIR . 'app/votes.php';

		qa_flag_clear($question, $userid, $handle, $cookieid);
		return true;
	}

	if (qa_clicked('q_doclearflags') && $question['clearflaggable'] && qa_page_q_click_check_form_code($question, $error)) {
		require_once QA_INCLUDE_DIR . 'app/votes.php';

		qa_flags_clear_all($question, $userid, $handle, $cookieid);
		return true;
	}

	return false;
}


/**
 * Checks for a POSTed click on $answer by the current user and returns true if it was permitted and processed. Pass in
 * the $question, all of its $answers, and all $commentsfollows from it or its answers. Set $allowselectmove to whether
 * it is legitimate to change the selected answer for the question from one to another (this can't be done via Ajax).
 * If there is an error to display, it will be passed out in $error.
 * @param $answer
 * @param $question
 * @param $answers
 * @param $commentsfollows
 * @param $allowselectmove
 * @param $error
 * @return bool
 */
function qa_page_q_single_click_a($answer, $question, $answers, $commentsfollows, $allowselectmove, &$error)
{
	$userid = qa_get_logged_in_userid();
	$handle = qa_get_logged_in_handle();
	$cookieid = qa_cookie_get();

	$prefix = 'a' . $answer['postid'] . '_';

	if (qa_clicked($prefix . 'doselect') && $question['aselectable'] && ($allowselectmove || ((!isset($question['selchildid'])) && !qa_opt('do_close_on_select'))) && qa_page_q_click_check_form_code($answer, $error)) {
		qa_question_set_selchildid($userid, $handle, $cookieid, $question, $answer['postid'], $answers);
		return true;
	}

	if (qa_clicked($prefix . 'dounselect') && $question['aselectable'] && ($question['selchildid'] == $answer['postid']) && ($allowselectmove || !qa_opt('do_close_on_select')) && qa_page_q_click_check_form_code($answer, $error)) {
		qa_question_set_selchildid($userid, $handle, $cookieid, $question, null, $answers);
		return true;
	}

	if ((qa_clicked($prefix . 'dohide') && $answer['hideable']) || (qa_clicked($prefix . 'doreject') && $answer['moderatable'])) {
		if (qa_page_q_click_check_form_code($answer, $error)) {
			qa_answer_set_status($answer, QA_POST_STATUS_HIDDEN, $userid, $handle, $cookieid, $question, $commentsfollows);
			return true;
		}
	}

	if ((qa_clicked($prefix . 'doreshow') && $answer['reshowable']) || (qa_clicked($prefix . 'doapprove') && $answer['moderatable'])) {
		if (qa_page_q_click_check_form_code($answer, $error)) {
			if ($answer['moderatable'] || $answer['reshowimmed']) {
				$status = QA_POST_STATUS_NORMAL;

			} else {
				$in = qa_page_q_prepare_post_for_filters($answer);
				$filtermodules = qa_load_modules_with('filter', 'filter_answer'); // run through filters but only for queued status

				foreach ($filtermodules as $filtermodule) {
					$tempin = $in; // always pass original answer in because we aren't modifying anything else
					$filtermodule->filter_answer($tempin, $temperrors, $question, $answer);
					$in['queued'] = $tempin['queued']; // only preserve queued status in loop
				}

				$status = $in['queued'] ? QA_POST_STATUS_QUEUED : QA_POST_STATUS_NORMAL;
			}

			qa_answer_set_status($answer, $status, $userid, $handle, $cookieid, $question, $commentsfollows);
			return true;
		}
	}

	if (qa_clicked($prefix . 'dodelete') && $answer['deleteable'] && qa_page_q_click_check_form_code($answer, $error)) {
		qa_answer_delete($answer, $question, $userid, $handle, $cookieid);
		return true;
	}

	if (qa_clicked($prefix . 'doclaim') && $answer['claimable'] && qa_page_q_click_check_form_code($answer, $error)) {
		if (qa_user_limits_remaining(QA_LIMIT_ANSWERS)) { // already checked 'permit_post_a'
			qa_answer_set_userid($answer, $userid, $handle, $cookieid);
			return true;

		} else
			$error = qa_lang_html('question/answer_limit');
	}

	if (qa_clicked($prefix . 'doflag') && $answer['flagbutton'] && qa_page_q_click_check_form_code($answer, $error)) {
		require_once QA_INCLUDE_DIR . 'app/votes.php';

		$error = qa_flag_error_html($answer, $userid, qa_request());
		if (!$error) {
			if (qa_flag_set_tohide($answer, $userid, $handle, $cookieid, $question))
				qa_answer_set_status($answer, QA_POST_STATUS_HIDDEN, null, null, null, $question, $commentsfollows); // hiding not really by this user so pass nulls

			return true;
		}
	}

	if (qa_clicked($prefix . 'dounflag') && $answer['unflaggable'] && qa_page_q_click_check_form_code($answer, $error)) {
		require_once QA_INCLUDE_DIR . 'app/votes.php';

		qa_flag_clear($answer, $userid, $handle, $cookieid);
		return true;
	}

	if (qa_clicked($prefix . 'doclearflags') && $answer['clearflaggable'] && qa_page_q_click_check_form_code($answer, $error)) {
		require_once QA_INCLUDE_DIR . 'app/votes.php';

		qa_flags_clear_all($answer, $userid, $handle, $cookieid);
		return true;
	}

	return false;
}


/**
 * Checks for a POSTed click on $comment by the current user and returns true if it was permitted and processed. Pass
 * in the antecedent $question and the comment's $parent post. If there is an error to display, it will be passed out
 * in $error.
 * @param $comment
 * @param $question
 * @param $parent
 * @param $error
 * @return bool
 */
function qa_page_q_single_click_c($comment, $question, $parent, &$error)
{
	$userid = qa_get_logged_in_userid();
	$handle = qa_get_logged_in_handle();
	$cookieid = qa_cookie_get();

	$prefix = 'c' . $comment['postid'] . '_';

	if ((qa_clicked($prefix . 'dohide') && $comment['hideable']) || (qa_clicked($prefix . 'doreject') && $comment['moderatable'])) {
		if (qa_page_q_click_check_form_code($parent, $error)) {
			qa_comment_set_status($comment, QA_POST_STATUS_HIDDEN, $userid, $handle, $cookieid, $question, $parent);
			return true;
		}
	}

	if ((qa_clicked($prefix . 'doreshow') && $comment['reshowable']) || (qa_clicked($prefix . 'doapprove') && $comment['moderatable'])) {
		if (qa_page_q_click_check_form_code($parent, $error)) {
			if ($comment['moderatable'] || $comment['reshowimmed']) {
				$status = QA_POST_STATUS_NORMAL;

			} else {
				$in = qa_page_q_prepare_post_for_filters($comment);
				$filtermodules = qa_load_modules_with('filter', 'filter_comment'); // run through filters but only for queued status

				foreach ($filtermodules as $filtermodule) {
					$tempin = $in; // always pass original comment in because we aren't modifying anything else
					$filtermodule->filter_comment($tempin, $temperrors, $question, $parent, $comment);
					$in['queued'] = $tempin['queued']; // only preserve queued status in loop
				}

				$status = $in['queued'] ? QA_POST_STATUS_QUEUED : QA_POST_STATUS_NORMAL;
			}

			qa_comment_set_status($comment, $status, $userid, $handle, $cookieid, $question, $parent);
			return true;
		}
	}

	if (qa_clicked($prefix . 'dodelete') && $comment['deleteable'] && qa_page_q_click_check_form_code($parent, $error)) {
		qa_comment_delete($comment, $question, $parent, $userid, $handle, $cookieid);
		return true;
	}

	if (qa_clicked($prefix . 'doclaim') && $comment['claimable'] && qa_page_q_click_check_form_code($parent, $error)) {
		if (qa_user_limits_remaining(QA_LIMIT_COMMENTS)) {
			qa_comment_set_userid($comment, $userid, $handle, $cookieid);
			return true;

		} else
			$error = qa_lang_html('question/comment_limit');
	}

	if (qa_clicked($prefix . 'doflag') && $comment['flagbutton'] && qa_page_q_click_check_form_code($parent, $error)) {
		require_once QA_INCLUDE_DIR . 'app/votes.php';

		$error = qa_flag_error_html($comment, $userid, qa_request());
		if (!$error) {
			if (qa_flag_set_tohide($comment, $userid, $handle, $cookieid, $question))
				qa_comment_set_status($comment, QA_POST_STATUS_HIDDEN, null, null, null, $question, $parent); // hiding not really by this user so pass nulls

			return true;
		}
	}

	if (qa_clicked($prefix . 'dounflag') && $comment['unflaggable'] && qa_page_q_click_check_form_code($parent, $error)) {
		require_once QA_INCLUDE_DIR . 'app/votes.php';

		qa_flag_clear($comment, $userid, $handle, $cookieid);
		return true;
	}

	if (qa_clicked($prefix . 'doclearflags') && $comment['clearflaggable'] && qa_page_q_click_check_form_code($parent, $error)) {
		require_once QA_INCLUDE_DIR . 'app/votes.php';

		qa_flags_clear_all($comment, $userid, $handle, $cookieid);
		return true;
	}

	return false;
}


/**
 * Check the form security (anti-CSRF protection) for one of the buttons shown for post $post. Return true if the
 * security passed, otherwise return false and set an error message in $error
 * @param $post
 * @param $error
 * @return bool
 */
function qa_page_q_click_check_form_code($post, &$error)
{
	$result = qa_check_form_security_code('buttons-' . $post['postid'], qa_post_text('code'));

	if (!$result)
		$error = qa_lang_html('misc/form_security_again');

	return $result;
}


/**
 * Processes a POSTed form to add an answer to $question, returning the postid if successful, otherwise null. Pass in
 * other $answers to the question and whether a $usecaptcha is required. The form fields submitted will be passed out
 * as an array in $in, as well as any $errors on those fields.
 * @param $question
 * @param $answers
 * @param $usecaptcha
 * @param $in
 * @param $errors
 * @return mixed|null
 */
function qa_page_q_add_a_submit($question, $answers, $usecaptcha, &$in, &$errors)
{
	$in = array(
		'name' => qa_opt('allow_anonymous_naming') ? qa_post_text('a_name') : null,
		'notify' => qa_post_text('a_notify') !== null,
		'email' => qa_post_text('a_email'),
		'queued' => qa_user_moderation_reason(qa_user_level_for_post($question)) !== false,
	);

	qa_get_post_content('a_editor', 'a_content', $in['editor'], $in['content'], $in['format'], $in['text']);

	$errors = array();

	if (!qa_check_form_security_code('answer-' . $question['postid'], qa_post_text('code')))
		$errors['content'] = qa_lang_html('misc/form_security_again');

	else {
		// call any filter plugins
		$filtermodules = qa_load_modules_with('filter', 'filter_answer');
		foreach ($filtermodules as $filtermodule) {
			$oldin = $in;
			$filtermodule->filter_answer($in, $errors, $question, null);
			qa_update_post_text($in, $oldin);
		}

		// check CAPTCHA
		if ($usecaptcha)
			qa_captcha_validate_post($errors);

		// check for duplicate posts
		if (empty($errors)) {
			$testwords = implode(' ', qa_string_to_words($in['content']));

			foreach ($answers as $answer) {
				if (!$answer['hidden']) {
					if (implode(' ', qa_string_to_words($answer['content'])) == $testwords) {
						$errors['content'] = qa_lang_html('question/duplicate_content');
						break;
					}
				}
			}
		}

		$userid = qa_get_logged_in_userid();

		// if this is an additional answer, check we can add it
		if (empty($errors) && !qa_opt('allow_multi_answers')) {
			foreach ($answers as $answer) {
				if (qa_post_is_by_user($answer, $userid, qa_cookie_get())) {
					$errors[] = '';
					break;
				}
			}
		}

		// create the answer
		if (empty($errors)) {
			$handle = qa_get_logged_in_handle();
			$cookieid = isset($userid) ? qa_cookie_get() : qa_cookie_get_create(); // create a new cookie if necessary

			$answerid = qa_answer_create($userid, $handle, $cookieid, $in['content'], $in['format'], $in['text'], $in['notify'], $in['email'],
				$question, $in['queued'], $in['name']);

			return $answerid;
		}
	}

	return null;
}


/**
 * Processes a POSTed form to add a comment, returning the postid if successful, otherwise null. Pass in the antecedent
 * $question and the comment's $parent post. Set $usecaptcha to whether a captcha is required. Pass an array which
 * includes the other comments with the same parent in $commentsfollows (it can contain other posts which are ignored).
 * The form fields submitted will be passed out as an array in $in, as well as any $errors on those fields.
 * @param $question
 * @param $parent
 * @param $commentsfollows
 * @param $usecaptcha
 * @param $in
 * @param $errors
 * @return mixed|null
 */
function qa_page_q_add_c_submit($question, $parent, $commentsfollows, $usecaptcha, &$in, &$errors)
{
	$parentid = $parent['postid'];

	$prefix = 'c' . $parentid . '_';

	$in = array(
		'name' => qa_opt('allow_anonymous_naming') ? qa_post_text($prefix . 'name') : null,
		'notify' => qa_post_text($prefix . 'notify') !== null,
		'email' => qa_post_text($prefix . 'email'),
		'queued' => qa_user_moderation_reason(qa_user_level_for_post($parent)) !== false,
	);

	qa_get_post_content($prefix . 'editor', $prefix . 'content', $in['editor'], $in['content'], $in['format'], $in['text']);

	$errors = array();

	if (!qa_check_form_security_code('comment-' . $parent['postid'], qa_post_text($prefix . 'code')))
		$errors['content'] = qa_lang_html('misc/form_security_again');

	else {
		$filtermodules = qa_load_modules_with('filter', 'filter_comment');
		foreach ($filtermodules as $filtermodule) {
			$oldin = $in;
			$filtermodule->filter_comment($in, $errors, $question, $parent, null);
			qa_update_post_text($in, $oldin);
		}

		if ($usecaptcha)
			qa_captcha_validate_post($errors);

		if (empty($errors)) {
			$testwords = implode(' ', qa_string_to_words($in['content']));

			foreach ($commentsfollows as $comment) {
				if ($comment['basetype'] == 'C' && $comment['parentid'] == $parentid && !$comment['hidden']) {
					if (implode(' ', qa_string_to_words($comment['content'])) == $testwords) {
						$errors['content'] = qa_lang_html('question/duplicate_content');
						break;
					}
				}
			}
		}

		if (empty($errors)) {
			$userid = qa_get_logged_in_userid();
			$handle = qa_get_logged_in_handle();
			$cookieid = isset($userid) ? qa_cookie_get() : qa_cookie_get_create(); // create a new cookie if necessary

			$commentid = qa_comment_create($userid, $handle, $cookieid, $in['content'], $in['format'], $in['text'], $in['notify'], $in['email'],
				$question, $parent, $commentsfollows, $in['queued'], $in['name']);

			return $commentid;
		}
	}

	return null;
}


/**
 * Return the array of information to be passed to filter modules for the post in $post (from the database)
 * @param $post
 * @return array
 */
function qa_page_q_prepare_post_for_filters($post)
{
	$in = array(
		'content' => $post['content'],
		'format' => $post['format'],
		'text' => qa_viewer_text($post['content'], $post['format']),
		'notify' => isset($post['notify']),
		'email' => qa_email_validate($post['notify']) ? $post['notify'] : null,
		'queued' => qa_user_moderation_reason(qa_user_level_for_post($post)) !== false,
	);

	if ($post['basetype'] == 'Q') {
		$in['title'] = $post['title'];
		$in['tags'] = qa_tagstring_to_tags($post['tags']);
		$in['categoryid'] = $post['categoryid'];
		$in['extra'] = $post['extra'];
	}

	return $in;
}