Index: decisions.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/decisions/decisions.module,v
retrieving revision 1.231
diff -u -p -r1.231 decisions.module
--- decisions.module	22 Feb 2010 17:32:35 -0000	1.231
+++ decisions.module	22 Feb 2010 19:21:46 -0000
@@ -12,7 +12,7 @@
  * all the that was already written. (...and debugged!)
  */
 
-// $Id: decisions.module,v 1.231 2010/02/22 17:32:35 ezrag Exp $
+// $Id: decisions.module,v 1.228 2010/02/08 23:45:33 anarcat Exp $
 
 define('DECISIONS_DEFAULT_ELECTORAL_LIST', 0);
 // always, aftervote, or afterclose
@@ -215,6 +215,20 @@ function decisions_admin() {
     '#options' => $view_results,
   );
 
+  $form['main']['decisions_1click'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Enable one-click voting where available'),
+    '#description' => t('Some modules offer one-click voting with buttons or links instead of radios.'),
+    '#default_value' => variable_get('decisions_1click', 0),
+  );
+
+  $form['main']['decisions_ahah'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Enable asynchronous (AJAX) voting where available'),
+    '#description' => t('Some modules allow votes to be submitted with an asynchronous interface.'),
+    '#default_value' => variable_get('decisions_ahah', 0),
+  );
+  
   return system_settings_form($form);
 }
 
@@ -1264,7 +1278,7 @@ function decisions_add_choices_js() {
  */
 function decisions_view(&$node, $teaser = FALSE, $page = FALSE) {
   $mode = _decisions_get_mode($node);
-
+  
   // Since we have a body (the decision's description), we need to
   // include that in the $node->content array, too.
   $node = node_prepare($node, $teaser);
@@ -1289,7 +1303,6 @@ function decisions_view(&$node, $teaser 
   }
   $node->content['decisions']['status']['#value'] = $status_messages;
   $node->content['decisions']['#weight'] = 1;
-
   if (arg(2) != 'results' && _decisions_can_vote($node)) {
     // User hasn't voted and we're not on the results tab
     $mode = _decisions_get_mode($node);
Index: modes/selection.js
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/decisions/modes/selection.js,v
retrieving revision 1.1
diff -u -p -r1.1 selection.js
--- modes/selection.js	22 Feb 2010 17:25:29 -0000	1.1
+++ modes/selection.js	22 Feb 2010 19:21:46 -0000
@@ -1,8 +1,9 @@
 $(document).ready(function(){  
   $("a.decisions_selection_vote").click(function() {
     var link = this;
+    var path = $(this).attr('href');
     var votingDiv = $(link).parents("div.decisions_selection_voting");
-    var path = $(link).attr('href');
+    var resultsDiv =  $(link).parents(".decisions_selection_voting").siblings(".decisions_selection_results");
     $.ajax({
       type: "GET",
       global: false,
@@ -14,14 +15,14 @@ $(document).ready(function(){  
         });
       },
       success: function(html) {
-        $(link).parents(".decisions_selection_voting").siblings(".decisions_selection_results").html(html);
+        resultsDiv.html(html);
         //If the page is a WSOD with an HTTP 200 response, that's not successful. This check could be expanded if necessary.
         if (html == '') {
           decisionsErrorText(votingDiv);
         }
         else {
           $(votingDiv).queue(function(){
-            $(this).siblings(".decisions_selection_results").fadeIn('slow');
+            resultsDiv.fadeIn('slow');
             $(votingDiv).dequeue();
           });
         }
@@ -34,7 +35,7 @@ $(document).ready(function(){  
   });
   function decisionsErrorText(votingDiv) {
     $(votingDiv).queue(function(){
-      $(votingDiv).children(".decisions_selection_error").html('<span class="error">' + Drupal.t('errortext') + '</span>');
+      $(votingDiv).children(".decisions_selection_error").html('<span class="error">' + Drupal.t('An error occured. Please try voting again.') + '</span>');
       votingDiv.fadeIn('slow');
       $(votingDiv).dequeue();
     });
Index: modes/selection.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/decisions/modes/selection.module,v
retrieving revision 1.21
diff -u -p -r1.21 selection.module
--- modes/selection.module	22 Feb 2010 17:32:36 -0000	1.21
+++ modes/selection.module	22 Feb 2010 19:21:46 -0000
@@ -1,13 +1,81 @@
 <?php
-
+// $Id: selection.module,v 1.19 2010/02/16 15:48:38 ezrag Exp $
 /**
  * @file
  *
  * This file implements a selection voting mode
  */
 
-// $Id: selection.module,v 1.21 2010/02/22 17:32:36 ezrag Exp $
+/*
+ * Implementation of hook_menu().
+ */
+function selection_menu() {
+  $items['decisions_selection/vote'] = array(
+    'title' => 'Vote',
+    'page callback' => 'selection_vote',
+    'access arguments' => array('vote on decisions'),
+    'type' => MENU_CALLBACK,
+  );
+  return $items;
+}
 
+function selection_vote($nid = 0, $cid = -1) {
+  global $user;
+  if (selection_check_token($_REQUEST['token'], $nid, $cid)) {
+    $valid_cid = db_result(db_query("SELECT label FROM {decisions_choices} WHERE nid = %d AND vote_offset = %d", $nid, $cid));
+    if (empty($valid_cid)) {
+      if (empty($_REQUEST['ajax'])) {
+        drupal_goto(drupal_get_destination());
+      }
+      else {
+        print '';
+        exit();
+      }
+    }
+    $voted = selection_get_votes($nid, $user->uid);
+    if (empty($voted)) {
+      $vote = array(
+        'value' => 1,
+        'tag' => $cid,
+        'value_type' => 'option',
+        'content_type' => 'decisions',
+        'content_id' => $nid,
+      );
+      $votes[] = $vote;
+      votingapi_add_votes($votes);
+    }
+    else {
+      if (empty($_REQUEST['ajax'])) {
+        drupal_set_message(t("You have already voted."));
+        drupal_goto(drupal_get_destination());
+      }
+      else {
+        print '';
+        exit();
+      }
+    }
+    //Render the results
+    if (empty($_REQUEST['ajax'])) {
+      //Vote confirmation is displayed in the results so that the degradable behavior matches the AHAH behavior.
+      drupal_goto(drupal_get_destination());
+    }
+    else {
+      //Sadly, we must reload the node.
+      $node = node_load($nid);
+      print selection_decisions_view_results($node, FALSE, FALSE, $votes);
+      exit();
+    }
+  }
+  //Invalid token.
+  if (empty($_REQUEST['ajax'])) {
+    drupal_set_message(t("Invalid token. Please try voting again."));
+    drupal_goto(drupal_get_destination());
+  }
+  else {
+    print '';
+    exit();
+  }
+}
 function selection_help($section) {
   $help = "";
   switch ($section) {
@@ -34,15 +102,31 @@ function selection_decisions_algorithms(
   return array('plurality' => t('The plurality algorithm determines the winner based solely on the number of votes received: the one with the larger number of votes wins. See the <a href="http://en.wikipedia.org/wiki/Plurality_voting_system">Plurality voting system on Wikipedia</a> for more information'));
 }
 
+function selection_get_token($nid = 0, $cid = 0) {
+  global $user;
+  $session_id = $user->uid ? session_id() : '';
+  $private_key = drupal_get_private_key();
+  return md5($session_id . "selection-vote-$nid-$cid" . $private_key);
+}
+
+function selection_check_token($token, $nid = 0, $cid = 0) {
+  return selection_get_token($nid, $cid) == $token;
+}
+
 /**
  * Implementation of decisions_hook_voting_form.
  * Presents a list of choices for the user to vote on.
  */
 function decisions_selection_voting_form($form_state, &$node, $teaser, $page) {
+  $one_click = variable_get('decisions_1click', 0);
+  global $user;
   $form['node'] = array(
     '#type' => 'value',
     '#value' => $node,
   );
+  $form['#prefix'] = '<div class="decisions_selection_voting"><div class="decisions_selection_error"></div>';
+  //The decisions_selection_results div exists as a workaround for 1click voting when jQuery 1.3+'s live() is unavailable.
+  $form['#suffix'] = '</div><div class="decisions_selection_results" style="display: none;"></div>';
 
   if ($node->choice) {
     $list = array();
@@ -51,14 +135,45 @@ function decisions_selection_voting_form
     if ($node->randomize) {
       $node->choice = _decisions_randomize_options($node->choice, $node->choices);
     }
+    //Single choice, "Plurality" voting.
     if ($node->maxchoices == 1) {
-      // plurality voting
-      foreach ($node->choice as $i => $choice) {
-        if ($choice['label']) {
-          $list[$i] = check_plain($choice['label']);
+      //Are we using the 1-click form?
+      if ($one_click == 1) {
+        drupal_add_css(drupal_get_path('module', 'selection') . '/selection.css', 'module');
+        $links = '';
+        
+        foreach ($node->choice as $i => $choice) {
+          $links[$i]['attributes']['class'] = 'decisions_selection_vote';
+          $links[$i]['query'] = drupal_get_destination() . '&token='. selection_get_token($node->nid, $i);
+          $links[$i]['title'] = check_plain($choice['label']);
+          $links[$i]['href'] = "decisions_selection/vote/$node->nid/$i/";
         }
-      }
+        
+        if (!empty($links)) {
+          $output = '';
+          $output .= '<div class="decisions_selection_1click">';
+          $output .= theme('links', $links, array('class' => 'decisions_selection_1click'));
+          $output .= '</div>';
+        }
+        $form['choices']['1click'] = array(
+          '#value' => $output,
+        );
 
+        //If asynchronous voting is enabled, add the necessary JS.
+        if (variable_get('decisions_ahah', 0) == 1) {
+          drupal_add_js(drupal_get_path('module', 'selection') . '/selection.js');
+        }
+        //At this point, we can return the form since 1-click uses its own callback for vote submission.
+        return $form;
+      }
+      //Not using 1-click. Use radios.
+      else {
+        foreach ($node->choice as $i => $choice) {
+          if ($choice['label']) {
+            $list[$i] = check_plain($choice['label']);
+          }
+        }
+      }
       $form['choice'] = array(
         '#type' => 'radios',
         '#title' => $page ? '' : check_plain($node->title),
@@ -66,8 +181,8 @@ function decisions_selection_voting_form
         '#options' => $list,
       );
     }
+    //Approval/multiple-choice voting.
     else {
-      // approval voting
       foreach ($node->choice as $i => $choice) {
         if ($choice['label']) {
           $list[$i] = check_plain($choice['label']);
@@ -80,6 +195,7 @@ function decisions_selection_voting_form
       );
     }
   }
+  //Add the submit button. 1click has already returned the form by this point.
   $form['vote'] = array(
     '#type' => 'submit',
     '#value' => t('Vote'),
@@ -92,13 +208,11 @@ function decisions_selection_voting_form
  *
  * TODO: implement (http://drupal.org/node/48249)
  */
-function selection_decisions_view_results($node, $teaser, $page) {
-
+function selection_decisions_view_results($node, $teaser, $page, $this_vote = array()) {
   $output = "";
-  $results = votingapi_select_votes(array('content_id' => $node->nid, 'content_type' => 'decisions'));
+  $results = votingapi_select_results(array('content_id' => $node->nid, 'content_type' => 'decisions'));
   // Count the votes for each choice
   $votes = array();
-
   foreach ($results as $result) {
 
     // approval
@@ -123,6 +237,17 @@ function selection_decisions_view_result
   if ($node->choice && $total_votes > 0) {
     // TODO: Those <div>s and <br />s should be in a theme function. First collect all the data in a structure, then theme it.
     // display results for each possible choice
+
+    //If AHAH and 1click are enabled, ensure that the fallback behavior shows the same vote confirmation.
+    if (variable_get('decisions_ahah', 0) == 1 && variable_get('decisions_ahah', 0) == 1 && ($node->maxchoices == 1)) {
+      if (empty($this_vote)) {
+        //Fallback behavior.
+        $this_vote = selection_get_votes($node->nid, $user->uid);
+      }
+      $this_vote = array_shift($this_vote);
+      $vote_label = $node->choice[$this_vote['tag']]['label'];
+      $output .= '<span class="status">' . t('You voted: @vote_tag', array('@vote_tag' => $vote_label)) . '</span>';
+    }
     $output .= '<div class="poll">';
     $max_votes = 0;
     foreach ($votes as $i => $vote) {
@@ -134,7 +259,6 @@ function selection_decisions_view_result
     }
     $output .= '</div>';
   }
-
   $output .= '<br />';
   return $output;
 }
@@ -157,11 +281,28 @@ function selection_decisions_format_vote
   return implode(', ', $unordered_votes);
 }
 
+function selection_get_votes($nid, $uid) {
+  $criteria['content_id'] = $nid;
+  $criteria['value_type'] = 'option';
+  $criteria['content_type'] = 'decisions';
+  $criteria['content_id'] = $nid;
+  if ($uid == 0) {
+    $criteria['vote_source'] = ip_address();
+  }
+  else {
+    $criteria['uid'] = $uid;
+  }
+  $results = votingapi_select_votes($criteria);
+  return $results;
+}
 /**
  * Registers the vote as a key for this node using votingapi_set_vote().
  */
 function decisions_selection_voting_form_submit($form, &$form_state) {
   $node = $form_state['values']['node'];
+  $one_click = variable_get('decisions_1click', 0);
+  global $user;
+  $node = $form_state['values']['node'];
   $votes = array();
   if ($node->maxchoices == 1) {
     // plurality voting
@@ -190,6 +331,8 @@ function decisions_selection_voting_form
     }
   }
   votingapi_add_votes($votes);
+  //If this becomes a performance problem, we use votingapi_set_votes and the site-wide recalculation time.
+  votingapi_recalculate_results('decisions', $node->nid);
   drupal_set_message(t('Your vote was registered.'));
 }
 
@@ -203,24 +346,15 @@ function decisions_selection_voting_form
 function decisions_selection_voting_form_validate($form, &$form_state) {
   $node = $form_state['values']['node'];
   if ($node->maxchoices == 1) {
-    // plurality voting
     if (!array_key_exists($form_state['values']['choice'], $node->choice)) {
       form_set_error('choice', 'At least one choice must be selected.');
     }
   }
   else {
-    // approval voting
-
+    //Approval/Multiple-choice voting
     // array used to check which values are set
     $setvalues = array();
     foreach ($node->choice as $key => $choice) {
-
-      // need a minchoices choice to do this
-      //if (empty($_POST['edit']['Choice_' . $key])) {
-      //form_set_error('Choice_'.$key, "choice $key cannot be empty");
-      //$ok = FALSE;
-      //}
-
       // see if the box is checked
       if (!empty($form_state['values']['choice'][$key])) {
         $numchoices++;
@@ -235,7 +369,6 @@ function decisions_selection_voting_form
         )
       );
     }
-
     // not enough choices ranked
     $minchoices = 1;
     if ($numchoices < $minchoices) {
