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, 'General')), ); } return $trans; } /* * Returns an array of possible transaction statuses. */ function userpoints_txn_status() { static $stati; if (empty($stati)) { $stati = array( USERPOINTS_TXN_STATUS_APPROVED => t('Approved'), USERPOINTS_TXN_STATUS_PENDING => t('Pending'), USERPOINTS_TXN_STATUS_DECLINED => t('Declined'), ); } return $stati; } /** * Implements 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()); } } /** * Checks access for administrative functionality. * * Provides simplified access checks for the administrative permissions: * - administer userpoints * - add userpoints * - edit userpoints * - moderate userpoints * * @param $type * The access type to check. The administer permission has access to all of * them. Supported strings: * - list: Access to the userpoints list, default local task. All * administrative permissions have access to this. * - add: Permission to add new userpoint transactions. * - edit: Permission to edit existing userpoint transactions. * - moderate: Permission to approve/decline pending transactions. * - administer: Unlimited userpoints permissions, used for settings page. * * @return * TRUE if the current user has access, FALSE if not. */ function userpoints_admin_access($type = 'list') { // Administer userpoints permission has full access. if (user_access('administer userpoints')) { return TRUE; } switch ($type) { // All admin permissions have access to the list page. case 'list': return user_access('add userpoints') || user_access('edit userpoints') || user_access('moderate userpoints'); break; case 'add': return user_access('add userpoints'); break; case 'edit': return user_access('edit userpoints'); break; case 'moderate': return user_access('moderate userpoints'); break; case 'administer': // administer permission was already checked, this exists for // documentation purposes only. break; } return FALSE; } /** * Implements hook_menu(). */ function userpoints_menu() { $items = array(); $items['admin/config/people/userpoints'] = array( 'title' => '!Points', 'title arguments' => userpoints_translation(), 'description' => strtr('Manage !points', userpoints_translation()), 'page callback' => 'userpoints_admin_points', 'access callback' => 'userpoints_admin_access', 'access arguments' => array('list'), 'file' => 'userpoints.admin.inc', ); $items['admin/config/people/userpoints/list'] = array( 'title' => 'List', 'description' => strtr('List users by !points', userpoints_translation()), 'access callback' => 'userpoints_admin_access', 'access arguments' => array('list'), 'file' => 'userpoints.admin.inc', 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -2, ); $items['admin/config/people/userpoints/list/totals'] = array( 'title' => 'Totals', 'description' => strtr('List users by !points', userpoints_translation()), 'access callback' => 'userpoints_admin_access', 'access arguments' => array('list'), 'file' => 'userpoints.admin.inc', 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => 0, ); $items['admin/config/people/userpoints/list/transactions'] = array( 'title' => 'Transactions', 'title arguments' => userpoints_translation(), 'description' => 'List transactions', 'page callback' => 'userpoints_admin_transactions', 'access callback' => 'userpoints_admin_access', 'access arguments' => array('edit'), 'file' => 'userpoints.admin.inc', 'type' => MENU_LOCAL_TASK, 'weight' => 2, ); $items['admin/config/people/userpoints/moderate'] = array( 'title' => 'Moderation', 'title arguments' => userpoints_translation(), 'description' => strtr('Review !points in moderation', userpoints_translation()), 'page callback' => 'userpoints_admin_manage', 'access callback' => 'userpoints_admin_access', 'access arguments' => array('moderate'), 'file' => 'userpoints.admin.inc', 'type' => MENU_LOCAL_TASK, 'weight' => -1, ); $items['admin/config/people/userpoints/add'] = array( 'title' => 'Add', 'description' => 'Admin add/delete userpoints', 'page callback' => 'drupal_get_form', 'page arguments' => array('userpoints_admin_txn', 4, 5), 'access callback' => 'userpoints_admin_access', 'access arguments' => array('add'), 'file' => 'userpoints.admin.inc', 'type' => MENU_LOCAL_TASK, 'weight' => 0, ); $items['admin/config/people/userpoints/edit'] = array( 'title' => 'Edit', 'page callback' => 'drupal_get_form', 'page arguments' => array('userpoints_admin_txn', 4, 5), 'access callback' => 'userpoints_admin_access', 'access arguments' => array('edit'), 'file' => 'userpoints.admin.inc', 'type' => MENU_CALLBACK ); $items['admin/config/people/userpoints/approve'] = array( 'title' => 'Approve Userpoints', 'page callback' => 'userpoints_admin_approve', 'page arguments' => array(4, 5), 'access callback' => 'userpoints_admin_access', 'access arguments' => array('moderate'), 'file' => 'userpoints.admin.inc', 'type' => MENU_CALLBACK ); $items['admin/config/people/userpoints/decline'] = array( 'title' => 'Approve !points', 'title arguments' => userpoints_translation(), 'page callback' => 'userpoints_admin_approve', 'page arguments' => array(4, 5), 'access callback' => 'userpoints_admin_access', 'access arguments' => array('moderate'), 'file' => 'userpoints.admin.inc', 'type' => MENU_CALLBACK ); $items['admin/config/people/userpoints/settings'] = 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 callback' => 'userpoints_admin_access', 'access arguments' => array('administer'), 'file' => 'userpoints.admin.inc', 'type' => MENU_LOCAL_TASK, 'weight' => 10, ); $items['userpoints'] = array( 'title' => 'Users by !points', 'title arguments' => userpoints_translation(), 'page callback' => 'userpoints_list_users', 'access arguments' => array('view userpoints'), 'file' => 'userpoints.pages.inc', 'type' => MENU_NORMAL_ITEM, ); $items['userpoints/operation-autocomplete'] = array( 'title' => 'Operation autocomplete', 'page callback' => 'userpoints_operation_autocomplete', 'access callback' => 'userpoints_admin_access', 'access arguments' => array('add'), 'file' => 'userpoints.admin.inc', 'type' => MENU_CALLBACK, ); $items['userpoints/view/%userpoints_transaction'] = array( 'title' => '!Point transaction', 'title callback' => 'userpoints_view_transaction_title', 'title arguments' => array(2), 'page callback' => 'userpoints_view_transaction', 'page arguments' => array(2), 'access callback' => 'userpoints_access_view_transaction', 'access arguments' => array(2), 'file' => 'userpoints.pages.inc', 'type' => MENU_NORMAL_ITEM, ); $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), 'file' => 'userpoints.pages.inc', 'type' => MENU_NORMAL_ITEM, 'menu_name' => 'user-menu', ); 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 userpoints_admin_access('edit'); } return (user_access('view userpoints') && user_is_logged_in()) || user_access('view own userpoints'); } /** * Checks if a user has access to a transaction. * * @return * TRUE if the user has permissions to view the transaction. */ function userpoints_access_view_transaction($transaction) { if (empty($transaction->user)) { $account = user_load($transaction->uid); } else { $account = $transaction->user; } return userpoints_access_my_points($account); } /** * Title callback function, display transaction ID. * @param $transaction * Transaction object. * @return * Page title. */ function userpoints_view_transaction_title($transaction) { return t('View !points transaction #@id', array('@id' => $transaction->txn_id) + userpoints_translation()); } /** * Implements hook_permission(). */ function userpoints_permission() { return array( 'view own userpoints' => array( 'title' => t('View own !points', userpoints_translation()), 'description' => t('Allows to view own !points, including own !point transactions.', userpoints_translation()), ), 'view userpoints' => array( 'title' => t('View all !points', userpoints_translation()), 'description' => t('Allows to view the !points of other users, but not the transactions.', userpoints_translation()), ), 'add userpoints' => array( 'title' => t('Add new !point transactions', userpoints_translation()), 'description' => t('Allows to create new !point transactions.', userpoints_translation()), ), 'edit userpoints' => array( 'title' => t('Edit !point transactions', userpoints_translation()), 'description' => t('Allows to modify existing !point transactions, including the ability to view transaction history for all users.', userpoints_translation()), ), 'moderate userpoints' => array( 'title' => t('Moderate !point transactions', userpoints_translation()), 'description' => t('Allows to approve or disapprove !point transactions.', userpoints_translation()), ), 'administer userpoints' => array( 'title' => t('Administer Userpoints'), 'description' => t('Allows to configure the settings and includes full read and write access of all !point transactions.', userpoints_translation()), ), ); } /** * Implements hook_theme(). */ function userpoints_theme() { return array( 'userpoints_list_users' => array( 'variables' => array( 'header' => NULL, 'rows' => NULL, 'tid' => NULL, 'pager_limit' => NULL, ), 'file' => 'userpoints.theme.inc', ), 'userpoints_list_users_header' => array( 'variables' => array(), 'file' => 'userpoints.theme.inc', ), 'userpoints_view_category' => array( 'render element' => 'element', 'file' => 'userpoints.theme.inc', ), 'userpoints_view_item' => array( 'render element' => 'element', 'file' => 'userpoints.theme.inc', ), 'userpoints_list_users_row' => array( 'variables' => array( 'row' => NULL, ), 'file' => 'userpoints.theme.inc', ), ); } /** * Implements hook_tokens(). */ function userpoints_tokens($type, $tokens, array $data = array(), array $options = array()) { if ($type == 'user' && isset($data['user']) && isset($tokens['userpoints'])) { return array($tokens['userpoints'] => userpoints_get_current_points($data['user']->uid)); } } /** * Implements hook_token_list(). */ function userpoints_token_list($type = 'all') { return array( 'tokens' => array( 'user' => array( 'name' => t('User points'), 'description' => t('The number of points a user has.'), ) ) ); } /** * Get current points of a user. * * @param $uid * User id of the user to get or lose the points. * * @return * Number of current points in that user's account. */ function userpoints_get_current_points($uid = NULL, $tid = NULL) { if (!$uid) { global $user; $uid = $user->uid; } // 0 is a valid value for the Uncategorized category. if (!isset($tid)) { $tid = userpoints_get_default_tid(); } elseif ($tid === 'all') { return (int) db_query('SELECT SUM(points) FROM {userpoints} WHERE uid = :uid', array(':uid' => $uid))->fetchField(); } return (int) db_query('SELECT points FROM {userpoints} WHERE uid = :uid AND tid = :tid', array(':uid' => $uid, ':tid' => $tid))->fetchField(); } /** * Gets the number of maximal points of that user. * * @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) { $max = drupal_static(__FUNCTION__, 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 (!isset($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_query('SELECT SUM(max_points) FROM {userpoints} WHERE uid = :uid', array(':uid' => $uid))->fetchField(); } else { // A term ID is specified, so fetch its maximum points. $max[$uid][$tid] = db_query('SELECT max_points FROM {userpoints} WHERE uid = :uid AND tid = :tid', array(':uid' => $uid, ':tid' => $tid))->fetchField(); } } // Return the cached value. return $max[$uid][$tid]; } /** * Save userpoint changes and call hooks. * * @param $params * 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) { global $user; // 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_int($params)) { $params = array('points' => $params); } 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'])) { $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'] = userpoints_get_default_tid(); } // 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_query('SELECT uid from {userpoints_txn} WHERE txn_id = :txn_id', array(':txn_id' => $params['txn_id']))->fetchField(); } // If txn_id. // Load the user object that will be awarded the points. $account = user_load($params['uid']); if (!$account) { 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 = userpoints_invoke_all('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)), ); } } $ret = _userpoints_transaction($params); if ($ret == FALSE) { return array( 'status' => FALSE, 'reason' => 'transaction failed in _userpoints_transaction, this is an internal module error', ); } // Allow modules to define custom messages. if (!empty($params['message'])) { $message = $params['message']; } // Display message if either display property is not set and messages should // be displayed by default or display property is not FALSE. elseif (!empty($params['display']) || (!isset($params['display']) && variable_get(USERPOINTS_DISPLAY_MESSAGE, 1))) { // Prepare arguments. They are the same for all string combinations. $categories = userpoints_get_categories(); $arguments = array_merge(userpoints_translation(), array( '!username' => theme('username', array('account' => $account)), '%total' => userpoints_get_current_points($params['uid'], $params['tid']), '%category' => isset($categories[$params['tid']]) ? $categories[$params['tid']] : $categories[0], )); $view_own_points = user_access('view own userpoints') || user_access('view userpoints') || user_access('administer userpoints'); $view_all_points = user_access('view userpoints') || user_access('administer userpoints'); if ($params['status'] == USERPOINTS_TXN_STATUS_DECLINED) { // Points have been declined. if ($account->uid == $user->uid && $view_own_points) { $message = format_plural($params['points'], 'You did not receive approval for @count !point in the %category category.', 'You did not receive approval for @count !points in the %category category.', $arguments); } elseif ($view_all_points) { $message = format_plural($params['points'], '!username did not receive approval for @count !point in the %category category.', '!username did not receive approval for @count !points in the %category category.', $arguments); } } elseif (isset($params['points']) && $params['points'] < 0) { if ($params['status'] == USERPOINTS_TXN_STATUS_PENDING) { if ($account->uid == $user->uid && $view_own_points) { // Directly address the user if he is loosing points. $message = format_plural(abs($params['points']), 'You just had a !point deducted, pending administrator approval.', 'You just had @count !points deducted, pending administrator approval.', $arguments); } elseif ($view_all_points) { // Only display message about other users if user has permission to view userpoints. $message = format_plural(abs($params['points']), '!username just had a !point deducted, pending administrator approval.', '!username just had @count !points deducted, pending administrator approval.', $arguments); } } else { if ($account->uid == $user->uid && $view_own_points) { $message = format_plural(abs($params['points']), 'You just had a !point deducted and now have %total !points in the %category category.', 'You just had @count !points deducted and now have %total !points in the %category category.', $arguments); } elseif ($view_all_points) { $message = format_plural(abs($params['points']), '!username just had a !point deducted and now has %total !points in the %category category.', '!username just had @count !points deducted and now has %total !points in the %category category.', $arguments); } } } elseif (!empty($params['points'])) { if ($params['status'] == USERPOINTS_TXN_STATUS_PENDING) { if ($account->uid == $user->uid && $view_own_points) { // Directly address the user if he is loosing points. $message = format_plural(abs($params['points']), 'You just earned a !point, pending administrator approval.', 'You just earned @count !points, pending administrator approval.', $arguments); } elseif ($view_all_points) { // Only display message about other users if user has permission to view userpoints. $message = format_plural(abs($params['points']), '!username just earned a !point, pending administrator approval.', '!username just earned @count !points, pending administrator approval.', $arguments); } } else { if ($account->uid == $user->uid && $view_own_points) { $message = format_plural(abs($params['points']), 'You just earned a !point and now have %total !points in the %category category.', 'You just earned @count !points and now have %total !points in the %category category.', $arguments); } elseif ($view_all_points) { $message = format_plural(abs($params['points']), '!username just earned a !point and now has %total !points in the %category category.', '!username just earned @count !points and now has %total !points in the %category category.', $arguments); } } } if (isset($message)) { drupal_set_message($message); } } // Call the _userpoints hook to allow modules to act after points are awarded. userpoints_invoke_all('points after', $params); return array( 'status' => TRUE, 'transaction' => $params, ); } /** * Adds the points to the txn table. */ function _userpoints_transaction(&$params) { // Check, again, for a properly formed array. if (!is_array($params)) { return FALSE; } 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'] = userpoints_get_default_tid(); } 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'] = REQUEST_TIME; } } // Always force changed timestamp to current REQUEST_TIME for transaction tracking. $params['changed'] = REQUEST_TIME; if (!empty($params['txn_id']) && $params['txn_id'] > 0) { // A transaction ID was passed in so we'll update the transaction. $txn = (array) userpoints_transaction_load($params['txn_id']); if (!$txn) { return FALSE; } // 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, $txn); } } else { // Create new transaction record. $ret = drupal_write_record('userpoints_txn', $params); if ($ret != FALSE) { _userpoints_update_cache($params); } } return TRUE; } /** * Update the caching table. * * @param $params * Array with the transaction params. * @param $txn * The original transaction, if this is an update. */ function _userpoints_update_cache($txn, $old_txn = NULL) { // Store eventual updates in this array. $updates = array(); if (!$old_txn) { // For new transactions, only update the cache for fully approved non-expired // points. if ($txn['status'] == USERPOINTS_TXN_STATUS_APPROVED && $txn['expired'] != 1) { // Calculate the current points based upon the tid. $updates['points'] = $txn['points'] + userpoints_get_current_points($txn['uid'], $txn['tid']); $max_points = userpoints_get_max_points($txn['uid'], $txn['tid']); // If the new points are higher then the max, update the maximum. if ($updates['points'] > $max_points) { $updates['max_points'] = $updates['points']; } } } else { // For existing transactions, it is a bit more complex. // Expired transactions that were expired before can be ignored. if ($txn['expired'] == 1 && $old_txn['expired'] == 1) { return; } if ($old_txn['tid'] != $txn['tid']) { // If the category has changed, remove the points of the old transaction // from the old category. $remove_points = userpoints_get_current_points($txn['uid'], $old_txn['tid']) - $old_txn['points']; db_merge('userpoints') ->key(array( 'uid' => $txn['uid'], 'tid' => (int) $old_txn['tid'], )) ->fields(array( 'points' => $remove_points, )) ->execute(); if ($txn['status'] == USERPOINTS_TXN_STATUS_APPROVED) { // Make sure to add the points so that they are added to the new category. $updates['points'] = userpoints_get_current_points($txn['uid'], $txn['tid']) + $txn['points']; } } else if ($old_txn['status'] == USERPOINTS_TXN_STATUS_APPROVED && $txn['status'] != USERPOINTS_TXN_STATUS_APPROVED) { // If the transaction goes from approved to not approved, subtract the // points to the total. $updates['points'] = userpoints_get_current_points($txn['uid'], $txn['tid']) - $old_txn['points']; $max_points = userpoints_get_max_points($txn['uid'], $txn['tid']); // If the new points are higher then the max, update the maximum. if ($updates['points'] > $max_points) { $updates['max_points'] = $updates['points']; } } else if ($txn['points'] != $old_txn['points'] && $old_txn['status'] == USERPOINTS_TXN_STATUS_APPROVED && $txn['status'] == USERPOINTS_TXN_STATUS_APPROVED) { // If the category did not change but the points and the transaction // was and still is approved, update the points difference. $updates['points'] = userpoints_get_current_points($txn['uid'], $txn['tid']) + ($txn['points'] - $old_txn['points']); } elseif ($old_txn['status'] != USERPOINTS_TXN_STATUS_APPROVED && $txn['status'] == USERPOINTS_TXN_STATUS_APPROVED) { // Calculate the current points based upon the tid. $updates['points'] = userpoints_get_current_points($txn['uid'], $txn['tid']) + $txn['points']; } } if (empty($updates)) { return; } $max_points = userpoints_get_max_points($txn['uid'], $txn['tid']); // If the new points are higher then the maximum, update it. if ($updates['points'] > $max_points) { $updates['max_points'] = $updates['points']; } $updates['last_update'] = REQUEST_TIME; // Insert or update the userpoints caching table with the user's current // points. db_merge('userpoints') ->key(array( 'uid' => $txn['uid'], 'tid' => (int) $txn['tid'], )) ->fields($updates) ->execute(); } /** * Determines the correct default expiration date. * * @return * The default expiration date. */ function userpoints_get_default_expiry_date() { $expirydate = userpoints_date_to_timestamp(variable_get(USERPOINTS_EXPIREON_DATE, 0)); if ($expirydate < REQUEST_TIME) { $expirydate = variable_get(USERPOINTS_EXPIREAFTER_DATE, 0); if ($expirydate) { $expirydate = REQUEST_TIME + $expirydate; } } return (int) $expirydate; } /* * 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. * @return * TRUE if user found, FALSE otherwise. */ function _userpoints_user_exists($uid, $tid = NULL) { if (is_numeric($tid)) { return (int) db_query('SELECT COUNT(uid) FROM {userpoints} WHERE uid = :uid AND tid = :tid', array(':uid' => $uid, ':tid' => $tid))->fetchField(); } else { return (int) db_query('SELECT COUNT(uid) FROM {userpoints} WHERE uid = :uid', array(':uid' => $uid))->fetchField(); } } /** * Implements hook_user_delete(). */ function userpoints_user_delete($account) { // The user is being deleted, delete all traces in userpoints and txn tables. db_delete('userpoints') ->condition('uid', $account->uid) ->execute(); db_delete('userpoints_txn') ->condition('uid', $account->uid) ->execute(); } /** * Implements hook_user_view(). */ function userpoints_user_view($account, $view_mode) { global $user; if (user_access('view userpoints') || (user_access('view own userpoints') && $user->uid == $account->uid)) { $points_list = userpoints_get_points_list($account); if (!empty($details) || $points_list) { $account->content['userpoints'] = array( 'title' => array( '#markup' => '

' . t('!Points', userpoints_translation()) . '

', ), '#weight' => 0, ); if ($points_list) { $account->content['userpoints'] += $points_list; } } } } /** * Implements hook_field_extra_fields(). */ function userpoints_field_extra_fields() { $extra['user']['user'] = array( 'display' => array( 'userpoints' => array( 'label' => t('!Points', userpoints_translation()), 'description' => t('!Points related information and actions.', userpoints_translation()), 'weight' => 0, ) ) ); return $extra; } /** * Returns a renderable array that displays the points and action links. * * @param $account * User object for which the points should be displayed. * @return * Renderable array with the points and actions. */ function userpoints_get_points_list($account = NULL) { if (empty($account)) { global $user; $account = $user; } $output = array(); $categories = userpoints_get_categories(); // 0 can not be used as a checkbox value. $categories = array('uncategorized' => $categories[0]) + $categories + array('all' => t('Total !points in all categories', userpoints_translation())); unset($categories[0]); $tids = array_filter(variable_get(USERPOINTS_CATEGORY_PROFILE_DISPLAY_TID, array_keys($categories))); if (!empty($tids)) { $points_list = array(); $total = NULL; foreach ($tids as $tid) { // Which points are we displaying. Special case for uncategorized. $points = userpoints_get_current_points($account->uid, $tid == 'uncategorized' ? 0 : $tid); if ($tid == 'all') { $total = t('Total (all categories): @points', userpoints_translation() + array('@points' => $points)); } else { $points_list[] = t('%category: @points', userpoints_translation() + array('@points' => $points, '%category' => $categories[$tid])); } } // If there are multiple categories, create a list. $output['list'] = array( '#theme' => 'item_list', '#items' => $points_list, '#attributes' => array('class' => array('userpoints-points')), ); if ($total) { $output['total'] = array( '#markup' => '
' . $total . '
', ); } } $links = array(); if (userpoints_access_my_points($account)) { $links['userpoints-view'] = array( 'title' => t('View !points transactions', userpoints_translation()), 'href' => 'myuserpoints/' . $account->uid, ); } if (user_access('administer userpoints')) { $links['userpoints-adjust'] = array( 'title' => t('Add or deduct !points', userpoints_translation()), 'href' => 'admin/config/people/userpoints/add/' . $account->uid, ); } $output['actions'] = array( '#theme' => 'links__userpoints_actions', '#links' => $links, '#attributes' => array('class' => array('links', 'userpoints-links')), '#attached' => array( 'css' => array(drupal_get_path('module', 'userpoints') . '/userpoints.css'), ), ); return $output; } /** * Provides a dropdown to filter by category. */ function userpoints_filter_cat_select($form, &$form_state, $path, $tid, $account = NULL) { $current_cat = url($path . $tid); $form = array(); $formname = 'catselect'; $cats = userpoints_get_categories($account); $options = array(); $options[url(substr($path, 0, strlen($path) - 1))] = 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; } /** * Implements hook_block_info(). */ function userpoints_block_info() { $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()); ; } return $blocks; } /** * Implements hook_block_view(). */ function userpoints_block_view($delta) { global $user; if ($delta == -1 && (user_access('view userpoints') || user_access('view own userpoints'))) { $title = t('My !points balance', userpoints_translation()); if ($user->uid) { $content = userpoints_get_points_list(); } else { $content = t('!Points are visible to logged in users only', userpoints_translation()); } } elseif (user_access('view userpoints')) { // $delta is our tid for pulling the points. // If 0 we pull 0 or NULL. $title = t('Highest Users'); $query = db_select('userpoints', 'p') ->fields('p', array('uid', 'points')) ->orderBy('p.points', 'DESC') ->range(0, variable_get('userpoints_block_up_records_' . $delta, 5)); if ($delta == 0) { $query->condition(db_or()->condition('p.tid', 0)->isNull('p.tid')); } else { $query->condition('p.tid', $delta); } // Exclude blocked users. $query->join('users', 'u', 'u.uid = p.uid AND u.status = 1'); $rows = array(); foreach ($query->execute() as $data) { $rows[] = array( array('data' => theme('username', array('account' => user_load($data->uid)))), array('data' => $data->points, 'align' => 'right')); } $header = array(t('User'), t('!Points', userpoints_translation())); $content = theme('table', array('header' => $header, 'rows' => $rows)); $content .= ''; } if (!empty($title) && !empty($content)) { $block['subject'] = $title; $block['content'] = $content; return $block; } } /** * Implements hook_block_configure(). */ function userpoints_block_configure($delta) { if ($delta > 1) { $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; } } /** * Implements hook_block_save(). */ function userpoints_block_save($delta, $edit) { variable_set('userpoints_block_up_records_' . $delta, isset($edit['up_records']) ? $edit['up_records'] : 10); } /** * 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', ); } /** * Modifies FAPI date setting to timestamp. * * @return * 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']); } } /** * Finds and expires expired points. * * Finds all transactions with a expirydate < REQUEST_TIME and posts * opposite transactions (sum of 0). */ function userpoints_expire_transactions() { $sql = "SELECT txn_id, uid, points, time_stamp, operation, description, tid FROM {userpoints_txn} WHERE status = 0 AND expired = 0 AND (expirydate < :expiry_date AND expirydate != 0)"; $result = db_query($sql, array(':expiry_date' => REQUEST_TIME)); foreach ($result as $line) { $time_stamp_formatted = format_date($line->time_stamp, 'custom', 'Y-m-d H:i'); $arguments = array_merge(userpoints_translation(), array( '!operation' => $line->operation, '!description' => $line->description, '!txn_id' => $line->txn_id, '!date' => $time_stamp_formatted, )); $description = t(variable_get(USERPOINTS_EXPIRY_DESCRIPTION, NULL), $arguments); $params = array( 'points' => -$line->points, 'uid' => $line->uid, 'operation' => 'expiry', 'description' => $description, 'parent_txn_id' => $line->txn_id, 'moderate' => FALSE, 'tid' => $line->tid, 'time_stamp' => $line->time_stamp, '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); } } /** * Implements hook_cron(). */ function userpoints_cron() { userpoints_expire_transactions(); } /** * 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 {taxonomy_vocabulary} WHERE module='userpoints'"; $vid = db_query($sql)->fetchField(); if (!$vid) { drupal_set_message(t("Created Userpoints vocabulary")); // No vocabulary exists, we'll create one. $vocab = (object) array( 'name' => USERPOINTS_CATEGORY_NAME, 'description' => t('Automatically created by the userpoints module'), 'machine_name' => 'userpoints', 'multiple' => '0', 'required' => '0', 'hierarchy' => '1', 'relations' => '0', 'module' => 'userpoints', ); taxonomy_vocabulary_save($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; } /** * Returns an array of possible categories, suitable for inclusion in FAPI. */ function userpoints_get_categories($account = NULL) { $cache = drupal_static(__FUNCTION__, array()); $key = $account ? $account->uid : 0; if (!isset($cache[$key])) { // Create the "Uncategorized" category. $options = array(); $options[0] = t('!Uncategorized', userpoints_translation()); if (module_exists('taxonomy')) { $vid = userpoints_get_vid(); if ($vid) { // If an account is passed, load the terms directly from the database. if ($account) { $query = db_select('taxonomy_term_data', 't') ->fields('t', array('tid', 'name')) ->condition('t.vid', userpoints_get_vid()) ->groupBy('t.tid') ->groupBy('t.name') ->orderBy('t.weight'); $query->join('userpoints_txn', 'p', 't.tid = p.tid AND p.uid = :uid', array(':uid' => $account->uid)); $terms = $query->execute(); } else { $terms = taxonomy_get_tree($vid); } foreach ($terms as $term) { $options[$term->tid] = $term->name; } } } $cache[$key] = $options; } return $cache[$key]; } /** * Wrapper function to return the default tid via API call */ function userpoints_get_default_tid() { return (int) variable_get(USERPOINTS_CATEGORY_DEFAULT_TID, 0); } /** * Implements hook_views_api(). */ function userpoints_views_api() { return array( 'api' => 2.0, ); } /** * Invokes hook_userpoints() with params passed by references. * * @param $op * The operation being performed. * @param &$params * Parameters to be passed to the hook. * * @return * An array of return values of the hook implementations. If modules return * arrays from their implementations, those are merged into one array. */ function userpoints_invoke_all($op, &$params = array()) { $return = array(); foreach (module_implements('userpoints') as $module) { $function = $module .'_userpoints'; $result = $function($op, $params); if (isset($result) && is_array($result)) { $return = array_merge_recursive($return, $result); } else if (isset($result)) { $return[] = $result; } } return $return; } /** * Returns information about point-providing modules and operations. * * @see hook_userpoints_info() */ function userpoints_get_info($operation = NULL) { static $info = NULL; if (!isset($info)) { // Collect information. $info = module_invoke_all('userpoints_info'); // Allow other modules to alter that information. drupal_alter('userpoints_info', $info); } if ($operation) { if (isset($info[$operation])) { return $info[$operation]; } return NULL; } return $info; } /** * Creates a descriptive reason for a userpoints_transaction. * * The following resources are considered, in this order: * * * description key in the information array for that operation. * * description of the transaction. * * name of the operation. * * @param $transaction * The transaction object for which the description shall be generated. * * @param $options * Array of options: * - link: If FALSE, no link is generated to the linked entity even if there * were one. Defaults to TRUE. * - truncate: Define if the reason should be truncated. Defaults to TRUE. * - skip_description: Allows to skip the eventually existing custom * description a transaction has and always uses the generated description. * * @return * The reason for that transaction, linked to the referenced * entity if available. */ function userpoints_create_description($transaction, array $options = array()) { // Default options. $options += array( 'link' => TRUE, 'truncate' => TRUE, ); // Check if there is a valid entity referenced and which can be linked to. $entity = NULL; if ($transaction->entity_type && entity_get_info($transaction->entity_type)) { $entity = reset(entity_load($transaction->entity_type, array($transaction->entity_id))); } $safe = FALSE; // Check transaction description first to allow custom overrides. if (!empty($transaction->description) && empty($options['skip_description'])) { $description = $transaction->description; } else { $info = userpoints_get_info($transaction->operation); // Check if there is a valid description callback defined for this // operation. if (!empty($info['description callback']) && function_exists($info['description callback'])) { $description = $info['description callback']($transaction, $entity); $safe = TRUE; } // Try static description key. elseif (!empty($info['description'])) { $description = $info['description']; $safe = TRUE; } } // Fallback to the operation name if there is no source. if (empty($description)) { $description = $transaction->operation; } // Truncate description. $attributes = array(); $stripped_description = strip_tags($description); if ($options['truncate'] && drupal_strlen($stripped_description) > variable_get('userpoints_truncate', 30) + 3) { // The title attribute will be check_plain()'d again drupal_attributes(), // avoid double escaping. $attributes['title'] = html_entity_decode($stripped_description, ENT_QUOTES); $description = truncate_utf8($stripped_description, variable_get('userpoints_truncate', 30), FALSE, TRUE); } // Link to the referenced entity, if available. if ($entity && $options['link']) { $uri = entity_uri($transaction->entity_type, $entity); if ($uri) { $description = l($description, $uri['path'], $uri['options'] + array('html' => $safe, 'attributes' => $attributes)); } } if ((empty($entity) || empty($uri)) && !$safe) { // Escape possible user provided reason. $description = check_plain($description); } return $description; } /** * Implements hook_userpoints_info(). */ function userpoints_userpoints_info() { return array( 'expiry' => array( 'description' => t('!Points have expired.', userpoints_translation()), 'admin description' => t('Expire an existing transaction'), ) ); } /** * Load a userpoints transaction. * * @param $txn_id * Userpoints transaction Id. * * @return * A loaded userpoints transaction object. */ function userpoints_transaction_load($txn_id) { $transaction = db_query('SELECT * from {userpoints_txn} WHERE txn_id = :txn', array(':txn' => $txn_id))->fetchObject(); // Load corresponding user object. $transaction->user = user_load($transaction->uid); // Load category. $categories = userpoints_get_categories(); $transaction->category = isset($categories[$transaction->tid]) ? $categories[$transaction->tid] : $categories[userpoints_get_default_tid()]; return $transaction; } /** * Returns a list of operations as links. * * @param $transaction * Transaction object. * * @param $show_view * FALSE if the view link should not be displayed. Defaults to TRUE. * * @return * A string with operation links. */ function userpoints_get_transaction_actions($transaction, $show_view = TRUE) { $actions = array(); $url_options = array('query' => drupal_get_destination()); if ($show_view && userpoints_access_view_transaction($transaction)) { $actions[] = l('view', 'userpoints/view/' . $transaction->txn_id); } if (userpoints_admin_access('edit')) { $actions[] = l('edit', "admin/config/people/userpoints/edit/$transaction->txn_id", $url_options); } if (userpoints_admin_access('moderate') && $transaction->status == USERPOINTS_TXN_STATUS_PENDING) { $actions[] = l('approve', "admin/config/people/userpoints/approve/$transaction->txn_id", $url_options); $actions[] = l('decline', "admin/config/people/userpoints/decline/$transaction->txn_id", $url_options); } return implode(' ', $actions); } /** * Returns a table header for a transaction listing. * * @param $settings * Array with setings about which column shall be displayed. All settings * default to TRUE. * - show_category, show category column. * - show_user, show user column. * - show_status, show status column. * @return * Table header definition for theme_table() and TableSort. */ function userpoints_get_transaction_header($settings) { $settings += array( 'show_category' => FALSE, 'show_user' => TRUE, 'show_status' => TRUE, ); $header = array(); $header[] = array('data' => t('!Points', userpoints_translation()), 'field' => 'points', 'class' => array('userpoints-transactions-header-points')); // Only display category if there is more than one category. In contrast to // the filter, this is not specific for the categories. If there are // categories, we want tell the user in which he has points, even if he // only has points in a single category. if ($settings['show_category']) { $header[] = array('data' => t('Category'), 'field' => 't.name', 'class' => array('userpoints-transactions-header-category')); } if ($settings['show_user']) { $header[] = array('data' => t('User'), 'field' => 'uid', 'class' => array('userpoints-transactions-header-status')); } $header[] = array('data' => t('Date'), 'field' => 'time_stamp', 'sort' => 'desc', 'class' => array('userpoints-transactions-header-timestamp')); $header[] = array('data' => t('Reason'), 'class' => array('userpoints-transactions-header-reason')); if ($settings['show_status']) { $header[] = array('data' => t('Status'), 'field' => 'status', 'class' => array('userpoints-transactions-header-status')); } $header[] = array('data' => t('Actions'), 'class' => array('userpoints-transactions-header-actions')); return $header; } /** * Returns a single row for a transaction listing. * @param $transaction * Transaction object. * @param $settings * Array with setings about which column shall be displayed. All settings * default to TRUE. * - show_category, show category column. * - show_user, show user column. * - show_status, show status column. * @return * A table row array for use with theme_table(). */ function userpoints_get_transaction_row($transaction, $settings = array()) { $settings += array( 'show_category' => TRUE, 'show_user' => TRUE, 'show_status' => TRUE, ); $stati = userpoints_txn_status(); $css_stati = array( USERPOINTS_TXN_STATUS_APPROVED => 'approved', USERPOINTS_TXN_STATUS_DECLINED => 'declined', USERPOINTS_TXN_STATUS_PENDING => 'pending', ); $row = array('class' => array( 'userpoints-transaction-row-status-' . $css_stati[$transaction->status], 'userpoints-transaction-row-category-' . $transaction->tid), ); $row['data'][] = array( 'data' => $transaction->points, 'class' => array('userpoints-transactions-field-points', 'userpoints-transaction-points-' . ($transaction->points > 0 ? 'positive' : 'negative')), ); if ($settings['show_category']) { $categories = userpoints_get_categories(); $row['data'][] = array( 'data' => isset($categories[$transaction->tid]) ? $categories[$transaction->tid] : $categories[0], 'class' => array('userpoints-transactions-field-category'), ); } if ($settings['show_user']) { $row['data'][] = array( 'data' => theme('username', array('account' => user_load($transaction->uid))), 'class' => array('userpoints-transactions-field-user'), ); } $row['data'][] = array( 'data' => format_date($transaction->time_stamp, 'small'), 'class' => array('userpoints-transactions-field-timestamp'), ); $row['data'][] = array( 'data' => userpoints_create_description($transaction), 'class' => array('userpoints-transactions-field-reason'), ); if ($settings['show_status']) { $row['data'][] = array( 'data' => $stati[$transaction->status], 'class' => array('userpoints-transactions-field-status'), ); } $row['data'][] = array( 'data' => userpoints_get_transaction_actions($transaction), 'class' => array('userpoints-transactions-field-actions'), ); return $row; }