Index: mollom.admin.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/mollom/mollom.admin.inc,v
retrieving revision 1.1.2.33
diff -u -p -r1.1.2.33 mollom.admin.inc
--- mollom.admin.inc	11 Aug 2010 00:47:10 -0000	1.1.2.33
+++ mollom.admin.inc	1 Sep 2010 21:07:18 -0000
@@ -140,6 +140,22 @@ function mollom_admin_configure_form(&$f
       );
 
       if (!empty($mollom_form['elements'])) {
+        // By default, Mollom module rejects all posts that did not successfully
+        // pass mollom.checkContent. Instead of rejecting posts, site admins
+        // can optionally configure that posts shall be unpublished instead, so
+        // they can go through manual moderation. The negated form widget may
+        // look odd, but users can rather relate to more common terms like
+        // "unpublish" or "moderation". Furthermore, by default, Mollom should
+        // reject bad posts, so this setting should stay optional and disabled
+        // by default.
+        $form['mollom']['reject'] = array(
+          '#type' => 'checkbox',
+          '#title' => t('Unpublish bad posts for manual moderation instead of rejecting them'),
+          '#default_value' => !$mollom_form['reject'],
+          // Only possible for forms supporting moderation of unpublished posts.
+          '#access' => !empty($mollom_form['reject callback']),
+        );
+
         // Add the text analysis protection mode as first, suggested option.
         $form['mollom']['mode']['#options'] = array(
           MOLLOM_MODE_ANALYSIS => t('Text analysis and CAPTCHA backup'),
@@ -236,10 +252,9 @@ function mollom_admin_configure_form_sub
   $mollom_form = $form_state['values']['mollom'];
   // Merge in form information from $form_state.
   $mollom_form += $form_state['storage']['mollom_form'];
-  // Update form information in $form_state for potential rebuilds.
-  $form_state['storage']['mollom_form'] = $mollom_form;
 
   // Prepare selected fields for storage.
+  $mollom_form['reject'] = !$mollom_form['reject'];
   $enabled_fields = array();
   foreach (array_keys(array_filter($mollom_form['enabled_fields'])) as $field) {
     $enabled_fields[] = rawurldecode($field);
Index: mollom.api.php
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/mollom/mollom.api.php,v
retrieving revision 1.1.2.6
diff -u -p -r1.1.2.6 mollom.api.php
--- mollom.api.php	18 Aug 2010 00:43:27 -0000	1.1.2.6
+++ mollom.api.php	1 Sep 2010 21:52:06 -0000
@@ -153,6 +153,9 @@
  *         'entity' => 'im',
  *         // Optional: User permission list to skip Mollom's protection for.
  *         'bypass access' => array('administer instant messages'),
+ *         // Optional: Function to invoke to unpublish a bad form submission
+ *         // instead of rejecting it.
+ *         'reject callback' => 'im_mollom_reject',
  *         // Optional: To allow textual analysis of the form values, the form
  *         // elements needs to be registered individually. The keys are the
  *         // field keys in $form_state['values']. Sub-keys are noted using "]["
@@ -208,6 +211,16 @@
  * Additionally, the "post_id" data property always needs to be mapped to a form
  * element that holds the entity id.
  *
+ * When registering a 'reject callback', then the registered function needs to
+ * be available when the form is validated, and it is responsible for changing
+ * the submitted form values in a way that results in an unpublished post ending
+ * up in a moderation queue:
+ * @code
+ * function im_mollom_reject(&$form, &$form_state) {
+ *   $form_state['values']['status'] = 0;
+ * }
+ * @endcode
+ *
  * @see mollom_node
  * @see mollom_comment
  * @see mollom_user
@@ -275,6 +288,10 @@ function hook_mollom_form_list() {
  *     current user to determine whether to protect the form with Mollom or do
  *     not validate submitted form values. If the current user has at least one
  *     of the listed permissions, the form will not be protected.
+ *   - reject callback: (optional) A function name to invoke when a form
+ *     submission would normally be rejected. This allows modules to put such
+ *     posts into a moderation queue (i.e., accept but not publish them) by
+ *     altering the $form or $form_state information being passed by reference.
  *   - mail ids: (optional) An array of mail IDs that will be sent as a result
  *     of this form being submitted. When these mails are sent, a 'report to
  *     Mollom' link will be included at the bottom of the mail body. Be sure to
Index: mollom.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/mollom/mollom.install,v
retrieving revision 1.2.2.31
diff -u -p -r1.2.2.31 mollom.install
--- mollom.install	6 Apr 2010 22:17:51 -0000	1.2.2.31
+++ mollom.install	1 Sep 2010 21:47:48 -0000
@@ -118,6 +118,13 @@ function mollom_schema() {
         'not null' => TRUE,
         'default' => 0,
       ),
+      'reject' => array(
+        'description' => 'Whether to reject (1) or unpublish (0) bad form submissions.',
+        'type' => 'int',
+        'size' => 'tiny',
+        'not null' => TRUE,
+        'default' => 1,
+      ),
       'enabled_fields' => array(
         'description' => 'A list of form elements configured for textual analysis.',
         'type' => 'text',
@@ -382,3 +389,19 @@ function mollom_update_6112() {
   }
   return $ret;
 }
+
+/**
+ * Add {mollom_form}.reject column to form configuration.
+ */
+function mollom_update_6113() {
+  $ret = array();
+  if (!db_column_exists('mollom_form', 'reject')) {
+    db_add_field($ret, 'mollom_form', 'reject', array(
+      'type' => 'int',
+      'size' => 'tiny',
+      'not null' => TRUE,
+      'default' => 1,
+    ));
+  }
+  return $ret;
+}
Index: mollom.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/mollom/mollom.module,v
retrieving revision 1.2.2.156
diff -u -p -r1.2.2.156 mollom.module
--- mollom.module	7 Aug 2010 02:49:44 -0000	1.2.2.156
+++ mollom.module	1 Sep 2010 22:12:14 -0000
@@ -400,7 +400,6 @@ function mollom_data_load($entity, $id) 
  * - session: The session ID returned by the Mollom server.
  * - quality: A quality rating assigned to the content to tell whether or not
  *   it's spam.
- * - reputation: The reputation of the author.
  * - languages: An array containing language codes the content might be
  *   written in.
  *
@@ -414,14 +413,14 @@ function mollom_data_load($entity, $id) 
  * @param $id
  *   The entity id the data belongs to.
  *
- * @todo Remove usage of global $mollom_response variable.
+ * @todo Remove usage of global $mollom variable.
  */
 function mollom_data_save($entity, $id) {
   // Nothing to do, if we do not have a valid Mollom response.
-  if (!isset($GLOBALS['mollom_response']['session_id'])) {
+  if (empty($GLOBALS['mollom']['response']['session_id'])) {
     return FALSE;
   }
-  $data = $GLOBALS['mollom_response'];
+  $data = $GLOBALS['mollom']['response'];
   $data['session'] = $data['session_id'];
   $data['entity'] = $entity;
   $data['did'] = $id;
@@ -632,6 +631,7 @@ function mollom_form_alter(&$form, &$for
       // Add Mollom form validation handlers.
       $form['#validate'][] = 'mollom_validate_analysis';
       $form['#validate'][] = 'mollom_validate_captcha';
+      $form['#validate'][] = 'mollom_validate_post';
 
       // Add a submit handler to remove form state storage.
       $form['#submit'][] = 'mollom_form_submit';
@@ -779,6 +779,7 @@ function mollom_form_info($form_id, $mod
     'entity' => NULL,
     'title' => $form_id,
     'mode' => NULL,
+    'reject' => TRUE,
     'bypass access' => array(),
     'elements' => array(),
     'mapping' => array(),
@@ -1226,18 +1227,22 @@ function mollom_process_mollom($element,
   // Setup initial Mollom session and form information.
   if (empty($form_state['mollom'])) {
     $form_state['mollom'] = array(
-      'form_id' => $element['#mollom_form']['form_id'],
       'require_analysis' => $element['#mollom_form']['mode'] == MOLLOM_MODE_ANALYSIS,
       'require_captcha' => $element['#mollom_form']['mode'] == MOLLOM_MODE_CAPTCHA,
       'passed_captcha' => FALSE,
-      'entity' => $element['#mollom_form']['entity'],
-      'enabled_fields' => $element['#mollom_form']['enabled_fields'],
-      'mapping' => $element['#mollom_form']['mapping'],
       'response' => array(
         'session_id' => '',
       ),
     );
   }
+  $form_state['mollom'] += $element['#mollom_form'];
+
+  // By default, bad form submissions are rejected, unless the form was
+  // configured to unpublish bad posts. 'reject' may only be FALSE, if there is
+  // a valid 'reject callback'. Otherwise, it must be TRUE.
+  if (empty($form_state['mollom']['reject callback']) || !function_exists($form_state['mollom']['reject callback'])) {
+    $form_state['mollom']['reject'] = TRUE;
+  }
 
   // Add the Mollom session element.
   $element['session_id'] = array(
@@ -1265,6 +1270,10 @@ function mollom_process_mollom($element,
   // $form_state['mollom'] will be carried over to the element for rendering.
   $element['#mollom'] = &$form_state['mollom'];
 
+  // Make Mollom form and session information available to entirely different
+  // functions.
+  $GLOBALS['mollom'] = &$form_state['mollom'];
+
   return $element;
 }
 
@@ -1343,26 +1352,25 @@ function mollom_validate_analysis(&$form
   }
   // Store the response returned by Mollom.
   $form_state['mollom']['response'] = $result;
-  // @todo Only used for Contact module and mail integration in general.
-  $GLOBALS['mollom_response'] = $form_state['mollom']['response'];
-  $GLOBALS['mollom_form'] = $form['mollom']['#mollom_form'];
 
   switch ($result['spam']) {
     case MOLLOM_ANALYSIS_HAM:
+      $form_state['mollom']['require_captcha'] = FALSE;
       watchdog('mollom', 'Ham: <pre>@message</pre>Result: <pre>@result</pre>', array('@message' => print_r($data, TRUE), '@result' => print_r($result, TRUE)));
       break;
 
     case MOLLOM_ANALYSIS_SPAM:
-      form_set_error('mollom', t('Your submission has triggered the spam filter and will not be accepted.'));
+      $form_state['mollom']['require_captcha'] = FALSE;
+      if ($form_state['mollom']['reject']) {
+        form_set_error('mollom', t('Your submission has triggered the spam filter and will not be accepted.'));
+      }
       watchdog('mollom', 'Spam: <pre>@message</pre>Result: <pre>@result</pre>', array('@message' => print_r($data, TRUE), '@result' => print_r($result, TRUE)));
       break;
 
-    default:
-      // Fall back to a CAPTCHA.
-      form_set_error('mollom', t("To complete this form, please complete the word verification below."));
-      watchdog('mollom', 'Unsure: <pre>@message</pre>Result: <pre>@result</pre>', array('@message' => print_r($data, TRUE), '@result' => print_r($result, TRUE)));
-
+    case MOLLOM_ANALYSIS_UNSURE:
       $form_state['mollom']['require_captcha'] = TRUE;
+      form_set_error('mollom][captcha', t('To complete this form, please complete the word verification below.'));
+      watchdog('mollom', 'Unsure: <pre>@message</pre>Result: <pre>@result</pre>', array('@message' => print_r($data, TRUE), '@result' => print_r($result, TRUE)));
       break;
   }
 }
@@ -1378,11 +1386,10 @@ function mollom_validate_captcha(&$form,
   // When re-validating a form that already passed a CAPTCHA in a previous
   // request, we need to re-populate our global variable for mollom_data_save().
   if ($form_state['mollom']['passed_captcha']) {
-    $GLOBALS['mollom_response'] = $form_state['mollom']['response'];
     return;
   }
 
-  // Bail out if no value was provided.
+  // Nothing to validate if there is no value.
   if (empty($form_state['values']['mollom']['captcha'])) {
     form_set_error('mollom][captcha', t('The word verification field is required.'));
     return;
@@ -1400,9 +1407,7 @@ function mollom_validate_captcha(&$form,
     'author_id' => isset($data['author_id']) ? $data['author_id'] : NULL,
   ));
   // Store the response for #submit handlers.
-  $form_state['mollom']['response']['spam'] = (int) $result;
-  $GLOBALS['mollom_response'] = $form_state['mollom']['response'];
-  $GLOBALS['mollom_form'] = $form['mollom']['#mollom_form'];
+  $form_state['mollom']['response']['captcha'] = $result;
 
   // Explictly check for TRUE, since mollom.checkCaptcha() can also return an
   // error message (e.g. expired or invalid session_id).
@@ -1418,6 +1423,26 @@ function mollom_validate_captcha(&$form,
 }
 
 /**
+ * Form validation handler to perform post-validation tasks.
+ *
+ * Since our individual form validation handlers are not re-run after positive
+ * validation, any changes applied to form values will not persist across
+ * multiple form submission attempts and rebuilds.
+ */
+function mollom_validate_post(&$form, &$form_state) {
+  // Unpublish a post instead of rejecting it. If 'reject' is not TRUE, then
+  // the 'reject callback' is responsible for altering $form_state in a way that
+  // the post ends up unpublished in a moderation queue. Most callbacks will
+  // only want to set a value in $form_state. Technically, modules do not need
+  // to implement a 'reject callback' to achieve this, they may simply add a
+  // custom form validation handler (or use an existing one).
+  if (!$form_state['mollom']['reject']) {
+    $function = $form_state['mollom']['reject callback'];
+    $function($form, $form_state);
+  }
+}
+
+/**
  * Form element #pre_render callback for CAPTCHA element.
  *
  * Conditionally alters the #type of the CAPTCHA form element into a 'hidden'
@@ -1841,7 +1866,7 @@ function mollom_mail_alter(&$message) {
   // submitted form's hook_mollom_form_info(). This should ensure that the
   // report link is added to mails sent by actual users and not any mails sent
   // by Drupal since they should never be reported as spam.
-  if (!empty($GLOBALS['mollom_form']['mail ids']) && in_array($message['id'], $GLOBALS['mollom_form']['mail ids'])) {
+  if (!empty($GLOBALS['mollom']['mail ids']) && in_array($message['id'], $GLOBALS['mollom']['mail ids'])) {
     mollom_mail_add_report_link($message);
   }
 }
@@ -1850,10 +1875,10 @@ function mollom_mail_alter(&$message) {
  * Add the 'Report as innapropriate' link to an e-mail message.
  */
 function mollom_mail_add_report_link(&$message) {
-  if (!empty($GLOBALS['mollom_response']['session_id'])) {
-    mollom_data_save('session', $GLOBALS['mollom_response']['session_id']);
+  if (!empty($GLOBALS['mollom']['response']['session_id'])) {
+    mollom_data_save('session', $GLOBALS['mollom']['response']['session_id']);
     $report_link = t('Report as inappropriate: @link', array(
-      '@link' => url('mollom/report/session/' . $GLOBALS['mollom_response']['session_id'], array('absolute' => TRUE)),
+      '@link' => url('mollom/report/session/' . $GLOBALS['mollom']['response']['session_id'], array('absolute' => TRUE)),
     ));
     // Until D7, hook_mail_alter() accepts both arrays and strings.
     if (is_array($message['body'])) {
@@ -1902,6 +1927,7 @@ function node_mollom_form_info($form_id)
     'bypass access' => array('administer nodes', 'edit any ' . $type->type . ' content'),
     'entity' => 'node',
     'bundle' => $type->type,
+    'reject callback' => 'node_mollom_reject',
     'elements' => array(),
     'mapping' => array(
       'post_id' => 'nid',
@@ -1944,6 +1970,13 @@ function mollom_nodeapi($node, $op) {
 }
 
 /**
+ * Mollom reject callback.
+ */
+function node_mollom_reject(&$form, &$form_state) {
+  $form_state['values']['status'] = 0;
+}
+
+/**
  * Implements hook_form_FORMID_alter().
  *
  * Hook into the mass comment administration page and add some operations to
@@ -2004,6 +2037,7 @@ function comment_mollom_form_info($form_
     'mode' => MOLLOM_MODE_ANALYSIS,
     'bypass access' => array('administer comments'),
     'entity' => 'comment',
+    'reject callback' => 'comment_mollom_reject',
     'elements' => array(
       'subject' => t('Subject'),
       'comment' => t('Comment'),
@@ -2081,6 +2115,13 @@ function mollom_comment($comment, $op) {
 }
 
 /**
+ * Mollom reject callback.
+ */
+function comment_mollom_reject(&$form, &$form_state) {
+  $form_state['values']['status'] = COMMENT_NOT_PUBLISHED;
+}
+
+/**
  * Implements hook_form_FORMID_alter().
  *
  * Hook into the mass comment administration page and add some operations to
@@ -2146,6 +2187,7 @@ function user_mollom_form_info($form_id)
         'title' => t('User registration form'),
         'mode' => MOLLOM_MODE_CAPTCHA,
         'bypass access' => array('administer users'),
+        'reject callback' => 'user_mollom_reject',
         'entity' => 'user',
         'mapping' => array(
           'post_id' => 'uid',
@@ -2160,6 +2202,7 @@ function user_mollom_form_info($form_id)
         'title' => t('User password request form'),
         'mode' => MOLLOM_MODE_CAPTCHA,
         'bypass access' => array('administer users'),
+        'reject callback' => 'user_mollom_reject',
         'entity' => 'user',
         'mapping' => array(
           'post_id' => 'uid',
@@ -2173,6 +2216,13 @@ function user_mollom_form_info($form_id)
 }
 
 /**
+ * Mollom reject callback.
+ */
+function user_mollom_reject(&$form, &$form_state) {
+  $form_state['values']['status'] = 0;
+}
+
+/**
  * @} End of "name mollom_user".
  */
 
