Completed
Pull Request — development (#2334)
by Joshua
12:46
created

Post_Controller::action_post()   F

Complexity

Conditions 134
Paths > 20000

Size

Total Lines 504
Code Lines 272

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 18090
Metric Value
dl 0
loc 504
ccs 0
cts 310
cp 0
rs 2
cc 134
eloc 272
nc 4294967295
nop 0
crap 18090

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 software is a derived product, based on:
13
 *
14
 * Simple Machines Forum (SMF)
15
 * copyright:	2011 Simple Machines (http://www.simplemachines.org)
16
 * license:		BSD, See included LICENSE.TXT for terms and conditions.
17
 *
18
 * @version 1.1 dev
19
 *
20
 */
21
22 1
if (!defined('ELK'))
23 1
	die('No access...');
24
25
/**
26
 * Post Controller
27
 */
28
class Post_Controller extends Action_Controller
29
{
30
	/**
31
	 * The post (messages) errors object
32
	 * @var null|object
33
	 */
34
	protected $_post_errors = null;
35
36
	/**
37
	 * The template layers object
38
	 * @var null|object
39
	 */
40
	protected $_template_layers = null;
41
42
	/**
43
	 * An array of attributes of the topic (if not new)
44
	 * @var mixed[]
45
	 */
46
	protected $_topic_attributes = array();
47
48
	/**
49
	 * Sets up common stuff for all or most of the actions.
50
	 */
51
	public function pre_dispatch()
52
	{
53
		$this->_post_errors = Error_Context::context('post', 1);
54
		$this->_template_layers = Template_Layers::getInstance();
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
	 * - Validates that we're posting in a board.
79
	 * - Find the topic id if a message id is passed, else assume it's a new message
80
	 * - Get the response prefix in the default forum language.
81
	 * - Triggers events associated with posting.
82
	 * - Additionally handles previews of posts.
83
	 * - Requires different permissions depending on the actions, but most notably post_new, post_reply_own, and post_reply_any.
84
	 * - Shows options for the editing and posting of calendar events and attachments, and as the posting of polls (using modules).
85
	 * - Accessed from ?action=post or called from action_post2 in the event of errors or preview from quick reply.
86
	 *
87
	 * @uses the Post template and language file, main sub template.
88
	 * @uses Errors language
89
	 */
90
	public function action_post()
0 ignored issues
show
Coding Style introduced by
action_post uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
action_post uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
action_post uses the super-global variable $_GET which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
action_post uses the super-global variable $_SESSION which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
91
	{
92
		global $txt, $scripturl, $topic, $modSettings, $board, $user_info, $context, $options;
93
94
		loadLanguage('Post');
95
		loadLanguage('Errors');
96
97
		$context['robot_no_index'] = true;
98
		$this->_template_layers->add('postarea');
99
		$this->_topic_attributes = array(
0 ignored issues
show
Documentation Bug introduced by
It seems like array('locked' => false,... 'last_post_time' => 0) of type array<string,false|integ..._post_time":"integer"}> is incompatible with the declared type array<integer,*> of property $_topic_attributes.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
100
			'locked' => false,
101
			'notify' => false,
102
			'is_sticky' => false,
103
			'id_last_msg' => 0,
104
			'id_member' => 0,
105
			'id_first_msg' => 0,
106
			'subject' => '',
107
			'last_post_time' => 0
108
		);
109
110
		// Trigger the prepare_post event
111
		$this->_events->trigger('prepare_post', array('topic_attributes' => &$this->_topic_attributes));
112
113
		// You must be posting to *some* board.
114
		if (empty($board) && !$context['make_event'])
115
			Errors::instance()->fatal_lang_error('no_board', false);
116
117
		// All those wonderful modifiers and attachments
118
		$this->_template_layers->add('additional_options', 200);
119
120
		if (isset($_REQUEST['xml']))
121
		{
122
			$context['sub_template'] = 'post';
123
124
			// Just in case of an earlier error...
125
			$context['preview_message'] = '';
126
			$context['preview_subject'] = '';
127
		}
128
129
		// No message is complete without a topic.
130
		if (empty($topic) && !empty($_REQUEST['msg']))
131
		{
132
			$topic = associatedTopic((int) $_REQUEST['msg']);
133
			if (empty($topic))
134
				unset($_REQUEST['msg'], $_POST['msg'], $_GET['msg']);
135
		}
136
137
		// Check if it's locked. It isn't locked if no topic is specified.
138
		if (!empty($topic))
139
		{
140
			$this->_topic_attributes = topicUserAttributes($topic, $user_info['id']);
141
			$context['notify'] = $this->_topic_attributes['notify'];
142
			$context['topic_last_message'] = $this->_topic_attributes['id_last_msg'];
143
144
			if (empty($_REQUEST['msg']))
145
			{
146
				if ($user_info['is_guest'] && !allowedTo('post_reply_any') && (!$modSettings['postmod_active'] || !allowedTo('post_unapproved_replies_any')))
147
					is_not_guest();
148
149
				// By default the reply will be approved...
150
				$context['becomes_approved'] = true;
151
				if ($this->_topic_attributes['id_member'] != $user_info['id'])
152
				{
153
					if ($modSettings['postmod_active'] && allowedTo('post_unapproved_replies_any') && !allowedTo('post_reply_any'))
154
						$context['becomes_approved'] = false;
155
					else
156
						isAllowedTo('post_reply_any');
157
				}
158
				elseif (!allowedTo('post_reply_any'))
159
				{
160
					if ($modSettings['postmod_active'])
161
					{
162
						if (allowedTo('post_unapproved_replies_own') && !allowedTo('post_reply_own'))
163
							$context['becomes_approved'] = false;
164
						// Guests do not have post_unapproved_replies_own permission, so it's always post_unapproved_replies_any
165
						elseif ($user_info['is_guest'] && allowedTo('post_unapproved_replies_any'))
166
							$context['becomes_approved'] = false;
167
						else
168
							isAllowedTo('post_reply_own');
169
					}
170
					else
171
						isAllowedTo('post_reply_own');
172
				}
173
			}
174
			else
175
				$context['becomes_approved'] = true;
176
177
			$context['can_lock'] = allowedTo('lock_any') || ($user_info['id'] == $this->_topic_attributes['id_member'] && allowedTo('lock_own'));
178
			$context['can_sticky'] = allowedTo('make_sticky');
179
			$context['notify'] = !empty($context['notify']);
180
			$context['sticky'] = isset($_REQUEST['sticky']) ? !empty($_REQUEST['sticky']) : $this->_topic_attributes['is_sticky'];
181
		}
182
		else
183
		{
184
			$this->_topic_attributes['id_member'] = 0;
185
			$context['becomes_approved'] = true;
186
			if (empty($context['make_event']) || !empty($board))
187
			{
188
				if ($modSettings['postmod_active'] && !allowedTo('post_new') && allowedTo('post_unapproved_topics'))
189
					$context['becomes_approved'] = false;
190
				else
191
					isAllowedTo('post_new');
192
			}
193
194
			$this->_topic_attributes['locked'] = 0;
195
196
			// @todo These won't work if you're making an event.
197
			$context['can_lock'] = allowedTo(array('lock_any', 'lock_own'));
198
			$context['can_sticky'] = allowedTo('make_sticky');
199
200
			$context['notify'] = !empty($context['notify']);
201
			$context['sticky'] = !empty($_REQUEST['sticky']);
202
		}
203
204
		// @todo These won't work if you're posting an event!
205
		$context['can_notify'] = allowedTo('mark_any_notify');
206
		$context['can_move'] = allowedTo('move_any');
207
		$context['move'] = !empty($_REQUEST['move']);
208
		$context['announce'] = !empty($_REQUEST['announce']);
209
210
		// You can only announce topics that will get approved...
211
		$context['can_announce'] = allowedTo('announce_topic') && $context['becomes_approved'];
212
		$context['locked'] = !empty($this->_topic_attributes['locked']) || !empty($_REQUEST['lock']);
213
		$context['can_quote'] = empty($modSettings['disabledBBC']) || !in_array('quote', explode(',', $modSettings['disabledBBC']));
214
215
		// Generally don't show the approval box... (Assume we want things approved)
216
		$context['show_approval'] = allowedTo('approve_posts') && $context['becomes_approved'] ? 2 : (allowedTo('approve_posts') ? 1 : 0);
217
218
		// Don't allow a post if it's locked and you aren't all powerful.
219
		if ($this->_topic_attributes['locked'] && !allowedTo('moderate_board'))
220
			Errors::instance()->fatal_lang_error('topic_locked', false);
221
222
		try
223
		{
224
			$this->_events->trigger('prepare_context', array('id_member_poster' => $this->_topic_attributes['id_member']));
225
		}
226
		catch (Controller_Redirect_Exception $e)
227
		{
228
			return $e->doRedirect($this);
229
		}
230
231
		// See if any new replies have come along.
232
		if (empty($_REQUEST['msg']) && !empty($topic))
233
		{
234
			if (empty($options['no_new_reply_warning']) && isset($_REQUEST['last_msg']) && $context['topic_last_message'] > $_REQUEST['last_msg'])
235
			{
236
				$context['new_replies'] = countMessagesSince($topic, (int) $_REQUEST['last_msg'], false, $modSettings['postmod_active'] && !allowedTo('approve_posts'));
237
238
				if (!empty($context['new_replies']))
239
				{
240
					if ($context['new_replies'] == 1)
241
						$txt['error_new_replies'] = isset($_GET['last_msg']) ? $txt['error_new_reply_reading'] : $txt['error_new_reply'];
242
					else
243
						$txt['error_new_replies'] = sprintf(isset($_GET['last_msg']) ? $txt['error_new_replies_reading'] : $txt['error_new_replies'], $context['new_replies']);
244
245
					$this->_post_errors->addError('new_replies', 0);
246
247
					$modSettings['topicSummaryPosts'] = $context['new_replies'] > $modSettings['topicSummaryPosts'] ? max($modSettings['topicSummaryPosts'], 5) : $modSettings['topicSummaryPosts'];
248
				}
249
			}
250
		}
251
252
		// Get a response prefix (like 'Re:') in the default forum language.
253
		$context['response_prefix'] = response_prefix();
254
		$context['destination'] = 'post2;start=' . $_REQUEST['start'];
255
256
		// Previewing, modifying, or posting?
0 ignored issues
show
Unused Code Comprehensibility introduced by
37% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
257
		// Do we have a body, but an error happened.
258
		if (isset($_REQUEST['message']) || $this->_post_errors->hasErrors())
259
		{
260
			// Validate inputs.
261
			if (!$this->_post_errors->hasErrors())
262
			{
263
				// This means they didn't click Post and get an error.
264
				$really_previewing = true;
265
			}
266
			else
267
			{
268
				if (!isset($_REQUEST['subject']))
269
					$_REQUEST['subject'] = '';
270
271
				if (!isset($_REQUEST['message']))
272
					$_REQUEST['message'] = '';
273
274
				if (!isset($_REQUEST['icon']))
275
					$_REQUEST['icon'] = 'xx';
276
277
				// They are previewing if they asked to preview (i.e. came from quick reply).
278
				$really_previewing = !empty($_REQUEST['preview']) || isset($_REQUEST['xml']);
279
			}
280
281
			// Trigger the prepare_modifying event
282
			$this->_events->trigger('prepare_modifying', array('post_errors' => $this->_post_errors, 'really_previewing' => &$really_previewing));
283
284
			// In order to keep the approval status flowing through, we have to pass it through the form...
285
			$context['becomes_approved'] = empty($_REQUEST['not_approved']);
286
			$context['show_approval'] = isset($_REQUEST['approve']) ? ($_REQUEST['approve'] ? 2 : 1) : 0;
287
			$context['can_announce'] &= $context['becomes_approved'];
288
289
			// Set up the inputs for the form.
290
			$form_subject = strtr(Util::htmlspecialchars($_REQUEST['subject']), array("\r" => '', "\n" => '', "\t" => ''));
291
			$form_message = Util::htmlspecialchars($_REQUEST['message'], ENT_QUOTES, 'UTF-8', true);
292
293
			// Make sure the subject isn't too long - taking into account special characters.
294
			if (Util::strlen($form_subject) > 100)
295
				$form_subject = Util::substr($form_subject, 0, 100);
296
297
			// Are you... a guest?
298
			if ($user_info['is_guest'])
299
			{
300
				$context['name'] = !isset($_REQUEST['guestname']) ? '' : Util::htmlspecialchars(trim($_REQUEST['guestname']));
301
				$context['email'] = !isset($_REQUEST['email']) ? '' : Util::htmlspecialchars(trim($_REQUEST['email']));
302
				$user_info['name'] = $context['name'];
303
			}
304
305
			// Only show the preview stuff if they hit Preview.
306
			if ($really_previewing === true)
307
			{
308
				// Set up the preview message and subject
309
				$context['preview_message'] = $form_message;
310
				preparsecode($form_message, true);
311
312
				// Do all bulletin board code thing on the message
313
				$bbc_parser = \BBC\ParserWrapper::getInstance();
314
				preparsecode($context['preview_message']);
315
				$context['preview_message'] = $bbc_parser->parseMessage($context['preview_message'], isset($_REQUEST['ns']) ? 0 : 1);
0 ignored issues
show
Documentation introduced by
isset($_REQUEST['ns']) ? 0 : 1 is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
316
				$context['preview_message'] = censor($context['preview_message']);
317
318
				// Don't forget the subject
319
				$context['preview_subject'] = censor($form_subject);
320
321
				// Any errors we should tell them about?
322
				if ($form_subject === '')
323
				{
324
					$this->_post_errors->addError('no_subject');
325
					$context['preview_subject'] = '<em>' . $txt['no_subject'] . '</em>';
326
				}
327
328
				if ($context['preview_message'] === '')
329
					$this->_post_errors->addError('no_message');
330
				elseif (!empty($modSettings['max_messageLength']) && Util::strlen($form_message) > $modSettings['max_messageLength'])
331
					$this->_post_errors->addError(array('long_message', array($modSettings['max_messageLength'])));
332
333
				// Protect any CDATA blocks.
334
				if (isset($_REQUEST['xml']))
335
					$context['preview_message'] = strtr($context['preview_message'], array(']]>' => ']]]]><![CDATA[>'));
336
			}
337
338
			// Set up the checkboxes.
339
			$context['notify'] = !empty($_REQUEST['notify']);
340
			$context['use_smileys'] = !isset($_REQUEST['ns']);
341
			$context['icon'] = isset($_REQUEST['icon']) ? preg_replace('~[\./\\\\*\':"<>]~', '', $_REQUEST['icon']) : 'xx';
342
343
			// Set the destination action for submission.
344
			$context['destination'] .= isset($_REQUEST['msg']) ? ';msg=' . $_REQUEST['msg'] . ';' . $context['session_var'] . '=' . $context['session_id'] : '';
345
			$context['submit_label'] = isset($_REQUEST['msg']) ? $txt['save'] : $txt['post'];
346
347
			// Previewing an edit?
348
			if (isset($_REQUEST['msg']) && !empty($topic))
349
			{
350
				$msg_id = (int) $_REQUEST['msg'];
351
352
				// Get the existing message.
353
				$message = messageDetails((int) $msg_id, $topic);
354
355
				// The message they were trying to edit was most likely deleted.
356
				// @todo Change this error message?
357
				if ($message === false)
358
					Errors::instance()->fatal_lang_error('no_board', false);
359
360
				$errors = checkMessagePermissions($message['message']);
361
				if (!empty($errors))
362
					foreach ($errors as $error)
363
						$this->_post_errors->addError($error);
364
365
				prepareMessageContext($message);
0 ignored issues
show
Documentation introduced by
$message is of type false|array<string,?,{"m...chment_stuff":"array"}>, but the function expects a array<integer,*>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
366
			}
367
			elseif (isset($_REQUEST['last_msg']))
368
			{
369
				// @todo: sort out what kind of combinations are actually possible
370
				// Posting a quoted reply?
371
				if ((!empty($topic) && !empty($_REQUEST['quote'])) || (!empty($modSettings['enableFollowup']) && !empty($_REQUEST['followup'])))
372
					$case = 2;
373
				// Posting a reply without a quote?
374
				elseif (!empty($topic) && empty($_REQUEST['quote']))
375
					$case = 3;
376
				else
377
					$case = 4;
378
379
				list ($form_subject,) = getFormMsgSubject($case, $topic, $this->_topic_attributes['subject']);
380
			}
381
382
			// No check is needed, since nothing is really posted.
383
			checkSubmitOnce('free');
384
		}
385
		// Editing a message...
386
		elseif (isset($_REQUEST['msg']) && !empty($topic))
387
		{
388
			$msg_id = (int) $_REQUEST['msg'];
389
390
			$message = getFormMsgSubject(1, $topic, '', $msg_id);
391
392
			// The message they were trying to edit was most likely deleted.
393
			if ($message === false)
394
				Errors::instance()->fatal_lang_error('no_message', false);
395
396
			$this->_events->trigger('prepare_editing', array('topic' => $topic, 'message' => &$message));
397
398
			if (!empty($message['errors']))
399
				foreach ($message['errors'] as $error)
400
					$this->_post_errors->addError($error);
401
402
			// Get the stuff ready for the form.
403
			$form_subject = $message['message']['subject'];
404
			$form_message = un_preparsecode($message['message']['body']);
405
406
			$form_message = censor($form_message);
407
			$form_subject = censor($form_subject);
408
409
			// Check the boxes that should be checked.
410
			$context['use_smileys'] = !empty($message['message']['smileys_enabled']);
411
			$context['icon'] = $message['message']['icon'];
412
413
			// Set the destination.
414
			$context['destination'] .= ';msg=' . $msg_id . ';' . $context['session_var'] . '=' . $context['session_id'];
415
			$context['submit_label'] = $txt['save'];
416
		}
417
		// Posting...
418
		else
419
		{
420
			// By default....
421
			$context['use_smileys'] = true;
422
			$context['icon'] = 'xx';
423
424
			if ($user_info['is_guest'])
425
			{
426
				$context['name'] = isset($_SESSION['guest_name']) ? $_SESSION['guest_name'] : '';
427
				$context['email'] = isset($_SESSION['guest_email']) ? $_SESSION['guest_email'] : '';
428
			}
429
430
			$this->_events->trigger('prepare_posting');
431
432
			$context['submit_label'] = $txt['post'];
433
434
			// @todo: sort out what kind of combinations are actually possible
435
			// Posting a quoted reply?
436
			if ((!empty($topic) && !empty($_REQUEST['quote'])) || (!empty($modSettings['enableFollowup']) && !empty($_REQUEST['followup'])))
437
				$case = 2;
438
			// Posting a reply without a quote?
439
			elseif (!empty($topic) && empty($_REQUEST['quote']))
440
				$case = 3;
441
			else
442
				$case = 4;
443
444
			list ($form_subject, $form_message) = getFormMsgSubject($case, $topic, $this->_topic_attributes['subject']);
445
		}
446
447
		// Check whether this is a really old post being bumped...
448
		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']))
449
			$this->_post_errors->addError(array('old_topic', array($modSettings['oldTopicDays'])), 0);
450
451
		$this->_events->trigger('post_errors');
452
453
		// Any errors occurred?
454
		$context['post_error'] = array(
455
			'errors' => $this->_post_errors->prepareErrors(),
456
			'type' => $this->_post_errors->getErrorType() == 0 ? 'minor' : 'serious',
457
			'title' => $this->_post_errors->getErrorType() == 0 ? $txt['warning_while_submitting'] : $txt['error_while_submitting'],
458
		);
459
460
		// What are you doing? Posting, modifying, previewing, new post, or reply...
461
		if (empty($context['page_title']))
462
		{
463
			if (isset($_REQUEST['msg']))
464
				$context['page_title'] = $txt['modify_msg'];
465
			elseif (isset($_REQUEST['subject'], $context['preview_subject']))
466
				$context['page_title'] = $txt['post_reply'];
467
			elseif (empty($topic))
468
				$context['page_title'] = $txt['start_new_topic'];
469
			else
470
				$context['page_title'] = $txt['post_reply'];
471
		}
472
473
		// Update the topic summary, needed to show new posts in a preview
474
		if (!empty($topic) && !empty($modSettings['topicSummaryPosts']))
475
		{
476
			$only_approved = $modSettings['postmod_active'] && !allowedTo('approve_posts');
477
478
			if (isset($_REQUEST['xml']))
479
				$limit = empty($context['new_replies']) ? 0 : (int) $context['new_replies'];
480
			else
481
				$limit = $modSettings['topicSummaryPosts'];
482
483
			$before = isset($_REQUEST['msg']) ? array('before' => (int) $_REQUEST['msg']) : array();
484
485
			$counter = 0;
486
			$context['previous_posts'] = empty($limit) ? array() : selectMessages($topic, 0, $limit, $before, $only_approved);
487
			foreach ($context['previous_posts'] as &$post)
488
			{
489
				$post['is_new'] = !empty($context['new_replies']);
490
				$post['counter'] = $counter++;
491
				$post['is_ignored'] = !empty($modSettings['enable_buddylist']) && in_array($post['id_poster'], $user_info['ignoreusers']);
492
493
				if (!empty($context['new_replies']))
494
					$context['new_replies']--;
495
			}
496
		}
497
498
		// Just ajax previewing then lets stop now
499
		if (isset($_REQUEST['xml']))
500
			obExit();
501
502
		$context['subject'] = addcslashes($form_subject, '"');
503
		$context['message'] = str_replace(array('"', '<', '>', '&nbsp;'), array('&quot;', '&lt;', '&gt;', ' '), $form_message);
504
505
		// Needed for the editor and message icons.
506
		require_once(SUBSDIR . '/Editor.subs.php');
507
508
		// Now create the editor.
509
		$editorOptions = array(
510
			'id' => 'message',
511
			'value' => $context['message'],
512
			'labels' => array(
513
				'post_button' => $context['submit_label'],
514
			),
515
			// add height and width for the editor
516
			'height' => '275px',
517
			'width' => '100%',
518
			// We do XML preview here.
519
			'preview_type' => 2
520
		);
521
522
		// Message icons - customized or not, retrieve them...
523
		$context['icons'] = getMessageIcons($board);
524
525
		$context['icon_url'] = '';
526
527
		if (!empty($context['icons']))
528
		{
529
			$context['icons'][count($context['icons']) - 1]['is_last'] = true;
530
			$context['icons'][0]['selected'] = true;
531
532
			// $context['icon'] is set when editing a message
533
			if (!isset($context['icon']))
534
				$context['icon'] = $context['icons'][0]['value'];
535
			$found = false;
536
			foreach ($context['icons'] as $icon)
537
			{
538
				if ($icon['value'] === $context['icon'])
539
				{
540
					$found = true;
541
					$context['icon_url'] = $icon['url'];
542
					break;
543
				}
544
			}
545
			// Fail safe
546
			if (!$found)
547
			{
548
				$context['icon'] = $context['icons'][0]['value'];
549
				$context['icon_url'] = $context['icons'][0]['url'];
550
			}
551
		}
552
553
		$context['show_additional_options'] = !empty($_POST['additional_options']) || isset($_GET['additionalOptions']);
554
555
		// Trigger the finalize_post_form event
556
		$this->_events->trigger('finalize_post_form', array('destination' => &$context['destination'], 'page_title' => &$context['page_title'], 'show_additional_options' => &$context['show_additional_options'], 'editorOptions' => &$editorOptions));
557
558
		// Initialize the editor
559
		create_control_richedit($editorOptions);
560
561
		// Build the link tree.
562
		if (empty($topic))
563
		{
564
			$context['linktree'][] = array(
565
				'name' => '<em>' . $txt['start_new_topic'] . '</em>'
566
			);
567
		}
568
		else
569
		{
570
			$context['linktree'][] = array(
571
				'url' => $scripturl . '?topic=' . $topic . '.' . $_REQUEST['start'],
572
				'name' => $form_subject,
573
				'extra_before' => '<span><strong class="nav">' . $context['page_title'] . ' ( </strong></span>',
574
				'extra_after' => '<span><strong class="nav"> )</strong></span>'
575
			);
576
		}
577
578
		$context['back_to_topic'] = isset($_REQUEST['goback']) || (isset($_REQUEST['msg']) && !isset($_REQUEST['subject']));
579
		$context['is_new_topic'] = empty($topic);
580
		$context['is_new_post'] = !isset($_REQUEST['msg']);
581
		$context['is_first_post'] = $context['is_new_topic'] || (isset($_REQUEST['msg']) && $_REQUEST['msg'] == $this->_topic_attributes['id_first_msg']);
582
		$context['current_action'] = 'post';
583
584
		// Register this form in the session variables.
585
		checkSubmitOnce('register');
586
587
		// Finally, load the template.
588
		if (!isset($_REQUEST['xml']))
589
		{
590
			loadTemplate('Post');
591
			$context['sub_template'] = 'post_page';
592
		}
593
	}
594
595
	/**
596
	 * Posts or saves the message composed with Post().
597
	 *
598
	 * requires various permissions depending on the action.
599
	 * handles attachment, post, and calendar saving.
600
	 * sends off notifications, and allows for announcements and moderation.
601
	 * accessed from ?action=post2.
602
	 */
603
	public function action_post2()
0 ignored issues
show
Coding Style introduced by
action_post2 uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
action_post2 uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
action_post2 uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
action_post2 uses the super-global variable $_GET which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
action_post2 uses the super-global variable $_SESSION which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
604
	{
605
		global $board, $topic, $txt, $modSettings, $context, $user_settings;
606
		global $user_info, $board_info, $options;
607
608
		// Sneaking off, are we?
609
		if (empty($_POST) && empty($topic))
610
		{
611
			if (empty($_SERVER['CONTENT_LENGTH']))
612
				redirectexit('action=post;board=' . $board . '.0');
613
			else
614
				Errors::instance()->fatal_lang_error('post_upload_error', false);
615
		}
616
		elseif (empty($_POST) && !empty($topic))
617
			redirectexit('action=post;topic=' . $topic . '.0');
618
619
		// No need!
620
		$context['robot_no_index'] = true;
621
622
		// We are now in post2 action
623
		$context['current_action'] = 'post2';
624
625
		// If the session has timed out, let the user re-submit their form.
626
		if (checkSession('post', '', false) != '')
627
		{
628
			$this->_post_errors->addError('session_timeout');
629
630
			// Disable the preview so that any potentially malicious code is not executed
631
			$_REQUEST['preview'] = false;
632
633
			return $this->action_post();
634
		}
635
636
		$topic_info = array();
637
638
		// Previewing? Go back to start.
639
		if (isset($_REQUEST['preview']))
640
			return $this->action_post();
641
642
		require_once(SUBSDIR . '/Boards.subs.php');
643
		loadLanguage('Post');
644
645
		$this->_events->trigger('prepare_save_post', array('topic_info' => &$topic_info));
646
647
		// Prevent double submission of this form.
648
		checkSubmitOnce('check');
649
650
		// If this isn't a new topic load the topic info that we need.
651
		if (!empty($topic))
652
		{
653
			$topic_info = getTopicInfo($topic);
654
655
			// Though the topic should be there, it might have vanished.
656
			if (empty($topic_info))
657
				Errors::instance()->fatal_lang_error('topic_doesnt_exist');
658
659
			// Did this topic suddenly move? Just checking...
660
			if ($topic_info['id_board'] != $board)
661
				Errors::instance()->fatal_lang_error('not_a_topic');
662
		}
663
664
		// Replying to a topic?
665
		if (!empty($topic) && !isset($_REQUEST['msg']))
666
		{
667
			// Don't allow a post if it's locked.
668
			if ($topic_info['locked'] != 0 && !allowedTo('moderate_board'))
669
				Errors::instance()->fatal_lang_error('topic_locked', false);
670
671
			// Do the permissions and approval stuff...
672
			$becomesApproved = true;
673
			if ($topic_info['id_member_started'] != $user_info['id'])
674
			{
675
				if ($modSettings['postmod_active'] && allowedTo('post_unapproved_replies_any') && !allowedTo('post_reply_any'))
676
					$becomesApproved = false;
677
				else
678
					isAllowedTo('post_reply_any');
679
			}
680
			elseif (!allowedTo('post_reply_any'))
681
			{
682
				if ($modSettings['postmod_active'])
683
				{
684
					if (allowedTo('post_unapproved_replies_own') && !allowedTo('post_reply_own'))
685
						$becomesApproved = false;
686
					// Guests do not have post_unapproved_replies_own permission, so it's always post_unapproved_replies_any
687
					elseif ($user_info['is_guest'] && allowedTo('post_unapproved_replies_any'))
688
						$becomesApproved = false;
689
					else
690
						isAllowedTo('post_reply_own');
691
				}
692
			}
693
694
			if (isset($_POST['lock']))
695
			{
696
				$_POST['lock'] = $this->_checkLocked($_POST['lock'], $topic_info);
697
			}
698
699
			// So you wanna (un)sticky this...let's see.
700
			if (isset($_POST['sticky']) && ($_POST['sticky'] == $topic_info['is_sticky'] || !allowedTo('make_sticky')))
701
				unset($_POST['sticky']);
702
703
			$this->_events->trigger('save_replying', array('topic_info' => &$topic_info));
704
705
			// If the number of replies has changed, if the setting is enabled, go back to action_post() - which handles the error.
706
			if (empty($options['no_new_reply_warning']) && isset($_POST['last_msg']) && $topic_info['id_last_msg'] > $_POST['last_msg'])
707
			{
708
				addInlineJavascript('
709
					$(document).ready(function () {
710
						$("html,body").scrollTop($(\'.category_header:visible:first\').offset().top);
711
					});'
712
				);
713
714
				return $this->action_post();
715
			}
716
717
			$posterIsGuest = $user_info['is_guest'];
718
		}
719
		// Posting a new topic.
720
		elseif (empty($topic))
721
		{
722
			// Now don't be silly, new topics will get their own id_msg soon enough.
723
			unset($_REQUEST['msg'], $_POST['msg'], $_GET['msg']);
724
725
			// Do like, the permissions, for safety and stuff...
726
			$becomesApproved = true;
727
			if ($modSettings['postmod_active'] && !allowedTo('post_new') && allowedTo('post_unapproved_topics'))
728
				$becomesApproved = false;
729
			else
730
				isAllowedTo('post_new');
731
732
			$this->_events->trigger('save_new_topic', array('becomesApproved' => &$becomesApproved));
733
734
			if (isset($_POST['lock']))
735
			{
736
				$_POST['lock'] = $this->_checkLocked($_POST['lock']);
737
			}
738
739
			if (isset($_POST['sticky']) && (empty($_POST['sticky']) || !allowedTo('make_sticky')))
740
				unset($_POST['sticky']);
741
742
			$posterIsGuest = $user_info['is_guest'];
743
		}
744
		// Modifying an existing message?
745
		elseif (isset($_REQUEST['msg']) && !empty($topic))
746
		{
747
			$_REQUEST['msg'] = (int) $_REQUEST['msg'];
748
749
			$msgInfo = basicMessageInfo($_REQUEST['msg'], true);
750
751
			if (empty($msgInfo))
752
				Errors::instance()->fatal_lang_error('cant_find_messages', false);
753
754
			$this->_events->trigger('save_modify', array('msgInfo' => &$msgInfo));
755
756
			if (!empty($topic_info['locked']) && !allowedTo('moderate_board'))
757
				Errors::instance()->fatal_lang_error('topic_locked', false);
758
759
			if (isset($_POST['lock']))
760
			{
761
				$_POST['lock'] = $this->_checkLocked($_POST['lock'], $topic_info);
762
			}
763
764
			// Change the sticky status of this topic?
765
			if (isset($_POST['sticky']) && (!allowedTo('make_sticky') || $_POST['sticky'] == $topic_info['is_sticky']))
766
				unset($_POST['sticky']);
767
768
			if ($msgInfo['id_member'] == $user_info['id'] && !allowedTo('modify_any'))
769
			{
770
				if ((!$modSettings['postmod_active'] || $msgInfo['approved']) && !empty($modSettings['edit_disable_time']) && $msgInfo['poster_time'] + ($modSettings['edit_disable_time'] + 5) * 60 < time())
771
					Errors::instance()->fatal_lang_error('modify_post_time_passed', false);
772
				elseif ($topic_info['id_member_started'] == $user_info['id'] && !allowedTo('modify_own'))
773
					isAllowedTo('modify_replies');
774
				else
775
					isAllowedTo('modify_own');
776
			}
777
			elseif ($topic_info['id_member_started'] == $user_info['id'] && !allowedTo('modify_any'))
778
			{
779
				isAllowedTo('modify_replies');
780
781
				// If you're modifying a reply, I say it better be logged...
782
				$moderationAction = true;
783
			}
784
			else
785
			{
786
				isAllowedTo('modify_any');
787
788
				// Log it, assuming you're not modifying your own post.
789
				if ($msgInfo['id_member'] != $user_info['id'])
790
					$moderationAction = true;
791
			}
792
793
			$posterIsGuest = empty($msgInfo['id_member']);
794
795
			// Can they approve it?
796
			$can_approve = allowedTo('approve_posts');
797
			$becomesApproved = $modSettings['postmod_active'] ? ($can_approve && !$msgInfo['approved'] ? (!empty($_REQUEST['approve']) ? 1 : 0) : $msgInfo['approved']) : 1;
798
			$approve_has_changed = $msgInfo['approved'] != $becomesApproved;
799
800
			if (!allowedTo('moderate_forum') || !$posterIsGuest)
801
			{
802
				$_POST['guestname'] = $msgInfo['poster_name'];
803
				$_POST['email'] = $msgInfo['poster_email'];
804
			}
805
		}
806
807
		// In case we want to override
808
		if (allowedTo('approve_posts'))
809
		{
810
			$becomesApproved = !isset($_REQUEST['approve']) || !empty($_REQUEST['approve']) ? 1 : 0;
811
			$approve_has_changed = isset($msgInfo['approved']) ? $msgInfo['approved'] != $becomesApproved : false;
812
		}
813
814
		// If the poster is a guest evaluate the legality of name and email.
815
		if ($posterIsGuest)
0 ignored issues
show
Bug introduced by
The variable $posterIsGuest does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
816
		{
817
			$_POST['guestname'] = !isset($_POST['guestname']) ? '' : Util::htmlspecialchars(trim($_POST['guestname']));
818
			$_POST['email'] = !isset($_POST['email']) ? '' : Util::htmlspecialchars(trim($_POST['email']));
819
820
			if ($_POST['guestname'] == '' || $_POST['guestname'] == '_')
821
				$this->_post_errors->addError('no_name');
822
823
			if (Util::strlen($_POST['guestname']) > 25)
824
				$this->_post_errors->addError('long_name');
825
826
			if (empty($modSettings['guest_post_no_email']))
827
			{
828
				// Only check if they changed it!
829
				if (!isset($msgInfo) || $msgInfo['poster_email'] != $_POST['email'])
830
				{
831
					if (!allowedTo('moderate_forum') && !Data_Validator::is_valid($_POST, array('email' => 'valid_email|required'), array('email' => 'trim')))
832
						empty($_POST['email']) ? $this->_post_errors->addError('no_email') : $this->_post_errors->addError('bad_email');
833
				}
834
835
				// Now make sure this email address is not banned from posting.
836
				isBannedEmail($_POST['email'], 'cannot_post', sprintf($txt['you_are_post_banned'], $txt['guest_title']));
837
			}
838
839
			// In case they are making multiple posts this visit, help them along by storing their name.
840
			if (!$this->_post_errors->hasErrors())
841
			{
842
				$_SESSION['guest_name'] = $_POST['guestname'];
843
				$_SESSION['guest_email'] = $_POST['email'];
844
			}
845
		}
846
847
		try
848
		{
849
			$this->_events->trigger('before_save_post', array('post_errors' => $this->_post_errors, 'topic_info' => $topic_info));
850
		}
851
		catch (Controller_Redirect_Exception $e)
852
		{
853
			return $e->doRedirect($this);
854
		}
855
856
		// Check the subject and message.
857
		if (!isset($_POST['subject']) || Util::htmltrim(Util::htmlspecialchars($_POST['subject'])) === '')
858
			$this->_post_errors->addError('no_subject');
859
860
		if (!isset($_POST['message']) || Util::htmltrim(Util::htmlspecialchars($_POST['message'], ENT_QUOTES)) === '')
861
			$this->_post_errors->addError('no_message');
862
		elseif (!empty($modSettings['max_messageLength']) && Util::strlen($_POST['message']) > $modSettings['max_messageLength'])
863
			$this->_post_errors->addError(array('long_message', array($modSettings['max_messageLength'])));
864
		else
865
		{
866
			// Prepare the message a bit for some additional testing.
867
			$_POST['message'] = Util::htmlspecialchars($_POST['message'], ENT_QUOTES, 'UTF-8', true);
868
869
			// Preparse code. (Zef)
870
			if ($user_info['is_guest'])
871
				$user_info['name'] = $_POST['guestname'];
872
			preparsecode($_POST['message']);
873
874
			$bbc_parser = \BBC\ParserWrapper::getInstance();
875
876
			// Let's see if there's still some content left without the tags.
877
			if (Util::htmltrim(strip_tags($bbc_parser->parseMessage($_POST['message'], false), '<img>')) === '' && (!allowedTo('admin_forum') || strpos($_POST['message'], '[html]') === false))
878
				$this->_post_errors->addError('no_message');
879
		}
880
881
		if ($posterIsGuest)
882
		{
883
			// If user is a guest, make sure the chosen name isn't taken.
884
			require_once(SUBSDIR . '/Members.subs.php');
885
			if (isReservedName($_POST['guestname'], 0, true, false) && (!isset($msgInfo['poster_name']) || $_POST['guestname'] != $msgInfo['poster_name']))
0 ignored issues
show
Bug introduced by
The variable $msgInfo does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
886
				$this->_post_errors->addError('bad_name');
887
		}
888
		// If the user isn't a guest, get his or her name and email.
889
		elseif (!isset($_REQUEST['msg']))
890
		{
891
			$_POST['guestname'] = $user_info['username'];
892
			$_POST['email'] = $user_info['email'];
893
		}
894
895
		// Posting somewhere else? Are we sure you can?
896
		if (!empty($_REQUEST['post_in_board']))
897
		{
898
			$new_board = (int) $_REQUEST['post_in_board'];
899
			if (!allowedTo('post_new', $new_board))
900
			{
901
				$post_in_board = boardInfo($new_board);
902
903
				if (!empty($post_in_board))
904
					$this->_post_errors->addError(array('post_new_board', array($post_in_board['name'])));
905
				else
906
					$this->_post_errors->addError('post_new');
907
			}
908
		}
909
910
		// Any mistakes?
911
		if ($this->_post_errors->hasErrors())
912
		{
913
			addInlineJavascript('
914
				$(document).ready(function () {
915
					$("html,body").scrollTop($(\'.category_header:visible:first\').offset().top);
916
				});'
917
			);
918
919
			return $this->action_post();
920
		}
921
922
		// Make sure the user isn't spamming the board.
923
		if (!isset($_REQUEST['msg']))
924
			spamProtection('post');
925
926
		// At about this point, we're posting and that's that.
927
		ignore_user_abort(true);
928
		setTimeLimit(300);
929
930
		// Add special html entities to the subject, name, and email.
931
		$_POST['subject'] = strtr(Util::htmlspecialchars($_POST['subject']), array("\r" => '', "\n" => '', "\t" => ''));
932
		$_POST['guestname'] = htmlspecialchars($_POST['guestname'], ENT_COMPAT, 'UTF-8');
933
		$_POST['email'] = htmlspecialchars($_POST['email'], ENT_COMPAT, 'UTF-8');
934
935
		// At this point, we want to make sure the subject isn't too long.
936
		if (Util::strlen($_POST['subject']) > 100)
937
			$_POST['subject'] = Util::substr($_POST['subject'], 0, 100);
938
939
		// Creating a new topic?
940
		$newTopic = empty($_REQUEST['msg']) && empty($topic);
941
942
		// Collect all parameters for the creation or modification of a post.
943
		$msgOptions = array(
944
			'id' => empty($_REQUEST['msg']) ? 0 : (int) $_REQUEST['msg'],
945
			'subject' => $_POST['subject'],
946
			'body' => $_POST['message'],
947
			'icon' => preg_replace('~[\./\\\\*:"\'<>]~', '', $_POST['icon']),
948
			'smileys_enabled' => !isset($_POST['ns']),
949
			'approved' => $becomesApproved,
0 ignored issues
show
Bug introduced by
The variable $becomesApproved does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
950
		);
951
952
		$topicOptions = array(
953
			'id' => empty($topic) ? 0 : $topic,
954
			'board' => $board,
955
			'lock_mode' => isset($_POST['lock']) ? (int) $_POST['lock'] : null,
956
			'sticky_mode' => isset($_POST['sticky']) ? (int) $_POST['sticky'] : null,
957
			'mark_as_read' => true,
958
			'is_approved' => !$modSettings['postmod_active'] || empty($topic) || !empty($board_info['cur_topic_approved']),
959
		);
960
961
		$posterOptions = array(
962
			'id' => $user_info['id'],
963
			'name' => $_POST['guestname'],
964
			'email' => $_POST['email'],
965
			'update_post_count' => !$user_info['is_guest'] && !isset($_REQUEST['msg']) && $board_info['posts_count'],
966
		);
967
968
		$this->_events->trigger('pre_save_post', array('msgOptions' => &$msgOptions, 'topicOptions' => &$topicOptions, 'posterOptions' => &$posterOptions));
969
970
		// This is an already existing message. Edit it.
971
		if (!empty($_REQUEST['msg']))
972
		{
973
			// Have admins allowed people to hide their screwups?
974
			if (time() - $msgInfo['poster_time'] > $modSettings['edit_wait_time'] || $user_info['id'] != $msgInfo['id_member'])
975
			{
976
				$msgOptions['modify_time'] = time();
977
				$msgOptions['modify_name'] = $user_info['name'];
978
			}
979
980
			// This will save some time...
981
			if (empty($approve_has_changed))
982
				unset($msgOptions['approved']);
983
984
			modifyPost($msgOptions, $topicOptions, $posterOptions);
985
		}
986
		// This is a new topic or an already existing one. Save it.
987
		else
988
		{
989
			// We also have to fake the board:
990
			// if it's valid and it's not the current, let's forget about the "current" and load the new one
991
			if (!empty($new_board) && $board !== $new_board)
992
			{
993
				$board = $new_board;
994
				loadBoard();
995
996
				// Some details changed
997
				$topicOptions['board'] = $board;
998
				$topicOptions['is_approved'] = !$modSettings['postmod_active'] || empty($topic) || !empty($board_info['cur_topic_approved']);
999
				$posterOptions['update_post_count'] = !$user_info['is_guest'] && !isset($_REQUEST['msg']) && $board_info['posts_count'];
1000
			}
1001
1002
			createPost($msgOptions, $topicOptions, $posterOptions);
1003
1004
			if (isset($topicOptions['id']))
1005
				$topic = $topicOptions['id'];
1006
		}
1007
1008
		$this->_events->trigger('after_save_post', array('board' => $board, 'topic' => $topic, 'msgOptions' => $msgOptions, 'topicOptions' => $topicOptions, 'becomesApproved' => $becomesApproved, 'posterOptions' => $posterOptions));
1009
1010
		// Marking boards as read.
1011
		// (You just posted and they will be unread.)
1012
		if (!$user_info['is_guest'])
1013
		{
1014
			$board_list = !empty($board_info['parent_boards']) ? array_keys($board_info['parent_boards']) : array();
1015
1016
			// Returning to the topic?
1017
			if (!empty($_REQUEST['goback']))
1018
				$board_list[] = $board;
1019
1020
			if (!empty($board_list))
1021
				markBoardsRead($board_list, false, false);
1022
		}
1023
1024
		// Turn notification on or off.
1025
		if (!empty($_POST['notify']) && allowedTo('mark_any_notify'))
1026
			setTopicNotification($user_info['id'], $topic, true);
1027
		elseif (!$newTopic)
1028
			setTopicNotification($user_info['id'], $topic, false);
1029
1030
		// Log an act of moderation - modifying.
1031
		if (!empty($moderationAction))
1032
			logAction('modify', array('topic' => $topic, 'message' => (int) $_REQUEST['msg'], 'member' => $msgInfo['id_member'], 'board' => $board));
1033
1034
		if (isset($_POST['lock']) && $_POST['lock'] != 2)
1035
			logAction(empty($_POST['lock']) ? 'unlock' : 'lock', array('topic' => $topicOptions['id'], 'board' => $topicOptions['board']));
1036
1037
		if (isset($_POST['sticky']))
1038
			logAction(empty($_POST['sticky']) ? 'unsticky' : 'sticky', array('topic' => $topicOptions['id'], 'board' => $topicOptions['board']));
1039
1040
		// Notify any members who have notification turned on for this topic/board - only do this if it's going to be approved(!)
1041
		if ($becomesApproved)
1042
		{
1043
			require_once(SUBSDIR . '/Notification.subs.php');
1044
			if ($newTopic)
1045
			{
1046
				$notifyData = array(
1047
					'body' => $_POST['message'],
1048
					'subject' => $_POST['subject'],
1049
					'name' => $user_info['name'],
1050
					'poster' => $user_info['id'],
1051
					'msg' => $msgOptions['id'],
1052
					'board' => $board,
1053
					'topic' => $topic,
1054
					'signature' => (isset($user_settings['signature']) ? $user_settings['signature'] : ''),
1055
				);
1056
				sendBoardNotifications($notifyData);
1057
			}
1058
			elseif (empty($_REQUEST['msg']))
1059
			{
1060
				// Only send it to everyone if the topic is approved, otherwise just to the topic starter if they want it.
1061
				if ($topic_info['approved'])
1062
					sendNotifications($topic, 'reply');
1063
				else
1064
					sendNotifications($topic, 'reply', array(), $topic_info['id_member_started']);
1065
			}
1066
		}
1067
1068
		if ($board_info['num_topics'] == 0)
1069
			cache_put_data('board-' . $board, null, 120);
1070
1071
		if (!empty($_POST['announce_topic']))
1072
			redirectexit('action=announce;sa=selectgroup;topic=' . $topic . (!empty($_POST['move']) && allowedTo('move_any') ? ';move' : '') . (empty($_REQUEST['goback']) ? '' : ';goback'));
1073
1074
		if (!empty($_POST['move']) && allowedTo('move_any'))
1075
			redirectexit('action=movetopic;topic=' . $topic . '.0' . (empty($_REQUEST['goback']) ? '' : ';goback'));
1076
1077
		// Return to post if the mod is on.
1078
		if (isset($_REQUEST['msg']) && !empty($_REQUEST['goback']))
1079
			redirectexit('topic=' . $topic . '.msg' . $_REQUEST['msg'] . '#msg' . $_REQUEST['msg'], isBrowser('ie'));
1080
		elseif (!empty($_REQUEST['goback']))
1081
			redirectexit('topic=' . $topic . '.new#new', isBrowser('ie'));
1082
		// Dut-dut-duh-duh-DUH-duh-dut-duh-duh!  *dances to the Final Fantasy Fanfare...*
1083
		else
1084
			redirectexit('board=' . $board . '.0');
1085
	}
1086
1087
	/**
1088
	 * Loads a post and inserts it into the current editing text box.
1089
	 * Used to quick edit a post as well as to quote a post and place it in the quick reply box
1090
	 * Can be used to quick edit just the subject from the topic listing
1091
	 *
1092
	 * uses the Post language file.
1093
	 * uses special (sadly browser dependent) javascript to parse entities for internationalization reasons.
1094
	 * accessed with ?action=quotefast and ?action=quotefast;modify
1095
	 */
1096
	public function action_quotefast()
0 ignored issues
show
Coding Style introduced by
action_quotefast uses the super-global variable $_GET which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
action_quotefast uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
1097
	{
1098
		global $user_info, $context;
1099
1100
		loadLanguage('Post');
1101
1102
		// Where we going if we need to?
1103
		$context['post_box_name'] = isset($_GET['pb']) ? $_GET['pb'] : '';
1104
1105
		$row = quoteMessageInfo((int) $_REQUEST['quote'], isset($_REQUEST['modify']));
1106
1107
		$context['sub_template'] = 'quotefast';
1108
		if (!empty($row))
1109
			$can_view_post = $row['approved'] || ($row['id_member'] != 0 && $row['id_member'] == $user_info['id']) || allowedTo('approve_posts', $row['id_board']);
1110
1111
		if (!empty($can_view_post))
1112
		{
1113
			// Remove special formatting we don't want anymore.
1114
			$row['body'] = un_preparsecode($row['body']);
1115
1116
			// Censor the message!
1117
			$row['body'] = censor($row['body']);
1118
1119
			$row['body'] = preg_replace('~<br ?/?' . '>~i', "\n", $row['body']);
1120
1121
			// Want to modify a single message by double clicking it?
1122
			if (isset($_REQUEST['modify']))
1123
			{
1124
				$row['body'] = censor($row['subject']);
1125
1126
				$context['sub_template'] = 'modifyfast';
1127
				$context['message'] = array(
1128
					'id' => $_REQUEST['quote'],
1129
					'body' => $row['body'],
1130
					'subject' => addcslashes($row['subject'], '"'),
1131
				);
1132
1133
				return;
1134
			}
1135
1136
			// Remove any nested quotes.
1137
			$row['body'] = removeNestedQuotes($row['body']);
1138
1139
			// Add a quote string on the front and end.
1140
			$context['quote']['xml'] = '[quote author=' . $row['poster_name'] . ' link=msg=' . (int) $_REQUEST['quote'] . ' date=' . $row['poster_time'] . "]\n" . $row['body'] . "\n[/quote]";
1141
			$context['quote']['text'] = strtr(un_htmlspecialchars($context['quote']['xml']), array('\'' => '\\\'', '\\' => '\\\\', "\n" => '\\n', '</script>' => '</\' + \'script>'));
1142
			$context['quote']['xml'] = strtr($context['quote']['xml'], array('&nbsp;' => '&#160;', '<' => '&lt;', '>' => '&gt;'));
1143
1144
			$context['quote']['mozilla'] = strtr(Util::htmlspecialchars($context['quote']['text']), array('&quot;' => '"'));
1145
		}
1146
		//@todo Needs a nicer interface.
1147
		// In case our message has been removed in the meantime.
1148
		elseif (isset($_REQUEST['modify']))
1149
		{
1150
			$context['sub_template'] = 'modifyfast';
1151
			$context['message'] = array(
1152
				'id' => 0,
1153
				'body' => '',
1154
				'subject' => '',
1155
			);
1156
		}
1157
		else
1158
			$context['quote'] = array(
1159
				'xml' => '',
1160
				'mozilla' => '',
1161
				'text' => '',
1162
			);
1163
	}
1164
1165
	/**
1166
	 * Used to edit the body or subject of a message inline
1167
	 * called from action=jsmodify from script and topic js
1168
	 */
1169
	public function action_jsmodify()
0 ignored issues
show
Coding Style introduced by
action_jsmodify uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
action_jsmodify uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
1170
	{
1171
		global $modSettings, $board, $topic;
1172
		global $user_info, $context;
1173
1174
		// We have to have a topic!
1175
		if (empty($topic))
1176
			obExit(false);
1177
1178
		checkSession('get');
1179
1180
		$row = getTopicInfoByMsg($topic, empty($_REQUEST['msg']) ? 0 : (int) $_REQUEST['msg']);
1181
1182
		if (empty($row))
1183
			Errors::instance()->fatal_lang_error('no_board', false);
1184
1185
		// Change either body or subject requires permissions to modify messages.
1186
		if (isset($_POST['message']) || isset($_POST['subject']) || isset($_REQUEST['icon']))
1187
		{
1188
			if (!empty($row['locked']))
1189
				isAllowedTo('moderate_board');
1190
1191
			if ($row['id_member'] == $user_info['id'] && !allowedTo('modify_any'))
1192
			{
1193
				if ((!$modSettings['postmod_active'] || $row['approved']) && !empty($modSettings['edit_disable_time']) && $row['poster_time'] + ($modSettings['edit_disable_time'] + 5) * 60 < time())
1194
					Errors::instance()->fatal_lang_error('modify_post_time_passed', false);
1195
				elseif ($row['id_member_started'] == $user_info['id'] && !allowedTo('modify_own'))
1196
					isAllowedTo('modify_replies');
1197
				else
1198
					isAllowedTo('modify_own');
1199
			}
1200
			// Otherwise, they're locked out; someone who can modify the replies is needed.
1201
			elseif ($row['id_member_started'] == $user_info['id'] && !allowedTo('modify_any'))
1202
				isAllowedTo('modify_replies');
1203
			else
1204
				isAllowedTo('modify_any');
1205
1206
			// Only log this action if it wasn't your message.
1207
			$moderationAction = $row['id_member'] != $user_info['id'];
1208
		}
1209
1210
		if (isset($_POST['subject']) && Util::htmltrim(Util::htmlspecialchars($_POST['subject'])) !== '')
1211
		{
1212
			$_POST['subject'] = strtr(Util::htmlspecialchars($_POST['subject']), array("\r" => '', "\n" => '', "\t" => ''));
1213
1214
			// Maximum number of characters.
1215
			if (Util::strlen($_POST['subject']) > 100)
1216
				$_POST['subject'] = Util::substr($_POST['subject'], 0, 100);
1217
		}
1218
		elseif (isset($_POST['subject']))
1219
		{
1220
			$this->_post_errors->addError('no_subject');
1221
			unset($_POST['subject']);
1222
		}
1223
1224
		if (isset($_POST['message']))
1225
		{
1226
			if (Util::htmltrim(Util::htmlspecialchars($_POST['message'])) === '')
1227
			{
1228
				$this->_post_errors->addError('no_message');
1229
				unset($_POST['message']);
1230
			}
1231
			elseif (!empty($modSettings['max_messageLength']) && Util::strlen($_POST['message']) > $modSettings['max_messageLength'])
1232
			{
1233
				$this->_post_errors->addError(array('long_message', array($modSettings['max_messageLength'])));
1234
				unset($_POST['message']);
1235
			}
1236
			else
1237
			{
1238
				$_POST['message'] = Util::htmlspecialchars($_POST['message'], ENT_QUOTES, 'UTF-8', true);
1239
1240
				preparsecode($_POST['message']);
1241
				$bbc_parser = \BBC\ParserWrapper::getInstance();
1242
1243
				if (Util::htmltrim(strip_tags($bbc_parser->parseMessage($_POST['message'], false), '<img>')) === '')
1244
				{
1245
					$this->_post_errors->addError('no_message');
1246
					unset($_POST['message']);
1247
				}
1248
			}
1249
		}
1250
1251
		if (isset($_POST['lock']))
1252
		{
1253
			$_POST['lock'] = $this->_checkLocked($_POST['lock'], $row);
1254
		}
1255
1256
		if (isset($_POST['sticky']) && !allowedTo('make_sticky'))
1257
			unset($_POST['sticky']);
1258
1259
		if (!$this->_post_errors->hasErrors())
1260
		{
1261
			if (!empty($modSettings['mentions_enabled']))
1262
			{
1263
				if (!empty($_REQUEST['uid']))
1264
				{
1265
					$query_params = array();
1266
					$query_params['member_ids'] = array_unique(array_map('intval', $_REQUEST['uid']));
1267
					require_once(SUBSDIR . '/Members.subs.php');
1268
					$mentioned_members = membersBy('member_ids', $query_params, true);
1269
					$replacements = 0;
1270
					$actually_mentioned = array();
1271
					foreach ($mentioned_members as $member)
1272
					{
1273
						$_POST['message'] = str_replace('@' . $member['real_name'], '[member=' . $member['id_member'] . ']' . $member['real_name'] . '[/member]', $_POST['message'], $replacements);
1274
						if ($replacements > 0)
1275
							$actually_mentioned[] = $member['id_member'];
1276
					}
1277
				}
1278
1279
				if (!empty($actually_mentioned))
1280
				{
1281
					require_once(CONTROLLERDIR . '/Mentions.controller.php');
1282
					$mentions = new Mentions_Controller();
0 ignored issues
show
Bug introduced by
The call to Mentions_Controller::__construct() misses a required argument $eventManager.

This check looks for function calls that miss required arguments.

Loading history...
1283
					$mentions->setData(array(
0 ignored issues
show
Deprecated Code introduced by
The method Mentions_Controller::setData() has been deprecated with message: since 1.1

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
1284
						'id_member' => $actually_mentioned,
1285
						'type' => 'men',
1286
						'id_msg' => $row['id_msg'],
1287
						'status' => $row['approved'] ? 'new' : 'unapproved',
1288
					));
1289
					$mentions->action_add();
0 ignored issues
show
Deprecated Code introduced by
The method Mentions_Controller::action_add() has been deprecated with message: since 1.1 - Use Notifications::create instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
1290
				}
1291
			}
1292
1293
			$msgOptions = array(
1294
				'id' => $row['id_msg'],
1295
				'subject' => isset($_POST['subject']) ? $_POST['subject'] : null,
1296
				'body' => isset($_POST['message']) ? $_POST['message'] : null,
1297
				'icon' => isset($_REQUEST['icon']) ? preg_replace('~[\./\\\\*\':"<>]~', '', $_REQUEST['icon']) : null,
1298
			);
1299
1300
			$topicOptions = array(
1301
				'id' => $topic,
1302
				'board' => $board,
1303
				'lock_mode' => isset($_POST['lock']) ? (int) $_POST['lock'] : null,
1304
				'sticky_mode' => isset($_POST['sticky']) ? (int) $_POST['sticky'] : null,
1305
				'mark_as_read' => false,
1306
			);
1307
1308
			$posterOptions = array();
1309
1310
			// Only consider marking as editing if they have edited the subject, message or icon.
1311
			if ((isset($_POST['subject']) && $_POST['subject'] != $row['subject']) || (isset($_POST['message']) && $_POST['message'] != $row['body']) || (isset($_REQUEST['icon']) && $_REQUEST['icon'] != $row['icon']))
1312
			{
1313
				// And even then only if the time has passed...
1314
				if (time() - $row['poster_time'] > $modSettings['edit_wait_time'] || $user_info['id'] != $row['id_member'])
1315
				{
1316
					$msgOptions['modify_time'] = time();
1317
					$msgOptions['modify_name'] = $user_info['name'];
1318
				}
1319
			}
1320
			// If nothing was changed there's no need to add an entry to the moderation log.
1321
			else
1322
				$moderationAction = false;
1323
1324
			modifyPost($msgOptions, $topicOptions, $posterOptions);
1325
1326
			// If we didn't change anything this time but had before put back the old info.
1327
			if (!isset($msgOptions['modify_time']) && !empty($row['modified_time']))
1328
			{
1329
				$msgOptions['modify_time'] = $row['modified_time'];
1330
				$msgOptions['modify_name'] = $row['modified_name'];
1331
			}
1332
1333
			// Changing the first subject updates other subjects to 'Re: new_subject'.
1334
			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'))))
1335
			{
1336
				// Get the proper (default language) response prefix first.
1337
				$context['response_prefix'] = response_prefix();
1338
1339
				topicSubject(array('id_topic' => $topic, 'id_first_msg' => $row['id_first_msg']), $_POST['subject'], $context['response_prefix'], true);
1340
			}
1341
1342
			if (!empty($moderationAction))
1343
				logAction('modify', array('topic' => $topic, 'message' => $row['id_msg'], 'member' => $row['id_member'], 'board' => $board));
1344
		}
1345
1346
		if (isset($_REQUEST['xml']))
1347
		{
1348
			$context['sub_template'] = 'modifydone';
1349
			if (!$this->_post_errors->hasErrors() && isset($msgOptions['subject']) && isset($msgOptions['body']))
1350
			{
1351
				$context['message'] = array(
1352
					'id' => $row['id_msg'],
1353
					'modified' => array(
1354
						'time' => isset($msgOptions['modify_time']) ? standardTime($msgOptions['modify_time']) : '',
1355
						'html_time' => isset($msgOptions['modify_time']) ? htmlTime($msgOptions['modify_time']) : '',
1356
						'timestamp' => isset($msgOptions['modify_time']) ? forum_time(true, $msgOptions['modify_time']) : 0,
1357
						'name' => isset($msgOptions['modify_time']) ? $msgOptions['modify_name'] : '',
1358
					),
1359
					'subject' => $msgOptions['subject'],
1360
					'first_in_topic' => $row['id_msg'] == $row['id_first_msg'],
1361
					'body' => strtr($msgOptions['body'], array(']]>' => ']]]]><![CDATA[>')),
1362
				);
1363
1364
				$context['message']['subject'] = censor($context['message']['subject']);
1365
				$context['message']['body'] = censor($context['message']['body']);
1366
1367
				$context['message']['body'] = $bbc_parser->parseMessage($context['message']['body'], $row['smileys_enabled']);
0 ignored issues
show
Bug introduced by
The variable $bbc_parser does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1368
			}
1369
			// Topic?
1370
			elseif (!$this->_post_errors->hasErrors())
1371
			{
1372
				$context['sub_template'] = 'modifytopicdone';
1373
				$context['message'] = array(
1374
					'id' => $row['id_msg'],
1375
					'modified' => array(
1376
						'time' => isset($msgOptions['modify_time']) ? standardTime($msgOptions['modify_time']) : '',
1377
						'html_time' => isset($msgOptions['modify_time']) ? htmlTime($msgOptions['modify_time']) : '',
1378
						'timestamp' => isset($msgOptions['modify_time']) ? forum_time(true, $msgOptions['modify_time']) : 0,
1379
						'name' => isset($msgOptions['modify_time']) ? $msgOptions['modify_name'] : '',
1380
					),
1381
					'subject' => isset($msgOptions['subject']) ? $msgOptions['subject'] : '',
1382
				);
1383
1384
				$context['message']['subject'] = censor($context['message']['subject']);
1385
			}
1386
			else
1387
			{
1388
				$context['message'] = array(
1389
					'id' => $row['id_msg'],
1390
					'errors' => array(),
1391
					'error_in_subject' => $this->_post_errors->hasError('no_subject'),
1392
					'error_in_body' => $this->_post_errors->hasError('no_message') || $this->_post_errors->hasError('long_message'),
1393
				);
1394
				$context['message']['errors'] = $this->_post_errors->prepareErrors();
1395
			}
1396
		}
1397
		else
1398
			obExit(false);
1399
	}
1400
1401
	/**
1402
	 * Spell checks the post for typos ;).
1403
	 * It uses the pspell library, which MUST be installed.
1404
	 * It has problems with internationalization.
1405
	 * It is accessed via ?action=spellcheck.
1406
	 * @deprecated since 1.1
1407
	 */
1408
	public function action_spellcheck()
1409
	{
1410
		// Initialize this controller with its own event manager
1411
		$controller = new Spellcheck_Controller(new Event_Manager());
1412
1413
		// Fetch controllers generic hook name from the action controller
1414
		$hook = $controller->getHook();
1415
1416
		// Call the controllers pre dispatch method
1417
		$controller->pre_dispatch();
1418
1419
		// Call integrate_action_XYZ_before -> XYZ_controller -> integrate_action_XYZ_after
1420
		call_integration_hook('integrate_action_' . $hook . '_before', array('action_index'));
1421
1422
		$result = $controller->action_index();
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $result is correct as $controller->action_index() (which targets Spellcheck_Controller::action_index()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
1423
1424
		call_integration_hook('integrate_action_' . $hook . '_after', array('action_index'));
1425
1426
		return $result;
1427
	}
1428
1429
	protected function _checkLocked($lock, $topic_info = null)
1430
	{
1431
		global $user_info;
1432
1433
		// A new topic
1434
		if ($topic_info === null)
1435
		{
1436
			// New topics are by default not locked.
1437
			if (empty($lock))
1438
				return null;
1439
			// Besides, you need permission.
1440
			elseif (!allowedTo(array('lock_any', 'lock_own')))
1441
				return null;
1442
			// A moderator-lock (1) can override a user-lock (2).
1443
			else
1444
				return allowedTo('lock_any') ? 1 : 2;
1445
		}
1446
1447
		// Nothing changes to the lock status.
1448
		if ((empty($lock) && empty($topic_info['locked'])) || (!empty($lock) && !empty($topic_info['locked'])))
1449
			return null;
1450
		// You're simply not allowed to (un)lock this.
1451
		elseif (!allowedTo(array('lock_any', 'lock_own')) || (!allowedTo('lock_any') && $user_info['id'] != $topic_info['id_member_started']))
1452
			return null;
1453
		// You're only allowed to lock your own topics.
1454
		elseif (!allowedTo('lock_any'))
1455
		{
1456
			// You're not allowed to break a moderator's lock.
1457
			if ($topic_info['locked'] == 1)
1458
				return null;
1459
			// Lock it with a soft lock or unlock it.
1460
			else
1461
				$lock = empty($lock) ? 0 : 2;
1462
		}
1463
		// You must be the moderator.
1464
		else
1465
			$lock = empty($lock) ? 0 : 1;
1466
1467
		return $lock;
1468
	}
1469
}
1470