You are here

userpoints.module in User Points 6

File

userpoints.module
View source
<?php

// Copyright 2005-2007 Khalid Baheyeldin http://2bits.com
define('USERPOINTS_PERM_VIEW', 'view userpoints');
define('USERPOINTS_PERM_VIEW_OWN', 'view own userpoints');

//The permission(PERM_USE) was removed from use as per #158490 it'll remain

//out until code exists to use the permission (jredding 12/26/2007)
define('USERPOINTS_PERM_USE', 'use userpoints');
define('USERPOINTS_PERM_ADMIN', 'administer userpoints');
define('USERPOINTS_TRANS_UCPOINTS', 'userpoints_trans_ucpoints');
define('USERPOINTS_TRANS_LCPOINTS', 'userpoints_trans_lcpoints');
define('USERPOINTS_TRANS_UCPOINT', 'userpoints_trans_ucpoint');
define('USERPOINTS_TRANS_LCPOINT', 'userpoints_trans_lcpoint');
define('USERPOINTS_TRANS_UNCAT', 'userpoints_trans_uncat');
define('USERPOINTS_STATUS', 'userpoints_status');
define('USERPOINTS_POINTS_MODERATION', 'userpoints_points_moderation');
define('USERPOINTS_TXN_STATUS_APPROVED', 0);
define('USERPOINTS_TXN_STATUS_PENDING', 1);
define('USERPOINTS_TXN_STATUS_DECLINED', 2);
define('USERPOINTS_EXPIRY_DESCRIPTION', 'userpoints_expiry_description');
define('USERPOINTS_EXPIREON_DATE', 'userpoints_expireon_date');
define('USERPOINTS_EXPIREAFTER_DATE', 'userpoints_expireafter_date');
define('USERPOINTS_DISPLAY_MESSAGE', 'userpoints_display_message');
define('USERPOINTS_REPORT_USERCOUNT', 'userpoints_report_usercount');
define('USERPOINTS_REPORT_LIMIT', 'userpoints_report_limit');
define('USERPOINTS_REPORT_DISPLAYZERO', 'userpoints_report_displayzero');
define('USERPOINTS_CATEGORY_NAME', 'Userpoints');
define('USERPOINTS_CATEGORY_DEFAULT_VID', 'userpoints_category_default_vid');
define('USERPOINTS_CATEGORY_DEFAULT_TID', 'userpoints_category_default_tid');
define('USERPOINTS_CATEGORY_PROFILE_DISPLAY_TID', 'userpoints_category_profile_display_tid');
define('USERPOINTS_TRANSACTION_TIMESTAMP', 'userpoints_transaction_timestamp');

/**
 * Purpose: Returns an array of common translation placeholders
 */
function userpoints_translation() {
  static $trans;
  if (!isset($trans)) {
    $trans = array(
      '!Points' => check_plain(variable_get(USERPOINTS_TRANS_UCPOINTS, 'Points')),
      '!points' => check_plain(variable_get(USERPOINTS_TRANS_LCPOINTS, 'points')),
      '!Point' => check_plain(variable_get(USERPOINTS_TRANS_UCPOINT, 'Point')),
      '!point' => check_plain(variable_get(USERPOINTS_TRANS_LCPOINT, 'point')),
      '!Uncategorized' => check_plain(variable_get(USERPOINTS_TRANS_UNCAT, 'Uncategorized')),
    );
  }
  return $trans;
}

/*
 * Purpose: Returns an array of possible transaction statuses
 */
function userpoints_txn_status() {
  return array(
    USERPOINTS_TXN_STATUS_APPROVED => t('Approved'),
    USERPOINTS_TXN_STATUS_PENDING => t('Pending'),
    USERPOINTS_TXN_STATUS_DECLINED => t('Declined'),
  );
}

/**
 * Implementation of hook_help().
 */
function userpoints_help($path, $arg) {
  switch ($path) {
    case 'admin/settings/userpoints':
      return t('Configure userpoints moderation and branding translation');
    case 'admin/help#userpoints':
      return t('Users earn !points as they post nodes, comments, and vote on nodes', userpoints_translation());
  }
}

/**
 * Implementation of hook_menu().
 */
function userpoints_menu() {
  $items = array();
  $items['admin/settings/userpoints'] = array(
    'title' => '!Points settings',
    'description' => strtr('Settings for !points', userpoints_translation()),
    'title arguments' => userpoints_translation(),
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'userpoints_admin_settings',
    ),
    'access arguments' => array(
      USERPOINTS_PERM_ADMIN,
    ),
    'type' => MENU_NORMAL_ITEM,
  );
  $items['admin/user/userpoints'] = array(
    'title' => '!Points',
    'title arguments' => userpoints_translation(),
    'description' => strtr('Manage !points', userpoints_translation()),
    'page callback' => 'userpoints_admin_points',
    'access arguments' => array(
      USERPOINTS_PERM_ADMIN,
    ),
  );
  $items['admin/user/userpoints/list'] = array(
    'title' => 'List',
    'title arguments' => userpoints_translation(),
    'description' => strtr('List users by !points', userpoints_translation()),
    'access arguments' => array(
      USERPOINTS_PERM_VIEW,
    ),
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => -2,
  );
  $items['admin/user/userpoints/moderate'] = array(
    'title' => 'Moderation',
    'title arguments' => userpoints_translation(),
    'description' => strtr('Review !points in moderation', userpoints_translation()),
    'page callback' => 'userpoints_admin_manage',
    'access arguments' => array(
      USERPOINTS_PERM_ADMIN,
    ),
    'type' => MENU_LOCAL_TASK,
    'weight' => -1,
  );
  $items['admin/user/userpoints/add'] = array(
    'title' => 'Add',
    'description' => 'Admin add/delete userpoints',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'userpoints_admin_txn',
    ),
    'access arguments' => array(
      USERPOINTS_PERM_ADMIN,
    ),
    'type' => MENU_LOCAL_TASK,
    'weight' => 0,
  );
  $items['admin/user/userpoints/edit'] = array(
    'title' => 'Edit',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'userpoints_admin_txn',
    ),
    'access arguments' => array(
      USERPOINTS_PERM_ADMIN,
    ),
    'type' => MENU_CALLBACK,
  );
  $items['admin/user/userpoints/approve'] = array(
    'title' => 'Approve Userpoints',
    'page callback' => 'userpoints_admin_approve',
    'access arguments' => array(
      USERPOINTS_PERM_ADMIN,
    ),
    'type' => MENU_CALLBACK,
  );
  $items['admin/user/userpoints/decline'] = array(
    'title' => t('Approve !points', userpoints_translation()),
    'page callback' => 'userpoints_admin_approve',
    'access arguments' => array(
      USERPOINTS_PERM_ADMIN,
    ),
    'type' => MENU_CALLBACK,
  );
  $items['userpoints'] = array(
    'title' => 'Users by !points',
    'title arguments' => userpoints_translation(),
    'page callback' => 'userpoints_list_users',
    'access arguments' => array(
      USERPOINTS_PERM_VIEW,
    ),
    'type' => MENU_NORMAL_ITEM,
  );
  $items['myuserpoints'] = array(
    'title' => 'My !points',
    'title arguments' => userpoints_translation(),
    'page callback' => 'userpoints_list_my_userpoints_forward',
    'access callback' => TRUE,
    'type' => MENU_CALLBACK,
  );
  $items['myuserpoints/%user_uid_optional'] = array(
    'title' => 'My !points',
    'title arguments' => userpoints_translation(),
    'page callback' => 'userpoints_list_my_userpoints',
    'page arguments' => array(
      1,
    ),
    'access callback' => 'userpoints_access_my_points',
    'access arguments' => array(
      1,
    ),
    'type' => MENU_NORMAL_ITEM,
  );
  return $items;
}

/**
 * Checks if user can access their points - used via hook_menu().
 *
 * @return
 *   TRUE if user has permissions to view userpoints and if the user is logged
 *   in.
 */
function userpoints_access_my_points($account = NULL) {
  global $user;
  if ($account && $user->uid != $account->uid) {
    return user_access(USERPOINTS_PERM_ADMIN);
  }
  return user_access(USERPOINTS_PERM_VIEW) && user_is_logged_in() || user_access(USERPOINTS_PERM_VIEW_OWN);
}

/**
 * Forward to the myuserpoints page of the current user.
 */
function userpoints_list_my_userpoints_forward() {
  global $user;
  drupal_goto('myuserpoints/' . $user->uid);
}

/**
 * Implementation of hook_perm().
 */
function userpoints_perm() {
  return array(
    USERPOINTS_PERM_VIEW,
    USERPOINTS_PERM_VIEW_OWN,
    USERPOINTS_PERM_ADMIN,
  );
}

/**
 * Implementation of hook_theme().
 */
function userpoints_theme() {
  return array(
    'userpoints_list_users' => array(
      'arguments' => array(
        'header' => NULL,
        'rows' => NULL,
        'tid' => NULL,
        'pager_limit' => NULL,
      ),
    ),
    'userpoints_list_users_header' => array(
      'arguments' => array(),
    ),
    'userpoints_list_users_row' => array(
      'arguments' => array(
        'row' => NULL,
      ),
    ),
    'userpoints_list_my_userpoints' => array(
      'arguments' => array(
        'args' => NULL,
        'header' => NULL,
        'rows' => NULL,
      ),
    ),
  );

  //return array
}

/**
 * Implementation of hook_token_values().
 */
function userpoints_token_values($type, $object = NULL, $options = array()) {
  if ($type == 'user') {
    $tokens = array(
      'userpoints' => userpoints_get_current_points($object->uid),
    );
    return $tokens;
  }
}

/**
 * Implementation of hook_token_list().
 */
function userpoints_token_list($type = 'all') {
  if ($type == 'user' || $type == 'all') {
    $tokens['user'] = array(
      'userpoints' => t('The number of points a user has.'),
    );
    return $tokens;
  }
}

/**
 * menu callback for settings form.
 */
function userpoints_admin_settings() {
  $form = array();
  $group = 'renaming';
  $form[$group] = array(
    '#type' => 'fieldset',
    '#collapsible' => TRUE,
    '#collapsed' => TRUE,
    '#title' => t('Points branding'),
  );
  $form[$group][USERPOINTS_TRANS_UCPOINTS] = array(
    '#type' => 'textfield',
    '#title' => t('Word to use in the interface for the upper case plural word !Points', userpoints_translation()),
    '#default_value' => variable_get(USERPOINTS_TRANS_UCPOINTS, 'Points'),
    '#size' => 20,
    '#maxlength' => 20,
  );
  $form[$group][USERPOINTS_TRANS_LCPOINTS] = array(
    '#type' => 'textfield',
    '#title' => t('Word to use in the interface for the lower case plural word !points', userpoints_translation()),
    '#default_value' => variable_get(USERPOINTS_TRANS_LCPOINTS, 'points'),
    '#size' => 20,
    '#maxlength' => 20,
  );
  $form[$group][USERPOINTS_TRANS_UCPOINT] = array(
    '#type' => 'textfield',
    '#title' => t('Word to use in the interface for the upper case singular word !Point', userpoints_translation()),
    '#default_value' => variable_get(USERPOINTS_TRANS_UCPOINT, 'Point'),
    '#size' => 20,
    '#maxlength' => 20,
  );
  $form[$group][USERPOINTS_TRANS_LCPOINT] = array(
    '#type' => 'textfield',
    '#title' => t('Word to use in the interface for the lower case singular word !point', userpoints_translation()),
    '#default_value' => variable_get(USERPOINTS_TRANS_LCPOINT, 'point'),
    '#size' => 20,
    '#maxlength' => 20,
  );
  $form[$group][USERPOINTS_TRANS_UNCAT] = array(
    '#type' => 'textfield',
    '#title' => t('Word to use for the uncategorized category'),
    '#default_value' => variable_get(USERPOINTS_TRANS_UNCAT, 'Uncategorized'),
    '#size' => 20,
    '#maxlength' => 20,
  );
  $group = 'status';
  $form[$group] = array(
    '#type' => 'fieldset',
    '#title' => t('Moderation'),
    '#collapsible' => TRUE,
    '#collapsed' => TRUE,
    '#weight' => -1,
  );
  $form[$group][USERPOINTS_POINTS_MODERATION] = array(
    '#type' => 'radios',
    '#title' => t('Transaction status'),
    '#default_value' => variable_get(USERPOINTS_POINTS_MODERATION, 0),
    '#options' => array(
      t('Approved'),
      t('Moderated'),
    ),
    '#description' => t('Select whether all !points should be approved automatically, or moderated, and require admin approval', userpoints_translation()),
  );
  $group = "Points expiration";
  $form[$group] = array(
    '#type' => 'fieldset',
    '#collapsible' => TRUE,
    '#collapsed' => TRUE,
    '#title' => t('!Points expiration', userpoints_translation()),
    '#description' => t('These settings affect new !points only, they are not retroactive. !Points expiration depends upon cron.', userpoints_translation()),
  );
  $form[$group][USERPOINTS_EXPIREAFTER_DATE] = array(
    '#type' => 'select',
    '#title' => t('Expire !points after', userpoints_translation()),
    '#description' => t('Once !points have been obtained by the user
                          they will expire according to this setting', userpoints_translation()),
    '#options' => expiry_dates(),
    '#default_value' => variable_get(USERPOINTS_EXPIREAFTER_DATE, NULL),
  );

  /**
   * If the expiration date is earlier than today
   * new points will last forever. Although this may be desirable
   * it could also be an oversight so we'll display a message
   * to the administrator
   *
   */
  $warning = "";
  if (userpoints_date_to_timestamp(variable_get(USERPOINTS_EXPIREON_DATE, array(
    'day' => 1,
    'month' => 1,
    'year' => 1900,
  ))) < time()) {
    $warning = '<br /><strong>' . t('This setting will not take affect, date must be in the future') . '</strong>';
  }
  $form[$group][USERPOINTS_EXPIREON_DATE] = array(
    '#type' => 'date',
    '#title' => t('Expire !points on this date', userpoints_translation()),
    '#description' => t('Once !points have been obtained by the user they will
                         last until this date. This setting overrides the
                         "Expire after setting" above ', userpoints_translation()) . $warning,
    '#default_value' => variable_get(USERPOINTS_EXPIREON_DATE, array(
      'day' => 1,
      'month' => 1,
      'year' => 1980,
    )),
  );
  $form[$group][USERPOINTS_EXPIRY_DESCRIPTION] = array(
    '#type' => 'textarea',
    '#title' => t('Expiration entry description'),
    '#description' => t('A negating expiration entry is made to expire
                         !points leaving the original entry intact
                         (e.g. original !points + expiration !points = 0).
                         When the expiration entry is made this description will
                         be placed on the entry. This is useful so the users will
                         know what happened to their !point balance. In crafting
                         your message you can use the following variables.', userpoints_translation()) . t('
                         <br /> !points = The name used in branding
                         above (also use !Points and !point)') . t('
                         <br /> !operation = Original operation that granted the !points
                         <br /> !description = Original description for the !point
                         <br /> !txn_id Original transaction ID
                         <br /> !date = Date of the original entry', userpoints_translation()),
    '#default_value' => variable_get(USERPOINTS_EXPIRY_DESCRIPTION, ''),
  );
  $group = "misc";
  $form[$group] = array(
    '#type' => 'fieldset',
    '#collapsible' => TRUE,
    '#collapsed' => TRUE,
    '#title' => t('Messages'),
    '#description' => t('Control the behavior of messages users see.'),
  );
  $form[$group][USERPOINTS_DISPLAY_MESSAGE] = array(
    '#type' => 'radios',
    '#title' => t('Display message'),
    '#default_value' => variable_get(USERPOINTS_DISPLAY_MESSAGE, 1),
    '#options' => array(
      0 => t('No'),
      1 => t('Yes'),
    ),
    '#description' => t('Determines if a message should be displayed whenever !points are awarded/substracted ', userpoints_translation()),
  );
  $group = "reports";
  $form[$group] = array(
    '#type' => 'fieldset',
    '#collapsible' => TRUE,
    '#collapsed' => TRUE,
    '#title' => t('Report Settings'),
  );
  $form[$group][USERPOINTS_REPORT_LIMIT] = array(
    '#type' => 'select',
    '#title' => t('Transactions per page'),
    '#default_value' => variable_get(USERPOINTS_REPORT_LIMIT, 10),
    '#options' => array(
      10 => 10,
      20 => 20,
      30 => 30,
      40 => 40,
      50 => 50,
      100 => 100,
    ),
    '#description' => t('Limits the number of transactions displayed per page'),
  );
  $form[$group][USERPOINTS_REPORT_DISPLAYZERO] = array(
    '#type' => 'radios',
    '#title' => t('Display zero !point users?', userpoints_translation()),
    '#default_value' => variable_get(USERPOINTS_REPORT_DISPLAYZERO, 1),
    '#options' => array(
      t('No'),
      t('Yes'),
    ),
    '#description' => t('If set to "No" users with zero !points will not be displayed in the reports ', userpoints_translation()),
  );
  $form[$group][USERPOINTS_REPORT_USERCOUNT] = array(
    '#type' => 'select',
    '#title' => t('Users per page'),
    '#default_value' => variable_get(USERPOINTS_REPORT_USERCOUNT, 30),
    '#options' => array(
      10 => 10,
      20 => 20,
      30 => 30,
      40 => 40,
      50 => 50,
      100 => 100,
    ),
    '#description' => t('When listing !points by user limit how many users are displayed on a single page', userpoints_translation()),
  );

  /* Categories will only appear if the taxonomy module is enabled as
   * the module is required for this functionality but not necessarily
   * a requirement for the module.
   */
  if (module_exists('taxonomy')) {
    $group = 'category';
    $form[$group] = array(
      '#type' => 'fieldset',
      '#collapsible' => TRUE,
      '#collapsed' => TRUE,
      '#title' => t('!Points Categorization', userpoints_translation()),
    );
    $form[$group][USERPOINTS_CATEGORY_DEFAULT_TID] = array(
      '#type' => 'select',
      '#title' => t('Default Category'),
      '#default_value' => variable_get(USERPOINTS_CATEGORY_DEFAULT_TID, NULL),
      '#options' => userpoints_get_categories(),
      '#description' => t('By default all !points are assigned to this category.  You can modify what categories are available by modifying the <a href="!url">Userpoints taxonomy</a>', array_merge(userpoints_translation(), array(
        '!url' => url('admin/content/taxonomy/' . variable_get(USERPOINTS_CATEGORY_DEFAULT_VID, '')),
      ))),
    );
    $options = userpoints_get_categories();

    //$options doesn't include all so we'll add it
    $options['all'] = t('All categories');
    $form[$group][USERPOINTS_CATEGORY_PROFILE_DISPLAY_TID] = array(
      '#type' => 'select',
      '#title' => t('Category to display on the user profile page'),
      '#default_value' => variable_get(USERPOINTS_CATEGORY_PROFILE_DISPLAY_TID, NULL),
      '#options' => $options,
      '#description' => t('Select which category of !points to display on the user profile page. Select "All" to display a sum total of all categories', userpoints_translation()),
    );
  }

  // New configuration options to overide current timestamp
  $group = "stamping";
  $form[$group] = array(
    '#type' => 'fieldset',
    '#collapsible' => TRUE,
    '#collapsed' => TRUE,
    '#title' => t('Transaction stamping'),
  );
  $form[$group][USERPOINTS_TRANSACTION_TIMESTAMP] = array(
    '#type' => 'checkbox',
    '#title' => t('Always use system time'),
    '#default_value' => variable_get(USERPOINTS_TRANSACTION_TIMESTAMP, 1),
    '#description' => t('Sets if the transaction timestamp should obey current time, or can be modified by the API operations. Unchecking this option will allow customization of timetamp for the transactions.'),
  );
  $form['setting'] = module_invoke_all('userpoints', 'setting');
  return system_settings_form($form);
}

/**
 * Get current points of a user.
 *
 * @param $uid
 *   User ID of the user to get or lose the points.
 * @param $tid
 *   Term ID to get points for, or 'all'.
 * @param $reset
 *   Reset the static cache.
 *
 * @return
 *   Number of current points in that user's account. No return value if
 *   $reset is passed.
 */
function userpoints_get_current_points($uid = NULL, $tid = NULL, $reset = FALSE) {
  static $points = array();
  if ($reset) {
    $points = array();
    return;
  }
  if (!$uid) {
    global $user;
    $uid = $user->uid;
  }
  if (!$tid) {
    $tid = userpoints_get_default_tid();
  }
  if (!isset($points[$uid][$tid])) {
    if ($tid == 'all') {
      $points[$uid][$tid] = (int) db_result(db_query('SELECT SUM(points) FROM {userpoints} WHERE uid = %d', $uid));
    }
    else {
      $points[$uid][$tid] = (int) db_result(db_query('SELECT points FROM {userpoints} WHERE uid = %d AND tid = %d', $uid, $tid));
    }
  }
  return $points[$uid][$tid];
}

/**
 * @param uid: user id of the user to get or lose the points
 *
 * @return number of max points in that user's account
 */
function userpoints_get_max_points($uid = NULL, $tid = NULL) {
  static $max = array();

  // Check if uid is passed as a parameter
  if (!$uid) {

    // It is not, so we use the currently logged in user's uid
    global $user;
    $uid = $user->uid;
  }

  // Check if a term id is passed as a parameter
  if (!$tid) {

    // It is not, so get the default term id
    $tid = userpoints_get_default_tid();
  }

  // Check if we have already cached the maximum for the user/term combination on previous calls
  if (!isset($max[$uid][$tid])) {

    // We did not cache it
    if ($tid == 'all') {

      // There is no term id, so we use "all"
      $max[$uid][$tid] = db_result(db_query('SELECT SUM(max_points) FROM {userpoints} WHERE uid = %d', $uid));
    }
    else {

      // A term ID is specified, so fetch its maximum points
      $max[$uid][$tid] = db_result(db_query('SELECT max_points FROM {userpoints} WHERE uid = %d AND tid = %d', $uid, $tid));
    }
  }

  // Return
  return $max[$uid][$tid];
}

/**
 * @param $params(array) or (int)
 *    if (int) assumed to be points for current user
 *    Accepts an array of keyed variables and parameters
 *    'points' => # of points (int) (required)
 *    'moderate' => TRUE/FALSE
 *    'uid' => $user->uid
 *    'time_stamp' => unix time of the points assignement date
 *    'operation' => 'published' 'moderated' etc.
 *    'tid' => 'category ID'
 *    'expirydate' => timestamp or 0, 0 = non-expiring; NULL = site default
 *    'description' => 'description'
 *    'reference' => reserved for module specific use
 *    'display' => whether or not to display "points awarded" message
 *    'txn_id' => Transaction ID of points, If present an UPDATE is performed
 *    'entity_id' => ID of an entity in the Database. ex. $node->id or $user->uid
 *    'entity_type' => string of the entity type. ex. 'node' or 'user' NOT 'node-content-custom'
 *
 * @return array with status and reason.
 *     'status' => FALSE when no action is take, TRUE when points are credited or debited
 *     'reason' => (string) error message to indicate reason for failure
 */
function userpoints_userpointsapi($params) {

  //Test for the existence of parameters and set defaults if necessary
  if (!isset($params['txn_id'])) {

    //If a txn_id is passed in we'll do an UPDATE thus the std checks don't apply
    if (is_numeric($params)) {
      $points = $params;
      $params = array();
      $params['points'] = $points;
      unset($points);
    }
    if (!is_array($params)) {

      //has to be an array to continue
      return array(
        'status' => FALSE,
        'reason' => 'Parameters did not properly form as an array,
                     this is an internal module error.
                    ',
      );
    }
    if (!isset($params['uid'])) {
      global $user;
      $params['uid'] = $user->uid;
    }

    // check if parameters are set
    $params_null_check = array(
      'operation',
      'description',
      'reference',
      'display',
      'entity_id',
      'entity_type',
    );
    foreach ($params_null_check as $param_null_check) {
      if (!isset($params[$param_null_check])) {
        $params[$param_null_check] = NULL;
      }
    }
    if (!isset($params['moderate'])) {

      // If not passed then site default is used.
      $params['status'] = variable_get(USERPOINTS_POINTS_MODERATION, USERPOINTS_TXN_STATUS_APPROVED);
    }
    else {
      $params['status'] = $params['moderate'] ? USERPOINTS_TXN_STATUS_PENDING : USERPOINTS_TXN_STATUS_APPROVED;
    }
    if (!isset($params['tid']) || !is_numeric($params['tid'])) {

      //if not passed then site default is used
      $params['tid'] = variable_get(USERPOINTS_CATEGORY_DEFAULT_TID, NULL);
    }

    // anonymous users do not get points, and there have to be points to process
    if ($params['uid'] == 0 || $params['points'] == 0) {
      return array(
        'status' => FALSE,
        'reason' => 'uid or points = 0. Anonymous users do not get points
                     and there must be points to process.',
      );
    }
  }
  else {

    //We have a txn_id so we can look up some user information
    $params['uid'] = db_result(db_query('SELECT uid from {userpoints_txn} WHERE txn_id = %d', $params['txn_id']));
  }

  //if txn_id

  // Load the user object that will be awarded the points
  $name = db_result(db_query('SELECT name FROM {users} WHERE uid = %d', $params['uid']));
  if ($name == '') {
    return array(
      'status' => FALSE,
      'reason' => 'invalid uid or user account could not be loaded',
    );
  }

  // Call the _userpoints hook, and stop if one of them returns FALSE
  $rc = module_invoke_all('userpoints', 'points before', $params);
  foreach ($rc as $key => $value) {
    if ($value == FALSE) {

      // Do not process the points
      return array(
        'status' => FALSE,
        'reason' => t('@key returned FALSE from the hook_userpoints points before call', array(
          '@key' => $key,
        )),
      );
    }
  }
  if (isset($params['points']) && $params['points'] < 0) {
    $msg = t('lost');
  }
  elseif (isset($params['status']) && $params['status'] == USERPOINTS_TXN_STATUS_DECLINED) {

    //points have been declined
    $msg = t('was declined');
  }
  elseif (!empty($params['expired'])) {
    $msg = t('expired');
  }
  else {
    $msg = t('earned');
  }
  $ret = _userpoints_transaction($params);

  // Reset the static cache of userpoints.
  userpoints_get_current_points(NULL, NULL, TRUE);
  if ($ret == FALSE) {
    return array(
      'status' => FALSE,
      'reason' => 'transaction failed in _userpoints_transaction, this is an internal module error',
    );
  }
  if ($params['status'] == USERPOINTS_TXN_STATUS_PENDING) {
    $mesg = t('User %uname %op %pointsvalue !points, pending administrator approval.', array_merge(userpoints_translation(), array(
      '%uname' => $name,
      '%op' => $msg,
      '%pointsvalue' => abs($params['points']),
      '%total' => userpoints_get_current_points($params['uid'], $params['tid']),
    )));
  }
  else {
    $mesg = t('User %uname %op %pointsvalue !points Total now is %total !points.', array_merge(userpoints_translation(), array(
      '%uname' => $name,
      '%op' => $msg,
      '%pointsvalue' => abs($params['points']),
      '%total' => userpoints_get_current_points($params['uid'], $params['tid']),
    )));
  }

  //if $params['status']
  if ($mesg && (!empty($params['display']) || empty($params['display']) && variable_get(USERPOINTS_DISPLAY_MESSAGE, 1) == 1)) {
    drupal_set_message($mesg);
  }

  // Call the _userpoints hook to allow modules to act after points are awarded
  module_invoke_all('userpoints', 'points after', $params);
  return array(
    'status' => TRUE,
  );
}

/*
 * Adds the points to the txn table
 * PRIVATE FUNCTION use userpoints_userpointsapi!
 */
function _userpoints_transaction(&$params) {

  //Check, again, for a properly formed array
  if (!is_array($params)) {
    return FALSE;
  }
  $time = time();
  if (!isset($params['txn_id'])) {

    //If a txn_id is preset we UPDATE the record instead of adding one

    //the standard checks don't apply
    if (!is_numeric($params['points'])) {
      return FALSE;
    }
    if (!isset($params['uid'])) {
      global $user;
      $params['uid'] = $user->uid;

      //there must be a UID, anonymous does not receive points
      if (!$params['uid'] > 0) {
        return FALSE;
      }
    }
    if (isset($params['expirydate']) && !is_numeric($params['expirydate'])) {
      return FALSE;
    }

    // check if parameters are set
    $params_null_check = array(
      'operation',
      'description',
      'reference',
      'expired',
      'parent_txn_id',
      'entity_id',
      'entity_type',
    );
    foreach ($params_null_check as $param_null_check) {
      if (!isset($params[$param_null_check])) {
        $params[$param_null_check] = NULL;
      }
    }
    if (!isset($params['tid']) || !is_numeric($params['tid'])) {
      $params['tid'] = variable_get(USERPOINTS_CATEGORY_DEFAULT_TID, NULL);
    }
    elseif ($params['tid'] == 0) {

      //tid with 0 are uncategorized and are set to NULL

      //this is a backwards compatibilty issue
      $params['tid'] = NULL;
    }
    if (!isset($params['expirydate'])) {
      $params['expirydate'] = userpoints_get_default_expiry_date();
    }

    // Use current time for time_stamp if configured to always use the default,
    // not set, not a positive integer or in the future.
    if (variable_get(USERPOINTS_TRANSACTION_TIMESTAMP, 1) || !isset($params['time_stamp']) || $params['time_stamp'] <= 0 || $params['time_stamp'] > $time) {
      $params['time_stamp'] = $time;
    }
  }

  // if txn_id
  // Always force changed timestamp to current time() for transaction tracking.
  $params['changed'] = $time;
  if (!empty($params['txn_id']) && is_numeric($params['txn_id'])) {

    //A transaction ID was passed in so we'll update the transaction
    $result = db_query("SELECT txn_id, uid, approver_uid, points,\n      time_stamp, status, operation, description, reference, expirydate, expired,\n      parent_txn_id, tid, entity_id, entity_type\n      FROM {userpoints_txn}\n      WHERE txn_id = %d", $params['txn_id']);
    $txn = db_fetch_array($result);

    //don't superseed existing keys, just complete missing keys
    $params += $txn;

    //Update existing transaction record for key txn_id
    $ret = drupal_write_record('userpoints_txn', $params, array(
      'txn_id',
    ));

    //Only update if the record has been successfully updated
    if ($ret != FALSE) {
      _userpoints_update_cache($params);
    }
  }
  else {

    //Create new transaction record
    $ret = drupal_write_record('userpoints_txn', $params);

    //don't cache entry if it's pending
    if ($params['status'] != TRUE && $ret != FALSE) {
      _userpoints_update_cache($params);
    }
  }
  return TRUE;
}

//function userpoints_transaction

/*
 * Update the caching table
 */
function _userpoints_update_cache(&$params) {
  if ($params['status'] != 0 || $params['expired'] == 1) {

    //Only update the cache for fully approved non-expired points
    return FALSE;
  }
  if (!isset($params['tid'])) {
    $params['tid'] = NULL;
  }

  // Calculate the current points based upon the tid
  $current_points = (int) $params['points'] + userpoints_get_current_points($params['uid'], $params['tid']);

  //Grab the user's maximum points to preserve it
  $max_points = db_result(db_query('SELECT max_points FROM {userpoints} WHERE uid = %d AND tid = %d', $params['uid'], $params['tid']));
  if ($params['points'] > 0) {

    //points are greater than zero, update their max_points
    $max_points = (int) $params['points'] + (int) $max_points;
  }

  // insert or update the userpoints caching table with the user's current points
  if (_userpoints_user_exists($params['uid'], $params['tid'])) {
    db_query("UPDATE {userpoints}\n              SET points = %d, max_points = %d, last_update = %d\n              WHERE uid = %d AND tid = %d", $current_points, $max_points, time(), $params['uid'], $params['tid']);
  }
  else {
    $result = db_query("INSERT INTO {userpoints}\n      (uid, points, max_points, last_update, tid)\n      VALUES (%d, %d, %d, %d, %d )", $params['uid'], $current_points, $max_points, time(), $params['tid']);
  }
  unset($params);
}

/* Purpose: Determines the correct default expiration date
 * Returns: timestamp
 */
function userpoints_get_default_expiry_date() {
  $expirydate = userpoints_date_to_timestamp(variable_get(USERPOINTS_EXPIREON_DATE, NULL));
  if ($expirydate < time()) {
    $expirydate = variable_get(USERPOINTS_EXPIREAFTER_DATE, NULL);
    if ($expirydate) {
      $expirydate = time() + $expirydate;
    }
  }
  return $expirydate;
}

//userpoints_get_default_expiry_date

/*
 * Purpose: Checks to ensure that a user exists corresponding to a category
 * @param $uid User ID to check for existence of points for the user
 * @param $tid taxonomy id of the category to limit to, if omitted
 *   if the use has points in any category the return is TRUE
 * Returns : TRUE if user found, falase otherwise
 */
function _userpoints_user_exists($uid, $tid = NULL) {
  if (is_numeric($tid)) {
    return (int) db_result(db_query('SELECT COUNT(uid)
      FROM {userpoints}
      WHERE uid = %d AND tid = %d', $uid, $tid));
  }
  else {
    return (int) db_result(db_query('SELECT COUNT(uid)
      FROM {userpoints}
      WHERE uid = %d', $uid));
  }
}

/*
 * Implementation of hook_user()
 *
 */
function userpoints_user($op, &$edit, &$account, $category = '') {
  global $user;
  switch ($op) {
    case 'delete':

      // The user is being deleted, delete all traces in userpoints and txn tables
      db_query('DELETE FROM {userpoints} WHERE uid = %d', $account->uid);
      db_query('DELETE FROM {userpoints_txn} WHERE uid = %d', $account->uid);
      break;
    case 'view':
      if (userpoints_access_my_points($account)) {
        $details = array();
        $details[] = l(t('View'), 'myuserpoints/' . $account->uid, array(
          'attributes' => array(
            'title' => t('View detailed transactions'),
          ),
        ));
        if (user_access(USERPOINTS_PERM_ADMIN)) {
          $details[] = l(t('Adjust'), 'admin/user/userpoints/add/' . $account->uid, array(
            'attributes' => array(
              'title' => t('Add/substract !points from this user', userpoints_translation()),
            ),
          ));
        }
        $details = implode(', ', $details);

        //Which points are we display?
        $points = userpoints_get_current_points($account->uid, variable_get(USERPOINTS_CATEGORY_PROFILE_DISPLAY_TID, 0));
        $account->content['userpoints'] = array(
          '#type' => 'user_profile_category',
          '#title' => t('User !points', userpoints_translation()),
        );
        $account->content['userpoints']['points'] = array(
          '#type' => 'user_profile_item',
          '#title' => t('!Points', userpoints_translation()),
          '#value' => $details ? $points . ' - ' . $details : $points,
        );
      }
      break;
  }
}
function userpoints_admin_manage() {
  $tid = arg(4);
  $cat_count = count(userpoints_get_categories());
  $header = array(
    array(
      'data' => t('User'),
      'field' => 'uid',
    ),
    array(
      'data' => t('Time stamp'),
      'field' => 'time_stamp',
      'sort' => 'desc',
    ),
    array(
      'data' => t('!Points', userpoints_translation()),
      'field' => 'points',
    ),
    array(
      'data' => t('Operation'),
      'field' => 'operation',
    ),
    array(
      'data' => t('Category'),
      'field' => 'cat',
    ),
    array(
      'data' => t('Operation'),
    ),
  );
  $sql = "SELECT p.txn_id, p.uid, p.time_stamp, p.points, p.operation, p.status,\n          p.entity_type, p.entity_id, t.name as cat\n          FROM {userpoints_txn} p\n          LEFT JOIN {term_data} t ON p.tid = t.tid\n          WHERE p.status = %d";

  //Check for filtering
  if (is_numeric($tid) && $tid == 0) {
    $sql .= " AND (p.tid IS NULL OR p.tid = '')";
    $cat = t('!Uncategorized', userpoints_translation());
  }
  elseif (is_numeric($tid)) {
    $sql .= " AND p.tid = %d";
    $cat = db_result(db_query("SELECT name from {term_data} WHERE tid = %d", $tid));
  }
  else {
    $cat = t('All');
  }

  //Set the title of the page
  drupal_set_title(t($cat) . " " . t("!points", userpoints_translation()));
  $sql .= tablesort_sql($header);
  $pager_limit = variable_get(USERPOINTS_REPORT_USERCOUNT, 30);
  $result = pager_query($sql, $pager_limit, 0, NULL, USERPOINTS_TXN_STATUS_PENDING, $tid);
  $rows = array();
  while ($data = db_fetch_object($result)) {
    $user = user_load(array(
      'uid' => $data->uid,
    ));
    if (!$data->cat) {
      $data->cat = t('!Uncategorized', userpoints_translation());
    }
    $operations = module_invoke_all('userpoints', 'entity_type', $data);
    if (is_array($operations) && isset($operations[0])) {
      $operation = $operations[0];
    }
    else {
      switch ($data->entity_type) {
        case 'node':
          $node = node_load($data->entity_id);
          if ($node) {
            $operation = l($data->operation, 'node/' . $node->nid, array(
              'attributes' => array(
                'title' => $node->title,
              ),
            ));
          }
          else {
            $operation = check_plain($data->operation);
          }
          break;
        case 'comment':
          if (module_exists('comment')) {

            //We have to load the comment to get the nid for the comment
            $comment = _comment_load($data->entity_id);
            if ($comment) {
              $operation = l($data->operation, 'node/' . $comment->nid, array(
                'attributes' => array(
                  'title' => $comment->subject,
                ),
                'fragment' => 'comment-' . $comment->cid,
              ));
            }
            else {
              $operation = check_plain($data->operation);
            }
          }
          break;
        default:
          $operation = check_plain($data->operation);
      }
    }
    $rows[] = array(
      array(
        'data' => theme('username', $user),
      ),
      array(
        'data' => format_date($data->time_stamp, 'custom', 'Y-m-d H:i'),
      ),
      array(
        'data' => $data->points,
        'align' => 'right',
      ),
      array(
        'data' => $operation,
      ),
      array(
        'data' => $data->cat,
      ),
      array(
        'data' => l('approve', "admin/user/userpoints/approve/{$data->txn_id}") . ' ' . l('decline', "admin/user/userpoints/decline/{$data->txn_id}") . ' ' . l('edit', "admin/user/userpoints/edit/{$data->txn_id}"),
      ),
    );
  }
  if (!$rows) {

    //no points in moderation
    $rows[] = array(
      array(
        'data' => t('No !points awaiting moderation', userpoints_translation()),
        'colspan' => 6,
        'align' => 'center',
      ),
    );
  }
  if ($cat_count > 1) {
    $output = drupal_get_form('userpoints_filter_cat_select', 'admin/user/userpoints/moderate/', arg(4));
    $output .= theme('table', $header, $rows);
    $output .= theme('pager', NULL, $pager_limit, 0);
  }
  else {
    $output = theme('table', $header, $rows);
    $output .= theme('pager', NULL, $pager_limit, 0);
  }
  return $output;
}

/*
 * Purpose: Approves moderated points
 */
function userpoints_admin_approve() {
  $operation = arg(3);
  $txn_id = (int) arg(4);
  return drupal_get_form('userpoints_confirm_approve', $operation, $txn_id);
}
function userpoints_confirm_approve($form_state, $operation, $txn_id) {
  $form = array(
    'operation' => array(
      '#type' => 'value',
      '#value' => $operation,
    ),
    'txn_id' => array(
      '#type' => 'value',
      '#value' => $txn_id,
    ),
  );
  return confirm_form($form, t('Are you sure you want to @op txn @txn_id', array(
    '@op' => $operation,
    '@txn_id' => $txn_id,
  )), 'admin/user/userpoints/moderate');
}
function userpoints_confirm_approve_submit($form, &$form_state) {
  global $user;
  switch ($form_state['values']['operation']) {
    case 'approve':
      $status = USERPOINTS_TXN_STATUS_APPROVED;
      break;
    case 'decline':
      $status = USERPOINTS_TXN_STATUS_DECLINED;
      break;
    default:
      return FALSE;
  }
  $params = array(
    'txn_id' => $form_state['values']['txn_id'],
    'approver_uid' => $user->uid,
    'status' => $status,
  );
  userpoints_userpointsapi($params);
  $form_state['redirect'] = 'admin/user/userpoints/moderate';
}
function userpoints_admin_txn($form_state) {
  global $user;
  $mode = arg(3);
  $timestamp = format_date(time(), 'custom', 'Y-m-d H:i O');
  if ($mode == 'edit' && arg(4)) {
    $txn_id = (int) arg(4);
    $result = db_query('SELECT * FROM {userpoints_txn} WHERE txn_id = %d', $txn_id);
    $txn = db_fetch_object($result);
    $timestamp = format_date($txn->time_stamp, 'custom', 'Y-m-d H:i O');
    $txn_user = user_load(array(
      'uid' => $txn->uid,
    ));
  }
  elseif ($mode == 'add' && arg(4)) {
    $uid = (int) arg(4);
    $txn_user = user_load(array(
      'uid' => $uid,
    ));
  }
  $form['txn_user'] = array(
    '#type' => 'textfield',
    '#title' => t('User Name'),
    '#size' => 30,
    '#maxlength' => 60,
    '#default_value' => !empty($txn_user->name) ? $txn_user->name : '',
    '#autocomplete_path' => 'user/autocomplete',
    '#description' => t('User Name for the user you want the !points to affect', userpoints_translation()),
  );
  $form['points'] = array(
    '#type' => 'textfield',
    '#title' => t('Points'),
    '#size' => 10,
    '#maxlength' => 10,
    '#default_value' => isset($txn->points) ? $txn->points : 0,
    '#description' => t('Number of !points to add/subtract from the user. For example, 25 (to add !points) or -25 (to subtract !points).', userpoints_translation()),
  );
  $form['time_stamp'] = array(
    '#type' => 'textfield',
    '#title' => t('Date/Time'),
    '#default_value' => $timestamp,
    '#size' => 30,
    '#maxlength' => 30,
    '#description' => t('Date and time of this transaction, in the form YYYY-MM-DD HH:MM +ZZZZ'),
  );
  if (!empty($txn->txn_id)) {
    if (!empty($txn->expirydate)) {
      $expirydate = format_date($txn->expirydate, 'custom', 'Y-m-d H:i O');
    }
  }
  else {

    //If we're not editing we use site defaults
    $expirydate = userpoints_get_default_expiry_date();
    if ($expirydate) {
      $expirydate = format_date($expirydate, 'custom', 'Y-m-d H:i O');
    }
  }
  $form['expirydate'] = array(
    '#type' => 'textfield',
    '#title' => t('Expiration date'),
    '#default_value' => $expirydate,
    '#size' => 30,
    '#maxlength' => 30,
    '#description' => t('Date and time to expire these !points, in the form YYYY-MM-DD HH:MM +ZZZZ', userpoints_translation()) . '<br />' . t('Leave blank for non-expiring !points', userpoints_translation()),
  );
  if (module_exists('taxonomy')) {
    $form['tid'] = array(
      '#type' => 'select',
      '#title' => t('Category'),
      '#default_value' => variable_get(USERPOINTS_CATEGORY_DEFAULT_TID, 0),
      '#options' => userpoints_get_categories(),
      '#description' => t('Category to apply these !points to', userpoints_translation()),
    );
  }
  $form['reference'] = array(
    '#type' => 'textfield',
    '#title' => t('Reference'),
    '#default_value' => !empty($txn->reference) ? $txn->reference : '',
    '#size' => 30,
    '#maxlength' => 128,
    '#description' => t('Enter optional reference for this transaction. This field will be indexed and searchable.'),
  );
  $form['description'] = array(
    '#type' => 'textarea',
    '#title' => t('Description'),
    '#default_value' => !empty($txn->description) ? $txn->description : '',
    '#width' => 70,
    '#lines' => 5,
    '#description' => t('Enter an optional description for this transaction, such as the reason it is created.'),
  );
  switch ($mode) {
    case 'add':
      $form['approver_uid'] = array(
        '#type' => 'hidden',
        '#value' => $user->uid,
      );
      $form['operation'] = array(
        '#type' => 'hidden',
        '#value' => 'admin',
      );
      $form['status'] = array(
        '#type' => 'hidden',
        '#value' => USERPOINTS_TXN_STATUS_PENDING,
      );
      $form['mode'] = array(
        '#type' => 'hidden',
        '#value' => $mode,
      );
      break;
    case 'edit':
      $form['txn_user']['#disabled'] = TRUE;
      unset($form['txn_user']['#autocomplete_path']);
      $form['txn_uid'] = array(
        '#type' => 'value',
        '#value' => $txn->uid,
      );
      $form['txn_id'] = array(
        '#type' => 'value',
        '#value' => $txn_id,
      );
      $form['approver_uid'] = array(
        '#type' => 'textfield',
        '#description' => t('Approver ID'),
        '#default_value' => $txn->approver_uid,
        '#size' => 10,
        '#maxlength' => 7,
        '#description' => t('The user ID of the person who approved this transaction. 0 means not yet approved.'),
      );
      $form['operation'] = array(
        '#type' => 'textfield',
        '#description' => t('Operation'),
        '#default_value' => $txn->operation,
        '#size' => 20,
        '#maxlength' => 20,
        '#description' => t('The operation type for this transaction. Normally, it is "admin".'),
      );
      $form['status'] = array(
        '#title' => t('Approval status'),
        '#type' => 'radios',
        '#options' => userpoints_txn_status(),
        '#description' => t('Approval status of the transaction.'),
        '#default_value' => $txn->status,
      );
      break;
  }
  $form['mode'] = array(
    '#type' => 'hidden',
    '#default_value' => $mode,
  );
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Save'),
  );
  return $form;
}
function userpoints_admin_txn_submit($form, &$form_state) {
  if ($form_state['values']['form_id'] != 'userpoints_admin_txn') {
    return;
  }
  $txn_user = user_load(array(
    'name' => $form_state['values']['txn_user'],
  ));
  switch ($form_state['values']['mode']) {
    case 'add':
      $params = array(
        'points' => $form_state['values']['points'],
        'uid' => $txn_user->uid,
        'operation' => 'admin',
        'description' => $form_state['values']['description'],
        'reference' => $form_state['values']['reference'],
        'tid' => $form_state['values']['tid'],
        'time_stamp' => strtotime($form_state['values']['time_stamp']),
      );
      if ($form_state['values']['expirydate']) {

        //Check for the existence of an expirydate
        $params['expirydate'] = strtotime($form_state['values']['expirydate']);
      }
      userpoints_userpointsapi($params);
      break;
    case 'edit':
      if ($form_state['values']['expirydate']) {
        $expirydate = strtotime($form_state['values']['expirydate']);
      }
      $params = array(
        'uid' => $form_state['values']['txn_uid'],
        'approver_id' => $form_state['values']['approver_uid'],
        'points' => $form_state['values']['points'],
        'time_stamp' => strtotime($form_state['values']['time_stamp']),
        'operation' => $form_state['values']['operation'],
        'description' => $form_state['values']['description'],
        'reference' => $form_state['values']['reference'],
        'status' => $form_state['values']['status'],
        'expirydate' => $expirydate,
        'txn_id' => $form_state['values']['txn_id'],
      );
      userpoints_userpointsapi($params);
  }
  $form_state['redirect'] = 'admin/user/userpoints';
}

/*
 * Provides an administrative Interface for point listing, moderation and adding
 */
function userpoints_admin_points() {
  $tid = arg(3);
  $cat_count = count(userpoints_get_categories());
  $sql = "SELECT p.uid, u.name, p.points, p.tid, t.name as cat\n          FROM {userpoints} p INNER JOIN {users} u USING (uid)\n          LEFT JOIN {term_data} t ON p.tid = t.tid\n          ";

  //Check for filtering
  if ($tid === 0) {
    $sql .= "WHERE p.tid = 0";
    $cat = t('!Uncategorized', userpoints_translation());
  }
  elseif (is_numeric($tid)) {
    $sql .= "WHERE p.tid = %d";
    $cat = db_result(db_query("SELECT name from {term_data} WHERE tid = %d", $tid));
  }
  else {
    $cat = t('All');
  }
  drupal_set_title(t($cat) . " " . t("!points", userpoints_translation()));
  $sql_cnt = "SELECT COUNT(DISTINCT(uid))\n              FROM {userpoints}\n              WHERE tid = %d\n              ";
  if (variable_get(USERPOINTS_REPORT_DISPLAYZERO, 1) == 0) {

    //The user would NOT like to see users with zero points
    $sql .= " AND p.points <> 0";
    $sql_cnt .= " AND points <> 0";
  }
  $sql .= " GROUP BY p.uid, u.name, p.points, p.tid, t.name";
  $header = array(
    array(
      'data' => t('User'),
      'field' => 'u.name',
    ),
    array(
      'data' => t('Category'),
      'field' => 't.name',
    ),
    array(
      'data' => t('!Points', userpoints_translation()),
      'field' => 'p.points',
      'sort' => 'desc',
    ),
  );
  $sql .= tablesort_sql($header);
  $pager_limit = variable_get(USERPOINTS_REPORT_USERCOUNT, 30);
  $result = pager_query($sql, $pager_limit, 0, $sql_cnt, $tid);
  $rows = array();
  while ($data = db_fetch_object($result)) {
    if (!$data->cat) {
      $data->cat = t('!Uncategorized', userpoints_translation());
    }
    $rows[] = array(
      array(
        'data' => theme('username', $data) . "&nbsp;&nbsp;" . l(t("(details)"), "myuserpoints/{$data->uid}"),
      ),
      array(
        'data' => $data->cat,
        'align' => 'right',
      ),
      array(
        'data' => $data->points,
        'align' => 'right',
      ),
    );
  }

  //If there is only one category there is no sense in display the category filter dropdown
  if ($cat_count > 1) {
    $output = drupal_get_form('userpoints_filter_cat_select', 'admin/user/userpoints/', arg(3));
    $output .= theme('table', $header, $rows);
    $output .= theme('pager', NULL, $pager_limit, 0);
  }
  else {
    $output = theme('table', $header, $rows);
    $output .= theme('pager', NULL, $pager_limit, 0);
  }
  return $output;
}

/*
 * Purpose: List the users and their point totals by all or by category
 * Returns: HTML from the theme function
 */
function userpoints_list_users() {
  $tid = arg(1);
  $sql = "SELECT p.uid, u.name, p.points, p.tid, t.name as cat\n          FROM {userpoints} p INNER JOIN {users} u ON p.uid = u.uid AND u.status = 1\n          LEFT JOIN {term_data} t ON p.tid = t.tid\n          ";

  //Check for filtering
  if (is_numeric($tid)) {
    if ($tid > 0) {
      $sql .= "WHERE p.tid = %d";
      $cat = db_result(db_query("SELECT name from {term_data} WHERE tid = %d", $tid));
    }
    else {
      $sql .= "WHERE p.tid = %d";
      $cat = t('!Uncategorized', userpoints_translation());
    }
  }
  else {
    $cat = t('All');
  }
  $sql_cnt = "SELECT COUNT(DISTINCT(uid))\n              FROM {userpoints}\n              WHERE tid = %d\n              ";
  if (variable_get(USERPOINTS_REPORT_DISPLAYZERO, 1) == 0) {

    //The user would NOT like to see users with zero points
    $sql .= " AND p.points <> 0";
    $sql_cnt .= " AND points <> 0";
  }
  $sql .= " GROUP BY p.uid, u.name, p.points, p.tid, t.name";
  $header = theme('userpoints_list_users_header');
  $sql .= tablesort_sql($header);
  $pager_limit = variable_get(USERPOINTS_REPORT_USERCOUNT, 30);
  $result = pager_query($sql, $pager_limit, 0, $sql_cnt, $tid);
  $rows = array();
  while ($data = db_fetch_object($result)) {
    $rows[] = theme('userpoints_list_users_row', $data);
  }
  drupal_set_title(t($cat) . " " . t("!points", userpoints_translation()));
  return theme('userpoints_list_users', $header, $rows, $tid, $pager_limit);
}

/*
 * Themes the output of users by points page accessible at /userpoints
 * individual rows are themed with theme_userpoints_list_users_rows
 *
 * @return
 *   HTML of the table and a pager
 */
function theme_userpoints_list_users($header, $rows, $tid, $pager_limit) {

  // If there is only one category, there is no sense in display the category filter dropdown
  if (count(userpoints_get_categories()) > 1) {
    $output = drupal_get_form('userpoints_filter_cat_select', 'userpoints/', $tid);
    $output .= theme('table', $header, $rows);
    $output .= theme('pager', NULL, $pager_limit, 0);
  }
  else {
    $output = theme('table', $header, $rows);
    $output .= theme('pager', NULL, $pager_limit, 0);
  }
  return $output;
}

/*
 * Themes the header of the table on the "user by points" page accessible at /userpoints
 *
 * @return
 *   An array suitable for use with tablesort_sql
 */
function theme_userpoints_list_users_header() {
  return array(
    array(
      'data' => t('User'),
      'field' => 'u.name',
    ),
    array(
      'data' => t('Category'),
      'field' => 't.name',
    ),
    array(
      'data' => t('!Points', userpoints_translation()),
      'field' => 'p.points',
      'sort' => 'desc',
    ),
  );
}

/*
 * Themes the output of a single row of the "user by points" page at /userpoints
 * @return
 *   Array for a single row, suitable for inclusion with theme_table
 */
function theme_userpoints_list_users_row($row) {
  global $user;
  if (!$row->cat) {
    $row->cat = t('!Uncategorized', userpoints_translation());
  }
  $details = '';
  if ($user->uid == $row->uid) {
    $details = "&nbsp;&nbsp;" . l(t("(details)"), "myuserpoints");
  }
  return array(
    array(
      'data' => theme('username', $row) . $details,
    ),
    array(
      'data' => $row->cat,
      'align' => 'right',
    ),
    array(
      'data' => $row->points,
      'align' => 'right',
    ),
  );
}

/*
 * Purpose: Provides a dropdown to filter by category
 */
function userpoints_filter_cat_select($form_state, $path, $tid) {
  $current_cat = url($path . $tid);
  $form = array();
  $formname = 'catselect';
  $sql = "SELECT DISTINCT p.tid, t.name\n          FROM {userpoints_txn} p\n          LEFT JOIN {term_data} t on p.tid = t.tid";
  $cats = userpoints_get_categories();
  $options = array();
  $options[url($path)] = t('Display all');
  foreach ($cats as $key => $value) {
    $options[url($path . $key)] = $value;
  }
  $form['catselect'] = array(
    '#type' => 'select',
    '#name' => $formname,
    '#id' => $formname,
    '#title' => t('Filter by category'),
    '#default_value' => $current_cat,
    '#options' => $options,
    '#multiple' => FALSE,
    '#required' => FALSE,
    '#attributes' => array(
      'onChange' => "top.location.href=document.getElementById('{$formname}').options[document.getElementById('{$formname}').selectedIndex].value",
    ),
  );
  return $form;
}

//userpoints_list_users_cat_select
function userpoints_block($op = 'list', $delta = 0, $edit = array()) {
  global $user;
  switch ($op) {
    case 'list':
      $blocks[-1]['info'] = t('User\'s !points', userpoints_translation());

      //Grab a list of the available terms
      $terms = userpoints_get_categories();
      foreach ($terms as $key => $value) {
        $blocks[$key]['info'] = t("Highest {$value} !points", userpoints_translation());
      }

      //foreach
      return $blocks;
    case 'view':
      if ($delta == -1 && (user_access(USERPOINTS_PERM_VIEW) || user_access(USERPOINTS_PERM_VIEW_OWN))) {
        $title = t('@user\'s !points', array_merge(array(
          '@user' => $user->name,
        ), userpoints_translation()));
        if ($user->uid) {
          $points = userpoints_get_current_points($user->uid, variable_get(USERPOINTS_CATEGORY_PROFILE_DISPLAY_TID, 0));
          $show_points = format_plural($points, '!point', '!points', userpoints_translation());
          $content = t('You have %p %c', array(
            '%p' => $points,
            '%c' => $show_points,
          ));
        }
        else {
          $content = t('!Points are visible to logged in users only', userpoints_translation());
        }
      }
      elseif (user_access(USERPOINTS_PERM_VIEW)) {

        //$delta is our tid for pulling the points

        //if 0 we pull 0 or NULL
        $title = t('Highest Users');
        $num = variable_get('userpoints_block_up_records_' . $delta, 5);
        $sql = 'SELECT p.uid, u.name, p.points FROM {userpoints} p INNER JOIN {users} u ON u.uid = p.uid AND u.status = 1';
        if ($delta == 0) {
          $sql .= ' WHERE p.tid = 0 OR p.tid IS NULL ORDER BY p.points DESC';
          $result = db_query_range($sql, 0, $num);
        }
        else {
          $sql .= ' WHERE p.tid = %d ORDER BY p.points DESC';
          $result = db_query_range($sql, $delta, 0, $num);
        }
        while ($data = db_fetch_object($result)) {
          $rows[] = array(
            array(
              'data' => theme('username', $data),
            ),
            array(
              'data' => $data->points,
              'align' => 'right',
            ),
          );
        }
        $header = array(
          t('User'),
          t('!Points', userpoints_translation()),
        );
        $content = theme('table', $header, $rows);
        $content .= '<div class="more-link">' . l(t('more'), 'userpoints/' . $delta, array(
          'attributes' => array(
            'title' => t('All users by !points', userpoints_translation()),
          ),
        )) . '</div>';
      }

      //if $delta
      $block['subject'] = $title;
      $block['content'] = $content;
      return $block;
    case 'configure':
      $form['up_records'] = array(
        '#type' => 'select',
        '#title' => t('Number of users to display'),
        '#default_value' => variable_get('userpoints_block_up_records_' . $delta, 10),
        '#options' => array(
          1 => 1,
          5 => 5,
          10 => 10,
          15 => 15,
          20 => 20,
          30 => 30,
          40 => 40,
          50 => 50,
          60 => 60,
          70 => 70,
          80 => 80,
          90 => 90,
          100 => 100,
          200 => 200,
        ),
        '#description' => t('Limit the number of users displayed to this value'),
      );
      return $form;
    case 'save':
      variable_set('userpoints_block_up_records_' . $delta, $edit['up_records']);
      break;
  }

  //switch $op
}

/*
 * returns an array of possible expiry times
 * to the administrative settings page
 */
function expiry_dates() {
  return array(
    NULL => 'Never',
    3600 => 'One hour',
    86400 => 'One Day',
    604800 => 'One Week',
    1209600 => 'Two Weeks',
    2419200 => 'Four Weeks',
    31536000 => '365 Days',
  );
}

//expiry_dates

/* userpoints_date_to_timestamp
 * Purpose: modifies FAPI date setting to timestamp
 * Returns UNIX timestamp
 */
function userpoints_date_to_timestamp($date) {

  //This takes the FAPI date form array and returns a timestamp
  if ($date) {
    return mktime(0, 0, 0, $date['month'], $date['day'], $date['year']);
  }
}

/*
 * Purpose: Finds all transactions with a expirydate < time() and posts
 *          opposite transactions (sum of 0)
 */
function userpoints_expire_transactions() {
  $sql = "SELECT txn_id, uid, points, time_stamp, operation, description\n          FROM {userpoints_txn}\n          WHERE status = 0 AND expired = 0\n          AND (expirydate < %d AND expirydate != 0)";
  $results = db_query($sql, time());
  while ($line = db_fetch_array($results)) {
    $line['time_stamp'] = format_date($line['time_stamp'], 'custom', 'Y-m-d H:i');
    $description = t(variable_get(USERPOINTS_EXPIRY_DESCRIPTION, NULL), array_merge(userpoints_translation(), array(
      '!operation' => $line['operation'],
      '!description' => $line['description'],
      '!txn_id' => $line['txn_id'],
      '!date' => $line['time_stamp'],
    )));

    //$description
    $params = array(
      'points' => -$line['points'],
      'uid' => $line['uid'],
      'operation' => 'expiry',
      'description' => $description,
      'parent_txn_id' => $line['txn_id'],
      'moderate' => FALSE,
      'expirydate' => 0,
    );
    userpoints_userpointsapi($params);

    //Ok we've expired the entry lets update the original entry to set the expired flag
    $params = array(
      'txn_id' => $line['txn_id'],
      'expired' => 1,
    );
    userpoints_userpointsapi($params);
  }
}

//userpoints_expire_transactions

/*
 * Implementation of hook_cron to expire points
 */
function userpoints_cron() {
  userpoints_expire_transactions();
}

/*
 * Purpose: Displays a detailed transaction report for an individual user
 * no URL argument returns current user otherwise accepts uid.
 */
function userpoints_list_my_userpoints($points_user) {
  $overall_total = 0;
  $unapproved_total = 0;
  $approved_total = 0;
  $title = t('!Points for ', userpoints_translation()) . check_plain($points_user->name);
  drupal_set_title($title);

  //Grab all tids for this user
  $sql = "SELECT p.tid, t.name\n    FROM {userpoints_txn} p\n    LEFT JOIN {term_data} t on p.tid = t.tid\n    WHERE uid = %d\n    GROUP BY p.tid, t.name";
  $results = db_query($sql, $points_user->uid);
  $grand_total = 0;
  while ($result = db_fetch_array($results)) {
    if ($result['name'] == NULL) {
      $result['name'] = t('!Uncategorized', userpoints_translation());
    }

    //pull the sum from the caching table for resource reason and b/c the
    $result['total'] = userpoints_get_current_points($points_user->uid, $result['tid']);
    $args['subtotals'][$result['tid']] = $result;

    //maintain a grand total
    $grand_total += $result['total'];
  }
  $args['approved_total'] = $grand_total;

  //Grab the unmoderated point total
  $args['unapproved_total'] = (int) db_result(db_query("SELECT SUM(points) FROM {userpoints_txn} WHERE uid = %d AND status = 1", $points_user->uid));
  $args['overall_total'] = $args['approved_total'] + $args['unapproved_total'];
  $header = array(
    array(
      'data' => t('!Points', userpoints_translation()),
      'field' => 'points',
    ),
    array(
      'data' => t('Approved?'),
      'field' => 'status',
    ),
    array(
      'data' => t('Date'),
      'field' => 'time_stamp',
      'sort' => 'desc',
    ),
    array(
      'data' => t('Operation'),
      'field' => 'operation',
    ),
    array(
      'data' => t('Category'),
      'field' => 'cat',
    ),
    array(
      'data' => t('Description'),
      'field' => 'description',
    ),
  );
  $sql = "SELECT p.points, p.time_stamp, p.operation, p.description, p.status, p.tid, t.name as cat , p.entity_id, p.entity_type\n           FROM {userpoints_txn} p\n           LEFT JOIN {term_data} t on p.tid = t.tid\n           WHERE p.uid = %d";
  $sql .= tablesort_sql($header);
  $pager_limit = variable_get(USERPOINTS_REPORT_LIMIT, 10);
  $result = pager_query($sql, $pager_limit, 0, NULL, $points_user->uid);
  $stati = userpoints_txn_status();
  $num_rows = 0;
  $rows = array();
  while ($row = db_fetch_object($result)) {
    $num_rows++;
    $status = $stati[$row->status];
    if (!$row->cat) {
      $row->cat = t('!Uncategorized', userpoints_translation());
    }
    if ($row->description) {
      $description = filter_xss($row->description);
    }
    else {
      $description = t('None');
    }
    $operations = module_invoke_all('userpoints', 'entity_type', $row);
    if (is_array($operations) && isset($operations[0])) {
      $operation = $operations[0];
    }
    else {
      switch ($row->entity_type) {
        case 'node':
          $node = node_load($row->entity_id);
          if ($node) {
            $operation = l($row->operation, 'node/' . $node->nid, array(
              'attributes' => array(
                'title' => $node->title,
              ),
            ));
          }
          else {
            $operation = check_plain($row->operation);
          }
          break;
        case 'comment':
          if (module_exists('comment')) {

            //We have to load the comment to get the nid for the comment
            $comment = _comment_load($row->entity_id);
            if ($comment) {
              $operation = l($row->operation, 'node/' . $comment->nid, array(
                'attributes' => array(
                  'title' => $comment->subject,
                ),
                'fragment' => 'comment-' . $comment->cid,
              ));
            }
            else {
              $operation = check_plain($row->operation);
            }
          }
          break;
        case 'user':
          $operation = l($row->operation, 'user/' . $row->entity_id);
          break;
        default:
          $operation = check_plain($row->operation);
      }
    }
    $rows[] = array(
      array(
        'data' => $row->points,
        'align' => 'center',
      ),
      array(
        'data' => $status,
        'align' => 'center',
      ),
      array(
        'data' => format_date($row->time_stamp, 'small'),
        'align' => 'center',
      ),
      array(
        'data' => $operation,
      ),
      array(
        'data' => $row->cat,
      ),
      array(
        'data' => $description,
      ),
    );
    if ($num_rows <= 0) {
      $rows[] = array(
        array(
          'data' => t('No !Points earned', userpoints_translation()),
          'colspan' => 5,
          'align' => 'center',
        ),
      );
    }
  }
  return theme('userpoints_list_my_userpoints', $args, $header, $rows);
}

/*
 *  Returns the Vocabulary ID (vid) used by userpoints for categorization
 *  if no vocab exists it will create one
 */
function userpoints_get_vid() {
  if (!module_exists('taxonomy')) {
    return FALSE;
  }

  //code lovingly inspired by the image.module w/ code by drewish
  $vid = variable_get(USERPOINTS_CATEGORY_DEFAULT_VID, '');
  if (empty($vid) || !taxonomy_vocabulary_load($vid)) {
    $sql = "SELECT vid FROM {vocabulary} WHERE module='userpoints'";
    $vid = db_result(db_query($sql));
    if (!$vid) {
      drupal_set_message(t("Created Userpoints vocabulary"));

      //No vocabulary exists, we'll create one
      $vocab = array(
        'name' => t(USERPOINTS_CATEGORY_NAME),
        'description' => t('Automatically created by the userpoints module'),
        'multiple' => '0',
        'required' => '0',
        'hierarchy' => '1',
        'relations' => '0',
        'module' => 'userpoints',
      );
      taxonomy_save_vocabulary($vocab);
      $vid = $vocab['vid'];
    }
    variable_set(USERPOINTS_CATEGORY_DEFAULT_VID, $vid);
  }
  if (!is_numeric($vid)) {
    watchdog('userpoints', 'userpoints module was unable to select or create a vocabulary. !Points will be uncategorized', array(), WATCHDOG_ERROR);
  }
  return $vid;
}

/*
 * Purposes: returns an array of possible categories, suitable for inclusion in FAPI
 */
function userpoints_get_categories() {

  //Create the "Uncategorized" category
  $options = array();
  $options[0] = t('!Uncategorized', userpoints_translation());
  if (module_exists('taxonomy')) {
    $vid = userpoints_get_vid();
    if ($vid) {
      $tree = taxonomy_get_tree($vid, 0, -1, 1);
      foreach ($tree as $term) {
        $options[$term->tid] = $term->name;
      }
    }
  }
  return $options;
}

/*
 * Wrapper function to return the default tid via API call
 */
function userpoints_get_default_tid() {
  return variable_get(USERPOINTS_CATEGORY_DEFAULT_TID, NULL);
}

/*
 * Purpose: Allow my_userpoints to be themeable
 */
function theme_userpoints_list_my_userpoints($args, $header, $rows) {
  $pager_limit = variable_get(USERPOINTS_REPORT_LIMIT, 10);
  $output = theme('table', $header, $rows);
  $output .= "<p>";
  if (isset($args) && isset($args['subtotals'])) {
    foreach ($args['subtotals'] as $tid => $data) {
      $output .= '<strong>' . $data['name'] . ' ' . t('!points Balance', userpoints_translation()) . ':</strong> ' . $data['total'] . '<br />';
    }
  }
  $output .= "</p>";
  $output .= "<p>";
  $output .= '<strong>' . t('Approved !points Balance', userpoints_translation()) . ':</strong> ' . $args['approved_total'] . '<br />';
  $output .= '<strong>' . t('!Points awaiting moderation', userpoints_translation()) . ':</strong> ' . $args['unapproved_total'] . '<br />';
  $output .= '<strong>' . t('Net !points Balance', userpoints_translation()) . ':</strong> ' . $args['overall_total'] . '<br />';
  $output .= "</p>";
  $output .= theme('pager', NULL, $pager_limit, 0);
  return $output;
}

/**
 *  Implement hook_views_api().
 */
function userpoints_views_api() {
  return array(
    'api' => 2.0,
  );
}

Functions

Namesort descending Description
expiry_dates
theme_userpoints_list_my_userpoints
theme_userpoints_list_users
theme_userpoints_list_users_header
theme_userpoints_list_users_row
userpoints_access_my_points Checks if user can access their points - used via hook_menu().
userpoints_admin_approve
userpoints_admin_manage
userpoints_admin_points
userpoints_admin_settings menu callback for settings form.
userpoints_admin_txn
userpoints_admin_txn_submit
userpoints_block
userpoints_confirm_approve
userpoints_confirm_approve_submit
userpoints_cron
userpoints_date_to_timestamp
userpoints_expire_transactions
userpoints_filter_cat_select
userpoints_get_categories
userpoints_get_current_points Get current points of a user.
userpoints_get_default_expiry_date
userpoints_get_default_tid
userpoints_get_max_points
userpoints_get_vid
userpoints_help Implementation of hook_help().
userpoints_list_my_userpoints
userpoints_list_my_userpoints_forward Forward to the myuserpoints page of the current user.
userpoints_list_users
userpoints_menu Implementation of hook_menu().
userpoints_perm Implementation of hook_perm().
userpoints_theme Implementation of hook_theme().
userpoints_token_list Implementation of hook_token_list().
userpoints_token_values Implementation of hook_token_values().
userpoints_translation Purpose: Returns an array of common translation placeholders
userpoints_txn_status
userpoints_user
userpoints_userpointsapi
userpoints_views_api Implement hook_views_api().
_userpoints_transaction
_userpoints_update_cache
_userpoints_user_exists

Constants