Index: captcha.admin.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/captcha/captcha.admin.inc,v
retrieving revision 1.41
diff -u -p -r1.41 captcha.admin.inc
--- captcha.admin.inc	31 Dec 2010 01:45:49 -0000	1.41
+++ captcha.admin.inc	15 Jan 2011 01:43:42 -0000
@@ -66,10 +66,11 @@ function captcha_admin_settings() {
   );
   $form['captcha_form_protection']['captcha_form_id_overview']['captcha_captcha_points'] = array();
   $captcha_type_options = _captcha_available_challenge_types();
-  $result = db_select('captcha_points', 'cp')->fields('cp')->orderBy('form_id')->execute();
-  foreach ($result as $captcha_point) {
-    $form['captcha_form_protection']['captcha_form_id_overview']['captcha_captcha_points'][$captcha_point->form_id] = array();
-    $form['captcha_form_protection']['captcha_form_id_overview']['captcha_captcha_points'][$captcha_point->form_id]['form_id'] = array(
+
+  $captcha_points = captcha_get_captcha_points();
+  foreach ($captcha_points as $captcha_point) {
+    $elem = array();
+    $elem['form_id'] = array(
       '#markup' => $captcha_point->form_id,
     );
     // Select widget for CAPTCHA type.
@@ -82,17 +83,25 @@ function captcha_admin_settings() {
     else {
       $captcha_type = 'none';
     }
-    $form['captcha_form_protection']['captcha_form_id_overview']['captcha_captcha_points'][$captcha_point->form_id]['captcha_type'] = array(
+    $elem['captcha_type'] = array(
       '#type' => 'select',
       '#default_value' => $captcha_type,
       '#options' => $captcha_type_options,
     );
-    // Additional operations.
-    $form['captcha_form_protection']['captcha_form_id_overview']['captcha_captcha_points'][$captcha_point->form_id]['operations'] = array(
-      '#markup' => implode(", ", array(
-        l(t('delete'), "admin/config/people/captcha/captcha/captcha_point/{$captcha_point->form_id}/delete"),
-      ))
-    );
+
+    $ops = array();
+    if (module_exists('ctools') && $captcha_point->export_type & EXPORT_IN_CODE) {
+      if ($captcha_point->export_type & EXPORT_IN_DATABASE) {
+        $ops[] = l(t('revert'), "admin/config/people/captcha/captcha/captcha_point/{$captcha_point->form_id}/delete");
+      }
+      // TODO Disable exported points.
+    }
+    else {
+      $ops[] = l(t('delete'), "admin/config/people/captcha/captcha/captcha_point/{$captcha_point->form_id}/delete");
+    }
+    $elem['operations'] = array('#markup' => implode(", ", $ops));
+
+    $form['captcha_form_protection']['captcha_form_id_overview']['captcha_captcha_points'][$captcha_point->form_id] = $elem;
   }
 
   // Form items for new form_id.
@@ -269,7 +278,30 @@ function captcha_admin_settings_submit($
 
   // Process CAPTCHA points
   if (isset($form_state['values']['captcha_form_id_overview']['captcha_captcha_points'])) {
+    // Load existing data.
+    $captcha_points = captcha_get_captcha_points();
+     
+
     foreach ($form_state['values']['captcha_form_id_overview']['captcha_captcha_points'] as $captcha_point_form_id => $data) {
+
+      // If this is an in-code captcha point and its settings are unchanged,
+      // don't save to the database.
+      if (module_exists('ctools') && isset($captcha_points[$captcha_point_form_id])) {
+        // Parse module and captcha_type from submitted values.
+        if (is_string($data['captcha_type']) && substr_count($data['captcha_type'], '/') == 1) {
+          list($module, $captcha_type) = explode('/', $data['captcha_type']);
+        }
+        else {
+          $module = '';
+          $captcha_type = $data['captcha_type'];
+        }
+
+        $point = $captcha_points[$captcha_point_form_id];
+        if ($point->export_type & EXPORT_IN_CODE && !($point->export_type & EXPORT_IN_DATABASE) && $point->module == $module && $point->captcha_type == $captcha_type) {
+          continue;
+        }
+      }
+
       captcha_set_form_id_setting($captcha_point_form_id, $data['captcha_type']);
     }
   }
Index: captcha.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/captcha/captcha.inc,v
retrieving revision 1.19
diff -u -p -r1.19 captcha.inc
--- captcha.inc	31 Dec 2010 01:45:49 -0000	1.19
+++ captcha.inc	15 Jan 2011 01:43:43 -0000
@@ -70,9 +70,16 @@ function captcha_set_form_id_setting($fo
  *   or in the form 'captcha/Math'.
  */
 function captcha_get_form_id_setting($form_id, $symbolic=FALSE) {
-  $result = db_query("SELECT module, captcha_type FROM {captcha_points} WHERE form_id = :form_id",
-    array(':form_id' =>  $form_id));
-  $captcha_point = $result->fetchObject();
+  if (module_exists('ctools')) {
+    ctools_include('export');
+    $obj = ctools_export_load_object('captcha_points', 'names', array($form_id));
+    $captcha_point = array_pop($obj);
+  }
+  else {
+    $result = db_query("SELECT module, captcha_type FROM {captcha_points} WHERE form_id = :form_id",
+      array(':form_id' =>  $form_id));
+    $captcha_point = $result->fetchObject();
+  }
   if (!$captcha_point) {
     $captcha_point = NULL;
   }
@@ -95,7 +101,25 @@ function captcha_get_form_id_setting($fo
   return $captcha_point;
 }
 
-
+/**
+ * Helper function to load all captcha points.
+ *
+ * @return array of all captcha_points
+ */
+function captcha_get_captcha_points() {
+  if (module_exists('ctools')) {
+    ctools_include('export');
+    $captcha_points = ctools_export_load_object('captcha_points', 'all');
+  }
+  else {
+    $captcha_points = array();
+    $result = db_select('captcha_points', 'cp')->fields('cp')->orderBy('form_id')->execute();
+    foreach ($result as $captcha_point) {
+      $captcha_points[] = $captcha_point;
+    }
+  }
+  return $captcha_points;
+}
 /**
  * Helper function for generating a new CAPTCHA session.
  *
Index: captcha.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/captcha/captcha.install,v
retrieving revision 1.22
diff -u -p -r1.22 captcha.install
--- captcha.install	30 Dec 2010 23:59:07 -0000	1.22
+++ captcha.install	15 Jan 2011 01:43:43 -0000
@@ -13,6 +13,18 @@ function captcha_schema() {
   // Table for positions and types of the challenges.
   $schema['captcha_points'] = array(
     'description' => 'This table describes which challenges should be added to which forms.',
+    'export' => array(
+      'key' => 'form_id',
+      'identifier' => 'captcha',
+      'default hook' => 'captcha_default_points',  // Function hook name.
+      'status' => 'mark_status',
+      'api' => array(
+        'owner' => 'captcha',
+        'api' => 'captcha',  // Base name for api include files.
+        'minimum_version' => 1,
+        'current_version' => 1,
+      ),
+    ),
     'fields' => array(
       'form_id' => array(
         'description' => 'The form_id of the form to add a CAPTCHA to.',
@@ -134,25 +146,6 @@ function captcha_requirements($phase) {
  */
 function captcha_install() {
   $t = get_t();
-  // Insert some default CAPTCHA points.
-  $form_ids = array(
-    'contact_site_form', 'contact_personal_form',
-    'user_register_form', 'user_pass', 'user_login', 'user_login_block',
-    'forum_node_form'
-  );
-  // Add form_ids of all currently known node types too.
-  foreach (node_type_get_names() as $type => $name) {
-    $form_ids[] = 'comment_node_' . $type . '_form';
-  }
-  foreach ($form_ids as $form_id) {
-    db_insert('captcha_points')
-      ->fields(array(
-        'form_id' => $form_id,
-        'module' => NULL,
-        'captcha_type' => NULL,
-      ))
-      ->execute();
-  }
 
   // Be friendly to your users: what to do after install?
   drupal_set_message($t('You can now <a href="!captcha_admin">configure the CAPTCHA module</a> for your site.',
Index: captcha.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/captcha/captcha.module,v
retrieving revision 1.121
diff -u -p -r1.121 captcha.module
--- captcha.module	30 Dec 2010 00:29:15 -0000	1.121
+++ captcha.module	15 Jan 2011 01:43:43 -0000
@@ -287,6 +287,53 @@ function captcha_element_process($elemen
   return $element;
 }
 
+/**
+ * Implementation of hook_captcha_default_points_alter().
+ *
+ * Provide some default captchas only if defaults are not already
+ * provided by other modules.
+ */
+function captcha_captcha_default_points_alter(&$items)  {
+  $modules = array(
+    'comment' => array(
+    ),
+    'contact' => array(
+      'contact_site_form',
+      'contact_personal_form'
+    ),
+    'forum' => array(
+      'forum_node_form',
+    ),
+    'user' => array(
+      'user_register_form',
+      'user_pass',
+      'user_login',
+      'user_login_block',
+    ),
+  );
+  // Add comment form_ids of all currently known node types.
+  foreach (node_type_get_names() as $type => $name) {
+    $modules['comment'][] = 'comment_node_' . $type . '_form';
+  }
+
+  foreach ($modules as $module => $form_ids) {
+    // Only give defaults if the module exists.
+    if (module_exists($module)) {
+      foreach ($form_ids as $form_id) {
+        // Ensure a default has not been provided already.
+        if (!isset($items[$form_id])) {
+          $captcha = new stdClass;
+          $captcha->disabled = FALSE;
+          $captcha->api_version = 1;
+          $captcha->form_id = $form_id;
+          $captcha->module = '';
+          $captcha->captcha_type = 'default';
+          $items[$form_id] = $captcha;
+        }
+      }
+    }
+  }
+}
 
 /**
  * Theme function for a CAPTCHA element.
