Code coverage for /20080809/modules/poll/poll.module

Line #Times calledCode
1
<?php
2
// $Id: poll.module,v 1.270 2008/07/24 16:25:18 dries 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
 * Implementation of hook_help().
12
 */
13176
function poll_help($path, $arg) {
14
  switch ($path) {
15128
    case 'admin/help#poll':
160
      $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>';
170
      $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>';
180
      $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>';
190
      return $output;
200
  }
21128
}
22
23
/**
24
 * Implementation of hook_init().
25
 */
26176
function poll_init() {
27173
  drupal_add_css(drupal_get_path('module', 'poll') . '/poll.css');
28173
}
29
30
/**
31
 * Implementation of hook_theme().
32
 */
33176
function poll_theme() {
34
  return array(
35
    'poll_vote' => array(
364
      'template' => 'poll-vote',
374
      'arguments' => array('form' => NULL),
384
    ),
39
    'poll_choices' => array(
404
      'arguments' => array('form' => NULL),
414
    ),
42
    'poll_results' => array(
434
      'template' => 'poll-results',
444
      'arguments' => array('raw_title' => NULL, 'results' => NULL, 'votes'
=> NULL, 'raw_links' => NULL, 'block' => NULL, 'nid' => NULL, 'vote' =>
NULL),
454
    ),
46
    'poll_bar' => array(
474
      'template' => 'poll-bar',
484
      'arguments' => array('title' => NULL, 'votes' => NULL, 'total_votes'
=> NULL, 'vote' => NULL, 'block' => NULL),
494
    ),
504
  );
510
}
52
53
/**
54
 * Implementation of hook_perm().
55
 */
56176
function poll_perm() {
573
  $perms = node_list_permissions('poll');
58
  $perms += array(
593
    'vote on polls' => t('Cast votes on polls.'),
603
    'cancel own vote' => t('Retract and optionally change own votes.'),
613
    'inspect all votes' => t('View voting results.'),
620
  );
63
643
  return $perms;
650
}
66
67
/**
68
 * Implementation of hook_access().
69
 */
70176
function poll_access($op, $node, $account) {
71
  switch ($op) {
72124
    case 'create':
73121
      return user_access('create poll content', $account);
7412
    case 'update':
759
      return user_access('edit any poll content', $account) ||
(user_access('edit own poll content', $account) && ($node->uid ==
$account->uid));
7611
    case 'delete':
773
      return user_access('delete any poll content', $account) ||
(user_access('delete own poll content', $account) && ($node->uid ==
$account->uid));
780
  }
799
}
80
81
/**
82
 * Implementation of hook_menu().
83
 */
84176
function poll_menu() {
853
  $items['poll'] = array(
863
    'title' => 'Polls',
873
    'page callback' => 'poll_page',
883
    'access arguments' => array('access content'),
893
    'type' => MENU_SUGGESTED_ITEM,
90
  );
91
923
  $items['node/%node/votes'] = array(
933
    'title' => 'Votes',
943
    'page callback' => 'poll_votes',
953
    'page arguments' => array(1),
963
    'access callback' => '_poll_menu_access',
973
    'access arguments' => array(1, 'inspect all votes', FALSE),
983
    'weight' => 3,
993
    'type' => MENU_LOCAL_TASK,
100
  );
101
1023
  $items['node/%node/results'] = array(
1033
    'title' => 'Results',
1043
    'page callback' => 'poll_results',
1053
    'page arguments' => array(1),
1063
    'access callback' => '_poll_menu_access',
1073
    'access arguments' => array(1, 'access content', TRUE),
1083
    'weight' => 3,
1093
    'type' => MENU_LOCAL_TASK,
110
  );
111
1123
  $items['poll/js'] = array(
1133
    'title' => 'Javascript Choice Form',
1143
    'page callback' => 'poll_choice_js',
1153
    'access arguments' => array('access content'),
1163
    'type' => MENU_CALLBACK,
117
  );
118
1193
  return $items;
1200
}
121
122
/**
123
 * Callback function to see if a node is acceptable for poll menu items.
124
 */
125176
function _poll_menu_access($node, $perm, $inspect_allowvotes) {
12617
  return user_access($perm) && ($node->type == 'poll') &&
($node->allowvotes || !$inspect_allowvotes);
1270
}
128
129
/**
130
 * Implementation of hook_block().
131
 *
132
 * Generates a block containing the latest poll.
133
 */
134176
function poll_block($op = 'list', $delta = '') {
1350
  if (user_access('access content')) {
1360
    if ($op == 'list') {
1370
      $blocks['recent']['info'] = t('Most recent poll');
1380
      return $blocks;
1390
    }
1400
    else if ($op == 'view') {
141
      // Retrieve the latest poll.
1420
      $sql = db_rewrite_sql("SELECT MAX(n.created) FROM {node} n INNER JOIN
{poll} p ON p.nid = n.nid WHERE n.status = 1 AND p.active = 1");
1430
      $timestamp = db_result(db_query($sql));
1440
      if ($timestamp) {
1450
        $poll = node_load(array('type' => 'poll', 'created' => $timestamp,
'status' => 1));
146
1470
        if ($poll->nid) {
1480
          $poll = poll_view($poll, TRUE, FALSE, TRUE);
1490
        }
1500
      }
1510
      $block['subject'] = t('Poll');
1520
      $block['content'] = drupal_render($poll->content);
1530
      return $block;
1540
    }
1550
  }
1560
}
157
158
/**
159
 * Implementation of hook_cron().
160
 *
161
 * Closes polls that have exceeded their allowed runtime.
162
 */
163176
function poll_cron() {
1641
  $result = db_query('SELECT p.nid FROM {poll} p INNER JOIN {node} n ON
p.nid = n.nid WHERE (n.created + p.runtime) < ' . time() . ' AND p.active =
1 AND p.runtime != 0');
1651
  while ($poll = db_fetch_object($result)) {
1660
    db_query("UPDATE {poll} SET active = 0 WHERE nid = %d", $poll->nid);
1670
  }
1681
}
169
170
/**
171
 * Implementation of hook_node_info().
172
 */
173176
function poll_node_info() {
174
  return array(
175
    'poll' => array(
176146
      'name' => t('Poll'),
177146
      'module' => 'poll',
178146
      '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.'),
179146
      'title_label' => t('Question'),
180146
      'has_body' => FALSE,
181
    )
182146
  );
1830
}
184
185
/**
186
 * Implementation of hook_form().
187
 */
188176
function poll_form(&$node, $form_state) {
1897
  global $user;
190
1917
  $admin = user_access('administer nodes') || user_access('edit any poll
content') || (user_access('edit own poll content') && $user->uid ==
$node->uid);
192
1937
  $type = node_get_types('type', $node);
194
195
  $form = array(
1967
    '#cache' => TRUE,
1977
  );
198
1997
  $form['title'] = array(
2007
    '#type' => 'textfield',
2017
    '#title' => check_plain($type->title_label),
2027
    '#required' => TRUE,
2037
    '#default_value' => $node->title,
2047
    '#weight' => -5,
205
  );
206
2077
  if (isset($form_state['choice_count'])) {
2082
    $choice_count = $form_state['choice_count'];
2092
  }
210
  else {
2115
    $choice_count = max(2, empty($node->choice) ? 2 :
count($node->choice));
212
  }
213
214
  // Add a wrapper for the choices and more button.
2157
  $form['choice_wrapper'] = array(
2167
    '#tree' => FALSE,
2177
    '#weight' => -4,
2187
    '#prefix' => '<div class="clear-block" id="poll-choice-wrapper">',
2197
    '#suffix' => '</div>',
220
  );
221
222
  // Container for just the poll choices.
2237
  $form['choice_wrapper']['choice'] = array(
2247
    '#prefix' => '<div id="poll-choices">',
2257
    '#suffix' => '</div>',
2267
    '#theme' => 'poll_choices',
227
  );
228
229
  // Add the current choices to the form.
2307
  $delta = 0;
2317
  $weight = 0;
2327
  if (isset($node->choice)) {
2334
    $delta = count($node->choice);
2344
    $weight = -$delta;
2354
    foreach ($node->choice as $chid => $choice) {
2364
      $key = 'chid:'. $chid;
2374
      $form['choice_wrapper']['choice'][$key] = _poll_choice_form($key,
$choice['chid'], $choice['chtext'], $choice['chvotes'], $choice['weight'],
$choice_count);
2384
      $weight = ($choice['weight'] > $weight) ? $choice['weight'] :
$weight;
2394
    }
2404
  }
241
242
  // Add initial or additional choices.
2437
  $existing_delta = $delta;
2447
  for ($delta; $delta < $choice_count; $delta++) {
2455
    $key = 'new:'. ($delta - $existing_delta);
2465
    $form['choice_wrapper']['choice'][$key] = _poll_choice_form($key, NULL,
'', 0, $weight, $choice_count);
2475
  }
248
249
  // We name our button 'poll_more' to avoid conflicts with other modules
using
250
  // AHAH-enabled buttons with the id 'more'.
2517
  $form['choice_wrapper']['poll_more'] = array(
2527
    '#type' => 'submit',
2537
    '#value' => t('More choices'),
2547
    '#description' => t("If the amount of boxes above isn't enough, click
here to add more choices."),
2557
    '#weight' => 1,
2567
    '#submit' => array('poll_more_choices_submit'), // If no javascript
action.
257
    '#ahah' => array(
2587
      'path' => 'poll/js',
2597
      'wrapper' => 'poll-choices',
2607
      'method' => 'replace',
2617
      'effect' => 'fade',
2627
    ),
263
  );
264
265
  // Poll attributes
2667
  $duration = array(0 => t('Unlimited')) + drupal_map_assoc(array(86400,
172800, 345600, 604800, 1209600, 2419200, 4838400, 9676800, 31536000),
"format_interval");
2677
  $active = array(0 => t('Closed'), 1 => t('Active'));
268
2697
  $form['settings'] = array(
2707
    '#type' => 'fieldset',
2717
    '#collapsible' => TRUE,
2727
    '#title' => t('Poll settings'),
2737
    '#weight' => -3,
2747
    '#access' => $admin,
275
  );
276
2777
  $form['settings']['active'] = array(
2787
    '#type' => 'radios',
2797
    '#title' => t('Poll status'),
2807
    '#default_value' => isset($node->active) ? $node->active : 1,
2817
    '#options' => $active,
2827
    '#description' => t('When a poll is closed, visitors can no longer vote
for it.'),
2837
    '#access' => $admin,
284
  );
2857
  $form['settings']['runtime'] = array(
2867
    '#type' => 'select',
2877
    '#title' => t('Poll duration'),
2887
    '#default_value' => isset($node->runtime) ? $node->runtime : 0,
2897
    '#options' => $duration,
2907
    '#description' => t('After this period, the poll will be closed
automatically.'),
291
  );
292
2937
  return $form;
2940
}
295
296
/**
297
 * Submit handler to add more choices to a poll form. This handler is used
when
298
 * javascript is not available. It makes changes to the form state and the
299
 * entire form is rebuilt during the page reload.
300
 */
301176
function poll_more_choices_submit($form, &$form_state) {
302
  // Set the form to rebuild and run submit handlers.
3032
  node_form_submit_build_node($form, $form_state);
304
305
  // Make the changes we want to the form state.
3062
  if ($form_state['values']['poll_more']) {
3072
    $form_state['choice_count'] = count($form_state['values']['choice']) +
5;
3082
  }
3092
}
310
311176
function _poll_choice_form($key, $chid = NULL, $value = '', $votes = 0,
$weight = 0, $size = 10) {
3127
  $admin = user_access('administer nodes');
313
314
  $form = array(
3157
    '#tree' => TRUE,
3167
  );
317
318
  // We'll manually set the #parents property of these fields so that
319
  // their values appear in the $form_state['values']['choice'] array.
3207
  $form['chid'] = array(
3217
    '#type' => 'value',
3227
    '#value' => $chid,
3237
    '#parents' => array('choice', $key, 'chid'),
324
  );
325
3267
  $form['chtext'] = array(
3277
    '#type' => 'textfield',
3287
    '#default_value' => $value,
3297
    '#parents' => array('choice', $key, 'chtext'),
330
  );
331
3327
  $form['chvotes'] = array(
3337
    '#type' => 'textfield',
3347
    '#default_value' => $votes,
3357
    '#size' => 5,
3367
    '#maxlength' => 7,
3377
    '#parents' => array('choice', $key, 'chvotes'),
3387
    '#access' => user_access('administer nodes'),
339
  );
340
3417
  $form['weight'] = array(
3427
    '#type' => 'weight',
3437
    '#default_value' => $weight,
3447
    '#delta' => $size,
3457
    '#parents' => array('choice', $key, 'weight'),
346
  );
347
3487
  return $form;
3490
}
350
351
/**
352
 * Menu callback for AHAH additions.
353
 */
354176
function poll_choice_js() {
355
  // Add the new element to the stored form state. Without adding the
element
356
  // to the form, Drupal is not aware of this new elements existence and
will
357
  // not process it. We retreive the cached form, add the element, and
resave.
3580
  $form_build_id = $_POST['form_build_id'];
3590
  $form_state = array('submitted' => FALSE);
3600
  $form = form_get_cache($form_build_id, $form_state);
361
3620
  $delta = count($_POST['choice']);
3630
  $key = isset($form['#node']->choice) ? 'new:'. ($delta -
count($form['#node']->choice)) : 'new:'. $delta;
364
365
  // Match the new choice at the current greatest weight.
3660
  $weight = 0;
3670
  foreach ($_POST['choice'] as $choice) {
3680
    $weight = $choice['weight'] > $weight ? $choice['weight'] : $weight;
3690
  }
370
371
  // Build our new form element.
3720
  $form_element = _poll_choice_form($key, NULL, NULL, NULL, $weight, $delta
+ 1);
3730
  drupal_alter('form', $form_element, array(), 'poll_choice_js');
374
375
  // Dynamically increase the delta of the weight field for every field
added.
3760
  foreach(element_children($form['choice_wrapper']['choice']) as $n) {
3770
    $form['choice_wrapper']['choice'][$n]['weight']['#delta'] = $delta +
1;
3780
  }
379
380
  // Add the new poll choice.
3810
  $form['choice_wrapper']['choice'][$key] = $form_element;
382
383
  // Reorder the form to use the same order as post.
3840
  $order = array_flip(array_keys($_POST['choice']));
3850
  $form['choice_wrapper']['choice'] = array_merge($order,
$form['choice_wrapper']['choice']);
386
387
  // Resave the cache.
3880
  form_set_cache($form_build_id, $form, $form_state);
389
  $form += array(
3900
    '#post' => $_POST,
3910
    '#programmed' => FALSE,
3920
  );
393
394
  // Rebuild the form.
3950
  $form = form_builder('poll_node_form', $form, $form_state);
396
397
  // Render the new output.
3980
  $choice_form = $form['choice_wrapper']['choice'];
3990
  unset($choice_form['#prefix'], $choice_form['#suffix']); // Prevent
duplicate wrappers.
4000
  $choice_form[$key]['#attributes']['class'] =
empty($choice_form[$key]['#attributes']['class']) ? 'ahah-new-content' :
$choice_form[$key]['#attributes']['class'] .' ahah-new-content';
4010
  $choice_form[$key]['chvotes']['#value'] = 0;
4020
  $output = theme('status_messages') . drupal_render($choice_form);
403
4040
  drupal_json(array('status' => TRUE, 'data' => $output));
4050
}
406
407
/**
408
 * Implementation of hook_submit().
409
 */
410176
function poll_node_form_submit(&$form, &$form_state) {
411
  // Renumber fields
4127
  $form_state['values']['choice'] =
array_values($form_state['values']['choice']);
4137
  $form_state['values']['teaser'] =
poll_teaser((object)$form_state['values']);
4147
}
415
416
/**
417
 * Implementation of hook_validate().
418
 */
419176
function poll_validate($node) {
4207
  if (isset($node->title)) {
421
    // Check for at least two options and validate amount of votes:
4227
    $realchoices = 0;
423
    // Renumber fields
4247
    $node->choice = array_values($node->choice);
4257
    foreach ($node->choice as $i => $choice) {
4267
      if ($choice['chtext'] != '') {
4277
        $realchoices++;
4287
      }
4297
      if (isset($choice['chvotes']) && $choice['chvotes'] < 0) {
4300
        form_set_error("choice][$i][chvotes", t('Negative values are not
allowed.'));
4310
      }
4327
    }
433
4347
    if ($realchoices < 2) {
4350
      form_set_error("choice][$realchoices][chtext", t('You must fill in at
least two choices.'));
4360
    }
4377
  }
4387
}
439
440
/**
441
 * Implementation of hook_load().
442
 */
443176
function poll_load($node) {
44416
  global $user;
445
44616
  $poll = db_fetch_object(db_query("SELECT runtime, active FROM {poll}
WHERE nid = %d", $node->nid));
447
448
  // Load the appropriate choices into the $poll object.
44916
  $result = db_query("SELECT chid, chtext, chvotes, weight FROM
{poll_choices} WHERE nid = %d ORDER BY weight", $node->nid);
45016
  while ($choice = db_fetch_array($result)) {
45116
    $poll->choice[$choice['chid']] = $choice;
45216
  }
453
454
  // Determine whether or not this user is allowed to vote.
45516
  $poll->allowvotes = FALSE;
45616
  if (user_access('vote on polls') && $poll->active) {
4577
    if ($user->uid) {
4587
      $result = db_fetch_object(db_query('SELECT chid FROM {poll_votes}
WHERE nid = %d AND uid = %d', $node->nid, $user->uid));
4597
    }
460
    else {
4610
      $result = db_fetch_object(db_query("SELECT chid FROM {poll_votes}
WHERE nid = %d AND hostname = '%s'", $node->nid, ip_address()));
462
    }
4637
    if (isset($result->chid)) {
4642
      $poll->vote = $result->chid;
4652
    }
466
    else {
4675
      $poll->vote = -1;
4685
      $poll->allowvotes = TRUE;
469
    }
4707
  }
47116
  return $poll;
4720
}
473
474
/**
475
 * Implementation of hook_insert().
476
 */
477176
function poll_insert($node) {
4783
  if (!user_access('administer nodes')) {
479
    // Make sure all votes are 0 initially
4803
    foreach ($node->choice as $i => $choice) {
4813
      $node->choice[$i]['chvotes'] = 0;
4823
    }
4833
    $node->active = 1;
4843
  }
485
4863
  db_query("INSERT INTO {poll} (nid, runtime, active) VALUES (%d, %d, %d)",
$node->nid, $node->runtime, $node->active);
487
4883
  foreach ($node->choice as $choice) {
4893
    if ($choice['chtext'] != '') {
4903
      db_query("INSERT INTO {poll_choices} (nid, chtext, chvotes, weight)
VALUES (%d, '%s', %d, %d)", $node->nid, $choice['chtext'],
$choice['chvotes'], $choice['weight']);
4913
    }
4923
  }
4933
}
494
495
/**
496
 * Implementation of hook_update().
497
 */
498176
function poll_update($node) {
499
  // Update poll settings.
5001
  db_query('UPDATE {poll} SET runtime = %d, active = %d WHERE nid = %d',
$node->runtime, $node->active, $node->nid);
501
502
  // Poll choices with empty titles signifies removal. We remove all votes
to
503
  // the removed options, so people who voted on them can vote again.
5041
  foreach ($node->choice as $key => $choice) {
5051
    if (!empty($choice['chtext'])) {
5061
      if (isset($choice['chid'])) {
5071
        db_query("UPDATE {poll_choices} SET chtext = '%s', chvotes = %d,
weight = %d WHERE chid = %d", $choice['chtext'], (int)$choice['chvotes'],
$choice['weight'], $choice['chid']);
5081
      }
509
      else {
5100
        db_query("INSERT INTO {poll_choices} (nid, chtext, chvotes, weight)
VALUES (%d, '%s', %d, %d)", $node->nid, $choice['chtext'],
(int)$choice['chvotes'], $choice['weight']);
511
      }
5121
    }
513
    else {
5140
      db_query("DELETE FROM {poll_votes} WHERE nid = %d AND chid = %d",
$node->nid, $key);
515
    }
5161
  }
5171
}
518
519
/**
520
 * Implementation of hook_delete().
521
 */
522176
function poll_delete($node) {
5231
  db_query("DELETE FROM {poll} WHERE nid = %d", $node->nid);
5241
  db_query("DELETE FROM {poll_choices} WHERE nid = %d", $node->nid);
5251
  db_query("DELETE FROM {poll_votes} WHERE nid = %d", $node->nid);
5261
}
527
528
/**
529
 * Implementation of hook_view().
530
 *
531
 * @param $block
532
 *   An extra parameter that adapts the hook to display a block-ready
533
 *   rendering of the poll.
534
 */
535176
function poll_view($node, $teaser = FALSE, $page = FALSE, $block = FALSE)
{
5369
  global $user;
5379
  $output = '';
538
539
  // Special display for side-block
5409
  if ($block) {
541
    // No 'read more' link
5420
    $node->readmore = FALSE;
543
5440
    $links = module_invoke_all('link', 'node', $node, 1);
5450
    $links[] = array('title' => t('Older polls'), 'href' => 'poll',
'attributes' => array('title' => t('View the list of polls on this
site.')));
5460
    if ($node->allowvotes && $block) {
5470
      $links[] = array('title' => t('Results'), 'href' => 'node/' .
$node->nid . '/results', 'attributes' => array('title' => t('View the
current poll results.')));
5480
    }
549
5500
    $node->links = $links;
5510
  }
552
5539
  if (!empty($node->allowvotes) && ($block || empty($node->show_results)))
{
5541
    $node->content['body'] = array(
5552
      '#markup' => drupal_get_form('poll_view_voting', $node, $block),
556
    );
5571
  }
558
  else {
5597
    $node->content['body'] = array(
5607
      '#markup' => poll_view_results($node, $teaser, $page, $block),
561
    );
562
  }
5638
  return $node;
5640
}
565
566
/**
567
 * Creates a simple teaser that lists all the choices.
568
 *
569
 * This is primarily used for RSS.
570
 */
571176
function poll_teaser($node) {
5727
  $teaser = NULL;
5737
  if (is_array($node->choice)) {
5747
    foreach ($node->choice as $k => $choice) {
5757
      if ($choice['chtext'] != '') {
5767
        $teaser .= '* ' . check_plain($choice['chtext']) . "\n";
5777
      }
5787
    }
5797
  }
5807
  return $teaser;
5810
}
582
583
/**
584
 * Generates the voting form for a poll.
585
 *
586
 * @ingroup forms
587
 * @see poll_vote()
588
 * @see phptemplate_preprocess_poll_vote()
589
 */
590176
function poll_view_voting(&$form_state, $node, $block) {
5911
  if ($node->choice) {
5921
    $list = array();
5931
    foreach ($node->choice as $i => $choice) {
5941
      $list[$i] = check_plain($choice['chtext']);
5951
    }
5961
    $form['choice'] = array(
5971
      '#type' => 'radios',
5981
      '#default_value' => -1,
5991
      '#options' => $list,
600
    );
6011
  }
602
6031
  $form['vote'] = array(
6041
    '#type' => 'submit',
6051
    '#value' => t('Vote'),
6061
    '#submit' => array('poll_vote'),
607
  );
608
609
  // Store the node so we can get to it in submit functions.
6101
  $form['#node'] = $node;
6111
  $form['#block'] = $block;
612
613
  // Set form caching because we could have multiple of these forms on
614
  // the same page, and we want to ensure the right one gets picked.
6151
  $form['#cache'] = TRUE;
616
617
  // Provide a more cleanly named voting form theme.
6181
  $form['#theme'] = 'poll_vote';
6191
  return $form;
6200
}
621
622
/**
623
 * Validation function for processing votes
624
 */
625176
function poll_view_voting_validate($form, &$form_state) {
6261
  if ($form_state['values']['choice'] == -1) {
6270
    form_set_error( 'choice', t('Your vote could not be recorded because
you did not select any of the choices.'));
6280
  }
6291
}
630
631
/**
632
 * Submit handler for processing a vote
633
 */
634176
function poll_vote($form, &$form_state) {
6351
  $node = $form['#node'];
6361
  $choice = $form_state['values']['choice'];
637
6381
  global $user;
6391
  if ($user->uid) {
6401
    db_query('INSERT INTO {poll_votes} (nid, chid, uid) VALUES (%d, %d,
%d)', $node->nid, $choice, $user->uid);
6411
  }
642
  else {
6430
    db_query("INSERT INTO {poll_votes} (nid, chid, hostname) VALUES (%d,
%d, '%s')", $node->nid, $choice, ip_address());
644
  }
645
646
  // Add one to the votes.
6471
  db_query("UPDATE {poll_choices} SET chvotes = chvotes + 1 WHERE chid =
%d", $choice);
648
6491
  cache_clear_all();
6501
  drupal_set_message(t('Your vote was recorded.'));
651
652
  // Return the user to whatever page they voted from.
6531
}
654
655
/**
656
 * Themes the voting form for a poll.
657
 *
658
 * Inputs: $form
659
 */
660176
function template_preprocess_poll_vote(&$variables) {
6611
  $form = $variables['form'];
6621
  $variables['choice'] = drupal_render($form['choice']);
6631
  $variables['title'] = check_plain($form['#node']->title);
6641
  $variables['vote'] = drupal_render($form['vote']);
6651
  $variables['rest'] = drupal_render($form);
6661
  $variables['block'] = $form['#block'];
667
  // If this is a block, allow a different tpl.php to be used.
6681
  if ($variables['block']) {
6690
    $variables['template_files'][] = 'poll-vote-block';
6700
  }
6711
}
672
673
/**
674
 * Generates a graphical representation of the results of a poll.
675
 */
676176
function poll_view_results(&$node, $teaser, $page, $block) {
677
  // Count the votes and find the maximum
6787
  $total_votes = 0;
6797
  $max_votes = 0;
6807
  foreach ($node->choice as $choice) {
6817
    if (isset($choice['chvotes'])) {
6827
      $total_votes += $choice['chvotes'];
6837
      $max_votes = max($max_votes, $choice['chvotes']);
6847
    }
6857
  }
686
6877
  $poll_results = '';
6887
  foreach ($node->choice as $i => $choice) {
6897
    if (!empty($choice['chtext'])) {
6907
      $chvotes = isset($choice['chvotes']) ? $choice['chvotes'] : NULL;
6917
      $poll_results .= theme('poll_bar', $choice['chtext'], $chvotes,
$total_votes, isset($node->vote) && $node->vote == $i, $block);
6927
    }
6937
  }
694
6957
  return theme('poll_results', $node->title, $poll_results, $total_votes,
isset($node->links) ? $node->links : array(), $block, $node->nid,
isset($node->vote) ? $node->vote : NULL);
6960
}
697
698
699
/**
700
 * Theme the admin poll form for choices.
701
 *
702
 * @ingroup themeable
703
 */
704176
function theme_poll_choices($form) {
7057
  drupal_add_tabledrag('poll-choice-table', 'order', 'sibling',
'poll-weight');
706
7077
  $delta = 0;
7087
  $rows = array();
709
  $headers = array(
7107
    '',
7117
    t('Choice'),
7127
    t('Vote count'),
7137
    t('Weight'),
7147
  );
715
7167
  foreach (element_children($form) as $key) {
7177
    $delta++;
718
    // Set special classes for drag and drop updating.
7197
    $form[$key]['weight']['#attributes']['class'] = 'poll-weight';
720
721
    // Build the table row.
722
    $row = array(
723
      'data' => array(
7247
        array('class' => 'choice-flag'),
7257
        drupal_render($form[$key]['chtext']),
7267
        drupal_render($form[$key]['chvotes']),
7277
        drupal_render($form[$key]['weight']),
7287
      ),
7297
      'class' => 'draggable',
7307
    );
731
732
    // Add any additional classes set on the row.
7337
    $row['class'] .= isset($form[$key]['#attributes']['class']) ? ' '.
$form[$key]['#attributes']['class'] : '';
734
7357
    $rows[] = $row;
7367
  }
737
7387
  $output = theme('table', $headers, $rows, array('id' =>
'poll-choice-table'));
7397
  $output .= drupal_render($form);
7407
  return $output;
7410
}
742
743
/**
744
 * Preprocess the poll_results theme hook.
745
 *
746
 * Inputs: $raw_title, $results, $votes, $raw_links, $block, $nid, $vote.
The
747
 * $raw_* inputs to this are naturally unsafe; often safe versions are
748
 * made to simply overwrite the raw version, but in this case it seems
likely
749
 * that the title and the links may be overridden by the theme layer, so
they
750
 * are left in with a different name for that purpose.
751
 *
752
 * @see poll-results.tpl.php
753
 * @see poll-results-block.tpl.php
754
 * @see theme_poll_results()
755
 */
756176
function template_preprocess_poll_results(&$variables) {
7577
  $variables['links'] = theme('links', $variables['raw_links']);
7587
  if (isset($variables['vote']) && $variables['vote'] > -1 &&
user_access('cancel own vote')) {
7591
    $variables['cancel_form'] = drupal_get_form('poll_cancel_form',
$variables['nid']);
7601
  }
7617
  $variables['title'] = check_plain($variables['raw_title']);
762
763
  // If this is a block, allow a different tpl.php to be used.
7647
  if ($variables['block']) {
7650
    $variables['template_files'][] = 'poll-results-block';
7660
  }
7677
}
768
769
/**
770
 * Preprocess the poll_bar theme hook.
771
 *
772
 * Inputs: $title, $votes, $total_votes, $voted, $block
773
 *
774
 * @see poll-bar.tpl.php
775
 * @see poll-bar-block.tpl.php
776
 * @see theme_poll_bar()
777
 */
778176
function template_preprocess_poll_bar(&$variables) {
7797
  if ($variables['block']) {
7800
    $variables['template_files'][] = 'poll-bar-block';
7810
  }
7827
  $variables['title'] = check_plain($variables['title']);
7837
  $variables['percentage'] = round($variables['votes'] * 100 /
max($variables['total_votes'], 1));
7847
}
785
786
/**
787
 * Builds the cancel form for a poll.
788
 *
789
 * @ingroup forms
790
 * @see poll_cancel()
791
 */
792176
function poll_cancel_form(&$form_state, $nid) {
793
  // Store the nid so we can get to it in submit functions.
7941
  $form['#nid'] = $nid;
795
7961
  $form['submit'] = array(
7971
    '#type' => 'submit',
7981
    '#value' => t('Cancel your vote'),
7991
    '#submit' => array('poll_cancel')
8001
  );
801
8021
  $form['#cache'] = TRUE;
803
8041
  return $form;
8050
}
806
807
/**
808
 * Submit callback for poll_cancel_form
809
 */
810176
function poll_cancel($form, &$form_state) {
8110
  $node = node_load($form['#nid']);
8120
  global $user;
813
8140
  if ($user->uid) {
8150
    db_query('DELETE FROM {poll_votes} WHERE nid = %d and uid = %d',
$node->nid, $user->uid);
8160
  }
817
  else {
8180
    db_query("DELETE FROM {poll_votes} WHERE nid = %d and hostname = '%s'",
$node->nid, ip_address());
819
  }
820
821
  // Subtract from the votes.
8220
  db_query("UPDATE {poll_choices} SET chvotes = chvotes - 1 WHERE chid =
%d", $node->vote);
8230
}
824
825
/**
826
 * Implementation of hook_user().
827
 */
828176
function poll_user($op, &$edit, &$user) {
829155
  if ($op == 'delete') {
8301
    db_query('UPDATE {poll_votes} SET uid = 0 WHERE uid = %d',
$user->uid);
8311
  }
832155
}
833176