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	15 Aug 2010 11:51:12 -0000
@@ -138,6 +138,24 @@ function mollom_admin_configure_form(&$f
         ),
         '#default_value' => $mollom_form['mode'],
       );
+      // The negated form widget and value handling looks a bit weird, but users
+      // can rather relate to more common "unpublish" and "moderation" terms,
+      // and by default, Mollom should operate correctly and therefore also
+      // reject spam posts, so this setting should stay optional and disabled by
+      // default. The internally stored value for 'reject' is technically smart,
+      // since a value of 1 means to reject spam posts, and 0 means to unpublish
+      // posts; the value therefore represents "rejection" and a "publishing
+      // status" at the same time.
+      // @todo Is this checkbox limited to spam? (what about profanity?)
+      $form['mollom']['reject'] = array(
+        '#type' => 'checkbox',
+        '#title' => t('Unpublish spam posts instead of rejecting'),
+        '#default_value' => (int) !$mollom_form['reject'],
+      );
+      if (!isset($mollom_form['mapping']['post_status'])) {
+        $form['mollom']['reject']['#disabled'] = TRUE;
+        $form['mollom']['reject']['#description'] = t('This form does not support moderation of unpublished posts.');
+      }
 
       if (!empty($mollom_form['elements'])) {
         // Add the text analysis protection mode as first, suggested option.
Index: mollom.api.php
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/mollom/mollom.api.php,v
retrieving revision 1.1.2.5
diff -u -p -r1.1.2.5 mollom.api.php
--- mollom.api.php	5 Aug 2010 02:22:45 -0000	1.1.2.5
+++ mollom.api.php	12 Aug 2010 16:55:50 -0000
@@ -172,6 +172,8 @@
  *           // find the message id of the created, edited, or deleted message
  *           // in $form_state['values']['im']['id'].
  *           'post_id' => 'im][id',
+ *           // @todo
+ *           'post_status' => 'status',
  *           // Required if the form or entity contains a title-alike field:
  *           'post_title' => 'im][subject',
  *           // Optional: If our instant message form was accessible for
@@ -723,6 +725,7 @@ function hook_mollom_form_list() {
  *     The following mappings are possible:
  *     - post_id: The form element value that denotes the ID of the content
  *       stored in the database.
+ *     - post_status: @todo
  *     - post_title: The form element value that should be used as title.
  *     - post_body: Mollom automatically assigns this property based on all
  *       elements that have been selected for textual analysis in Mollom's
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	12 Aug 2010 16:06:11 -0000
@@ -118,6 +118,13 @@ function mollom_schema() {
         'not null' => TRUE,
         'default' => 0,
       ),
+      'reject' => array(
+        'description' => 'Whether to reject (1) or unpublish (0) spam 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	15 Aug 2010 12:04:06 -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.
  *
@@ -418,7 +417,7 @@ function mollom_data_load($entity, $id) 
  */
 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'];
@@ -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_status';
 
       // 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' => 1,
     'bypass access' => array(),
     'elements' => array(),
     'mapping' => array(),
@@ -949,6 +950,10 @@ function mollom_form_get_values($form_va
   if (!empty($mapping['post_id'])) {
     $data['post_id'] = $mapping['post_id'];
   }
+  // Post status; not sent to Mollom.
+  if (!empty($mapping['post_status'])) {
+    $data['post_status'] = $mapping['post_status'];
+  }
   // Post title.
   if (!empty($mapping['post_title'])) {
     $data['post_title'] = $mapping['post_title'];
@@ -1226,18 +1231,15 @@ 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'];
 
   // Add the Mollom session element.
   $element['session_id'] = array(
@@ -1265,6 +1267,12 @@ function mollom_process_mollom($element,
   // $form_state['mollom'] will be carried over to the element for rendering.
   $element['#mollom'] = &$form_state['mollom'];
 
+  // Another trick to make Mollom form and session information available to
+  // entirely different functions.
+  // @todo Rename 'mollom_form' into 'mollom', and remove 'mollom_response'.
+  $GLOBALS['mollom_response'] = &$form_state['mollom']['response'];
+  $GLOBALS['mollom_form'] = &$form_state['mollom'];
+
   return $element;
 }
 
@@ -1322,10 +1330,8 @@ function mollom_validate_analysis(&$form
 
   // Perform textual analysis.
   $data = mollom_form_get_values($form_state['values'], $form_state['mollom']['enabled_fields'], $form_state['mollom']['mapping']);
-  // Remove 'post_id' property; only used by mollom_form_submit().
-  if (isset($data['post_id'])) {
-    unset($data['post_id']);
-  }
+  // Remove non-XML-RPC properties.
+  unset($data['post_id'], $data['post_status']);
   if (!empty($form_state['mollom']['response']['session_id'])) {
     $data['session_id'] = $form_state['mollom']['response']['session_id'];
   }
@@ -1343,9 +1349,6 @@ 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:
@@ -1353,13 +1356,17 @@ function mollom_validate_analysis(&$form
       break;
 
     case MOLLOM_ANALYSIS_SPAM:
-      form_set_error('mollom', t('Your submission has triggered the spam filter and will not be accepted.'));
+      // By default, the submission is rejected, unless the form was configured
+      // to unpublish spam posts instead.
+      // @see mollom_validate_status()
+      if ($form_state['mollom']['reject'] && isset($form_state['mollom']['mapping']['post_status'])) {
+        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."));
+    case MOLLOM_ANALYSIS_UNSURE:
+      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)));
 
       $form_state['mollom']['require_captcha'] = TRUE;
@@ -1378,11 +1385,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 +1406,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 +1422,28 @@ function mollom_validate_captcha(&$form,
 }
 
 /**
+ * Form validation handler to perform post-validation tasks.
+ *
+ * Required, since our individual form validation handlers may not be re-run
+ * after positive validation. Thus, any changes applied to form values may not
+ * persist across multiple submission attempts/form rebuilds.
+ */
+function mollom_validate_status(&$form, &$form_state) {
+  if (isset($form_state['mollom']['response']['spam']) && $form_state['mollom']['response']['spam'] == MOLLOM_ANALYSIS_SPAM && !$form_state['mollom']['reject']) {
+    // When not rejecting, there must be a 'post_status' form value mapping.
+    $parents = explode('][', $form_state['mollom']['mapping']['post_status']);
+    $status = 0;
+    // Comment module is the only abnormal thing we make a hard-coded
+    // exception for, and really only just because it's insane.
+    // @todo Remove for D7, already fixed there.
+    if ($form_state['values']['form_id'] == 'comment_form') {
+      $status = COMMENT_NOT_PUBLISHED;
+    }
+    form_set_value(array('#parents' => $parents), $status, $form_state);
+  }
+}
+
+/**
  * Form element #pre_render callback for CAPTCHA element.
  *
  * Conditionally alters the #type of the CAPTCHA form element into a 'hidden'
@@ -1905,6 +1931,7 @@ function node_mollom_form_info($form_id)
     'elements' => array(),
     'mapping' => array(
       'post_id' => 'nid',
+      'post_status' => 'status',
       'author_name' => 'name',
     ),
   );
@@ -2010,6 +2037,7 @@ function comment_mollom_form_info($form_
     ),
     'mapping' => array(
       'post_id' => 'cid',
+      'post_status' => 'status',
       'post_title' => 'subject',
       // In D6, comment_form() dynamically uses different form elements for
       // anonymous users, authenticated users, and comment administrators.
@@ -2149,6 +2177,7 @@ function user_mollom_form_info($form_id)
         'entity' => 'user',
         'mapping' => array(
           'post_id' => 'uid',
+          'post_status' => 'status',
           'author_name' => 'name',
           'author_mail' => 'mail',
         ),
@@ -2163,6 +2192,7 @@ function user_mollom_form_info($form_id)
         'entity' => 'user',
         'mapping' => array(
           'post_id' => 'uid',
+          'post_status' => 'status',
           'author_name' => 'name',
           // The 'name' form element accepts either a username or mail address.
           'author_mail' => 'name',
