I've written a module that allows users to vote on nodes. Users can change their vote, see the average vote, and view all voted nodes by average score. The problem is I don't know all that much about PHP or Drupal so it's likely that I'm doing a lot of stupid things. In particular there is at least one bug where sometimes the author of the original node will change to the person who last voted on the node. Opps. But I don't see where this could be happening.
So if anyone is willing please look over this module and let me know where I'm messing things up. Of course feel free to use it yourself.
<?php
// $Id: voting.module,v 1.2 2004/09/08 21:22:26 JonBob Exp $
/**
* @file
*
* This module adds the ability for users to vote on nodes and view nodes sorted by the average vote.
*
* To use:
*
* 1. Enable voting module.
* 2. Go to "administer/content/configure/content types/" and check "Ratable" for each type that users can rate.
* 3. Now when a user visits a votable node that nodes average vote and a popup for setting the users vote will show.
*
* To store this extra information, we need these auxiliary database tables:
*
* @code
* CREATE TABLE voting_node_votes (
* nid int(10) unsigned NOT NULL default '0',
* voting_closed int(2) NOT NULL default '0',
* vote_count int(10) unsigned NOT NULL default '0',
* votes_sum int(10) unsigned NOT NULL default '0',
* avg_vote FLOAT(10) unsigned NOT NULL default '0',
* PRIMARY KEY (nid)
* );
* CREATE TABLE voting_user_votes (
* uid int(10) unsigned NOT NULL default '0',
* nid int(10) unsigned NOT NULL default '0',
* vote int(10) unsigned NOT NULL default '0'
* );
* @endcode
*/
/**
* Hooks
*/
function voting_help($section) {
switch ($section) {
case 'admin/modules#description':
return t('Allow users to vote on nodes and view sorted display of nodes by average vote.');
}
}
function voting_settings() {
return form_select(t('Node Types'), 'voting_node_types', variable_get('voting_node_types', drupal_map_assoc(node_list())), drupal_map_assoc(node_list()), t('Select the node types that may be voted on.'), 0, 1);
}
function voting_perm() {
return array('view votes', 'edit own votes');
}
function voting_menu($may_cache) {
$items = array();
if ($may_cache) {
$access = user_access('view votes');
$items[] = array('path' => 'voting', 'title' => t('voting'), 'callback' => 'voting_list_all_votable', 'access' => $access);
} else {
$access = user_access('edit own votes');
$items[] = array('path' => 'voting/vote', 'title' => t('add vote'), 'callback' => 'voting_vote_on_node', 'access' => $access, 'type' => MENU_CALLBACK);
}
return $items;
}
function voting_nodeapi(&$node, $op, $teaser, $page) {
switch ($op) {
case 'form pre':
$form = '';
if (in_array($node->type, variable_get('voting_node_types', drupal_map_assoc(node_list())))) {
$form .= form_checkbox(t('Voting closed'), 'voting_voting_closed', 1, $node->voting_voting_closed, "Turn of voting for this node, the node will no longer be shown in the list of votable nodes.");
$form .= form_hidden("voting_votes_sum", $node->voting_votes_sum);
$form .= form_hidden("voting_vote_count", $node->voting_vote_count);
$form .= form_hidden("voting_avg_vote", $node->voting_avg_vote);
}
return $form;
case 'insert':
if (in_array($node->type, variable_get('voting_node_types', drupal_map_assoc(node_list())))) {
$average = ($node->voting_vote_count != 0) ? $node->voting_votes_sum / $node->voting_vote_count : 0;
db_query("INSERT INTO {voting_node_votes} (nid, voting_closed, votes_sum, vote_count, avg_vote) VALUES (%d, %d, %d, %d, %f)", $node->nid, $node->voting_voting_closed, $node->voting_votes_sum, $node->voting_vote_count, $average);
}
break;
case 'update':
if (in_array($node->type, variable_get('voting_node_types', drupal_map_assoc(node_list())))) {
db_query("DELETE FROM {voting_node_votes} WHERE nid = %d", $node->nid);
$average = ($node->voting_vote_count != 0) ? $node->voting_votes_sum / $node->voting_vote_count : 0;
db_query("INSERT INTO {voting_node_votes} (nid, voting_closed, votes_sum, vote_count, avg_vote) VALUES (%d, %d, %d, %d, %f)", $node->nid, $node->voting_voting_closed, $node->voting_votes_sum, $node->voting_vote_count, $average);
}
break;
case 'delete':
if (in_array($node->type, variable_get('voting_node_types', drupal_map_assoc(node_list())))) {
db_query("DELETE FROM {voting_node_votes} WHERE nid = %d", $node->nid);
}
break;
case 'load':
if (in_array($node->type, variable_get('voting_node_types', drupal_map_assoc(node_list())))) {
$object = db_fetch_object(db_query('SELECT voting_closed, vote_count, votes_sum, avg_vote FROM {voting_node_votes} WHERE nid = %d', $node->nid));
return array('voting_voting_closed' => $object->voting_closed, 'voting_vote_count' => $object->vote_count, 'voting_votes_sum' => $object->votes_sum, 'voting_avg_vote' => $object->avg_vote);
}
break;
case 'view':
if (in_array($node->type, variable_get('voting_node_types', drupal_map_assoc(node_list())))) {
$node->body = $node->body . theme('voting_view', $node);
$node->teaser = $node->teaser . theme('voting_view', $node);
}
break;
}
}
/**
* Vote functions
*/
function voting_vote_on_node($nid) {
global $user;
$edit = $_POST['edit'];
$vote = $edit['vote'];
if (is_numeric($vote)) {
voting_set_vote($user->uid, $nid, $vote);
} else {
watchdog('vote', "Got non-number vote, skipping voting_set_vote.", WATCHDOG_WARNING);
}
drupal_set_message(t('Your vote has been recorded. Thank you.'));
drupal_goto($edit['destination']);
}
function voting_get_vote($uid, $nid) {
$result = db_query('SELECT vote FROM {voting_user_votes} WHERE uid = %d AND nid = %d', $uid, $nid);
if ($row = db_fetch_object($result)) {
return $row->vote;
} else {
return 0;
}
}
function voting_set_vote($uid, $nid, $new_vote) {
$node = node_load(array('nid' => $nid));
$old_vote = voting_get_vote($uid, $nid);
if ($old_vote != 0) {
$node->voting_votes_sum -= $old_vote;
$node->voting_vote_count -= 1;
db_query('DELETE FROM {voting_user_votes} WHERE uid = %d AND nid = %d', $uid, $nid);
}
if ($new_vote != 0) {
$node->voting_votes_sum += $new_vote;
$node->voting_vote_count += 1;
db_query('DELETE FROM {voting_user_votes} WHERE uid = %d AND nid = %d', $uid, $nid);
db_query('INSERT INTO {voting_user_votes} (uid, nid, vote) VALUES (%d, %d, %d)', $uid, $nid, $new_vote);
}
$node->taxonomy = array_keys(taxonomy_node_get_terms($nid));
$node = node_validate($node);
node_save($node);
}
function voting_list_all_votable() {
global $user;
drupal_set_title("Voting");
$access = user_access('edit own votes');
if ($access) {
$header = array(
array('data' => t('title'), 'field' => 'n.title'),
array('data' => t('type'), 'field' => 'n.type'),
array('data' => t('my vote'), 'field' => 'vote'),
array('data' => t('avg vote'), 'field' => 'nr.avg_vote', 'sort' => 'desc'),
array('data' => t('votes'), 'field' => 'nr.vote_count')
);
} else {
$header = array(
array('data' => t('title'), 'field' => 'n.title'),
array('data' => t('type'), 'field' => 'n.type'),
array('data' => t('avg vote'), 'field' => 'nr.avg_vote', 'sort' => 'desc'),
array('data' => t('votes'), 'field' => 'nr.vote_count')
);
}
$cols = 2;
$title = t('my unvoted nodes');
$types = implode("','", variable_get('voting_node_types', drupal_map_assoc(node_list())));
$sql = "SELECT n.nid, n.title, n.type, n.created, uv.vote, nr.voting_closed, nr.avg_vote, nr.vote_count FROM {node} n LEFT JOIN {voting_user_votes} uv ON n.nid = uv.nid AND uv.uid = $user->uid LEFT JOIN {voting_node_votes} nr ON n.nid = nr.nid WHERE n.type IN ('$types') AND (ISNULL(nr.voting_closed) OR NOT nr.voting_closed)";
$sql .= tablesort_sql($header);
$result = pager_query(db_rewrite_sql($sql), 50);
while ($row = db_fetch_object($result)) {
if ($access) {
$rows[] = array(l($row->title, "node/$row->nid"), $row->type, $row->vote, $row->avg_vote, $row->vote_count);
} else {
$rows[] = array(l($row->title, "node/$row->nid"), $row->type, $row->avg_vote, $row->vote_count);
}
}
if ($pager = theme('pager', NULL, 50, 0, tablesort_pager())) {
$rows[] = array(array('data' => $pager, 'colspan' => $cols));
}
$output .= theme('table', $header, $rows);
print theme('page', $output, $title);
}
/**
* Themes
*/
function theme_voting_view($node) {
global $user;
$voting_closed = $node->voting_voting_closed;
$output = '
';
if ($voting_closed) {
$output .= t('Voting closed.');
} else {
$votes_sum = $node->voting_votes_sum;
$vote_count = $node->voting_vote_count;
$vote = voting_get_vote($user->uid, $node->nid);
$options = array(
0 => t('No Vote'),
5 => t('5 Stars'),
4 => t('4 Stars'),
3 => t('3 Stars'),
2 => t('2 Stars'),
1 => t('1 Star')
);
if ($vote_count != 0) {
$output .= t('
Average Vote: %average_vote