Index: comment.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/project_issue/comment.inc,v
retrieving revision 1.137
diff -u -p -r1.137 comment.inc
--- comment.inc	22 Jan 2009 05:32:39 -0000	1.137
+++ comment.inc	26 Jan 2009 16:33:26 -0000
@@ -13,19 +13,13 @@ function project_issue_comment(&$arg, $o
   // Make a copy here so we have all the original metadata, since some
   // of it can change below.
   $original_node = drupal_clone($node);
+  $old_data = (object) $original_node->project_issue;
+  $old_data->title = $original_node->title;
 
   if ($node->type != 'project_issue') {
     return;
   }
 
-  // Make sure project is current here -- it may have changed when posted.
-  // This is ugly, but the form workflow doesn't really offer a better
-  // choice for this scenario.
-  if (isset($_POST['project_info']['pid'])) {
-    $node->project_issue['pid'] = $_POST['project_info']['pid'];
-  }
-  $project = node_load(array('nid' => $node->project_issue['pid'], 'type' => 'project_project'));
-
   switch ($op) {
     case 'insert':
       // Get a lock on the issue in order to generate the next comment ID.
@@ -62,111 +56,36 @@ function project_issue_comment(&$arg, $o
         // The hard redirect prevents any bogus data from being inserted for the failed comment.
         drupal_goto('node/'. $arg['nid']);
       }
-
       break;
+
     case 'update':
       project_issue_update_by_comment($arg, 'update');
       break;
+
     case 'delete':
       db_query("DELETE FROM {project_issue_comments} WHERE cid = %d", $arg->cid);
       project_issue_update_by_comment($arg, 'delete');
       break;
+
     case 'view':
       if (isset($arg->cid)) {
         $project_issue_table = project_issue_comment_view($original_node, $arg);
       }
       else {
+        // Previewing a comment.
         $test = drupal_clone($arg);
         $test->pid = $arg->project_info['pid'];
         $test->component = $arg->project_info['component'];
         // Add a dummy rid if necessary -- prevents incorrect change data.
         $test->rid = $arg->project_info['rid'] ? $arg->project_info['rid'] : 0;
-        $comment_changes = project_issue_metadata_changes($node, $original_node, $test, project_issue_field_labels('web'));
+        $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);
       }
       if ($project_issue_table) {
         $arg->comment = '<div class="project-issue"><div class="summary">'. $project_issue_table .'</div></div>' . $arg->comment;
       }
       break;
-    case 'validate':
-      // 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($arg);
-
-      // Only validate metadata changes on new followups.
-      if (isset($arg['cid'])) {
-        return;
-      }
-      if ($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);
-
-        // Validate version.
-        if (module_exists('project_release') && ($releases = project_release_get_releases($project, 0, 'version', 'all', array($arg['project_info']['rid'])))) {
-          $rid = $arg['project_info']['rid'];
-          if ($rid && !in_array($rid, array_keys($releases))) {
-            $rid = 0;
-          }
-          // Check to make sure this release is not marked as an invalid
-          // release node for user selection.
-          $invalid_rids = variable_get('project_issue_invalid_releases', array());
-          if (!empty($invalid_rids) &&
-              ((empty($rid) && in_array($node->project_issue['rid'], $invalid_rids))
-               || in_array($rid, $invalid_rids))) {
-            form_set_error('project_info][rid', t('%version is not a valid version, please select a different value.', array('%version' => $releases[$node->project_issue['rid']])));
-          }
-          elseif (empty($rid)) {
-            form_set_error('project_info][rid', t('You have to specify a valid version.'));
-          }
-        }
-        // Validate component.
-        $component = $arg['project_info']['component'];
-        if ($component && !in_array($component, $project->project_issue['components'])) {
-          $component = 0;
-        }
-        empty($component) && form_set_error('project_info][component', t('You have to specify a valid component.'));
-      }
-      else {
-        form_set_error('project_info][pid', t('You have to specify a valid project.'));
-      }
-      empty($arg['category']) && form_set_error('category', t('You have to specify a valid category.'));
 
-      // Now, make sure the comment changes *something* about the issue.
-      // Unfortunately, hook_comment() is evil, and in the 'validate' case, we
-      // get a form_values array in $arg, whereas in 'view', we get an
-      // object. So, we can't really share the code to initialize this array
-      // of changes, and have to do it again here using the array. 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($arg['files']) ? $arg['files'] : array();
-      foreach ($files as $number => $data) {
-        if (empty($data['remove'])) {
-          $has_file = TRUE;
-          break;
-        }
-      }
-      if (!$has_file && empty($arg['comment'])) {
-        $comment = drupal_clone((object)$arg);
-        $comment->pid = $arg['project_info']['pid'];
-        $comment->component = $arg['project_info']['component'];
-        // Add a dummy rid if necessary -- prevents incorrect change data.
-        $comment->rid = $arg['project_info']['rid'] ? $arg['project_info']['rid'] : 0;
-        $comment_changes = project_issue_metadata_changes($node, $original_node, $comment, project_issue_field_labels('web'));
-        $has_change = FALSE;
-        foreach ($comment_changes as $field => $changes) {
-          if (isset($changes['new'])) {
-            $has_change = TRUE;
-            break;
-          }
-        }
-        if (!$has_change) {
-          form_set_error('comment', t('You must either add a comment or change something about this issue.'));
-        }
-      }
-      break;
   }
 }
 
@@ -183,6 +102,8 @@ function project_issue_form_comment_form
   $node = node_load($nid);
   $project = node_load(array('nid' => $node->project_issue['pid'], 'type' => 'project_project'));
 
+  $form['#validate'][] = 'project_issue_form_comment_validate';
+
   // Only allow metadata changes on new followups.
   if (isset($form['cid']['#value'])) {
     return;
@@ -192,8 +113,7 @@ function project_issue_form_comment_form
 
   // We need to ask for almost the same metadata as project issue itself
   // so let's reuse the form.
-  $project_issue_form_state = array();
-  $form += drupal_retrieve_form('project_issue_form', $node, $project_issue_form_state, TRUE);
+  $form += drupal_retrieve_form('project_issue_form', $node, $form_state, TRUE);
 
   // We need this otherwise pid collides with comment.
   $form['project_info']['#tree'] = TRUE;
@@ -213,6 +133,16 @@ function project_issue_form_comment_form
     '#required' => TRUE,
   );
 
+  // Since we allow the user to change the project during a comment, add the
+  // JS that is responsible to update the valid choices for version, component
+  // and assigned when the project changes.
+  drupal_add_js(drupal_get_path('module', 'project_issue') .'/project_issue.js');
+  drupal_add_js(
+    array(
+      'projectUrl' => url('project/issues/update_project'),
+      'issueNid' => $node->nid,
+    ), 'setting');
+  
   $form['issue_info']['#weight'] = -1;
   $form['#prefix'] = '<div class="project-issue"><div class="node-form"><div class="standard">';
   $form['#suffix'] = '</div></div></div>';
@@ -243,10 +173,113 @@ function project_issue_form_comment_form
   $form['original_issue']['issue_info'] = $form['issue_info'];
   unset($form['project_info'], $form['issue_info']);
   unset($form['issue_details'], $form['project_help']);
+
   return $form;
 }
 
 /**
+ * Validate issue metadata on the comment form.
+ *
+ * @param $form
+ *   The Drupal form structure.
+ * @param $form_state
+ *   The current state of the form.
+ */
+function project_issue_form_comment_validate($form, &$form_state) {
+  $nid = $form_state['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);
+  $old_data = (object) $original_node->project_issue;
+  $old_data->title = $original_node->title;
+
+  // 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']);
+  
+  // Only validate metadata changes on new followups.
+  if (isset($form_state['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'];
+  }
+  $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);
+
+    // 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 ($rid && !in_array($rid, array_keys($releases))) {
+        $rid = 0;
+      }
+      // Check to make sure this release is not marked as an invalid
+      // release node for user selection.
+      $invalid_rids = variable_get('project_issue_invalid_releases', array());
+      if (!empty($invalid_rids) &&
+          ((empty($rid) && in_array($node->project_issue['rid'], $invalid_rids))
+           || in_array($rid, $invalid_rids))) {
+        form_set_error('project_info][rid', t('%version is not a valid version, please select a different value.', array('%version' => $releases[$node->project_issue['rid']])));
+      }
+      elseif (empty($rid)) {
+        form_set_error('project_info][rid', t('You have to specify a valid version.'));
+      }
+    }
+    // Validate component.
+    $component = $form_state['values']['project_info']['component'];
+    if ($component && !in_array($component, $project->project_issue['components'])) {
+      $component = 0;
+    }
+    empty($component) && form_set_error('project_info][component', t('You have to specify a valid component.'));
+  }
+  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.'));
+
+  // 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, 
+  // we consider that a valid change to the issue, too.
+  $has_file = FALSE;
+  $files = isset($form_state['values']['files']) ? $form_state['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;
+    $comment_changes = project_issue_metadata_changes($node, $old_data, $comment, project_issue_field_labels('web'));
+    $has_change = FALSE;
+    foreach ($comment_changes as $field => $changes) {
+      if (isset($changes['new'])) {
+        $has_change = TRUE;
+        break;
+      }
+    }
+    if (!$has_change) {
+      form_set_error('comment', t('You must either add a comment or change something about this issue.'));
+    }
+  }
+  // To handle cases where the project changes, we need to rebuild the form.
+//  $form_state['rebuild'] = TRUE;
+}
+
+/**
  * Theme a project issue metadata table.
  *
  * @param $comment_changes
Index: issue.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/project_issue/issue.inc,v
retrieving revision 1.328
diff -u -p -r1.328 issue.inc
--- issue.inc	22 Jan 2009 06:01:41 -0000	1.328
+++ issue.inc	26 Jan 2009 16:33:26 -0000
@@ -489,7 +489,18 @@ function project_issue_form($node, $form
     return;
   }
 
-  if (empty($node->project_issue['pid'])) {
+  // Figure out what project we should use for the issue metadata.
+  if (!empty($form_state['values']['project_info']['pid'])) {
+    // The project has been selected in the form itself (e.g. it's been
+    // changed and we're previewing, etc.)
+    $pid = $form_state['values']['project_info']['pid'];
+  }
+  elseif (!empty($node->project_issue['pid'])) {
+    // The issue node already knows what project it belongs to.
+    $pid = $node->project_issue['pid'];
+  }
+  else {
+    // Fallback and try to learn the project from the URL -- evil.
     $pid = arg(3);
     if (!empty($pid)) {
       if (is_numeric($pid)) {
@@ -500,12 +511,10 @@ function project_issue_form($node, $form
       }
     }
   }
-  $pid = $node->project_issue['pid'];
 
   if (empty($pid)) {
     drupal_set_message(t('Invalid project selected.'), 'error');
-    drupal_goto('node/add/project-issue');
-    return;
+    return drupal_goto('node/add/project-issue');
   }
 
   // If this issue has already been created and is just being
@@ -518,18 +527,13 @@ function project_issue_form($node, $form
     $allow_metadata_changes = empty($node->nid);
   }
 
-  // Load Javascript (unless the issue is being edited).
-  if ($allow_metadata_changes) {
-    drupal_add_js(drupal_get_path('module', 'project_issue') .'/project_issue.js');
-    drupal_add_js(
-      array(
-        'projectUrl' => url('project/issues/update_project'),
-        'issueNid' => $node->nid,
-      ), 'setting');
-  }
-
   // Load the project and initialize some support arrays.
-  $project = node_load(array('nid' => $pid, 'type' => 'project_project'));
+  $project = node_load($pid);
+  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');
+  }
   if ($allow_metadata_changes) {
     if (module_exists('project_release') &&
         $releases = project_release_get_releases($project, 0, 'version', 'all', array($node->project_issue['rid']))) {
