question-post.php 33.2 KB
Newer Older
Scott committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
<?php
/*
	Question2Answer by Gideon Greenspan and contributors
	http://www.question2answer.org/

	Description: More control for question page if it's submitted by HTTP POST


	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
*/

Scott committed
22
if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser
23
	header('Location: ../../');
Scott committed
24 25
	exit;
}
Scott committed
26

Scott committed
27 28
require_once QA_INCLUDE_DIR . 'app/limits.php';
require_once QA_INCLUDE_DIR . 'pages/question-submit.php';
Scott committed
29 30


Scott committed
31
$code = qa_post_text('code');
Scott committed
32 33


Scott committed
34
// Process general cancel button
Scott committed
35

Scott committed
36 37
if (qa_clicked('docancel'))
	qa_page_q_refresh($pagestart);
Scott committed
38 39


Scott committed
40
// Process incoming answer (or button)
Scott committed
41

Scott committed
42 43 44
if ($question['answerbutton']) {
	if (qa_clicked('q_doanswer'))
		qa_page_q_refresh($pagestart, 'answer');
Scott committed
45

Scott committed
46 47
	// The 'approve', 'login', 'confirm', 'limit', 'userblock', 'ipblock' permission errors are reported to the user here
	// The other option ('level') prevents the answer button being shown, in qa_page_q_post_rules(...)
Scott committed
48

Scott committed
49 50 51 52 53
	if (qa_clicked('a_doadd') || $pagestate == 'answer') {
		switch (qa_user_post_permit_error('permit_post_a', $question, QA_LIMIT_ANSWERS)) {
			case 'login':
				$pageerror = qa_insert_login_links(qa_lang_html('question/answer_must_login'), qa_request());
				break;
Scott committed
54

Scott committed
55 56 57
			case 'confirm':
				$pageerror = qa_insert_login_links(qa_lang_html('question/answer_must_confirm'), qa_request());
				break;
Scott committed
58

Scott committed
59
			case 'approve':
60 61 62 63
				$pageerror = strtr(qa_lang_html('question/answer_must_be_approved'), array(
					'^1' => '<a href="' . qa_path_html('account') . '">',
					'^2' => '</a>',
				));
Scott committed
64
				break;
Scott committed
65

Scott committed
66 67 68
			case 'limit':
				$pageerror = qa_lang_html('question/answer_limit');
				break;
Scott committed
69

Scott committed
70 71 72
			default:
				$pageerror = qa_lang_html('users/no_permission');
				break;
Scott committed
73

Scott committed
74 75 76
			case false:
				if (qa_clicked('a_doadd')) {
					$answerid = qa_page_q_add_a_submit($question, $answers, $usecaptcha, $anewin, $anewerrors);
Scott committed
77

Scott committed
78 79 80 81
					if (isset($answerid))
						qa_page_q_refresh(0, null, 'A', $answerid);
					else
						$formtype = 'a_add'; // show form again
Scott committed
82

Scott committed
83 84 85 86
				} else
					$formtype = 'a_add'; // show form as if first time
				break;
		}
Scott committed
87
	}
Scott committed
88
}
Scott committed
89 90


Scott committed
91
// Process close buttons for question
Scott committed
92

Scott committed
93 94 95
if ($question['closeable']) {
	if (qa_clicked('q_doclose'))
		qa_page_q_refresh($pagestart, 'close');
Scott committed
96

Scott committed
97 98 99 100 101
	elseif (qa_clicked('doclose') && qa_page_q_permit_edit($question, 'permit_close_q', $pageerror)) {
		if (qa_page_q_close_q_submit($question, $closepost, $closein, $closeerrors))
			qa_page_q_refresh($pagestart);
		else
			$formtype = 'q_close'; // keep editing if an error
Scott committed
102

Scott committed
103
	} elseif ($pagestate == 'close' && qa_page_q_permit_edit($question, 'permit_close_q', $pageerror))
Scott committed
104 105
		$formtype = 'q_close';
}
Scott committed
106 107


Scott committed
108
// Process any single click operations or delete button for question
Scott committed
109

Scott committed
110 111
if (qa_page_q_single_click_q($question, $answers, $commentsfollows, $closepost, $pageerror))
	qa_page_q_refresh($pagestart);
Scott committed
112

Scott committed
113 114 115 116
if (qa_clicked('q_dodelete') && $question['deleteable'] && qa_page_q_click_check_form_code($question, $pageerror)) {
	qa_question_delete($question, $userid, qa_get_logged_in_handle(), $cookieid, $closepost);
	qa_redirect(''); // redirect since question has gone
}
Scott committed
117 118


Scott committed
119
// Process edit or save button for question
Scott committed
120

Scott committed
121 122 123
if ($question['editbutton'] || $question['retagcatbutton']) {
	if (qa_clicked('q_doedit'))
		qa_page_q_refresh($pagestart, 'edit-' . $questionid);
Scott committed
124

Scott committed
125 126 127 128 129 130 131
	elseif (qa_clicked('q_dosave') && qa_page_q_permit_edit($question, 'permit_edit_q', $pageerror, 'permit_retag_cat')) {
		if (qa_page_q_edit_q_submit($question, $answers, $commentsfollows, $closepost, $qin, $qerrors))
			qa_redirect(qa_q_request($questionid, $qin['title'])); // don't use refresh since URL may have changed
		else {
			$formtype = 'q_edit'; // keep editing if an error
			$pageerror = @$qerrors['page']; // for security code failure
		}
Scott committed
132

Scott committed
133 134
	} elseif ($pagestate == ('edit-' . $questionid) && qa_page_q_permit_edit($question, 'permit_edit_q', $pageerror, 'permit_retag_cat'))
		$formtype = 'q_edit';
Scott committed
135

Scott committed
136 137 138 139 140
	if ($formtype == 'q_edit') { // get tags for auto-completion
		if (qa_opt('do_complete_tags'))
			$completetags = array_keys(qa_db_select_with_pending(qa_db_popular_tags_selectspec(0, QA_DB_RETRIEVE_COMPLETE_TAGS)));
		else
			$completetags = array();
Scott committed
141
	}
Scott committed
142
}
Scott committed
143 144


Scott committed
145
// Process adding a comment to question (shows form or processes it)
Scott committed
146

Scott committed
147 148 149
if ($question['commentbutton']) {
	if (qa_clicked('q_docomment'))
		qa_page_q_refresh($pagestart, 'comment-' . $questionid, 'C', $questionid);
Scott committed
150

Scott committed
151 152 153
	if (qa_clicked('c' . $questionid . '_doadd') || $pagestate == ('comment-' . $questionid))
		qa_page_q_do_comment($question, $question, $commentsfollows, $pagestart, $usecaptcha, $cnewin, $cnewerrors, $formtype, $formpostid, $pageerror);
}
Scott committed
154 155


Scott committed
156
// Process clicked buttons for answers
Scott committed
157

Scott committed
158 159
foreach ($answers as $answerid => $answer) {
	$prefix = 'a' . $answerid . '_';
Scott committed
160

Scott committed
161 162
	if (qa_page_q_single_click_a($answer, $question, $answers, $commentsfollows, true, $pageerror))
		qa_page_q_refresh($pagestart, null, 'A', $answerid);
Scott committed
163

Scott committed
164 165 166
	if ($answer['editbutton']) {
		if (qa_clicked($prefix . 'doedit'))
			qa_page_q_refresh($pagestart, 'edit-' . $answerid);
Scott committed
167

Scott committed
168 169
		elseif (qa_clicked($prefix . 'dosave') && qa_page_q_permit_edit($answer, 'permit_edit_a', $pageerror)) {
			$editedtype = qa_page_q_edit_a_submit($answer, $question, $answers, $commentsfollows, $aeditin[$answerid], $aediterrors[$answerid]);
Scott committed
170

Scott committed
171 172
			if (isset($editedtype))
				qa_page_q_refresh($pagestart, null, $editedtype, $answerid);
Scott committed
173

Scott committed
174 175 176
			else {
				$formtype = 'a_edit';
				$formpostid = $answerid; // keep editing if an error
Scott committed
177 178
			}

Scott committed
179 180 181
		} elseif ($pagestate == ('edit-' . $answerid) && qa_page_q_permit_edit($answer, 'permit_edit_a', $pageerror)) {
			$formtype = 'a_edit';
			$formpostid = $answerid;
Scott committed
182
		}
Scott committed
183
	}
Scott committed
184

Scott committed
185 186 187
	if ($answer['commentbutton']) {
		if (qa_clicked($prefix . 'docomment'))
			qa_page_q_refresh($pagestart, 'comment-' . $answerid, 'C', $answerid);
Scott committed
188

Scott committed
189 190
		if (qa_clicked('c' . $answerid . '_doadd') || $pagestate == ('comment-' . $answerid))
			qa_page_q_do_comment($question, $answer, $commentsfollows, $pagestart, $usecaptcha, $cnewin, $cnewerrors, $formtype, $formpostid, $pageerror);
Scott committed
191 192
	}

Scott committed
193 194 195 196
	if (qa_clicked($prefix . 'dofollow')) {
		$params = array('follow' => $answerid);
		if (isset($question['categoryid']))
			$params['cat'] = $question['categoryid'];
Scott committed
197

Scott committed
198 199 200
		qa_redirect('ask', $params);
	}
}
Scott committed
201 202


Scott committed
203
// Process hide, show, delete, flag, unflag, edit or save button for comments
Scott committed
204

Scott committed
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
foreach ($commentsfollows as $commentid => $comment) {
	if ($comment['basetype'] == 'C') {
		$cparentid = $comment['parentid'];
		$commentparent = isset($answers[$cparentid]) ? $answers[$cparentid] : $question;
		$prefix = 'c' . $commentid . '_';

		if (qa_page_q_single_click_c($comment, $question, $commentparent, $pageerror))
			qa_page_q_refresh($pagestart, 'showcomments-' . $cparentid, $commentparent['basetype'], $cparentid);

		if ($comment['editbutton']) {
			if (qa_clicked($prefix . 'doedit')) {
				if (qa_page_q_permit_edit($comment, 'permit_edit_c', $pageerror)) // extra check here ensures error message is visible
					qa_page_q_refresh($pagestart, 'edit-' . $commentid, 'C', $commentid);
			} elseif (qa_clicked($prefix . 'dosave') && qa_page_q_permit_edit($comment, 'permit_edit_c', $pageerror)) {
				if (qa_page_q_edit_c_submit($comment, $question, $commentparent, $ceditin[$commentid], $cediterrors[$commentid]))
					qa_page_q_refresh($pagestart, null, 'C', $commentid);
				else {
Scott committed
222
					$formtype = 'c_edit';
Scott committed
223
					$formpostid = $commentid; // keep editing if an error
Scott committed
224
				}
Scott committed
225
			} elseif ($pagestate == ('edit-' . $commentid) && qa_page_q_permit_edit($comment, 'permit_edit_c', $pageerror)) {
Scott committed
226 227
				$formtype = 'c_edit';
				$formpostid = $commentid;
Scott committed
228 229 230
			}
		}
	}
Scott committed
231
}
Scott committed
232 233


234
// Functions used above - also see functions in /qa-include/pages/question-submit.php (which are shared with Ajax)
Scott committed
235 236 237 238

/*
	Redirects back to the question page, with the specified parameters
*/
Scott committed
239 240 241
function qa_page_q_refresh($start = 0, $state = null, $showtype = null, $showid = null)
{
	$params = array();
Scott committed
242

Scott committed
243 244 245 246
	if ($start > 0)
		$params['start'] = $start;
	if (isset($state))
		$params['state'] = $state;
Scott committed
247

Scott committed
248 249 250 251 252
	if (isset($showtype) && isset($showid)) {
		$anchor = qa_anchor($showtype, $showid);
		$params['show'] = $showid;
	} else
		$anchor = null;
Scott committed
253

Scott committed
254 255
	qa_redirect(qa_request(), $params, null, null, $anchor);
}
Scott committed
256 257 258 259 260 261


/*
	Returns whether the editing operation (as specified by $permitoption or $permitoption2) on $post is permitted.
	If not, sets the $error variable appropriately
*/
Scott committed
262 263 264 265
function qa_page_q_permit_edit($post, $permitoption, &$error, $permitoption2 = null)
{
	// The 'login', 'confirm', 'userblock', 'ipblock' permission errors are reported to the user here
	// The other options ('approve', 'level') prevent the edit button being shown, in qa_page_q_post_rules(...)
Scott committed
266

Scott committed
267 268
	$permiterror = qa_user_post_permit_error($post['isbyuser'] ? null : $permitoption, $post);
	// if it's by the user, this will only check whether they are blocked
Scott committed
269

Scott committed
270 271
	if ($permiterror && isset($permitoption2)) {
		$permiterror2 = qa_user_post_permit_error($post['isbyuser'] ? null : $permitoption2, $post);
Scott committed
272

Scott committed
273 274 275
		if ($permiterror == 'level' || $permiterror == 'approve' || !$permiterror2) // if it's a less strict error
			$permiterror = $permiterror2;
	}
Scott committed
276

Scott committed
277 278 279 280
	switch ($permiterror) {
		case 'login':
			$error = qa_insert_login_links(qa_lang_html('question/edit_must_login'), qa_request());
			break;
Scott committed
281

Scott committed
282 283 284
		case 'confirm':
			$error = qa_insert_login_links(qa_lang_html('question/edit_must_confirm'), qa_request());
			break;
Scott committed
285

Scott committed
286 287 288
		default:
			$error = qa_lang_html('users/no_permission');
			break;
Scott committed
289

Scott committed
290 291
		case false:
			break;
Scott committed
292 293
	}

Scott committed
294 295 296
	return !$permiterror;
}

Scott committed
297 298 299 300

/*
	Returns a $qa_content form for editing the question and sets up other parts of $qa_content accordingly
*/
Scott committed
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315
function qa_page_q_edit_q_form(&$qa_content, $question, $in, $errors, $completetags, $categories)
{
	$form = array(
		'tags' => 'method="post" action="' . qa_self_html() . '"',

		'style' => 'tall',

		'fields' => array(
			'title' => array(
				'type' => $question['editable'] ? 'text' : 'static',
				'label' => qa_lang_html('question/q_title_label'),
				'tags' => 'name="q_title"',
				'value' => qa_html(($question['editable'] && isset($in['title'])) ? $in['title'] : $question['title']),
				'error' => qa_html(@$errors['title']),
			),
Scott committed
316

Scott committed
317 318 319 320
			'category' => array(
				'label' => qa_lang_html('question/q_category_label'),
				'error' => qa_html(@$errors['categoryid']),
			),
Scott committed
321

Scott committed
322 323 324 325
			'content' => array(
				'label' => qa_lang_html('question/q_content_label'),
				'error' => qa_html(@$errors['content']),
			),
Scott committed
326

Scott committed
327 328 329 330 331
			'extra' => array(
				'label' => qa_html(qa_opt('extra_field_prompt')),
				'tags' => 'name="q_extra"',
				'value' => qa_html(isset($in['extra']) ? $in['extra'] : $question['extra']),
				'error' => qa_html(@$errors['extra']),
Scott committed
332 333
			),

Scott committed
334 335 336
			'tags' => array(
				'error' => qa_html(@$errors['tags']),
			),
Scott committed
337

Scott committed
338 339 340 341 342 343
		),

		'buttons' => array(
			'save' => array(
				'tags' => 'onclick="qa_show_waiting_after(this, false);"',
				'label' => qa_lang_html('main/save_button'),
Scott committed
344 345
			),

Scott committed
346 347 348
			'cancel' => array(
				'tags' => 'name="docancel"',
				'label' => qa_lang_html('main/cancel_button'),
Scott committed
349
			),
Scott committed
350
		),
Scott committed
351

Scott committed
352 353 354 355 356
		'hidden' => array(
			'q_dosave' => '1',
			'code' => qa_get_form_security_code('edit-' . $question['postid']),
		),
	);
Scott committed
357

Scott committed
358 359 360
	if ($question['editable']) {
		$content = isset($in['content']) ? $in['content'] : $question['content'];
		$format = isset($in['format']) ? $in['format'] : $question['format'];
Scott committed
361

Scott committed
362 363
		$editorname = isset($in['editor']) ? $in['editor'] : qa_opt('editor_for_qs');
		$editor = qa_load_editor($content, $format, $editorname);
Scott committed
364

Scott committed
365 366
		$form['fields']['content'] = array_merge($form['fields']['content'],
			qa_editor_load_field($editor, $qa_content, $content, $format, 'q_content', 12, true));
Scott committed
367

Scott committed
368 369
		if (method_exists($editor, 'update_script'))
			$form['buttons']['save']['tags'] = 'onclick="qa_show_waiting_after(this, false); ' . $editor->update_script('q_content') . '"';
Scott committed
370

Scott committed
371
		$form['hidden']['q_editor'] = qa_html($editorname);
Scott committed
372

Scott committed
373 374
	} else
		unset($form['fields']['content']);
Scott committed
375

Scott committed
376 377 378 379 380 381 382
	if (qa_using_categories() && count($categories) && $question['retagcatable']) {
		qa_set_up_category_field($qa_content, $form['fields']['category'], 'q_category', $categories,
			isset($in['categoryid']) ? $in['categoryid'] : $question['categoryid'],
			qa_opt('allow_no_category') || !isset($question['categoryid']), qa_opt('allow_no_sub_category'));
	} else {
		unset($form['fields']['category']);
	}
Scott committed
383

Scott committed
384 385
	if (!($question['editable'] && qa_opt('extra_field_active')))
		unset($form['fields']['extra']);
Scott committed
386

Scott committed
387 388 389 390 391 392
	if (qa_using_tags() && $question['retagcatable']) {
		qa_set_up_tag_field($qa_content, $form['fields']['tags'], 'q_tags', isset($in['tags']) ? $in['tags'] : qa_tagstring_to_tags($question['tags']),
			array(), $completetags, qa_opt('page_size_ask_tags'));
	} else {
		unset($form['fields']['tags']);
	}
Scott committed
393

Scott committed
394
	if ($question['isbyuser']) {
395
		if (!qa_is_logged_in() && qa_opt('allow_anonymous_naming'))
Scott committed
396
			qa_set_up_name_field($qa_content, $form['fields'], isset($in['name']) ? $in['name'] : @$question['name'], 'q_');
Scott committed
397

Scott committed
398 399 400 401
		qa_set_up_notify_fields($qa_content, $form['fields'], 'Q', qa_get_logged_in_email(),
			isset($in['notify']) ? $in['notify'] : !empty($question['notify']),
			isset($in['email']) ? $in['email'] : @$question['notify'], @$errors['email'], 'q_');
	}
Scott committed
402

Scott committed
403 404 405 406 407 408 409
	if (!qa_user_post_permit_error('permit_edit_silent', $question)) {
		$form['fields']['silent'] = array(
			'type' => 'checkbox',
			'label' => qa_lang_html('question/save_silent_label'),
			'tags' => 'name="q_silent"',
			'value' => qa_html(@$in['silent']),
		);
Scott committed
410 411
	}

Scott committed
412 413 414
	return $form;
}

Scott committed
415 416 417 418

/*
	Processes a POSTed form for editing the question and returns true if successful
*/
Scott committed
419 420 421 422 423 424 425 426 427
function qa_page_q_edit_q_submit($question, $answers, $commentsfollows, $closepost, &$in, &$errors)
{
	$in = array();

	if ($question['editable']) {
		$in['title'] = qa_get_post_title('q_title');
		qa_get_post_content('q_editor', 'q_content', $in['editor'], $in['content'], $in['format'], $in['text']);
		$in['extra'] = qa_opt('extra_field_active') ? qa_post_text('q_extra') : null;
	}
Scott committed
428

Scott committed
429 430 431
	if ($question['retagcatable']) {
		if (qa_using_tags())
			$in['tags'] = qa_get_tags_field_value('q_tags');
Scott committed
432

Scott committed
433 434 435
		if (qa_using_categories())
			$in['categoryid'] = qa_get_category_field_value('q_category');
	}
Scott committed
436

Scott committed
437 438 439 440
	if (array_key_exists('categoryid', $in)) { // need to check if we can move it to that category, and if we need moderation
		$categories = qa_db_select_with_pending(qa_db_category_nav_selectspec($in['categoryid'], true));
		$categoryids = array_keys(qa_category_path($categories, $in['categoryid']));
		$userlevel = qa_user_level_for_categories($categoryids);
Scott committed
441

Scott committed
442 443
	} else
		$userlevel = null;
Scott committed
444

Scott committed
445
	if ($question['isbyuser']) {
446
		$in['name'] = qa_opt('allow_anonymous_naming') ? qa_post_text('q_name') : null;
Scott committed
447 448 449
		$in['notify'] = qa_post_text('q_notify') !== null;
		$in['email'] = qa_post_text('q_email');
	}
Scott committed
450

Scott committed
451 452
	if (!qa_user_post_permit_error('permit_edit_silent', $question))
		$in['silent'] = qa_post_text('q_silent');
Scott committed
453

Scott committed
454
	// here the $in array only contains values for parts of the form that were displayed, so those are only ones checked by filters
Scott committed
455

Scott committed
456
	$errors = array();
Scott committed
457

Scott committed
458 459
	if (!qa_check_form_security_code('edit-' . $question['postid'], qa_post_text('code')))
		$errors['page'] = qa_lang_html('misc/form_security_again');
Scott committed
460

Scott committed
461 462
	else {
		$in['queued'] = qa_opt('moderate_edited_again') && qa_user_moderation_reason($userlevel);
Scott committed
463

Scott committed
464 465 466 467
		$filtermodules = qa_load_modules_with('filter', 'filter_question');
		foreach ($filtermodules as $filtermodule) {
			$oldin = $in;
			$filtermodule->filter_question($in, $errors, $question);
Scott committed
468

Scott committed
469 470 471
			if ($question['editable'])
				qa_update_post_text($in, $oldin);
		}
Scott committed
472

Scott committed
473 474 475 476
		if (array_key_exists('categoryid', $in) && strcmp($in['categoryid'], $question['categoryid'])) {
			if (qa_user_permit_error('permit_post_q', null, $userlevel))
				$errors['categoryid'] = qa_lang_html('question/category_ask_not_allowed');
		}
Scott committed
477

Scott committed
478 479 480 481
		if (empty($errors)) {
			$userid = qa_get_logged_in_userid();
			$handle = qa_get_logged_in_handle();
			$cookieid = qa_cookie_get();
Scott committed
482

Scott committed
483 484
			// now we fill in the missing values in the $in array, so that we have everything we need for qa_question_set_content()
			// we do things in this way to avoid any risk of a validation failure on elements the user can't see (e.g. due to admin setting changes)
Scott committed
485

Scott committed
486 487 488 489 490 491 492
			if (!$question['editable']) {
				$in['title'] = $question['title'];
				$in['content'] = $question['content'];
				$in['format'] = $question['format'];
				$in['text'] = qa_viewer_text($in['content'], $in['format']);
				$in['extra'] = $question['extra'];
			}
Scott committed
493

Scott committed
494 495
			if (!isset($in['tags']))
				$in['tags'] = qa_tagstring_to_tags($question['tags']);
Scott committed
496

Scott committed
497 498
			if (!array_key_exists('categoryid', $in))
				$in['categoryid'] = $question['categoryid'];
Scott committed
499

Scott committed
500 501
			if (!isset($in['silent']))
				$in['silent'] = false;
Scott committed
502

Scott committed
503
			$setnotify = $question['isbyuser'] ? qa_combine_notify_email($question['userid'], $in['notify'], $in['email']) : $question['notify'];
Scott committed
504

Scott committed
505 506
			qa_question_set_content($question, $in['title'], $in['content'], $in['format'], $in['text'], qa_tags_to_tagstring($in['tags']),
				$setnotify, $userid, $handle, $cookieid, $in['extra'], @$in['name'], $in['queued'], $in['silent']);
Scott committed
507

Scott committed
508 509 510
			if (qa_using_categories() && strcmp($in['categoryid'], $question['categoryid'])) {
				qa_question_set_category($question, $in['categoryid'], $userid, $handle, $cookieid,
					$answers, $commentsfollows, $closepost, $in['silent']);
Scott committed
511 512
			}

Scott committed
513 514
			return true;
		}
Scott committed
515 516
	}

Scott committed
517 518 519
	return false;
}

Scott committed
520 521 522 523

/*
	Returns a $qa_content form for closing the question and sets up other parts of $qa_content accordingly
*/
Scott committed
524 525 526 527
function qa_page_q_close_q_form(&$qa_content, $question, $id, $in, $errors)
{
	$form = array(
		'tags' => 'method="post" action="' . qa_self_html() . '"',
Scott committed
528

Scott committed
529
		'id' => $id,
Scott committed
530

Scott committed
531
		'style' => 'tall',
Scott committed
532

Scott committed
533
		'title' => qa_lang_html('question/close_form_title'),
Scott committed
534

Scott committed
535 536 537 538 539 540 541
		'fields' => array(
			'details' => array(
				'tags' => 'name="q_close_details" id="q_close_details"',
				'label' =>
					'<span id="close_label_other">' . qa_lang_html('question/close_reason_title') . '</span>',
				'value' => @$in['details'],
				'error' => qa_html(@$errors['details']),
Scott committed
542
			),
Scott committed
543
		),
Scott committed
544

Scott committed
545 546 547 548
		'buttons' => array(
			'close' => array(
				'tags' => 'onclick="qa_show_waiting_after(this, false);"',
				'label' => qa_lang_html('question/close_form_button'),
Scott committed
549 550
			),

Scott committed
551 552 553
			'cancel' => array(
				'tags' => 'name="docancel"',
				'label' => qa_lang_html('main/cancel_button'),
Scott committed
554
			),
Scott committed
555
		),
Scott committed
556

Scott committed
557 558 559 560 561
		'hidden' => array(
			'doclose' => '1',
			'code' => qa_get_form_security_code('close-' . $question['postid']),
		),
	);
Scott committed
562

Scott committed
563 564 565 566
	$qa_content['focusid'] = 'q_close_details';

	return $form;
}
Scott committed
567 568 569 570 571


/*
	Processes a POSTed form for closing the question and returns true if successful
*/
Scott committed
572 573 574 575 576 577 578 579 580 581
function qa_page_q_close_q_submit($question, $closepost, &$in, &$errors)
{
	$in = array(
		'details' => trim(qa_post_text('q_close_details')),
	);

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

582 583
	$sanitizedUrl = filter_var($in['details'], FILTER_SANITIZE_URL);
	$isduplicateurl = filter_var($sanitizedUrl, FILTER_VALIDATE_URL);
Scott committed
584 585 586 587 588 589 590 591 592

	if (!qa_check_form_security_code('close-' . $question['postid'], qa_post_text('code'))) {
		$errors['details'] = qa_lang_html('misc/form_security_again');
	} elseif ($isduplicateurl) {
		// be liberal in what we accept, but there are two potential unlikely pitfalls here:
		// a) URLs could have a fixed numerical path, e.g. http://qa.mysite.com/1/478/...
		// b) There could be a question title which is just a number, e.g. http://qa.mysite.com/478/12345/...
		// so we check if more than one question could match, and if so, show an error

593
		$parts = preg_split('|[=/&]|', $sanitizedUrl, -1, PREG_SPLIT_NO_EMPTY);
Scott committed
594 595 596 597 598
		$keypostids = array();

		foreach ($parts as $part) {
			if (preg_match('/^[0-9]+$/', $part))
				$keypostids[$part] = true;
599
		}
Scott committed
600

Scott committed
601
		$questionids = qa_db_posts_filter_q_postids(array_keys($keypostids));
Scott committed
602

Scott committed
603 604 605
		if (count($questionids) == 1 && $questionids[0] != $question['postid']) {
			qa_question_close_duplicate($question, $closepost, $questionids[0], $userid, $handle, $cookieid);
			return true;
Scott committed
606

Scott committed
607 608
		} else
			$errors['details'] = qa_lang('question/close_duplicate_error');
Scott committed
609

Scott committed
610 611 612 613
	} else {
		if (strlen($in['details']) > 0) {
			qa_question_close_other($question, $closepost, $in['details'], $userid, $handle, $cookieid);
			return true;
Scott committed
614

Scott committed
615 616
		} else
			$errors['details'] = qa_lang('main/field_required');
Scott committed
617 618
	}

Scott committed
619 620 621
	return false;
}

Scott committed
622 623 624 625

/*
	Returns a $qa_content form for editing an answer and sets up other parts of $qa_content accordingly
*/
Scott committed
626 627 628
function qa_page_q_edit_a_form(&$qa_content, $id, $answer, $question, $answers, $commentsfollows, $in, $errors)
{
	require_once QA_INCLUDE_DIR . 'util/string.php';
Scott committed
629

Scott committed
630 631
	$answerid = $answer['postid'];
	$prefix = 'a' . $answerid . '_';
Scott committed
632

Scott committed
633 634
	$content = isset($in['content']) ? $in['content'] : $answer['content'];
	$format = isset($in['format']) ? $in['format'] : $answer['format'];
Scott committed
635

Scott committed
636 637
	$editorname = isset($in['editor']) ? $in['editor'] : qa_opt('editor_for_as');
	$editor = qa_load_editor($content, $format, $editorname);
Scott committed
638

Scott committed
639 640 641 642 643
	$hascomments = false;
	foreach ($commentsfollows as $commentfollow) {
		if ($commentfollow['parentid'] == $answerid)
			$hascomments = true;
	}
Scott committed
644

Scott committed
645 646
	$form = array(
		'tags' => 'method="post" action="' . qa_self_html() . '"',
Scott committed
647

Scott committed
648
		'id' => $id,
Scott committed
649

Scott committed
650
		'title' => qa_lang_html('question/edit_a_title'),
Scott committed
651

Scott committed
652
		'style' => 'tall',
Scott committed
653

Scott committed
654 655 656 657 658 659
		'fields' => array(
			'content' => array_merge(
				qa_editor_load_field($editor, $qa_content, $content, $format, $prefix . 'content', 12),
				array(
					'error' => qa_html(@$errors['content']),
				)
Scott committed
660
			),
Scott committed
661
		),
Scott committed
662

Scott committed
663 664 665 666 667
		'buttons' => array(
			'save' => array(
				'tags' => 'onclick="qa_show_waiting_after(this, false); ' .
					(method_exists($editor, 'update_script') ? $editor->update_script($prefix . 'content') : '') . '"',
				'label' => qa_lang_html('main/save_button'),
Scott committed
668 669
			),

Scott committed
670 671 672
			'cancel' => array(
				'tags' => 'name="docancel"',
				'label' => qa_lang_html('main/cancel_button'),
Scott committed
673
			),
Scott committed
674 675 676 677 678 679 680 681
		),

		'hidden' => array(
			$prefix . 'editor' => qa_html($editorname),
			$prefix . 'dosave' => '1',
			$prefix . 'code' => qa_get_form_security_code('edit-' . $answerid),
		),
	);
Scott committed
682

Scott committed
683
	// Show option to convert this answer to a comment, if appropriate
Scott committed
684

Scott committed
685
	$commentonoptions = array();
Scott committed
686

Scott committed
687 688
	$lastbeforeid = $question['postid']; // used to find last post created before this answer - this is default given
	$lastbeforetime = $question['created'];
Scott committed
689

Scott committed
690 691 692 693
	if ($question['commentable']) {
		$commentonoptions[$question['postid']] =
			qa_lang_html('question/comment_on_q') . qa_html(qa_shorten_string_line($question['title'], 80));
	}
Scott committed
694

Scott committed
695 696 697 698
	foreach ($answers as $otheranswer) {
		if ($otheranswer['postid'] != $answerid && $otheranswer['created'] < $answer['created'] && $otheranswer['commentable'] && !$otheranswer['hidden']) {
			$commentonoptions[$otheranswer['postid']] =
				qa_lang_html('question/comment_on_a') . qa_html(qa_shorten_string_line(qa_viewer_text($otheranswer['content'], $otheranswer['format']), 80));
Scott committed
699

Scott committed
700 701 702
			if ($otheranswer['created'] > $lastbeforetime) {
				$lastbeforeid = $otheranswer['postid'];
				$lastbeforetime = $otheranswer['created'];
Scott committed
703 704
			}
		}
Scott committed
705
	}
Scott committed
706

Scott committed
707 708 709 710 711 712 713 714
	if (count($commentonoptions)) {
		$form['fields']['tocomment'] = array(
			'tags' => 'name="' . $prefix . 'dotoc" id="' . $prefix . 'dotoc"',
			'label' => '<span id="' . $prefix . 'toshown">' . qa_lang_html('question/a_convert_to_c_on') . '</span>' .
				'<span id="' . $prefix . 'tohidden" style="display:none;">' . qa_lang_html('question/a_convert_to_c') . '</span>',
			'type' => 'checkbox',
			'tight' => true,
		);
Scott committed
715

Scott committed
716 717 718 719 720 721 722 723
		$form['fields']['commenton'] = array(
			'tags' => 'name="' . $prefix . 'commenton"',
			'id' => $prefix . 'commenton',
			'type' => 'select',
			'note' => qa_lang_html($hascomments ? 'question/a_convert_warn_cs' : 'question/a_convert_warn'),
			'options' => $commentonoptions,
			'value' => @$commentonoptions[$lastbeforeid],
		);
Scott committed
724

Scott committed
725 726 727 728 729 730
		qa_set_display_rules($qa_content, array(
			$prefix . 'commenton' => $prefix . 'dotoc',
			$prefix . 'toshown' => $prefix . 'dotoc',
			$prefix . 'tohidden' => '!' . $prefix . 'dotoc',
		));
	}
Scott committed
731

Scott committed
732
	// Show name and notification field if appropriate
Scott committed
733

Scott committed
734
	if ($answer['isbyuser']) {
735
		if (!qa_is_logged_in() && qa_opt('allow_anonymous_naming'))
Scott committed
736 737 738 739 740
			qa_set_up_name_field($qa_content, $form['fields'], isset($in['name']) ? $in['name'] : @$answer['name'], $prefix);

		qa_set_up_notify_fields($qa_content, $form['fields'], 'A', qa_get_logged_in_email(),
			isset($in['notify']) ? $in['notify'] : !empty($answer['notify']),
			isset($in['email']) ? $in['email'] : @$answer['notify'], @$errors['email'], $prefix);
Scott committed
741 742
	}

Scott committed
743 744 745 746 747 748 749 750 751 752 753 754
	if (!qa_user_post_permit_error('permit_edit_silent', $answer)) {
		$form['fields']['silent'] = array(
			'type' => 'checkbox',
			'label' => qa_lang_html('question/save_silent_label'),
			'tags' => 'name="' . $prefix . 'silent"',
			'value' => qa_html(@$in['silent']),
		);
	}

	return $form;
}

Scott committed
755 756 757 758

/*
	Processes a POSTed form for editing an answer and returns the new type of the post if successful
*/
Scott committed
759 760 761 762 763 764 765 766 767 768 769
function qa_page_q_edit_a_submit($answer, $question, $answers, $commentsfollows, &$in, &$errors)
{
	$answerid = $answer['postid'];
	$prefix = 'a' . $answerid . '_';

	$in = array(
		'dotoc' => qa_post_text($prefix . 'dotoc'),
		'commenton' => qa_post_text($prefix . 'commenton'),
	);

	if ($answer['isbyuser']) {
770
		$in['name'] = qa_opt('allow_anonymous_naming') ? qa_post_text($prefix . 'name') : null;
Scott committed
771 772 773
		$in['notify'] = qa_post_text($prefix . 'notify') !== null;
		$in['email'] = qa_post_text($prefix . 'email');
	}
Scott committed
774

Scott committed
775 776
	if (!qa_user_post_permit_error('permit_edit_silent', $answer))
		$in['silent'] = qa_post_text($prefix . 'silent');
Scott committed
777

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

Scott committed
780
	// here the $in array only contains values for parts of the form that were displayed, so those are only ones checked by filters
Scott committed
781

Scott committed
782
	$errors = array();
Scott committed
783

Scott committed
784 785
	if (!qa_check_form_security_code('edit-' . $answerid, qa_post_text($prefix . 'code')))
		$errors['content'] = qa_lang_html('misc/form_security_again');
Scott committed
786

Scott committed
787 788
	else {
		$in['queued'] = qa_opt('moderate_edited_again') && qa_user_moderation_reason(qa_user_level_for_post($answer));
Scott committed
789

Scott committed
790 791 792 793 794 795
		$filtermodules = qa_load_modules_with('filter', 'filter_answer');
		foreach ($filtermodules as $filtermodule) {
			$oldin = $in;
			$filtermodule->filter_answer($in, $errors, $question, $answer);
			qa_update_post_text($in, $oldin);
		}
Scott committed
796

Scott committed
797 798 799 800
		if (empty($errors)) {
			$userid = qa_get_logged_in_userid();
			$handle = qa_get_logged_in_handle();
			$cookieid = qa_cookie_get();
Scott committed
801

Scott committed
802 803
			if (!isset($in['silent']))
				$in['silent'] = false;
Scott committed
804

Scott committed
805
			$setnotify = $answer['isbyuser'] ? qa_combine_notify_email($answer['userid'], $in['notify'], $in['email']) : $answer['notify'];
Scott committed
806

Scott committed
807 808 809 810 811 812 813 814
			if ($in['dotoc'] && (
					(($in['commenton'] == $question['postid']) && $question['commentable']) ||
					(($in['commenton'] != $answerid) && @$answers[$in['commenton']]['commentable'])
				)
			) { // convert to a comment
				if (qa_user_limits_remaining(QA_LIMIT_COMMENTS)) { // already checked 'permit_post_c'
					qa_answer_to_comment($answer, $in['commenton'], $in['content'], $in['format'], $in['text'], $setnotify,
						$userid, $handle, $cookieid, $question, $answers, $commentsfollows, @$in['name'], $in['queued'], $in['silent']);
Scott committed
815

Scott committed
816
					return 'C'; // to signify that redirect should be to the comment
Scott committed
817

Scott committed
818 819
				} else
					$errors['content'] = qa_lang_html('question/comment_limit'); // not really best place for error, but it will do
Scott committed
820

Scott committed
821 822 823
			} else {
				qa_answer_set_content($answer, $in['content'], $in['format'], $in['text'], $setnotify,
					$userid, $handle, $cookieid, $question, @$in['name'], $in['queued'], $in['silent']);
Scott committed
824

Scott committed
825
				return 'A';
Scott committed
826 827 828 829
			}
		}
	}

Scott committed
830 831 832
	return null;
}

Scott committed
833 834 835 836

/*
	Processes a request to add a comment to $parent, with antecedent $question, checking for permissions errors
*/
Scott committed
837 838 839 840
function qa_page_q_do_comment($question, $parent, $commentsfollows, $pagestart, $usecaptcha, &$cnewin, &$cnewerrors, &$formtype, &$formpostid, &$error)
{
	// The 'approve', 'login', 'confirm', 'userblock', 'ipblock' permission errors are reported to the user here
	// The other option ('level') prevents the comment button being shown, in qa_page_q_post_rules(...)
Scott committed
841

Scott committed
842
	$parentid = $parent['postid'];
Scott committed
843

Scott committed
844 845 846 847
	switch (qa_user_post_permit_error('permit_post_c', $parent, QA_LIMIT_COMMENTS)) {
		case 'login':
			$error = qa_insert_login_links(qa_lang_html('question/comment_must_login'), qa_request());
			break;
Scott committed
848

Scott committed
849 850 851
		case 'confirm':
			$error = qa_insert_login_links(qa_lang_html('question/comment_must_confirm'), qa_request());
			break;
Scott committed
852

Scott committed
853
		case 'approve':
854 855 856 857
			$error = strtr(qa_lang_html('question/comment_must_be_approved'), array(
				'^1' => '<a href="' . qa_path_html('account') . '">',
				'^2' => '</a>',
			));
Scott committed
858
			break;
Scott committed
859

Scott committed
860 861 862
		case 'limit':
			$error = qa_lang_html('question/comment_limit');
			break;
Scott committed
863

Scott committed
864 865 866
		default:
			$error = qa_lang_html('users/no_permission');
			break;
Scott committed
867

Scott committed
868 869 870
		case false:
			if (qa_clicked('c' . $parentid . '_doadd')) {
				$commentid = qa_page_q_add_c_submit($question, $parent, $commentsfollows, $usecaptcha, $cnewin[$parentid], $cnewerrors[$parentid]);
Scott committed
871

Scott committed
872 873
				if (isset($commentid))
					qa_page_q_refresh($pagestart, null, 'C', $commentid);
Scott committed
874

Scott committed
875 876 877
				else {
					$formtype = 'c_add';
					$formpostid = $parentid; // show form again
Scott committed
878
				}
Scott committed
879 880 881 882 883 884

			} else {
				$formtype = 'c_add';
				$formpostid = $parentid; // show form first time
			}
			break;
Scott committed
885
	}
Scott committed
886
}
Scott committed
887 888 889 890 891


/*
	Returns a $qa_content form for editing a comment and sets up other parts of $qa_content accordingly
*/
Scott committed
892 893 894 895
function qa_page_q_edit_c_form(&$qa_content, $id, $comment, $in, $errors)
{
	$commentid = $comment['postid'];
	$prefix = 'c' . $commentid . '_';
Scott committed
896

Scott committed
897 898
	$content = isset($in['content']) ? $in['content'] : $comment['content'];
	$format = isset($in['format']) ? $in['format'] : $comment['format'];
Scott committed
899

Scott committed
900 901
	$editorname = isset($in['editor']) ? $in['editor'] : qa_opt('editor_for_cs');
	$editor = qa_load_editor($content, $format, $editorname);
Scott committed
902

Scott committed
903 904
	$form = array(
		'tags' => 'method="post" action="' . qa_self_html() . '"',
Scott committed
905

Scott committed
906
		'id' => $id,
Scott committed
907

Scott committed
908
		'title' => qa_lang_html('question/edit_c_title'),
Scott committed
909

Scott committed
910
		'style' => 'tall',
Scott committed
911

Scott committed
912 913 914 915 916 917
		'fields' => array(
			'content' => array_merge(
				qa_editor_load_field($editor, $qa_content, $content, $format, $prefix . 'content', 4, true),
				array(
					'error' => qa_html(@$errors['content']),
				)
Scott committed
918
			),
Scott committed
919
		),
Scott committed
920

Scott committed
921 922 923 924 925
		'buttons' => array(
			'save' => array(
				'tags' => 'onclick="qa_show_waiting_after(this, false); ' .
					(method_exists($editor, 'update_script') ? $editor->update_script($prefix . 'content') : '') . '"',
				'label' => qa_lang_html('main/save_button'),
Scott committed
926 927
			),

Scott committed
928 929 930
			'cancel' => array(
				'tags' => 'name="docancel"',
				'label' => qa_lang_html('main/cancel_button'),
Scott committed
931
			),
Scott committed
932 933 934 935 936 937 938 939 940 941
		),

		'hidden' => array(
			$prefix . 'editor' => qa_html($editorname),
			$prefix . 'dosave' => '1',
			$prefix . 'code' => qa_get_form_security_code('edit-' . $commentid),
		),
	);

	if ($comment['isbyuser']) {
942
		if (!qa_is_logged_in() && qa_opt('allow_anonymous_naming'))
Scott committed
943 944 945 946 947 948
			qa_set_up_name_field($qa_content, $form['fields'], isset($in['name']) ? $in['name'] : @$comment['name'], $prefix);

		qa_set_up_notify_fields($qa_content, $form['fields'], 'C', qa_get_logged_in_email(),
			isset($in['notify']) ? $in['notify'] : !empty($comment['notify']),
			isset($in['email']) ? $in['email'] : @$comment['notify'], @$errors['email'], $prefix);
	}
Scott committed
949

Scott committed
950 951 952 953 954 955 956
	if (!qa_user_post_permit_error('permit_edit_silent', $comment)) {
		$form['fields']['silent'] = array(
			'type' => 'checkbox',
			'label' => qa_lang_html('question/save_silent_label'),
			'tags' => 'name="' . $prefix . 'silent"',
			'value' => qa_html(@$in['silent']),
		);
Scott committed
957 958
	}

Scott committed
959 960 961
	return $form;
}

Scott committed
962 963 964 965

/*
	Processes a POSTed form for editing a comment and returns true if successful
*/
Scott committed
966 967 968 969
function qa_page_q_edit_c_submit($comment, $question, $parent, &$in, &$errors)
{
	$commentid = $comment['postid'];
	$prefix = 'c' . $commentid . '_';
Scott committed
970

Scott committed
971
	$in = array();
Scott committed
972

Scott committed
973
	if ($comment['isbyuser']) {
974
		$in['name'] = qa_opt('allow_anonymous_naming') ? qa_post_text($prefix . 'name') : null;
Scott committed
975 976 977
		$in['notify'] = qa_post_text($prefix . 'notify') !== null;
		$in['email'] = qa_post_text($prefix . 'email');
	}
Scott committed
978

Scott committed
979 980
	if (!qa_user_post_permit_error('permit_edit_silent', $comment))
		$in['silent'] = qa_post_text($prefix . 'silent');
Scott committed
981

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

Scott committed
984
	// here the $in array only contains values for parts of the form that were displayed, so those are only ones checked by filters
Scott committed
985

Scott committed
986
	$errors = array();
Scott committed
987

Scott committed
988 989
	if (!qa_check_form_security_code('edit-' . $commentid, qa_post_text($prefix . 'code')))
		$errors['content'] = qa_lang_html('misc/form_security_again');
Scott committed
990

Scott committed
991 992
	else {
		$in['queued'] = qa_opt('moderate_edited_again') && qa_user_moderation_reason(qa_user_level_for_post($comment));
Scott committed
993

Scott committed
994 995 996 997 998 999
		$filtermodules = qa_load_modules_with('filter', 'filter_comment');
		foreach ($filtermodules as $filtermodule) {
			$oldin = $in;
			$filtermodule->filter_comment($in, $errors, $question, $parent, $comment);
			qa_update_post_text($in, $oldin);
		}
Scott committed
1000

Scott committed
1001 1002 1003 1004
		if (empty($errors)) {
			$userid = qa_get_logged_in_userid();
			$handle = qa_get_logged_in_handle();
			$cookieid = qa_cookie_get();
Scott committed
1005

Scott committed
1006 1007
			if (!isset($in['silent']))
				$in['silent'] = false;
Scott committed
1008

Scott committed
1009
			$setnotify = $comment['isbyuser'] ? qa_combine_notify_email($comment['userid'], $in['notify'], $in['email']) : $comment['notify'];
Scott committed
1010

Scott committed
1011 1012
			qa_comment_set_content($comment, $in['content'], $in['format'], $in['text'], $setnotify,
				$userid, $handle, $cookieid, $question, $parent, @$in['name'], $in['queued'], $in['silent']);
Scott committed
1013

Scott committed
1014
			return true;
Scott committed
1015 1016 1017
		}
	}

Scott committed
1018 1019
	return false;
}