? .cvsignore
? userpoints_entity_operation_autocomplete_form.patch
Index: userpoints.admin.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/userpoints/Attic/userpoints.admin.inc,v
retrieving revision 1.1.2.11
diff -u -p -r1.1.2.11 userpoints.admin.inc
--- userpoints.admin.inc	1 Dec 2010 10:15:03 -0000	1.1.2.11
+++ userpoints.admin.inc	16 Jan 2011 19:33:23 -0000
@@ -32,6 +32,8 @@ function userpoints_confirm_approve_subm
 function userpoints_admin_txn($form, &$form_state, $mode, $txn_id = NULL) {
   global $user;
 
+  drupal_add_css(drupal_get_path('module', 'userpoints') . '/userpoints.css');
+
   $timestamp = format_date(REQUEST_TIME, 'custom', 'Y-m-d H:i O');
   if ($mode == 'edit' && $txn_id) {
     $txn = db_query('SELECT * FROM {userpoints_txn} WHERE txn_id = :txn_id', array(':txn_id' => $txn_id))->fetch();
@@ -151,16 +153,17 @@ function userpoints_admin_txn($form, &$f
       '#access' => count($options) > 1,
     );
   }
-  
+
   $form['operation'] = array(
       '#type' => 'textfield',
       '#title' => t('Operation'),
       '#default_value' => isset($txn->operation) ? $txn->operation : t('admin'),
       '#size' => 32,
       '#maxlength' => 48,
-      '#description' => t('The operation type for this transaction (default is %admin).', array('%admin' => t('admin'))),
+      '#description' => t('The operation type for this transaction (default is %admin). Any value is valid but using an existing operation has the benefit of translated reason.', array('%admin' => t('admin'))),
       '#weight' => 5,
       '#required' => FALSE,
+      '#autocomplete_path' => 'userpoints/operation-autocomplete',
   );
 
   $form['description'] = array(
@@ -183,6 +186,33 @@ function userpoints_admin_txn($form, &$f
       '#weight' => 15,
   );
 
+  $options = array('' => '< ' . t('None') . ' >');
+  foreach (entity_get_info() as $type => $info) {
+    $options[$type] = $info['label'];
+  }
+  $form['entity'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Entity'),
+    '#description' => t('Choose the entity type and ID to be referenced. A link to the entity will be shown.'),
+    '#attributes' => array('class' => array('userpoints-entity-fieldset')),
+  );
+  $form['entity']['entity_type'] = array(
+    '#type' => 'select',
+    '#title' => t('Entity type'),
+    '#title_display' => 'invisible',
+    '#weight' => 20,
+    '#options' => $options,
+  );
+
+  $form['entity']['entity_id'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Entity ID'),
+    '#title_display' => 'invisible',
+    '#weight' => 25,
+    '#size' => 5,
+    '#maxlength' => 20,
+  );
+
   $approved_by = !empty($txn->approver_uid) ? user_load($txn->approver_uid) : NULL;
   if ($approved_by) {
     $form['approver'] = array(
@@ -208,6 +238,51 @@ function userpoints_admin_txn($form, &$f
   );
   return $form;
 }
+/**
+ * Autocomplete callback for search an operation.
+ *
+ * @param $search
+ *   Search string.
+ */
+function userpoints_operation_autocomplete($search) {
+  $results = array();
+  if (!empty($search)) {
+    foreach (userpoints_get_info() as $operation => $info) {
+      if (strpos($operation, $search) !== FALSE) {
+        $results[$operation] = userpoints_create_operation_autocomplete_label($operation, $info, $search);
+      }
+      elseif (isset($info['admin description']) && strpos($info['admin description'], $search) !== FALSE) {
+        $results[$operation] = userpoints_create_operation_autocomplete_label($operation, $info, $search);
+      }
+      elseif (isset($info['description']) && strpos($info['description'], $search) !== FALSE) {
+        $results[$operation] = userpoints_create_operation_autocomplete_label($operation, $info, $search);
+      }
+    }
+  }
+  drupal_json_output((object)$results);
+}
+
+/**
+ * Create a autocomplete label with highlighted search text.
+ *
+ * @param $operation
+ *   Operation string.
+ * @param $info
+ *   Operation info array.
+ * @param $search
+ *   Search text that will be highlighted if found.
+ *
+ * @return
+ *   Highlighted label. If existing, the admin description is used and if not,
+ *   the operation string.
+ */
+function userpoints_create_operation_autocomplete_label($operation, $info, $search) {
+  $label = $operation;
+  if (!empty($info['admin description'])) {
+    $label = $info['admin description'];
+  }
+  return preg_replace("/(" . preg_quote($search) . ")/i","<b>$1</b>", $label);
+}
 
 /**
  * Validate function for userpoints transaction form.
@@ -252,6 +327,10 @@ function userpoints_admin_txn_submit($fo
       // Check for the existence of an expirydate.
       $params['expirydate'] = strtotime($form_state['values']['expirydate']);
     }
+    if (!empty($form_state['values']['entity_id']) && !empty($form_state['values']['entity_type'])) {
+      $params['entity_type'] = $form_state['values']['entity_type'];
+      $params['entity_id'] = (int)$form_state['values']['entity_id'];
+    }
   }
   else {
     $expirydate = 0;
@@ -282,6 +361,10 @@ function userpoints_admin_txn_submit($fo
       'txn_id' => $form_state['values']['txn']->txn_id,
       'display' => FALSE,
     );
+    if (!empty($form_state['values']['entity_id']) && !empty($form_state['values']['entity_type'])) {
+      $params['entity_type'] = $form_state['values']['entity_type'];
+      $params['entity_id'] = (int)$form_state['values']['entity_id'];
+    }
     drupal_set_message(t('Changes to the !points transaction have been saved.', userpoints_translation()));
   }
   userpoints_userpointsapi($params);
Index: userpoints.api.php
===================================================================
RCS file: userpoints.api.php
diff -N userpoints.api.php
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ userpoints.api.php	16 Jan 2011 19:33:23 -0000
@@ -0,0 +1,34 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * API documentation for userpoints.module
+ */
+
+/**
+ * Return information about registered operations.
+ *
+ * Modules can register operion strings
+ *
+ * @return
+ *   An array whose keys are operation strings used in
+ *   userpoints_userpoints_api() and the which has the following properties:
+ *   - description: A string that is used as a reason when transactions are
+ *     displayed. Either this or a callback (see below) is required.
+ *   - description callback: If the reason is dynamic, because he for example
+ *     includes the title of a node, a callback function can be given which
+ *     receives the transaction object and (if existing) and entity object as
+ *     arguments.
+ *   - admin description: A description which is searched for and displayed in
+ *     the operation autocomplete field in the add points form.
+ *
+ */
+function hook_userpoints_info() {
+  return array(
+    'expiry' => array(
+      'description' => t('!Points have expired.', userpoints_translation()),
+      'admin description' => t('Expire an existing transaction'),
+    )
+  );
+}
\ No newline at end of file
Index: userpoints.css
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/userpoints/Attic/userpoints.css,v
retrieving revision 1.1.2.3
diff -u -p -r1.1.2.3 userpoints.css
--- userpoints.css	19 Nov 2010 00:47:21 -0000	1.1.2.3
+++ userpoints.css	16 Jan 2011 19:33:23 -0000
@@ -47,3 +47,7 @@ table.userpoints-myuserpoints-total {
 .userpoints-view-transaction dd {
   margin: 0 0 1em 0;
 }
+
+fieldset.userpoints-entity-fieldset div.form-item {
+    display: inline;
+}
Index: userpoints.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/userpoints/userpoints.module,v
retrieving revision 1.68.2.22
diff -u -p -r1.68.2.22 userpoints.module
--- userpoints.module	19 Nov 2010 00:47:21 -0000	1.68.2.22
+++ userpoints.module	16 Jan 2011 19:33:23 -0000
@@ -248,6 +248,15 @@ function userpoints_menu() {
       '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',
@@ -1231,7 +1240,7 @@ function userpoints_views_api() {
  *   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.
@@ -1338,6 +1347,7 @@ function userpoints_userpoints_info() {
   return array(
     'expiry' => array(
       'description' => t('!Points have expired.', userpoints_translation()),
+      'admin description' => t('Expire an existing transaction'),
     )
   );
 }
