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

Line #Times calledCode
1
<?php
2
// $Id: poll.module,v 1.277 2008/10/12 04:30:07 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
 * Implementation of hook_help().
12
 */
13176
function poll_help($path, $arg) {
14
  switch ($path) {
15114
    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
  }
21114
}
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(
59
    'vote on polls' => array(
603
      'title' => t('Vote on polls'),
613
      'description' => t('Cast votes on polls.'),
623
    ),
63
    'cancel own vote' => array(
643
      'title' => t('Cancel own vote'),
653
      'description' => t('Retract and optionally change own votes.'),
663
    ),
67
    'inspect all votes' => array(
683
      'title' => t('Inspect all votes'),
693
      'description' => t('View voting results.'),
703
    ),
710
  );
72
733
  return $perms;
740
}
75
76
/**
77
 * Implementation of hook_access().
78
 */
79176
function poll_access($op, $node, $account) {
80
  switch ($op) {
81124
    case 'create':
82121
      return user_access('create poll content', $account);
8312
    case 'update':
849
      return user_access('edit any poll content', $account) ||
(user_access('edit own poll content', $account) && ($node->uid ==
$account->uid));
8511
    case 'delete':
863
      return user_access('delete any poll content', $account) ||
(user_access('delete own poll content', $account) && ($node->uid ==
$account->uid));
870
  }
889
}
89
90
/**
91
 * Implementation of hook_menu().
92
 */
93176
function poll_menu() {
943
  $items['poll'] = array(
953
    'title' => 'Polls',
963
    'page callback' => 'poll_page',
973
    'access arguments' => array('access content'),
983
    'type' => MENU_SUGGESTED_ITEM,
99
  );
100
1013
  $items['node/%node/votes'] = array(
1023
    'title' => 'Votes',
1033
    'page callback' => 'poll_votes',
1043
    'page arguments' => array(1),
1053
    'access callback' => '_poll_menu_access',
1063
    'access arguments' => array(1, 'inspect all votes', FALSE),
1073
    'weight' => 3,
1083
    'type' => MENU_LOCAL_TASK,
109
  );
110
1113
  $items['node/%node/results'] = array(
1123
    'title' => 'Results',
1133
    'page callback' => 'poll_results',
1143
    'page arguments' => array(1),
1153
    'access callback' => '_poll_menu_access',
1163
    'access arguments' => array(1, 'access content', TRUE),
1173
    'weight' => 3,
1183
    'type' => MENU_LOCAL_TASK,
119
  );
120
1213
  $items['poll/js'] = array(
1223
    'title' => 'Javascript Choice Form',
1233
    'page callback' => 'poll_choice_js',
1243
    'access arguments' => array('access content'),
1253
    'type' => MENU_CALLBACK,
126
  );
127
1283
  return $items;
1290
}
130
131
/**
132
 * Callback function to see if a node is acceptable for poll menu items.
133
 */
134176
function _poll_menu_access($node, $perm, $inspect_allowvotes) {
13517
  return user_access($perm) && ($node->type == 'poll') &&
($node->allowvotes || !$inspect_allowvotes);
1360
}
137
138
/**
139
 * Implementation of hook_block().
140
 *
141
 * Generates a block containing the latest poll.
142
 */
143176
function poll_block($op = 'list', $delta = '') {
1440
  if (user_access('access content')) {
1450
    if ($op == 'list') {
1460
      $blocks['recent']['info'] = t('Most recent poll');
1470
      return $blocks;
1480
    }
1490
    elseif ($op == 'view') {
150
      // Retrieve the latest poll.
1510
      $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");
1520
      $timestamp = db_result(db_query($sql));
1530
      if ($timestamp) {
1540
        $poll = node_load(array('type' => 'poll', 'created' => $timestamp,
'status' => 1));
155
1560
        if ($poll->nid) {
1570
          $poll = poll_view($poll, TRUE, FALSE, TRUE);
1580
        }
1590
      }
1600
      $block['subject'] = t('Poll');
1610
      $block['content'] = drupal_render($poll->content);
1620
      return $block;
1630
    }
1640
  }
1650
}
166
167
/**
168
 * Implementation of hook_cron().
169
 *
170
 * Closes polls that have exceeded their allowed runtime.
171
 */
172176
function poll_cron() {
1731
  $result = 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 = 1 AND p.runtime != 0');
1741
  while ($poll = db_fetch_object($result)) {
1750
    db_query("UPDATE {poll} SET active = 0 WHERE nid = %d", $poll->nid);
1760
  }
1771
}
178
179
/**
180
 * Implementation of hook_node_info().
181
 */
182176
function poll_node_info() {
183
  return array(
184
    'poll' => array(
185146
      'name' => t('Poll'),
186146
      'base' => 'poll',
187146
      '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.'),
188146
      'title_label' => t('Question'),
189146
      'has_body' => FALSE,
190
    )
191146
  );
1920
}
193
194
/**
195
 * Implementation of hook_form().
196
 */
197176
function poll_form(&$node, $form_state) {
1987
  global $user;
199
2007
  $admin = user_access('administer nodes') || user_access('edit any poll
content') || (user_access('edit own poll content') && $user->uid ==
$node->uid);
201
2027
  $type = node_get_types('type', $node);
203
204
  $form = array(
2057
    '#cache' => TRUE,
2067
  );
207
2087
  $form['title'] = array(
2097
    '#type' => 'textfield',
2107
    '#title' => check_plain($type->title_label),
2117
    '#required' => TRUE,
2127
    '#default_value' => $node->title,
2137
    '#weight' => -5,
214
  );
215
2167
  if (isset($form_state['choice_count'])) {
2172
    $choice_count = $form_state['choice_count'];
2182
  }
219
  else {
2205
    $choice_count = max(2, empty($node->choice) ? 2 :
count($node->choice));
221
  }
222
223
  // Add a wrapper for the choices and more button.
2247
  $form['choice_wrapper'] = array(
2257
    '#tree' => FALSE,
2267
    '#weight' => -4,
2277
    '#prefix' => '<div class="clear-block" id="poll-choice-wrapper">',
2287
    '#suffix' => '</div>',
229
  );
230
231
  // Container for just the poll choices.
2327
  $form['choice_wrapper']['choice'] = array(
2337
    '#prefix' => '<div id="poll-choices">',
2347
    '#suffix' => '</div>',
2357
    '#theme' => 'poll_choices',
236
  );
237
238
  // Add the current choices to the form.
2397
  $delta = 0;
2407
  $weight = 0;
2417
  if (isset($node->choice)) {
2424
    $delta = count($node->choice);
2434
    $weight = -$delta;
2444
    foreach ($node->choice as $chid => $choice) {
2454
      $key = 'chid:'. $chid;
2464
      $form['choice_wrapper']['choice'][$key] = _poll_choice_form($key,
$choice['chid'], $choice['chtext'], $choice['chvotes'], $choice['weight'],
$choice_count);
2474
      $weight = ($choice['weight'] > $weight) ? $choice['weight'] :
$weight;
2484
    }
2494
  }
250
251
  // Add initial or additional choices.
2527
  $existing_delta = $delta;
2537
  for ($delta; $delta < $choice_count; $delta++) {
2545
    $key = 'new:'. ($delta - $existing_delta);
2555
    $form['choice_wrapper']['choice'][$key] = _poll_choice_form($key, NULL,
'', 0, $weight, $choice_count);
2565
  }
257
258
  // We name our button 'poll_more' to avoid conflicts with other modules
using
259
  // AHAH-enabled buttons with the id 'more'.
2607
  $form['choice_wrapper']['poll_more'] = array(
2617
    '#type' => 'submit',
2627
    '#value' => t('More choices'),
2637
    '#description' => t("If the amount of boxes above isn't enough, click
here to add more choices."),
2647
    '#weight' => 1,
2657
    '#submit' => array('poll_more_choices_submit'), // If no javascript
action.
266
    '#ahah' => array(
2677
      'path' => 'poll/js',
2687
      'wrapper' => 'poll-choices',
2697
      'method' => 'replace',
2707
      'effect' => 'fade',
2717
    ),
272
  );
273
274
  // Poll attributes
2757
  $duration = array(0 => t('Unlimited')) + drupal_map_assoc(array(86400,
172800, 345600, 604800, 1209600, 2419200, 4838400, 9676800, 31536000),
"format_interval");
2767
  $active = array(0 => t('Closed'), 1 => t('Active'));
277
2787
  $form['settings'] = array(
2797
    '#type' => 'fieldset',
2807
    '#collapsible' => TRUE,
2817
    '#title' => t('Poll settings'),
2827
    '#weight' => -3,
2837
    '#access' => $admin,
284
  );
285
2867
  $form['settings']['active'] = array(
2877
    '#type' => 'radios',
2887
    '#title' => t('Poll status'),
2897
    '#default_value' => isset($node->active) ? $node->active : 1,
2907
    '#options' => $active,
2917
    '#description' => t('When a poll is closed, visitors can no longer vote
for it.'),
2927
    '#access' => $admin,
293
  );
2947
  $form['settings']['runtime'] = array(
2957
    '#type' => 'select',
2967
    '#title' => t('Poll duration'),
2977
    '#default_value' => isset($node->runtime) ? $node->runtime : 0,
2987
    '#options' => $duration,
2997
    '#description' => t('After this period, the poll will be closed
automatically.'),
300
  );
301
3027
  return $form;
3030
}
304
305
/**
306
 * Submit handler to add more choices to a poll form. This handler is used
when
307
 * javascript is not available. It makes changes to the form state and the
308
 * entire form is rebuilt during the page reload.
309
 */
310176
function poll_more_choices_submit($form, &$form_state) {
311
  // Set the form to rebuild and run submit handlers.
3122
  node_form_submit_build_node($form, $form_state);
313
314
  // Make the changes we want to the form state.
3152
  if ($form_state['values']['poll_more']) {
3162
    $form_state['choice_count'] = count($form_state['values']['choice']) +
5;
3172
  }
3182
}
319
320176
function _poll_choice_form($key, $chid = NULL, $value = '', $votes = 0,
$weight = 0, $size = 10) {
3217
  $admin = user_access('administer nodes');
322
323
  $form = array(
3247
    '#tree' => TRUE,
3257
  );
326
327
  // We'll manually set the #parents property of these fields so that
328
  // their values appear in the $form_state['values']['choice'] array.
3297
  $form['chid'] = array(
3307
    '#type' => 'value',
3317
    '#value' => $chid,
3327
    '#parents' => array('choice', $key, 'chid'),
333
  );
334
3357
  $form['chtext'] = array(
3367
    '#type' => 'textfield',
3377
    '#default_value' => $value,
3387
    '#parents' => array('choice', $key, 'chtext'),
339
  );
340
3417
  $form['chvotes'] = array(
3427
    '#type' => 'textfield',
3437
    '#default_value' => $votes,
3447
    '#size' => 5,
3457
    '#maxlength' => 7,
3467
    '#parents' => array('choice', $key, 'chvotes'),
3477
    '#access' => user_access('administer nodes'),
348
  );
349
3507
  $form['weight'] = array(
3517
    '#type' => 'weight',
3527
    '#default_value' => $weight,
3537
    '#delta' => $size,
3547
    '#parents' => array('choice', $key, 'weight'),
355
  );
356
3577
  return $form;
3580
}
359
360
/**
361
 * Menu callback for AHAH additions.
362
 */
363176
function poll_choice_js() {
364
  // Add the new element to the stored form state. Without adding the
element
365
  // to the form, Drupal is not aware of this new elements existence and
will
366
  // not process it. We retreive the cached form, add the element, and
resave.
3670
  $form_build_id = $_POST['form_build_id'];
3680
  $form_state = array('submitted' => FALSE);
3690
  if (!$form = form_get_cache($form_build_id, $form_state)) {
3700
    exit();
3710
  }
372
3730
  $delta = count($_POST['choice']);
3740
  $key = isset($form['#node']->choice) ? 'new:'. ($delta -
count($form['#node']->choice)) : 'new:'. $delta;
375
376
  // Match the new choice at the current greatest weight.
3770
  $weight = 0;
3780
  foreach ($_POST['choice'] as $choice) {
3790
    $weight = $choice['weight'] > $weight ? $choice['weight'] : $weight;
3800
  }
381
382
  // Build our new form element.
3830
  $form_element = _poll_choice_form($key, NULL, NULL, NULL, $weight, $delta
+ 1);
3840
  drupal_alter('form', $form_element, array(), 'poll_choice_js');
385
386
  // Dynamically increase the delta of the weight field for every field
added.
3870
  foreach(element_children($form['choice_wrapper']['choice']) as $n) {
3880
    $form['choice_wrapper']['choice'][$n]['weight']['#delta'] = $delta + 1;
3890
  }
390
391
  // Add the new poll choice.
3920
  $form['choice_wrapper']['choice'][$key] = $form_element;
393
394
  // Reorder the form to use the same order as post.
3950
  $order = array_flip(array_keys($_POST['choice']));
3960
  $form['choice_wrapper']['choice'] = array_merge($order,
$form['choice_wrapper']['choice']);
397
398
  // Resave the cache.
3990
  form_set_cache($form_build_id, $form, $form_state);
400
  $form += array(
4010
    '#post' => $_POST,
4020
    '#programmed' => FALSE,
4030
  );
404
405
  // Rebuild the form.
4060
  $form = form_builder('poll_node_form', $form, $form_state);
407
408
  // Render the new output.
4090
  $choice_form = $form['choice_wrapper']['choice'];
4100
  unset($choice_form['#prefix'], $choice_form['#suffix']); // Prevent
duplicate wrappers.
4110
  $choice_form[$key]['#attributes']['class'] =
empty($choice_form[$key]['#attributes']['class']) ? 'ahah-new-content' :
$choice_form[$key]['#attributes']['class'] .' ahah-new-content';
4120
  $choice_form[$key]['chvotes']['#value'] = 0;
4130
  $output = theme('status_messages') . drupal_render($choice_form);
414
4150
  drupal_json(array('status' => TRUE, 'data' => $output));
4160
}
417
418
/**
419
 * Implementation of hook_submit().
420
 */
421176
function poll_node_form_submit(&$form, &$form_state) {
422
  // Renumber fields
4237
  $form_state['values']['choice'] =
array_values($form_state['values']['choice']);
4247
  $form_state['values']['teaser'] =
poll_teaser((object)$form_state['values']);
4257
}
426
427
/**
428
 * Implementation of hook_validate().
429
 */
430176
function poll_validate($node) {
4317
  if (isset($node->title)) {
432
    // Check for at least two options and validate amount of votes:
4337
    $realchoices = 0;
434
    // Renumber fields
4357
    $node->choice = array_values($node->choice);
4367
    foreach ($node->choice as $i => $choice) {
4377
      if ($choice['chtext'] != '') {
4387
        $realchoices++;
4397
      }
4407
      if (isset($choice['chvotes']) && $choice['chvotes'] < 0) {
4410
        form_set_error("choice][$i][chvotes", t('Negative values are not
allowed.'));
4420
      }
4437
    }
444
4457
    if ($realchoices < 2) {
4460
      form_set_error("choice][$realchoices][chtext", t('You must fill in at
least two choices.'));
4470
    }
4487
  }
4497
}
450
451
/**
452
 * Implementation of hook_load().
453
 */
454176
function poll_load($node) {
45516
  global $user;
456
45716
  $poll = db_fetch_object(db_query("SELECT runtime, active FROM {poll}
WHERE nid = %d", $node->nid));
458
459
  // Load the appropriate choices into the $poll object.
46016
  $result = db_query("SELECT chid, chtext, chvotes, weight FROM
{poll_choices} WHERE nid = %d ORDER BY weight", $node->nid);
46116
  while ($choice = db_fetch_array($result)) {
46216
    $poll->choice[$choice['chid']] = $choice;
46316
  }
464
465
  // Determine whether or not this user is allowed to vote.
46616
  $poll->allowvotes = FALSE;
46716
  if (user_access('vote on polls') && $poll->active) {
4687
    if ($user->uid) {
4697
      $result = db_fetch_object(db_query('SELECT chid FROM {poll_votes}
WHERE nid = %d AND uid = %d', $node->nid, $user->uid));
4707
    }
471
    else {
4720
      $result = db_fetch_object(db_query("SELECT chid FROM {poll_votes}
WHERE nid = %d AND hostname = '%s'", $node->nid, ip_address()));
473
    }
4747
    if (isset($result->chid)) {
4752
      $poll->vote = $result->chid;
4762
    }
477
    else {
4785
      $poll->vote = -1;
4795
      $poll->allowvotes = TRUE;
480
    }
4817
  }
48216
  return $poll;
4830
}
484
485
/**
486
 * Implementation of hook_insert().
487
 */
488176
function poll_insert($node) {
4893
  if (!user_access('administer nodes')) {
490
    // Make sure all votes are 0 initially
4913
    foreach ($node->choice as $i => $choice) {
4923
      $node->choice[$i]['chvotes'] = 0;
4933
    }
4943
    $node->active = 1;
4953
  }
496
4973
  db_query("INSERT INTO {poll} (nid, runtime, active) VALUES (%d, %d, %d)",
$node->nid, $node->runtime, $node->active);
498
4993
  foreach ($node->choice as $choice) {
5003
    if ($choice['chtext'] != '') {
5013
      db_query("INSERT INTO {poll_choices} (nid, chtext, chvotes, weight)
VALUES (%d, '%s', %d, %d)", $node->nid, $choice['chtext'],
$choice['chvotes'], $choice['weight']);
5023
    }
5033
  }
5043
}
505
506
/**
507
 * Implementation of hook_update().
508
 */
509176
function poll_update($node) {
510
  // Update poll settings.
5111
  db_query('UPDATE {poll} SET runtime = %d, active = %d WHERE nid = %d',
$node->runtime, $node->active, $node->nid);
512
513
  // Poll choices with empty titles signifies removal. We remove all votes
to
514
  // the removed options, so people who voted on them can vote again.
5151
  foreach ($node->choice as $key => $choice) {
5161
    if (!empty($choice['chtext'])) {
5171
      if (isset($choice['chid'])) {
5181
        db_query("UPDATE {poll_choices} SET chtext = '%s', chvotes = %d,
weight = %d WHERE chid = %d", $choice['chtext'], (int)$choice['chvotes'],
$choice['weight'], $choice['chid']);
5191
      }
520
      else {
5210
        db_query("INSERT INTO {poll_choices} (nid, chtext, chvotes, weight)
VALUES (%d, '%s', %d, %d)", $node->nid, $choice['chtext'],
(int)$choice['chvotes'], $choice['weight']);
522
      }
5231
    }
524
    else {
5250
      db_query("DELETE FROM {poll_votes} WHERE nid = %d AND chid = %d",
$node->nid, $key);
526
    }
5271
  }
5281
}
529
530
/**
531
 * Implementation of hook_delete().
532
 */
533176
function poll_delete($node) {
5341
  db_query("DELETE FROM {poll} WHERE nid = %d", $node->nid);
5351
  db_query("DELETE FROM {poll_choices} WHERE nid = %d", $node->nid);
5361
  db_query("DELETE FROM {poll_votes} WHERE nid = %d", $node->nid);
5371
}
538
539
/**
540
 * Implementation of hook_view().
541
 *
542
 * @param $block
543
 *   An extra parameter that adapts the hook to display a block-ready
544
 *   rendering of the poll.
545
 */
546176
function poll_view($node, $teaser = FALSE, $page = FALSE, $block = FALSE) {
5479
  global $user;
5489
  $output = '';
549
550
  // Special display for side-block
5519
  if ($block) {
552
    // No 'read more' link
5530
    $node->readmore = FALSE;
554
5550
    $links = module_invoke_all('link', 'node', $node, 1);
5560
    $links[] = array('title' => t('Older polls'), 'href' => 'poll',
'attributes' => array('title' => t('View the list of polls on this
site.')));
5570
    if ($node->allowvotes && $block) {
5580
      $links[] = array('title' => t('Results'), 'href' => 'node/' .
$node->nid . '/results', 'attributes' => array('title' => t('View the
current poll results.')));
5590
    }
560
5610
    $node->links = $links;
5620
  }
563
5649
  if (!empty($node->allowvotes) && ($block || empty($node->show_results)))
{
5651
    $node->content['body'] = array(
5662
      '#markup' => drupal_get_form('poll_view_voting', $node, $block),
567
    );
5681
  }
569
  else {
5707
    $node->content['body'] = array(
5717
      '#markup' => poll_view_results($node, $teaser, $page, $block),
572
    );
573
  }
5748
  return $node;
5750
}
576
577
/**
578
 * Creates a simple teaser that lists all the choices.
579
 *
580
 * This is primarily used for RSS.
581
 */
582176
function poll_teaser($node) {
5837
  $teaser = NULL;
5847
  if (is_array($node->choice)) {
5857
    foreach ($node->choice as $k => $choice) {
5867
      if ($choice['chtext'] != '') {
5877
        $teaser .= '* ' . check_plain($choice['chtext']) . "\n";
5887
      }
5897
    }
5907
  }
5917
  return $teaser;
5920
}
593
594
/**
595
 * Generates the voting form for a poll.
596
 *
597
 * @ingroup forms
598
 * @see poll_vote()
599
 * @see phptemplate_preprocess_poll_vote()
600
 */
601176
function poll_view_voting(&$form_state, $node, $block) {
6021
  if ($node->choice) {
6031
    $list = array();
6041
    foreach ($node->choice as $i => $choice) {
6051
      $list[$i] = check_plain($choice['chtext']);
6061
    }
6071
    $form['choice'] = array(
6081
      '#type' => 'radios',
6091
      '#default_value' => -1,
6101
      '#options' => $list,
611
    );
6121
  }
613
6141
  $form['vote'] = array(
6151
    '#type' => 'submit',
6161
    '#value' => t('Vote'),
6171
    '#submit' => array('poll_vote'),
618
  );
619
620
  // Store the node so we can get to it in submit functions.
6211
  $form['#node'] = $node;
6221
  $form['#block'] = $block;
623
624
  // Set form caching because we could have multiple of these forms on
625
  // the same page, and we want to ensure the right one gets picked.
6261
  $form['#cache'] = TRUE;
627
628
  // Provide a more cleanly named voting form theme.
6291
  $form['#theme'] = 'poll_vote';
6301
  return $form;
6310
}
632
633
/**
634
 * Validation function for processing votes
635
 */
636176
function poll_view_voting_validate($form, &$form_state) {
6371
  if ($form_state['values']['choice'] == -1) {
6380
    form_set_error( 'choice', t('Your vote could not be recorded because
you did not select any of the choices.'));
6390
  }
6401
}
641
642
/**
643
 * Submit handler for processing a vote
644
 */
645176
function poll_vote($form, &$form_state) {
6461
  $node = $form['#node'];
6471
  $choice = $form_state['values']['choice'];
648
6491
  global $user;
6501
  if ($user->uid) {
6511
    db_query('INSERT INTO {poll_votes} (nid, chid, uid) VALUES (%d, %d,
%d)', $node->nid, $choice, $user->uid);
6521
  }
653
  else {
6540
    db_query("INSERT INTO {poll_votes} (nid, chid, hostname) VALUES (%d,
%d, '%s')", $node->nid, $choice, ip_address());
655
  }
656
657
  // Add one to the votes.
6581
  db_query("UPDATE {poll_choices} SET chvotes = chvotes + 1 WHERE chid =
%d", $choice);
659
6601
  cache_clear_all();
6611
  drupal_set_message(t('Your vote was recorded.'));
662
663
  // Return the user to whatever page they voted from.
6641
}
665
666
/**
667
 * Themes the voting form for a poll.
668
 *
669
 * Inputs: $form
670
 */
671176
function template_preprocess_poll_vote(&$variables) {
6721
  $form = $variables['form'];
6731
  $variables['choice'] = drupal_render($form['choice']);
6741
  $variables['title'] = check_plain($form['#node']->title);
6751
  $variables['vote'] = drupal_render($form['vote']);
6761
  $variables['rest'] = drupal_render($form);
6771
  $variables['block'] = $form['#block'];
678
  // If this is a block, allow a different tpl.php to be used.
6791
  if ($variables['block']) {
6800
    $variables['template_files'][] = 'poll-vote-block';
6810
  }
6821
}
683
684
/**
685
 * Generates a graphical representation of the results of a poll.
686
 */
687176
function poll_view_results(&$node, $teaser, $page, $block) {
688
  // Count the votes and find the maximum
6897
  $total_votes = 0;
6907
  $max_votes = 0;
6917
  foreach ($node->choice as $choice) {
6927
    if (isset($choice['chvotes'])) {
6937
      $total_votes += $choice['chvotes'];
6947
      $max_votes = max($max_votes, $choice['chvotes']);
6957
    }
6967
  }
697
6987
  $poll_results = '';
6997
  foreach ($node->choice as $i => $choice) {
7007
    if (!empty($choice['chtext'])) {
7017
      $chvotes = isset($choice['chvotes']) ? $choice['chvotes'] : NULL;
7027
      $poll_results .= theme('poll_bar', $choice['chtext'], $chvotes,
$total_votes, isset($node->vote) && $node->vote == $i, $block);
7037
    }
7047
  }
705
7067
  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);
7070
}
708
709
710
/**
711
 * Theme the admin poll form for choices.
712
 *
713
 * @ingroup themeable
714
 */
715176
function theme_poll_choices($form) {
7167
  drupal_add_tabledrag('poll-choice-table', 'order', 'sibling',
'poll-weight');
717
7187
  $delta = 0;
7197
  $rows = array();
720
  $headers = array(
7217
    '',
7227
    t('Choice'),
7237
    t('Vote count'),
7247
    t('Weight'),
7257
  );
726
7277
  foreach (element_children($form) as $key) {
7287
    $delta++;
729
    // Set special classes for drag and drop updating.
7307
    $form[$key]['weight']['#attributes']['class'] = 'poll-weight';
731
732
    // Build the table row.
733
    $row = array(
734
      'data' => array(
7357
        array('class' => 'choice-flag'),
7367
        drupal_render($form[$key]['chtext']),
7377
        drupal_render($form[$key]['chvotes']),
7387
        drupal_render($form[$key]['weight']),
7397
      ),
7407
      'class' => 'draggable',
7417
    );
742
743
    // Add any additional classes set on the row.
7447
    $row['class'] .= isset($form[$key]['#attributes']['class']) ? ' '.
$form[$key]['#attributes']['class'] : '';
745
7467
    $rows[] = $row;
7477
  }
748
7497
  $output = theme('table', $headers, $rows, array('id' =>
'poll-choice-table'));
7507
  $output .= drupal_render($form);
7517
  return $output;
7520
}
753
754
/**
755
 * Preprocess the poll_results theme hook.
756
 *
757
 * Inputs: $raw_title, $results, $votes, $raw_links, $block, $nid, $vote.
The
758
 * $raw_* inputs to this are naturally unsafe; often safe versions are
759
 * made to simply overwrite the raw version, but in this case it seems
likely
760
 * that the title and the links may be overridden by the theme layer, so
they
761
 * are left in with a different name for that purpose.
762
 *
763
 * @see poll-results.tpl.php
764
 * @see poll-results-block.tpl.php
765
 * @see theme_poll_results()
766
 */
767176
function template_preprocess_poll_results(&$variables) {
7687
  $variables['links'] = theme('links', $variables['raw_links']);
7697
  if (isset($variables['vote']) && $variables['vote'] > -1 &&
user_access('cancel own vote')) {
7701
    $variables['cancel_form'] = drupal_get_form('poll_cancel_form',
$variables['nid']);
7711
  }
7727
  $variables['title'] = check_plain($variables['raw_title']);
773
774
  // If this is a block, allow a different tpl.php to be used.
7757
  if ($variables['block']) {
7760
    $variables['template_files'][] = 'poll-results-block';
7770
  }
7787
}
779
780
/**
781
 * Preprocess the poll_bar theme hook.
782
 *
783
 * Inputs: $title, $votes, $total_votes, $voted, $block
784
 *
785
 * @see poll-bar.tpl.php
786
 * @see poll-bar-block.tpl.php
787
 * @see theme_poll_bar()
788
 */
789176
function template_preprocess_poll_bar(&$variables) {
7907
  if ($variables['block']) {
7910
    $variables['template_files'][] = 'poll-bar-block';
7920
  }
7937
  $variables['title'] = check_plain($variables['title']);
7947
  $variables['percentage'] = round($variables['votes'] * 100 /
max($variables['total_votes'], 1));
7957
}
796
797
/**
798
 * Builds the cancel form for a poll.
799
 *
800
 * @ingroup forms
801
 * @see poll_cancel()
802
 */
803176
function poll_cancel_form(&$form_state, $nid) {
804
  // Store the nid so we can get to it in submit functions.
8051
  $form['#nid'] = $nid;
806
8071
  $form['submit'] = array(
8081
    '#type' => 'submit',
8091
    '#value' => t('Cancel your vote'),
8101
    '#submit' => array('poll_cancel')
8111
  );
812
8131
  $form['#cache'] = TRUE;
814
8151
  return $form;
8160
}
817
818
/**
819
 * Submit callback for poll_cancel_form
820
 */
821176
function poll_cancel($form, &$form_state) {
8220
  $node = node_load($form['#nid']);
8230
  global $user;
824
8250
  if ($user->uid) {
8260
    db_query('DELETE FROM {poll_votes} WHERE nid = %d and uid = %d',
$node->nid, $user->uid);
8270
  }
828
  else {
8290
    db_query("DELETE FROM {poll_votes} WHERE nid = %d and hostname = '%s'",
$node->nid, ip_address());
830
  }
831
832
  // Subtract from the votes.
8330
  db_query("UPDATE {poll_choices} SET chvotes = chvotes - 1 WHERE chid =
%d", $node->vote);
8340
}
835
836
/**
837
 * Implementation of hook_user_delete().
838
 */
839176
function poll_user_delete(&$edit, &$user) {
8401
  db_query('UPDATE {poll_votes} SET uid = 0 WHERE uid = %d', $user->uid);
8411
}
842
843176