Failed Conditions
Branch release-2.1 (4e22cf)
by Rick
06:39
created

Post.php ➔ QuoteFast()   D

Complexity

Conditions 15
Paths 100

Size

Total Lines 107
Code Lines 61

Duplication

Lines 2
Ratio 1.87 %

Importance

Changes 0
Metric Value
cc 15
eloc 61
nc 100
nop 0
dl 2
loc 107
rs 4.9121
c 0
b 0
f 0

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
 * Simple Machines Forum (SMF)
8
 *
9
 * @package SMF
10
 * @author Simple Machines http://www.simplemachines.org
11
 * @copyright 2017 Simple Machines and individual contributors
12
 * @license http://www.simplemachines.org/about/smf/license.php BSD
13
 *
14
 * @version 2.1 Beta 4
15
 */
16
17
if (!defined('SMF'))
18
	die('No direct access...');
19
20
/**
21
 * Handles showing the post screen, loading the post to be modified, and loading any post quoted.
22
 *
23
 * - additionally handles previews of posts.
24
 * - @uses the Post template and language file, main sub template.
25
 * - requires different permissions depending on the actions, but most notably post_new, post_reply_own, and post_reply_any.
26
 * - shows options for the editing and posting of calendar events and attachments, as well as the posting of polls.
27
 * - accessed from ?action=post.
28
 *
29
 *  @param array $post_errors Holds any errors found while tyring to post
30
 */
31
function Post($post_errors = array())
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
32
{
33
	global $txt, $scripturl, $topic, $modSettings, $board;
34
	global $user_info, $context, $settings;
35
	global $sourcedir, $smcFunc, $language;
36
37
	loadLanguage('Post');
38
	if (!empty($modSettings['drafts_post_enabled']))
39
		loadLanguage('Drafts');
40
41
	// You can't reply with a poll... hacker.
42
	if (isset($_REQUEST['poll']) && !empty($topic) && !isset($_REQUEST['msg']))
43
		unset($_REQUEST['poll']);
44
45
	// Posting an event?
46
	$context['make_event'] = isset($_REQUEST['calendar']);
47
	$context['robot_no_index'] = true;
48
49
	// Get notification preferences for later
50
	require_once($sourcedir . '/Subs-Notify.php');
51
	// use $temp to get around "Only variables should be passed by reference"
52
	$temp = getNotifyPrefs($user_info['id']);
53
	$context['notify_prefs'] = (array) array_pop($temp);
54
	$context['auto_notify'] = !empty($context['notify_prefs']['msg_auto_notify']);
55
56
	// You must be posting to *some* board.
57
	if (empty($board) && !$context['make_event'])
58
		fatal_lang_error('no_board', false);
59
60
	require_once($sourcedir . '/Subs-Post.php');
61
62
	if (isset($_REQUEST['xml']))
63
	{
64
		$context['sub_template'] = 'post';
65
66
		// Just in case of an earlier error...
67
		$context['preview_message'] = '';
68
		$context['preview_subject'] = '';
69
	}
70
71
	// No message is complete without a topic.
72 View Code Duplication
	if (empty($topic) && !empty($_REQUEST['msg']))
73
	{
74
		$request = $smcFunc['db_query']('', '
75
			SELECT id_topic
76
			FROM {db_prefix}messages
77
			WHERE id_msg = {int:msg}',
78
			array(
79
				'msg' => (int) $_REQUEST['msg'],
80
		));
81
		if ($smcFunc['db_num_rows']($request) != 1)
82
			unset($_REQUEST['msg'], $_POST['msg'], $_GET['msg']);
83
		else
84
			list ($topic) = $smcFunc['db_fetch_row']($request);
85
		$smcFunc['db_free_result']($request);
86
	}
87
88
	// Check if it's locked. It isn't locked if no topic is specified.
89
	if (!empty($topic))
90
	{
91
		$request = $smcFunc['db_query']('', '
92
			SELECT
93
				t.locked, COALESCE(ln.id_topic, 0) AS notify, t.is_sticky, t.id_poll, t.id_last_msg, mf.id_member,
94
				t.id_first_msg, mf.subject, ml.modified_reason,
95
				CASE WHEN ml.poster_time > ml.modified_time THEN ml.poster_time ELSE ml.modified_time END AS last_post_time
96
			FROM {db_prefix}topics AS t
97
				LEFT JOIN {db_prefix}log_notify AS ln ON (ln.id_topic = t.id_topic AND ln.id_member = {int:current_member})
98
				LEFT JOIN {db_prefix}messages AS mf ON (mf.id_msg = t.id_first_msg)
99
				LEFT JOIN {db_prefix}messages AS ml ON (ml.id_msg = t.id_last_msg)
100
			WHERE t.id_topic = {int:current_topic}
101
			LIMIT 1',
102
			array(
103
				'current_member' => $user_info['id'],
104
				'current_topic' => $topic,
105
			)
106
		);
107
		list ($locked, $context['notify'], $sticky, $pollID, $context['topic_last_message'], $id_member_poster, $id_first_msg, $first_subject, $editReason, $lastPostTime) = $smcFunc['db_fetch_row']($request);
0 ignored issues
show
Unused Code introduced by
The assignment to $editReason is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
108
		$smcFunc['db_free_result']($request);
109
110
		// If this topic already has a poll, they sure can't add another.
111
		if (isset($_REQUEST['poll']) && $pollID > 0)
112
			unset($_REQUEST['poll']);
113
114
		if (empty($_REQUEST['msg']))
115
		{
116
			if ($user_info['is_guest'] && !allowedTo('post_reply_any') && (!$modSettings['postmod_active'] || !allowedTo('post_unapproved_replies_any')))
117
				is_not_guest();
118
119
			// By default the reply will be approved...
120
			$context['becomes_approved'] = true;
121
			if ($id_member_poster != $user_info['id'] || $user_info['is_guest'])
122
			{
123
				if ($modSettings['postmod_active'] && allowedTo('post_unapproved_replies_any') && !allowedTo('post_reply_any'))
124
					$context['becomes_approved'] = false;
125
				else
126
					isAllowedTo('post_reply_any');
127
			}
128
			elseif (!allowedTo('post_reply_any'))
129
			{
130
				if ($modSettings['postmod_active'] && ((allowedTo('post_unapproved_replies_own') && !allowedTo('post_reply_own')) || allowedTo('post_unapproved_replies_any')))
131
					$context['becomes_approved'] = false;
132
				else
133
					isAllowedTo('post_reply_own');
134
			}
135
		}
136
		else
137
			$context['becomes_approved'] = true;
138
139
		$context['can_lock'] = allowedTo('lock_any') || ($user_info['id'] == $id_member_poster && allowedTo('lock_own'));
140
		$context['can_sticky'] = allowedTo('make_sticky');
141
142
		// We don't always want the request vars to override what's in the db...
143
		$context['already_locked'] = $locked;
144
		$context['already_sticky'] = $sticky;
145
		$context['notify'] = !empty($context['notify']);
146
		$context['sticky'] = isset($_REQUEST['sticky']) ? !empty($_REQUEST['sticky']) : $sticky;
147
148
		// Check whether this is a really old post being bumped...
149
		if (!empty($modSettings['oldTopicDays']) && $lastPostTime + $modSettings['oldTopicDays'] * 86400 < time() && empty($sticky) && !isset($_REQUEST['subject']))
150
			$post_errors[] = array('old_topic', array($modSettings['oldTopicDays']));
151
	}
152
	else
153
	{
154
		$context['becomes_approved'] = true;
155
		if ((!$context['make_event'] || !empty($board)))
156
		{
157
			if ($modSettings['postmod_active'] && !allowedTo('post_new') && allowedTo('post_unapproved_topics'))
158
				$context['becomes_approved'] = false;
159
			else
160
				isAllowedTo('post_new');
161
		}
162
163
		$locked = 0;
164
165
		// @todo These won't work if you're making an event.
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
166
		$context['can_lock'] = allowedTo(array('lock_any', 'lock_own'));
167
		$context['can_sticky'] = allowedTo('make_sticky');
168
		$context['already_sticky'] = 0;
169
		$context['already_locked'] = 0;
170
		$context['notify'] = !empty($context['notify']);
171
		$context['sticky'] = !empty($_REQUEST['sticky']);
172
	}
173
174
	// @todo These won't work if you're posting an event!
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
175
	$context['can_notify'] = !$context['user']['is_guest'];
176
	$context['can_move'] = allowedTo('move_any');
177
	$context['move'] = !empty($_REQUEST['move']);
178
	$context['announce'] = !empty($_REQUEST['announce']);
179
	// You can only announce topics that will get approved...
180
	$context['can_announce'] = allowedTo('announce_topic') && $context['becomes_approved'];
181
	$context['locked'] = !empty($locked) || !empty($_REQUEST['lock']);
182
	$context['can_quote'] = empty($modSettings['disabledBBC']) || !in_array('quote', explode(',', $modSettings['disabledBBC']));
183
184
	// Generally don't show the approval box... (Assume we want things approved)
185
	$context['show_approval'] = allowedTo('approve_posts') && $context['becomes_approved'] ? 2 : (allowedTo('approve_posts') ? 1 : 0);
186
187
	// An array to hold all the attachments for this topic.
188
	$context['current_attachments'] = array();
189
190
	// Clear out prior attachment activity when starting afresh
191
	if (empty ($_REQUEST['message']) && empty ($_REQUEST['preview'])) {
192
		unset($_SESSION['already_attached']);
193
	}
194
195
	// Don't allow a post if it's locked and you aren't all powerful.
196
	if ($locked && !allowedTo('moderate_board'))
197
		fatal_lang_error('topic_locked', false);
198
	// Check the users permissions - is the user allowed to add or post a poll?
199
	if (isset($_REQUEST['poll']) && $modSettings['pollMode'] == '1')
200
	{
201
		// New topic, new poll.
202 View Code Duplication
		if (empty($topic))
203
			isAllowedTo('poll_post');
204
		// This is an old topic - but it is yours!  Can you add to it?
205
		elseif ($user_info['id'] == $id_member_poster && !allowedTo('poll_add_any'))
0 ignored issues
show
Bug introduced by
The variable $id_member_poster 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...
206
			isAllowedTo('poll_add_own');
207
		// If you're not the owner, can you add to any poll?
208
		else
209
			isAllowedTo('poll_add_any');
210
211
		require_once($sourcedir . '/Subs-Members.php');
212
		$allowedVoteGroups = groupsAllowedTo('poll_vote', $board);
213
214
		// Set up the poll options.
215
		$context['poll_options'] = array(
216
			'max_votes' => empty($_POST['poll_max_votes']) ? '1' : max(1, $_POST['poll_max_votes']),
217
			'hide' => empty($_POST['poll_hide']) ? 0 : $_POST['poll_hide'],
218
			'expire' => !isset($_POST['poll_expire']) ? '' : $_POST['poll_expire'],
219
			'change_vote' => isset($_POST['poll_change_vote']),
220
			'guest_vote' => isset($_POST['poll_guest_vote']),
221
			'guest_vote_enabled' => in_array(-1, $allowedVoteGroups['allowed']),
222
		);
223
224
		// Make all five poll choices empty.
225
		$context['choices'] = array(
226
			array('id' => 0, 'number' => 1, 'label' => '', 'is_last' => false),
227
			array('id' => 1, 'number' => 2, 'label' => '', 'is_last' => false),
228
			array('id' => 2, 'number' => 3, 'label' => '', 'is_last' => false),
229
			array('id' => 3, 'number' => 4, 'label' => '', 'is_last' => false),
230
			array('id' => 4, 'number' => 5, 'label' => '', 'is_last' => true)
231
		);
232
		$context['last_choice_id'] = 4;
233
	}
234
235
	if ($context['make_event'])
236
	{
237
		// They might want to pick a board.
238
		if (!isset($context['current_board']))
239
			$context['current_board'] = 0;
240
241
		// Start loading up the event info.
242
		$context['event'] = array();
243
		$context['event']['title'] = isset($_REQUEST['evtitle']) ? $smcFunc['htmlspecialchars'](stripslashes($_REQUEST['evtitle'])) : '';
244
		$context['event']['location'] = isset($_REQUEST['event_location']) ? $smcFunc['htmlspecialchars'](stripslashes($_REQUEST['event_location'])) : '';
245
246
		$context['event']['id'] = isset($_REQUEST['eventid']) ? (int) $_REQUEST['eventid'] : -1;
247
		$context['event']['new'] = $context['event']['id'] == -1;
248
249
		// Permissions check!
250
		isAllowedTo('calendar_post');
251
252
		// We want a fairly compact version of the time, but as close as possible to the user's settings.
253 View Code Duplication
		if (preg_match('~%[HkIlMpPrRSTX](?:[^%]*%[HkIlMpPrRSTX])*~', $user_info['time_format'], $matches) == 0 || empty($matches[0]))
254
			$time_string = '%k:%M';
255
		else
256
			$time_string = str_replace(array('%I', '%H', '%S', '%r', '%R', '%T'), array('%l', '%k', '', '%l:%M %p', '%k:%M', '%l:%M'), $matches[0]);
257
258
		$js_time_string = str_replace(
259
			array('%H', '%k', '%I', '%l', '%M', '%p', '%P', '%r',      '%R',  '%S', '%T',    '%X'),
260
			array('H',  'G',  'h',  'g',  'i',  'A',  'a',  'h:i:s A', 'H:i', 's',  'H:i:s', 'H:i:s'),
261
			$time_string
262
		);
263
264
		// Editing an event?  (but NOT previewing!?)
265
		if (empty($context['event']['new']) && !isset($_REQUEST['subject']))
266
		{
267
			// If the user doesn't have permission to edit the post in this topic, redirect them.
268
			if ((empty($id_member_poster) || $id_member_poster != $user_info['id'] || !allowedTo('modify_own')) && !allowedTo('modify_any'))
269
			{
270
				require_once($sourcedir . '/Calendar.php');
271
				return CalendarPost();
272
			}
273
274
			// Get the current event information.
275
			require_once($sourcedir . '/Subs-Calendar.php');
276
			$eventProperties = getEventProperties($context['event']['id']);
277
			$context['event'] = array_merge($context['event'], $eventProperties);
278
		}
279
		else
280
		{
281
			// Get the current event information.
282
			require_once($sourcedir . '/Subs-Calendar.php');
283
			$eventProperties = getNewEventDatetimes();
284
			$context['event'] = array_merge($context['event'], $eventProperties);
285
286
			// Make sure the year and month are in the valid range.
287
			if ($context['event']['month'] < 1 || $context['event']['month'] > 12)
288
				fatal_lang_error('invalid_month', false);
289 View Code Duplication
			if ($context['event']['year'] < $modSettings['cal_minyear'] || $context['event']['year'] > $modSettings['cal_maxyear'])
290
				fatal_lang_error('invalid_year', false);
291
292
			// Get a list of boards they can post in.
293
			$boards = boardsAllowedTo('post_new');
294
			if (empty($boards))
295
				fatal_lang_error('cannot_post_new', 'user');
296
297
			// Load a list of boards for this event in the context.
298
			require_once($sourcedir . '/Subs-MessageIndex.php');
299
			$boardListOptions = array(
300
				'included_boards' => in_array(0, $boards) ? null : $boards,
301
				'not_redirection' => true,
302
				'use_permissions' => true,
303
				'selected_board' => empty($context['current_board']) ? $modSettings['cal_defaultboard'] : $context['current_board'],
304
			);
305
			$context['event']['categories'] = getBoardList($boardListOptions);
306
		}
307
308
		// Find the last day of the month.
309
		$context['event']['last_day'] = (int) strftime('%d', mktime(0, 0, 0, $context['event']['month'] == 12 ? 1 : $context['event']['month'] + 1, 0, $context['event']['month'] == 12 ? $context['event']['year'] + 1 : $context['event']['year']));
310
311
		// An all day event? Set up some nice defaults in case the user wants to change that
312 View Code Duplication
		if ($context['event']['allday'] == true)
313
		{
314
			$context['event']['tz'] = getUserTimezone();
315
			$context['event']['start_time'] = timeformat(time(), $time_string);
0 ignored issues
show
Documentation introduced by
$time_string is of type string, 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['event']['end_time'] = timeformat(time() + 3600, $time_string);
0 ignored issues
show
Documentation introduced by
$time_string is of type string, 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...
317
		}
318
		// Otherwise, just adjust these to look nice on the input form
319
		else
320
		{
321
			$context['event']['start_time'] = $context['event']['start_time_orig'];
322
			$context['event']['end_time'] = $context['event']['end_time_orig'];
323
		}
324
325
		// Need this so the user can select a timezone for the event.
326
		$context['all_timezones'] = smf_list_timezones($context['event']['start_date']);
327
		unset($context['all_timezones']['']);
328
329
		// If the event's timezone is not in SMF's standard list of time zones, prepend it to the list
330 View Code Duplication
		if (!in_array($context['event']['tz'], array_keys($context['all_timezones'])))
331
		{
332
			$d = date_create($context['event']['start_datetime'] . ' ' . $context['event']['tz']);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $d. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
333
			$context['all_timezones'] = array($context['event']['tz'] => fix_tz_abbrev($context['event']['tz'], date_format($d, 'T')) . ' - ' . $context['event']['tz'] . ' [UTC' . date_format($d, 'P') . ']') + $context['all_timezones'];
334
		}
335
336
		loadCSSFile('jquery-ui.datepicker.css', array('defer' => false), 'smf_datepicker');
337
		loadCSSFile('jquery.timepicker.css', array('defer' => false), 'smf_timepicker');
338
		loadJavaScriptFile('jquery-ui.datepicker.min.js', array('defer' => true), 'smf_datepicker');
339
		loadJavaScriptFile('jquery.timepicker.min.js', array('defer' => true), 'smf_timepicker');
340
		loadJavaScriptFile('datepair.min.js', array('defer' => true), 'smf_datepair');
341
		addInlineJavaScript('
342
	$("#allday").click(function(){
343
		$("#start_time").attr("disabled", this.checked);
344
		$("#end_time").attr("disabled", this.checked);
345
		$("#tz").attr("disabled", this.checked);
346
	});
347
	$("#event_time_input .date_input").datepicker({
348
		dateFormat: "yy-mm-dd",
349
		autoSize: true,
350
		isRTL: ' . ($context['right_to_left'] ? 'true' : 'false') . ',
351
		constrainInput: true,
352
		showAnim: "",
353
		showButtonPanel: false,
354
		minDate: "' . $modSettings['cal_minyear'] . '-01-01",
355
		maxDate: "' . $modSettings['cal_maxyear'] . '-12-31",
356
		yearRange: "' . $modSettings['cal_minyear'] . ':' . $modSettings['cal_maxyear'] . '",
357
		hideIfNoPrevNext: true,
358
		monthNames: ["' . implode('", "', $txt['months_titles']) . '"],
359
		monthNamesShort: ["' . implode('", "', $txt['months_short']) . '"],
360
		dayNames: ["' . implode('", "', $txt['days']) . '"],
361
		dayNamesShort: ["' . implode('", "', $txt['days_short']) . '"],
362
		dayNamesMin: ["' . implode('", "', $txt['days_short']) . '"],
363
		prevText: "' . $txt['prev_month'] . '",
364
		nextText: "' . $txt['next_month'] . '",
365
	});
366
	$(".time_input").timepicker({
367
		timeFormat: "' . $js_time_string . '",
368
		showDuration: true,
369
		maxTime: "23:59:59",
370
	});
371
	var date_entry = document.getElementById("event_time_input");
372
	var date_entry_pair = new Datepair(date_entry, {
373
		timeClass: "time_input",
374
		dateClass: "date_input",
375
		parseDate: function (el) {
376
		    var utc = new Date($(el).datepicker("getDate"));
377
		    return utc && new Date(utc.getTime() + (utc.getTimezoneOffset() * 60000));
378
		},
379
		updateDate: function (el, v) {
380
		    $(el).datepicker("setDate", new Date(v.getTime() - (v.getTimezoneOffset() * 60000)));
381
		}
382
	});
383
	', true);
384
385
		$context['event']['board'] = !empty($board) ? $board : $modSettings['cal_defaultboard'];
386
		$context['event']['topic'] = !empty($topic) ? $topic : 0;
387
	}
388
389
	// See if any new replies have come along.
390
	// Huh, $_REQUEST['msg'] is set upon submit, so this doesn't get executed at submit
391
	// only at preview
392
	if (empty($_REQUEST['msg']) && !empty($topic))
393
	{
394
		if (isset($_REQUEST['last_msg']) && $context['topic_last_message'] > $_REQUEST['last_msg'])
395
		{
396
			$request = $smcFunc['db_query']('', '
397
				SELECT COUNT(*)
398
				FROM {db_prefix}messages
399
				WHERE id_topic = {int:current_topic}
400
					AND id_msg > {int:last_msg}' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : '
401
					AND approved = {int:approved}') . '
402
				LIMIT 1',
403
				array(
404
					'current_topic' => $topic,
405
					'last_msg' => (int) $_REQUEST['last_msg'],
406
					'approved' => 1,
407
				)
408
			);
409
			list ($context['new_replies']) = $smcFunc['db_fetch_row']($request);
410
			$smcFunc['db_free_result']($request);
411
412
			if (!empty($context['new_replies']))
413
			{
414
				if ($context['new_replies'] == 1)
415
					$txt['error_new_replies'] = isset($_GET['last_msg']) ? $txt['error_new_reply_reading'] : $txt['error_new_reply'];
416
				else
417
					$txt['error_new_replies'] = sprintf(isset($_GET['last_msg']) ? $txt['error_new_replies_reading'] : $txt['error_new_replies'], $context['new_replies']);
418
419
				$post_errors[] = 'new_replies';
420
421
				$modSettings['topicSummaryPosts'] = $context['new_replies'] > $modSettings['topicSummaryPosts'] ? max($modSettings['topicSummaryPosts'], 5) : $modSettings['topicSummaryPosts'];
422
			}
423
		}
424
	}
425
426
	// Get a response prefix (like 'Re:') in the default forum language.
427 View Code Duplication
	if (!isset($context['response_prefix']) && !($context['response_prefix'] = cache_get_data('response_prefix')))
428
	{
429
		if ($language === $user_info['language'])
430
			$context['response_prefix'] = $txt['response_prefix'];
431
		else
432
		{
433
			loadLanguage('index', $language, false);
434
			$context['response_prefix'] = $txt['response_prefix'];
435
			loadLanguage('index');
436
		}
437
		cache_put_data('response_prefix', $context['response_prefix'], 600);
438
	}
439
440
	// 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...
441
	// Do we have a body, but an error happened.
442
	if (isset($_REQUEST['message']) || isset($_REQUEST['quickReply']) || !empty($context['post_error']))
443
	{
444
		if (isset($_REQUEST['quickReply']))
445
			$_REQUEST['message'] = $_REQUEST['quickReply'];
446
447
		// Validate inputs.
448
		if (empty($context['post_error']))
449
		{
450
			// This means they didn't click Post and get an error.
451
			$really_previewing = true;
452
453
		}
454
		else
455
		{
456
			if (!isset($_REQUEST['subject']))
457
				$_REQUEST['subject'] = '';
458
			if (!isset($_REQUEST['message']))
459
				$_REQUEST['message'] = '';
460
			if (!isset($_REQUEST['icon']))
461
				$_REQUEST['icon'] = 'xx';
462
463
			// They are previewing if they asked to preview (i.e. came from quick reply).
464
			$really_previewing = !empty($_POST['preview']);
465
		}
466
467
		// In order to keep the approval status flowing through, we have to pass it through the form...
468
		$context['becomes_approved'] = empty($_REQUEST['not_approved']);
469
		$context['show_approval'] = isset($_REQUEST['approve']) ? ($_REQUEST['approve'] ? 2 : 1) : 0;
470
		$context['can_announce'] &= $context['becomes_approved'];
471
472
		// Set up the inputs for the form.
473
		$form_subject = strtr($smcFunc['htmlspecialchars']($_REQUEST['subject']), array("\r" => '', "\n" => '', "\t" => ''));
474
		$form_message = $smcFunc['htmlspecialchars']($_REQUEST['message'], ENT_QUOTES);
475
476
		// Make sure the subject isn't too long - taking into account special characters.
477
		if ($smcFunc['strlen']($form_subject) > 100)
478
			$form_subject = $smcFunc['substr']($form_subject, 0, 100);
479
480
		if (isset($_REQUEST['poll']))
481
		{
482
			$context['question'] = isset($_REQUEST['question']) ? $smcFunc['htmlspecialchars'](trim($_REQUEST['question'])) : '';
483
484
			$context['choices'] = array();
485
			$choice_id = 0;
486
487
			$_POST['options'] = empty($_POST['options']) ? array() : htmlspecialchars__recursive($_POST['options']);
488
			foreach ($_POST['options'] as $option)
0 ignored issues
show
Bug introduced by
The expression $_POST['options'] of type array|string is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
489
			{
490
				if (trim($option) == '')
491
					continue;
492
493
				$context['choices'][] = array(
494
					'id' => $choice_id++,
495
					'number' => $choice_id,
496
					'label' => $option,
497
					'is_last' => false
498
				);
499
			}
500
501
			// One empty option for those with js disabled...I know are few... :P
502
			$context['choices'][] = array(
503
				'id' => $choice_id++,
504
				'number' => $choice_id,
505
				'label' => '',
506
				'is_last' => false
507
			);
508
509 View Code Duplication
			if (count($context['choices']) < 2)
510
			{
511
				$context['choices'][] = array(
512
					'id' => $choice_id++,
513
					'number' => $choice_id,
514
					'label' => '',
515
					'is_last' => false
516
				);
517
			}
518
			$context['last_choice_id'] = $choice_id;
519
			$context['choices'][count($context['choices']) - 1]['is_last'] = true;
520
		}
521
522
		// Are you... a guest?
523
		if ($user_info['is_guest'])
524
		{
525
			$_REQUEST['guestname'] = !isset($_REQUEST['guestname']) ? '' : trim($_REQUEST['guestname']);
526
			$_REQUEST['email'] = !isset($_REQUEST['email']) ? '' : trim($_REQUEST['email']);
527
528
			$_REQUEST['guestname'] = $smcFunc['htmlspecialchars']($_REQUEST['guestname']);
529
			$context['name'] = $_REQUEST['guestname'];
530
			$_REQUEST['email'] = $smcFunc['htmlspecialchars']($_REQUEST['email']);
531
			$context['email'] = $_REQUEST['email'];
532
533
			$user_info['name'] = $_REQUEST['guestname'];
534
		}
535
536
		// Only show the preview stuff if they hit Preview.
537
		if (($really_previewing == true || isset($_REQUEST['xml'])) && !isset($_REQUEST['save_draft']))
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
538
		{
539
			// Set up the preview message and subject and censor them...
540
			$context['preview_message'] = $form_message;
541
			preparsecode($form_message, true);
542
			preparsecode($context['preview_message']);
543
544
			// Do all bulletin board code tags, with or without smileys.
545
			$context['preview_message'] = parse_bbc($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...
546
			censorText($context['preview_message']);
547
548
			if ($form_subject != '')
549
			{
550
				$context['preview_subject'] = $form_subject;
551
552
				censorText($context['preview_subject']);
553
			}
554
			else
555
				$context['preview_subject'] = '<em>' . $txt['no_subject'] . '</em>';
556
557
			// Protect any CDATA blocks.
558
			if (isset($_REQUEST['xml']))
559
				$context['preview_message'] = strtr($context['preview_message'], array(']]>' => ']]]]><![CDATA[>'));
560
		}
561
562
		// Set up the checkboxes.
563
		$context['notify'] = !empty($_REQUEST['notify']);
564
		$context['use_smileys'] = !isset($_REQUEST['ns']);
565
566
		$context['icon'] = isset($_REQUEST['icon']) ? preg_replace('~[\./\\\\*\':"<>]~', '', $_REQUEST['icon']) : 'xx';
567
568
		// Set the destination action for submission.
569
		$context['destination'] = 'post2;start=' . $_REQUEST['start'] . (isset($_REQUEST['msg']) ? ';msg=' . $_REQUEST['msg'] . ';' . $context['session_var'] . '=' . $context['session_id'] : '') . (isset($_REQUEST['poll']) ? ';poll' : '');
570
		$context['submit_label'] = isset($_REQUEST['msg']) ? $txt['save'] : $txt['post'];
571
572
		// Previewing an edit?
573
		if (isset($_REQUEST['msg']) && !empty($topic))
574
		{
575
			// Get the existing message. Previewing.
576
			$request = $smcFunc['db_query']('', '
577
				SELECT
578
					m.id_member, m.modified_time, m.smileys_enabled, m.body,
579
					m.poster_name, m.poster_email, m.subject, m.icon, m.approved,
580
					COALESCE(a.size, -1) AS filesize, a.filename, a.id_attach,
581
					a.approved AS attachment_approved, t.id_member_started AS id_member_poster,
582
					m.poster_time, log.id_action
583
				FROM {db_prefix}messages AS m
584
					INNER JOIN {db_prefix}topics AS t ON (t.id_topic = {int:current_topic})
585
					LEFT JOIN {db_prefix}attachments AS a ON (a.id_msg = m.id_msg AND a.attachment_type = {int:attachment_type})
586
					LEFT JOIN {db_prefix}log_actions AS log ON (m.id_topic = log.id_topic AND log.action = {string:announce_action})
587
				WHERE m.id_msg = {int:id_msg}
588
					AND m.id_topic = {int:current_topic}',
589
				array(
590
					'current_topic' => $topic,
591
					'attachment_type' => 0,
592
					'id_msg' => $_REQUEST['msg'],
593
					'announce_action' => 'announce_topic',
594
				)
595
			);
596
			// The message they were trying to edit was most likely deleted.
597
			// @todo Change this error message?
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
598
			if ($smcFunc['db_num_rows']($request) == 0)
599
				fatal_lang_error('no_board', false);
600
			$row = $smcFunc['db_fetch_assoc']($request);
601
602
			$attachment_stuff = array($row);
603
			while ($row2 = $smcFunc['db_fetch_assoc']($request))
604
				$attachment_stuff[] = $row2;
605
			$smcFunc['db_free_result']($request);
606
607 View Code Duplication
			if ($row['id_member'] == $user_info['id'] && !allowedTo('modify_any'))
608
			{
609
				// Give an extra five minutes over the disable time threshold, so they can type - assuming the post is public.
610
				if ($row['approved'] && !empty($modSettings['edit_disable_time']) && $row['poster_time'] + ($modSettings['edit_disable_time'] + 5) * 60 < time())
611
					fatal_lang_error('modify_post_time_passed', false);
612
				elseif ($row['id_member_poster'] == $user_info['id'] && !allowedTo('modify_own'))
613
					isAllowedTo('modify_replies');
614
				else
615
					isAllowedTo('modify_own');
616
			}
617
			elseif ($row['id_member_poster'] == $user_info['id'] && !allowedTo('modify_any'))
618
				isAllowedTo('modify_replies');
619
			else
620
				isAllowedTo('modify_any');
621
622 View Code Duplication
			if ($context['can_announce'] && !empty($row['id_action']))
623
			{
624
				loadLanguage('Errors');
625
				$context['post_error']['messages'][] = $txt['error_topic_already_announced'];
626
			}
627
628
			if (!empty($modSettings['attachmentEnable']))
629
			{
630
				$request = $smcFunc['db_query']('', '
631
					SELECT COALESCE(size, -1) AS filesize, filename, id_attach, approved, mime_type, id_thumb
632
					FROM {db_prefix}attachments
633
					WHERE id_msg = {int:id_msg}
634
						AND attachment_type = {int:attachment_type}
635
					ORDER BY id_attach',
636
					array(
637
						'id_msg' => (int) $_REQUEST['msg'],
638
						'attachment_type' => 0,
639
					)
640
				);
641
642
				while ($row = $smcFunc['db_fetch_assoc']($request))
643
				{
644
					if ($row['filesize'] <= 0)
645
						continue;
646
					$context['current_attachments'][$row['id_attach']] = array(
647
						'name' => $smcFunc['htmlspecialchars']($row['filename']),
648
						'size' => $row['filesize'],
649
						'attachID' => $row['id_attach'],
650
						'approved' => $row['approved'],
651
						'mime_type' => $row['mime_type'],
652
						'thumb' => $row['id_thumb'],
653
					);
654
				}
655
				$smcFunc['db_free_result']($request);
656
			}
657
658
			// Allow moderators to change names....
659
			if (allowedTo('moderate_forum') && !empty($topic))
660
			{
661
				$request = $smcFunc['db_query']('', '
662
					SELECT id_member, poster_name, poster_email
663
					FROM {db_prefix}messages
664
					WHERE id_msg = {int:id_msg}
665
						AND id_topic = {int:current_topic}
666
					LIMIT 1',
667
					array(
668
						'current_topic' => $topic,
669
						'id_msg' => (int) $_REQUEST['msg'],
670
					)
671
				);
672
				$row = $smcFunc['db_fetch_assoc']($request);
673
				$smcFunc['db_free_result']($request);
674
675 View Code Duplication
				if (empty($row['id_member']))
676
				{
677
					$context['name'] = $smcFunc['htmlspecialchars']($row['poster_name']);
678
					$context['email'] = $smcFunc['htmlspecialchars']($row['poster_email']);
679
				}
680
			}
681
		}
682
683
		// No check is needed, since nothing is really posted.
684
		checkSubmitOnce('free');
685
	}
686
	// Editing a message...
687
	elseif (isset($_REQUEST['msg']) && !empty($topic))
688
	{
689
		$context['editing'] = true;
690
691
		$_REQUEST['msg'] = (int) $_REQUEST['msg'];
692
693
		// Get the existing message. Editing.
694
		$request = $smcFunc['db_query']('', '
695
			SELECT
696
				m.id_member, m.modified_time, m.modified_name, m.modified_reason, m.smileys_enabled, m.body,
697
				m.poster_name, m.poster_email, m.subject, m.icon, m.approved,
698
				COALESCE(a.size, -1) AS filesize, a.filename, a.id_attach, a.mime_type, a.id_thumb,
699
				a.approved AS attachment_approved, t.id_member_started AS id_member_poster,
700
				m.poster_time, log.id_action
701
			FROM {db_prefix}messages AS m
702
				INNER JOIN {db_prefix}topics AS t ON (t.id_topic = {int:current_topic})
703
				LEFT JOIN {db_prefix}attachments AS a ON (a.id_msg = m.id_msg AND a.attachment_type = {int:attachment_type})
704
					LEFT JOIN {db_prefix}log_actions AS log ON (m.id_topic = log.id_topic AND log.action = {string:announce_action})
705
			WHERE m.id_msg = {int:id_msg}
706
				AND m.id_topic = {int:current_topic}',
707
			array(
708
				'current_topic' => $topic,
709
				'attachment_type' => 0,
710
				'id_msg' => $_REQUEST['msg'],
711
				'announce_action' => 'announce_topic',
712
			)
713
		);
714
		// The message they were trying to edit was most likely deleted.
715
		if ($smcFunc['db_num_rows']($request) == 0)
716
			fatal_lang_error('no_message', false);
717
		$row = $smcFunc['db_fetch_assoc']($request);
718
719
		$attachment_stuff = array($row);
720
		while ($row2 = $smcFunc['db_fetch_assoc']($request))
721
			$attachment_stuff[] = $row2;
722
		$smcFunc['db_free_result']($request);
723
724 View Code Duplication
		if ($row['id_member'] == $user_info['id'] && !allowedTo('modify_any'))
725
		{
726
			// Give an extra five minutes over the disable time threshold, so they can type - assuming the post is public.
727
			if ($row['approved'] && !empty($modSettings['edit_disable_time']) && $row['poster_time'] + ($modSettings['edit_disable_time'] + 5) * 60 < time())
728
				fatal_lang_error('modify_post_time_passed', false);
729
			elseif ($row['id_member_poster'] == $user_info['id'] && !allowedTo('modify_own'))
730
				isAllowedTo('modify_replies');
731
			else
732
				isAllowedTo('modify_own');
733
		}
734
		elseif ($row['id_member_poster'] == $user_info['id'] && !allowedTo('modify_any'))
735
			isAllowedTo('modify_replies');
736
		else
737
			isAllowedTo('modify_any');
738
739 View Code Duplication
		if ($context['can_announce'] && !empty($row['id_action']))
740
		{
741
			loadLanguage('Errors');
742
			$context['post_error']['messages'][] = $txt['error_topic_already_announced'];
743
		}
744
745
		// When was it last modified?
746
		if (!empty($row['modified_time']))
747
		{
748
			$context['last_modified'] = timeformat($row['modified_time']);
749
			$context['last_modified_reason'] = censorText($row['modified_reason']);
750
			$context['last_modified_text'] = sprintf($txt['last_edit_by'], $context['last_modified'], $row['modified_name']) . empty($row['modified_reason']) ? '' : '&nbsp;' . $txt['last_edit_reason'] . ':&nbsp;' . $row['modified_reason'];
751
		}
752
753
		// Get the stuff ready for the form.
754
		$form_subject = $row['subject'];
755
		$form_message = un_preparsecode($row['body']);
756
		censorText($form_message);
757
		censorText($form_subject);
758
759
		// Check the boxes that should be checked.
760
		$context['use_smileys'] = !empty($row['smileys_enabled']);
761
		$context['icon'] = $row['icon'];
762
763
		// Show an "approve" box if the user can approve it, and the message isn't approved.
764
		if (!$row['approved'] && !$context['show_approval'])
765
			$context['show_approval'] = allowedTo('approve_posts');
766
767
		// Sort the attachments so they are in the order saved
768
		$temp = array();
769
		foreach ($attachment_stuff as $attachment)
770
		{
771
			if ($attachment['filesize'] >= 0 && !empty($modSettings['attachmentEnable']))
772
				$temp[$attachment['id_attach']] = $attachment;
773
774
		}
775
		ksort($temp);
776
777
		// Load up 'em attachments!
778
		foreach ($temp as $attachment)
779
		{
780
			$context['current_attachments'][$attachment['id_attach']] = array(
781
				'name' => $smcFunc['htmlspecialchars']($attachment['filename']),
782
				'size' => $attachment['filesize'],
783
				'attachID' => $attachment['id_attach'],
784
				'approved' => $attachment['attachment_approved'],
785
				'mime_type' => $attachment['mime_type'],
786
				'thumb' => $attachment['id_thumb'],
787
			);
788
		}
789
790
		// Allow moderators to change names....
791 View Code Duplication
		if (allowedTo('moderate_forum') && empty($row['id_member']))
792
		{
793
			$context['name'] = $smcFunc['htmlspecialchars']($row['poster_name']);
794
			$context['email'] = $smcFunc['htmlspecialchars']($row['poster_email']);
795
		}
796
797
		// Set the destination.
798
		$context['destination'] = 'post2;start=' . $_REQUEST['start'] . ';msg=' . $_REQUEST['msg'] . ';' . $context['session_var'] . '=' . $context['session_id'] . (isset($_REQUEST['poll']) ? ';poll' : '');
799
		$context['submit_label'] = $txt['save'];
800
	}
801
	// Posting...
802
	else
803
	{
804
		// By default....
805
		$context['use_smileys'] = true;
806
		$context['icon'] = 'xx';
807
808
		if ($user_info['is_guest'])
809
		{
810
			$context['name'] = isset($_SESSION['guest_name']) ? $_SESSION['guest_name'] : '';
811
			$context['email'] = isset($_SESSION['guest_email']) ? $_SESSION['guest_email'] : '';
812
		}
813
		$context['destination'] = 'post2;start=' . $_REQUEST['start'] . (isset($_REQUEST['poll']) ? ';poll' : '');
814
815
		$context['submit_label'] = $txt['post'];
816
817
		// Posting a quoted reply?
818
		if (!empty($topic) && !empty($_REQUEST['quote']))
819
		{
820
			// Make sure they _can_ quote this post, and if so get it.
821
			$request = $smcFunc['db_query']('', '
822
				SELECT m.subject, COALESCE(mem.real_name, m.poster_name) AS poster_name, m.poster_time, m.body
823
				FROM {db_prefix}messages AS m
824
					INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board AND {query_see_board})
825
					LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
826
				WHERE m.id_msg = {int:id_msg}' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : '
827
					AND m.approved = {int:is_approved}') . '
828
				LIMIT 1',
829
				array(
830
					'id_msg' => (int) $_REQUEST['quote'],
831
					'is_approved' => 1,
832
				)
833
			);
834
			if ($smcFunc['db_num_rows']($request) == 0)
835
				fatal_lang_error('quoted_post_deleted', false);
836
			list ($form_subject, $mname, $mdate, $form_message) = $smcFunc['db_fetch_row']($request);
837
			$smcFunc['db_free_result']($request);
838
839
			// Add 'Re: ' to the front of the quoted subject.
840
			if (trim($context['response_prefix']) != '' && $smcFunc['strpos']($form_subject, trim($context['response_prefix'])) !== 0)
841
				$form_subject = $context['response_prefix'] . $form_subject;
842
843
			// Censor the message and subject.
844
			censorText($form_message);
845
			censorText($form_subject);
846
847
			// But if it's in HTML world, turn them into htmlspecialchar's so they can be edited!
848
			if (strpos($form_message, '[html]') !== false)
849
			{
850
				$parts = preg_split('~(\[/code\]|\[code(?:=[^\]]+)?\])~i', $form_message, -1, PREG_SPLIT_DELIM_CAPTURE);
851
				for ($i = 0, $n = count($parts); $i < $n; $i++)
852
				{
853
					// It goes 0 = outside, 1 = begin tag, 2 = inside, 3 = close tag, repeat.
854
					if ($i % 4 == 0)
855
						$parts[$i] = preg_replace_callback('~\[html\](.+?)\[/html\]~is', function($m)
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $m. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
856
						{
857
							return '[html]' . preg_replace('~<br\s?/?' . '>~i', '&lt;br /&gt;<br>', "$m[1]") . '[/html]';
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $m instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
858
						}, $parts[$i]);
859
				}
860
				$form_message = implode('', $parts);
861
			}
862
863
			$form_message = preg_replace('~<br ?/?' . '>~i', "\n", $form_message);
864
865
			// Remove any nested quotes, if necessary.
866 View Code Duplication
			if (!empty($modSettings['removeNestedQuotes']))
867
				$form_message = preg_replace(array('~\n?\[quote.*?\].+?\[/quote\]\n?~is', '~^\n~', '~\[/quote\]~'), '', $form_message);
868
869
			// Add a quote string on the front and end.
870
			$form_message = '[quote author=' . $mname . ' link=msg=' . (int) $_REQUEST['quote'] . ' date=' . $mdate . ']' . "\n" . rtrim($form_message) . "\n" . '[/quote]';
871
		}
872
		// Posting a reply without a quote?
873
		elseif (!empty($topic) && empty($_REQUEST['quote']))
874
		{
875
			// Get the first message's subject.
876
			$form_subject = $first_subject;
0 ignored issues
show
Bug introduced by
The variable $first_subject 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...
877
878
			// Add 'Re: ' to the front of the subject.
879 View Code Duplication
			if (trim($context['response_prefix']) != '' && $form_subject != '' && $smcFunc['strpos']($form_subject, trim($context['response_prefix'])) !== 0)
880
				$form_subject = $context['response_prefix'] . $form_subject;
881
882
			// Censor the subject.
883
			censorText($form_subject);
884
885
			$form_message = '';
886
		}
887
		else
888
		{
889
			$form_subject = isset($_GET['subject']) ? $_GET['subject'] : '';
890
			$form_message = '';
891
		}
892
	}
893
894
	$context['can_post_attachment'] = !empty($modSettings['attachmentEnable']) && $modSettings['attachmentEnable'] == 1 && (allowedTo('post_attachment') || ($modSettings['postmod_active'] && allowedTo('post_unapproved_attachments')));
895
	if ($context['can_post_attachment'])
896
	{
897
		// If there are attachments, calculate the total size and how many.
898
		$context['attachments']['total_size'] = 0;
899
		$context['attachments']['quantity'] = 0;
900
901
		// If this isn't a new post, check the current attachments.
902 View Code Duplication
		if (isset($_REQUEST['msg']))
903
		{
904
			$context['attachments']['quantity'] = count($context['current_attachments']);
905
			foreach ($context['current_attachments'] as $attachment)
906
				$context['attachments']['total_size'] += $attachment['size'];
907
		}
908
909
		// A bit of house keeping first.
910 View Code Duplication
		if (!empty($_SESSION['temp_attachments']) && count($_SESSION['temp_attachments']) == 1)
911
			unset($_SESSION['temp_attachments']);
912
913
		if (!empty($_SESSION['temp_attachments']))
914
		{
915
			// Is this a request to delete them?
916
			if (isset($_GET['delete_temp']))
917
			{
918 View Code Duplication
				foreach ($_SESSION['temp_attachments'] as $attachID => $attachment)
919
				{
920
					if (strpos($attachID, 'post_tmp_' . $user_info['id']) !== false)
921
						if (file_exists($attachment['tmp_name']))
922
							unlink($attachment['tmp_name']);
923
				}
924
				$post_errors[] = 'temp_attachments_gone';
925
				$_SESSION['temp_attachments'] = array();
926
			}
927
			// Hmm, coming in fresh and there are files in session.
928
			elseif ($context['current_action'] != 'post2' || !empty($_POST['from_qr']))
929
			{
930
				// Let's be nice and see if they belong here first.
931
				if ((empty($_REQUEST['msg']) && empty($_SESSION['temp_attachments']['post']['msg']) && $_SESSION['temp_attachments']['post']['board'] == $board) || (!empty($_REQUEST['msg']) && $_SESSION['temp_attachments']['post']['msg'] == $_REQUEST['msg']))
932
				{
933
					// See if any files still exist before showing the warning message and the files attached.
934
					foreach ($_SESSION['temp_attachments'] as $attachID => $attachment)
935
					{
936
						if (strpos($attachID, 'post_tmp_' . $user_info['id']) === false)
937
							continue;
938
939
						if (file_exists($attachment['tmp_name']))
940
						{
941
							$post_errors[] = 'temp_attachments_new';
942
							$context['files_in_session_warning'] = $txt['attached_files_in_session'];
943
							unset($_SESSION['temp_attachments']['post']['files']);
944
							break;
945
						}
946
					}
947
				}
948
				else
949
				{
950
					// Since, they don't belong here. Let's inform the user that they exist..
951
					if (!empty($topic))
952
						$delete_url = $scripturl . '?action=post' . (!empty($_REQUEST['msg']) ? (';msg=' . $_REQUEST['msg']) : '') . (!empty($_REQUEST['last_msg']) ? (';last_msg=' . $_REQUEST['last_msg']) : '') . ';topic=' . $topic . ';delete_temp';
953
					else
954
						$delete_url = $scripturl . '?action=post;board=' . $board . ';delete_temp';
955
956
					// Compile a list of the files to show the user.
957
					$file_list = array();
958
					foreach ($_SESSION['temp_attachments'] as $attachID => $attachment)
959
						if (strpos($attachID, 'post_tmp_' . $user_info['id']) !== false)
960
							$file_list[] = $attachment['name'];
961
962
					$_SESSION['temp_attachments']['post']['files'] = $file_list;
963
					$file_list = '<div class="attachments">' . implode('<br>', $file_list) . '</div>';
964
965
					if (!empty($_SESSION['temp_attachments']['post']['msg']))
966
					{
967
						// We have a message id, so we can link back to the old topic they were trying to edit..
968
						$goback_url = $scripturl . '?action=post' . (!empty($_SESSION['temp_attachments']['post']['msg']) ? (';msg=' . $_SESSION['temp_attachments']['post']['msg']) : '') . (!empty($_SESSION['temp_attachments']['post']['last_msg']) ? (';last_msg=' . $_SESSION['temp_attachments']['post']['last_msg']) : '') . ';topic=' . $_SESSION['temp_attachments']['post']['topic'] . ';additionalOptions';
969
970
						$post_errors[] = array('temp_attachments_found', array($delete_url, $goback_url, $file_list));
971
						$context['ignore_temp_attachments'] = true;
972
					}
973
					else
974
					{
975
						$post_errors[] = array('temp_attachments_lost', array($delete_url, $file_list));
976
						$context['ignore_temp_attachments'] = true;
977
					}
978
				}
979
			}
980
981
			if (!empty($context['we_are_history']))
982
				$post_errors[] = $context['we_are_history'];
983
984
			foreach ($_SESSION['temp_attachments'] as $attachID => $attachment)
985
			{
986
				if (isset($context['ignore_temp_attachments']) || isset($_SESSION['temp_attachments']['post']['files']))
987
					break;
988
989 View Code Duplication
				if ($attachID != 'initial_error' && strpos($attachID, 'post_tmp_' . $user_info['id']) === false)
990
					continue;
991
992 View Code Duplication
				if ($attachID == 'initial_error')
993
				{
994
					$txt['error_attach_initial_error'] = $txt['attach_no_upload'] . '<div style="padding: 0 1em;">' . (is_array($attachment) ? vsprintf($txt[$attachment[0]], $attachment[1]) : $txt[$attachment]) . '</div>';
995
					$post_errors[] = 'attach_initial_error';
996
					unset($_SESSION['temp_attachments']);
997
					break;
998
				}
999
1000
				// Show any errors which might have occured.
1001
				if (!empty($attachment['errors']))
1002
				{
1003
					$txt['error_attach_errors'] = empty($txt['error_attach_errors']) ? '<br>' : '';
1004
					$txt['error_attach_errors'] .= vsprintf($txt['attach_warning'], $attachment['name']) . '<div style="padding: 0 1em;">';
1005
					foreach ($attachment['errors'] as $error)
1006
						$txt['error_attach_errors'] .= (is_array($error) ? vsprintf($txt[$error[0]], $error[1]) : $txt[$error]) . '<br >';
1007
					$txt['error_attach_errors'] .= '</div>';
1008
					$post_errors[] = 'attach_errors';
1009
1010
					// Take out the trash.
1011
					unset($_SESSION['temp_attachments'][$attachID]);
1012
					if (file_exists($attachment['tmp_name']))
1013
						unlink($attachment['tmp_name']);
1014
					continue;
1015
				}
1016
1017
				// More house keeping.
1018
				if (!file_exists($attachment['tmp_name']))
1019
				{
1020
					unset($_SESSION['temp_attachments'][$attachID]);
1021
					continue;
1022
				}
1023
1024
				$context['attachments']['quantity']++;
1025
				$context['attachments']['total_size'] += $attachment['size'];
1026
				if (!isset($context['files_in_session_warning']))
1027
					$context['files_in_session_warning'] = $txt['attached_files_in_session'];
1028
1029
				$context['current_attachments'][$attachID] = array(
1030
					'name' => '<u>' . $smcFunc['htmlspecialchars']($attachment['name']) . '</u>',
1031
					'size' => $attachment['size'],
1032
					'attachID' => $attachID,
1033
					'unchecked' => false,
1034
					'approved' => 1,
1035
					'mime_type' => '',
1036
					'thumb' => 0,
1037
				);
1038
			}
1039
		}
1040
	}
1041
1042
	// Do we need to show the visual verification image?
1043
	$context['require_verification'] = !$user_info['is_mod'] && !$user_info['is_admin'] && !empty($modSettings['posts_require_captcha']) && ($user_info['posts'] < $modSettings['posts_require_captcha'] || ($user_info['is_guest'] && $modSettings['posts_require_captcha'] == -1));
1044 View Code Duplication
	if ($context['require_verification'])
1045
	{
1046
		require_once($sourcedir . '/Subs-Editor.php');
1047
		$verificationOptions = array(
1048
			'id' => 'post',
1049
		);
1050
		$context['require_verification'] = create_control_verification($verificationOptions);
1051
		$context['visual_verification_id'] = $verificationOptions['id'];
1052
	}
1053
1054
	// If they came from quick reply, and have to enter verification details, give them some notice.
1055
	if (!empty($_REQUEST['from_qr']) && !empty($context['require_verification']))
1056
		$post_errors[] = 'need_qr_verification';
1057
1058
	/*
1059
	 * There are two error types: serious and minor. Serious errors
1060
	 * actually tell the user that a real error has occurred, while minor
1061
	 * errors are like warnings that let them know that something with
1062
	 * their post isn't right.
1063
	 */
1064
	$minor_errors = array('not_approved', 'new_replies', 'old_topic', 'need_qr_verification', 'no_subject', 'topic_locked', 'topic_unlocked', 'topic_stickied', 'topic_unstickied');
1065
1066
	call_integration_hook('integrate_post_errors', array(&$post_errors, &$minor_errors));
1067
1068
	// Any errors occurred?
1069
	if (!empty($post_errors))
1070
	{
1071
		loadLanguage('Errors');
1072
		$context['error_type'] = 'minor';
1073
		foreach ($post_errors as $post_error)
1074
			if (is_array($post_error))
1075
			{
1076
				$post_error_id = $post_error[0];
1077
				$context['post_error'][$post_error_id] = vsprintf($txt['error_' . $post_error_id], $post_error[1]);
1078
1079
				// If it's not a minor error flag it as such.
1080
				if (!in_array($post_error_id, $minor_errors))
1081
					$context['error_type'] = 'serious';
1082
			}
1083
			else
1084
			{
1085
				$context['post_error'][$post_error] = $txt['error_' . $post_error];
1086
1087
				// If it's not a minor error flag it as such.
1088
				if (!in_array($post_error, $minor_errors))
1089
					$context['error_type'] = 'serious';
1090
			}
1091
	}
1092
1093
	// What are you doing? Posting a poll, modifying, previewing, new post, or reply...
1094
	if (isset($_REQUEST['poll']))
1095
		$context['page_title'] = $txt['new_poll'];
1096
	elseif ($context['make_event'])
1097
		$context['page_title'] = $context['event']['id'] == -1 ? $txt['calendar_post_event'] : $txt['calendar_edit'];
1098
	elseif (isset($_REQUEST['msg']))
1099
		$context['page_title'] = $txt['modify_msg'];
1100
	elseif (isset($_REQUEST['subject'], $context['preview_subject']))
1101
		$context['page_title'] = $txt['preview'] . ' - ' . strip_tags($context['preview_subject']);
1102
	elseif (empty($topic))
1103
		$context['page_title'] = $txt['start_new_topic'];
1104
	else
1105
		$context['page_title'] = $txt['post_reply'];
1106
1107
	// Build the link tree.
1108
	if (empty($topic))
1109
		$context['linktree'][] = array(
1110
			'name' => '<em>' . $txt['start_new_topic'] . '</em>'
1111
		);
1112
	else
1113
		$context['linktree'][] = array(
1114
			'url' => $scripturl . '?topic=' . $topic . '.' . $_REQUEST['start'],
1115
			'name' => $form_subject,
1116
			'extra_before' => '<span><strong class="nav">' . $context['page_title'] . ' (</strong></span>',
1117
			'extra_after' => '<span><strong class="nav">)</strong></span>'
1118
		);
1119
1120
	$context['subject'] = addcslashes($form_subject, '"');
1121
	$context['message'] = str_replace(array('"', '<', '>', '&nbsp;'), array('&quot;', '&lt;', '&gt;', ' '), $form_message);
1122
1123
	// Are post drafts enabled?
1124
	$context['drafts_save'] = !empty($modSettings['drafts_post_enabled']) && allowedTo('post_draft');
1125
	$context['drafts_autosave'] = !empty($context['drafts_save']) && !empty($modSettings['drafts_autosave_enabled']) && allowedTo('post_autosave_draft');
1126
1127
	// Build a list of drafts that they can load in to the editor
1128
	if (!empty($context['drafts_save']))
1129
	{
1130
		require_once($sourcedir . '/Drafts.php');
1131
		ShowDrafts($user_info['id'], $topic);
1132
	}
1133
1134
	// Needed for the editor and message icons.
1135
	require_once($sourcedir . '/Subs-Editor.php');
1136
1137
	// Now create the editor.
1138
	$editorOptions = array(
1139
		'id' => 'message',
1140
		'value' => $context['message'],
1141
		'labels' => array(
1142
			'post_button' => $context['submit_label'],
1143
		),
1144
		// add height and width for the editor
1145
		'height' => '275px',
1146
		'width' => '100%',
1147
		// We do XML preview here.
1148
		'preview_type' => 2,
1149
		'required' => true,
1150
	);
1151
	create_control_richedit($editorOptions);
1152
1153
	// Store the ID.
1154
	$context['post_box_name'] = $editorOptions['id'];
1155
1156
	$context['attached'] = '';
1157
	$context['make_poll'] = isset($_REQUEST['poll']);
1158
1159
	// Message icons - customized icons are off?
1160
	$context['icons'] = getMessageIcons($board);
1161
1162 View Code Duplication
	if (!empty($context['icons']))
1163
		$context['icons'][count($context['icons']) - 1]['is_last'] = true;
1164
1165
	// Are we starting a poll? if set the poll icon as selected if its available
1166
	if (isset($_REQUEST['poll']))
1167
	{
1168
		foreach ($context['icons'] as $icons)
1169
		{
1170
			if (isset($icons['value']) && $icons['value'] == 'poll')
1171
			{
1172
				// if found we are done
1173
				$context['icon'] = 'poll';
1174
				break;
1175
			}
1176
		}
1177
	}
1178
1179
	$context['icon_url'] = '';
1180
	for ($i = 0, $n = count($context['icons']); $i < $n; $i++)
1181
	{
1182
		$context['icons'][$i]['selected'] = $context['icon'] == $context['icons'][$i]['value'];
1183
		if ($context['icons'][$i]['selected'])
1184
			$context['icon_url'] = $context['icons'][$i]['url'];
1185
	}
1186
	if (empty($context['icon_url']))
1187
	{
1188
		$context['icon_url'] = $settings[file_exists($settings['theme_dir'] . '/images/post/' . $context['icon'] . '.png') ? 'images_url' : 'default_images_url'] . '/post/' . $context['icon'] . '.png';
1189
		array_unshift($context['icons'], array(
1190
			'value' => $context['icon'],
1191
			'name' => $txt['current_icon'],
1192
			'url' => $context['icon_url'],
1193
			'is_last' => empty($context['icons']),
1194
			'selected' => true,
1195
		));
1196
	}
1197
1198
	if (!empty($topic) && !empty($modSettings['topicSummaryPosts']))
1199
		getTopic();
1200
1201
	// If the user can post attachments prepare the warning labels.
1202
	if ($context['can_post_attachment'])
1203
	{
1204
		// If they've unchecked an attachment, they may still want to attach that many more files, but don't allow more than num_allowed_attachments.
1205
		$context['num_allowed_attachments'] = empty($modSettings['attachmentNumPerPostLimit']) ? 50 : min($modSettings['attachmentNumPerPostLimit'] - count($context['current_attachments']), $modSettings['attachmentNumPerPostLimit']);
1206
		$context['can_post_attachment_unapproved'] = allowedTo('post_attachment');
1207
		$context['attachment_restrictions'] = array();
1208
		$context['allowed_extensions'] = strtr(strtolower($modSettings['attachmentExtensions']), array(',' => ', '));
1209
		$attachmentRestrictionTypes = array('attachmentNumPerPostLimit', 'attachmentPostLimit', 'attachmentSizeLimit');
1210
		foreach ($attachmentRestrictionTypes as $type)
1211
			if (!empty($modSettings[$type]))
1212
			{
1213
				// Show the max number of attachments if not 0.
1214
				if ($type == 'attachmentNumPerPostLimit')
1215
					$context['attachment_restrictions'][] = sprintf($txt['attach_remaining'], $modSettings['attachmentNumPerPostLimit'] - $context['attachments']['quantity']);
1216
			}
1217
	}
1218
1219
	$context['back_to_topic'] = isset($_REQUEST['goback']) || (isset($_REQUEST['msg']) && !isset($_REQUEST['subject']));
1220
	$context['show_additional_options'] = !empty($_POST['additional_options']) || isset($_SESSION['temp_attachments']['post']) || isset($_GET['additionalOptions']);
1221
1222
	$context['is_new_topic'] = empty($topic);
1223
	$context['is_new_post'] = !isset($_REQUEST['msg']);
1224
	$context['is_first_post'] = $context['is_new_topic'] || (isset($_REQUEST['msg']) && $_REQUEST['msg'] == $id_first_msg);
0 ignored issues
show
Bug introduced by
The variable $id_first_msg 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...
1225
1226
	// WYSIWYG only works if BBC is enabled
1227
	$modSettings['disable_wysiwyg'] = !empty($modSettings['disable_wysiwyg']) || empty($modSettings['enableBBC']);
1228
1229
	// Register this form in the session variables.
1230
	checkSubmitOnce('register');
1231
1232
	// Mentions
1233 View Code Duplication
	if (!empty($modSettings['enable_mentions']) && allowedTo('mention'))
1234
	{
1235
		loadJavaScriptFile('jquery.caret.min.js', array('defer' => true), 'smf_caret');
1236
		loadJavaScriptFile('jquery.atwho.min.js', array('defer' => true), 'smf_atwho');
1237
		loadJavaScriptFile('mentions.js', array('defer' => true), 'smf_mentions');
1238
	}
1239
1240
	// quotedText.js
1241
	loadJavaScriptFile('quotedText.js', array('defer' => true), 'smf_quotedText');
1242
1243
	// Mock files to show already attached files.
1244
	addInlineJavaScript('
1245
	var current_attachments = [];', true);
1246
1247
	if (!empty($context['current_attachments']))
1248
	{
1249
		foreach ($context['current_attachments'] as $key => $mock)
1250
			addInlineJavaScript('
1251
	current_attachments.push({
1252
		name: '. JavaScriptEscape($mock['name']) . ',
1253
		size: '. $mock['size'] . ',
1254
		attachID: '. $mock['attachID'] . ',
1255
		approved: '. $mock['approved'] . ',
1256
		type: '. JavaScriptEscape(!empty($mock['mime_type']) ? $mock['mime_type'] : '') . ',
1257
		thumbID: '. (!empty($mock['thumb']) ? $mock['thumb'] : 0) . '
1258
	});', true);
1259
	}
1260
1261
	// File Upload.
1262
	if ($context['can_post_attachment'])
1263
	{
1264
		$acceptedFiles = implode(',', array_map(function($val) use($smcFunc) { return '.' . $smcFunc['htmltrim']($val); } , explode(',', $context['allowed_extensions'])));
0 ignored issues
show
Coding Style introduced by
It is generally recommended to place each PHP statement on a line by itself.

Let’s take a look at an example:

// Bad
$a = 5; $b = 6; $c = 7;

// Good
$a = 5;
$b = 6;
$c = 7;
Loading history...
1265
1266
		loadJavaScriptFile('dropzone.min.js', array('defer' => true), 'smf_dropzone');
1267
		loadJavaScriptFile('smf_fileUpload.js', array('defer' => true), 'smf_fileUpload');
1268
		addInlineJavaScript('
1269
	$(function() {
1270
		smf_fileUpload({
1271
			dictDefaultMessage : '. JavaScriptEscape($txt['attach_drop_zone']) . ',
1272
			dictFallbackMessage : '. JavaScriptEscape($txt['attach_drop_zone_no']) . ',
1273
			dictCancelUpload : '. JavaScriptEscape($txt['modify_cancel']) . ',
1274
			genericError: '. JavaScriptEscape($txt['attach_php_error']) . ',
1275
			text_attachLeft: '. JavaScriptEscape($txt['attached_attachedLeft']) . ',
1276
			text_deleteAttach: '. JavaScriptEscape($txt['attached_file_delete']) . ',
1277
			text_attachDeleted: '. JavaScriptEscape($txt['attached_file_deleted']) . ',
1278
			text_insertBBC: '. JavaScriptEscape($txt['attached_insertBBC']) . ',
1279
			text_attachUploaded: '. JavaScriptEscape($txt['attached_file_uploaded']) . ',
1280
			text_attach_unlimited: '. JavaScriptEscape($txt['attach_drop_unlimited']) . ',
1281
			text_totalMaxSize: '. JavaScriptEscape($txt['attach_max_total_file_size_current']) . ',
1282
			text_max_size_progress: '. JavaScriptEscape($txt['attach_max_size_progress']) . ',
1283
			dictMaxFilesExceeded: '. JavaScriptEscape($txt['more_attachments_error']) . ',
1284
			dictInvalidFileType: '. JavaScriptEscape(sprintf($txt['cant_upload_type'], $context['allowed_extensions'])) . ',
1285
			dictFileTooBig: '. JavaScriptEscape(sprintf($txt['file_too_big'], comma_format($modSettings['attachmentSizeLimit'], 0))) . ',
1286
			acceptedFiles: '. JavaScriptEscape($acceptedFiles) . ',
1287
			thumbnailWidth: '.(!empty($modSettings['attachmentThumbWidth']) ? $modSettings['attachmentThumbWidth'] : 'null') . ',
1288
			thumbnailHeight: '.(!empty($modSettings['attachmentThumbHeight']) ? $modSettings['attachmentThumbHeight'] : 'null') . ',
1289
			limitMultiFileUploadSize:'. round(max($modSettings['attachmentPostLimit'] - ($context['attachments']['total_size'] / 1024), 0)) * 1024 . ',
1290
			maxFileAmount: '. (!empty($context['num_allowed_attachments']) ? $context['num_allowed_attachments'] : 'null') . ',
1291
			maxTotalSize: ' . (!empty($modSettings['attachmentPostLimit']) ? $modSettings['attachmentPostLimit'] : '0') . ',
1292
			maxFileSize: '. (!empty($modSettings['attachmentSizeLimit']) ? $modSettings['attachmentSizeLimit'] : '0') . ',
1293
		});
1294
	});', true);
1295
	}
1296
1297
	// Knowing the current board ID might be handy.
1298
	addInlineJavaScript('
1299
	var current_board = '. (empty($context['current_board']) ? 'null' : $context['current_board']) . ';', false);
1300
1301
	// Now let's set up the fields for the posting form header...
1302
	$context['posting_fields'] = array();
1303
1304
	// Guests must supply their name and email.
1305
	if (isset($context['name']) && isset($context['email']))
1306
	{
1307
		$context['posting_fields']['guestname'] = array(
1308
			'dt' => '<span id="caption_guestname"' .  (isset($context['post_error']['long_name']) || isset($context['post_error']['no_name']) || isset($context['post_error']['bad_name']) ? ' class="error"' : '') . '>' . $txt['name'] . '</span>',
1309
			'dd' => '<input type="text" name="guestname" size="25" value="' . $context['name'] . '" class="input_text" required>',
1310
		);
1311
1312
		if (empty($modSettings['guest_post_no_email']))
1313
		{
1314
			$context['posting_fields']['email'] = array(
1315
				'dt' => '<span id="caption_email"' .  (isset($context['post_error']['no_email']) || isset($context['post_error']['bad_email']) ? ' class="error"' : '') . '>' . $txt['email'] . '</span>',
1316
				'dd' => '<input type="email" name="email" size="25" value="' . $context['email'] . '" class="input_text" required>',
1317
			);
1318
		}
1319
	}
1320
1321
	// Gotta have a subject.
1322
	$context['posting_fields']['subject'] = array(
1323
		'dt' => '<span id="caption_subject"' . (isset($context['post_error']['no_subject']) ? ' class="error"' : '') . '>' . $txt['subject'] . '</span>',
1324
		'dd' => '<input type="text" name="subject" value="' . $context['subject'] . '" size="80" maxlength="80" class="input_text" required>',
1325
	);
1326
1327
	// Icons are fun.
1328
	$context['posting_fields']['icon'] = array(
1329
		'dt' => $txt['message_icon'],
1330
		'dd' => '<select name="icon" id="icon" onchange="showimage()">',
1331
	);
1332
	foreach ($context['icons'] as $icon)
1333
	{
1334
		$context['posting_fields']['icon']['dd'] .= '
1335
							<option value="' . $icon['value'] . '"' . ($icon['value'] == $context['icon'] ? ' selected' : '') . '>' . $icon['name'] . '</option>';
1336
	}
1337
	$context['posting_fields']['icon']['dd'] .= '
1338
						</select>
1339
						<img id="icons" src="' . $context['icon_url'] . '">';
1340
1341
1342
	// Finally, load the template.
1343
	if (!isset($_REQUEST['xml']))
1344
		loadTemplate('Post');
1345
1346
	call_integration_hook('integrate_post_end');
1347
}
1348
1349
/**
1350
 * Posts or saves the message composed with Post().
1351
 *
1352
 * requires various permissions depending on the action.
1353
 * handles attachment, post, and calendar saving.
1354
 * sends off notifications, and allows for announcements and moderation.
1355
 * accessed from ?action=post2.
1356
 */
1357
function Post2()
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
1358
{
1359
	global $board, $topic, $txt, $modSettings, $sourcedir, $context;
1360
	global $user_info, $board_info, $smcFunc, $settings;
1361
1362
	// Sneaking off, are we?
1363
	if (empty($_POST) && empty($topic))
1364
	{
1365
		if (empty($_SERVER['CONTENT_LENGTH']))
1366
			redirectexit('action=post;board=' . $board . '.0');
1367
		else
1368
			fatal_lang_error('post_upload_error', false);
1369
	}
1370
	elseif (empty($_POST) && !empty($topic))
1371
		redirectexit('action=post;topic=' . $topic . '.0');
1372
1373
	// No need!
1374
	$context['robot_no_index'] = true;
1375
1376
	// Prevent double submission of this form.
1377
	checkSubmitOnce('check');
1378
1379
	// No errors as yet.
1380
	$post_errors = array();
1381
1382
	// If the session has timed out, let the user re-submit their form.
1383
	if (checkSession('post', '', false) != '')
1384
		$post_errors[] = 'session_timeout';
1385
1386
	// Wrong verification code?
1387
	if (!$user_info['is_admin'] && !$user_info['is_mod'] && !empty($modSettings['posts_require_captcha']) && ($user_info['posts'] < $modSettings['posts_require_captcha'] || ($user_info['is_guest'] && $modSettings['posts_require_captcha'] == -1)))
1388
	{
1389
		require_once($sourcedir . '/Subs-Editor.php');
1390
		$verificationOptions = array(
1391
			'id' => 'post',
1392
		);
1393
		$context['require_verification'] = create_control_verification($verificationOptions, true);
1394
		if (is_array($context['require_verification']))
1395
			$post_errors = array_merge($post_errors, $context['require_verification']);
1396
	}
1397
1398
	require_once($sourcedir . '/Subs-Post.php');
1399
	loadLanguage('Post');
1400
1401
	// Drafts enabled and needed?
1402
	if (!empty($modSettings['drafts_post_enabled']) && (isset($_POST['save_draft']) || isset($_POST['id_draft'])))
1403
		require_once($sourcedir . '/Drafts.php');
1404
1405
	// First check to see if they are trying to delete any current attachments.
1406
	if (isset($_POST['attach_del']))
1407
	{
1408
		$keep_temp = array();
1409
		$keep_ids = array();
1410
		foreach ($_POST['attach_del'] as $dummy)
1411
			if (strpos($dummy, 'post_tmp_' . $user_info['id']) !== false)
1412
				$keep_temp[] = $dummy;
1413
			else
1414
				$keep_ids[] = (int) $dummy;
1415
1416
		if (isset($_SESSION['temp_attachments']))
1417
			foreach ($_SESSION['temp_attachments'] as $attachID => $attachment)
1418
			{
1419
				if ((isset($_SESSION['temp_attachments']['post']['files'], $attachment['name']) && in_array($attachment['name'], $_SESSION['temp_attachments']['post']['files'])) || in_array($attachID, $keep_temp) || strpos($attachID, 'post_tmp_' . $user_info['id']) === false)
1420
					continue;
1421
1422
				unset($_SESSION['temp_attachments'][$attachID]);
1423
				unlink($attachment['tmp_name']);
1424
			}
1425
1426
		if (!empty($_REQUEST['msg']))
1427
		{
1428
			require_once($sourcedir . '/ManageAttachments.php');
1429
			$attachmentQuery = array(
1430
				'attachment_type' => 0,
1431
				'id_msg' => (int) $_REQUEST['msg'],
1432
				'not_id_attach' => $keep_ids,
1433
			);
1434
			removeAttachments($attachmentQuery);
1435
		}
1436
	}
1437
1438
	// Then try to upload any attachments.
1439
	$context['can_post_attachment'] = !empty($modSettings['attachmentEnable']) && $modSettings['attachmentEnable'] == 1 && (allowedTo('post_attachment') || ($modSettings['postmod_active'] && allowedTo('post_unapproved_attachments')));
1440
	if ($context['can_post_attachment'] && empty($_POST['from_qr']))
1441
	{
1442
		require_once($sourcedir . '/Subs-Attachments.php');
1443
		processAttachments();
1444
	}
1445
1446
	// If this isn't a new topic load the topic info that we need.
1447
	if (!empty($topic))
1448
	{
1449
		$request = $smcFunc['db_query']('', '
1450
			SELECT locked, is_sticky, id_poll, approved, id_first_msg, id_last_msg, id_member_started, id_board
1451
			FROM {db_prefix}topics
1452
			WHERE id_topic = {int:current_topic}
1453
			LIMIT 1',
1454
			array(
1455
				'current_topic' => $topic,
1456
			)
1457
		);
1458
		$topic_info = $smcFunc['db_fetch_assoc']($request);
1459
		$smcFunc['db_free_result']($request);
1460
1461
		// Though the topic should be there, it might have vanished.
1462
		if (!is_array($topic_info))
1463
			fatal_lang_error('topic_doesnt_exist', 404);
1464
1465
		// Did this topic suddenly move? Just checking...
1466
		if ($topic_info['id_board'] != $board)
1467
			fatal_lang_error('not_a_topic');
1468
	}
1469
1470
	// Replying to a topic?
1471
	if (!empty($topic) && !isset($_REQUEST['msg']))
1472
	{
1473
		// Don't allow a post if it's locked.
1474 View Code Duplication
		if ($topic_info['locked'] != 0 && !allowedTo('moderate_board'))
0 ignored issues
show
Bug introduced by
The variable $topic_info 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...
1475
			fatal_lang_error('topic_locked', false);
1476
1477
		// Sorry, multiple polls aren't allowed... yet.  You should stop giving me ideas :P.
1478
		if (isset($_REQUEST['poll']) && $topic_info['id_poll'] > 0)
1479
			unset($_REQUEST['poll']);
1480
1481
		// Do the permissions and approval stuff...
1482
		$becomesApproved = true;
1483
		$topicAndMessageBothUnapproved = false;
1484
1485
		// If the topic is unapproved the message automatically becomes unapproved too.
1486
		if (empty($topic_info['approved']))
1487
		{
1488
			$becomesApproved = false;
1489
1490
			// camelCase fan much? :P
1491
			$topicAndMessageBothUnapproved = true;
1492
1493
			// Set a nice session var...
1494
			$_SESSION['becomesUnapproved'] = true;
1495
		}
1496
1497
		elseif ($topic_info['id_member_started'] != $user_info['id'])
1498
		{
1499
			if ($modSettings['postmod_active'] && allowedTo('post_unapproved_replies_any') && !allowedTo('post_reply_any'))
1500
				$becomesApproved = false;
1501
1502
			else
1503
				isAllowedTo('post_reply_any');
1504
		}
1505
		elseif (!allowedTo('post_reply_any'))
1506
		{
1507
			if ($modSettings['postmod_active'] && allowedTo('post_unapproved_replies_own') && !allowedTo('post_reply_own'))
1508
				$becomesApproved = false;
1509
1510
			else
1511
				isAllowedTo('post_reply_own');
1512
		}
1513
1514 View Code Duplication
		if (isset($_POST['lock']))
1515
		{
1516
			// Nothing is changed to the lock.
1517
			if ((empty($topic_info['locked']) && empty($_POST['lock'])) || (!empty($_POST['lock']) && !empty($topic_info['locked'])))
1518
				unset($_POST['lock']);
1519
1520
			// You're have no permission to lock this topic.
1521
			elseif (!allowedTo(array('lock_any', 'lock_own')) || (!allowedTo('lock_any') && $user_info['id'] != $topic_info['id_member_started']))
1522
				unset($_POST['lock']);
1523
1524
			// You are allowed to (un)lock your own topic only.
1525
			elseif (!allowedTo('lock_any'))
1526
			{
1527
				// You cannot override a moderator lock.
1528
				if ($topic_info['locked'] == 1)
1529
					unset($_POST['lock']);
1530
1531
				else
1532
					$_POST['lock'] = empty($_POST['lock']) ? 0 : 2;
1533
			}
1534
			// Hail mighty moderator, (un)lock this topic immediately.
1535
			else
1536
			{
1537
				$_POST['lock'] = empty($_POST['lock']) ? 0 : 1;
1538
1539
				// Did someone (un)lock this while you were posting?
1540
				if (isset($_POST['already_locked']) && $_POST['already_locked'] != $topic_info['locked'])
1541
					$post_errors[] = 'topic_' . (empty($topic_info['locked']) ? 'un' : '') . 'locked';
1542
			}
1543
		}
1544
1545
		// So you wanna (un)sticky this...let's see.
1546 View Code Duplication
		if (isset($_POST['sticky']) && ($_POST['sticky'] == $topic_info['is_sticky'] || !allowedTo('make_sticky')))
1547
			unset($_POST['sticky']);
1548
		elseif (isset($_POST['sticky']))
1549
		{
1550
			// Did someone (un)sticky this while you were posting?
1551
			if (isset($_POST['already_sticky']) && $_POST['already_sticky'] != $topic_info['is_sticky'])
1552
				$post_errors[] = 'topic_' . (empty($topic_info['is_sticky']) ? 'un' : '') . 'sticky';
1553
		}
1554
1555
		// If drafts are enabled, then pass this off
1556 View Code Duplication
		if (!empty($modSettings['drafts_post_enabled']) && isset($_POST['save_draft']))
1557
		{
1558
			SaveDraft($post_errors);
1559
			return Post();
1560
		}
1561
1562
		// If the number of replies has changed, if the setting is enabled, go back to Post() - which handles the error.
1563
		if (isset($_POST['last_msg']) && $topic_info['id_last_msg'] > $_POST['last_msg'])
1564
		{
1565
			$_REQUEST['preview'] = true;
1566
			return Post();
1567
		}
1568
1569
		$posterIsGuest = $user_info['is_guest'];
1570
	}
1571
	// Posting a new topic.
1572
	elseif (empty($topic))
1573
	{
1574
		// Now don't be silly, new topics will get their own id_msg soon enough.
1575
		unset($_REQUEST['msg'], $_POST['msg'], $_GET['msg']);
1576
1577
		// Do like, the permissions, for safety and stuff...
1578
		$becomesApproved = true;
1579
		if ($modSettings['postmod_active'] && !allowedTo('post_new') && allowedTo('post_unapproved_topics'))
1580
			$becomesApproved = false;
1581
		else
1582
			isAllowedTo('post_new');
1583
1584
		if (isset($_POST['lock']))
1585
		{
1586
			// New topics are by default not locked.
1587
			if (empty($_POST['lock']))
1588
				unset($_POST['lock']);
1589
			// Besides, you need permission.
1590
			elseif (!allowedTo(array('lock_any', 'lock_own')))
1591
				unset($_POST['lock']);
1592
			// A moderator-lock (1) can override a user-lock (2).
1593
			else
1594
				$_POST['lock'] = allowedTo('lock_any') ? 1 : 2;
1595
		}
1596
1597
		if (isset($_POST['sticky']) && (empty($_POST['sticky']) || !allowedTo('make_sticky')))
1598
			unset($_POST['sticky']);
1599
1600
		// Saving your new topic as a draft first?
1601 View Code Duplication
		if (!empty($modSettings['drafts_post_enabled']) && isset($_POST['save_draft']))
1602
		{
1603
			SaveDraft($post_errors);
1604
			return Post();
1605
		}
1606
1607
		$posterIsGuest = $user_info['is_guest'];
1608
	}
1609
	// Modifying an existing message?
1610
	elseif (isset($_REQUEST['msg']) && !empty($topic))
1611
	{
1612
		$_REQUEST['msg'] = (int) $_REQUEST['msg'];
1613
1614
		$request = $smcFunc['db_query']('', '
1615
			SELECT id_member, poster_name, poster_email, poster_time, approved
1616
			FROM {db_prefix}messages
1617
			WHERE id_msg = {int:id_msg}
1618
			LIMIT 1',
1619
			array(
1620
				'id_msg' => $_REQUEST['msg'],
1621
			)
1622
		);
1623
		if ($smcFunc['db_num_rows']($request) == 0)
1624
			fatal_lang_error('cant_find_messages', false);
1625
		$row = $smcFunc['db_fetch_assoc']($request);
1626
		$smcFunc['db_free_result']($request);
1627
1628 View Code Duplication
		if (!empty($topic_info['locked']) && !allowedTo('moderate_board'))
1629
			fatal_lang_error('topic_locked', false);
1630
1631 View Code Duplication
		if (isset($_POST['lock']))
1632
		{
1633
			// Nothing changes to the lock status.
1634
			if ((empty($_POST['lock']) && empty($topic_info['locked'])) || (!empty($_POST['lock']) && !empty($topic_info['locked'])))
1635
				unset($_POST['lock']);
1636
			// You're simply not allowed to (un)lock this.
1637
			elseif (!allowedTo(array('lock_any', 'lock_own')) || (!allowedTo('lock_any') && $user_info['id'] != $topic_info['id_member_started']))
1638
				unset($_POST['lock']);
1639
			// You're only allowed to lock your own topics.
1640
			elseif (!allowedTo('lock_any'))
1641
			{
1642
				// You're not allowed to break a moderator's lock.
1643
				if ($topic_info['locked'] == 1)
1644
					unset($_POST['lock']);
1645
				// Lock it with a soft lock or unlock it.
1646
				else
1647
					$_POST['lock'] = empty($_POST['lock']) ? 0 : 2;
1648
			}
1649
			// You must be the moderator.
1650
			else
1651
			{
1652
				$_POST['lock'] = empty($_POST['lock']) ? 0 : 1;
1653
1654
				// Did someone (un)lock this while you were posting?
1655
				if (isset($_POST['already_locked']) && $_POST['already_locked'] != $topic_info['locked'])
1656
					$post_errors[] = 'topic_' . (empty($topic_info['locked']) ? 'un' : '') . 'locked';
1657
			}
1658
		}
1659
1660
		// Change the sticky status of this topic?
1661 View Code Duplication
		if (isset($_POST['sticky']) && (!allowedTo('make_sticky') || $_POST['sticky'] == $topic_info['is_sticky']))
1662
			unset($_POST['sticky']);
1663
		elseif (isset($_POST['sticky']))
1664
		{
1665
			// Did someone (un)sticky this while you were posting?
1666
			if (isset($_POST['already_sticky']) && $_POST['already_sticky'] != $topic_info['is_sticky'])
1667
				$post_errors[] = 'topic_' . (empty($topic_info['locked']) ? 'un' : '') . 'stickied';
1668
		}
1669
1670
		if ($row['id_member'] == $user_info['id'] && !allowedTo('modify_any'))
1671
		{
1672
			if ((!$modSettings['postmod_active'] || $row['approved']) && !empty($modSettings['edit_disable_time']) && $row['poster_time'] + ($modSettings['edit_disable_time'] + 5) * 60 < time())
1673
				fatal_lang_error('modify_post_time_passed', false);
1674
			elseif ($topic_info['id_member_started'] == $user_info['id'] && !allowedTo('modify_own'))
1675
				isAllowedTo('modify_replies');
1676
			else
1677
				isAllowedTo('modify_own');
1678
		}
1679
		elseif ($topic_info['id_member_started'] == $user_info['id'] && !allowedTo('modify_any'))
1680
		{
1681
			isAllowedTo('modify_replies');
1682
1683
			// If you're modifying a reply, I say it better be logged...
1684
			$moderationAction = true;
1685
		}
1686
		else
1687
		{
1688
			isAllowedTo('modify_any');
1689
1690
			// Log it, assuming you're not modifying your own post.
1691
			if ($row['id_member'] != $user_info['id'])
1692
				$moderationAction = true;
1693
		}
1694
1695
		// If drafts are enabled, then lets send this off to save
1696 View Code Duplication
		if (!empty($modSettings['drafts_post_enabled']) && isset($_POST['save_draft']))
1697
		{
1698
			SaveDraft($post_errors);
1699
			return Post();
1700
		}
1701
1702
		$posterIsGuest = empty($row['id_member']);
1703
1704
		// Can they approve it?
1705
		$can_approve = allowedTo('approve_posts');
1706
		$becomesApproved = $modSettings['postmod_active'] ? ($can_approve && !$row['approved'] ? (!empty($_REQUEST['approve']) ? 1 : 0) : $row['approved']) : 1;
1707
		$approve_has_changed = $row['approved'] != $becomesApproved;
1708
1709
		if (!allowedTo('moderate_forum') || !$posterIsGuest)
1710
		{
1711
			$_POST['guestname'] = $row['poster_name'];
1712
			$_POST['email'] = $row['poster_email'];
1713
		}
1714
	}
1715
1716
	// In case we want to override but still respect the unapproved topic rule.
1717
	if (allowedTo('approve_posts') && empty($topicAndMessageBothUnapproved))
1718
	{
1719
		$becomesApproved = !isset($_REQUEST['approve']) || !empty($_REQUEST['approve']) ? 1 : 0;
1720
		$approve_has_changed = isset($row['approved']) ? $row['approved'] != $becomesApproved : false;
1721
	}
1722
1723
	// If the poster is a guest evaluate the legality of name and email.
1724
	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...
1725
	{
1726
		$_POST['guestname'] = !isset($_POST['guestname']) ? '' : trim($_POST['guestname']);
1727
		$_POST['email'] = !isset($_POST['email']) ? '' : trim($_POST['email']);
1728
1729
		if ($_POST['guestname'] == '' || $_POST['guestname'] == '_')
1730
			$post_errors[] = 'no_name';
1731
		if ($smcFunc['strlen']($_POST['guestname']) > 25)
1732
			$post_errors[] = 'long_name';
1733
1734
		if (empty($modSettings['guest_post_no_email']))
1735
		{
1736
			// Only check if they changed it!
1737
			if (!isset($row) || $row['poster_email'] != $_POST['email'])
1738
			{
1739
				if (!allowedTo('moderate_forum') && (!isset($_POST['email']) || $_POST['email'] == ''))
1740
					$post_errors[] = 'no_email';
1741
				if (!allowedTo('moderate_forum') && !filter_var($_POST['email'], FILTER_VALIDATE_EMAIL))
1742
					$post_errors[] = 'bad_email';
1743
			}
1744
1745
			// Now make sure this email address is not banned from posting.
1746
			isBannedEmail($_POST['email'], 'cannot_post', sprintf($txt['you_are_post_banned'], $txt['guest_title']));
1747
		}
1748
1749
		// In case they are making multiple posts this visit, help them along by storing their name.
1750
		if (empty($post_errors))
1751
		{
1752
			$_SESSION['guest_name'] = $_POST['guestname'];
1753
			$_SESSION['guest_email'] = $_POST['email'];
1754
		}
1755
	}
1756
1757
	// Coming from the quickReply?
1758
	if (isset($_POST['quickReply']))
1759
		$_POST['message'] = $_POST['quickReply'];
1760
1761
	// Check the subject and message.
1762
	if (!isset($_POST['subject']) || $smcFunc['htmltrim']($smcFunc['htmlspecialchars']($_POST['subject'])) === '')
1763
		$post_errors[] = 'no_subject';
1764
	if (!isset($_POST['message']) || $smcFunc['htmltrim']($smcFunc['htmlspecialchars']($_POST['message']), ENT_QUOTES) === '')
1765
		$post_errors[] = 'no_message';
1766
	elseif (!empty($modSettings['max_messageLength']) && $smcFunc['strlen']($_POST['message']) > $modSettings['max_messageLength'])
1767
		$post_errors[] = array('long_message', array($modSettings['max_messageLength']));
1768
	else
1769
	{
1770
		// Prepare the message a bit for some additional testing.
1771
		$_POST['message'] = $smcFunc['htmlspecialchars']($_POST['message'], ENT_QUOTES);
1772
1773
		// Preparse code. (Zef)
1774
		if ($user_info['is_guest'])
1775
			$user_info['name'] = $_POST['guestname'];
1776
		preparsecode($_POST['message']);
1777
1778
		// Let's see if there's still some content left without the tags.
1779 View Code Duplication
		if ($smcFunc['htmltrim'](strip_tags(parse_bbc($_POST['message'], false), implode('', $context['allowed_html_tags']))) === '' && (!allowedTo('admin_forum') || strpos($_POST['message'], '[html]') === false))
1780
			$post_errors[] = 'no_message';
1781
	}
1782
	if (isset($_POST['calendar']) && !isset($_REQUEST['deleteevent']) && $smcFunc['htmltrim']($_POST['evtitle']) === '')
1783
		$post_errors[] = 'no_event';
1784
	// You are not!
1785
	if (isset($_POST['message']) && strtolower($_POST['message']) == 'i am the administrator.' && !$user_info['is_admin'])
1786
		fatal_error('Knave! Masquerader! Charlatan!', false);
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string.

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...
1787
1788
	// Validate the poll...
1789
	if (isset($_REQUEST['poll']) && $modSettings['pollMode'] == '1')
1790
	{
1791
		if (!empty($topic) && !isset($_REQUEST['msg']))
1792
			fatal_lang_error('no_access', false);
1793
1794
		// This is a new topic... so it's a new poll.
1795 View Code Duplication
		if (empty($topic))
1796
			isAllowedTo('poll_post');
1797
		// Can you add to your own topics?
1798
		elseif ($user_info['id'] == $topic_info['id_member_started'] && !allowedTo('poll_add_any'))
1799
			isAllowedTo('poll_add_own');
1800
		// Can you add polls to any topic, then?
1801
		else
1802
			isAllowedTo('poll_add_any');
1803
1804
		if (!isset($_POST['question']) || trim($_POST['question']) == '')
1805
			$post_errors[] = 'no_question';
1806
1807
		$_POST['options'] = empty($_POST['options']) ? array() : htmltrim__recursive($_POST['options']);
1808
1809
		// Get rid of empty ones.
1810
		foreach ($_POST['options'] as $k => $option)
0 ignored issues
show
Bug introduced by
The expression $_POST['options'] of type array|string is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
1811
			if ($option == '')
1812
				unset($_POST['options'][$k], $_POST['options'][$k]);
1813
1814
		// What are you going to vote between with one choice?!?
1815
		if (count($_POST['options']) < 2)
1816
			$post_errors[] = 'poll_few';
1817
		elseif (count($_POST['options']) > 256)
1818
			$post_errors[] = 'poll_many';
1819
	}
1820
1821
	if ($posterIsGuest)
1822
	{
1823
		// If user is a guest, make sure the chosen name isn't taken.
1824
		require_once($sourcedir . '/Subs-Members.php');
1825
		if (isReservedName($_POST['guestname'], 0, true, false) && (!isset($row['poster_name']) || $_POST['guestname'] != $row['poster_name']))
0 ignored issues
show
Bug introduced by
The variable $row 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...
1826
			$post_errors[] = 'bad_name';
1827
	}
1828
	// If the user isn't a guest, get his or her name and email.
1829
	elseif (!isset($_REQUEST['msg']))
1830
	{
1831
		$_POST['guestname'] = $user_info['username'];
1832
		$_POST['email'] = $user_info['email'];
1833
	}
1834
1835
	// Any mistakes?
1836
	if (!empty($post_errors))
1837
	{
1838
		// Previewing.
1839
		$_REQUEST['preview'] = true;
1840
1841
		return Post($post_errors);
1842
	}
1843
1844
	// Previewing? Go back to start.
1845
	if (isset($_REQUEST['preview']))
1846
	{
1847
		if (checkSession('post', '', false) != '')
1848
		{
1849
			loadLanguage('Errors');
1850
			$post_errors[] = 'session_timeout';
1851
			unset ($_POST['preview'], $_REQUEST['xml']); // just in case
1852
		}
1853
		return Post($post_errors);
1854
	}
1855
1856
	// Make sure the user isn't spamming the board.
1857
	if (!isset($_REQUEST['msg']))
1858
		spamProtection('post');
1859
1860
	// At about this point, we're posting and that's that.
1861
	ignore_user_abort(true);
1862
	@set_time_limit(300);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1863
1864
	// Add special html entities to the subject, name, and email.
1865
	$_POST['subject'] = strtr($smcFunc['htmlspecialchars']($_POST['subject']), array("\r" => '', "\n" => '', "\t" => ''));
1866
	$_POST['guestname'] = $smcFunc['htmlspecialchars']($_POST['guestname']);
1867
	$_POST['email'] = $smcFunc['htmlspecialchars']($_POST['email']);
1868
	$_POST['modify_reason'] = empty($_POST['modify_reason']) ? '' : strtr($smcFunc['htmlspecialchars']($_POST['modify_reason']), array("\r" => '', "\n" => '', "\t" => ''));
1869
1870
	// At this point, we want to make sure the subject isn't too long.
1871 View Code Duplication
	if ($smcFunc['strlen']($_POST['subject']) > 100)
1872
		$_POST['subject'] = $smcFunc['substr']($_POST['subject'], 0, 100);
1873
1874
	// Same with the "why did you edit this" text.
1875 View Code Duplication
	if ($smcFunc['strlen']($_POST['modify_reason']) > 100)
1876
		$_POST['modify_reason'] = $smcFunc['substr']($_POST['modify_reason'], 0, 100);
1877
1878
	// Make the poll...
1879
	if (isset($_REQUEST['poll']))
1880
	{
1881
		// Make sure that the user has not entered a ridiculous number of options..
1882
		if (empty($_POST['poll_max_votes']) || $_POST['poll_max_votes'] <= 0)
1883
			$_POST['poll_max_votes'] = 1;
1884
		elseif ($_POST['poll_max_votes'] > count($_POST['options']))
1885
			$_POST['poll_max_votes'] = count($_POST['options']);
1886
		else
1887
			$_POST['poll_max_votes'] = (int) $_POST['poll_max_votes'];
1888
1889
		$_POST['poll_expire'] = (int) $_POST['poll_expire'];
1890
		$_POST['poll_expire'] = $_POST['poll_expire'] > 9999 ? 9999 : ($_POST['poll_expire'] < 0 ? 0 : $_POST['poll_expire']);
1891
1892
		// Just set it to zero if it's not there..
1893
		if (!isset($_POST['poll_hide']))
1894
			$_POST['poll_hide'] = 0;
1895
		else
1896
			$_POST['poll_hide'] = (int) $_POST['poll_hide'];
1897
		$_POST['poll_change_vote'] = isset($_POST['poll_change_vote']) ? 1 : 0;
1898
1899
		$_POST['poll_guest_vote'] = isset($_POST['poll_guest_vote']) ? 1 : 0;
1900
		// Make sure guests are actually allowed to vote generally.
1901 View Code Duplication
		if ($_POST['poll_guest_vote'])
1902
		{
1903
			require_once($sourcedir . '/Subs-Members.php');
1904
			$allowedVoteGroups = groupsAllowedTo('poll_vote', $board);
1905
			if (!in_array(-1, $allowedVoteGroups['allowed']))
1906
				$_POST['poll_guest_vote'] = 0;
1907
		}
1908
1909
		// If the user tries to set the poll too far in advance, don't let them.
1910
		if (!empty($_POST['poll_expire']) && $_POST['poll_expire'] < 1)
1911
			fatal_lang_error('poll_range_error', false);
1912
		// Don't allow them to select option 2 for hidden results if it's not time limited.
1913
		elseif (empty($_POST['poll_expire']) && $_POST['poll_hide'] == 2)
1914
			$_POST['poll_hide'] = 1;
1915
1916
		// Clean up the question and answers.
1917
		$_POST['question'] = $smcFunc['htmlspecialchars']($_POST['question']);
1918
		$_POST['question'] = $smcFunc['truncate']($_POST['question'], 255);
1919
		$_POST['question'] = preg_replace('~&amp;#(\d{4,5}|[2-9]\d{2,4}|1[2-9]\d);~', '&#$1;', $_POST['question']);
1920
		$_POST['options'] = htmlspecialchars__recursive($_POST['options']);
1921
	}
1922
1923
	// ...or attach a new file...
1924
	if (empty($ignore_temp) && $context['can_post_attachment'] && !empty($_SESSION['temp_attachments']) && empty($_POST['from_qr']))
0 ignored issues
show
Bug introduced by
The variable $ignore_temp seems to never exist, and therefore empty should always return true. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() on variables that are yet undefined. These calls will always produce the same result and can be removed.

This is most likely caused by the renaming of a variable or the removal of a function/method parameter.

Loading history...
1925
	{
1926
		$attachIDs = array();
1927
		$attach_errors = array();
1928
		if (!empty($context['we_are_history']))
1929
			$attach_errors[] = '<dd>' . $txt['error_temp_attachments_flushed'] . '<br><br></dd>';
1930
1931
		foreach ($_SESSION['temp_attachments'] as  $attachID => $attachment)
1932
		{
1933 View Code Duplication
			if ($attachID != 'initial_error' && strpos($attachID, 'post_tmp_' . $user_info['id']) === false)
1934
				continue;
1935
1936
			// If there was an initial error just show that message.
1937 View Code Duplication
			if ($attachID == 'initial_error')
1938
			{
1939
				$attach_errors[] = '<dt>' . $txt['attach_no_upload'] . '</dt>';
1940
				$attach_errors[] = '<dd>' . (is_array($attachment) ? vsprintf($txt[$attachment[0]], $attachment[1]) : $txt[$attachment]) . '</dd>';
1941
1942
				unset($_SESSION['temp_attachments']);
1943
				break;
1944
			}
1945
1946
			$attachmentOptions = array(
1947
				'post' => isset($_REQUEST['msg']) ? $_REQUEST['msg'] : 0,
1948
				'poster' => $user_info['id'],
1949
				'name' => $attachment['name'],
1950
				'tmp_name' => $attachment['tmp_name'],
1951
				'size' => isset($attachment['size']) ? $attachment['size'] : 0,
1952
				'mime_type' => isset($attachment['type']) ? $attachment['type'] : '',
1953
				'id_folder' => isset($attachment['id_folder']) ? $attachment['id_folder'] : $modSettings['currentAttachmentUploadDir'],
1954
				'approved' => !$modSettings['postmod_active'] || allowedTo('post_attachment'),
1955
				'errors' => $attachment['errors'],
1956
			);
1957
1958
			if (empty($attachment['errors']))
1959
			{
1960
				if (createAttachment($attachmentOptions))
1961
				{
1962
					$attachIDs[] = $attachmentOptions['id'];
1963
					if (!empty($attachmentOptions['thumb']))
1964
						$attachIDs[] = $attachmentOptions['thumb'];
1965
				}
1966
			}
1967
			else
1968
				$attach_errors[] = '<dt>&nbsp;</dt>';
1969
1970
			if (!empty($attachmentOptions['errors']))
1971
			{
1972
				// Sort out the errors for display and delete any associated files.
1973
				$attach_errors[] = '<dt>' . vsprintf($txt['attach_warning'], $attachment['name']) . '</dt>';
1974
				$log_these = array('attachments_no_create', 'attachments_no_write', 'attach_timeout', 'ran_out_of_space', 'cant_access_upload_path', 'attach_0_byte_file');
1975
				foreach ($attachmentOptions['errors'] as $error)
1976
				{
1977
					if (!is_array($error))
1978
					{
1979
						$attach_errors[] = '<dd>' . $txt[$error] . '</dd>';
1980 View Code Duplication
						if (in_array($error, $log_these))
1981
							log_error($attachment['name'] . ': ' . $txt[$error], 'critical');
1982
					}
1983
					else
1984
						$attach_errors[] = '<dd>' . vsprintf($txt[$error[0]], $error[1]) . '</dd>';
1985
				}
1986
				if (file_exists($attachment['tmp_name']))
1987
					unlink($attachment['tmp_name']);
1988
			}
1989
		}
1990
		unset($_SESSION['temp_attachments']);
1991
	}
1992
1993
	// Make the poll...
1994
	if (isset($_REQUEST['poll']))
1995
	{
1996
		// Create the poll.
1997
		$id_poll = $smcFunc['db_insert']('',
1998
			'{db_prefix}polls',
1999
			array(
2000
				'question' => 'string-255', 'hide_results' => 'int', 'max_votes' => 'int', 'expire_time' => 'int', 'id_member' => 'int',
2001
				'poster_name' => 'string-255', 'change_vote' => 'int', 'guest_vote' => 'int'
2002
			),
2003
			array(
2004
				$_POST['question'], $_POST['poll_hide'], $_POST['poll_max_votes'], (empty($_POST['poll_expire']) ? 0 : time() + $_POST['poll_expire'] * 3600 * 24), $user_info['id'],
2005
				$_POST['guestname'], $_POST['poll_change_vote'], $_POST['poll_guest_vote'],
2006
			),
2007
			array('id_poll'),
2008
			1
2009
		);
2010
2011
		// Create each answer choice.
2012
		$i = 0;
2013
		$pollOptions = array();
2014
		foreach ($_POST['options'] as $option)
2015
		{
2016
			$pollOptions[] = array($id_poll, $i, $option);
2017
			$i++;
2018
		}
2019
2020
		$smcFunc['db_insert']('insert',
2021
			'{db_prefix}poll_choices',
2022
			array('id_poll' => 'int', 'id_choice' => 'int', 'label' => 'string-255'),
2023
			$pollOptions,
2024
			array('id_poll', 'id_choice')
2025
		);
2026
2027
		call_integration_hook('integrate_poll_add_edit', array($id_poll, false));
2028
	}
2029
	else
2030
		$id_poll = 0;
2031
2032
	// Creating a new topic?
2033
	$newTopic = empty($_REQUEST['msg']) && empty($topic);
2034
2035
	// Check the icon.
2036
	if (!isset($_POST['icon']))
2037
		$_POST['icon'] = 'xx';
2038
2039
	else
2040
	{
2041
		$_POST['icon'] = $smcFunc['htmlspecialchars']($_POST['icon']);
2042
2043
		// Need to figure it out if this is a valid icon name.
2044
		if ((!file_exists($settings['theme_dir'] . '/images/post/' . $_POST['icon'] . '.png')) && (!file_exists($settings['default_theme_dir'] . '/images/post/' . $_POST['icon'] . '.png')))
2045
			$_POST['icon'] = 'xx';
2046
	}
2047
2048
	// Collect all parameters for the creation or modification of a post.
2049
	$msgOptions = array(
2050
		'id' => empty($_REQUEST['msg']) ? 0 : (int) $_REQUEST['msg'],
2051
		'subject' => $_POST['subject'],
2052
		'body' => $_POST['message'],
2053
		'icon' => preg_replace('~[\./\\\\*:"\'<>]~', '', $_POST['icon']),
2054
		'smileys_enabled' => !isset($_POST['ns']),
2055
		'attachments' => empty($attachIDs) ? array() : $attachIDs,
2056
		'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...
2057
	);
2058
	$topicOptions = array(
2059
		'id' => empty($topic) ? 0 : $topic,
2060
		'board' => $board,
2061
		'poll' => isset($_REQUEST['poll']) ? $id_poll : null,
2062
		'lock_mode' => isset($_POST['lock']) ? (int) $_POST['lock'] : null,
2063
		'sticky_mode' => isset($_POST['sticky']) ? (int) $_POST['sticky'] : null,
2064
		'mark_as_read' => true,
2065
		'is_approved' => !$modSettings['postmod_active'] || empty($topic) || !empty($board_info['cur_topic_approved']),
2066
	);
2067
	$posterOptions = array(
2068
		'id' => $user_info['id'],
2069
		'name' => $_POST['guestname'],
2070
		'email' => $_POST['email'],
2071
		'update_post_count' => !$user_info['is_guest'] && !isset($_REQUEST['msg']) && $board_info['posts_count'],
2072
	);
2073
2074
	// This is an already existing message. Edit it.
2075
	if (!empty($_REQUEST['msg']))
2076
	{
2077
		// Have admins allowed people to hide their screwups?
2078
		if (time() - $row['poster_time'] > $modSettings['edit_wait_time'] || $user_info['id'] != $row['id_member'])
2079
		{
2080
			$msgOptions['modify_time'] = time();
2081
			$msgOptions['modify_name'] = $user_info['name'];
2082
			$msgOptions['modify_reason'] = $_POST['modify_reason'];
2083
		}
2084
2085
		// This will save some time...
2086
		if (empty($approve_has_changed))
2087
			unset($msgOptions['approved']);
2088
2089
		modifyPost($msgOptions, $topicOptions, $posterOptions);
2090
	}
2091
	// This is a new topic or an already existing one. Save it.
2092
	else
2093
	{
2094
		createPost($msgOptions, $topicOptions, $posterOptions);
2095
2096
		if (isset($topicOptions['id']))
2097
			$topic = $topicOptions['id'];
2098
	}
2099
2100
	// Assign the previously uploaded attachments to the brand new message.
2101
	if (!empty($msgOptions['id']) && !empty($_SESSION['already_attached']))
2102
	{
2103
		require_once($sourcedir . '/Subs-Attachments.php');
2104
		assignAttachments($_SESSION['already_attached'], $msgOptions['id']);
2105
		unset($_SESSION['already_attached']);
2106
	}
2107
2108
	// If we had a draft for this, its time to remove it since it was just posted
2109
	if (!empty($modSettings['drafts_post_enabled']) && !empty($_POST['id_draft']))
2110
		DeleteDraft($_POST['id_draft']);
2111
2112
	// Editing or posting an event?
2113
	if (isset($_POST['calendar']) && (!isset($_REQUEST['eventid']) || $_REQUEST['eventid'] == -1))
2114
	{
2115
		require_once($sourcedir . '/Subs-Calendar.php');
2116
2117
		// Make sure they can link an event to this post.
2118
		canLinkEvent();
2119
2120
		// Insert the event.
2121
		$eventOptions = array(
2122
			'board' => $board,
2123
			'topic' => $topic,
2124
			'title' => $_POST['evtitle'],
2125
			'location' => $_POST['event_location'],
2126
			'member' => $user_info['id'],
2127
		);
2128
		insertEvent($eventOptions);
2129
	}
2130
	elseif (isset($_POST['calendar']))
2131
	{
2132
		$_REQUEST['eventid'] = (int) $_REQUEST['eventid'];
2133
2134
		// Validate the post...
2135
		require_once($sourcedir . '/Subs-Calendar.php');
2136
		validateEventPost();
2137
2138
		// If you're not allowed to edit any events, you have to be the poster.
2139
		if (!allowedTo('calendar_edit_any'))
2140
		{
2141
			// Get the event's poster.
2142
			$request = $smcFunc['db_query']('', '
2143
				SELECT id_member
2144
				FROM {db_prefix}calendar
2145
				WHERE id_event = {int:id_event}',
2146
				array(
2147
					'id_event' => $_REQUEST['eventid'],
2148
				)
2149
			);
2150
			$row2 = $smcFunc['db_fetch_assoc']($request);
2151
			$smcFunc['db_free_result']($request);
2152
2153
			// Silly hacker, Trix are for kids. ...probably trademarked somewhere, this is FAIR USE! (parody...)
2154
			isAllowedTo('calendar_edit_' . ($row2['id_member'] == $user_info['id'] ? 'own' : 'any'));
2155
		}
2156
2157
		// Delete it?
2158
		if (isset($_REQUEST['deleteevent']))
2159
			$smcFunc['db_query']('', '
2160
				DELETE FROM {db_prefix}calendar
2161
				WHERE id_event = {int:id_event}',
2162
				array(
2163
					'id_event' => $_REQUEST['eventid'],
2164
				)
2165
			);
2166
		// ... or just update it?
2167
		else
2168
		{
2169
			// Set up our options
2170
			$eventOptions = array(
2171
				'board' => $board,
2172
				'topic' => $topic,
2173
				'title' => $_POST['evtitle'],
2174
				'location' => $_POST['event_location'],
2175
				'member' => $user_info['id'],
2176
			);
2177
			modifyEvent($_REQUEST['eventid'], $eventOptions);
2178
		}
2179
	}
2180
2181
	// Marking read should be done even for editing messages....
2182
	// Mark all the parents read.  (since you just posted and they will be unread.)
2183
	if (!$user_info['is_guest'] && !empty($board_info['parent_boards']))
2184
	{
2185
		$smcFunc['db_query']('', '
2186
			UPDATE {db_prefix}log_boards
2187
			SET id_msg = {int:id_msg}
2188
			WHERE id_member = {int:current_member}
2189
				AND id_board IN ({array_int:board_list})',
2190
			array(
2191
				'current_member' => $user_info['id'],
2192
				'board_list' => array_keys($board_info['parent_boards']),
2193
				'id_msg' => $modSettings['maxMsgID'],
2194
			)
2195
		);
2196
	}
2197
2198
	// Turn notification on or off.  (note this just blows smoke if it's already on or off.)
2199
	if (!empty($_POST['notify']) && !$context['user']['is_guest'])
2200
	{
2201
		$smcFunc['db_insert']('ignore',
2202
			'{db_prefix}log_notify',
2203
			array('id_member' => 'int', 'id_topic' => 'int', 'id_board' => 'int'),
2204
			array($user_info['id'], $topic, 0),
2205
			array('id_member', 'id_topic', 'id_board')
2206
		);
2207
	}
2208
	elseif (!$newTopic)
2209
		$smcFunc['db_query']('', '
2210
			DELETE FROM {db_prefix}log_notify
2211
			WHERE id_member = {int:current_member}
2212
				AND id_topic = {int:current_topic}',
2213
			array(
2214
				'current_member' => $user_info['id'],
2215
				'current_topic' => $topic,
2216
			)
2217
		);
2218
2219
	// Log an act of moderation - modifying.
2220 View Code Duplication
	if (!empty($moderationAction))
2221
		logAction('modify', array('topic' => $topic, 'message' => (int) $_REQUEST['msg'], 'member' => $row['id_member'], 'board' => $board));
2222
2223 View Code Duplication
	if (isset($_POST['lock']) && $_POST['lock'] != 2)
2224
		logAction(empty($_POST['lock']) ? 'unlock' : 'lock', array('topic' => $topicOptions['id'], 'board' => $topicOptions['board']));
2225
2226 View Code Duplication
	if (isset($_POST['sticky']))
2227
		logAction(empty($_POST['sticky']) ? 'unsticky' : 'sticky', array('topic' => $topicOptions['id'], 'board' => $topicOptions['board']));
2228
2229
	// Returning to the topic?
2230 View Code Duplication
	if (!empty($_REQUEST['goback']))
2231
	{
2232
		// Mark the board as read.... because it might get confusing otherwise.
2233
		$smcFunc['db_query']('', '
2234
			UPDATE {db_prefix}log_boards
2235
			SET id_msg = {int:maxMsgID}
2236
			WHERE id_member = {int:current_member}
2237
				AND id_board = {int:current_board}',
2238
			array(
2239
				'current_board' => $board,
2240
				'current_member' => $user_info['id'],
2241
				'maxMsgID' => $modSettings['maxMsgID'],
2242
			)
2243
		);
2244
	}
2245
2246
	if ($board_info['num_topics'] == 0)
2247
		cache_put_data('board-' . $board, null, 120);
2248
2249
	call_integration_hook('integrate_post2_end');
2250
2251 View Code Duplication
	if (!empty($_POST['announce_topic']))
2252
		redirectexit('action=announce;sa=selectgroup;topic=' . $topic . (!empty($_POST['move']) && allowedTo('move_any') ? ';move' : '') . (empty($_REQUEST['goback']) ? '' : ';goback'));
2253
2254 View Code Duplication
	if (!empty($_POST['move']) && allowedTo('move_any'))
2255
		redirectexit('action=movetopic;topic=' . $topic . '.0' . (empty($_REQUEST['goback']) ? '' : ';goback'));
2256
2257
	// Return to post if the mod is on.
2258
	if (isset($_REQUEST['msg']) && !empty($_REQUEST['goback']))
2259
		redirectexit('topic=' . $topic . '.msg' . $_REQUEST['msg'] . '#msg' . $_REQUEST['msg'], isBrowser('ie'));
2260 View Code Duplication
	elseif (!empty($_REQUEST['goback']))
2261
		redirectexit('topic=' . $topic . '.new#new', isBrowser('ie'));
2262
	// Dut-dut-duh-duh-DUH-duh-dut-duh-duh!  *dances to the Final Fantasy Fanfare...*
2263
	else
2264
		redirectexit('board=' . $board . '.0');
2265
}
2266
2267
/**
2268
 * Handle the announce topic function (action=announce).
2269
 *
2270
 * checks the topic announcement permissions and loads the announcement template.
2271
 * requires the announce_topic permission.
2272
 * uses the ManageMembers template and Post language file.
2273
 * call the right function based on the sub-action.
2274
 */
2275
function AnnounceTopic()
2276
{
2277
	global $context, $txt, $topic;
2278
2279
	isAllowedTo('announce_topic');
2280
2281
	validateSession();
2282
2283
	if (empty($topic))
2284
		fatal_lang_error('topic_gone', false);
2285
2286
	loadLanguage('Post');
2287
	loadTemplate('Post');
2288
2289
	$subActions = array(
2290
		'selectgroup' => 'AnnouncementSelectMembergroup',
2291
		'send' => 'AnnouncementSend',
2292
	);
2293
2294
	$context['page_title'] = $txt['announce_topic'];
2295
2296
	// Call the function based on the sub-action.
2297
	$call = isset($_REQUEST['sa']) && isset($subActions[$_REQUEST['sa']]) ? $_REQUEST['sa'] : 'selectgroup';
2298
	call_helper($subActions[$call]);
2299
}
2300
2301
/**
2302
 * Allow a user to chose the membergroups to send the announcement to.
2303
 *
2304
 * lets the user select the membergroups that will receive the topic announcement.
2305
 */
2306
function AnnouncementSelectMembergroup()
2307
{
2308
	global $txt, $context, $topic, $board_info, $smcFunc;
2309
2310
	$groups = array_merge($board_info['groups'], array(1));
2311
	foreach ($groups as $id => $group)
2312
		$groups[$id] = (int) $group;
2313
2314
	$context['groups'] = array();
2315
	if (in_array(0, $groups))
2316
	{
2317
		$context['groups'][0] = array(
2318
			'id' => 0,
2319
			'name' => $txt['announce_regular_members'],
2320
			'member_count' => 'n/a',
2321
		);
2322
	}
2323
2324
	// Get all membergroups that have access to the board the announcement was made on.
2325
	$request = $smcFunc['db_query']('', '
2326
		SELECT mg.id_group, COUNT(mem.id_member) AS num_members
2327
		FROM {db_prefix}membergroups AS mg
2328
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_group = mg.id_group OR FIND_IN_SET(mg.id_group, mem.additional_groups) != 0 OR mg.id_group = mem.id_post_group)
2329
		WHERE mg.id_group IN ({array_int:group_list})
2330
		GROUP BY mg.id_group',
2331
		array(
2332
			'group_list' => $groups,
2333
			'newbie_id_group' => 4,
2334
		)
2335
	);
2336
	while ($row = $smcFunc['db_fetch_assoc']($request))
2337
	{
2338
		$context['groups'][$row['id_group']] = array(
2339
			'id' => $row['id_group'],
2340
			'name' => '',
2341
			'member_count' => $row['num_members'],
2342
		);
2343
	}
2344
	$smcFunc['db_free_result']($request);
2345
2346
	// Now get the membergroup names.
2347
	$request = $smcFunc['db_query']('', '
2348
		SELECT id_group, group_name
2349
		FROM {db_prefix}membergroups
2350
		WHERE id_group IN ({array_int:group_list})',
2351
		array(
2352
			'group_list' => $groups,
2353
		)
2354
	);
2355 View Code Duplication
	while ($row = $smcFunc['db_fetch_assoc']($request))
2356
		$context['groups'][$row['id_group']]['name'] = $row['group_name'];
2357
	$smcFunc['db_free_result']($request);
2358
2359
	// Get the subject of the topic we're about to announce.
2360
	$request = $smcFunc['db_query']('', '
2361
		SELECT m.subject
2362
		FROM {db_prefix}topics AS t
2363
			INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)
2364
		WHERE t.id_topic = {int:current_topic}',
2365
		array(
2366
			'current_topic' => $topic,
2367
		)
2368
	);
2369
	list ($context['topic_subject']) = $smcFunc['db_fetch_row']($request);
2370
	$smcFunc['db_free_result']($request);
2371
2372
	censorText($context['announce_topic']['subject']);
2373
2374
	$context['move'] = isset($_REQUEST['move']) ? 1 : 0;
2375
	$context['go_back'] = isset($_REQUEST['goback']) ? 1 : 0;
2376
2377
	$context['sub_template'] = 'announce';
2378
}
2379
2380
/**
2381
 * Send the announcement in chunks.
2382
 *
2383
 * splits the members to be sent a topic announcement into chunks.
2384
 * composes notification messages in all languages needed.
2385
 * does the actual sending of the topic announcements in chunks.
2386
 * calculates a rough estimate of the percentage items sent.
2387
 */
2388
function AnnouncementSend()
2389
{
2390
	global $topic, $board, $board_info, $context, $modSettings;
2391
	global $language, $scripturl, $sourcedir, $smcFunc;
2392
2393
	checkSession();
2394
2395
	$context['start'] = empty($_REQUEST['start']) ? 0 : (int) $_REQUEST['start'];
2396
	$groups = array_merge($board_info['groups'], array(1));
2397
2398
	if (isset($_POST['membergroups']))
2399
		$_POST['who'] = explode(',', $_POST['membergroups']);
2400
2401
	// Check whether at least one membergroup was selected.
2402
	if (empty($_POST['who']))
2403
		fatal_lang_error('no_membergroup_selected');
2404
2405
	// Make sure all membergroups are integers and can access the board of the announcement.
2406
	foreach ($_POST['who'] as $id => $mg)
2407
		$_POST['who'][$id] = in_array((int) $mg, $groups) ? (int) $mg : 0;
2408
2409
	// Get the topic subject and censor it.
2410
	$request = $smcFunc['db_query']('', '
2411
		SELECT m.id_msg, m.subject, m.body
2412
		FROM {db_prefix}topics AS t
2413
			INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)
2414
		WHERE t.id_topic = {int:current_topic}',
2415
		array(
2416
			'current_topic' => $topic,
2417
		)
2418
	);
2419
	list ($id_msg, $context['topic_subject'], $message) = $smcFunc['db_fetch_row']($request);
2420
	$smcFunc['db_free_result']($request);
2421
2422
	censorText($context['topic_subject']);
2423
	censorText($message);
2424
2425
	$message = trim(un_htmlspecialchars(strip_tags(strtr(parse_bbc($message, false, $id_msg), array('<br>' => "\n", '</div>' => "\n", '</li>' => "\n", '&#91;' => '[', '&#93;' => ']')))));
2426
2427
	// We need this in order to be able send emails.
2428
	require_once($sourcedir . '/Subs-Post.php');
2429
2430
	// Select the email addresses for this batch.
2431
	$request = $smcFunc['db_query']('', '
2432
		SELECT mem.id_member, mem.email_address, mem.lngfile
2433
		FROM {db_prefix}members AS mem
2434
		WHERE (mem.id_group IN ({array_int:group_list}) OR mem.id_post_group IN ({array_int:group_list}) OR FIND_IN_SET({raw:additional_group_list}, mem.additional_groups) != 0)
2435
			AND mem.is_activated = {int:is_activated}
2436
			AND mem.id_member > {int:start}
2437
		ORDER BY mem.id_member
2438
		LIMIT {int:chunk_size}',
2439
		array(
2440
			'group_list' => $_POST['who'],
2441
			'is_activated' => 1,
2442
			'start' => $context['start'],
2443
			'additional_group_list' => implode(', mem.additional_groups) != 0 OR FIND_IN_SET(', $_POST['who']),
2444
			// @todo Might need an interface?
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
2445
			'chunk_size' => 500,
2446
		)
2447
	);
2448
2449
	// All members have received a mail. Go to the next screen.
2450
	if ($smcFunc['db_num_rows']($request) == 0)
2451
	{
2452
		logAction('announce_topic', array('topic' => $topic), 'user');
2453
		if (!empty($_REQUEST['move']) && allowedTo('move_any'))
2454
			redirectexit('action=movetopic;topic=' . $topic . '.0' . (empty($_REQUEST['goback']) ? '' : ';goback'));
2455 View Code Duplication
		elseif (!empty($_REQUEST['goback']))
2456
			redirectexit('topic=' . $topic . '.new;boardseen#new', isBrowser('ie'));
2457
		else
2458
			redirectexit('board=' . $board . '.0');
2459
	}
2460
2461
	$announcements = array();
2462
	// Loop through all members that'll receive an announcement in this batch.
2463
	$rows = array();
2464
	while ($row = $smcFunc['db_fetch_assoc']($request))
2465
	{
2466
		$rows[$row['id_member']] = $row;
2467
	}
2468
	$smcFunc['db_free_result']($request);
2469
2470
	// Load their alert preferences
2471
	require_once($sourcedir . '/Subs-Notify.php');
2472
	$prefs = getNotifyPrefs(array_keys($rows), 'announcements', true);
2473
2474
	foreach ($rows as $row)
2475
	{
2476
		// Force them to have it?
2477
		if (empty($prefs[$row['id_member']]['announcements']))
2478
			continue;
2479
2480
		$cur_language = empty($row['lngfile']) || empty($modSettings['userLanguage']) ? $language : $row['lngfile'];
2481
2482
		// If the language wasn't defined yet, load it and compose a notification message.
2483
		if (!isset($announcements[$cur_language]))
2484
		{
2485
			$replacements = array(
2486
				'TOPICSUBJECT' => $context['topic_subject'],
2487
				'MESSAGE' => $message,
2488
				'TOPICLINK' => $scripturl . '?topic=' . $topic . '.0',
2489
			);
2490
2491
			$emaildata = loadEmailTemplate('new_announcement', $replacements, $cur_language);
2492
2493
			$announcements[$cur_language] = array(
2494
				'subject' => $emaildata['subject'],
2495
				'body' => $emaildata['body'],
2496
				'is_html' => $emaildata['is_html'],
2497
				'recipients' => array(),
2498
			);
2499
		}
2500
2501
		$announcements[$cur_language]['recipients'][$row['id_member']] = $row['email_address'];
2502
		$context['start'] = $row['id_member'];
2503
	}
2504
2505
	// For each language send a different mail - low priority...
2506
	foreach ($announcements as $lang => $mail)
2507
		sendmail($mail['recipients'], $mail['subject'], $mail['body'], null, 'ann-' . $lang, $mail['is_html'], 5);
2508
2509
	$context['percentage_done'] = round(100 * $context['start'] / $modSettings['latestMember'], 1);
2510
2511
	$context['move'] = empty($_REQUEST['move']) ? 0 : 1;
2512
	$context['go_back'] = empty($_REQUEST['goback']) ? 0 : 1;
2513
	$context['membergroups'] = implode(',', $_POST['who']);
2514
	$context['sub_template'] = 'announcement_send';
2515
2516
	// Go back to the correct language for the user ;).
2517
	if (!empty($modSettings['userLanguage']))
2518
		loadLanguage('Post');
2519
}
2520
2521
/**
2522
 * Get the topic for display purposes.
2523
 *
2524
 * gets a summary of the most recent posts in a topic.
2525
 * depends on the topicSummaryPosts setting.
2526
 * if you are editing a post, only shows posts previous to that post.
2527
 */
2528
function getTopic()
2529
{
2530
	global $topic, $modSettings, $context, $smcFunc, $counter, $options;
2531
2532
	if (isset($_REQUEST['xml']))
2533
		$limit = '
2534
		LIMIT ' . (empty($context['new_replies']) ? '0' : $context['new_replies']);
2535
	else
2536
		$limit = empty($modSettings['topicSummaryPosts']) ? '' : '
2537
		LIMIT ' . (int) $modSettings['topicSummaryPosts'];
2538
2539
	// If you're modifying, get only those posts before the current one. (otherwise get all.)
2540
	$request = $smcFunc['db_query']('', '
2541
		SELECT
2542
			COALESCE(mem.real_name, m.poster_name) AS poster_name, m.poster_time,
2543
			m.body, m.smileys_enabled, m.id_msg, m.id_member
2544
		FROM {db_prefix}messages AS m
2545
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
2546
		WHERE m.id_topic = {int:current_topic}' . (isset($_REQUEST['msg']) ? '
2547
			AND m.id_msg < {int:id_msg}' : '') . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : '
2548
			AND m.approved = {int:approved}') . '
2549
		ORDER BY m.id_msg DESC' . $limit,
2550
		array(
2551
			'current_topic' => $topic,
2552
			'id_msg' => isset($_REQUEST['msg']) ? (int) $_REQUEST['msg'] : 0,
2553
			'approved' => 1,
2554
		)
2555
	);
2556
	$context['previous_posts'] = array();
2557
	while ($row = $smcFunc['db_fetch_assoc']($request))
2558
	{
2559
		// Censor, BBC, ...
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% 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...
2560
		censorText($row['body']);
2561
		$row['body'] = parse_bbc($row['body'], $row['smileys_enabled'], $row['id_msg']);
2562
2563
		// ...and store.
2564
		$context['previous_posts'][] = array(
2565
			'counter' => $counter++,
2566
			'poster' => $row['poster_name'],
2567
			'message' => $row['body'],
2568
			'time' => timeformat($row['poster_time']),
2569
			'timestamp' => forum_time(true, $row['poster_time']),
2570
			'id' => $row['id_msg'],
2571
			'is_new' => !empty($context['new_replies']),
2572
			'is_ignored' => !empty($modSettings['enable_buddylist']) && !empty($options['posts_apply_ignore_list']) && in_array($row['id_member'], $context['user']['ignoreusers']),
2573
		);
2574
2575
		if (!empty($context['new_replies']))
2576
			$context['new_replies']--;
2577
	}
2578
	$smcFunc['db_free_result']($request);
2579
}
2580
2581
/**
2582
 * Loads a post an inserts it into the current editing text box.
2583
 * uses the Post language file.
2584
 * uses special (sadly browser dependent) javascript to parse entities for internationalization reasons.
2585
 * accessed with ?action=quotefast.
2586
 */
2587
function QuoteFast()
2588
{
2589
	global $modSettings, $user_info, $context;
2590
	global $sourcedir, $smcFunc;
2591
2592
	loadLanguage('Post');
2593
	if (!isset($_REQUEST['xml']))
2594
		loadTemplate('Post');
2595
2596
	include_once($sourcedir . '/Subs-Post.php');
2597
2598
	$moderate_boards = boardsAllowedTo('moderate_board');
2599
2600
	// Where we going if we need to?
2601
	$context['post_box_name'] = isset($_GET['pb']) ? $_GET['pb'] : '';
2602
2603
	$request = $smcFunc['db_query']('', '
2604
		SELECT COALESCE(mem.real_name, m.poster_name) AS poster_name, m.poster_time, m.body, m.id_topic, m.subject,
2605
			m.id_board, m.id_member, m.approved, m.modified_time, m.modified_name, m.modified_reason
2606
		FROM {db_prefix}messages AS m
2607
			INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic)
2608
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board AND {query_see_board})
2609
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
2610
		WHERE m.id_msg = {int:id_msg}' . (isset($_REQUEST['modify']) || (!empty($moderate_boards) && $moderate_boards[0] == 0) ? '' : '
2611
			AND (t.locked = {int:not_locked}' . (empty($moderate_boards) ? '' : ' OR b.id_board IN ({array_int:moderation_board_list})') . ')') . '
2612
		LIMIT 1',
2613
		array(
2614
			'current_member' => $user_info['id'],
2615
			'moderation_board_list' => $moderate_boards,
2616
			'id_msg' => (int) $_REQUEST['quote'],
2617
			'not_locked' => 0,
2618
		)
2619
	);
2620
	$context['close_window'] = $smcFunc['db_num_rows']($request) == 0;
2621
	$row = $smcFunc['db_fetch_assoc']($request);
2622
	$smcFunc['db_free_result']($request);
2623
2624
	$context['sub_template'] = 'quotefast';
2625
	if (!empty($row))
2626
		$can_view_post = $row['approved'] || ($row['id_member'] != 0 && $row['id_member'] == $user_info['id']) || allowedTo('approve_posts', $row['id_board']);
2627
2628
	if (!empty($can_view_post))
2629
	{
2630
		// Remove special formatting we don't want anymore.
2631
		$row['body'] = un_preparsecode($row['body']);
2632
2633
		// Censor the message!
2634
		censorText($row['body']);
2635
2636
		$row['body'] = preg_replace('~<br ?/?' . '>~i', "\n", $row['body']);
2637
2638
		// Want to modify a single message by double clicking it?
2639
		if (isset($_REQUEST['modify']))
2640
		{
2641
			censorText($row['subject']);
2642
2643
			$context['sub_template'] = 'modifyfast';
2644
			$context['message'] = array(
2645
				'id' => $_REQUEST['quote'],
2646
				'body' => $row['body'],
2647
				'subject' => addcslashes($row['subject'], '"'),
2648
				'reason' => array(
2649
					'name' => $row['modified_name'],
2650
					'text' => $row['modified_reason'],
2651
					'time' => $row['modified_time'],
2652
				),
2653
			);
2654
2655
			return;
2656
		}
2657
2658
		// Remove any nested quotes.
2659 View Code Duplication
		if (!empty($modSettings['removeNestedQuotes']))
2660
			$row['body'] = preg_replace(array('~\n?\[quote.*?\].+?\[/quote\]\n?~is', '~^\n~', '~\[/quote\]~'), '', $row['body']);
2661
2662
		$lb = "\n";
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $lb. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
2663
2664
		// Add a quote string on the front and end.
2665
		$context['quote']['xml'] = '[quote author=' . $row['poster_name'] . ' link=msg=' . (int) $_REQUEST['quote'] . ' date=' . $row['poster_time'] . ']' . $lb . $row['body'] . $lb . '[/quote]';
2666
		$context['quote']['text'] = strtr(un_htmlspecialchars($context['quote']['xml']), array('\'' => '\\\'', '\\' => '\\\\', "\n" => '\\n', '</script>' => '</\' + \'script>'));
2667
		$context['quote']['xml'] = strtr($context['quote']['xml'], array('&nbsp;' => '&#160;', '<' => '&lt;', '>' => '&gt;'));
2668
2669
		$context['quote']['mozilla'] = strtr($smcFunc['htmlspecialchars']($context['quote']['text']), array('&quot;' => '"'));
2670
	}
2671
	//@todo Needs a nicer interface.
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
2672
	// In case our message has been removed in the meantime.
2673
	elseif (isset($_REQUEST['modify']))
2674
	{
2675
		$context['sub_template'] = 'modifyfast';
2676
		$context['message'] = array(
2677
			'id' => 0,
2678
			'body' => '',
2679
			'subject' => '',
2680
			'reason' => array(
2681
				'name' => '',
2682
				'text' => '',
2683
				'time' => '',
2684
			),
2685
		);
2686
	}
2687
	else
2688
		$context['quote'] = array(
2689
			'xml' => '',
2690
			'mozilla' => '',
2691
			'text' => '',
2692
		);
2693
}
2694
2695
/**
2696
 * Used to edit the body or subject of a message inline
2697
 * called from action=jsmodify from script and topic js
2698
 */
2699
function JavaScriptModify()
2700
{
2701
	global $sourcedir, $modSettings, $board, $topic, $txt;
2702
	global $user_info, $context, $smcFunc, $language, $board_info;
2703
2704
	// We have to have a topic!
2705
	if (empty($topic))
2706
		obExit(false);
2707
2708
	checkSession('get');
2709
	require_once($sourcedir . '/Subs-Post.php');
2710
2711
	// Assume the first message if no message ID was given.
2712
	$request = $smcFunc['db_query']('', '
2713
		SELECT
2714
			t.locked, t.num_replies, t.id_member_started, t.id_first_msg,
2715
			m.id_msg, m.id_member, m.poster_time, m.subject, m.smileys_enabled, m.body, m.icon,
2716
			m.modified_time, m.modified_name, m.modified_reason, m.approved,
2717
			m.poster_name, m.poster_email
2718
		FROM {db_prefix}messages AS m
2719
			INNER JOIN {db_prefix}topics AS t ON (t.id_topic = {int:current_topic})
2720
		WHERE m.id_msg = {raw:id_msg}
2721
			AND m.id_topic = {int:current_topic}' . (allowedTo('modify_any') || allowedTo('approve_posts') ? '' : (!$modSettings['postmod_active'] ? '
2722
			AND (m.id_member != {int:guest_id} AND m.id_member = {int:current_member})' : '
2723
			AND (m.approved = {int:is_approved} OR (m.id_member != {int:guest_id} AND m.id_member = {int:current_member}))')),
2724
		array(
2725
			'current_member' => $user_info['id'],
2726
			'current_topic' => $topic,
2727
			'id_msg' => empty($_REQUEST['msg']) ? 't.id_first_msg' : (int) $_REQUEST['msg'],
2728
			'is_approved' => 1,
2729
			'guest_id' => 0,
2730
		)
2731
	);
2732
	if ($smcFunc['db_num_rows']($request) == 0)
2733
		fatal_lang_error('no_board', false);
2734
	$row = $smcFunc['db_fetch_assoc']($request);
2735
	$smcFunc['db_free_result']($request);
2736
2737
	// Change either body or subject requires permissions to modify messages.
2738
	if (isset($_POST['message']) || isset($_POST['subject']) || isset($_REQUEST['icon']))
2739
	{
2740
		if (!empty($row['locked']))
2741
			isAllowedTo('moderate_board');
2742
2743 View Code Duplication
		if ($row['id_member'] == $user_info['id'] && !allowedTo('modify_any'))
2744
		{
2745
			if ((!$modSettings['postmod_active'] || $row['approved']) && !empty($modSettings['edit_disable_time']) && $row['poster_time'] + ($modSettings['edit_disable_time'] + 5) * 60 < time())
2746
				fatal_lang_error('modify_post_time_passed', false);
2747
			elseif ($row['id_member_started'] == $user_info['id'] && !allowedTo('modify_own'))
2748
				isAllowedTo('modify_replies');
2749
			else
2750
				isAllowedTo('modify_own');
2751
		}
2752
		// Otherwise, they're locked out; someone who can modify the replies is needed.
2753
		elseif ($row['id_member_started'] == $user_info['id'] && !allowedTo('modify_any'))
2754
			isAllowedTo('modify_replies');
2755
		else
2756
			isAllowedTo('modify_any');
2757
2758
		// Only log this action if it wasn't your message.
2759
		$moderationAction = $row['id_member'] != $user_info['id'];
2760
	}
2761
2762
	$post_errors = array();
2763
	if (isset($_POST['subject']) && $smcFunc['htmltrim']($smcFunc['htmlspecialchars']($_POST['subject'])) !== '')
2764
	{
2765
		$_POST['subject'] = strtr($smcFunc['htmlspecialchars']($_POST['subject']), array("\r" => '', "\n" => '', "\t" => ''));
2766
2767
		// Maximum number of characters.
2768 View Code Duplication
		if ($smcFunc['strlen']($_POST['subject']) > 100)
2769
			$_POST['subject'] = $smcFunc['substr']($_POST['subject'], 0, 100);
2770
	}
2771
	elseif (isset($_POST['subject']))
2772
	{
2773
		$post_errors[] = 'no_subject';
2774
		unset($_POST['subject']);
2775
	}
2776
2777
	if (isset($_POST['message']))
2778
	{
2779
		if ($smcFunc['htmltrim']($smcFunc['htmlspecialchars']($_POST['message'])) === '')
2780
		{
2781
			$post_errors[] = 'no_message';
2782
			unset($_POST['message']);
2783
		}
2784 View Code Duplication
		elseif (!empty($modSettings['max_messageLength']) && $smcFunc['strlen']($_POST['message']) > $modSettings['max_messageLength'])
2785
		{
2786
			$post_errors[] = 'long_message';
2787
			unset($_POST['message']);
2788
		}
2789
		else
2790
		{
2791
			$_POST['message'] = $smcFunc['htmlspecialchars']($_POST['message'], ENT_QUOTES);
2792
2793
			preparsecode($_POST['message']);
2794
2795
			if ($smcFunc['htmltrim'](strip_tags(parse_bbc($_POST['message'], false), implode('', $context['allowed_html_tags']))) === '')
2796
			{
2797
				$post_errors[] = 'no_message';
2798
				unset($_POST['message']);
2799
			}
2800
		}
2801
	}
2802
2803
	if (isset($_POST['lock']))
2804
	{
2805
		if (!allowedTo(array('lock_any', 'lock_own')) || (!allowedTo('lock_any') && $user_info['id'] != $row['id_member']))
2806
			unset($_POST['lock']);
2807
		elseif (!allowedTo('lock_any'))
2808
		{
2809
			if ($row['locked'] == 1)
2810
				unset($_POST['lock']);
2811
			else
2812
				$_POST['lock'] = empty($_POST['lock']) ? 0 : 2;
2813
		}
2814
		elseif (!empty($row['locked']) && !empty($_POST['lock']) || $_POST['lock'] == $row['locked'])
2815
			unset($_POST['lock']);
2816
		else
2817
			$_POST['lock'] = empty($_POST['lock']) ? 0 : 1;
2818
	}
2819
2820
	if (isset($_POST['sticky']) && !allowedTo('make_sticky'))
2821
		unset($_POST['sticky']);
2822
2823
	if (isset($_POST['modify_reason']))
2824
	{
2825
		$_POST['modify_reason'] = strtr($smcFunc['htmlspecialchars']($_POST['modify_reason']), array("\r" => '', "\n" => '', "\t" => ''));
2826
2827
		// Maximum number of characters.
2828 View Code Duplication
		if ($smcFunc['strlen']($_POST['modify_reason']) > 100)
2829
			$_POST['modify_reason'] = $smcFunc['substr']($_POST['modify_reason'], 0, 100);
2830
	}
2831
2832
	if (empty($post_errors))
2833
	{
2834
		$msgOptions = array(
2835
			'id' => $row['id_msg'],
2836
			'subject' => isset($_POST['subject']) ? $_POST['subject'] : null,
2837
			'body' => isset($_POST['message']) ? $_POST['message'] : null,
2838
			'icon' => isset($_REQUEST['icon']) ? preg_replace('~[\./\\\\*\':"<>]~', '', $_REQUEST['icon']) : null,
2839
			'modify_reason' => (isset($_POST['modify_reason']) ? $_POST['modify_reason'] : ''),
2840
		);
2841
		$topicOptions = array(
2842
			'id' => $topic,
2843
			'board' => $board,
2844
			'lock_mode' => isset($_POST['lock']) ? (int) $_POST['lock'] : null,
2845
			'sticky_mode' => isset($_POST['sticky']) ? (int) $_POST['sticky'] : null,
2846
			'mark_as_read' => true,
2847
		);
2848
		$posterOptions = array(
2849
			'id' => $user_info['id'],
2850
			'name' => $row['poster_name'],
2851
			'email' => $row['poster_email'],
2852
			'update_post_count' => !$user_info['is_guest'] && !isset($_REQUEST['msg']) && $board_info['posts_count'],
2853
		);
2854
2855
		// Only consider marking as editing if they have edited the subject, message or icon.
2856
		if ((isset($_POST['subject']) && $_POST['subject'] != $row['subject']) || (isset($_POST['message']) && $_POST['message'] != $row['body']) || (isset($_REQUEST['icon']) && $_REQUEST['icon'] != $row['icon']))
2857
		{
2858
			// And even then only if the time has passed...
2859
			if (time() - $row['poster_time'] > $modSettings['edit_wait_time'] || $user_info['id'] != $row['id_member'])
2860
			{
2861
				$msgOptions['modify_time'] = time();
2862
				$msgOptions['modify_name'] = $user_info['name'];
2863
			}
2864
		}
2865
		// If nothing was changed there's no need to add an entry to the moderation log.
2866
		else
2867
			$moderationAction = false;
2868
2869
		modifyPost($msgOptions, $topicOptions, $posterOptions);
2870
2871
		// If we didn't change anything this time but had before put back the old info.
2872 View Code Duplication
		if (!isset($msgOptions['modify_time']) && !empty($row['modified_time']))
2873
		{
2874
			$msgOptions['modify_time'] = $row['modified_time'];
2875
			$msgOptions['modify_name'] = $row['modified_name'];
2876
			$msgOptions['modify_reason'] = $row['modified_reason'];
2877
		}
2878
2879
		// Changing the first subject updates other subjects to 'Re: new_subject'.
2880
		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'))))
2881
		{
2882
			// Get the proper (default language) response prefix first.
2883 View Code Duplication
			if (!isset($context['response_prefix']) && !($context['response_prefix'] = cache_get_data('response_prefix')))
2884
			{
2885
				if ($language === $user_info['language'])
2886
					$context['response_prefix'] = $txt['response_prefix'];
2887
				else
2888
				{
2889
					loadLanguage('index', $language, false);
2890
					$context['response_prefix'] = $txt['response_prefix'];
2891
					loadLanguage('index');
2892
				}
2893
				cache_put_data('response_prefix', $context['response_prefix'], 600);
2894
			}
2895
2896
			$smcFunc['db_query']('', '
2897
				UPDATE {db_prefix}messages
2898
				SET subject = {string:subject}
2899
				WHERE id_topic = {int:current_topic}
2900
					AND id_msg != {int:id_first_msg}',
2901
				array(
2902
					'current_topic' => $topic,
2903
					'id_first_msg' => $row['id_first_msg'],
2904
					'subject' => $context['response_prefix'] . $_POST['subject'],
2905
				)
2906
			);
2907
		}
2908
2909 View Code Duplication
		if (!empty($moderationAction))
2910
			logAction('modify', array('topic' => $topic, 'message' => $row['id_msg'], 'member' => $row['id_member'], 'board' => $board));
2911
	}
2912
2913
	if (isset($_REQUEST['xml']))
2914
	{
2915
		$context['sub_template'] = 'modifydone';
2916
		if (empty($post_errors) && isset($msgOptions['subject']) && isset($msgOptions['body']))
2917
		{
2918
			$context['message'] = array(
2919
				'id' => $row['id_msg'],
2920
				'modified' => array(
2921
					'time' => isset($msgOptions['modify_time']) ? timeformat($msgOptions['modify_time']) : '',
2922
					'timestamp' => isset($msgOptions['modify_time']) ? forum_time(true, $msgOptions['modify_time']) : 0,
2923
					'name' => isset($msgOptions['modify_time']) ? $msgOptions['modify_name'] : '',
2924
					'reason' => $msgOptions['modify_reason'],
2925
				),
2926
				'subject' => $msgOptions['subject'],
2927
				'first_in_topic' => $row['id_msg'] == $row['id_first_msg'],
2928
				'body' => strtr($msgOptions['body'], array(']]>' => ']]]]><![CDATA[>')),
2929
			);
2930
2931
			censorText($context['message']['subject']);
2932
			censorText($context['message']['body']);
2933
2934
			$context['message']['body'] = parse_bbc($context['message']['body'], $row['smileys_enabled'], $row['id_msg']);
2935
		}
2936
		// Topic?
2937
		elseif (empty($post_errors))
2938
		{
2939
			$context['sub_template'] = 'modifytopicdone';
2940
			$context['message'] = array(
2941
				'id' => $row['id_msg'],
2942
				'modified' => array(
2943
					'time' => isset($msgOptions['modify_time']) ? timeformat($msgOptions['modify_time']) : '',
2944
					'timestamp' => isset($msgOptions['modify_time']) ? forum_time(true, $msgOptions['modify_time']) : 0,
2945
					'name' => isset($msgOptions['modify_time']) ? $msgOptions['modify_name'] : '',
2946
				),
2947
				'subject' => isset($msgOptions['subject']) ? $msgOptions['subject'] : '',
2948
			);
2949
2950
			censorText($context['message']['subject']);
2951
		}
2952
		else
2953
		{
2954
			$context['message'] = array(
2955
				'id' => $row['id_msg'],
2956
				'errors' => array(),
2957
				'error_in_subject' => in_array('no_subject', $post_errors),
2958
				'error_in_body' => in_array('no_message', $post_errors) || in_array('long_message', $post_errors),
2959
			);
2960
2961
			loadLanguage('Errors');
2962
			foreach ($post_errors as $post_error)
2963
			{
2964
				if ($post_error == 'long_message')
2965
					$context['message']['errors'][] = sprintf($txt['error_' . $post_error], $modSettings['max_messageLength']);
2966
				else
2967
					$context['message']['errors'][] = $txt['error_' . $post_error];
2968
			}
2969
		}
2970
	}
2971
	else
2972
		obExit(false);
2973
}
2974
2975
?>