EditPoll2()   F
last analyzed

Complexity

Conditions 57
Paths > 20000

Size

Total Lines 316
Code Lines 157

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 57
eloc 157
c 0
b 0
f 0
nop 0
dl 0
loc 316
rs 0
nc 985385092

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 https://www.simplemachines.org
11
 * @copyright 2022 Simple Machines and individual contributors
12
 * @license https://www.simplemachines.org/about/smf/license.php BSD
13
 *
14
 * @version 2.1.0
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
57
	$row = $smcFunc['db_fetch_assoc']($request);
58
	$smcFunc['db_free_result']($request);
59
60
	// If this is a guest can they vote?
61
	if ($user_info['is_guest'])
62
	{
63
		// Guest voting disabled?
64
		if (!$row['guest_vote'])
65
			fatal_lang_error('guest_vote_disabled');
66
		// Guest already voted?
67
		elseif (!empty($_COOKIE['guest_poll_vote']) && preg_match('~^[0-9,;]+$~', $_COOKIE['guest_poll_vote']) && strpos($_COOKIE['guest_poll_vote'], ';' . $row['id_poll'] . ',') !== false)
68
		{
69
			// ;id,timestamp,[vote,vote...]; etc
70
			$guestinfo = explode(';', $_COOKIE['guest_poll_vote']);
71
			// Find the poll we're after.
72
			foreach ($guestinfo as $i => $guestvoted)
73
			{
74
				$guestvoted = explode(',', $guestvoted);
75
				if ($guestvoted[0] == $row['id_poll'])
76
					break;
77
			}
78
			// Has the poll been reset since guest voted?
79
			if ($row['reset_poll'] > $guestvoted[1])
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $guestvoted seems to be defined by a foreach iteration on line 72. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
80
			{
81
				// Remove the poll info from the cookie to allow guest to vote again
82
				unset($guestinfo[$i]);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $i seems to be defined by a foreach iteration on line 72. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
83
				if (!empty($guestinfo))
84
					$_COOKIE['guest_poll_vote'] = ';' . implode(';', $guestinfo);
85
				else
86
					unset($_COOKIE['guest_poll_vote']);
87
			}
88
			else
89
				fatal_lang_error('poll_error', false);
90
			unset($guestinfo, $guestvoted, $i);
91
		}
92
	}
93
94
	// Is voting locked or has it expired?
95
	if (!empty($row['voting_locked']) || (!empty($row['expire_time']) && time() > $row['expire_time']))
96
		fatal_lang_error('poll_error', false);
97
98
	// If they have already voted and aren't allowed to change their vote - hence they are outta here!
99
	if (!$user_info['is_guest'] && $row['selected'] != -1 && empty($row['change_vote']))
100
		fatal_lang_error('poll_error', false);
101
	// Otherwise if they can change their vote yet they haven't sent any options... remove their vote and redirect.
102
	elseif (!empty($row['change_vote']) && !$user_info['is_guest'] && empty($_POST['options']))
103
	{
104
		checkSession('request');
105
		$pollOptions = array();
106
107
		// Find out what they voted for before.
108
		$request = $smcFunc['db_query']('', '
109
			SELECT id_choice
110
			FROM {db_prefix}log_polls
111
			WHERE id_member = {int:current_member}
112
				AND id_poll = {int:id_poll}',
113
			array(
114
				'current_member' => $user_info['id'],
115
				'id_poll' => $row['id_poll'],
116
			)
117
		);
118
		while ($choice = $smcFunc['db_fetch_row']($request))
119
			$pollOptions[] = $choice[0];
120
		$smcFunc['db_free_result']($request);
121
122
		// Just skip it if they had voted for nothing before.
123
		if (!empty($pollOptions))
124
		{
125
			// Update the poll totals.
126
			$smcFunc['db_query']('', '
127
				UPDATE {db_prefix}poll_choices
128
				SET votes = votes - 1
129
				WHERE id_poll = {int:id_poll}
130
					AND id_choice IN ({array_int:poll_options})
131
					AND votes > {int:votes}',
132
				array(
133
					'poll_options' => $pollOptions,
134
					'id_poll' => $row['id_poll'],
135
					'votes' => 0,
136
				)
137
			);
138
139
			// Delete off the log.
140
			$smcFunc['db_query']('', '
141
				DELETE FROM {db_prefix}log_polls
142
				WHERE id_member = {int:current_member}
143
					AND id_poll = {int:id_poll}',
144
				array(
145
					'current_member' => $user_info['id'],
146
					'id_poll' => $row['id_poll'],
147
				)
148
			);
149
		}
150
151
		// Redirect back to the topic so the user can vote again!
152
		if (empty($_POST['options']))
153
			redirectexit('topic=' . $topic . '.' . $_REQUEST['start']);
154
	}
155
156
	checkSession('request');
157
158
	// Make sure the option(s) are valid.
159
	if (empty($_POST['options']))
160
		fatal_lang_error('didnt_select_vote', false);
161
162
	// Too many options checked!
163
	if (count($_REQUEST['options']) > $row['max_votes'])
164
		fatal_lang_error('poll_too_many_votes', false, array($row['max_votes']));
165
166
	$pollOptions = array();
167
	$inserts = array();
168
	foreach ($_REQUEST['options'] as $id)
169
	{
170
		$id = (int) $id;
171
172
		$pollOptions[] = $id;
173
		$inserts[] = array($row['id_poll'], $user_info['id'], $id);
174
	}
175
176
	// Add their vote to the tally.
177
	$smcFunc['db_insert']('insert',
178
		'{db_prefix}log_polls',
179
		array('id_poll' => 'int', 'id_member' => 'int', 'id_choice' => 'int'),
180
		$inserts,
181
		array('id_poll', 'id_member', 'id_choice')
182
	);
183
184
	$smcFunc['db_query']('', '
185
		UPDATE {db_prefix}poll_choices
186
		SET votes = votes + 1
187
		WHERE id_poll = {int:id_poll}
188
			AND id_choice IN ({array_int:poll_options})',
189
		array(
190
			'poll_options' => $pollOptions,
191
			'id_poll' => $row['id_poll'],
192
		)
193
	);
194
195
	// If it's a guest don't let them vote again.
196
	if ($user_info['is_guest'] && count($pollOptions) > 0)
197
	{
198
		// Time is stored in case the poll is reset later, plus what they voted for.
199
		$_COOKIE['guest_poll_vote'] = empty($_COOKIE['guest_poll_vote']) ? '' : $_COOKIE['guest_poll_vote'];
200
		// ;id,timestamp,[vote,vote...]; etc
201
		$_COOKIE['guest_poll_vote'] .= ';' . $row['id_poll'] . ',' . time() . ',' . implode(',', $pollOptions);
202
203
		// Increase num guest voters count by 1
204
		$smcFunc['db_query']('', '
205
			UPDATE {db_prefix}polls
206
			SET num_guest_voters = num_guest_voters + 1
207
			WHERE id_poll = {int:id_poll}',
208
			array(
209
				'id_poll' => $row['id_poll'],
210
			)
211
		);
212
213
		require_once($sourcedir . '/Subs-Auth.php');
214
		$cookie_url = url_parts(!empty($modSettings['localCookies']), !empty($modSettings['globalCookies']));
215
		smf_setcookie('guest_poll_vote', $_COOKIE['guest_poll_vote'], time() + 2500000, $cookie_url[1], $cookie_url[0], false, false);
216
	}
217
218
	// Maybe let a social networking mod log this, or something?
219
	call_integration_hook('integrate_poll_vote', array(&$row['id_poll'], &$pollOptions));
220
221
	// Return to the post...
222
	redirectexit('topic=' . $topic . '.' . $_REQUEST['start']);
223
}
224
225
/**
226
 * Lock the voting for a poll.
227
 * Must be called with a topic specified in the URL.
228
 * An admin always has over riding permission to lock a poll.
229
 * If not an admin must have poll_lock_any permission, otherwise must
230
 * be poll starter with poll_lock_own permission.
231
 * Upon successful completion of action will direct user back to topic.
232
 * Accessed via ?action=lockvoting.
233
 */
234
function LockVoting()
235
{
236
	global $topic, $user_info, $smcFunc;
237
238
	checkSession('get');
239
240
	// Get the poll starter, ID, and whether or not it is locked.
241
	$request = $smcFunc['db_query']('', '
242
		SELECT t.id_member_started, t.id_poll, p.voting_locked
243
		FROM {db_prefix}topics AS t
244
			INNER JOIN {db_prefix}polls AS p ON (p.id_poll = t.id_poll)
245
		WHERE t.id_topic = {int:current_topic}
246
		LIMIT 1',
247
		array(
248
			'current_topic' => $topic,
249
		)
250
	);
251
	list ($memberID, $pollID, $voting_locked) = $smcFunc['db_fetch_row']($request);
252
253
	// If the user _can_ modify the poll....
254
	if (!allowedTo('poll_lock_any'))
255
		isAllowedTo('poll_lock_' . ($user_info['id'] == $memberID ? 'own' : 'any'));
256
257
	// It's been locked by a non-moderator.
258
	if ($voting_locked == '1')
259
		$voting_locked = '0';
260
	// Locked by a moderator, and this is a moderator.
261
	elseif ($voting_locked == '2' && allowedTo('moderate_board'))
262
		$voting_locked = '0';
263
	// Sorry, a moderator locked it.
264
	elseif ($voting_locked == '2' && !allowedTo('moderate_board'))
265
		fatal_lang_error('locked_by_admin', 'user');
266
	// A moderator *is* locking it.
267
	elseif ($voting_locked == '0' && allowedTo('moderate_board'))
268
		$voting_locked = '2';
269
	// Well, it's gonna be locked one way or another otherwise...
270
	else
271
		$voting_locked = '1';
272
273
	// Lock!  *Poof* - no one can vote.
274
	$smcFunc['db_query']('', '
275
		UPDATE {db_prefix}polls
276
		SET voting_locked = {int:voting_locked}
277
		WHERE id_poll = {int:id_poll}',
278
		array(
279
			'voting_locked' => $voting_locked,
280
			'id_poll' => $pollID,
281
		)
282
	);
283
284
	logAction(($voting_locked ? '' : 'un') . 'lock_poll', array('topic' => $topic));
285
286
	redirectexit('topic=' . $topic . '.' . $_REQUEST['start']);
287
}
288
289
/**
290
 * Display screen for editing or adding a poll.
291
 * Must be called with a topic specified in the URL.
292
 * If the user is adding a poll to a topic, must contain the variable
293
 * 'add' in the url.
294
 * User must have poll_edit_any/poll_add_any permission for the
295
 * relevant action, otherwise must be poll starter with poll_edit_own
296
 * permission for editing, or be topic starter with poll_add_any permission for adding.
297
 * Accessed via ?action=editpoll.
298
 *
299
 * Uses Post language file.
300
 * Uses Poll template, main sub-template.
301
 */
302
function EditPoll()
303
{
304
	global $txt, $user_info, $context, $topic, $board, $smcFunc, $sourcedir, $scripturl;
305
306
	if (empty($topic))
307
		fatal_lang_error('no_access', false);
308
309
	loadLanguage('Post');
310
	loadTemplate('Poll');
311
312
	$context['start'] = (int) $_REQUEST['start'];
313
	$context['is_edit'] = isset($_REQUEST['add']) ? 0 : 1;
314
315
	// Check if a poll currently exists on this topic, and get the id, question and starter.
316
	$request = $smcFunc['db_query']('', '
317
		SELECT
318
			t.id_member_started, p.id_poll, p.question, p.hide_results, p.expire_time, p.max_votes, p.change_vote,
319
			m.subject, p.guest_vote, p.id_member AS poll_starter
320
		FROM {db_prefix}topics AS t
321
			INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)
322
			LEFT JOIN {db_prefix}polls AS p ON (p.id_poll = t.id_poll)
323
		WHERE t.id_topic = {int:current_topic}
324
		LIMIT 1',
325
		array(
326
			'current_topic' => $topic,
327
		)
328
	);
329
330
	// Assume the the topic exists, right?
331
	if ($smcFunc['db_num_rows']($request) == 0)
332
		fatal_lang_error('no_board');
333
334
	// Get the poll information.
335
	$pollinfo = $smcFunc['db_fetch_assoc']($request);
336
	$smcFunc['db_free_result']($request);
337
338
	// If we are adding a new poll - make sure that there isn't already a poll there.
339
	if (!$context['is_edit'] && !empty($pollinfo['id_poll']))
340
		fatal_lang_error('poll_already_exists');
341
	// Otherwise, if we're editing it, it does exist I assume?
342
	elseif ($context['is_edit'] && empty($pollinfo['id_poll']))
343
		fatal_lang_error('poll_not_found');
344
345
	// Can you do this?
346
	if ($context['is_edit'] && !allowedTo('poll_edit_any'))
347
		isAllowedTo('poll_edit_' . ($user_info['id'] == $pollinfo['id_member_started'] || ($pollinfo['poll_starter'] != 0 && $user_info['id'] == $pollinfo['poll_starter']) ? 'own' : 'any'));
348
	elseif (!$context['is_edit'] && !allowedTo('poll_add_any'))
349
		isAllowedTo('poll_add_' . ($user_info['id'] == $pollinfo['id_member_started'] ? 'own' : 'any'));
350
	$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'));
351
352
	// Do we enable guest voting?
353
	require_once($sourcedir . '/Subs-Members.php');
354
	$groupsAllowedVote = groupsAllowedTo('poll_vote', $board);
355
356
	// Want to make sure before you actually submit?  Must be a lot of options, or something.
357
	if (isset($_POST['preview']))
358
	{
359
		$question = $smcFunc['htmlspecialchars']($_POST['question']);
360
361
		// Basic theme info...
362
		$context['poll'] = array(
363
			'id' => $pollinfo['id_poll'],
364
			'question' => $question,
365
			'hide_results' => empty($_POST['poll_hide']) ? 0 : $_POST['poll_hide'],
366
			'change_vote' => isset($_POST['poll_change_vote']),
367
			'guest_vote' => isset($_POST['poll_guest_vote']),
368
			'guest_vote_allowed' => in_array(-1, $groupsAllowedVote['allowed']),
369
			'max_votes' => empty($_POST['poll_max_votes']) ? '1' : max(1, $_POST['poll_max_votes']),
370
		);
371
372
		// Start at number one with no last id to speak of.
373
		$number = 1;
374
		$last_id = 0;
375
376
		// Get all the choices - if this is an edit.
377
		if ($context['is_edit'])
378
		{
379
			$request = $smcFunc['db_query']('', '
380
				SELECT label, votes, id_choice
381
				FROM {db_prefix}poll_choices
382
				WHERE id_poll = {int:id_poll}',
383
				array(
384
					'id_poll' => $pollinfo['id_poll'],
385
				)
386
			);
387
			$context['choices'] = array();
388
			while ($row = $smcFunc['db_fetch_assoc']($request))
389
			{
390
				// Get the highest id so we can add more without reusing.
391
				if ($row['id_choice'] >= $last_id)
392
					$last_id = $row['id_choice'] + 1;
393
394
				// They cleared this by either omitting it or emptying it.
395
				if (!isset($_POST['options'][$row['id_choice']]) || $_POST['options'][$row['id_choice']] == '')
396
					continue;
397
398
				censorText($row['label']);
399
400
				// Add the choice!
401
				$context['choices'][$row['id_choice']] = array(
402
					'id' => $row['id_choice'],
403
					'number' => $number++,
404
					'votes' => $row['votes'],
405
					'label' => $row['label'],
406
					'is_last' => false
407
				);
408
			}
409
			$smcFunc['db_free_result']($request);
410
		}
411
412
		// Work out how many options we have, so we get the 'is_last' field right...
413
		$totalPostOptions = 0;
414
		foreach ($_POST['options'] as $id => $label)
415
			if ($label != '')
416
				$totalPostOptions++;
417
418
		$count = 1;
419
		// If an option exists, update it.  If it is new, add it - but don't reuse ids!
420
		foreach ($_POST['options'] as $id => $label)
421
		{
422
			$label = $smcFunc['htmlspecialchars']($label);
423
			censorText($label);
424
425
			if (isset($context['choices'][$id]))
426
				$context['choices'][$id]['label'] = $label;
427
			elseif ($label != '')
428
				$context['choices'][] = array(
429
					'id' => $last_id++,
430
					'number' => $number++,
431
					'label' => $label,
432
					'votes' => -1,
433
					'is_last' => $count++ == $totalPostOptions && $totalPostOptions > 1 ? true : false,
434
				);
435
		}
436
437
		// Make sure we have two choices for sure!
438
		if ($totalPostOptions < 2)
439
		{
440
			// Need two?
441
			if ($totalPostOptions == 0)
442
				$context['choices'][] = array(
443
					'id' => $last_id++,
444
					'number' => $number++,
445
					'label' => '',
446
					'votes' => -1,
447
					'is_last' => false
448
				);
449
			$poll_errors[] = 'poll_few';
0 ignored issues
show
Comprehensibility Best Practice 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.
Loading history...
450
		}
451
452
		// Always show one extra box...
453
		$context['choices'][] = array(
454
			'id' => $last_id++,
455
			'number' => $number++,
456
			'label' => '',
457
			'votes' => -1,
458
			'is_last' => true
459
		);
460
461
		$context['last_choice_id'] = $last_id;
462
463
		if ($context['can_moderate_poll'])
464
			$context['poll']['expiration'] = $_POST['poll_expire'];
465
466
		// Check the question/option count for errors.
467
		if (trim($_POST['question']) == '' && empty($context['poll_error']))
468
			$poll_errors[] = 'no_question';
469
470
		// No check is needed, since nothing is really posted.
471
		checkSubmitOnce('free');
472
473
		// Take a check for any errors... assuming we haven't already done so!
474
		if (!empty($poll_errors) && empty($context['poll_error']))
475
		{
476
			loadLanguage('Errors');
477
478
			$context['poll_error'] = array('messages' => array());
479
			foreach ($poll_errors as $poll_error)
480
			{
481
				$context['poll_error'][$poll_error] = true;
482
				$context['poll_error']['messages'][] = $txt['error_' . $poll_error];
483
			}
484
		}
485
	}
486
	else
487
	{
488
		// Basic theme info...
489
		$context['poll'] = array(
490
			'id' => $pollinfo['id_poll'],
491
			'question' => $pollinfo['question'],
492
			'hide_results' => $pollinfo['hide_results'],
493
			'max_votes' => $pollinfo['max_votes'],
494
			'change_vote' => !empty($pollinfo['change_vote']),
495
			'guest_vote' => !empty($pollinfo['guest_vote']),
496
			'guest_vote_allowed' => in_array(-1, $groupsAllowedVote['allowed']),
497
		);
498
499
		// Poll expiration time?
500
		$context['poll']['expiration'] = empty($pollinfo['expire_time']) || !$context['can_moderate_poll'] ? '' : ceil($pollinfo['expire_time'] <= time() ? -1 : ($pollinfo['expire_time'] - time()) / (3600 * 24));
501
502
		// Get all the choices - if this is an edit.
503
		if ($context['is_edit'])
504
		{
505
			$request = $smcFunc['db_query']('', '
506
				SELECT label, votes, id_choice
507
				FROM {db_prefix}poll_choices
508
				WHERE id_poll = {int:id_poll}',
509
				array(
510
					'id_poll' => $pollinfo['id_poll'],
511
				)
512
			);
513
			$context['choices'] = array();
514
			$number = 1;
515
			while ($row = $smcFunc['db_fetch_assoc']($request))
516
			{
517
				censorText($row['label']);
518
519
				$context['choices'][$row['id_choice']] = array(
520
					'id' => $row['id_choice'],
521
					'number' => $number++,
522
					'votes' => $row['votes'],
523
					'label' => $row['label'],
524
					'is_last' => false
525
				);
526
			}
527
			$smcFunc['db_free_result']($request);
528
529
			$last_id = max(array_keys($context['choices'])) + 1;
530
531
			// Add an extra choice...
532
			$context['choices'][] = array(
533
				'id' => $last_id,
534
				'number' => $number,
535
				'votes' => -1,
536
				'label' => '',
537
				'is_last' => true
538
			);
539
			$context['last_choice_id'] = $last_id;
540
		}
541
		// New poll?
542
		else
543
		{
544
			// Setup the default poll options.
545
			$context['poll'] = array(
546
				'id' => 0,
547
				'question' => '',
548
				'hide_results' => 0,
549
				'max_votes' => 1,
550
				'change_vote' => 0,
551
				'guest_vote' => 0,
552
				'guest_vote_allowed' => in_array(-1, $groupsAllowedVote['allowed']),
553
				'expiration' => '',
554
			);
555
556
			// Make all five poll choices empty.
557
			$context['choices'] = array(
558
				array('id' => 0, 'number' => 1, 'votes' => -1, 'label' => '', 'is_last' => false),
559
				array('id' => 1, 'number' => 2, 'votes' => -1, 'label' => '', 'is_last' => false),
560
				array('id' => 2, 'number' => 3, 'votes' => -1, 'label' => '', 'is_last' => false),
561
				array('id' => 3, 'number' => 4, 'votes' => -1, 'label' => '', 'is_last' => false),
562
				array('id' => 4, 'number' => 5, 'votes' => -1, 'label' => '', 'is_last' => true)
563
			);
564
			$context['last_choice_id'] = 4;
565
		}
566
	}
567
	$context['page_title'] = $context['is_edit'] ? $txt['poll_edit'] : $txt['add_poll'];
568
569
	// Build the link tree.
570
	censorText($pollinfo['subject']);
571
	$context['linktree'][] = array(
572
		'url' => $scripturl . '?topic=' . $topic . '.0',
573
		'name' => $pollinfo['subject'],
574
	);
575
	$context['linktree'][] = array(
576
		'name' => $context['page_title'],
577
	);
578
579
	// Register this form in the session variables.
580
	checkSubmitOnce('register');
581
}
582
583
/**
584
 * Update the settings for a poll, or add a new one.
585
 * Must be called with a topic specified in the URL.
586
 * The user must have poll_edit_any/poll_add_any permission
587
 * for the relevant action. Otherwise they must be poll starter
588
 * with poll_edit_own permission for editing, or be topic starter
589
 * with poll_add_any permission for adding.
590
 * In the case of an error, this function will redirect back to
591
 * EditPoll and display the relevant error message.
592
 * Upon successful completion of action will direct user back to topic.
593
 * Accessed via ?action=editpoll2.
594
 */
595
function EditPoll2()
596
{
597
	global $txt, $topic, $board, $context;
598
	global $user_info, $smcFunc, $sourcedir;
599
600
	// Sneaking off, are we?
601
	if (empty($_POST))
602
		redirectexit('action=editpoll;topic=' . $topic . '.0');
603
604
	if (checkSession('post', '', false) != '')
605
		$poll_errors[] = 'session_timeout';
0 ignored issues
show
Comprehensibility Best Practice 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.
Loading history...
606
607
	if (isset($_POST['preview']))
608
		return EditPoll();
609
610
	// HACKERS (!!) can't edit :P.
611
	if (empty($topic))
612
		fatal_lang_error('no_access', false);
613
614
	// Is this a new poll, or editing an existing?
615
	$isEdit = isset($_REQUEST['add']) ? 0 : 1;
616
617
	// Get the starter and the poll's ID - if it's an edit.
618
	$request = $smcFunc['db_query']('', '
619
		SELECT t.id_member_started, t.id_poll, p.id_member AS poll_starter, p.expire_time
620
		FROM {db_prefix}topics AS t
621
			LEFT JOIN {db_prefix}polls AS p ON (p.id_poll = t.id_poll)
622
		WHERE t.id_topic = {int:current_topic}
623
		LIMIT 1',
624
		array(
625
			'current_topic' => $topic,
626
		)
627
	);
628
	if ($smcFunc['db_num_rows']($request) == 0)
629
		fatal_lang_error('no_board');
630
	$bcinfo = $smcFunc['db_fetch_assoc']($request);
631
	$smcFunc['db_free_result']($request);
632
633
	// Check their adding/editing is valid.
634
	if (!$isEdit && !empty($bcinfo['id_poll']))
635
		fatal_lang_error('poll_already_exists');
636
	// Are we editing a poll which doesn't exist?
637
	elseif ($isEdit && empty($bcinfo['id_poll']))
638
		fatal_lang_error('poll_not_found');
639
640
	// Check if they have the power to add or edit the poll.
641
	if ($isEdit && !allowedTo('poll_edit_any'))
642
		isAllowedTo('poll_edit_' . ($user_info['id'] == $bcinfo['id_member_started'] || ($bcinfo['poll_starter'] != 0 && $user_info['id'] == $bcinfo['poll_starter']) ? 'own' : 'any'));
643
	elseif (!$isEdit && !allowedTo('poll_add_any'))
644
		isAllowedTo('poll_add_' . ($user_info['id'] == $bcinfo['id_member_started'] ? 'own' : 'any'));
645
646
	$optionCount = 0;
647
	$idCount = 0;
648
	// Ensure the user is leaving a valid amount of options - there must be at least two.
649
	foreach ($_POST['options'] as $k => $option)
650
	{
651
		if (trim($option) != '')
652
		{
653
			$optionCount++;
654
			$idCount = max($idCount, $k);
655
		}
656
	}
657
	if ($optionCount < 2)
658
		$poll_errors[] = 'poll_few';
659
	elseif ($optionCount > 256 || $idCount > 255)
660
		$poll_errors[] = 'poll_many';
661
662
	// Also - ensure they are not removing the question.
663
	if (trim($_POST['question']) == '')
664
		$poll_errors[] = 'no_question';
665
666
	// Got any errors to report?
667
	if (!empty($poll_errors))
668
	{
669
		loadLanguage('Errors');
670
		// Previewing.
671
		$_POST['preview'] = true;
672
673
		$context['poll_error'] = array('messages' => array());
674
		foreach ($poll_errors as $poll_error)
675
		{
676
			$context['poll_error'][$poll_error] = true;
677
			$context['poll_error']['messages'][] = $txt['error_' . $poll_error];
678
		}
679
680
		return EditPoll();
681
	}
682
683
	// Prevent double submission of this form.
684
	checkSubmitOnce('check');
685
686
	// Now we've done all our error checking, let's get the core poll information cleaned... question first.
687
	$_POST['question'] = $smcFunc['htmlspecialchars']($_POST['question']);
688
	$_POST['question'] = $smcFunc['truncate']($_POST['question'], 255);
689
690
	$_POST['poll_hide'] = (int) $_POST['poll_hide'];
691
	$_POST['poll_expire'] = isset($_POST['poll_expire']) ? (int) $_POST['poll_expire'] : 0;
692
	$_POST['poll_change_vote'] = isset($_POST['poll_change_vote']) ? 1 : 0;
693
	$_POST['poll_guest_vote'] = isset($_POST['poll_guest_vote']) ? 1 : 0;
694
695
	// Make sure guests are actually allowed to vote generally.
696
	if ($_POST['poll_guest_vote'])
697
	{
698
		require_once($sourcedir . '/Subs-Members.php');
699
		$allowedGroups = groupsAllowedTo('poll_vote', $board);
700
		if (!in_array(-1, $allowedGroups['allowed']))
701
			$_POST['poll_guest_vote'] = 0;
702
	}
703
704
	// Ensure that the number options allowed makes sense, and the expiration date is valid.
705
	if (!$isEdit || allowedTo('moderate_board'))
706
	{
707
		$_POST['poll_expire'] = $_POST['poll_expire'] > 9999 ? 9999 : ($_POST['poll_expire'] < 0 ? 0 : $_POST['poll_expire']);
708
709
		if (empty($_POST['poll_expire']) && $_POST['poll_hide'] == 2)
710
			$_POST['poll_hide'] = 1;
711
		elseif (!$isEdit || $_POST['poll_expire'] != ceil($bcinfo['expire_time'] <= time() ? -1 : ($bcinfo['expire_time'] - time()) / (3600 * 24)))
712
			$_POST['poll_expire'] = empty($_POST['poll_expire']) ? '0' : time() + $_POST['poll_expire'] * 3600 * 24;
713
		else
714
			$_POST['poll_expire'] = $bcinfo['expire_time'];
715
716
		if (empty($_POST['poll_max_votes']) || $_POST['poll_max_votes'] <= 0)
717
			$_POST['poll_max_votes'] = 1;
718
		else
719
			$_POST['poll_max_votes'] = (int) $_POST['poll_max_votes'];
720
	}
721
722
	// If we're editing, let's commit the changes.
723
	if ($isEdit)
724
	{
725
		$smcFunc['db_query']('', '
726
			UPDATE {db_prefix}polls
727
			SET question = {string:question}, change_vote = {int:change_vote},' . (allowedTo('moderate_board') ? '
728
				hide_results = {int:hide_results}, expire_time = {int:expire_time}, max_votes = {int:max_votes},
729
				guest_vote = {int:guest_vote}' : '
730
				hide_results = CASE WHEN expire_time = {int:expire_time_zero} AND {int:hide_results} = 2 THEN 1 ELSE {int:hide_results} END') . '
731
			WHERE id_poll = {int:id_poll}',
732
			array(
733
				'change_vote' => $_POST['poll_change_vote'],
734
				'hide_results' => $_POST['poll_hide'],
735
				'expire_time' => !empty($_POST['poll_expire']) ? $_POST['poll_expire'] : 0,
736
				'max_votes' => !empty($_POST['poll_max_votes']) ? $_POST['poll_max_votes'] : 0,
737
				'guest_vote' => $_POST['poll_guest_vote'],
738
				'expire_time_zero' => 0,
739
				'id_poll' => $bcinfo['id_poll'],
740
				'question' => $_POST['question'],
741
			)
742
		);
743
	}
744
	// Otherwise, let's get our poll going!
745
	else
746
	{
747
		// Create the poll.
748
		$bcinfo['id_poll'] = $smcFunc['db_insert']('',
749
			'{db_prefix}polls',
750
			array(
751
				'question' => 'string-255', 'hide_results' => 'int', 'max_votes' => 'int', 'expire_time' => 'int', 'id_member' => 'int',
752
				'poster_name' => 'string-255', 'change_vote' => 'int', 'guest_vote' => 'int'
753
			),
754
			array(
755
				$_POST['question'], $_POST['poll_hide'], $_POST['poll_max_votes'], $_POST['poll_expire'], $user_info['id'],
756
				$user_info['username'], $_POST['poll_change_vote'], $_POST['poll_guest_vote'],
757
			),
758
			array('id_poll'),
759
			1
760
		);
761
762
		// Link the poll to the topic
763
		$smcFunc['db_query']('', '
764
			UPDATE {db_prefix}topics
765
			SET id_poll = {int:id_poll}
766
			WHERE id_topic = {int:current_topic}',
767
			array(
768
				'current_topic' => $topic,
769
				'id_poll' => $bcinfo['id_poll'],
770
			)
771
		);
772
	}
773
774
	// Get all the choices.  (no better way to remove all emptied and add previously non-existent ones.)
775
	$request = $smcFunc['db_query']('', '
776
		SELECT id_choice
777
		FROM {db_prefix}poll_choices
778
		WHERE id_poll = {int:id_poll}',
779
		array(
780
			'id_poll' => $bcinfo['id_poll'],
781
		)
782
	);
783
	$choices = array();
784
	while ($row = $smcFunc['db_fetch_assoc']($request))
785
		$choices[] = $row['id_choice'];
786
	$smcFunc['db_free_result']($request);
787
788
	$delete_options = array();
789
	foreach ($_POST['options'] as $k => $option)
790
	{
791
		// Make sure the key is numeric for sanity's sake.
792
		$k = (int) $k;
793
794
		// They've cleared the box.  Either they want it deleted, or it never existed.
795
		if (trim($option) == '')
796
		{
797
			// They want it deleted.  Bye.
798
			if (in_array($k, $choices))
799
				$delete_options[] = $k;
800
801
			// Skip the rest...
802
			continue;
803
		}
804
805
		// Dress the option up for its big date with the database.
806
		$option = $smcFunc['htmlspecialchars']($option);
807
808
		// If it's already there, update it.  If it's not... add it.
809
		if (in_array($k, $choices))
810
			$smcFunc['db_query']('', '
811
				UPDATE {db_prefix}poll_choices
812
				SET label = {string:option_name}
813
				WHERE id_poll = {int:id_poll}
814
					AND id_choice = {int:id_choice}',
815
				array(
816
					'id_poll' => $bcinfo['id_poll'],
817
					'id_choice' => $k,
818
					'option_name' => $option,
819
				)
820
			);
821
		else
822
			$smcFunc['db_insert']('',
823
				'{db_prefix}poll_choices',
824
				array(
825
					'id_poll' => 'int', 'id_choice' => 'int', 'label' => 'string-255', 'votes' => 'int',
826
				),
827
				array(
828
					$bcinfo['id_poll'], $k, $option, 0,
829
				),
830
				array()
831
			);
832
	}
833
834
	// I'm sorry, but... well, no one was choosing you.  Poor options, I'll put you out of your misery.
835
	if (!empty($delete_options))
836
	{
837
		$smcFunc['db_query']('', '
838
			DELETE FROM {db_prefix}log_polls
839
			WHERE id_poll = {int:id_poll}
840
				AND id_choice IN ({array_int:delete_options})',
841
			array(
842
				'delete_options' => $delete_options,
843
				'id_poll' => $bcinfo['id_poll'],
844
			)
845
		);
846
		$smcFunc['db_query']('', '
847
			DELETE FROM {db_prefix}poll_choices
848
			WHERE id_poll = {int:id_poll}
849
				AND id_choice IN ({array_int:delete_options})',
850
			array(
851
				'delete_options' => $delete_options,
852
				'id_poll' => $bcinfo['id_poll'],
853
			)
854
		);
855
	}
856
857
	// Shall I reset the vote count, sir?
858
	if (isset($_POST['resetVoteCount']))
859
	{
860
		$smcFunc['db_query']('', '
861
			UPDATE {db_prefix}polls
862
			SET num_guest_voters = {int:no_votes}, reset_poll = {int:time}
863
			WHERE id_poll = {int:id_poll}',
864
			array(
865
				'no_votes' => 0,
866
				'id_poll' => $bcinfo['id_poll'],
867
				'time' => time(),
868
			)
869
		);
870
		$smcFunc['db_query']('', '
871
			UPDATE {db_prefix}poll_choices
872
			SET votes = {int:no_votes}
873
			WHERE id_poll = {int:id_poll}',
874
			array(
875
				'no_votes' => 0,
876
				'id_poll' => $bcinfo['id_poll'],
877
			)
878
		);
879
		$smcFunc['db_query']('', '
880
			DELETE FROM {db_prefix}log_polls
881
			WHERE id_poll = {int:id_poll}',
882
			array(
883
				'id_poll' => $bcinfo['id_poll'],
884
			)
885
		);
886
	}
887
888
	call_integration_hook('integrate_poll_add_edit', array($bcinfo['id_poll'], $isEdit));
889
890
	/* Log this edit, but don't go crazy.
891
		Only specifically adding a poll	or resetting votes is logged.
892
		Everything else is simply an edit.*/
893
	if (isset($_REQUEST['add']))
894
	{
895
		// Added a poll
896
		logAction('add_poll', array('topic' => $topic));
897
	}
898
	elseif (isset($_REQUEST['deletevotes']))
899
	{
900
		// Reset votes
901
		logAction('reset_poll', array('topic' => $topic));
902
	}
903
	else
904
	{
905
		// Something else
906
		logAction('edit_poll', array('topic' => $topic));
907
	}
908
909
	// Off we go.
910
	redirectexit('topic=' . $topic . '.' . $_REQUEST['start']);
911
}
912
913
/**
914
 * Remove a poll from a topic without removing the topic.
915
 * Must be called with a topic specified in the URL.
916
 * Requires poll_remove_any permission, unless it's the poll starter
917
 * with poll_remove_own permission.
918
 * Upon successful completion of action will direct user back to topic.
919
 * Accessed via ?action=removepoll.
920
 */
921
function RemovePoll()
922
{
923
	global $topic, $user_info, $smcFunc;
924
925
	// Make sure the topic is not empty.
926
	if (empty($topic))
927
		fatal_lang_error('no_access', false);
928
929
	// Verify the session.
930
	checkSession('get');
931
932
	// Check permissions.
933
	if (!allowedTo('poll_remove_any'))
934
	{
935
		$request = $smcFunc['db_query']('', '
936
			SELECT t.id_member_started, p.id_member AS poll_starter
937
			FROM {db_prefix}topics AS t
938
				INNER JOIN {db_prefix}polls AS p ON (p.id_poll = t.id_poll)
939
			WHERE t.id_topic = {int:current_topic}
940
			LIMIT 1',
941
			array(
942
				'current_topic' => $topic,
943
			)
944
		);
945
		if ($smcFunc['db_num_rows']($request) == 0)
946
			fatal_lang_error('no_access', false);
947
		list ($topicStarter, $pollStarter) = $smcFunc['db_fetch_row']($request);
948
		$smcFunc['db_free_result']($request);
949
950
		isAllowedTo('poll_remove_' . ($topicStarter == $user_info['id'] || ($pollStarter != 0 && $user_info['id'] == $pollStarter) ? 'own' : 'any'));
951
	}
952
953
	// Retrieve the poll ID.
954
	$request = $smcFunc['db_query']('', '
955
		SELECT id_poll
956
		FROM {db_prefix}topics
957
		WHERE id_topic = {int:current_topic}
958
		LIMIT 1',
959
		array(
960
			'current_topic' => $topic,
961
		)
962
	);
963
	list ($pollID) = $smcFunc['db_fetch_row']($request);
964
	$smcFunc['db_free_result']($request);
965
966
	// Remove all user logs for this poll.
967
	$smcFunc['db_query']('', '
968
		DELETE FROM {db_prefix}log_polls
969
		WHERE id_poll = {int:id_poll}',
970
		array(
971
			'id_poll' => $pollID,
972
		)
973
	);
974
	// Remove all poll choices.
975
	$smcFunc['db_query']('', '
976
		DELETE FROM {db_prefix}poll_choices
977
		WHERE id_poll = {int:id_poll}',
978
		array(
979
			'id_poll' => $pollID,
980
		)
981
	);
982
	// Remove the poll itself.
983
	$smcFunc['db_query']('', '
984
		DELETE FROM {db_prefix}polls
985
		WHERE id_poll = {int:id_poll}',
986
		array(
987
			'id_poll' => $pollID,
988
		)
989
	);
990
	// Finally set the topic poll ID back to 0!
991
	$smcFunc['db_query']('', '
992
		UPDATE {db_prefix}topics
993
		SET id_poll = {int:no_poll}
994
		WHERE id_topic = {int:current_topic}',
995
		array(
996
			'current_topic' => $topic,
997
			'no_poll' => 0,
998
		)
999
	);
1000
1001
	// A mod might have logged this (social network?), so let them remove, it too
1002
	call_integration_hook('integrate_poll_remove', array($pollID));
1003
1004
	// Log this!
1005
	logAction('remove_poll', array('topic' => $topic));
1006
1007
	// Take the moderator back to the topic.
1008
	redirectexit('topic=' . $topic . '.' . $_REQUEST['start']);
1009
}
1010
1011
?>