Index: project_issue_contact.info
===================================================================
--- project_issue_contact.info	(revision 0)
+++ project_issue_contact.info	(revision 0)
@@ -0,0 +1,7 @@
+; $Id$
+name = Project issue Contact integration
+description = Allows the site-wide contact tab to optionally generate project issues instead of emails.
+dependencies[] = contact
+dependencies[] = project_issue
+package = Project
+core = 6.x
Index: project_issue_contact.module
===================================================================
--- project_issue_contact.module	(revision 0)
+++ project_issue_contact.module	(revision 0)
@@ -0,0 +1,231 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Integrates Project issue tracking with the core Contact module so that the
+ * site-wide contact tab can be optionally configured to create new project
+ * issues instead of sending email.
+ *
+ * @todo Need to do something reasonable about permissions.
+ *
+ * @todo All the validation hooks need to be written.
+ *
+ * @todo core validate/submit handler assumes email is required
+ *
+ * @todo Do something intelligent with the name/email fields for
+ *   anonymous users? Perhaps we should maintain a table to store that
+ *   info and associate with the node, then have a hook_nodeapi() in
+ *   here to somehow display at least the name (and for site admins,
+ *   the email or something?
+ */
+
+/**
+ * Implementation of hook_form_FORM_ID_alter().
+ *
+ * Alter the admin form for adding or editing contact form categories. Adds
+ * optional settings to send the information into an issue queue, not just to
+ * the email recipients.
+ */
+function project_issue_contact_form_contact_admin_edit_alter(&$form, &$form_state) {
+
+  // If a record for this contact already exists, populate the default values.
+  if ($form['contact_op']['#value'] == 'edit') {
+    $result = db_fetch_array(db_query('SELECT * FROM {project_issue_contact} WHERE cid = %d', $form['cid']['#value']));
+  }
+
+  $form['issues'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Issue queue integration'),
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
+    '#weight' => 9,
+  );
+
+  // Establish default values if we're editing an existing category.
+  $defaults = array();
+  if ($form['cid']['#value'] > 0) {
+    $defaults = db_fetch_array(db_query('SELECT * FROM {project_issue_contact} WHERE cid = %d', $form['cid']['#value']));
+  }
+
+  // Pre-populate project issue-related selections.
+  // @todo AJAXify this up!
+  $default_state = variable_get('project_issue_default_state', 1);
+  $uris = NULL;
+  $projects = array(t('<none>')) + project_projects_select_options($uris);
+  if (isset($defaults['pid'])) {
+    $project = node_load(array('nid' => $defaults['pid'], 'type' => 'project_project'));
+  }
+
+  $components = array();
+  if (!empty($project->project_issue['components'])) {
+    $components = array(t('<none>'));
+    foreach ($project->project_issue['components'] as $component) {
+      $component = check_plain($component);
+      $components[$component] = $component;
+    }
+  }
+  if (module_exists('project_release') && isset($project) &&
+      $releases = project_release_get_releases($project, 0)) {
+    $releases = array(t('<none>')) + $releases;
+  }
+  $categories = array_merge(array(t('<none>')), project_issue_category(0, 0));
+  $priorities = project_issue_priority();
+  $states = project_issue_state();
+
+  // Add the appropriate description to the fieldset.
+  $form['issues']['#description'] = t('Optionally define an issue queue where issues should be created whenever a user submits a contact form for this category. If the %project setting is set to %none, no issue will be generated and the other settings are ignored. However, if you select a %project, the other settings determine the values to use the corresponding fields in the issue creation form when users use this contact category.', array('%project' => t('Project'), '%none' => t('<none>')));
+  $form['issues']['pid'] = array(
+    '#type' => 'select',
+    '#title' => t('Project'),
+    '#default_value' => isset($defaults['pid']) ? $defaults['pid'] : 0,
+    '#options' => $projects,
+  );
+  if (!empty($components)) {
+    $form['issues']['component'] = array(
+      '#type' => 'select',
+      '#title' => t('Component'),
+      '#default_value' => isset($defaults['component']) ? $defaults['component'] : 0,
+      '#options' => $components,
+    );
+  }
+  if (!empty($releases)) {
+    $form['issues']['rid'] = array(
+      '#type' => 'select',
+      '#title' => t('Version'),
+      '#default_value' => isset($defaults['rid']) ? $defaults['rid'] : 0,
+      '#options' => $releases,
+    );
+  }
+  $form['issues']['issue_category'] = array(
+    '#type' => 'select',
+    '#title' => t('Issue category'),
+    '#default_value' => isset($defaults['category']) ? $defaults['category'] : 'support',
+    '#options' => $categories,
+  );
+  $form['issues']['priority'] = array(
+    '#type' => 'select',
+    '#title' => t('Priority'),
+    '#default_value' => isset($defaults['priority']) ? $defaults['priority'] : 2,
+    '#options' => $priorities,
+  );
+  if (count($states) > 1) {
+    $form['issues']['sid'] = array(
+      '#type' => 'select',
+      '#title' => t('Status'),
+      '#default_value' => isset($defaults['sid']) ? $defaults['sid'] : $default_state,
+      '#options' => $states,
+    );
+  }
+  else {
+    $form['issues']['sid'] = array(
+      '#type' => 'hidden',
+      '#value' => $default_state,
+    );
+    $form['issues']['status'] = array(
+      '#type' => 'item',
+      '#title' => t('Status'),
+      '#value' => project_issue_state($default_state),
+    );
+  }
+  $form['#validate'][] = 'project_issue_contact_admin_edit_validate';
+  $form['#submit'][] = 'project_issue_contact_admin_edit_submit';
+
+  // Re-weight the submit button below the fieldset.
+  $form['submit']['#weight'] = 10;
+}
+
+/**
+ * Validation handler for contact admin form.
+ *
+ * @todo !!!
+ *
+ */
+function project_issue_contact_admin_edit_validate($form, &$form_state) {
+}
+
+/**
+ * Submit handler for contact admin form. Deletes, updates, or inserts
+ * records into the {project_issue_contact} table as approriate.
+ */
+function project_issue_contact_admin_edit_submit($form, &$form_state) {
+  // If no target project was selected, nothing to do here. Clean up stale
+  // records in case they're still laying around.
+  if ($form_state['values']['pid'] == 0) {
+    db_query("DELETE FROM {project_issue_contact} WHERE cid = %d", $form_state['values']['cid']);
+  }
+
+  // Check to see if a record exists. If so, perform update logic.
+  elseif (db_result(db_query('SELECT 1 FROM {project_issue_contact} WHERE cid = %d', $form_state['values']['cid']))) {
+    db_query("UPDATE {project_issue_contact} SET pid = %d, rid = %d, category = '%s', component = '%s', priority = %d, sid = %d WHERE cid = %d", $form_state['values']['pid'], $form_state['values']['rid'], $form_state['values']['issue_category'], $form_state['values']['component'], $form_state['values']['priority'], $form_state['values']['sid'], $form_state['values']['cid']);
+    drupal_set_message(t('Category %category issue queue settings modified.', array('%category' => $form_state['values']['category'])));
+  }
+
+  // If we get here, perform insert logic.
+  else if (!empty($form_state['values']['cid'])) {
+    db_query("INSERT INTO {project_issue_contact} (cid, pid, rid, category, component, priority, sid) VALUES (%d, %d, %d, '%s', '%s', %d, %d)", $form_state['values']['cid'], $form_state['values']['pid'], $form_state['values']['rid'], $form_state['values']['issue_category'], $form_state['values']['component'], $form_state['values']['priority'], $form_state['values']['sid']);
+    drupal_set_message(t('Category %category configured to create issues when users fill out the contact form.', array('%category' => $form_state['values']['category'])));
+  }
+}
+
+/**
+ * Implementation of hook_form_FORM_ID_alter().
+ *
+ * Add additional submission logic when contact module category is deleted.
+ */
+function project_issue_contact_form_contact_admin_delete_alter(&$form, &$form_state) {
+  $form['#submit'][] = 'project_issue_contact_delete_submit';
+}
+
+/**
+ * Clean up {project_issue_contact} when Contact module category removed.
+ */
+function project_issue_contact_delete_submit($form, &$form_state) {
+  $cid = $form_state['values']['contact']['cid'];
+  db_query("DELETE FROM {project_issue_contact} WHERE cid = %d", $cid);
+}
+
+/**
+ * Implementation of hook_form_FORM_ID_alter().
+ *
+ * Add additional submission logic when contact form is submitted.
+ */
+function project_issue_contact_form_contact_mail_page_alter(&$form, &$form_state) {
+  // for debugging only
+  unset($form['#submit']);
+  $form['#submit'][] = 'project_issue_contact_mail_page_form_submit';
+}
+
+/**
+ * Automatically generate an issue when contact form is submitted.
+ */
+function project_issue_contact_mail_page_form_submit($form, &$form_state) {
+  $project_issue_contact_values = db_fetch_array(db_query("SELECT * FROM {project_issue_contact} WHERE cid = %d", $form_state['values']['cid']));
+  global $user;
+
+  $node->type = 'project_issue';
+  $node->uid = $user->uid;
+  $node->title = $form_state['values']['subject'];
+  $node->body = $form_state['values']['message'];
+  $node->teaser = node_teaser($node->body);
+  $node->filter = variable_get('filter_default_format', 1);
+  $node->status = 1;
+  // Turn comments to Read/Write.
+  // @todo this doesn't work. :(
+  $node->comment = 2;
+
+  foreach (array('pid', 'rid', 'category', 'component', 'priority', 'sid') as $field) {
+    $node->$field = $project_issue_contact_values[$field];
+  }
+
+  /// @todo : store name/email somewhere reasonable for anon contacts?
+
+  node_save($node);
+  if (node_access('view', $node)) {
+    drupal_set_message(t('Your message has been recorded into <a href="@url">this issue.', array('@url' => url('node/' . $node->nid))));
+  }
+  else {
+    drupal_set_message(t('Your message has been recorded on the site'));
+  }
+}
+
Index: project_issue_contact.install
===================================================================
--- project_issue_contact.install	(revision 0)
+++ project_issue_contact.install	(revision 0)
@@ -0,0 +1,89 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Install, update, and uninstall functions for the module. 
+ */
+
+/**
+ * Implementation of hook_schema().
+ */
+function project_issue_contact_schema() {
+  $schema['project_issue_contact'] = array(
+    'description' => t('Stores the generated issue configuration for a given Contact module category.'),
+    'fields' => array(
+      'cid' => array(
+        'description' => t('Primary Key: The {contact}.cid (Contact module category) for this configuration record.'),
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'pid' => array(
+        'description' => t('The {project_projects}.nid (project) to which the generated issue should be assigned.'),
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'rid' => array(
+        'description' => 'The {project_release_nodes}.rid (version identifier) to which the generated issue should be assigned.',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'category' => array(
+        'description' => t('The {project_issues}.category to which the generated issue should be assigned.'),
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'component' => array(
+        'description' => t('The {project_issues}.component to which the generated issue should be assigned.'),
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'priority' => array(
+        'description' => t('The {project_issues}.priority to which the generated issue should be assigned.'),
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'size' => 'tiny',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'sid' => array(
+        'description' => t('The {project_issue_state}.sid (status) to which the generated issue should be assigned.'),
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+    ),
+    'primary key' => array('cid'),
+  );
+
+  return $schema;
+}
+
+/**
+ * Implementation of hook_install().
+ */
+function project_issue_contact_install() {
+  drupal_install_schema('project_issue_contact');
+
+  // Match the weighting of project and project_issue modules.
+  db_query("UPDATE {system} SET weight = 2 WHERE name = 'project_issue_contact'");
+}
+
+/**
+ * Implementation of hook_uninstall().
+ */
+function project_issue_contact_uninstall() {
+  drupal_uninstall_schema('project_issue_contact');
+}
+
