Index: mollom.module =================================================================== RCS file: /cvs/drupal/contributions/modules/mollom/mollom.module,v retrieving revision 1.2.2.137 diff -u -r1.2.2.137 mollom.module --- mollom.module 24 Mar 2010 16:27:51 -0000 1.2.2.137 +++ mollom.module 21 May 2010 00:10:39 -0000 @@ -47,14 +47,29 @@ define('MOLLOM_MODE_ANALYSIS', 2); /** - * XML-RPC communication failure fallback mode: Block all submissions of protected forms. + * Form processing trigger: XML-RPC communication failure. */ -define('MOLLOM_FALLBACK_BLOCK', 0); +define('MOLLOM_TRIGGER_COM_FAILURE', 1); /** - * XML-RPC communication failure fallback mode: Accept all submissions of protected forms. + * Form processing trigger: Text analysis response HAM. */ -define('MOLLOM_FALLBACK_ACCEPT', 1); +define('MOLLOM_TRIGGER_ANALYSIS_HAM', 2); + +/** + * Form processing trigger: Text analysis response SPAM. + */ +define('MOLLOM_TRIGGER_ANALYSIS_SPAM', 3); + +/** + * Form processing trigger: Text analysis response UNSURE. + */ +define('MOLLOM_TRIGGER_ANALYSIS_UNSURE', 4); + +/** + * Form processing trigger: Captcha completed correctly. + */ +define('MOLLOM_TRIGGER_PASSED_CAPTCHA', 5); /** * XML-RPC communication failure: No servers could be reached. @@ -734,6 +749,99 @@ } /** + * Returns information about the available actions for a given form. + * + * @param $form_id + * (optional) A form id to return actions for. If no form_id is supplied all + * 'global' actions will be returned (actions with no form limitations). + * @param $trigger + * (optional) A trigger to filter the list of actions by. + */ +function mollom_actions($form_id = NULL, $trigger = NULL) { + static $all_actions; + if (is_null($all_actions)) { + $action_info = module_invoke_all('mollom_action_info'); + drupal_alter('mollom_action_info', $action_info); + } + $actions = $all_actions; + foreach ($action_info as $key => $action) { + if (is_null($trigger) || !isset($action['triggers']) || in_array($trigger, $action['triggers'])) { + if (!isset($action['forms']) || empty($action['forms'])) { + $actions['global'][$key] = $action; + } + else { + foreach ($action['forms'] as $form) { + $actions['forms'][$form][$key] = $action; + } + } + } + } + if (is_null($form_id)) { + return $actions['global']; + } + else { + return $actions['global'] + (isset($actions['forms'][$form_id]) ? $actions['forms'][$form_id] : array()); + } +} + +/** + * Helper function to return default actions. + */ +function _mollom_default_action($trigger) { + switch ($trigger) { + case MOLLOM_TRIGGER_COM_FAILURE: + return 'mollom_block'; + + case MOLLOM_TRIGGER_ANALYSIS_HAM: + return 'mollom_continue'; + + case MOLLOM_TRIGGER_ANALYSIS_SPAM: + return 'mollom_reject'; + + case MOLLOM_TRIGGER_ANALYSIS_UNSURE: + return 'mollom_captcha'; + + case MOLLOM_TRIGGER_PASSED_CAPTCHA: + return 'mollom_continue'; + } +} + +/** + * Helper function to convert actions into form API options. + */ +function _mollom_actions_options($actions) { + $options = array(); + foreach ($actions as $key => $action) { + $options[$key] = $action['title']; + } + return $options; +} + +/** + * Returns an associative array of triggers. Used primarily for + * administrative interfaces. + */ +function mollom_triggers() { + return array( + MOLLOM_TRIGGER_COM_FAILURE => array( + 'title' => t('The Mollom servers are down or otherwise unreachable'), + ), + MOLLOM_TRIGGER_ANALYSIS_HAM => array( + 'title' => t('Text analysis response: HAM'), + ), + MOLLOM_TRIGGER_ANALYSIS_SPAM => array( + 'title' => t('Text analysis response: SPAM'), + ), + MOLLOM_TRIGGER_ANALYSIS_UNSURE => array( + 'title' => t('Text analysis response: UNSURE'), + ), + MOLLOM_TRIGGER_PASSED_CAPTCHA => array( + 'title' => t('Passed captcha'), + ), + ); +} + +/** * Menu argument loader; Loads Mollom configuration and form information for a given form id. */ function mollom_form_load($form_id) { @@ -750,6 +858,12 @@ 'title' => $form_id, 'elements' => array(), ); + + // Prepare actions array. + $mollom_form['actions'] = unserialize($mollom_form['actions']); + if (!is_array($mollom_form['actions'])) { + $mollom_form['actions'] = array(); + } } return $mollom_form; } @@ -1042,11 +1156,6 @@ * Helper function to log and optionally output an error message when Mollom servers are unavailable. */ function _mollom_fallback() { - $fallback = variable_get('mollom_fallback', MOLLOM_FALLBACK_BLOCK); - if ($fallback == MOLLOM_FALLBACK_BLOCK) { - form_set_error('mollom', t("The spam filter installed on this site is currently unavailable. Per site policy, we are unable to accept new submissions until that problem is resolved. Please try resubmitting the form in a couple of minutes.")); - } - $servers = variable_get('mollom_servers', array()); $variables = array( '%servers' => $servers ? implode(', ', $servers) : '--', @@ -1057,6 +1166,37 @@ } /** + * Helper function to trigger an action for a given form and form state. + * + * @param $trigger + * The trigger that is prompting the action. + * @param $form + * The full form array of the form being processed. + * @param &$form_state + * The current $form_state, passed by reference. + */ +function _mollom_trigger_action($trigger, $form, &$form_state) { + if (isset($form['mollom']['#mollom_form'])) { + // Get the action to perform. + $mollom_form = $form['mollom']['#mollom_form']; + $actions = mollom_actions($form_id); + if (!isset($mollom_form['actions'][$trigger])) { + $action = $actions[_mollom_default_action($trigger)]; + } + else { + $action = $actions[$mollom_form['actions'][$trigger]]; + } + // Perform the configured action. + if (isset($action['callback'])) { + $function = $action['callback']; + if (function_exists($function)) { + $function($form, $form_state); + } + } + } +} + +/** * @defgroup mollom_form_api Mollom Form API workarounds * @{ * Various helper functions to work around bugs in Form API. @@ -1273,10 +1413,12 @@ $data += array('session_id' => $form_state['mollom']['session_id']); } $result = mollom('mollom.checkContent', $data); + $result = NULL; - // Trigger global fallback behavior if there is no result. + // Trigger communications failure action if there is no result. if (!isset($result['session_id']) || !isset($result['spam'])) { - return _mollom_fallback(); + _mollom_trigger_action(MOLLOM_TRIGGER_COM_FAILURE, $form, $form_state); + return; } // Assign the session ID returned by Mollom. @@ -1289,19 +1431,18 @@ switch ($result['spam']) { case MOLLOM_ANALYSIS_HAM: watchdog('mollom', 'Ham:
@message
Result:
@result
', array('@message' => print_r($data, TRUE), '@result' => print_r($result, TRUE))); + _mollom_trigger_action(MOLLOM_TRIGGER_ANALYSIS_HAM, $form, $form_state); break; case MOLLOM_ANALYSIS_SPAM: - form_set_error('mollom', t('Your submission has triggered the spam filter and will not be accepted.')); watchdog('mollom', 'Spam:
@message
Result:
@result
', array('@message' => print_r($data, TRUE), '@result' => print_r($result, TRUE))); + _mollom_trigger_action(MOLLOM_TRIGGER_ANALYSIS_SPAM, $form, $form_state); break; + case MOLLOM_ANALYSIS_UNSURE: default: - // Fall back to a CAPTCHA. - form_set_error('mollom', t("To complete this form, please complete the word verification below.")); watchdog('mollom', 'Unsure:
@message
Result:
@result
', array('@message' => print_r($data, TRUE), '@result' => print_r($result, TRUE))); - - $form_state['mollom']['require_captcha'] = TRUE; + _mollom_trigger_action(MOLLOM_TRIGGER_ANALYSIS_UNSURE, $form, $form_state); break; } } @@ -1318,6 +1459,7 @@ // 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']; + _mollom_trigger_action(MOLLOM_TRIGGER_PASSED_CAPTCHA, $form, $form_state); return; } @@ -1345,6 +1487,7 @@ $form_state['mollom']['passed_captcha'] = TRUE; watchdog('mollom', 'Correct CAPTCHA:
@data
', array('@data' => print_r($form_state['values'], TRUE)));
+    _mollom_trigger_action(MOLLOM_TRIGGER_PASSED_CAPTCHA, $form, $form_state);
   }
   else {
     form_set_error('mollom][captcha', t('The CAPTCHA was not completed correctly. Please complete this new CAPTCHA and try again.'));
@@ -1603,14 +1746,6 @@
   }
 
   // If none of the servers worked, activate the fallback mechanism.
-  // @todo mollom() can be invoked outside of form processing. _mollom_fallback()
-  //   unconditionally invokes form_set_error(), which always displays the
-  //   fallback error message. Ideally, we would pass a $verbose argument to
-  //   _mollom_fallback(), but for that, we'd have to know here already.
-  //   Consequently, mollom() would need that $verbose argument. In the end, we
-  //   likely want to either embed the fallback handling into form processing,
-  //   or introduce a new helper function that is invoked instead of mollom()
-  //   during form processing.
   if ($method != 'mollom.verifyKey') {
     _mollom_fallback();
   }
@@ -1729,6 +1864,84 @@
 }
 
 /**
+ * Implements hook_mollom_action_info().
+ */
+function mollom_mollom_action_info() {
+  $actions = array(
+    'mollom_block' => array(
+      'title' => t('Block all submissions until the server problems are resolved'),
+      'callback' => 'mollom_action_block',
+      'triggers' => array(MOLLOM_TRIGGER_COM_FAILURE),
+    ),
+    'mollom_accept' => array(
+      'title' => t('Leave the form unprotected and accept all submissions'),
+      'triggers' => array(MOLLOM_TRIGGER_COM_FAILURE),
+    ),
+    'mollom_continue' => array(
+      'title' => t('Continue the normal form execution'),
+      'triggers' => array(
+        MOLLOM_TRIGGER_ANALYSIS_HAM,
+        MOLLOM_TRIGGER_ANALYSIS_SPAM,
+        MOLLOM_TRIGGER_ANALYSIS_UNSURE,
+        MOLLOM_TRIGGER_PASSED_CAPTCHA,
+      ),
+    ),
+    'mollom_reject' => array(
+      'title' => t('Reject the form submission'),
+      'callback' => 'mollom_action_reject',
+      'triggers' => array(
+        MOLLOM_TRIGGER_ANALYSIS_HAM,
+        MOLLOM_TRIGGER_ANALYSIS_SPAM,
+        MOLLOM_TRIGGER_ANALYSIS_UNSURE,
+      ),
+    ),
+    'mollom_captcha' => array(
+      'title' => t('Present the user with a captcha'),
+      'callback' => 'mollom_action_captcha',
+      'triggers' => array(
+        MOLLOM_TRIGGER_ANALYSIS_HAM,
+        MOLLOM_TRIGGER_ANALYSIS_SPAM,
+        MOLLOM_TRIGGER_ANALYSIS_UNSURE,
+      ),
+    ),
+  );
+
+  return $actions;
+}
+
+/**
+ * Callback for the 'mollom_block' action.
+ *
+ * Blocks the form submission and prints an appropriate error.
+ */
+function mollom_action_block($form, &$form_state) {
+  form_set_error('mollom', t("The spam filter installed on this site is currently unavailable. Per site policy, we are unable to accept new submissions until that problem is resolved. Please try resubmitting the form in a couple of minutes."));
+  _mollom_fallback();
+}
+
+/**
+ * Callback for the 'mollom_reject' action.
+ *
+ * Rejects the form submission with an error.
+ */
+function mollom_action_reject($form, &$form_state) {
+  form_set_error('mollom', t('Your submission has triggered the spam filter and will not be accepted.'));
+}
+
+/**
+ * Callback for the 'mollom_captcha' action.
+ *
+ * Requires a captcha to be completed before form processing can continue.
+ */
+function mollom_action_captcha($form, &$form_state) {
+  if (!$form_state['mollom']['passed_captcha']) {
+    form_set_error('mollom', t("To complete this form, please complete the word verification below."));
+    $form_state['mollom']['require_captcha'] = TRUE;
+  }
+}
+
+
+/**
  * @name mollom_node Node module integration for Mollom.
  * @{
  */
@@ -1885,6 +2098,27 @@
 }
 
 /**
+ * Implement hook_mollom_action_info().
+ */
+function comment_mollom_action_info() {
+  $actions = array(
+    'comment_unpublish' => array(
+      'title' => t('Post comment to the moderation queue'),
+      'callback' => 'comment_mollom_action_unpublish',
+    ),
+  );
+
+  return $actions;
+}
+
+/**
+ * Callback for the 'comment_unpublish' Mollom action.
+ */
+function comment_mollom_action_unpublish($form, &$form_state) {
+  $form_state['values']['status'] = COMMENT_NOT_PUBLISHED;
+}
+
+/**
  * Implements hook_form_FORMID_alter().
  *
  * When a registered user posts a comment or when a comment administrator edits
Index: mollom.api.php
===================================================================
RCS file: /cvs/drupal/contributions/modules/mollom/mollom.api.php,v
retrieving revision 1.1.2.2
diff -u -r1.1.2.2 mollom.api.php
--- mollom.api.php	6 Mar 2010 21:37:59 -0000	1.1.2.2
+++ mollom.api.php	21 May 2010 00:10:39 -0000
@@ -152,6 +152,33 @@
 }
 
 /**
+ * Return information about possible actions to take after Mollom text analysis.
+ *
+ * @return
+ * An associative array containing action definitions.  Each action should be
+ * identified by a unique key and include the following information:
+ * - title: The title of the action.
+ * - callback: (optional) A function that will be called during form
+ *   validation.
+ * - forms: (optional) An array of form_ids that this action should be
+ *   limited to.
+ * - triggers: (optional) An array of triggers that this action should be
+ *   limited to.
+ */
+function hook_mollom_action_info() {
+  $actions = array(
+    'mymodule_action' => array(
+      'title' => t('An custom action defined by my module'),
+      'callback' => 'mymodule_action',
+      'forms' => array('mymodule_form'),
+      'triggers' => array(MOLLOM_TRIGGER_PASSED_CAPTCHA),
+    ),
+  );
+
+  return $actions;
+}
+
+/**
  * Alter registered information about a form that can be protected by Mollom.
  *
  * @param &$form_info
@@ -166,3 +193,16 @@
   }
 }
 
+/**
+ * Alter registered information about actions that can be performed by Mollom.
+ *
+ * @param &$actions
+ *   An associative array describing the available actions. See
+ *   hook_mollom_action_info() for details.
+ */
+function hook_mollom_action_info_alter(&$actions) {
+  if (isset($actions['mymodule_action'])) {
+    $actions['mymodule_action']['forms'][] = 'another_form_id';
+  }
+}
+
Index: mollom.admin.inc
===================================================================
RCS file: /cvs/drupal/contributions/modules/mollom/mollom.admin.inc,v
retrieving revision 1.1.2.26
diff -u -r1.1.2.26 mollom.admin.inc
--- mollom.admin.inc	12 Mar 2010 03:07:44 -0000	1.1.2.26
+++ mollom.admin.inc	21 May 2010 00:10:39 -0000
@@ -112,7 +112,7 @@
         '#options' => mollom_admin_form_options(),
         '#required' => TRUE,
       );
-      $form['actions']['next'] = array(
+      $form['buttons']['next'] = array(
         '#type' => 'submit',
         '#value' => t('Next'),
         '#submit' => array('mollom_admin_configure_form_next_submit'),
@@ -160,14 +160,30 @@
       if (empty($form['mollom']['enabled_fields']['#options'])) {
         $form['mollom']['enabled_fields']['#description'] = t('No fields are available.');
       }
-      $form['actions']['submit'] = array(
+      $form['mollom']['actions'] = array(
+        '#type' => 'fieldset',
+        '#title' => t('Actions'),
+        '#description' => t('Mollom can trigger the following actions, based on various conditions.  Select how you would like Mollom to respond to each trigger.'),
+        '#tree' => TRUE,
+      );
+      $triggers = mollom_triggers();
+      foreach ($triggers as $trigger => $info) {
+        $actions = mollom_actions($mollom_form['form_id'], $trigger);
+        $form['mollom']['actions'][$trigger] = array(
+          '#type' => 'radios',
+          '#title' => $info['title'],
+          '#options' =>  _mollom_actions_options($actions),
+          '#default_value' => (isset($mollom_form['actions'][$trigger])) ? $mollom_form['actions'][$trigger] : NULL,
+        );
+      }
+      $form['buttons']['submit'] = array(
         '#type' => 'submit',
         '#value' => t('Save'),
       );
       break;
   }
 
-  $form['actions']['cancel'] = array(
+  $form['buttons']['cancel'] = array(
     '#value' => l(t('Cancel'), 'admin/settings/mollom'),
   );
 
@@ -438,20 +454,11 @@
   $form['server'] = array(
     '#type' => 'fieldset',
     '#title' => t('Fallback strategy'),
-    '#description' => t('When the Mollom servers are down or otherwise unreachable, no text analysis is performed and no CAPTCHAs are generated. If this occurs, your site will use the configured fallback strategy. Subscribers to Mollom Plus receive access to Mollom\'s high-availability backend infrastructure, not available to free users, reducing potential downtime.', array(
+    '#description' => t('When the Mollom servers are down or otherwise unreachable, no text analysis is performed and no CAPTCHAs are generated. If this occurs, your forms will use the configured fallback action. Subscribers to Mollom Plus receive access to Mollom\'s high-availability backend infrastructure, not available to free users, reducing potential downtime.', array(
       '@pricing-url' => 'http://mollom.com/pricing',
       '@sla-url' => 'http://mollom.com/standard-service-level-agreement',
     )),
   );
-  $form['server']['mollom_fallback'] = array(
-    '#type' => 'radios',
-    // Default to treating everything as inappropriate.
-    '#default_value' => variable_get('mollom_fallback', MOLLOM_FALLBACK_BLOCK),
-    '#options' => array(
-      MOLLOM_FALLBACK_BLOCK => t('Block all submissions of protected forms until the server problems are resolved'),
-      MOLLOM_FALLBACK_ACCEPT => t('Leave all forms unprotected and accept all submissions'),
-    ),
-  );
 
   $form['access-keys'] = array(
     '#type' => 'fieldset',
Index: mollom.install
===================================================================
RCS file: /cvs/drupal/contributions/modules/mollom/mollom.install,v
retrieving revision 1.2.2.28
diff -u -r1.2.2.28 mollom.install
--- mollom.install	30 Mar 2010 14:53:03 -0000	1.2.2.28
+++ mollom.install	21 May 2010 00:10:39 -0000
@@ -123,6 +123,11 @@
         'type' => 'text',
         'serialize' => TRUE,
       ),
+      'actions' => array(
+        'description' => 'A list of actions configured for the form.',
+        'type' => 'text',
+        'serialize' => TRUE,
+      ),
       'module' => array(
         'description' => 'The module name the $form_id belongs to.',
         'type' => 'varchar',
@@ -382,3 +387,17 @@
   }
   return $ret;
 }
+
+/**
+ * Add the {mollom_form}.actions column.
+ */
+function mollom_update_6113() {
+  $ret = array();
+  // Add the 'actions' column.
+  db_add_field($ret, 'mollom_form', 'actions', array(
+    'description' => 'The configured action for the form.',
+    'type' => 'text',
+    'serialize' => TRUE,
+  ));
+  return $ret;
+}