Index: project_issue.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/project_issue/project_issue.module,v
retrieving revision 1.78
diff -u -F^f -u -F^f -r1.78 project_issue.module
--- project_issue.module	14 Jan 2008 20:01:25 -0000	1.78
+++ project_issue.module	16 Jan 2008 17:32:44 -0000
@@ -182,6 +182,30 @@ function project_issue_settings_form() {
     '#default_value' => variable_get('project_issue_reply_to', variable_get('site_mail', ini_get('sendmail_from'))),
     '#description' => t('All issue e-mails sent via subscriptions will appear from this e-mail address. You can use %project as a placeholder which will be replaced with the %short_project_name setting for the issue\'s current project.', array('%project' => '%project', '%short_project_name' => t('Short project name'))),
   );
+
+  // Determine the auto-close username from the auto-close setting.
+  $auto_close_username = '';
+  $uid = variable_get('project_issue_auto_close_user', 'anon');
+  $anon = variable_get('anonymous', t('Anonymous'));
+  if ($uid) {
+    if ($uid == 'anon') {
+      $auto_close_username = $anon;
+    }
+    elseif ($account = user_load(array('uid' => $uid))) {
+      $auto_close_username = $account->name;
+    }
+  }
+
+  $form['project_issue_auto_close_user'] = array(
+    '#title' => t('Auto-close user'),
+    '#type' => 'textfield',
+    '#default_value' => $auto_close_username,
+    '#maxlength' => 60,
+    '#description' => t('Enter the user which will auto-close fixed issues -- leave empty to disable auto-closing or set to %anon to use the anonymous user.', array('%anon' => $anon)),
+    '#validate' => array('project_issue_validate_auto_close_user' => array()),
+    '#autocomplete_path' => 'user/autocomplete',
+  );
+
   if (module_exists('mailhandler')) {
     // TODO: move this stuff to mailhandler.module ?
     $items = array(t('<none>'));
@@ -207,9 +231,33 @@ function project_issue_validate_issues_p
   }
 }
 
-function project_issue_cron() {
-  global $user;
+/**
+ * Validates that the auto-close user exists, and has sufficient permissions
+ * to auto-close issues.
+ */
+function project_issue_validate_auto_close_user($form) {
+  $name = $form['#value'];
+  if ($name) {
+    // Make this check case-insensitive to allow the admin some data entry leeway.
+    $is_anon = drupal_strtolower($name) == drupal_strtolower(variable_get('anonymous', t('Anonymous')));
+    // Load the user. (don't see a constant for uid 0... )
+    $account = $is_anon ? user_load(array('uid' => 0)) : user_load(array('name' => $name));
+    if ($account) {
+      if (user_access('access project issues', $account)) {
+        // Transform the username into the more stable user ID.
+        form_set_value($form, $is_anon ? 'anon' : $account->uid);
+      }
+      else {
+        form_error($form, t('%name does not have sufficient permissions to auto-close issues.', array('%name' => $is_anon ? variable_get('anonymous', t('Anonymous')) : $name)));
+      }
+    }
+    else {
+      form_error($form, t('%name is not a valid user.', array('%name' => $name)));
+    }
+  }
+}
 
+function project_issue_cron() {
   if (time() - variable_get('project_issue_digest_last', 0) > variable_get('project_issue_digest_interval', 7 * 24 * 60 * 60)) {
     variable_set('project_issue_digest_last', time());
     project_mail_digest();
@@ -220,65 +268,102 @@ function project_issue_cron() {
     project_mail_reminder();
   }
 
-  $result = db_query('SELECT p.nid, p.pid, p.category, p.component, p.priority, p.rid, p.assigned, p.sid, n.title FROM {project_issues} p INNER JOIN {node} n ON n.nid = p.nid WHERE n.status = 1 AND p.sid = 2 AND n.changed < %d', time() - 14 * 24 * 60 * 60);
+  // Auto-close fixed issues;
+  project_issue_auto_close();
+}
+
+/**
+ * Automatically closes issues marked as fixed after two weeks.
+ */
+function project_issue_auto_close() {
+  global $user;
+
+  if ($uid = variable_get('project_issue_auto_close_user', 'anon')) {
+    // If a user exists for auto-closing comments, load them into
+    // the global user object temporarily. We use session_save_session()
+    // to provide safe user impersonation.
+    $auto_close = TRUE;
+    $original_user = $user;
+    session_save_session(FALSE);
+    $is_anon = $uid == 'anon';
+    $user = $is_anon ? user_load(array('uid' => 0)) : user_load(array('uid' => $uid));
+    // Safety check -- we have to have a valid user here.
+    if(!$user) {
+      watchdog('project_issue', t('Auto-close user failed to load.'), WATCHDOG_ERROR);
+      $auto_close = FALSE;
+    }
+    // Safety check -- selected user must still have the correct permissions to auto-close issues.
+    if (!user_access('access project issues')) {
+      watchdog('project_issue', t('%name does not have sufficient permissions to auto-close issues.', array('%name' => $is_anon ? variable_get('anonymous', t('Anonymous')) : $user->name)), WATCHDOG_ERROR);
+      $auto_close = FALSE;
+    }
+
+    if ($auto_close) {
+      $result = db_query('SELECT p.nid, p.pid, p.category, p.component, p.priority, p.rid, p.assigned, p.sid, n.title FROM {project_issues} p INNER JOIN {node} n ON n.nid = p.nid WHERE n.status = 1 AND p.sid = 2 AND n.changed < %d', time() - 14 * 24 * 60 * 60);
+
+      // Set up the persistent {comments} data.
+      $comment['pid'] = 0;
+      $comment['uid'] = $user->uid;
+      // The correct subject number is supplied during the save cycle.
+      $comment['subject'] = 'temp';
+      $comment['comment'] = theme('project_issue_auto_close_message');
+      $comment['format'] = FILTER_FORMAT_DEFAULT;
+      $comment['status'] = COMMENT_PUBLISHED;
+      $comment['name'] = $user->name;
+      $comment['mail'] = '';
+      $comment['homepage'] = '';
+
+      $roles = variable_get('comment_roles', array());
+      $score = 0;
+      foreach (array_intersect(array_keys($roles), array_keys($user->roles)) as $rid) {
+        $score = max($roles[$rid], $score);
+      }
+      $users = serialize(array(0 => $score));
+
+      // Set up the persistent {project_issue_comments} data.
+      // TODO: It's evil to hard-code the status here.
+      $comment['sid'] = 7;
+
+      $clear_cache = FALSE;
+
+      while ($issue = db_fetch_object($result)) {
+        $clear_cache = TRUE;
+
+        $comment['cid'] = db_next_id('{comments}_cid');
+        $comment['timestamp'] = time();
+        $comment['nid'] = $issue->nid;
+        $comment['category'] = $issue->category;
+        $comment['priority'] = $issue->priority;
+        $comment['assigned'] = $issue->assigned;
+        $comment['title'] = $issue->title;
+        $comment['project_info']['pid'] = $issue->pid;
+        $comment['project_info']['rid'] = $issue->rid;
+        $comment['project_info']['component'] = $issue->component;
+
+        // Build vancode
+        $max = db_result(db_query('SELECT MAX(thread) FROM {comments} WHERE nid = %d', $comment['nid']));
+        // Strip the "/" from the end of the thread.
+        $max = rtrim($max, '/');
+        // Finally, build the thread field for this new comment.
+        $thread = int2vancode(vancode2int($max) + 1) .'/';
+
+        db_query("INSERT INTO {comments} (cid, nid, pid, uid, subject, comment, format, hostname, timestamp, status, score, users, thread, name, mail, homepage) VALUES (%d, %d, %d, %d, '%s', '%s', %d, '%s', %d, %d, %d, '%s', '%s', '%s', '%s', '%s')", $comment['cid'], $comment['nid'], $comment['pid'], $comment['uid'], $comment['subject'], $comment['comment'], $comment['format'], $_SERVER['REMOTE_ADDR'], $comment['timestamp'], $comment['status'], $score, $users, $thread, $comment['name'], $comment['mail'], $comment['homepage']);
+
+        _comment_update_node_statistics($comment['nid']);
+
+        // Tell the other modules a new comment has been submitted.
+        comment_invoke_comment($comment, 'insert');
+      }
+
+      if ($clear_cache) {
+        // Clear cache so anonymous users can see the new post.
+        cache_clear_all();
+      }
+    }
 
-  // Set up the persistent {comments} data.
-  $comment['pid'] = 0;
-  $comment['uid'] = $user->uid;
-  // The correct subject number is supplied during the save cycle.
-  $comment['subject'] = 'temp';
-  $comment['comment'] = theme('project_issue_auto_close_message');
-  $comment['format'] = FILTER_FORMAT_DEFAULT;
-  $comment['status'] = COMMENT_PUBLISHED;
-  $comment['name'] = $user->name;
-  $comment['mail'] = '';
-  $comment['homepage'] = '';
-
-  $roles = variable_get('comment_roles', array());
-  $score = 0;
-  foreach (array_intersect(array_keys($roles), array_keys($user->roles)) as $rid) {
-    $score = max($roles[$rid], $score);
-  }
-  $users = serialize(array(0 => $score));
-
-  // Set up the persistent {project_issue_comments} data.
-  // TODO: It's evil to hard-code the status here.
-  $comment['sid'] = 7;
-
-  $clear_cache = FALSE;
-
-  while ($issue = db_fetch_object($result)) {
-    $clear_cache = TRUE;
-
-    $comment['cid'] = db_next_id('{comments}_cid');
-    $comment['timestamp'] = time();
-    $comment['nid'] = $issue->nid;
-    $comment['category'] = $issue->category;
-    $comment['priority'] = $issue->priority;
-    $comment['assigned'] = $issue->assigned;
-    $comment['title'] = $issue->title;
-    $comment['project_info']['pid'] = $issue->pid;
-    $comment['project_info']['rid'] = $issue->rid;
-    $comment['project_info']['component'] = $issue->component;
-
-    // Build vancode
-    $max = db_result(db_query('SELECT MAX(thread) FROM {comments} WHERE nid = %d', $comment['nid']));
-    // Strip the "/" from the end of the thread.
-    $max = rtrim($max, '/');
-    // Finally, build the thread field for this new comment.
-    $thread = int2vancode(vancode2int($max) + 1) .'/';
-
-    db_query("INSERT INTO {comments} (cid, nid, pid, uid, subject, comment, format, hostname, timestamp, status, score, users, thread, name, mail, homepage) VALUES (%d, %d, %d, %d, '%s', '%s', %d, '%s', %d, %d, %d, '%s', '%s', '%s', '%s', '%s')", $comment['cid'], $comment['nid'], $comment['pid'], $comment['uid'], $comment['subject'], $comment['comment'], $comment['format'], $_SERVER['REMOTE_ADDR'], $comment['timestamp'], $comment['status'], $score, $users, $thread, $comment['name'], $comment['mail'], $comment['homepage']);
-
-    _comment_update_node_statistics($comment['nid']);
-
-    // Tell the other modules a new comment has been submitted.
-    comment_invoke_comment($comment, 'insert');
-  }
-
-  if ($clear_cache) {
-    // Clear cache so anonymous users can see the new post.
-    cache_clear_all();
+    // Load the original user back in.
+    $user = $original_user;
+    session_save_session(TRUE);
   }
 }
 
