'voting/flash', 'title' => t('voting'), 'callback' => 'voting_flash', 'access' => user_access('access content'), 'type' => MENU_CALLBACK); return $items; } /** * Implementation of hook_settings() */ function voting_settings() { if (!function_exists('votingapi_set_vote')) { drupal_set_message('The voting module depends on the voting API module. Please install the voting API module before attempting to use this module.', 'error'); } // General options $form['general'] = array( '#type' => 'fieldset', '#title' => t('General options'), '#collapsible' => true, '#collapsed' => false, ); $form['general']['voting_location'] = array( '#type' => 'radios', '#title' => t('Voting controls on nodes'), '#default_value' => variable_get('voting_location', 1), '#options' => array(t('Display above the node'), t('Display below the node'), t('Do not display')), '#description' => t('Position of the voting control. Select "Do not display" if you are calling the voting module function directly from your theme and/or module code.'), ); $form['general']['voting_location'] = array( '#type' => 'radios', '#title' => t('Show voting in teaser'), '#default_value' => variable_get('voting_show_in_teaser', 0), '#options' => array(1 => t('yes'), 0 => t('no')), ); $form['general']['voting_ip_timeout'] = array( '#type' => 'textfield', '#title' => t("Voting IP timeout (in seconds)"), '#default_value' => variable_get('voting_ip_timeout', 3600), '#size' => 8, '#maxlength' => 8, '#description' => t('Controls if and how often users with the same IP address can vote for the same thing. Set it to 0 to disable this feature.'), ); // Colors $form['colors'] = array( '#type' => 'fieldset', '#title' => t('Colors'), '#collapsible' => true, '#collapsed' => true, ); $form['colors']['voting_bgcolor'] = array( '#type' => 'textfield', '#title' => t('Background color for voting control'), '#default_value' => variable_get('voting_bgcolor', '0xffffff'), '#size' => 8, '#maxlength' => 8, ); $form['colors']['voting_on_fill'] = array( '#type' => 'textfield', '#title' => t("Star 'on' fill color"), '#default_value' => variable_get('voting_on_fill', '0x990000'), '#size' => 8, '#maxlength' => 8, ); $form['colors']['voting_on_border'] = array( '#type' => 'textfield', '#title' => t("Star 'on' border color"), '#default_value' => variable_get('voting_on_border', '0x330000'), '#size' => 8, '#maxlength' => 8, ); $form['colors']['voting_off_fill'] = array( '#type' => 'textfield', '#title' => t("Star 'off' fill color"), '#default_value' => variable_get('voting_off_fill', '0xcccccc'), '#size' => 8, '#maxlength' => 8, ); $form['colors']['voting_off_border'] = array( '#type' => 'textfield', '#title' => t("Star 'off' border color"), '#default_value' => variable_get('voting_off_border', '0x333333'), '#size' => 8, '#maxlength' => 8, ); $form['colors']['voting_txt_color'] = array( '#type' => 'textfield', '#title' => t("Text color"), '#default_value' => variable_get('voting_txt_color', '0x000000'), '#size' => 8, '#maxlength' => 8, ); // Text strings $form['text'] = array( '#type' => 'fieldset', '#title' => t('Text Strings'), '#collapsible' => true, '#collapsed' => true, ); $form['text']['voting_txt_vote1'] = array( '#type' => 'textfield', '#title' => t('Star 1'), '#default_value' => variable_get('voting_txt_vote1', 'Awful'), '#size' => 20, '#maxlength' => 20, ); $form['text']['voting_txt_vote2'] = array( '#type' => 'textfield', '#title' => t('Star 2'), '#default_value' => variable_get('voting_txt_vote2', 'Poor'), '#size' => 20, '#maxlength' => 20, ); $form['text']['voting_txt_vote3'] = array( '#type' => 'textfield', '#title' => t('Star 3'), '#default_value' => variable_get('voting_txt_vote3', 'Average'), '#size' => 20, '#maxlength' => 20, ); $form['text']['voting_txt_vote4'] = array( '#type' => 'textfield', '#title' => t('Star 4'), '#default_value' => variable_get('voting_txt_vote4', 'Good'), '#size' => 20, '#maxlength' => 20, ); $form['text']['voting_txt_vote5'] = array( '#type' => 'textfield', '#title' => t('Star 5'), '#default_value' => variable_get('voting_txt_vote5', 'Excellent'), '#size' => 20, '#maxlength' => 20, ); $form['text']['voting_txt_before_vote'] = array( '#type' => 'textfield', '#title' => t('Message shown BEFORE a user votes'), '#default_value' => variable_get('voting_txt_before_vote', 'Rate This:'), '#size' => 20, '#maxlength' => 20, ); $form['text']['voting_txt_after_vote'] = array( '#type' => 'textfield', '#title' => t('Message shown AFTER a user votes'), '#default_value' => variable_get('voting_txt_after_vote', 'My Vote:'), '#size' => 20, '#maxlength' => 20, ); $form['text']['voting_txt_login'] = array( '#type' => 'textfield', '#title' => t('No permission to vote message'), '#default_value' => variable_get('voting_txt_login', 'Please login or register to vote.'), '#size' => 40, '#maxlength' => 40, ); return $form; } /** * Return a vote object (containing the user's vote, average vote, and number of votes) */ function voting_get_vote($content_type, $content_id) { global $user; // my vote if ($_COOKIE["vote_{$content_type}_{$content_id}"]) { // get user's vote from a cookie on their computer $voting_id = $_COOKIE["vote_{$content_type}_{$content_id}"]; $result = db_query("SELECT * FROM {votes} WHERE voting_id=%d", $voting_id); $vote = db_fetch_object($result); } elseif ($user->uid) { // get the user's vote by using their user_id $result = db_query("SELECT * FROM {votes} WHERE content_type='%s' AND content_id=%d AND uid=%d", $content_type, $content_id, $user->uid); $vote = db_fetch_object($result); } else { // get the user's vote from their IP address (only within one hour, or whatever the voting_ip_timeout variable is set to) $result = db_query("SELECT * FROM {votes} WHERE content_type='%s' AND content_id=%d AND hostname='%s' AND timestamp > %d", $content_type, $content_id, $_SERVER['REMOTE_ADDR'], time() - variable_get("voting_ip_timeout", 60 * 60)); $vote = db_fetch_object($result); } // average vote $result = db_query("SELECT SUM(vote)/COUNT(vote) AS avg_vote FROM {votes} WHERE content_type='%s' AND content_id=%d", $content_type, $content_id); $vote->avg_vote = $result ? db_result($result, "avg_vote") : 0; $vote->avg_vote = round($vote->avg_vote / 0.5) * 0.5; // rounds to nearest half $vote->avg_vote = number_format($vote->avg_vote, 1); // fixes to 1 decimal place // number of votes $result = db_query("SELECT COUNT(vote) FROM {votes} WHERE content_type='%s' AND content_id=%d", $content_type, $content_id); $vote->num_votes = $result ? db_result($result, 0) : 0; return $vote; } function voting_convert_to_percent($vote) { // round to nearest integer $vote = round($vote); // set limits $vote = ($vote < 1) ? 1 : $vote; $vote = ($vote > 5) ? 5 : $vote; // convert to percentage switch($vote) { case 5: return 90; case 4: return 70; case 3: return 50; case 2: return 30; case 1: return 10; default: return 0; } } function voting_convert_from_percent($vote) { // round to nearest ten $vote = 10 * round($vote / 10); // set limits $vote = ($vote < 0) ? 0 : $vote; $vote = ($vote > 100) ? 100 : $vote; // convert to scale from 1.0 to 5.0 switch($vote) { case 100: case 90: return 5.0; case 80: return 4.5; case 70: return 4.0; case 60: return 3.5; case 50: return 3.0; case 40: return 2.5; case 30: return 2.0; case 20: return 1.5; case 10: return 1.0; default: return 0; } } /** * This function is used with a Macromedia Flash based voting control to send and receive data in * one step. First, Flash is passed the content_type and content_id from the page where it is used. * Then, Flash calls /voting/flash and sends the content_type and content_id using POST. A vote is * not passed, so this function will not insert a vote, but it will return the current voting information. * If a user votes (or changes their vote), this function is called again from Flash, this time with * a vote. */ function voting_flash() { global $user; $content_type = $_POST['content_type']; $content_id = $_POST['content_id']; $user_vote = $_POST['vote']; if ($user_vote) { // if a new vote is sent... $vote->value = voting_convert_to_percent($user_vote); $vote->value_type = 'percent'; $vote->tag = 'vote'; votingapi_set_vote($content_type, $content_id, $vote); } else { $vote = votingapi_get_user_votes($content_type, $content_id); $user_vote = voting_convert_from_percent($vote[0]->value); } // get average and count $average = votingapi_get_voting_result($content_type, $content_id, 'percent', 'vote', 'average'); $avg_vote = voting_convert_from_percent($average->value); $count = votingapi_get_voting_result($content_type, $content_id, 'percent', 'vote', 'count'); // return the current vote info to flash $user_vote = ($user_vote) ? $user_vote : 0; $avg_vote = ($avg_vote) ? $avg_vote : 0; $num_votes = ($count->value) ? $count->value : 0; echo "vote=$user_vote&avg_vote=$avg_vote&num_votes=$num_votes"; } /** * Call this from a node to display a voting control */ function voting_control_node(&$node, $teaser = false, $page = false) { // called by node-based modules (or the nodeapi's view function in this file) if (user_access('vote on content') || user_access('show average without voting')) { if (($node->voting) && ($page)) { return theme('voting_control_flash', 'node', $node->nid); } } } /** * Call this from anywhere to display a voting control */ function voting_control_generic($content_type, $content_id, $attributes = array()) { // called by non-node types of content if (user_access('vote on content') || user_access('show average without voting')) { return theme('voting_control_flash', $content_type, $content_id, $attributes); } } /** * Display a voting control in Flash */ function theme_voting_control_flash($content_type, $content_id, $attributes = array()) { global $user; // optional user_access() dependent variables if (user_access('vote on content') && user_access('show average without voting')) { $mode = 'show_avg_first'; } elseif (user_access('show average without voting')) { $mode = 'show_avg_only'; } // optional variables set from administer/settings/voting $star_on_fill = variable_get('voting_on_fill', '0x990000'); $star_on_border = variable_get('voting_on_border', '0x330000'); $star_off_fill = variable_get('voting_off_fill', '0xcccccc'); $star_off_border = variable_get('voting_off_border', '0x333333'); $txt_color = variable_get('voting_txt_color', '0x000000'); $txt_vote1 = variable_get('voting_txt_vote1', 'Awful'); $txt_vote2 = variable_get('voting_txt_vote2', 'Poor'); $txt_vote3 = variable_get('voting_txt_vote3', 'Average'); $txt_vote4 = variable_get('voting_txt_vote4', 'Good'); $txt_vote5 = variable_get('voting_txt_vote5', 'Excellent'); $txt_login = urlencode(variable_get('voting_txt_login', 'Please login or register to vote.')); // optional: certain variables can be overridden on individual voting controls // ex: $output .= voting_control_generic('pagevote', $node->nid, array('prompt' => 'Do you like it?', 'confirmation' => 'Vote saved!', 'bgcolor' => '0xffffff')); $txt_before_vote = urlencode(($attributes['prompt']) ? $attributes['prompt'] : variable_get('voting_txt_before_vote', 'Rate This:')); $txt_after_vote = urlencode(($attributes['confirmation']) ? $attributes['confirmation'] : variable_get('voting_txt_after_vote', 'My Vote:')); $bgcolor = ($attributes['bgcolor']) ? $attributes['bgcolor'] : variable_get('voting_bgcolor', '0xffffff'); // make optional variables into a really long querystring $optional_vars = "&mode=$mode&bgcolor=$bgcolor&star_on_fill=$star_on_fill&star_on_border=$star_on_border"; $optional_vars .= "&star_off_fill=$star_off_fill&star_off_border=$star_off_border&txt_color=$txt_color"; $optional_vars .= "&txt_vote1=$txt_vote1&txt_vote2=$txt_vote2&txt_vote3=$txt_vote3&txt_vote4=$txt_vote4&txt_vote5=$txt_vote5"; $optional_vars .= "&txt_login=$txt_login&txt_before_vote=$txt_before_vote&txt_after_vote=$txt_after_vote"; // create the Flash filename, including the required variables and optional querystring $filename = base_path() . drupal_get_path('module', 'voting') . "/voting.swf?content_type=$content_type&content_id=$content_id&base_url=" . base_path() . $optional_vars; // create the HTML to put the Flash object on the page // // NOTE: The object is nested in a table for CSS styling. It does // not seem to work correctly in Firefox when a DIV is used instead. $output .= "
\n"; $output .= '' . "\n"; $output .= '' . "\n"; $output .= '' . "\n"; $output .= '' . "\n"; $output .= "\n"; $output .= "
\n"; return $output; } /** * Implementation of hook_nodeapi() */ function voting_nodeapi(&$node, $op, $teaser, $page) { switch ($op) { case 'view': if ($node->voting) { // if voting is enabled for this node and this view (votes don't show on teasers) switch (variable_get('voting_location', 1)) { case 0: // display above the node $node->body = voting_control_node($node, $teaser, $page) . $node->body; break; case 1: // display below the node $node->body .= voting_control_node($node, $teaser, $page); break; case 2: // do not display } } break; case 'load': $result = db_query("SELECT voting FROM {node_voting} WHERE nid = %d", $node->nid); if ($voting = db_fetch_object($result)) { $node->voting = $voting->voting; } else { $node->voting = variable_get("voting_nodeapi_{$node->type}", 'never'); } if ($teaser && !variable_get('voting_show_in_teaser', 0)) { $node->voting = 0; } break; case 'validate': if (!user_access('administer nodes')) { // Force default (for this content type) for normal users: $node->voting = variable_get('voting_' . $node->type, 0); } voting_filter_validate($node); break; case 'update': $result = db_query("SELECT * FROM {node_voting} WHERE nid = %d", $node->nid); if ($row = db_fetch_object($result)) { // if the voting on/off setting is already in the database db_query("UPDATE {node_voting} SET voting=%d WHERE nid = %d", $node->voting, $node->nid); } else { // or else add this setting to the database db_query("INSERT INTO {node_voting} (nid, voting) VALUES (%d, %d)", $node->nid, $node->voting); } break; case 'insert': db_query("INSERT INTO {node_voting} (nid, voting) VALUES (%d, %d)", $node->nid, $node->voting); break; case 'delete': db_query('DELETE FROM {node_voting} WHERE nid = %d', $node->nid); //db_query("DELETE FROM {votes} WHERE content_type='node' AND content_id = %d", $node->nid); break; } } /** * @defgroup event_nodeapi Functions for nodeapi integration */ function voting_form_alter($form_id, &$form) { global $user; $type = (isset($form['type']) && isset($form['type']['#value'])) ? $form['type']['#value'] : NULL; $node = isset($form['#node']) ? $form['#node'] : NULL; switch ($form_id) { // node settings form case $type .'_node_settings': $form['workflow']['voting_nodeapi_'. $type] = array( '#type' => 'radios', '#title' => t('Show voting control'), '#default_value' => variable_get('voting_nodeapi_'. $type, 'never'), '#options' => array('always' => t('Always'), 'usually' => t('Usually'), 'sometimes' => 'Sometimes', 'never' => t('Never')), '#description' => t('Always: A voting control will be displayed with all nodes of this types (including existing nodes).
Usually: Voting controls will usually be shown with this node type, but a setting in the node edit form will allow voting administrators to disable some voting controls.
Sometimes: Voting control will be disabed by default, but a setting in the node edit form will allow voting administrators to enable some voting controls.
Never: Voting controls will never be shown with this node type.') ); break; // node edit form case $type .'_node_form': switch (variable_get('voting_nodeapi_' . $type, 'never')) { case 'always': break; // don't display an option if voting is set to 'always' for this content type case 'never': break; // don't display an option if voting is set to 'never' for this content type case 'usually': $default = 1; case 'sometimes': $default = 0; $selected = isset($node->voting) ? $node->voting : $default; $form['voting'] = array( '#type' => 'fieldset', '#title' => t('Voting'), '#collapsible' => true, '#collapsed' => true, ); $form['voting']['voting'] = array( '#type' => 'radios', '#title' => 'Voting', '#default_value' => $selected, '#options' => array(0 => t('Disabled'), 1 => t('Enabled')), ); } break; } } /** --------------------------------------------------------------------------------------------------- * Voting Filter * * This section implements the hook_filter() and hook_filter_tips() to allow uses to add voting controls * to their posts. The format is [voting|type=artwork|prompt=Rate my artwork:|confirmation=Thanks for voting!] * The voting filter works great with the tinyMCE WYSIWYG editor and the drupalvoting plugin. * ---------------------------------------------------------------------------------------------------- */ /** * Implementation of hook_filter(). */ function voting_filter($op, $delta = 0, $format = -1, $text = '') { switch ($op) { case 'list': return array(0 => t('Inline voting')); case 'description': return t('Add voting to your posts.'); case 'process': // get all instances of the filter and send each one to the voting_filter_process() function $regex_tags = "/\[voting\|([^\]]+)\]/"; $text = preg_replace_callback($regex_tags, "voting_filter_process", $text); return $text; default: return $text; } } /** * Implementation of hook_filter_tips(). */ function voting_filter_tips($delta, $format, $long = false) { switch ($long) { case 0: return t('Voting controls can be added to this post.'); case 1: return t('

Adding Voting Controls

You can let people vote on your post, or things in your post, just by adding a carefully formatted tag in your post. The tag should look like this:
[voting|type=artwork|prompt=Rate my artwork:|confirmation=Thanks for voting!] The prompt and confirmation are optional.

'); } } /** * Called by voting_filter for each instance of the voting filter on a page * $match[0] is the full filter tag, to be replaced with the voting control * $match[1] is the attributes in the form "type=node|id=123|prompt=Rate This:|confirmation=My Vote:" */ function voting_filter_process($match) { $regex_attributes = "/([a-zA-Z]*)=([^\|]+)/"; preg_match_all($regex_attributes, $match[1], $attributes, PREG_SET_ORDER); foreach ($attributes as $attribute) { $atts[$attribute[1]] = $attribute[2]; } return voting_control_generic($atts['type'], $atts['id'], $atts); } /** * Called by voting_nodeapi, this function adds an ID (the nid) to the voting filter if it has been left off */ function voting_filter_validate(&$node) { // get all instances of the voting filter $regex_tags = "/\[voting\|([^\]]+)\]/"; preg_match_all($regex_tags, $node->body, $matches, PREG_SET_ORDER); foreach ($matches as $match) { $orig_filter_str = $match[0]; if (!preg_match("/id=[0-9]+/", $orig_filter_str)) { if ($node->nid) { $voting_nid = $node->nid; } else { // on a new post the NID hasn't been assigned yet and we can't call // db_next_id() yet becuase it will mess up the sequence for the node_save(). // my current solution is to use two modified lines from db_next_id() to get the // next nid without incrementing it. $name = db_prefix_tables('{node}_nid'); $voting_nid = db_result(db_query("SELECT id FROM {sequences} WHERE name = '%s'", $name)) + 1; } $new_filter_str = str_replace('[voting', '[voting|id=' . $voting_nid, $orig_filter_str); $node->body = str_replace($orig_filter_str, $new_filter_str, $node->body); } } } ?>