Index: admin.js
===================================================================
RCS file: /cvs/drupal/contributions/modules/user_titles/admin.js,v
retrieving revision 1.9
diff -u -r1.9 admin.js
--- admin.js	26 Jul 2007 23:25:11 -0000	1.9
+++ admin.js	6 Dec 2007 02:35:26 -0000
@@ -11,6 +11,7 @@
   // Bind our buttons.
   $("input#edit-add-submit").click(Drupal.UserTitles.addTitle);
   $("table#user-titles-settings img.title-delete").click(Drupal.UserTitles.removeTitle);
+  $("input.user-titles-hook-module").click(Drupal.UserTitles.toggleTypeCheckboxes);
 }
 
 Drupal.UserTitles.addTitle = function() {
@@ -78,6 +79,13 @@
       .addClass('even');
 }
 
+// toggles type checkboxes disabled status by checking userpoints field
+Drupal.UserTitles.toggleTypeCheckboxes = function() {
+  var disableType = ! $("input.user-titles-hook-module").eq(0).attr('checked');
+  $('.user-titles-types').attr('disabled', disableType ? 'disabled' : '');
+  $('input#edit-types-disabled').val(disableType ? '1' : '0');
+}
+
 // We are ignoring the global Drupal killswitch, because this module doesn't
 // currently degrade gracefully. It can, but the code has not yet been written.
 
Index: user_titles.module
===================================================================
RCS file: /cvs/drupal/contributions/modules/user_titles/user_titles.module,v
retrieving revision 1.13
diff -u -r1.13 user_titles.module
--- user_titles.module	19 Nov 2007 00:46:36 -0000	1.13
+++ user_titles.module	6 Dec 2007 04:22:48 -0000
@@ -42,6 +42,19 @@
 }
 
 /**
+ * Determines what the current hook module implementation is, or '' if
+ * using built-in version. Ensures the module is loaded too.
+ */
+function user_titles_hook_module() {
+  $module = variable_get('user_titles_hook_module', $default = 'user_titles');
+  if (module_exists($module)) {
+    return $module;
+  } else {
+    return $default;
+  }
+}
+
+/**
  * Form to determine titles and levels.
  */
 function user_titles_settings_form() {
@@ -51,12 +64,39 @@
     $nodes[$type] = $info->name;
   }
 
+  $hook_module  = user_titles_hook_module();
+  $hook_modules = module_invoke_all('user_titles', 'register');
+  
+  $form['hook-module'] = array(
+    '#type' => 'radios',
+    '#title' => 'Point scheme',
+    '#default_value' => $hook_module,
+    '#options' => array(),
+    '#attributes' => array('class' => 'user-titles-hook-module'),
+  );
+  foreach ($hook_modules as $module) {
+    $form['hook-module']['#options'][$module] = array(
+      'name'        => $name = module_invoke($module, 'user_titles', 'name'),
+      'description' => module_invoke($module, 'user_titles', 'description'),
+      'url'         => module_invoke($module, 'user_titles', 'url'),
+    );
+    $name_index[$module] = $name;
+  }
+  array_multisort($name_index, SORT_ASC, SORT_STRING, $form['hook-module']['#options']);
+  
+  // Implementation of user_titles' default behavior
   $form['types'] = array(
     '#type' => 'checkboxes',
     '#title' => t('Counted node types'),
     '#description' => t('Only the checked node types will be counted'),
     '#options' => $nodes,
     '#default_value' => $types,
+    '#disabled' => $hook_module !== 'user_titles',
+    '#attributes' => array('class' => 'user-titles-types'),
+  );
+  $form['types_disabled'] = array(
+    '#type' => 'hidden',
+    '#default_value' => $hook_module !== 'user_titles',
   );
 
   $titles = user_titles_get_titles();
@@ -128,8 +168,9 @@
   drupal_add_js(array('userTitles' => array('imageURL' => base_path() ."misc/watchdog-error.png")), 'setting');
   drupal_add_js("$path/admin.js");
   drupal_add_css("$path/admin.css");
+  $units = module_invoke(user_titles_hook_module(), 'user_titles', 'units');
   $header = array(
-    array('data' => t('Posts'), 'class' => 'num-posts'),
+    array('data' => $units, 'class' => 'num-posts'),
     array('data' => t('Title'), 'class' => 'user-title'),
     '',
   );
@@ -158,7 +199,21 @@
   $output .= theme('table', array(), $add_rows, array('id' => 'user-titles-add'));
   $output .= '</div>';
 
+  // Properly format hook-module
+  foreach ($form['hook-module']['#options'] as $module => $info) {
+    if ($info['url']) {
+      $name = l($info['name'], $info['url']);
+    }
+    else {
+      $name = check_plain($info['name']);
+    }
+    // drupal_render uses this area, not the original
+    $form['hook-module'][$module]['#title'] =
+      '<strong>'. $name .'</strong><div class="description">'. $info['description'] .'</div>';
+  }
+  
   $output .= '<div class="right">';
+  $output .= drupal_render($form['hook-module']);
   $output .= drupal_render($form['types']);
   $output .= '</div>';
 
@@ -208,7 +263,10 @@
     db_query("TRUNCATE {user_titles_posts}");
   }
 
-  user_titles_set_allowed_types(array_keys(array_filter($form_values['types'])));
+  if (!$form_values['types_disabled']) {
+    user_titles_set_allowed_types(array_keys(array_filter($form_values['types'])));
+  }
+  variable_set('user_titles_hook_module', $form_values['hook-module']);
   drupal_set_message(t('The configuration has been updated.'));
 }
 
@@ -240,17 +298,17 @@
 /**
  * Load the number of posts for a user. If we don't have a stored count
  * in the database, count and store that value.
+ * 
+ * @todo When porting to 6.x, rename to user_title_get_points
  *
  * @param $uid
  *   The user to fetch the number of posts for.
  */
 function user_titles_get_posts($uid) {
   static $cache = array();
-  if (!array_key_exists($uid, $cache)) {
-    $cache[$uid] = db_result(db_query("SELECT posts FROM {user_titles_posts} WHERE uid = %d", $uid));
-    if ($cache[$uid] === FALSE) {
-      $cache[$uid] = user_titles_update_post_count($uid);
-    }
+  if (!isset($cache[$uid])) {
+    $module = user_titles_hook_module();
+    $cache[$uid] = module_invoke($module, 'user_titles', 'get', $uid);
   }
   return $cache[$uid];
 }
@@ -352,8 +410,8 @@
  */
 function user_titles_get_titles() {
   $query = db_query("SELECT title, value, tid FROM {user_titles} ORDER BY value DESC");
-  if(db_num_rows($query)) {
-    while($r = db_fetch_array($query)) {
+  if (db_num_rows($query)) {
+    while ($r = db_fetch_array($query)) {
       $result[] = $r;
     }
   }
@@ -368,8 +426,8 @@
  */
 function user_titles_set_titles($titles) {
   db_query("TRUNCATE {user_titles}");
-  foreach($titles as $title) {
-    if($title['tid'] == 'new') {
+  foreach ($titles as $title) {
+    if ($title['tid'] == 'new') {
       $title['tid'] = db_next_id("{user_titles}_tid");
     }
     db_query("INSERT INTO {user_titles} (title, value, tid) VALUES ('%s', %d, %d)", $title['title'], $title['value'], $title['tid']);
@@ -400,7 +458,7 @@
     }
 
     $form['user_titles']['current_title'] = array(
-      '#value' => '<div><strong>' . t('Current user title:') . ' </strong> ' . filter_xss_admin($title) . '</div>',
+      '#value' => '<div><strong>'. t('Current user title:') .' </strong> '. filter_xss_admin($title) .'</div>',
     );
 
     if (isset($title_info['title']) && empty($title_info['tid'])) {
@@ -412,7 +470,7 @@
         $default_title = $default_title_info['title'];
       }
       $form['user_titles']['default_title'] = array(
-        '#value' => '<div><strong>' . t('Default user title:') . ' </strong> ' . filter_xss_admin($default_title) . '</div>',
+        '#value' => '<div><strong>'. t('Default user title:') .' </strong> '. filter_xss_admin($default_title) .'</div>',
       );
     }
 
@@ -435,3 +493,49 @@
     }
   }
 }
+
+/**
+ * Built-in hook implementation that counts nodes
+ */
+function user_titles_user_titles($op, $uid = null) {
+  switch ($op) {
+    case 'register':
+      return 'user_titles';
+    case 'name':
+      return t('Post count');
+    case 'description':
+      return t('Built-in, see below');
+    case 'units':
+      return t('Posts');
+    case 'url':
+      // No url implemented
+      return;
+    case 'get':
+      $res = db_result(db_query("SELECT posts FROM {user_titles_posts} WHERE uid = %d", $uid));
+      if ($res === false) $res = user_titles_update_post_count($uid);
+      return $res;
+  }
+}
+
+/**
+ * Sample hook implementation for userpoints; should be placed in userpoints
+ */
+if (!function_exists('userpoints_user_titles')) {
+  function userpoints_user_titles($op, $uid = null) {
+    if (!module_exists('userpoints')) return;
+    switch ($op) {
+      case 'register':
+        return 'userpoints';
+      case 'name':
+        return t('User points');
+      case 'description':
+        return t('Different points values are assigned to user actions');
+      case 'units':
+        return t('Points');
+      case 'url':
+        return 'admin/help/userpoints';
+      case 'get':
+        return userpoints_get_current_points($uid);
+    }
+  }
+}
