/[drupal]/drupal/modules/poll/poll.module
ViewVC logotype

Contents of /drupal/modules/poll/poll.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.322 - (show annotations) (download) (as text)
Sun Nov 1 12:11:10 2009 UTC (3 weeks, 5 days ago) by dries
Branch: MAIN
Changes since 1.321: +13 -13 lines
File MIME type: text/x-php
- Patch #595084 by c960657: use type hinting for .
1 <?php
2 // $Id: poll.module,v 1.321 2009/10/23 22:24:16 webchick Exp $
3
4 /**
5 * @file
6 * Enables your site to capture votes on different topics in the form of multiple
7 * choice questions.
8 */
9
10 /**
11 * Implement hook_help().
12 */
13 function poll_help($path, $arg) {
14 switch ($path) {
15 case 'admin/help#poll':
16 $output = '<p>' . t('The poll module can be used to create simple polls for site users. A poll is a simple, multiple choice questionnaire which displays the cumulative results of the answers to the poll. Having polls on the site is a good way to receive feedback from community members.') . '</p>';
17 $output .= '<p>' . t('When creating a poll, enter the question being posed, as well as the potential choices (and beginning vote counts for each choice). The status and duration (length of time the poll remains active for new votes) can also be specified. Use the <a href="@poll">poll</a> menu item to view all current polls. To vote in or view the results of a specific poll, click on the poll itself.', array('@poll' => url('poll'))) . '</p>';
18 $output .= '<p>' . t('For more information, see the online handbook entry for <a href="@poll">Poll module</a>.', array('@poll' => 'http://drupal.org/handbook/modules/poll/')) . '</p>';
19 return $output;
20 }
21 }
22
23 /**
24 * Implement hook_init().
25 */
26 function poll_init() {
27 drupal_add_css(drupal_get_path('module', 'poll') . '/poll.css');
28 }
29
30 /**
31 * Implement hook_theme().
32 */
33 function poll_theme() {
34 return array(
35 'poll_vote' => array(
36 'template' => 'poll-vote',
37 'render element' => 'form',
38 ),
39 'poll_choices' => array(
40 'render element' => 'form',
41 ),
42 'poll_results' => array(
43 'template' => 'poll-results',
44 'variables' => array('raw_title' => NULL, 'results' => NULL, 'votes' => NULL, 'raw_links' => NULL, 'block' => NULL, 'nid' => NULL, 'vote' => NULL),
45 ),
46 'poll_bar' => array(
47 'template' => 'poll-bar',
48 'variables' => array('title' => NULL, 'votes' => NULL, 'total_votes' => NULL, 'vote' => NULL, 'block' => NULL),
49 ),
50 );
51 }
52
53 /**
54 * Implement hook_permission().
55 */
56 function poll_permission() {
57 $perms = array(
58 'vote on polls' => array(
59 'title' => t('Vote on polls'),
60 'description' => t('Cast votes on polls.'),
61 ),
62 'cancel own vote' => array(
63 'title' => t('Cancel own vote'),
64 'description' => t('Retract and optionally change own votes.'),
65 ),
66 'inspect all votes' => array(
67 'title' => t('Inspect all votes'),
68 'description' => t('View voting results.'),
69 ),
70 );
71
72 return $perms;
73 }
74
75 /**
76 * Implement hook_menu().
77 */
78 function poll_menu() {
79 $items['poll'] = array(
80 'title' => 'Polls',
81 'page callback' => 'poll_page',
82 'access arguments' => array('access content'),
83 'type' => MENU_SUGGESTED_ITEM,
84 'file' => 'poll.pages.inc',
85 );
86
87 $items['node/%node/votes'] = array(
88 'title' => 'Votes',
89 'page callback' => 'poll_votes',
90 'page arguments' => array(1),
91 'access callback' => '_poll_menu_access',
92 'access arguments' => array(1, 'inspect all votes', FALSE),
93 'weight' => 3,
94 'type' => MENU_LOCAL_TASK,
95 'file' => 'poll.pages.inc',
96 );
97
98 $items['node/%node/results'] = array(
99 'title' => 'Results',
100 'page callback' => 'poll_results',
101 'page arguments' => array(1),
102 'access callback' => '_poll_menu_access',
103 'access arguments' => array(1, 'access content', TRUE),
104 'weight' => 3,
105 'type' => MENU_LOCAL_TASK,
106 'file' => 'poll.pages.inc',
107 );
108
109 return $items;
110 }
111
112 /**
113 * Callback function to see if a node is acceptable for poll menu items.
114 */
115 function _poll_menu_access(stdClass $node, $perm, $inspect_allowvotes) {
116 return user_access($perm) && ($node->type == 'poll') && ($node->allowvotes || !$inspect_allowvotes);
117 }
118
119 /**
120 * Implement hook_block_info().
121 */
122 function poll_block_info() {
123 if (user_access('access content')) {
124 $blocks['recent']['info'] = t('Most recent poll');
125 return $blocks;
126 }
127 }
128
129 /**
130 * Implement hook_block_view().
131 *
132 * Generates a block containing the latest poll.
133 */
134 function poll_block_view($delta = '') {
135 if (user_access('access content')) {
136 // Retrieve the latest poll.
137 $select = db_select('node', 'n');
138 $select->join('poll', 'p', 'p.nid = n.nid');
139 $select->fields('n', array('nid'))
140 ->condition('n.status', 1)
141 ->condition('p.active', 1)
142 ->orderBy('n.created', 'DESC')
143 ->range(0, 1)
144 ->addTag('node_access');
145
146 $record = $select->execute()->fetchObject();
147 if ($record) {
148 $poll = node_load($record->nid);
149 if ($poll->nid) {
150 $poll = poll_block_latest_poll_view($poll);
151 $block['subject'] = t('Poll');
152 $block['content'] = $poll->content;
153 return $block;
154 }
155 }
156 }
157 }
158
159 /**
160 * Implement hook_cron().
161 *
162 * Closes polls that have exceeded their allowed runtime.
163 */
164 function poll_cron() {
165 $nids = db_query('SELECT p.nid FROM {poll} p INNER JOIN {node} n ON p.nid = n.nid WHERE (n.created + p.runtime) < :request_time AND p.active = :active AND p.runtime <> :runtime', array(':request_time' => REQUEST_TIME, ':active' => 1, ':runtime' => 0))->fetchCol();
166 if (!empty($nids)) {
167 db_update('poll')
168 ->fields(array('active' => 0))
169 ->condition('nid', $nids, 'IN')
170 ->execute();
171 }
172 }
173
174 /**
175 * Implement hook_node_info().
176 */
177 function poll_node_info() {
178 return array(
179 'poll' => array(
180 'name' => t('Poll'),
181 'base' => 'poll',
182 'description' => t('A <em>poll</em> is a question with a set of possible responses. A <em>poll</em>, once created, automatically provides a simple running count of the number of votes received for each response.'),
183 'title_label' => t('Question'),
184 'has_body' => FALSE,
185 )
186 );
187 }
188
189 /**
190 * Implement hook_field_extra_fields().
191 */
192 function poll_field_extra_fields($bundle) {
193 $extra = array();
194
195 if ($bundle == 'poll') {
196 $extra['choice_wrapper'] = array(
197 'label' => t('Poll choices'),
198 'description' => t('Poll module choices.'),
199 'weight' => -4,
200 );
201 $extra['settings'] = array(
202 'label' => t('Poll settings'),
203 'description' => t('Poll module settings.'),
204 'weight' => -3,
205 );
206 }
207
208 return $extra;
209 }
210
211 /**
212 * Implement hook_form().
213 */
214 function poll_form(stdClass $node, $form_state) {
215 global $user;
216
217 $admin = user_access('administer nodes') || user_access('edit any poll content') || (user_access('edit own poll content') && $user->uid == $node->uid);
218
219 $type = node_type_get_type($node);
220
221 $form = array(
222 '#cache' => TRUE,
223 );
224
225 if (isset($form_state['choice_count'])) {
226 $choice_count = $form_state['choice_count'];
227 }
228 else {
229 $choice_count = max(2, empty($node->choice) ? 2 : count($node->choice));
230 }
231
232 // Add a wrapper for the choices and more button.
233 $form['choice_wrapper'] = array(
234 '#tree' => FALSE,
235 '#weight' => -4,
236 '#prefix' => '<div class="clearfix" id="poll-choice-wrapper">',
237 '#suffix' => '</div>',
238 );
239
240 // Container for just the poll choices.
241 $form['choice_wrapper']['choice'] = array(
242 '#prefix' => '<div id="poll-choices">',
243 '#suffix' => '</div>',
244 '#theme' => 'poll_choices',
245 );
246
247 // Add the current choices to the form.
248 $delta = 0;
249 $weight = 0;
250 if (isset($node->choice)) {
251 $delta = count($node->choice);
252 $weight = -$delta;
253 foreach ($node->choice as $chid => $choice) {
254 $key = 'chid:' . $chid;
255 $form['choice_wrapper']['choice'][$key] = _poll_choice_form($key, $choice['chid'], $choice['chtext'], $choice['chvotes'], $choice['weight'], $choice_count);
256 $weight = ($choice['weight'] > $weight) ? $choice['weight'] : $weight;
257 }
258 }
259
260 // Add initial or additional choices.
261 $existing_delta = $delta;
262 for ($delta; $delta < $choice_count; $delta++) {
263 $key = 'new:' . ($delta - $existing_delta);
264 $form['choice_wrapper']['choice'][$key] = _poll_choice_form($key, NULL, '', 0, $weight, $choice_count);
265 }
266
267 // We name our button 'poll_more' to avoid conflicts with other modules using
268 // AJAX-enabled buttons with the id 'more'.
269 $form['choice_wrapper']['poll_more'] = array(
270 '#type' => 'submit',
271 '#value' => t('More choices'),
272 '#description' => t("If the amount of boxes above isn't enough, click here to add more choices."),
273 '#weight' => 1,
274 '#submit' => array('poll_more_choices_submit'), // If no javascript action.
275 '#ajax' => array(
276 'callback' => 'poll_choice_js',
277 'wrapper' => 'poll-choices',
278 'method' => 'replace',
279 'effect' => 'fade',
280 ),
281 );
282
283 // Poll attributes
284 $duration = array(
285 // 1-6 days.
286 86400, 2 * 86400, 3 * 86400, 4 * 86400, 5 * 86400, 6 * 86400,
287 // 1-3 weeks (7 days).
288 604800, 2 * 604800, 3 * 604800,
289 // 1-3,6,9 months (30 days).
290 2592000, 2 * 2592000, 3 * 2592000, 6 * 2592000, 9 * 2592000,
291 // 1 year (365 days).
292 31536000,
293 );
294 $duration = array(0 => t('Unlimited')) + drupal_map_assoc($duration, 'format_interval');
295 $active = array(0 => t('Closed'), 1 => t('Active'));
296
297 $form['settings'] = array(
298 '#type' => 'fieldset',
299 '#collapsible' => TRUE,
300 '#title' => t('Poll settings'),
301 '#weight' => -3,
302 '#access' => $admin,
303 );
304
305 $form['settings']['active'] = array(
306 '#type' => 'radios',
307 '#title' => t('Poll status'),
308 '#default_value' => isset($node->active) ? $node->active : 1,
309 '#options' => $active,
310 '#description' => t('When a poll is closed, visitors can no longer vote for it.'),
311 '#access' => $admin,
312 );
313 $form['settings']['runtime'] = array(
314 '#type' => 'select',
315 '#title' => t('Poll duration'),
316 '#default_value' => isset($node->runtime) ? $node->runtime : 0,
317 '#options' => $duration,
318 '#description' => t('After this period, the poll will be closed automatically.'),
319 );
320
321 return $form;
322 }
323
324 /**
325 * Submit handler to add more choices to a poll form. This handler is used when
326 * javascript is not available. It makes changes to the form state and the
327 * entire form is rebuilt during the page reload.
328 */
329 function poll_more_choices_submit($form, &$form_state) {
330 include_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'node') . '/node.pages.inc';
331 // Set the form to rebuild and run submit handlers.
332 node_form_submit_build_node($form, $form_state);
333
334 // Make the changes we want to the form state.
335 if ($form_state['values']['poll_more']) {
336 $n = $_GET['q'] == 'system/ajax' ? 1 : 5;
337 $form_state['choice_count'] = count($form_state['values']['choice']) + $n;
338 }
339 }
340
341 function _poll_choice_form($key, $chid = NULL, $value = '', $votes = 0, $weight = 0, $size = 10) {
342 $admin = user_access('administer nodes');
343
344 $form = array(
345 '#tree' => TRUE,
346 );
347
348 // We'll manually set the #parents property of these fields so that
349 // their values appear in the $form_state['values']['choice'] array.
350 $form['chid'] = array(
351 '#type' => 'value',
352 '#value' => $chid,
353 '#parents' => array('choice', $key, 'chid'),
354 );
355
356 $form['chtext'] = array(
357 '#type' => 'textfield',
358 '#default_value' => $value,
359 '#parents' => array('choice', $key, 'chtext'),
360 );
361
362 $form['chvotes'] = array(
363 '#type' => 'textfield',
364 '#default_value' => $votes,
365 '#size' => 5,
366 '#maxlength' => 7,
367 '#parents' => array('choice', $key, 'chvotes'),
368 '#access' => user_access('administer nodes'),
369 );
370
371 $form['weight'] = array(
372 '#type' => 'weight',
373 '#default_value' => $weight,
374 '#delta' => $size,
375 '#parents' => array('choice', $key, 'weight'),
376 );
377
378 return $form;
379 }
380
381 /**
382 * Menu callback for AHAH additions. Render the new poll choices.
383 */
384 function poll_choice_js($form, $form_state) {
385 return $form['choice_wrapper']['choice'];
386 }
387
388 /**
389 * Renumber fields and create a teaser when a poll node is submitted.
390 */
391 function poll_node_form_submit(&$form, &$form_state) {
392 // Renumber fields
393 $form_state['values']['choice'] = array_values($form_state['values']['choice']);
394 $form_state['values']['teaser'] = poll_teaser((object)$form_state['values']);
395 }
396
397 /**
398 * Implement hook_validate().
399 */
400 function poll_validate(stdClass $node, $form) {
401 if (isset($node->title)) {
402 // Check for at least two options and validate amount of votes:
403 $realchoices = 0;
404 // Renumber fields
405 $node->choice = array_values($node->choice);
406 foreach ($node->choice as $i => $choice) {
407 if ($choice['chtext'] != '') {
408 $realchoices++;
409 }
410 if (isset($choice['chvotes']) && $choice['chvotes'] < 0) {
411 form_set_error("choice][$i][chvotes", t('Negative values are not allowed.'));
412 }
413 }
414
415 if ($realchoices < 2) {
416 form_set_error("choice][$realchoices][chtext", t('You must fill in at least two choices.'));
417 }
418 }
419 }
420
421 /**
422 * Implement hook_node_prepare_translation().
423 */
424 function poll_node_prepare_translation(stdClass $node) {
425 if ($node->type == 'poll') {
426 $node->choice = $node->translation_source->choice;
427 }
428 }
429
430 /**
431 * Implement hook_load().
432 */
433 function poll_load($nodes) {
434 global $user;
435 foreach ($nodes as $node) {
436 $poll = db_query("SELECT runtime, active FROM {poll} WHERE nid = :nid", array(':nid' => $node->nid))->fetchObject();
437
438 // Load the appropriate choices into the $poll object.
439 $poll->choice = db_select('poll_choice', 'c')
440 ->addTag('translatable')
441 ->fields('c', array('chid', 'chtext', 'chvotes', 'weight'))
442 ->condition('c.nid', $node->nid)
443 ->orderBy('weight')
444 ->execute()->fetchAllAssoc('chid', PDO::FETCH_ASSOC);
445
446 // Determine whether or not this user is allowed to vote.
447 $poll->allowvotes = FALSE;
448 if (user_access('vote on polls') && $poll->active) {
449 if ($user->uid) {
450 $result = db_query('SELECT chid FROM {poll_vote} WHERE nid = :nid AND uid = :uid', array(':nid' => $node->nid, ':uid' => $user->uid))->fetchObject();
451 }
452 else {
453 $result = db_query("SELECT chid FROM {poll_vote} WHERE nid = :nid AND hostname = :hostname", array(':nid' => $node->nid, ':hostname' => ip_address()))->fetchObject();
454 }
455 if ($result) {
456 $poll->vote = $result->chid;
457 }
458 else {
459 $poll->vote = -1;
460 $poll->allowvotes = TRUE;
461 }
462 }
463 foreach ($poll as $key => $value) {
464 $nodes[$node->nid]->$key = $value;
465 }
466 }
467 }
468
469 /**
470 * Implement hook_insert().
471 */
472 function poll_insert(stdClass $node) {
473 if (!user_access('administer nodes')) {
474 // Make sure all votes are 0 initially
475 foreach ($node->choice as $i => $choice) {
476 $node->choice[$i]['chvotes'] = 0;
477 }
478 $node->active = 1;
479 }
480
481 db_insert('poll')
482 ->fields(array(
483 'nid' => $node->nid,
484 'runtime' => $node->runtime,
485 'active' => $node->active,
486 ))
487 ->execute();
488
489 foreach ($node->choice as $choice) {
490 if ($choice['chtext'] != '') {
491 db_insert('poll_choice')
492 ->fields(array(
493 'nid' => $node->nid,
494 'chtext' => $choice['chtext'],
495 'chvotes' => $choice['chvotes'],
496 'weight' => $choice['weight'],
497 ))
498 ->execute();
499 }
500 }
501 }
502
503 /**
504 * Implement hook_update().
505 */
506 function poll_update(stdClass $node) {
507 // Update poll settings.
508 db_update('poll')
509 ->fields(array(
510 'runtime' => $node->runtime,
511 'active' => $node->active,
512 ))
513 ->condition('nid', $node->nid)
514 ->execute();
515
516 // Poll choices with empty titles signifies removal. We remove all votes to
517 // the removed options, so people who voted on them can vote again.
518 foreach ($node->choice as $key => $choice) {
519 if (!empty($choice['chtext'])) {
520 db_merge('poll_choice')
521 ->key(array('chid' => $choice['chid']))
522 ->fields(array(
523 'nid' => $node->nid,
524 'chtext' => $choice['chtext'],
525 'chvotes' => (int) $choice['chvotes'],
526 'weight' => $choice['weight'],
527 ))
528 ->updateExcept('nid')
529 ->execute();
530 }
531 else {
532 db_delete('poll_vote')
533 ->condition('nid', $node->nid)
534 ->condition('chid', $key)
535 ->execute();
536 }
537 }
538 }
539
540 /**
541 * Implement hook_delete().
542 */
543 function poll_delete(stdClass $node) {
544 db_delete('poll')
545 ->condition('nid', $node->nid)
546 ->execute();
547 db_delete('poll_choice')
548 ->condition('nid', $node->nid)
549 ->execute();
550 db_delete('poll_vote')
551 ->condition('nid', $node->nid)
552 ->execute();
553 }
554
555 /**
556 * Return content for 'latest poll' block.
557 *
558 * @param $node
559 * The node object to load.
560 */
561 function poll_block_latest_poll_view(stdClass $node) {
562 global $user;
563 $output = '';
564
565 // This is necessary for shared objects because PHP doesn't copy objects, but
566 // passes them by reference. So when the objects are cached it can result in
567 // the wrong output being displayed on subsequent calls. The cloning and
568 // unsetting of $node->content prevents the block output from being the same
569 // as the node output.
570 $node = clone $node;
571 unset($node->content);
572
573 // No 'read more' link.
574 $node->readmore = FALSE;
575 $node->teaser = '';
576
577 $links = array();
578 $links[] = array('title' => t('Older polls'), 'href' => 'poll', 'attributes' => array('title' => t('View the list of polls on this site.')));
579 if ($node->allowvotes) {
580 $links[] = array('title' => t('Results'), 'href' => 'node/' . $node->nid . '/results', 'attributes' => array('title' => t('View the current poll results.')));
581 }
582
583 $node->links = $links;
584
585 if (!empty($node->allowvotes)) {
586 $node->content['poll_view_voting'] = drupal_get_form('poll_view_voting', $node, TRUE);
587 }
588 else {
589 $node->content['poll_view_results'] = array('#markup' => poll_view_results($node, TRUE, TRUE));
590 }
591
592 return $node;
593 }
594
595
596 /**
597 * Implement hook_view().
598 */
599 function poll_view(stdClass $node, $build_mode = 'full') {
600 global $user;
601 $output = '';
602
603 if (!empty($node->allowvotes) && empty($node->show_results)) {
604 $node->content['poll_view_voting'] = drupal_get_form('poll_view_voting', $node);
605 }
606 else {
607 $node->content['poll_view_results'] = array('#markup' => poll_view_results($node, $build_mode));
608 }
609 return $node;
610 }
611
612 /**
613 * Creates a simple teaser that lists all the choices.
614 *
615 * This is primarily used for RSS.
616 */
617 function poll_teaser(stdClass $node) {
618 $teaser = NULL;
619 if (is_array($node->choice)) {
620 foreach ($node->choice as $k => $choice) {
621 if ($choice['chtext'] != '') {
622 $teaser .= '* ' . check_plain($choice['chtext']) . "\n";
623 }
624 }
625 }
626 return $teaser;
627 }
628
629 /**
630 * Generates the voting form for a poll.
631 *
632 * @ingroup forms
633 * @see poll_vote()
634 * @see phptemplate_preprocess_poll_vote()
635 */
636 function poll_view_voting($form, &$form_state, stdClass $node, $block = FALSE) {
637 if ($node->choice) {
638 $list = array();
639 foreach ($node->choice as $i => $choice) {
640 $list[$i] = check_plain($choice['chtext']);
641 }
642 $form['choice'] = array(
643 '#type' => 'radios',
644 '#default_value' => -1,
645 '#options' => $list,
646 );
647 }
648
649 $form['vote'] = array(
650 '#type' => 'submit',
651 '#value' => t('Vote'),
652 '#submit' => array('poll_vote'),
653 );
654
655 // Store the node so we can get to it in submit functions.
656 $form['#node'] = $node;
657 $form['#block'] = $block;
658
659 // Set form caching because we could have multiple of these forms on
660 // the same page, and we want to ensure the right one gets picked.
661 $form['#cache'] = TRUE;
662
663 // Provide a more cleanly named voting form theme.
664 $form['#theme'] = 'poll_vote';
665 return $form;
666 }
667
668 /**
669 * Validation function for processing votes
670 */
671 function poll_view_voting_validate($form, &$form_state) {
672 if ($form_state['values']['choice'] == -1) {
673 form_set_error( 'choice', t('Your vote could not be recorded because you did not select any of the choices.'));
674 }
675 }
676
677 /**
678 * Submit handler for processing a vote.
679 */
680 function poll_vote($form, &$form_state) {
681 $node = $form['#node'];
682 $choice = $form_state['values']['choice'];
683
684 global $user;
685 db_insert('poll_vote')
686 ->fields(array(
687 'nid' => $node->nid,
688 'chid' => $choice,
689 'uid' => $user->uid,
690 'hostname' => $user->uid ? ip_address() : '',
691 ))
692 ->execute();
693
694 // Add one to the votes.
695 db_update('poll_choice')
696 ->expression('chvotes', 'chvotes + 1')
697 ->condition('chid', $choice)
698 ->execute();
699
700 cache_clear_all();
701 drupal_set_message(t('Your vote was recorded.'));
702
703 // Return the user to whatever page they voted from.
704 }
705
706 /**
707 * Themes the voting form for a poll.
708 *
709 * Inputs: $form
710 */
711 function template_preprocess_poll_vote(&$variables) {
712 $form = $variables['form'];
713 $variables['choice'] = drupal_render($form['choice']);
714 $variables['title'] = check_plain($form['#node']->title[FIELD_LANGUAGE_NONE][0]['value']);
715 $variables['vote'] = drupal_render($form['vote']);
716 $variables['rest'] = drupal_render_children($form);
717 $variables['block'] = $form['#block'];
718 // If this is a block, allow a different tpl.php to be used.
719 if ($variables['block']) {
720 $variables['template_files'][] = 'poll-vote-block';
721 }
722 }
723
724 /**
725 * Generates a graphical representation of the results of a poll.
726 */
727 function poll_view_results(stdClass $node, $build_mode, $block = FALSE) {
728 // Count the votes and find the maximum
729 $total_votes = 0;
730 $max_votes = 0;
731 foreach ($node->choice as $choice) {
732 if (isset($choice['chvotes'])) {
733 $total_votes += $choice['chvotes'];
734 $max_votes = max($max_votes, $choice['chvotes']);
735 }
736 }
737
738 $poll_results = '';
739 foreach ($node->choice as $i => $choice) {
740 if (!empty($choice['chtext'])) {
741 $chvotes = isset($choice['chvotes']) ? $choice['chvotes'] : NULL;
742 $poll_results .= theme('poll_bar', array('title' => $choice['chtext'], 'votes' => $chvotes, 'total_votes' => $total_votes, 'vote' => isset($node->vote) && $node->vote == $i, 'block' => $block));
743 }
744 }
745
746 return theme('poll_results', array('raw_title' => $node->title[FIELD_LANGUAGE_NONE][0]['value'], 'results' => $poll_results, 'votes' => $total_votes, 'raw_links' => isset($node->links) ? $node->links : array(), 'block' => $block, 'nid' => $node->nid, 'vote' => isset($node->vote) ? $node->vote : NULL));
747 }
748
749
750 /**
751 * Theme the admin poll form for choices.
752 *
753 * @ingroup themeable
754 */
755 function theme_poll_choices($variables) {
756 $form = $variables['form'];
757
758 drupal_add_tabledrag('poll-choice-table', 'order', 'sibling', 'poll-weight');
759
760 $delta = 0;
761 $rows = array();
762 $headers = array(
763 '',
764 t('Choice'),
765 t('Vote count'),
766 t('Weight'),
767 );
768
769 foreach (element_children($form) as $key) {
770 $delta++;
771 // Set special classes for drag and drop updating.
772 $form[$key]['weight']['#attributes']['class'] = array('poll-weight');
773
774 // Build the table row.
775 $row = array(
776 'data' => array(
777 array('class' => array('choice-flag')),
778 drupal_render($form[$key]['chtext']),
779 drupal_render($form[$key]['chvotes']),
780 drupal_render($form[$key]['weight']),
781 ),
782 'class' => array('draggable'),
783 );
784
785 // Add any additional classes set on the row.
786 if (!empty($form[$key]['#attributes']['class'])) {
787 $row['class'] = array_merge($row['class'], $form[$key]['#attributes']['class']);
788 }
789
790 $rows[] = $row;
791 }
792
793 $output = theme('table', array('header' => $headers, 'rows' => $rows, 'attributes' => array('id' => 'poll-choice-table')));
794 $output .= drupal_render_children($form);
795 return $output;
796 }
797
798 /**
799 * Preprocess the poll_results theme hook.
800 *
801 * Inputs: $raw_title, $results, $votes, $raw_links, $block, $nid, $vote. The
802 * $raw_* inputs to this are naturally unsafe; often safe versions are
803 * made to simply overwrite the raw version, but in this case it seems likely
804 * that the title and the links may be overridden by the theme layer, so they
805 * are left in with a different name for that purpose.
806 *
807 * @see poll-results.tpl.php
808 * @see poll-results-block.tpl.php
809 * @see theme_poll_results()
810 */
811 function template_preprocess_poll_results(&$variables) {
812 $variables['links'] = theme('links', array('links' => $variables['raw_links']));
813 if (isset($variables['vote']) && $variables['vote'] > -1 && user_access('cancel own vote')) {
814 $variables['cancel_form'] = drupal_render(drupal_get_form('poll_cancel_form', $variables['nid']));
815 }
816 $variables['title'] = check_plain($variables['raw_title']);
817
818 // If this is a block, allow a different tpl.php to be used.
819 if ($variables['block']) {
820 $variables['template_files'][] = 'poll-results-block';
821 }
822 }
823
824 /**
825 * Preprocess the poll_bar theme hook.
826 *
827 * Inputs: $title, $votes, $total_votes, $voted, $block
828 *
829 * @see poll-bar.tpl.php
830 * @see poll-bar-block.tpl.php
831 * @see theme_poll_bar()
832 */
833 function template_preprocess_poll_bar(&$variables) {
834 if ($variables['block']) {
835 $variables['template_files'][] = 'poll-bar-block';
836 }
837 $variables['title'] = check_plain($variables['title']);
838 $variables['percentage'] = round($variables['votes'] * 100 / max($variables['total_votes'], 1));
839 }
840
841 /**
842 * Builds the cancel form for a poll.
843 *
844 * @ingroup forms
845 * @see poll_cancel()
846 */
847 function poll_cancel_form($form, &$form_state, $nid) {
848 // Store the nid so we can get to it in submit functions.
849 $form['#nid'] = $nid;
850
851 $form['submit'] = array(
852 '#type' => 'submit',
853 '#value' => t('Cancel your vote'),
854 '#submit' => array('poll_cancel')
855 );
856
857 $form['#cache'] = TRUE;
858
859 return $form;
860 }
861
862 /**
863 * Submit callback for poll_cancel_form().
864 */
865 function poll_cancel($form, &$form_state) {
866 global $user;
867 $node = node_load($form['#nid']);
868
869 db_delete('poll_vote')
870 ->condition('nid', $node->nid)
871 ->condition($user->uid ? 'uid' : 'hostname', $user->uid ? $user->uid : ip_address())
872 ->execute();
873
874 // Subtract from the votes.
875 db_update('poll_choice')
876 ->expression('chvotes', 'chvotes - 1')
877 ->condition('chid', $node->vote)
878 ->execute();
879
880 drupal_set_message(t('Your vote was cancelled.'));
881 }
882
883 /**
884 * Implement hook_user_cancel().
885 */
886 function poll_user_cancel($edit, $account, $method) {
887 switch ($method) {
888 case 'user_cancel_reassign':
889 db_update('poll_vote')
890 ->fields(array('uid' => 0))
891 ->condition('uid', $account->uid)
892 ->execute();
893 break;
894
895 case 'user_cancel_delete':
896 db_delete('poll_vote')
897 ->condition('uid', $account->uid)
898 ->execute();
899 break;
900 }
901 }
902

  ViewVC Help
Powered by ViewVC 1.1.2