Index: mail.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/project_issue/mail.inc,v
retrieving revision 1.105
diff -u -r1.105 mail.inc
--- mail.inc	7 Mar 2008 21:30:23 -0000	1.105
+++ mail.inc	2 May 2008 07:09:57 -0000
@@ -1,6 +1,6 @@
 <?php
 // $Id: mail.inc,v 1.105 2008/03/07 21:30:23 thehunmonkgroup Exp $
-// $Name:  $
+// $Name: DRUPAL-5--2-2 $

 function project_issue_mailhandler($node, $result, $i, $header, $mailbox) {
   if ($node->type == 'project') {
@@ -173,7 +173,7 @@
   }
 }

-function project_mail_notify($nid) {
+function project_mail_notify($nid, $ops = array('notify')) {
   global $base_url, $user;

   if (defined('PROJECT_NOMAIL')) {
@@ -189,40 +189,7 @@
   // 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->original_issue_data);
-  foreach ($fields as $field => $label) {
-    if ($field != 'name' && $field != 'updator') {
-      $issue->$field = $original_issue_data->$field;
-    }
-  }
-
-  // Record users that are connected to this issue.
-  $uids = array();
-  if ($node->assigned) {
-    $uids[$node->assigned] = $node->assigned;
-  }
-
-  // Create complete history of the bug report.
   $history = array($issue);
-  $result = db_query('SELECT u.name, c.cid, c.nid, c.subject, c.comment, c.uid, c.format, pic.* FROM {project_issue_comments} pic INNER JOIN {comments} c ON c.cid = pic.cid INNER JOIN {users} u ON u.uid = c.uid WHERE c.nid = %d AND c.status = %d ORDER BY pic.timestamp', $node->nid, COMMENT_PUBLISHED);
-
-  while ($comment = db_fetch_object($result)) {
-    $comment->comment = db_decode_blob($comment->comment);
-    $comment->files = _comment_upload_load_files($comment->cid);
-    $history[] = $comment;
-    // Record users that are connected to this issue.
-    if ($comment->uid) {
-      $uids[$comment->uid] = $comment->uid;
-    }
-    // We need the most recent cid and the next most recent cid for the
-    // message headers.  Instead of issuing another query, just keep track
-    // of them here.
-    $previous_cid = isset($cid) ? $cid : '';
-    $cid = $comment->cid;
-  }

   // Create mail header
   $sender->name = mime_header_encode(t('!name (!site)', array('!name' => $user->name, '!site' => variable_get('site_name', 'Drupal'))));
@@ -255,51 +222,88 @@
     $header['Message-Id'] = "<type=project&nid=$node->nid&host=@$domain>";
   }

-  if (count($uids)) {
-    $placeholders = implode(',', array_fill(0, count($uids), '%d'));
-    array_unshift($uids, $node->pid);
-    $result = db_query("SELECT p.*, u.uid, u.name, u.mail FROM {project_subscriptions} p INNER JOIN {users} u ON p.uid = u.uid WHERE u.status = 1 AND p.nid = %d AND (p.level = 2 OR (p.level = 1 AND u.uid IN ($placeholders)))", $uids);
-  }
-  else {
-    $result = db_query('SELECT p.*, u.uid, u.name, u.mail FROM {project_subscriptions} p INNER JOIN {users} u ON p.uid = u.uid WHERE u.status = 1 AND p.nid = %d AND p.level = 2', $node->pid);
-  }
-
-  // Create link to related node
-  $links = t('Issue status update for !link', array('!link' => "\n". url("node/$node->nid", NULL, NULL, 1))) ."\n";
-  $links .= t('Post a follow up: !link', array('!link' => "\n". url("comment/reply/$node->nid", NULL, 'comment-form', 1))) ."\n";
-
-  // To save workload, check here if either the anonymous role or the
-  // authenticated role has the 'view uploaded files' permission, since
-  // we only need to process each user's file access permission if this
-  // is NOT the case.
-  $check_file_perms = !db_num_rows(db_query("SELECT rid FROM {permission} WHERE perm LIKE '%view uploaded files%' AND rid IN (%d, %d)", DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID));
-
   project_mail_output($node->title, 0);
-  $subject = t('[!short_name] [!category] !title', array('!short_name' => $project->uri, '!category' => $node->category, '!title' => $node->title));

-  while ($recipient = db_fetch_object($result)) {
-    if ($check_file_perms) {
-      $account = user_load(array('uid' => $recipient->uid));
-      $display_files = user_access('view uploaded files', $account);
+  // Having set up the elements common to all the emails, we now dispatch
+  // emails according to the array of $ops that were passed in.
+  if (($op = array_search('assign_update', $ops)) || ($op = array_search('assign_insert', $ops))) {
+    $assignee = user_load($node->assigned);
+    $links = t('!user has assigned an issue to you in the !short_name issue queue: !link', array('!user' => $user->name, '!short_name' => $project->uri, '!link' => "\n". url("node/$node->nid", NULL, NULL, 1))) ."\n";
+    $links .= t('Post a follow up: !link', array('!link' => "\n". url("comment/reply/$node->nid", NULL, 'comment-form', 1))) ."\n";
+
+    $subject = t('[!short_name - Issue Assigned] [!category] !title', array('!short_name' => $project->uri, '!category' => $node->category, '!title' => $node->title));
+    $body = "$links\n". project_mail_generate_followup_mail_body($node, $history, FALSE, $op);
+    drupal_mail('project_issue_update', $assignee->mail, $subject, $body, $from, $header);
+  }
+  if (in_array('notify', $ops)) {
+    // Create link to related node
+    $links = t('Issue status update for !link', array('!link' => "\n". url("node/$node->nid", NULL, NULL, 1))) ."\n";
+    $links .= t('Post a follow up: !link', array('!link' => "\n". url("comment/reply/$node->nid", NULL, 'comment-form', 1))) ."\n";
+
+    // To save workload, check here if either the anonymous role or the
+    // authenticated role has the 'view uploaded files' permission, since
+    // we only need to process each user's file access permission if this
+    // is NOT the case.
+    $check_file_perms = !db_num_rows(db_query("SELECT rid FROM {permission} WHERE perm LIKE '%view uploaded files%' AND rid IN (%d, %d)", DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID));
+
+    // Record users that are connected to this issue.
+    $uids = array();
+    if ($node->assigned) {
+      $uids[$node->assigned] = $node->assigned;
+    }
+
+    // Create complete history of the bug report.
+    $result = db_query('SELECT u.name, c.cid, c.nid, c.subject, c.comment, c.uid, c.format, pic.* FROM {project_issue_comments} pic INNER JOIN {comments} c ON c.cid = pic.cid INNER JOIN {users} u ON u.uid = c.uid WHERE c.nid = %d AND c.status = %d ORDER BY pic.timestamp', $node->nid, COMMENT_PUBLISHED);
+    while ($comment = db_fetch_object($result)) {
+      $comment->comment = db_decode_blob($comment->comment);
+      $comment->files = _comment_upload_load_files($comment->cid);
+      $history[] = $comment;
+      // Record users that are connected to this issue.
+      if ($comment->uid) {
+        $uids[$comment->uid] = $comment->uid;
+      }
+      // We need the most recent cid and the next most recent cid for the
+      // message headers.  Instead of issuing another query, just keep track
+      // of them here.
+      $previous_cid = isset($cid) ? $cid : '';
+      $cid = $comment->cid;
+    }
+
+    $subject = t('[!short_name] [!category] !title', array('!short_name' => $project->uri, '!category' => $node->category, '!title' => $node->title));
+
+    if (count($uids)) {
+      $placeholders = implode(',', array_fill(0, count($uids), '%d'));
+      array_unshift($uids, $node->pid);
+      $result = db_query("SELECT p.*, u.uid, u.name, u.mail FROM {project_subscriptions} p INNER JOIN {users} u ON p.uid = u.uid WHERE u.status = 1 AND p.nid = %d AND (p.level = 2 OR (p.level = 1 AND u.uid IN ($placeholders)))", $uids);
     }
     else {
-      $display_files = TRUE;
+      $result = db_query('SELECT p.*, u.uid, u.name, u.mail FROM {project_subscriptions} p INNER JOIN {users} u ON p.uid = u.uid WHERE u.status = 1 AND p.nid = %d AND p.level = 2', $node->pid);
+    }
+
+    while ($recipient = db_fetch_object($result)) {
+      if ($check_file_perms) {
+        $account = user_load(array('uid' => $recipient->uid));
+        $display_files = user_access('view uploaded files', $account);
+      }
+      else {
+        $display_files = TRUE;
+      }
+      $body = "$links\n". project_mail_generate_followup_mail_body($node, $history, $display_files);
+      drupal_mail('project_issue_update', $recipient->mail, $subject, $body, $from, $header);
+    }
+
+    if (is_array($project->mail_copy_filter) && count(array_filter($project->mail_copy_filter)) && !$project->mail_copy_filter[$node->category]) {
+      return;
+    }
+
+    if (is_array($project->mail_copy_filter_state) && count(array_filter($project->mail_copy_filter_state)) && !$project->mail_copy_filter_state[$node->sid]) {
+      return;
+    }
+
+    if ($project->mail_copy) {
+      $body = "$links\n". project_mail_generate_followup_mail_body($node, $history, TRUE);
+      drupal_mail('project_issue_update', $project->mail_copy, $subject, $body, $from, $header);
     }
-    $body = "$links\n". project_mail_generate_followup_mail_body($node, $history, $display_files);
-    drupal_mail('project_issue_update', $recipient->mail, $subject, $body, $from, $header);
-  }
-
-  if (is_array($project->mail_copy_filter) && count(array_filter($project->mail_copy_filter)) && !$project->mail_copy_filter[$node->category]) {
-    return;
-  }
-
-  if (is_array($project->mail_copy_filter_state) && count(array_filter($project->mail_copy_filter_state)) && !$project->mail_copy_filter_state[$node->sid]) {
-    return;
-  }
-
-  if ($project->mail_copy) {
-    $body = "$links\n". project_mail_generate_followup_mail_body($node, $history, TRUE);
-    drupal_mail('project_issue_update', $project->mail_copy, $subject, $body, $from, $header);
   }
 }

@@ -312,22 +316,24 @@
  *   An array containing the history of issue followups.
  * @param $display_files
  *   Boolean indicating if file attachments should be displayed.
+ * @param $op
+ *   A string indicating the type of message body to be generated.
  * @return
  *   A string of the email body.
  */
-function project_mail_generate_followup_mail_body($node, $history, $display_files) {
+function project_mail_generate_followup_mail_body($node, $history, $display_files, $op = 'notify') {
   global $user;
-  static $output_with_files =  NULL, $output_without_files = NULL;
+  static $output_with_files = array(), $output_without_files = array();

   // Return cached output if available.
   if ($display_files) {
-    if (isset($output_with_files)) {
-      return $output_with_files;
+    if (isset($output_with_files[$op])) {
+      return $output_with_files[$op];
     }
   }
   else {
-    if (isset($output_without_files)) {
-      return $output_without_files;
+    if (isset($output_without_files[$op])) {
+      return $output_without_files[$op];
     }
   }

@@ -370,7 +376,7 @@

   $hr = str_repeat('-', 72);

-  if (count($history)) {
+  if (count($history) && $op == 'notify') {

     $body .= "\n\n";
     $body .= t('Original issue:') ."\n";
@@ -382,15 +388,16 @@
       }
     }
   }
-
-  $output = "$summary\n$body";
+
+  $assignee_text = "\n" . t("The text of your co-maintainer's comment where s/he assigned this issue to you is below.") . "\n" . $hr;
+  $output = $op == 'notify' ? "$summary\n$body" : "$assignee_text\n\n$summary\n$body";

   // Set cached output.
   if ($display_files) {
-    $output_with_files = $output;
+    $output_with_files[$op] = $output;
   }
   else {
-    $output_without_files = $output;
+    $output_without_files[$op] = $output;
   }

   return $output;
Index: issue.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/project_issue/issue.inc,v
retrieving revision 1.304.2.6
diff -u -r1.304.2.6 issue.inc
--- issue.inc	14 Apr 2008 00:27:24 -0000	1.304.2.6
+++ issue.inc	2 May 2008 07:09:57 -0000
@@ -1,6 +1,6 @@
 <?php
 // $Id: issue.inc,v 1.304.2.6 2008/04/14 00:27:24 thehunmonkgroup Exp $
-// $Name:  $
+// $Name: DRUPAL-5--2-2 $

 function project_issue_page() {
   global $user;
@@ -720,6 +720,7 @@
       $function = "{$module}_project_issue_assignees";
       $function($assigned, $node);
     }
+    $assigned = array_unique($assigned);
     natcasesort($assigned);
     $assigned = array(0 => empty($node->assigned) ? t('Unassigned') : t('Unassign')) + $assigned;
   }
@@ -797,6 +798,10 @@
       '#default_value' => $node->assigned,
       '#options' => $assigned,
     );
+    $form['issue_info']['originally_assigned'] = array(
+      '#type' => 'hidden',
+      '#value' => $node->assigned,
+    );
     if (count($states) > 1) {
       $form['issue_info']['sid'] = array(
         '#type' => 'select',
@@ -1004,6 +1009,15 @@
     }
     // Always let the person replying assign it to themselves.
     $assigned[$user->uid] = $user->name;
+
+    // If the current user is one of the project maintainers, allow them to select
+    // from a list of all the project maintainers when assigning the issue.
+    if (db_result(db_query("SELECT nid FROM {cvs_project_maintainers} WHERE uid = %d AND nid = %d", $user->uid, $node->nid))) {
+      $result = db_query("SELECT m.uid, u.name FROM {cvs_project_maintainers} m INNER JOIN {users} u ON m.uid = u.uid WHERE m.uid != 0 AND m.nid = %d", $node->nid);
+      while ($row = db_fetch_array($result)) {
+        $assigned = array_flip(array_merge(array_flip($assigned), array($row['name'] => $row['uid']))); // these array acrobatics ensure that we have no duplicate values being added
+      }
+    }
   }
 }

@@ -2066,7 +2080,7 @@
         );
         $sql[] = '('. implode(' OR ', $_sql) .')';
         break;
-      case 'assigned':
+      case 'assigned': // TODO I think this is where the change would need to be made
         $sql[] = project_issue_query_sql_field('p.assigned', $value, 0, ' OR ', 'project_issue_query_user');
         break;
       case 'submitted':
Index: project_issue.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/project_issue/project_issue.module,v
retrieving revision 1.88.2.2
diff -u -r1.88.2.2 project_issue.module
--- project_issue.module	13 Apr 2008 20:13:12 -0000	1.88.2.2
+++ project_issue.module	2 May 2008 07:09:57 -0000
@@ -1,6 +1,6 @@
 <?php
 // $Id: project_issue.module,v 1.88.2.2 2008/04/13 20:13:12 thehunmonkgroup Exp $
-// $Name:  $
+// $Name: DRUPAL-5--2-2 $

 // issue nodes      -> project_issues
 // issue comments   -> project_issue_comments
@@ -794,7 +794,7 @@
  *   If $nid is not passed, an associative array of nids that are marked for
  *   notification emails, with the following structure: key = nid, value = nid.
  */
-function project_issue_set_mail_notify($nid = NULL) {
+function project_issue_set_mail_notify($nid = NULL, $op = 'notify') {
   static $nids = array();

   if (!isset($nid)) {
@@ -803,7 +803,7 @@
     return $return;
   }
   else {
-    $nids[$nid] = $nid;
+    $nids[$nid][$op] = $op;
   }
 }

@@ -819,8 +819,8 @@
   // loaded. Since the cached pages won't have any new mail notifications,
   // we can safely test for this case.
   if (!empty($nids)) {
-    foreach ($nids as $nid) {
-      project_mail_notify($nid);
+    foreach ($nids as $nid => $ops) {
+      project_mail_notify($nid, $ops);
     }
   }
 }
Index: comment.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/project_issue/comment.inc,v
retrieving revision 1.129.2.2
diff -u -r1.129.2.2 comment.inc
--- comment.inc	14 Apr 2008 00:26:24 -0000	1.129.2.2
+++ comment.inc	2 May 2008 07:09:57 -0000
@@ -1,6 +1,6 @@
 <?php
 // $Id: comment.inc,v 1.129.2.2 2008/04/14 00:26:24 thehunmonkgroup Exp $
-// $Name:  $
+// $Name: DRUPAL-5--2-2 $

 function project_issue_comment(&$arg, $op) {
   // $arg can be a comment object, or a form or form_values.
@@ -353,6 +353,13 @@
       $comment_data = (object) $comment_data;
       break;
   }
+  global $user;
+  if ($comment_data->assigned && $comment_data->assigned != $user->uid && $comment_data->assigned != $comment_data->originally_assigned) {
+    // Only way we get here is if the issue isn't (being) unassigned, and it's being assigned
+    // to someone who's NOT the current user, and who it wasn't originally assigned to. In other words
+    // we only get here if one maintainer is assigning it to another maintainer.
+    project_issue_set_mail_notify($comment_data->nid, "assign_$op");
+  }

   // In order to deal with deleted/unpublished comments, make sure that we're performing
   // the updates to the issue with the latest available published comment.