Index: mollom.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/mollom/mollom.module,v
retrieving revision 1.6
diff -u -p -r1.6 mollom.module
--- mollom.module	23 Dec 2009 08:57:03 -0000	1.6
+++ mollom.module	23 Dec 2009 22:21:42 -0000
@@ -124,6 +124,11 @@ function mollom_help($path, $arg) {
   }
 }
 
+function mollom_init() {
+  // Re-route Mollom communication to this testing site.
+  variable_set('mollom_servers', array($GLOBALS['base_url'] . '/xmlrpc.php?version='));
+}
+
 /**
  * Implements hook_exit().
  */
@@ -384,12 +389,12 @@ function mollom_data_delete($entity, $id
  */
 function mollom_form_alter(&$form, &$form_state, $form_id) {
   // Site administrators don't have their content checked with Mollom.
-  if (!user_access('post with no checking')) {
+  if (1 || !user_access('post with no checking')) {
     // Retrieve configuration for this form.
     if ($mollom_form = mollom_form_load($form_id)) {
       // Determine whether to bypass validation for the current user.
       foreach ($mollom_form['bypass access'] as $permission) {
-        if (user_access($permission)) {
+        if (0 && user_access($permission)) {
           return;
         }
       }
@@ -748,50 +753,21 @@ function _mollom_fallback() {
  * @{
  * Various helper functions to work around bugs in Form API.
  *
- * Normally, Mollom's integration with Form API would be quite simple:
+ * Mollom's integration with Form API is quite simple:
  * - If a form is protected by Mollom, we setup initial information
- *   about the session and the form in $form_state['storage'], bound to the
- *   'form_build_id'.
- * - We enable form caching via $form_state['cache'], so our information in the
- *   form storage is cached. Form API then automatically ensures a proper
- *   'form_build_id' for every form and every user.
+ *   about the session and the form in $form_state['mollom'].
  * - We mainly work in and after form validation. Textual analysis validates all
  *   values in the form as a form validation handler. If this validation fails,
  *   we alter the form (during validation) to add a CAPTCHA. If the CAPTCHA
  *   response is invalid, we still alter the form during validation to display a
  *   new CAPTCHA, but without the previously entered value.
- * - In short, roughly:
+ * - Form API keeps our $form_state information, because we short-cut form
+ *   rebuilding until the submitted form values are valid.
+ * - In short, very roughly:
  *   - Form construction: Nothing.
  *   - Form processing: Nothing.
  *   - Form validation: Perform validation and alterations based on validation.
  *
- * This, however, is not possible due to various bugs in Drupal core.
- * - Form caching cannot be enabled for certain forms, because they contain
- *   processing and validation logic.
- *   http://drupal.org/node/644222
- * - $form_state['storage'] is not updated after form processing and validation.
- *   http://drupal.org/node/644150
- * - Form validation handlers cannot alter the form structure.
- *   http://drupal.org/node/642702
- *
- * Hence, something that could be done in one simple function becomes quite a
- * nightmare:
- * - We need our own {cache_mollom} table as replacement for native form
- *   caching, as well as our own logic to validate a submitted 'session_id'
- *   ('form_build_id') against forms and users.
- * - We need to perform form alterations during form rendering, where
- *   $form_state is no longer available. To make this possible, we leverage the
- *   fact that an element property that is a reference to a key in $form_state
- *   (which in itself is passed by reference) persists on to the rendering
- *   layer. The essential part is:
- *   @code
- *     $element['#mollom'] = &$form_state['mollom'];
- *   @endcode
- * - Since we cannot alter elements in the form structure during form
- *   validation, this reference already needs to be set up during form
- *   processing (in a #process callback), while everything else lives in form
- *   validation handlers (unless it needs to add or alter the form structure).
- *
  * @see mollom_form_alter()
  */
 
@@ -802,43 +778,18 @@ function mollom_element_info() {
   return array(
     'mollom' => array(
       '#process' => array(
-        // Try to fetch a Mollom session from cache during form processing/validation.
+        // Try to fetch a Mollom session from cache during form processing.
         'mollom_process_mollom_session_id',
         // Setup a new Mollom session.
         'mollom_process_mollom',
       ),
+      // @todo Remove.
       '#pre_render' => array('mollom_pre_render_mollom'),
     ),
   );
 }
 
 /**
- * Implements hook_theme().
- */
-function mollom_theme() {
-  return array(
-    'mollom' => array(
-      'render element' => 'element',
-    ),
-  );
-}
-
-/**
- * Format the Mollom form element.
- *
- * This works like #type 'markup' and is only required, because D6 only supports
- * #process callbacks on elements with #input = TRUE.
- * @see form_builder()
- * @see _form_builder_handle_input_element()
- *
- * @todo Remove in D7; also #input from mollom_elements().
- */
-function theme_mollom($variables) {
-  $element = $variables['element'];
-  return isset($element['#children']) ? $element['#children'] : '';
-}
-
-/**
  * Form element #process callback for the 'mollom' element.
  *
  * The 'mollom' form element is stateful. The Mollom session ID that is exchanged
@@ -854,7 +805,6 @@ function mollom_process_mollom($element,
   // Setup initial Mollom session and form information.
   if (empty($form_state['mollom'])) {
     $form_state['mollom'] = array(
-      'form_build_id' => $complete_form['#build_id'],
       'session_id' => NULL,
       'form_id' => $element['#mollom_form']['form_id'],
       'require_analysis' => $element['#mollom_form']['mode'] == MOLLOM_MODE_ANALYSIS,
@@ -881,6 +831,44 @@ function mollom_process_mollom($element,
     '#description' => t("Type the characters you see in the picture above; if you can't read them, submit the form and a new image will be generated. Not case sensitive."),
   );
 
+  // Request and inject a CAPTCHA when required; but also in case validation
+  // through textual analysis failed.
+  if ($form_state['mollom']['require_captcha'] && !$form_state['mollom']['passed_captcha']) {
+    $element['captcha']['#required'] = TRUE;
+
+    $data['author_ip'] = ip_address();
+    if (!empty($element['#mollom']['session_id'])) {
+      $data['session_id'] = $element['#mollom']['session_id'];
+    }
+    $result = mollom('mollom.getImageCaptcha', $data);
+
+    // If we get a response, add the image CAPTCHA to the form element.
+    if (isset($result['session_id']) && isset($result['url'])) {
+      $captcha = '<a href="http://mollom.com" class="mollom-captcha">';
+      $captcha .= '<img src="' . url($result['url']) . '" alt="Mollom CAPTCHA" />';
+      // @todo This suffix needs to be injected via JavaScript.
+      $captcha .= '</a> (<a href="#" class="mollom-audio-captcha">' . t('play audio CAPTCHA') . '</a>)';
+      $element['captcha']['#field_prefix'] = $captcha;
+
+      // Assign the session ID returned by Mollom.
+      $form_state['mollom']['session_id'] = $result['session_id'];
+      // If we received a Mollom session id via textual analysis or a CAPTCHA
+      // request, inject it to the form.
+      $timestamp = REQUEST_TIME;
+      $element['session_id']['#value'] = $timestamp . '-' . $result['session_id'];
+
+      // Store the Mollom session id in the user session to force a
+      // session for anonymous users in Drupal 7 and Drupal 6 Pressflow.
+      // @see mollom_exit()
+      // @see mollom_form_submit()
+      $_SESSION['mollom_sessions'][$result['session_id']] = $timestamp;
+    }
+  }
+  // If no CAPTCHA is required or the response was correct, hide the CAPTCHA.
+  elseif (!$form_state['mollom']['require_captcha'] || $form_state['mollom']['passed_captcha']) {
+    $element['captcha']['#access'] = FALSE;
+  }
+
   // Add the JavaScript to switch to an audio CAPTCHA.
   drupal_add_js(drupal_get_path('module', 'mollom') . '/mollom.js');
   drupal_add_css(drupal_get_path('module', 'mollom') . '/mollom.css');
@@ -896,16 +884,6 @@ function mollom_process_mollom($element,
 
 /**
  * Form element #process callback for Mollom's form storage handling.
- *
- * Albeit this *should* be an #element_validate handler that is only executed
- * during form validation, we must use a #process callback, because
- * mollom_process_mollom() needs to copy over $form_state['mollom'] into
- * $element['#mollom'], and as of now, Form API does not allow form validation
- * handlers to alter any elements in the form structure by reference.
- * @see http://drupal.org/node/642702
- *
- * @todo Investigate usage of $form_state['storage']['mollom'], but watch out
- *   for broken form state caching.
  */
 function mollom_process_mollom_session_id($element, &$form_state) {
   // The current state can come either from the $form_state, if the form
@@ -945,11 +923,12 @@ function mollom_process_mollom_session_i
  * the form). In case Mollom's textual analysis returns no definite result, we
  * must fall back to a CAPTCHA.
  */
-function mollom_validate_analysis($form, &$form_state) {
+function mollom_validate_analysis(&$form, &$form_state) {
   if (!$form_state['mollom']['require_analysis'] || $form_state['mollom']['require_captcha']) {
     return;
   }
 
+  // Perform textual analysis.
   $data = mollom_form_get_values($form_state['values'], $form_state['mollom']['fields'], $form_state['mollom']['mapping']);
   if (!empty($form_state['mollom']['session_id'])) {
     $data += array('session_id' => $form_state['mollom']['session_id']);
@@ -963,6 +942,17 @@ function mollom_validate_analysis($form,
 
   // Assign the session ID returned by Mollom.
   $form_state['mollom']['session_id'] = $result['session_id'];
+  // If we received a Mollom session id via textual analysis, inject it to the
+  // form.
+  $timestamp = REQUEST_TIME;
+  $form['mollom']['session_id']['#value'] = $timestamp . '-' . $result['session_id'];
+
+  // Store the Mollom session id in the user session to force a
+  // session for anonymous users in Drupal 7 and Drupal 6 Pressflow.
+  // @see mollom_exit()
+  // @see mollom_form_submit()
+  $_SESSION['mollom_sessions'][$result['session_id']] = $timestamp;
+
   // Store the response for #submit handlers.
   $GLOBALS['mollom_response'] = $result;
 
@@ -978,10 +968,27 @@ function mollom_validate_analysis($form,
 
     default:
       // Fall back to a CAPTCHA.
-      $form_state['mollom']['require_captcha'] = TRUE;
-
       form_set_error('mollom', t("We're sorry, but to submit this post, please complete the word verification below."));
       watchdog('mollom', 'Unsure: %message<br />Session: %session', array('%message' => $data['post_body'], '%session' => $result['session_id']));
+
+      $form_state['mollom']['require_captcha'] = TRUE;
+      $form['mollom']['captcha']['#access'] = TRUE;
+      $form['mollom']['captcha']['#required'] = TRUE;
+
+      $captcha_data = array(
+        'author_ip' => $data['author_ip'],
+        'session_id' => $result['session_id'],
+      );
+      $result = mollom('mollom.getImageCaptcha', $captcha_data);
+
+      // If we get a response, add the image CAPTCHA to the form element.
+      if (isset($result['url'])) {
+        $captcha = '<a href="http://mollom.com" class="mollom-captcha">';
+        $captcha .= '<img src="' . url($result['url']) . '" alt="Mollom CAPTCHA" />';
+        // @todo This suffix needs to be injected via JavaScript.
+        $captcha .= '</a> (<a href="#" class="mollom-audio-captcha">' . t('play audio CAPTCHA') . '</a>)';
+        $form['mollom']['captcha']['#field_prefix'] = $captcha;
+      }
       break;
   }
 }
@@ -991,9 +998,13 @@ function mollom_validate_analysis($form,
  */
 function mollom_validate_captcha(&$form, &$form_state) {
   if (!$form_state['mollom']['require_captcha'] || $form_state['mollom']['passed_captcha']) {
+    $form['mollom']['captcha']['#access'] = FALSE;
     return;
   }
 
+  // Prevent the page cache from storing a form containing a CAPTCHA element.
+  $GLOBALS['conf']['cache'] = CACHE_DISABLED;
+
   // @todo Problem: This error is output also in case we have no valid keys or a
   //   communication error, in this request or perhaps even in the last already.
   //   Thus, in case the fallback method blocks all submissions, a user gets TWO
@@ -1028,10 +1039,14 @@ function mollom_validate_captcha(&$form,
   // error message (e.g. expired or invalid session_id).
   if ($result === TRUE) {
     $form_state['mollom']['passed_captcha'] = TRUE;
+    $form['mollom']['captcha']['#access'] = FALSE;
 
     watchdog('mollom', 'The CAPTCHA response was correct. Form data: %data', array('%data' => print_r($form_state['values'], TRUE)));
   }
   else {
+    // Empty the CAPTCHA field value, since the user has to re-enter a new one.
+    $form['mollom']['captcha']['#value'] = '';
+
     form_set_error('mollom][captcha', t('The CAPTCHA was not completed correctly. Please complete this new CAPTCHA and try again.'));
     watchdog('mollom', 'The CAPTCHA response was incorrect. Form data: %data', array('%data' => print_r($form_state['values'], TRUE)));
   }
@@ -1040,95 +1055,9 @@ function mollom_validate_captcha(&$form,
 /**
  * Form element #pre_render callback for CAPTCHA element.
  *
- * Conditionally alters the #type of the CAPTCHA form element into a 'hidden'
- * element if the response was correct. If it was not, then we empty the value
- * of the textfield to allow the user to re-enter a new one.
- *
- * This #pre_render trick is required, because form API validation does not
- * allow form validation handlers to alter the actual form structure. Both the
- * form constructor function and the #process callback for the 'mollom' element
- * are therefore executed too early (before form validation), so the CAPTCHA
- * element still contains not yet validated (default) values.
- * We also cannot invoke a form validation handler during form construction or
- * processing, because mollom_form_get_values() would be invoked too early
- * and therefore $form_state['values'] would not contain any additions from
- * form validation functions like mollom_comment_form_validate().
- * @see http://drupal.org/node/642702
+ * @todo Kill this entirely.
  */
 function mollom_pre_render_mollom($element) {
-  // Prevent the page cache from storing a form containing a CAPTCHA element.
-  if ($element['#mollom']['require_captcha']) {
-    $GLOBALS['conf']['cache'] = CACHE_DISABLED;
-  }
-
-  // Request and inject a CAPTCHA when required; but also in case validation
-  // through textual analysis failed.
-  if ($element['#mollom']['require_captcha'] && !$element['#mollom']['passed_captcha']) {
-    // Request a CAPTCHA; always default to an image CAPTCHA.
-    $data['author_ip'] = ip_address();
-    if (!empty($element['#mollom']['session_id'])) {
-      $data['session_id'] = $element['#mollom']['session_id'];
-    }
-    // @todo D7: A horrible effect of the new rendering system: If we cannot
-    //   reach Mollom servers, then mollom() will invoke _mollom_fallback(),
-    //   which outputs a message. However, this is #pre_render. The main page
-    //   content is render()'ed *within* page.tpl.php. Which is also where
-    //   $messages are rendered. But $messages are rendered before $content!
-    //   I already briefly discussed this chicken-n-egg problem with moshe, but
-    //   as of now, we have no ideas. 19/12/2009 sun
-    $result = mollom('mollom.getImageCaptcha', $data);
-
-    // If we get a response, add the image CAPTCHA to the form element.
-    if (isset($result['session_id']) && isset($result['url'])) {
-      $captcha = '<a href="http://mollom.com" class="mollom-captcha">';
-      $captcha .= '<img src="' . url($result['url']) . '" alt="Mollom CAPTCHA" />';
-      // @todo This suffix needs to be injected via JavaScript.
-      $captcha .= '</a> (<a href="#" class="mollom-audio-captcha">' . t('play audio CAPTCHA') . '</a>)';
-      $element['captcha']['#field_prefix'] = $captcha;
-
-      // Assign the session ID returned by Mollom.
-      $element['#mollom']['session_id'] = $result['session_id'];
-    }
-    // Otherwise, hide the CAPTCHA.
-    else {
-      $element['captcha']['#type'] = 'hidden';
-      unset($element['captcha']['#theme'], $element['captcha']['#theme_wrappers']);
-    }
-  }
-
-  // If we received a Mollom session id via textual analysis or a CAPTCHA
-  // request, inject it to the form.
-  $timestamp = REQUEST_TIME;
-  if (!empty($element['#mollom']['session_id'])) {
-    $element['session_id']['#value'] = $timestamp . '-' . $element['#mollom']['session_id'];
-
-    // Store the Mollom session id in the user session to force a
-    // session for anonymous users in Drupal 7 and Drupal 6 Pressflow.
-    // @see mollom_init()
-    // @see mollom_form_submit()
-    $_SESSION['mollom_sessions'][$element['#mollom']['session_id']] = $timestamp;
-  }
-
-  // If no CAPTCHA is required or the response was correct, hide the CAPTCHA.
-  if (!$element['#mollom']['require_captcha'] || $element['#mollom']['passed_captcha']) {
-    $element['captcha']['#type'] = 'hidden';
-    unset($element['captcha']['#theme'], $element['captcha']['#theme_wrappers']);
-  }
-  // Otherwise, empty the value of the CAPTCHA, since the user has to re-enter
-  // a new one.
-  else {
-    $element['captcha']['#value'] = '';
-    $element['captcha']['#required'] = $element['#mollom']['require_captcha'];
-  }
-
-  // Due to a bug in Drupal core, we need to manually update Mollom session
-  // information in $form_state.
-  // @see http://drupal.org/node/644150
-  // And due to yet even more bugs in Drupal core form constructor functions,
-  // some forms (such as comment_form()) cannot be cached, since they contain
-  // processing/validation logic. We therefore cannot use $form_state
-  // along with form caching. >:-/
-  // @see http://drupal.org/node/644222
   if (!empty($element['#mollom']['session_id'])) {
     cache_set($element['#mollom']['session_id'], $element['#mollom'], 'cache_mollom', REQUEST_TIME + 21600);
   }
@@ -1677,7 +1606,7 @@ function mollom_mail_alter(&$message) {
 function _mollom_debug($message) {
   // print $message .'<br />';
   // error_log($message);
-  $message = "-----\n$message\n";
-  file_put_contents('/tmp/mollom-debug.txt', $message, FILE_APPEND);
+  #$message = "-----\n$message\n";
+  #file_put_contents('/tmp/mollom-debug.txt', $message, FILE_APPEND);
 }
 
Index: tests/mollom_test.info
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/mollom/tests/mollom_test.info,v
retrieving revision 1.3
diff -u -p -r1.3 mollom_test.info
--- tests/mollom_test.info	21 Dec 2009 20:24:22 -0000	1.3
+++ tests/mollom_test.info	23 Dec 2009 20:41:58 -0000
@@ -2,5 +2,5 @@
 name = Mollom Test
 description = Testing module for Mollom functionality.
 core = 7.x
-hidden = TRUE
+;hidden = TRUE
 files[] = mollom_test.module
