? 373574-casetracker-tokens-3.patch
? casetracker.token.inc
Index: casetracker.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/casetracker/casetracker.module,v
retrieving revision 1.123
diff -u -p -r1.123 casetracker.module
--- casetracker.module	11 Feb 2009 21:18:58 -0000	1.123
+++ casetracker.module	6 Apr 2010 17:46:31 -0000
@@ -1,5 +1,5 @@
 <?php
-// $Id: casetracker.module,v 1.123 2009/02/11 21:18:58 jmiccolis Exp $
+// $Id: casetracker.module,v 1.123.2.22 2010/02/20 18:49:44 yhahn Exp $
 
 /**
  * @file
@@ -36,6 +36,7 @@ function casetracker_perm() {
   return array(
     'access case tracker',
     'administer case tracker',
+    'assign cases',    
   );
 }
 
@@ -88,19 +89,19 @@ function casetracker_menu() {
     'title' => 'Add case state',
     'type' => MENU_LOCAL_TASK,
   );
-  $items['admin/settings/casetracker/states/edit'] = array(
+  $items['admin/settings/casetracker/states/edit/%casetracker_case_state'] = array(
     'file' => 'casetracker_admin.inc',
     'access arguments' => array('administer case tracker'),
     'page callback' => 'drupal_get_form',
-    'page arguments' => array('casetracker_case_state_edit'),
+    'page arguments' => array('casetracker_case_state_edit', 5),
     'title' => 'Edit case state',
     'type' => MENU_CALLBACK,
   );
-  $items['admin/settings/casetracker/states/delete'] = array(
+  $items['admin/settings/casetracker/states/delete/%casetracker_case_state'] = array(
     'file' => 'casetracker_admin.inc',
     'access arguments' => array('administer case tracker'),
     'page callback' => 'drupal_get_form',
-    'page arguments' => array('casetracker_case_state_confirm_delete'),
+    'page arguments' => array('casetracker_case_state_confirm_delete', 5),
     'title' => 'Delete case state',
     'type' => MENU_CALLBACK,
   );
@@ -132,10 +133,18 @@ function casetracker_nodeapi(&$node, $op
         db_query('DELETE FROM {casetracker_case} WHERE nid = %d', $node->nid);
         break;
 
+      case 'presave':
+        $node->casetracker = (object) $node->casetracker;
+        break;
+
       case 'insert':
+        // $node->casetracker is an Array and we wanna it to be an object. I
+        // guess it's a nasty workaround.
+        $node->casetracker = (object) $node->casetracker;
+
         // cases: generate a case ID and send it along.
-        $record = (object) $node->casetracker;
-        $record->assign_to = casetracker_get_uid($record->assign_to);
+        $record = $node->casetracker;
+        $record->assign_to = is_numeric($record->assign_to) ? $record->assign_to : casetracker_get_uid($record->assign_to);
         $record->nid = $node->nid;
         $record->vid = $node->vid;
 
@@ -151,7 +160,7 @@ function casetracker_nodeapi(&$node, $op
 
       case 'update':
         $record = (object) $node->casetracker;
-        $record->assign_to = casetracker_get_uid($record->assign_to);
+        $record->assign_to = is_numeric($record->assign_to) ? $record->assign_to : casetracker_get_uid($record->assign_to);
         $record->nid = $node->nid;
         $record->vid = $node->vid;
 
@@ -160,15 +169,24 @@ function casetracker_nodeapi(&$node, $op
         break;
 
       case 'view':
+        // On preview the case will be an array, we want an object.
+        if (isset($node->build_mode) && $node->build_mode == NODE_BUILD_PREVIEW) {
+          $node->casetracker = (object)$node->casetracker;
+        }
+
         // used in the breadcrumb and our theme function, mostly for nid and project number display.
         $project = node_load($node->casetracker->pid);
-        $trail = array(
-          l(t('Home'), NULL),
-          l(t('Case Tracker projects'), 'casetracker/projects'),
-          l($project->title, "node/{$node->casetracker->pid}"),
-          l(t('All cases'), "casetracker/cases/{$node->casetracker->pid}/all"),
-        );
-        drupal_set_breadcrumb($trail);
+
+        if ($page) {
+          $trail = array(
+            l(t('Home'), NULL),
+            l(t('Case Tracker'), 'casetracker/projects'),
+            l($project->title, "node/{$node->casetracker->pid}"),
+            l(t('All cases'), "casetracker/cases/{$node->casetracker->pid}/all"),
+          );
+          drupal_set_breadcrumb($trail);
+        }
+
         $node->content['casetracker_case_summary'] = array(
           '#value' => theme('casetracker_case_summary', $node, $project),
           '#weight' => -10
@@ -195,6 +213,14 @@ function casetracker_nodeapi(&$node, $op
         break;
 
       case 'view':
+        if ($page) {
+          $trail = array(
+            l(t('Home'), NULL),
+            l(t('Case Tracker'), 'casetracker/projects'),
+          );
+          drupal_set_breadcrumb($trail);
+        }
+      
         $node->content['casetracker_project_summary'] = array('#value' => theme('casetracker_project_summary', $node), '#weight' => -10);
         break;
     }
@@ -244,12 +270,21 @@ function casetracker_comment(&$comment, 
       db_query("DELETE FROM {casetracker_comment_status} WHERE cid = %d", $comment->cid);
       break;
     case 'view':
-      $results = db_query("SELECT * FROM {casetracker_comment_status} WHERE cid = %d", $comment->cid);
-      while ($result = db_fetch_object($results)) {
-        $state = $result->state ? 'new' : 'old';
-        $case_data[$state] = $result;
+      // If this is a preview we won't have a cid yet.
+      if (empty($comment->cid)) {
+        $case_data['new'] = (object)$comment->casetracker;
+        $case_data['new']->assign_to = casetracker_get_uid($case_data['new']->assign_to);
+        $case = node_load($comment->nid);
+        $case_data['old'] = drupal_clone($case->casetracker);
       }
-      $comment->comment = theme('casetracker_comment_changes', $case_data['old'], $case_data['new']) . $comment->comment;
+      else {
+        $results = db_query("SELECT * FROM {casetracker_comment_status} WHERE cid = %d", $comment->cid);
+        while ($result = db_fetch_object($results)) {
+          $state = $result->state ? 'new' : 'old';
+          $case_data[$state] = $result;
+        }
+      }
+      $comment->comment = theme('casetracker_comment_changes', $case_data['old'], $case_data['new']) . $comment->comment;        
       break;
   }
 }
@@ -265,13 +300,15 @@ function casetracker_form_alter(&$form, 
     if (casetracker_is_case($node->type)) {
       $count = count(casetracker_project_options());
       if ($count == 0) {
-        // We can't make a link to a project here because the admin may have assigned more than one node type as project usable.
-        // @TODO of course we can... ever heard of multiple links?
         drupal_set_message(t('You must create a project before adding cases.'), 'error');
         return;
       }
       else {
-        casetracker_case_form_common($form);
+        $default_project = null;
+        if (!isset($form['#node']->nid) && is_numeric(arg(3))) {
+          $default_project = arg(3);
+        }
+        casetracker_case_form_common($form, $default_project);
       }
     }
   }
@@ -301,7 +338,7 @@ function casetracker_form_comment_form_a
  * @param $form
  *   A Forms API $form, as received from a hook_form_alter().
  * @param $default_project
- *   The project ID that should be pre-selected (ie., no select box).
+ *   The project ID that should be pre-selected.
  * @return $form
  *   A modified Forms API $form.
  */
@@ -310,16 +347,16 @@ function casetracker_case_form_common(&$
 
   $node = $form['#node'];
 
-  // project to set as the default is based on how the user got here.
-  if (isset($default_project)) {
-    $default_project = $default_project;
+  // On preview the case will be an array, we want an object.
+  if (isset($node->build_mode) && $node->build_mode == NODE_BUILD_PREVIEW) {
+    $node->casetracker = (object)$node->casetracker;
   }
-  else if (!empty($node->casetracker->pid)) {
+
+  // project to set as the default is based on how the user got here.
+ if (empty($default_project) && !empty($node->casetracker->pid)) {
     $default_project = $node->casetracker->pid;
   }
-  else {
-    $default_project = 0;
-  }
+
   $project_options = casetracker_project_options();
 
   $form['casetracker'] = array(
@@ -350,36 +387,41 @@ function casetracker_case_form_common(&$
     );
   }
 
-  $options = casetracker_user_options();
+  // Retrieve the assign_to default value.
+  if (isset($node->casetracker->assign_to)) {
+    $default_assign_to = is_numeric($node->casetracker->assign_to) ? casetracker_get_name($node->casetracker->assign_to) : $node->casetracker->assign_to;
+  }
+  else {
+    $default_assign_to = casetracker_default_assign_to();
+  }
 
+  // Only show this element if the user has access.
   $form['casetracker']['assign_to'] = array(
-    '#type' => 'textfield',
     '#title' => t('Assign to'),
-    '#autocomplete_path' => 'casetracker_autocomplete',
     '#required' => TRUE,
-    '#size' => 12,
+    '#access' => user_access('assign cases'),
   );
-  if (count($options) < 20) {
-    $normalized = array();
-    foreach ($options as $name) {
-      $normalized[$name] = $name;
-    }
-    $form['casetracker']['assign_to'] = array(
-    '#type' => 'radios',
-    '#title' => t('Assign to'),
-    '#required' => TRUE,
-    '#options' => $normalized,
-    );
-  }
 
-  if (isset($node->casetracker->assign_to)) {
-    $form['casetracker']['assign_to']['#default_value'] = is_numeric($node->casetracker->assign_to) ? casetracker_get_name($node->casetracker->assign_to) : $node->casetracker->assign_to;
+  // Use different widgets based on the potential assignees.
+  $options = drupal_map_assoc(casetracker_user_options());
+  if (count($options) < 25) {
+    $form['casetracker']['assign_to']['#type'] = 'radios';
+    $form['casetracker']['assign_to']['#options'] = $options;
+  }
+  else if (count($options) < 50) {
+    $form['casetracker']['assign_to']['#type'] = 'select';
+    $form['casetracker']['assign_to']['#options'] = $options;
   }
   else {
-    $form['casetracker']['assign_to']['#default_value'] = variable_get('casetracker_default_assign_to', variable_get('anonymous', t('Anonymous')));
+    $form['casetracker']['assign_to']['#type'] = 'textfield';
+    $form['casetracker']['assign_to']['#autocomplete_path'] = 'casetracker_autocomplete';
+    $form['casetracker']['assign_to']['#size'] = 12;
   }
 
-  $case_status_options = casetracker_case_state_load('status');
+  // Set the default value if it is valid.
+  $form['casetracker']['assign_to']['#default_value'] = in_array($default_assign_to, $options, TRUE) ? $default_assign_to : NULL;
+
+  $case_status_options = casetracker_realm_load('status');
   $default_status = !empty($node->casetracker->case_status_id) ? $node->casetracker->case_status_id : variable_get('casetracker_default_case_status', key($case_status_options));
   $form['casetracker']['case_status_id'] = array(
     '#type' => 'select',
@@ -388,7 +430,7 @@ function casetracker_case_form_common(&$
     '#default_value' => $default_status,
   );
 
-  $case_priority_options = casetracker_case_state_load('priority');
+  $case_priority_options = casetracker_realm_load('priority');
   $default_priority = !empty($node->casetracker->case_priority_id) ? $node->casetracker->case_priority_id : variable_get('casetracker_default_case_priority', key($case_priority_options));
     $form['casetracker']['case_priority_id'] = array(
     '#type' => 'select',
@@ -397,7 +439,7 @@ function casetracker_case_form_common(&$
     '#default_value' => $default_priority,
   );
 
-  $case_type_options = casetracker_case_state_load('type');
+  $case_type_options = casetracker_realm_load('type');
   $default_type = !empty($node->casetracker->case_type_id) ? $node->casetracker->case_type_id : variable_get('casetracker_default_case_type', key($case_type_options));
   $form['casetracker']['case_type_id'] = array(
     '#type' => 'select',
@@ -410,6 +452,67 @@ function casetracker_case_form_common(&$
 }
 
 /**
+* Implementation of hook_block
+*/
+function casetracker_block($op = 'list', $delta = 0, $edit = array()) {
+  switch ($op) {
+    case 'list':
+      $blocks[0] = array(
+        'info' => t('Jump to case'),
+      );
+      return $blocks;
+    case 'configure':
+       return array();
+    case 'save':
+        return;
+    case 'view':
+      switch ($delta) {
+        case 0:
+          if (user_access('access case tracker')) {
+            $block['subject'] = t('Jump to case');
+            $block['content'] = drupal_get_form('casetracker_block_jump_to_case_number');
+          }
+          break;
+      }
+      return $block;
+  }
+}
+
+/**
+ * Form for "Jump to case number" block.
+ */
+function casetracker_block_jump_to_case_number() {
+  $form = array();
+  $form['case_number'] = array(
+    '#maxlength' => 60, 
+    '#required' => TRUE,
+    '#size' => 15,
+    '#title' => t('Case number'),
+    '#type' => 'textfield',
+    '#prefix' => '<div class="container-inline">',
+  );
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Go'),
+    '#suffix' => '</div>',
+  );
+  return $form;
+} 
+
+/**
+ * Submit function for our "Jump to case number" block.
+ */
+function casetracker_block_jump_to_case_number_submit($form, $form_state) {
+  list($pid, $nid) = explode('-', $form_state['values']['case_number']);
+  $case_nid = db_result(db_query("SELECT nid FROM {casetracker_case} WHERE pid = %d AND nid = %d", $pid, $nid));
+  if (!$case_nid) {
+    drupal_set_message(t('Your case number was not found.'), 'error');
+    return;
+  }
+  drupal_goto('node/'. $case_nid);
+}
+
+/**
  * CASE STATE CRUD ====================================================
  */
 
@@ -417,43 +520,43 @@ function casetracker_case_form_common(&$
  * Returns information about the various case states and their options.
  * The number of parameters passed will determine the return value.
  *
- * @param $realm
- *   Optional; the name of the realm ('status', 'priority', or 'type').
  * @param $csid
  *   Optional; the state ID to return from the passed $realm.
+ * @param $realm
+ *   Optional; the name of the realm ('status', 'priority', or 'type').
+ * @param $reset
+ *   Optional; set to TRUE to reset the static cache.
+ *
  * @return $values
  *   If only $realm is passed, you'll receive an array with the keys
  *   being the state ID and the values being their names. If a $csid
  *   is also passed, you'll receive just a string of the state name.
  *   If ONLY a $csid is passed, we'll return a list of 'name', 'realm'.
  */
-function casetracker_case_state_load($realm = NULL, $csid = NULL) {
-  static $states_lookup = array();
+function casetracker_case_state_load($csid = NULL, $realm = NULL, $reset = FALSE) {
+  static $states_lookup;
 
-  if (!$states_lookup) {
-    $results = db_query("SELECT csid, case_state_name, case_state_realm, weight FROM {casetracker_case_states} ORDER BY weight");
-    while ($result = db_fetch_object($results)) { // offer cached csid and realm lookups from a one-time query.
-      $states_lookup[$result->case_state_realm][$result->csid] = array(
-        'name' => $result->case_state_name, 
-        'realm' => $result->case_state_realm, 
-        'weight' => (int)$result->weight,
-        'csid' => (int)$result->csid
-      );
-      $states_lookup[$result->csid] = $states_lookup[$result->case_state_realm][$result->csid];
+  if (!$states_lookup || $reset) {
+    $results = db_query("SELECT csid, case_state_name AS name, case_state_realm AS realm, weight 
+                         FROM {casetracker_case_states} ORDER BY weight");
+    $states_lookup = array();
+    while ($row = db_fetch_object($results)) {
+      $row->display = casetracker_tt("case_states:$row->csid:name", $row->name);
+      $states_lookup[$row->realm][$row->csid] = $states_lookup['all'][$row->csid] = $row;
     }
   }
 
   if ($csid && $realm) {
-    return $states_lookup[$csid]['name'];
+    return $states_lookup['all'][$csid]->display;
   }
   elseif ($csid && !$realm) {
-    return $states_lookup[$csid];
+    return $states_lookup['all'][$csid];
   }
   elseif (!$csid && $realm) {
     $options = array(); // suitable for form api.
     if (!empty($states_lookup[$realm])) {
       foreach ($states_lookup[$realm] as $state) {
-        $options[$state['csid']] = $state['name'];
+        $options[$state->csid] = $state->display;
       }
     }
     return $options;
@@ -461,6 +564,61 @@ function casetracker_case_state_load($re
 }
 
 /**
+ * Translate user defined string. Wrapper function for tt() if i18nstrings enabled.
+ * 
+ * The string id for case states will be: case:[realm]#[csid]:name
+ * 
+ * @param $name
+ *   String id without 'casetracker', which will be prepended automatically
+ */
+function casetracker_tt($name, $string, $langcode = NULL, $update = FALSE) {
+  if (function_exists('tt')) {
+    return tt('casetracker:' . $name, $string, $langcode, $update);
+  }
+  else {
+    return $string;
+  }
+}
+
+/**
+ * Implementation of hook_locale().
+ */
+function casetracker_locale($op = 'groups', $group = NULL) {
+  switch ($op) {
+    case 'groups':
+      return array('casetracker' => t('Case Tracker'));
+    case 'info':
+      $info['casetracker']['refresh callback'] = 'casetracker_locale_refresh';
+      return $info;
+  }
+}
+
+/**
+ * Refresh locale strings.
+ */
+function casetracker_locale_refresh() {
+  $results = db_query("SELECT csid, case_state_name AS name, case_state_realm AS realm FROM {casetracker_case_states}");
+  while ($row = db_fetch_object($results)) {
+    casetracker_tt("case_states:$row->csid:name", $row->name, NULL, TRUE);
+  }
+  // Meaning it completed with no issues. @see i18nmenu_locale_refresh().
+  return TRUE;
+}
+
+/**
+ * Load states for a particular realm. Wrapper around casetracker_case_state_load()
+ *
+ * @param $realm
+ *   Name of the realm ('status', 'priority', or 'type').
+ *
+ * @return
+ *   array with the keys being the state ID and the values being their names.
+ */
+function casetracker_realm_load($realm) {
+  return casetracker_case_state_load(null, $realm);
+}
+
+/**
  * Saves a case state.
  *
  * @param $case_state
@@ -485,6 +643,9 @@ function casetracker_case_state_save($ca
   else {
     drupal_write_record('casetracker_case_states', $record);
   }
+  // Update translations
+  casetracker_tt('case_states:'. $record['csid'] .':name', $case_state['name'], NULL, TRUE);
+
   return $result;
 }
 
@@ -510,22 +671,28 @@ function casetracker_case_state_delete($
  */
 
 /**
- * Retrieve a pipe delimited string of autocomplete suggestions for existing
- * users. Stolen from user_autocomplete. Eventually this will be expanded to
- * include OG specific users subscribed to a project.
+ * Retrieve autocomplete suggestions for assign to user options.
+ *
+ * @TODO: In order to get this down to 1 query and respect any custom
+ * views selected for use as user option filters, we need to:
+ * - Submit a patch to the Views user name filter/argument handler to support LIKE filtering.
+ * - Ensure that the custom view uses this handler or add it if does not.
+ * - Generate the query & result set using this modified View.
  */
 function casetracker_autocomplete($string) {
   $matches = array();
-  $group = module_exists('og') ? og_get_group_context() : NULL;
-  if ($group) {
-    $gid = $group->nid;
-    $result = db_query_range("SELECT u.name FROM {users} u LEFT JOIN {og_uid} ou ON u.uid = ou.uid WHERE LOWER(name) LIKE LOWER('%s%%') AND u.status > 0 AND ou.nid = %d", $string, $gid, 0, 10);
-  }
-  else {
-    $result = db_query_range("SELECT name FROM {users} WHERE LOWER(name) LIKE LOWER('%s%%')", $string, 0, 10);
-  }
+  $options = casetracker_user_options();
+  $result = db_query_range("SELECT name FROM {users} WHERE LOWER(name) LIKE LOWER('%s%%')", $string, 0, 10);
   while ($user = db_fetch_object($result)) {
-    $matches[$user->name] = check_plain($user->name);
+    if (in_array($user->name, $options, TRUE)) {
+      $matches[$user->name] = check_plain($user->name);
+    }
+  }
+
+  // Special case for 'Unassigned'
+  $unassigned = t('Unassigned');
+  if (strpos(strtolower($unassigned), strtolower($string)) !== FALSE) {
+    $matches[$unassigned] = $unassigned;
   }
   drupal_json($matches);
 }
@@ -610,20 +777,20 @@ function theme_casetracker_comment_chang
           $new->{$field} = l($new_title, "node/{$new->pid}");
           break;
         case 'case_status_id':
-          $old->{$field} = casetracker_case_state_load('status', $old->{$field});
-          $new->{$field} = casetracker_case_state_load('status', $new->{$field});
+          $old->{$field} = casetracker_case_state_load($old->{$field}, 'status');
+          $new->{$field} = casetracker_case_state_load($new->{$field}, 'status');
           break;
         case 'assign_to':
           $old->{$field} = casetracker_get_name($old->{$field});
           $new->{$field} = casetracker_get_name($new->{$field});
           break;
         case 'case_priority_id':
-          $old->{$field} = casetracker_case_state_load('priority', $old->{$field});
-          $new->{$field} = casetracker_case_state_load('priority', $new->{$field});
+          $old->{$field} = casetracker_case_state_load($old->{$field}, 'priority');
+          $new->{$field} = casetracker_case_state_load($new->{$field}, 'priority');
           break;
         case 'case_type_id':
-          $old->{$field} = casetracker_case_state_load('type', $old->{$field});
-          $new->{$field} = casetracker_case_state_load('type', $new->{$field});
+          $old->{$field} = casetracker_case_state_load($old->{$field}, 'type');
+          $new->{$field} = casetracker_case_state_load($new->{$field}, 'type');
           break;
       }
       $rows[] = array(t('!label: !old &raquo; !new', array('!label' => $label, '!old' => $old->{$field}, '!new' => $new->{$field})));
@@ -643,14 +810,16 @@ function theme_casetracker_case_form_com
   $output .= drupal_render($form['case_title']);
 
   if ($form['assign_to']['#type'] == 'radios') {
-    $header = array_fill(0, 5, array());
-    $header[0] = $form['assign_to']['#title'];
-    $radios = array();
-    foreach (element_children($form['assign_to']) as $id) {
-      $radios[] = drupal_render($form['assign_to'][$id]);
+    if ($form['assign_to']['#access']) {
+      $header = array_fill(0, 5, array());
+      $header[0] = $form['assign_to']['#title'];
+      $radios = array();
+      foreach (element_children($form['assign_to']) as $id) {
+        $radios[] = drupal_render($form['assign_to'][$id]);
+      }
+      $radios = array_chunk($radios, 5);
+      $output .= theme('table', $header, $radios, array('class' => 'casetracker-assign-to'));
     }
-    $radios = array_chunk($radios, 5);
-    $output .= theme('table', $header, $radios, array('class' => 'casetracker-assign-to'));
     drupal_render($form['assign_to']);
   }
   else {
@@ -681,25 +850,45 @@ function theme_casetracker_case_summary(
   $last_comment = db_result(db_query('SELECT last_comment_timestamp FROM {node_comment_statistics} WHERE nid = %d', $case->nid));
   $rows = array();
 
-  $rows[] = array(
-    t('Assigned to:'),
-    theme('username', user_load(array('uid' => $case->casetracker->assign_to))),
-  );
+  // On node preview the form logic can't translate assign_to back to a uid for
+  // us so we need to be able handle it either way.
+  if (is_numeric($case->casetracker->assign_to)) {
+    $assign_to = user_load(array('uid' => $case->casetracker->assign_to));
+  }
+  else {
+    $assign_to = user_load(array('name' => $case->casetracker->assign_to));
+  }
+
+  if (empty($assign_to) || $assign_to->uid == 0) {
+    $rows[] = array(
+      t('Assigned to:'),
+      '<em>' . t('Unassigned')  . '</em>',
+    );
+  }
+  else {
+    $rows[] = array(
+      t('Assigned to:'),
+      theme('username', $assign_to),
+    );
+  }
   $rows[] = array(
     t('Created:'),
-    theme_username($case) ." ". t('at') ." ". format_date($case->created, 'medium'),
+    t('!username at !date', array('!username' => theme('username', $case), '!date' => format_date($case->created, 'medium'))),
   );
   $rows[] = array(
     t('Status:'),
-    "<strong>". casetracker_case_state_load('status', $case->casetracker->case_status_id) ."</strong> (" .
-    casetracker_case_state_load('type', $case->casetracker->case_type_id) .' / '.
-    t("Priority")  ." ".
-    casetracker_case_state_load('priority', $case->casetracker->case_priority_id) .
-    ")",
+    t('<stong>!status</strong> (!type / Priority !priority)', array(
+      '!status' => casetracker_case_state_load($case->casetracker->case_status_id, 'status'),
+      '!type' => casetracker_case_state_load($case->casetracker->case_type_id, 'type'),
+      '!priority' => casetracker_case_state_load($case->casetracker->case_priority_id, 'priority'),
+    )),
   );
+
+  // On node preview a case may not have a nid, so we use some placeholder text.
+  $case_id = isset($case->nid) ? $case->nid : t("NEW");
   $rows[] = array(
     t('Case ID:'),
-    l($project->title, 'node/'. $case->casetracker->pid) .': '. $project->nid .'-'. $case->nid,
+    l($project->title, 'node/'. $case->casetracker->pid) .': '. $project->nid .'-'. $case_id,
   );
   if ($last_comment != $case->created) {
     $rows[] = array(
@@ -723,7 +912,7 @@ function theme_casetracker_case_summary(
 function theme_casetracker_project_summary($project) {
   $rows = array();
   $rows[] = array(t('Project number:'), $project->nid);
-  $rows[] = array(t('Opened by:'), theme_username($project));
+  $rows[] = array(t('Opened by:'), theme('username', $project));
   $rows[] = array(t('Opened on:'), format_date($project->created, 'large'));
   $rows[] = array(t('Last modified:'), format_date($project->changed, 'large'));
 
@@ -755,7 +944,7 @@ function theme_casetracker_project_summa
 function casetracker_project_options() {
   $projects = array();
   // Fetch the views list of projects, which is space-aware.
-  if ($view = views_get_view('casetracker_project_options')) {
+  if ($view = views_get_view(variable_get('casetracker_view_project_options', 'casetracker_project_options'))) {
     $view->set_display();
     $view->set_items_per_page(0);
     $view->execute();
@@ -770,17 +959,28 @@ function casetracker_project_options() {
  * API function that returns valid user options.
  */
 function casetracker_user_options() {
-  $group = module_exists('og') ? og_get_group_context() : NULL;
-  $options = array(0 => variable_get('casetracker_default_assign_to', variable_get('anonymous', t('Anonymous'))));
-  if ($group) {
-    $gid = $group->nid;
-    $results = db_query('SELECT u.uid, u.name FROM {users} u INNER JOIN {og_uid} ou ON u.uid = ou.uid WHERE u.status > 0 AND u.uid > 0 AND ou.nid = %d ORDER BY u.name ASC', $gid);
-  }
-  else {
-    $results = db_query('SELECT u.uid, u.name FROM {users} u WHERE u.status > 0 AND u.uid > 0 ORDER BY u.name ASC');
+  $users = array();
+  $options = array();
+  if ($view = views_get_view(variable_get('casetracker_view_assignee_options', 'casetracker_assignee_options'))) {
+    $view->set_display();
+    $view->set_items_per_page(0);
+    $view->execute();
+    foreach ($view->result as $row) {
+      $options[$row->uid] = $row->users_name;
+    }
   }
-  while ($result = db_fetch_object($results)) {
-    $options[$result->uid] = check_plain($result->name);
+
+  $anon_user = casetracker_default_assign_to();
+
+  // fill in "Unassigned" value because view is not rendered and the redundant option in views is irrelevant
+  // @TODO render the view before display so this isn't needed.
+  if (isset($options[0])) {
+    $options[0] = $anon_user;
+  }
+  // if "Unassigned" is the default assignee, we graft it onto the view results here as most views
+  // that do any substantive filtering will exclude the anonymous user.
+  elseif (in_array(variable_get('casetracker_default_assign_to', $anon_user),  array($anon_user, variable_get('anonymous', t('Anonymous'))))) {
+    $options = array($anon_user) + $options;
   }
   return $options;
 }
@@ -840,8 +1040,40 @@ function casetracker_get_uid($name = NUL
 function casetracker_get_name($uid = NULL, $reset = FALSE) {
   static $users = array();
   if (!isset($users[$uid]) || $reset) {
-    $result = db_result(db_query("SELECT name FROM {users} WHERE uid = %d", $uid));
-    $users[$uid] = $result ? $result : '';
+    if ($uid == 0) {
+      $users[0] = t('Unassigned'); 
+    }
+    else {
+      $result = db_result(db_query("SELECT name FROM {users} WHERE uid = %d", $uid));
+      $users[$uid] = $result ? $result : '';
+    }
+  }
+  return !empty($users[$uid]) ? $users[$uid] : casetracker_default_assign_to();
+}
+
+/**
+ *
+ */
+function casetracker_default_assign_to() {
+  $assign_to = variable_get('casetracker_default_assign_to', t('Unassigned'));
+  if ($assign_to == variable_get('anonymous', t('Anonymous'))) {
+    return t('Unassigned');
   }
-  return !empty($users[$uid]) ? $users[$uid] : variable_get('casetracker_default_assign_to', variable_get('anonymous', t('Anonymous')));
+  return $assign_to;
+}
+
+/**
+ * Implementation of hook_token_values().
+ */
+function casetracker_token_values($type, $object = NULL) {
+  require_once(drupal_get_path('module', 'casetracker') .'/casetracker.token.inc');
+  return _casetracker_token_values($type, $object);
+}
+
+/**
+ * Implementation of hook_token_list().
+ */
+function casetracker_token_list($type = 'all') {
+  require_once(drupal_get_path('module', 'casetracker') .'/casetracker.token.inc');
+  return _casetracker_token_list($type);
 }
