? updown.patch
Index: updown.info
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/updown/updown.info,v
retrieving revision 1.1
diff -u -p -r1.1 updown.info
--- updown.info	20 Aug 2008 23:35:09 -0000	1.1
+++ updown.info	22 Aug 2008 17:27:25 -0000
@@ -3,5 +3,4 @@ name = UpDown
 description = Provides a very simple up/down voting widget to use for nodes.
 package = Voting
 version = VERSION
-core = 6.x
-dependencies[] = votingapi
\ No newline at end of file
+dependencies = votingapi
\ No newline at end of file
Index: updown.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/updown/updown.module,v
retrieving revision 1.1
diff -u -p -r1.1 updown.module
--- updown.module	20 Aug 2008 23:35:09 -0000	1.1
+++ updown.module	22 Aug 2008 17:27:25 -0000
@@ -7,52 +7,214 @@
  * ========================================================================
  */
 
+define('UPDOWN_VALUE_HIGH',   100);
+define('UPDOWN_VALUE_LOW',    0);
+define('UPDOWN_VALUE_UNVOTE', 'undo');
+
+/**
+ * Implementation of hook_perm().
+ */
 function updown_perm() {
-  return array('vote on logo');
+  return array('rate content');
 }
 
-function updown_menu() {
-  $items['node/%/vote/%'] = array(
-    'title' => 'Up/Down Vote',
-    'type' => MENU_CALLBACK,
-    'page callback' => '_updown_vote',
-    'page arguments' => array(1, 3),
-    'access callback' => 'user_access',
-    'access arguments' => array('vote on logo'),
-  );
+/**
+ * Implementation of hook_menu().
+ */
+function updown_menu($may_cache) {
+  if ($may_cache) {
+  }
+  else {
+    if (is_numeric(arg(1))) {
+      if ($node = node_load(arg(1))) {
+        $items[] = array(
+          'path' => 'node/'.arg(1).'/vote',
+          'title' => 'Up/Down Vote',
+          'type' => MENU_CALLBACK,
+          'callback' => 'updown_node_vote',
+          'callback arguments' => array($node, arg(3)),
+          'access' => user_access('rate content'),
+        );
+      }
+    }
+  }
   
   return $items;
 }
 
-function _updown_vote($nid, $vote) {
-  if ($vote == 'undo') {
-    _updown_delete_vote($nid);
-  }
-  elseif (is_numeric($nid)) {
-    $node = node_load($nid);
+/**
+ * Implementation of hook_form_alter
+ * Adds updown enaable and position to the node-type configuration form.
+ * 
+ */
+function updown_form_alter($form_id, &$form) {
+  if ($form_id == 'node_type_form' && isset($form['identity']['type'])) {
+    // Goofy hack to get the buttons at the end of the array.
+    $form['workflow']['#weight'] = isset($form['workflow']['#weight']) ? $form['workflow']['#weight'] + 1 : 1;
+    $form['submit']['#weight'] = isset($form['submit']['#weight']) ? $form['submit']['#weight'] + 1 : 1;
+    $form['delete']['#weight'] = isset($form['delete']['#weight']) ? $form['delete']['#weight'] + 1 : 1;
+
+    $form['updown'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('UpDown ratings'),
+      '#collapsible' => TRUE,
+      '#collapsed' => !variable_get('updown_'. $form['#node_type']->type, 0),
+      '#description' => t('To rate this content, enable UpDown rating below.'),
+      //'#theme' => 'updown_node_type_form',
+      '#attributes' => array('id' => 'updown-node-type-form'),
+    );
+
+    $form['updown']['updown'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Enable UpDown rating'),
+      '#default_value' => variable_get('updown_'. $form['#node_type']->type, 0),
+      '#return_value' => 1,
+      '#weight' => -5,
+    );
+    
+    $form['updown']['updown_unvote'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Allow users to undo their votes'),
+      '#default_value' => variable_get('updown_unvote_'. $form['#node_type']->type, 0),
+      '#return_value' => 1,
+    );
+    
+    $form['updown']['updown_position_teaser'] = array(
+      '#type' => 'select',
+      '#title' => t('Teaser display'),
+      '#default_value' => variable_get('updown_position_teaser_'. $form['#node_type']->type, 'hidden'),
+      '#options' => array(
+        'above' => t('Clickable widget above teaser'),
+        'below' => t('Clickable widget below teaser'),
+        'above_static' => t('Static display above teaser'),
+        'below_static' => t('Static display below teaser'),
+        'link' => t('Teaser link to full node widget'),
+        'hidden' => t('<Hidden>'),
+      ),
+    );
+
+    $form['updown']['updown_position'] = array(
+      '#type' => 'select',
+      '#title' => t('Full node display'),
+      '#default_value' => variable_get('updown_position_'. $form['#node_type']->type, 'below'),
+      '#options' => array(
+        'above' => t('Clickable widget above node body'),
+        'below' => t('Clickable widget below node body'),
+        'above_static' => t('Static display above node body'),
+        'below_static' => t('Static display below node body'),
+        'hidden' => t('<Hidden>'),
+      ),
+    );
     
+    $form['#submit'][] = 'updown_node_type_form_submit';
+  }
+}
+
+/**
+ * Additional submit handler for the node type form.
+ */
+function updown_node_type_form_submit($form, &$form_values) {
+  // Do not save any updown variables if updown is disabled.
+  if (isset($form_values['updown']) && $form_values['updown'] === 0) {
+    foreach ($form_values as $key => $value) {
+      if (strpos($key, 'updown') === 0) {
+        variable_del($key .'_'. $form_values['type']);
+      }
+    }
+  }
+}
+
+/**
+ * Implementation of hook_node_type().
+ */
+function updown_node_type($op, $info) {
+  $type = $info->type;
+  $variables = array(
+    'updown',
+    'updown_unvote',
+    'updown_position_teaser',
+    'updown_position',
+  );
+
+  // Be responsible and cleanup unneeded variables.
+  if ($op == 'delete') {
+    foreach ($variables as $variable) {
+      variable_del($variable .'_'. $type);
+    }
+  }
+  // When changing the type name, update the variables.
+  elseif ($op == 'update' && !empty($info->old_type) && $info->old_type != $info->type) {
+    foreach ($variables as $variable) {
+      $value = variable_get($variable .'_'. $type, -1);
+      if ($value != -1) {
+        variable_del($variable .'_'. $type);
+        variable_set($variable .'_'. $type, $value);
+      }
+    }
+  }
+}
+
+/**
+ * Implementation of hook_nodeapi()
+ * 
+ * Adds the updown widget to the node view.
+ */
+function updown_nodeapi(&$node, $op, $teaser, $page) {
+  switch ($op) {
+    case 'view':
+      if ($node->build_mode != NODE_BUILD_PREVIEW && !isset($node->modr8_form_teaser) && variable_get('updown_'. $node->type, 0)) {
+        if ($teaser) {
+          $position = variable_get('updown_position_teaser_'. $node->type, 'above');
+        }
+        else {
+          $position = variable_get('updown_position_'. $node->type, 'above');
+        }
+        switch ($position) {
+          case 'above':
+          case 'below':
+            if (user_access('rate content with updown')) {
+              $node->content['updown_widget'] = array(
+                '#value' => theme('updown_active_widget', $node, $teaser),
+                '#weight' => $position == 'above' ? -10 : 50,
+              );
+              break;
+            } // Fall through to static if not allowed to rate.
+          case 'above_static':
+          case 'below_static':
+            $node->content['updown_widget'] = array(
+              '#value' => theme('updown_inactive_widget', $node, $teaser),
+              '#weight' => $position == 'above_static' ? -10 : 50,
+            );
+            break;
+          default:
+            // We'll do nothing.
+            break;
+        }
+      }
+      break;
+  }
+}
+
+function updown_node_vote($node, $vote) {
+  if ($vote == UPDOWN_VALUE_UNVOTE) {
+    if (variable_get('updown_unvote_'. $node->type, 0)) {
+      _updown_delete_vote($node);
+    }
+  }
+  else {
     // additional validity check
-    if (($node->type == 'logo' || $node->type = 'session_proposal') && is_numeric($vote) && $vote <= 100 && $vote >= 0) {
+    if (variable_get('updown_'. $node->type, 0) && is_numeric($vote) && $vote <= UPDOWN_VALUE_HIGH && $vote >= UPDOWN_VALUE_LOW) {
       global $user;
       
-      // assemble the vote array
-      $vote = array(array(
-        'content_type' => 'node',
-        'content_id' => $nid,
-        'value_type' => 'percent',
-        'value' => $vote,
-        'uid' => $user->uid,
-      ));
-      
       // log the vote
-      votingapi_set_votes($vote);
+      votingapi_set_vote('node', $node->nid, $vote, $user->uid);
     }
   }
   
   // if the request came from a link, return the user to the page they were on;
   // otherwise, return the new score
   if ($_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') {
-    exit(print(_updown_get_current_score($nid)));
+    exit(print(_updown_get_current_score($node)));
   }
   else {
     header('Location: '. $_SERVER['HTTP_REFERER']);
@@ -60,45 +222,46 @@ function _updown_vote($nid, $vote) {
   }
 }
 
-function _updown_delete_vote($nid) {
-  // check the nid for validity
-  if (is_numeric($nid)) {
-    $node = node_load($nid);
+function _updown_delete_vote($node) {
+  // additional validity check 
+  if (variable_get('updown_'. $node->type, 0)) {
+    global $user;
     
-    // additional validity check 
-    if (in_array($node->type, array('logo', 'session_proposal'))) {
-      global $user;
-      
-      $votes = array();
-      $criteria = array(
-        'content_type' => 'node',
-        'content_id' => $nid,
-        'uid' => $user->uid,
-      );
-      
-      $votes = votingapi_select_votes($criteria);
-      votingapi_delete_votes($votes);
+    $votes = array();
+    $criteria = array(
+      'content_type' => 'node',
+      'content_id' => $nid,
+      'uid' => $user->uid,
+    );
+    
+    $votes = votingapi_select_votes($criteria);
+    // can't use votingapi_delete_votes($votes) since it expects an array
+    // of objects and we have an array of arrays
+    if (is_array($votes)) {
+      foreach ($votes as $vobj) {
+        votingapi_delete_vote((object)$vobj);
+      }
     }
   }
   
-  return _updown_get_current_score($nid);
+  return _updown_get_current_score($node);
 }
 
-function _updown_get_current_score($nid) {
+function _updown_get_current_score($node) {
   $current_score = 0;
   
   $criteria = array(
     'content_type' => 'node',
-    'content_id' => $nid,
+    'content_id' => $node->nid,
   );
   
   $votes = votingapi_select_votes($criteria);
   
   foreach ($votes as $vote) {
-    if ($vote['value'] == 100) {
+    if ($vote['value'] == UPDOWN_VALUE_HIGH) {
       ++$current_score;
     }
-    elseif ($vote['value'] == 0) {
+    elseif ($vote['value'] == UPDOWN_VALUE_LOW) {
       --$current_score;
     }
   }
@@ -106,107 +269,251 @@ function _updown_get_current_score($nid)
   return $current_score;
 }
 
-function _updown_user_voted($nid) {
+function _updown_user_voted($node, $account=NULL) {
   global $user;
   
+  if (empty($account)) {
+    $account = $user;
+  }
+  
   $criteria = array(
     'content_type' => 'node',
-    'content_id' => $nid,
-    'uid' => $user->uid,
+    'content_id' => $node->nid,
+    'uid' => $account->uid,
   );
   
   return count(votingapi_select_votes($criteria));
 }
 
-
-
 /**
  * ========================================================================
  * Theme Functions
  * ========================================================================
  */
 
-function updown_theme() {
-  return array(
-    'updown_widget' => array(
-      'arguments' => array('nid' => NULL, 'type' => NULL),
-    ),
-    'updown_inactive_widget' => array(
-      'arguments' => array('nid' => NULL),
-    ),
-    'updown_active_widget' => array(
-      'arguments' => array('nid' => NULL),
-    ),
-  );
-}
-
-function theme_updown_widget($nid, $type) {
-  // no voting for anonymous users
+function theme_updown_inactive_widget($node, $teaser=NULL, $user=NULL) {
   global $user;
-  if (!$user->uid || $type == 'logo') {
-    return theme('updown_inactive_widget', $nid);
-  }
-  else {
-    return theme('updown_active_widget', $nid);
-  }
-}
 
-function theme_updown_inactive_widget($nid) {
-  $current_score = _updown_get_current_score($nid);
+  if (empty($variables['user'])) {
+    $variables['user'] = $user;
+  }
+  
+  $current_score = _updown_get_current_score($node->nid);
+  $user_voted = _updown_user_voted($node->nid);
+  $can_unvote = variable_get('updown_unvote_'. $node->type, 0);
+  
+  ob_start();
+  ?>
+  <div class="updown-widget">
+    <div class="updown-score"><span class="updown-current-score"><?php print $current_score ?></span> score</div>
+  </div>
+  <?php
+  $output = ob_get_contents();
+  ob_end_clean();
   
-  return <<<MARKUP
-<div class="updown-widget">
-  <div class="updown-score"><span class="updown-current-score">{$current_score}</span> score</div>
-</div>
-MARKUP;
+  return $output;
 }
 
-function theme_updown_active_widget($nid) {
-  $current_score = _updown_get_current_score($nid);
-  $vote_up_uri = base_path() .'node/'. $nid .'/vote/100';
-  $vote_down_uri = base_path() .'node/'. $nid .'/vote/0';
-  $vote_undo_uri = base_path() .'node/'. $nid .'/vote/undo';
-  $vote_class = _updown_user_voted($nid) ? 'voted' : 'voting';
+function theme_updown_active_widget($node, $teaser=NULL, $user=NULL) {
+  global $user;
+
+  if (empty($variables['user'])) {
+    $variables['user'] = $user;
+  }
   
-  return <<<MARKUP
-<script type="text/javascript">
-  jQuery(document).ready(function() {
-    currentScore = $('#updown-widget-{$nid}').children('.updown-score').children('.updown-current-score');
-    voteElement = $('#updown-widget-{$nid}').children('.updown-vote');
-    voteUndoElement = $('#updown-widget-{$nid}').children('.updown-voteundo');
-    
-    // clicking on the '+' or '-' buttons
-    voteElement.children().children('a').click(function() {
-      $.get($(this).attr('href'), function(data) {
-       currentScore.html(data);
-       voteElement.hide();
-       voteUndoElement.show();
-      });
+  $current_score = _updown_get_current_score($node->nid);
+  $user_voted = _updown_user_voted($node->nid);
+  $can_unvote = variable_get('updown_unvote_'. $node->type, 0);
+
+  $vote_up_uri = base_path() .'node/'. $node->nid .'/vote/'.UPDOWN_VALUE_HIGH;
+  $vote_down_uri = base_path() .'node/'. $node->nid .'/vote/'.UPDOWN_VALUE_LOW;
+  $vote_undo_uri = base_path() .'node/'. $node->nid .'/vote/'.UPDOWN_VALUE_UNVOTE;
+  $vote_class = $user_voted ? 'voted' : 'voting';
+  
+  ob_start();
+  ?>
+  <script type="text/javascript">
+    jQuery(document).ready(function() {
+      currentScore = $('#updown-widget-<?php print $node->nid ?>').children('.updown-score').children('.updown-current-score');
+      voteElement = $('#updown-widget-<?php print $node->nid ?>').children('.updown-vote');
+      <?php if ($can_unvote) : ?>
+        voteUndoElement = $('#updown-widget-<?php print $node->nid ?>').children('.updown-voteundo');
+      <?php endif; ?>
       
-      // disable the normal link
-      return false;
-    });
-    
-    // clicking on the 'undo' button
-    voteUndoElement.children('a').click(function() {
-      $.get($(this).attr('href'), function(data) {
-        currentScore.html(data);
-        voteUndoElement.hide();
-        voteElement.show();
+      // clicking on the '+' or '-' buttons
+      voteElement.children().children('a').click(function() {
+        $.get($(this).attr('href'), function(data) {
+         currentScore.html(data);
+         voteElement.hide();
+         <?php if ($can_unvote) : ?>
+          voteUndoElement.show();
+         <?php endif; ?>
+        });
+        
+        // disable the normal link
+        return false;
       });
       
-      // disable the normal link
-      return false;
+      // clicking on the 'undo' button
+      voteUndoElement.children('a').click(function() {
+        $.get($(this).attr('href'), function(data) {
+          currentScore.html(data);
+          <?php if ($can_unvote) : ?>
+            voteUndoElement.hide();
+          <?php endif; ?>
+          voteElement.show();
+        });
+        
+        // disable the normal link
+        return false;
+      });
     });
-  });
-</script>
-<div class="updown-widget updown-widget-{$vote_class}" id="updown-widget-{$nid}">
-  <div class="updown-score"><span class="updown-current-score">{$current_score}</span> score</div>
-  <div class="updown-vote">
-    <div class="updown-voteup"><a href="{$vote_up_uri}">+</a></div>
-    <div class="updown-votedown"><a href="{$vote_down_uri}">-</a></div>
+  </script>
+  <div class="updown-widget updown-widget-<?php print $vote_class ?>" id="updown-widget-<?php print $node->nid ?>">
+    <div class="updown-score"><span class="updown-current-score"><?php print $current_score ?></span> score</div>
+    <div class="updown-vote">
+      <div class="updown-voteup"><a href="<?php print $vote_up_uri ?>">+</a></div>
+      <div class="updown-votedown"><a href="<?php print $vote_down_uri ?>">-</a></div>
+    </div>
+    <?php if ($can_unvote) : ?>
+      <div class="updown-voteundo"><a href="<?php print $vote_undo_uri ?>">undo</a></div>
+    <?php endif; ?>
   </div>
-  <div class="updown-voteundo"><a href="{$vote_undo_uri}">undo</a></div>
-</div>
-MARKUP;
+  <?php
+  $output = ob_get_contents();
+  ob_end_clean();
+  
+  return $output;
+}
+
+/**
+ * ========================================================================
+ * VotingAPI Functions
+ *    These have been back ported from Voting API 2.0-beta6
+ * ========================================================================
+ */
+
+if (!function_exists('votingapi_select_votes')) {
+  /**
+   * Select individual votes from the database.
+   *
+   * @param $criteria
+   *   A keyed array used to build the select query. Keys can contain
+   *   a single value or an array of values to be matched.
+   *   $criteria['vote_id']       (If this is set, all other keyes are skipped)
+   *   $criteria['content_id']
+   *   $criteria['content_type']
+   *   $criteria['value_type']
+   *   $criteria['tag']
+   *   $criteria['uid']
+   *   $criteria['vote_source']
+   *   $criteria['timestamp']   (If this is set, records with timestamps
+   *      GREATER THAN the set value will be selected.)
+   * @return
+   *   An array of votes matching the criteria.
+   */
+  function votingapi_select_votes($criteria = array()) {
+    if (!empty($criteria['vote_source'])) {
+      $criteria['timestamp'] = time() - variable_get('votingapi_anonymous_window', 3600);
+    }
+    $votes = array();
+    $result = _votingapi_select('vote', $criteria);
+    while ($vote = db_fetch_array($result)) {
+      $votes[] = $vote;
+    }
+    return $votes;
+  }
+}
+
+if (!function_exists('_votingapi_select')) {
+  /**
+   * Internal helper function constructs SELECT queries. Don't use unless you're me.
+   */
+  function _votingapi_select($table = 'vote', $criteria = array(), $limit = 0) {
+    $query = "SELECT * FROM {votingapi_". $table ."} v WHERE 1 = 1";
+    $details = _votingapi_query('vote', $criteria);
+
+    $query .= $details['query'];
+    return $limit ? db_query_range($query, $details['args'], 0, $limit) : db_query($query, $details['args']);
+  }
+}
+
+if (!function_exists('_votingapi_query')) {
+  /**
+   * Internal helper function constructs WHERE clauses. Don't use unless you're me.
+   */
+  function _votingapi_query($table = 'vote', $criteria = array(), $alias = 'v.') {
+    $criteria += array(
+      'vote_id' => NULL,
+      'vote_cache_id' => NULL,
+      'content_id' => NULL,
+      'content_type' => NULL,
+      'value_type' => NULL,
+      'value' => NULL,
+      'tag' => NULL,
+      'uid' => NULL,
+      'timestamp' => NULL,
+      'vote_source' => NULL,
+      'function' => NULL,
+    );
+
+    $query = '';
+    $args = array();
+    if (!empty($criteria['vote_id'])) {
+      _votingapi_query_builder($alias . 'vote_id', $criteria['vote_id'], $query, $args);
+    } elseif (!empty($criteria['vote_cache_id'])) {
+      _votingapi_query_builder($alias . 'vote_cache_id', $criteria['vote_cache_id'], $query, $args);
+    } else {
+      _votingapi_query_builder($alias . 'content_type', $criteria['content_type'], $query, $args, TRUE);
+      _votingapi_query_builder($alias . 'content_id', $criteria['content_id'], $query, $args);
+      _votingapi_query_builder($alias . 'value_type', $criteria['value_type'], $query, $args, TRUE);
+      _votingapi_query_builder($alias . 'tag', $criteria['tag'], $query, $args, TRUE);
+      _votingapi_query_builder($alias . 'function', $criteria['function'], $query, $args, TRUE);
+      _votingapi_query_builder($alias . 'uid', $criteria['uid'], $query, $args);
+      _votingapi_query_builder($alias . 'vote_source', $criteria['vote_source'], $query, $args, TRUE);
+      _votingapi_query_builder($alias . 'timestamp', $criteria['timestamp'], $query, $args);
+    }
+    return array('query' => $query, 'args' => $args);
+  }
+}
+
+if (!function_exists('_votingapi_query_builder')) {
+  /**
+   * Internal helper function constructs individual elements of WHERE clauses.
+   * Don't use unless you're me.
+   */
+  function _votingapi_query_builder($name, $value, &$query, &$args, $col_is_string = FALSE) {
+    if (!isset($value)) {
+      // Do nothing
+    }
+    elseif ($name === 'timestamp') {
+      $query .= " AND timestamp >= %d";
+      $args[] = $value;
+    }
+    elseif ($name === 'v.timestamp') {
+      $query .= " AND v.timestamp >= %d";
+      $args[] = $value;
+    }
+    else {
+      if (is_array($value)) {
+        if ($col_is_string) {
+          $query .= " AND $name IN ('". array_fill(1, count($value), "'%s'") ."')";
+          $args += $value;
+        }
+        else {
+          $query .= " AND $name IN (". array_fill(1, count($value), "%d") .")";
+          $args += $value;
+        }
+      } else {
+        if ($col_is_string) {
+          $query .= " AND $name = '%s'";
+          $args[] = $value;
+        }
+        else {
+          $query .= " AND $name = %d";
+          $args[] = $value;
+        }
+      }
+    }
+  }
 }
