? .cvsignore
? userpoints_view.patch
? userpoints_view2.patch
Index: userpoints.admin.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/userpoints/Attic/userpoints.admin.inc,v
retrieving revision 1.1.2.9
diff -u -p -r1.1.2.9 userpoints.admin.inc
--- userpoints.admin.inc	1 Nov 2010 23:36:02 -0000	1.1.2.9
+++ userpoints.admin.inc	8 Nov 2010 19:59:08 -0000
@@ -517,7 +517,7 @@ function userpoints_confirm_approve($for
     ),
   );
 
-  $transaction = userpoints_get_transaction($txn_id);
+  $transaction = userpoints_transaction_load($txn_id);
   $arguments = array(
     '!user' => theme('username', array('account' => $transaction->user)),
     '%category' => $transaction->category,
Index: userpoints.css
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/userpoints/Attic/userpoints.css,v
retrieving revision 1.1.2.2
diff -u -p -r1.1.2.2 userpoints.css
--- userpoints.css	1 Nov 2010 23:36:02 -0000	1.1.2.2
+++ userpoints.css	8 Nov 2010 19:59:08 -0000
@@ -1,10 +1,10 @@
 /* $Id: userpoints.css,v 1.1.2.2 2010/11/01 23:36:02 berdir Exp $ */
 
-div.profile ul.userpoints-links {
+div.userpoints-view-transaction ul.userpoints-links {
     padding-left: 0;
 }
 
-div.profile ul.userpoints-links li.first {
+div.userpoints-view-transaction ul.userpoints-links li.first {
     padding-left: 0;
 }
 
@@ -30,3 +30,20 @@ table.userpoints-myuserpoints-total {
   min-width: 300px;
 }
 
+.userpoints-view-transaction {
+  clear: both;
+  margin: 1em 0;
+}
+.userpoints-view-transaction h3 {
+  border-bottom: 1px solid #ccc;
+}
+.userpoints-view-transaction dl {
+  margin: 0 0 1.5em 0;
+}
+.userpoints-view-transaction dt {
+  margin: 0 0 0.2em 0;
+  font-weight: bold;
+}
+.userpoints-view-transaction dd {
+  margin: 0 0 1em 0;
+}
Index: userpoints.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/userpoints/userpoints.module,v
retrieving revision 1.68.2.21
diff -u -p -r1.68.2.21 userpoints.module
--- userpoints.module	1 Nov 2010 23:36:02 -0000	1.68.2.21
+++ userpoints.module	8 Nov 2010 19:59:13 -0000
@@ -248,6 +248,18 @@ function userpoints_menu() {
       'type' => MENU_NORMAL_ITEM,
   );
 
+  $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(),
@@ -278,6 +290,33 @@ function userpoints_access_my_points($ac
 }
 
 /**
+ * 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('Details for transaction #@id', array('@id' => $transaction->txn_id));
+}
+
+/**
  * Implements hook_permission().
  */
 function userpoints_permission() {
@@ -327,6 +366,14 @@ function userpoints_theme() {
           '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,
@@ -670,7 +717,7 @@ function _userpoints_transaction(&$param
 
   if (!empty($params['txn_id']) && $params['txn_id'] > 0) {
     // A transaction ID was passed in so we'll update the transaction.
-    $txn = (array) userpoints_get_transaction($params['txn_id']);
+    $txn = (array) userpoints_transaction_load($params['txn_id']);
     if (!$txn) {
       return FALSE;
     }
@@ -1242,7 +1289,7 @@ function userpoints_get_info($operation 
  *   The reason for that transaction, linked to the referenced
  *   entity if available.
  */
-function userpoints_create_description($transaction) {
+function userpoints_create_description($transaction, array $options = array()) {
   // 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)) {
@@ -1250,7 +1297,7 @@ function userpoints_create_description($
   }
   $safe = FALSE;
   // Check transaction description first to allow custom overrides.
-  if (!empty($transaction->description)) {
+  if (!empty($transaction->description) && empty($options['skip_description'])) {
     $description = $transaction->description;
   } else {
     $info = userpoints_get_info($transaction->operation);
@@ -1271,7 +1318,7 @@ function userpoints_create_description($
     $description = $transaction->operation;
   }
   // Link to the referenced entity, if available.
-  if ($entity) {
+  if ($entity && (!isset($options['link']) || $options['link'] != FALSE)) {
     $uri = entity_uri($transaction->entity_type, $entity);
     if ($uri) {
       $description = l($description, $uri['path'], $uri['options'] + array('html' => $safe));
@@ -1304,7 +1351,7 @@ function userpoints_userpoints_info() {
  * @return
  *   A loaded userpoints transaction object.
  */
-function userpoints_get_transaction($txn_id) {
+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.
@@ -1320,22 +1367,30 @@ function userpoints_get_transaction($txn
  * Returns a list of operations as links.
  *
  * @param $transaction
- *  Transaction object.
+ *   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_operations($transaction) {
-  $operations = array();
+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')) {
-    $operations[] = l('edit', "admin/config/people/userpoints/edit/$transaction->txn_id", $url_options);
+    $actions[] = l('edit', "admin/config/people/userpoints/edit/$transaction->txn_id", $url_options);
   }
   if (userpoints_admin_access('moderate') && $transaction->status == USERPOINTS_TXN_STATUS_PENDING) {
-    $operations[] = l('approve', "admin/config/people/userpoints/approve/$transaction->txn_id", $url_options);
-    $operations[] = l('decline', "admin/config/people/userpoints/decline/$transaction->txn_id", $url_options);
+    $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(' ', $operations);
+  return implode(' ', $actions);
 }
 
 /**
@@ -1373,7 +1428,7 @@ function userpoints_get_transaction_head
   if ($settings['show_status']) {
     $header[] = array('data' => t('Status'), 'field' => 'status', 'class' => array('userpoints-transactions-header-status'));
   }
-  $header[] = array('data' => t('Operations'), 'class' => array('userpoints-transactions-header-operations'));
+  $header[] = array('data' => t('Actions'), 'class' => array('userpoints-transactions-header-actions'));
   return $header;
 }
 
@@ -1439,8 +1494,8 @@ function userpoints_get_transaction_row(
     );
   }
   $row['data'][] = array(
-    'data' => userpoints_get_transaction_operations($transaction),
-    'class' => array('userpoints-transactions-field-operations'),
+    'data' => userpoints_get_transaction_actions($transaction),
+    'class' => array('userpoints-transactions-field-actions'),
   );
   return $row;
 }
\ No newline at end of file
Index: userpoints.pages.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/userpoints/Attic/userpoints.pages.inc,v
retrieving revision 1.1.2.6
diff -u -p -r1.1.2.6 userpoints.pages.inc
--- userpoints.pages.inc	1 Nov 2010 23:36:02 -0000	1.1.2.6
+++ userpoints.pages.inc	8 Nov 2010 19:59:14 -0000
@@ -91,7 +91,7 @@ function userpoints_list_my_userpoints($
     '#attributes' => array('class' => array('userpoints-myuserpoints-list')),
   );
   $output['pager'] = array(
-    '#theme' => 'pager',
+    '#markup' => theme('pager'),
     '#weight' => 0,
   );
 
@@ -178,3 +178,179 @@ function userpoints_list_users($tid = NU
   }
   return theme('userpoints_list_users', array('header' => $header, 'rows' => $rows, 'tid' => $tid));
 }
+
+/**
+ * Menu callback; display details about a specific transaction.
+ *
+ * @param $transaction
+ *   Transaction object.
+ * @return
+ *   Render-able array with all the information about this transaction.
+ */
+function userpoints_view_transaction($transaction) {
+  drupal_add_css(drupal_get_path('module', 'userpoints') . '/userpoints.css');
+
+  $content = array(
+    '#prefix' => '<div class="userpoints-view-transaction">',
+    '#suffix' => '</div>',
+  );
+
+  $content['general'] = array(
+    '#theme' => 'userpoints_view_category',
+    '#title' => t('General'),
+  );
+
+  $content['general']['points'] = array(
+    '#theme' => 'userpoints_view_item',
+    '#title' => t('!Points amount', userpoints_translation()),
+    '#value' => $transaction->points,
+  );
+
+  $content['general']['category'] = array(
+    '#theme' => 'userpoints_view_item',
+    '#title' => t('Category'),
+    '#value' => $transaction->category,
+  );
+
+  $content['general']['user'] = array(
+    '#theme' => 'userpoints_view_item',
+    '#title' => t('User'),
+    '#value' => theme('username', array('account' => $transaction->user)),
+  );
+
+  $content['general']['date'] = array(
+    '#theme' => 'userpoints_view_item',
+    '#title' => t('Date'),
+    '#value' => format_date($transaction->time_stamp),
+  );
+
+  $content['general']['reason'] = array(
+    '#theme' => 'userpoints_view_item',
+    '#title' => t('Reason'),
+    '#value' => userpoints_create_description($transaction),
+  );
+
+  $content['general']['transaction'] = array(
+    '#theme' => 'userpoints_view_item',
+    '#title' => t('Transaction ID'),
+    '#value' => $transaction->txn_id,
+  );
+
+  $content['status'] = array(
+    '#theme' => 'userpoints_view_category',
+    '#title' => t('Status'),
+  );
+
+  $stati = userpoints_txn_status();
+  $content['status']['status'] = array(
+    '#theme' => 'userpoints_view_item',
+    '#title' => t('Approval status'),
+    '#value' => $stati[$transaction->status],
+  );
+
+  $content['status']['changed'] = array(
+    '#theme' => 'userpoints_view_item',
+    '#title' => t('Last modification date'),
+    '#value' => format_date($transaction->changed),
+  );
+
+  if (!empty($transaction->expirydate)) {
+    $content['status']['expiration_status'] = array(
+      '#theme' => 'userpoints_view_item',
+      '#title' => t('Expiration status'),
+      '#value' => format_date($transaction->expirydate),
+    );
+
+    $content['status']['expiration_date'] = array(
+      '#theme' => 'userpoints_view_item',
+      '#title' => t('Expiration date'),
+      '#value' => $transaction->expired ? t('Expired') : t('Not expired'),
+    );
+  }
+
+  if (!empty($transaction->parent_txn_id)) {
+    $parent_transaction = userpoints_transaction_load($transaction->parent_txn_id);
+    $parent = l(userpoints_create_description($parent_transaction, array('link' => FALSE)), 'userpoints/view/' . $transaction->parent_txn_id, array('html' => TRUE));
+  }
+
+  $child_txn_ids = db_query('SELECT txn_id FROM {userpoints_txn} WHERE parent_txn_id = :txn_id', array(':txn_id' => $transaction->txn_id))->fetchCol();
+  $children = array();
+  foreach ($child_txn_ids as $child_txn_id) {
+    $child_transaction = userpoints_transaction_load($child_txn_id);
+    $children[] = l(userpoints_create_description($child_transaction, array('link' => FALSE)), 'userpoints/view/' . $child_txn_id, array('html' => TRUE));
+  }
+  $children = !empty($children) ? theme('item_list', array('items' => $children)) : '';
+
+  if (!empty($parent) || !empty($children)) {
+    $content['related'] = array(
+      '#theme' => 'userpoints_view_category',
+      '#title' => t('Related !points transactions', userpoints_translation()),
+    );
+
+    if (!empty($parent)) {
+      $content['related']['parent'] = array(
+        '#theme' => 'userpoints_view_item',
+        '#title' => t('Parent transaction'),
+        '#value' => $parent,
+      );
+    }
+
+    if (!empty($children)) {
+      $content['related']['children'] = array(
+        '#theme' => 'userpoints_view_item',
+        '#title' => t('Children transactions'),
+        '#value' => $children,
+      );
+    }
+  }
+
+  if (userpoints_admin_access('edit')) {
+    $content['admin'] = array(
+      '#theme' => 'userpoints_view_category',
+      '#title' => t('Admin'),
+    );
+
+    if (!empty($transaction->approver_uid)) {
+      $content['admin']['moderator'] = array(
+        '#theme' => 'userpoints_view_item',
+        '#title' => t('Moderator'),
+        '#value' => theme('username', array('account' => user_load($transaction->approver_uid))),
+      );
+    }
+
+    if (!empty($transaction->description)) {
+      $content['admin']['description_manual'] = array(
+        '#theme' => 'userpoints_view_item',
+        '#title' => t('Description (manually entered)'),
+        '#value' => $transaction->description,
+      );
+
+      $content['admin']['description_generated'] = array(
+        '#theme' => 'userpoints_view_item',
+        '#title' => t('Description (auto generated)'),
+        '#value' => userpoints_create_description($transaction, array('skip_description' => TRUE)),
+      );
+    }
+
+    $content['admin']['operation'] = array(
+      '#theme' => 'userpoints_view_item',
+      '#title' => t('Operation'),
+      '#value' => $transaction->operation,
+    );
+
+    if (!empty($transaction->reference)) {
+      $content['admin']['reference'] = array(
+        '#theme' => 'userpoints_view_item',
+        '#title' => t('Reference'),
+        '#value' => $transaction->reference,
+      );
+    }
+
+    $content['admin']['actions'] = array(
+      '#theme' => 'userpoints_view_item',
+      '#title' => t('Actions'),
+      '#value' => userpoints_get_transaction_actions($transaction, FALSE),
+    );
+  }
+  return $content;
+}
\ No newline at end of file
Index: userpoints.theme.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/userpoints/Attic/userpoints.theme.inc,v
retrieving revision 1.1.2.2
diff -u -p -r1.1.2.2 userpoints.theme.inc
--- userpoints.theme.inc	11 Oct 2010 07:48:28 -0000	1.1.2.2
+++ userpoints.theme.inc	8 Nov 2010 19:59:14 -0000
@@ -73,3 +73,39 @@ function theme_userpoints_list_users_row
       array('data' => $row->points, 'align' => 'right'),
   );
 }
+
+/**
+ * Theme implementation to display a userpoints category.
+ */
+function theme_userpoints_view_category($variables) {
+  $element = $variables['element'];
+  $element += array(
+    '#attributes' => array(),
+  );
+
+  $output = '';
+  if (!empty($element['#title'])) {
+    $output .= '<h3>' . $element['#title'] . '</h3>';
+  }
+
+  $output .= '<dl ' . drupal_attributes(($element['#attributes'])) . '>';
+  $output .= drupal_render_children($element);
+  $output .= '</dl>';
+
+  return $output;
+}
+
+/**
+ * Theme implementation to display a userpoints item.
+ */
+function theme_userpoints_view_item($variables) {
+  $element = $variables['element'];
+  $element += array(
+    '#attributes' => array(),
+  );
+
+  $output = '<dt ' . drupal_attributes(($element['#attributes'])) . '>' . $element['#title'] . '</dt>';
+  $output .= '<dd ' . drupal_attributes(($element['#attributes'])) . '>' . $element['#value'] . '</dd>';
+
+  return $output;
+}
