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
![]() |
|||
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
|
|||
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
|
|||
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
|
|||
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 | ?> |