home | career | drupal | java | mac | mysql | perl | php | scala | uml | unix

Drupal example source code file (fivestar.module)

This example Drupal source code file (fivestar.module) is included in the DevDaily.com "Drupal Source Code Warehouse" project. The intent of this project is to help you "Learn Drupal by Example".

PHP - Drupal tags/keywords

array, element, function, if, isset, null, output, php, return, stars, tag, type, value, variable

The fivestar.module Drupal example source code

<?php

/**
 * @file
 * A simple n-star voting widget, usable in other forms.
 */

include_once dirname(__FILE__) . '/includes/fivestar.field.inc';

/**
 * Implementation of hook_help().
 */
function fivestar_help($path, $arg) {
  $output = '';
  switch ($path) {
    case 'admin/config/content/fivestar':
      $output = t('This page is used to configure site-wide features of the Fivestar module. To setup Fivestar to rate content:');
      $steps = array(
        t('Configure site-wide settings for Fivestar below.'),
        t('Go to <a href="!types">admin/structure/types</a> and edit the type you would like to rate.', array('!types' => url('admin/structure/types'))),
        t('On the settings page for the content type, a set of options is available for fivestar, where you can enable rating for that type and set rating options.'),
      );
      $output .= theme('item_list', array('items' => $steps, 'type' => 'ol'));
      break;
    case 'admin/content/node-type/'. $arg[3] .'/fivestar':
      $arg[5] = 'vote';
    case 'admin/content/node-type/'. $arg[3] .'/fivestar/'. $arg[5]:
      $output = t('Use the settings on this page to set up Fivestar rating for the %type content type. These settings specifically affect the %axis rating axis. If needing to set up different criteria for voting, see the main <a href="@url">Fivestar settings page</a>.', array('%type' => node_get_types('name', $arg[3]), '%axis' => $arg[5], '@url' => url('admin/config/content/fivestar')));
      break;
  }
  return $output;
}

/**
 * Implementation of hook_menu().
 */
function fivestar_menu() {
  $items = array();
  $items['admin/config/content/fivestar'] = array(
    'title'             => 'Fivestar',
    'description'       => 'Configure site-wide widgets used for Fivestar rating.',
    'page callback'     => 'drupal_get_form',
    'page arguments'    => array('fivestar_settings'),
    'access callback'   => 'user_access',
    'access arguments'  => array('administer site configuration'),
    'type'              => MENU_NORMAL_ITEM,
    'file'              => 'includes/fivestar.admin.inc',
  );
  $items['fivestar/preview/node'] = array(
    'page callback'     => 'fivestar_preview',
    'access callback'   => 'user_access',
    'access arguments'  => array('administer content types'),
    'type'              => MENU_CALLBACK,
    'file'              => 'includes/fivestar.admin.inc',
  );
  $items['fivestar/preview/color'] = array(
    'page callback'     => 'fivestar_preview_color',
    'access callback'   => 'user_access',
    'access arguments'  => array('administer site configuration'),
    'type'              => MENU_CALLBACK,
    'file'              => 'includes/fivestar.color.inc',
  );
  $items['fivestar/vote'] = array(
    'page callback'     => 'fivestar_vote',
    'access callback'   => 'user_access',
    'access arguments'  => array('rate content'),
    'type'              => MENU_CALLBACK,
  );

  // Add a "fivestar" tab to each content type.
  // We can't yet add it to the "operations" column in content types, due to a TODO in CCK
  // (content.admin.inc line 32)

  $items['admin/structure/types/manage/%node_type/fivestar'] = array(
    'title' => 'Fivestar voting',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('fivestar_node_type_tag_form', 4),
    'access arguments' => array('administer content types'),
    'type' => MENU_LOCAL_TASK,
    'weight' => 5,
    'file' => 'includes/fivestar.admin.inc',
  );
  foreach (fivestar_get_tags() as $tag) {
    $items['admin/structure/types/manage/%node_type/fivestar/' . urlencode($tag)] = array(
      'title' => $tag,
      'page callback' => 'drupal_get_form',
      'page arguments' => array('fivestar_node_type_tag_form', 4, $tag),
      'access arguments' => array('administer content types'),
      'type' => $tag == 'vote' ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK,
      'weight' => $tag == 'vote' ? 0 : 1,
      'file' => 'includes/fivestar.admin.inc',
    );
  }

  return $items;
}

/**
 * Implementation of hook_init().
 *
 * These includes do not need to be loaded for cached pages.
 */
function fivestar_init() {
  module_load_include('inc', 'fivestar', 'includes/fivestar.field');

  // Add necessary CSS and JS.
  // TODO: These shouldn't be loaded on every page, but block caching omits
  // CSS and JS files that would be otherwise added.
  fivestar_add_js();
  fivestar_add_css();
}

/**
 * Implementation of hook_permission().
 *
 * Exposes permissions for rating content, viewing aggregate ratings, and using PHP
 * snippets when configuring fivestar CCK fields.
 */
function fivestar_permission() {
  return array(
    'rate content' => array(
      'title' => t('rate content'),
      'description' => 'TODO: write description for rate content',
    ),
    'use PHP for fivestar target' => array(
      'title' => t('use PHP for fivestar target'),
      'description' => 'TODO: write description for use PHP for fivestar target',
    ),
  );
}

/**
 * Implementation of hook_theme().
 */
function fivestar_theme() {
  return array(
    // Fivestar theme functions.
    'fivestar' => array(
      'render element' => 'element',
    ),
    'fivestar_select' => array(
      'render element' => 'element',
    ),
    'fivestar_static' => array(
      'variables' => array('rating' => NULL, 'stars' => 5, 'tag' => 'vote'),
    ),
    'fivestar_static_element' => array(
      'variables' => array('star_display' => NULL, 'title' => NULL, 'description' => NULL),
    ),
    'fivestar_summary' => array(
      'variables' => array('user_rating' => NULL, 'average_rating' => NULL, 'votes' => 0, 'stars' => 5, 'feedback_enable' => TRUE),
    ),
    'fivestar_widget' => array(
      'render element' => 'form',
    ),
    // fivestar.admin.inc.
    'fivestar_preview' => array(
      'variables' => array('style' => NULL, 'text' => NULL, 'stars' => NULL, 'unvote' => NULL, 'title' => NULL, 'feedback_enable' => TRUE, 'labels_enable' => TRUE, 'labels' => array()),
      'file' => 'includes/fivestar.admin.inc',
    ),
    'fivestar_preview_widget' => array(
      'variables' => array('css_file' => NULL),
      'file' => 'includes/fivestar.admin.inc',
    ),
    'fivestar_preview_wrapper' => array(
      'variables' => array('content' => NULL, 'type' => 'direct'),
      'file' => 'includes/fivestar.admin.inc',
    ),
    'fivestar_settings' => array(
      'render element' => 'form',
      'file' => 'includes/fivestar.admin.inc',
    ),
    'fivestar_node_type_tag_form' => array(
      'render element' => 'form',
      'file' => 'includes/fivestar.admin.inc',
    ),
    // fivestar.color.inc.
    'fivestar_color_form' => array(
      'render element' => 'form',
    ),
    // fivestar.field.inc.
    'fivestar_formatter_default' => array(
      'render element' => 'element',
    ),
    'fivestar_formatter_rating' => array(
      'render element' => 'element',
    ),
    'fivestar_formatter_percentage' => array(
      'render element' => 'element',
    ),
  );
}

/**
 * Implementation of hook_node_type_delete().
 */
function fivestar_node_type_delete($info) {
  $type = $info->type;

  // Be responsible and cleanup unneeded variables.
  foreach (_fivestar_variables() as $variable) {
    foreach (fivestar_get_tags() as $tag) {
      $suffix = fivestar_get_suffix($type, $tag);
      variable_del($variable . '_' . $suffix);
    }
  }
}

/**
 * Implementation of hook_node_type_update().
 */
function fivestar_node_type_update($info) {
  // When changing the type name, update the variables.
  if (!empty($info->old_type) && $info->old_type != $info->type) {
    foreach (_fivestar_variables() as $variable) {
      foreach (fivestar_get_tags() as $tag) {
        $oldvarname = $variable . '_' . fivestar_get_suffix($info->old_type, $tag);
        $newvarname = $variable . '_' . fivestar_get_suffix($info->type, $tag);
        $value = variable_get($oldvarname, -1);
        if ($value != -1) {
          variable_del($oldvarname);
          variable_set($newvarname, $value);
        }
      }
    }
  }
}

function _fivestar_variables() {
  return array('fivestar', 'fivestar_unvote', 'fivestar_style', 'fivestar_stars', 'fivestar_comment', 'fivestar_position', 'fivestar_position_teaser', 'fivestar_labels_enable', 'fivestar_labels', 'fivestar_text', 'fivestar_title', 'fivestar_feedback');
}

/**
 * Callback function for fivestar/vote.
 *
 * @param type
 *   A content-type to log the vote to. 'node' is the most common.
 * @param cid
 *   A content id to log the vote to. This would be a node ID, a comment ID, etc.
 * @param tag
 *   Multi-axis tag to allow multiple votes per node. 'vote' is the most common.
 * @param value
 *   A value from 1-100, representing the vote cast for the content.
 * @return
 *  An XML chunk containing the results of the vote, for use by the client-side
 *  javascript code.
 */
function fivestar_vote($type, $cid, $tag, $value) {
  drupal_add_http_header("Content-Type", "text/xml");
  $output = '';
  $output .= '<?xml version="1.0" encoding="UTF-8"?>';

  // Rebuild the #auto_submit_path that was used as the token seed.
  $path = preg_replace('/\/'. $value .'$/', '', $_GET['q']);
  if (!isset($_GET['token']) || !fivestar_check_token($_GET['token'], $path)) {
    $output .= '<xml><error>'. t('Invalid token') .'</error></xml>';
    echo $output;
    drupal_exit();
  }

  $result = _fivestar_cast_vote($type, $cid, $value, $tag, NULL, TRUE);
  votingapi_recalculate_results($type, $cid);

  if ($type == 'node') {
    $node = node_load($cid);
  }

  $suffix = fivestar_get_suffix((!isset($node) ? 'default' : $node->type), $tag);

  $stars = variable_get('fivestar_stars_' . $suffix, 5);
  $feedback_enable = variable_get('fivestar_feedback_' . $suffix, 1);

  $output .= '<xml><result>';

  if (count($result)) {
    foreach ($result as $data) {
      if (isset($data['tag']) && $data['tag'] == $tag) {
        $output .= '<'. $data['function'] .'>'. $data['value'] .'</'. $data['function'] .'>';
        $summary[$data['tag']][$data['function']] = $data['value'];
      }
    }
  }
  $arguments = array(
    'user_rating' => $value,
    'average_rating' => $summary[$tag]['average'],
    'votes' => $summary[$tag]['count'],
    'stars' => $stars,
    'feedback_enable' => $feedback_enable,
  );
  $skip_map = array(
    'average' => array('user_rating', 'votes'),
    'average_count' => array('user_rating'),
    'user' => array('average_rating', 'votes'),
    'user_count' => array('average_rating'),
    'combo' => array(),
    'count' => array('user_rating', 'average_rating'),
  );
  $output .= '<summary>';
  foreach ($skip_map as $tag => $skip) {
    $output .= "<$tag>![CDATA[". theme('fivestar_summary', array_diff_key($arguments, array_flip($skip))) . "]]></$tag>";
  }
  $output .= '</summary>';
  $output .= '</result>';

  $output .= '<vote>';
  $output .= '<value>'. $value .'</value>';
  $output .= '<type>'. $type .'</type>';
  $output .= '<id>'. $cid .'</id>';
  $output .= '<tag>'. $tag .'</tag>';
  $output .= '</vote></xml>';

  drupal_add_http_header("Content-Type", "text/xml");
  echo $output;
  drupal_exit();
}

/**
 * Internal function to handle vote casting, flood control, XSS, IP based
 * voting, etc...
 */
function _fivestar_cast_vote($type, $cid, $value, $tag = NULL, $uid = NULL, $result = FALSE, $skip_validation = FALSE) {
  global $user;
  $tag = empty($tag) ? 'vote' : $tag;
  $uid = empty($uid) ? $user->uid : $uid;

  // Bail out if the user's trying to vote on an invalid object.
  if (!$skip_validation && !fivestar_validate_target($type, $cid, $tag, $uid)) {
    return array();
  }

  // Sanity-check the incoming values.
  if (is_numeric($cid) && is_numeric($value)) {
    if ($value > 100) {
      $value = 100;
    }

    // Get the user's current vote.
    $criteria = array('entity_type' => $type, 'entity_id' => $cid, 'tag' => $tag, 'uid' => $uid);
    // Get the unique identifier for the user (IP Address if anonymous).
    $user_criteria = votingapi_current_user_identifier();
    $user_votes = votingapi_select_votes($criteria + $user_criteria);

    if ($value == 0) {
      votingapi_delete_votes($user_votes);
    }
    else {
      $votes = $criteria += array('value' => $value);
      votingapi_set_votes($votes, $user_votes);
    }
    return fivestar_get_votes($type, $cid, $tag, $uid);
  }
}

/**
 * Utility function to retreive VotingAPI votes.
 *
 * Note that this should not be used for general vote retreival, instead the
 * VotingAPI function votingapi_select_results() should be used, which is more
 * efficient when retrieving multiple votes.
 *
 * @param $type
 *   The content type for which to retreive votes.
 * @param $cid
 *   The content ID for which to retreive votes.
 * @param $tag
 *   The VotingAPI tag for which to retreive votes.
 * @param $uid
 *   Optional. A user ID for which to retreive votes.
 * @return
 *   An array of the following keys:
 *   - average: An array of VotingAPI results, including the average 'value'.
 *   - count: An array of VotingAPI results, including the count 'value'.
 *   - user: An array of VotingAPI results, including the user's vote 'value'.
 */
function fivestar_get_votes($type, $cid, $tag = 'vote', $uid = NULL) {
  global $user;

  if (!isset($uid)) {
    $uid = $user->uid;
  }

  $criteria = array(
    'entity_type' => $type,
    'entity_id' => $cid,
    'value_type' => 'percent',
    'tag' => $tag,
  );

  $votes = array(
    'average' => array(),
    'count' => array(),
    'user' => array(),
  );

  $results = votingapi_select_results($criteria);
  foreach ($results as $result) {
    if ($result['function'] == 'average') {
      $votes['average'] = $result;
    }
    if ($result['function'] == 'count') {
      $votes['count'] = $result;
    }
  }
  if ($uid) {
    $user_vote = votingapi_select_votes($criteria += array('uid' => $uid));
    if ($user_vote) {
      $votes['user'] = $user_vote[0];
      $votes['user']['function'] = 'user';
    }
  }
  else {
    // If the user is anonymous, we never bother loading their existing votes.
    // Not only would it be hit-or-miss, it would break page caching. Safer to always
    // show the 'fresh' version to anon users.
    $votes['user'] = array('value' => 0);
  }

  return $votes;
}

/**
 * Check that an item being voted upon is a valid vote.
 *
 * @param $type
 *   Type of target (currently only node is supported).
 * @param $id
 *   Identifier within the type (in this case nid).
 * @param $tag
 *   The VotingAPI tag string.
 * @param $uid
 *   The user trying to cast the vote.
 *
 * @return boolean
 */
function fivestar_validate_target($type, $id, $tag, $uid = NULL) {
  if (!isset($uid)) {
    $uid = $GLOBALS['user']->uid;
  }

  $access = module_invoke_all('fivestar_access', $type, $id, $tag, $uid);
  foreach ($access as $result) {
    if ($result == TRUE) {
      return TRUE;
    }
    if ($result === FALSE) {
      return FALSE;
    }
  }
}

/**
 * Implementation of hook_fivestar_access().
 *
 * This hook is called before every vote is cast through Fivestar. It allows
 * modules to allow voting on any type of content, such as nodes, users, or
 * comments, even though only nodes are supported by Fivestar directly.
 *
 * @param $type
 *   Type of target (currently only node is supported).
 * @param $id
 *   Identifier within the type (in this case nid).
 * @param $tag
 *   The VotingAPI tag string.
 * @param $uid
 *   The user ID trying to cast the vote.
 *
 * @return boolean or NULL
 *   Returns TRUE if voting is supported on this object.
 *   Returns NULL if voting is not supported on this object by this module.
 *   If needing to absolutely deny all voting on this object, regardless
 *   of permissions defined in other modules, return FALSE. Note if all
 *   modules return NULL, stating no preference, then access will be denied.
 */
function fivestar_fivestar_access($type, $id, $tag, $uid) {
  if ($type == 'node' && $node = node_load($id)) {
    if (variable_get('fivestar_'. fivestar_get_suffix($node->type, $tag), 0)) {
      return TRUE;
    }
  }
}

/**
 * Implementation of hook_fivestar_widgets().
 *
 * This hook allows other modules to create additional custom widgets for
 * the fivestar module.
 *
 * @return array
 *   An array of key => value pairs suitable for inclusion as the #options in a
 *   select or radios form element. Each key must be the location of a css
 *   file for a fivestar widget. Each value should be the name of the widget.
 */
function fivestar_fivestar_widgets() {
  $widgets_directory = drupal_get_path('module', 'fivestar') .'/widgets';
  $files = file_scan_directory($widgets_directory, '/\.css$/');

  $widgets = array();
  foreach ($files as $file) {
    if (strpos($file->filename, '-rtl.css') === FALSE) {
      $widgets[$file->uri] = drupal_ucfirst(str_replace('-color', '', $file->name));
    }
  }
  return $widgets;
}

/**
 * Implementation of hook_node_view().
 *
 * Adds the fievestar widget to the node view.
 */
function fivestar_node_view($node, $view_mode) {
  $exclude_modes = array(
    'preview',
    'search_index',
    'search_result',
    'rss',
  );
  if (!in_array($view_mode, $exclude_modes) && !isset($node->modr8_form_teaser) && variable_get('fivestar_'. $node->type, 0)) {
    if ($view_mode == 'teaser') {
      $position = variable_get('fivestar_position_teaser_'. $node->type, 'above');
    }
    else {
      $position = variable_get('fivestar_position_'. $node->type, 'above');
    }

    switch ($position) {
      case 'above':
      case 'below':
        if (user_access('rate content')) {
          $content = '';
          foreach (fivestar_get_tags() as $tag) {
            if (fivestar_validate_target('node', $node->nid, $tag)) {
              $node->content['fivestar_widget_' . $tag]['form'] = fivestar_widget_form($node, $tag);
              $node->content['fivestar_widget_' . $tag]['#weight'] = ($position == 'above') ? -10 : 50;
            }
          }
          break;
        } // Fall through to static if not allowed to rate.
        $position .= '_static';
      case 'above_static':
      case 'below_static':
        $content = '';
        foreach (fivestar_get_tags() as $tag) {
          if (fivestar_validate_target('node', $node->nid, $tag)) {
            $content .= fivestar_static('node', $node->nid, $node->type, $tag);
          }
        }
        if ($content) {
          $node->content['fivestar_widget'] = array(
            '#markup' => $content,
            '#weight' => strpos($position, 'above') === 0 ? -10 : 50,
          );
        }
        
        break;
      case 'link':
        $node->content['links']['fivestar_widget'] = array(
          '#links' => array(
            'fivestar-rate' => array(
              'title' => t('Rate'),
              'href' => 'node/'. $node->nid,
              'fragment' => 'fivestar-form-node-'. $node->nid,
              'attributes' => array('title' => t('Rate this @type', array('@type' => _node_types_build()->types[$node->type]->name))),
            ),
          ),
        );
        break;
    }
  }
}

function fivestar_block_info() {
  $blocks[0]['info'] = t('Fivestar: Rate this node');
  return $blocks;
}

function fivestar_block_view($delta = 0) {
  if (user_access('access content') && user_access('rate content') && (arg(2) == '' || arg(2) == 'view') && ($node = menu_get_object())) {
    $block = array('subject' => t('Rate This'));
    foreach (fivestar_get_tags() as $tag) {
      if (fivestar_validate_target('node', $node->nid, $tag)) {
        $block['content'][$tag] = fivestar_widget_form($node, $tag);
      }
    }
    return $block;
  }
}

function fivestar_widget_form($node, $tag = 'vote') {
  return drupal_get_form('fivestar_form_node_'. $node->nid .'_'. $tag, 'node', $node->nid, $tag);
}

/**
 * Get a private token used to protect links from CSRF attacks.
 */
function fivestar_get_token($value) {
  global $user;

  // Anonymous users don't get a session ID, which breaks page caching.
  $session_id = $user->uid ? session_id() : '';
  $private_key = drupal_get_private_key();
  return md5($session_id . $value . $private_key);
}

/**
 * Check to see if a token value matches the specified node.
 */
function fivestar_check_token($token, $value) {
  return fivestar_get_token($value) == $token;
}

/**
 * Implementation of hook_forms().
 *
 * This is necessary when multiple fivestar forms appear on the same page, each
 * requiring a separate form_id, but all using the same underlying callbacks.
 */
function fivestar_forms($form_id, $args) {
  if (strpos($form_id, 'fivestar_form') !== FALSE) {
    if ($form_id == 'fivestar_form_'. $args[0] .'_'. $args[1] .'_'. $args[2]) {
      $forms[$form_id] = array('callback' => 'fivestar_form');
      return $forms;
    }
  }
}

/**
 * Create the fivestar form for the current item.
 * Note that this is not an implementation of hook_form(). We should probably
 * change the function to reflect that.
 */
function fivestar_form($form, &$form_state, $content_type, $content_id, $tag) {
  global $user;

  if ($content_type == 'node') {
    if (is_numeric($content_id)) {
      $node = node_load($content_id);
    }
    else {
      return array();
    }
  }

  $suffix = fivestar_get_suffix($node->type, $tag);
  $star_display = variable_get('fivestar_style_' . $suffix, 'average');
  $text_display = variable_get('fivestar_text_' . $suffix, 'dual');

  if ($star_display == 'average' && ($text_display == 'average' || $text_display == 'none')) {
    // Save a query and don't retrieve the user vote unnecessarily.
    $votes = fivestar_get_votes($content_type, $content_id, $tag, 0);
  }
  else {
    $votes = fivestar_get_votes($content_type, $content_id, $tag);
  }

  $values = array(
    'user' => isset($votes['user']['value']) ? $votes['user']['value'] : 0,
    'average' => isset($votes['average']['value']) ? $votes['average']['value'] : 0,
    'count' => isset($votes['count']['value']) ? $votes['count']['value'] : 0,
  );

  $settings = array(
    'stars' => variable_get('fivestar_stars_' . $suffix, 5),
    'allow_clear' => variable_get('fivestar_unvote_' . $suffix, FALSE),
    'style' => $star_display,
    'text' => $text_display,
    'content_type' => $content_type,
    'content_id' => $content_id,
    'tag' => 'vote',
    'autosubmit' => TRUE,
    'title' => variable_get('fivestar_title_' . $suffix, 1) ? NULL : FALSE,
    'feedback_enable' => variable_get('fivestar_feedback_' . $suffix, 1),
    'labels_enable' => variable_get('fivestar_labels_enable_' . $suffix, 1),
    'labels' => variable_get('fivestar_labels_' . $suffix, array()),
    'tag' => $tag,
  );

  return fivestar_custom_widget(array(), $form_state, $values, $settings);
}

/**
 * Retreive and print out a static display of stars for a piece of content.
 *
 * @param $content_type
 *   The type of content that will have its vote retreived. i.e. "node".
 * @param $content_id
 *   The ID of the content that will have its vote retreived.
 * @param $node_type
 *   Optional. If retreiving a node's rating, passing in the node type will
 *   prevent Fivestar from doing an additional query to find it.
 * @param $tag
 *   Optional. The voting tag that will be retreived. Defaults to "vote" if none
 *   is specified.
 */
function fivestar_static($content_type, $content_id, $node_type = NULL, $tag = 'vote') {
  global $user;

  $criteria = array(
    'content_type' => $content_type,
    'content_id' => $content_id,
    'value_type' => 'percent',
    'tag' => 'vote',
  );

  $votes = fivestar_get_votes($content_type, $content_id, $tag);

  if ($content_type == 'node') {
    // Content type should always be passed to avoid this node load.
    if (!isset($node_type)) {
      $node = node_load($content_id);
      $node_type = $node->type;
    }

    $settings = fivestar_get_settings($node_type, $tag);
    $stars = $settings['stars'];
    switch ($settings['star_display']) {
      case 'average':
      case 'dual':
        $star_value = (isset($votes['average']['value'])) ? $votes['average']['value']: '';
        $title = $settings['title_display'] ? t('Average') : NULL;
        break;
      case 'user':
        $star_value = (isset($votes['user']['value'])) ? $votes['user']['value'] : '';
        $title = $settings['title_display'] ? t('Your rating') : NULL;
        break;
      case 'smart':
        $star_value = (isset($votes['user']['value'])) ? $votes['user']['value'] : $votes['average']['value'];
        $title = $settings['title_display'] ? $votes['user']['value'] ? t('Your rating') : t('Average') : NULL;
        break;
    }

    if ($tag != 'vote') {
      $title .= ' (' . ucfirst($tag) . ')';
    }

    // Set all text values, then unset the unnecessary ones.
    $user_value = (isset($votes['user']['value'])) ? $votes['user']['value'] : NULL;
    $average_value = (isset($votes['average']['value'])) ? $votes['user']['value'] : NULL;
    $count_value = (isset($votes['count']['value'])) ? $votes['user']['value'] : NULL;
    switch ($settings['text_display']) {
      case 'average':
        $user_value = NULL;
        break;
      case 'user':
        $average_value = NULL;
        break;
      case 'smart':
        if ($votes['user']['value']) {
          $average_value = NULL;
        }
        else {
          $user_value = NULL;
        }
        break;
    }
  }
  // Possibly add other content types here (comment, user, etc).
  else {
    $stars = 5;
    $star_value = $votes['average']['value'];
    $user_value = $votes['user']['value'];
    $average_value = $votes['average']['value'];
    $count_value = $votes['count']['value'];
  }

  $star_display = theme('fivestar_static', array(
    'rating' => $star_value,
    'stars' => $stars,
  ));
  $text_display = $settings['text_display'] == 'none' ? NULL : theme('fivestar_summary', array(
    'user_rating' => $user_value,
    'average_rating' => $average_value,
    'votes' => $count_value,
    'stars'=> $stars,
    'feedback_enable' =>FALSE,
  ));
  return theme('fivestar_static_element', array('star_display' => $star_display, 'title' => $title, 'description' => $text_display));
}

/**
 * Form builder; Build a custom Fivestar rating widget with arbitrary settings.
 *
 * This function is usually not called directly, instead call
 * drupal_get_form('fivestar_custom_widget', $values, $settings) when wanting
 * to display a widget.
 *
 * @param $form_state
 *   The form state provided by Form API.
 * @param $values
 *   An array of current vote values from 0 to 100, with the following array
 *   keys:
 *   - user: The user's current vote.
 *   - average: The average vote value.
 *   - count: The total number of votes so far on this content.
 * @param $settings
 *   An array of settings that configure the properties of the rating widget.
 *   Available keys for the settings include:
 *   - content_type: The type of content which will be voted upon.
 *   - content_id: The content ID which will be voted upon.
 *   - stars: The number of stars to display in this widget, from 2 to 10.
 *     Defaults to 5.
 *   - autosubmit: Whether the form should be submitted upon star selection.
 *     Defaults to TRUE.
 *   - allow_clear: Whether or not to show the "Clear current vote" icon when
 *     showing the widget. Defaults to FALSE.
 *   - required: Whether this field is required before the form can be
 *     submitted. Defaults to FALSE.
 *   - feedback_enable: Toggles the option to show the "Vote is being saved"
 *     text while a vote is being registered through AJAX. Defaults to TRUE.
 *   - labels_enable: Toggles the option to show the "Give it 2/5 stars" text
 *     while hovering over the stars with the mouse.
 *   - labels: An array of labels to be used. The number of labels should match
 *     the number of stars.
 *   - tag: The VotingAPI tag that will be registered by this widget. Defaults
 *     to "vote".
 */
function fivestar_custom_widget($form, &$form_state, $values, $settings) {
  $form = array(
    '#attributes' => array('class' => array('fivestar-widget')),
    '#redirect' => FALSE,
    '#theme' => 'fivestar_widget',
  );
  $form['#submit'][] = 'fivestar_form_submit';

  if (isset($settings['content_type'])) {
    $form['content_type'] = array(
      '#type' => 'hidden',
      '#value' => $settings['content_type'],
    );
  }

  if (isset($settings['content_id'])) {
    $form['content_id'] = array(
      '#type' => 'hidden',
      '#value' => $settings['content_id'],
    );
  }

  if (isset($settings['tag'])) {
    $form['tag'] = array(
      '#type' => 'hidden',
      '#value' => $settings['tag'],
    );
  }

  $form['vote'] = array(
    '#type' => 'fivestar',
    '#stars' => $settings['stars'],
    '#vote_count' => $values['count'],
    '#vote_average' => $values['average'],
    '#auto_submit' => isset($settings['autosubmit']) ? $settings['autosubmit'] : TRUE,
    '#auto_submit_path' => (!isset($settings['autosubmit']) || $settings['autosubmit']) ? 'fivestar/vote/'. $settings['content_type'] .'/'. $settings['content_id'] .'/' . $settings['tag'] : NULL,
    '#allow_clear' => $settings['allow_clear'],
    '#content_id' => isset($settings['content_id']) ? $settings['content_id'] : NULL,
    '#required' => isset($settings['required']) ? $settings['required'] : FALSE,
    '#feedback_enable' => isset($settings['feedback_enable']) ? $settings['feedback_enable'] : TRUE,
    '#labels_enable' => isset($settings['labels_enable']) ? $settings['labels_enable'] : TRUE,
    '#labels' => isset($settings['labels']) ? $settings['labels'] : NULL,
    '#tag' => isset($settings['tag']) ? $settings['tag'] : 'vote',
  );

  $form['destination'] = array(
    '#type' => 'hidden',
    '#value' => $_GET['q'],
  );

  $form['fivestar_submit'] = array(
    '#type' => 'submit',
    '#value' => t('Rate'),
    '#attributes' => array('class' => array('fivestar-submit')),
  );

  $form['vote']['#attributes']['class'] = isset($form['vote']['#attributes']['class']) ? $form['vote']['#attributes']['class'] : array();
  $settings['feedback_enable'] = isset($settings['feedback_enable']) ? $settings['feedback_enable'] : TRUE;
  switch ($settings['text']) {
    case 'user':
      $form['vote']['#description'] = theme('fivestar_summary', array(
        'user_rating' => $values['user'],
        'votes' => $settings['style'] == 'dual' ? NULL : $values['count'],
        'stars' => $settings['stars'],
        'feedback_enable' => $settings['feedback_enable'],
      ));
      $form['vote']['#attributes']['class'][] = 'fivestar-user-text';
      break;
    case 'average':
      $form['vote']['#description'] = $settings['style'] == 'dual' ? NULL : theme('fivestar_summary', array(
        'average_rating' => $values['average'],
        'votes' => $values['count'],
        'stars' => $settings['stars'],
        'feedback_enable' => $settings['feedback_enable'],
      ));
      $form['vote']['#attributes']['class'][] = 'fivestar-average-text';
      break;
    case 'smart':
      $form['vote']['#description'] = ($settings['style'] == 'dual' && !$values['user']) ? NULL : theme('fivestar_summary', array(
        'user_rating' => $values['user'],
        'average_rating' => $values['user'] ? NULL : $values['average'],
        'votes' => $settings['style'] == 'dual' ? NULL : $values['count'],
        'stars' => $settings['stars'],
        'feedback_enable' => $settings['feedback_enable'],
      ));
      $form['vote']['#attributes']['class'][] = 'fivestar-smart-text';
      $form['vote']['#attributes']['class'][] = $values['user'] ? 'fivestar-user-text' : 'fivestar-average-text';
      break;
    case 'dual':
      $form['vote']['#description'] = theme('fivestar_summary', array(
        'user_rating' => $values['user'],
        'average_rating' => $settings['style'] == 'dual' ? NULL : $values['average'],
        'votes' => $settings['style'] == 'dual' ? NULL : $values['count'],
        'stars' => $settings['stars'],
        'feedback_enable' => $settings['feedback_enable'],
      ));
      $form['vote']['#attributes']['class'][] = ' fivestar-combo-text';
      break;
  }

  switch ($settings['style']) {
    case 'average':
      $form['vote']['#title'] = t('Average');
      $form['vote']['#default_value'] = $values['average'];
      $form['vote']['#attributes']['class'][] = 'fivestar-average-stars';
      break;
    case 'user':
      $form['vote']['#title'] = t('Your rating');
      $form['vote']['#default_value'] = $values['user'];
      $form['vote']['#attributes']['class'][] = 'fivestar-user-stars';
      break;
    case 'smart':
      $form['vote']['#title'] = $values['user'] ? t('Your rating') : t('Average');
      $form['vote']['#default_value'] = $values['user'] ? $values['user'] : $values['average'];
      $form['vote']['#attributes']['class'][] = 'fivestar-smart-stars '. ($values['user'] ? 'fivestar-user-stars' : 'fivestar-average-stars');
      break;
    case 'dual':
      $form['vote']['#title'] = t('Your rating');
      $form['vote']['#default_value'] = $values['user'];
      $form['vote']['#attributes']['class'][] = 'fivestar-combo-stars';
      $form['#attributes']['class'][] = 'fivestar-combo-stars';
      $static_average = theme('fivestar_static', array(
        'rating' => $values['average'],
        'stars' => $settings['stars'],
        'tag' => $settings['tag'],
      ));
      if ($settings['text'] == 'none' && !$settings['labels_enable'] && !$settings['feedback_enable']) {
        $static_description = NULL;
      }
      elseif ($settings['text'] != 'none') {
        $static_description = theme('fivestar_summary', array(
          'averrage_rating' => $settings['text'] == 'user' ? NULL : (isset($values['average']) ? $values['average'] : 0),
          'votes' => isset($values['count']) ? $values['count'] : 0,
          'stars' => $settings['stars'],
          'feedback_enable' => FALSE
        ));
      }
      else {
        $static_description = '&nbsp;';
      }
      $form['average'] = array(
        '#type' => 'markup',
        '#markup' => theme('fivestar_static_element', array(
          'star_display' => $static_average,
          'title' => $settings['title'] !== FALSE ? t('Average') : NULL,
          'description' => $static_description,
        )),
        '#weight' => -1,
      );
      break;
  }

  // Set an over-ridding title if passed in.
  // An empty title won't change the default, a string will set a new title,
  // and title === FALSE will unset the title entirely.
  if (isset($settings['title'])) {
    if ($settings['title'] !== FALSE) {
      $form['vote']['#title'] = $settings['title'];
    }
    else {
      unset($form['vote']['#title']);
      unset($form['average']['#title']);
    }
  }
  elseif ($settings['tag'] && $settings['tag'] != 'vote') {
    $form['vote']['#title'] .= ' (' . ucfirst($settings['tag']) . ')';
  }

  return $form;
}

/**
 * Submit handler for the above form (non-javascript version).
 */
function fivestar_form_submit($form, &$form_state) {
  if ($form_state['values']['form_id'] == 'fivestar_form_'. $form_state['values']['content_type'] .'_'. $form_state['values']['content_id'] . '_' . $form_state['values']['tag']) {
    // Cast the vote.
    _fivestar_cast_vote($form_state['values']['content_type'], $form_state['values']['content_id'], $form_state['values']['vote'], $form_state['values']['tag']);
    votingapi_recalculate_results($form_state['values']['content_type'], $form_state['values']['content_id']);

    // Set a message that the vote was received.
    if ($form_state['values']['vote'] === '0') {
      drupal_set_message(t('Your vote has been cleared.'));
    }
    elseif (is_numeric($form_state['values']['vote'])) {
      drupal_set_message(t('Thank you for your vote.'));
    }
    // Regenerate the page with a drupal_goto() to update the current values.
    drupal_goto();
  }
}

/**
 * Implementation of hook_elements().
 *
 * Defines 'fivestar' form element type
 */
function fivestar_element_info() {
  $type['fivestar'] = array(
    '#input' => TRUE,
    '#stars' => 5,
    '#widget' => 'stars',
    '#allow_clear' => FALSE,
    '#auto_submit' => FALSE,
    '#auto_submit_path' => '',
    '#labels_enable' => TRUE,
    '#feedback_enable' => TRUE,
    '#process' => array('fivestar_expand'),
  );
  return $type;
}

/**
 * Theme the fivestar form element by adding necessary css and javascript.
 */
function theme_fivestar($variables) {
  $element = $variables['element'];
  if (empty($element['#description'])) {
    if ($element['#feedback_enable']) {
      $element['#description'] = '<div class="fivestar-summary fivestar-feedback-enabled">&nbsp;</div>';
    }
    elseif ($element['#labels_enable']) {
      $element['#description'] = '<div class="fivestar-summary">&nbsp;</div>';
    }
  }

  return theme('form_element', array('element' => $element));
}

/**
 * Theme the straight HTML version of the fivestar select list. This is used
 * to remove the wrapping 'form-item' div from the select list.
 */
function theme_fivestar_select($variables) {
  $element = $variables['element'];
  element_set_attributes($element, array('id', 'name', 'size'));
  _form_set_class($element, array('form-select'));
  return '<select' . drupal_attributes($element['#attributes']) . '>' . form_select_options($element) . '</select>';
}
/**
 * Theme an entire fivestar widget, including the submit button and the normal
 * fivestar widget themed in the theme_fivestar() function.
 */
function theme_fivestar_widget($variables) {
  $form = $variables['form'];
  // Only print out the summary if text is being displayed or using rollover text.
  if (empty($form['vote']['#description']) && strpos($form['vote']['#prefix'], 'fivestar-labels-hover') === FALSE) {
    unset($form['vote']['#description']);
  }

  $class = 'fivestar-form';
  $class .= '-'. (isset($form['vote']['#tag']) ? $form['vote']['#tag'] : 'vote');
  $class .= '-'. (isset($form['content_id']['#value']) ? $form['content_id']['#value'] : 0);

  $output  = '';
  $output .= '<div class="'. $class .' clear-block">';
  $output .= drupal_render_children($form);
  $output .= '</div>';
  return $output;
}

/**
 * Display a plain HTML view-only version of the widget with a specified rating.
 *
 * @param $rating
 *   The desired rating to display out of 100 (i.e. 80 is 4 out of 5 stars).
 * @param $stars
 *   The total number of stars this rating is out of.
 * @param $tag
 *   Allows multiple ratings per node.
 * @return
 *   A themed HTML string representing the star widget.
 */
function theme_fivestar_static($variables) {
  $rating  = $variables['rating'];
  $stars = $variables['stars'];
  $tag = $variables['tag'];
  $output = '';
  $output .= '<div class="fivestar-widget-static fivestar-widget-static-'. $tag .' fivestar-widget-static-'. $stars .' clear-block">';
  if (empty($stars)) {
    $stars = 5;
  }
  $numeric_rating = $rating/(100/$stars);
  for ($n=1; $n <= $stars; $n++) {
    $star_value = ceil((100/$stars) * $n);
    $prev_star_value = ceil((100/$stars) * ($n-1));
    $zebra = ($n % 2 == 0) ? 'even' : 'odd';
    $first = $n == 1 ? ' star-first' : '';
    $last = $n == $stars ? ' star-last' : '';
    $output .= '<div class="star star-'. $n .' star-'. $zebra . $first . $last .'">';
    if ($rating < $star_value && $rating > $prev_star_value) {
      $percent = (($rating - $prev_star_value) / ($star_value - $prev_star_value)) * 100;
      $output .= '<span class="on" style="width: '. $percent .'%">';
    }
    elseif ($rating >= $star_value) {
      $output .= '<span class="on">';
    }
    else {
      $output .= '<span class="off">';
    }
    if ($n == 1)$output .= $numeric_rating;
    $output .= '</span></div>';
  }
  $output .= '</div>';
  return $output;
}

/**
 * Display the text associated with a static star display.
 *
 * Note that passing in explicit data types is extremely important when using
 * this function. A NULL value will exclude the value entirely from display,
 * while a 0 value indicates that the text should be shown but it has no value
 * yet.
 *
 * All ratings are from 0 to 100.
 *
 * @param $user_rating
 *   The current user's rating.
 * @param $average
 *   The average rating.
 * @param $votes
 *   The total number of votes.
 * @param $stars
 *   The number of stars being displayed.
 * @param $feedback
 *   A toggle that enables AJAX indicator message when a vote is being saved.
 * @return
 *   A themed HTML string representing the star widget.
 */
function theme_fivestar_summary($variables) {
  extract($variables, EXTR_SKIP);
  $output = '';
  $div_class = '';
  if (isset($user_rating)) {
    $div_class = isset($votes) ? 'user-count' : 'user';
    $user_stars = round(($user_rating * $stars) / 100, 1);
    $output .= '<span class="user-rating">'. t('Your rating: <span>!stars</span>', array('!stars' => $user_rating ? $user_stars : t('None'))) .'</span>';
  }
  if (isset($user_rating) && isset($average_rating)) {
    $output .= ' ';
  }
  if (isset($average_rating)) {
    $div_class = isset($votes) ? 'average-count' : 'average';
    $average_stars = round(($average_rating * $stars) / 100, 1);
    $output .= '<span class="average-rating">'. t('Average: <span>!stars</span>', array('!stars' => $average_stars)) .'</span>';
  }
  if (isset($user_rating) && isset($average_rating)) {
    $div_class = 'combo';
  }

  if (isset($votes) && !(isset($user_rating) || isset($average_rating))) {
    $output .= ' <span class="total-votes">'. format_plural($votes, '<span>@count</span> vote', '<span>@count</span> votes') .'</span>';
    $div_class = 'count';
  }
  elseif (isset($votes)) {
    $output .= ' <span class="total-votes">('. format_plural($votes, '<span>@count</span> vote', '<span>@count</span> votes') .')</span>';
  }

  if ($votes === 0) {
    $output = '<span class="empty">'. t('No votes yet') .'</span>';
  }

  $output = '<div class="fivestar-summary fivestar-summary-'. $div_class . ($feedback_enable ? ' fivestar-feedback-enabled' : '') .'">'. $output .'</div>';
  return $output;
}

/**
 * Display a static fivestar value as stars with a title and description.
 */
function theme_fivestar_static_element($variables) {
  $output = '';
  $output .= '<div class="fivestar-static-form-item">';
  $element = array(
    '#type' => 'item',
    '#title' => $variables['title'],
    '#description' => $variables['description'],
    '#children' => $variables['star_display'],
  );

  $output .= theme('form_element', array('element' => $element));
  $output .= '</div>';
  return $output;
}

/**
 * Fetch the necessary CSS files to render the fivestar widget.
 */
function fivestar_add_css($widget_css = NULL) {
  // Add fivestar CSS.
  drupal_add_css(drupal_get_path('module', 'fivestar') .'/css/fivestar.css');

  // Add widget specific CSS.
  if (!isset($widget_css)) {
    $widget_css = variable_get('fivestar_widget', 'default');
  }

  if ($widget_css != 'default') {
    drupal_add_css($widget_css);
  }
}

/**
 * Add necessary JS files and settings to render the fivestar widget.
 */
function fivestar_add_js() {
  static $js_added = FALSE;

  // Add necessary javascript only once per page.
  if (!$js_added) {
    $settings = array(
      'titleUser' => t('Your rating') .': ',
      'titleAverage' => t('Average') .': ',
      'feedbackSavingVote' => t('Saving your vote...'),
      'feedbackVoteSaved' => t('Your vote has been saved.'),
      'feedbackDeletingVote' => t('Deleting your vote...'),
      'feedbackVoteDeleted' => t('Your vote has been deleted.'),
    );

    drupal_add_js(drupal_get_path('module', 'fivestar') .'/js/fivestar.js');
    drupal_add_js(array('fivestar' => $settings), 'setting');
    $js_added = TRUE;
  }
}


/**
 * Add Inline CSS to the page, only used on admin/config/content/fivestar page.
 */
function fivestar_add_inline_css($widget_key = NULL, $css = NULL, $reset = FALSE) {
  static $inline_css;

  if (!isset($inline_css) || $reset) {
    $inline_css = array();
  }

  if (isset($widget_key) && isset($inline_css)) {
    $inline_css[$widget_key] = $css;
  }

  return $inline_css;
}

/**
 * Retrieve a list of all inline CSS to be added to the page.
 */
function fivestar_get_inline_css() {
  $inline_css = fivestar_add_inline_css();
  return implode("\n", $inline_css);
}

/**
 * Process callback for fivestar_element -- see fivestar_element()
 */
function fivestar_expand($element) {

  if (isset($element['#vote_count'])) {
    $element['vote_count'] =  array(
      '#type' => 'hidden',
      '#value' => $element['#vote_count'],
    );
  }

  if (isset($element['#vote_average'])) {
    $element['vote_average'] =  array(
      '#type' => 'hidden',
      '#value' => $element['#vote_average'],
    );
  }

  if ($element['#auto_submit'] && !empty($element['#auto_submit_path'])) {
    $element['auto_submit_path'] = array(
      '#type' => 'hidden',
      '#value' => url($element['#auto_submit_path']),
      '#attributes' => array('class' => 'fivestar-path'),
    );
    $element['auto_submit_token'] = array(
      '#type' => 'hidden',
      '#value' => fivestar_get_token($element['#auto_submit_path']),
      '#attributes' => array('class' => 'fivestar-token'),
    );
  }

  if (!isset($element['#default_value'])) {
    $element['#default_value'] = 0;
  }

  $options = array('-' => t('Select rating'));
  $default_value = $element['#default_value'];
  for ($i = 0; $i <= $element['#stars']; $i++) {
    $this_value = ceil($i * 100/$element['#stars']);
    $next_value = ceil(($i+1) * 100/$element['#stars']);

    // Display clear button only if enabled.
    if ($element['#allow_clear'] == TRUE && $i == 0) {
      $options[$this_value] = isset($element['#labels'][$i]) ? t(filter_xss_admin($element['#labels'][$i])) : t('Cancel rating');
    }
    // Display a normal star value.
    if ($i > 0) {
      if (isset($element['#labels'][$i])) {
        $options[$this_value] = $element['#labels'][$i] == '' ? $i : t(filter_xss_admin($element['#labels'][$i]), array('@star' => $i, '@count' => $element['#stars']));
      }
      else {
        $options[$this_value] = t('Give it @star/@count', array('@star' => $i, '@count' => $element['#stars']));
      }
    }
    // Round up the default value to the next exact star value if needed.
    if ($this_value < $element['#default_value'] && $next_value > $element['#default_value']) {
      $default_value = $next_value;
    }
  }

  $element['vote'] = array(
    '#type' => 'select',
    '#options' => $options,
    '#required' => $element['#required'],
    '#default_value' => $default_value,
    '#parents' => $element['#parents'],
    '#theme' => 'fivestar_select',
    '#weight' => $element['#weight'],
  );

  // If a default value is not exactly on a radio value, round up to the next one
  if ($element['#default_value'] > $this_value && $element['#default_value'] <= $next_value) {
    $element['vote']['#default_value'] = $next_value;
  }

  $class = isset($element['#attributes']['class']) ? $element['#attributes']['class'] : array();
  $class[] = 'fivestar-form-item';
  if ($element['#labels_enable']) {
    // Set a class for the display of label text on hover.
    $class[] = 'fivestar-labels-hover';
  }
  $element['#prefix'] = '<div ' . drupal_attributes(array('class' => $class)) . '>';
  $element['#suffix'] = '</div>';

  // Add validation function that considers a 0 value as empty.
  $element['#element_validate'] = array('fivestar_validate');

  return $element;
}

/**
 * An #element_validate function for "fivestar" elements.
 */
function fivestar_validate($element, &$form_state) {
  if ($element['#required'] && (empty($element['#value']) || $element['#value'] == '-')) {
    form_error($element, t('!name field is required.', array('!name' => $element['#title'])));
  }
}

/**
 * Implementation of hook_votingapi_views_formatters().
 */
function fivestar_votingapi_views_formatters($details = array()) {
  if ($details->field == 'value') {
    return array(
      'fivestar_views_value_display_handler' => t('Fivestar Stars (display only)'),
      'fivestar_views_value_text_handler' => t('Fivestar Stars (text star count)'),
      'fivestar_views_widget_compact_handler' => t('Fivestar Stars (clickable, no text)'),
      'fivestar_views_widget_normal_handler' => t('Fivestar Stars (clickable, with text)'),
    );
  }
}

/**
 * VotingAPI Views formatter for displaying static stars.
 */
function fivestar_views_value_display_handler($value, $field, $columns) {
  // Determine number of stars to display
  // TODO need to get the tag here to support >1 tag
  // For now we're only going to support one tag ("vote").
  // We'll add the multiple tag once the module is somewhat stable.
  $tag = 'vote';

  if ($field->view->base_table == 'node') {
    if (isset($columns->node_type)) {
      $stars = variable_get('fivestar_stars_'. $columns->node_type, 5);
    }
    else {
      $node_type = db_query("SELECT type FROM {node} WHERE nid = :nid", array(':nid' => $columns->nid))->fetchField();
      $stars = variable_get('fivestar_stars_'. (!isset($node_type) ? 'default' : $node_type), 5);
    }

    // Find the VotingAPI tag for this field.
    foreach ($field->query->table_queue[$field->relationship]['join']->extra as $votingapi_setting) {
      if ($votingapi_setting['field'] == 'tag') {
        $tag = $votingapi_setting['value'];
      }
    }
  }
  else {
    $stars = 5;
    $tag = 'vote';
  }

  return theme('fivestar_static', array(
    'rating' => $value,
    'stars' => $stars,
    'tag' => $tag,
  ));
}

/**
 * VotingAPI Views formatter for displaying number of stars as text.
 */
function fivestar_views_value_text_handler($value, $field, $columns) {
  // Get the number of stars for this node type.
  $node_type = isset($columns->node_type) ? $columns->node_type : db_query("SELECT type FROM {node} WHERE nid = :nid", array(':nid' => $columns->nid))->fetchField();
  $stars = variable_get('fivestar_stars_'. $node_type, 5);

  // If displaying a user's vote, always display a whole value.
  if ($field->table == 'votingapi_vote') {
    return ceil(($value / 100) * $stars);
  }
  else {
    if ($field->options['set_precision']) {
      return round(($value / 100) * $stars, $field->options['precision']);
    }
    return ($value / 100) * $stars;
  }
}

/**
 * VotingAPI Views formatter for displaying rating widget without text.
 */
function fivestar_views_widget_compact_handler($value, $field, $columns) {
  return fivestar_views_widget_handler($value, $field, $columns, FALSE);
}

/**
 * VotingAPI Views formatter for displaying rating widget with text.
 */
function fivestar_views_widget_normal_handler($value, $field, $columns) {
  return fivestar_views_widget_handler($value, $field, $columns, TRUE);
}

/**
 * Generic VotingAPI Views formatter for displaying rating widget.
 */
function fivestar_views_widget_handler($value, $field, $columns, $summary) {
  // For now we're only supporting one tag.
  $tag = 'vote';
  // If the user can't rate, use the display handler.
  if (!user_access('rate content')) {
    return fivestar_views_value_display_handler($value, $field, $columns);
  }

  if ($field->view->base_table == 'node') {

    // Find the VotingAPI tag for this field.
    foreach ($field->query->table_queue[$field->relationship]['join']->extra as $votingapi_setting) {
      if ($votingapi_setting['field'] == 'tag') {
        $tag = $votingapi_setting['value'];
      }
    }

    $content_type = 'node';
    $content_id = $columns->nid;
    $node_type = isset($columns->node_type) ? $columns->node_type : db_query("SELECT type FROM {node} WHERE nid = :nid", array(":nid" => $columns->nid))->fetchField();
    $values = array(
      'user' => 0,
      'average' => 0,
      'count' => 0,
    );

    if ($field->table == 'votingapi_vote') {
      $values['user'] = $value;
    }

    if ($field->table == 'votingapi_cache') {
      $values['average'] = $value;
    }

    // Only pull in all the votes if we need to display the summary text.
    if ($summary) {
      $votes = fivestar_get_votes($content_type, $content_id, $tag);
      if ($field->table != 'votingapi_vote') {
        $values['user'] = isset($votes['user']['value']) ? $votes['user']['value'] : 0;
      }
      if ($field->table != 'votingapi_cache') {
        $values['average'] = isset($votes['average']['value']) ? $votes['average']['value'] : 0;
      }
      $values['count'] = isset($votes['count']['value']) ? $votes['count']['value'] : 0;
    }

    $settings = array(
      'stars' => variable_get('fivestar_stars_'. $node_type, 5),
      'allow_clear' => variable_get('fivestar_unvote_'. $node_type, FALSE),
      'style' => $field->table == 'votingapi_vote' ? 'user' : 'average',
      'text' => $summary ? variable_get('fivestar_text_'. $node_type, 'dual') : 'none',
      'content_type' => $content_type,
      'content_id' => $content_id,
      'tag' => $tag,
      'autosubmit' => TRUE,
      'title' => FALSE,
      'feedback_enable' => $summary ? variable_get('fivestar_feedback_'. $node_type, 1) : FALSE,
      'labels_enable' => $summary ? variable_get('fivestar_labels_enable_'. $node_type, 1) : FALSE,
      'labels' => $summary ? variable_get('fivestar_labels_'. $node_type, array()) : array(),
    );

    return drupal_get_form('fivestar_custom_widget', $values, $settings);
  }
  else {
    return theme('fivestar_static', array(
      'rating' => $value,
      'stars' => 5,
    ));
  }
}

function fivestar_get_tags() {
  $tags_txt = variable_get('fivestar_tags', 'vote');
  $tags_exploded = explode(',', $tags_txt);

  $tags = array();
  $got_vote = false;
  foreach ($tags_exploded as $tag) {
    $tag_trimmed = trim($tag);
    if ($tag_trimmed) {
      $tags[] = $tag_trimmed;
      if ($tag_trimmed == 'vote') {
        $got_vote = true;
      }
    }
  }

  if (!$got_vote) {
    $tags[] = 'vote';
  }
  return $tags;
}

/**
 * Gets the variable suffix for node/tag-specific variables.
 *
 * Note that for backwards compatibility, the tag suffix is omitted for the tag
 * "vote". So instead of "story_vote", this just returns "story".
 */
function fivestar_get_suffix($node_type, $tag) {
  return $node_type . ($tag == 'vote' ? '' : '_' . $tag);
}

/**
 * Get all the settings set for a specific node type.
 */
function fivestar_get_settings($node_type, $tag) {
  $suffix = fivestar_get_suffix($node_type, $tag);

  $settings = array(
    'fivestar' => variable_get('fivestar_' . $suffix, 0),
    'stars' => variable_get('fivestar_stars_' . $suffix, 5),
    'unvote_enable' => variable_get('fivestar_unvote_' . $suffix, 0),
    'feedback_enable' => variable_get('fivestar_feedback_'. $suffix, 1),
    'labels_enable' => variable_get('fivestar_labels_enable_'. $suffix, 1),
    'labels' => variable_get('fivestar_labels_'. $suffix, array()),
    'star_display' => variable_get('fivestar_style_' . $suffix, 'average'),
    'text_display' => variable_get('fivestar_text_' . $suffix, 'dual'),
    'title_display' => variable_get('fivestar_title_' . $suffix, 1),
    'position' => variable_get('fivestar_position_' . $suffix, 'below'),
    'position_teaser' => variable_get('fivestar_position_teaser_' . $suffix, 'hidden'),
  );

  // Set default labels.
  if ($settings['stars'] == 5 && empty($settings['labels'])) {
    $settings['labels'] = array(t('Cancel rating'), t('Poor'), t('Okay'), t('Good'), t('Great'), t('Awesome'));
  }
  for ($n = 0; $n <= 10; $n++) {
    $settings['labels'][$n] = isset($settings['labels'][$n]) ? $settings['labels'][$n] : t('Give it @star/@count');
  }

  return $settings;
}


/**
 * Implementation of hook_votingapi_metadata_alter().
 */
function fivestar_votingapi_metadata_alter(&$data) {
  // TODO: This needs porting 
  // static $tags;
  // if (!isset($tags)) {
  //   $tags = array();
  // 
    // // Grab the list of fields to update.
    // foreach (content_types_install() as $type_name => $fields) {
    //   foreach ($fields as $field) {
    //     if ($field['type'] == 'fivestar') {
    //       if (!empty($field['axis'])) {
    //         $data['tags'][$field['axis']] = array(
    //           'name' => drupal_ucfirst($field['axis']),
    //           'description' => t('Used by %field in the %type content type.', array('%type' => $type_name, '%field' => $field['field_name'])),
    //           'module' => 'fivestar', // This is optional; we can add it for our own purposes.
    //         );
    //       }
    //     }
    //   }
    // }
  // }
  // return $tags;
}

Other Drupal examples (source code examples)

Here is a short list of links related to this Drupal fivestar.module source code file:

new blog posts

"Drupal" is a registered trademark of Dries Buytaert.

my drupal tutorials and examples  

Copyright 1998-2016 Alvin Alexander, alvinalexander.com
All Rights Reserved.

Beginning in 2016, a portion of the proceeds from pages under the '/drupal-code-examples/' URI will be donated to charity.