Index: signup.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/signup/signup.module,v
retrieving revision 1.194
diff -u -p -r1.194 signup.module
--- signup.module	13 Nov 2008 07:52:42 -0000	1.194
+++ signup.module	13 Nov 2008 16:06:19 -0000
@@ -77,8 +77,9 @@ function signup_theme() {
       'path' => $path,
       'arguments' => array(
         'node' => NULL,
-        'header' => NULL,
-        'rows' => NULL,
+        'signup_node_admin_summary_form' => NULL,
+        'signup_node_admin_details_form' => NULL,
+        'signup_form' => NULL,
       ),
     ),
     'signup_node_admin_summary_form' => array(
@@ -88,6 +89,13 @@ function signup_theme() {
         'form' => NULL,
       ),
     ),
+    'signup_node_admin_details_form' => array(
+      'file' => 'node.admin.inc',
+      'path' => $path,
+      'arguments' => array(
+        'form' => NULL,
+      ),
+    ),
     'signup_custom_data' => array(
       'file' => 'node.admin.inc',
       'path' => $path,
@@ -440,6 +448,13 @@ function signup_menu() {
     'type' => MENU_LOCAL_TASK,
     'weight' => 20,
   );
+  $items['node/%node/signups/confirm'] = array(
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('signup_cancel_multiple_confirm', 1),
+    'access callback' => '_signup_menu_access',
+    'access arguments' => array(1, 'admin'),
+    'type' => MENU_CALLBACK,
+  );
   $items['node/%node/signup-broadcast'] = array(
     'title' => 'Signup broadcast',
     'page callback' => 'drupal_get_form',
@@ -1878,51 +1893,159 @@ function signup_sign_up_user($signup_for
 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')),
-    array('data' => t('Operations')),
   );
 
   $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);
-  $rows = array();
 
   // Loop through the users, unserializing their user data.
   while ($signed_up_user = db_fetch_object($result)) {
-    $table_data = array();
-    $form_data = unserialize($signed_up_user->form_data);
-
-    // Compose the user data.
-    $signup_form_data = theme('signup_custom_data', $form_data);
-
     // The username and the unique form identifier are different for
     // anon signups and registered user signups.  For registered users,
     // 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);
-      $id = $signed_up_user->anon_mail;
     }
     else {
+      $key = $signed_up_user->uid;
       $username = theme('username', $signed_up_user);
-      $id = $signed_up_user->uid;
     }
 
-    // Build the row for this user.
-    $rows[] = array(
-      'username' => $username,
-      'signup_date' => format_date($signed_up_user->signup_time, variable_get('signup_date_format', 'small')),
-      'signup_form_data' => $signup_form_data,
-      'cancel_signup_form' => drupal_get_form('signup_cancel_form', $node, $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['table_header'] = array(
+      '#type' => 'value',
+      '#value' => $header,
     );
   }
+  return $form;
+}
 
-  return theme('signup_node_admin_page', $node, $header, $rows);
+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) {
Index: theme/node.admin.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/signup/theme/node.admin.inc,v
retrieving revision 1.2
diff -u -p -r1.2 node.admin.inc
--- theme/node.admin.inc	13 Nov 2008 07:52:42 -0000	1.2
+++ theme/node.admin.inc	13 Nov 2008 16:06:19 -0000
@@ -18,33 +18,29 @@
  *
  * @param $node
  *   The node object for the signup-enabled node this is a tab on.
- * @param $header
- *   Array containing the header for the signup details table.
- * @param $rows
- *   Array containing the rows for the signup details table, keyed by
- *   column name.
+ * @param $signup_node_admin_summary_form
+ *   The rendered HTML for the signup node summary form (to set the signup
+ *   limit, open/close signups, see the total number of signups, etc).
+ * @param $signup_node_admin_details_form
+ *   The rendered HTML for the signup node details form (to view all the users
+ *   who have signed up, their full signup details, and checkboxes to cancel
+ *   multiple signups at once.
+ * @param $signup_form
+ *   If the signups are open on this node, the HTML to signup another user,
+ *   otherwise, and empty string.
  */
-function theme_signup_node_admin_page($node, $header, $rows) {
+ function theme_signup_node_admin_page($node, $signup_node_admin_summary_form, $signup_node_admin_details_form, $signup_form) {
   $output = '';
 
-  // Administrative table to control signups for this node.
-  $output .= drupal_get_form('signup_node_admin_summary_form', $node);
+  // Administrative summary table to control signups for this node.
+  $output .= $signup_node_admin_summary_form;
 
-  $signup_details_table = theme('table', $header, $rows);
-
-  $fieldset = array(
-    '#title' => t('Signup details'),
-    '#collapsible' => TRUE,
-    '#collapsed' => FALSE,
-    '#value' => $signup_details_table,
-  );
-  $output .= theme('fieldset', $fieldset);
-
-  if ($node->signup_status) {
-    // Add a form to allow the administrator to signup other users.
-    $output .= drupal_get_form('signup_form', $node, 'admin');
-  }
+  // Details for each user who signed up.
+  $output .= $signup_node_admin_details_form;
 
+  // Form for admins to signup other users (if signups are still open).
+  $output .= $signup_form;
+  
   return $output;
 }
 
@@ -73,6 +69,31 @@ function theme_signup_node_admin_summary
   return theme('fieldset', $fieldset);
 }
 
+function theme_signup_node_admin_details_form($form) {
+  $fieldset = array(
+    '#title' => t('Signup details'),
+    '#collapsible' => TRUE,
+    '#collapsed' => FALSE,
+  );
+  if (!empty($form['users']['#options'])) {
+    $header = $form['table_header']['#value'];
+    $rows = array();
+    foreach ($form['users']['#options'] as $key => $value) {
+      $rows[] = array(
+        'cancel_checkbox' => drupal_render($form['users'][$key]),
+        'username' => drupal_render($form['username'][$key]),
+        'signup_date' => drupal_render($form['signup_date'][$key]),
+        'signup_form_data' => drupal_render($form['signup_form_data'][$key]),
+      );
+    }
+    $fieldset['#value'] = theme('table', $header, $rows) . drupal_render($form['submit_cancel']);
+  }
+  else {
+    $fieldset['#value'] = '<span>'. drupal_render($form['no_users']) .'</span>';
+  }
+  return theme('fieldset', $fieldset) . drupal_render($form);
+}
+
 /**
  * Renders custom signup user data into a human-readable format.
  *
