Index: comment.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/project_issue/comment.inc,v
retrieving revision 1.116
diff -u -p -r1.116 comment.inc
--- comment.inc	21 Jan 2008 17:34:45 -0000	1.116
+++ comment.inc	22 Jan 2008 15:19:16 -0000
@@ -175,21 +175,18 @@ function project_issue_comment(&$arg, $o
 /**
  * Create a project issue metadata table.
  *
- * @param $labels
- *  An array, keys are field names, values are the displayed labels.
- * @param $old
- *  The previous comment (or the node).
- * @param $followup
- *  The current comment.
+ * @param $comment_changes
+ *  Array containing metadata differences between comments
+ *  as returned by project_issue_comment_changes().
  */
-function _project_issue_comment_table($labels, $old, $followup) {
+function _project_issue_comment_table($comment_changes) {
   $rows = array();
-  foreach ($labels as $field => $text) {
-    if ($old->$field != $followup->$field) {
+  foreach ($comment_changes as $field => $change) {
+    if (!empty($change['label']) && isset($change['old']) && isset($change['new'])) {
       $rows[] = array(
-        $labels[$field] .':',
-        project_mail_summary($field, $old->$field),
-        '&raquo; '. project_mail_summary($field, $followup->$field),
+        $change['label']. ':',
+        project_issue_change_summary($field, $change['old']),
+        '&raquo; '. project_issue_change_summary($field, $change['new']),
       );
     }
   }
@@ -218,13 +215,103 @@ function project_issue_comment_view(&$no
     $labels = project_issue_field_labels('web');
     $result = db_query('SELECT cid, title, pid, rid, component, category, priority, assigned, sid FROM {project_issue_comments} WHERE nid = %d ORDER BY timestamp ASC', $node->nid);
     while ($followup = db_fetch_object($result)) {
-      $project_issue_tables[$followup->cid] = _project_issue_comment_table($labels, $old, $followup);
+      $followup_changes = project_issue_comment_changes($node, $old, $followup, project_issue_field_labels('web'));
+      $project_issue_tables[$followup->cid] = _project_issue_comment_table($followup_changes);
       $old = $followup;
     }
   }
 }
 
 /**
+ * Calculate the differences in project_issue comment metadata
+ * between the original issue and a comment or between two
+ * comments.
+ *
+ * @param $node
+ *  The issue node.
+ * @param $old_data
+ *  Object containing old metadata.
+ * @param $new_data
+ *  Object containing new metadata.
+ * @param $field_labels
+ *  An associative array of field_name=>display_name pairs.
+ *  In most cases, this will be the array returned by project_issue_change_summary().
+ *
+ * @return
+ *  An associative array containing information about changes between
+ *  the two objects.
+ *  For example:
+ *  array(
+ *    'component' => array(
+ *      'name' => t('Component'),
+ *      'old' => 'Code',
+ *      'new' => 'User interface',
+ *    ),
+ *    'sid' => array(
+ *      'name' => t('Status'),
+ *      'old' => 8,
+ *      'new' => 13,
+ *    ),
+ *  )
+ */
+function project_issue_comment_changes($node, $old_data, $new_data, $field_labels = array()) {
+  // Populate output array with available names and labels.
+  $changes = array();
+  foreach ($field_labels as $property => $name) {
+    $changes[$property] = array('label' => $name);
+  }
+
+  // Create an array that contains the name of all properties in both $old_data and $new_data
+  $all_properties = array();
+  if (!empty($old_data)){
+    $all_properties = array_keys(get_object_vars($old_data));
+  }
+  if (!empty($new_data)) {
+    $all_properties = array_merge($all_properties, array_keys(get_object_vars($new_data)));
+  }
+  $all_properties = array_unique($all_properties);
+  foreach ($all_properties as $property) {
+    if (isset($old_data->$property) && isset($new_data->$property)) {
+      if ($old_data->$property != $new_data->$property) {
+        $changes[$property]['old'] = $old_data->$property;
+        $changes[$property]['new'] = $new_data->$property;
+      }
+    }
+    elseif (isset($old_data->$property)) {
+      $changes[$property]['old'] = $old_data->$property;
+      $changes[$property]['new'] = '';
+    }
+    else {
+      $changes[$property]['old'] = '';
+      $changes[$property]['new'] = $new_data->$property;
+    }
+  }
+
+  // Allow other modules to implement hook_followup_metadata_changes() so that they
+  // can find changes in additional metadata.  In most cases other modules will
+  // be responsible for storing this metadata in their own tables.  Developers
+  // of modules that implement this hook should keep in mind the following:
+  // 1.  Implementations of hook_followup_metadata_changes() must take the
+  //     $project_issue_comment_changes array by reference.
+  // 2.  Differences in properties will only be processed later on for
+  //     elements of the array which have the 'label', 'old', and 'new' properties
+  //     defined.
+  // In other words, for each line in the differences table (or field in the email)
+  // that is displayed, your hook should add something like the following as a
+  // new element of the $changes:
+  //    'taxonomy_vid_10' => array(
+  //       'name' => 'Vocabulary 10',
+  //       'old' => 'MySQL,PGSQL',
+  //       'new' => 'PGSQL',
+  //     ),
+  foreach (module_implements('followup_metadata_changes') as $module) {
+    $function = $module .'_followup_metadata_changes';
+    $function($node, $changes, $old_data, $new_data);
+  }
+  return $changes;
+}
+
+/**
  * Updates the project issue based on the comment inserted/updated/deleted.
  *
  * @param $comment_data
Index: issue.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/project_issue/issue.inc,v
retrieving revision 1.291
diff -u -p -r1.291 issue.inc
--- issue.inc	21 Jan 2008 15:58:00 -0000	1.291
+++ issue.inc	22 Jan 2008 15:19:16 -0000
@@ -2013,6 +2013,48 @@ function project_issue_field_labels($con
   return $labels;
 }
 
+/**
+ * Provide the text displayed to a user for a specific metadata field.
+ *
+ * @param $field
+ *   Name of the field.
+ * @param $value
+ *   Value of the field.
+ *
+ * @return
+ *   Text to display to user.
+ */
+function project_issue_change_summary($field, $value) {
+  switch ($field) {
+    case 'pid':
+      $project = node_load(array('nid' => $value, 'type' => 'project_project'));
+      return $project->title;
+    case 'category':
+      return $value ? project_issue_category($value, 0) : t('<none>');
+    case 'priority':
+      return $value ? project_issue_priority($value) : t('<none>');
+    case 'rid':
+      if ($value) {
+        $release->nid = $value;
+        if (module_exists('project_release')) {
+          $release = project_release_load($release);
+        }
+        else {
+          $release->version = t('Unknown');
+        }
+        return $release->version;
+      }
+      return t('<none>');
+    case 'assigned':
+      $user = user_load(array('uid' => $value));
+      return $value === 0 ? variable_get('anonymous', t('Anonymous')) : $user->name;
+    case 'sid':
+      return $value ? project_issue_state($value) : t('<none>');
+    default:
+      return check_plain($value);
+  }
+}
+
 function theme_project_issue_follow_up_forbidden($nid) {
   global $user;
   if ($user->uid) {
Index: mail.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/project_issue/mail.inc,v
retrieving revision 1.100
diff -u -p -r1.100 mail.inc
--- mail.inc	21 Jan 2008 23:59:21 -0000	1.100
+++ mail.inc	22 Jan 2008 15:19:16 -0000
@@ -164,37 +164,6 @@ function project_mail_output(&$body, $ht
   }
 }
 
-function project_mail_summary($field, $value) {
-  switch ($field) {
-    case 'pid':
-      $project = node_load(array('nid' => $value, 'type' => 'project_project'));
-      return $project->title;
-    case 'category':
-      return $value ? project_issue_category($value, 0) : t('<none>');
-    case 'priority':
-      return $value ? project_issue_priority($value) : t('<none>');
-    case 'rid':
-      if ($value) {
-        $release->nid = $value;
-        if (module_exists('project_release')) {
-          $release = project_release_load($release);
-        }
-        else {
-          $release->version = t('Unknown');
-        }
-        return $release->version;
-      }
-      return t('<none>');
-    case 'assigned':
-      $user = user_load(array('uid' => $value));
-      return $value === 0 ? variable_get('anonymous', t('Anonymous')) : $user->name;
-    case 'sid':
-      return $value ? project_issue_state($value) : t('<none>');
-    default:
-      return check_plain($value);
-  }
-}
-
 function project_mail_notify($nid) {
   global $base_url, $user;
 
@@ -368,16 +337,18 @@ function project_mail_generate_followup_
     $content = $entry->comment;
   }
 
+  $comment_changes = project_issue_comment_changes($node, $previous, $entry, $fields);
+
   // Mail summary (status values).
   $summary = '';
-  foreach ($fields as $field => $text) {
-    $text = str_pad("$text:", 14);
-    if (isset($previous) && $previous->$field != $entry->$field && $field != 'updator' && $field != 'name') {
-      $summary .= "-$text". project_mail_summary($field, $previous->$field) ."\n";
-      $summary .= "+$text". project_mail_summary($field, $entry->$field) ."\n";
+  foreach ($comment_changes as $field => $change) {
+    $text = str_pad($change['label']. ':', 14);
+    if (!empty($change['label']) && isset($change['old']) && isset($change['new']) && $field != 'updator' && $field != 'name') {
+      $summary .= "-$text". project_issue_change_summary($field, $change['old']) ."\n";
+      $summary .= "+$text". project_issue_change_summary($field, $change['new']) ."\n";
     }
-    else {
-      $summary .= " $text". project_mail_summary($field, $node->$field) ."\n";
+    elseif (!empty($change['label'])) {
+      $summary .= " $text". project_issue_change_summary($field, $node->$field) ."\n";
     }
   }
 
