Files
godot-question2answer/qa-include/pages/question-submit.php
Emi 07ec659385 Importing project into Git
This project lived only on the server without version control. This is now the starting point for the repository.
2023-05-23 20:03:24 +02:00

520 lines
19 KiB
PHP

<?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;
}