Submitted by Alaa on Sun, 09/01/2005 - 16:31.
( categories: )
<?php

/**
* @file
* Enables your site members to vote on decisions using a simple ranking voting system
*/

/**
* Implementation of hook_help().
*/
function rankvote_help($section) {
  switch(
$section) {
  case
'admin/help#rankvote':
    return
t("<p>some text to help admins</p>");
  case
'admin/modules#description':
    return
t("Enables your site members to vote on decisions using a simple ranking vote system");
  case
'node/add#rankvote':
    return
t("A Ranking Vote is a form where visitors can rank each option");
  }
}

/**
* Implementation of hook_access().
*/
function rankvote_access($op, $node) {
  if (
$op == 'create') {
    return
user_access('create ranking votes');
  }
}

/**
* Implementation of hook_block().
*
* Generates a block containing the latest rankvote.
*/
function rankvote_block($op = 'list', $delta = 0) {
  if (
user_access('access content')) {
    if (
$op == 'list') {
      
$blocks[0]['info'] = t('Most recent Ranking Vote');
      return
$blocks;
    }
    else {
      
// Retrieve the latest rankvote.
      
$timestamp = db_result(db_query('SELECT MAX(n.created) FROM {node} n '. node_access_join_sql() ." WHERE n.type = 'rankvote' AND n.status = 1 AND ". node_access_where_sql() .' AND n.moderate = 0'));
      if (
$timestamp) {
    
$rankvote = node_load(array('type' => 'rankvote', 'created' => $timestamp, 'moderate' => 0, 'status' => 1));
    
    if (
$rankvote->nid) {
      
// rankvote_view() dumps the output into $rankvote->body.
      
rankvote_view($rankvote, 1, 0, 1);
    }
      }
      
$block['subject'] = t('Ranking Vote');
      
$block['content'] = $rankvote->body;
      return
$block;
    }
  }
}

/**
* Implementation of hook_cron().
*
* Closes rankvotes that ave exceeded their allowed runtime.
*/
function rankvote_cron() {
  
$result = db_query("SELECT p.nid FROM {rankvote} p INNER JOIN {node} n ON p.nid=n.nid WHERE (n.created + p.runtime) < '". time() ."' AND p.active = '1' AND p.runtime != '0'");
  while (
$rankvote = db_fetch_object($result)) {
    
db_query("UPDATE {rankvote} SET active='0' WHERE nid = %d", $rankvote->nid);
  }
}

/**
* Implementation of hook_delete().
*/
function rankvote_delete($node) {
  
db_query("DELETE FROM {rankvote} WHERE nid=%d", $node->nid);
  
db_query("DELETE FROM {rankvote_choices} WHERE nid = %d", $node->nid);
}

/**
* Implementation of hook_validate().
*/
function rankvote_validate(&$node) {
  if (isset(
$node->title)) {
    
// Check for at least two options and validate amount of votes:
    
$realchoices = 0;
    foreach(
$node->choice as $i => $choice) {
      if (
$choice['chtext'] != '') {
    
$realchoices++;
      }

      if (
$choice['chvotes'] < 0) {
    
form_set_error("choice][$i][chvotes", t('Negative values are not allowed.'));
      }
    }

    if (
$realchoices < 2) {
      
form_set_error("choice][$realchoices][chtext", t('You must fill in at least two choices.'));
    }
  }

  
$node->teaser = rankvote_teaser($node);
}

/**
* Implementation of hook_form().
*/
function rankvote_form(&$node) {
  
$admin = user_access('administer nodes');

  if (
function_exists('taxonomy_node_form')) {
    
$output = implode('', taxonomy_node_form('rankvote', $node));
  }

  if (!isset(
$node->choices)) {
    
$node->choices = max(2, count($node->choice) ? count($node->choice) : 5);
  }

  
// User ticked 'need more choices'.
  
if ($node->morechoices) {
    
$node->choices *= 2;
  }

  
$output .= '<div class="rankvote-form">';
  
// RankVote choices
  //$opts = drupal_map_assoc(range(2, $node->choices * 2 + 5));
  
for ($a = 0; $a < $node->choices; $a++) {
    
$group1 .= form_textfield(t('Choice %n', array('%n' => ($a + 1))), "choice][$a][chtext", $node->choice[$a]['chtext'], 50, 127);
    if (
$admin) {
      
$group1 .= form_textfield(t('Votes for choice %n', array('%n' => ($a + 1))), "choice][$a][chvotes", (int)$node->choice[$a]['chvotes'], 7, 7);
    }
  }
  
$group1 .= form_hidden('choices', $node->choices);
  
$group1 .= form_checkbox(t('Need more choices'), 'morechoices', 1, 0, t("If the amount of boxes above isn't enough, check this box and click the Preview button below to add some more."));
  
$output .= form_group(t('Choices'), $group1);

  
// RankVote attributes
  
$_duration = array(0 => t('Unlimited')) + drupal_map_assoc(array(86400, 172800, 345600, 604800, 1209600, 2419200, 4838400, 9767800, 31536000), "format_interval");
  
$_active = array(0 => t('Closed'), 1=> t('Active'));

  if (
$admin) {
    
$group2 .= form_radios(t('Ranking Vote status'), 'active', isset($node->active) ? $node->active : 1, $_active, t('When a Ranking Vote is closed, visitors can no longer vote for it.'));
  }
  
$group2 .= form_select(t('Ranking Vote duration'), 'runtime', $node->runtime ? $node->runtime : 0, $_duration, t('After this period, the Ranking Vote will be closed automatically.'));
  
  
$output .= form_group(t('Settings'), $group2);
  
$output .= '</div>';
  
  return
$output;
}

/**
* Implementation of hook_insert().
*/
function rankvote_insert($node) {
  if (!
user_access('administer nodes')) {
    
// Make sure all votes are 0 initially
    
foreach ($node->choice as $i => $choice) {
      
$node->choice[$i]['chvotes'] = 0;
    }
    
$node->active = 1;
  }

  
db_query("INSERT INTO {rankvote} (nid, runtime, voters, active) VALUES (%d, %d, '', %d)", $node->nid, $node->runtime, $node->active);

  
$i=0;

  foreach (
$node->choice as $choice) {
    if (
$choice['chtext'] != '') {
      
db_query("INSERT INTO {rankvote_choices} (nid, chtext, chvotes, chorder) VALUES (%d, '%s', %d, %d)", $node->nid, $choice['chtext'], $choice['chvotes'], $i++);
    }
  }
}

/**
* Implementation of hook_link().
*/
function rankvote_link($type, $node = 0, $main) {
  
$links = array();

  if (
$type == 'page' && user_access('access content')) {
    
$links[] = l(t('votes'), 'rankvote', array('title' => t('View the list of Ranking Votes on this site.')));
  }

  return
$links;
}

/**
* Implementation of hook_menu().
*/
function rankvote_menu($may_cache) {
  
$items = array();

  if(
$may_cache) {
    
$items[] = array('path' => 'node/add/rankvote', 'title' => t('ranking vote'),
             
'access' => user_access('create ranking votes'));
    
$items[] = array('path' => 'rankvote', 'title' => t('ranking votes'),
             
'callback' => 'rankvote_page',
             
'access' => user_access('access content'),
             
'type' => MENU_SUGGESTED_ITEM);

    
$items[] = array('path' => 'rankvote/vote',
            
'title' => t('vote'),
            
'callback' => 'rankvote_vote',
            
'access' => user_access('vote on ranking votes'),
            
'type' => MENU_CALLBACK);
  }
  else {
    if (
arg(0) == 'node' && is_numeric(arg(1))) {
      
$node = node_load(array('nid' => arg(1)));

      if (
$node->type == 'rankvote' && $node->allowvotes) {
    
$items[] = array('path' => 'node/'. arg(1) .'/results',
            
'title' => t('results'),
            
'callback' => 'rankvote_results',
            
'access' => user_access('access content'),
            
'weight' => 3,
            
'type' => MENU_LOCAL_TASK);
      }
    }
  }

  return
$items;
}

/**
* Determine an adjusted user id, to allow for basic tracking of anonymous
* users (IP-based).
*/
function rankvote_uid() {
  global
$user;
  if (
$user->uid) {
    
// Pad the UID with underscores to allow a simple strstr() search
    
$id = '_'. $user->uid .'_';
  }
  else {
    
$id = $_SERVER['REMOTE_ADDR'];
  }
  return
$id;
}

/**
* Implementation of hook_load().
*/
function rankvote_load($node) {
  
// Load the appropriate choices into the $node object
  
$rankvote = db_fetch_object(db_query("SELECT runtime, voters, active FROM {rankvote} WHERE nid = %d", $node->nid));
  
  
$result = db_query("SELECT chtext, chvotes, chorder FROM {rankvote_choices} WHERE nid=%d ORDER BY RAND()", $node->nid);
  while (
$choice = db_fetch_array($result)) {
    
$rankvote->choice[$choice['chorder']] = $choice;
  }

  
// Determine whether or not this user is allowed to vote
  
$rankvote->allowvotes = false;
  if (
user_access('vote on ranking votes')) {
    if(!
strstr($rankvote->voters, rankvote_uid())) {
      
$rankvote->allowvotes = $rankvote->active;
    }
  }
  return
$rankvote;
}

/**
* Implementation of hook_node_name().
*/
function rankvote_node_name($node) {
  return
t("Ranking Vote");
}

function
rankvote_page() {
  
// List all Ranking Votes
  
$result = pager_query("SELECT DISTINCT(n.nid), n.title, p.active, n.created, SUM(c.chvotes) AS votes FROM {node} n ". node_access_join_sql() ." INNER JOIN {rankvote} p ON n.nid=p.nid INNER JOIN {rankvote_choices} c ON n.nid=c.nid WHERE type = 'rankvote' AND status = 1 AND ". node_access_where_sql() ." AND moderate = 0 GROUP BY n.nid, n.title, p.active, n.created ORDER BY n.created DESC", 15);
  
$output = '<ul>';
  while (
$node = db_fetch_object($result)) {
    
$output .= '<li>'. l($node->title, "node/$node->nid") .' - '. format_plural($node->votes, '1 vote', '%count votes') .' - '. ($node->active ? t('open') : t('closed')) . '</li>';
  }
  
$output .= '</ul>';
  
$output .= theme("pager", NULL, 15);
  print
theme('page', $output);
}

/**
* Implementation of hook_perm().
*/
function rankvote_perm() {
  return array(
'create ranking votes', 'vote on ranking votes');
}

/**
* Creates a simple teaser that lists all the choices.
*/
function rankvote_teaser($node) {
  if (
is_array($node->choice)) {
    foreach (
$node->choice as $k => $choice) {
      
$teaser .= '* '. $choice['chtext'] .'\n';
    }
  }
  return
$teaser;
}

/**
* Generates the voting form for a ranking vote.
*/
function rankvote_view_voting(&$node, $main, $page, $block) {
  
$output = '<div class="rankvote">';

  
$form = '<div class="vote-form">';
  
$form .= '<div class="choices">';
  if (
$node->choice) {
    
$options = drupal_map_assoc(range(1, count($node->choice)));
    foreach (
$node->choice as $i => $choice) {
      
$form .= form_select(drupal_specialchars($choice['chtext']), "choice][". $choice['chorder'], -1, $options, '', 0, false, true);
    }
  }
  
$form .= '</div>';
  
$form .= form_hidden('nid', $node->nid);
  
$form .= form_submit(t('Vote'), 'vote') .'</div>';
  
  
$output .= form($form, 'post', url('rankvote/vote/'. $node->nid));
  
$output .= '</div>';
  
  return
$output;
}

/**
* Generates a graphical representation of the results of a ranking vote.
*/
function rankvote_view_results(&$node, $main, $page, $block) {
  
// Display the results

  // Count the votes and find the maximum

  
$temp = array();
  foreach(
$node->choice as $i => $choice) {
    
$temp[$i] = $choice['chvotes'];
  }
  
asort($temp);

  
// Output the divs for the text, bars and percentages
  
$output .= '<div class="rankvote">';
  if (
$block) {
    
$output .= '<div class="title">'. $node->title .'</div>';
  }

  
$output .= '<ul>';

  foreach(
$temp as $i => $score) {
    
$output .='<li>'. drupal_specialchars($node->choice[$i]['chtext']) .' <small>score = '. $node->choice[$i]['chvotes'] . '</small></li>';
  }
  
$output .= '</ul></div>';

  return
$output;
}

/**
* Callback for the 'results' tab for ranking votes you can vote on
*/
function rankvote_results() {
  if (
$node = node_load(array('nid' => arg(1)))) {
    print
theme('page', node_show($node, 0), $node->title);
  }
  else {
    
drupal_not_found();
  }
}



function
_valid_rankvote($choice) {
  
$temp = array();
  foreach(
$choice as $rank) {
    
$temp[] = $rank;
  }

  
sort($temp);
  
  for (
$i=0; $i < count($temp); ++$i) {
    if (
$i != $temp[$i]-1) return false;
  }
  return
true;
}

/**
* Callback for processing a vote
*/
function rankvote_vote(&$node) {
  
$nid = arg(2);
  if (
$node = node_load(array('nid' => $nid))) {
    
$edit = $_POST['edit'];
    
$choice = $edit['choice'];
    
$vote = $_POST['vote'];

    if (isset(
$choice) && count($choice) == count($node->choice)) { //need to check valid vote data here
      
if (_valid_rankvote($choice)) {
    if (
$node->allowvotes) {
      
$id = rankvote_uid();
      
$node->voters = $node->voters ? ($node->voters .' '. $id) : $id;
      
db_query("UPDATE {rankvote} SET voters='%s' WHERE nid = %d", $node->voters, $node->nid);
      foreach(
$choice as $i => $rank) {
        
db_query("UPDATE {rankvote_choices} SET chvotes = chvotes + %d WHERE nid = %d AND chorder = %d", $rank, $node->nid, $i);
        
$node->choice[$i]['chvotes'] += $rank;
      }
      
$node->allowvotes = false;
      
drupal_set_message(t('Your vote was recorded.'));
    }
    else {
      
drupal_set_message(t("You're not allowed to vote on this ranking vote."), 'error');
    }
    } else {
    
drupal_set_message(t("Each choice should get a unique rank for the vote to be valid."), 'error');
    }
    } else {
      
drupal_set_message(t("All choices must be ranked for the vote to be valid."), 'error');
    }
    
    
drupal_goto('node/'. $nid);
  }
  else {
    
drupal_not_found();
  }
}

/**
* Implementation of hook_view().
*
* @param $block
*   An extra parameter that adapts the hook to display a block-ready
*   rendering of the ranking vote.
*/
function rankvote_view(&$node, $teaser = FALSE, $page = FALSE, $block = FALSE) {
  global
$user;
  
$output = '';

  if (
$node->allowvotes && ($block || arg(2) != 'results')) {
    
$output .= rankvote_view_voting($node, $main, $page, $block);
  }
  else {
    
$output .= rankvote_view_results($node, $main, $page, $block);
  }

  
// Special display for side-block
  
if ($block) {
    
// No 'read more' link
    
$node->body = $node->teaser = '';

    
$links = link_node($node, $main);
    
$links[] = l(t('older ranking votes'), 'rankvote', array('title' => t('View the list of ranking votes on the site.')));
    if (
$node->allowvotes && $block) {
      
$link[] = l(t('results'), 'node/'. $node->nid .'/results', array('title' => t('View the current ranking vote results.')));
    }

    
$output .= '<div class="links">'. theme("links", $links) .'</div>';
  }

  
$node->body = $node->teaser = $output;
}

/**
* Implementation of hook_update().
*/
function rankvote_update($node) {
  
db_query('UPDATE {rankvote} SET runtime = %d, active = %d WHERE nid = %d', $node->runtime, $node->active, $node->nid);
  
  
db_query('DELETE FROM {rankvote_choices} WHERE nid = %d', $node->nid);
  foreach (
$node->choice as $choice) {
    
$chvotes = (int)$choice['chvotes'];
    
$chtext = $choice['chtext'];
    
    
$i=0;
    if (
$chtext != '') {
      
db_query("INSERT INTO {rankvote_choices} (nid, chtext, chvotes, chorder) VALUES (%d, '%s', %d, %d)", $node->nid, $chtext, $chvotes, $i++);
    }
  }
}