web/drupal/modules/poll/poll.module
branchdrupal
changeset 74 0ff3ba646492
equal deleted inserted replaced
73:fcf75e232c5b 74:0ff3ba646492
       
     1 <?php
       
     2 // $Id: poll.module,v 1.263.2.3 2008/12/18 15:46:20 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  */
       
    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  * Implementation of hook_init().
       
    25  */
       
    26 function poll_init() {
       
    27   drupal_add_css(drupal_get_path('module', 'poll') .'/poll.css');
       
    28 }
       
    29 
       
    30 /**
       
    31  * Implementation of hook_theme()
       
    32  */
       
    33 function poll_theme() {
       
    34   return array(
       
    35     'poll_vote' => array(
       
    36       'template' => 'poll-vote',
       
    37       'arguments' => array('form' => NULL),
       
    38     ),
       
    39     'poll_choices' => array(
       
    40       'arguments' => array('form' => NULL),
       
    41     ),
       
    42     'poll_results' => array(
       
    43       'template' => 'poll-results',
       
    44       'arguments' => 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       'arguments' => array('title' => NULL, 'votes' => NULL, 'total_votes' => NULL, 'vote' => NULL, 'block' => NULL),
       
    49     ),
       
    50   );
       
    51 }
       
    52 
       
    53 /**
       
    54  * Implementation of hook_perm().
       
    55  */
       
    56 function poll_perm() {
       
    57   return array('create poll content', 'delete own poll content', 'delete any poll content', 'edit any poll content', 'edit own poll content', 'vote on polls', 'cancel own vote', 'inspect all votes');
       
    58 }
       
    59 
       
    60 /**
       
    61  * Implementation of hook_access().
       
    62  */
       
    63 function poll_access($op, $node, $account) {
       
    64   switch ($op) {
       
    65     case 'create':
       
    66       return user_access('create poll content', $account) ? TRUE : NULL;
       
    67     case 'update':
       
    68       return user_access('edit any poll content', $account) || (user_access('edit own poll content', $account) && ($node->uid == $account->uid)) ? TRUE : NULL;
       
    69     case 'delete':
       
    70       return user_access('delete any poll content', $account) || (user_access('delete own poll content', $account) && ($node->uid == $account->uid)) ? TRUE : NULL;
       
    71   }
       
    72 }
       
    73 
       
    74 /**
       
    75  * Implementation of hook_menu().
       
    76  */
       
    77 function poll_menu() {
       
    78   $items['poll'] = array(
       
    79     'title' => 'Polls',
       
    80     'page callback' => 'poll_page',
       
    81     'access arguments' => array('access content'),
       
    82     'type' => MENU_SUGGESTED_ITEM,
       
    83     'file' => 'poll.pages.inc',
       
    84   );
       
    85 
       
    86   $items['node/%node/votes'] = array(
       
    87     'title' => 'Votes',
       
    88     'page callback' => 'poll_votes',
       
    89     'page arguments' => array(1),
       
    90     'access callback' => '_poll_menu_access',
       
    91     'access arguments' => array(1, 'inspect all votes', FALSE),
       
    92     'weight' => 3,
       
    93     'type' => MENU_LOCAL_TASK,
       
    94     'file' => 'poll.pages.inc',
       
    95   );
       
    96 
       
    97   $items['node/%node/results'] = array(
       
    98     'title' => 'Results',
       
    99     'page callback' => 'poll_results',
       
   100     'page arguments' => array(1),
       
   101     'access callback' => '_poll_menu_access',
       
   102     'access arguments' => array(1, 'access content', TRUE),
       
   103     'weight' => 3,
       
   104     'type' => MENU_LOCAL_TASK,
       
   105     'file' => 'poll.pages.inc',
       
   106   );
       
   107 
       
   108   $items['poll/js'] = array(
       
   109     'title' => 'Javascript Choice Form',
       
   110     'page callback' => 'poll_choice_js',
       
   111     'access arguments' => array('access content'),
       
   112     'type' => MENU_CALLBACK,
       
   113   );
       
   114 
       
   115   return $items;
       
   116 }
       
   117 
       
   118 /**
       
   119  * Callback function to see if a node is acceptable for poll menu items.
       
   120  */
       
   121 function _poll_menu_access($node, $perm, $inspect_allowvotes) {
       
   122   return user_access($perm) && ($node->type == 'poll') && ($node->allowvotes || !$inspect_allowvotes);
       
   123 }
       
   124 
       
   125 /**
       
   126  * Implementation of hook_block().
       
   127  *
       
   128  * Generates a block containing the latest poll.
       
   129  */
       
   130 function poll_block($op = 'list', $delta = 0) {
       
   131   if (user_access('access content')) {
       
   132     if ($op == 'list') {
       
   133       $blocks[0]['info'] = t('Most recent poll');
       
   134       return $blocks;
       
   135     }
       
   136     else if ($op == 'view') {
       
   137       // Retrieve the latest poll.
       
   138       $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");
       
   139       $timestamp = db_result(db_query($sql));
       
   140       if ($timestamp) {
       
   141         $poll = node_load(array('type' => 'poll', 'created' => $timestamp, 'status' => 1));
       
   142 
       
   143         if ($poll->nid) {
       
   144           $poll = poll_view($poll, TRUE, FALSE, TRUE);
       
   145         }
       
   146       }
       
   147       $block['subject'] = t('Poll');
       
   148       $block['content'] = drupal_render($poll->content);
       
   149       return $block;
       
   150     }
       
   151   }
       
   152 }
       
   153 
       
   154 /**
       
   155  * Implementation of hook_cron().
       
   156  *
       
   157  * Closes polls that have exceeded their allowed runtime.
       
   158  */
       
   159 function poll_cron() {
       
   160   $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');
       
   161   while ($poll = db_fetch_object($result)) {
       
   162     db_query("UPDATE {poll} SET active = 0 WHERE nid = %d", $poll->nid);
       
   163   }
       
   164 }
       
   165 
       
   166 /**
       
   167  * Implementation of hook_node_info().
       
   168  */
       
   169 function poll_node_info() {
       
   170   return array(
       
   171     'poll' => array(
       
   172       'name' => t('Poll'),
       
   173       'module' => 'poll',
       
   174       '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.'),
       
   175       'title_label' => t('Question'),
       
   176       'has_body' => FALSE,
       
   177     )
       
   178   );
       
   179 }
       
   180 
       
   181 /**
       
   182  * Implementation of hook_form().
       
   183  */
       
   184 function poll_form(&$node, $form_state) {
       
   185   global $user;
       
   186 
       
   187   $admin = user_access('administer nodes') || user_access('edit any poll content') || (user_access('edit own poll content') && $user->uid == $node->uid);
       
   188 
       
   189   $type = node_get_types('type', $node);
       
   190 
       
   191   $form = array(
       
   192     '#cache' => TRUE,
       
   193   );
       
   194 
       
   195   $form['title'] = array(
       
   196     '#type' => 'textfield',
       
   197     '#title' => check_plain($type->title_label),
       
   198     '#required' => TRUE,
       
   199     '#default_value' => $node->title,
       
   200     '#weight' => -5,
       
   201   );
       
   202 
       
   203   if (isset($form_state['choice_count'])) {
       
   204     $choice_count = $form_state['choice_count'];
       
   205   }
       
   206   else {
       
   207     $choice_count = max(2, empty($node->choice) ? 2 : count($node->choice));
       
   208   }
       
   209 
       
   210   // Add a wrapper for the choices and more button.
       
   211   $form['choice_wrapper'] = array(
       
   212     '#tree' => FALSE,
       
   213     '#weight' => -4,
       
   214     '#prefix' => '<div class="clear-block" id="poll-choice-wrapper">',
       
   215     '#suffix' => '</div>',
       
   216   );
       
   217 
       
   218   // Container for just the poll choices.
       
   219   $form['choice_wrapper']['choice'] = array(
       
   220     '#prefix' => '<div id="poll-choices">',
       
   221     '#suffix' => '</div>',
       
   222     '#theme' => 'poll_choices',
       
   223   );
       
   224 
       
   225   // Add the current choices to the form.
       
   226   for ($delta = 0; $delta < $choice_count; $delta++) {
       
   227     $text = isset($node->choice[$delta]['chtext']) ? $node->choice[$delta]['chtext'] : '';
       
   228     $votes = isset($node->choice[$delta]['chvotes']) ? $node->choice[$delta]['chvotes'] : 0;
       
   229 
       
   230     $form['choice_wrapper']['choice'][$delta] = _poll_choice_form($delta, $text, $votes);
       
   231   }
       
   232 
       
   233   // We name our button 'poll_more' to avoid conflicts with other modules using
       
   234   // AHAH-enabled buttons with the id 'more'.
       
   235   $form['choice_wrapper']['poll_more'] = array(
       
   236     '#type' => 'submit',
       
   237     '#value' => t('More choices'),
       
   238     '#description' => t("If the amount of boxes above isn't enough, click here to add more choices."),
       
   239     '#weight' => 1,
       
   240     '#submit' => array('poll_more_choices_submit'), // If no javascript action.
       
   241     '#ahah' => array(
       
   242       'path' => 'poll/js',
       
   243       'wrapper' => 'poll-choices',
       
   244       'method' => 'replace',
       
   245       'effect' => 'fade',
       
   246     ),
       
   247   );
       
   248 
       
   249   // Poll attributes
       
   250   $_duration = array(0 => t('Unlimited')) + drupal_map_assoc(array(86400, 172800, 345600, 604800, 1209600, 2419200, 4838400, 9676800, 31536000), "format_interval");
       
   251   $_active = array(0 => t('Closed'), 1 => t('Active'));
       
   252 
       
   253   if ($admin) {
       
   254     $form['settings'] = array(
       
   255       '#type' => 'fieldset',
       
   256       '#collapsible' => TRUE,
       
   257       '#title' => t('Poll settings'),
       
   258       '#weight' => -3,
       
   259     );
       
   260 
       
   261     $form['settings']['active'] = array(
       
   262       '#type' => 'radios',
       
   263       '#title' => t('Poll status'),
       
   264       '#default_value' => isset($node->active) ? $node->active : 1,
       
   265       '#options' => $_active,
       
   266       '#description' => t('When a poll is closed, visitors can no longer vote for it.')
       
   267     );
       
   268   }
       
   269   $form['settings']['runtime'] = array(
       
   270     '#type' => 'select',
       
   271     '#title' => t('Poll duration'),
       
   272     '#default_value' => isset($node->runtime) ? $node->runtime : 0,
       
   273     '#options' => $_duration,
       
   274     '#description' => t('After this period, the poll will be closed automatically.'),
       
   275   );
       
   276 
       
   277   return $form;
       
   278 }
       
   279 
       
   280 /**
       
   281  * Submit handler to add more choices to a poll form. This handler is used when
       
   282  * javascript is not available. It makes changes to the form state and the
       
   283  * entire form is rebuilt during the page reload.
       
   284  */
       
   285 function poll_more_choices_submit($form, &$form_state) {
       
   286   // Set the form to rebuild and run submit handlers.
       
   287   node_form_submit_build_node($form, $form_state);
       
   288 
       
   289   // Make the changes we want to the form state.
       
   290   if ($form_state['values']['poll_more']) {
       
   291     $n = $_GET['q'] == 'poll/js' ? 1 : 5;
       
   292     $form_state['choice_count'] = count($form_state['values']['choice']) + $n;
       
   293   }
       
   294 }
       
   295 
       
   296 function _poll_choice_form($delta, $value = '', $votes = 0) {
       
   297   $admin = user_access('administer nodes');
       
   298 
       
   299   $form = array(
       
   300     '#tree' => TRUE,
       
   301   );
       
   302 
       
   303   // We'll manually set the #parents property of these fields so that
       
   304   // their values appear in the $form_state['values']['choice'] array.
       
   305   $form['chtext'] = array(
       
   306     '#type' => 'textfield',
       
   307     '#title' => t('Choice @n', array('@n' => ($delta + 1))),
       
   308     '#default_value' => $value,
       
   309     '#parents' => array('choice', $delta, 'chtext'),
       
   310   );
       
   311 
       
   312   if ($admin) {
       
   313     $form['chvotes'] = array(
       
   314       '#type' => 'textfield',
       
   315       '#title' => t('Votes for choice @n', array('@n' => ($delta + 1))),
       
   316       '#default_value' => $votes,
       
   317       '#size' => 5,
       
   318       '#maxlength' => 7,
       
   319       '#parents' => array('choice', $delta, 'chvotes'),
       
   320     );
       
   321   }
       
   322 
       
   323   return $form;
       
   324 }
       
   325 
       
   326 /**
       
   327  * Menu callback for AHAH additions.
       
   328  */
       
   329 function poll_choice_js() {
       
   330   include_once 'modules/node/node.pages.inc';
       
   331   $form_state = array('storage' => NULL, 'submitted' => FALSE);
       
   332   $form_build_id = $_POST['form_build_id'];
       
   333   // Get the form from the cache.
       
   334   $form = form_get_cache($form_build_id, $form_state);
       
   335   $args = $form['#parameters'];
       
   336   $form_id = array_shift($args);
       
   337   // We will run some of the submit handlers so we need to disable redirecting.
       
   338   $form['#redirect'] = FALSE;
       
   339   // We need to process the form, prepare for that by setting a few internals
       
   340   // variables.
       
   341   $form['#post'] = $_POST;
       
   342   $form['#programmed'] = FALSE;
       
   343   $form_state['post'] = $_POST;
       
   344   // Build, validate and if possible, submit the form.
       
   345   drupal_process_form($form_id, $form, $form_state);
       
   346   // This call recreates the form relying solely on the form_state that the
       
   347   // drupal_process_form set up.
       
   348   $form = drupal_rebuild_form($form_id, $form_state, $args, $form_build_id);
       
   349   // Render the new output.
       
   350   $choice_form = $form['choice_wrapper']['choice'];
       
   351   unset($choice_form['#prefix'], $choice_form['#suffix']); // Prevent duplicate wrappers.
       
   352   $output = theme('status_messages') . drupal_render($choice_form);
       
   353 
       
   354   drupal_json(array('status' => TRUE, 'data' => $output));
       
   355 }
       
   356 
       
   357 /**
       
   358  * Implementation of hook_submit().
       
   359  */
       
   360 function poll_node_form_submit(&$form, &$form_state) {
       
   361   // Renumber fields
       
   362   $form_state['values']['choice'] = array_values($form_state['values']['choice']);
       
   363   $form_state['values']['teaser'] = poll_teaser((object)$form_state['values']);
       
   364 }
       
   365 
       
   366 /**
       
   367  * Implementation of hook_validate().
       
   368  */
       
   369 function poll_validate($node) {
       
   370   if (isset($node->title)) {
       
   371     // Check for at least two options and validate amount of votes:
       
   372     $realchoices = 0;
       
   373     // Renumber fields
       
   374     $node->choice = array_values($node->choice);
       
   375     foreach ($node->choice as $i => $choice) {
       
   376       if ($choice['chtext'] != '') {
       
   377         $realchoices++;
       
   378       }
       
   379       if (isset($choice['chvotes']) && $choice['chvotes'] < 0) {
       
   380         form_set_error("choice][$i][chvotes", t('Negative values are not allowed.'));
       
   381       }
       
   382     }
       
   383 
       
   384     if ($realchoices < 2) {
       
   385       form_set_error("choice][$realchoices][chtext", t('You must fill in at least two choices.'));
       
   386     }
       
   387   }
       
   388 }
       
   389 
       
   390 /**
       
   391  * Implementation of hook_load().
       
   392  */
       
   393 function poll_load($node) {
       
   394   global $user;
       
   395 
       
   396   $poll = db_fetch_object(db_query("SELECT runtime, active FROM {poll} WHERE nid = %d", $node->nid));
       
   397 
       
   398   // Load the appropriate choices into the $poll object.
       
   399   $result = db_query("SELECT chtext, chvotes, chorder FROM {poll_choices} WHERE nid = %d ORDER BY chorder", $node->nid);
       
   400   while ($choice = db_fetch_array($result)) {
       
   401     $poll->choice[$choice['chorder']] = $choice;
       
   402   }
       
   403 
       
   404   // Determine whether or not this user is allowed to vote.
       
   405   $poll->allowvotes = FALSE;
       
   406   if (user_access('vote on polls') && $poll->active) {
       
   407     if ($user->uid) {
       
   408       $result = db_fetch_object(db_query('SELECT chorder FROM {poll_votes} WHERE nid = %d AND uid = %d', $node->nid, $user->uid));
       
   409     }
       
   410     else {
       
   411       $result = db_fetch_object(db_query("SELECT chorder FROM {poll_votes} WHERE nid = %d AND hostname = '%s'", $node->nid, ip_address()));
       
   412     }
       
   413     if (isset($result->chorder)) {
       
   414       $poll->vote = $result->chorder;
       
   415     }
       
   416     else {
       
   417       $poll->vote = -1;
       
   418       $poll->allowvotes = TRUE;
       
   419     }
       
   420   }
       
   421   return $poll;
       
   422 }
       
   423 
       
   424 /**
       
   425  * Implementation of hook_insert().
       
   426  */
       
   427 function poll_insert($node) {
       
   428   if (!user_access('administer nodes')) {
       
   429     // Make sure all votes are 0 initially
       
   430     foreach ($node->choice as $i => $choice) {
       
   431       $node->choice[$i]['chvotes'] = 0;
       
   432     }
       
   433     $node->active = 1;
       
   434   }
       
   435 
       
   436   db_query("INSERT INTO {poll} (nid, runtime, active) VALUES (%d, %d, %d)", $node->nid, $node->runtime, $node->active);
       
   437 
       
   438   $i = 0;
       
   439   foreach ($node->choice as $choice) {
       
   440     if ($choice['chtext'] != '') {
       
   441       db_query("INSERT INTO {poll_choices} (nid, chtext, chvotes, chorder) VALUES (%d, '%s', %d, %d)", $node->nid, $choice['chtext'], $choice['chvotes'], $i++);
       
   442     }
       
   443   }
       
   444 }
       
   445 
       
   446 /**
       
   447  * Implementation of hook_update().
       
   448  */
       
   449 function poll_update($node) {
       
   450   // Update poll settings.
       
   451   db_query('UPDATE {poll} SET runtime = %d, active = %d WHERE nid = %d', $node->runtime, $node->active, $node->nid);
       
   452 
       
   453   // Clean poll choices.
       
   454   db_query('DELETE FROM {poll_choices} WHERE nid = %d', $node->nid);
       
   455 
       
   456   // Poll choices come in the same order with the same numbers as they are in
       
   457   // the database, but some might have an empty title, which signifies that
       
   458   // they should be removed. We remove all votes to the removed options, so
       
   459   // people who voted on them can vote again.
       
   460   $new_chorder = 0;
       
   461   foreach ($node->choice as $old_chorder => $choice) {
       
   462     $chvotes = isset($choice['chvotes']) ? (int)$choice['chvotes'] : 0;
       
   463     $chtext = $choice['chtext'];
       
   464 
       
   465     if (!empty($chtext)) {
       
   466       db_query("INSERT INTO {poll_choices} (nid, chtext, chvotes, chorder) VALUES (%d, '%s', %d, %d)", $node->nid, $chtext, $chvotes, $new_chorder);
       
   467       if ($new_chorder != $old_chorder) {
       
   468         // We can only remove items in the middle, not add, so
       
   469         // new_chorder is always <= old_chorder, making this safe.
       
   470         db_query("UPDATE {poll_votes} SET chorder = %d WHERE nid = %d AND chorder = %d", $new_chorder, $node->nid, $old_chorder);
       
   471       }
       
   472       $new_chorder++;
       
   473     }
       
   474     else {
       
   475       db_query("DELETE FROM {poll_votes} WHERE nid = %d AND chorder = %d", $node->nid, $old_chorder);
       
   476     }
       
   477   }
       
   478 }
       
   479 
       
   480 /**
       
   481  * Implementation of hook_delete().
       
   482  */
       
   483 function poll_delete($node) {
       
   484   db_query("DELETE FROM {poll} WHERE nid = %d", $node->nid);
       
   485   db_query("DELETE FROM {poll_choices} WHERE nid = %d", $node->nid);
       
   486   db_query("DELETE FROM {poll_votes} WHERE nid = %d", $node->nid);
       
   487 }
       
   488 
       
   489 /**
       
   490  * Implementation of hook_view().
       
   491  *
       
   492  * @param $block
       
   493  *   An extra parameter that adapts the hook to display a block-ready
       
   494  *   rendering of the poll.
       
   495  */
       
   496 function poll_view($node, $teaser = FALSE, $page = FALSE, $block = FALSE) {
       
   497   global $user;
       
   498   $output = '';
       
   499 
       
   500   // Special display for side-block
       
   501   if ($block) {
       
   502     // No 'read more' link
       
   503     $node->readmore = FALSE;
       
   504 
       
   505     $links = module_invoke_all('link', 'node', $node, 1);
       
   506     $links[] = array('title' => t('Older polls'), 'href' => 'poll', 'attributes' => array('title' => t('View the list of polls on this site.')));
       
   507     if ($node->allowvotes && $block) {
       
   508       $links[] = array('title' => t('Results'), 'href' => 'node/'. $node->nid .'/results', 'attributes' => array('title' => t('View the current poll results.')));
       
   509     }
       
   510 
       
   511     $node->links = $links;
       
   512   }
       
   513 
       
   514   if (!empty($node->allowvotes) && ($block || empty($node->show_results))) {
       
   515     $node->content['body'] = array(
       
   516       '#value' => drupal_get_form('poll_view_voting', $node, $block),
       
   517     );
       
   518   }
       
   519   else {
       
   520     $node->content['body'] = array(
       
   521       '#value' => poll_view_results($node, $teaser, $page, $block),
       
   522     );
       
   523   }
       
   524   return $node;
       
   525 }
       
   526 
       
   527 /**
       
   528  * Creates a simple teaser that lists all the choices.
       
   529  *
       
   530  * This is primarily used for RSS.
       
   531  */
       
   532 function poll_teaser($node) {
       
   533   $teaser = NULL;
       
   534   if (is_array($node->choice)) {
       
   535     foreach ($node->choice as $k => $choice) {
       
   536       if ($choice['chtext'] != '') {
       
   537         $teaser .= '* '. check_plain($choice['chtext']) ."\n";
       
   538       }
       
   539     }
       
   540   }
       
   541   return $teaser;
       
   542 }
       
   543 
       
   544 /**
       
   545  * Generates the voting form for a poll.
       
   546  *
       
   547  * @ingroup forms
       
   548  * @see poll_vote()
       
   549  * @see phptemplate_preprocess_poll_vote()
       
   550  */
       
   551 function poll_view_voting(&$form_state, $node, $block) {
       
   552   if ($node->choice) {
       
   553     $list = array();
       
   554     foreach ($node->choice as $i => $choice) {
       
   555       $list[$i] = check_plain($choice['chtext']);
       
   556     }
       
   557     $form['choice'] = array(
       
   558       '#type' => 'radios',
       
   559       '#default_value' => -1,
       
   560       '#options' => $list,
       
   561     );
       
   562   }
       
   563 
       
   564   $form['vote'] = array(
       
   565     '#type' => 'submit',
       
   566     '#value' => t('Vote'),
       
   567     '#submit' => array('poll_vote'),
       
   568   );
       
   569 
       
   570   // Store the node so we can get to it in submit functions.
       
   571   $form['#node'] = $node;
       
   572   $form['#block'] = $block;
       
   573 
       
   574   // Set form caching because we could have multiple of these forms on
       
   575   // the same page, and we want to ensure the right one gets picked.
       
   576   $form['#cache'] = TRUE;
       
   577 
       
   578   // Provide a more cleanly named voting form theme.
       
   579   $form['#theme'] = 'poll_vote';
       
   580   return $form;
       
   581 }
       
   582 
       
   583 /**
       
   584  * Validation function for processing votes
       
   585  */
       
   586 function poll_view_voting_validate($form, &$form_state) {
       
   587   if ($form_state['values']['choice'] == -1) {
       
   588     form_set_error( 'choice', t('Your vote could not be recorded because you did not select any of the choices.'));
       
   589   }
       
   590 }
       
   591 
       
   592 /**
       
   593  * Submit handler for processing a vote
       
   594  */
       
   595 function poll_vote($form, &$form_state) {
       
   596   $node = $form['#node'];
       
   597   $choice = $form_state['values']['choice'];
       
   598 
       
   599   global $user;
       
   600   if ($user->uid) {
       
   601     db_query('INSERT INTO {poll_votes} (nid, chorder, uid) VALUES (%d, %d, %d)', $node->nid, $choice, $user->uid);
       
   602   }
       
   603   else {
       
   604     db_query("INSERT INTO {poll_votes} (nid, chorder, hostname) VALUES (%d, %d, '%s')", $node->nid, $choice, ip_address());
       
   605   }
       
   606 
       
   607   // Add one to the votes.
       
   608   db_query("UPDATE {poll_choices} SET chvotes = chvotes + 1 WHERE nid = %d AND chorder = %d", $node->nid, $choice);
       
   609 
       
   610   cache_clear_all();
       
   611   drupal_set_message(t('Your vote was recorded.'));
       
   612 
       
   613   // Return the user to whatever page they voted from.
       
   614 }
       
   615 
       
   616 /**
       
   617  * Themes the voting form for a poll.
       
   618  *
       
   619  * Inputs: $form
       
   620  */
       
   621 function template_preprocess_poll_vote(&$variables) {
       
   622   $form = $variables['form'];
       
   623   $variables['choice'] = drupal_render($form['choice']);
       
   624   $variables['title'] = check_plain($form['#node']->title);
       
   625   $variables['vote'] = drupal_render($form['vote']);
       
   626   $variables['rest'] = drupal_render($form);
       
   627   $variables['block'] = $form['#block'];
       
   628   // If this is a block, allow a different tpl.php to be used.
       
   629   if ($variables['block']) {
       
   630     $variables['template_files'][] = 'poll-vote-block';
       
   631   }
       
   632 }
       
   633 
       
   634 /**
       
   635  * Generates a graphical representation of the results of a poll.
       
   636  */
       
   637 function poll_view_results(&$node, $teaser, $page, $block) {
       
   638   // Count the votes and find the maximum
       
   639   $total_votes = 0;
       
   640   $max_votes = 0;
       
   641   foreach ($node->choice as $choice) {
       
   642     if (isset($choice['chvotes'])) {
       
   643       $total_votes += $choice['chvotes'];
       
   644       $max_votes = max($max_votes, $choice['chvotes']);
       
   645     }
       
   646   }
       
   647 
       
   648   $poll_results = '';
       
   649   foreach ($node->choice as $i => $choice) {
       
   650     if (!empty($choice['chtext'])) {
       
   651       $chvotes = isset($choice['chvotes']) ? $choice['chvotes'] : NULL;
       
   652       $poll_results .= theme('poll_bar', $choice['chtext'], $chvotes, $total_votes, isset($node->vote) && $node->vote == $i, $block);
       
   653     }
       
   654   }
       
   655 
       
   656   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);
       
   657 }
       
   658 
       
   659 
       
   660 /**
       
   661  * Theme the admin poll form for choices.
       
   662  *
       
   663  * @ingroup themeable
       
   664  */
       
   665 function theme_poll_choices($form) {
       
   666   // Change the button title to reflect the behavior when using JavaScript.
       
   667   drupal_add_js('if (Drupal.jsEnabled) { $(document).ready(function() { $("#edit-poll-more").val("'. t('Add another choice') .'"); }); }', 'inline');
       
   668 
       
   669   $rows = array();
       
   670   $headers = array(
       
   671     t('Choice'),
       
   672     t('Vote count'),
       
   673   );
       
   674 
       
   675   foreach (element_children($form) as $key) {
       
   676     // No need to print the field title every time.
       
   677     unset($form[$key]['chtext']['#title'], $form[$key]['chvotes']['#title']);
       
   678 
       
   679     // Build the table row.
       
   680     $row = array(
       
   681       'data' => array(
       
   682         array('data' => drupal_render($form[$key]['chtext']), 'class' => 'poll-chtext'),
       
   683         array('data' => drupal_render($form[$key]['chvotes']), 'class' => 'poll-chvotes'),
       
   684       ),
       
   685     );
       
   686 
       
   687     // Add additional attributes to the row, such as a class for this row.
       
   688     if (isset($form[$key]['#attributes'])) {
       
   689       $row = array_merge($row, $form[$key]['#attributes']);
       
   690     }
       
   691     $rows[] = $row;
       
   692   }
       
   693 
       
   694   $output = theme('table', $headers, $rows);
       
   695   $output .= drupal_render($form);
       
   696   return $output;
       
   697 }
       
   698 
       
   699 /**
       
   700  * Preprocess the poll_results theme hook.
       
   701  *
       
   702  * Inputs: $raw_title, $results, $votes, $raw_links, $block, $nid, $vote. The
       
   703  * $raw_* inputs to this are naturally unsafe; often safe versions are
       
   704  * made to simply overwrite the raw version, but in this case it seems likely
       
   705  * that the title and the links may be overridden by the theme layer, so they
       
   706  * are left in with a different name for that purpose.
       
   707  *
       
   708  * @see poll-results.tpl.php
       
   709  * @see poll-results-block.tpl.php
       
   710  * @see theme_poll_results()
       
   711  */
       
   712 function template_preprocess_poll_results(&$variables) {
       
   713   $variables['links'] = theme('links', $variables['raw_links']);
       
   714   if (isset($variables['vote']) && $variables['vote'] > -1 && user_access('cancel own vote')) {
       
   715     $variables['cancel_form'] = drupal_get_form('poll_cancel_form', $variables['nid']);
       
   716   }
       
   717   $variables['title'] = check_plain($variables['raw_title']);
       
   718 
       
   719   // If this is a block, allow a different tpl.php to be used.
       
   720   if ($variables['block']) {
       
   721     $variables['template_files'][] = 'poll-results-block';
       
   722   }
       
   723 }
       
   724 
       
   725 /**
       
   726  * Preprocess the poll_bar theme hook.
       
   727  *
       
   728  * Inputs: $title, $votes, $total_votes, $voted, $block
       
   729  *
       
   730  * @see poll-bar.tpl.php
       
   731  * @see poll-bar-block.tpl.php
       
   732  * @see theme_poll_bar()
       
   733  */
       
   734 function template_preprocess_poll_bar(&$variables) {
       
   735   if ($variables['block']) {
       
   736     $variables['template_files'][] = 'poll-bar-block';
       
   737   }
       
   738   $variables['title'] = check_plain($variables['title']);
       
   739   $variables['percentage'] = round($variables['votes'] * 100 / max($variables['total_votes'], 1));
       
   740 }
       
   741 
       
   742 /**
       
   743  * Builds the cancel form for a poll.
       
   744  *
       
   745  * @ingroup forms
       
   746  * @see poll_cancel()
       
   747  */
       
   748 function poll_cancel_form(&$form_state, $nid) {
       
   749   // Store the nid so we can get to it in submit functions.
       
   750   $form['#nid'] = $nid;
       
   751 
       
   752   $form['submit'] = array(
       
   753     '#type' => 'submit',
       
   754     '#value' => t('Cancel your vote'),
       
   755     '#submit' => array('poll_cancel')
       
   756   );
       
   757 
       
   758   $form['#cache'] = TRUE;
       
   759 
       
   760   return $form;
       
   761 }
       
   762 
       
   763 /**
       
   764  * Submit callback for poll_cancel_form
       
   765  */
       
   766 function poll_cancel($form, &$form_state) {
       
   767   $node = node_load($form['#nid']);
       
   768   global $user;
       
   769 
       
   770   if ($user->uid) {
       
   771     db_query('DELETE FROM {poll_votes} WHERE nid = %d and uid = %d', $node->nid, $user->uid);
       
   772   }
       
   773   else {
       
   774     db_query("DELETE FROM {poll_votes} WHERE nid = %d and hostname = '%s'", $node->nid, ip_address());
       
   775   }
       
   776 
       
   777   // Subtract from the votes.
       
   778   db_query("UPDATE {poll_choices} SET chvotes = chvotes - 1 WHERE nid = %d AND chorder = %d", $node->nid, $node->vote);
       
   779 }
       
   780 
       
   781 /**
       
   782  * Implementation of hook_user().
       
   783  */
       
   784 function poll_user($op, &$edit, &$user) {
       
   785   if ($op == 'delete') {
       
   786     db_query('UPDATE {poll_votes} SET uid = 0 WHERE uid = %d', $user->uid);
       
   787   }
       
   788 }