Index: comment.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/project_issue/comment.inc,v
retrieving revision 1.140
diff -u -F^f -u -F^f -r1.140 comment.inc
--- comment.inc	27 Jan 2009 17:03:58 -0000	1.140
+++ comment.inc	29 Jan 2009 21:22:27 -0000
@@ -78,7 +78,7 @@ function project_issue_comment(&$arg, $o
         $test->component = $arg->project_info['component'];
         $test->assigned = $arg->project_info['assigned'];
         // Add a dummy rid if necessary -- prevents incorrect change data.
-        $test->rid = $arg->project_info['rid'] ? $arg->project_info['rid'] : 0;
+        $test->rid = isset($arg->project_info['rid']) ? $arg->project_info['rid'] : 0;
         $comment_changes = project_issue_metadata_changes($node, $old_data, $test, project_issue_field_labels('web'));
         $project_issue_table = theme('project_issue_comment_table', $comment_changes);
       }
@@ -92,13 +92,13 @@ function project_issue_comment(&$arg, $o
 
 /**
  * Add project issue metadata to the comment form.
- * 
+ *
  * @param $form
  *   Reference to form structure.
  * @param $form_state
  *   Current form state.
  */
-function project_issue_form_comment_form_alter(&$form, &$form_state) {
+function _project_issue_form_comment_form_alter(&$form, &$form_state) {
   $nid = $form['nid']['#value'];
   $node = node_load($nid);
 
@@ -106,7 +106,7 @@ function project_issue_form_comment_form
   if ($node->type != 'project_issue') {
     return;
   }
-  
+
   // Add our own custom validation to the comment form for issue nodes.
   $form['#validate'][] = 'project_issue_form_comment_validate';
 
@@ -143,6 +143,10 @@ function project_issue_form_comment_form
     $form['#pre_render'][] = 'project_issue_comment_pre_render';
   }
 
+  // Make sure project is current here -- it may have changed when posted.
+  if (!empty($form_state['values']['project_info']['pid'])) {
+    $node->project_issue['pid'] = $form_state['values']['project_info']['pid'];
+  }
   $project = node_load(array('nid' => $node->project_issue['pid'], 'type' => 'project_project'));
 
   // We have to set $form['#action'] to prevent AHAH nastiness.
@@ -207,7 +211,7 @@ function project_issue_form_comment_form
     '#weight' => -30,
     '#required' => TRUE,
   );
-  
+
   $form['project_info']['assigned'] = $form['issue_info']['assigned'];
   unset($form['issue_info']['assigned']);
 
@@ -231,8 +235,6 @@ function project_issue_form_comment_form
     $form['attachments']['#collapsed'] = FALSE;
     $form['attachments']['#weight'] = 2;
   }
-
-  return $form;
 }
 
 /**
@@ -243,13 +245,15 @@ function project_issue_form_comment_form
  * @param $form_state
  *   The current state of the form.
  */
-function project_issue_form_comment_validate($form, &$form_state) {    
-  if(isset($form_state['project_issue_ahah'])) {
+function project_issue_form_comment_validate($form, &$form_state) {
+  if(!empty($form_state['rebuild'])) {
     return;
   }
-  $nid = $form_state['values']['nid'];
+  $values = $form_state['values'];
+  $project_info = $form_state['values']['project_info'];
+  $nid = $values['nid'];
   $node = node_load($nid);
-  
+
   // Make a copy here so we have all the original metadata, since some
   // of it can change below.
   $original_node = drupal_clone($node);
@@ -259,27 +263,28 @@ function project_issue_form_comment_vali
   // Adjust new file attachments to go to the issues directory.
   // We have to do this during validate, otherwise we might miss
   // adjusting the filename before comment upload saves it (module weighting)
-  project_issue_change_comment_upload_path($form_state['values']);
-  
+  // TODO: is this still true?
+  project_issue_change_comment_upload_path($values);
+
   // Only validate metadata changes on new followups.
-  if (isset($form_state['values']['cid'])) {
+  if (isset($values['cid'])) {
     return;
   }
 
   // Make sure project is current here -- it may have changed when posted.
-  if (isset($form_state['values']['project_info']['pid'])) {
-    $node->project_issue['pid'] = $form_state['values']['project_info']['pid'];
+  if (isset($project_info['pid'])) {
+    $node->project_issue['pid'] = $project_info['pid'];
   }
   $project = node_load($node->project_issue['pid']);
 
   if (!empty($project) && $project->type == 'project_project') {
     // Force all comments to be a child of the main issue, to match the
     // flat display, and also to prevent accidentally deleting a thread.
-    form_set_value(array('#parents' => array('pid')), 0, $form_state);
+    $form_state['values']['pid'] = 0;
 
     // Validate version.
-    if (module_exists('project_release') && ($releases = project_release_get_releases($project, 0, 'version', 'all', array($form_state['values']['project_info']['rid'])))) {
-      $rid = $form_state['values']['project_info']['rid'];
+    if (module_exists('project_release') && isset($project_info['rid']) && ($releases = project_release_get_releases($project, 0, 'version', 'all', array($project_info['rid'])))) {
+      $rid = $project_info['rid'];
       if ($rid && !in_array($rid, array_keys($releases))) {
         $rid = 0;
       }
@@ -295,8 +300,12 @@ function project_issue_form_comment_vali
         form_set_error('project_info][rid', t('You have to specify a valid version.'));
       }
     }
+    // Add a dummy rid if necessary -- prevents incorrect change data.
+    else {
+      $rid = 0;
+    }
     // Validate component.
-    $component = $form_state['values']['project_info']['component'];
+    $component = $project_info['component'];
     if ($component && !in_array($component, $project->project_issue['components'])) {
       $component = 0;
     }
@@ -305,26 +314,31 @@ function project_issue_form_comment_vali
   else {
     form_set_error('project_info][pid', t('You have to specify a valid project.'));
   }
-  empty($form_state['values']['category']) && form_set_error('category', t('You have to specify a valid category.'));
+  empty($values['category']) && form_set_error('category', t('You have to specify a valid category.'));
 
   // Now, make sure the comment changes *something* about the issue.
-  // If the user uploaded a file, so long as it's not marked for removal, 
+  // If the user uploaded a file, so long as it's not marked for removal,
   // we consider that a valid change to the issue, too.
   $has_file = FALSE;
-  $files = isset($form_state['values']['files']) ? $form_state['values']['files'] : array();
+  $files = isset($values['files']) ? $values['files'] : array();
   foreach ($files as $number => $data) {
     if (empty($data['remove'])) {
       $has_file = TRUE;
       break;
     }
   }
-  if (!$has_file && empty($form_state['values']['comment'])) {
-    $comment = drupal_clone((object)$form_state['values']);
-    $comment->pid = $form_state['values']['project_info']['pid'];
-    $comment->component = $form_state['values']['project_info']['component'];
-    // Add a dummy rid if necessary -- prevents incorrect change data.
-    $comment->rid = $form_state['values']['project_info']['rid'] ? $form_state['values']['project_info']['rid'] : 0;
+  if (!$has_file && empty($values['comment'])) {
+
+    $comment = drupal_clone((object) $values);
+    $comment->pid = $project_info['pid'];
+    $comment->component = $component;
+    $comment->rid = $rid;
+    $comment->assigned = $project_info['assigned'];
     $comment_changes = project_issue_metadata_changes($node, $old_data, $comment, project_issue_field_labels('web'));
+    // If the PID changed, rebuild the form
+    if (isset($comment_changes['pid']['new']) && $comment_changes['pid']['new'] === TRUE) {
+      $form_state['rebuild'] = TRUE;
+    }
     $has_change = FALSE;
     foreach ($comment_changes as $field => $changes) {
       if (isset($changes['new'])) {
@@ -333,7 +347,7 @@ function project_issue_form_comment_vali
       }
     }
     if (!$has_change) {
-      form_set_error('comment', t('You must either add a comment or change something about this issue.'));
+      form_set_error('comment', t('You must either add a comment, upload a file, or change something about this issue.'));
     }
   }
 }
@@ -444,7 +458,7 @@ function project_issue_comment_view(&$no
   if (isset($comment)) {
     return isset($project_issue_tables[$comment->cid]) ? $project_issue_tables[$comment->cid] : '';
   }
-  if ($node->comment_count) {
+  if (!empty($node->comment_count)) {
     $old = unserialize(db_result(db_query('SELECT original_issue_data FROM {project_issues} WHERE nid = %d', $node->nid)));
     $labels = project_issue_field_labels('web');
     $result = db_query('SELECT p.cid, p.title, p.pid, p.rid, p.component, p.category, p.priority, p.assigned, p.sid FROM {project_issue_comments} p INNER JOIN {comments} c ON p.cid = c.cid WHERE p.nid = %d AND c.status = %d ORDER BY p.timestamp ASC', $node->nid, COMMENT_PUBLISHED);
@@ -573,7 +587,7 @@ function project_issue_comment_pre_rende
   // after the comment form on preview, in the case where the comment
   // parent is 0.  If we want our issue previews to be consistent, this
   // ugly hack is necessary.
-  if ($_POST['op'] == t('Preview comment')) {
+  if (isset($_POST['op']) && $_POST['op'] == t('Preview comment')) {
     $preview = comment_render($node, 0);
   }
   else {
Index: issue.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/project_issue/issue.inc,v
retrieving revision 1.334
diff -u -F^f -u -F^f -r1.334 issue.inc
--- issue.inc	28 Jan 2009 23:43:10 -0000	1.334
+++ issue.inc	29 Jan 2009 21:22:27 -0000
@@ -14,18 +14,16 @@ function project_issue_update_project() 
   $form_id = array_shift($args);
   $form_state['post'] = $form['#post'] = $_POST;
   $form['#programmed'] = $form['#redirect'] = FALSE;
-  
-  // Skip validation in project issue.
-  $form_state['project_issue_ahah'] = TRUE;
+
   drupal_process_form($form_id, $form, $form_state);
-  
+
   // Rebuild the form and cache it again.
   $form = drupal_rebuild_form($form_id, $form_state, $args, $form_build_id);
 
   // These used to be path arguments. Now they arrive via form element.
   $issue_nid = $form['nid']['#value'];
   $pid = $form_state['values']['project_info']['pid'];
-  $rid = $form_state['values']['project_info']['rid'];
+  $rid = isset($form_state['values']['project_info']['rid']) ? $form_state['values']['project_info']['rid'] : 0;
   $cid = $form_state['values']['cid'];
   $assigned_uid = $form_state['values']['project_info']['assigned'];
 
@@ -33,7 +31,7 @@ function project_issue_update_project() 
 
   // Only generate release stuff if the project_release module is enabled.
   if (module_exists('project_release')) {
-    
+
     $project->nid = $pid;
     if ($releases = project_release_get_releases($project, 0)) {
       $old_version = db_result(db_query("SELECT version FROM {project_release_nodes} WHERE nid = %d", $rid));
@@ -443,17 +441,25 @@ function project_issue_pick_project_form
 function project_issue_form($node, $form_state, $include_metadata_fields = FALSE) {
   global $user;
 
+  $defaults = array(
+    'rid',
+    'component',
+    'category',
+    'priority',
+    'assigned',
+    'sid',
+  );
+
+  // Set some defaults for new forms.
+  if (!isset($node->nid)) {
+    foreach ($defaults as $default) {
+      $node->project_issue[$default] = 0;
+    }
+  }
+
   // In the case of an issue preview, get our defaults from the submitted form.
   // TODO: do we want to #tree our form so we don't need this hack?
   if (isset($form_state['node'])) {
-    $defaults = array(
-      'rid',
-      'component',
-      'category',
-      'priority',
-      'assigned',
-      'sid',
-    );
     foreach ($defaults as $default) {
       if (isset($form_state['node'][$default])) {
         $node->project_issue[$default] = $form_state['node'][$default];
@@ -512,7 +518,7 @@ function project_issue_form($node, $form
 
   if (empty($pid)) {
     drupal_set_message(t('Invalid project selected.'), 'error');
-    return drupal_goto('node/add/project-issue');
+    drupal_goto('node/add/project-issue');
   }
 
   // If this issue has already been created and is just being
@@ -530,10 +536,10 @@ function project_issue_form($node, $form
   if ($project->type != 'project_project') {
     drupal_set_message(t('Invalid project selected.'), 'error');
     // Not sure the best place to go here...
-    return drupal_goto('node/add/project-issue');
+    drupal_goto('node/add/project-issue');
   }
   if ($allow_metadata_changes) {
-    if (module_exists('project_release') &&
+    if (module_exists('project_release') && isset($node->project_issue['rid']) &&
         $releases = project_release_get_releases($project, 0, 'version', 'all', array($node->project_issue['rid']))) {
       $releases = array(t('<none>')) + $releases;
     }
@@ -551,7 +557,7 @@ function project_issue_form($node, $form
     }
     $categories = array_merge(array(t('<none>')), project_issue_category(0, 0));
     $priorities = project_issue_priority();
-    $states = project_issue_state(0, true, $node->nid && ($node->uid == $user->uid), $node->project_issue['sid']);
+    $states = project_issue_state(0, TRUE, !empty($node->nid) && ($node->uid == $user->uid), $node->project_issue['sid']);
     $assigned = project_issue_assigned_choices($node);
   }
 
Index: mail.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/project_issue/mail.inc,v
retrieving revision 1.117
diff -u -F^f -u -F^f -r1.117 mail.inc
--- mail.inc	26 Jan 2009 15:53:40 -0000	1.117
+++ mail.inc	29 Jan 2009 21:22:27 -0000
@@ -182,6 +182,20 @@ function project_mail_notify($nid) {
   $node = node_load($nid, NULL, TRUE);
   $project = node_load(array('nid' => $node->project_issue['pid'], 'type' => 'project_project'));
 
+  // Store a copy of the issue, so we can load the original issue values
+  // below.
+  $issue = drupal_clone($node);
+
+  // Load in the original issue data here, since we want a running
+  // reverse history.
+  $original_issue_data = unserialize($node->project_issue['original_issue_data']);
+  $fields = project_issue_field_labels('email');
+  foreach ($fields as $field => $label) {
+    if ($field != 'name' && $field != 'updator') {
+      $issue->original_issue_metadata->$field = $original_issue_data->$field;
+    }
+  }
+
   // Record users that are connected to this issue.
   $uids = array();
   if (!empty($node->uid)) {
@@ -236,7 +250,7 @@ function project_mail_notify($nid) {
   $params['node']    = $node;
   $params['project'] = $project;
   $params['history'] = $history;
-  
+
   $sender->name = t('!name (!site)', array('!name' => $user->name, '!site' => variable_get('site_name', 'Drupal')));
   $sender->mail = strtr(variable_get('project_issue_reply_to', variable_get('site_mail', ini_get('sendmail_from'))), array('%project' => $project->project['uri']));
   // The sender name is enclosed by double quotes below
@@ -244,20 +258,24 @@ function project_mail_notify($nid) {
   // which requires double quotes when special characters (including
   // some punctuation) are used.  See example in Appendix A.1.2.
   $from = "\"$sender->name\" <$sender->mail>";
-  
+
   while ($recipient = db_fetch_object($result)) {
     // To save work, only go through a user_load if we need it.
     if ($check_file_perms || $check_node_access) {
       $account = user_load(array('uid' => $recipient->uid));
+      $language = user_preferred_language($account);
+    }
+    else {
+      $language = language_default();
     }
-    
+
     $can_access = $check_node_access ? node_access('view', $node, $account) : TRUE;
-    
+
     if ($can_access) {
       $display_files = $check_file_perms ? user_access('view uploaded files', $account) : TRUE;
 
       $params['display_files'] = $display_files;
-      drupal_mail('project_issue', 'project_issue_update_notification', $recipient->mail, user_preferred_language($account), $params, $from);      
+      drupal_mail('project_issue', 'project_issue_update_notification', $recipient->mail, $language, $params, $from);
     }
   }
 
@@ -288,26 +306,13 @@ function project_issue_mail($key, &$mess
       // There could be stale data in the cached node, so reset the cache.
       $node    = $params['node'];
       $project = $params['project'];
-      $history = $params['history'];      
+      $history = $params['history'];
       $fields  = project_issue_field_labels('email');
 
-      // Store a copy of the issue, so we can load the original issue values
-      // below.
-      $issue = drupal_clone($node);
-
-      // Load in the original issue data here, since we want a running
-      // reverse history.
-      $original_issue_data = unserialize($node->project_issue['original_issue_data']);
-      foreach ($fields as $field => $label) {
-        if ($field != 'name' && $field != 'updator') {
-          $issue->project_issue[$field] = $original_issue_data->$field;
-        }
-      }
-      
       $domain = preg_replace('|.+://([a-zA-Z0-9\._-]+).*|', '\1', $base_url);
-            
-      $message['headers'] += array(        
-        'Date' => date('r'),        
+
+      $message['headers'] += array(
+        'Date' => date('r'),
         'X-Mailer' => 'Drupal Project module (http://drupal.org/project/project)',
         'List-Id' => "$project->title <". $project->project['uri'] ."-issues-$domain>",
         'List-Archive' => '<'. url('project/issues/'. $project->project['uri'], array('absolute' => TRUE)) .'>',
@@ -322,7 +327,7 @@ function project_issue_mail($key, &$mess
           // message headers.  Instead of issuing another query, just keep track
           // of them here.
           $previous_cid = isset($cid) ? $cid : '';
-          $cid = $comment->cid;
+          $cid = isset($comment->cid) ? $comment->cid : 0;
         }
         $message['headers']['Message-Id'] = "<type=project&nid=$node->nid&cid=$cid&host=@$domain>";
         $message['headers']['In-Reply-To'] = "<type=project&nid=$node->nid&host=@$domain>";
@@ -373,22 +378,31 @@ function project_mail_generate_followup_
     }
   }
 
-  $fields = project_issue_field_labels('email');
-
   // Get most recent update.
   $entry = array_pop($history);
+
   $node->project_issue['updator'] = $entry->name ? $entry->name : $user->name;
 
   // Check if the latest entry is actually the initial issue.
   if (empty($history)) {
+    $metadata_previous = new stdClass();
+    // Have to get the metadata into the entry object.
+    $metadata_entry = $entry->original_issue_metadata;
     $content = $entry->body;
   }
   else {
-    $previous = end($history);
+    $metadata_previous = end($history);
+    // If the previous was the original issue, then we need to pull
+    // out the metadata from project_issue.
+    if (isset($metadata_previous->original_issue_metadata)) {
+      $metadata_previous = $metadata_previous->original_issue_metadata;
+    }
+    $metadata_entry = $entry;
     $content = $entry->comment;
   }
 
-  $comment_changes = project_issue_metadata_changes($node, $previous, $entry, $fields);
+  $fields = project_issue_field_labels('email');
+  $comment_changes = project_issue_metadata_changes($node, $metadata_previous, $metadata_entry, $fields);
 
   // Since $node->name will always be the original issue author, and since
   // $node->project_issue['updator'] isn't a property of either $previous or
@@ -558,6 +572,9 @@ function project_mail_format_attachments
   $output = '';
   if ($display_files && is_array($entry->files)) {
     foreach ($entry->files as $file) {
+      // Comment upload has it's files in an array, so cast to an object
+      // for consistency.
+      $file = (object) $file;
       $output .= ' '. str_pad(t('Attachment') .':', 14) . file_create_url($file->filepath) .' ('. format_size($file->filesize) .")\n";
     }
   }
@@ -597,8 +614,9 @@ function project_mail_format_entry($entr
 
   $output .= project_mail_format_attachments($entry, $display_files);
 
-  // Must distinguish between nodes and comments.
-  if (!$entry->vid) {
+  // Must distinguish between nodes and comments -- here we do it
+  // by looking for a revision ID.
+  if (empty($entry->vid)) {
     $content = $entry->comment;
   }
   else {
Index: project_issue.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/project_issue/project_issue.module,v
retrieving revision 1.116
diff -u -F^f -u -F^f -r1.116 project_issue.module
--- project_issue.module	28 Jan 2009 23:43:10 -0000	1.116
+++ project_issue.module	29 Jan 2009 21:22:27 -0000
@@ -334,9 +334,16 @@ function project_issue_form_alter(&$form
       }
       break;
 
+    case 'comment_form':
+      // Using a form-specific hook here causes some of the altering
+      // code to be run before we have all the form elements we need.
+      // So here we break out this code into a separate function for
+      // clarity but leave it in the general hook_form_alter().
+      _project_issue_form_comment_form_alter($form, $form_state);
+      break;
+
     case 'comment_confirm_delete':
-      $comment = $form['comment']['#value'];
-      $type = db_result(db_query("SELECT type FROM {node} WHERE nid = %d", $comment->nid));
+      $type = db_result(db_query("SELECT type FROM {node} WHERE nid = %d", $form['#comment']->nid));
       if (!empty($type) && $type == 'project_issue') {
         $form['description']['#value'] = t('This action cannot be undone.');
       }
@@ -1368,7 +1375,7 @@ function project_issue_metadata_changes(
     elseif (isset($old_data->$property)) {
       $changes[$property]['old'] = $old_data->$property;
     }
-    else {
+    elseif (isset($new_data->$property)) {
       $changes[$property]['new'] = $new_data->$property;
     }
   }
