You are here

rate.module in Rate 6.2

Same filename and directory in other branches
  1. 8.2 rate.module
  2. 8 rate.module
  3. 7.2 rate.module
  4. 7 rate.module

Rate module

File

rate.module
View source
<?php

/**
 * @file
 * Rate module
 */

// Define display modes for the rate widget.
define('RATE_FULL', 1);
define('RATE_COMPACT', 2);
define('RATE_DISABLED', 3);
define('RATE_COMPACT_DISABLED', 5);
define('RATE_CLOSED', 4);

// Define display modes for node / comment bodies.
define('RATE_DISPLAY_DISABLE', 0);
define('RATE_DISPLAY_ABOVE_CONTENT', 1);
define('RATE_DISPLAY_BELOW_CONTENT', 2);
define('RATE_DISPLAY_LINKS', 3);

// Define modes for which rating to display
define('RATE_AVERAGE', 1);
define('RATE_USER', 2);
define('RATE_USER_OR_AVERAGE', 3);

// Define modes for behaviour with users w/o permission to vote.
define('RATE_NOPERM_REDIRECT_WITH_MESSAGE', 1);
define('RATE_NOPERM_REDIRECT_WITHOUT_MESSAGE', 2);
define('RATE_NOPERM_SHOW_DISABLED_WIDGET', 3);
define('RATE_NOPERM_HIDE_WIDGET', 4);

// Define constants for paths.
define('RATE_PATH_ADMIN_PAGE', 'admin/build/rate');
define('RATE_PATH_ADMIN_PAGE_LIST', 'admin/build/rate/list');
define('RATE_PATH_ADMIN_PAGE_ADD', 'admin/build/rate/add');
define('RATE_PATH_ADMIN_PAGE_EDIT', 'admin/build/rate/%/edit');
define('RATE_PATH_ADMIN_PAGE_DELETE', 'admin/build/rate/%/delete');
define('RATE_PATH_ADMIN_PAGE_TEMPLATE', 'admin/build/rate/%/template');
define('RATE_PATH_ADMIN_AHAH', 'rate/options/js');
define('RATE_PATH_AHAH', 'rate/vote/js');
define('RATE_PATH_RESULTS_PAGE', 'node/%node/rating');

// Define constants for variable names.
define('RATE_VAR_WIDGETS', 'rate_widgets');

// Define constants for permission status.
define('RATE_PERMISSION_OK', 1);
define('RATE_PERMISSION_DISALLOWED_ROLE', 2);
define('RATE_PERMISSION_DISALLOWED_AUTHOR', 3);

/**
 * Implements hook_perm().
 */
function rate_perm() {
  return array(
    'administer rate',
    'view rate results page',
  );
}

/**
 * Implements hook_menu().
 */
function rate_menu() {
  $menu = array();
  $menu[RATE_PATH_ADMIN_PAGE] = array(
    'title' => t('Rate widgets'),
    'description' => t('Manage rating widgets.'),
    'page callback' => 'rate_admin_page',
    'access arguments' => array(
      'administer rate',
    ),
    'type' => MENU_NORMAL_ITEM,
    'file' => 'rate.admin.inc',
  );
  $menu[RATE_PATH_ADMIN_PAGE_LIST] = array(
    'title' => t('List'),
    'page callback' => 'rate_admin_page',
    'access arguments' => array(
      'administer rate',
    ),
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'file' => 'rate.admin.inc',
    'weight' => 0,
  );
  $menu[RATE_PATH_ADMIN_PAGE_ADD] = array(
    'title' => t('Add widget'),
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'rate_widget_form',
    ),
    'access arguments' => array(
      'administer rate',
    ),
    'type' => MENU_CALLBACK,
    'file' => 'rate.admin.inc',
    'weight' => 1,
  );
  $menu[RATE_PATH_ADMIN_PAGE_EDIT] = array(
    'title' => t('Edit widget'),
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'rate_widget_form',
      3,
    ),
    'access arguments' => array(
      'administer rate',
    ),
    'type' => MENU_CALLBACK,
    'file' => 'rate.admin.inc',
  );
  $menu[RATE_PATH_ADMIN_PAGE_DELETE] = array(
    'title' => t('Delete widget'),
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'rate_widget_delete_form',
      3,
    ),
    'access arguments' => array(
      'administer rate',
    ),
    'type' => MENU_CALLBACK,
    'file' => 'rate.admin.inc',
  );
  $menu[RATE_PATH_ADMIN_PAGE_TEMPLATE] = array(
    'title' => t('Switch template'),
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'rate_choose_template_form',
      3,
    ),
    'access arguments' => array(
      'administer rate',
    ),
    'type' => MENU_CALLBACK,
    'file' => 'rate.admin.inc',
  );
  $menu[RATE_PATH_ADMIN_AHAH] = array(
    'page callback' => 'rate_widget_form_ahah',
    'access arguments' => array(
      'administer rate',
    ),
    'type' => MENU_CALLBACK,
    'file' => 'rate.admin.inc',
  );
  $menu[RATE_PATH_AHAH] = array(
    'page callback' => 'rate_vote_ahah',
    'access arguments' => array(
      'access content',
    ),
    'type' => MENU_CALLBACK,
  );
  $menu[RATE_PATH_RESULTS_PAGE] = array(
    'title' => t('Voting results'),
    'page callback' => 'rate_results_page',
    'page arguments' => array(
      1,
    ),
    'access callback' => 'rate_results_page_access',
    'access arguments' => array(
      'view rate results page',
      1,
    ),
    'type' => MENU_LOCAL_TASK,
    'weight' => 10,
    'file' => 'rate.results.inc',
  );
  return $menu;
}

/**
 * Get list of active widgets.
 *
 * @param string $content_type Node type
 * @param string $type "node" or "comment"
 * @return array
 */
function rate_get_active_widgets($content_type, $type, $teaser = FALSE) {
  $widgets = variable_get(RATE_VAR_WIDGETS, array());
  $active = array();
  foreach ($widgets as $widget_id => $widget) {
    if ($content_type == 'node' && in_array($type, $widget->node_types)) {
      isset($widget->teaser_display) or $widget->teaser_display = TRUE;
      if (!$teaser || $widget->teaser_display) {
        $active[$widget_id] = $widget;
      }
    }
    if ($content_type == 'comment' && in_array($type, $widget->comment_types)) {
      $active[$widget_id] = $widget;
    }
  }
  return $active;
}

/**
 * Generate a widget.
 *
 * @param int $widget_id Widget id
 * @param string $content_type "node" or "comment"
 * @param int $content_id Node id (nid) or comment id (cid)
 * @param bool $teaser
 * @param bool $include_div
 * @return array
 */
function rate_generate_widget($widget_id, $content_type, $content_id, $mode = RATE_FULL, $include_div = TRUE, $just_voted = FALSE, $displayed = NULL) {
  global $user;

  // Check input.
  if (!is_numeric($widget_id) || empty($content_type) || !is_numeric($content_id)) {
    return NULL;
  }
  $widgets = variable_get(RATE_VAR_WIDGETS, array());
  $widget = $widgets[$widget_id];
  _rate_check_widget($widget);

  // Determine if the user may vote.
  $node = $content_type == 'node' ? node_load($content_id) : NULL;
  $permission_status = _rate_check_permissions($widget, $node);

  // This option should be available, check for legacy.
  isset($widget->noperm_behaviour) or $widget->noperm_behaviour = RATE_NOPERM_REDIRECT_WITH_MESSAGE;
  if ($permission_status != RATE_PERMISSION_OK && $widget->noperm_behaviour == RATE_NOPERM_HIDE_WIDGET) {
    return NULL;
  }
  elseif ($permission_status == RATE_PERMISSION_DISALLOWED_ROLE && $widget->noperm_behaviour == RATE_NOPERM_SHOW_DISABLED_WIDGET) {
    $mode = RATE_DISABLED;
  }
  elseif ($permission_status == RATE_PERMISSION_DISALLOWED_AUTHOR) {
    $mode = RATE_DISABLED;
  }

  // Store the display mode in $widget, so it can be altered by hook_rate_widget.
  $widget->mode = $mode;

  // Let other modules alter the rate widget.
  foreach (module_implements('rate_widget') as $module) {
    $hook = "{$module}_rate_widget";
    $hook('view', $widget, array(
      'content_type' => $content_type,
      'content_id' => $content_id,
      'teaser' => $teaser,
    ));
  }
  $dom_id = "rate-{$content_type}-{$content_id}-{$widget_id}-{$mode}";
  $theme_name = str_replace('-', '_', $widget->name);
  $theme = array(
    'rate_widget__' . $theme_name,
    'rate_widget',
  );
  if (isset($widget->theme)) {

    // This overrules the default theming pattern. Used for templates.
    $theme = $widget->theme;
  }

  // Get voting results.
  $results = rate_get_results($content_type, $content_id, $widget_id);
  $results['empty'] = $results['count'] ? FALSE : TRUE;

  // Handle interaction modes.
  if (is_null($displayed)) {
    $displayed = RATE_AVERAGE;
    if ($just_voted) {
      if ($widget->displayed_just_voted == RATE_USER) {
        $displayed = RATE_USER;
      }
    }
    else {
      if (isset($results['user_vote']) && $widget->displayed == RATE_USER_OR_AVERAGE) {
        $displayed = RATE_USER;
      }
      if ($widget->displayed == RATE_USER) {
        $displayed = RATE_USER;
      }
    }
  }
  elseif ($displayed == RATE_USER_OR_AVERAGE && isset($results['user_vote'])) {
    $displayed = RATE_USER;
  }
  elseif ($displayed == RATE_USER) {
    $displayed = RATE_USER;
  }
  else {
    $displayed = RATE_AVERAGE;
  }
  if ($displayed == RATE_USER) {
    $results['rating'] = isset($results['user_vote']) ? $results['user_vote'] : 0;
    if (!isset($results['user_vote'])) {

      // We should display an empty rating in this case.
      $results['empty'] = TRUE;
    }
  }

  // Add generic javascript.
  drupal_add_js(drupal_get_path('module', 'rate') . '/rate.js');
  $links = array();
  foreach ($widget->options as $option) {

    // This name must be unique for all submit buttons across the page.
    $id = "opt-{$widget_id}-{$content_type}-{$content_id}-{$option[0]}";
    $token = _rate_get_token($id);
    if (isset($_GET['rate']) && $_GET['rate'] == $token) {
      rate_save_vote($widget, $content_type, $content_id, $option[0]);
      drupal_goto($_GET['q'], _rate_get_query());
    }
    isset($widget->translate) or $widget->translate = TRUE;
    $link_text = $widget->translate ? t($option[1]) : $option[1];
    $link_href = url($_GET['q'], array(
      'query' => _rate_get_query($token),
    ));
    if (isset($results['options'])) {
      $link_votes = $results['options'][$option[0]];
    }
    else {
      $link_votes = NULL;
    }
    $disabled = FALSE;
    if ($widget->mode == RATE_DISABLED || $widget->mode == RATE_COMPACT_DISABLED || $widget->mode == RATE_CLOSED) {
      $disabled = TRUE;
    }
    $links[] = array(
      'text' => $link_text,
      'href' => $disabled ? NULL : $link_href,
      'value' => $option[0],
      'votes' => $link_votes,
    );
  }
  if (isset($widget->css)) {
    drupal_add_css($widget->css);
  }
  if (isset($widget->js)) {
    drupal_add_js($widget->js);
  }
  if ($widget->mode == RATE_CLOSED && ($mode == RATE_COMPACT || $mode == RATE_DISABLED || $mode == RATE_COMPACT_DISABLED)) {

    // The widget was closed by hook_rate_widget, but we want to display a compact or disabled
    // widget. Set the mode back to compact / disabled in order to display a compact / disabled
    // widget with disabled voting buttons.
    $widget->mode = $mode == RATE_DISABLED ? RATE_DISABLED : RATE_COMPACT_DISABLED;
  }
  $widget_html = theme($theme, $links, $results, $widget->mode, $just_voted, $content_type, $content_id);
  if ($include_div) {
    $classes = array(
      'rate-widget-' . $widget_id,
      'rate-widget',
      'clear-block',
    );
    $classes[] = $displayed == RATE_AVERAGE ? 'rate-average' : 'rate-user';
    if (!empty($widget->template)) {
      $classes[] = 'rate-widget-' . $widget->template;
    }
    $classes = implode(' ', $classes);
    return theme('rate_widget_container', $classes, $dom_id, $widget_html);
  }
  else {

    // We do not want the div for AJAX callbacks, we would have 2 div's otherwise.
    return $widget_html;
  }
}

/**
 * Get a query string used for links to the same page.
 *
 * @param string $token
 * @return string
 */
function _rate_get_query($token = NULL) {
  $args = $_GET;
  if (isset($args['q'])) {
    unset($args['q']);
  }
  if (isset($args['rate'])) {
    unset($args['rate']);
  }
  if ($token) {
    $args['rate'] = $token;
  }
  return http_build_query($args, NULL, '&');
}

/**
 * Check if the current user has permission to vote on the given widget.
 *
 * @param object $widget
 * @return bool
 */
function _rate_check_permissions($widget, $node = NULL) {
  global $user;

  // This option should be available, check for legacy.
  isset($widget->roles) or $widget->roles = array();
  isset($widget->allow_voting_by_author) or $widget->allow_voting_by_author = TRUE;
  if (!$widget->allow_voting_by_author) {
    if ($user->uid == 0) {
      return RATE_PERMISSION_DISALLOWED_ROLE;
    }
    if ($node && $node->uid == $user->uid) {
      return RATE_PERMISSION_DISALLOWED_AUTHOR;
    }
  }
  $roles_selected = FALSE;
  foreach ($widget->roles as $rid => $role) {
    if ($role) {
      $roles_selected = TRUE;
      if (isset($user->roles[$rid])) {
        return RATE_PERMISSION_OK;
      }
    }
  }
  if (!$roles_selected) {

    // Allow voting for all roles if no roles are selected.
    return RATE_PERMISSION_OK;
  }
  return RATE_PERMISSION_DISALLOWED_ROLE;
}

/**
 * Get results for a voting widget.
 *
 * @param string $content_type "node" or "comment"
 * @param int $content_id Node id (nid) or comment id (cid)
 * @param int $widget_id Widget id
 * @return array
 */
function rate_get_results($content_type, $content_id, $widget_id) {
  global $user;
  $widgets = variable_get(RATE_VAR_WIDGETS, array());
  $widget = $widgets[$widget_id];
  $criteria = array(
    'content_type' => $content_type,
    'content_id' => $content_id,
    'tag' => $widget->tag,
  );
  $results = votingapi_select_results($criteria);
  $output = array(
    'count' => 0,
  );
  if ($widget->value_type != 'option') {

    // Set a default. Does not apply to options.
    $output['rating'] = 0;
  }
  if ($widget->value_type == 'option') {
    $output['options'] = array();
    foreach ($widget->options as $option) {
      $output['options'][$option[0]] = 0;
    }
  }
  foreach ($results as $result) {
    if ($widget->value_type == 'percent' && $result['function'] == 'average') {
      $output['rating'] = $result['value'];
    }
    elseif ($widget->value_type == 'points' && $result['function'] == 'sum') {
      $output['rating'] = $result['value'];
    }
    elseif ($result['function'] == 'count') {
      $output['count'] = $result['value'];
    }
    elseif (preg_match('/^option\\-([\\-0-9]+)$/', $result['function'], $match)) {
      $output['options'][$match[1]] = $result['value'];
      $output['count'] += $result['value'];
    }
  }

  // Check if this is a thumbs up / down voting, if so, calculate thumbs up / down percentages
  if ($widget->value_type == 'points' && count($widget->options) == 2) {

    // Votes down have a -1 value, votes up +1, $output['rating'] are the summed votes
    $output['down'] = (int) ($output['count'] - $output['rating']) / 2;
    $output['up'] = $output['count'] - $output['down'];
    if ($output['count'] == 0) {

      // If no one voted its 50-50
      $output['up_percent'] = 50;
    }
    else {
      $output['up_percent'] = (int) ($output['up'] * 100 / $output['count']);
    }
    $output['down_percent'] = 100 - $output['up_percent'];
  }
  $criteria += array(
    'uid' => $user->uid,
    'value_type' => $widget->value_type,
  );
  if (!$user->uid) {
    $criteria += array(
      'vote_source' => $_SERVER['REMOTE_ADDR'],
    );
  }
  if ($user_vote = votingapi_select_votes($criteria)) {

    // Check the anonymous window. We should not display this vote when its anonymous and casted too long ago.
    $anonymous_window = variable_get('votingapi_anonymous_window', 86400);
    if ($user->uid || $user_vote[0]['timestamp'] > time() - $anonymous_window || $anonymous_window == -1) {
      $output['user_vote'] = $user_vote[0]['value'];
      if ($widget->value_type == 'option') {
        foreach ($widget->options as $option) {
          if ($option[0] == $user_vote[0]['value']) {
            $output['user_vote'] = $option[1];
          }
        }
      }
    }
  }
  return $output;
}

/**
 * Save a vote to the database.
 *
 * @param object $widget
 * @param string $content_type
 * @param int $content_id
 * @param int $value
 */
function rate_save_vote($widget, $content_type, $content_id, $value, $ahah = FALSE) {

  // Prevent votes from saved twice. This does not check for different widgets /
  // content id's, but it's only possible to save a single vote per request for now.
  static $saved = FALSE;
  if ($saved) {
    return;
  }
  $saved = TRUE;

  // Determine if the user may vote.
  $node = $content_type == 'node' ? node_load($content_id) : NULL;
  $permission_status = _rate_check_permissions($widget, $node);

  // This option should be available, check for legacy.
  isset($widget->noperm_behaviour) or $widget->noperm_behaviour = RATE_NOPERM_REDIRECT_WITH_MESSAGE;
  if ($permission_status != RATE_PERMISSION_OK) {
    switch ($widget->noperm_behaviour) {
      case RATE_NOPERM_REDIRECT_WITH_MESSAGE:
        drupal_set_message(t('You must login before you can vote.'));

        // Redirects via AHAH have a destination param to prevent redirecting to the AJAX callback.
        $destination = isset($_GET['destination']) ? $_GET['destination'] : $_GET['q'];

        // Strip off protocol domain name to prevent long query strings in URL.
        $destination = preg_replace('/http:\\/\\/[^\\/]+\\//', '', $destination);
        $query = array(
          'destination' => $destination,
        );
        if ($ahah) {
          print url('user', array(
            'query' => $query,
            'absolute' => TRUE,
          ));
          module_invoke_all('exit') & exit;
        }
        else {
          drupal_goto('user', http_build_query($query, NULL, '&'));
        }
        break;
      case RATE_NOPERM_REDIRECT_WITHOUT_MESSAGE:
        $query = array(
          'destination' => $_GET['q'],
        );
        if ($ahah) {
          print url('user', array(
            'query' => $query,
            'absolute' => TRUE,
          ));
          module_invoke_all('exit') & exit;
        }
        else {
          drupal_goto('user', http_build_query($query, NULL, '&'));
        }
        break;
      default:
        return;
    }
  }
  $votes = array(
    'content_type' => $content_type,
    'content_id' => $content_id,
    'value_type' => $widget->value_type,
    'value' => $value,
    'tag' => $widget->tag,
  );

  // Call hook_rate_vote_alter().
  $redirect = FALSE;
  $save = TRUE;
  $context = array(
    'redirect' => &$redirect,
    'save' => &$save,
    'widget' => $widget,
  );
  drupal_alter('rate_vote', $votes, $context);
  if ($save) {
    votingapi_set_votes($votes);
  }
  if ($redirect) {
    preg_match('/^(.+?)(\\?(.*?))?(\\#(.*))?$/', $redirect, $parts);
    $path = $parts[1];
    $query = isset($parts[3]) ? $parts[3] : '';
    $fragment = isset($parts[5]) ? $parts[5] : '';
    if ($ahah) {
      print url($path, array(
        'absolute' => TRUE,
        'query' => $query,
        'fragment' => $fragment,
      ));
      module_invoke_all('exit') & exit;
    }
    else {
      drupal_goto($path, $query, $fragment);
    }
  }
}

/**
 * Implements hook_nodeapi().
 */
function rate_nodeapi(&$node, $op, $a3, $a4) {

  // Adding the form to the node view
  if ($op == 'view') {
    $widgets = rate_get_active_widgets('node', $node->type, $a3);
    foreach ($widgets as $widget_id => $widget) {
      $widget_name = 'rate_' . $widget->name;
      _rate_check_widget($widget);
      $display_mode = $a3 ? $widget->teaser_display_mode : $widget->node_display_mode;
      $widget_code = array(
        '#value' => rate_generate_widget($widget_id, 'node', $node->nid, $display_mode),
        '#weight' => $widget->node_display == RATE_DISPLAY_ABOVE_CONTENT ? 0 : 50,
      );
      if ($widget->node_display != RATE_DISPLAY_ABOVE_CONTENT && $widget->node_display != RATE_DISPLAY_BELOW_CONTENT) {
        $node->{$widget_name} = $widget_code;
      }
      else {
        $node->content[$widget_name] = $widget_code;
      }
    }
  }
}

/**
 * Implements hook_comment().
 */
function rate_comment(&$comment, $op) {

  // Adding the form to the node view
  if ($op == 'view') {
    $node = node_load($comment->nid);
    $widgets = rate_get_active_widgets('comment', $node->type);
    foreach ($widgets as $widget_id => $widget) {
      $widget_name = 'rate_' . $widget->name;
      _rate_check_widget($widget);
      $widget_code = rate_generate_widget($widget_id, 'comment', $comment->cid, $widget->comment_display_mode);
      switch ($widget->comment_display) {
        case RATE_DISPLAY_ABOVE_CONTENT:
          $comment->comment = $widget_code . $comment->comment;
          break;
        case RATE_DISPLAY_BELOW_CONTENT:
          $comment->comment = $comment->comment . $widget_code;
          break;
        case RATE_DISPLAY_DISABLE:
          $comment->{$widget_name} = $widget_code;
          break;
      }
    }
  }
}

/**
 * Implements hook_link()
 */
function rate_link($type, $object, $teaser = FALSE) {
  $links = array();

  // Adding widget to node links
  if ($type == 'node') {
    $widgets = rate_get_active_widgets('node', $object->type, $teaser);
    foreach ($widgets as $widget_id => $widget) {
      _rate_check_widget($widget);
      $widget_name = 'rate_' . $widget->name;
      $display_mode = $teaser ? $widget->teaser_display_mode : $widget->node_display_mode;
      $widget_code = rate_generate_widget($widget_id, 'node', $object->nid, $display_mode);
      if ($widget->node_display == RATE_DISPLAY_LINKS) {
        $links[$widget_name] = array(
          'title' => $widget_code,
          'html' => TRUE,
        );
      }
    }
  }

  // Adding widget to comment links
  if ($type == 'comment') {
    $node = node_load($object->nid);
    $widgets = rate_get_active_widgets('comment', $node->type);
    foreach ($widgets as $widget_id => $widget) {
      _rate_check_widget($widget);
      $widget_name = 'rate_' . $widget->name;
      $widget_code = rate_generate_widget($widget_id, 'comment', $object->cid, $widget->comment_display_mode);
      if ($widget->comment_display == RATE_DISPLAY_LINKS) {
        $links[$widget_name] = array(
          'title' => $widget_code,
          'html' => TRUE,
        );
      }
    }
  }
  return $links;
}

/**
 * AHAH callback for the vote buttons.
 */
function rate_vote_ahah() {
  $content_type = $_GET['content_type'];
  $content_id = (int) $_GET['content_id'];
  $widget_id = (int) $_GET['widget_id'];
  $widget_mode = (int) $_GET['widget_mode'];
  $widgets = variable_get(RATE_VAR_WIDGETS, array());
  $widget = $widgets[$widget_id];
  $links = array();
  foreach ($widget->options as $option) {

    // This name must be unique for all submit buttons across the page, AHAH will fail otherwise.
    $id = "opt-{$widget_id}-{$content_type}-{$content_id}-{$option[0]}";
    $token = _rate_get_token($id);
    if (isset($_GET['token']) && $_GET['token'] == $token) {
      rate_save_vote($widget, $content_type, $content_id, $option[0], TRUE);
    }
  }
  print rate_generate_widget($widget_id, $content_type, $content_id, $widget_mode, TRUE, TRUE);
  module_invoke_all('exit') & exit;
}

/**
 * Get a template object by name.
 *
 * @param string $name
 * @return object
 */
function _rate_get_template($name) {
  $templates = array();
  foreach (module_implements('rate_templates') as $module) {
    if ($module_templates = module_invoke($module, 'rate_templates')) {
      $templates = array_merge($module_templates, $templates);
    }
  }
  return isset($templates[$name]) ? $templates[$name] : FALSE;
}

/**
 * Implements hook_rate_templates().
 */
function rate_rate_templates() {
  $templates = array();
  $templates['thumbs_up'] = new stdClass();
  $templates['thumbs_up']->value_type = 'points';
  $templates['thumbs_up']->options = array(
    array(
      1,
      'up',
    ),
  );
  $templates['thumbs_up']->theme = 'rate_template_thumbs_up';
  $templates['thumbs_up']->css = drupal_get_path('module', 'rate') . '/templates/thumbs-up/thumbs-up.css';
  $templates['thumbs_up']->customizable = FALSE;
  $templates['thumbs_up']->translate = TRUE;
  $templates['thumbs_up']->template_title = t('Thumbs up');
  $templates['thumbs_up_down'] = new stdClass();
  $templates['thumbs_up_down']->value_type = 'points';
  $templates['thumbs_up_down']->options = array(
    array(
      1,
      'up',
    ),
    array(
      -1,
      'down',
    ),
  );
  $templates['thumbs_up_down']->theme = 'rate_template_thumbs_up_down';
  $templates['thumbs_up_down']->css = drupal_get_path('module', 'rate') . '/templates/thumbs-up-down/thumbs-up-down.css';
  $templates['thumbs_up_down']->customizable = FALSE;
  $templates['thumbs_up_down']->translate = TRUE;
  $templates['thumbs_up_down']->template_title = t('Thumbs up / down');
  $templates['fivestar'] = new stdClass();
  $templates['fivestar']->value_type = 'percent';
  $templates['fivestar']->options = array(
    array(
      0,
      '1',
    ),
    array(
      25,
      '2',
    ),
    array(
      50,
      '3',
    ),
    array(
      75,
      '4',
    ),
    array(
      100,
      '5',
    ),
  );
  $templates['fivestar']->theme = 'rate_template_fivestar';
  $templates['fivestar']->css = drupal_get_path('module', 'rate') . '/templates/fivestar/fivestar.css';
  $templates['fivestar']->js = drupal_get_path('module', 'rate') . '/templates/fivestar/fivestar.js';
  $templates['fivestar']->customizable = FALSE;
  $templates['fivestar']->translate = FALSE;
  $templates['fivestar']->template_title = t('Fivestar');
  $templates['emotion'] = new stdClass();
  $templates['emotion']->value_type = 'option';
  $templates['emotion']->options = array(
    array(
      1,
      'funny',
    ),
    array(
      2,
      'mad',
    ),
    array(
      3,
      'angry',
    ),
  );
  $templates['emotion']->theme = 'rate_template_emotion';
  $templates['emotion']->css = drupal_get_path('module', 'rate') . '/templates/emotion/emotion.css';
  $templates['emotion']->customizable = TRUE;
  $templates['emotion']->translate = TRUE;
  $templates['emotion']->template_title = t('Emotion');
  $templates['yesno'] = new stdClass();
  $templates['yesno']->value_type = 'option';
  $templates['yesno']->options = array(
    array(
      1,
      'yes',
    ),
    array(
      2,
      'no',
    ),
  );
  $templates['yesno']->theme = 'rate_template_yesno';
  $templates['yesno']->css = drupal_get_path('module', 'rate') . '/templates/yesno/yesno.css';
  $templates['yesno']->customizable = TRUE;
  $templates['yesno']->translate = TRUE;
  $templates['yesno']->template_title = t('Yes / no');
  return $templates;
}

/**
 * Implements hook_init().
 */
function rate_init() {
  drupal_add_css(drupal_get_path('module', 'rate') . '/rate.css');
}

/**
 * Menu access callback.
 */
function rate_results_page_access($permission, $node) {
  if (!user_access($permission)) {
    return FALSE;
  }
  if (!rate_get_active_widgets('node', $node->type)) {
    return FALSE;
  }
  return TRUE;
}

/**
 * Implements hook_votingapi_views_formatters().
 */
function rate_votingapi_views_formatters($details = array()) {
  if ($details->field == 'value') {
    return array(
      'rate_views_widget_disabled' => t('Rate widget (display only)'),
      'rate_views_widget_compact_disabled' => t('Rate widget (display only, compact)'),
      'rate_views_widget_compact' => t('Rate widget (compact)'),
      'rate_views_widget' => t('Rate widget'),
    );
  }
}
function rate_views_widget($value, $field, $columns, $mode = RATE_FULL) {
  if ($field->view->base_table != 'node' && $field->view->base_table != 'comments') {
    return '';
  }

  // Find the VotingAPI tag and value_type for this field.
  foreach ($field->query->table_queue[$field->relationship]['join']->extra as $votingapi_setting) {
    if ($votingapi_setting['field'] == 'tag') {
      $tag = $votingapi_setting['value'];
    }
    elseif ($votingapi_setting['field'] == 'value_type') {
      $value_type = $votingapi_setting['value'];
    }
  }
  switch ($field->view->base_table) {
    case 'node':
      $content_type = 'node';
      $content_id = $columns->nid;
      if (isset($columns->node_type)) {
        $node_type = $columns->node_type;
      }
      else {
        $node_type = db_result(db_query('SELECT type FROM {node} WHERE nid = %d', $columns->nid));
      }
      break;
    case 'comments':
      $content_type = 'comment';
      $content_id = $columns->cid;
      if (isset($columns->node_type)) {
        $node_type = $columns->node_type;
      }
      else {
        $node_type = db_result(db_query('SELECT n.type FROM {comments} c JOIN {node} n ON n.nid = c.nid WHERE c.cid = %d', $columns->cid));
      }
      break;
  }
  if ($field->query->table_queue[$field->relationship]['table'] == 'votingapi_cache') {
    $displayed_rating = RATE_AVERAGE;
  }
  else {
    $displayed_rating = RATE_USER;
  }

  // Loop through all the widgets and search for a suitable widget to display.
  $widgets = variable_get(RATE_VAR_WIDGETS, array());
  foreach ($widgets as $widget_id => $widget) {
    if ($tag == $widget->tag && $value_type == $widget->value_type && in_array($node_type, $widget->node_types)) {
      return rate_generate_widget($widget_id, $content_type, $content_id, $mode, TRUE, FALSE, $displayed_rating);
    }
  }

  // No suitable widget was found.
  return '';
}
function rate_views_widget_disabled($value, $field, $columns) {
  return rate_views_widget($value, $field, $columns, RATE_DISABLED);
}
function rate_views_widget_compact($value, $field, $columns) {
  return rate_views_widget($value, $field, $columns, RATE_COMPACT);
}
function rate_views_widget_compact_disabled($value, $field, $columns) {
  return rate_views_widget($value, $field, $columns, RATE_COMPACT_DISABLED);
}

/**
 * Get a token for the voting button.
 *
 * This is a wrapper around drupal_get_token() to make this compatible with
 * Pressflow. This function uses session_id(), which will generate a different
 * id for each request from anonymous users when using Pressflow. Tokens will
 * never match in this case, thus the votes are not cast.
 *
 * @param string $value
 * @return string
 */
function _rate_get_token($value) {
  global $user;
  if ($user->uid) {
    return drupal_get_token($value);
  }
  else {
    return md5($value . drupal_get_private_key());
  }
}

/**
 * Check widget object for missing values.
 * We use this for legacy reasons. Values may not be available after updating
 * from an old version.
 *
 * @param object $widget
 */
function _rate_check_widget(&$widget) {
  isset($widget->translate) or $widget->translate = TRUE;
  isset($widget->node_display) or $widget->node_display = RATE_DISPLAY_BELOW_CONTENT;
  isset($widget->teaser_display) or $widget->teaser_display = TRUE;
  isset($widget->comment_display) or $widget->comment_display = RATE_DISPLAY_BELOW_CONTENT;
  isset($widget->node_display_mode) or $widget->node_display_mode = RATE_FULL;
  isset($widget->teaser_display_mode) or $widget->teaser_display_mode = RATE_FULL;
  isset($widget->comment_display_mode) or $widget->comment_display_mode = RATE_FULL;
  isset($widget->roles) or $widget->roles = array();
  isset($widget->allow_voting_by_author) or $widget->allow_voting_by_author = TRUE;
  isset($widget->noperm_behaviour) or $widget->noperm_behaviour = RATE_NOPERM_REDIRECT_WITH_MESSAGE;
  isset($widget->displayed) or $widget->displayed = RATE_AVERAGE;
  isset($widget->displayed_just_voted) or $widget->displayed_just_voted = RATE_USER;
  isset($widget->template) or $widget->template = 'custom';
  isset($widget->customizable) or $widget->customizable = TRUE;
}

/**
 * Implements hook_theme().
 */
function rate_theme() {
  return array(
    'rate_widget' => array(
      'pattern' => 'rate_widget__',
      'arguments' => array(
        'links' => NULL,
        'results' => NULL,
        'mode' => NULL,
        'just_voted' => FALSE,
        'content_type' => NULL,
        'content_id' => NULL,
      ),
      'template' => 'rate-widget',
    ),
    'rate_widget_container' => array(
      'pattern' => 'rate_widget_container',
      'arguments' => array(
        'classes' => NULL,
        'dom_id' => NULL,
        'widget' => NULL,
      ),
      'template' => 'rate-widget-container',
    ),
    'rate_button' => array(
      'pattern' => 'rate_button__',
      'arguments' => array(
        'text' => NULL,
        'href' => NULL,
        'class' => NULL,
      ),
    ),
    'rate_admin_types' => array(
      'arguments' => array(
        'element' => NULL,
      ),
      'file' => 'rate.admin.inc',
    ),
    'rate_admin_options' => array(
      'arguments' => array(
        'element' => NULL,
      ),
      'file' => 'rate.admin.inc',
    ),
    // Templates for default widget types.
    'rate_template_thumbs_up' => array(
      'arguments' => array(
        'links' => NULL,
        'results' => NULL,
        'mode' => NULL,
        'just_voted' => FALSE,
        'content_type' => NULL,
        'content_id' => NULL,
      ),
      'template' => 'thumbs-up',
      'path' => drupal_get_path('module', 'rate') . '/templates/thumbs-up',
    ),
    'rate_template_thumbs_up_down' => array(
      'arguments' => array(
        'links' => NULL,
        'results' => NULL,
        'mode' => NULL,
        'just_voted' => FALSE,
        'content_type' => NULL,
        'content_id' => NULL,
      ),
      'template' => 'thumbs-up-down',
      'path' => drupal_get_path('module', 'rate') . '/templates/thumbs-up-down',
    ),
    'rate_template_fivestar' => array(
      'arguments' => array(
        'links' => NULL,
        'results' => NULL,
        'mode' => NULL,
        'just_voted' => FALSE,
        'content_type' => NULL,
        'content_id' => NULL,
      ),
      'template' => 'fivestar',
      'path' => drupal_get_path('module', 'rate') . '/templates/fivestar',
    ),
    'rate_template_emotion' => array(
      'arguments' => array(
        'links' => NULL,
        'results' => NULL,
        'mode' => NULL,
        'just_voted' => FALSE,
        'content_type' => NULL,
        'content_id' => NULL,
      ),
      'template' => 'emotion',
      'path' => drupal_get_path('module', 'rate') . '/templates/emotion',
    ),
    'rate_template_yesno' => array(
      'arguments' => array(
        'links' => NULL,
        'results' => NULL,
        'mode' => NULL,
        'just_voted' => FALSE,
        'content_type' => NULL,
        'content_id' => NULL,
      ),
      'template' => 'yesno',
      'path' => drupal_get_path('module', 'rate') . '/templates/yesno',
    ),
  );
}

/**
 * Preprocess function for the emotion template.
 */
function rate_preprocess_rate_widget(&$variables) {
  extract($variables);
  $buttons = array();
  foreach ($links as $link) {
    $button = theme('rate_button', $link['text'], $link['href'], 'rate-btn');
    $buttons[] = $button;
  }
  $variables['buttons'] = $buttons;
  $info = array();
  if ($mode == RATE_CLOSED) {
    $info[] = t('Voting is closed.');
  }
  if ($mode != RATE_COMPACT && $mode != RATE_COMPACT_DISABLED) {
    if ($results['user_vote']) {
      $info[] = t('You voted \'@option\'.', array(
        '@option' => t($results['user_vote']),
      ));
    }
  }
  $variables['info'] = implode(' ', $info);
}

/**
 * Preprocess function for the emotion template.
 */
function rate_preprocess_rate_template_emotion(&$variables) {
  extract($variables);
  $buttons = array();
  foreach ($links as $link) {
    $button = theme('rate_button', $link['text'], $link['href'], 'rate-emotion-btn');
    $button .= $link['votes'];
    $buttons[] = $button;
  }
  $variables['buttons'] = $buttons;
  $info = array();
  if ($mode == RATE_CLOSED) {
    $info[] = t('Voting is closed.');
  }
  if ($mode != RATE_COMPACT && $mode != RATE_COMPACT_DISABLED) {
    if ($results['user_vote']) {
      $info[] = t('You voted \'@option\'.', array(
        '@option' => t($results['user_vote']),
      ));
    }
  }
  $variables['info'] = implode(' ', $info);
}

/**
 * Preprocess function for the fivestar template.
 */
function rate_preprocess_rate_template_fivestar(&$variables) {
  extract($variables);
  $stars = array();
  $count = count($links);
  $percent_per_star = 100 / ($count - 1);
  for ($i = 0; $i < $count; $i++) {
    if (round($results['rating'] / $percent_per_star) >= $i && !$results['empty']) {
      $class = 'rate-fivestar-btn-filled';
    }
    else {
      $class = 'rate-fivestar-btn-empty';
    }
    $class .= ' rate-fivestar-' . ($i + 1);
    $stars[] = theme('rate_button', $links[$i]['text'], $links[$i]['href'], $class);
  }
  $variables['stars'] = $stars;
  $info = array();
  if ($mode == RATE_CLOSED) {
    $info[] = t('Voting is closed.');
  }
  if ($mode != RATE_COMPACT && $mode != RATE_COMPACT_DISABLED) {
    if (isset($results['user_vote'])) {
      $vote = round($results['user_vote'] / 25) + 1;
      $info[] = t('You voted !vote.', array(
        '!vote' => $vote,
      ));
    }
    $info[] = t('Total votes: !count', array(
      '!count' => $results['count'],
    ));
  }
  $variables['info'] = implode(' ', $info);
}

/**
 * Preprocess function for the thumbs_up_down template.
 */
function rate_preprocess_rate_template_thumbs_up_down(&$variables) {
  extract($variables);
  $up_classes = 'rate-thumbs-up-down-btn-up';
  $down_classes = 'rate-thumbs-up-down-btn-down';
  if (isset($results['user_vote'])) {
    switch ($results['user_vote']) {
      case $links[0]['value']:
        $up_classes .= ' rate-voted';
        break;
      case $links[1]['value']:
        $down_classes .= ' rate-voted';
        break;
    }
  }
  $variables['up_button'] = theme('rate_button', $links[0]['text'], $links[0]['href'], $up_classes);
  $variables['down_button'] = theme('rate_button', $links[1]['text'], $links[1]['href'], $down_classes);
  $info = array();
  if ($mode == RATE_CLOSED) {
    $info[] = t('Voting is closed.');
  }
  if ($mode != RATE_COMPACT && $mode != RATE_COMPACT_DISABLED) {
    if (isset($results['user_vote'])) {
      $info[] = t('You voted \'@option\'.', array(
        '@option' => $results['user_vote'] == 1 ? $links[0]['text'] : $links[1]['text'],
      ));
    }
  }
  $variables['info'] = implode(' ', $info);
}

/**
 * Preprocess function for the thumbs_up_down template.
 */
function rate_preprocess_rate_template_thumbs_up(&$variables) {
  extract($variables);
  $variables['up_button'] = theme('rate_button', $links[0]['text'], $links[0]['href'], 'rate-thumbs-up-btn-up');
  $info = array();
  if ($mode == RATE_CLOSED) {
    $info[] = t('Voting is closed.');
  }
  if ($mode != RATE_COMPACT && $mode != RATE_COMPACT_DISABLED) {
    if ($results['user_vote']) {
      $info[] = format_plural($results['count'], 'Only you voted.', '@count users have voted, including you.');
    }
    else {
      $info[] = format_plural($results['count'], '@count user has voted.', '@count users have voted.');
    }
  }
  $variables['info'] = implode(' ', $info);
}

/**
 * Preprocess function for the yesno template.
 */
function rate_preprocess_rate_template_yesno(&$variables) {
  extract($variables);
  $buttons = array();
  foreach ($links as $link) {
    $button = theme('rate_button', $link['text'], $link['href'], 'rate-yesno-btn');
    $button .= $link['votes'];
    $buttons[] = $button;
  }
  $variables['buttons'] = $buttons;
  $info = array();
  if ($mode == RATE_CLOSED) {
    $info[] = t('Voting is closed.');
  }
  if ($mode != RATE_COMPACT && $mode != RATE_COMPACT_DISABLED) {
    if ($results['user_vote']) {
      $info[] = t('You voted \'@option\'.', array(
        '@option' => t($results['user_vote']),
      ));
    }
  }
  $variables['info'] = implode(' ', $info);
}

/**
 * Theme rate button.
 *
 * @param string $text
 * @param string $href
 * @param string $class
 * @return string
 */
function theme_rate_button($text, $href, $class = NULL) {
  static $id = 0;
  $id++;
  $classes = 'rate-button';
  if ($class) {
    $classes .= ' ' . $class;
  }
  if (empty($href)) {

    // Widget is disabled or closed.
    return '<span class="' . $classes . '" id="rate-button-' . $id . '">' . check_plain($text) . '</span>';
  }
  else {
    return '<a class="' . $classes . '" id="rate-button-' . $id . '" rel="nofollow" href="' . htmlentities($href) . '">' . check_plain($text) . '</a>';
  }
}

Functions

Namesort descending Description
rate_comment Implements hook_comment().
rate_generate_widget Generate a widget.
rate_get_active_widgets Get list of active widgets.
rate_get_results Get results for a voting widget.
rate_init Implements hook_init().
rate_link Implements hook_link()
rate_menu Implements hook_menu().
rate_nodeapi Implements hook_nodeapi().
rate_perm Implements hook_perm().
rate_preprocess_rate_template_emotion Preprocess function for the emotion template.
rate_preprocess_rate_template_fivestar Preprocess function for the fivestar template.
rate_preprocess_rate_template_thumbs_up Preprocess function for the thumbs_up_down template.
rate_preprocess_rate_template_thumbs_up_down Preprocess function for the thumbs_up_down template.
rate_preprocess_rate_template_yesno Preprocess function for the yesno template.
rate_preprocess_rate_widget Preprocess function for the emotion template.
rate_rate_templates Implements hook_rate_templates().
rate_results_page_access Menu access callback.
rate_save_vote Save a vote to the database.
rate_theme Implements hook_theme().
rate_views_widget
rate_views_widget_compact
rate_views_widget_compact_disabled
rate_views_widget_disabled
rate_vote_ahah AHAH callback for the vote buttons.
rate_votingapi_views_formatters Implements hook_votingapi_views_formatters().
theme_rate_button Theme rate button.
_rate_check_permissions Check if the current user has permission to vote on the given widget.
_rate_check_widget Check widget object for missing values. We use this for legacy reasons. Values may not be available after updating from an old version.
_rate_get_query Get a query string used for links to the same page.
_rate_get_template Get a template object by name.
_rate_get_token Get a token for the voting button.

Constants