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

Poll.php ➔ EditPoll()   F

Complexity

Conditions 49
Paths > 20000

Size

Total Lines 279
Code Lines 158

Duplication

Lines 22
Ratio 7.89 %

Importance

Changes 0
Metric Value
cc 49
eloc 158
nc 57600
nop 0
dl 22
loc 279
rs 2
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
 * This file contains the functions for voting, locking, removing and
5
 * editing polls. Note that that posting polls is done in Post.php.
6
 *
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
 * Allow the user to vote.
22
 * It is called to register a vote in a poll.
23
 * Must be called with a topic and option specified.
24
 * Requires the poll_vote permission.
25
 * Upon successful completion of action will direct user back to topic.
26
 * Accessed via ?action=vote.
27
 *
28
 * @uses Post language file.
29
 */
30
function Vote()
31
{
32
	global $topic, $user_info, $smcFunc, $sourcedir, $modSettings;
33
34
	// Make sure you can vote.
35
	isAllowedTo('poll_vote');
36
37
	loadLanguage('Post');
38
39
	// Check if they have already voted, or voting is locked.
40
	$request = $smcFunc['db_query']('', '
41
		SELECT COALESCE(lp.id_choice, -1) AS selected, p.voting_locked, p.id_poll, p.expire_time, p.max_votes, p.change_vote,
42
			p.guest_vote, p.reset_poll, p.num_guest_voters
43
		FROM {db_prefix}topics AS t
44
			INNER JOIN {db_prefix}polls AS p ON (p.id_poll = t.id_poll)
45
			LEFT JOIN {db_prefix}log_polls AS lp ON (p.id_poll = lp.id_poll AND lp.id_member = {int:current_member} AND lp.id_member != {int:not_guest})
46
		WHERE t.id_topic = {int:current_topic}
47
		LIMIT 1',
48
		array(
49
			'current_member' => $user_info['id'],
50
			'current_topic' => $topic,
51
			'not_guest' => 0,
52
		)
53
	);
54
	if ($smcFunc['db_num_rows']($request) == 0)
55
		fatal_lang_error('poll_error', false);
56
	$row = $smcFunc['db_fetch_assoc']($request);
57
	$smcFunc['db_free_result']($request);
58
59
	// If this is a guest can they vote?
60
	if ($user_info['is_guest'])
61
	{
62
		// Guest voting disabled?
63
		if (!$row['guest_vote'])
64
			fatal_lang_error('guest_vote_disabled');
65
		// Guest already voted?
66
		elseif (!empty($_COOKIE['guest_poll_vote']) && preg_match('~^[0-9,;]+$~', $_COOKIE['guest_poll_vote']) && strpos($_COOKIE['guest_poll_vote'], ';' . $row['id_poll'] . ',') !== false)
67
		{
68
			// ;id,timestamp,[vote,vote...]; etc
0 ignored issues
show
Unused Code Comprehensibility introduced by
54% 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...
69
			$guestinfo = explode(';', $_COOKIE['guest_poll_vote']);
70
			// Find the poll we're after.
71
			foreach ($guestinfo as $i => $guestvoted)
72
			{
73
				$guestvoted = explode(',', $guestvoted);
74
				if ($guestvoted[0] == $row['id_poll'])
75
					break;
76
			}
77
			// Has the poll been reset since guest voted?
78
			if ($row['reset_poll'] > $guestvoted[1])
0 ignored issues
show
Bug introduced by
The variable $guestvoted 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...
79
			{
80
				// Remove the poll info from the cookie to allow guest to vote again
81
				unset($guestinfo[$i]);
82
				if (!empty($guestinfo))
83
					$_COOKIE['guest_poll_vote'] = ';' . implode(';', $guestinfo);
84
				else
85
					unset($_COOKIE['guest_poll_vote']);
86
			}
87
			else
88
				fatal_lang_error('poll_error', false);
89
			unset($guestinfo, $guestvoted, $i);
90
		}
91
	}
92
93
	// Is voting locked or has it expired?
94
	if (!empty($row['voting_locked']) || (!empty($row['expire_time']) && time() > $row['expire_time']))
95
		fatal_lang_error('poll_error', false);
96
97
	// If they have already voted and aren't allowed to change their vote - hence they are outta here!
98
	if (!$user_info['is_guest'] && $row['selected'] != -1 && empty($row['change_vote']))
99
		fatal_lang_error('poll_error', false);
100
	// Otherwise if they can change their vote yet they haven't sent any options... remove their vote and redirect.
101
	elseif (!empty($row['change_vote']) && !$user_info['is_guest'] && empty($_POST['options']))
102
	{
103
		checkSession('request');
104
		$pollOptions = array();
105
106
		// Find out what they voted for before.
107
		$request = $smcFunc['db_query']('', '
108
			SELECT id_choice
109
			FROM {db_prefix}log_polls
110
			WHERE id_member = {int:current_member}
111
				AND id_poll = {int:id_poll}',
112
			array(
113
				'current_member' => $user_info['id'],
114
				'id_poll' => $row['id_poll'],
115
			)
116
		);
117
		while ($choice = $smcFunc['db_fetch_row']($request))
118
			$pollOptions[] = $choice[0];
119
		$smcFunc['db_free_result']($request);
120
121
		// Just skip it if they had voted for nothing before.
122
		if (!empty($pollOptions))
123
		{
124
			// Update the poll totals.
125
			$smcFunc['db_query']('', '
126
				UPDATE {db_prefix}poll_choices
127
				SET votes = votes - 1
128
				WHERE id_poll = {int:id_poll}
129
					AND id_choice IN ({array_int:poll_options})
130
					AND votes > {int:votes}',
131
				array(
132
					'poll_options' => $pollOptions,
133
					'id_poll' => $row['id_poll'],
134
					'votes' => 0,
135
				)
136
			);
137
138
			// Delete off the log.
139
			$smcFunc['db_query']('', '
140
				DELETE FROM {db_prefix}log_polls
141
				WHERE id_member = {int:current_member}
142
					AND id_poll = {int:id_poll}',
143
				array(
144
					'current_member' => $user_info['id'],
145
					'id_poll' => $row['id_poll'],
146
				)
147
			);
148
		}
149
150
		// Redirect back to the topic so the user can vote again!
151
		if (empty($_POST['options']))
152
			redirectexit('topic=' . $topic . '.' . $_REQUEST['start']);
153
	}
154
155
	checkSession('request');
156
157
	// Make sure the option(s) are valid.
158
	if (empty($_POST['options']))
159
		fatal_lang_error('didnt_select_vote', false);
160
161
	// Too many options checked!
162
	if (count($_REQUEST['options']) > $row['max_votes'])
163
		fatal_lang_error('poll_too_many_votes', false, array($row['max_votes']));
164
165
	$pollOptions = array();
166
	$inserts = array();
167 View Code Duplication
	foreach ($_REQUEST['options'] as $id)
168
	{
169
		$id = (int) $id;
170
171
		$pollOptions[] = $id;
172
		$inserts[] = array($row['id_poll'], $user_info['id'], $id);
173
	}
174
175
	// Add their vote to the tally.
176
	$smcFunc['db_insert']('insert',
177
		'{db_prefix}log_polls',
178
		array('id_poll' => 'int', 'id_member' => 'int', 'id_choice' => 'int'),
179
		$inserts,
180
		array('id_poll', 'id_member', 'id_choice')
181
	);
182
183
	$smcFunc['db_query']('', '
184
		UPDATE {db_prefix}poll_choices
185
		SET votes = votes + 1
186
		WHERE id_poll = {int:id_poll}
187
			AND id_choice IN ({array_int:poll_options})',
188
		array(
189
			'poll_options' => $pollOptions,
190
			'id_poll' => $row['id_poll'],
191
		)
192
	);
193
194
	// If it's a guest don't let them vote again.
195
	if ($user_info['is_guest'] && count($pollOptions) > 0)
196
	{
197
		// Time is stored in case the poll is reset later, plus what they voted for.
198
		$_COOKIE['guest_poll_vote'] = empty($_COOKIE['guest_poll_vote']) ? '' : $_COOKIE['guest_poll_vote'];
199
		// ;id,timestamp,[vote,vote...]; etc
0 ignored issues
show
Unused Code Comprehensibility introduced by
54% 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...
200
		$_COOKIE['guest_poll_vote'] .= ';' . $row['id_poll'] . ',' . time() . ',' . implode(',', $pollOptions);
201
202
		// Increase num guest voters count by 1
203
		$smcFunc['db_query']('', '
204
			UPDATE {db_prefix}polls
205
			SET num_guest_voters = num_guest_voters + 1
206
			WHERE id_poll = {int:id_poll}',
207
			array(
208
				'id_poll' => $row['id_poll'],
209
			)
210
		);
211
212
		require_once($sourcedir . '/Subs-Auth.php');
213
		$cookie_url = url_parts(!empty($modSettings['localCookies']), !empty($modSettings['globalCookies']));
214
		smf_setcookie('guest_poll_vote', $_COOKIE['guest_poll_vote'], time() + 2500000, $cookie_url[1], $cookie_url[0], false, false);
215
	}
216
217
	// Maybe let a social networking mod log this, or something?
218
	call_integration_hook('integrate_poll_vote', array(&$row['id_poll'], &$pollOptions));
219
220
	// Return to the post...
221
	redirectexit('topic=' . $topic . '.' . $_REQUEST['start']);
222
}
223
224
/**
225
 * Lock the voting for a poll.
226
 * Must be called with a topic specified in the URL.
227
 * An admin always has over riding permission to lock a poll.
228
 * If not an admin must have poll_lock_any permission, otherwise must
229
 * be poll starter with poll_lock_own permission.
230
 * Upon successful completion of action will direct user back to topic.
231
 * Accessed via ?action=lockvoting.
232
 */
233
function LockVoting()
234
{
235
	global $topic, $user_info, $smcFunc;
236
237
	checkSession('get');
238
239
	// Get the poll starter, ID, and whether or not it is locked.
240
	$request = $smcFunc['db_query']('', '
241
		SELECT t.id_member_started, t.id_poll, p.voting_locked
242
		FROM {db_prefix}topics AS t
243
			INNER JOIN {db_prefix}polls AS p ON (p.id_poll = t.id_poll)
244
		WHERE t.id_topic = {int:current_topic}
245
		LIMIT 1',
246
		array(
247
			'current_topic' => $topic,
248
		)
249
	);
250
	list ($memberID, $pollID, $voting_locked) = $smcFunc['db_fetch_row']($request);
251
252
	// If the user _can_ modify the poll....
253
	if (!allowedTo('poll_lock_any'))
254
		isAllowedTo('poll_lock_' . ($user_info['id'] == $memberID ? 'own' : 'any'));
255
256
	// It's been locked by a non-moderator.
257
	if ($voting_locked == '1')
258
		$voting_locked = '0';
259
	// Locked by a moderator, and this is a moderator.
260
	elseif ($voting_locked == '2' && allowedTo('moderate_board'))
261
		$voting_locked = '0';
262
	// Sorry, a moderator locked it.
263
	elseif ($voting_locked == '2' && !allowedTo('moderate_board'))
264
		fatal_lang_error('locked_by_admin', 'user');
265
	// A moderator *is* locking it.
266
	elseif ($voting_locked == '0' && allowedTo('moderate_board'))
267
		$voting_locked = '2';
268
	// Well, it's gonna be locked one way or another otherwise...
269
	else
270
		$voting_locked = '1';
271
272
	// Lock!  *Poof* - no one can vote.
273
	$smcFunc['db_query']('', '
274
		UPDATE {db_prefix}polls
275
		SET voting_locked = {int:voting_locked}
276
		WHERE id_poll = {int:id_poll}',
277
		array(
278
			'voting_locked' => $voting_locked,
279
			'id_poll' => $pollID,
280
		)
281
	);
282
283
	logAction(($voting_locked ? '' : 'un') . 'lock_poll', array('topic' => $topic));
284
285
	redirectexit('topic=' . $topic . '.' . $_REQUEST['start']);
286
}
287
288
/**
289
 * Display screen for editing or adding a poll.
290
 * Must be called with a topic specified in the URL.
291
 * If the user is adding a poll to a topic, must contain the variable
292
 * 'add' in the url.
293
 * User must have poll_edit_any/poll_add_any permission for the
294
 * relevant action, otherwise must be poll starter with poll_edit_own
295
 * permission for editing, or be topic starter with poll_add_any permission for adding.
296
 * Accessed via ?action=editpoll.
297
 *
298
 * @uses Post language file.
299
 * @uses Poll template, main sub-template.
300
 */
301
function EditPoll()
302
{
303
	global $txt, $user_info, $context, $topic, $board, $smcFunc, $sourcedir, $scripturl;
304
305
	if (empty($topic))
306
		fatal_lang_error('no_access', false);
307
308
	loadLanguage('Post');
309
	loadTemplate('Poll');
310
311
	$context['start'] = (int) $_REQUEST['start'];
312
	$context['is_edit'] = isset($_REQUEST['add']) ? 0 : 1;
313
314
	// Check if a poll currently exists on this topic, and get the id, question and starter.
315
	$request = $smcFunc['db_query']('', '
316
		SELECT
317
			t.id_member_started, p.id_poll, p.question, p.hide_results, p.expire_time, p.max_votes, p.change_vote,
318
			m.subject, p.guest_vote, p.id_member AS poll_starter
319
		FROM {db_prefix}topics AS t
320
			INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)
321
			LEFT JOIN {db_prefix}polls AS p ON (p.id_poll = t.id_poll)
322
		WHERE t.id_topic = {int:current_topic}
323
		LIMIT 1',
324
		array(
325
			'current_topic' => $topic,
326
		)
327
	);
328
329
	// Assume the the topic exists, right?
330
	if ($smcFunc['db_num_rows']($request) == 0)
331
		fatal_lang_error('no_board');
332
	// Get the poll information.
333
	$pollinfo = $smcFunc['db_fetch_assoc']($request);
334
	$smcFunc['db_free_result']($request);
335
336
	// If we are adding a new poll - make sure that there isn't already a poll there.
337 View Code Duplication
	if (!$context['is_edit'] && !empty($pollinfo['id_poll']))
338
		fatal_lang_error('poll_already_exists');
339
	// Otherwise, if we're editing it, it does exist I assume?
340
	elseif ($context['is_edit'] && empty($pollinfo['id_poll']))
341
		fatal_lang_error('poll_not_found');
342
343
	// Can you do this?
344 View Code Duplication
	if ($context['is_edit'] && !allowedTo('poll_edit_any'))
345
		isAllowedTo('poll_edit_' . ($user_info['id'] == $pollinfo['id_member_started'] || ($pollinfo['poll_starter'] != 0 && $user_info['id'] == $pollinfo['poll_starter']) ? 'own' : 'any'));
346
	elseif (!$context['is_edit'] && !allowedTo('poll_add_any'))
347
		isAllowedTo('poll_add_' . ($user_info['id'] == $pollinfo['id_member_started'] ? 'own' : 'any'));
348
	$context['can_moderate_poll'] = isset($_REQUEST['add']) ? true : allowedTo('poll_edit_' . ($user_info['id'] == $pollinfo['id_member_started'] || ($pollinfo['poll_starter'] != 0 && $user_info['id'] == $pollinfo['poll_starter']) ? 'own' : 'any'));
349
350
	// Do we enable guest voting?
351
	require_once($sourcedir . '/Subs-Members.php');
352
	$groupsAllowedVote = groupsAllowedTo('poll_vote', $board);
353
354
	// Want to make sure before you actually submit?  Must be a lot of options, or something.
355
	if (isset($_POST['preview']))
356
	{
357
		$question = $smcFunc['htmlspecialchars']($_POST['question']);
358
359
		// Basic theme info...
360
		$context['poll'] = array(
361
			'id' => $pollinfo['id_poll'],
362
			'question' => $question,
363
			'hide_results' => empty($_POST['poll_hide']) ? 0 : $_POST['poll_hide'],
364
			'change_vote' => isset($_POST['poll_change_vote']),
365
			'guest_vote' => isset($_POST['poll_guest_vote']),
366
			'guest_vote_allowed' => in_array(-1, $groupsAllowedVote['allowed']),
367
			'max_votes' => empty($_POST['poll_max_votes']) ? '1' : max(1, $_POST['poll_max_votes']),
368
		);
369
370
		// Start at number one with no last id to speak of.
371
		$number = 1;
372
		$last_id = 0;
373
374
		// Get all the choices - if this is an edit.
375
		if ($context['is_edit'])
376
		{
377
			$request = $smcFunc['db_query']('', '
378
				SELECT label, votes, id_choice
379
				FROM {db_prefix}poll_choices
380
				WHERE id_poll = {int:id_poll}',
381
				array(
382
					'id_poll' => $pollinfo['id_poll'],
383
				)
384
			);
385
			$context['choices'] = array();
386
			while ($row = $smcFunc['db_fetch_assoc']($request))
387
			{
388
				// Get the highest id so we can add more without reusing.
389
				if ($row['id_choice'] >= $last_id)
390
					$last_id = $row['id_choice'] + 1;
391
392
				// They cleared this by either omitting it or emptying it.
393
				if (!isset($_POST['options'][$row['id_choice']]) || $_POST['options'][$row['id_choice']] == '')
394
					continue;
395
396
				censorText($row['label']);
397
398
				// Add the choice!
399
				$context['choices'][$row['id_choice']] = array(
400
					'id' => $row['id_choice'],
401
					'number' => $number++,
402
					'votes' => $row['votes'],
403
					'label' => $row['label'],
404
					'is_last' => false
405
				);
406
			}
407
			$smcFunc['db_free_result']($request);
408
		}
409
410
		// Work out how many options we have, so we get the 'is_last' field right...
411
		$totalPostOptions = 0;
412
		foreach ($_POST['options'] as $id => $label)
413
			if ($label != '')
414
				$totalPostOptions++;
415
416
		$count = 1;
417
		// If an option exists, update it.  If it is new, add it - but don't reuse ids!
418
		foreach ($_POST['options'] as $id => $label)
419
		{
420
			$label = $smcFunc['htmlspecialchars']($label);
421
			censorText($label);
422
423
			if (isset($context['choices'][$id]))
424
				$context['choices'][$id]['label'] = $label;
425
			elseif ($label != '')
426
				$context['choices'][] = array(
427
					'id' => $last_id++,
428
					'number' => $number++,
429
					'label' => $label,
430
					'votes' => -1,
431
					'is_last' => $count++ == $totalPostOptions && $totalPostOptions > 1 ? true : false,
432
				);
433
		}
434
435
		// Make sure we have two choices for sure!
436
		if ($totalPostOptions < 2)
437
		{
438
			// Need two?
439 View Code Duplication
			if ($totalPostOptions == 0)
440
				$context['choices'][] = array(
441
					'id' => $last_id++,
442
					'number' => $number++,
443
					'label' => '',
444
					'votes' => -1,
445
					'is_last' => false
446
				);
447
			$poll_errors[] = 'poll_few';
0 ignored issues
show
Coding Style Comprehensibility introduced by
$poll_errors was never initialized. Although not strictly required by PHP, it is generally a good practice to add $poll_errors = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
448
		}
449
450
		// Always show one extra box...
451
		$context['choices'][] = array(
452
			'id' => $last_id++,
453
			'number' => $number++,
454
			'label' => '',
455
			'votes' => -1,
456
			'is_last' => true
457
		);
458
459
		$context['last_choice_id'] = $last_id;
460
461
		if ($context['can_moderate_poll'])
462
			$context['poll']['expiration'] = $_POST['poll_expire'];
463
464
		// Check the question/option count for errors.
465
		if (trim($_POST['question']) == '' && empty($context['poll_error']))
466
			$poll_errors[] = 'no_question';
0 ignored issues
show
Bug introduced by
The variable $poll_errors 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...
467
468
		// No check is needed, since nothing is really posted.
469
		checkSubmitOnce('free');
470
471
		// Take a check for any errors... assuming we haven't already done so!
472
		if (!empty($poll_errors) && empty($context['poll_error']))
473
		{
474
			loadLanguage('Errors');
475
476
			$context['poll_error'] = array('messages' => array());
477 View Code Duplication
			foreach ($poll_errors as $poll_error)
478
			{
479
				$context['poll_error'][$poll_error] = true;
480
				$context['poll_error']['messages'][] = $txt['error_' . $poll_error];
481
			}
482
		}
483
	}
484
	else
485
	{
486
		// Basic theme info...
487
		$context['poll'] = array(
488
			'id' => $pollinfo['id_poll'],
489
			'question' => $pollinfo['question'],
490
			'hide_results' => $pollinfo['hide_results'],
491
			'max_votes' => $pollinfo['max_votes'],
492
			'change_vote' => !empty($pollinfo['change_vote']),
493
			'guest_vote' => !empty($pollinfo['guest_vote']),
494
			'guest_vote_allowed' => in_array(-1, $groupsAllowedVote['allowed']),
495
		);
496
497
		// Poll expiration time?
498
		$context['poll']['expiration'] = empty($pollinfo['expire_time']) || !$context['can_moderate_poll'] ? '' : ceil($pollinfo['expire_time'] <= time() ? -1 : ($pollinfo['expire_time'] - time()) / (3600 * 24));
499
500
		// Get all the choices - if this is an edit.
501
		if ($context['is_edit'])
502
		{
503
			$request = $smcFunc['db_query']('', '
504
				SELECT label, votes, id_choice
505
				FROM {db_prefix}poll_choices
506
				WHERE id_poll = {int:id_poll}',
507
				array(
508
					'id_poll' => $pollinfo['id_poll'],
509
				)
510
			);
511
			$context['choices'] = array();
512
			$number = 1;
513
			while ($row = $smcFunc['db_fetch_assoc']($request))
514
			{
515
				censorText($row['label']);
516
517
				$context['choices'][$row['id_choice']] = array(
518
					'id' => $row['id_choice'],
519
					'number' => $number++,
520
					'votes' => $row['votes'],
521
					'label' => $row['label'],
522
					'is_last' => false
523
				);
524
			}
525
			$smcFunc['db_free_result']($request);
526
527
			$last_id = max(array_keys($context['choices'])) + 1;
528
529
			// Add an extra choice...
530
			$context['choices'][] = array(
531
				'id' => $last_id,
532
				'number' => $number,
533
				'votes' => -1,
534
				'label' => '',
535
				'is_last' => true
536
			);
537
			$context['last_choice_id'] = $last_id;
538
		}
539
		// New poll?
540
		else
541
		{
542
			// Setup the default poll options.
543
			$context['poll'] = array(
544
				'id' => 0,
545
				'question' => '',
546
				'hide_results' => 0,
547
				'max_votes' => 1,
548
				'change_vote' => 0,
549
				'guest_vote' => 0,
550
				'guest_vote_allowed' => in_array(-1, $groupsAllowedVote['allowed']),
551
				'expiration' => '',
552
			);
553
554
			// Make all five poll choices empty.
555
			$context['choices'] = array(
556
				array('id' => 0, 'number' => 1, 'votes' => -1, 'label' => '', 'is_last' => false),
557
				array('id' => 1, 'number' => 2, 'votes' => -1, 'label' => '', 'is_last' => false),
558
				array('id' => 2, 'number' => 3, 'votes' => -1, 'label' => '', 'is_last' => false),
559
				array('id' => 3, 'number' => 4, 'votes' => -1, 'label' => '', 'is_last' => false),
560
				array('id' => 4, 'number' => 5, 'votes' => -1, 'label' => '', 'is_last' => true)
561
			);
562
			$context['last_choice_id'] = 4;
563
		}
564
	}
565
	$context['page_title'] = $context['is_edit'] ? $txt['poll_edit'] : $txt['add_poll'];
566
567
	// Build the link tree.
568
	censorText($pollinfo['subject']);
569
	$context['linktree'][] = array(
570
		'url' => $scripturl . '?topic=' . $topic . '.0',
571
		'name' => $pollinfo['subject'],
572
	);
573
	$context['linktree'][] = array(
574
		'name' => $context['page_title'],
575
	);
576
577
	// Register this form in the session variables.
578
	checkSubmitOnce('register');
579
}
580
581
/**
582
 * Update the settings for a poll, or add a new one.
583
 * Must be called with a topic specified in the URL.
584
 * The user must have poll_edit_any/poll_add_any permission
585
 * for the relevant action. Otherwise they must be poll starter
586
 * with poll_edit_own permission for editing, or be topic starter
587
 * with poll_add_any permission for adding.
588
 * In the case of an error, this function will redirect back to
589
 * EditPoll and display the relevant error message.
590
 * Upon successful completion of action will direct user back to topic.
591
 * Accessed via ?action=editpoll2.
592
 */
593
function EditPoll2()
594
{
595
	global $txt, $topic, $board, $context;
596
	global $user_info, $smcFunc, $sourcedir;
597
598
	// Sneaking off, are we?
599
	if (empty($_POST))
600
		redirectexit('action=editpoll;topic=' . $topic . '.0');
601
602
	if (checkSession('post', '', false) != '')
603
		$poll_errors[] = 'session_timeout';
0 ignored issues
show
Coding Style Comprehensibility introduced by
$poll_errors was never initialized. Although not strictly required by PHP, it is generally a good practice to add $poll_errors = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
604
605
	if (isset($_POST['preview']))
606
		return EditPoll();
607
608
	// HACKERS (!!) can't edit :P.
609
	if (empty($topic))
610
		fatal_lang_error('no_access', false);
611
612
	// Is this a new poll, or editing an existing?
613
	$isEdit = isset($_REQUEST['add']) ? 0 : 1;
614
615
	// Get the starter and the poll's ID - if it's an edit.
616
	$request = $smcFunc['db_query']('', '
617
		SELECT t.id_member_started, t.id_poll, p.id_member AS poll_starter, p.expire_time
618
		FROM {db_prefix}topics AS t
619
			LEFT JOIN {db_prefix}polls AS p ON (p.id_poll = t.id_poll)
620
		WHERE t.id_topic = {int:current_topic}
621
		LIMIT 1',
622
		array(
623
			'current_topic' => $topic,
624
		)
625
	);
626
	if ($smcFunc['db_num_rows']($request) == 0)
627
		fatal_lang_error('no_board');
628
	$bcinfo = $smcFunc['db_fetch_assoc']($request);
629
	$smcFunc['db_free_result']($request);
630
631
	// Check their adding/editing is valid.
632 View Code Duplication
	if (!$isEdit && !empty($bcinfo['id_poll']))
633
		fatal_lang_error('poll_already_exists');
634
	// Are we editing a poll which doesn't exist?
635
	elseif ($isEdit && empty($bcinfo['id_poll']))
636
		fatal_lang_error('poll_not_found');
637
638
	// Check if they have the power to add or edit the poll.
639 View Code Duplication
	if ($isEdit && !allowedTo('poll_edit_any'))
640
		isAllowedTo('poll_edit_' . ($user_info['id'] == $bcinfo['id_member_started'] || ($bcinfo['poll_starter'] != 0 && $user_info['id'] == $bcinfo['poll_starter']) ? 'own' : 'any'));
641
	elseif (!$isEdit && !allowedTo('poll_add_any'))
642
		isAllowedTo('poll_add_' . ($user_info['id'] == $bcinfo['id_member_started'] ? 'own' : 'any'));
643
644
	$optionCount = 0;
645
	$idCount = 0;
646
	// Ensure the user is leaving a valid amount of options - there must be at least two.
647
	foreach ($_POST['options'] as $k => $option)
648
	{
649
		if (trim($option) != '')
650
		{
651
			$optionCount++;
652
			$idCount = max($idCount, $k);
653
		}
654
	}
655
	if ($optionCount < 2)
656
		$poll_errors[] = 'poll_few';
0 ignored issues
show
Bug introduced by
The variable $poll_errors 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...
657
	elseif ($optionCount > 256 || $idCount > 255)
658
		$poll_errors[] = 'poll_many';
659
660
	// Also - ensure they are not removing the question.
661
	if (trim($_POST['question']) == '')
662
		$poll_errors[] = 'no_question';
663
664
	// Got any errors to report?
665
	if (!empty($poll_errors))
666
	{
667
		loadLanguage('Errors');
668
		// Previewing.
669
		$_POST['preview'] = true;
670
671
		$context['poll_error'] = array('messages' => array());
672 View Code Duplication
		foreach ($poll_errors as $poll_error)
673
		{
674
			$context['poll_error'][$poll_error] = true;
675
			$context['poll_error']['messages'][] = $txt['error_' . $poll_error];
676
		}
677
678
		return EditPoll();
679
	}
680
681
	// Prevent double submission of this form.
682
	checkSubmitOnce('check');
683
684
	// Now we've done all our error checking, let's get the core poll information cleaned... question first.
685
	$_POST['question'] = $smcFunc['htmlspecialchars']($_POST['question']);
686
	$_POST['question'] = $smcFunc['truncate']($_POST['question'], 255);
687
688
	$_POST['poll_hide'] = (int) $_POST['poll_hide'];
689
	$_POST['poll_expire'] = isset($_POST['poll_expire']) ? (int) $_POST['poll_expire'] : 0;
690
	$_POST['poll_change_vote'] = isset($_POST['poll_change_vote']) ? 1 : 0;
691
	$_POST['poll_guest_vote'] = isset($_POST['poll_guest_vote']) ? 1 : 0;
692
693
	// Make sure guests are actually allowed to vote generally.
694 View Code Duplication
	if ($_POST['poll_guest_vote'])
695
	{
696
		require_once($sourcedir . '/Subs-Members.php');
697
		$allowedGroups = groupsAllowedTo('poll_vote', $board);
698
		if (!in_array(-1, $allowedGroups['allowed']))
699
			$_POST['poll_guest_vote'] = 0;
700
	}
701
702
	// Ensure that the number options allowed makes sense, and the expiration date is valid.
703
	if (!$isEdit || allowedTo('moderate_board'))
704
	{
705
		$_POST['poll_expire'] = $_POST['poll_expire'] > 9999 ? 9999 : ($_POST['poll_expire'] < 0 ? 0 : $_POST['poll_expire']);
706
707
		if (empty($_POST['poll_expire']) && $_POST['poll_hide'] == 2)
708
			$_POST['poll_hide'] = 1;
709
		elseif (!$isEdit || $_POST['poll_expire'] != ceil($bcinfo['expire_time'] <= time() ? -1 : ($bcinfo['expire_time'] - time()) / (3600 * 24)))
710
			$_POST['poll_expire'] = empty($_POST['poll_expire']) ? '0' : time() + $_POST['poll_expire'] * 3600 * 24;
711
		else
712
			$_POST['poll_expire'] = $bcinfo['expire_time'];
713
714
		if (empty($_POST['poll_max_votes']) || $_POST['poll_max_votes'] <= 0)
715
			$_POST['poll_max_votes'] = 1;
716
		else
717
			$_POST['poll_max_votes'] = (int) $_POST['poll_max_votes'];
718
	}
719
720
	// If we're editing, let's commit the changes.
721
	if ($isEdit)
722
	{
723
		$smcFunc['db_query']('', '
724
			UPDATE {db_prefix}polls
725
			SET question = {string:question}, change_vote = {int:change_vote},' . (allowedTo('moderate_board') ? '
726
				hide_results = {int:hide_results}, expire_time = {int:expire_time}, max_votes = {int:max_votes},
727
				guest_vote = {int:guest_vote}' : '
728
				hide_results = CASE WHEN expire_time = {int:expire_time_zero} AND {int:hide_results} = 2 THEN 1 ELSE {int:hide_results} END') . '
729
			WHERE id_poll = {int:id_poll}',
730
			array(
731
				'change_vote' => $_POST['poll_change_vote'],
732
				'hide_results' => $_POST['poll_hide'],
733
				'expire_time' => !empty($_POST['poll_expire']) ? $_POST['poll_expire'] : 0,
734
				'max_votes' => !empty($_POST['poll_max_votes']) ? $_POST['poll_max_votes'] : 0,
735
				'guest_vote' => $_POST['poll_guest_vote'],
736
				'expire_time_zero' => 0,
737
				'id_poll' => $bcinfo['id_poll'],
738
				'question' => $_POST['question'],
739
			)
740
		);
741
	}
742
	// Otherwise, let's get our poll going!
743
	else
744
	{
745
		// Create the poll.
746
		$bcinfo['id_poll'] = $smcFunc['db_insert']('',
747
			'{db_prefix}polls',
748
			array(
749
				'question' => 'string-255', 'hide_results' => 'int', 'max_votes' => 'int', 'expire_time' => 'int', 'id_member' => 'int',
750
				'poster_name' => 'string-255', 'change_vote' => 'int', 'guest_vote' => 'int'
751
			),
752
			array(
753
				$_POST['question'], $_POST['poll_hide'], $_POST['poll_max_votes'], $_POST['poll_expire'], $user_info['id'],
754
				$user_info['username'], $_POST['poll_change_vote'], $_POST['poll_guest_vote'],
755
			),
756
			array('id_poll'),
757
			1
758
		);
759
760
		// Link the poll to the topic
761
		$smcFunc['db_query']('', '
762
			UPDATE {db_prefix}topics
763
			SET id_poll = {int:id_poll}
764
			WHERE id_topic = {int:current_topic}',
765
			array(
766
				'current_topic' => $topic,
767
				'id_poll' => $bcinfo['id_poll'],
768
			)
769
		);
770
	}
771
772
	// Get all the choices.  (no better way to remove all emptied and add previously non-existent ones.)
773
	$request = $smcFunc['db_query']('', '
774
		SELECT id_choice
775
		FROM {db_prefix}poll_choices
776
		WHERE id_poll = {int:id_poll}',
777
		array(
778
			'id_poll' => $bcinfo['id_poll'],
779
		)
780
	);
781
	$choices = array();
782
	while ($row = $smcFunc['db_fetch_assoc']($request))
783
		$choices[] = $row['id_choice'];
784
	$smcFunc['db_free_result']($request);
785
786
	$delete_options = array();
787
	foreach ($_POST['options'] as $k => $option)
788
	{
789
		// Make sure the key is numeric for sanity's sake.
790
		$k = (int) $k;
791
792
		// They've cleared the box.  Either they want it deleted, or it never existed.
793
		if (trim($option) == '')
794
		{
795
			// They want it deleted.  Bye.
796
			if (in_array($k, $choices))
797
				$delete_options[] = $k;
798
799
			// Skip the rest...
800
			continue;
801
		}
802
803
		// Dress the option up for its big date with the database.
804
		$option = $smcFunc['htmlspecialchars']($option);
805
806
		// If it's already there, update it.  If it's not... add it.
807
		if (in_array($k, $choices))
808
			$smcFunc['db_query']('', '
809
				UPDATE {db_prefix}poll_choices
810
				SET label = {string:option_name}
811
				WHERE id_poll = {int:id_poll}
812
					AND id_choice = {int:id_choice}',
813
				array(
814
					'id_poll' => $bcinfo['id_poll'],
815
					'id_choice' => $k,
816
					'option_name' => $option,
817
				)
818
			);
819
		else
820
			$smcFunc['db_insert']('',
821
				'{db_prefix}poll_choices',
822
				array(
823
					'id_poll' => 'int', 'id_choice' => 'int', 'label' => 'string-255', 'votes' => 'int',
824
				),
825
				array(
826
					$bcinfo['id_poll'], $k, $option, 0,
827
				),
828
				array()
829
			);
830
	}
831
832
	// I'm sorry, but... well, no one was choosing you.  Poor options, I'll put you out of your misery.
833 View Code Duplication
	if (!empty($delete_options))
834
	{
835
		$smcFunc['db_query']('', '
836
			DELETE FROM {db_prefix}log_polls
837
			WHERE id_poll = {int:id_poll}
838
				AND id_choice IN ({array_int:delete_options})',
839
			array(
840
				'delete_options' => $delete_options,
841
				'id_poll' => $bcinfo['id_poll'],
842
			)
843
		);
844
		$smcFunc['db_query']('', '
845
			DELETE FROM {db_prefix}poll_choices
846
			WHERE id_poll = {int:id_poll}
847
				AND id_choice IN ({array_int:delete_options})',
848
			array(
849
				'delete_options' => $delete_options,
850
				'id_poll' => $bcinfo['id_poll'],
851
			)
852
		);
853
	}
854
855
	// Shall I reset the vote count, sir?
856
	if (isset($_POST['resetVoteCount']))
857
	{
858
		$smcFunc['db_query']('', '
859
			UPDATE {db_prefix}polls
860
			SET num_guest_voters = {int:no_votes}, reset_poll = {int:time}
861
			WHERE id_poll = {int:id_poll}',
862
			array(
863
				'no_votes' => 0,
864
				'id_poll' => $bcinfo['id_poll'],
865
				'time' => time(),
866
			)
867
		);
868
		$smcFunc['db_query']('', '
869
			UPDATE {db_prefix}poll_choices
870
			SET votes = {int:no_votes}
871
			WHERE id_poll = {int:id_poll}',
872
			array(
873
				'no_votes' => 0,
874
				'id_poll' => $bcinfo['id_poll'],
875
			)
876
		);
877
		$smcFunc['db_query']('', '
878
			DELETE FROM {db_prefix}log_polls
879
			WHERE id_poll = {int:id_poll}',
880
			array(
881
				'id_poll' => $bcinfo['id_poll'],
882
			)
883
		);
884
	}
885
886
	call_integration_hook('integrate_poll_add_edit', array($bcinfo['id_poll'], $isEdit));
887
888
	/* Log this edit, but don't go crazy.
889
		Only specifically adding a poll	or resetting votes is logged.
890
		Everything else is simply an edit.*/
891
	if (isset($_REQUEST['add']))
892
	{
893
		// Added a poll
894
		logAction('add_poll', array('topic' => $topic));
895
	}
896
	elseif (isset($_REQUEST['deletevotes']))
897
	{
898
		// Reset votes
899
		logAction('reset_poll', array('topic' => $topic));
900
	}
901
	else
902
	{
903
		// Something else
904
		logAction('editpoll', array('topic' => $topic));
905
	}
906
907
	// Off we go.
908
	redirectexit('topic=' . $topic . '.' . $_REQUEST['start']);
909
}
910
911
/**
912
 * Remove a poll from a topic without removing the topic.
913
 * Must be called with a topic specified in the URL.
914
 * Requires poll_remove_any permission, unless it's the poll starter
915
 * with poll_remove_own permission.
916
 * Upon successful completion of action will direct user back to topic.
917
 * Accessed via ?action=removepoll.
918
 */
919
function RemovePoll()
920
{
921
	global $topic, $user_info, $smcFunc;
922
923
	// Make sure the topic is not empty.
924
	if (empty($topic))
925
		fatal_lang_error('no_access', false);
926
927
	// Verify the session.
928
	checkSession('get');
929
930
	// Check permissions.
931
	if (!allowedTo('poll_remove_any'))
932
	{
933
		$request = $smcFunc['db_query']('', '
934
			SELECT t.id_member_started, p.id_member AS poll_starter
935
			FROM {db_prefix}topics AS t
936
				INNER JOIN {db_prefix}polls AS p ON (p.id_poll = t.id_poll)
937
			WHERE t.id_topic = {int:current_topic}
938
			LIMIT 1',
939
			array(
940
				'current_topic' => $topic,
941
			)
942
		);
943
		if ($smcFunc['db_num_rows']($request) == 0)
944
			fatal_lang_error('no_access', false);
945
		list ($topicStarter, $pollStarter) = $smcFunc['db_fetch_row']($request);
946
		$smcFunc['db_free_result']($request);
947
948
		isAllowedTo('poll_remove_' . ($topicStarter == $user_info['id'] || ($pollStarter != 0 && $user_info['id'] == $pollStarter) ? 'own' : 'any'));
949
	}
950
951
	// Retrieve the poll ID.
952
	$request = $smcFunc['db_query']('', '
953
		SELECT id_poll
954
		FROM {db_prefix}topics
955
		WHERE id_topic = {int:current_topic}
956
		LIMIT 1',
957
		array(
958
			'current_topic' => $topic,
959
		)
960
	);
961
	list ($pollID) = $smcFunc['db_fetch_row']($request);
962
	$smcFunc['db_free_result']($request);
963
964
	// Remove all user logs for this poll.
965
	$smcFunc['db_query']('', '
966
		DELETE FROM {db_prefix}log_polls
967
		WHERE id_poll = {int:id_poll}',
968
		array(
969
			'id_poll' => $pollID,
970
		)
971
	);
972
	// Remove all poll choices.
973
	$smcFunc['db_query']('', '
974
		DELETE FROM {db_prefix}poll_choices
975
		WHERE id_poll = {int:id_poll}',
976
		array(
977
			'id_poll' => $pollID,
978
		)
979
	);
980
	// Remove the poll itself.
981
	$smcFunc['db_query']('', '
982
		DELETE FROM {db_prefix}polls
983
		WHERE id_poll = {int:id_poll}',
984
		array(
985
			'id_poll' => $pollID,
986
		)
987
	);
988
	// Finally set the topic poll ID back to 0!
989
	$smcFunc['db_query']('', '
990
		UPDATE {db_prefix}topics
991
		SET id_poll = {int:no_poll}
992
		WHERE id_topic = {int:current_topic}',
993
		array(
994
			'current_topic' => $topic,
995
			'no_poll' => 0,
996
		)
997
	);
998
999
	// A mod might have logged this (social network?), so let them remove, it too
1000
	call_integration_hook('integrate_poll_remove', array($pollID));
1001
1002
	// Log this!
1003
	logAction('remove_poll', array('topic' => $topic));
1004
1005
	// Take the moderator back to the topic.
1006
	redirectexit('topic=' . $topic . '.' . $_REQUEST['start']);
1007
}
1008
1009
?>