? panels/content_types/signup_form.inc.full
Index: signup.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/signup/signup.module,v
retrieving revision 1.197
diff -u -p -r1.197 signup.module
--- signup.module	14 Nov 2008 04:14:04 -0000	1.197
+++ signup.module	14 Nov 2008 05:41:21 -0000
@@ -162,14 +162,16 @@ function signup_theme() {
         'email' => NULL,
       ),
     ),
-    'signup_view_label' => array(
-      'file' => 'signup.module',
+    'signup_settings_view_label' => array(
+      'file' => 'admin.settings.inc',
+      'path' => drupal_get_path('module', 'signup') .'/includes',
       'arguments' => array(
         'view' => NULL,
       ),
     ),
     'signup_token_help' => array(
-      'file' => 'signup.module',
+      'file' => 'token_help.inc',
+      'path' => drupal_get_path('module', 'signup') .'/includes',
       'arguments' => array(
         'tokens' => NULL,
       ),
@@ -205,130 +207,13 @@ function signup_init() {
  * @see _signup_cron_autoclose()
  */
 function signup_cron() {
+  module_load_include('inc', 'signup', 'includes/cron');
   _signup_initialize_scheduler_backend();
   _signup_cron_send_reminders();
   _signup_cron_autoclose();
 }
 
 /**
- * Helper function that sends cron-based reminder e-mails.
- *
- * Invokes the method for the installed event/date backend module to get the
- * right query fragments, and builds a query to find all nodes that need a
- * reminder email. For each one, it loops over the users signed up for that
- * node and send off the emails.
- *
- * @see signup_cron()
- * @see signup_reminder_sql()
- * @see _signup_build_query()
- */
-function _signup_cron_send_reminders() {
-  $type_reminder_sql = array();
-  foreach (signup_content_types() as $type) {
-    $type_sql = signup_reminder_sql($type);
-    if (!empty($type_sql)) {
-      $type_reminder_sql[$type] = $type_sql;
-    }
-  }
-  if (empty($type_reminder_sql)) {
-    // No node types support reminder emails, so bail out now.
-    return;
-  }
-
-  $reminder_common_sql = array(
-    'primary' => '{node} n',
-    'fields' => array('n.title', 'n.nid', 'n.type', 's.reminder_email', 's.forwarding_email'),
-    'where' => array('s.send_reminder = 1', "n.type = '%s'"),
-    'joins' => array('INNER JOIN {signup} s ON s.nid = n.nid'),
-  );
-
-  $from = variable_get('site_mail', ini_get('sendmail_from'));
-
-  foreach ($type_reminder_sql as $type => $reminder_sql) {
-    $sql = _signup_build_query($reminder_common_sql, $reminder_sql);
-    $result = db_query($sql, $type);
-
-    // Grab each node, construct the email header and subject, and query
-    // the signup log to pull all users who are signed up for this node.
-    while ($node = db_fetch_object($result)) {
-      $subject = t('!node_type reminder: !title', array('!node_type' => node_get_types('name', $type), '!title' => $node->title));
-      $signups = db_query("SELECT u.name, u.mail, s_l.anon_mail, s_l.form_data FROM {signup_log} s_l INNER JOIN {users} u ON u.uid = s_l.uid WHERE s_l.nid = %d", $node->nid);
-
-      // Loop through the users, composing their customized message
-      // and sending the email.
-      while ($signup = db_fetch_object($signups)) {
-        $user_mail = _signup_get_email($signup);
-        $params = array(
-          'subject' => $subject,
-          'body' => $node->reminder_email,
-          'node' => $node,
-          'signup' => $signup,
-        );
-        if (module_exists('token')) {
-          $params['body'] = token_replace($params['body'], 'node', node_load($node->nid));
-        }
-        $language = user_preferred_language($signup);
-        drupal_mail('signup', 'signup_reminder_mail', $user_mail, $language, $params, $from);
-        watchdog('signup', 'Reminder for %title sent to %user_mail.', array('%title' => $node->title, '%user_mail' => $user_mail), WATCHDOG_NOTICE, l(t('view'), 'node/'. $node->nid));
-      }
-
-      // Reminders for this node are all sent, so mark it in the
-      // database so they're not sent again.
-      db_query("UPDATE {signup} SET send_reminder = 0 WHERE nid = %d", $node->nid);
-    }
-  }
-}
-
-/**
- * Helper function that handles auto-closing time-based nodes during cron.
- *
- * Loops over all the node types that are signup-enabled.  For each one, it
- * invokes the method for the installed event/date backend module to get the
- * right query fragments, and builds a query to find all nodes of that type
- * where signups should be closed (e.g. events that already started, etc).
- *
- * @see signup_cron()
- * @see signup_autoclose_sql()
- * @see _signup_build_query()
- */
-function _signup_cron_autoclose() {
-  $type_autoclose_sql = array();
-  foreach (signup_content_types() as $type) {
-    $type_sql = signup_autoclose_sql($type);
-    if (!empty($type_sql)) {
-      $type_autoclose_sql[$type] = $type_sql;
-    }
-  }
-  if (empty($type_autoclose_sql)) {
-    // No node types support auto-close, so bail out now.
-    return;
-  }
-
-  $autoclose_common_sql = array(
-    'primary' => '{node} n',
-    'fields' => array('n.nid', 'n.type'),
-    'where' => array('s.status = 1', "n.type = '%s'"),
-    'joins' => array('INNER JOIN {signup} s ON s.nid = n.nid'),
-  );
-
-  foreach ($type_autoclose_sql as $type => $autoclose_sql) {
-    $sql = _signup_build_query($autoclose_common_sql, $autoclose_sql);
-    $result = db_query($sql, $type);
-
-    // Loop through the results, calling the signup closing function.
-    while ($signup = db_fetch_object($result)) {
-      signup_close_signup($signup->nid, $cron = 'yes');
-      $node = node_load($signup->nid);
-      foreach (module_implements('signup_close') as $module) {
-        $function = $module .'_signup_close';
-        $function($node);
-      }
-      watchdog('signup', 'Signups closed for %title by cron.', array('%title' => $node->title), WATCHDOG_NOTICE, l(t('view'), 'node/'. $node->nid));
-    }
-  }
-}
-
-/**
  * Private query builder helper function.
  *
  * @param $common_sql
@@ -415,13 +300,16 @@ function signup_help($path, $arg) {
  * @ingroup signup_core
  */
 function signup_menu() {
+  $path = drupal_get_path('module', 'signup') .'/includes';
   $items = array();
   $items['admin/settings/signup'] = array(
     'description' => 'Configure settings for signups.',
     'access arguments' => array('administer all signups'),
     'page callback' => 'drupal_get_form',
-    'page arguments' => array('signup_settings_page'),
+    'page arguments' => array('signup_settings_form'),
     'title' => user_access('access administration pages') ? 'Signup' : 'Signup settings',
+    'file' => 'admin.settings.inc',
+    'file path' => $path,
   );
 
   $items['admin/content/signup'] = array(
@@ -429,6 +317,8 @@ function signup_menu() {
     'access arguments' => array('administer all signups'),
     'page callback' => 'signup_admin_page',
     'title' => 'Signup administration',
+    'file' => 'admin.signup_administration.inc',
+    'file path' => $path,
   );
 
   // Conditionally add any available signup-related tabs to nodes.
@@ -440,6 +330,8 @@ function signup_menu() {
     'access arguments' => array(1, 'node'),
     'type' => MENU_LOCAL_TASK,
     'weight' => 19,
+    'file' => 'node_output.inc',
+    'file path' => $path,
   );
   $items['node/%node/signups'] = array(
     'title' => 'Signups',
@@ -449,6 +341,8 @@ function signup_menu() {
     'access arguments' => array(1, 'admin'),
     'type' => MENU_LOCAL_TASK,
     'weight' => 20,
+    'file' => 'node_admin.inc',
+    'file path' => $path,
   );
   $items['node/%node/signups/confirm'] = array(
     'page callback' => 'drupal_get_form',
@@ -456,6 +350,8 @@ function signup_menu() {
     'access callback' => '_signup_menu_access',
     'access arguments' => array(1, 'admin'),
     'type' => MENU_CALLBACK,
+    'file' => 'node_admin.inc',
+    'file path' => $path,
   );
   $items['node/%node/signup-broadcast'] = array(
     'title' => 'Signup broadcast',
@@ -465,6 +361,8 @@ function signup_menu() {
     'access arguments' => array(1, 'email'),
     'type' => MENU_LOCAL_TASK,
     'weight' => 21,
+    'file' => 'broadcast.inc',
+    'file path' => $path,
   );
 
   // Add extra menu items if we're not using views.
@@ -597,6 +495,7 @@ function signup_user($type, &$edit, &$us
 function signup_form_alter(&$form, &$form_state, $form_id) {
   if (!empty($form['type']['#value'])) {
     if ($form_id == $form['type']['#value'] .'_node_form') {
+      module_load_include('inc', 'signup', 'includes/node_form');
       signup_alter_node_form($form, $form_state, $form_id);
     }
   }
@@ -626,178 +525,6 @@ function signup_form_node_type_form_alte
 }
 
 /**
- * Alters the node form to inject the appropriate per-node signup settings.
- */
-function signup_alter_node_form(&$form, &$form_state, $form_id) {
-  global $user;
-
-  // Load the node if it already exists.
-  if (!empty($form['nid']['#value'])) {
-    $node = node_load($form['nid']['#value']);
-  }
-  else {
-    $node = NULL;
-  }
-  $node_type = $form['type']['#value'];
-
-  $signup_type_default = variable_get('signup_node_default_state_'. $node_type, 'disabled');
-  if (!empty($node)) {
-    $node_scheduler = _signup_get_node_scheduler($node);
-  }
-  else {
-    $node_scheduler = _signup_get_node_type_scheduler($node_type);
-  }
-  $node_has_date = $node_scheduler != 'none';
-
-  // If signups are possible, and the current user either has the global
-  // 'administer all signups' permission or has the 'administer signups
-  // for own content' permission and is creating new content or editing
-  // their own content, add a fieldset for signup-related settings.
-
-  // Signups are possible if they're not explicitly disallowed for this
-  // node type, or if this node is already signup-enabled (in case an
-  // admin erroneously marks a node-type to disallow signups when there
-  // are already nodes of that type with signups enabled).
-  $signups_possible = $signup_type_default != 'disabled' || (!empty($node) && !empty($node->signup));
-  $admin_all = user_access('administer all signups');
-  $admin_own = user_access('administer signups for own content') && (empty($node) || ($node->uid == $user->uid));
-  if ($signups_possible && ($admin_all || $admin_own)) {
-    $form['signup'] = array(
-      '#type' => 'fieldset',
-      '#title' => t('Signup settings'),
-      '#collapsible' => TRUE,
-      '#collapsed' => TRUE,
-      '#weight' => 30,
-    );
-
-    // Figure out what the options should be.  If there are already
-    // people signed-up for this node, we need a 3rd choice: disable
-    // signups and remove all signup data.
-    $has_signups = !empty($node) && db_result(db_query("SELECT COUNT(*) from {signup_log} WHERE nid = %d", $node->nid));
-    $radio_options[1] = t('Enabled');
-    if ($has_signups) {
-      $radio_options[0] = t('Disabled, but save existing signup information');
-      $radio_options[2] = t('Disabled, and remove all signup information') .' <strong>('. t('This can not be undone, use with extreme caution!') .')</strong>';
-    }
-    else {
-      $radio_options[0] = t('Disabled');
-    }
-
-    // Figure out what the default selection for signups should be.
-    if (isset($node->signup)) {
-      $default_option = $node->signup;
-    }
-    else {
-      $default_option = $signup_type_default == 'enabled_on' ? 1 : 0;
-    }
-    if ($default_option == 1) {
-      $hint = t('If enabled, you can control whether users may sign up by visiting the !signups tab and toggling if signups are %open or %closed for this %node_type.', array('!signups' => !empty($node) ? l(t('Signups'), 'node/'. $node->nid .'/signups') : theme('placeholder', t('Signups')), '%open' => t('open'), '%closed' => t('closed'), '%node_type' => node_get_types('name', $node_type)));
-    }
-    else {
-      $hint = '';
-    }
-    // Add the form element to toggle if signups are allowed.
-    $form['signup']['signup_enabled'] = array(
-      '#type' => 'radios',
-      '#options' => $radio_options,
-      '#default_value' => $default_option,
-      '#description' => $hint .'<div class="js-hide">'. t('If disabled, all of the other signup settings will be ignored.') .'</div>',
-      '#prefix' => '<div class="signup-allow-radios">',
-      '#suffix' => '</div>',
-    );
-
-    // If JS is enabled, signup.css will hide all the settings on page
-    // load if signups aren't enabled on this node.
-    $settings_class = "signup-node-settings";
-    if ($default_option != 1) {
-      $settings_class .= " js-hide";
-    }
-
-    // Add the actual settings.  We wrap this in a div to make it easy
-    // to use jQuery to hide these settings when signups are disabled.
-    drupal_add_js(drupal_get_path('module', 'signup') .'/js/node_form.js');
-    drupal_add_css(drupal_get_path('module', 'signup') .'/signup.css');
-    $form['signup']['node_settings'] = array(
-      '#prefix' => '<div class="'. $settings_class .'">',
-      '#suffix' => '</div>',
-    );
-    $form['signup']['node_settings']['settings'] = signup_node_settings_form(array(), $node, $node_type, $node_has_date);
-  }
-}
-
-/**
- * Submits the cancel signup form
- *
- * @ingroup signup_core
- *
- * @param $form_id The ID of the form being submitted.
- * @param $form_values The constructed form values array of the submitted form.
- */
-function signup_cancel_form_submit($form, &$form_state) {
-  $uid = $form_state['values']['uid'];
-  $nid = $form_state['values']['nid'];
-  $anon_mail = NULL;
-  if (isset($form_state['values']['signup_anon_mail'])) {
-    $anon_mail = $form_state['values']['signup_anon_mail'];
-  }
-  signup_cancel_signup($uid, $nid, $anon_mail);
-}
-
-/**
- * Executes the user signup form
- *
- * @ingroup signup_core
- *
- * @param $form_id The ID of the form being submitted.
- * @param $form_values The constructed form values array of the submitted form.
- */
-function signup_form_submit($form, &$form_state) {
-  if (isset($form_state['values']['signup_username'])) {
-    $account = user_load(array('name' => $form_state['values']['signup_username']));
-    $form_state['values']['uid'] = $account->uid;
-  }
-  signup_sign_up_user($form_state['values']);
-}
-
-/**
- * Validates the email address on the anonymous user signup form.
- *
- * @param $form
- *   Form array for the anonymous user email field.
- * @param $nid
- *   Node id of the node the user is trying to signup for.
- */
-function signup_form_validate_anon($form, $form_state) {
-  $nid = $form_state['values']['nid'];
-  $anon_mail = $form_state['values']['signup_anon_mail'];
-  signup_validate_anon_email($nid, $anon_mail, 'signup_anon_mail');
-}
-
-/**
- * Validates the username on the admin form to signup another user.
- *
- * @param $form
- *   Form array for the username field.
- * @param $nid
- *   Node id of the node the user is being signed up for.
- */
-function signup_form_validate_username($form, $form_state) {
-  $nid = $form_state['values']['nid'];
-  $username = $form_state['values']['signup_username'];
-  $account = user_load(array('name' => $username));
-  if (empty($account)) {
-    form_set_error('signup_username', t('User %user_name does not exist.', array('%user_name' => $username)));
-  }
-  elseif (!user_access('sign up for content', $account)) {
-    form_set_error('signup_username', t('User !user does not have permission to sign up.', array('!user' => theme('username', $account))));
-  }
-  elseif (db_result(db_query("SELECT COUNT(*) FROM {signup_log} WHERE uid = %d AND nid = %d", $account->uid, $nid)) > 0) {
-    $node = node_load($nid);
-    form_set_error('signup_username', t('User !user is already signed up for %title', array('!user' => theme('username', $account), '%title' => $node->title)));
-  }
-}
-
-/**
  * @defgroup signup_nodeapi Functions for nodeapi integration
  */
 
@@ -810,6 +537,7 @@ function signup_nodeapi(&$node, $op, $te
   switch ($op) {
     case 'insert':
     case 'update':
+      module_load_include('inc', 'signup', 'includes/node_form');
       signup_save_node($node, $op);
       break;
 
@@ -851,6 +579,7 @@ function signup_nodeapi(&$node, $op, $te
       // a page, not during the view from comment validation, etc.
       if ($page && _signup_needs_output($node) &&
           variable_get('signup_form_location', 'node') == 'node') {
+        module_load_include('inc', 'signup', 'includes/node_output');
         $output = _signup_node_output($node);
         if (!empty($output)) {
           // Save into a node property for retrieval from the theme layer.
@@ -867,247 +596,6 @@ function signup_nodeapi(&$node, $op, $te
 }
 
 /**
- * Save signup-related information when a node is created or edited.
- *
- * This is a helper function invoked via signup_nodeapi().  It ensures that
- * the currently selected signup values are properly saved into the database.
- * If the node is signup-enabled, the per-node configuration values are saved
- * to the {signup} table. If signups are disabled, the record from {signup} is
- * cleared.  If the signup administrator editing the node decided to remove
- * all signup data, all the records from the {signup_log} table for this node
- * are also removed.  This function is also responsible for testing if the
- * node * has a start time and if the autoclose period has already begun, in
- * which case signups are closed.  Finally, if the signup limit was changed
- * while editing the node, the function compares the limit against the current
- * total number of signups and opens or closes signups as appropriate.
- *
- * @param $node
- *   The node object given to signup_nodeapi().
- * @param $op
- *   The hook_nodeapi() operation, either 'insert' or 'update'.
- *
- * @return
- *   Nothing, this function is expected to update the database.
- *
- * @see signup_nodeapi()
- */
-function signup_save_node($node, $op) {
-  // See if the form indicates that signups are enabled on this node.
-  if (isset($node->signup_enabled)) {
-    if ($node->signup_enabled == 1) {
-      $values = array(
-        $node->signup_forwarding_email,
-        $node->signup_send_confirmation,
-        $node->signup_confirmation_email,
-        $node->signup_close_signup_limit,
-      );
-      // 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.
-      $values[] = isset($node->signup_send_reminder) ? $node->signup_send_reminder : 0;
-      $values[] = isset($node->signup_reminder_days_before) ? $node->signup_reminder_days_before : 0;
-      $values[] = isset($node->signup_reminder_email) ? $node->signup_reminder_email : '';
-    }
-  }
-  elseif ($op == 'insert' && variable_get('signup_node_default_state_'. $node->type, 'disabled') == 'enabled_on') {
-    // The form doesn't include any information about signups, but the node
-    // type is signup-enabled. This would happen if a user without any signup
-    // admin permissions creates a node that has been signup-enabled based on
-    // the node type. In this case, we use the site-wide default signup
-    // settings.
-    $defaults = db_fetch_object(db_query("SELECT * from {signup} WHERE nid = 0"));
-    $values = array(
-      $defaults->forwarding_email,
-      $defaults->send_confirmation,
-      $defaults->confirmation_email,
-      $defaults->close_signup_limit,
-      $defaults->send_reminder,
-      $defaults->reminder_days_before,
-      $defaults->reminder_email,
-    );
-  }
-
-  if (isset($values)) {
-    // If $values is set, we need to save them to the DB.
-
-    // Before we update the DB, see if the limit is changing, so we can take
-    // appropriate action after we update to the new settings.
-    $has_signup_record = FALSE;
-    $limit_changed = FALSE;
-    if ($op == 'update') {
-      $signup = db_fetch_object(db_query("SELECT close_signup_limit, status FROM {signup} WHERE nid = %d", $node->nid));
-      $cur_limit = $signup->close_signup_limit;
-      $node->signup_status = $signup->status;
-      if ($cur_limit !== FALSE) {
-        $has_signup_record = TRUE;
-        $limit_changed = $cur_limit != $node->signup_close_signup_limit;
-      }
-    }
-
-    // See if we need to update an existing record or insert a new one.
-    // Either way, we always the nid as the final value. The nid will either
-    // be used as the last column in the INSERT, or the argument to the WHERE
-    // clause for the UPDATE.
-    $values[] = $node->nid;
-    if ($has_signup_record) {
-      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);
-    }
-    else {
-      db_query("INSERT INTO {signup} (forwarding_email, send_confirmation, confirmation_email, close_signup_limit, send_reminder, reminder_days_before, reminder_email, nid) VALUES ('%s', %d, '%s', %d, %d, %d, '%s', %d)", $values);
-    }
-    if (_signup_node_completed($node) && $node->signup_status) {
-      // If this is an time-based node, and it's already past the close in
-      // advance time, and signups are still open, close them now (and don't
-      // consider the limit for changing the status).
-      signup_close_signup($node->nid);
-      drupal_set_message(t('%node_type start time is already past the signup close-in-advance time, signups now closed.', array('%node_type' => node_get_types('name', $node->type))));
-    }
-    elseif ($limit_changed) {
-      $node->signup_total = db_result(db_query("SELECT COUNT(*) FROM {signup_log} WHERE nid = %d", $node->nid));
-      _signup_check_limit($node, 'limit');
-    }
-  }
-  elseif ($op == 'update' && isset($node->signup_enabled)) {
-    // $values was not set, because signups are now disabled on this node.
-    switch ($node->signup_enabled) {
-      case 2: // Disabled, and delete {signup_log}, too
-        db_query("DELETE FROM {signup_log} WHERE nid = %d", $node->nid);
-        // No break, fall through and remove from {signup} too.
-      case 0: // Disabled, but leave {signup_log} alone
-        if ($has_signup_record) {
-          db_query("DELETE FROM {signup} WHERE nid = %d", $node->nid);
-        }
-        break;
-    }
-  }
-}
-
-/**
- * Generate all the signup-related output for a given node.
- *
- * Because of the global setting to control if the signup details and form
- * appear at the bottom of the node or on a separate tab, this function is
- * shared by multiple callers.
- *
- * @param $node
- *   The fully loaded node object.
- * @param $type
- *   The kind of output would we render: can be either 'node' or 'tab'.
- *
- * @return
- *   The fully rendered HTML for all signup-related forms and info.
- *
- * @see signup_nodeapi()
- * @see signup_node_tab()
- *
- * @todo This needs to be much more theme-friendly.
- *
- */
-function _signup_node_output($node, $type = 'node') {
-  global $user;
-  $output = theme('signup_node_output_header', $node);
-  // The node has been closed for signups, and the user has
-  // signup permissions.  Let them know it's closed.
-  if (!$node->signup_status) {
-    if (user_access('sign up for content')) {
-      $current_signup = '';
-      // If they're logged in and already signed up, show their current
-      // signup info and give them the option to cancel.
-      if ($user->uid) {
-        $result = db_query("SELECT signup_time, form_data FROM {signup_log} WHERE uid = %d AND nid = %d", $user->uid, $node->nid);
-        $signup_info = db_fetch_object($result);
-        if (!empty($signup_info)) {
-          $current_signup = _signup_print_current_signup($node, $signup_info);
-        }
-      }
-      $output .= theme('signup_signups_closed', $node, $current_signup);
-    }
-  }
-  else {
-    $fieldset = $type == 'node' ? TRUE : FALSE;
-    if ($user->uid == 0) {
-      // This is an anonymous user.
-      if (user_access('sign up for content')) {
-        // If they can signup, render the anonymous sigup form.
-        $output .= drupal_get_form('signup_form', $node, 'anon', $fieldset);
-      }
-      else {
-        // If not, then display the appropriate login/register link if the
-        // default authenticated user role can signup.
-        $anon_login_text = '';
-        $signup_roles = user_roles(FALSE, 'sign up for content');
-        if (!empty($signup_roles[DRUPAL_AUTHENTICATED_RID])) {
-          $token_array = array(
-            '!login' => l(t('login'), 'user/login', array('query' => drupal_get_destination())),
-            '!register' => l(t('register'), 'user/register', array('query' => drupal_get_destination())),
-            '%node_type' => node_get_types('name', $node->type),
-          );
-          if (variable_get('user_register', 1) == 0) {
-            $anon_login_text = t('Please !login to sign up for this %node_type.', $token_array);
-          }
-          else {
-            $anon_login_text = t('Please !login or !register to sign up for this %node_type.', $token_array);
-          }
-        }
-        $output .= theme('signup_anonymous_user_login_text', $anon_login_text);
-      }
-    }
-    else {
-      // An authenticated user.
-
-      // See if the user is already signed up for this node.
-      $result = db_query("SELECT signup_time, form_data FROM {signup_log} WHERE uid = %d AND nid = %d", $user->uid, $node->nid);
-      $signup_info = db_fetch_object($result);
-      if (empty($signup_info)) {
-        // Not yet signed up
-        if (user_access('sign up for content')) {
-          // User has permission to do so, so give them the form.
-          $output .= drupal_get_form('signup_form', $node, 'auth', $fieldset);
-        }
-      }
-      else {
-        // Already signed up, display their info.
-        $output .= _signup_print_current_signup($node, $signup_info);
-      }
-    }
-  }
-
-  // How should the list of signed-up users be displayed, if at all?
-  $display_list = variable_get('signup_display_signup_user_list', 'signup');
-
-  // If the user has the view signups perm and the admin decides to display
-  // the list at the bottom of the page, display the current signups.
-  if (user_access('view all signups')) {
-    if ($display_list == 'signup') {
-      // Admin wants the hard-coded signup listing.
-      $registered_query = db_query("SELECT u.uid, u.name, s.signup_time, s.form_data FROM {signup_log} s INNER JOIN {users} u ON u.uid = s.uid WHERE s.nid = %d AND u.uid <> 0", $node->nid);
-      $registered_signups = array();
-      while ($signed_up_user = db_fetch_object($registered_query)) {
-        $registered_signups[] = $signed_up_user;
-      }
-      $anon_query = db_query("SELECT * FROM {signup_log} WHERE nid = %d AND uid = 0", $node->nid);
-      $anon_signups = array();
-      while ($signed_up_user = db_fetch_object($anon_query)) {
-        $anon_signups[] = $signed_up_user;
-      }
-      $output .= theme('signup_user_list', $node, $registered_signups, $anon_signups);
-    }
-    elseif ($display_list == 'embed-view' && module_exists('views')) {
-      $signup_view = variable_get('signup_user_list_view', 'signup_user_list:page');
-      $signup_view_parts = explode(':', $signup_view);
-      $view_name = $signup_view_parts[0];
-      $view_display = $signup_view_parts[1];
-      $view = views_get_view($view_name);
-      $view_args = array($node->nid);
-      $output .=  $view->preview($view_display, $view_args);
-    }
-    // Otherwise, they're on their own, and either don't want it displayed at
-    // all, or they want to handle where/how it's displayed via views.
-  }
-  return $output;
-}
-
-/**
  * Helper function that determines if a given node should have any
  * signup-related output.
  *
@@ -1130,338 +618,6 @@ function _signup_needs_output($node) {
   return TRUE;
 }
 
-function signup_node_tab($node) {
-  drupal_set_title(check_plain($node->title));
-  return _signup_node_output($node, 'tab');
-}
-
-/**
- * Helper function to display the current user's signup information.
- *
- * Contains the logic to determine what should be displayed, then invokes the
- * appropriate form builder and theme functions.
- *
- * @param $node
- *   The fully-loaded node object to display signup data about.
- * @param $signup_info
- *   Database object with information about the signup to display.
- *
- * @return
- *   Themed HTML to output for the given node and signup.
- *
- * @see theme_signup_current_signup()
- */
-function _signup_print_current_signup($node, $signup_info) {
-  global $user;
-  // Generate an array of data about the current signup for the theme function.
-  $signup_data = array();
-  $signup_data['custom_data'] = unserialize($signup_info->form_data);
-  $signup_data['signup_timestamp'] = $signup_info->signup_time;
-
-  // See if the current user is allowed to cancel their signup, and if so,
-  // render the HTML for the cancel form (which is just a single button).
-  $cancel_signup_form = '';
-  if (user_access('cancel own signups')) {
-    $cancel_signup_form = drupal_get_form('signup_cancel_form', $node, $user);
-  }
-
-  // Hand off everything to the theme function for actual HTML generation.
-  return theme('signup_current_signup', $signup_data, $cancel_signup_form);
-}
-
-/**
- * @defgroup signup_callback
- *   Functions which are the menu callbacks for this module.
- */
-
-/**
- * Builder function for the signup form
- * @ingroup signup_callback
- *
- * @param $node
- *   The fully loaded node object.
- * @param $signup_type
- *   Determines what kind of signup to generate a form for. Possible values:
- *    'auth' -- regular authenticated user signup form
- *    'anon' -- anonymous user signup form (includes required email field).
- *    'admin' -- admin form to signup another user (includes user selector).
- * @param $fieldset
- *   Boolean that indicates if the signup form should be in a fieldset.
- */
-function signup_form(&$form_state, $node, $signup_type = 'auth', $fieldset = TRUE) {
-  global $user;
-  module_load_include('theme', 'signup', 'theme/signup');
-
-  $form = array();
-  $form['nid'] = array('#type' => 'value', '#value' => $node->nid);
-  $form['uid'] = array('#type' => 'value', '#value' => $user->uid);
-
-  if ($fieldset) {
-    $form['collapse'] = array(
-      '#type' => 'fieldset',
-      '#collapsible' => TRUE,
-      '#collapsed' => variable_get('signup_fieldset_collapsed', 1),
-    );
-    if ($signup_type == 'admin') {
-      $form['collapse']['#title'] = t('Sign up another user');
-    }
-    else {
-      $form['collapse']['#title'] = t('Sign up for @title', array('@title' => $node->title));
-    }
-  }
-  else {
-    $form['collapse'] = array();
-  }
-
-  $signup_form = array();
-  if ($signup_type == 'anon') {
-    $anon_form = array();
-    $anon_form['signup_anon_mail'] = array(
-      '#type' => 'textfield',
-      '#title' => t('Email'),
-      '#description' => t('An e-mail address is required for users who are not registered at this site. If you are a registered user at this site, please !login to sign up for this %node_type.', array('!login' => l(t('login'), 'user/login', array('query' => drupal_get_destination())), '%node_type' => node_get_types('name', $node->type))),
-      '#size' => 40,
-      '#maxlength' => 255,
-      '#required' => TRUE,
-    );
-    $validate_handler = 'signup_form_validate_anon';
-    $signup_form += $anon_form;
-  }
-  elseif ($signup_type == 'admin') {
-    $admin_form = array();
-    $admin_form['signup_username'] = array(
-      '#title' => t('Username'),
-      '#type' => 'textfield',
-      '#autocomplete_path' => 'user/autocomplete',
-      '#maxlength' => USERNAME_MAX_LENGTH,
-      '#size' => 40,
-      '#weight' => -1,
-      '#required' => TRUE,
-    );
-    $validate_handler = 'signup_form_validate_username';
-    $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']);
-    }
-  }
-  $signup_form += $signup_themed_form;
-
-  $form['collapse']['signup_user_form'] = $signup_form;
-  $form['collapse']['submit'] = array(
-    '#type' => 'submit',
-    '#value' => t('Sign up'),
-  );
-  if (!empty($validate_handler)) {
-    $form['#validate'][] = $validate_handler;
-  }
-  return $form;
-}
-
-/**
- * Builder function for the cancel signup form
- * @ingroup signup_callback
- */
-function signup_cancel_form(&$form_state, $node, $account) {
-  $form['nid'] = array('#type' => 'value', '#value' => $node->nid);
-  $form['uid'] = array('#type' => 'value', '#value' => $account->uid);
-  if (!empty($account->anon_mail)) {
-    $form['signup_anon_mail'] = array('#type' => 'value', '#value' => $account->anon_mail);
-  }
-  $form['submit'] = array('#type' => 'submit', '#value' => t('Cancel signup'));
-  return $form;
-}
-
-/**
- * Prints the admin signup overview page located at admin/content/signup
- * @ingroup signup_callback
- */
-function signup_admin_page() {
-  drupal_add_css(drupal_get_path('module', 'signup') .'/signup.css');
-  $filter_status_form = drupal_get_form('signup_filter_status_form');
-  $signup_admin_form = drupal_get_form('signup_admin_form');
-  return theme('signup_admin_page', $filter_status_form, $signup_admin_form);
-}
-
-function signup_filter_status_form(&$form_state) {
-  $options = array(
-    'all' => t('All'),
-    'open' => t('Open'),
-    'closed' => t('Closed'),
-  );
-  if (empty($_SESSION['signup_status_filter'])) {
-    $_SESSION['signup_status_filter'] = 'all';
-  }
-  $form['filter'] = array(
-    '#type' => 'select',
-    '#title' => t('Filter by signup status'),
-    '#options' => $options,
-    '#default_value' => $_SESSION['signup_status_filter'],
-  );
-  $form['submit'] = array('#type' => 'submit', '#value' => t('Filter'));
-//  $form['#redirect'] = FALSE;
-  return $form;
-}
-
-function signup_filter_status_form_submit($form, &$form_state) {
-  $_SESSION['signup_status_filter'] = $form_state['values']['filter'];
-}
-
-function signup_admin_form($form_state) {
-  // Figure out if the current user has permission to use signup broadcast.
-  $access_broadcast = user_access('email all signed up users');
-
-  $header = array(
-    array('data' => t('Title'), 'field' => 'n.title', 'sort' => 'asc'),
-    array('data' => t('Signups'), 'field' => 'signup_total'),
-    array('data' => t('Limit'), 'field' => 'signup_close_signup_limit'),
-    array('data' => t('Status'), 'field' => 'signup_status'),
-    array('data' => t('Operations')),
-  );
-
-  $start_column = signup_admin_form_header();
-  if (!empty($start_column)) {
-    array_unshift($header, $start_column);
-  }
-
-  list($sql, $sql_count) = signup_admin_form_sql();
-
-  $form['header']['#value'] = $header;
-
-  $sql .= tablesort_sql($header);
-
-  $result = pager_query($sql, 25, 0, $sql_count);
-
-  // Loop through the signup nodes, and generate our form elements
-  while ($signup_node = db_fetch_object($result)) {
-    $row = array();
-    if (!empty($start_column)) {
-      $row['start'] = signup_admin_form_extra($signup_node);
-    }
-
-    // Instead of duplicating the logic from the node/N/signups admin
-    // form, we just call that form builder here and lift the elements
-    // we need directly from that.
-    $node_admin_form = signup_node_admin_summary_form(array(), $signup_node);
-    $row['title'] = array(
-      '#type' => 'markup',
-      '#value' => l($signup_node->title, "node/$signup_node->nid"),
-    );
-    $row['status'] = $node_admin_form['status'];
-    $row['total'] = array(
-      '#type' => 'markup',
-      '#value' => $signup_node->signup_total,
-    );
-    $row['limit'] = $node_admin_form['limit'];
-    $op_links = l(t('View signups'), "node/$signup_node->nid/signups");
-    if ($access_broadcast) {
-      $op_links .= '<br />';
-      $options['attributes']['title'] = t('Send an email message to all users who signed up.');
-      $op_links .= l(t('Signup broadcast'), "node/$signup_node->nid/signup-broadcast", $options);
-    }
-    $row['operations'] = array(
-      '#type' => 'markup',
-      '#value' => $op_links,
-    );
-    $form['nids'][$signup_node->nid] = $row;
-  }
-  $form['#tree'] = TRUE;
-  $form['submit'] = array(
-    '#type' => 'submit',
-    '#value' => t('Update'),
-  );
-  return $form;
-}
-
-function signup_admin_form_header() {
-  if (module_exists('date')) {
-    // If we're using CCK date, we can't sort since the date field used for
-    // each content type can come from different tables.
-    return array('data' => t('Start'), 'field' => NULL);
-  }
-  elseif (module_exists('event')) {
-    // If we've got event, but not date, we can sort by e.event_start.
-    return array('data' => t('Start'), 'field' => 'e.event_start');
-  }
-  // If we've got no scheduling backend at all, there's no start time column.
-  return array();
-}
-
-function signup_admin_form_extra($signup_node) {
-  return array(
-    '#type' => 'markup',
-    '#value' => signup_format_date($signup_node),
-  );
-}
-
-function signup_admin_form_sql() {
-  $admin_common_sql = array(
-    'primary' => '{node} n',
-    'fields' => array(
-      'n.nid',
-      'n.title',
-      'n.type',
-      's.status AS signup_status',
-      'COUNT(s_l.nid) AS signup_total',
-      's.close_signup_limit AS signup_close_signup_limit',
-    ),
-    'group_by' => array(
-      'n.nid',
-      'n.title',
-      'signup_status',
-      'signup_close_signup_limit',
-    ),
-    'joins' => array(
-      'INNER JOIN {signup} s ON s.nid = n.nid',
-      'LEFT JOIN {signup_log} s_l ON s.nid = s_l.nid',
-    ),
-  );
-
-  $type = $_SESSION['signup_status_filter'];
-  if ($type == 'open') {
-    $filter_status = 1;
-  }
-  elseif ($type == 'closed') {
-    $filter_status = 0;
-  }
-  if (isset($filter_status)) {
-    $admin_common_sql['where'] = array("s.status = $filter_status");
-  }
-
-  // Get the right query elements from the currently installed backend
-  $admin_sql = array();
-  foreach (signup_content_types() as $type) {
-    $admin_sql = array_merge_recursive($admin_sql, signup_admin_sql($type));
-  }
-
-  // Build the main query.
-  $sql = _signup_build_query($admin_common_sql, $admin_sql);
-
-  // Construct the proper pager query using just the WHERE clauses (if any).
-  $all_fragments = array_merge_recursive($admin_common_sql, $admin_sql);
-  $sql_count = "SELECT COUNT(s.nid) FROM {signup} s";
-  if (!empty($all_fragments['where'])) {
-    $sql_count .= ' WHERE '. implode(' AND ', $all_fragments['where']);
-  }
-
-  return array(db_rewrite_sql($sql), db_rewrite_sql($sql_count, 's'));
-}
-
-function signup_admin_form_submit($form, &$form_state) {
-  foreach ($form_state['values']['nids'] as $nid => $values) {
-    $values['nid'] = $nid;
-    $temp_state['values'] = $values;
-    signup_node_admin_summary_form_submit($form, $temp_state);
-  }
-}
-
 /**
  * Callback function for canceling signups
  * @ingroup signup_callback
@@ -1514,219 +670,6 @@ function signup_open_signup($nid, $cron 
 }
 
 /**
- * Form builder for the settings page under admin/setttings/signup
- */
-function signup_settings_page() {
-  if (signup_site_has_dates()) {
-    $form['signup_close_early'] = array(
-      '#title' => t('Close x hours before'),
-      '#type' => 'textfield',
-      '#default_value' => variable_get('signup_close_early', 1),
-      '#size' => 5, '#maxlength' => 10,
-      '#description' => t('The number of hours before the event which signups will no longer be allowed. Use negative numbers to close signups after the event start (example: -12).'),
-    );
-  };
-  $form['node_defaults'] = array(
-    '#type' => 'fieldset',
-    '#title' => t('Default signup information'),
-    '#description' => t('New signup-enabled nodes will start with these settings.'),
-    '#collapsible' => TRUE,
-  );
-  $form['node_defaults']['signup_node_settings_form'] = signup_node_settings_form(array(), NULL, NULL, signup_site_has_dates());
-
-  $form['adv_settings'] = array(
-    '#type' => 'fieldset',
-    '#title' => t('Advanced settings'),
-    '#collapsible' => TRUE,
-    '#collapsed' => TRUE,
-  );
-  $form['adv_settings']['signup_date_format'] = array(
-    '#title' => t('Format string for displaying signup-related dates'),
-    '#type' => 'select',
-    '#options' => array(
-      'small' => t('Small'),
-      'medium' => t('Medium'),
-      'large' => t('Large'),
-    ),
-    '#default_value' => variable_get('signup_date_format', 'small'),
-    '#description' => t('Whenever this module needs to print a date (both in the administrative interface, and in the various e-mail messages it can send), this setting controls which date format string to use. The format strings are defined at the <a href="!settings_url">Date and time settings page</a>.', array('!settings_url' => url('admin/settings/date-time'))),
-  );
-  $form['adv_settings']['signup_form_location'] = array(
-    '#title' => t('Location of the signup form and related information'),
-    '#type' => 'radios',
-    '#options' => array(
-      'node' => t('At the bottom of each node'),
-      'tab' => t('On a separate %sign_up tab', array('%sign_up' => t('Sign up'))),
-      'none' => t('Do not display signup form'),
-    ),
-    '#default_value' => variable_get('signup_form_location', 'node'),
-    '#description' => t('On every signup-enabled node, users with permission to
- sign up can be presented with a form and additional signup-related information. This setting controls where this information should be displayed: either directly on the node itself, on a separate tab, or not at all.'),
-    '#prefix' => '<div class="signup-form-location-radios">',
-    '#suffix' => '</div>',
-  );
-
-  // The rest of the advanced settings are conditional on if/where the signup
-  // form is being displayed.  We use jQuery to hide settings when they're not
-  // relevant.
-  $signup_path = drupal_get_path('module', 'signup');
-  drupal_add_js($signup_path .'/js/admin.settings.js');
-  drupal_add_css($signup_path .'/signup.css');
-
-  // For each setting that should be hidden, signup.css will hide all the
-  // settings on page load if.
-
-  $class = 'signup-fieldset_collapsed-setting';
-  if (variable_get('signup_form_location', 'node') != 'node') {
-    $class .= ' js-hide';
-  }
-  $form['adv_settings']['signup_fieldset_collapsed'] = array(
-    '#title' => t('Default fieldset behavior for per-node signup form'),
-    '#type' => 'radios',
-    '#options' => array(1 => t('Collapsed'), 0 => t('Expanded')),
-    '#default_value' => variable_get('signup_fieldset_collapsed', 1),
-    '#description' => t('If the signup form is included at the bottom of each node, the signup form will be encapsulated in a collapsible fieldset. This setting controls if that fieldset is expanded or collapsed by default.'),
-    '#prefix' => '<div class="'. $class .'">',
-    '#suffix' => '</div>',
-  );
-
-  // If views.module is enabled provide the option to display the list
-  // of signed-up users in a tab and/or at the bottom of the node.
-  $display_options = array();
-  $display_options['signup'] = t('Use the built-in listing');
-  $views_help_text = '';
-  if (module_exists('views')) {
-    $display_options['embed-view'] = t('Embed a view');
-    $views_help_text = t('If you choose to embed a view, you will be able to select which view you want to use below. The view you select will have a single argument passed in, the node id (nid) of the signup-enabled node being viewed. You can also use views to display this listing on its own tab or in a block if you disable this setting.');
-  }
-  else {
-    $views_help_text = t('If you enable the !views_url, you will be able to embed a view directly onto the page for this listing.', array('!views_url' => l(t('Views module'), 'http://drupal.org/project/views', array('absolute' => TRUE))));
-  }
-  $display_options['none'] = t('Do not display a listing at all');
-
-  $class = 'signup-display-signup-user-list-setting';
-  if (variable_get('signup_form_location', 'node') == 'none') {
-    $class .= ' js-hide';
-  }
-  $form['adv_settings']['signup_display_signup_user_list'] = array(
-    '#title' => t('How to display the list of signed-up users'),
-    '#type' => 'radios',
-    '#options' => $display_options,
-    '#default_value' => variable_get('signup_display_signup_user_list', 'signup'),
-    '#description' => t('If the signup form is being displayed, users with the %view_signups permission can see a list of all users who have signed up. This setting controls if and how that list should be generated. !views_help_text', array('%view_signups' => t('view all signups'), '!views_help_text' => $views_help_text)),
-    '#prefix' => '<div class="'. $class .'">',
-    '#suffix' => '</div>',
-  );
-
-  if (module_exists('views')) {
-    $class = 'signup-user-list-view-settings';
-    if (variable_get('signup_form_location', 'node') == 'none' || variable_get('signup_display_signup_user_list', 'signup') != 'embed-view') {
-      $class .= ' js-hide';
-    }
-    $form['adv_settings']['view_settings'] = array(
-      '#prefix' => '<div class="'. $class .'">',
-      '#suffix' => '</div>',
-    );
-
-    $views = views_get_all_views();
-    foreach ($views as $view) {
-      foreach (array_keys($view->display) as $display_id) {
-        if ($display_id != 'default') {
-          $key = $view->name .':'. $display_id;
-          $view_options[$key] = theme('signup_view_label', $view, $display_id);
-        }
-      }
-    }
-    $form['adv_settings']['view_settings']['signup_user_list_view'] = array(
-      '#title' => t('View to embed for the signup user list'),
-      '#type' => 'select',
-      '#options' => $view_options,
-      '#default_value' => variable_get('signup_user_list_view', 'signup_user_list:page_1'),
-      '#description' => t("If the signup user list is being generated by embedding a view, this selects which view should be used. The view's name, description, and display(s) it defines are listed.  NOTE: if you enable or customize the view being used for this, you should strongly consider disabling the view's menu items to prevent a duplicate tab showing the same information."),
-    );
-  }
-
-  // Use our own submit handler, so we can do some processing before
-  // we hand control to system_settings_form_submit.
-  $form = system_settings_form($form);
-  unset($form['#submit']);
-  $form['#submit'][] = 'signup_settings_page_submit';
-  return $form;
-}
-
-/**
- * Generates the appropriate selector label for a given view.
- *
- * @param $view
- *   An object containing data about the view to generate a label for.
- *   Contains at least a name (string), description (string), page
- *   (bool), and block (bool) fields.
- *
- * @return
- *   The plain text (no HTML allowed) to include as the label for this view in
- *   the drop-down selector for which view to embed on the site-wide signup
- *   settings page.
- *
- * @see signup_settings_page()
- */
-function theme_signup_view_label($view, $display_id) {
-  $display_title = check_plain($view->display[$display_id]->display_title);
-  $label = $view->name;
-  $label .= ' ['. $display_title .']: ';
-  $label .= $view->description;
-  return $label;
-}
-
-/**
- * Submits the signup settings form
- *
- * @param $form_id The ID of the form being submitted.
- * @param $form_values The constructed form values array of the submitted form.
- */
-function signup_settings_page_submit($form, &$form_state) {
-  $op = isset($form_state['values']['op']) ? $form_state['values']['op'] : '';
-  if ($op == t('Save configuration') && db_result(db_query('SELECT COUNT(*) FROM {signup} WHERE nid = 0'))) {
-    $values = array(
-      $form_state['values']['signup_forwarding_email'],
-      $form_state['values']['signup_send_confirmation'],
-      $form_state['values']['signup_confirmation_email'],
-      $form_state['values']['signup_close_signup_limit'],
-    );
-    $values[] = isset($form_state['values']['signup_send_reminder']) ? $form_state['values']['signup_send_reminder'] : 0;
-    $values[] = isset($form_state['values']['signup_reminder_days_before']) ? $form_state['values']['signup_reminder_days_before'] : 0;
-    $values[] = isset($form_state['values']['signup_reminder_email']) ? $form_state['values']['signup_reminder_email'] : '';
-    $values[] = 0;  // "nid" of the row in {signup} for the global settings.
-    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);
-  }
-  else {
-    module_load_include('install', 'signup', 'signup');
-    db_query("DELETE FROM {signup} WHERE nid = 0");
-    signup_insert_default_signup_info();
-  }
-
-  // Now, remove all the settings we just processed from our copy of
-  // $form_state['values'], so system_settings_form_submit() doesn't see them.
-  $settings = array(
-    'signup_forwarding_email',
-    'signup_send_confirmation',
-    'signup_confirmation_email',
-    'signup_send_reminder',
-    'signup_reminder_days_before',
-    'signup_reminder_email',
-    'signup_close_signup_limit',
-  );
-  foreach ($settings as $setting) {
-    unset($form_state['values'][$setting]);
-  }
-  // Remove the hidden element from signup_node_settings_form(), too.
-  unset($form_state['values']['signup']);
-
-  // Finally, let system_settings_form_submit() do its magic with the
-  // rest of the settings.
-  system_settings_form_submit($form, $form_state);
-}
-
-/**
  * Returns a list of content types that have signups enabled
  */
 function signup_content_types() {
@@ -1889,227 +832,6 @@ function signup_sign_up_user($signup_for
 }
 
 /**
- * Prints the signup details for a single node when the signups tab is clicked
- * @ingroup signup_callback
- */
-function signup_node_admin_page($node) {
-  drupal_set_title(check_plain($node->title));
-
-  // Administrative table to control signups for this node.
-  $signup_node_admin_summary_form = drupal_get_form('signup_node_admin_summary_form', $node);
-
-  // Signup details table, including cancel checkboxes.
-  $signup_node_admin_details_form = drupal_get_form('signup_node_admin_details_form', $node);
-
-  if ($node->signup_status) {
-    // Add a form to allow the administrator to signup other users.
-    $signup_form = drupal_get_form('signup_form', $node, 'admin');
-  }
-  else {
-    $signup_form = '';
-  }
-
-  return theme('signup_node_admin_page', $node, $signup_node_admin_summary_form, $signup_node_admin_details_form, $signup_form);
-}
-
-function signup_node_admin_details_form(&$form_state, $node) {
-  unset($_SESSION['signup_cancel_multiple_users']);
-  $form = array();
-
-  // Prepare a table header that allows sorting on name and signup time.
-  $header = array(
-    theme('table_select_header_cell'),
-    array('data' => t('Name'), 'field' => 'u.name', 'sort' => 'asc'),
-    array('data' => t('Signup time'), 'field' => 's.signup_time'),
-    array('data' => t('Extra information')),
-  );
-
-  $sql = "SELECT u.uid, u.name, s.anon_mail, s.signup_time, s.form_data FROM {signup_log} s INNER JOIN {users} u ON u.uid = s.uid WHERE s.nid = %d";
-  $sql .= tablesort_sql($header);
-  $result = db_query($sql, $node->nid);
-
-  // Loop through the users, unserializing their user data.
-  while ($signed_up_user = db_fetch_object($result)) {
-    // The username and the unique form identifier are different for
-    // anon signups and registered user signups.  For registered users,
-    // provide a link to the user profile, and use the uid as the
-    // identifier.  For anon, use the user's email address as the
-    // identifier and name.
-    if ($signed_up_user->uid == 0) {
-      $key = '__anon:'. $signed_up_user->anon_mail;
-      $username = check_plain($signed_up_user->anon_mail);
-    }
-    else {
-      $key = $signed_up_user->uid;
-      $username = theme('username', $signed_up_user);
-    }
-
-    $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)));
-  }
-  if (empty($users)) {
-    $form['no_users'] = array('#value' => t('No users have signed up for this %node_type.', array('%node_type' => node_get_types('name', $node->type))));
-  }
-  else {
-    $form['nid'] = array(
-      '#type' => 'hidden',
-      '#value' => $node->nid,
-    );
-    $form['users'] = array('#type' => 'checkboxes', '#options' => $users);
-    $form['submit_cancel'] = array(
-      '#type' => 'submit',
-      '#value' => t('Cancel signups'),
-      '#submit' => array('signup_cancel_multiple_submit'),
-      '#validate' => array('signup_cancel_multiple_validate'),
-    );
-    $form['#header'] = $header;
-  }
-  return $form;
-}
-
-function signup_cancel_multiple_validate($form, &$form_state) {
-  $users = array_filter($form_state['values']['users']);
-  if (empty($users)) {
-    form_set_error('', t('No users selected.'));
-  }
-}
-
-/**
- * Submit handler for cancelling multiple signups via node/N/signups.
- *
- * This saves the selected users into SESSION and redirects to a confirm form
- * which is registered at node/N/signups/confirm.
- */
-function signup_cancel_multiple_submit($form, &$form_state) {
-  $_SESSION['signup_cancel_multiple_users'] = array_filter($form_state['values']['users']);
-  $form_state['redirect'] = 'node/'. $form_state['values']['nid'] .'/signups/confirm';
-}
-
-/**
- * Builds the confirm form when canceling multiple signups from node/N/signups.
- */
-function signup_cancel_multiple_confirm(&$form_state, $node) {
-  $form = array();
-  $form['nid'] =  array(
-    '#type' => 'hidden',
-    '#value' => $node->nid,
-  );
-  $form['users'] =  array(
-    '#prefix' => '<ul>',
-    '#suffix' => '</ul>',
-    '#tree' => TRUE,
-  );
-  foreach ($_SESSION['signup_cancel_multiple_users'] as $key) {
-    $label = '';
-    $matches = array();
-    if (preg_match('/__anon:(.*)/', $key, $matches)) {
-      $label = t('Anonymous signup: %anon_mail', array('%anon_mail' => $matches[1]));
-    }
-    else {
-      $account = db_fetch_object(db_query("SELECT uid, name FROM {users} WHERE uid = %d", $key));
-      $label = theme('username', $account);
-    }
-    $form['users'][$key] = array(
-      '#type' => 'hidden',
-      '#value' => $key,
-      '#prefix' => '<li>',
-      '#suffix' => $label ."</li>\n",
-    );
-  }
-  $form['#submit'][] = 'signup_cancel_multiple_confirm_submit';
-  return confirm_form(
-    $form,
-    t('Are you sure you want to cancel signups for these users?'),
-    'node/'. $node->nid .'/signups',
-    t('This action cannot be undone.'),
-    t('Cancel signups'), t('Keep signups')
-  );
-}
-
-/**
- * Submit handler for the confirm form to cancel multiple signups.
- */
-function signup_cancel_multiple_confirm_submit($form, &$form_state) {
-  $nid = $form_state['values']['nid'];
-  foreach ($form_state['values']['users'] as $key) {
-    $matches = array();
-    if (preg_match('/__anon:(.*)/', $key, $matches)) {
-      $uid = 0;
-      $anon_mail = $matches[1];
-    }
-    else {
-      $uid = $key;
-      $anon_mail = NULL;
-    }
-    signup_cancel_signup($uid, $nid, $anon_mail);
-  }
-  $form_state['redirect'] = 'node/'. $nid .'/signups';
-  unset($_SESSION['signup_cancel_multiple_users']);
-}
-
-function signup_node_admin_summary_form($form_state, $node) {
-  $form = array();
-  if ($node->signup_close_signup_limit &&
-    $node->signup_total >= $node->signup_close_signup_limit
-  ) {
-    $form['status'] = array(
-      '#value' => t('Closed (limit reached)'),
-    );
-  }
-  else {
-    $form['status'] = array(
-      '#type' => 'select',
-      '#options' => array(0 => t('Closed'), 1 => t('Open')),
-      '#default_value' => $node->signup_status,
-    );
-  }
-  $form['limit'] = array(
-    '#type' => 'textfield',
-    '#default_value' => $node->signup_close_signup_limit,
-    '#size' => 4, '#maxlength' => 8,
-  );
-  $form['total'] = array(
-    '#value' => $node->signup_total,
-  );
-  $form['submit'] = array(
-    '#type' => 'submit',
-    '#value' => t('Update'),
-    '#submit' => array('signup_node_admin_summary_form_submit'),
-  );
-  $form['nid'] = array(
-    '#type' => 'value',
-    '#value' => $node->nid,
-  );
-  return $form;
-}
-
-function signup_node_admin_summary_form_submit($form, &$form_state) {
-  $nid = $form_state['values']['nid'];
-  $node = node_load($nid);
-  $limit_status = 0;
-  if (isset($form_state['values']['limit']) && ($form_state['values']['limit'] != $node->signup_close_signup_limit)) {
-    db_query("UPDATE {signup} SET close_signup_limit = %d WHERE nid = %d", $form_state['values']['limit'], $nid);
-    $node->signup_close_signup_limit = $form_state['values']['limit'];
-    $limit_status = _signup_check_limit($node, 'limit');
-  }
-
-  // Only consider the form's status value if the signup limit didn't
-  // touch the status already.
-  if (!$limit_status && isset($form_state['values']['status']) && ($form_state['values']['status'] != $node->signup_status)) {
-    if ($form_state['values']['status']) {
-      signup_open_signup($nid);
-      drupal_set_message(t('Signups opened for !title.', array('!title' => l($node->title, "node/$node->nid"))));
-    }
-    else {
-      signup_close_signup($nid);
-      drupal_set_message(t('Signups closed for !title.', array('!title' => l($node->title, "node/$node->nid"))));
-    }
-  }
-}
-
-/**
  * Checks the signup limit for a given node, and sees if a change in
  * either the limit or total # of signups should result in a change in
  * signup status (open vs. closed) and prints a message indicating
@@ -2177,156 +899,6 @@ function _signup_check_limit($node, $typ
 }
 
 /**
- * Validates an anonymous signup email.
- *
- * @param $nid The node the user is signing up for.
- * @param $anon_mail The anonymous email address to validate.
- * @param $name Optional. The form element being validated.
- *
- * @return Boolean.  TRUE if the address validates, FALSE otherwise.
- */
-function signup_validate_anon_email($nid, $anon_mail, $name = FALSE) {
-  if (!valid_email_address($anon_mail)) {
-    $message = t('Invalid email address entered for signup.');
-  }
-  elseif (db_result(db_query("SELECT COUNT(*) FROM {users} WHERE mail = '%s'", $anon_mail))) {
-    $message = t('The email address entered belongs to a registered user.');
-  }
-  elseif (db_result(db_query("SELECT COUNT(*) FROM {signup_log} WHERE anon_mail = '%s' AND nid = %d", $anon_mail, $nid))) {
-    $node = node_load($nid);
-    $message = t('The email address entered has already been used to sign up for this %node_type.', array('%node_type' => node_get_types('name', $node->type)));
-  }
-
-  // If there's no message, it's a valid email, so return success.
-  if (!isset($message)) {
-    return TRUE;
-  }
-
-  // Depending on how we were called, propagate the error accordinly.
-  if ($name) {
-    form_set_error($name, $message);
-  }
-  else {
-    drupal_set_message($message, 'error');
-  }
-  return FALSE;
-}
-
-/**
- * @defgroup signup_internal Internal module functions
- */
-
-/**
- * Returns the form for the per-node signup settings.
- *
- * This is shared by the settings page and the node edit page.
- *
- * @param $node
- *   The fully loaded node object if we've got it.
- * @param $node_type
- *   The type of the node.  When creating new content, the caller can know the
- *   node type, even if $node is NULL.
- * @param $has_date
- *   Boolean flag indicating if this node (or site) has signup-aware
- *   date functionality, which is required for reminder emails to be in
- *   the form.
- *
- * @return
- *   The form array for the per-node signup settings.
- *
- * @ingroup signup_internal
- */
-function signup_node_settings_form($form_state, $node = NULL, $node_type = NULL, $has_date = FALSE) {
-  if (module_exists('token')) {
-    $signup_token_description = t('Supported string substitutions: %node_title, %node_url, %node_start_time, %user_name, %user_mail, %user_signup_info (additional information from the signup form), and any tokens in the %replacement_tokens list.', array('%replacement_tokens' => t('Replacement tokens')));
-  }
-  else {
-    $signup_token_description = t('Supported string substitutions: %node_title, %node_url, %node_start_time, %user_name, %user_mail, %user_signup_info (additional information from the signup form).');
-  }
-
-  // Load the default admin form data for new nodes.
-  if (!$node || !$node->signup) {
-    $result = db_fetch_object(db_query("SELECT * FROM {signup} WHERE nid = 0"));
-    $node->signup_forwarding_email = $result->forwarding_email;
-    $node->signup_send_confirmation = $result->send_confirmation;
-    $node->signup_confirmation_email = $result->confirmation_email;
-    $node->signup_send_reminder = $result->send_reminder;
-    $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;
-  }
-
-  $form['signup_forwarding_email'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Send signups to'),
-    '#default_value' => $node->signup_forwarding_email,
-    '#size' => 40, '#maxlength' => 64,
-    '#description' => t('Email address where notification of new signups will be sent. Leave blank for no notifications.'),
-  );
-  $form['signup_send_confirmation'] = array(
-    '#type' => 'checkbox',
-    '#title' => t('Send confirmation'),
-    '#default_value' => $node->signup_send_confirmation,
-  );
-  $form['signup_confirmation_email'] = array(
-    '#type' => 'textarea',
-    '#title' => t('Confirmation email'),
-    '#default_value' => $node->signup_confirmation_email,
-    '#cols' => 40, '#rows' => 6,
-    '#description' => t('Email sent to user upon signup. !token_description', array('!token_description' => $signup_token_description)),
-  );
-  if (module_exists('token')) {
-    _signup_token_help($form, 'signup_confirmation_token_fieldset');
-  }
-
-  if ($has_date) {
-    // Define a sub-tree to wrap the next 2 form elements together in an
-    // inline div for better display.
-    $form['signup_reminder'] = array(
-      '#prefix' => '<div class="container-inline">',
-      '#suffix' => '</div>',
-    );
-    $form['signup_reminder']['signup_send_reminder'] = array(
-      '#type' => 'checkbox',
-      '#title' => t('Send reminder'),
-      '#default_value' => $node->signup_send_reminder,
-    );
-    $options = array();
-    for ($i = 1; $i <= 60; $i++) {
-      $options[$i] = $i;
-    }
-    $node_type_name = isset($node_type) ? node_get_types('name', $node_type) : '';
-    $form['signup_reminder']['signup_reminder_days_before'] = array(
-      '#type' => 'select',
-      '#default_value' => $node->signup_reminder_days_before,
-      '#options' => $options,
-      '#suffix' => !empty($node_type_name) ? t('day(s) before this %node_type', array('%node_type' => $node_type_name)) : t('day(s) before start time'),
-    );
-    $form['signup_reminder_email'] = array(
-      '#type' => 'textarea',
-      '#title' => t('Reminder email'),
-      '#default_value' => $node->signup_reminder_email,
-      '#cols' => 40, '#rows' => 6,
-      '#description' =>  !empty($node_type_name) ? t('Email sent to user as a reminder before the %node_type starts. !token_description', array('%node_type' => $node_type_name, '!token_description' => $signup_token_description)) : t('Email sent to user as a reminder before the start time. !token_description', array('!token_description' => $signup_token_description)),
-    );
-    if (module_exists('token')) {
-      _signup_token_help($form, 'signup_reminder_token_fieldset');
-    }
-  }
-
-  $form['signup_close_signup_limit'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Signup limit'),
-    '#default_value' => $node->signup_close_signup_limit,
-    '#size' => 4, '#maxlength' => 8,
-    '#description' => t('Maximum number of users who can sign up before signups are automatically closed. If set to 0, there is no limit.'),
-  );
-  $form['signup'] = array('#type' => 'hidden', '#value' => 1);
-
-  return $form;
-}
-
-/**
  * Converts an arbitrary string into something safe to use for a CSS id.
  *
  * Stolen wholesale from the Zen theme. ;)
@@ -2345,161 +917,6 @@ function signup_id_safe($string) {
 }
 
 /**
- * Form builder for the signup broadcast form.
- *
- * @param $node
- *   The node that the broadcast form is being attached to.
- */
-function signup_broadcast_form($form_state, $node) {
-  $addresses = signup_get_email_addresses($node->nid);
-  if (empty($addresses)) {
-    $form['no_users'] = array(
-      '#value' => t('No users have signup up for this %node_type.', array('%node_type' => node_get_types('name', $node->type))),
-    );
-    return $form;
-  }
-
-  $tokens = array('%node_title', '%node_url', '%user_name', '%user_mail');
-  $tokens = array_merge($tokens, signup_extra_tokens($node));
-  sort($tokens);
-  if (module_exists('token')) {
-    $token_text = t('Supported string substitutions: %tokens, and any tokens in the %replacement_tokens list.', array('%tokens' => implode(', ', $tokens), '%replacement_tokens' => t('Replacement tokens')));
-  }
-  else {
-    $token_text = t('Supported string substitutions: %tokens.', array('%tokens' => implode(', ', $tokens)));
-  }
-
-  $form['subject'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Subject'),
-    '#required' => TRUE,
-  );
-  $form['message'] = array(
-    '#type' => 'textarea',
-    '#title' => t('Message body'),
-    '#required' => TRUE,
-    '#description' => t('Body of the email message you wish to send to all users who have signed up for this %node_type. !token_description', array('%node_type' => node_get_types('name', $node->type), '!token_description' => $token_text)),
-    '#rows' => 10,
-  );
-
-  if (module_exists('token')) {
-    _signup_token_help($form, 'message_tokens_fieldset');
-  }
-
-  $form['copy'] = array(
-    '#type' => 'checkbox',
-    '#title' => t('Send yourself a copy.'),
-  );
-
-  $form['send'] = array(
-    '#type' => 'submit',
-    '#value' => t('Send'),
-  );
-  $form['nid'] = array(
-    '#type' => 'value',
-    '#value' => $node->nid,
-  );
-
-  global $user;
-  if (user_access('administer site configuration')) {
-    $form['from'] = array(
-      '#type' => 'textfield',
-      '#title' => t('From'),
-      '#required' => TRUE,
-      '#default_value' => $user->mail,
-      '#weight' => '-10',
-    );
-  }
-  else {
-    $form['from'] = array(
-      '#value' => t('This message will be sent from: %from', array('%from' => $user->mail)),
-      '#pre' => '<strong>',
-      '#post' => '</strong>',
-    );
-  }
-  return $form;
-}
-
-/**
- * Return an array of extra email tokens supported by the given node.
- */
-function signup_extra_tokens($node) {
-  $scheduler = _signup_get_node_scheduler($node);
-  return $scheduler == 'none' ? array() : array('%node_start_time');
-}
-
-/**
- * Retrieve a list of all users who have signed up for a node.
- *
- * @param $nid
- *
- * @return An array of objects containing signup data
- */
-function signup_get_email_addresses($nid) {
-  $signup_data = array();
-  $signups = db_query("SELECT u.uid, u.name, u.mail, s_l.anon_mail, s_l.form_data FROM {signup_log} s_l INNER JOIN {users} u ON u.uid = s_l.uid WHERE s_l.nid = %d", $nid);
-  while ($signup_entry = db_fetch_object($signups)) {
-    $signup_data[] = $signup_entry;
-  }
-  return $signup_data;
-}
-
-/**
- * Send an email message to all those signed up to a node.
- *
- * @param $form_id
- * @param $form_values
- */
-function signup_broadcast_form_submit($form, &$form_state) {
-  global $user;
-  $addresses = signup_get_email_addresses($form_state['values']['nid']);
-  if (is_array($addresses)) {
-    if (user_access('administer site configuration')) {
-      $from = $form_state['values']['from'];
-    }
-    else {
-      $from = $user->mail;
-    }
-    $node = node_load($form_state['values']['nid']);
-    foreach ($addresses as $signup) {
-      $user_mail = _signup_get_email($signup);
-      $params = array(
-        'subject' => $form_state['values']['subject'],
-        'body' => $form_state['values']['message'],
-        'node' => $node,
-        'signup' => $signup,
-      );
-      if (module_exists('token')) {
-        $params['body'] = token_replace($params['body'], 'node', $node);
-      }
-      $language = user_preferred_language($signup);
-      drupal_mail('signup', 'signup_broadcast_mail', $user_mail, $language, $params, $from);
-      watchdog('signup', 'Broadcast email for %title sent to %email.', array('%title' => $node->title, '%email' => $user_mail), WATCHDOG_NOTICE, l(t('view'), 'node/'. $node->nid));
-    }
-    if ($form_state['values']['copy']) {
-      $sender_email = _signup_get_email($user);
-      $signup_tokens = _signup_get_email_tokens($node, $user);
-      $message = strtr($form_state['values']['message'], $signup_tokens);
-      if (module_exists('token')) {
-        // If the token.module is enabled, also handle any tokens it provides.
-        $message = token_replace($message, 'node', $node);
-      }
-      $final_text = theme('signup_broadcast_sender_copy', $form_state['values']['message'], $message);
-      $params = array(
-        'subject' => $form_state['values']['subject'],
-        'body' => $final_text,
-        'ignore_tokens' => TRUE,
-      );
-      $language = user_preferred_language($user);
-      drupal_mail('signup', 'signup_broadcast_mail', $sender_email, $language, $params, $from);
-      watchdog('signup', 'Broadcast email copy for %title sent to %email.', array('%title' => $node->title, '%email' => $sender_email), WATCHDOG_NOTICE, l(t('view'), 'node/'. $node->nid));
-      drupal_set_message(t('Sent a copy of this message to %email', array('%email' => $sender_email)));
-    }
-  }
-  drupal_set_message(t('Message sent to all users who have signed up'));
-}
-
-/**
  * Implementation of hook_views_api().
  */
 function signup_views_api() {
@@ -2510,64 +927,6 @@ function signup_views_api() {
 }
 
 /**
- * Private function to generate HTML for showing the available tokens.
- *
- * @param $form
- *   Reference to the form array to include the help fieldset in.
- * @param $element_name
- *   Name of the form element to use for the help fieldset.
- */
-function _signup_token_help(&$form, $element_name) {
-  $form[$element_name] = array(
-    '#type' => 'fieldset',
-    '#title' => t('Replacement tokens'),
-    '#collapsible' => TRUE,
-    '#collapsed' => TRUE,
-  );
-  $form[$element_name]['help_text'] = array(
-    '#value' => _signup_build_token_help(),
-  );
-}
-
-/**
- * Private function to generate HTML for showing the available tokens
- *
- * @return The themed representation of the available tokens.
- */
-function _signup_build_token_help() {
-  static $help_html = '';
-  if (empty($help_html)) {
-    $patterns = token_get_list('node');
-    foreach ($patterns as $type => $pattern_set) {
-      foreach ($pattern_set as $pattern => $description) {
-        $tokens[$pattern] = $description;
-      }
-    }
-    $help_html = theme('signup_token_help', $tokens);
-  }
-  return $help_html;
-}
-
-/**
- * theme_signup_token_help()
- *
- * @param $tokens
- *   An array of token patterns mapping to their description
- *
- * @return The themed representation of the tokens.
- */
-function theme_signup_token_help($tokens) {
-  $tokens_html = "<dl>\n";
-  foreach ($tokens as $name => $description) {
-    $tokens_html .= '<dt>['. $name .']</dt>';
-    $tokens_html .= '<dd>'. $description ."</dd>\n";
-  }
-  $tokens_html .= "</dl>\n";
-
-  return $tokens_html;
-}
-
-/**
  * Implementation of hook_mail().
  *
  * Constructs all of the email messages generated by the signup module.
cvs diff: Diffing includes
Index: includes/admin.settings.inc
===================================================================
RCS file: includes/admin.settings.inc
diff -N includes/admin.settings.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ includes/admin.settings.inc	14 Nov 2008 05:41:21 -0000
@@ -0,0 +1,223 @@
+<?php
+// $Id$
+
+
+/**
+ * @file
+ * Code required for the signup settings page (admin/settings/signup).
+ */
+
+/**
+ * Form builder for the settings page under admin/setttings/signup
+ */
+function signup_settings_form() {
+  module_load_include('inc', 'signup', 'includes/node_settings');
+  if (signup_site_has_dates()) {
+    $form['signup_close_early'] = array(
+      '#title' => t('Close x hours before'),
+      '#type' => 'textfield',
+      '#default_value' => variable_get('signup_close_early', 1),
+      '#size' => 5, '#maxlength' => 10,
+      '#description' => t('The number of hours before the event which signups will no longer be allowed. Use negative numbers to close signups after the event start (example: -12).'),
+    );
+  };
+  $form['node_defaults'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Default signup information'),
+    '#description' => t('New signup-enabled nodes will start with these settings.'),
+    '#collapsible' => TRUE,
+  );
+  $form['node_defaults']['signup_node_settings_form'] = signup_node_settings_form(array(), NULL, NULL, signup_site_has_dates());
+
+  $form['adv_settings'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Advanced settings'),
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
+  );
+  $form['adv_settings']['signup_date_format'] = array(
+    '#title' => t('Format string for displaying signup-related dates'),
+    '#type' => 'select',
+    '#options' => array(
+      'small' => t('Small'),
+      'medium' => t('Medium'),
+      'large' => t('Large'),
+    ),
+    '#default_value' => variable_get('signup_date_format', 'small'),
+    '#description' => t('Whenever this module needs to print a date (both in the administrative interface, and in the various e-mail messages it can send), this setting controls which date format string to use. The format strings are defined at the <a href="!settings_url">Date and time settings page</a>.', array('!settings_url' => url('admin/settings/date-time'))),
+  );
+  $form['adv_settings']['signup_form_location'] = array(
+    '#title' => t('Location of the signup form and related information'),
+    '#type' => 'radios',
+    '#options' => array(
+      'node' => t('At the bottom of each node'),
+      'tab' => t('On a separate %sign_up tab', array('%sign_up' => t('Sign up'))),
+      'none' => t('Do not display signup form'),
+    ),
+    '#default_value' => variable_get('signup_form_location', 'node'),
+    '#description' => t('On every signup-enabled node, users with permission to
+ sign up can be presented with a form and additional signup-related information. This setting controls where this information should be displayed: either directly on the node itself, on a separate tab, or not at all.'),
+    '#prefix' => '<div class="signup-form-location-radios">',
+    '#suffix' => '</div>',
+  );
+
+  // The rest of the advanced settings are conditional on if/where the signup
+  // form is being displayed.  We use jQuery to hide settings when they're not
+  // relevant.
+  $signup_path = drupal_get_path('module', 'signup');
+  drupal_add_js($signup_path .'/js/admin.settings.js');
+  drupal_add_css($signup_path .'/signup.css');
+
+  // For each setting that should be hidden, signup.css will hide all the
+  // settings on page load if.
+
+  $class = 'signup-fieldset_collapsed-setting';
+  if (variable_get('signup_form_location', 'node') != 'node') {
+    $class .= ' js-hide';
+  }
+  $form['adv_settings']['signup_fieldset_collapsed'] = array(
+    '#title' => t('Default fieldset behavior for per-node signup form'),
+    '#type' => 'radios',
+    '#options' => array(1 => t('Collapsed'), 0 => t('Expanded')),
+    '#default_value' => variable_get('signup_fieldset_collapsed', 1),
+    '#description' => t('If the signup form is included at the bottom of each node, the signup form will be encapsulated in a collapsible fieldset. This setting controls if that fieldset is expanded or collapsed by default.'),
+    '#prefix' => '<div class="'. $class .'">',
+    '#suffix' => '</div>',
+  );
+
+  // If views.module is enabled provide the option to display the list
+  // of signed-up users in a tab and/or at the bottom of the node.
+  $display_options = array();
+  $display_options['signup'] = t('Use the built-in listing');
+  $views_help_text = '';
+  if (module_exists('views')) {
+    $display_options['embed-view'] = t('Embed a view');
+    $views_help_text = t('If you choose to embed a view, you will be able to select which view you want to use below. The view you select will have a single argument passed in, the node id (nid) of the signup-enabled node being viewed. You can also use views to display this listing on its own tab or in a block if you disable this setting.');
+  }
+  else {
+    $views_help_text = t('If you enable the !views_url, you will be able to embed a view directly onto the page for this listing.', array('!views_url' => l(t('Views module'), 'http://drupal.org/project/views', array('absolute' => TRUE))));
+  }
+  $display_options['none'] = t('Do not display a listing at all');
+
+  $class = 'signup-display-signup-user-list-setting';
+  if (variable_get('signup_form_location', 'node') == 'none') {
+    $class .= ' js-hide';
+  }
+  $form['adv_settings']['signup_display_signup_user_list'] = array(
+    '#title' => t('How to display the list of signed-up users'),
+    '#type' => 'radios',
+    '#options' => $display_options,
+    '#default_value' => variable_get('signup_display_signup_user_list', 'signup'),
+    '#description' => t('If the signup form is being displayed, users with the %view_signups permission can see a list of all users who have signed up. This setting controls if and how that list should be generated. !views_help_text', array('%view_signups' => t('view all signups'), '!views_help_text' => $views_help_text)),
+    '#prefix' => '<div class="'. $class .'">',
+    '#suffix' => '</div>',
+  );
+
+  if (module_exists('views')) {
+    $class = 'signup-user-list-view-settings';
+    if (variable_get('signup_form_location', 'node') == 'none' || variable_get('signup_display_signup_user_list', 'signup') != 'embed-view') {
+      $class .= ' js-hide';
+    }
+    $form['adv_settings']['view_settings'] = array(
+      '#prefix' => '<div class="'. $class .'">',
+      '#suffix' => '</div>',
+    );
+
+    $views = views_get_all_views();
+    foreach ($views as $view) {
+      foreach (array_keys($view->display) as $display_id) {
+        if ($display_id != 'default') {
+          $key = $view->name .':'. $display_id;
+          $view_options[$key] = theme('signup_settings_view_label', $view, $display_id);
+        }
+      }
+    }
+    $form['adv_settings']['view_settings']['signup_user_list_view'] = array(
+      '#title' => t('View to embed for the signup user list'),
+      '#type' => 'select',
+      '#options' => $view_options,
+      '#default_value' => variable_get('signup_user_list_view', 'signup_user_list:page_1'),
+      '#description' => t("If the signup user list is being generated by embedding a view, this selects which view should be used. The view's name, description, and display(s) it defines are listed.  NOTE: if you enable or customize the view being used for this, you should strongly consider disabling the view's menu items to prevent a duplicate tab showing the same information."),
+    );
+  }
+
+  // Use our own submit handler, so we can do some processing before
+  // we hand control to system_settings_form_submit.
+  $form = system_settings_form($form);
+  unset($form['#submit']);
+  $form['#submit'][] = 'signup_settings_form_submit';
+  return $form;
+}
+
+/**
+ * Submits the signup settings form
+ *
+ * @param $form_id The ID of the form being submitted.
+ * @param $form_values The constructed form values array of the submitted form.
+ */
+function signup_settings_form_submit($form, &$form_state) {
+  $op = isset($form_state['values']['op']) ? $form_state['values']['op'] : '';
+  if ($op == t('Save configuration') && db_result(db_query('SELECT COUNT(*) FROM {signup} WHERE nid = 0'))) {
+    $values = array(
+      $form_state['values']['signup_forwarding_email'],
+      $form_state['values']['signup_send_confirmation'],
+      $form_state['values']['signup_confirmation_email'],
+      $form_state['values']['signup_close_signup_limit'],
+    );
+    $values[] = isset($form_state['values']['signup_send_reminder']) ? $form_state['values']['signup_send_reminder'] : 0;
+    $values[] = isset($form_state['values']['signup_reminder_days_before']) ? $form_state['values']['signup_reminder_days_before'] : 0;
+    $values[] = isset($form_state['values']['signup_reminder_email']) ? $form_state['values']['signup_reminder_email'] : '';
+    $values[] = 0;  // "nid" of the row in {signup} for the global settings.
+    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);
+  }
+  else {
+    module_load_include('install', 'signup', 'signup');
+    db_query("DELETE FROM {signup} WHERE nid = 0");
+    signup_insert_default_signup_info();
+  }
+
+  // Now, remove all the settings we just processed from our copy of
+  // $form_state['values'], so system_settings_form_submit() doesn't see them.
+  $settings = array(
+    'signup_forwarding_email',
+    'signup_send_confirmation',
+    'signup_confirmation_email',
+    'signup_send_reminder',
+    'signup_reminder_days_before',
+    'signup_reminder_email',
+    'signup_close_signup_limit',
+  );
+  foreach ($settings as $setting) {
+    unset($form_state['values'][$setting]);
+  }
+  // Remove the hidden element from signup_node_settings_form(), too.
+  unset($form_state['values']['signup']);
+
+  // Finally, let system_settings_form_submit() do its magic with the
+  // rest of the settings.
+  system_settings_form_submit($form, $form_state);
+}
+
+/**
+ * Generates the appropriate selector label for a given view.
+ *
+ * @param $view
+ *   An object containing data about the view to generate a label for.
+ *   Contains at least a name (string), description (string), page
+ *   (bool), and block (bool) fields.
+ *
+ * @return
+ *   The plain text (no HTML allowed) to include as the label for this view in
+ *   the drop-down selector for which view to embed on the site-wide signup
+ *   settings page.
+ *
+ * @see signup_settings_form()
+ */
+function theme_signup_settings_view_label($view, $display_id) {
+  $display_title = check_plain($view->display[$display_id]->display_title);
+  $label = $view->name;
+  $label .= ' ['. $display_title .']: ';
+  $label .= $view->description;
+  return $label;
+}
+
Index: includes/admin.signup_administration.inc
===================================================================
RCS file: includes/admin.signup_administration.inc
diff -N includes/admin.signup_administration.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ includes/admin.signup_administration.inc	14 Nov 2008 05:41:22 -0000
@@ -0,0 +1,202 @@
+<?php
+// $Id$
+
+
+/**
+ * @file
+ * Code related to the Signup administration page (admin/content/signup).
+ */
+
+/**
+ * Print the admin signup overview page located at admin/content/signup.
+ */
+function signup_admin_page() {
+  drupal_add_css(drupal_get_path('module', 'signup') .'/signup.css');
+  $filter_status_form = drupal_get_form('signup_filter_status_form');
+  $signup_admin_form = drupal_get_form('signup_admin_form');
+  return theme('signup_admin_page', $filter_status_form, $signup_admin_form);
+}
+
+/**
+ * Form builder for the signup status filter on the signup administration page.
+ */
+function signup_filter_status_form(&$form_state) {
+  $options = array(
+    'all' => t('All'),
+    'open' => t('Open'),
+    'closed' => t('Closed'),
+  );
+  if (empty($_SESSION['signup_status_filter'])) {
+    $_SESSION['signup_status_filter'] = 'all';
+  }
+  $form['filter'] = array(
+    '#type' => 'select',
+    '#title' => t('Filter by signup status'),
+    '#options' => $options,
+    '#default_value' => $_SESSION['signup_status_filter'],
+  );
+  $form['submit'] = array('#type' => 'submit', '#value' => t('Filter'));
+//  $form['#redirect'] = FALSE;
+  return $form;
+}
+
+/**
+ * Submit handler for the status filter on the signup administration page.
+ */
+function signup_filter_status_form_submit($form, &$form_state) {
+  $_SESSION['signup_status_filter'] = $form_state['values']['filter'];
+}
+
+/**
+ * Form builder for the main form on the signup administration page.
+ */
+function signup_admin_form($form_state) {
+  // Figure out if the current user has permission to use signup broadcast.
+  $access_broadcast = user_access('email all signed up users');
+
+  $header = array(
+    array('data' => t('Title'), 'field' => 'n.title', 'sort' => 'asc'),
+    array('data' => t('Signups'), 'field' => 'signup_total'),
+    array('data' => t('Limit'), 'field' => 'signup_close_signup_limit'),
+    array('data' => t('Status'), 'field' => 'signup_status'),
+    array('data' => t('Operations')),
+  );
+
+  $start_column = signup_admin_form_header();
+  if (!empty($start_column)) {
+    array_unshift($header, $start_column);
+  }
+
+  list($sql, $sql_count) = signup_admin_form_sql();
+
+  $form['header']['#value'] = $header;
+
+  $sql .= tablesort_sql($header);
+
+  $result = pager_query($sql, 25, 0, $sql_count);
+
+  // Loop through the signup nodes, and generate our form elements
+  while ($signup_node = db_fetch_object($result)) {
+    $row = array();
+    if (!empty($start_column)) {
+      $row['start'] = signup_admin_form_extra($signup_node);
+    }
+
+    // Instead of duplicating the logic from the node/N/signups admin
+    // form, we just call that form builder here and lift the elements
+    // we need directly from that.
+    module_load_include('inc', 'signup', 'includes/node_admin_summary');
+    $node_admin_form = signup_node_admin_summary_form(array(), $signup_node);
+    $row['title'] = array(
+      '#type' => 'markup',
+      '#value' => l($signup_node->title, "node/$signup_node->nid"),
+    );
+    $row['status'] = $node_admin_form['status'];
+    $row['total'] = array(
+      '#type' => 'markup',
+      '#value' => $signup_node->signup_total,
+    );
+    $row['limit'] = $node_admin_form['limit'];
+    $op_links = l(t('View signups'), "node/$signup_node->nid/signups");
+    if ($access_broadcast) {
+      $op_links .= '<br />';
+      $options['attributes']['title'] = t('Send an email message to all users who signed up.');
+      $op_links .= l(t('Signup broadcast'), "node/$signup_node->nid/signup-broadcast", $options);
+    }
+    $row['operations'] = array(
+      '#type' => 'markup',
+      '#value' => $op_links,
+    );
+    $form['nids'][$signup_node->nid] = $row;
+  }
+  $form['#tree'] = TRUE;
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Update'),
+  );
+  return $form;
+}
+
+function signup_admin_form_header() {
+  if (module_exists('date')) {
+    // If we're using CCK date, we can't sort since the date field used for
+    // each content type can come from different tables.
+    return array('data' => t('Start'), 'field' => NULL);
+  }
+  elseif (module_exists('event')) {
+    // If we've got event, but not date, we can sort by e.event_start.
+    return array('data' => t('Start'), 'field' => 'e.event_start');
+  }
+  // If we've got no scheduling backend at all, there's no start time column.
+  return array();
+}
+
+function signup_admin_form_extra($signup_node) {
+  return array(
+    '#type' => 'markup',
+    '#value' => signup_format_date($signup_node),
+  );
+}
+
+function signup_admin_form_sql() {
+  $admin_common_sql = array(
+    'primary' => '{node} n',
+    'fields' => array(
+      'n.nid',
+      'n.title',
+      'n.type',
+      's.status AS signup_status',
+      'COUNT(s_l.nid) AS signup_total',
+      's.close_signup_limit AS signup_close_signup_limit',
+    ),
+    'group_by' => array(
+      'n.nid',
+      'n.title',
+      'signup_status',
+      'signup_close_signup_limit',
+    ),
+    'joins' => array(
+      'INNER JOIN {signup} s ON s.nid = n.nid',
+      'LEFT JOIN {signup_log} s_l ON s.nid = s_l.nid',
+    ),
+  );
+
+  $type = $_SESSION['signup_status_filter'];
+  if ($type == 'open') {
+    $filter_status = 1;
+  }
+  elseif ($type == 'closed') {
+    $filter_status = 0;
+  }
+  if (isset($filter_status)) {
+    $admin_common_sql['where'] = array("s.status = $filter_status");
+  }
+
+  // Get the right query elements from the currently installed backend
+  $admin_sql = array();
+  foreach (signup_content_types() as $type) {
+    $admin_sql = array_merge_recursive($admin_sql, signup_admin_sql($type));
+  }
+
+  // Build the main query.
+  $sql = _signup_build_query($admin_common_sql, $admin_sql);
+
+  // Construct the proper pager query using just the WHERE clauses (if any).
+  $all_fragments = array_merge_recursive($admin_common_sql, $admin_sql);
+  $sql_count = "SELECT COUNT(s.nid) FROM {signup} s";
+  if (!empty($all_fragments['where'])) {
+    $sql_count .= ' WHERE '. implode(' AND ', $all_fragments['where']);
+  }
+
+  return array(db_rewrite_sql($sql), db_rewrite_sql($sql_count, 's'));
+}
+
+function signup_admin_form_submit($form, &$form_state) {
+  module_load_include('inc', 'signup', 'includes/node_admin_summary');
+  foreach ($form_state['values']['nids'] as $nid => $values) {
+    $values['nid'] = $nid;
+    $temp_state['values'] = $values;
+    signup_node_admin_summary_form_submit($form, $temp_state);
+  }
+}
+
Index: includes/broadcast.inc
===================================================================
RCS file: includes/broadcast.inc
diff -N includes/broadcast.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ includes/broadcast.inc	14 Nov 2008 05:41:22 -0000
@@ -0,0 +1,158 @@
+<?php
+// $Id$
+
+
+/**
+ * @file
+ * Code related to sending signup broadcast messages.
+ */
+
+/**
+ * Form builder for the signup broadcast form.
+ *
+ * @param $node
+ *   The node that the broadcast form is being attached to.
+ */
+function signup_broadcast_form($form_state, $node) {
+  $addresses = signup_get_email_addresses($node->nid);
+  if (empty($addresses)) {
+    $form['no_users'] = array(
+      '#value' => t('No users have signup up for this %node_type.', array('%node_type' => node_get_types('name', $node->type))),
+    );
+    return $form;
+  }
+
+  $tokens = array('%node_title', '%node_url', '%user_mail', '%user_name');
+  if (_signup_get_node_scheduler($node) != 'none') {
+    $tokens = array_merge(array('%node_start_time'), $tokens);
+  }
+  if (module_exists('token')) {
+    $token_text = t('Supported string substitutions: %tokens, and any tokens in the %replacement_tokens list.', array('%tokens' => implode(', ', $tokens), '%replacement_tokens' => t('Replacement tokens')));
+  }
+  else {
+    $token_text = t('Supported string substitutions: %tokens.', array('%tokens' => implode(', ', $tokens)));
+  }
+
+  $form['subject'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Subject'),
+    '#required' => TRUE,
+  );
+  $form['message'] = array(
+    '#type' => 'textarea',
+    '#title' => t('Message body'),
+    '#required' => TRUE,
+    '#description' => t('Body of the email message you wish to send to all users who have signed up for this %node_type. !token_description', array('%node_type' => node_get_types('name', $node->type), '!token_description' => $token_text)),
+    '#rows' => 10,
+  );
+
+  if (module_exists('token')) {
+    module_load_include('inc', 'signup', 'includes/token_help');
+    _signup_token_help($form, 'message_tokens_fieldset');
+  }
+
+  $form['copy'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Send yourself a copy.'),
+  );
+
+  $form['send'] = array(
+    '#type' => 'submit',
+    '#value' => t('Send'),
+  );
+  $form['nid'] = array(
+    '#type' => 'value',
+    '#value' => $node->nid,
+  );
+
+  global $user;
+  if (user_access('administer site configuration')) {
+    $form['from'] = array(
+      '#type' => 'textfield',
+      '#title' => t('From'),
+      '#required' => TRUE,
+      '#default_value' => $user->mail,
+      '#weight' => '-10',
+    );
+  }
+  else {
+    $form['from'] = array(
+      '#value' => t('This message will be sent from: %from', array('%from' => $user->mail)),
+      '#pre' => '<strong>',
+      '#post' => '</strong>',
+    );
+  }
+  return $form;
+}
+
+/**
+ * Retrieve a list of all users who have signed up for a node.
+ *
+ * @param $nid
+ *
+ * @return An array of objects containing signup data
+ */
+function signup_get_email_addresses($nid) {
+  $signup_data = array();
+  $signups = db_query("SELECT u.uid, u.name, u.mail, s_l.anon_mail, s_l.form_data FROM {signup_log} s_l INNER JOIN {users} u ON u.uid = s_l.uid WHERE s_l.nid = %d", $nid);
+  while ($signup_entry = db_fetch_object($signups)) {
+    $signup_data[] = $signup_entry;
+  }
+  return $signup_data;
+}
+
+/**
+ * Send an email message to all those signed up to a node.
+ *
+ * @param $form_id
+ * @param $form_values
+ */
+function signup_broadcast_form_submit($form, &$form_state) {
+  global $user;
+  $addresses = signup_get_email_addresses($form_state['values']['nid']);
+  if (is_array($addresses)) {
+    if (user_access('administer site configuration')) {
+      $from = $form_state['values']['from'];
+    }
+    else {
+      $from = $user->mail;
+    }
+    $node = node_load($form_state['values']['nid']);
+    foreach ($addresses as $signup) {
+      $user_mail = _signup_get_email($signup);
+      $params = array(
+        'subject' => $form_state['values']['subject'],
+        'body' => $form_state['values']['message'],
+        'node' => $node,
+        'signup' => $signup,
+      );
+      if (module_exists('token')) {
+        $params['body'] = token_replace($params['body'], 'node', $node);
+      }
+      $language = user_preferred_language($signup);
+      drupal_mail('signup', 'signup_broadcast_mail', $user_mail, $language, $params, $from);
+      watchdog('signup', 'Broadcast email for %title sent to %email.', array('%title' => $node->title, '%email' => $user_mail), WATCHDOG_NOTICE, l(t('view'), 'node/'. $node->nid));
+    }
+    if ($form_state['values']['copy']) {
+      $sender_email = _signup_get_email($user);
+      $signup_tokens = _signup_get_email_tokens($node, $user);
+      $message = strtr($form_state['values']['message'], $signup_tokens);
+      if (module_exists('token')) {
+        // If the token.module is enabled, also handle any tokens it provides.
+        $message = token_replace($message, 'node', $node);
+      }
+      $final_text = theme('signup_broadcast_sender_copy', $form_state['values']['message'], $message);
+      $params = array(
+        'subject' => $form_state['values']['subject'],
+        'body' => $final_text,
+        'ignore_tokens' => TRUE,
+      );
+      $language = user_preferred_language($user);
+      drupal_mail('signup', 'signup_broadcast_mail', $sender_email, $language, $params, $from);
+      watchdog('signup', 'Broadcast email copy for %title sent to %email.', array('%title' => $node->title, '%email' => $sender_email), WATCHDOG_NOTICE, l(t('view'), 'node/'. $node->nid));
+      drupal_set_message(t('Sent a copy of this message to %email', array('%email' => $sender_email)));
+    }
+  }
+  drupal_set_message(t('Message sent to all users who have signed up'));
+}
+
Index: includes/cron.inc
===================================================================
RCS file: includes/cron.inc
diff -N includes/cron.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ includes/cron.inc	14 Nov 2008 05:41:22 -0000
@@ -0,0 +1,127 @@
+<?php
+// $Id$
+
+
+/**
+ * @file
+ * Code required during regular cron runs.
+ */
+
+/**
+ * Helper function that sends cron-based reminder e-mails.
+ *
+ * Invokes the method for the installed event/date backend module to get the
+ * right query fragments, and builds a query to find all nodes that need a
+ * reminder email. For each one, it loops over the users signed up for that
+ * node and send off the emails.
+ *
+ * @see signup_cron()
+ * @see signup_reminder_sql()
+ * @see _signup_build_query()
+ */
+function _signup_cron_send_reminders() {
+  $type_reminder_sql = array();
+  foreach (signup_content_types() as $type) {
+    $type_sql = signup_reminder_sql($type);
+    if (!empty($type_sql)) {
+      $type_reminder_sql[$type] = $type_sql;
+    }
+  }
+  if (empty($type_reminder_sql)) {
+    // No node types support reminder emails, so bail out now.
+    return;
+  }
+
+  $reminder_common_sql = array(
+    'primary' => '{node} n',
+    'fields' => array('n.title', 'n.nid', 'n.type', 's.reminder_email', 's.forwarding_email'),
+    'where' => array('s.send_reminder = 1', "n.type = '%s'"),
+    'joins' => array('INNER JOIN {signup} s ON s.nid = n.nid'),
+  );
+
+  $from = variable_get('site_mail', ini_get('sendmail_from'));
+
+  foreach ($type_reminder_sql as $type => $reminder_sql) {
+    $sql = _signup_build_query($reminder_common_sql, $reminder_sql);
+    $result = db_query($sql, $type);
+
+    // Grab each node, construct the email header and subject, and query
+    // the signup log to pull all users who are signed up for this node.
+    while ($node = db_fetch_object($result)) {
+      $subject = t('!node_type reminder: !title', array('!node_type' => node_get_types('name', $type), '!title' => $node->title));
+      $signups = db_query("SELECT u.name, u.mail, s_l.anon_mail, s_l.form_data FROM {signup_log} s_l INNER JOIN {users} u ON u.uid = s_l.uid WHERE s_l.nid = %d", $node->nid);
+
+      // Loop through the users, composing their customized message
+      // and sending the email.
+      while ($signup = db_fetch_object($signups)) {
+        $user_mail = _signup_get_email($signup);
+        $params = array(
+          'subject' => $subject,
+          'body' => $node->reminder_email,
+          'node' => $node,
+          'signup' => $signup,
+        );
+        if (module_exists('token')) {
+          $params['body'] = token_replace($params['body'], 'node', node_load($node->nid));
+        }
+        $language = user_preferred_language($signup);
+        drupal_mail('signup', 'signup_reminder_mail', $user_mail, $language, $params, $from);
+        watchdog('signup', 'Reminder for %title sent to %user_mail.', array('%title' => $node->title, '%user_mail' => $user_mail), WATCHDOG_NOTICE, l(t('view'), 'node/'. $node->nid));
+      }
+
+      // Reminders for this node are all sent, so mark it in the
+      // database so they're not sent again.
+      db_query("UPDATE {signup} SET send_reminder = 0 WHERE nid = %d", $node->nid);
+    }
+  }
+}
+
+/**
+ * Helper function that handles auto-closing time-based nodes during cron.
+ *
+ * Loops over all the node types that are signup-enabled.  For each one, it
+ * invokes the method for the installed event/date backend module to get the
+ * right query fragments, and builds a query to find all nodes of that type
+ * where signups should be closed (e.g. events that already started, etc).
+ *
+ * @see signup_cron()
+ * @see signup_autoclose_sql()
+ * @see _signup_build_query()
+ */
+function _signup_cron_autoclose() {
+  $type_autoclose_sql = array();
+  foreach (signup_content_types() as $type) {
+    $type_sql = signup_autoclose_sql($type);
+    if (!empty($type_sql)) {
+      $type_autoclose_sql[$type] = $type_sql;
+    }
+  }
+  if (empty($type_autoclose_sql)) {
+    // No node types support auto-close, so bail out now.
+    return;
+  }
+
+  $autoclose_common_sql = array(
+    'primary' => '{node} n',
+    'fields' => array('n.nid', 'n.type'),
+    'where' => array('s.status = 1', "n.type = '%s'"),
+    'joins' => array('INNER JOIN {signup} s ON s.nid = n.nid'),
+  );
+
+  foreach ($type_autoclose_sql as $type => $autoclose_sql) {
+    $sql = _signup_build_query($autoclose_common_sql, $autoclose_sql);
+    $result = db_query($sql, $type);
+
+    // Loop through the results, calling the signup closing function.
+    while ($signup = db_fetch_object($result)) {
+      signup_close_signup($signup->nid, $cron = 'yes');
+      $node = node_load($signup->nid);
+      foreach (module_implements('signup_close') as $module) {
+        $function = $module .'_signup_close';
+        $function($node);
+      }
+      watchdog('signup', 'Signups closed for %title by cron.', array('%title' => $node->title), WATCHDOG_NOTICE, l(t('view'), 'node/'. $node->nid));
+    }
+  }
+}
+
Index: includes/node_admin.inc
===================================================================
RCS file: includes/node_admin.inc
diff -N includes/node_admin.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ includes/node_admin.inc	14 Nov 2008 05:41:22 -0000
@@ -0,0 +1,171 @@
+<?php
+// $Id$
+
+
+/**
+ * @file
+ * Code related to the signup administration tab on each node.
+ */
+
+/**
+ * Print the signup administration tab for a single node.
+ */
+function signup_node_admin_page($node) {
+  drupal_set_title(check_plain($node->title));
+
+  // Administrative table to control signups for this node.
+  module_load_include('inc', 'signup', 'includes/node_admin_summary');
+  $signup_node_admin_summary_form = drupal_get_form('signup_node_admin_summary_form', $node);
+
+  // Signup details table, including cancel checkboxes.
+  $signup_node_admin_details_form = drupal_get_form('signup_node_admin_details_form', $node);
+
+  if ($node->signup_status) {
+    // Add a form to allow the administrator to signup other users.
+    module_load_include('inc', 'signup', 'includes/signup_form');
+    $signup_form = drupal_get_form('signup_form', $node, 'admin');
+  }
+  else {
+    $signup_form = '';
+  }
+
+  return theme('signup_node_admin_page', $node, $signup_node_admin_summary_form, $signup_node_admin_details_form, $signup_form);
+}
+
+function signup_node_admin_details_form(&$form_state, $node) {
+  unset($_SESSION['signup_cancel_multiple_users']);
+  $form = array();
+
+  // Prepare a table header that allows sorting on name and signup time.
+  $header = array(
+    theme('table_select_header_cell'),
+    array('data' => t('Name'), 'field' => 'u.name', 'sort' => 'asc'),
+    array('data' => t('Signup time'), 'field' => 's.signup_time'),
+    array('data' => t('Extra information')),
+  );
+
+  $sql = "SELECT u.uid, u.name, s.anon_mail, s.signup_time, s.form_data FROM {signup_log} s INNER JOIN {users} u ON u.uid = s.uid WHERE s.nid = %d";
+  $sql .= tablesort_sql($header);
+  $result = db_query($sql, $node->nid);
+
+  // Loop through the users, unserializing their user data.
+  while ($signed_up_user = db_fetch_object($result)) {
+    // The username and the unique form identifier are different for
+    // anon signups and registered user signups.  For registered users,
+    // provide a link to the user profile, and use the uid as the
+    // identifier.  For anon, use the user's email address as the
+    // identifier and name.
+    if ($signed_up_user->uid == 0) {
+      $key = '__anon:'. $signed_up_user->anon_mail;
+      $username = check_plain($signed_up_user->anon_mail);
+    }
+    else {
+      $key = $signed_up_user->uid;
+      $username = theme('username', $signed_up_user);
+    }
+
+    $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)));
+  }
+  if (empty($users)) {
+    $form['no_users'] = array('#value' => t('No users have signed up for this %node_type.', array('%node_type' => node_get_types('name', $node->type))));
+  }
+  else {
+    $form['nid'] = array(
+      '#type' => 'hidden',
+      '#value' => $node->nid,
+    );
+    $form['users'] = array('#type' => 'checkboxes', '#options' => $users);
+    $form['submit_cancel'] = array(
+      '#type' => 'submit',
+      '#value' => t('Cancel signups'),
+      '#submit' => array('signup_cancel_multiple_submit'),
+      '#validate' => array('signup_cancel_multiple_validate'),
+    );
+    $form['#header'] = $header;
+  }
+  return $form;
+}
+
+function signup_cancel_multiple_validate($form, &$form_state) {
+  $users = array_filter($form_state['values']['users']);
+  if (empty($users)) {
+    form_set_error('', t('No users selected.'));
+  }
+}
+
+/**
+ * Submit handler for cancelling multiple signups via node/N/signups.
+ *
+ * This saves the selected users into SESSION and redirects to a confirm form
+ * which is registered at node/N/signups/confirm.
+ */
+function signup_cancel_multiple_submit($form, &$form_state) {
+  $_SESSION['signup_cancel_multiple_users'] = array_filter($form_state['values']['users']);
+  $form_state['redirect'] = 'node/'. $form_state['values']['nid'] .'/signups/confirm';
+}
+
+/**
+ * Builds the confirm form when canceling multiple signups from node/N/signups.
+ */
+function signup_cancel_multiple_confirm(&$form_state, $node) {
+  $form = array();
+  $form['nid'] =  array(
+    '#type' => 'hidden',
+    '#value' => $node->nid,
+  );
+  $form['users'] =  array(
+    '#prefix' => '<ul>',
+    '#suffix' => '</ul>',
+    '#tree' => TRUE,
+  );
+  foreach ($_SESSION['signup_cancel_multiple_users'] as $key) {
+    $label = '';
+    $matches = array();
+    if (preg_match('/__anon:(.*)/', $key, $matches)) {
+      $label = t('Anonymous signup: %anon_mail', array('%anon_mail' => $matches[1]));
+    }
+    else {
+      $account = db_fetch_object(db_query("SELECT uid, name FROM {users} WHERE uid = %d", $key));
+      $label = theme('username', $account);
+    }
+    $form['users'][$key] = array(
+      '#type' => 'hidden',
+      '#value' => $key,
+      '#prefix' => '<li>',
+      '#suffix' => $label ."</li>\n",
+    );
+  }
+  $form['#submit'][] = 'signup_cancel_multiple_confirm_submit';
+  return confirm_form(
+    $form,
+    t('Are you sure you want to cancel signups for these users?'),
+    'node/'. $node->nid .'/signups',
+    t('This action cannot be undone.'),
+    t('Cancel signups'), t('Keep signups')
+  );
+}
+
+/**
+ * Submit handler for the confirm form to cancel multiple signups.
+ */
+function signup_cancel_multiple_confirm_submit($form, &$form_state) {
+  $nid = $form_state['values']['nid'];
+  foreach ($form_state['values']['users'] as $key) {
+    $matches = array();
+    if (preg_match('/__anon:(.*)/', $key, $matches)) {
+      $uid = 0;
+      $anon_mail = $matches[1];
+    }
+    else {
+      $uid = $key;
+      $anon_mail = NULL;
+    }
+    signup_cancel_signup($uid, $nid, $anon_mail);
+  }
+  $form_state['redirect'] = 'node/'. $nid .'/signups';
+  unset($_SESSION['signup_cancel_multiple_users']);
+}
+
Index: includes/node_admin_summary.inc
===================================================================
RCS file: includes/node_admin_summary.inc
diff -N includes/node_admin_summary.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ includes/node_admin_summary.inc	14 Nov 2008 05:41:22 -0000
@@ -0,0 +1,69 @@
+<?php
+// $Id$
+
+
+/**
+ * @file
+ * Code related to the signup administration tab on each node.
+ */
+
+function signup_node_admin_summary_form($form_state, $node) {
+  $form = array();
+  if ($node->signup_close_signup_limit &&
+    $node->signup_total >= $node->signup_close_signup_limit
+  ) {
+    $form['status'] = array(
+      '#value' => t('Closed (limit reached)'),
+    );
+  }
+  else {
+    $form['status'] = array(
+      '#type' => 'select',
+      '#options' => array(0 => t('Closed'), 1 => t('Open')),
+      '#default_value' => $node->signup_status,
+    );
+  }
+  $form['limit'] = array(
+    '#type' => 'textfield',
+    '#default_value' => $node->signup_close_signup_limit,
+    '#size' => 4, '#maxlength' => 8,
+  );
+  $form['total'] = array(
+    '#value' => $node->signup_total,
+  );
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Update'),
+    '#submit' => array('signup_node_admin_summary_form_submit'),
+  );
+  $form['nid'] = array(
+    '#type' => 'value',
+    '#value' => $node->nid,
+  );
+  return $form;
+}
+
+function signup_node_admin_summary_form_submit($form, &$form_state) {
+  $nid = $form_state['values']['nid'];
+  $node = node_load($nid);
+  $limit_status = 0;
+  if (isset($form_state['values']['limit']) && ($form_state['values']['limit'] != $node->signup_close_signup_limit)) {
+    db_query("UPDATE {signup} SET close_signup_limit = %d WHERE nid = %d", $form_state['values']['limit'], $nid);
+    $node->signup_close_signup_limit = $form_state['values']['limit'];
+    $limit_status = _signup_check_limit($node, 'limit');
+  }
+
+  // Only consider the form's status value if the signup limit didn't
+  // touch the status already.
+  if (!$limit_status && isset($form_state['values']['status']) && ($form_state['values']['status'] != $node->signup_status)) {
+    if ($form_state['values']['status']) {
+      signup_open_signup($nid);
+      drupal_set_message(t('Signups opened for !title.', array('!title' => l($node->title, "node/$node->nid"))));
+    }
+    else {
+      signup_close_signup($nid);
+      drupal_set_message(t('Signups closed for !title.', array('!title' => l($node->title, "node/$node->nid"))));
+    }
+  }
+}
+
Index: includes/node_form.inc
===================================================================
RCS file: includes/node_form.inc
diff -N includes/node_form.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ includes/node_form.inc	14 Nov 2008 05:41:22 -0000
@@ -0,0 +1,226 @@
+<?php
+// $Id$
+
+
+/**
+ * @file
+ * Signup-related code needed while editing a node.
+ */
+
+/**
+ * Save signup-related information when a node is created or edited.
+ *
+ * This is a helper function invoked via signup_nodeapi().  It ensures that
+ * the currently selected signup values are properly saved into the database.
+ * If the node is signup-enabled, the per-node configuration values are saved
+ * to the {signup} table. If signups are disabled, the record from {signup} is
+ * cleared.  If the signup administrator editing the node decided to remove
+ * all signup data, all the records from the {signup_log} table for this node
+ * are also removed.  This function is also responsible for testing if the
+ * node * has a start time and if the autoclose period has already begun, in
+ * which case signups are closed.  Finally, if the signup limit was changed
+ * while editing the node, the function compares the limit against the current
+ * total number of signups and opens or closes signups as appropriate.
+ *
+ * @param $node
+ *   The node object given to signup_nodeapi().
+ * @param $op
+ *   The hook_nodeapi() operation, either 'insert' or 'update'.
+ *
+ * @return
+ *   Nothing, this function is expected to update the database.
+ *
+ * @see signup_nodeapi()
+ */
+function signup_save_node($node, $op) {
+  // See if the form indicates that signups are enabled on this node.
+  if (isset($node->signup_enabled)) {
+    if ($node->signup_enabled == 1) {
+      $values = array(
+        $node->signup_forwarding_email,
+        $node->signup_send_confirmation,
+        $node->signup_confirmation_email,
+        $node->signup_close_signup_limit,
+      );
+      // 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.
+      $values[] = isset($node->signup_send_reminder) ? $node->signup_send_reminder : 0;
+      $values[] = isset($node->signup_reminder_days_before) ? $node->signup_reminder_days_before : 0;
+      $values[] = isset($node->signup_reminder_email) ? $node->signup_reminder_email : '';
+    }
+  }
+  elseif ($op == 'insert' && variable_get('signup_node_default_state_'. $node->type, 'disabled') == 'enabled_on') {
+    // The form doesn't include any information about signups, but the node
+    // type is signup-enabled. This would happen if a user without any signup
+    // admin permissions creates a node that has been signup-enabled based on
+    // the node type. In this case, we use the site-wide default signup
+    // settings.
+    $defaults = db_fetch_object(db_query("SELECT * from {signup} WHERE nid = 0"));
+    $values = array(
+      $defaults->forwarding_email,
+      $defaults->send_confirmation,
+      $defaults->confirmation_email,
+      $defaults->close_signup_limit,
+      $defaults->send_reminder,
+      $defaults->reminder_days_before,
+      $defaults->reminder_email,
+    );
+  }
+
+  if (isset($values)) {
+    // If $values is set, we need to save them to the DB.
+
+    // Before we update the DB, see if the limit is changing, so we can take
+    // appropriate action after we update to the new settings.
+    $has_signup_record = FALSE;
+    $limit_changed = FALSE;
+    if ($op == 'update') {
+      $signup = db_fetch_object(db_query("SELECT close_signup_limit, status FROM {signup} WHERE nid = %d", $node->nid));
+      $cur_limit = $signup->close_signup_limit;
+      $node->signup_status = $signup->status;
+      if ($cur_limit !== FALSE) {
+        $has_signup_record = TRUE;
+        $limit_changed = $cur_limit != $node->signup_close_signup_limit;
+      }
+    }
+
+    // See if we need to update an existing record or insert a new one.
+    // Either way, we always the nid as the final value. The nid will either
+    // be used as the last column in the INSERT, or the argument to the WHERE
+    // clause for the UPDATE.
+    $values[] = $node->nid;
+    if ($has_signup_record) {
+      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);
+    }
+    else {
+      db_query("INSERT INTO {signup} (forwarding_email, send_confirmation, confirmation_email, close_signup_limit, send_reminder, reminder_days_before, reminder_email, nid) VALUES ('%s', %d, '%s', %d, %d, %d, '%s', %d)", $values);
+    }
+    if (_signup_node_completed($node) && $node->signup_status) {
+      // If this is an time-based node, and it's already past the close in
+      // advance time, and signups are still open, close them now (and don't
+      // consider the limit for changing the status).
+      signup_close_signup($node->nid);
+      drupal_set_message(t('%node_type start time is already past the signup close-in-advance time, signups now closed.', array('%node_type' => node_get_types('name', $node->type))));
+    }
+    elseif ($limit_changed) {
+      $node->signup_total = db_result(db_query("SELECT COUNT(*) FROM {signup_log} WHERE nid = %d", $node->nid));
+      _signup_check_limit($node, 'limit');
+    }
+  }
+  elseif ($op == 'update' && isset($node->signup_enabled)) {
+    // $values was not set, because signups are now disabled on this node.
+    switch ($node->signup_enabled) {
+      case 2: // Disabled, and delete {signup_log}, too
+        db_query("DELETE FROM {signup_log} WHERE nid = %d", $node->nid);
+        // No break, fall through and remove from {signup} too.
+      case 0: // Disabled, but leave {signup_log} alone
+        if ($has_signup_record) {
+          db_query("DELETE FROM {signup} WHERE nid = %d", $node->nid);
+        }
+        break;
+    }
+  }
+}
+
+/**
+ * Alters the node form to inject the appropriate per-node signup settings.
+ */
+function signup_alter_node_form(&$form, &$form_state, $form_id) {
+  global $user;
+
+  // Load the node if it already exists.
+  if (!empty($form['nid']['#value'])) {
+    $node = node_load($form['nid']['#value']);
+  }
+  else {
+    $node = NULL;
+  }
+  $node_type = $form['type']['#value'];
+
+  $signup_type_default = variable_get('signup_node_default_state_'. $node_type, 'disabled');
+  if (!empty($node)) {
+    $node_scheduler = _signup_get_node_scheduler($node);
+  }
+  else {
+    $node_scheduler = _signup_get_node_type_scheduler($node_type);
+  }
+  $node_has_date = $node_scheduler != 'none';
+
+  // If signups are possible, and the current user either has the global
+  // 'administer all signups' permission or has the 'administer signups
+  // for own content' permission and is creating new content or editing
+  // their own content, add a fieldset for signup-related settings.
+
+  // Signups are possible if they're not explicitly disallowed for this
+  // node type, or if this node is already signup-enabled (in case an
+  // admin erroneously marks a node-type to disallow signups when there
+  // are already nodes of that type with signups enabled).
+  $signups_possible = $signup_type_default != 'disabled' || (!empty($node) && !empty($node->signup));
+  $admin_all = user_access('administer all signups');
+  $admin_own = user_access('administer signups for own content') && (empty($node) || ($node->uid == $user->uid));
+  if ($signups_possible && ($admin_all || $admin_own)) {
+    $form['signup'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Signup settings'),
+      '#collapsible' => TRUE,
+      '#collapsed' => TRUE,
+      '#weight' => 30,
+    );
+
+    // Figure out what the options should be.  If there are already
+    // people signed-up for this node, we need a 3rd choice: disable
+    // signups and remove all signup data.
+    $has_signups = !empty($node) && db_result(db_query("SELECT COUNT(*) from {signup_log} WHERE nid = %d", $node->nid));
+    $radio_options[1] = t('Enabled');
+    if ($has_signups) {
+      $radio_options[0] = t('Disabled, but save existing signup information');
+      $radio_options[2] = t('Disabled, and remove all signup information') .' <strong>('. t('This can not be undone, use with extreme caution!') .')</strong>';
+    }
+    else {
+      $radio_options[0] = t('Disabled');
+    }
+
+    // Figure out what the default selection for signups should be.
+    if (isset($node->signup)) {
+      $default_option = $node->signup;
+    }
+    else {
+      $default_option = $signup_type_default == 'enabled_on' ? 1 : 0;
+    }
+    if ($default_option == 1) {
+      $hint = t('If enabled, you can control whether users may sign up by visiting the !signups tab and toggling if signups are %open or %closed for this %node_type.', array('!signups' => !empty($node) ? l(t('Signups'), 'node/'. $node->nid .'/signups') : theme('placeholder', t('Signups')), '%open' => t('open'), '%closed' => t('closed'), '%node_type' => node_get_types('name', $node_type)));
+    }
+    else {
+      $hint = '';
+    }
+    // Add the form element to toggle if signups are allowed.
+    $form['signup']['signup_enabled'] = array(
+      '#type' => 'radios',
+      '#options' => $radio_options,
+      '#default_value' => $default_option,
+      '#description' => $hint .'<div class="js-hide">'. t('If disabled, all of the other signup settings will be ignored.') .'</div>',
+      '#prefix' => '<div class="signup-allow-radios">',
+      '#suffix' => '</div>',
+    );
+
+    // If JS is enabled, signup.css will hide all the settings on page
+    // load if signups aren't enabled on this node.
+    $settings_class = "signup-node-settings";
+    if ($default_option != 1) {
+      $settings_class .= " js-hide";
+    }
+
+    // Add the actual settings.  We wrap this in a div to make it easy
+    // to use jQuery to hide these settings when signups are disabled.
+    drupal_add_js(drupal_get_path('module', 'signup') .'/js/node_form.js');
+    drupal_add_css(drupal_get_path('module', 'signup') .'/signup.css');
+    $form['signup']['node_settings'] = array(
+      '#prefix' => '<div class="'. $settings_class .'">',
+      '#suffix' => '</div>',
+    );
+    module_load_include('inc', 'signup', 'includes/node_settings');
+    $form['signup']['node_settings']['settings'] = signup_node_settings_form(array(), $node, $node_type, $node_has_date);
+  }
+}
+
Index: includes/node_output.inc
===================================================================
RCS file: includes/node_output.inc
diff -N includes/node_output.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ includes/node_output.inc	14 Nov 2008 05:41:22 -0000
@@ -0,0 +1,191 @@
+<?php
+// $Id$
+
+
+/**
+ * @file
+ * Code used to generate singup-related output when viewing nodes.
+ */
+
+/**
+ * Generate all the signup-related output for a given node.
+ *
+ * Because of the global setting to control if the signup details and form
+ * appear at the bottom of the node or on a separate tab, this function is
+ * shared by multiple callers.
+ *
+ * @param $node
+ *   The fully loaded node object.
+ * @param $type
+ *   The kind of output would we render: can be either 'node' or 'tab'.
+ *
+ * @return
+ *   The fully rendered HTML for all signup-related forms and info.
+ *
+ * @see signup_nodeapi()
+ * @see signup_node_tab()
+ *
+ * @todo This needs to be much more theme-friendly.
+ *
+ */
+function _signup_node_output($node, $type = 'node') {
+  global $user;
+  $output = theme('signup_node_output_header', $node);
+  // The node has been closed for signups, and the user has
+  // signup permissions.  Let them know it's closed.
+  if (!$node->signup_status) {
+    if (user_access('sign up for content')) {
+      $current_signup = '';
+      // If they're logged in and already signed up, show their current
+      // signup info and give them the option to cancel.
+      if ($user->uid) {
+        $result = db_query("SELECT signup_time, form_data FROM {signup_log} WHERE uid = %d AND nid = %d", $user->uid, $node->nid);
+        $signup_info = db_fetch_object($result);
+        if (!empty($signup_info)) {
+          $current_signup = _signup_print_current_signup($node, $signup_info);
+        }
+      }
+      $output .= theme('signup_signups_closed', $node, $current_signup);
+    }
+  }
+  else {
+    $fieldset = $type == 'node' ? TRUE : FALSE;
+    if ($user->uid == 0) {
+      // This is an anonymous user.
+      if (user_access('sign up for content')) {
+        // If they can signup, render the anonymous sigup form.
+        module_load_include('inc', 'signup', 'includes/signup_form');
+        $output .= drupal_get_form('signup_form', $node, 'anon', $fieldset);
+      }
+      else {
+        // If not, then display the appropriate login/register link if the
+        // default authenticated user role can signup.
+        $anon_login_text = '';
+        $signup_roles = user_roles(FALSE, 'sign up for content');
+        if (!empty($signup_roles[DRUPAL_AUTHENTICATED_RID])) {
+          $token_array = array(
+            '!login' => l(t('login'), 'user/login', array('query' => drupal_get_destination())),
+            '!register' => l(t('register'), 'user/register', array('query' => drupal_get_destination())),
+            '%node_type' => node_get_types('name', $node->type),
+          );
+          if (variable_get('user_register', 1) == 0) {
+            $anon_login_text = t('Please !login to sign up for this %node_type.', $token_array);
+          }
+          else {
+            $anon_login_text = t('Please !login or !register to sign up for this %node_type.', $token_array);
+          }
+        }
+        $output .= theme('signup_anonymous_user_login_text', $anon_login_text);
+      }
+    }
+    else {
+      // An authenticated user.
+
+      // See if the user is already signed up for this node.
+      $result = db_query("SELECT signup_time, form_data FROM {signup_log} WHERE uid = %d AND nid = %d", $user->uid, $node->nid);
+      $signup_info = db_fetch_object($result);
+      if (empty($signup_info)) {
+        // Not yet signed up
+        if (user_access('sign up for content')) {
+          // User has permission to do so, so give them the form.
+          module_load_include('inc', 'signup', 'includes/signup_form');
+          $output .= drupal_get_form('signup_form', $node, 'auth', $fieldset);
+        }
+      }
+      else {
+        // Already signed up, display their info.
+        $output .= _signup_print_current_signup($node, $signup_info);
+      }
+    }
+  }
+
+  // How should the list of signed-up users be displayed, if at all?
+  $display_list = variable_get('signup_display_signup_user_list', 'signup');
+
+  // If the user has the view signups perm and the admin decides to display
+  // the list at the bottom of the page, display the current signups.
+  if (user_access('view all signups')) {
+    if ($display_list == 'signup') {
+      // Admin wants the hard-coded signup listing.
+      $registered_query = db_query("SELECT u.uid, u.name, s.signup_time, s.form_data FROM {signup_log} s INNER JOIN {users} u ON u.uid = s.uid WHERE s.nid = %d AND u.uid <> 0", $node->nid);
+      $registered_signups = array();
+      while ($signed_up_user = db_fetch_object($registered_query)) {
+        $registered_signups[] = $signed_up_user;
+      }
+      $anon_query = db_query("SELECT * FROM {signup_log} WHERE nid = %d AND uid = 0", $node->nid);
+      $anon_signups = array();
+      while ($signed_up_user = db_fetch_object($anon_query)) {
+        $anon_signups[] = $signed_up_user;
+      }
+      $output .= theme('signup_user_list', $node, $registered_signups, $anon_signups);
+    }
+    elseif ($display_list == 'embed-view' && module_exists('views')) {
+      $signup_view = variable_get('signup_user_list_view', 'signup_user_list:page');
+      $signup_view_parts = explode(':', $signup_view);
+      $view_name = $signup_view_parts[0];
+      $view_display = $signup_view_parts[1];
+      $view = views_get_view($view_name);
+      $view_args = array($node->nid);
+      $output .=  $view->preview($view_display, $view_args);
+    }
+    // Otherwise, they're on their own, and either don't want it displayed at
+    // all, or they want to handle where/how it's displayed via views.
+  }
+  return $output;
+}
+
+/**
+ * Page handler for the optional 'signup' tab on nodes.
+ *
+ * This is only used if the site has configured the signup form and related
+ * output to appear on a separate tab, instead of directly embedded in the
+ * node.
+ *
+ * @param $node
+ *   The node to generate a signup tab for.
+ *
+ * @return
+ *   The contents of the signup tab.
+ *
+ * @see _signup_node_output()
+ */
+function signup_node_tab($node) {
+  drupal_set_title(check_plain($node->title));
+  return _signup_node_output($node, 'tab');
+}
+
+/**
+ * Helper function to display the current user's signup information.
+ *
+ * Contains the logic to determine what should be displayed, then invokes the
+ * appropriate form builder and theme functions.
+ *
+ * @param $node
+ *   The fully-loaded node object to display signup data about.
+ * @param $signup_info
+ *   Database object with information about the signup to display.
+ *
+ * @return
+ *   Themed HTML to output for the given node and signup.
+ *
+ * @see theme_signup_current_signup()
+ */
+function _signup_print_current_signup($node, $signup_info) {
+  global $user;
+  // Generate an array of data about the current signup for the theme function.
+  $signup_data = array();
+  $signup_data['custom_data'] = unserialize($signup_info->form_data);
+  $signup_data['signup_timestamp'] = $signup_info->signup_time;
+
+  // See if the current user is allowed to cancel their signup, and if so,
+  // render the HTML for the cancel form (which is just a single button).
+  $cancel_signup_form = '';
+  if (user_access('cancel own signups')) {
+    module_load_include('inc', 'signup', 'includes/signup_cancel_form');
+    $cancel_signup_form = drupal_get_form('signup_cancel_form', $node, $user);
+  }
+
+  // Hand off everything to the theme function for actual HTML generation.
+  return theme('signup_current_signup', $signup_data, $cancel_signup_form);
+}
+
Index: includes/node_settings.inc
===================================================================
RCS file: includes/node_settings.inc
diff -N includes/node_settings.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ includes/node_settings.inc	14 Nov 2008 05:41:22 -0000
@@ -0,0 +1,120 @@
+<?php
+// $Id$
+
+
+/**
+ * @file
+ * Code related to the per-node signup settings form.
+ */
+
+/**
+ * Returns the form for the per-node signup settings.
+ *
+ * This is shared by the settings page and the node edit page.
+ *
+ * @param $node
+ *   The fully loaded node object if we've got it.
+ * @param $node_type
+ *   The type of the node.  When creating new content, the caller can know the
+ *   node type, even if $node is NULL.
+ * @param $has_date
+ *   Boolean flag indicating if this node (or site) has signup-aware
+ *   date functionality, which is required for reminder emails to be in
+ *   the form.
+ *
+ * @return
+ *   The form array for the per-node signup settings.
+ *
+ */
+function signup_node_settings_form($form_state, $node = NULL, $node_type = NULL, $has_date = FALSE) {
+  if (module_exists('token')) {
+    $signup_token_description = t('Supported string substitutions: %node_title, %node_url, %node_start_time, %user_name, %user_mail, %user_signup_info (additional information from the signup form), and any tokens in the %replacement_tokens list.', array('%replacement_tokens' => t('Replacement tokens')));
+  }
+  else {
+    $signup_token_description = t('Supported string substitutions: %node_title, %node_url, %node_start_time, %user_name, %user_mail, %user_signup_info (additional information from the signup form).');
+  }
+
+  // Load the default admin form data for new nodes.
+  if (!$node || !$node->signup) {
+    $result = db_fetch_object(db_query("SELECT * FROM {signup} WHERE nid = 0"));
+    $node->signup_forwarding_email = $result->forwarding_email;
+    $node->signup_send_confirmation = $result->send_confirmation;
+    $node->signup_confirmation_email = $result->confirmation_email;
+    $node->signup_send_reminder = $result->send_reminder;
+    $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;
+  }
+
+  $form['signup_forwarding_email'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Send signups to'),
+    '#default_value' => $node->signup_forwarding_email,
+    '#size' => 40, '#maxlength' => 64,
+    '#description' => t('Email address where notification of new signups will be sent. Leave blank for no notifications.'),
+  );
+  $form['signup_send_confirmation'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Send confirmation'),
+    '#default_value' => $node->signup_send_confirmation,
+  );
+  $form['signup_confirmation_email'] = array(
+    '#type' => 'textarea',
+    '#title' => t('Confirmation email'),
+    '#default_value' => $node->signup_confirmation_email,
+    '#cols' => 40, '#rows' => 6,
+    '#description' => t('Email sent to user upon signup. !token_description', array('!token_description' => $signup_token_description)),
+  );
+  if (module_exists('token')) {
+    module_load_include('inc', 'signup', 'includes/token_help');
+    _signup_token_help($form, 'signup_confirmation_token_fieldset');
+  }
+
+  if ($has_date) {
+    // Define a sub-tree to wrap the next 2 form elements together in an
+    // inline div for better display.
+    $form['signup_reminder'] = array(
+      '#prefix' => '<div class="container-inline">',
+      '#suffix' => '</div>',
+    );
+    $form['signup_reminder']['signup_send_reminder'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Send reminder'),
+      '#default_value' => $node->signup_send_reminder,
+    );
+    $options = array();
+    for ($i = 1; $i <= 60; $i++) {
+      $options[$i] = $i;
+    }
+    $node_type_name = isset($node_type) ? node_get_types('name', $node_type) : '';
+    $form['signup_reminder']['signup_reminder_days_before'] = array(
+      '#type' => 'select',
+      '#default_value' => $node->signup_reminder_days_before,
+      '#options' => $options,
+      '#suffix' => !empty($node_type_name) ? t('day(s) before this %node_type', array('%node_type' => $node_type_name)) : t('day(s) before start time'),
+    );
+    $form['signup_reminder_email'] = array(
+      '#type' => 'textarea',
+      '#title' => t('Reminder email'),
+      '#default_value' => $node->signup_reminder_email,
+      '#cols' => 40, '#rows' => 6,
+      '#description' =>  !empty($node_type_name) ? t('Email sent to user as a reminder before the %node_type starts. !token_description', array('%node_type' => $node_type_name, '!token_description' => $signup_token_description)) : t('Email sent to user as a reminder before the start time. !token_description', array('!token_description' => $signup_token_description)),
+    );
+    if (module_exists('token')) {
+      module_load_include('inc', 'signup', 'includes/token_help');
+      _signup_token_help($form, 'signup_reminder_token_fieldset');
+    }
+  }
+
+  $form['signup_close_signup_limit'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Signup limit'),
+    '#default_value' => $node->signup_close_signup_limit,
+    '#size' => 4, '#maxlength' => 8,
+    '#description' => t('Maximum number of users who can sign up before signups are automatically closed. If set to 0, there is no limit.'),
+  );
+  $form['signup'] = array('#type' => 'hidden', '#value' => 1);
+
+  return $form;
+}
+
Index: includes/signup_cancel_form.inc
===================================================================
RCS file: includes/signup_cancel_form.inc
diff -N includes/signup_cancel_form.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ includes/signup_cancel_form.inc	14 Nov 2008 05:41:22 -0000
@@ -0,0 +1,40 @@
+<?php
+// $Id$
+
+
+/**
+ * @file
+ * Code for the signup cancel form.
+ */
+
+/**
+ * Submit handler for the cancel signup form.
+ *
+ * @param $form
+ *   The form being submitted.
+ * @param $form_state
+ *   The state of the submitted form, including the values.
+ */
+function signup_cancel_form_submit($form, &$form_state) {
+  $uid = $form_state['values']['uid'];
+  $nid = $form_state['values']['nid'];
+  $anon_mail = NULL;
+  if (isset($form_state['values']['signup_anon_mail'])) {
+    $anon_mail = $form_state['values']['signup_anon_mail'];
+  }
+  signup_cancel_signup($uid, $nid, $anon_mail);
+}
+
+/**
+ * Form builder for the cancel signup form.
+ */
+function signup_cancel_form(&$form_state, $node, $account) {
+  $form['nid'] = array('#type' => 'value', '#value' => $node->nid);
+  $form['uid'] = array('#type' => 'value', '#value' => $account->uid);
+  if (!empty($account->anon_mail)) {
+    $form['signup_anon_mail'] = array('#type' => 'value', '#value' => $account->anon_mail);
+  }
+  $form['submit'] = array('#type' => 'submit', '#value' => t('Cancel signup'));
+  return $form;
+}
+
Index: includes/signup_form.inc
===================================================================
RCS file: includes/signup_form.inc
diff -N includes/signup_form.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ includes/signup_form.inc	14 Nov 2008 05:41:22 -0000
@@ -0,0 +1,192 @@
+<?php
+// $Id$
+
+
+/**
+ * @file
+ * Code for the form when users sign up.
+ */
+
+/**
+ * Build the user signup form.
+ *
+ * @param $node
+ *   The fully loaded node object.
+ * @param $signup_type
+ *   Determines what kind of signup to generate a form for. Possible values:
+ *    'auth' -- regular authenticated user signup form
+ *    'anon' -- anonymous user signup form (includes required email field).
+ *    'admin' -- admin form to signup another user (includes user selector).
+ * @param $fieldset
+ *   Boolean that indicates if the signup form should be in a fieldset.
+ */
+function signup_form(&$form_state, $node, $signup_type = 'auth', $fieldset = TRUE) {
+  global $user;
+
+  $form = array();
+  $form['nid'] = array('#type' => 'value', '#value' => $node->nid);
+  $form['uid'] = array('#type' => 'value', '#value' => $user->uid);
+
+  if ($fieldset) {
+    $form['collapse'] = array(
+      '#type' => 'fieldset',
+      '#collapsible' => TRUE,
+      '#collapsed' => variable_get('signup_fieldset_collapsed', 1),
+    );
+    if ($signup_type == 'admin') {
+      $form['collapse']['#title'] = t('Sign up another user');
+    }
+    else {
+      $form['collapse']['#title'] = t('Sign up for @title', array('@title' => $node->title));
+    }
+  }
+  else {
+    $form['collapse'] = array();
+  }
+
+  $signup_form = array();
+  if ($signup_type == 'anon') {
+    $anon_form = array();
+    $anon_form['signup_anon_mail'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Email'),
+      '#description' => t('An e-mail address is required for users who are not registered at this site. If you are a registered user at this site, please !login to sign up for this %node_type.', array('!login' => l(t('login'), 'user/login', array('query' => drupal_get_destination())), '%node_type' => node_get_types('name', $node->type))),
+      '#size' => 40,
+      '#maxlength' => 255,
+      '#required' => TRUE,
+    );
+    $validate_handler = 'signup_form_validate_anon';
+    $signup_form += $anon_form;
+  }
+  elseif ($signup_type == 'admin') {
+    $admin_form = array();
+    $admin_form['signup_username'] = array(
+      '#title' => t('Username'),
+      '#type' => 'textfield',
+      '#autocomplete_path' => 'user/autocomplete',
+      '#maxlength' => USERNAME_MAX_LENGTH,
+      '#size' => 40,
+      '#weight' => -1,
+      '#required' => TRUE,
+    );
+    $validate_handler = 'signup_form_validate_username';
+    $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']);
+    }
+  }
+  $signup_form += $signup_themed_form;
+
+  $form['collapse']['signup_user_form'] = $signup_form;
+  $form['collapse']['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Sign up'),
+  );
+  if (!empty($validate_handler)) {
+    $form['#validate'][] = $validate_handler;
+  }
+  return $form;
+}
+
+/**
+ * Submit handler for the user signup form.
+ *
+ * @param $form
+ *   The form being submitted.
+ * @param $form_values
+ *   The state of the form, including the submitted values.
+ */
+function signup_form_submit($form, &$form_state) {
+  if (isset($form_state['values']['signup_username'])) {
+    $account = user_load(array('name' => $form_state['values']['signup_username']));
+    $form_state['values']['uid'] = $account->uid;
+  }
+  signup_sign_up_user($form_state['values']);
+}
+
+/**
+ * Validate handler for the email address on the anonymous user signup form.
+ *
+ * @param $form
+ *   Form array for the anonymous user email field.
+ * @param $form_state
+ *   State of the form, including the submitted values to validate.
+ */
+function signup_form_validate_anon($form, $form_state) {
+  $nid = $form_state['values']['nid'];
+  $anon_mail = $form_state['values']['signup_anon_mail'];
+  signup_validate_anon_email($nid, $anon_mail, 'signup_anon_mail');
+}
+
+/**
+ * Validate the email address for an anonymous signup.
+ *
+ * @param $nid
+ *   The node the user is signing up for.
+ * @param $anon_mail
+ *   The anonymous email address to validate.
+ * @param $name
+ *   The form element being validated (optional).
+ *
+ * @return Boolean.
+ *   TRUE if the address validates, FALSE otherwise.
+ */
+function signup_validate_anon_email($nid, $anon_mail, $name = FALSE) {
+  if (!valid_email_address($anon_mail)) {
+    $message = t('Invalid email address entered for signup.');
+  }
+  elseif (db_result(db_query("SELECT COUNT(*) FROM {users} WHERE mail = '%s'", $anon_mail))) {
+    $message = t('The email address entered belongs to a registered user.');
+  }
+  elseif (db_result(db_query("SELECT COUNT(*) FROM {signup_log} WHERE anon_mail = '%s' AND nid = %d", $anon_mail, $nid))) {
+    $node = node_load($nid);
+    $message = t('The email address entered has already been used to sign up for this %node_type.', array('%node_type' => node_get_types('name', $node->type)));
+  }
+
+  // If there's no message, it's a valid email, so return success.
+  if (!isset($message)) {
+    return TRUE;
+  }
+
+  // Depending on how we were called, propagate the error accordinly.
+  if ($name) {
+    form_set_error($name, $message);
+  }
+  else {
+    drupal_set_message($message, 'error');
+  }
+  return FALSE;
+}
+
+/**
+ * Validates the username on the admin form to signup another user.
+ *
+ * @param $form
+ *   Form array for the username field.
+ * @param $nid
+ *   Node id of the node the user is being signed up for.
+ */
+function signup_form_validate_username($form, $form_state) {
+  $nid = $form_state['values']['nid'];
+  $username = $form_state['values']['signup_username'];
+  $account = user_load(array('name' => $username));
+  if (empty($account)) {
+    form_set_error('signup_username', t('User %user_name does not exist.', array('%user_name' => $username)));
+  }
+  elseif (!user_access('sign up for content', $account)) {
+    form_set_error('signup_username', t('User !user does not have permission to sign up.', array('!user' => theme('username', $account))));
+  }
+  elseif (db_result(db_query("SELECT COUNT(*) FROM {signup_log} WHERE uid = %d AND nid = %d", $account->uid, $nid)) > 0) {
+    $node = node_load($nid);
+    form_set_error('signup_username', t('User !user is already signed up for %title', array('!user' => theme('username', $account), '%title' => $node->title)));
+  }
+}
+
Index: includes/token_help.inc
===================================================================
RCS file: includes/token_help.inc
diff -N includes/token_help.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ includes/token_help.inc	14 Nov 2008 05:41:23 -0000
@@ -0,0 +1,67 @@
+<?php
+// $Id$
+
+
+/**
+ * @file
+ * Code for building help text for token.module support on various forms.
+ */
+
+/**
+ * Private function to generate HTML for showing the available tokens.
+ *
+ * @param $form
+ *   Reference to the form array to include the help fieldset in.
+ * @param $element_name
+ *   Name of the form element to use for the help fieldset.
+ */
+function _signup_token_help(&$form, $element_name) {
+  $form[$element_name] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Replacement tokens'),
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
+  );
+  $form[$element_name]['help_text'] = array(
+    '#value' => _signup_build_token_help(),
+  );
+}
+
+/**
+ * Private function to generate HTML for showing the available tokens
+ *
+ * @return The themed representation of the available tokens.
+ */
+function _signup_build_token_help() {
+  static $help_html = '';
+  if (empty($help_html)) {
+    $patterns = token_get_list('node');
+    foreach ($patterns as $type => $pattern_set) {
+      foreach ($pattern_set as $pattern => $description) {
+        $tokens[$pattern] = $description;
+      }
+    }
+    $help_html = theme('signup_token_help', $tokens);
+  }
+  return $help_html;
+}
+
+/**
+ * theme_signup_token_help()
+ *
+ * @param $tokens
+ *   An array of token patterns mapping to their description
+ *
+ * @return The themed representation of the tokens.
+ */
+function theme_signup_token_help($tokens) {
+  $tokens_html = "<dl>\n";
+  foreach ($tokens as $name => $description) {
+    $tokens_html .= '<dt>['. $name .']</dt>';
+    $tokens_html .= '<dd>'. $description ."</dd>\n";
+  }
+  $tokens_html .= "</dl>\n";
+
+  return $tokens_html;
+}
+
cvs diff: Diffing panels
cvs diff: Diffing panels/content_types
Index: panels/content_types/signup_form.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/signup/panels/content_types/signup_form.inc,v
retrieving revision 1.3
diff -u -p -r1.3 signup_form.inc
--- panels/content_types/signup_form.inc	14 Nov 2008 05:40:28 -0000	1.3
+++ panels/content_types/signup_form.inc	14 Nov 2008 05:41:23 -0000
@@ -50,6 +50,7 @@ function signup_panels_content_signup_fo
   }
   else {
     $block->delta = $node->nid;
+    module_load_include('inc', 'signup', 'includes/node_output');
     $block->content = _signup_node_output($node);
   }
   return $block;
