? 361649-12.patch
? 361649-13.patch
? 361649-3.patch
? 361649-4.patch
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	27 Jan 2009 00:04: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.
@@ -47,7 +41,7 @@ function project_issue_comment(&$arg, $o
       }
 
       if (isset($id)) {
-        db_query("INSERT INTO {project_issue_comments} (nid, cid, pid, rid, component, category, priority, assigned, sid, title, timestamp, comment_number) VALUES (%d, %d, %d, %d, '%s', '%s', %d, %d, %d, '%s', %d, %d)", $arg['nid'], $arg['cid'], $arg['project_info']['pid'], $arg['project_info']['rid'], $arg['project_info']['component'], $arg['category'], $arg['priority'], $arg['assigned'], $arg['sid'], $arg['title'], $arg['timestamp'], $id);
+        db_query("INSERT INTO {project_issue_comments} (nid, cid, pid, rid, component, category, priority, assigned, sid, title, timestamp, comment_number) VALUES (%d, %d, %d, %d, '%s', '%s', %d, %d, %d, '%s', %d, %d)", $arg['nid'], $arg['cid'], $arg['project_info']['pid'], $arg['project_info']['rid'], $arg['project_info']['component'], $arg['category'], $arg['priority'], $arg['project_info']['assigned'], $arg['sid'], $arg['title'], $arg['timestamp'], $id);
         db_query("UPDATE {comments} SET subject = '%s' WHERE cid = %d", "#$id", $arg['cid']);
         project_issue_update_by_comment($arg, 'insert');
       }
@@ -62,111 +56,37 @@ 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'];
+        $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;
-        $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 +103,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 +114,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;
@@ -211,8 +132,13 @@ function project_issue_form_comment_form
     '#default_value' => $node->project_issue['pid'],
     '#options' => $projects,
     '#required' => TRUE,
+    '#ahah' => array(
+      'path' => 'project/issues/update_project',
+      'wrapper' => 'project-info-wrapper',
+      'event' => 'change'
+    )
   );
-
+  
   $form['issue_info']['#weight'] = -1;
   $form['#prefix'] = '<div class="project-issue"><div class="node-form"><div class="standard">';
   $form['#suffix'] = '</div></div></div>';
@@ -233,20 +159,130 @@ function project_issue_form_comment_form
     '#weight' => -30,
     '#required' => TRUE,
   );
+  
+  $form['project_info']['assigned'] = $form['issue_info']['assigned'];
+  $form['project_info']['#prefix'] = '<div id="project-info-wrapper" class="inline-options">';
+  $form['project_info']['#suffix'] = '</div>';
 
   // Remove the 'Project information' and 'Issue information' fieldsets,
   // they're ugly after we move things inside the 'Edit issue settings' fieldset.
-  unset($form['project_info']['#type'], $form['project_info']['#title'], $form['issue_info']['#type'], $form['issue_info']['#title']);
+  unset($form['project_info']['#type'], $form['project_info']['#title'], $form['issue_info']['#type'], $form['issue_info']['#title'], $form['issue_info']['assigned']);
 
   // Restructure the UI to de-emphasize the original project form inputs.
   $form['original_issue']['project_info'] = $form['project_info'];
   $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) {    
+  if(isset($form_state['project_issue_ahah'])) {
+    return;
+  }
+  $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	27 Jan 2009 00:04:27 -0000
@@ -5,39 +5,36 @@
 /**
  * JS callback method to return updated elements on the issue form.
  * This function is called when someone changes the "Project" selector.
- * See project_issue.js.
- *
- * @param $pid
- *   The nid of the project we're updating to.
- * @param $issue_nid
- *   The issue nid.
- * @param $cid
- *   The component from the previously selected project.
- * @param $rid
- *   The version from the previously selected project.
- * @param $assigned_uid
- *   The uid of the assigned user from the previously selected project.
  */
-function project_issue_update_project($pid, $issue_nid, $cid = NULL, $rid = NULL, $assigned_uid = NULL) {
-  // Prevent a malicious user from snooping this callback directly.
-  $error = array('error' => t('An error was detected. Please try again.'));
-  $node = node_load(array('nid' => $pid, 'type' => 'project_project'));
-  if ($node) {
-    if (!node_access('view', $node)) {
-      print drupal_to_js($error);
-      exit();
-    }
-  }
-  else {
-    print drupal_to_js($error);
-    exit();
-  }
+function project_issue_update_project() {
+  $form_state = array('storage' => NULL, 'submitted' => FALSE);
+  $form_build_id = $_POST['form_build_id'];
+  $form = form_get_cache($form_build_id, $form_state);
+  $args = $form['#parameters'];
+  $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'];
+  $cid = $form_state['values']['cid'];
+  $assigned_uid = $form_state['values']['project_info']['assigned'];
 
-  // Release id options ("Version" selector).
-  $return['rid'] = '';
+  $project_info = $form['original_issue']['project_info'];
 
   // 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)) {
       $releases = array(t('<none>')) + $releases;
@@ -49,21 +46,15 @@ function project_issue_update_project($p
           $default = $key;
         }
       }
-      $form = array();
       // Element is tree'd here to match the original form layout.
-      $form['project_info']['#tree'] = TRUE;
-      $form['project_info']['rid'] = array(
-        '#type' => 'select',
-        '#title' => t('Version'),
-        '#default_value' => $default,
-        '#options' => $releases,
-        '#required' => TRUE,
-      );
-
-      // Build the HTML output for the rid select.
-      $form_state = array();
-      $form = form_builder('rid', $form, $form_state);
-      $return['rid'] .= drupal_render($form);
+      $project_info['#tree'] = TRUE;
+      $project_info['rid']['#default_value'] = $default;
+      $project_info['rid']['#options'] = $releases;
+      $project_info['rid']['#disabled'] = FALSE;
+    }
+    else {
+      $project_info['rid']['#disabled'] = TRUE;
+      $project_info['rid']['#options'] = array();
     }
   }
 
@@ -77,24 +68,12 @@ function project_issue_update_project($p
     $issue = new stdClass;
   }
   $issue->project_issue['pid'] = $pid;
-  $return['assigned'] = '';
   $assigned_choices = project_issue_assigned_choices($issue);
 
-  $form = array();
-  $form['issue_info']['assigned'] = array(
-    '#type' => 'select',
-    '#title' => t('Assigned'),
-    '#default_value' => $assigned_uid,
-    '#options' => $assigned_choices,
-  );
-
-  // Build the HTML output for the assigned select.
-  $form_state = array();
-  $form = form_builder('assigned', $form, $form_state);
-  $return['assigned'] .= drupal_render($form);
+  $project_info['assigned']['#default_value'] = $assigned_uid;
+  $project_info['assigned']['#options'] = $assigned_choices;
 
   // Components.
-  $return['component'] = '';
   $project = db_fetch_object(db_query('SELECT * FROM {project_issue_projects} WHERE nid = %d', $pid));
   $components = array();
   if ($project->components) {
@@ -105,28 +84,12 @@ function project_issue_update_project($p
     }
   }
 
-  $form = array();
-  // Element is tree'd here to match the original form layout.
-  $form['project_info']['#tree'] = TRUE;
-  $form['project_info']['component'] = array(
-    '#type' => 'select',
-    '#title' => t('Component'),
-    '#default_value' => $cid,
-    '#options' => $components,
-    '#required' => TRUE,
-  );
+  $project_info['component']['#default_value'] = $cid;
+  $project_info['component']['#options'] = $components;
 
   // Build the HTML output for the component select.
-  $form_state = array();
-  $form = form_builder('component', $form, $form_state);
-  $return['component'] .= drupal_render($form);
-
-  // Set proper content type header for the AJAX call.
-  drupal_set_header('Content-type: text/javascript');
-
-  // Translate to js before output, so the calling js can work with the data.
-  print drupal_to_js($return);
-  exit();
+  $output = theme('status_messages') . drupal_render($project_info);
+  drupal_json(array('status' => TRUE, 'data' => $output));
 }
 
 function project_issue_statistics($project = 0) {
@@ -489,7 +452,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 +474,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 +490,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']))) {
