Issues (1686)

sources/ElkArte/Controller/Post.php (16 issues)

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
 * @package   ElkArte Forum
9
 * @copyright ElkArte Forum contributors
10
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file)
11
 *
12
 * This file contains code covered by:
13
 * copyright: 2011 Simple Machines (http://www.simplemachines.org)
14
 *
15
 * @version 2.0 dev
16
 *
17
 */
18
19
namespace ElkArte\Controller;
20
21
use BBC\ParserWrapper;
22
use BBC\PreparseCode;
23
use ElkArte\AbstractController;
24
use ElkArte\Cache\Cache;
25
use ElkArte\Errors\ErrorContext;
26
use ElkArte\Exceptions\ControllerRedirectException;
27
use ElkArte\Exceptions\Exception;
28
use ElkArte\Helper\DataValidator;
29
use ElkArte\Helper\Util;
30
use ElkArte\Languages\Txt;
31
use ElkArte\Notifications\Notifications;
32
use ElkArte\Notifications\NotificationsTask;
33
use ElkArte\Themes\TemplateLayers;
34
use ElkArte\User;
35
36
/**
37
 * Everything related to posting new replies and topics and modifications of them
38
 */
39
class Post extends AbstractController
40
{
41
	/** @var null|ErrorContext The post (messages) errors object */
42
	protected $_post_errors;
43
44
	/** @var null|TemplateLayers The template layers object */
45
	protected $_template_layers;
46
47
	/** @var array An array of attributes of the topic (if not new) */
48
	protected $_topic_attributes = array();
49
50
	/** @var string The message subject */
51
	protected $_form_subject = '';
52
53
	/** @var string The message */
54
	protected $_form_message = '';
55
56
	/** @var PreparseCode */
57
	protected $preparse;
58
59
	/**
60 6
	 * Sets up common stuff for all or most of the actions.
61
	 */
62 6
	public function pre_dispatch()
63 6
	{
64
		$this->_post_errors = ErrorContext::context('post', 1);
65 6
		$this->_template_layers = theme()->getLayers();
66
67 6
		$this->preparse = PreparseCode::instance($this->user->name);
0 ignored issues
show
Bug Best Practice introduced by
The property name does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
68 6
69 6
		require_once(SUBSDIR . '/Post.subs.php');
70 6
		require_once(SUBSDIR . '/Messages.subs.php');
71
		require_once(SUBSDIR . '/Topic.subs.php');
72
	}
73
74
	/**
75
	 * Dispatch to the right action method for the request.
76
	 *
77
	 * @see AbstractController::action_index
78
	 */
79
	public function action_index()
80
	{
81
		// Figure out the right action to do.
82
		// hint: I'm post controller. :P
83
		$this->action_post();
84
	}
85
86
	/**
87
	 * Handles showing the post screen, loading the post to be modified, loading any post quoted, previews,
88
	 * display of errors and polls.
89
	 *
90
	 * What it does:
91
	 *
92
	 * - Validates that we're posting in a board.
93
	 * - Find the topic id if a message id is passed, else assume it's a new message
94
	 * - Get the response prefix in the default forum language.
95
	 * - Triggers events associated with posting.
96
	 *    - prepare_post, prepare_context, prepare_modifying, prepare_editing,
97
	 *    - prepare_posting, post_errors, finalize_post_form
98
	 * - Additionally handles previews of posts.
99
	 * - Requires different permissions depending on the actions, but most notably post_new, post_reply_own, and post_reply_any.
100
	 * - Shows options for the editing and posting of calendar events and attachments, and as the posting of polls (using modules).
101
	 * - Accessed from ?action=post or called from action_post2 in the event of errors or preview from quick reply.
102
	 *
103
	 * @uses the Post template and language file, main sub template.
104
	 * @uses Errors language
105
	 */
106
	public function action_post()
107
	{
108
		global $context;
109
110
		// Initialize the post area
111
		$this->_beforePreparePost();
112
113
		// Trigger the prepare_post event
114
		$this->_events->trigger('prepare_post', ['topic_attributes' => &$this->_topic_attributes]);
115
116
		// Sets post form options / checkbox / etc
117
		$this->_beforePrepareContext();
118
119
		// Trigger the prepare_context event
120
		try
121
		{
122
			$this->_events->trigger('prepare_context', ['id_member_poster' => (int) $this->_topic_attributes['id_member']]);
123
		}
124
		catch (ControllerRedirectException $controllerRedirectException)
125
		{
126
			return $controllerRedirectException->doRedirect($this);
127
		}
128
129
		// Load up the message details if this is an existing msg
130
		$this->_generatingMessage();
131
132
		// Trigger post_errors event
133
		$this->_events->trigger('post_errors');
134
135
		$this->_preparingPage();
136
137
		// Needed for the editor and message icons.
138
		require_once(SUBSDIR . '/Editor.subs.php');
139
140
		// Now create the editor.
141
		$editorOptions = [
142
			'id' => 'message',
143
			'value' => $context['message'],
144
			'labels' => [
145
				'post_button' => $context['submit_label'],
146
			],
147
			// add height and width for the editor
148
			'height' => '275px',
149
			'width' => '100%',
150
			// We do XML preview here.
151
			'preview_type' => 2,
152
			'smiley_container' => 'smileyBox_message',
153
			'bbc_container' => 'bbcBox_message',
154
			'live_errors' => 1
155
		];
156
157
		// Trigger the finalize_post_form event
158
		$this->_events->trigger('finalize_post_form', array('destination' => &$context['destination'], 'page_title' => &$context['page_title'], 'show_additional_options' => &$context['show_additional_options'], 'editorOptions' => &$editorOptions));
159
160
		// Initialize the editor
161
		create_control_richedit($editorOptions);
162
163
		$this->_finalizePage();
164
	}
165
166
	/**
167
	 * Load language files, templates and prepare posting basics
168
	 */
169
	protected function _beforePreparePost()
170
	{
171
		global $context;
172
173
		Txt::load('Post');
174
		Txt::load('Errors');
175
176
		$context['robot_no_index'] = true;
177
		$this->_template_layers->add('postarea');
0 ignored issues
show
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

177
		$this->_template_layers->/** @scrutinizer ignore-call */ 
178
                           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...
178
		$this->_topic_attributes = array(
179
			'locked' => 0,
180
			'notify' => 0,
181
			'is_sticky' => 0,
182
			'id_last_msg' => 0,
183
			'id_member' => 0,
184
			'id_first_msg' => 0,
185
			'subject' => '',
186
			'last_post_time' => 0
187
		);
188
	}
189
190
	/**
191
	 * Does some basic checking and if everything is valid will
192
	 * load $context with needed post form options
193
	 *
194
	 * - Ensures we have a topic id
195
	 * - Checks if a topic is locked
196
	 * - Determines if this msg will be pre approved or member requires approval
197
	 *
198
	 * @throws Exception
199
	 */
200
	protected function _beforePrepareContext()
201
	{
202
		global $topic, $modSettings, $board, $context;
203
204
		// You must be posting to *some* board.
205
		if (empty($board) && !$context['make_event'])
206
		{
207
			throw new Exception('no_board', false);
208
		}
209
210
		// All those wonderful modifiers and attachments
211
		$this->_template_layers->add('additional_options', 200);
212
213
		if ($this->getApi() !== false)
214
		{
215
			$context['sub_template'] = 'post';
216
217
			// Just in case of an earlier error...
218
			$context['preview_message'] = '';
219
			$context['preview_subject'] = '';
220
		}
221
222
		// No message is complete without a topic.
223
		if (empty($topic) && !empty($_REQUEST['msg']))
224
		{
225
			$topic = associatedTopic((int) $_REQUEST['msg']);
226
			if (empty($topic))
227
			{
228
				$this->_req->clearValue('msg', 'both');
229
			}
230
		}
231
232
		// Check if it's locked. It isn't locked if no topic is specified.
233
		if (!empty($topic))
234
		{
235
			$this->_topic_attributes = topicUserAttributes($topic, $this->user->id);
0 ignored issues
show
Bug Best Practice introduced by
The property id does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
236
			$context['notify'] = $this->_topic_attributes['notify'];
237
			$context['topic_last_message'] = $this->_topic_attributes['id_last_msg'];
238
			$msg = $this->_req->getRequest('msg', 'intval', 0);
239
240
			if (empty($msg))
241
			{
242
				if ($this->user->is_guest && !allowedTo('post_reply_any')
0 ignored issues
show
Bug Best Practice introduced by
The property is_guest does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
243
					&& (!$modSettings['postmod_active'] || !allowedTo('post_unapproved_replies_any')))
244
				{
245
					is_not_guest();
246
				}
247
248
				// By default, the reply will be approved...
249
				$context['becomes_approved'] = true;
250
				if ($this->_topic_attributes['id_member'] != $this->user->id)
251
				{
252
					if ($modSettings['postmod_active'] && allowedTo('post_unapproved_replies_any')
253
						&& !allowedTo('post_reply_any'))
254
					{
255
						$context['becomes_approved'] = false;
256
					}
257
					else
258
					{
259
						isAllowedTo('post_reply_any');
260
					}
261
				}
262
				elseif (!allowedTo('post_reply_any'))
263
				{
264
					if ($modSettings['postmod_active'])
265
					{
266
						if (allowedTo('post_unapproved_replies_own') && !allowedTo('post_reply_own'))
267
						{
268
							$context['becomes_approved'] = false;
269
						}
270
						// Guests do not have post_unapproved_replies_own permission, so it's always post_unapproved_replies_any
271
						elseif ($this->user->is_guest && allowedTo('post_unapproved_replies_any'))
272
						{
273
							$context['becomes_approved'] = false;
274
						}
275
						else
276
						{
277
							isAllowedTo('post_reply_own');
278
						}
279
					}
280
					else
281
					{
282
						isAllowedTo('post_reply_own');
283
					}
284
				}
285
			}
286
			else
287
			{
288
				$context['becomes_approved'] = true;
289
			}
290
291
			$context['can_lock'] = allowedTo('lock_any') || ($this->user->id == $this->_topic_attributes['id_member'] && allowedTo('lock_own'));
292
			$context['can_sticky'] = allowedTo('make_sticky');
293
			$context['notify'] = !empty($context['notify']);
294
			$context['sticky'] = isset($_REQUEST['sticky']) ? !empty($_REQUEST['sticky']) : $this->_topic_attributes['is_sticky'];
295
		}
296
		else
297
		{
298
			$this->_topic_attributes['id_member'] = 0;
299
			$context['becomes_approved'] = true;
300
			if (empty($context['make_event']) || !empty($board))
301
			{
302
				if ($modSettings['postmod_active'] && !allowedTo('post_new') && allowedTo('post_unapproved_topics'))
303
				{
304
					$context['becomes_approved'] = false;
305
				}
306
				else
307
				{
308
					isAllowedTo('post_new');
309
				}
310
			}
311
312
			$this->_topic_attributes['locked'] = 0;
313
314
			// @todo These won't work if you're making an event.
315
			$context['can_lock'] = allowedTo(array('lock_any', 'lock_own'));
316
			$context['can_sticky'] = allowedTo('make_sticky');
317
318
			$context['notify'] = !empty($context['notify']);
319
			$context['sticky'] = !empty($_REQUEST['sticky']);
320
		}
321
322
		// @todo These won't work if you're posting an event!
323
		$context['can_notify'] = allowedTo('mark_any_notify');
324
		$context['can_move'] = allowedTo('move_any');
325
		$context['move'] = !empty($_REQUEST['move']);
326
		$context['announce'] = !empty($_REQUEST['announce']);
327
		$context['id_draft'] = $this->_req->getPost('id_draft', 'intval', 0);
328
329
		// You can only announce topics that will get approved...
330
		$context['can_announce'] = allowedTo('announce_topic') && $context['becomes_approved'];
331
		$context['locked'] = !empty($this->_topic_attributes['locked']) || !empty($_REQUEST['lock']);
332
		$context['can_quote'] = empty($modSettings['disabledBBC']) || !in_array('quote', explode(',', $modSettings['disabledBBC']));
333
334
		// Generally don't show the approval box... (Assume we want things approved)
335
		$context['show_approval'] = allowedTo('approve_posts') && $context['becomes_approved'] ? 2 : (allowedTo('approve_posts') ? 1 : 0);
336
337
		// Don't allow a post if it's locked and you aren't all powerful.
338
		if (!$this->_topic_attributes['locked'])
339
		{
340
			return;
341
		}
342
343
		if (allowedTo('moderate_board'))
344
		{
345
			return;
346
		}
347
348
		throw new Exception('topic_locked', false);
349
	}
350
351
	/**
352
	 * Get the message setup for ...
353
	 *
354
	 * - Sets up the form for preview / modify / new message status.  Items
355
	 * such as icons, text, etc.
356
	 * - Look if a new topic was posted while working on this prose
357
	 * - Shows the message preview if requested
358
	 * - Triggers prepare_modifying, prepare_editing, prepare_posting
359
	 */
360
	protected function _generatingMessage()
361
	{
362
		global $txt, $topic, $modSettings, $context, $options, $board_info;
363
364
		// Convert / Clean the input elements
365
		$msg = $this->_req->getRequest('msg', 'intval', null);
366
		$last_msg = $this->_req->getRequest('last_msg', 'intval', null);
367
		$message = $this->_req->getPost('message', 'trim', null);
368
		$subject = $this->_req->getPost('subject', 'trim', '');
369
370
		// See if any new replies have come along.
371
		if (empty($msg)
372
			&& !empty($topic)
373
			&& empty($options['no_new_reply_warning'])
374
			&& isset($last_msg)
375
			&& $context['topic_last_message'] > $last_msg)
376
		{
377
			$context['new_replies'] = countMessagesSince($topic, (int) $_REQUEST['last_msg'], false, $modSettings['postmod_active'] && !allowedTo('approve_posts'));
378
379
			if (!empty($context['new_replies']))
380
			{
381
				if ($context['new_replies'] == 1)
382
				{
383
					$txt['error_new_replies'] = isset($_GET['last_msg']) ? $txt['error_new_reply_reading'] : $txt['error_new_reply'];
384
				}
385
				else
386
				{
387
					$txt['error_new_replies'] = sprintf(isset($_GET['last_msg']) ? $txt['error_new_replies_reading'] : $txt['error_new_replies'], $context['new_replies']);
388
				}
389
390
				$this->_post_errors->addError('new_replies', 0);
0 ignored issues
show
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

390
				$this->_post_errors->/** @scrutinizer ignore-call */ 
391
                         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...
391
392
				$modSettings['topicSummaryPosts'] = $context['new_replies'] > $modSettings['topicSummaryPosts'] ? max($modSettings['topicSummaryPosts'], 5) : $modSettings['topicSummaryPosts'];
393
			}
394
		}
395
396
		// Get a response prefix (like 'Re:') in the default forum language.
397
		$context['response_prefix'] = response_prefix();
398
		$context['destination'] = 'post2;start=' . $this->_req->getRequest('start', 'intval', 0);
399
400
		// Previewing, modifying, or posting?
401
		// Do we have a body, but an error happened.
402
		if (isset($message) || $this->_post_errors->hasErrors())
403
		{
404
			$this->_previewPost($msg, $topic, $message, $subject);
405
		}
406
		// Editing a message...
407
		elseif (isset($msg) && !empty($topic))
408
		{
409
			$this->_editPost($msg, $topic);
410
		}
411
		// Posting...
412
		else
413
		{
414
			$this->_makePost($topic, $subject);
415
		}
416
417
		// Check whether this is a really old post being bumped...
418
		if (!empty($topic)
419
			&& !empty($board_info['old_posts'])
420
			&& !empty($modSettings['oldTopicDays'])
421
			&& $this->_topic_attributes['last_post_time'] + $modSettings['oldTopicDays'] * 86400 < time()
422
			&& empty($this->_topic_attributes['is_sticky'])
423
			&& !isset($_REQUEST['subject']))
424
		{
425
			$this->_post_errors->addError(array('old_topic', array($modSettings['oldTopicDays'])), 0);
426
		}
427
	}
428
429
	/**
430
	 * Preview a post,
431
	 * - From pressing preview
432
	 * - When errors are generated when trying to post.
433
	 *
434
	 * @param int $msg
435
	 * @param int $topic
436
	 * @param string $message
437
	 * @param string $subject
438
	 */
439
	private function _previewPost($msg, $topic, $message, $subject)
440
	{
441
		global $txt, $modSettings, $context;
442
443
		$preview = $this->_req->getPost('preview', 'isset', false);
444
		$more_options = $this->_req->getPost('more_options', 'isset', false);
445
		$ns = $this->_req->getPost('ns', 'isset', false);
446
		$notify = $this->_req->getPost('notify', 'intval', 0);
447
		$quote = $this->_req->getRequest('quote', 'intval', 0);
448
		$followup = $this->_req->getRequest('followup', 'intval', 0);
449
		$not_approved = $this->_req->getPost('not_approved', 'empty', true);
450
		$last_msg = $this->_req->getRequest('last_msg', 'intval', null);
451
		$icon = $this->_req->getPost('icon', 'trim', 'xx');
452
		$msg_id = 0;
453
454
		// Validate inputs if they are not just moving the full post form (from QR / QT)
455
		if (!$more_options && !$this->_post_errors->hasErrors())
456
		{
457
			// This means they didn't click Post and get an error.
458
			$really_previewing = true;
459
		}
460
		else
461
		{
462
			if (!isset($message))
463
			{
464
				$message = '';
465
			}
466
467
			// They are previewing if they asked to preview (i.e. came from quick reply).
468
			$really_previewing = !empty($preview) || ($this->getApi() === 'xml');
469
		}
470
471
		// Trigger the prepare_modifying event
472
		$this->_events->trigger('prepare_modifying', [
473
			'post_errors' => $this->_post_errors,
474
			'really_previewing' => &$really_previewing]
475
		);
476
477
		// In order to keep the approval status flowing through, we have to pass it through the form...
478
		$context['becomes_approved'] = $not_approved;
479
		$context['show_approval'] = isset($this->_req->post->approve) ? ($this->_req->post->approve ? 2 : 1) : 0;
480
		$context['can_announce'] = $context['can_announce'] && $context['becomes_approved'];
481
482
		// Set up the inputs for the form.
483
		$this->_form_subject = strtr(Util::htmlspecialchars($subject), array("\r" => '', "\n" => '', "\t" => ''));
484
		$this->_form_message = Util::htmlspecialchars($message, ENT_QUOTES, 'UTF-8', true);
485
486
		// Make sure the subject isn't too long - taking into account special characters.
487
		if (Util::strlen($this->_form_subject) > 100)
488
		{
489
			$this->_form_subject = Util::substr($this->_form_subject, 0, 100);
490
		}
491
492
		// Are you... a guest?
493
		if ($this->user->is_guest)
0 ignored issues
show
Bug Best Practice introduced by
The property is_guest does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
494
		{
495
			$context['name'] = $this->_req->getPost('guestname', 'Util::htmlspecialchars', '');
496
			$context['email'] = $this->_req->getPost('email', 'Util::htmlspecialchars', '');
497
			$this->user->name = $context['name'];
0 ignored issues
show
Bug Best Practice introduced by
The property name does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __set, consider adding a @property annotation.
Loading history...
498
		}
499
500
		// Only show the preview stuff if they really hit Preview, not post, not more options
501
		if ($really_previewing)
502
		{
503
			$this->_setupPreviewContext(!$ns);
504
		}
505
506
		// Set up the checkboxes.
507
		$context['notify'] = $notify;
508
		$context['use_smileys'] = !$ns;
509
		$context['icon'] = preg_replace('~[\./\\\\*\':"<>]~', '', $icon);
510
511
		// Set the destination action for submission.
512
		$context['destination'] .= isset($msg) ? ';msg=' . $msg . ';' . $context['session_var'] . '=' . $context['session_id'] : '';
513
		$context['submit_label'] = isset($msg) ? $txt['save'] : $txt['post'];
514
515
		// Previewing an edit? Modifying an existing message?
516
		if (isset($msg) && !empty($topic))
517
		{
518
			// Get the existing message.
519
			$message = messageDetails($msg, $topic);
520
521
			// The message they were trying to edit was most likely deleted.
522
			if ($message === false)
0 ignored issues
show
The condition $message === false is always false.
Loading history...
523
			{
524
				throw new Exception('no_message', false);
525
			}
526
527
			$errors = checkMessagePermissions($message['message']);
528
			if (!empty($errors))
529
			{
530
				foreach ($errors as $error)
531
				{
532
					$this->_post_errors->addError($error);
533
				}
534
			}
535
536
			prepareMessageContext($message);
537
		}
538
		elseif (isset($last_msg))
539
		{
540
			// @todo: sort out what kind of combinations are actually possible
541
			// Posting a quoted reply?
542
			if ((!empty($topic) && !empty($quote))
543
				|| (!empty($modSettings['enableFollowup']) && !empty($followup)))
544
			{
545
				$msg_id = empty($quote) ? $followup : $quote;
546
				$case = 2;
547
			}
548
			// Posting a reply without a quote?
549
			elseif (!empty($topic) && empty($quote))
550
			{
551
				$this->_topic_attributes['subject'] = $subject;
552
				$case = 3;
553
			}
554
			else
555
			{
556
				$case = 4;
557
			}
558
559
			[$this->_form_subject,] = getFormMsgSubject($case, $topic, $this->_topic_attributes['subject'], $msg_id);
560
		}
561
562
		// No check is needed, since nothing is really posted.
563
		checkSubmitOnce('free');
564
	}
565
566
	/**
567
	 * Going back to a message to make changes, like damn I should watch what
568
	 * my fingers are typing.
569
	 *
570
	 * @param int $msg
571
	 * @param int $topic
572
	 */
573
	private function _editPost($msg, $topic)
574
	{
575
		global $txt, $context;
576
577
		$message = getFormMsgSubject(1, $topic, '', $msg);
578
579
		// The message they were trying to edit was most likely deleted.
580
		if ($message === false)
581
		{
582
			throw new Exception('no_message', false);
583
		}
584
585
		// Trigger the prepare_editing event
586
		$this->_events->trigger('prepare_editing', array('topic' => $topic, 'message' => &$message));
587
588
		if (!empty($message['errors']))
589
		{
590
			foreach ($message['errors'] as $error)
591
			{
592
				$this->_post_errors->addError($error);
593
			}
594
		}
595
596
		// Get the stuff ready for the form.
597
		$this->_form_subject = censor($message['message']['subject']);
598
		$this->_form_message = censor($this->preparse->un_preparsecode($message['message']['body']));
599
600
		// Check the boxes that should be checked.
601
		$context['use_smileys'] = !empty($message['message']['smileys_enabled']);
602
		$context['icon'] = $message['message']['icon'];
603
604
		// Set the destination.
605
		$context['destination'] .= ';msg=' . $msg . ';' . $context['session_var'] . '=' . $context['session_id'];
606
		$context['submit_label'] = $txt['save'];
607
	}
608
609
	/**
610
	 * Think you are done and ready to make a post
611
	 *
612
	 * @param int $topic
613
	 * @param string $subject
614
	 */
615
	private function _makePost($topic, $subject)
616
	{
617
		global $context, $txt, $modSettings;
618
619
		$quote = $this->_req->getRequest('quote', 'intval', 0);
620
		$followup = $this->_req->getRequest('followup', 'intval', 0);
621
622
		// By default....
623
		$context['use_smileys'] = true;
624
		$context['icon'] = 'xx';
625
		$msg_id = 0;
626
627
		if ($this->user->is_guest)
0 ignored issues
show
Bug Best Practice introduced by
The property is_guest does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
628
		{
629
			$context['name'] = $_SESSION['guest_name'] ?? '';
630
			$context['email'] = $_SESSION['guest_email'] ?? '';
631
		}
632
633
		// Trigger the prepare_posting event
634
		$this->_events->trigger('prepare_posting');
635
636
		$context['submit_label'] = $txt['post'];
637
638
		// @todo: sort out what kind of combinations are actually possible
639
		// Posting a quoted reply?
640
		if ((!empty($topic) && $quote !== 0)
641
			|| (!empty($modSettings['enableFollowup']) && $followup !== 0))
642
		{
643
			$case = 2;
644
			$msg_id = empty($quote) ? $followup : $quote;
645
		}
646
		// Posting a reply without a quote?
647
		elseif (!empty($topic) && $quote === 0)
648
		{
649
			$case = 3;
650
		}
651
		else
652
		{
653
			$this->_topic_attributes['subject'] = $subject;
654
			$case = 4;
655
		}
656
657
		[$this->_form_subject, $this->_form_message] = getFormMsgSubject($case, $topic, $this->_topic_attributes['subject'], $msg_id);
658
	}
659
660
	/**
661
	 * Loads up the context global with a preview of the post
662
	 *
663
	 * @param bool $ns no smiley flag
664
	 */
665
	private function _setupPreviewContext($ns)
666
	{
667
		global $txt, $modSettings, $context;
668
669
		// Set up the preview message and subject
670
		$context['preview_message'] = $this->_form_message;
671
		$this->preparse->preparsecode($this->_form_message, true);
672
673
		// Do all bulletin board code thing on the message
674
		$bbc_parser = ParserWrapper::instance();
675
		$this->preparse->preparsecode($context['preview_message']);
676
		$context['preview_message'] = $bbc_parser->parseMessage($context['preview_message'], $ns);
677
		$context['preview_message'] = censor($context['preview_message']);
678
679
		// Don't forget the subject
680
		$context['preview_subject'] = censor($this->_form_subject);
681
682
		// Any errors we should tell them about?
683
		if ($this->_form_subject === '')
684
		{
685
			$this->_post_errors->addError('no_subject');
686
			$context['preview_subject'] = '<em>' . $txt['no_subject'] . '</em>';
687
		}
688
689
		if ($context['preview_message'] === '')
690
		{
691
			$this->_post_errors->addError('no_message');
692
		}
693
		elseif (!empty($modSettings['max_messageLength']) && Util::strlen($this->_form_message) > $modSettings['max_messageLength'])
694
		{
695
			$this->_post_errors->addError(array('long_message', array($modSettings['max_messageLength'])));
696
		}
697
698
		// Protect any CDATA blocks.
699
		if ($this->getApi() === 'xml')
700
		{
701
			$context['preview_message'] = strtr($context['preview_message'], [']]>' => ']]]]><![CDATA[>']);
702
		}
703
	}
704
705
	/**
706
	 * Preparing the page for post preview or error handling.
707
	 */
708
	protected function _preparingPage()
709
	{
710
		global $txt, $topic, $modSettings, $board, $context;
711
712
		// Any errors occurred?
713
		$context['post_error'] = array(
714
			'errors' => $this->_post_errors->prepareErrors(),
715
			'type' => $this->_post_errors->getErrorType() === 0 ? 'minor' : 'serious',
716
			'title' => $this->_post_errors->getErrorType() === 0 ? $txt['warning_while_submitting'] : $txt['error_while_submitting'],
717
		);
718
719
		// What are you doing? Posting, modifying, previewing, new post, or reply...
720
		if (empty($context['page_title']))
721
		{
722
			if (isset($_REQUEST['msg']))
723
			{
724
				$context['page_title'] = $txt['modify_msg'];
725
			}
726
			elseif (isset($_REQUEST['subject'], $context['preview_subject']))
727
			{
728
				$context['page_title'] = $txt['post_reply'];
729
			}
730
			elseif (empty($topic))
731
			{
732
				$context['page_title'] = $txt['start_new_topic'];
733
			}
734
			else
735
			{
736
				$context['page_title'] = $txt['post_reply'];
737
			}
738
		}
739
740
		// Update the topic summary, needed to show new posts in a preview
741
		if (!empty($topic) && !empty($modSettings['topicSummaryPosts']))
742
		{
743
			$only_approved = $modSettings['postmod_active'] && !allowedTo('approve_posts');
744
745
			if ($this->getApi() === 'xml')
746
			{
747
				$limit = empty($context['new_replies']) ? 0 : (int) $context['new_replies'];
748
			}
749
			else
750 6
			{
751
				$limit = $modSettings['topicSummaryPosts'];
752 6
			}
753 6
754
			$before = isset($_REQUEST['msg']) ? array('before' => (int) $_REQUEST['msg']) : array();
755
756 6
			$counter = 0;
757
			$context['previous_posts'] = empty($limit) ? [] : selectMessages($topic, 0, $limit, $before, $only_approved);
758
			foreach ($context['previous_posts'] as &$post)
759
			{
760
				$post['is_new'] = !empty($context['new_replies']);
761
				$post['counter'] = $counter++;
762
				$post['is_ignored'] = !empty($modSettings['enable_buddylist']) && in_array($post['id_poster'], $this->user->ignoreusers);
0 ignored issues
show
It seems like $this->user->ignoreusers can also be of type null; however, parameter $haystack of in_array() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

762
				$post['is_ignored'] = !empty($modSettings['enable_buddylist']) && in_array($post['id_poster'], /** @scrutinizer ignore-type */ $this->user->ignoreusers);
Loading history...
Bug Best Practice introduced by
The property ignoreusers does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
763
764
				if (!empty($context['new_replies']))
765
				{
766
					$context['new_replies']--;
767 6
				}
768
			}
769
770
			// If there are previous posts, enable QQ for them
771
			if (count($context['previous_posts']) > 0)
772
			{
773 6
				loadJavascriptFile('quickQuote.js', ['defer' => true]);
774
				theme()->addInlineJavascript("
775
					document.addEventListener('DOMContentLoaded', () => new Elk_QuickQuote(), false);", true
776 6
				);
777
			}
778
		}
779 6
780
		// Just ajax previewing then lets stop now
781
		if ($this->getApi() === 'xml')
782
		{
783
			obExit();
784
		}
785
786
		$context['subject'] = addcslashes($this->_form_subject, '"');
787
		$context['message'] = str_replace(array('"', '<', '>', '&nbsp;'), array('&quot;', '&lt;', '&gt;', ' '), $this->_form_message);
788
789 6
		// Message icons - customized or not, retrieve them...
790
		require_once(SUBSDIR . '/MessageIcons.subs.php');
791
		$context['icons'] = array_values(getMessageIcons($board));
792 6
793
		$context['icon_url'] = '';
794
795
		if (!empty($context['icons']))
796
		{
797 6
			$context['icons'][count($context['icons']) - 1]['is_last'] = true;
798 6
			$context['icons'][0]['selected'] = true;
799
800
			// $context['icon'] is set when editing a message
801 6
			if (!isset($context['icon']))
802
			{
803
				$context['icon'] = $context['icons'][0]['value'];
804 6
			}
805
806
			$found = false;
807 6
			foreach ($context['icons'] as $icon)
808
			{
809 4
				if ($icon['value'] === $context['icon'])
810
				{
811
					$found = true;
812 4
					$context['icon_url'] = $icon['url'];
813
					break;
814
				}
815
			}
816
817
			// Fail safe
818 4
			if (!$found)
819
			{
820
				$context['icon'] = $context['icons'][0]['value'];
821
				$context['icon_url'] = $context['icons'][0]['url'];
822
			}
823
		}
824
825 6
		$context['show_additional_options'] = !empty($_POST['additional_options']) || isset($_GET['additionalOptions']);
826
	}
827
828 2
	/**
829
	 *
830
	 */
831
	protected function _finalizePage()
832
	{
833
		global $txt, $scripturl, $topic, $context;
834 2
835 2
		// Build the link tree.
836
		if (empty($topic))
837 2
		{
838
			$context['breadcrumbs'][] = [
839
				'name' => '<em>' . $txt['start_new_topic'] . '</em>'
840
			];
841
		}
842
		else
843 2
		{
844
			$context['breadcrumbs'][] = [
845
				'url' => $scripturl . '?topic=' . $topic . '.' . $_REQUEST['start'],
846
				'name' => $this->_form_subject,
847
				'extra_before' => '<span><strong class="nav">' . $context['page_title'] . ' ( </strong></span>',
848
				'extra_after' => '<span><strong class="nav"> )</strong></span>'
849
			];
850
		}
851
852
		$context['back_to_topic'] = isset($_REQUEST['goback']) || (isset($_REQUEST['msg']) && !isset($_REQUEST['subject']));
853
		$context['is_new_topic'] = empty($topic);
854
		$context['is_new_post'] = !isset($_REQUEST['msg']);
855
		$context['is_first_post'] = $context['is_new_topic'] || (isset($_REQUEST['msg']) && $_REQUEST['msg'] == $this->_topic_attributes['id_first_msg']);
856
		$context['current_action'] = 'post';
857
858
		// Register this form in the session variables.
859
		checkSubmitOnce('register');
860
861
		// Finally, load the template.
862
		if ($this->getApi() === false)
863
		{
864
			theme()->getTemplates()->load('Post');
865
			$context['sub_template'] = 'post_page';
866 2
		}
867
	}
868
869
	/**
870
	 * Posts or saves the message composed with Post().
871
	 *
872 2
	 * What it does:
873
	 *
874
	 * - Requires various permissions depending on the action.
875
	 * - Handles attachment, post, and calendar saving.
876
	 * - Sends off notifications, and allows for announcements and moderation.
877
	 * - Accessed from ?action=post2.
878 2
	 * - Triggers events associated with the actual posting
879
	 *   - prepare_save_post, save_replying, save_new_topic, save_modify
880
	 *   - before_save_post, pre_save_post, after_save_post
881 2
	 */
882
	public function action_post2()
883
	{
884
		global $board, $topic, $txt, $modSettings, $context, $board_info, $options;
885
886
		// Sneaking off, are we?
887
		if (empty($_POST) && empty($topic))
888
		{
889
			if (empty($_SERVER['CONTENT_LENGTH']))
890
			{
891
				redirectexit('action=post;board=' . $board . '.0');
892 2
			}
893
			else
894
			{
895
				throw new Exception('post_upload_error', false);
896
			}
897
		}
898 2
		elseif (empty($_POST) && !empty($topic))
899
		{
900
			redirectexit('action=post;topic=' . $topic . '.0');
901 2
		}
902 2
903
		// No need!
904
		$context['robot_no_index'] = true;
905
906
		// We are now in post2 action
907
		$context['current_action'] = 'post2';
908 2
909
		// If the session has timed out, let the user re-submit their form.
910
		if (checkSession('post', '', false) !== '')
911
		{
912 2
			$this->_post_errors->addError('session_timeout');
913
914 2
			// Disable the preview so that any potentially malicious code is not executed
915
			$_REQUEST['preview'] = false;
916
917
			return $this->action_post();
918
		}
919 2
920
		$topic_info = [];
921
922
		// Previewing? Go back to start.
923
		if (isset($_REQUEST['preview']) || isset($_POST['more_options']))
924 2
		{
925
			return $this->action_post();
926
		}
927 2
928
		require_once(SUBSDIR . '/Boards.subs.php');
929 2
		Txt::load('Post');
930
931 2
		// Trigger the prepare_save_post event
932
		$this->_events->trigger('prepare_save_post', ['topic_info' => &$topic_info]);
933 2
934
		// Prevent double submission of this form.
935
		checkSubmitOnce('check');
936
937
		// If this isn't a new topic load the topic info that we need.
938
		if (!empty($topic))
939 2
		{
940
			$topic_info = getTopicInfo($topic);
941 2
942
			// Though the topic should be there, it might have vanished.
943
			if (empty($topic_info))
944
			{
945
				throw new Exception('topic_doesnt_exist');
946 2
			}
947
948 2
			// Did this topic suddenly move? Just checking...
949
			if ($topic_info['id_board'] != $board)
950
			{
951
				throw new Exception('not_a_topic');
952 2
			}
953
		}
954
955
		// Replying to a topic?
956
		if (!empty($topic) && !isset($_REQUEST['msg']))
957 2
		{
958
			// Don't allow a post if it's locked.
959
			if ($topic_info['locked'] != 0 && !allowedTo('moderate_board'))
960
			{
961
				throw new Exception('topic_locked', false);
962
			}
963
964
			// Do the permissions and approval stuff...
965
			$becomesApproved = true;
966
			if ($topic_info['id_member_started'] != $this->user->id)
967
			{
968
				if ($modSettings['postmod_active'] && allowedTo('post_unapproved_replies_any') && !allowedTo('post_reply_any'))
969
				{
970
					$becomesApproved = false;
971
				}
972 2
				else
973
				{
974
					isAllowedTo('post_reply_any');
975
				}
976
			}
977
			elseif (!allowedTo('post_reply_any'))
978
			{
979
				if ($modSettings['postmod_active'])
980
				{
981 2
					if (allowedTo('post_unapproved_replies_own') && !allowedTo('post_reply_own'))
982
					{
983
						$becomesApproved = false;
984 2
					}
985
					// Guests do not have post_unapproved_replies_own permission, so it's always post_unapproved_replies_any
986
					elseif ($this->user->is_guest && allowedTo('post_unapproved_replies_any'))
987
					{
988
						$becomesApproved = false;
989
					}
990 2
					else
991
					{
992
						isAllowedTo('post_reply_own');
993 2
					}
994 2
				}
995 2
			}
996
997 2
			if (isset($_POST['lock']))
998
			{
999 2
				$_POST['lock'] = $this->_checkLocked($_POST['lock'], $topic_info);
1000 2
			}
1001
1002
			// So you wanna (un)sticky this...let's see.
1003
			if (isset($_POST['sticky']) && ($_POST['sticky'] == $topic_info['is_sticky'] || !allowedTo('make_sticky')))
1004
			{
1005 6
				unset($_POST['sticky']);
1006
			}
1007 6
1008 6
			// Trigger the save_replying event
1009
			$this->_events->trigger('save_replying', array('topic_info' => &$topic_info));
1010
1011
			// If the number of replies has changed, if the setting is enabled, go back to action_post() - which handles the error.
1012 6
			if (empty($options['no_new_reply_warning']) && isset($_POST['last_msg']) && $topic_info['id_last_msg'] > $_POST['last_msg'])
1013
			{
1014
				$context['scroll_to_top'] = true;
1015
1016
				return $this->action_post();
1017
			}
1018
1019
			$posterIsGuest = $this->user->is_guest;
1020
		}
1021
		// Posting a new topic.
1022
		elseif (empty($topic))
1023
		{
1024
			// Now don't be silly, new topics will get their own id_msg soon enough.
1025
			unset($_REQUEST['msg'], $_POST['msg'], $_GET['msg']);
1026
1027
			// Do like, the permissions, for safety and stuff...
1028
			$becomesApproved = true;
1029
			if ($modSettings['postmod_active'] && !allowedTo('post_new') && allowedTo('post_unapproved_topics'))
1030
			{
1031
				$becomesApproved = false;
1032
			}
1033
			else
1034
			{
1035
				isAllowedTo('post_new');
1036
			}
1037
1038
			// Trigger the save new topic event
1039
			$this->_events->trigger('save_new_topic', array('becomesApproved' => &$becomesApproved));
1040
1041
			if (isset($_POST['lock']))
1042
			{
1043
				$_POST['lock'] = $this->_checkLocked($_POST['lock']);
1044
			}
1045
1046
			if (isset($_POST['sticky']) && (empty($_POST['sticky']) || !allowedTo('make_sticky')))
1047
			{
1048
				unset($_POST['sticky']);
1049
			}
1050
1051
			$posterIsGuest = $this->user->is_guest;
1052
		}
1053 6
		// Modifying an existing message?
1054
		elseif (isset($_REQUEST['msg']) && !empty($topic))
1055
		{
1056
			$_REQUEST['msg'] = (int) $_REQUEST['msg'];
1057
1058
			$msgInfo = basicMessageInfo($_REQUEST['msg'], true, false, false);
1059
1060
			if (empty($msgInfo))
1061 6
			{
1062
				throw new Exception('cant_find_messages', false);
1063
			}
1064
1065
			// Trigger teh save_modify event
1066 6
			$this->_events->trigger('save_modify', array('msgInfo' => &$msgInfo));
1067
1068
			if (!empty($topic_info['locked']) && !allowedTo('moderate_board'))
1069
			{
1070 6
				throw new Exception('topic_locked', false);
1071
			}
1072
1073
			if (isset($_POST['lock']))
1074
			{
1075
				$_POST['lock'] = $this->_checkLocked($_POST['lock'], $topic_info);
1076
			}
1077 6
1078
			// Change the sticky status of this topic?
1079
			if (isset($_POST['sticky']) && (!allowedTo('make_sticky') || $_POST['sticky'] == $topic_info['is_sticky']))
1080 6
			{
1081
				unset($_POST['sticky']);
1082
			}
1083
1084
			if ($msgInfo['id_member'] == $this->user->id && !allowedTo('modify_any'))
1085 6
			{
1086
				if ((!$modSettings['postmod_active'] || $msgInfo['approved']) && !empty($modSettings['edit_disable_time']) && $msgInfo['poster_time'] + ($modSettings['edit_disable_time'] + 5) * 60 < time())
1087 6
				{
1088
					throw new Exception('modify_post_time_passed', false);
1089
				}
1090 6
				if ($topic_info['id_member_started'] == $this->user->id && !allowedTo('modify_own'))
1091
				{
1092
					isAllowedTo('modify_replies');
1093
				}
1094
				else
1095
				{
1096 6
					isAllowedTo('modify_own');
1097
				}
1098
			}
1099
			elseif ($topic_info['id_member_started'] == $this->user->id && !allowedTo('modify_any'))
1100
			{
1101
				isAllowedTo('modify_replies');
1102
1103
				// If you're modifying a reply, I say it better be logged...
1104
				$moderationAction = true;
1105
			}
1106 6
			else
1107
			{
1108 4
				isAllowedTo('modify_any');
1109 4
1110
				// Log it, assuming you're not modifying your own post.
1111
				if ($msgInfo['id_member'] != $this->user->id)
1112
				{
1113 6
					$moderationAction = true;
1114
				}
1115
			}
1116
1117
			$posterIsGuest = empty($msgInfo['id_member']);
1118
1119
			// Can they approve it?
1120
			$can_approve = allowedTo('approve_posts');
1121
			$becomesApproved = $modSettings['postmod_active'] ? ($can_approve && !$msgInfo['approved'] ? (empty($_REQUEST['approve']) ? 0 : 1) : $msgInfo['approved']) : 1;
1122
			$approve_has_changed = $msgInfo['approved'] != $becomesApproved;
1123
1124
			if (!allowedTo('moderate_forum') || !$posterIsGuest)
1125
			{
1126
				$_POST['guestname'] = $msgInfo['poster_name'];
1127
				$_POST['email'] = $msgInfo['poster_email'];
1128
			}
1129
		}
1130
1131
		// In case we want to override
1132 6
		if (!isset($_REQUEST['from_qr']) && allowedTo('approve_posts'))
1133
		{
1134
			$becomesApproved = !isset($_REQUEST['approve']) || !empty($_REQUEST['approve']) ? 1 : 0;
1135
			$approve_has_changed = isset($msgInfo['approved']) && $msgInfo['approved'] != $becomesApproved;
1136
		}
1137
1138
		// If the poster is a guest evaluate the legality of name and email.
1139
		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...
1140
		{
1141
			$_POST['guestname'] = isset($_POST['guestname']) ? Util::htmlspecialchars(trim($_POST['guestname'])) : '';
1142
			$_POST['email'] = isset($_POST['email']) ? Util::htmlspecialchars(trim($_POST['email'])) : '';
1143
1144
			if ($_POST['guestname'] === '' || $_POST['guestname'] === '_')
1145
			{
1146 6
				$this->_post_errors->addError('no_name');
1147
			}
1148 4
1149
			if (Util::strlen($_POST['guestname']) > 25)
1150
			{
1151
				$this->_post_errors->addError('long_name');
1152 6
			}
1153 6
1154
			if (empty($modSettings['guest_post_no_email']))
1155
			{
1156 6
				// Only check if they changed it!
1157 6
				if ((!isset($msgInfo) || $msgInfo['poster_email'] !== $_POST['email']) && (!allowedTo('moderate_forum') && !DataValidator::is_valid($_POST, array('email' => 'valid_email|required'), array('email' => 'trim'))))
1158 6
				{
1159
					empty($_POST['email']) ? $this->_post_errors->addError('no_email') : $this->_post_errors->addError('bad_email');
1160
				}
1161 6
1162
				// Now make sure this email address is not banned from posting.
1163
				isBannedEmail($_POST['email'], 'cannot_post', sprintf($txt['you_are_post_banned'], $txt['guest_title']));
1164
			}
1165
1166
			// In case they are making multiple posts this visit, help them along by storing their name.
1167 6
			if (!$this->_post_errors->hasErrors())
1168
			{
1169
				$_SESSION['guest_name'] = $_POST['guestname'];
1170
				$_SESSION['guest_email'] = $_POST['email'];
1171 6
			}
1172 6
		}
1173 6
1174 6
		// Trigger before_save_post event
1175 6
		try
1176 6
		{
1177
			$this->_events->trigger('before_save_post', array('post_errors' => $this->_post_errors, 'topic_info' => $topic_info));
1178
		}
1179
		catch (ControllerRedirectException $controllerRedirectException)
1180 6
		{
1181 6
			return $controllerRedirectException->doRedirect($this);
1182 6
		}
1183 6
1184
		// Check the subject and message.
1185 6
		if (!isset($_POST['subject']) || Util::htmltrim(Util::htmlspecialchars($_POST['subject'])) === '')
1186
		{
1187
			$this->_post_errors->addError('no_subject');
1188
		}
1189 6
1190 6
		if (!isset($_POST['message']) || Util::htmltrim(Util::htmlspecialchars($_POST['message'], ENT_QUOTES)) === '')
1191 6
		{
1192 6
			$this->_post_errors->addError('no_message');
1193
		}
1194
		elseif (!empty($modSettings['max_messageLength']) && Util::strlen($_POST['message']) > $modSettings['max_messageLength'])
1195
		{
1196 6
			$this->_post_errors->addError(array('long_message', array($modSettings['max_messageLength'])));
1197
		}
1198
		else
1199 6
		{
1200
			// Prepare the message a bit for some additional testing.
1201
			$_POST['message'] = Util::htmlspecialchars($_POST['message'], ENT_QUOTES, 'UTF-8', true);
1202 2
1203
			// Preparse code. (Zef)
1204
			if ($this->user->is_guest)
1205
			{
1206
				$this->user->name = $_POST['guestname'];
1207
			}
1208
1209 2
			$this->preparse->preparsecode($_POST['message']);
1210
1211 2
			$bbc_parser = ParserWrapper::instance();
1212
1213
			// Let's see if there's still some content left without the tags.
1214 2
			if (Util::htmltrim(strip_tags($bbc_parser->parseMessage($_POST['message'], false), '<img>')) === '' && (!allowedTo('admin_forum') || strpos($_POST['message'], '[html]') === false))
1215
			{
1216
				$this->_post_errors->addError('no_message');
1217
			}
1218
		}
1219
1220
		if ($posterIsGuest)
1221 4
		{
1222
			// If user is a guest, make sure the chosen name isn't taken.
1223
			require_once(SUBSDIR . '/Members.subs.php');
1224
			if (isReservedName($_POST['guestname'], 0, true, false) && (!isset($msgInfo['poster_name']) || $_POST['guestname'] !== $msgInfo['poster_name']))
1225
			{
1226
				$this->_post_errors->addError('bad_name');
1227
			}
1228
		}
1229
		// If the user isn't a guest, get his or her name and email.
1230
		elseif (!isset($_REQUEST['msg']))
1231
		{
1232 4
			$_POST['guestname'] = $this->user->username;
1233
			$_POST['email'] = $this->user->email;
1234 4
		}
1235
1236 4
		// Posting somewhere else? Are we sure you can?
1237
		if (!empty($_REQUEST['post_in_board']))
1238
		{
1239
			$new_board = (int) $_REQUEST['post_in_board'];
1240
			if (!allowedTo('post_new', $new_board))
1241 6
			{
1242
				$post_in_board = boardInfo($new_board);
1243
1244
				if (!empty($post_in_board))
1245 6
				{
1246
					$this->_post_errors->addError(array('post_new_board', array($post_in_board['name'])));
1247 6
				}
1248
				else
1249
				{
1250 6
					$this->_post_errors->addError('post_new');
1251
				}
1252
			}
1253
		}
1254
1255 6
		// Any mistakes?
1256
		if ($this->_post_errors->hasErrors())
1257
		{
1258
			$context['scroll_to_top'] = true;
1259
1260
			$_REQUEST['preview'] = false;
1261
1262 6
			return $this->action_post();
1263
		}
1264
1265
		// Make sure the user isn't spamming the board.
1266 6
		if (!isset($_REQUEST['msg']))
1267
		{
1268 4
			spamProtection('post');
1269
		}
1270
1271
		// At about this point, we're posting and that's that.
1272 6
		ignore_user_abort(true);
1273
		detectServer()->setTimeLimit(300);
1274
1275
		// Add special html entities to the subject, name, and email.
1276
		$_POST['subject'] = strtr(Util::htmlspecialchars($_POST['subject']), array("\r" => '', "\n" => '', "\t" => ''));
1277 6
		$_POST['guestname'] = htmlspecialchars($_POST['guestname'], ENT_COMPAT, 'UTF-8');
1278
		$_POST['email'] = htmlspecialchars($_POST['email'], ENT_COMPAT, 'UTF-8');
1279 2
1280
		// At this point, we want to make sure the subject isn't too long.
1281
		if (Util::strlen($_POST['subject']) > 100)
1282 6
		{
1283
			$_POST['subject'] = Util::substr($_POST['subject'], 0, 100);
1284
		}
1285
1286
		// Creating a new topic?
1287
		$newTopic = empty($_REQUEST['msg']) && empty($topic);
1288 6
1289
		// Collect all parameters for the creation or modification of a post.
1290 6
		$msgOptions = array(
1291 6
			'id' => empty($_REQUEST['msg']) ? 0 : (int) $_REQUEST['msg'],
1292
			'subject' => $_POST['subject'],
1293
			'body' => $_POST['message'],
1294 2
			'icon' => preg_replace('~[\./\\\\*:"\'<>]~', '', $_POST['icon']),
1295 2
			'smileys_enabled' => !isset($_POST['ns']),
1296 2
			'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...
1297 2
		);
1298 2
1299 2
		$topicOptions = array(
1300 2
			'id' => empty($topic) ? 0 : $topic,
1301 2
			'board' => $board,
1302
			'lock_mode' => isset($_POST['lock']) ? (int) $_POST['lock'] : null,
1303 2
			'sticky_mode' => isset($_POST['sticky']) ? (int) $_POST['sticky'] : null,
1304
			'mark_as_read' => true,
1305 4
			'is_approved' => !$modSettings['postmod_active'] || empty($topic) || !empty($board_info['cur_topic_approved']),
1306
		);
1307
1308 2
		$posterOptions = array(
1309
			'id' => $this->user->id,
1310 2
			'name' => $_POST['guestname'],
1311
			'email' => $_POST['email'],
1312
			'update_post_count' => $this->user->is_guest === false && !isset($_REQUEST['msg']) && $board_info['posts_count'],
1313
		);
1314
1315
		// Trigger the pre_save_post event
1316
		$this->_events->trigger('pre_save_post', array('msgOptions' => &$msgOptions, 'topicOptions' => &$topicOptions, 'posterOptions' => &$posterOptions));
1317
1318
		// This is an already existing message. Edit it.
1319 6
		if (!empty($_REQUEST['msg']))
1320
		{
1321
			$posterOptions['id_starter'] = $msgInfo['id_member'] ?? $this->user->id;
1322
1323
			// Have admins allowed people to hide their screwups?
1324 6
			if (time() - $msgInfo['poster_time'] > $modSettings['edit_wait_time'] || $this->user->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...
1325
			{
1326
				$msgOptions['modify_time'] = time();
1327
				$msgOptions['modify_name'] = $this->user->name;
1328
			}
1329 6
1330
			// This will save some time...
1331
			if (empty($approve_has_changed))
1332
			{
1333
				unset($msgOptions['approved']);
1334
			}
1335 6
1336
			modifyPost($msgOptions, $topicOptions, $posterOptions);
1337
		}
1338
		// This is a new topic or an already existing one. Save it.
1339 6
		else
1340
		{
1341
			// We also have to fake the board:
1342
			// if it's valid and it's not the current, let's forget about the "current" and load the new one
1343
			if (!empty($new_board) && $board !== $new_board)
1344
			{
1345
				$board = $new_board;
1346 6
				loadBoard();
1347
1348 6
				// Some details changed
1349
				$topicOptions['board'] = $board;
1350
				$topicOptions['is_approved'] = !$modSettings['postmod_active'] || empty($topic) || !empty($board_info['cur_topic_approved']);
1351
				$posterOptions['update_post_count'] = $this->user->is_guest === false && !isset($_REQUEST['msg']) && $board_info['posts_count'];
1352
			}
1353
1354
			createPost($msgOptions, $topicOptions, $posterOptions);
1355
1356
			$topic = $topicOptions['id'];
1357
		}
1358 2
1359
		// Trigger the after_save_post event
1360
		$this->_events->trigger('after_save_post', array('board' => $board, 'topic' => $topic, 'msgOptions' => $msgOptions, 'topicOptions' => $topicOptions, 'becomesApproved' => $becomesApproved, 'posterOptions' => $posterOptions));
1361 2
1362
		// Marking boards as read.
1363
		// (You just posted and they will be unread.)
1364
		if ($this->user->is_guest === false)
1365
		{
1366
			$board_list = empty($board_info['parent_boards']) ? array() : array_keys($board_info['parent_boards']);
1367
1368
			// Returning to the topic?
1369
			if (!empty($_REQUEST['goback']))
1370
			{
1371
				$board_list[] = $board;
1372
			}
1373
1374
			if (!empty($board_list))
1375
			{
1376
				markBoardsRead($board_list, false, false);
1377
			}
1378
		}
1379
1380
		// Turn notification on or off.
1381 2
		if (!empty($_POST['notify']) && allowedTo('mark_any_notify'))
1382
		{
1383
			setTopicNotification($this->user->id, $topic, true);
1384
		}
1385
		elseif (!$newTopic)
1386 2
		{
1387
			setTopicNotification($this->user->id, $topic, false);
1388
		}
1389
1390
		// Log an act of moderation - modifying.
1391 2
		if (!empty($moderationAction))
1392
		{
1393
			logAction('modify', array('topic' => $topic, 'message' => (int) $_REQUEST['msg'], 'member' => $msgInfo['id_member'], 'board' => $board));
1394
		}
1395
1396
		if (isset($_POST['lock']) && $_POST['lock'] != 2)
1397
		{
1398
			logAction(empty($_POST['lock']) ? 'unlock' : 'lock', array('topic' => $topicOptions['id'], 'board' => $topicOptions['board']));
1399
		}
1400
1401
		if (isset($_POST['sticky']))
1402
		{
1403
			logAction(empty($_POST['sticky']) ? 'unsticky' : 'sticky', array('topic' => $topicOptions['id'], 'board' => $topicOptions['board']));
1404
		}
1405
1406
		// Notify any members who have notification turned on for this topic/board - only do this if it's going to be approved(!)
1407 2
		if ($becomesApproved)
1408
		{
1409
			require_once(SUBSDIR . '/Notification.subs.php');
1410 2
			if ($newTopic)
1411
			{
1412
				$notifyData = array(
1413
					'body' => $_POST['message'],
1414
					'subject' => $_POST['subject'],
1415
					'name' => $this->user->name,
1416
					'poster' => $this->user->id,
1417
					'msg' => $msgOptions['id'],
1418
					'board' => $board,
1419
					'topic' => $topic,
1420
					'signature' => User::$settings->signature(''),
1421
				);
1422
				sendBoardNotifications($notifyData);
1423
			}
1424
			elseif (empty($_REQUEST['msg']))
1425
			{
1426
				// Only send it to everyone if the topic is approved, otherwise just to the topic starter if they want it.
1427
				if ($topic_info['approved'])
1428
				{
1429
					sendNotifications($topic, 'reply');
1430
				}
1431
				else
1432
				{
1433
					sendNotifications($topic, 'reply', array(), $topic_info['id_member_started']);
1434
				}
1435
			}
1436
		}
1437
1438
		if ($board_info['num_topics'] == 0)
1439
		{
1440
			Cache::instance()->remove('board-' . $board);
1441
		}
1442
1443
		if (!empty($_POST['announce_topic']))
1444
		{
1445
			redirectexit('action=announce;sa=selectgroup;topic=' . $topic . (!empty($_POST['move']) && allowedTo('move_any') ? ';move' : '') . (empty($_REQUEST['goback']) ? '' : ';goback'));
1446
		}
1447
1448
		if (!empty($_POST['move']) && allowedTo('move_any'))
1449
		{
1450
			redirectexit('action=movetopic;topic=' . $topic . '.0' . (empty($_REQUEST['goback']) ? '' : ';goback'));
1451
		}
1452
1453
		// Return to post if the mod is on.
1454
		if (isset($_REQUEST['msg']) && !empty($_REQUEST['goback']))
1455
		{
1456
			redirectexit('topic=' . $topic . '.msg' . $_REQUEST['msg'] . '#msg' . $_REQUEST['msg']);
1457
		}
1458
		elseif (!empty($_REQUEST['goback']))
1459
		{
1460
			redirectexit('topic=' . $topic . '.new#new');
1461
		}
1462
		// Dut-dut-duh-duh-DUH-duh-dut-duh-duh!  *dances to the Final Fantasy Fanfare...*
1463
		else
1464
		{
1465
			redirectexit('board=' . $board . '.0');
1466
		}
1467
	}
1468
1469
	/**
1470
	 * Toggle a post lock status
1471
	 *
1472
	 * @param int|null $lock
1473
	 * @param string|null $topic_info
1474
	 *
1475
	 * @return int|null
1476
	 */
1477
	protected function _checkLocked($lock, $topic_info = null)
1478
	{
1479
		// A new topic
1480
		if ($topic_info === null)
1481
		{
1482
			// New topics are by default not locked.
1483
			if (empty($lock))
1484
			{
1485
				return null;
1486
			}
1487
			// Besides, you need permission.
1488
			if (!allowedTo(array('lock_any', 'lock_own')))
1489
			{
1490
				return null;
1491
			}
1492
			// A moderator-lock (1) can override a user-lock (2).
1493
			else
1494
			{
1495
				return allowedTo('lock_any') ? 1 : 2;
1496
			}
1497
		}
1498
		// Nothing changes to the lock status.
1499
		if ((empty($lock) && empty($topic_info['locked'])) || (!empty($lock) && !empty($topic_info['locked'])))
1500
		{
1501
			return null;
1502
		}
1503
		// You're simply not allowed to (un)lock this.
1504
		if (!allowedTo(array('lock_any', 'lock_own')) || (!allowedTo('lock_any') && $this->user->id != $topic_info['id_member_started']))
0 ignored issues
show
Bug Best Practice introduced by
The property id does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
1505
		{
1506
			return null;
1507
		}
1508
		// You're only allowed to lock your own topics.
1509
		if (!allowedTo('lock_any'))
1510
		{
1511
			// You're not allowed to break a moderator's lock.
1512
			if ($topic_info['locked'] == 1)
1513
			{
1514
				return null;
1515
			}
1516
			$lock = empty($lock) ? 0 : 2;
1517
		}
1518
		// You must be the moderator.
1519
		else
1520
		{
1521
			$lock = empty($lock) ? 0 : 1;
1522
		}
1523
1524
		return $lock;
1525
	}
1526
1527
	/**
1528
	 * Loads a post and inserts it into the current editing text box.
1529
	 * Used to quick edit a post as well as to quote a post and place it in the quick reply box
1530
	 * Can be used to quick edit just the subject from the topic listing
1531
	 *
1532
	 * uses the Post language file.
1533
	 * uses special (sadly browser dependent) javascript to parse entities for internationalization reasons.
1534
	 * accessed with ?action=quotefast and ?action=quotefast;modify
1535
	 */
1536
	public function action_quotefast()
1537
	{
1538
		global $context;
1539
1540
		Txt::load('Post');
1541
1542
		// Where we going if we need to?
1543
		$context['post_box_name'] = $_GET['pb'] ?? '';
1544
1545
		$row = quoteMessageInfo((int) $_REQUEST['quote'], isset($_REQUEST['modify']));
1546
1547
		$context['sub_template'] = 'quotefast';
1548
		if (!empty($row))
1549
		{
1550
			$can_view_post = $row['approved'] || ($row['id_member'] != 0 && $row['id_member'] == $this->user->id) || allowedTo('approve_posts', $row['id_board']);
0 ignored issues
show
Bug Best Practice introduced by
The property id does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
1551
		}
1552
1553
		if (!empty($can_view_post))
1554
		{
1555
			// Remove special formatting we don't want anymore.
1556
			$row['body'] = $this->preparse->un_preparsecode($row['body']);
1557
1558
			// Censor the message!
1559
			$row['body'] = censor($row['body']);
1560
1561
			$row['body'] = preg_replace('~<br ?/?>~i', "\n", $row['body']);
1562
1563
			// Want to modify a single message by double clicking it?
1564
			if (isset($_REQUEST['modify']))
1565
			{
1566
				$row['subject'] = censor($row['subject']);
1567
1568
				$context['sub_template'] = 'modifyfast';
1569
				$context['message'] = array(
1570
					'id' => $_REQUEST['quote'],
1571
					'body' => $row['body'],
1572
					'subject' => addcslashes($row['subject'], '"'),
1573
				);
1574
1575
				return;
1576
			}
1577
1578
			// Remove any nested quotes.
1579
			$row['body'] = removeNestedQuotes($row['body']);
1580
1581
			// Add a quote string on the front and end.
1582
			$context['quote']['xml'] = '[quote author=' . $row['poster_name'] . ' link=msg=' . (int) $_REQUEST['quote'] . ' date=' . $row['poster_time'] . "]\n" . $row['body'] . "\n[/quote]";
1583
			$context['quote']['text'] = strtr(un_htmlspecialchars($context['quote']['xml']), array("'" => '\\\'', '\\' => '\\\\', "\n" => '\\n', '</script>' => "</' + 'script>"));
1584
			$context['quote']['xml'] = strtr($context['quote']['xml'], ['&nbsp;' => '&#160;', '<' => '&lt;', '>' => '&gt;']);
1585
		}
1586
		//@todo Needs a nicer interface.
1587
		// In case our message has been removed in the meantime.
1588
		elseif (isset($_REQUEST['modify']))
1589
		{
1590
			$context['sub_template'] = 'modifyfast';
1591
			$context['message'] = [
1592
				'id' => 0,
1593
				'body' => '',
1594
				'subject' => '',
1595
			];
1596
		}
1597
		else
1598
		{
1599
			$context['quote'] = [
1600
				'xml' => '',
1601
				'text' => '',
1602
			];
1603
		}
1604
	}
1605
1606
	/**
1607
	 * Used to edit the body or subject of a message inline
1608
	 * called from action=jsmodify from script and topic js
1609
	 */
1610
	public function action_jsmodify()
1611
	{
1612
		global $modSettings, $board, $topic, $context;
1613
1614
		// We have to have a topic!
1615
		if (empty($topic))
1616
		{
1617
			obExit(false);
1618
		}
1619
1620
		checkSession('request');
1621
1622
		$row = getTopicInfoByMsg($topic, empty($_REQUEST['msg']) ? 0 : (int) $_REQUEST['msg']);
1623
1624
		if (empty($row))
1625
		{
1626
			throw new Exception('no_board', false);
1627
		}
1628
1629
		// Change either body or subject requires permissions to modify messages.
1630
		if (isset($_POST['message']) || isset($_POST['subject']) || isset($_REQUEST['icon']))
1631
		{
1632
			if (!empty($row['locked']))
1633
			{
1634
				isAllowedTo('moderate_board');
1635
			}
1636
1637
			if ($row['id_member'] == $this->user->id && !allowedTo('modify_any'))
1638
			{
1639
				if ((!$modSettings['postmod_active'] || $row['approved']) && !empty($modSettings['edit_disable_time']) && $row['poster_time'] + ($modSettings['edit_disable_time'] + 5) * 60 < time())
1640
				{
1641
					throw new Exception('modify_post_time_passed', false);
1642
				}
1643
				if ($row['id_member_started'] == $this->user->id && !allowedTo('modify_own'))
1644
				{
1645
					isAllowedTo('modify_replies');
1646
				}
1647
				else
1648
				{
1649
					isAllowedTo('modify_own');
1650
				}
1651
			}
1652
			elseif ($row['id_member_started'] == $this->user->id && !allowedTo('modify_any'))
1653
			{
1654
				isAllowedTo('modify_replies');
1655
			}
1656
			else
1657
			{
1658
				isAllowedTo('modify_any');
1659
			}
1660
1661
			// Only log this action if it wasn't your message.
1662
			$moderationAction = $row['id_member'] != $this->user->id;
1663
		}
1664
1665
		if (isset($_POST['subject']) && Util::htmltrim(Util::htmlspecialchars($_POST['subject'])) !== '')
1666
		{
1667
			$_POST['subject'] = strtr(Util::htmlspecialchars($_POST['subject']), array("\r" => '', "\n" => '', "\t" => ''));
1668
1669
			// Maximum number of characters.
1670
			if (Util::strlen($_POST['subject']) > 100)
1671
			{
1672
				$_POST['subject'] = Util::substr($_POST['subject'], 0, 100);
1673
			}
1674
		}
1675
		elseif (isset($_POST['subject']))
1676
		{
1677
			$this->_post_errors->addError('no_subject');
1678
			unset($_POST['subject']);
1679
		}
1680
1681
		if (isset($_POST['message']))
1682
		{
1683
			if (Util::htmltrim(Util::htmlspecialchars($_POST['message'])) === '')
1684
			{
1685
				$this->_post_errors->addError('no_message');
1686
				unset($_POST['message']);
1687
			}
1688
			elseif (!empty($modSettings['max_messageLength']) && Util::strlen($_POST['message']) > $modSettings['max_messageLength'])
1689
			{
1690
				$this->_post_errors->addError(array('long_message', array($modSettings['max_messageLength'])));
1691
				unset($_POST['message']);
1692
			}
1693
			else
1694
			{
1695
				$_POST['message'] = Util::htmlspecialchars($_POST['message'], ENT_QUOTES, 'UTF-8', true);
1696
1697
				$this->preparse->preparsecode($_POST['message']);
1698
				$bbc_parser = ParserWrapper::instance();
1699
1700
				if (Util::htmltrim(strip_tags($bbc_parser->parseMessage($_POST['message'], false), '<img>')) === '')
1701
				{
1702
					$this->_post_errors->addError('no_message');
1703
					unset($_POST['message']);
1704
				}
1705
			}
1706
		}
1707
1708
		if (isset($_POST['lock']))
1709
		{
1710
			$_POST['lock'] = $this->_checkLocked($_POST['lock'], $row);
1711
		}
1712
1713
		if (isset($_POST['sticky']) && !allowedTo('make_sticky'))
1714
		{
1715
			unset($_POST['sticky']);
1716
		}
1717
1718
		if (!$this->_post_errors->hasErrors())
1719
		{
1720
			if (!empty($modSettings['mentions_enabled']))
1721
			{
1722
				if (!empty($_REQUEST['uid']))
1723
				{
1724
					$query_params = array();
1725
					$query_params['member_ids'] = array_unique(array_map('intval', $_REQUEST['uid']));
1726
1727
					require_once(SUBSDIR . '/Members.subs.php');
1728
					$mentioned_members = membersBy('member_ids', $query_params, true);
1729
					$replacements = 0;
1730
					$actually_mentioned = array();
1731
					foreach ($mentioned_members as $member)
1732
					{
1733
						$_POST['message'] = str_replace('@' . $member['real_name'], '[member=' . $member['id_member'] . ']' . $member['real_name'] . '[/member]', $_POST['message'], $replacements);
1734
						if ($replacements > 0)
1735
						{
1736
							$actually_mentioned[] = $member['id_member'];
1737
						}
1738
					}
1739
				}
1740
1741
				if (!empty($actually_mentioned))
1742
				{
1743
					$notifier = Notifications::instance();
1744
					$notifier->add(new NotificationsTask(
1745
						'Mentionmem',
1746
						$row['id_msg'],
1747
						$row['id_member'],
1748
						array('id_members' => $actually_mentioned, 'status' => $row['approved'] ? 'new' : 'unapproved')
1749
					));
1750
				}
1751
			}
1752
1753
			$msgOptions = array(
1754
				'id' => $row['id_msg'],
1755
				'subject' => $_POST['subject'] ?? null,
1756
				'body' => $_POST['message'] ?? null,
1757
				'icon' => isset($_REQUEST['icon']) ? preg_replace('~[\./\\\\*\':"<>]~', '', $_REQUEST['icon']) : null,
1758
			);
1759
1760
			$topicOptions = array(
1761
				'id' => $topic,
1762
				'board' => $board,
1763
				'lock_mode' => isset($_POST['lock']) ? (int) $_POST['lock'] : null,
1764
				'sticky_mode' => isset($_POST['sticky']) ? (int) $_POST['sticky'] : null,
1765
				'mark_as_read' => false,
1766
			);
1767
1768
			$posterOptions = array();
1769
1770
			// Only consider marking as editing if they have edited the subject, message or icon.
1771
			if ((isset($_POST['subject']) && $_POST['subject'] !== $row['subject']) || (isset($_POST['message']) && $_POST['message'] !== $row['body']) || (isset($_REQUEST['icon']) && $_REQUEST['icon'] !== $row['icon']))
1772
			{
1773
				// And even then only if the time has passed...
1774
				if (time() - $row['poster_time'] > $modSettings['edit_wait_time'] || $this->user->id != $row['id_member'])
1775
				{
1776
					$msgOptions['modify_time'] = time();
1777
					$msgOptions['modify_name'] = $this->user->name;
1778
				}
1779
			}
1780
			// If nothing was changed there's no need to add an entry to the moderation log.
1781
			else
1782
			{
1783
				$moderationAction = false;
1784
			}
1785
1786
			modifyPost($msgOptions, $topicOptions, $posterOptions);
1787
1788
			// If we didn't change anything this time but had before put back the old info.
1789
			if (!isset($msgOptions['modify_time']) && !empty($row['modified_time']))
1790
			{
1791
				$msgOptions['modify_time'] = $row['modified_time'];
1792
				$msgOptions['modify_name'] = $row['modified_name'];
1793
			}
1794
1795
			// Changing the first subject updates other subjects to 'Re: new_subject'.
1796
			if (isset($_POST['subject'], $_REQUEST['change_all_subjects']) && $row['id_first_msg'] == $row['id_msg'] && !empty($row['num_replies']) && (allowedTo('modify_any') || ($row['id_member_started'] == $this->user->id && allowedTo('modify_replies'))))
1797
			{
1798
				// Get the proper (default language) response prefix first.
1799
				$context['response_prefix'] = response_prefix();
1800
1801
				topicSubject(array('id_topic' => $topic, 'id_first_msg' => $row['id_first_msg']), $_POST['subject'], $context['response_prefix'], true);
1802
			}
1803
1804
			if (!empty($moderationAction))
1805
			{
1806
				logAction('modify', array('topic' => $topic, 'message' => $row['id_msg'], 'member' => $row['id_member'], 'board' => $board));
1807
			}
1808
		}
1809
1810
		if ($this->getApi() === 'xml')
1811
		{
1812
			$bbc_parser = ParserWrapper::instance();
1813
1814
			theme()->getTemplates()->load('Xml');
1815
			theme()->getLayers()->removeAll();
1816
			$context['sub_template'] = 'modifydone';
1817
1818
			if (isset($msgOptions['subject'], $msgOptions['body']) && !$this->_post_errors->hasErrors())
1819
			{
1820
				$context['message'] = array(
1821
					'id' => $row['id_msg'],
1822
					'modified' => array(
1823
						'time' => isset($msgOptions['modify_time']) ? standardTime($msgOptions['modify_time']) : '',
1824
						'html_time' => isset($msgOptions['modify_time']) ? htmlTime($msgOptions['modify_time']) : '',
1825
						'timestamp' => isset($msgOptions['modify_time']) ? forum_time(true, $msgOptions['modify_time']) : 0,
1826
						'name' => isset($msgOptions['modify_time']) ? $msgOptions['modify_name'] : '',
1827
					),
1828
					'subject' => $msgOptions['subject'],
1829
					'first_in_topic' => $row['id_msg'] == $row['id_first_msg'],
1830
					'body' => strtr($msgOptions['body'], array(']]>' => ']]]]><![CDATA[>')),
1831
				);
1832
1833
				$context['message']['subject'] = censor($context['message']['subject']);
1834
				$context['message']['body'] = censor($context['message']['body']);
1835
1836
				$context['message']['body'] = $bbc_parser->parseMessage($context['message']['body'], $row['smileys_enabled']);
1837
			}
1838
			// Topic?
1839
			elseif (!$this->_post_errors->hasErrors())
1840
			{
1841
				$context['sub_template'] = 'modifytopicdone';
1842
				$context['message'] = array(
1843
					'id' => $row['id_msg'],
1844
					'modified' => array(
1845
						'time' => isset($msgOptions['modify_time']) ? standardTime($msgOptions['modify_time']) : '',
1846
						'html_time' => isset($msgOptions['modify_time']) ? htmlTime($msgOptions['modify_time']) : '',
1847
						'timestamp' => isset($msgOptions['modify_time']) ? forum_time(true, $msgOptions['modify_time']) : 0,
1848
						'name' => isset($msgOptions['modify_time']) ? $msgOptions['modify_name'] : '',
1849
					),
1850
					'subject' => $msgOptions['subject'] ?? '',
1851
				);
1852
1853
				$context['message']['subject'] = censor($context['message']['subject']);
1854
			}
1855
			else
1856
			{
1857
				$context['message'] = array(
1858
					'id' => $row['id_msg'],
1859
					'errors' => array(),
1860
					'error_in_subject' => $this->_post_errors->hasError('no_subject'),
1861
					'error_in_body' => $this->_post_errors->hasError('no_message') || $this->_post_errors->hasError('long_message'),
1862
				);
1863
				$context['message']['errors'] = $this->_post_errors->prepareErrors();
1864
			}
1865
		}
1866
		else
1867
		{
1868
			obExit(false);
1869
		}
1870
	}
1871
}
1872