Index: issue.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/project_issue/issue.inc,v retrieving revision 1.354 diff -u -p -r1.354 issue.inc --- issue.inc 18 Jun 2009 03:28:55 -0000 1.354 +++ issue.inc 11 Aug 2009 16:31:29 -0000 @@ -22,10 +22,10 @@ function project_issue_update_project() // 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 = isset($form_state['values']['project_info']['rid']) ? $form_state['values']['project_info']['rid'] : 0; + $pid = $form_state['values']['project_issue']['pid']; + $rid = isset($form_state['values']['project_issue']['rid']) ? $form_state['values']['project_issue']['rid'] : 0; $cid = $form_state['values']['cid']; - $assigned_uid = $form_state['values']['project_info']['assigned']; + $assigned_uid = $form_state['values']['project_issue']['assigned']; $project_info = $form['original_issue']['project_info']; @@ -44,8 +44,6 @@ function project_issue_update_project() $default = $key; } } - // Element is tree'd here to match the original form layout. - $project_info['#tree'] = TRUE; $project_info['rid']['#options'] = $releases; $project_info['rid']['#disabled'] = FALSE; if (!empty($default)) { Index: includes/comment.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/project_issue/includes/comment.inc,v retrieving revision 1.154 diff -u -p -r1.154 comment.inc --- includes/comment.inc 18 Jun 2009 03:05:20 -0000 1.154 +++ includes/comment.inc 11 Aug 2009 16:31:30 -0000 @@ -30,10 +30,10 @@ function project_issue_comment(&$arg, $o $tries = 20; $sleep_increment = 0; while ($tries) { - $lock = db_query("UPDATE {project_issues} SET db_lock = 1 WHERE nid = %d AND db_lock = 0", $arg['nid']); + $lock = db_query("UPDATE {project_issues} SET db_lock = 1 WHERE nid = %d AND db_lock = 0", $nid); if (db_affected_rows()) { - $id = db_result(db_query("SELECT last_comment_id FROM {project_issues} WHERE nid = %d", $arg['nid'])) + 1; - db_query("UPDATE {project_issues} SET last_comment_id = %d, db_lock = 0 WHERE nid = %d", $id, $arg['nid']); + $id = db_result(db_query("SELECT last_comment_id FROM {project_issues} WHERE nid = %d", $nid)) + 1; + db_query("UPDATE {project_issues} SET last_comment_id = %d, db_lock = 0 WHERE nid = %d", $id, $nid); break; } @@ -45,12 +45,16 @@ function project_issue_comment(&$arg, $o } if (isset($id)) { - $rid = isset($arg['project_info']['rid']) ? $arg['project_info']['rid'] : 0; - 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'], $rid, $arg['project_info']['component'], $arg['category'], $arg['priority'], $arg['project_info']['assigned'], $arg['sid'], $arg['title'], $arg['timestamp'], $id); + $rid = isset($arg['project_issue']['rid']) ? $arg['project_issue']['rid'] : 0; + 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_issue']['pid'], $rid, $arg['project_issue']['component'], $arg['project_issue']['category'], $arg['project_issue']['priority'], $arg['project_issue']['assigned'], $arg['project_issue']['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'); + + // To hide the evil details of core's inconsistency, convert our $arg + // (which in this case is an array of form values, into an object like + // the other ops use, so that this helper function can be consistent. + project_issue_update_by_comment((object)$arg, 'insert'); $affected_projects[$old_data->pid] = 1; - $affected_projects[$arg['project_info']['pid']] = 1; + $affected_projects[$arg['project_issue']['pid']] = 1; } else { drupal_set_message(t('There was an error submitting your comment -- please try again. If the problem persists, contact the system administrator.'), 'error'); @@ -66,10 +70,15 @@ function project_issue_comment(&$arg, $o break; case 'update': - project_issue_update_by_comment($arg, 'update'); - // Updating a comment can't change anything relevant about the issue for - // the purposes of the issue blocks, so we don't need to touch - // $affected_projects here. + // To hide the evil details of core's inconsistency, convert our $arg + // (which in this case is an array of form values, into an object like + // the other ops use, so that this helper function can be consistent. + $current_data = project_issue_update_by_comment((object)$arg, 'update'); + + // We should also invalidate the block cache for whatever project is now + // used for this issue, since we might have just published a comment + // that moved an issue from one project to another. + $affected_projects[$current_data->project_issue['pid']] = 1; break; case 'delete': @@ -79,11 +88,12 @@ function project_issue_comment(&$arg, $o $affected_projects[$deleted_comment_project_id] = 1; // Actually delete the comment db_query("DELETE FROM {project_issue_comments} WHERE cid = %d", $arg->cid); + // $arg actually *is* an object at this point, amazing. Thanks, core. $current_data = project_issue_update_by_comment($arg, 'delete'); // We should also invalidate the block cache for whatever project is now // used for this issue, since we might be deleting a comment that moved // an issue from one project to another. - $affected_projects[$current_data->pid] = 1; + $affected_projects[$current_data->project_issue['pid']] = 1; break; case 'view': @@ -92,12 +102,11 @@ function project_issue_comment(&$arg, $o } 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']; + $test = (object)$arg->project_issue; + // Pull in the title which lives outside of the project_issue array. + $test->title = $arg->title; // Add a dummy rid if necessary -- prevents incorrect change data. - $test->rid = isset($arg->project_info['rid']) ? $arg->project_info['rid'] : 0; + $test->rid = isset($arg->project_issue['rid']) ? $arg->project_issue['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); } @@ -167,8 +176,8 @@ function project_issue_form_comment_form } // 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']; + if (!empty($form_state['values']['project_issue']['pid'])) { + $node->project_issue['pid'] = $form_state['values']['project_issue']['pid']; } $project = node_load(array('nid' => $node->project_issue['pid'], 'type' => 'project_project')); @@ -191,8 +200,6 @@ function project_issue_form_comment_form // so let's reuse the form. $form += drupal_retrieve_form('project_issue_form', $node, $form_state, TRUE); - // We need this otherwise pid collides with comment. - $form['project_info']['#tree'] = TRUE; $form['project_info']['#weight'] = -2; // Remove the form item that displays the current project, and @@ -200,7 +207,6 @@ function project_issue_form_comment_form // of all projects to make swapping simpler. unset($form['project_info']['project_display']); $uris = NULL; - if (variable_get('project_issue_autocomplete', 0) == 1) { $form['project_info']['project_title'] = array( '#type' => 'textfield', @@ -220,6 +226,7 @@ function project_issue_form_comment_form 'path' => 'project/issues/update_project', 'wrapper' => 'project-info-wrapper', ), + '#parents' => array('project_issue', 'project_title'), ); } else { @@ -236,6 +243,7 @@ function project_issue_form_comment_form 'wrapper' => 'project-info-wrapper', 'event' => 'change', ), + '#parents' => array('project_issue', 'pid'), ); } @@ -289,16 +297,16 @@ function project_issue_form_comment_form */ function project_issue_form_comment_validate($form, &$form_state) { if (empty($form['cid']['#value']) && variable_get('project_issue_autocomplete', 0) == 1) { - if (empty($form_state['values']['project_info']['project_title'])) { + if (empty($form_state['values']['project_issue']['project_title'])) { form_set_error('project_title', t('You must enter a project to navigate to.')); } else { - $pid = db_result(db_query("SELECT nid FROM {node} WHERE title = '%s' AND type = '%s'", $form_state['values']['project_info']['project_title'], 'project_project')); + $pid = db_result(db_query("SELECT nid FROM {node} WHERE title = '%s' AND type = '%s'", $form_state['values']['project_issue']['project_title'], 'project_project')); if (empty($pid)) { - form_set_error('project_info][project_title', t('The name you entered (%title) is not a valid project.', array('%title' => $form_state['values']['project_info']['project_title']))); + form_set_error('project_info][project_title', t('The name you entered (%title) is not a valid project.', array('%title' => $form_state['values']['project_issue']['project_title']))); } else { - $form_state['values']['project_info']['pid'] = $pid; + $form_state['values']['project_issue']['pid'] = $pid; } } } @@ -306,10 +314,15 @@ function project_issue_form_comment_vali if (!empty($form_state['rebuild'])) { return; } + + // Only validate metadata changes and do other magic on new followups. + if (isset($form_state['values']['cid'])) { + return; + } + $values = $form_state['values']; - $project_info = $form_state['values']['project_info']; - $nid = $values['nid']; - $node = node_load($nid); + $node = node_load($values['nid']); + $project_issue = $form_state['values']['project_issue']; // Make a copy here so we have all the original metadata, since some // of it can change below. @@ -323,14 +336,9 @@ function project_issue_form_comment_vali // TODO: is this still true? project_issue_change_comment_upload_path($values); - // Only validate metadata changes on new followups. - if (isset($values['cid'])) { - return; - } - // Make sure project is current here -- it may have changed when posted. - if (isset($project_info['pid'])) { - $node->project_issue['pid'] = $project_info['pid']; + if (isset($project_issue['pid'])) { + $node->project_issue['pid'] = $project_issue['pid']; } $project = node_load($node->project_issue['pid']); @@ -340,8 +348,8 @@ function project_issue_form_comment_vali $form_state['values']['pid'] = 0; // Validate version. - 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 (module_exists('project_release') && isset($project_issue['rid']) && ($releases = project_release_get_releases($project, 0, 'version', 'all', array($project_issue['rid'])))) { + $rid = $project_issue['rid']; if ($rid && !in_array($rid, array_keys($releases))) { $rid = 0; } @@ -362,7 +370,7 @@ function project_issue_form_comment_vali $rid = 0; } // Validate component. - $component = $project_info['component']; + $component = $project_issue['component']; if ($component && !in_array($component, $project->project_issue['components'])) { $component = 0; } @@ -371,7 +379,9 @@ function project_issue_form_comment_vali else { form_set_error('project_info][pid', t('You have to specify a valid project.')); } - empty($values['category']) && form_set_error('category', t('You have to specify a valid category.')); + if (empty($project_issue['category'])) { + form_set_error('project_info][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, @@ -385,12 +395,10 @@ function project_issue_form_comment_vali } } if (!$has_file && empty($values['comment'])) { - - $comment = drupal_clone((object) $values); - $comment->pid = $project_info['pid']; + $comment = drupal_clone((object) $project_issue); + $comment->title = $values['title']; $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) { @@ -531,38 +539,31 @@ function project_issue_comment_view(&$no * Updates the project issue based on the comment inserted/updated/deleted. * * @param $comment_data - * The comment data that's been submitted. + * The comment data that's been submitted, including nid, cid, and a + * project_issue array of all issue metadata. * @param $op - * The comment operation performed, 'insert', 'update', 'delete'. + * The comment operation performed, 'insert', 'update', 'delete'. * @return * An object representing the comment data used to update the issue. */ function project_issue_update_by_comment($comment_data, $op) { switch ($op) { case 'insert': - // Massage the incoming data so the structure is consistent throughout the function. - $comment_data['component'] = $comment_data['project_info']['component']; - $comment_data['pid'] = $comment_data['project_info']['pid']; - $comment_data['rid'] = isset($comment_data['project_info']['rid']) ? $comment_data['project_info']['rid'] : 0; - unset ($comment_data['project_info']); - $comment_data = (object) $comment_data; // Mark the node for email notification during hook_exit(), so all issue // and file data is in a consistent state before we generate the email. if (!isset($comment_data->followup_no_mail)) { // Temporary hack to get around sending of auto-close emails. project_issue_set_mail_notify($comment_data->nid); } break; - case 'update': - $comment_data = (object) $comment_data; - break; } - // 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. + // 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. $comment_data = project_issue_get_newest_comment($comment_data); // Update the issue data to reflect the new final states. - db_query("UPDATE {project_issues} SET pid = %d, category = '%s', component = '%s', priority = %d, rid = %d, assigned = %d, sid = %d WHERE nid = %d", $comment_data->pid, $comment_data->category, $comment_data->component, $comment_data->priority, $comment_data->rid, $comment_data->assigned, $comment_data->sid, $comment_data->nid); + db_query("UPDATE {project_issues} SET pid = %d, category = '%s', component = '%s', priority = %d, rid = %d, assigned = %d, sid = %d WHERE nid = %d", $comment_data->project_issue['pid'], $comment_data->project_issue['category'], $comment_data->project_issue['component'], $comment_data->project_issue['priority'], $comment_data->project_issue['rid'], $comment_data->project_issue['assigned'], $comment_data->project_issue['sid'], $comment_data->nid); // Update the issue title. $node = node_load($comment_data->nid, NULL, TRUE); // Don't use cached since we changed data above. @@ -601,20 +602,27 @@ function project_issue_change_comment_up * An object representing the most recent published comment for the issue. */ function project_issue_get_newest_comment($comment_data) { + $comment = new stdClass; // Get the cid of the most recent comment. $latest_cid = db_result(db_query_range('SELECT pic.cid FROM {project_issue_comments} pic INNER JOIN {comments} c ON c.cid = pic.cid WHERE c.nid = %d AND c.status = %d ORDER BY pic.timestamp DESC', $comment_data->nid, COMMENT_PUBLISHED, 0, 1)); if ($latest_cid) { - $comment_data = db_fetch_object(db_query('SELECT * FROM {project_issue_comments} WHERE cid = %d', $latest_cid)); + $data = db_fetch_array(db_query('SELECT * FROM {project_issue_comments} WHERE cid = %d', $latest_cid)); + foreach (array('cid', 'nid', 'title', 'timestamp', 'comment_number') as $field) { + $comment->$field = $data[$field]; + unset($data[$field]); + } + $comment->project_issue = $data; } // No more comments on the issue -- use the original issue metadata. else { // nid isn't stored in the original issue data, so capture it here and pass back // into the object. $nid = $comment_data->nid; - $comment_data = unserialize(db_result(db_query("SELECT original_issue_data FROM {project_issues} WHERE nid = %d", $comment_data->nid))); - $comment_data->nid = $nid; + $comment->project_issue = (array)unserialize(db_result(db_query("SELECT original_issue_data FROM {project_issues} WHERE nid = %d", $comment_data->nid))); + $comment->nid = $nid; + $comment->title = $comment->project_issue['title']; } - return $comment_data; + return $comment; } /** Index: includes/issue_node_form.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/project_issue/includes/issue_node_form.inc,v retrieving revision 1.3 diff -u -p -r1.3 issue_node_form.inc --- includes/issue_node_form.inc 6 Aug 2009 23:31:23 -0000 1.3 +++ includes/issue_node_form.inc 11 Aug 2009 16:31:30 -0000 @@ -87,6 +87,7 @@ function project_issue_pick_project_form function _project_issue_form($node, $form_state, $include_metadata_fields = FALSE) { global $user; + // Set some defaults for new forms. $defaults = array( 'rid', 'component', @@ -95,20 +96,10 @@ function _project_issue_form($node, $for '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'])) { - foreach ($defaults as $default) { - if (isset($form_state['node'][$default])) { - $node->project_issue[$default] = $form_state['node'][$default]; + if (!isset($node->project_issue[$default])) { + $node->project_issue[$default] = 0; } } } @@ -139,10 +130,10 @@ function _project_issue_form($node, $for } // Figure out what project we should use for the issue metadata. - if (!empty($form_state['values']['project_info']['pid'])) { + if (!empty($form_state['values']['project_issue']['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']; + $pid = $form_state['values']['project_issue']['pid']; } elseif (!empty($node->project_issue['pid'])) { // The issue node already knows what project it belongs to. @@ -232,6 +223,12 @@ function _project_issue_form($node, $for } if ($allow_metadata_changes) { + $form['project_issue'] = array('#tree' => TRUE); + $form['project_issue']['pid'] = array( + '#type' => 'value', + '#value' => $node->project_issue['pid'], + ); + $form['project_info'] = array( '#type' => 'fieldset', '#title' => t('Project information'), @@ -243,10 +240,6 @@ function _project_issue_form($node, $for '#title' => t('Project'), '#value' => check_plain($project->title), ); - $form['project_info']['pid'] = array( - '#type' => 'value', - '#value' => $node->project_issue['pid'], - ); if ($releases) { $form['project_info']['rid'] = array( '#type' => 'select', @@ -254,6 +247,7 @@ function _project_issue_form($node, $for '#default_value' => $node->project_issue['rid'], '#options' => $releases, '#required' => TRUE, + '#parents' => array('project_issue', 'rid'), ); } $form['project_info']['component'] = array( @@ -262,6 +256,7 @@ function _project_issue_form($node, $for '#default_value' => $default_component, '#options' => $components, '#required' => TRUE, + '#parents' => array('project_issue', 'component'), ); $form['issue_info'] = array( '#type' => 'fieldset', @@ -275,18 +270,21 @@ function _project_issue_form($node, $for '#default_value' => $node->project_issue['category'] ? $node->project_issue['category'] : arg(4), '#options' => $categories, '#required' => TRUE, + '#parents' => array('project_issue', 'category'), ); $form['issue_info']['priority'] = array( '#type' => 'select', '#title' => t('Priority'), '#default_value' => $node->project_issue['priority'] ? $node->project_issue['priority'] : 2, '#options' => $priorities, + '#parents' => array('project_issue', 'priority'), ); $form['issue_info']['assigned'] = array( '#type' => 'select', '#title' => t('Assigned'), '#default_value' => $node->project_issue['assigned'], '#options' => $assigned, + '#parents' => array('project_issue', 'assigned'), ); if (count($states) > 1) { $form['issue_info']['sid'] = array( @@ -294,6 +292,7 @@ function _project_issue_form($node, $for '#title' => t('Status'), '#default_value' => $node->project_issue['sid'] ? $node->project_issue['sid'] : $default_state, '#options' => $states, + '#parents' => array('project_issue', 'sid'), ); } else { @@ -402,8 +401,8 @@ function _project_issue_form($node, $for * * @param $node * An object of form values from the project_issue node form, not a fully - * loaded issue node object. Therefore, the fields are not in the usual - * $node->project_issue array. + * loaded issue node object. Thanks to setting '#parents', the fields we + * care about should be in the $node->project_issue array. */ function _project_issue_validate($node) { // If $node->nid is set, that means that the node was being @@ -411,23 +410,23 @@ function _project_issue_validate($node) // not presented with any of the metadata fields, so there's no // need to validate them here. if (empty($node->nid)) { - if (empty($node->pid)) { + if (empty($node->project_issue['pid'])) { form_set_error('pid', t('You have to specify a valid project.')); } - elseif ($project = node_load($node->pid)) { + elseif ($project = node_load($node->project_issue['pid'])) { if (module_exists('project_release') && $releases = project_release_get_releases($project, 0)) { - if (empty($node->rid)) { + if (empty($node->project_issue['rid'])) { form_set_error('rid', t('You have to specify a valid version.')); } } - if (isset($node->component) && !in_array($node->component, $project->project_issue['components'])) { + if (isset($node->project_issue['component']) && !in_array($node->project_issue['component'], $project->project_issue['components'])) { $node->component = 0; } - if (empty($node->component)) { + if (empty($node->project_issue['component'])) { form_set_error('component', t('You have to specify a valid component.')); } - if (empty($node->category)) { + if (empty($node->project_issue['category'])) { form_set_error('category', t('You have to specify a valid category.')); } } @@ -439,8 +438,8 @@ function _project_issue_validate($node) * * @param $node * Object containing form values from the project_issue node form. This is - * NOT a fully loaded $node object, so the issue-related values are directly - * in $node, not in the $node->project_issue array. + * NOT a fully loaded $node object, but thanks to setting '#parents', the + * fields we care about should be in the $node->project_issue array. */ function _project_issue_insert($node) { // Permanently store the original issue states in a serialized array. This @@ -458,20 +457,20 @@ function _project_issue_insert($node) { 'priority' => 0, 'assigned' => 0, 'sid' => 0, - 'title' => '', ); foreach ($fields as $field => $default) { // Some of the incoming data may not have the correct default. - if (empty($node->$field)) { - $node->$field = $default; + if (empty($node->project_issue[$field])) { + $node->project_issue[$field] = $default; } - $original_issue_data->$field = $node->$field; + $original_issue_data->$field = $node->project_issue[$field]; } + $original_issue_data->title = $node->title; - db_query("INSERT INTO {project_issues} (nid, pid, category, component, priority, rid, assigned, sid, original_issue_data, last_comment_id, db_lock) VALUES (%d, %d, '%s', '%s', %d, %d, %d, %d, '%s', %d, %d)", $node->nid, $node->pid, $node->category, $node->component, $node->priority, $node->rid, $node->assigned, $node->sid, serialize($original_issue_data), 0, 0); + db_query("INSERT INTO {project_issues} (nid, pid, category, component, priority, rid, assigned, sid, original_issue_data, last_comment_id, db_lock) VALUES (%d, %d, '%s', '%s', %d, %d, %d, %d, '%s', %d, %d)", $node->nid, $node->project_issue['pid'], $node->project_issue['category'], $node->project_issue['component'], $node->project_issue['priority'], $node->project_issue['rid'], $node->project_issue['assigned'], $node->project_issue['sid'], serialize($original_issue_data), 0, 0); // Invalidate the "Issue cockpit" block cache for this project, since the // new issue will have altered the summary totals. - cache_clear_all('project_issue_cockpit_block:'. $node->pid, 'cache'); + cache_clear_all('project_issue_cockpit_block:'. $node->project_issue['pid'], 'cache'); }