Completed
Pull Request — master (#3325)
by Emanuele
11:19
created

Post_Controller::action_jsmodify()   F

Complexity

Conditions 75
Paths > 20000

Size

Total Lines 230
Code Lines 132

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 5700

Importance

Changes 0
Metric Value
cc 75
eloc 132
dl 0
loc 230
rs 0
c 0
b 0
f 0
nc 282083046
nop 0
ccs 0
cts 166
cp 0
crap 5700

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * The job of this file is to handle everything related to posting replies,
5
 * new topics, quotes, and modifications to existing posts.  It also handles
6
 * quoting posts by way of javascript.
7
 *
8
 * @name      ElkArte Forum
9
 * @copyright ElkArte Forum contributors
10
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause
11
 *
12
 * This file contains code covered by:
13
 * copyright:	2011 Simple Machines (http://www.simplemachines.org)
14
 * license:		BSD, See included LICENSE.TXT for terms and conditions.
15
 *
16
 * @version 1.1.6
17
 *
18
 */
19
20
use ElkArte\Errors\ErrorContext;
21
22
/**
23
 * Post_Controller Class
24
 * Everything related to posting new replies and topics and modifications of them
25
 */
26
class Post_Controller extends Action_Controller
27
{
28
	/** @var null|ErrorContext The post (messages) errors object */
29
	protected $_post_errors = null;
30
31
	/** @var null|Template_Layers The template layers object */
32
	protected $_template_layers = null;
33
34
	/** @var array An array of attributes of the topic (if not new) */
35
	protected $_topic_attributes = array();
36
37
	/** @var string The message subject */
38
	protected $_form_subject = '';
39
40
	/** @var string The message  */
41
	protected $_form_message = '';
42
43
	/** @var \BBC\PreparseCode */
44
	protected $preparse;
45
46
	/**
47
	 * Sets up common stuff for all or most of the actions.
48
	 */
49
	public function pre_dispatch()
50
	{
51
		$this->_post_errors = ErrorContext::context('post', 1);
52
		$this->_template_layers = Template_Layers::instance();
53
54
		$this->preparse = \BBC\PreparseCode::instance();
55
56
		require_once(SUBSDIR . '/Post.subs.php');
57
		require_once(SUBSDIR . '/Messages.subs.php');
58
		require_once(SUBSDIR . '/Topic.subs.php');
59
	}
60
61
	/**
62
	 * Dispatch to the right action method for the request.
63
	 *
64
	 * @see Action_Controller::action_index()
65
	 */
66
	public function action_index()
67
	{
68
		// Figure out the right action to do.
69
		// hint: I'm post controller. :P
70
		$this->action_post();
71
	}
72
73
	/**
74
	 * Handles showing the post screen, loading the post to be modified, loading any post quoted, previews,
75
	 * display of errors and polls.
76
	 *
77
	 * What it does:
78
	 *
79
	 * - Validates that we're posting in a board.
80
	 * - Find the topic id if a message id is passed, else assume it's a new message
81
	 * - Get the response prefix in the default forum language.
82
	 * - Triggers events associated with posting.
83
	 *    - prepare_post, prepare_context, prepare_modifying, prepare_editing,
84
	 *    - prepare_posting, post_errors, finalize_post_form
85
	 * - Additionally handles previews of posts.
86
	 * - Requires different permissions depending on the actions, but most notably post_new, post_reply_own, and post_reply_any.
87
	 * - Shows options for the editing and posting of calendar events and attachments, and as the posting of polls (using modules).
88
	 * - Accessed from ?action=post or called from action_post2 in the event of errors or preview from quick reply.
89
	 *
90
	 * @uses the Post template and language file, main sub template.
91
	 * @uses Errors language
92
	 */
93
	public function action_post()
94
	{
95
		global $context;
96
97
		$this->_before_prepare_post();
98
99
		// Trigger the prepare_post event
100
		$this->_events->trigger('prepare_post', array('topic_attributes' => &$this->_topic_attributes));
101
102
		$this->_before_prepare_context();
103
104
		// Trigger the prepare_context event
105
		try
106
		{
107
			$this->_events->trigger('prepare_context', array('id_member_poster' => $this->_topic_attributes['id_member']));
108
		}
109
		catch (Controller_Redirect_Exception $e)
110
		{
111
			return $e->doRedirect($this);
112
		}
113
114
		$this->_generating_message();
115
116
		// Trigger post_errors event
117
		$this->_events->trigger('post_errors');
118
119
		$this->_preparing_page();
120
121
		// Needed for the editor and message icons.
122
		require_once(SUBSDIR . '/Editor.subs.php');
123
124
		// Now create the editor.
125
		$editorOptions = array(
126
			'id' => 'message',
127
			'value' => $context['message'],
128
			'labels' => array(
129
				'post_button' => $context['submit_label'],
130
			),
131
			// add height and width for the editor
132
			'height' => '275px',
133
			'width' => '100%',
134
			// We do XML preview here.
135
			'preview_type' => 2
136
		);
137
138
		// Trigger the finalize_post_form event
139
		$this->_events->trigger('finalize_post_form', array('destination' => &$context['destination'], 'page_title' => &$context['page_title'], 'show_additional_options' => &$context['show_additional_options'], 'editorOptions' => &$editorOptions));
140
141
		// Initialize the editor
142
		create_control_richedit($editorOptions);
143
144
		$this->_finalize_page();
145
	}
146
147
	protected function _before_prepare_post()
148
	{
149
		global $context;
150
151
		loadLanguage('Post');
152
		loadLanguage('Errors');
153
154
		$context['robot_no_index'] = true;
155
		$this->_template_layers->add('postarea');
0 ignored issues
show
Bug introduced by
The method add() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

155
		$this->_template_layers->/** @scrutinizer ignore-call */ 
156
                           add('postarea');

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
156
		$this->_topic_attributes = array(
157
			'locked' => false,
158
			'notify' => false,
159
			'is_sticky' => false,
160
			'id_last_msg' => 0,
161
			'id_member' => 0,
162
			'id_first_msg' => 0,
163
			'subject' => '',
164
			'last_post_time' => 0
165
		);
166
	}
167
168
	protected function _before_prepare_context()
169
	{
170
		global $topic, $modSettings, $board, $user_info, $context;
171
172
		// You must be posting to *some* board.
173
		if (empty($board) && !$context['make_event'])
174
			throw new Elk_Exception('no_board', false);
175
176
		// All those wonderful modifiers and attachments
177
		$this->_template_layers->add('additional_options', 200);
178
179
		if (isset($this->_req->query->xml))
180
		{
181
			$context['sub_template'] = 'post';
182
183
			// Just in case of an earlier error...
184
			$context['preview_message'] = '';
185
			$context['preview_subject'] = '';
186
		}
187
188
		// No message is complete without a topic.
189
		if (empty($topic) && !empty($_REQUEST['msg']))
190
		{
191
			$topic = associatedTopic((int) $_REQUEST['msg']);
192
			if (empty($topic))
193
				unset($_REQUEST['msg'], $_POST['msg'], $_GET['msg']);
194
		}
195
196
		// Check if it's locked. It isn't locked if no topic is specified.
197
		if (!empty($topic))
198
		{
199
			$this->_topic_attributes = topicUserAttributes($topic, $user_info['id']);
200
			$context['notify'] = $this->_topic_attributes['notify'];
201
			$context['topic_last_message'] = $this->_topic_attributes['id_last_msg'];
202
203
			if (empty($_REQUEST['msg']))
204
			{
205
				if ($user_info['is_guest'] && !allowedTo('post_reply_any') && (!$modSettings['postmod_active'] || !allowedTo('post_unapproved_replies_any')))
206
					is_not_guest();
207
208
				// By default the reply will be approved...
209
				$context['becomes_approved'] = true;
210
				if ($this->_topic_attributes['id_member'] != $user_info['id'])
211
				{
212
					if ($modSettings['postmod_active'] && allowedTo('post_unapproved_replies_any') && !allowedTo('post_reply_any'))
213
						$context['becomes_approved'] = false;
214
					else
215
						isAllowedTo('post_reply_any');
216
				}
217
				elseif (!allowedTo('post_reply_any'))
218
				{
219
					if ($modSettings['postmod_active'])
220
					{
221
						if (allowedTo('post_unapproved_replies_own') && !allowedTo('post_reply_own'))
222
							$context['becomes_approved'] = false;
223
						// Guests do not have post_unapproved_replies_own permission, so it's always post_unapproved_replies_any
224
						elseif ($user_info['is_guest'] && allowedTo('post_unapproved_replies_any'))
225
							$context['becomes_approved'] = false;
226
						else
227
							isAllowedTo('post_reply_own');
228
					}
229
					else
230
						isAllowedTo('post_reply_own');
231
				}
232
			}
233
			else
234
				$context['becomes_approved'] = true;
235
236
			$context['can_lock'] = allowedTo('lock_any') || ($user_info['id'] == $this->_topic_attributes['id_member'] && allowedTo('lock_own'));
237
			$context['can_sticky'] = allowedTo('make_sticky');
238
			$context['notify'] = !empty($context['notify']);
239
			$context['sticky'] = isset($_REQUEST['sticky']) ? !empty($_REQUEST['sticky']) : $this->_topic_attributes['is_sticky'];
240
		}
241
		else
242
		{
243
			$this->_topic_attributes['id_member'] = 0;
244
			$context['becomes_approved'] = true;
245
			if (empty($context['make_event']) || !empty($board))
246
			{
247
				if ($modSettings['postmod_active'] && !allowedTo('post_new') && allowedTo('post_unapproved_topics'))
248
					$context['becomes_approved'] = false;
249
				else
250
					isAllowedTo('post_new');
251
			}
252
253
			$this->_topic_attributes['locked'] = 0;
254
255
			// @todo These won't work if you're making an event.
256
			$context['can_lock'] = allowedTo(array('lock_any', 'lock_own'));
257
			$context['can_sticky'] = allowedTo('make_sticky');
258
259
			$context['notify'] = !empty($context['notify']);
260
			$context['sticky'] = !empty($_REQUEST['sticky']);
261
		}
262
263
		// @todo These won't work if you're posting an event!
264
		$context['can_notify'] = allowedTo('mark_any_notify');
265
		$context['can_move'] = allowedTo('move_any');
266
		$context['move'] = !empty($_REQUEST['move']);
267
		$context['announce'] = !empty($_REQUEST['announce']);
268
269
		// You can only announce topics that will get approved...
270
		$context['can_announce'] = allowedTo('announce_topic') && $context['becomes_approved'];
271
		$context['locked'] = !empty($this->_topic_attributes['locked']) || !empty($_REQUEST['lock']);
272
		$context['can_quote'] = empty($modSettings['disabledBBC']) || !in_array('quote', explode(',', $modSettings['disabledBBC']));
273
274
		// Generally don't show the approval box... (Assume we want things approved)
275
		$context['show_approval'] = allowedTo('approve_posts') && $context['becomes_approved'] ? 2 : (allowedTo('approve_posts') ? 1 : 0);
276
277
		// Don't allow a post if it's locked and you aren't all powerful.
278
		if ($this->_topic_attributes['locked'] && !allowedTo('moderate_board'))
279
			throw new Elk_Exception('topic_locked', false);
280
	}
281
282
	protected function _generating_message()
283
	{
284
		global $txt, $topic, $modSettings, $user_info, $context, $options;
285
286
		// See if any new replies have come along.
287
		if (empty($_REQUEST['msg']) && !empty($topic))
288
		{
289
			if (empty($options['no_new_reply_warning']) && isset($_REQUEST['last_msg']) && $context['topic_last_message'] > $_REQUEST['last_msg'])
290
			{
291
				$context['new_replies'] = countMessagesSince($topic, (int) $_REQUEST['last_msg'], false, $modSettings['postmod_active'] && !allowedTo('approve_posts'));
292
293
				if (!empty($context['new_replies']))
294
				{
295
					if ($context['new_replies'] == 1)
296
						$txt['error_new_replies'] = isset($_GET['last_msg']) ? $txt['error_new_reply_reading'] : $txt['error_new_reply'];
297
					else
298
						$txt['error_new_replies'] = sprintf(isset($_GET['last_msg']) ? $txt['error_new_replies_reading'] : $txt['error_new_replies'], $context['new_replies']);
299
300
					$this->_post_errors->addError('new_replies', 0);
0 ignored issues
show
Bug introduced by
The method addError() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

300
					$this->_post_errors->/** @scrutinizer ignore-call */ 
301
                          addError('new_replies', 0);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
301
302
					$modSettings['topicSummaryPosts'] = $context['new_replies'] > $modSettings['topicSummaryPosts'] ? max($modSettings['topicSummaryPosts'], 5) : $modSettings['topicSummaryPosts'];
303
				}
304
			}
305
		}
306
307
		// Get a response prefix (like 'Re:') in the default forum language.
308
		$context['response_prefix'] = response_prefix();
309
		$context['destination'] = 'post2;start=' . $_REQUEST['start'];
310
311
		// Previewing, modifying, or posting?
312
		// Do we have a body, but an error happened.
313
		if (isset($_REQUEST['message']) || $this->_post_errors->hasErrors())
314
		{
315
			// Validate inputs.
316
			if (!$this->_post_errors->hasErrors())
317
			{
318
				// This means they didn't click Post and get an error.
319
				$really_previewing = true;
320
			}
321
			else
322
			{
323
				if (!isset($_REQUEST['subject']))
324
					$_REQUEST['subject'] = '';
325
326
				if (!isset($_REQUEST['message']))
327
					$_REQUEST['message'] = '';
328
329
				if (!isset($_REQUEST['icon']))
330
					$_REQUEST['icon'] = 'xx';
331
332
				// They are previewing if they asked to preview (i.e. came from quick reply).
333
				$really_previewing = !empty($_REQUEST['preview']) || isset($_REQUEST['xml']);
334
			}
335
336
			// Trigger the prepare_modifying event
337
			$this->_events->trigger('prepare_modifying', array('post_errors' => $this->_post_errors, 'really_previewing' => &$really_previewing));
338
339
			// In order to keep the approval status flowing through, we have to pass it through the form...
340
			$context['becomes_approved'] = empty($_REQUEST['not_approved']);
341
			$context['show_approval'] = isset($_REQUEST['approve']) ? ($_REQUEST['approve'] ? 2 : 1) : 0;
342
			$context['can_announce'] &= $context['becomes_approved'];
343
344
			// Set up the inputs for the form.
345
			$this->_form_subject = strtr(Util::htmlspecialchars($_REQUEST['subject']), array("\r" => '', "\n" => '', "\t" => ''));
346
			$this->_form_message = Util::htmlspecialchars($_REQUEST['message'], ENT_QUOTES, 'UTF-8', true);
347
348
			// Make sure the subject isn't too long - taking into account special characters.
349
			if (Util::strlen($this->_form_subject) > 100)
350
				$this->_form_subject = Util::substr($this->_form_subject, 0, 100);
351
352
			// Are you... a guest?
353
			if ($user_info['is_guest'])
354
			{
355
				$context['name'] = !isset($_REQUEST['guestname']) ? '' : Util::htmlspecialchars(trim($_REQUEST['guestname']));
356
				$context['email'] = !isset($_REQUEST['email']) ? '' : Util::htmlspecialchars(trim($_REQUEST['email']));
357
				$user_info['name'] = $context['name'];
358
			}
359
360
			// Only show the preview stuff if they hit Preview.
361
			if ($really_previewing === true)
362
			{
363
				// Set up the preview message and subject
364
				$context['preview_message'] = $this->_form_message;
365
				$this->preparse->preparsecode($this->_form_message, true);
366
367
				// Do all bulletin board code thing on the message
368
				$bbc_parser = \BBC\ParserWrapper::instance();
369
				$this->preparse->preparsecode($context['preview_message']);
370
				$context['preview_message'] = $bbc_parser->parseMessage($context['preview_message'], isset($_REQUEST['ns']) ? 0 : 1);
371
				$context['preview_message'] = censor($context['preview_message']);
372
373
				// Don't forget the subject
374
				$context['preview_subject'] = censor($this->_form_subject);
375
376
				// Any errors we should tell them about?
377
				if ($this->_form_subject === '')
378
				{
379
					$this->_post_errors->addError('no_subject');
380
					$context['preview_subject'] = '<em>' . $txt['no_subject'] . '</em>';
381
				}
382
383
				if ($context['preview_message'] === '')
384
					$this->_post_errors->addError('no_message');
385
				elseif (!empty($modSettings['max_messageLength']) && Util::strlen($this->_form_message) > $modSettings['max_messageLength'])
386
					$this->_post_errors->addError(array('long_message', array($modSettings['max_messageLength'])));
387
388
				// Protect any CDATA blocks.
389
				if (isset($_REQUEST['xml']))
390
					$context['preview_message'] = strtr($context['preview_message'], array(']]>' => ']]]]><![CDATA[>'));
391
			}
392
393
			// Set up the checkboxes.
394
			$context['notify'] = !empty($_REQUEST['notify']);
395
			$context['use_smileys'] = !isset($_REQUEST['ns']);
396
			$context['icon'] = isset($_REQUEST['icon']) ? preg_replace('~[\./\\\\*\':"<>]~', '', $_REQUEST['icon']) : 'xx';
397
398
			// Set the destination action for submission.
399
			$context['destination'] .= isset($_REQUEST['msg']) ? ';msg=' . $_REQUEST['msg'] . ';' . $context['session_var'] . '=' . $context['session_id'] : '';
400
			$context['submit_label'] = isset($_REQUEST['msg']) ? $txt['save'] : $txt['post'];
401
402
			// Previewing an edit?
403
			if (isset($_REQUEST['msg']) && !empty($topic))
404
			{
405
				$msg_id = (int) $_REQUEST['msg'];
406
407
				// Get the existing message.
408
				$message = messageDetails((int) $msg_id, $topic);
409
410
				// The message they were trying to edit was most likely deleted.
411
				// @todo Change this error message?
412
				if ($message === false)
413
					throw new Elk_Exception('no_board', false);
414
415
				$errors = checkMessagePermissions($message['message']);
416
				if (!empty($errors))
417
					foreach ($errors as $error)
418
						$this->_post_errors->addError($error);
419
420
				prepareMessageContext($message);
421
			}
422
			elseif (isset($_REQUEST['last_msg']))
423
			{
424
				// @todo: sort out what kind of combinations are actually possible
425
				// Posting a quoted reply?
426
				if ((!empty($topic) && !empty($_REQUEST['quote'])) || (!empty($modSettings['enableFollowup']) && !empty($_REQUEST['followup'])))
427
					$case = 2;
428
				// Posting a reply without a quote?
429
				elseif (!empty($topic) && empty($_REQUEST['quote']))
430
					$case = 3;
431
				else
432
					$case = 4;
433
434
				list ($this->_form_subject,) = getFormMsgSubject($case, $topic, $this->_topic_attributes['subject']);
435
			}
436
437
			// No check is needed, since nothing is really posted.
438
			checkSubmitOnce('free');
439
		}
440
		// Editing a message...
441
		elseif (isset($_REQUEST['msg']) && !empty($topic))
442
		{
443
			$msg_id = (int) $_REQUEST['msg'];
444
445
			$message = getFormMsgSubject(1, $topic, '', $msg_id);
446
447
			// The message they were trying to edit was most likely deleted.
448
			if ($message === false)
0 ignored issues
show
introduced by
The condition $message === false is always true.
Loading history...
449
				throw new Elk_Exception('no_message', false);
450
451
			// Trigger the prepare_editing event
452
			$this->_events->trigger('prepare_editing', array('topic' => $topic, 'message' => &$message));
453
454
			if (!empty($message['errors']))
455
				foreach ($message['errors'] as $error)
456
					$this->_post_errors->addError($error);
457
458
			// Get the stuff ready for the form.
459
			$this->_form_subject = $message['message']['subject'];
460
			$this->_form_message = $this->preparse->un_preparsecode($message['message']['body']);
461
462
			$this->_form_message = censor($this->_form_message);
463
			$this->_form_subject = censor($this->_form_subject);
464
465
			// Check the boxes that should be checked.
466
			$context['use_smileys'] = !empty($message['message']['smileys_enabled']);
467
			$context['icon'] = $message['message']['icon'];
468
469
			// Set the destination.
470
			$context['destination'] .= ';msg=' . $msg_id . ';' . $context['session_var'] . '=' . $context['session_id'];
471
			$context['submit_label'] = $txt['save'];
472
		}
473
		// Posting...
474
		else
475
		{
476
			// By default....
477
			$context['use_smileys'] = true;
478
			$context['icon'] = 'xx';
479
480
			if ($user_info['is_guest'])
481
			{
482
				$context['name'] = isset($_SESSION['guest_name']) ? $_SESSION['guest_name'] : '';
483
				$context['email'] = isset($_SESSION['guest_email']) ? $_SESSION['guest_email'] : '';
484
			}
485
486
			// Trigger the prepare_posting event
487
			$this->_events->trigger('prepare_posting');
488
489
			$context['submit_label'] = $txt['post'];
490
491
			// @todo: sort out what kind of combinations are actually possible
492
			// Posting a quoted reply?
493
			if ((!empty($topic) && !empty($_REQUEST['quote'])) || (!empty($modSettings['enableFollowup']) && !empty($_REQUEST['followup'])))
494
				$case = 2;
495
			// Posting a reply without a quote?
496
			elseif (!empty($topic) && empty($_REQUEST['quote']))
497
				$case = 3;
498
			else
499
				$case = 4;
500
501
			list ($this->_form_subject, $this->_form_message) = getFormMsgSubject($case, $topic, $this->_topic_attributes['subject']);
502
		}
503
504
		// Check whether this is a really old post being bumped...
505
		if (!empty($topic) && !empty($modSettings['oldTopicDays']) && $this->_topic_attributes['last_post_time'] + $modSettings['oldTopicDays'] * 86400 < time() && empty($this->_topic_attributes['is_sticky']) && !isset($_REQUEST['subject']))
506
			$this->_post_errors->addError(array('old_topic', array($modSettings['oldTopicDays'])), 0);
507
	}
508
509
	protected function _preparing_page()
510
	{
511
		global $txt, $topic, $modSettings, $board, $user_info, $context;
512
513
		// Any errors occurred?
514
		$context['post_error'] = array(
515
			'errors' => $this->_post_errors->prepareErrors(),
516
			'type' => $this->_post_errors->getErrorType() == 0 ? 'minor' : 'serious',
517
			'title' => $this->_post_errors->getErrorType() == 0 ? $txt['warning_while_submitting'] : $txt['error_while_submitting'],
518
		);
519
520
		// What are you doing? Posting, modifying, previewing, new post, or reply...
521
		if (empty($context['page_title']))
522
		{
523
			if (isset($_REQUEST['msg']))
524
				$context['page_title'] = $txt['modify_msg'];
525
			elseif (isset($_REQUEST['subject'], $context['preview_subject']))
526
				$context['page_title'] = $txt['post_reply'];
527
			elseif (empty($topic))
528
				$context['page_title'] = $txt['start_new_topic'];
529
			else
530
				$context['page_title'] = $txt['post_reply'];
531
		}
532
533
		// Update the topic summary, needed to show new posts in a preview
534
		if (!empty($topic) && !empty($modSettings['topicSummaryPosts']))
535
		{
536
			$only_approved = $modSettings['postmod_active'] && !allowedTo('approve_posts');
537
538
			if (isset($_REQUEST['xml']))
539
				$limit = empty($context['new_replies']) ? 0 : (int) $context['new_replies'];
540
			else
541
				$limit = $modSettings['topicSummaryPosts'];
542
543
			$before = isset($_REQUEST['msg']) ? array('before' => (int) $_REQUEST['msg']) : array();
544
545
			$counter = 0;
546
			$context['previous_posts'] = empty($limit) ? array() : selectMessages($topic, 0, $limit, $before, $only_approved);
547
			foreach ($context['previous_posts'] as &$post)
548
			{
549
				$post['is_new'] = !empty($context['new_replies']);
550
				$post['counter'] = $counter++;
551
				$post['is_ignored'] = !empty($modSettings['enable_buddylist']) && in_array($post['id_poster'], $user_info['ignoreusers']);
552
553
				if (!empty($context['new_replies']))
554
					$context['new_replies']--;
555
			}
556
		}
557
558
		// Just ajax previewing then lets stop now
559
		if (isset($_REQUEST['xml']))
560
			obExit();
561
562
		$context['subject'] = addcslashes($this->_form_subject, '"');
563
		$context['message'] = str_replace(array('"', '<', '>', '&nbsp;'), array('&quot;', '&lt;', '&gt;', ' '), $this->_form_message);
564
565
		require_once(SUBSDIR . '/Editor.subs.php');
566
		// Message icons - customized or not, retrieve them...
567
		$context['icons'] = getMessageIcons($board);
568
569
		$context['icon_url'] = '';
570
571
		if (!empty($context['icons']))
572
		{
573
			$context['icons'][count($context['icons']) - 1]['is_last'] = true;
574
			$context['icons'][0]['selected'] = true;
575
576
			// $context['icon'] is set when editing a message
577
			if (!isset($context['icon']))
578
				$context['icon'] = $context['icons'][0]['value'];
579
			$found = false;
580
			foreach ($context['icons'] as $icon)
581
			{
582
				if ($icon['value'] === $context['icon'])
583
				{
584
					$found = true;
585
					$context['icon_url'] = $icon['url'];
586
					break;
587
				}
588
			}
589
			// Fail safe
590
			if (!$found)
591
			{
592
				$context['icon'] = $context['icons'][0]['value'];
593
				$context['icon_url'] = $context['icons'][0]['url'];
594
			}
595
		}
596
597
		$context['show_additional_options'] = !empty($_POST['additional_options']) || isset($_GET['additionalOptions']);
598
	}
599
600
	protected function _finalize_page()
601
	{
602
		global $txt, $scripturl, $topic, $context;
603
604
		// Build the link tree.
605
		if (empty($topic))
606
		{
607
			$context['linktree'][] = array(
608
				'name' => '<em>' . $txt['start_new_topic'] . '</em>'
609
			);
610
		}
611
		else
612
		{
613
			$context['linktree'][] = array(
614
				'url' => $scripturl . '?topic=' . $topic . '.' . $_REQUEST['start'],
615
				'name' => $this->_form_subject,
616
				'extra_before' => '<span><strong class="nav">' . $context['page_title'] . ' ( </strong></span>',
617
				'extra_after' => '<span><strong class="nav"> )</strong></span>'
618
			);
619
		}
620
621
		$context['back_to_topic'] = isset($_REQUEST['goback']) || (isset($_REQUEST['msg']) && !isset($_REQUEST['subject']));
622
		$context['is_new_topic'] = empty($topic);
623
		$context['is_new_post'] = !isset($_REQUEST['msg']);
624
		$context['is_first_post'] = $context['is_new_topic'] || (isset($_REQUEST['msg']) && $_REQUEST['msg'] == $this->_topic_attributes['id_first_msg']);
625
		$context['current_action'] = 'post';
626
627
		// Register this form in the session variables.
628
		checkSubmitOnce('register');
629
630
		// Finally, load the template.
631
		if (!isset($_REQUEST['xml']))
632
		{
633
			loadTemplate('Post');
634
			$context['sub_template'] = 'post_page';
635
		}
636
	}
637
638
	/**
639
	 * Posts or saves the message composed with Post().
640
	 *
641
	 * What it does:
642
	 *
643
	 * - Requires various permissions depending on the action.
644
	 * - Handles attachment, post, and calendar saving.
645
	 * - Sends off notifications, and allows for announcements and moderation.
646
	 * accessed from ?action=post2.
647
	 * - Triggers events associated with the actual posting
648
	 *   - prepare_save_post, save_replying, save_new_topic, save_modify
649
	 *   - before_save_post, pre_save_post, after_save_post
650
	 */
651
	public function action_post2()
652
	{
653
		global $board, $topic, $txt, $modSettings, $context, $user_settings;
654
		global $user_info, $board_info, $options;
655
656
		// Sneaking off, are we?
657
		if (empty($_POST) && empty($topic))
658
		{
659
			if (empty($_SERVER['CONTENT_LENGTH']))
660
				redirectexit('action=post;board=' . $board . '.0');
661
			else
662
				throw new Elk_Exception('post_upload_error', false);
663
		}
664
		elseif (empty($_POST) && !empty($topic))
665
			redirectexit('action=post;topic=' . $topic . '.0');
666
667
		// No need!
668
		$context['robot_no_index'] = true;
669
670
		// We are now in post2 action
671
		$context['current_action'] = 'post2';
672
673
		// If the session has timed out, let the user re-submit their form.
674
		if (checkSession('post', '', false) != '')
675
		{
676
			$this->_post_errors->addError('session_timeout');
677
678
			// Disable the preview so that any potentially malicious code is not executed
679
			$_REQUEST['preview'] = false;
680
681
			return $this->action_post();
682
		}
683
684
		$topic_info = array();
685
686
		// Previewing? Go back to start.
687
		if (isset($_REQUEST['preview']))
688
			return $this->action_post();
689
690
		require_once(SUBSDIR . '/Boards.subs.php');
691
		loadLanguage('Post');
692
693
		// Trigger the prepare_save_post event
694
		$this->_events->trigger('prepare_save_post', array('topic_info' => &$topic_info));
695
696
		// Prevent double submission of this form.
697
		checkSubmitOnce('check');
698
699
		// If this isn't a new topic load the topic info that we need.
700
		if (!empty($topic))
701
		{
702
			$topic_info = getTopicInfo($topic);
703
704
			// Though the topic should be there, it might have vanished.
705
			if (empty($topic_info))
706
				throw new Elk_Exception('topic_doesnt_exist');
707
708
			// Did this topic suddenly move? Just checking...
709
			if ($topic_info['id_board'] != $board)
710
				throw new Elk_Exception('not_a_topic');
711
		}
712
713
		// Replying to a topic?
714
		if (!empty($topic) && !isset($_REQUEST['msg']))
715
		{
716
			// Don't allow a post if it's locked.
717
			if ($topic_info['locked'] != 0 && !allowedTo('moderate_board'))
718
				throw new Elk_Exception('topic_locked', false);
719
720
			// Do the permissions and approval stuff...
721
			$becomesApproved = true;
722
			if ($topic_info['id_member_started'] != $user_info['id'])
723
			{
724
				if ($modSettings['postmod_active'] && allowedTo('post_unapproved_replies_any') && !allowedTo('post_reply_any'))
725
					$becomesApproved = false;
726
				else
727
					isAllowedTo('post_reply_any');
728
			}
729
			elseif (!allowedTo('post_reply_any'))
730
			{
731
				if ($modSettings['postmod_active'])
732
				{
733
					if (allowedTo('post_unapproved_replies_own') && !allowedTo('post_reply_own'))
734
						$becomesApproved = false;
735
					// Guests do not have post_unapproved_replies_own permission, so it's always post_unapproved_replies_any
736
					elseif ($user_info['is_guest'] && allowedTo('post_unapproved_replies_any'))
737
						$becomesApproved = false;
738
					else
739
						isAllowedTo('post_reply_own');
740
				}
741
			}
742
743
			if (isset($_POST['lock']))
744
			{
745
				$_POST['lock'] = $this->_checkLocked($_POST['lock'], $topic_info);
746
			}
747
748
			// So you wanna (un)sticky this...let's see.
749
			if (isset($_POST['sticky']) && ($_POST['sticky'] == $topic_info['is_sticky'] || !allowedTo('make_sticky')))
750
				unset($_POST['sticky']);
751
752
			// Trigger the save_replying event
753
			$this->_events->trigger('save_replying', array('topic_info' => &$topic_info));
754
755
			// If the number of replies has changed, if the setting is enabled, go back to action_post() - which handles the error.
756
			if (empty($options['no_new_reply_warning']) && isset($_POST['last_msg']) && $topic_info['id_last_msg'] > $_POST['last_msg'])
757
			{
758
				addInlineJavascript('
759
					$(function() {
760
						$("html,body").scrollTop($(\'.category_header:visible:first\').offset().top);
761
					});'
762
				);
763
764
				return $this->action_post();
765
			}
766
767
			$posterIsGuest = $user_info['is_guest'];
768
		}
769
		// Posting a new topic.
770
		elseif (empty($topic))
771
		{
772
			// Now don't be silly, new topics will get their own id_msg soon enough.
773
			unset($_REQUEST['msg'], $_POST['msg'], $_GET['msg']);
774
775
			// Do like, the permissions, for safety and stuff...
776
			$becomesApproved = true;
777
			if ($modSettings['postmod_active'] && !allowedTo('post_new') && allowedTo('post_unapproved_topics'))
778
				$becomesApproved = false;
779
			else
780
				isAllowedTo('post_new');
781
782
			// Trigger teh save new topic event
783
			$this->_events->trigger('save_new_topic', array('becomesApproved' => &$becomesApproved));
784
785
			if (isset($_POST['lock']))
786
			{
787
				$_POST['lock'] = $this->_checkLocked($_POST['lock']);
788
			}
789
790
			if (isset($_POST['sticky']) && (empty($_POST['sticky']) || !allowedTo('make_sticky')))
791
				unset($_POST['sticky']);
792
793
			$posterIsGuest = $user_info['is_guest'];
794
		}
795
		// Modifying an existing message?
796
		elseif (isset($_REQUEST['msg']) && !empty($topic))
797
		{
798
			$_REQUEST['msg'] = (int) $_REQUEST['msg'];
799
800
			$msgInfo = basicMessageInfo($_REQUEST['msg'], true);
801
802
			if (empty($msgInfo))
803
				throw new Elk_Exception('cant_find_messages', false);
804
805
			// Trigger teh save_modify event
806
			$this->_events->trigger('save_modify', array('msgInfo' => &$msgInfo));
807
808
			if (!empty($topic_info['locked']) && !allowedTo('moderate_board'))
809
				throw new Elk_Exception('topic_locked', false);
810
811
			if (isset($_POST['lock']))
812
			{
813
				$_POST['lock'] = $this->_checkLocked($_POST['lock'], $topic_info);
814
			}
815
816
			// Change the sticky status of this topic?
817
			if (isset($_POST['sticky']) && (!allowedTo('make_sticky') || $_POST['sticky'] == $topic_info['is_sticky']))
818
				unset($_POST['sticky']);
819
820
			if ($msgInfo['id_member'] == $user_info['id'] && !allowedTo('modify_any'))
821
			{
822
				if ((!$modSettings['postmod_active'] || $msgInfo['approved']) && !empty($modSettings['edit_disable_time']) && $msgInfo['poster_time'] + ($modSettings['edit_disable_time'] + 5) * 60 < time())
823
					throw new Elk_Exception('modify_post_time_passed', false);
824
				elseif ($topic_info['id_member_started'] == $user_info['id'] && !allowedTo('modify_own'))
825
					isAllowedTo('modify_replies');
826
				else
827
					isAllowedTo('modify_own');
828
			}
829
			elseif ($topic_info['id_member_started'] == $user_info['id'] && !allowedTo('modify_any'))
830
			{
831
				isAllowedTo('modify_replies');
832
833
				// If you're modifying a reply, I say it better be logged...
834
				$moderationAction = true;
835
			}
836
			else
837
			{
838
				isAllowedTo('modify_any');
839
840
				// Log it, assuming you're not modifying your own post.
841
				if ($msgInfo['id_member'] != $user_info['id'])
842
					$moderationAction = true;
843
			}
844
845
			$posterIsGuest = empty($msgInfo['id_member']);
846
847
			// Can they approve it?
848
			$can_approve = allowedTo('approve_posts');
849
			$becomesApproved = $modSettings['postmod_active'] ? ($can_approve && !$msgInfo['approved'] ? (!empty($_REQUEST['approve']) ? 1 : 0) : $msgInfo['approved']) : 1;
850
			$approve_has_changed = $msgInfo['approved'] != $becomesApproved;
851
852
			if (!allowedTo('moderate_forum') || !$posterIsGuest)
853
			{
854
				$_POST['guestname'] = $msgInfo['poster_name'];
855
				$_POST['email'] = $msgInfo['poster_email'];
856
			}
857
		}
858
859
		// In case we want to override
860
		if (allowedTo('approve_posts'))
861
		{
862
			$becomesApproved = !isset($_REQUEST['approve']) || !empty($_REQUEST['approve']) ? 1 : 0;
863
			$approve_has_changed = isset($msgInfo['approved']) ? $msgInfo['approved'] != $becomesApproved : false;
864
		}
865
866
		// If the poster is a guest evaluate the legality of name and email.
867
		if ($posterIsGuest)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $posterIsGuest does not seem to be defined for all execution paths leading up to this point.
Loading history...
868
		{
869
			$_POST['guestname'] = !isset($_POST['guestname']) ? '' : Util::htmlspecialchars(trim($_POST['guestname']));
870
			$_POST['email'] = !isset($_POST['email']) ? '' : Util::htmlspecialchars(trim($_POST['email']));
871
872
			if ($_POST['guestname'] == '' || $_POST['guestname'] == '_')
873
				$this->_post_errors->addError('no_name');
874
875
			if (Util::strlen($_POST['guestname']) > 25)
876
				$this->_post_errors->addError('long_name');
877
878
			if (empty($modSettings['guest_post_no_email']))
879
			{
880
				// Only check if they changed it!
881
				if (!isset($msgInfo) || $msgInfo['poster_email'] != $_POST['email'])
882
				{
883
					if (!allowedTo('moderate_forum') && !Data_Validator::is_valid($_POST, array('email' => 'valid_email|required'), array('email' => 'trim')))
884
						empty($_POST['email']) ? $this->_post_errors->addError('no_email') : $this->_post_errors->addError('bad_email');
885
				}
886
887
				// Now make sure this email address is not banned from posting.
888
				isBannedEmail($_POST['email'], 'cannot_post', sprintf($txt['you_are_post_banned'], $txt['guest_title']));
889
			}
890
891
			// In case they are making multiple posts this visit, help them along by storing their name.
892
			if (!$this->_post_errors->hasErrors())
893
			{
894
				$_SESSION['guest_name'] = $_POST['guestname'];
895
				$_SESSION['guest_email'] = $_POST['email'];
896
			}
897
		}
898
899
		// Trigger before_save_post event
900
		try
901
		{
902
			$this->_events->trigger('before_save_post', array('post_errors' => $this->_post_errors, 'topic_info' => $topic_info));
903
		}
904
		catch (Controller_Redirect_Exception $e)
905
		{
906
			return $e->doRedirect($this);
907
		}
908
909
		// Check the subject and message.
910
		if (!isset($_POST['subject']) || Util::htmltrim(Util::htmlspecialchars($_POST['subject'])) === '')
911
			$this->_post_errors->addError('no_subject');
912
913
		if (!isset($_POST['message']) || Util::htmltrim(Util::htmlspecialchars($_POST['message'], ENT_QUOTES)) === '')
914
			$this->_post_errors->addError('no_message');
915
		elseif (!empty($modSettings['max_messageLength']) && Util::strlen($_POST['message']) > $modSettings['max_messageLength'])
916
			$this->_post_errors->addError(array('long_message', array($modSettings['max_messageLength'])));
917
		else
918
		{
919
			// Prepare the message a bit for some additional testing.
920
			$_POST['message'] = Util::htmlspecialchars($_POST['message'], ENT_QUOTES, 'UTF-8', true);
921
922
			// Preparse code. (Zef)
923
			if ($user_info['is_guest'])
924
				$user_info['name'] = $_POST['guestname'];
925
926
			$this->preparse->preparsecode($_POST['message']);
927
928
			$bbc_parser = \BBC\ParserWrapper::instance();
929
930
			// Let's see if there's still some content left without the tags.
931
			if (Util::htmltrim(strip_tags($bbc_parser->parseMessage($_POST['message'], false), '<img>')) === '' && (!allowedTo('admin_forum') || strpos($_POST['message'], '[html]') === false))
932
				$this->_post_errors->addError('no_message');
933
		}
934
935
		if ($posterIsGuest)
936
		{
937
			// If user is a guest, make sure the chosen name isn't taken.
938
			require_once(SUBSDIR . '/Members.subs.php');
939
			if (isReservedName($_POST['guestname'], 0, true, false) && (!isset($msgInfo['poster_name']) || $_POST['guestname'] != $msgInfo['poster_name']))
940
				$this->_post_errors->addError('bad_name');
941
		}
942
		// If the user isn't a guest, get his or her name and email.
943
		elseif (!isset($_REQUEST['msg']))
944
		{
945
			$_POST['guestname'] = $user_info['username'];
946
			$_POST['email'] = $user_info['email'];
947
		}
948
949
		// Posting somewhere else? Are we sure you can?
950
		if (!empty($_REQUEST['post_in_board']))
951
		{
952
			$new_board = (int) $_REQUEST['post_in_board'];
953
			if (!allowedTo('post_new', $new_board))
954
			{
955
				$post_in_board = boardInfo($new_board);
956
957
				if (!empty($post_in_board))
958
					$this->_post_errors->addError(array('post_new_board', array($post_in_board['name'])));
959
				else
960
					$this->_post_errors->addError('post_new');
961
			}
962
		}
963
964
		// Any mistakes?
965
		if ($this->_post_errors->hasErrors())
966
		{
967
			addInlineJavascript('
968
				$(function() {
969
					$("html,body").scrollTop($(\'.category_header:visible:first\').offset().top);
970
				});'
971
			);
972
973
			$_REQUEST['preview'] = false;
974
			return $this->action_post();
975
		}
976
977
		// Make sure the user isn't spamming the board.
978
		if (!isset($_REQUEST['msg']))
979
			spamProtection('post');
980
981
		// At about this point, we're posting and that's that.
982
		ignore_user_abort(true);
983
		detectServer()->setTimeLimit(300);
984
985
		// Add special html entities to the subject, name, and email.
986
		$_POST['subject'] = strtr(Util::htmlspecialchars($_POST['subject']), array("\r" => '', "\n" => '', "\t" => ''));
987
		$_POST['guestname'] = htmlspecialchars($_POST['guestname'], ENT_COMPAT, 'UTF-8');
988
		$_POST['email'] = htmlspecialchars($_POST['email'], ENT_COMPAT, 'UTF-8');
989
990
		// At this point, we want to make sure the subject isn't too long.
991
		if (Util::strlen($_POST['subject']) > 100)
992
			$_POST['subject'] = Util::substr($_POST['subject'], 0, 100);
993
994
		// Creating a new topic?
995
		$newTopic = empty($_REQUEST['msg']) && empty($topic);
996
997
		// Collect all parameters for the creation or modification of a post.
998
		$msgOptions = array(
999
			'id' => empty($_REQUEST['msg']) ? 0 : (int) $_REQUEST['msg'],
1000
			'subject' => $_POST['subject'],
1001
			'body' => $_POST['message'],
1002
			'icon' => preg_replace('~[\./\\\\*:"\'<>]~', '', $_POST['icon']),
1003
			'smileys_enabled' => !isset($_POST['ns']),
1004
			'approved' => $becomesApproved,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $becomesApproved does not seem to be defined for all execution paths leading up to this point.
Loading history...
1005
		);
1006
1007
		$topicOptions = array(
1008
			'id' => empty($topic) ? 0 : $topic,
1009
			'board' => $board,
1010
			'lock_mode' => isset($_POST['lock']) ? (int) $_POST['lock'] : null,
1011
			'sticky_mode' => isset($_POST['sticky']) ? (int) $_POST['sticky'] : null,
1012
			'mark_as_read' => true,
1013
			'is_approved' => !$modSettings['postmod_active'] || empty($topic) || !empty($board_info['cur_topic_approved']),
1014
		);
1015
1016
		$posterOptions = array(
1017
			'id' => $user_info['id'],
1018
			'name' => $_POST['guestname'],
1019
			'email' => $_POST['email'],
1020
			'update_post_count' => !$user_info['is_guest'] && !isset($_REQUEST['msg']) && $board_info['posts_count'],
1021
		);
1022
1023
		// Trigger the pre_save_post event
1024
		$this->_events->trigger('pre_save_post', array('msgOptions' => &$msgOptions, 'topicOptions' => &$topicOptions, 'posterOptions' => &$posterOptions));
1025
1026
		// This is an already existing message. Edit it.
1027
		if (!empty($_REQUEST['msg']))
1028
		{
1029
			// Have admins allowed people to hide their screwups?
1030
			if (time() - $msgInfo['poster_time'] > $modSettings['edit_wait_time'] || $user_info['id'] != $msgInfo['id_member'])
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $msgInfo does not seem to be defined for all execution paths leading up to this point.
Loading history...
1031
			{
1032
				$msgOptions['modify_time'] = time();
1033
				$msgOptions['modify_name'] = $user_info['name'];
1034
			}
1035
1036
			// This will save some time...
1037
			if (empty($approve_has_changed))
1038
				unset($msgOptions['approved']);
1039
1040
			modifyPost($msgOptions, $topicOptions, $posterOptions);
1041
		}
1042
		// This is a new topic or an already existing one. Save it.
1043
		else
1044
		{
1045
			// We also have to fake the board:
1046
			// if it's valid and it's not the current, let's forget about the "current" and load the new one
1047
			if (!empty($new_board) && $board !== $new_board)
1048
			{
1049
				$board = $new_board;
1050
				loadBoard();
1051
1052
				// Some details changed
1053
				$topicOptions['board'] = $board;
1054
				$topicOptions['is_approved'] = !$modSettings['postmod_active'] || empty($topic) || !empty($board_info['cur_topic_approved']);
1055
				$posterOptions['update_post_count'] = !$user_info['is_guest'] && !isset($_REQUEST['msg']) && $board_info['posts_count'];
1056
			}
1057
1058
			createPost($msgOptions, $topicOptions, $posterOptions);
1059
1060
			if (isset($topicOptions['id']))
1061
				$topic = $topicOptions['id'];
1062
		}
1063
1064
		// Trigger the after_save_post event
1065
		$this->_events->trigger('after_save_post', array('board' => $board, 'topic' => $topic, 'msgOptions' => $msgOptions, 'topicOptions' => $topicOptions, 'becomesApproved' => $becomesApproved, 'posterOptions' => $posterOptions));
1066
1067
		// Marking boards as read.
1068
		// (You just posted and they will be unread.)
1069
		if (!$user_info['is_guest'])
1070
		{
1071
			$board_list = !empty($board_info['parent_boards']) ? array_keys($board_info['parent_boards']) : array();
1072
1073
			// Returning to the topic?
1074
			if (!empty($_REQUEST['goback']))
1075
				$board_list[] = $board;
1076
1077
			if (!empty($board_list))
1078
				markBoardsRead($board_list, false, false);
1079
		}
1080
1081
		// Turn notification on or off.
1082
		if (!empty($_POST['notify']) && allowedTo('mark_any_notify'))
1083
			setTopicNotification($user_info['id'], $topic, true);
1084
		elseif (!$newTopic)
1085
			setTopicNotification($user_info['id'], $topic, false);
1086
1087
		// Log an act of moderation - modifying.
1088
		if (!empty($moderationAction))
1089
			logAction('modify', array('topic' => $topic, 'message' => (int) $_REQUEST['msg'], 'member' => $msgInfo['id_member'], 'board' => $board));
1090
1091
		if (isset($_POST['lock']) && $_POST['lock'] != 2)
1092
			logAction(empty($_POST['lock']) ? 'unlock' : 'lock', array('topic' => $topicOptions['id'], 'board' => $topicOptions['board']));
1093
1094
		if (isset($_POST['sticky']))
1095
			logAction(empty($_POST['sticky']) ? 'unsticky' : 'sticky', array('topic' => $topicOptions['id'], 'board' => $topicOptions['board']));
1096
1097
		// Notify any members who have notification turned on for this topic/board - only do this if it's going to be approved(!)
1098
		if ($becomesApproved)
1099
		{
1100
			require_once(SUBSDIR . '/Notification.subs.php');
1101
			if ($newTopic)
1102
			{
1103
				$notifyData = array(
1104
					'body' => $_POST['message'],
1105
					'subject' => $_POST['subject'],
1106
					'name' => $user_info['name'],
1107
					'poster' => $user_info['id'],
1108
					'msg' => $msgOptions['id'],
1109
					'board' => $board,
1110
					'topic' => $topic,
1111
					'signature' => (isset($user_settings['signature']) ? $user_settings['signature'] : ''),
1112
				);
1113
				sendBoardNotifications($notifyData);
1114
			}
1115
			elseif (empty($_REQUEST['msg']))
1116
			{
1117
				// Only send it to everyone if the topic is approved, otherwise just to the topic starter if they want it.
1118
				if ($topic_info['approved'])
1119
					sendNotifications($topic, 'reply');
1120
				else
1121
					sendNotifications($topic, 'reply', array(), $topic_info['id_member_started']);
1122
			}
1123
		}
1124
1125
		if ($board_info['num_topics'] == 0)
1126
			Cache::instance()->remove('board-' . $board);
1127
1128
		if (!empty($_POST['announce_topic']))
1129
			redirectexit('action=announce;sa=selectgroup;topic=' . $topic . (!empty($_POST['move']) && allowedTo('move_any') ? ';move' : '') . (empty($_REQUEST['goback']) ? '' : ';goback'));
1130
1131
		if (!empty($_POST['move']) && allowedTo('move_any'))
1132
			redirectexit('action=movetopic;topic=' . $topic . '.0' . (empty($_REQUEST['goback']) ? '' : ';goback'));
1133
1134
		// Return to post if the mod is on.
1135
		if (isset($_REQUEST['msg']) && !empty($_REQUEST['goback']))
1136
			redirectexit('topic=' . $topic . '.msg' . $_REQUEST['msg'] . '#msg' . $_REQUEST['msg'], isBrowser('ie'));
1137
		elseif (!empty($_REQUEST['goback']))
1138
			redirectexit('topic=' . $topic . '.new#new', isBrowser('ie'));
1139
		// Dut-dut-duh-duh-DUH-duh-dut-duh-duh!  *dances to the Final Fantasy Fanfare...*
1140
		else
1141
			redirectexit('board=' . $board . '.0');
1142
	}
1143
1144
	/**
1145
	 * Loads a post and inserts it into the current editing text box.
1146
	 * Used to quick edit a post as well as to quote a post and place it in the quick reply box
1147
	 * Can be used to quick edit just the subject from the topic listing
1148
	 *
1149
	 * uses the Post language file.
1150
	 * uses special (sadly browser dependent) javascript to parse entities for internationalization reasons.
1151
	 * accessed with ?action=quotefast and ?action=quotefast;modify
1152
	 */
1153
	public function action_quotefast()
1154
	{
1155
		global $user_info, $context;
1156
1157
		loadLanguage('Post');
1158
1159
		// Where we going if we need to?
1160
		$context['post_box_name'] = isset($_GET['pb']) ? $_GET['pb'] : '';
1161
1162
		$row = quoteMessageInfo((int) $_REQUEST['quote'], isset($_REQUEST['modify']));
1163
1164
		$context['sub_template'] = 'quotefast';
1165
		if (!empty($row))
1166
			$can_view_post = $row['approved'] || ($row['id_member'] != 0 && $row['id_member'] == $user_info['id']) || allowedTo('approve_posts', $row['id_board']);
1167
1168
		if (!empty($can_view_post))
1169
		{
1170
			// Remove special formatting we don't want anymore.
1171
			$row['body'] = $this->preparse->un_preparsecode($row['body']);
1172
1173
			// Censor the message!
1174
			$row['body'] = censor($row['body']);
1175
1176
			$row['body'] = preg_replace('~<br ?/?' . '>~i', "\n", $row['body']);
1177
1178
			// Want to modify a single message by double clicking it?
1179
			if (isset($_REQUEST['modify']))
1180
			{
1181
				$row['subject'] = censor($row['subject']);
1182
1183
				$context['sub_template'] = 'modifyfast';
1184
				$context['message'] = array(
1185
					'id' => $_REQUEST['quote'],
1186
					'body' => $row['body'],
1187
					'subject' => addcslashes($row['subject'], '"'),
1188
				);
1189
1190
				return;
1191
			}
1192
1193
			// Remove any nested quotes.
1194
			$row['body'] = removeNestedQuotes($row['body']);
1195
1196
			// Add a quote string on the front and end.
1197
			$context['quote']['xml'] = '[quote author=' . $row['poster_name'] . ' link=msg=' . (int) $_REQUEST['quote'] . ' date=' . $row['poster_time'] . "]\n" . $row['body'] . "\n[/quote]";
1198
			$context['quote']['text'] = strtr(un_htmlspecialchars($context['quote']['xml']), array('\'' => '\\\'', '\\' => '\\\\', "\n" => '\\n', '</script>' => '</\' + \'script>'));
1199
			$context['quote']['xml'] = strtr($context['quote']['xml'], array('&nbsp;' => '&#160;', '<' => '&lt;', '>' => '&gt;'));
1200
1201
			$context['quote']['mozilla'] = strtr(Util::htmlspecialchars($context['quote']['text']), array('&quot;' => '"'));
1202
		}
1203
		//@todo Needs a nicer interface.
1204
		// In case our message has been removed in the meantime.
1205
		elseif (isset($_REQUEST['modify']))
1206
		{
1207
			$context['sub_template'] = 'modifyfast';
1208
			$context['message'] = array(
1209
				'id' => 0,
1210
				'body' => '',
1211
				'subject' => '',
1212
			);
1213
		}
1214
		else
1215
			$context['quote'] = array(
1216
				'xml' => '',
1217
				'mozilla' => '',
1218
				'text' => '',
1219
			);
1220
	}
1221
1222
	/**
1223
	 * Used to edit the body or subject of a message inline
1224
	 * called from action=jsmodify from script and topic js
1225
	 */
1226
	public function action_jsmodify()
1227
	{
1228
		global $modSettings, $board, $topic;
1229
		global $user_info, $context;
1230
1231
		// We have to have a topic!
1232
		if (empty($topic))
1233
			obExit(false);
1234
1235
		checkSession('get');
1236
1237
		$row = getTopicInfoByMsg($topic, empty($_REQUEST['msg']) ? 0 : (int) $_REQUEST['msg']);
1238
1239
		if (empty($row))
1240
			throw new Elk_Exception('no_board', false);
1241
1242
		// Change either body or subject requires permissions to modify messages.
1243
		if (isset($_POST['message']) || isset($_POST['subject']) || isset($_REQUEST['icon']))
1244
		{
1245
			if (!empty($row['locked']))
1246
				isAllowedTo('moderate_board');
1247
1248
			if ($row['id_member'] == $user_info['id'] && !allowedTo('modify_any'))
1249
			{
1250
				if ((!$modSettings['postmod_active'] || $row['approved']) && !empty($modSettings['edit_disable_time']) && $row['poster_time'] + ($modSettings['edit_disable_time'] + 5) * 60 < time())
1251
					throw new Elk_Exception('modify_post_time_passed', false);
1252
				elseif ($row['id_member_started'] == $user_info['id'] && !allowedTo('modify_own'))
1253
					isAllowedTo('modify_replies');
1254
				else
1255
					isAllowedTo('modify_own');
1256
			}
1257
			// Otherwise, they're locked out; someone who can modify the replies is needed.
1258
			elseif ($row['id_member_started'] == $user_info['id'] && !allowedTo('modify_any'))
1259
				isAllowedTo('modify_replies');
1260
			else
1261
				isAllowedTo('modify_any');
1262
1263
			// Only log this action if it wasn't your message.
1264
			$moderationAction = $row['id_member'] != $user_info['id'];
1265
		}
1266
1267
		if (isset($_POST['subject']) && Util::htmltrim(Util::htmlspecialchars($_POST['subject'])) !== '')
1268
		{
1269
			$_POST['subject'] = strtr(Util::htmlspecialchars($_POST['subject']), array("\r" => '', "\n" => '', "\t" => ''));
1270
1271
			// Maximum number of characters.
1272
			if (Util::strlen($_POST['subject']) > 100)
1273
				$_POST['subject'] = Util::substr($_POST['subject'], 0, 100);
1274
		}
1275
		elseif (isset($_POST['subject']))
1276
		{
1277
			$this->_post_errors->addError('no_subject');
1278
			unset($_POST['subject']);
1279
		}
1280
1281
		if (isset($_POST['message']))
1282
		{
1283
			if (Util::htmltrim(Util::htmlspecialchars($_POST['message'])) === '')
1284
			{
1285
				$this->_post_errors->addError('no_message');
1286
				unset($_POST['message']);
1287
			}
1288
			elseif (!empty($modSettings['max_messageLength']) && Util::strlen($_POST['message']) > $modSettings['max_messageLength'])
1289
			{
1290
				$this->_post_errors->addError(array('long_message', array($modSettings['max_messageLength'])));
1291
				unset($_POST['message']);
1292
			}
1293
			else
1294
			{
1295
				$_POST['message'] = Util::htmlspecialchars($_POST['message'], ENT_QUOTES, 'UTF-8', true);
1296
1297
				$this->preparse->preparsecode($_POST['message']);
1298
				$bbc_parser = \BBC\ParserWrapper::instance();
1299
1300
				if (Util::htmltrim(strip_tags($bbc_parser->parseMessage($_POST['message'], false), '<img>')) === '')
1301
				{
1302
					$this->_post_errors->addError('no_message');
1303
					unset($_POST['message']);
1304
				}
1305
			}
1306
		}
1307
1308
		if (isset($_POST['lock']))
1309
		{
1310
			$_POST['lock'] = $this->_checkLocked($_POST['lock'], $row);
1311
		}
1312
1313
		if (isset($_POST['sticky']) && !allowedTo('make_sticky'))
1314
			unset($_POST['sticky']);
1315
1316
		if (!$this->_post_errors->hasErrors())
1317
		{
1318
			if (!empty($modSettings['mentions_enabled']))
1319
			{
1320
				if (!empty($_REQUEST['uid']))
1321
				{
1322
					$query_params = array();
1323
					$query_params['member_ids'] = array_unique(array_map('intval', $_REQUEST['uid']));
1324
					require_once(SUBSDIR . '/Members.subs.php');
1325
					$mentioned_members = membersBy('member_ids', $query_params, true);
1326
					$replacements = 0;
1327
					$actually_mentioned = array();
1328
					foreach ($mentioned_members as $member)
1329
					{
1330
						$_POST['message'] = str_replace('@' . $member['real_name'], '[member=' . $member['id_member'] . ']' . $member['real_name'] . '[/member]', $_POST['message'], $replacements);
1331
						if ($replacements > 0)
1332
							$actually_mentioned[] = $member['id_member'];
1333
					}
1334
				}
1335
1336
				if (!empty($actually_mentioned))
1337
				{
1338
					$notifier = Notifications::instance();
1339
					$notifier->add(new Notifications_Task(
1340
						'Mentionmem',
1341
						$row['id_msg'],
1342
						$row['id_member'],
1343
						array('id_members' => $actually_mentioned, 'status' => $row['approved'] ? 'new' : 'unapproved')
1344
					));
1345
				}
1346
			}
1347
1348
			$msgOptions = array(
1349
				'id' => $row['id_msg'],
1350
				'subject' => isset($_POST['subject']) ? $_POST['subject'] : null,
1351
				'body' => isset($_POST['message']) ? $_POST['message'] : null,
1352
				'icon' => isset($_REQUEST['icon']) ? preg_replace('~[\./\\\\*\':"<>]~', '', $_REQUEST['icon']) : null,
1353
			);
1354
1355
			$topicOptions = array(
1356
				'id' => $topic,
1357
				'board' => $board,
1358
				'lock_mode' => isset($_POST['lock']) ? (int) $_POST['lock'] : null,
1359
				'sticky_mode' => isset($_POST['sticky']) ? (int) $_POST['sticky'] : null,
1360
				'mark_as_read' => false,
1361
			);
1362
1363
			$posterOptions = array();
1364
1365
			// Only consider marking as editing if they have edited the subject, message or icon.
1366
			if ((isset($_POST['subject']) && $_POST['subject'] != $row['subject']) || (isset($_POST['message']) && $_POST['message'] != $row['body']) || (isset($_REQUEST['icon']) && $_REQUEST['icon'] != $row['icon']))
1367
			{
1368
				// And even then only if the time has passed...
1369
				if (time() - $row['poster_time'] > $modSettings['edit_wait_time'] || $user_info['id'] != $row['id_member'])
1370
				{
1371
					$msgOptions['modify_time'] = time();
1372
					$msgOptions['modify_name'] = $user_info['name'];
1373
				}
1374
			}
1375
			// If nothing was changed there's no need to add an entry to the moderation log.
1376
			else
1377
				$moderationAction = false;
1378
1379
			modifyPost($msgOptions, $topicOptions, $posterOptions);
1380
1381
			// If we didn't change anything this time but had before put back the old info.
1382
			if (!isset($msgOptions['modify_time']) && !empty($row['modified_time']))
1383
			{
1384
				$msgOptions['modify_time'] = $row['modified_time'];
1385
				$msgOptions['modify_name'] = $row['modified_name'];
1386
			}
1387
1388
			// Changing the first subject updates other subjects to 'Re: new_subject'.
1389
			if (isset($_POST['subject']) && isset($_REQUEST['change_all_subjects']) && $row['id_first_msg'] == $row['id_msg'] && !empty($row['num_replies']) && (allowedTo('modify_any') || ($row['id_member_started'] == $user_info['id'] && allowedTo('modify_replies'))))
1390
			{
1391
				// Get the proper (default language) response prefix first.
1392
				$context['response_prefix'] = response_prefix();
1393
1394
				topicSubject(array('id_topic' => $topic, 'id_first_msg' => $row['id_first_msg']), $_POST['subject'], $context['response_prefix'], true);
1395
			}
1396
1397
			if (!empty($moderationAction))
1398
				logAction('modify', array('topic' => $topic, 'message' => $row['id_msg'], 'member' => $row['id_member'], 'board' => $board));
1399
		}
1400
1401
		if (isset($_REQUEST['xml']))
1402
		{
1403
			$bbc_parser = \BBC\ParserWrapper::instance();
1404
			$context['sub_template'] = 'modifydone';
1405
1406
			if (!$this->_post_errors->hasErrors() && isset($msgOptions['subject']) && isset($msgOptions['body']))
1407
			{
1408
				$context['message'] = array(
1409
					'id' => $row['id_msg'],
1410
					'modified' => array(
1411
						'time' => isset($msgOptions['modify_time']) ? standardTime($msgOptions['modify_time']) : '',
1412
						'html_time' => isset($msgOptions['modify_time']) ? htmlTime($msgOptions['modify_time']) : '',
1413
						'timestamp' => isset($msgOptions['modify_time']) ? forum_time(true, $msgOptions['modify_time']) : 0,
1414
						'name' => isset($msgOptions['modify_time']) ? $msgOptions['modify_name'] : '',
1415
					),
1416
					'subject' => $msgOptions['subject'],
1417
					'first_in_topic' => $row['id_msg'] == $row['id_first_msg'],
1418
					'body' => strtr($msgOptions['body'], array(']]>' => ']]]]><![CDATA[>')),
1419
				);
1420
1421
				$context['message']['subject'] = censor($context['message']['subject']);
1422
				$context['message']['body'] = censor($context['message']['body']);
1423
1424
				$context['message']['body'] = $bbc_parser->parseMessage($context['message']['body'], $row['smileys_enabled']);
1425
			}
1426
			// Topic?
1427
			elseif (!$this->_post_errors->hasErrors())
1428
			{
1429
				$context['sub_template'] = 'modifytopicdone';
1430
				$context['message'] = array(
1431
					'id' => $row['id_msg'],
1432
					'modified' => array(
1433
						'time' => isset($msgOptions['modify_time']) ? standardTime($msgOptions['modify_time']) : '',
1434
						'html_time' => isset($msgOptions['modify_time']) ? htmlTime($msgOptions['modify_time']) : '',
1435
						'timestamp' => isset($msgOptions['modify_time']) ? forum_time(true, $msgOptions['modify_time']) : 0,
1436
						'name' => isset($msgOptions['modify_time']) ? $msgOptions['modify_name'] : '',
1437
					),
1438
					'subject' => isset($msgOptions['subject']) ? $msgOptions['subject'] : '',
1439
				);
1440
1441
				$context['message']['subject'] = censor($context['message']['subject']);
1442
			}
1443
			else
1444
			{
1445
				$context['message'] = array(
1446
					'id' => $row['id_msg'],
1447
					'errors' => array(),
1448
					'error_in_subject' => $this->_post_errors->hasError('no_subject'),
1449
					'error_in_body' => $this->_post_errors->hasError('no_message') || $this->_post_errors->hasError('long_message'),
1450
				);
1451
				$context['message']['errors'] = $this->_post_errors->prepareErrors();
1452
			}
1453
		}
1454
		else
1455
			obExit(false);
1456
	}
1457
1458
	/**
1459
	 * Toggle a post lock status
1460
	 *
1461
	 * @param int|null $lock
1462
	 * @param string|null $topic_info
1463
	 *
1464
	 * @return int|null
1465
	 */
1466
	protected function _checkLocked($lock, $topic_info = null)
1467
	{
1468
		global $user_info;
1469
1470
		// A new topic
1471
		if ($topic_info === null)
1472
		{
1473
			// New topics are by default not locked.
1474
			if (empty($lock))
1475
				return null;
1476
			// Besides, you need permission.
1477
			elseif (!allowedTo(array('lock_any', 'lock_own')))
1478
				return null;
1479
			// A moderator-lock (1) can override a user-lock (2).
1480
			else
1481
				return allowedTo('lock_any') ? 1 : 2;
1482
		}
1483
1484
		// Nothing changes to the lock status.
1485
		if ((empty($lock) && empty($topic_info['locked'])) || (!empty($lock) && !empty($topic_info['locked'])))
1486
			return null;
1487
		// You're simply not allowed to (un)lock this.
1488
		elseif (!allowedTo(array('lock_any', 'lock_own')) || (!allowedTo('lock_any') && $user_info['id'] != $topic_info['id_member_started']))
1489
			return null;
1490
		// You're only allowed to lock your own topics.
1491
		elseif (!allowedTo('lock_any'))
1492
		{
1493
			// You're not allowed to break a moderator's lock.
1494
			if ($topic_info['locked'] == 1)
1495
				return null;
1496
			// Lock it with a soft lock or unlock it.
1497
			else
1498
				$lock = empty($lock) ? 0 : 2;
1499
		}
1500
		// You must be the moderator.
1501
		else
1502
			$lock = empty($lock) ? 0 : 1;
1503
1504
		return $lock;
1505
	}
1506
}
1507