Index: CHANGELOG.txt
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/form_controller/CHANGELOG.txt,v
retrieving revision 1.3
diff -u -p -r1.3 CHANGELOG.txt
--- CHANGELOG.txt	14 Nov 2009 23:46:39 -0000	1.3
+++ CHANGELOG.txt	19 Nov 2009 11:42:04 -0000
@@ -4,8 +4,9 @@ Form controller x.x-x.x, xxxx-xx-xx
 -----------------------------------
 
 
-Form controller 6.x-1.x, xxxx-xx-xx
+Form controller 6.x-2.x, xxxx-xx-xx
 -----------------------------------
+#602600 by sun: Revamped module; pre-alpha code to support many implementations.
 #602600 by sun: Very rough port to D6.
 
 
Index: form_controller.info
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/form_controller/form_controller.info,v
retrieving revision 1.3
diff -u -p -r1.3 form_controller.info
--- form_controller.info	14 Nov 2009 23:46:39 -0000	1.3
+++ form_controller.info	19 Nov 2009 11:26:12 -0000
@@ -3,3 +3,4 @@ name = Form controller
 description = Helper module for managing form elements of other modules.
 package = Development
 core = 6.x
+dependencies[] = contact
Index: form_controller.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/form_controller/form_controller.module,v
retrieving revision 1.4
diff -u -p -r1.4 form_controller.module
--- form_controller.module	14 Nov 2009 23:46:39 -0000	1.4
+++ form_controller.module	19 Nov 2009 11:39:54 -0000
@@ -3,7 +3,9 @@
 
 /**
  * @file
- * Loader and callback functions for Form controller.
+ * Form controller.
+ *
+ * @todo Increase module weight, since #process does not work on 'form' in D6.
  */
 
 /**
@@ -18,7 +20,8 @@ function form_controller_perm() {
  */
 function form_controller_menu() {
   $items['form_controller'] = array(
-    'page callback' => 'form_controller',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('form_controller_form'),
     'access arguments' => array('administer forms'),
     'type' => MENU_CALLBACK,
   );
@@ -27,115 +30,271 @@ function form_controller_menu() {
 
 /**
  * Implements hook_form_alter().
- *
- * Attaches Form controller to all forms, if there are any hook_form_controller()
- * implementations.
  */
 function form_controller_form_alter(&$form, &$form_state, $form_id) {
-  if (!user_access('administer forms') || $form_id == 'form_controller_form' || !form_controller_get_modules()) {
+  static $implementations;
+  if (!isset($implementations)) {
+    $implementations = (bool) module_implements('form_controller_info');
+  }
+  if ($form_id == 'form_controller_form' || !$implementations) {
     return;
   }
-  $form['form_controller'] = array(
-    '#prefix' => '<div class="form-controller">',
-    '#value' => l(t('Configure this form'), "form_controller/form_controller_form/$form_id"),
-    '#suffix' => '</div>',
-    '#weight' => -1000,
-  );
-}
+  // @todo This breaks form submissions in D6, so we have to inline the code :(
+  /*
+  $form['#input'] = TRUE;
+  $form['#process'][] = 'form_controller_process_form';
+  */
+  form_controller_process_form($form, array(), $form_state);
 
-/**
- * Menu callback; retrieves and renders a form.
- */
-function form_controller($form_id, $target_form_id = NULL) {
-  // Output target form_id as title in form settings widget.
-  if (empty($_POST) && $form_id == 'form_controller_form') {
-    echo '<h2>'. t('Form settings for %form_id', array('%form_id' => $target_form_id)) .'</h2>';
+  if (!user_access('administer forms')) {
+    return;
   }
-  $output = drupal_get_form($form_id, $target_form_id);
 
-  // Output and clear form validation error messages, if there are any.
-  echo theme_status_messages();
-  echo $output;
-  exit;
+  // @todo Toggle this + form_controller globally, auto-disable after 6 hours.
+  if (!isset($form['#cache'])) {
+    $form['#cache'] = TRUE;
+  }
+
+  // Store page title for potential usage in implementation forms.
+  $form['#form_controller']['title'] = drupal_set_title();
+
+  // Output a contextual configuration link.
+  $output = '<div class="form-controller">';
+  $output .= l(t('Configure this form'), 'form_controller/' . $form['form_build_id']['#value']);
+  $output .= '</div>';
+  if (!isset($form['#prefix'])) {
+    $form['#prefix'] = '';
+  }
+  $form['#prefix'] .= $output . $form['#prefix'];
 }
 
 /**
- * Builds a form based on hook_form_controller() implementations.
+ * Form builder to control form alterations.
  */
-function form_controller_form($target_form_id) {
-  $form = array();
+function form_controller_form(&$form_state, $form_build_id, $target_form_id = NULL) {
+  // Reload form from cache.
+  $form_state = array('storage' => NULL, 'submitted' => FALSE);
+  $cached_form = form_get_cache($form_build_id, $form_state);
+  $cached_form['#post'] = array();
+
+  // Rebuild cached form.
+  $form_id = $cached_form['form_id']['#value'];
+  $processed_form = form_builder($form_id, $cached_form, $form_state);
+
+  // Build form information.
+  $form_elements = form_controller_get_elements($processed_form);
+  $context = array(
+    'form_id' => $form_id,
+    'form' => $processed_form,
+    'elements' => $form_elements,
+  );
+  $form_state['form_controller'] = $context;
+
+  drupal_set_title(t('Configure %title form (@form_id)', array(
+    '%title' => $cached_form['#form_controller']['title'],
+    '@form_id' => $form_id,
+  )));
 
-  $form['form_controller']['target_form_id'] = array(
-    '#type' => 'value',
-    '#value' => $target_form_id,
-  );
-  foreach (form_controller_get_modules() as $module) {
-    $function = $module .'_form_controller';
-    $form['form_controller'][$module] = $function('settings', $target_form_id);
-    $form['form_controller'][$module]['#tree'] = TRUE;
+  $form = array();
+  $alterations = module_invoke_all('form_controller_info');
+  foreach ($alterations as $name => $info) {
+    // Fetch configuration form.
+    // @todo Take over configuration storage for most/simple implementations.
+    if (isset($info['form callback']) && function_exists($info['form callback'])) {
+      $function = $info['form callback'];
+      $settings = $function($form, $form_id, $context);
+      if (isset($settings) && is_array($settings)) {
+        $form[$name] = array(
+          '#type' => 'fieldset',
+          '#title' => check_plain($info['title']),
+          '#description' => check_plain($info['description']),
+          '#collapsible' => TRUE,
+          //'#collapsed' => TRUE,
+          '#tree' => TRUE,
+        );
+        $form[$name] += $settings;
+      }
+    }
   }
 
-  $form['submit'] = array(
-    '#type' => 'submit',
-    '#value' => t('Save'),
-  );
+  $form['submit'] = array('#type' => 'submit', '#value' => t('Save'));
+
   return $form;
 }
 
 /**
- * Render and return a Form controller settings form.
+ * Helper function to retrieve all user input fields in a form.
  *
- * To ensure that no other module adds elements to our form, we simply render
- * everything below $form['form_controller'] only. 
+ * This was mainly designed for Form controller implementations like Mollom,
+ * which want to do something with all exposed user input widgets in a form.
+ * I currently can't think of other implementations that would want to retrieve
+ * all widgets, so we limit this to real user input elements.
  */
-function theme_form_controller_form($form) {
-  foreach ($form as $key => $value) {
-    if ($key[0] != '#' && !in_array($key, array('form_controller', 'submit', 'form_token', 'form_id'))) {
-      unset($form[$key]);
+function form_controller_get_elements($elements) {
+  static $input = array('textfield', 'textarea');
+
+  $items = array();
+  foreach (element_children($elements) as $key) {
+    // @todo Find a better way to skip form_controller-controlled elements.
+    //   Wait. Why should we skip any possibly valid and useful form elements?
+    //   Anything that's contained in the form may be used by any implementation, no?
+    if (isset($elements[$key]['#form_controller'])) {
+      continue;
+    }
+    // Skip autocomplete widgets.
+    if (!empty($elements[$key]['#autocomplete_path'])) {
+      continue;
+    }
+    // Skip elements without a #type.
+    if (isset($elements[$key]['#type']) && in_array($elements[$key]['#type'], $input)) {
+      $items[] = $elements[$key];
+    }
+    // Recurse into children, if there are any.
+    if (element_children($elements[$key])) {
+      // @todo Do we want to key by $key?
+      $items = array_merge($items, form_controller_get_elements($elements[$key]));
     }
   }
-  return drupal_render($form);
+  return $items;
 }
 
-/**
- * Form controller settings validation callback.
- *
- * Allows hook_form_controller() implementations to react on form submissions.
- * To invalidate the form, implementations should invoke form_set_error().
- */
-function form_controller_form_validate($form_id, $form_values) {
-  foreach (form_controller_get_modules() as $module) {
-    $form_values[$module]['#form_id'] = $form_values['target_form_id'];
-    $function = $module .'_form_controller';
-    $function('validate', $form_values[$module]);
+function form_controller_process_form(&$element, $edit, &$form_state) {
+  // Build form information.
+  $form_id = $element['form_id']['#value'];
+  $form_elements = form_controller_get_elements($element);
+  $context = array(
+    'form_id' => $form_id,
+    'form' => $element,
+    'elements' => $form_elements,
+  );
+  $form_state['form_controller'] = $context;
+
+  // @todo Cache this.
+  foreach (module_invoke_all('form_controller_info') as $name => $info) {
+    if (!empty($info['process callback']) && function_exists($info['process callback'])) {
+      $function = $info['process callback'];
+      $function($element, $form_state, $form_id, $form_elements);
+    }
   }
+
+  // return $element;
+}
+
+function form_controller_get_value($form_state, $parents) {
+  $values = $form_state['values'];
+  foreach ($parents as $parent) {
+    $values = (isset($values[$parent]) ? $values[$parent] : NULL);
+  }
+  return $values;
 }
 
+// @see http://drupal.org/node/633058 for a D7 patch.
+function form_controller_validate_username($element, &$form_state) {
+  if (!empty($element['#value']) && !($account = user_load(array('name' => $element['#value'])))) {
+    form_error($element, t('The username %name does not exist.', array('%name' => $element['#value'])));
+  }
+}
+
+
+
+
 /**
- * Form controller settings submit callback.
+ * Implements hook_form_controller_info().
  *
- * Returns form values to hook_form_controller() implementations.
+ * Example implementation for Contact module. Replace 'contact' with 'mollom'
+ * or whatever to imagine other implementations.
  */
-function form_controller_form_submit($form_id, $form_values) {
-  foreach (form_controller_get_modules() as $module) {
-    $form_values[$module]['#form_id'] = $form_values['target_form_id'];
-    $function = $module .'_form_controller';
-    $function('submit', $form_values[$module]);
+function contact_form_controller_info() {
+  $alterations['contact_webmaster'] = array(
+    'title' => t('Contact webmaster'),
+    'description' => t('Sends the chosen field contents to a selected user.'),
+    'form callback' => 'contact_form_configure_webmaster',
+    'process callback' => 'contact_form_process_webmaster',
+  );
+  return $alterations;
+}
+
+function contact_form_configure_webmaster(&$complete_form, $form_id, $context) {
+  // Fetch configuration.
+  // @todo Take over configuration storage for most/simple implementations.
+  $config = variable_get('contact_webmaster_forms', array());
+
+  // I really wonder why Form API allows #element_validate, but not #element_submit... 15/11/2009 sun
+  $form['#submit'][] = 'contact_form_configure_webmaster_submit';
+
+  $form['status'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Send an e-mail notification upon form submission'),
+    '#default_value' => isset($config[$form_id]),
+  );
+  if (!isset($config[$form_id])) {
+    $config[$form_id] = array();
+  }
+  $config[$form_id] += array(
+    'recipient' => '',
+    'message' => '',
+  );
+  $form['recipient'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Recipient'),
+    '#default_value' => $config[$form_id]['recipient'],
+    '#autocomplete_path' => 'user/autocomplete',
+    '#element_validate' => array('form_controller_validate_username'),
+  );
+  $options = array();
+  foreach ($context['elements'] as $key => $element) {
+    if (isset($element['#title'])) {
+      $parents = implode('][', $element['#parents']);
+      $options[$parents] = $element['#title'];
+    }
   }
-  echo TRUE;
-  exit;
+  $form['element'] = array(
+    '#type' => 'select',
+    '#title' => t('Field containing message'),
+    '#default_value' => $config[$form_id]['message'],
+    '#options' => $options,
+  );
+  $complete_form['#submit'][] = 'contact_form_configure_webmaster_submit';
+
+  return $form;
 }
 
-/**
- * Get a list of current hook_form_controller() implementations.
- */
-function form_controller_get_modules() {
-  // Development 15/11/2009 sun
-  return TRUE;
-  static $modules;
-  if (!isset($modules)) {
-    $modules = module_implements('form_controller');
+function contact_form_configure_webmaster_submit($form, &$form_state) {
+  // @todo Take over configuration storage for most/simple implementations.
+
+  $config = variable_get('contact_webmaster_forms', array());
+  $form_id = $form_state['form_controller']['form_id'];
+  if ($form_state['values']['contact_webmaster']['status']) {
+    $elements = $form_state['form_controller']['elements'];
+
+    $config[$form_id] = array(
+      'recipient' => $form_state['values']['contact_webmaster']['recipient'],
+      'element' => explode('][', $form_state['values']['contact_webmaster']['element']),
+    );
+  }
+  else {
+    unset($config[$form_id]);
   }
-  return $modules;
+  variable_set('contact_webmaster_forms', $config);
+}
+
+function contact_form_process_webmaster(&$form, &$form_state, $form_id, $form_elements) {
+  $config = variable_get('contact_webmaster_forms', array());
+  if (isset($config[$form_id])) {
+    $form['#submit'][] = 'contact_form_webmaster_submit';
+  }
+}
+
+function contact_form_webmaster_submit($form, &$form_state) {
+  $config = variable_get('contact_webmaster_forms', array());
+  $form_id = $form['form_id']['#value'];
+
+  $recipient = $config[$form_id]['recipient'];
+  $message = form_controller_get_value($form_state, $config[$form_id]['element']);
+
+  drupal_set_message('<pre>' . var_export(array(
+    'recipient' => $recipient,
+    'message' => $message,
+  ), TRUE) . '</pre>');
 }
 
