Index: INSTALL.txt
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/signup/INSTALL.txt,v
retrieving revision 1.15
diff -u -p -r1.15 INSTALL.txt
--- INSTALL.txt	22 Jan 2009 18:59:28 -0000	1.15
+++ INSTALL.txt	10 Jan 2010 11:45:04 -0000
@@ -14,11 +14,8 @@ the Drupal package and read the online d
 
 2. (Optional) Customize the form presented to users when signing up
    for content on your site.  Unfortuantely, there is not yet a way to
-   do this from within your site, you must modify a theme function to
-   change this form.  See the comment above "theme_signup_user_form()"
-   in the theme/signup_form.inc file.  If you want to change the form,
-   you should define your own version of theme_signup_user_form() in
-   your site's theme (e.g. as phptemplate_signup_user_form()).
+   do this from within your site, you must implement hook_signup_pane_info in
+   a module to register a callback that returns form elements.
 
 
 3. Enable the signup module by navigating to:
Index: signup.api.php
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/signup/signup.api.php,v
retrieving revision 1.5
diff -u -p -r1.5 signup.api.php
--- signup.api.php	19 Sep 2009 01:42:52 -0000	1.5
+++ signup.api.php	10 Jan 2010 11:45:04 -0000
@@ -8,6 +8,37 @@
  */
 
 /**
+ * Hook to define panes available to insert into the signup form.
+ *
+ * Panes should be provided by callback functions as a FormAPI array.
+ * The callback should have the following signature:
+ *   function my_callback(&$signup_form, &$form_state, $node, $signup, $pane_id, $signup_type = 'auth')
+ * See signup_basic_form_form for an example.
+ * The values submitted to the form elements defined by this form will be 
+ * serialized and stored in the {signup_log} table as 'form_data'.
+ *
+ * @return
+ *   An array of possible forms, keyed by a unique ID. Each value is itself an 
+ *   array of data, with the following key-value pairs:
+ *     - 'label': (required) The human-readable name of the form.
+ *     - 'description': (required) Extra information about the form.
+ *     - 'callback': (required) The name of a function.
+ *     - 'operations': (optional) A string consisting of one or more links to 
+ *        useful operations concerning this pane.
+ *
+ * @see signup_basic_form_form.
+ */
+function hook_signup_pane_info() {
+  return array(
+    'basic' => array(
+      'label' => 'Basic form',
+      'description' => 'Collects name and phone number.',
+      'callback' => 'signup_basic_form_form',
+    ),  
+  );
+}
+
+/**
  * Hook to alter signup data before a signup is inserted or updated.
  *
  * @param $signup
@@ -80,7 +111,7 @@ function hook_signup_update($signup) {
  *
  * This hook allows other modules to inject information into the custom signup
  * data for each signup.  The array is merged with the values of any custom
- * fields from theme_signup_user_form(), serialized, and stored in the
+ * fields from hook_signup_pane_info(), serialized, and stored in the
  * {signup_log} database table.
  *
  * @param $node
@@ -94,7 +125,7 @@ function hook_signup_update($signup) {
  *   should be human-readable (and wrapped in t() to allow translation).
  *
  * @see signup_sign_up_user()
- * @see theme_signup_user_form()
+ * @see hook_signup_pane_info()
  */
 function hook_signup_sign_up($node, $account) {
   return array(
@@ -207,3 +238,33 @@ function hook_signup_menu_access($node, 
   // powers over events in their group.
 }
 
+/**
+ * Implementation of hook_signup_form_data_display().
+ *
+ * Allow modules to alter signup form data prior to displaying signup records
+ * in, for example, a node's list of signups at node/N/signups/admin.
+ *
+ * This allows modules that implement signup panes for format or even inject 
+ * their own data.
+ *
+ * @param $form_data
+ *  The user's signup data to alter.
+ * @param $nid
+ *  The node id for the signup-enabled node.
+ * @param $sid
+ *  The signup record id.
+ * @param $uid
+ *  The user id whose signup this is; 0 if this is an anonymous signup.
+ * @param $type
+ *  The type of output being prepared. Possible values are:
+ *    - 'list': The hardcoded admin lists of signups, eg at node/X/signups/admin
+ *    - 'view': The form data field in Views.
+ *    - 'mail': Email output. This is likely the only one that needs special 
+ *      handling; in this case, modules should be more generous about supplying
+ *      data since there's no other place to see it.
+ */
+function hook_signup_form_data_display_alter(&$form_data, $nid, $sid, $uid, $type = 'list') {
+  foreach ($form_data as $pane_id => $pane_data) {
+    // If this is one of your panes, do stuff to it.
+  }
+}
Index: signup.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/signup/signup.install,v
retrieving revision 1.30
diff -u -p -r1.30 signup.install
--- signup.install	19 Sep 2009 01:42:52 -0000	1.30
+++ signup.install	10 Jan 2010 11:45:05 -0000
@@ -117,7 +117,7 @@ function signup_schema() {
         'default' => 0,
       ),
       'form_data' => array(
-        'description' => t('Serialized string of additional signup form values.  See theme_signup_user_form() from theme/signup.theme for more information.'),
+        'description' => t('Serialized string of additional signup form values.  See hook_signup_pane_info() for more information.'),
         'type' => 'text',
         'size' => 'big',
         'not null' => TRUE,
@@ -141,6 +141,46 @@ function signup_schema() {
     ),
   );
 
+  $schema['signup_panes'] = array(
+    'description' => t('Signup panes for each signup node.'),
+    'fields' => array(
+      'nid' => array(
+        'description' => t('Node ID'),
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'pane_id' => array(
+        'description' => t('Pane ID as defined by hook_signup_pane_info().'),
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'callback' => array(
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => "The name of the function that renders the pane.",
+      ),
+      'weight' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+        'size' => 'tiny',
+        'description' => 'The weight of this pane in relation to other panes for the node.',
+      ),
+    ),
+    'indexes' => array(
+      'nid'        => array('nid'),
+      ),
+    'unique keys' => array(
+      'nid_pane_id' => array('nid', 'pane_id'),
+    ),
+  );
+
   return $schema;
 }
 
@@ -482,3 +522,22 @@ function signup_update_6003() {
   return $ret;
 }
 
+/**
+ * Create the {signup_panes} table, enable the signup_basic_form module,
+ * and add the basic form to all existing nodes.
+ */
+function signup_update_6200() {
+  $ret = array();
+  
+  $schema = signup_schema();
+  db_create_table(&$ret, 'signup_panes', $schema['signup_panes']);
+  
+  module_enable('signup_basic_form');
+  
+  $ret = db_query("INSERT INTO {signup_panes} (nid, pane_id, weight) SELECT nid, 'basic', 0 FROM {signup}");
+
+  $ret[] = array('success' => TRUE, 'query' => t('The Signup basic form module has been enabled and the basic "Name and Phone number" pane has been added to your existing signup-enabled content.'));
+  
+  return $ret;
+}
+
Index: signup.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/signup/signup.module,v
retrieving revision 1.242
diff -u -p -r1.242 signup.module
--- signup.module	8 Oct 2009 00:01:24 -0000	1.242
+++ signup.module	10 Jan 2010 11:45:06 -0000
@@ -151,13 +151,6 @@ function signup_theme() {
         'node' => NULL,
       ),
     ),
-    'signup_user_form' => array(
-      'file' => 'signup_form.inc',
-      'path' => $path,
-      'arguments' => array(
-        'node' => NULL,
-      ),
-    ),
     'signup_anonymous_username' => array(
       'file' => 'signup_form.inc',
       'path' => $path,
@@ -173,6 +166,11 @@ function signup_theme() {
         'view' => NULL,
       ),
     ),
+    'signup_node_settings_form_panes' => array(
+      'file' => 'node_settings.inc',
+      'path' => drupal_get_path('module', 'signup') . '/includes',
+      'arguments' => array('form'),
+    ),
   );
 }
 
@@ -744,6 +742,7 @@ function signup_nodeapi(&$node, $op, $te
     case 'delete':
         // Clean up the signup tables for the deleted node.
         db_query("DELETE FROM {signup} WHERE nid = %d", $node->nid);
+        db_query("DELETE FROM {signup_panes} WHERE nid = %d", $node->nid);
         db_query("DELETE FROM {signup_log} WHERE nid = %d", $node->nid);
       break;
 
@@ -763,11 +762,21 @@ function signup_nodeapi(&$node, $op, $te
         $node->signup_reminder_days_before = $signup->reminder_days_before;
         $node->signup_reminder_email = $signup->reminder_email;
         $node->signup_close_signup_limit = $signup->close_signup_limit;
+        $node->signup_form_panes = $signup->form_panes;        
         $node->signup_status = $signup->status;
         if ($node->nid) {
           $node->signup_total = db_result(db_query("SELECT COUNT(*) FROM {signup_log} WHERE nid = %d", $node->nid));
           $node->signup_effective_total = db_result(db_query("SELECT SUM(count_towards_limit) FROM {signup_log} WHERE nid = %d", $node->nid));
         }
+        // Load pane data
+        $result = db_query("SELECT * FROM {signup_panes} WHERE nid = %d ORDER BY weight", $node->nid);
+        $node->signup_form_panes = array();
+        while ($pane_data = db_fetch_array($result)) {
+          $node->signup_form_panes[$pane_data['pane_id']] = array(
+            'weight' => $pane_data['weight'],
+            'callback' => $pane_data['callback'],
+          );
+        }
       }
       else {
         $node->signup = 0;
@@ -1177,8 +1186,9 @@ function signup_content_types() {
  *   highly recommended to call the signup_validate_anon_email
  *   function in the external module's validation cycle or perform
  *   that function's validation logic prior to passing in this element!
- * $signup_form['signup_form_data'] : an array of key/value pairs --
- *   key is the data category, value is the user input
+ * $signup_form['signup_form_data'] : an associative array of extra data, 
+ *   keyed by signup form pane id. The value is itself an array in the same
+ *   format as a $form_values array, that is, form_element => value.
  *
  * @param $notify_user
  *   When set to TRUE, confirmation messages are displayed via
@@ -1266,6 +1276,13 @@ function signup_sign_up_user($signup_for
 
     // See if we should generate any notifications from this signup.
     if ($notify_user) {
+      // Invoke hook_signup_form_data_display_alter() to let other modules
+      // (in particular pane modules) alter the data for output.
+      // This may involve internal data being removed.
+      $form_data = $signup->form_data;
+      drupal_alter('signup_form_data_display', $form_data, $node->nid, $signup->sid, $signup->uid, 'mail');
+      $signup->form_data = $form_data;
+
       // Confirmation e-mail to the user who signed up.
       if ($node->signup_send_confirmation) {
         signup_send_confirmation_mail($signup, $node);
Index: includes/node_admin.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/signup/includes/node_admin.inc,v
retrieving revision 1.7
diff -u -p -r1.7 node_admin.inc
--- includes/node_admin.inc	14 Jan 2009 18:11:18 -0000	1.7
+++ includes/node_admin.inc	10 Jan 2010 11:45:07 -0000
@@ -84,7 +84,13 @@ function signup_node_admin_details_form(
     $users[$key] = '';
     $form['username'][$key] = array('#value' => $username);
     $form['signup_date'][$key] = array('#value' => format_date($signed_up_user->signup_time, variable_get('signup_date_format', 'small')));
-    $form['signup_form_data'][$key] = array('#value' => theme('signup_custom_data', unserialize($signed_up_user->form_data)));
+    // Unpack the stored signup form data.
+    $signup_form_data = unserialize($signed_up_user->form_data);
+    // Invoke hook_signup_form_data_display_alter() to let other modules
+    // (in particular pane modules) alter the data for output.
+    // This may involve internal data being removed.
+    drupal_alter('signup_form_data_display', $signup_form_data, $node->nid, $key, $signed_up_user->uid, 'list');
+    $form['signup_form_data'][$key] = array('#value' => theme('signup_custom_data', $signup_form_data));
     $form['attended'][$key] = array('#value' => theme('signup_attended_text', $signed_up_user->attended));
   }
   if (empty($users)) {
Index: includes/node_settings.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/signup/includes/node_settings.inc,v
retrieving revision 1.5
diff -u -p -r1.5 node_settings.inc
--- includes/node_settings.inc	21 Sep 2009 05:23:02 -0000	1.5
+++ includes/node_settings.inc	10 Jan 2010 11:45:07 -0000
@@ -46,6 +46,65 @@ function signup_node_settings_form($form
     $node->signup_reminder_days_before = $result->reminder_days_before;
     $node->signup_reminder_email = $result->reminder_email;
     $node->signup_close_signup_limit = $result->close_signup_limit;
+    $node->signup_form_panes = $result->form_panes;
+  }
+  
+  // Get information about all potential form panes from modules that 
+  // implement hook_signup_pane_info.
+  $signup_form_pane_info = module_invoke_all('signup_pane_info');
+  
+  // Put in weights from either saved settings or for panes not enabled on this 
+  // node a default heavy weight of 10.
+  foreach ($signup_form_pane_info as $id => $signup_form_pane) {
+    $signup_form_pane_info[$id]['weight'] = isset($node->signup_form_panes[$id]) ? $node->signup_form_panes[$id]['weight'] : 10;
+  }
+  // Sort the list by weight so it appears correctly in the UI.
+  uasort($signup_form_pane_info, '_signup_cmp_weight'); 
+  
+  // The label and the theme function for the form panes table.
+  $form['signup_form_panes'] = array(
+    // This gets a label on the whole table; not sure this is the best approach though.
+    // Improvements welcome.
+    '#type' => 'item',
+    '#title' => t('Signup form panes'),
+    '#tree' => TRUE,
+    '#description' => t('Enable the signup form panes you want to use on this content. You may also drag enabled panes into any order. The position of disabled panes in the list is ignored.'),
+    '#theme' => 'signup_node_settings_form_panes',
+  );
+  // Add a warning if there are already existing signups.
+  if ($node->signup_effective_total > 0) {
+     $form['signup_form_panes']['#description'] .= t(' <span class="warning">There are existing signups recorded. If you disable panes their data will remain in your records; if you enable panes then past signups will not have any data for them.</span>');
+  }
+  
+  foreach ($signup_form_pane_info as $id => $signup_form_pane) {
+    $form['signup_form_panes'][$id]['enabled'] = array(
+      '#type' => 'checkbox',
+      '#default_value' => isset($node->signup_form_panes[$id]),
+      // Don't set the #title: we want it in a separate column. 
+    );
+    $form['signup_form_panes'][$id]['label'] = array(
+      '#value' => $signup_form_pane['label'],
+    );
+    $form['signup_form_panes'][$id]['description'] = array(
+      '#value' => $signup_form_pane['description'],
+    );
+    if (isset($signup_form_pane['operations'])) {
+      $form['signup_form_panes'][$id]['operations'] = array(
+        '#value' => $signup_form_pane['operations'],
+      );
+    }
+    $form['signup_form_panes'][$id]['weight'] = array(
+      '#type' => 'weight',
+      '#delta' => 10,      
+      '#default_value' => $signup_form_pane['weight'],
+      '#attributes' => array(
+        'class' => "signup-pane-weight",
+      ),
+    );
+  }
+  // Unset weight item if only 1 element
+  if (count($signup_form_pane_info) == 1) {
+    unset($form['signup_form_panes'][$id]['weight']);
   }
 
   $form['signup_forwarding_email'] = array(
@@ -145,6 +204,62 @@ function signup_node_settings_page($node
 }
 
 /**
+ * Helper function to sort signup panes by their weight key.
+ */
+function _signup_cmp_weight($a, $b) {
+  return $a['weight'] - $b['weight'];
+}
+
+/**
+ * Theme function for the panes table in the node settings form.
+ */
+function theme_signup_node_settings_form_panes($form) {
+  // Based on taxonomy.admin.inc
+  foreach (element_children($form) as $key) {
+    $row = array();
+    $row[] = drupal_render($form[$key]['enabled']);
+    if (isset($form[$key]['weight'])) {
+      $form[$key]['weight']['#attributes']['class'] = 'signup-pane-weight';
+      $row[] = drupal_render($form[$key]['weight']);
+    }
+    $row[] = drupal_render($form[$key]['label']);
+    $row[] = drupal_render($form[$key]['description']);
+    $row[] = drupal_render($form[$key]['operations']);
+        
+    $rows[] = array(
+      'data' => $row,
+      'class' => 'draggable',
+    );
+  }
+  // A message if there are no rows.
+  // @todo: figure out why the form label doesn't show in this case.
+  if (!count($rows)) {
+    $rows[] = array(
+      array(
+        'data' => t('No form panes are available.'),
+        'colspan' => 3,
+      )
+    );
+  }
+  
+  $header[] = t('Enabled');
+  if (isset($form[$key]['weight'])) {
+    $header[] = t('Weight');
+  }
+  $header[] = t('Name');
+  $header[] = t('Description');
+  $header[] = t('Operations');
+  
+  if (isset($form[$key]['weight'])) {  
+    drupal_add_tabledrag('signup-node-settings-panes', 'order', 'sibling', 'signup-pane-weight');
+  }
+    
+  $output = drupal_render($form); // ???
+  $output .=  theme('table', $header, $rows, array('id' => 'signup-node-settings-panes'));
+  return $output;
+}
+
+/**
  * Submit handler for the per-node signup settings form.
  *
  * @param $form_id
@@ -153,16 +268,56 @@ function signup_node_settings_page($node
  *   The constructed form values array of the submitted form.
  */
 function signup_node_settings_form_submit($form, &$form_state) {
+  $node = $form['#node'];
   $op = isset($form_state['values']['op']) ? $form_state['values']['op'] : '';
   if ($op == t('Reset to defaults')) {
     // If we're resetting, we just want to grab the site-wide defaults.
     $values = db_fetch_array(db_query("SELECT forwarding_email, send_confirmation, confirmation_email, close_signup_limit, send_reminder, reminder_days_before, reminder_email FROM {signup} WHERE nid = 0"));
+    // Clear all the panes for the node.
+    db_query("DELETE FROM {signup_panes} WHERE nid = %d", $node->nid);    
   }
   else {
+    // Handle the pane settings.
+    // WTF new nodes come here with ALL WEIGHTS set at 10???
+    // @todo: new nodes probably need arbitrary weights setting?
+    foreach ($form_state['values']['signup_form_panes'] as $pane_id => $form_state_values_panes) {
+      if ($form_state_values_panes['enabled']) {
+        $signup_panes[$pane_id] = $form_state_values_panes['weight'];
+      }
+    }
+    // Clear all the panes for the node.
+    db_query("DELETE FROM {signup_panes} WHERE nid = %d", $node->nid);    
+    // If there are enabled panes, save them.
+    if (count($signup_panes)) {
+      // Give a singleton pane a weight value, arbitrarily set to 0.
+      if (count($signup_panes) == 1) {
+        // Convoluted way of getting the first (and only!) key to set the value to 0
+        // @todo Suggestions for improvement welcome!
+        reset($signup_panes);
+        $pane_id = key($signup_panes);
+        $signup_panes[$pane_id] = 0;
+      }
+      
+      // Get the pane info so we know the callbacks. Callbacks are saved
+      // (somewhat redundantly) into {signup_panes} so on node view and other
+      // form displays we don't need to invoke the 'signup_pane_info':
+      // some modules implementing this run queries, so it's potentially 
+      // expensive.
+      $signup_form_pane_info = module_invoke_all('signup_pane_info');
+      
+      // Sort by weight, preserve keys.
+     // obsolete asort($signup_panes);
+      foreach ($signup_panes as $pane_id => $weight) {
+        db_query("INSERT INTO {signup_panes} (nid, pane_id, callback, weight) VALUES (%d, '%s', '%s', %d)", 
+          $node->nid, $pane_id, $signup_form_pane_info[$pane_id]['callback'], $weight);
+      }
+    }
+    
     // Populate $values from $form_state.
     foreach (array('forwarding_email', 'send_confirmation', 'confirmation_email', 'close_signup_limit') as $setting) {
       $values[$setting] = $form_state['values']["signup_$setting"];
     }
+   
     // If we're dealing with a node that doesn't have a start time, these
     // fields are missing from the signup settings form, so we can't assume
     // they're defined.
@@ -173,7 +328,6 @@ function signup_node_settings_form_submi
 
   // Either way, we want to make sure we're updating the values for the
   // current node, not nid 0...
-  $node = $form['#node'];
   $values[] = $node->nid;
   db_query("UPDATE {signup} SET forwarding_email = '%s', send_confirmation = %d, confirmation_email = '%s', close_signup_limit = %d, send_reminder = %d, reminder_days_before = %d, reminder_email = '%s' WHERE nid = %d", $values);
 
Index: includes/signup_edit_form.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/signup/includes/signup_edit_form.inc,v
retrieving revision 1.7
diff -u -p -r1.7 signup_edit_form.inc
--- includes/signup_edit_form.inc	19 Sep 2009 00:31:56 -0000	1.7
+++ includes/signup_edit_form.inc	10 Jan 2010 11:45:07 -0000
@@ -85,24 +85,39 @@ function signup_edit_form($form_state, $
     );
   }
 
-  // Build the themed signup form for this site and include that.
-  $site_form = theme('signup_user_form', $node);
-  $form_data = unserialize($signup->form_data);
+  // Build all the signup panes for this node.
+  if (isset($node->signup_form_panes)) {
+    $form['elements']['signup_form_data'] = array(
+      '#tree' => TRUE,
+    );
+    foreach (array_keys($node->signup_form_panes) as $pane_id) {
+      $callback = $node->signup_form_panes[$pane_id]['callback'];
+      // Each pane goes into its own form element with #tree set
+      // to protect form values from clashing.
+      $form['elements']['signup_form_data'][$pane_id] = array(
+        '#tree' => TRUE,
+      );
+      $form['elements']['signup_form_data'][$pane_id] += $callback($form, $form_state, $node, $signup, $pane_id, $signup_type);
+    }
+  }
 
-  // This is sort of a hack, but we don't support nested arrays for the custom
-  // signup form anyway, so it works for now.  Obviously all this will change
-  // with signup_fields and friends, but for now it works.
-  foreach ($form_data as $key => $value) {
-    if (!empty($site_form['signup_form_data'][$key])) {
-      $site_form['signup_form_data'][$key]['#default_value'] = $value;
-      if (!$can_edit) {
-        // If they can't edit, mark all the fields as disabled.
-        $site_form['signup_form_data'][$key]['#disabled'] = TRUE;
+  $form_data = unserialize($signup->form_data);
+  // This is *still* sort of a hack, and means that nested arrays in signup 
+  // form panes won't work. 
+  // Does Drupal FormAPI provide a means to re-fill a $form array with values
+  // from a $form_values array? It really should.
+  foreach ($form_data as $pane_id => $pane_data) {
+    foreach ($pane_data as $key => $value) {
+      if (!empty($form['elements']['signup_form_data'][$pane_id][$key])) {
+        // The pane callback may have provided its own default values: if so
+        // then don't clobber them.
+        if (!isset($form['elements']['signup_form_data'][$pane_id][$key]['#default_value'])) {
+          $form['elements']['signup_form_data'][$pane_id][$key]['#default_value'] = $value;
+        }
       }
     }
   }
-  $form['elements'] += $site_form;
-
+  
   // Add the appropriate buttons based on permissions.
   if ($can_edit) {
     $form['elements']['save'] = array(
@@ -111,6 +126,11 @@ function signup_edit_form($form_state, $
       '#submit' => array('signup_edit_form_save_submit'),
     );
   }
+  else {
+    // If they can't edit, mark all the fields as disabled.
+    _signup_form_element_disable_recursive($form['elements']['signup_form_data']);
+  }
+  
   if ($can_cancel) {
     if (isset($_REQUEST['destination'])) {
       $destination = drupal_get_destination();
@@ -130,6 +150,20 @@ function signup_edit_form($form_state, $
 }
 
 /**
+ * Helper function to recursively disable all elements of a form.
+ */
+function _signup_form_element_disable_recursive(&$form) {
+  if (isset($form['#type'])) {
+    $form['#disabled'] = TRUE;    
+  }
+  foreach (element_children($form) as $key) {
+    if (is_array($form[$key])) {
+      _signup_form_element_disable_recursive($form[$key]);
+    }
+  }
+}
+
+/**
  * Validation callback when editing the anonymous email for an existing signup.
  */
 function signup_validate_anon_mail($form, &$form_state) {
Index: includes/signup_form.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/signup/includes/signup_form.inc,v
retrieving revision 1.2
diff -u -p -r1.2 signup_form.inc
--- includes/signup_form.inc	20 Dec 2008 06:31:22 -0000	1.2
+++ includes/signup_form.inc	10 Jan 2010 11:45:07 -0000
@@ -75,17 +75,23 @@ function signup_form(&$form_state, $node
     $signup_form += $admin_form;
   }
 
-  // Build the themed signup form for this site and include that.
-  $signup_themed_form = theme('signup_user_form', $node);
-
-  if ($signup_type == 'admin') {
-    // Special case hack for the default signup form, where the current
-    // username is being filled in as the default for the 'Name' field.
-    if (!empty($signup_themed_form['signup_form_data']['Name']['#default_value'])) {
-      unset($signup_themed_form['signup_form_data']['Name']['#default_value']);
+  // Insert the form components provided by other modules.
+  if (isset($node->signup_form_panes)) {
+    $signup_form['signup_form_data'] = array(
+      '#tree' => TRUE,
+    );
+    // Add each pane to the form. The node settings are stored in the weighted
+    // order so there is no need for further sorting here.
+    foreach ($node->signup_form_panes as $pane_id => $pane_data) {
+      $callback = $pane_data['callback'];
+      // Each pane goes into its own form element with #tree set
+      // to protect form values from clashing.
+      $signup_form['signup_form_data'][$pane_id] = array(
+        '#tree' => TRUE,
+      );
+      $signup_form['signup_form_data'][$pane_id] += $callback($signup_form, $form_state, $node, NULL, $pane_id, $signup_type);
     }
   }
-  $signup_form += $signup_themed_form;
 
   $form['collapse']['signup_user_form'] = $signup_form;
   $form['collapse']['submit'] = array(
Index: theme/email.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/signup/theme/email.inc,v
retrieving revision 1.2
diff -u -p -r1.2 email.inc
--- theme/email.inc	7 Jan 2009 00:55:00 -0000	1.2
+++ theme/email.inc	10 Jan 2010 11:45:07 -0000
@@ -56,6 +56,8 @@ function theme_signup_custom_data_email(
  *
  * @param $key
  *   Name of the custom signup field (the array key).
+ *   If this is numeric, it is assumed that the $value contains its own label,
+ *   in which case the $key is not output.
  * @param $value
  *   Value of the custom signup field (the array value).
  *
@@ -71,7 +73,14 @@ function theme_signup_custom_data_field_
   // warnings when extracting strings, "hide" the call to t() by using a
   // variable to hold the function name.
   $tr = 't';
-  return $tr($key) .': '. $value;
+  // If a key is numeric, we assume the module that set it knows what it is
+  // doing and provides its own label.
+  if (is_numeric($key)) {
+    return $value;
+  }
+  else {
+    return $tr($key) .': '. $value;
+  }
 }
 
 /**
Index: theme/node.admin.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/signup/theme/node.admin.inc,v
retrieving revision 1.8
diff -u -p -r1.8 node.admin.inc
--- theme/node.admin.inc	21 Sep 2009 05:23:02 -0000	1.8
+++ theme/node.admin.inc	10 Jan 2010 11:45:07 -0000
@@ -138,7 +138,14 @@ function theme_signup_custom_data($data)
       $output .= call_user_func(__FUNCTION__, $value);
     }
     else {
-      $output .= $tr($key) .': '. check_plain($value);
+      // If a key is numeric, we assume the module that set it knows what it is
+      // doing and provides its own label.
+      if (is_numeric($key)) {
+        $output .= check_plain($value);
+      }
+      else {
+        $output .= $tr($key) .': '. check_plain($value);
+      }
     }
     $output .= "</div>\n";
   }
Index: theme/signup_form.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/signup/theme/signup_form.inc,v
retrieving revision 1.5
diff -u -p -r1.5 signup_form.inc
--- theme/signup_form.inc	22 Jan 2009 18:59:29 -0000	1.5
+++ theme/signup_form.inc	10 Jan 2010 11:45:07 -0000
@@ -8,76 +8,11 @@
  */
 
 /**
- * Return the site-specific custom fields for the signup user form.
- *
- * To customize this for your site, copy this entire function into
- * your theme's template.php file, rename the function to
- * phptemplate_signup_user_form(), and modify to taste.  Feel free to
- * alter any elements in this section, remove them, or add any others.
- *
- * WARNING: If your site allows anonymous signups and you alter the
- * 'Name' field in this function, you will probably have to implement a
- * version of theme_signup_anonymous_username() for your site.
- *
- * In order for the form to be rendered properly and for the custom
- * fields to be fully translatable when printed in other parts of the
- * Signup module (displayed in signup lists, emails, etc), the name of
- * the form element must be $form['signup_form_data']['NameOfDataField'],
- * where NameOfDataField is replaced with the actual name of the data
- * field.  For translation to work, the displayed name of the field
- * (the '#title' property) be the same as the name of the data field,
- * but wrapped in t().  See below for examples.
- *
- * Fieldsets are not currently supported in this form.  Any
- * '#default_value' will be filled in by default when the form is
- * presented to the user.  Any field marked '#required' must be filled
- * in before the user can sign up.
- *
- * If you do not want any additional fields, the function can simply
- * return an empty array: "return array();"
- *
- * @param $node
- *   The fully loaded node object where this signup form is appearing.
- *
- * @return
- *   Array defining the form to present to the user to signup for a node.
- *
- * @see theme_signup_anonymous_username()
- */
-function theme_signup_user_form($node) {
-  global $user;
-  $form = array();
-
-  // If this function is providing any extra fields at all, the following
-  // line is required for form form to work -- DO NOT EDIT OR REMOVE.
-  $form['signup_form_data']['#tree'] = TRUE;
-
-  $form['signup_form_data']['Name'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Name'),
-    '#size' => 40, '#maxlength' => 64,
-    '#required' => TRUE,
-  );
-  $form['signup_form_data']['Phone'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Phone'),
-    '#size' => 40, '#maxlength' => 64,
-  );
-
-  // If the user is logged in, fill in their name by default.
-  if ($user->uid) {
-    $form['signup_form_data']['Name']['#default_value'] = $user->name;
-  }
-
-  return $form;
-}
-
-/**
  * Returns the value to use for the user name for anonymous signups.
  *
- * WARNING: If you implemented your own version of theme_signup_form_data()
- * that changed or removed the custom 'Name' field and your site
- * allows anonymous signups, you will need to modify this, too.
+ * WARNING: If you are using a custom signup form other than the basic one
+ * supplied with this module, you will need to alter this function to work
+ * correctly.
  *
  * This value is used for the %user_name email token for anonymous users, and
  * also to identify a particular anonymous signup in various places in the UI.
@@ -96,8 +31,8 @@ function theme_signup_anonymous_username
   // supplied email address, in which case, you should uncomment this:
   //return $email;
 
-  // WARNING: This line is only valid if you left the 'Name' field in
-  // your site's version of theme_signup_user_form().
+  // WARNING: This line is only valid if you are using the basic custom form
+  // or if your own custom form has a 'Name' field.
   return $form_data['Name'];
 }
 
Index: views/signup.views.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/signup/views/signup.views.inc,v
retrieving revision 1.11
diff -u -p -r1.11 signup.views.inc
--- views/signup.views.inc	22 Jul 2009 21:47:01 -0000	1.11
+++ views/signup.views.inc	10 Jan 2010 11:45:08 -0000
@@ -252,6 +252,20 @@ function signup_views_data() {
     'field' => array(
       'handler' => 'signup_handler_field_signup_user_form_data',
       'option' => 'string',
+      'additional fields' => array(
+        'sid' => array(
+          'table' => 'signup_log',
+          'field' => 'sid',
+        ),
+        'nid' => array(
+          'table' => 'signup_log',
+          'field' => 'nid',
+        ),
+        'uid' => array(
+          'table' => 'signup_log',
+          'field' => 'uid',
+        ),
+      ),
     ),
   );
 
Index: views/handlers/signup_handler_field_signup_user_form_data.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/signup/views/handlers/signup_handler_field_signup_user_form_data.inc,v
retrieving revision 1.2
diff -u -p -r1.2 signup_handler_field_signup_user_form_data.inc
--- views/handlers/signup_handler_field_signup_user_form_data.inc	7 Jan 2009 00:55:00 -0000	1.2
+++ views/handlers/signup_handler_field_signup_user_form_data.inc	10 Jan 2010 11:45:08 -0000
@@ -14,6 +14,17 @@ class signup_handler_field_signup_user_f
       '#default_value' => isset($this->options['form_data_fieldname']) ? $this->options['form_data_fieldname'] : '',
     );
   }
+  
+  /**
+   * Ensure we have the node nid and signup sid values to pass to 
+   * hook_signup_form_data_display_alter().
+   */
+  function query() {
+    $this->ensure_my_table();
+    $this->add_additional_fields();
+
+    parent::query();
+  }
 
   /**
    * Set each field value to "$key: $value" when rendering all fields.
@@ -25,8 +36,17 @@ class signup_handler_field_signup_user_f
       foreach ($values as $key => $result) {
         $form_data = unserialize($result->{$this->field_alias});
         if ($form_data) {
-          foreach ($form_data as $key => $value) {
-            $this->items[$result->{$this->field_alias}][$key] = check_plain(theme('signup_custom_data_field_text', $key, $value));
+          $sid = $result->{$this->aliases['sid']};
+          $nid = $result->{$this->aliases['nid']};
+          $uid = $result->{$this->aliases['uid']};
+          // Invoke hook_signup_form_data_display_alter() to let other modules
+          // (in particular pane modules) alter the data for output.
+          // This may involve internal data being removed.
+          drupal_alter('signup_form_data_display', $form_data, $nid, $sid, $uid, 'view');
+          foreach ($form_data as $pane_id => $pane_data) {
+            foreach ((array)$pane_data as $key => $value) {
+              $this->items[$result->{$this->field_alias}][$key] = check_plain(theme('signup_custom_data_field_text', $key, $value));
+            }
           }
         }
       }
