Index: signup.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/signup/signup.module,v
retrieving revision 1.151
diff -u -p -r1.151 signup.module
--- signup.module	8 Oct 2008 07:15:40 -0000	1.151
+++ signup.module	10 Oct 2008 15:00:14 -0000
@@ -856,16 +856,10 @@ function signup_node_tab($node) {
 }
 
 function _signup_print_current_signup($node, $signup_info) {
+  include_once(SIGNUP_PATH .'/signup.theme');
   $form_data = unserialize($signup_info->form_data);
-  $header = array(array('data' => t('Your signup information'), 'colspan' => 2));
-  $rows = array();
-  if (is_array($form_data)) {
-    $rows += signup_build_signup_data($form_data, 'table');
-  }
   $output = '';
-  if (!empty($rows)) {
-    $output .= theme('table', $header, $rows);
-  }
+  $output .= theme('signup_custom_data_table', $form_data);
   if (user_access('cancel own signups')) {
     $output .= drupal_get_form('signup_form_cancel', $node);
   }
@@ -1499,6 +1493,9 @@ function signup_sign_up_user($signup_for
 function signup_node_admin_page($node) {
   drupal_set_title(check_plain($node->title));
 
+  // Include the default copy of any theme functions used in here.
+  include_once(SIGNUP_PATH .'/signup.theme');
+
   // Prepare a table header that allows sorting on name and signup time.
   $header = array(
     array('data' => t('Name'), 'field' => 'u.name', 'sort' => 'asc'),
@@ -1521,7 +1518,7 @@ function signup_node_admin_page($node) {
     $form_data = unserialize($signed_up_user->form_data);
 
     // Compose the user data.
-    $signup_form_data = signup_build_signup_data($form_data);
+    $signup_form_data = theme('signup_custom_data', $form_data);
 
     // The username and the unique form identifier are different for
     // anon signups and registered user signups.  For registered users,
@@ -1908,56 +1905,22 @@ function _signup_admin_form($node = NULL
 }
 
 /**
- * Builds serialized user signup data into user-readable format.
- *
- * @param $data The serialized user signup data.
- * @param $type The type of formatting -- defaults to 'output'.
+ * Deprecated function to render signup data in a human-readable form.
  *
- * @return For table formatting, an array of table rows, for output formatting, raw user data in divs.
+ * @see theme_signup_custom_data()
+ * @see theme_signup_custom_data_email()
+ * @see theme_signup_custom_data_rows()
  */
 function signup_build_signup_data($data, $type = 'output') {
-
+  // Include the default copy of each theme function.
+  include_once(SIGNUP_PATH .'/signup.theme');
   switch ($type) {
-    case 'table':
-      static $rows = array();
-      // Loop through each first level element.
-      foreach ($data as $key => $value) {
-        // Element is nested, render it recursively.
-        if (is_array($value)) {
-          $rows[] = array('<div id="'. $key .'"></div>');
-          signup_build_signup_data($value, 'table');
-        }
-        else {
-          $rows[] = array($key .':', check_plain($value));
-        }
-      }
-      return $rows;
-    case 'output':
-      $output = '';
-      // Loop through each first level element.
-      foreach ($data as $key => $value) {
-        // Element is nested, render it recursively.
-        if (is_array($value)) {
-          $output .= '<div id="'. $key .'">'. signup_build_signup_data($value) .'</div>';
-        }
-        else {
-          $output .= '<div>'. $key .': '. check_plain($value) .'</div>';
-        }
-      }
-      return $output;
     case 'email':
-      $output = '';
-      // Loop through each first level element.
-      foreach ($data as $key => $value) {
-        // Element is nested, render it recursively.
-        if (is_array($value)) {
-          $output .= "\n\r". signup_build_signup_data($value, 'email') ."\n\r";
-        }
-        else {
-          $output .= $value ."\n\r";
-        }
-      }
-      return $output;
+      return theme('signup_custom_data_email', $data);
+    case 'table':
+      return theme('signup_custom_data_rows', $data);
+    default: 
+      return theme('signup_custom_data', $data);
   }
 }
 
@@ -2253,13 +2216,12 @@ function _signup_get_email_tokens($node,
 
   // Get the array of custom signup data (if any).
   if (is_array($signup->form_data)) {
-    $signup_data_array = $signup->form_data;
+    $signup_data = $signup->form_data;
   }
   else {
-    $signup_data_array = unserialize($signup->form_data);
+    $signup_data = unserialize($signup->form_data);
   }
-  if (!empty($signup_data_array)) {
-    $signup_data = signup_build_signup_data($signup_data_array, 'email');
+  if (!empty($signup_data)) {
     $tokens['%info'] = theme('signup_email_token_custom_data', $signup_data);
   }
   // Determine if this is an anon signup or not, and get the right info.
@@ -2273,3 +2235,21 @@ function _signup_get_email_tokens($node,
   }
   return $tokens;
 }
+
+/**
+ * Converts an arbitrary string into something safe to use for a CSS id.
+ *
+ * Stolen wholesale from the Zen theme. ;)
+ * @see zen_id_safe()
+ */
+function signup_id_safe($string) {
+  // Replace with dashes anything that isn't a-zA-Z, numbers, dashes, or
+  // underscores.
+  $string = strtolower(preg_replace('/[^a-zA-Z0-9_-]+/', '-', $string));
+  // If the first character is not a-z, add 'id' in front.
+  // Don't use ctype_alpha since it's locale aware.
+  if (!ctype_lower($string{0})) { 
+    $string = 'id' . $string;
+  }
+  return $string;
+}
Index: signup.theme
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/signup/signup.theme,v
retrieving revision 1.14
diff -u -p -r1.14 signup.theme
--- signup.theme	30 Sep 2008 21:10:43 -0000	1.14
+++ signup.theme	10 Oct 2008 15:00:14 -0000
@@ -104,7 +104,128 @@ function theme_signup_email_token_anonym
 
 /**
  * Returns the value to use for the %info email token for custom signup data.
+ *
+ * @param $signup_data
+ *   Array of custom data for a particular signup.
+ *
+ * @see theme_signup_user_form()
  */
 function theme_signup_email_token_custom_data($signup_data) {
-  return t('SIGNUP INFORMATION') . "\n\r" . $signup_data;
+  return t('SIGNUP INFORMATION') . "\n\r" . theme('signup_custom_data_email', $signup_data);
+}
+
+/**
+ * Renders custom signup data into unfiltered output for use in email.
+ * 
+ * WARNING: This theme function is recursive (it calls itself for
+ * nested data), so if you override it, be sure not to change the part
+ * where it does "call_user_func(__FUNCTION__)".
+ * 
+ * @param $data
+ *   Array of custom user signup data.
+ * 
+ * @return
+ *   Plain text output with newlines.
+ *
+ * @see theme_signup_user_form()
+ */
+function theme_signup_custom_data_email($data) {
+  $output = '';
+  // Loop through each first level element.
+  foreach ($data as $key => $value) {
+    if (is_array($value)) {
+      // Element is nested, render it recursively.
+      // Instead of the overhead of theme(), just call ourself directly.
+      $output .= "\n\r" . call_user_func(__FUNCTION__, $value) . "\n\r";
+    }
+    else {
+      $output .= $key . ': ' . $value . "\n\r";
+    }
+  }
+  return $output;
+}
+
+/**
+ * Renders custom signup user data into a table.
+ * 
+ * @param $data
+ *   Array of custom user signup data.
+ * 
+ * @return
+ *   The themed table with custom signup user data.
+ *
+ * @see theme_signup_user_form()
+ */
+function theme_signup_custom_data_table($data) {
+  $output = '';
+  if (is_array($data)) {
+    $header = array(array('data' => t('Your signup information'), 'colspan' => 2));
+    $rows = theme('signup_custom_data_rows', $data);
+    $output .= theme('table', $header, $rows);
+  }
+  return $output;
+}
+
+/**
+ * Renders custom signup user data into table rows.
+ *
+ * WARNING: This theme function is recursive (it calls itself for
+ * nested data), so if you override it, be sure not to change the part
+ * where it does "call_user_func(__FUNCTION__)".
+ * 
+ * @param $data
+ *   Array of custom user signup data.
+ * 
+ * @return
+ *   An array of table rows.
+ *
+ * @see theme_signup_user_form()
+ */
+function theme_signup_custom_data_rows($data) {
+  $rows = array();
+  // Loop through each first level element.
+  foreach ($data as $key => $value) {
+    if (is_array($value)) {
+      // Element is nested, render it recursively.
+      // Instead of the overhead of theme(), just call ourself directly.
+      $rows += call_user_func(__FUNCTION__, $value);
+    }
+    else {
+      $rows[] = array($key . ':', check_plain($value));
+    }
+  }
+  return $rows;
+}
+
+/**
+ * Renders custom signup user data into a human-readable format.
+ *
+ * WARNING: This theme function is recursive (it calls itself for
+ * nested data), so if you override it, be sure not to change the part
+ * where it does "call_user_func(__FUNCTION__)".
+ * 
+ * @param $data
+ *   Array of custom user signup data.
+ *
+ * @return
+ *   User data directly formatted in divs.
+ *
+ * @see theme_signup_user_form()
+ */
+function theme_signup_custom_data($data) {
+  $output = '';
+  // Loop through each first level element.
+  foreach ($data as $key => $value) {
+    $output .= '<div id="' . signup_id_safe($key) . '">';
+    if (is_array($value)) {
+      // Element is nested, render it recursively.
+      // Instead of the overhead of theme(), just call ourself directly.
+      $output .= call_user_func(__FUNCTION__, $value);
+    }
+    else {
+      $output .= $key . ': ' . check_plain($value);
+    }
+    $output .= "</div>\n";
+  }
+  return $output;
 }
