Index: mollom.admin.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/mollom/Attic/mollom.admin.inc,v
retrieving revision 1.1.2.8
diff -u -p -r1.1.2.8 mollom.admin.inc
--- mollom.admin.inc	17 Nov 2009 17:01:49 -0000	1.1.2.8
+++ mollom.admin.inc	19 Nov 2009 16:12:27 -0000
@@ -6,6 +6,215 @@
  * Administrative page callbacks for Mollom module.
  */
 
+function mollom_get_available_forms() {
+  static $forms;
+
+  if (!isset($forms)) {
+    $forms = mollom_get_forms();
+    $query = db_query("SELECT fid FROM {mollom_form}");
+    while ($form_id = db_result($query)) {
+      unset($forms[$form_id]);
+    }
+    foreach ($forms as $key => $form) {
+      $forms[$key] = $form['title'];
+    }
+    asort($forms);
+  }
+
+  return $forms;
+}
+
+function mollom_get_form_info($form_id) {
+  static $forms;
+
+  if (!isset($forms)) {
+    $forms = mollom_get_forms();
+  }
+
+  if (isset($forms[$form_id])) {
+    $forms[$form_id] += array(
+      'title' => t('Custom form (@form_id)', array('@form_id' => $form_id)),
+      'parameters' => array(),
+      'file' => FALSE,
+      'fields' => FALSE,
+    );
+    return $forms[$form_id];
+  }
+
+  return FALSE;
+}
+
+function mollom_list_forms() {
+  $header = array(
+    t('Form'),
+    t('Protection mode'),
+    array('data' => t('Operations'), 'colspan' => 2),
+  );
+  $rows = array();
+  $query = db_query("SELECT fid, fields FROM {mollom_form}");
+  while ($form = db_fetch_object($query)) {
+    $form->info = mollom_get_form_info($form->fid);
+    $form->fields = unserialize($form->fields);
+    $rows[] = array(
+      $form->info['title'],
+      $form->fields ? t('Text analysis with CAPTCHA backup') : t('CAPTCHA only'),
+      l(t('Edit'), 'admin/build/mollom/protect/' . $form->fid),
+      l(t('Unprotect'), 'admin/build/mollom/unprotect/' . $form->fid),
+    );
+  }
+
+  // Add a row to add a form.
+  $rows[] = array(array('colspan' => 4, 'data' => l(t('Add form protection'), 'admin/build/mollom/protect')));
+
+  $form['forms'] = array(
+    '#type' => 'markup',
+    '#value' => theme('table', $header, $rows),
+  );
+
+  return drupal_render($form);
+}
+
+function mollom_autocomplete_form_ids($string = '') {
+  $forms = mollom_get_available_forms();
+  $matches = $match_values = array();
+
+  foreach ($forms as $form_id => $form_title) {
+    if (strpos($form_id . $form_title, $string) !== FALSE) {
+      $matches[$form_key] = $form_title;
+      $match_values[$form_key] = min(levenshtein($string, $form_id), levenshtein($string, $form_title));
+      if (count($matches) == 10) {
+        break;
+      }
+    }
+  }
+
+  asort($match_values);
+  foreach (array_keys($match_values) as $form_id) {
+    $match_values[$form_id] = $matches[$form_id];
+  }
+
+  drupal_json($match_values);
+}
+
+function mollom_protect_form(&$form_state, $mollom_form = NULL) {
+  if (!isset($form_state['storage']['step'])) {
+    $form_state['storage']['step'] = $mollom_form ? 2 : 1;
+    $form_state['storage']['mollom_form'] = $mollom_form;
+  }
+  else {
+    $mollom_form = $form_state['storage']['mollom_form'];
+  }
+
+  switch ($form_state['storage']['step']) {
+    case 1:
+      drupal_add_js(drupal_get_path('module', 'mollom') . '/mollom.js');
+      //$mollom_forms = mollom_get_available_forms();
+      //foreach ($mollom_forms as $form_key => $form_details) {
+      //  $mollom_forms[$form_key] = $form_details['title'];
+      //}
+      //asort($mollom_forms);
+      $form['fid'] = array(
+        '#type' => 'select',
+        '#title' => t('Form'),
+        '#options' => mollom_get_available_forms() + array('fid_custom' => t('Custom form ID')),
+        '#required' => TRUE,
+      );
+      $form['fid_custom'] = array(
+        '#type' => 'textfield',
+        '#title' => t('Custom form ID'),
+        '#size' => 25,
+        '#autocomplete_path' => 'js/mollom/autocomplete/form_id',
+      );
+      $form['next'] = array(
+        '#type' => 'submit',
+        '#value' => t('Next'),
+      );
+      break;
+    case 2:
+      // Display a list of fields for textual analysis (last step).
+      module_load_include('inc', 'mollom');
+      $mollom_form->available_fields = mollom_get_form_fields($mollom_form->fid);
+      $mollom_form->info = mollom_get_form_info($mollom_form->fid);
+      //dpm($mollom_form);
+      $form['#mollom_form'] = $mollom_form;
+      $form['form_title'] = array(
+        '#type' => 'item',
+        '#title' => t('Form'),
+        '#value' => $mollom_form->info['title'],
+      );
+      $form['fid'] = array(
+        '#type' => 'value',
+        '#value' => $mollom_form->fid,
+      );
+      $form['fields'] = array(
+        '#type' => 'checkboxes',
+        '#title' => t('Fields to use for textual analysis'),
+        '#description' => t('If no fields are selected or available, the form will only be protected by a CAPTCHA.'),
+        '#options' => $mollom_form->available_fields,
+        '#default_value' => !empty($mollom_form->fields) ? $mollom_form->fields : array(),
+      );
+      if (!$form['fields']['#options']) {
+        $form['fields']['#options'] = array(t('No input fields available for selection'));
+        $form['fields']['#disabled'] = TRUE;
+      }
+      $form['save'] = array(
+        '#type' => 'submit',
+        '#value' => t('Save'),
+      );
+      break;
+  }
+
+  $form['cancel'] = array('#value' => l(t('Cancel'), 'admin/build/mollom'));
+  $form['#redirect'] = 'admin/build/mollom';
+  return $form;
+}
+
+function mollom_protect_form_submit($form, &$form_state) {
+  //$form_state['storage']['values'][$form_state['storage']['step']] = $form_state['values'];
+
+  if ($form_state['storage']['step'] == 1) {
+    // Build a form object to use with customization.
+    $mollom_form = new stdClass();
+    $mollom_form->fid = $form_state['values']['fid'];
+    if ($form_state['values']['fid'] == 'fid_custom') {
+     $mollom_form->fid = $form_state['values']['fid_custom'];
+    }
+    //$mollom_form->info = mollom_get_form_info($mollom_form->fid);
+    $form_state['storage']['mollom_form'] = $mollom_form;
+    $form_state['storage']['step']++;
+    $form_state['rebuild'] = TRUE;
+  }
+  else {
+    $mollom_form = $form_state['values'];
+    $mollom_form['fields'] = serialize(array_values(array_filter($mollom_form['fields'])));
+    if (db_result(db_query_range("SELECT 1 FROM {mollom_form} WHERE fid = '%s'", $mollom_form['fid'], 0, 1))) {
+      drupal_write_record('mollom_form', $mollom_form, array('fid'));
+    }
+    else {
+      drupal_write_record('mollom_form', $mollom_form);
+    }
+    $form_state['storage'] = $form_state['rebuild'] = FALSE;
+    drupal_set_message('Form protection saved.');
+    $form_state['redirect'] = 'admin/build/mollom';
+  }
+}
+
+function mollom_unprotect_form($form_state, $mollom_form) {
+  $mollom_form->info = mollom_get_form_info($mollom_form->fid);
+  $form['form'] = array(
+    '#type' => 'item',
+    '#title' => t('Form'),
+    '#value' => $mollom_form->info['title'],
+  );
+  $form['#mollom_form'] = $mollom_form;
+  $form['#redirect'] = 'admin/build/mollom';
+  return confirm_form($form, t('Are you sure you want to unprotect this form?'), 'admin/build/mollom', t('Mollom will no longer be able to protect this form from spam'));
+}
+
+function mollom_unprotect_form_submit($form, $form_state) {
+  db_query("DELETE FROM {mollom_form} WHERE fid = '%s'", $form['#mollom_form']->fid);
+}
+
 /**
  * Form builder; Administration settings form.
  */
Index: mollom.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/mollom/Attic/mollom.inc,v
retrieving revision 1.1.2.2
diff -u -p -r1.1.2.2 mollom.inc
--- mollom.inc	17 Nov 2009 17:01:49 -0000	1.1.2.2
+++ mollom.inc	19 Nov 2009 16:15:06 -0000
@@ -74,3 +74,41 @@ function _mollom_retrieve_server_list() 
   return array();
 }
 
+/**
+ * A simplified version of drupal_get_form() to fetch a form definition.
+ */
+function mollom_get_form($form_id, $args = array()) {
+  if ($form_info = mollom_get_form_info($form_id)) {
+    if ($form_info['file']) {
+      require_once $form_info['file'];
+    }
+    $args = $form_info['parameters'];
+  }
+
+  $form_state = array('storage' => NULL, 'submitted' => FALSE);
+  $args = array_merge(array($form_id, &$form_state), $args);
+
+  $form = call_user_func_array('drupal_retrieve_form', $args);
+  drupal_prepare_form($form_id, $form, $form_state);
+  $form['#post'] = FALSE;
+  drupal_process_form($form_id, $form, $form_state);
+  return $form;
+}
+
+function mollom_get_form_fields($form_id, $form = NULL) {
+  if (!isset($form)) {
+    $form = mollom_get_form($form_id);
+  }
+
+  $elements = array();
+  foreach (element_children($form) as $key) {
+    $input_elements = array('textfield', 'textarea');
+    if (!empty($form[$key]['#type']) && !empty($form[$key]['#input']) && in_array($form[$key]['#type'], $input_elements) && !in_array($key, array('teaser_js'))) {
+      //var_export($form[$key]); echo "\n\n";
+      $elements[$form[$key]['#name']] = isset($form[$key]['#title']) ? $form[$key]['#title'] : $form[$key]['#name'];
+    }
+    $elements = array_merge($elements, mollom_get_form_fields($form_id, $form[$key]));
+  }
+
+  return $elements;
+}
Index: mollom.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/mollom/mollom.install,v
retrieving revision 1.2.2.11
diff -u -p -r1.2.2.11 mollom.install
--- mollom.install	17 Nov 2009 17:01:49 -0000	1.2.2.11
+++ mollom.install	19 Nov 2009 16:16:05 -0000
@@ -53,6 +53,24 @@ function mollom_schema() {
     'primary key' => array('did'),
   );
 
+  $schema['mollom_form'] = array(
+    'description' => 'TODO',
+    'fields' => array(
+      'fid' => array(
+        'description' => 'The $form_id of the form being protected.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'fields' => array(
+        'type' => 'text',
+        'serialized' => TRUE,
+      ),
+    ),
+    'primary key' => array('fid'),
+  );
+
   $schema['cache_mollom'] = drupal_get_schema_unprocessed('system', 'cache');
   $schema['cache_mollom']['description'] = 'Cache table for the Mollom module to store information for forms it protects.';
 
@@ -120,3 +138,29 @@ function mollom_update_4() {
   return $ret;
 }
 
+/**
+ * Add the {mollom_form} table.
+ *
+ * @todo Write upgrade path from existing variable settings.
+ */
+function mollom_update_5() {
+  $ret = array();
+  $schema = array(
+    'fields' => array(
+      'fid' => array(
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'fields' => array(
+        'type' => 'text',
+        'serialized' => TRUE,
+      ),
+    ),
+    'primary key' => array('fid'),
+  );
+  db_create_table($ret, 'mollom_form', $schema);
+  return $ret;
+}
+
Index: mollom.js
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/mollom/mollom.js,v
retrieving revision 1.2.2.7
diff -u -p -r1.2.2.7 mollom.js
--- mollom.js	17 Nov 2009 17:01:49 -0000	1.2.2.7
+++ mollom.js	19 Nov 2009 16:16:31 -0000
@@ -3,7 +3,7 @@
 /**
  * Attach click event handlers for CAPTCHA links.
  */
-Drupal.behaviors.mollom = function(context) {
+Drupal.behaviors.mollomCaptcha = function(context) {
   $('a.mollom-audio-captcha', context).click(getAudioCaptcha);
   $('a.mollom-image-captcha', context).click(getImageCaptcha);
 }
@@ -45,3 +45,15 @@ function getImageCaptcha() {
    });
    return false;
 }
+
+Drupal.behaviors.mollomSettingsFormID = function() {
+  // Add onchange.event handler to toggle display of the custom form ID field.
+  $('select#edit-fid-1').change(function() {
+    if ($(this).val() == 'fid_custom') {
+      $('div#edit-fid-custom-1-wrapper').show();
+    }
+    else {
+      $('div#edit-fid-custom-1-wrapper').hide();
+    }
+  }).change();
+}
Index: mollom.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/mollom/mollom.module,v
retrieving revision 1.2.2.101
diff -u -p -r1.2.2.101 mollom.module
--- mollom.module	17 Nov 2009 23:39:49 -0000	1.2.2.101
+++ mollom.module	19 Nov 2009 16:17:37 -0000
@@ -155,6 +155,49 @@ function mollom_menu() {
     'file' => 'mollom.pages.inc',
     'type' => MENU_CALLBACK,
   );
+  $items['admin/build/mollom'] = array(
+    'title' => 'Mollom',
+    'description' => 'Mollom is a web service that helps you manage your community.',
+    'page callback' => 'mollom_list_forms',
+    'access arguments' => array('administer mollom'),
+    'file' => 'mollom.admin.inc',
+  );
+  $items['admin/build/mollom/forms'] = array(
+    'title' => 'Protected forms',
+    'type' => MENU_DEFAULT_LOCAL_TASK,
+    'weight' => -10,
+  );
+  $items['admin/build/mollom/protect'] = array(
+    'title' => 'Add form protection',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('mollom_protect_form'),
+    'access arguments' => array('administer mollom'),
+    'file' => 'mollom.admin.inc',
+    'type' => MENU_LOCAL_TASK,
+  );
+  $items['admin/build/mollom/protect/%mollom'] = array(
+    'title' => 'Edit form protection',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('mollom_protect_form', 4),
+    'access arguments' => array('administer mollom'),
+    'file' => 'mollom.admin.inc',
+  );
+  $items['admin/build/mollom/unprotect/%mollom'] = array(
+    'title' => 'Unprotect form',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('mollom_unprotect_form', 4),
+    'access arguments' => array('administer mollom'),
+    'file' => 'mollom.admin.inc',
+  );
+  $items['admin/build/mollom/settings'] = array(
+    'title' => 'Settings',
+    'description' => 'Administer Mollom settings.',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('mollom_admin_settings'),
+    'access arguments' => array('administer mollom'),
+    'file' => 'mollom.admin.inc',
+    'type' => MENU_LOCAL_TASK,
+  );
   $items['admin/settings/mollom'] = array(
     'title' => 'Mollom',
     'description' => 'Mollom is a web service that helps you manage your community.',
@@ -171,6 +214,16 @@ function mollom_menu() {
     'access arguments' => array('administer mollom'),
     'file' => 'mollom.admin.inc',
   );
+  $items['js/mollom/autocomplete/form_id'] = array(
+    'page callback' => 'mollom_autocomplete_form_ids',
+    'page arguments' => array(4),
+    'access arguments' => array('administer mollom'),
+    'file' => 'mollom.admin.inc',
+  );
+  //$items['admin/build/mollom/status'] = $items['admin/reports/mollom'];
+  //$items['admin/build/mollom/status']['title'] = 'Reports';
+  //$items['admin/build/mollom/status']['type'] = MENU_LOCAL_TASK;
+  //$items['admin/build/mollom/status']['weight'] = 10;
 
   // Menu callback used for AJAX request for new CAPTCHAs.
   $items['mollom/captcha/%/%'] = array(
@@ -184,6 +237,71 @@ function mollom_menu() {
   return $items;
 }
 
+function mollom_get_forms() {
+  static $forms;
+
+  if (!isset($forms)) {
+    module_load_include('inc', 'mollom', 'mollom.form');
+    $forms = module_invoke_all('form_info');
+  }
+
+  return $forms;
+}
+
+/**
+ * Given an array of values and an array of fields, extract data for use.
+ *
+ * @param $values
+ *   An array of data, usually $form_state['values'].
+ * @param $fields
+ *   An array of strings representing fields to extract. Nested fields are in
+ *   the form of 'parent[child]'.
+ */
+function mollom_get_form_data($values, $fields) {
+  global $user;
+  $data = $fields_data = array();
+
+  foreach ($fields as $field) {
+    // Nested field values are saved as parent[child], so we need to recurse.
+    $field = str_replace(']', '', $field);
+    $field = explode('[', $field);
+    $value = $values;
+    foreach ($field as $key) {
+      $value = isset($value[$key]) ? $value[$key] : NULL;
+    }
+
+    // Only save values that are not empty.
+    if (isset($value) && strlen($value)) {
+      $fields_data[implode('_', $field)] = $value;
+    }
+  }
+
+  // Add the data from the fields.
+  $data['post_title'] = NULL; // @todo Will this still be ok?
+  $data['post_body'] = $fields_data ? implode("\n", $fields_data) : NULL;
+
+  // Add common data.
+  $data['author_name'] = isset($values['name']) ? $values['name'] : (isset($user->name) ? $user->name : NULL);
+  $data['author_mail'] = isset($values['mail']) ? $values['mail'] : (isset($user->mail) ? $user->mail : NULL);
+  $data['author_url'] = isset($values['homepage']) ? $values['homepage'] : NULL;
+  $data['author_openid'] = $user->uid ? _mollom_get_openid($user) : NULL;
+  $data['author_id'] = $user->uid ? $user->uid : NULL;
+  $data['author_ip'] = ip_address();
+
+  return $data;
+}
+
+/**
+ * Menu load callback.
+ */
+function mollom_load($form_id) {
+  $mollom_form = db_fetch_object(db_query_range("SELECT fid, fields FROM {mollom_form} WHERE fid = '%s'", $form_id, 0, 1));
+  if ($mollom_form) {
+    $mollom_form->fields = unserialize($mollom_form->fields);
+  }
+  return $mollom_form;
+}
+
 /**
  * Access callback; check if the module is configured.
  *
@@ -305,11 +423,11 @@ function mollom_comment($comment, $op) {
  * This function intercepts all forms in Drupal and Mollom-enables them if
  * necessary.
  */
-function mollom_form_alter(&$form, $form_state, $form_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')) {
     // Retrieve the mode of protection required for this form.
-    if ($mode = _mollom_get_mode($form_id)) {
+    if ($form_mollom = mollom_load($form_id)) {
       // Compute the weight of the CAPTCHA so we can position it in the form.
       $weight = 99999;
       foreach (element_children($form) as $key) {
@@ -332,8 +450,9 @@ function mollom_form_alter(&$form, $form
       // Add Mollom form protection widget.
       $form['mollom'] = array(
         '#type' => 'mollom',
-        '#mode' => $mode,
+        '#mode' => $form_mollom->fields ? MOLLOM_MODE_ANALYSIS : MOLLOM_MODE_CAPTCHA,
         '#weight' => $weight,
+        '#fields' => $form_mollom->fields,
       );
       // Add a submit handler that will clean the Mollom state as soon as the
       // form is successfully submitted.
@@ -637,6 +756,7 @@ function mollom_expand_element($element,
       '#require_captcha' => $element['#mode'] == MOLLOM_MODE_CAPTCHA,
       '#passed_captcha' => FALSE,
       '#user_session_id' => session_id(),
+      '#fields' => $element['#fields'],
     );
   }
 
@@ -726,6 +846,7 @@ function mollom_validate_analysis(&$moll
   $data = array();
 
   $form_id = $form_state['values']['form_id'];
+  $data = mollom_get_form_data($form_state['values'], $mollom_state['#fields']);
 
   $pos = strpos($form_id, '_node_form');
   if ($pos !== FALSE) {
