You are here

plus1.module in Plus 1 7

Same filename and directory in other branches
  1. 6.2 plus1.module
  2. 6 plus1.module

File

plus1.module
View source
<?php

/**
 * @file
 * A simple Plus 1 voting widget module.
 */
include_once drupal_get_path('module', 'plus1') . '/theme/theme.inc';

/**
 * Implements hook_permission().
 */
function plus1_permission() {
  return array(
    'plus1 vote on node' => array(
      'title' => t('Vote on nodes'),
      'description' => t('Cast votes on site content using the Plus1 voting widget.'),
    ),
    'plus1 vote on comment' => array(
      'title' => t('Vote on comments'),
      'description' => t('Cast votes on site comments using the Plus1 voting widget.'),
    ),
    'plus1 vote on taxonomy_term' => array(
      'title' => t('Vote on taxonomy terms'),
      'description' => t('Cast votes on taxonomy terms using the Plus1 voting widget.'),
    ),
    'administer the plus1 voting widget' => array(
      'title' => t('Administer the voting widget'),
      'description' => t('Make configuration changes to the Plus1 voting widget.'),
    ),
  );
}

/**
 * Implements hook_menu().
 */
function plus1_menu() {
  $items['plus1/vote/%/%'] = array(
    'title' => 'Vote',
    'page callback' => 'plus1_vote',
    'page arguments' => array(
      2,
      3,
      4,
    ),
    'access callback' => 'plus1_user_access',
    'access arguments' => array(
      'plus1 vote on node',
      'plus1 vote on comment',
      'plus1 vote on taxonomy_term',
    ),
    'type' => MENU_CALLBACK,
  );
  $items['plus1/undo-vote/%/%'] = array(
    'title' => 'Undo vote',
    'page callback' => 'plus1_undo_vote',
    'page arguments' => array(
      2,
      3,
      4,
    ),
    'access callback' => 'plus1_user_access',
    'access arguments' => array(
      'plus1 vote on node',
      'plus1 vote on comment',
      'plus1 vote on taxonomy_term',
    ),
    'type' => MENU_CALLBACK,
  );
  $items['admin/config/user-interface/plus1'] = array(
    'title' => 'Plus 1',
    'description' => 'Allows readers to vote on content.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'plus1_settings',
    ),
    'access callback' => 'plus1_user_access',
    'access arguments' => array(
      'administer the plus1 voting widget',
    ),
    'file' => 'plus1.admin.inc',
  );
  return $items;
}

/**
 * Custom access function, works with array of permissions.
 */
function plus1_user_access() {
  global $user;
  $permissions = func_get_args();
  if ($user->uid == 1) {
    return TRUE;
  }
  foreach ($permissions as $perm) {
    if (user_access($perm)) {
      return TRUE;
    }
  }
}

/**
 * Implements hook_theme().
 */
function plus1_theme() {
  return array(
    'plus1_widget' => array(
      'variables' => array(
        'entity_type' => NULL,
        'entity_id' => NULL,
        'tag' => NULL,
        'score' => 0,
        'logged_in' => FALSE,
        'is_author' => FALSE,
        'voted' => FALSE,
        'vote_link' => NULL,
        'undo_vote_link' => NULL,
        'link_query' => array(),
        'can_vote' => NULL,
        'can_undo_vote' => NULL,
        'undo_vote_text' => NULL,
        'voted_text' => NULL,
        'vote_text' => NULL,
      ),
      'template' => 'plus1-widget',
      'path' => drupal_get_path('module', 'plus1') . '/theme',
    ),
    'plus1_json_response' => array(
      'variables' => array(
        'entity_type' => NULL,
        'entity_id' => NULL,
        'tag' => NULL,
        'score' => NULL,
        'vote_type' => NULL,
      ),
    ),
  );
}

/**
 * Page callback.
 * @param $entity_type
 * Type of the entity, node or comment
 * @param $entity_id
 * A node, comment or taxonomy term ID.
 * @param $tag
 *    Tag name to vote for.
 * @param $value
 *    Value of vote, can be only 1 or -1. if another value will be provided, it will be handled as error.
 * A tag for different voting on same entity type
 * Submits the vote request and refreshes the page without JavaScript.
 * Otherwise, it submits the vote request and returns JSON to be parsed by jQuery.
 */
function plus1_vote($entity_type, $entity_id, $tag = 'plus1_node_vote') {
  global $user;
  if (!drupal_valid_token($_GET['token'], $entity_id, true)) {
    watchdog('plus1', 'Voting form error: Invalid token.');
    return drupal_access_denied();
  }
  $voted = plus1_get_votes($entity_type, $entity_id, $user->uid, $tag);
  $vote_source = $user->uid ? $user->uid : ip_address();
  drupal_alter('plus1_source', $vote_source);

  // If the voter has not already voted.
  if (!$voted) {
    $votes[] = array(
      'entity_type' => $entity_type,
      'entity_id' => $entity_id,
      'value_type' => 'points',
      'value' => 1,
      'tag' => $tag ? $tag : 'plus1_' . $entity_type . '_vote',
      'vote_source' => $vote_source,
    );
    votingapi_set_votes($votes);
    $criteria = array(
      'entity_type' => $entity_type,
      'entity_id' => $entity_id,
      'function' => 'sum',
      'tag' => $tag ? $tag : 'plus1_' . $entity_type . '_vote',
    );
    $results = votingapi_select_results($criteria);
    $score = $results[0]['value'] ? $results[0]['value'] : 0;
    module_invoke_all('plus1_voted', 'vote', $entity_type, $entity_id, $tag, $score, $user);
    if (isset($_GET['json'])) {

      // Return json to client side, taking into consideration entity type.
      drupal_json_output(theme('plus1_json_response__' . $entity_type . '__' . $tag, array(
        'entity_type' => $entity_type,
        'entity_id' => $entity_id,
        'tag' => $tag,
        'score' => $score,
        'vote_type' => 'vote',
      )));
    }
    else {
      drupal_set_message(t('Thank you for your vote.'));

      // go to the page where user pressed vote button
      drupal_goto();
    }
  }
}
function plus1_undo_vote($entity_type, $entity_id, $tag = 'plus1_node_vote') {
  global $user;
  if (!drupal_valid_token($_GET['token'], $entity_id, true)) {
    watchdog('plus1', 'Voting form error: Invalid token.');
    return drupal_access_denied();
  }
  $voted = plus1_get_votes($entity_type, $entity_id, $user->uid, $tag);
  $can_undo_vote = variable_get('plus1_' . $entity_type . '_undo_vote', 0);

  // If the voter has already voted and he can undo his vote.
  if ($voted && $can_undo_vote) {
    $criteria['entity_type'] = $entity_type;
    $criteria['entity_id'] = $entity_id;
    $criteria['value_type'] = 'points';
    if ($user->uid) {
      $criteria['uid'] = $user->uid;
    }
    $criteria['vote_source'] = $user->uid ? $user->uid : ip_address();

    //Allow vote source to be altered by other modules
    drupal_alter('plus1_source', $criteria['vote_source']);
    if (isset($tag) && $tag != "") {
      $criteria['tag'] = $tag;
    }
    $votes = votingapi_select_votes($criteria);
    votingapi_delete_votes($votes);
    votingapi_recalculate_results($entity_type, $entity_id, TRUE);
    $criteria = array(
      'entity_type' => $entity_type,
      'entity_id' => $entity_id,
      'function' => 'sum',
      'tag' => $tag ? $tag : 'plus1_' . $entity_type . '_vote',
    );
    $results = votingapi_select_results($criteria);
    $score = isset($results[0]['value']) ? $results[0]['value'] : 0;
    module_invoke_all('plus1_voted', 'undo_vote', $entity_type, $entity_id, $tag, $score, $user);
    if (isset($_GET['json'])) {
      drupal_json_output(theme('plus1_json_response__' . $entity_type . '__' . $tag, array(
        'entity_type' => $entity_type,
        'entity_id' => $entity_id,
        'tag' => $tag,
        'score' => $score,
        'vote_type' => 'undo_vote',
      )));
    }
    else {
      drupal_set_message(t('Thank you for your vote.'));

      // go to the page where user pressed vote button
      drupal_goto();
    }
  }
}

/**
 * Return the number of votes for a given content.
 *
 * @param $entity_type
 * An entity type, node or comment.
 * @param $entity_id
 * An ID of node or comment.
 * @param $uid
 * A user ID.
 * @param $tag
 * A tag for different voting on the content of same type
 * @return Integer
 * Number of votes the user has cast on this node.
 */
function plus1_get_votes($entity_type, $entity_id, $uid, $tag = 'plus1_node_vote') {
  $criteria['entity_type'] = $entity_type;
  $criteria['entity_id'] = $entity_id;
  $criteria['value_type'] = 'points';
  $criteria['vote_source'] = $uid ? $uid : ip_address();
  if ($uid) {
    $criteria['uid'] = $uid;
  }
  if (isset($tag) && $tag != "") {
    $criteria['tag'] = $tag;
  }

  //Allow vote source to be altered by other modules
  drupal_alter('plus1_source', $criteria['vote_source']);
  $results = votingapi_select_votes($criteria);
  return count($results);
}

/**
 * Return the total score for a given content.
 *
 * @param $entity_type
 * Node or comment
 * @param $entity_id
 * A node or comment ID.
 * @param $tag
 * A tag to identify different votes on the same content type
 * @return Integer
 * The score.
 */
function plus1_get_score($entity_type, $entity_id, $tag = 'plus1_node_vote') {
  $criteria['entity_type'] = $entity_type;
  $criteria['entity_id'] = $entity_id;
  $criteria['value_type'] = 'points';
  $criteria['function'] = 'sum';
  if (isset($tag) && $tag != "") {
    $criteria['tag'] = $tag;
  }
  $results = votingapi_select_results($criteria);
  if (empty($results)) {
    return 0;
  }
  else {
    return $results[0]['value'];
  }
}

/**
 * Create voting widget to display on the webpage.
 */
function plus1_build_node_jquery_widget($entity_id, $tag = 'plus1_node_vote') {
  global $user;
  $node = node_load($entity_id);
  $score = plus1_get_score('node', $node->nid, $tag);
  $logged_in = $user->uid > 0;
  $is_author = $node->uid == $user->uid;
  $voted = plus1_get_votes('node', $node->nid, $user->uid, $tag);
  $build = array(
    '#theme' => 'plus1_widget__node__' . $tag,
    '#weight' => (int) variable_get('plus1_node_widget_weight', '100'),
    '#entity_type' => 'node',
    '#entity_id' => $node->nid,
    '#tag' => $tag,
    '#score' => $score,
    '#logged_in' => $logged_in,
    '#is_author' => $is_author,
    '#voted' => $voted,
    '#vote_link' => 'plus1/vote/node/' . $node->nid . '/' . $tag,
    '#undo_vote_link' => 'plus1/undo-vote/node/' . $node->nid . '/' . $tag,
    '#link_query' => array(
      'token' => drupal_get_token($node->nid),
      plus1_get_cleared_destination(),
    ),
    '#can_vote' => user_access('plus1 vote on node'),
    '#can_undo_vote' => variable_get('plus1_node_undo_vote', 0),
    '#undo_vote_text' => check_plain(t(variable_get('plus1_node_undo_vote_text', ''))),
    '#voted_text' => check_plain(t(variable_get('plus1_node_voted_text', 'You voted'))),
    '#vote_text' => check_plain(t(variable_get('plus1_node_vote_text', 'Vote'))),
  );
  if (variable_get('plus1_add_js', 1)) {
    $build['#attached']['js'][] = drupal_get_path('module', 'plus1') . '/jquery.plus1.js';
  }
  if (variable_get('plus1_add_css', 1)) {
    $build['#attached']['css'][] = drupal_get_path('module', 'plus1') . '/plus1.css';
  }
  drupal_alter('plus1_widget', $build, $node);
  return $build;
}

/**
 * Create voting widget to display on the webpage.
 */
function plus1_build_comment_jquery_widget($entity_id, $tag = 'plus1_comment_vote') {
  global $user;
  $comment = comment_load($entity_id);
  $score = plus1_get_score('comment', $comment->cid, $tag);
  $logged_in = $user->uid > 0;
  $is_author = $comment->uid == $user->uid;
  $voted = plus1_get_votes('comment', $comment->cid, $user->uid, $tag);
  $build = array(
    '#theme' => 'plus1_widget__comment__' . $tag,
    '#weight' => (int) variable_get('plus1_comment_widget_weight', '100'),
    '#entity_type' => 'comment',
    '#entity_id' => $comment->cid,
    '#tag' => $tag,
    '#score' => $score,
    '#logged_in' => $logged_in,
    '#is_author' => $is_author,
    '#voted' => $voted,
    '#vote_link' => 'plus1/vote/comment/' . $comment->cid . '/' . $tag,
    '#undo_vote_link' => 'plus1/undo-vote/comment/' . $comment->cid . '/' . $tag,
    '#link_query' => array(
      'token' => drupal_get_token($comment->cid),
      plus1_get_cleared_destination(),
    ),
    '#can_vote' => user_access('plus1 vote on comment'),
    '#can_undo_vote' => variable_get('plus1_comment_undo_vote', 0),
    '#undo_vote_text' => check_plain(t(variable_get('plus1_comment_undo_vote_text', ''))),
    '#voted_text' => check_plaint(t(variable_get('plus1_comment_voted_text', 'You voted'))),
    '#vote_text' => check_plain(t(variable_get('plus1_comment_vote_text', 'Vote'))),
  );
  if (variable_get('plus1_add_js', 1)) {
    $build['#attached']['js'][] = drupal_get_path('module', 'plus1') . '/jquery.plus1.js';
  }
  if (variable_get('plus1_add_css', 1)) {
    $build['#attached']['css'][] = drupal_get_path('module', 'plus1') . '/plus1.css';
  }
  drupal_alter('plus1_widget', $build, $comment);
  return $build;
}

/**
 * Create voting widget to display on the webpage.
 */
function plus1_build_taxonomy_term_jquery_widget($entity_id, $tag = 'plus1_taxonomy_term_vote') {
  global $user;
  $term = taxonomy_term_load($entity_id);
  $score = plus1_get_score('taxonomy_term', $term->tid, $tag);
  $logged_in = $user->uid > 0;
  $voted = plus1_get_votes('taxonomy_term', $term->tid, $user->uid, $tag);
  $build = array(
    '#theme' => 'plus1_widget__taxonomy_term__' . $tag,
    '#weight' => (int) variable_get('plus1_taxonomy_widget_weight', '100'),
    '#entity_type' => 'taxonomy_term',
    '#entity_id' => $term->tid,
    '#tag' => $tag,
    '#score' => $score,
    '#logged_in' => $logged_in,
    '#is_author' => FALSE,
    '#voted' => $voted,
    '#vote_link' => 'plus1/vote/taxonomy_term/' . $term->tid . '/' . $tag,
    '#undo_vote_link' => 'plus1/undo-vote/taxonomy_term/' . $term->tid . '/' . $tag,
    '#link_query' => array(
      'token' => drupal_get_token($term->tid),
      plus1_get_cleared_destination(),
    ),
    '#can_vote' => user_access('plus1 vote on taxonomy_term'),
    '#can_undo_vote' => variable_get('plus1_taxonomy_term_undo_vote', 0),
    '#undo_vote_text' => check_plain(t(variable_get('plus1_taxonomy_term_undo_vote_text', ''))),
    '#voted_text' => check_plain(t(variable_get('plus1_taxonomy_term_voted_text', 'You voted'))),
    '#vote_text' => check_plain(t(variable_get('plus1_taxonomy_term_vote_text', 'Vote'))),
  );
  if (variable_get('plus1_add_js', 1)) {
    $build['#attached']['js'][] = drupal_get_path('module', 'plus1') . '/jquery.plus1.js';
  }
  if (variable_get('plus1_add_css', 1)) {
    $build['#attached']['css'][] = drupal_get_path('module', 'plus1') . '/plus1.css';
  }
  drupal_alter('plus1_widget', $build, $term);
  return $build;
}

/**
 * Implements hook_node_view().
 */
function plus1_node_view($node, $view_mode) {

  // Only show the voting widget in allowed content types.
  if ($node->nid && in_array($node->type, variable_get('plus1_node_types', array()))) {
    $is_enabled = 'full' == $view_mode ? 1 : 0;
    if (variable_get('plus1_node_in_' . $view_mode . '_view', $is_enabled)) {
      $node->content['plus1_widget'] = plus1_build_node_jquery_widget($node->nid);
    }
  }
}

/**
 * Implements hook_node_delete().
 */
function plus1_node_delete($node) {
  $criteria['entity_id'] = $node->nid;
  $criteria['entity_type'] = 'node';
  $votes = votingapi_select_votes($criteria);
  votingapi_delete_votes($votes);
}

/**
 * Implements hook_taxonomy_term_view_alter().
 */
function plus1_taxonomy_term_view_alter(&$build) {
  if (variable_get('plus1_taxonomy_term_widget_show', 0) && in_array($build['#term']->vid, variable_get('plus1_taxonomy_vocabularies', array()))) {
    $build['plus1_taxonomy_widget'] = plus1_build_taxonomy_term_jquery_widget($build['#term']->tid);
  }
}

/**
 * Implements hook_taxonomy_term_delete().
 */
function plus1_taxonomy_term_delete($term) {
  $criteria['entity_id'] = $term->tid;
  $criteria['entity_type'] = 'taxonomy_term';
  $votes = votingapi_select_votes($criteria);
  votingapi_delete_votes($votes);
}

/**
 * Implements hook_comment_view().
 */
function plus1_comment_view($comment, $view_mode, $langcode) {
  if (variable_get('plus1_comment_widget_show', 0) && !isset($comment->in_preview)) {
    $comment->content['plus1_widget'] = plus1_build_comment_jquery_widget($comment->cid);
  }
}

/**
 * Implements gook_comment_delete().
 */
function plus1_comment_delete($comment) {
  $criteria['entity_id'] = $comment->cid;
  $criteria['entity_type'] = 'comment';
  $votes = votingapi_select_votes($criteria);
  votingapi_delete_votes($votes);
}

/**
 * Implements hook_votingapi_metadata_alter().
 */
function plus1_votingapi_metadata_alter(&$data) {

  // Document several custom tags for rating restaurants and meals.
  $data['tags']['plus1_node_vote'] = array(
    'name' => t('Plus1 node vote'),
    'description' => t('Plus1 votes for nodes.'),
    'module' => 'plus1',
  );
  $data['tags']['plus1_comment_vote'] = array(
    'name' => t('Plus1 comment vote'),
    'description' => t('Plus1 votes for comments.'),
    'module' => 'plus1',
  );
  $data['tags']['plus1_taxonomy_term_vote'] = array(
    'name' => t('Plus1 term vote'),
    'description' => t('Plus1 votes for taxonomy terms.'),
    'module' => 'plus1',
  );
}

/**
 * If widget is returned by ajax, and there are ajax_html_ids in the $_GET,
 * then we need to clear destination parameter, reduce query length.
 */
function plus1_get_cleared_destination() {
  $clear_destination =& drupal_static(__FUNCTION__);
  if (isset($clear_destination)) {
    return $clear_destination;
  }
  if (isset($_GET['destination'])) {
    $path = $_GET['destination'];
  }
  else {
    $path = $_GET['q'];
    $query = $_GET;
    unset($query['ajax_html_ids']);
    unset($query['ajax_page_state']);
    $query = drupal_http_build_query(drupal_get_query_parameters($query));
    if ($query != '') {
      $path .= '?' . $query;
    }
  }
  $clear_destination = array(
    'destination' => $path,
  );
  return $clear_destination;
}

/**
 * This hook is called when user votes on some content.
 * @param  $vote_type
 *    Vote type, "vote" or "undo_vote"
 * @param  $entity_type
 *    Entity type, node, comment etc.
 * @param  $entity_id
 * @param  $tag
 * @param  $score
 * @param  $user
 * @return void
 */
function hook_plus1_voted($vote_type, $entity_type, $entity_id, $tag, $score, $user) {
}

Functions

Namesort descending Description
hook_plus1_voted This hook is called when user votes on some content.
plus1_build_comment_jquery_widget Create voting widget to display on the webpage.
plus1_build_node_jquery_widget Create voting widget to display on the webpage.
plus1_build_taxonomy_term_jquery_widget Create voting widget to display on the webpage.
plus1_comment_delete Implements gook_comment_delete().
plus1_comment_view Implements hook_comment_view().
plus1_get_cleared_destination If widget is returned by ajax, and there are ajax_html_ids in the $_GET, then we need to clear destination parameter, reduce query length.
plus1_get_score Return the total score for a given content.
plus1_get_votes Return the number of votes for a given content.
plus1_menu Implements hook_menu().
plus1_node_delete Implements hook_node_delete().
plus1_node_view Implements hook_node_view().
plus1_permission Implements hook_permission().
plus1_taxonomy_term_delete Implements hook_taxonomy_term_delete().
plus1_taxonomy_term_view_alter Implements hook_taxonomy_term_view_alter().
plus1_theme Implements hook_theme().
plus1_undo_vote
plus1_user_access Custom access function, works with array of permissions.
plus1_vote Page callback.
plus1_votingapi_metadata_alter Implements hook_votingapi_metadata_alter().