Completed
Pull Request — development (#2330)
by Joshua
41:37 queued 30:33
created

Post_Controller   F

Complexity

Total Complexity 391

Size/Duplication

Total Lines 1431
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 12

Test Coverage

Coverage 0%
Metric Value
wmc 391
lcom 1
cbo 12
dl 0
loc 1431
ccs 0
cts 850
cp 0
rs 3.4839

8 Methods

Rating   Name   Duplication   Size   Complexity  
A pre_dispatch() 0 9 1
A action_index() 0 6 1
F action_post() 0 500 134
F action_post2() 0 483 154
C action_quotefast() 0 68 9
F action_jsmodify() 0 231 75
A action_spellcheck() 0 20 1
C _checkLocked() 0 40 16

How to fix   Complexity   

Complex Class

Complex classes like Post_Controller often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Post_Controller, and based on these observations, apply Extract Interface, too.

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