Index: apply_for_role.admin.inc
===================================================================
RCS file: apply_for_role.admin.inc
diff -N apply_for_role.admin.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ apply_for_role.admin.inc	8 Aug 2008 01:08:09 -0000
@@ -0,0 +1,215 @@
+<?php
+// $Id $
+
+/**
+ * @file
+ * Administration forms for the Apply for Role module.
+ */
+
+/**
+ * Module settings page.
+ */
+function apply_for_role_settings_form(&$form_state) {
+  $selected_roles = variable_get('users_apply_roles', array());
+  foreach ((array)$selected_roles as $rid => $value) {
+    if ($rid > 2) {
+      $selected_rids[] = $rid;
+    }
+  }
+  $form['options'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Apply for role options'),
+  );
+  $form['options']['multiple'] = array(
+    '#type' => 'radios',
+    '#title' => t('Allow multiple roles per application'),
+    '#options' => array(t('No'), t('Yes')),
+    '#default_value' => variable_get('apply_for_role_multiple', 0),
+    '#description' => t("Chosing 'no' will limit users to applying for only one role per role application. Choosing 'yes' will allow users to apply for multiple roles per role application."),
+    '#required' => TRUE,
+  );
+  $form['options']['register'] = array(
+    '#type' => 'radios',
+    '#title' => t('Allow users to apply for roles on registration'),
+    '#options' => array(t('No'), t('Yes')),
+    '#default_value' => variable_get('apply_for_role_register', 0),
+    '#description' => t("Choosing 'yes' will allow users to apply for roles when creating a new account."),
+    '#required' => TRUE,
+  );
+  $roles = (user_roles(TRUE));
+  unset($roles[DRUPAL_AUTHENTICATED_RID]);
+  $form['roles'] = array(
+    '#type' => 'checkboxes',
+    '#title' => t('Roles'),
+    '#default_value' => isset($selected_rids) ? $selected_rids : array(2),
+    '#options' => $roles,
+    '#description' => t('Select the roles that users will be able to apply for.'),
+    '#required' => TRUE,
+  );
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Update'),
+  );
+
+  return $form;
+}
+
+function apply_for_role_settings_form_submit($form, &$form_state) {
+  $roles = user_roles(TRUE);
+  foreach ($form_state['values']['roles'] as $key => $value) {
+    if ($value) {
+      $selected_roles[$value] = $roles[$value];
+    }
+  }
+
+  variable_set('users_apply_roles', $selected_roles);
+  variable_set('apply_for_role_multiple', $form['options']['multiple']['#value']);
+  variable_set('apply_for_role_register', $form['options']['register']['#value']);
+  drupal_set_message(t('Apply for role settings have been saved.'));
+
+  $form_state['redirect'] = 'admin/settings/apply_for_role';
+  return;
+}
+
+/**
+ * User management
+ */
+function apply_for_role_admin_form(&$form_state) {
+  $header = array(
+    array('data' => t('Username'), 'field' => 'u.name'),
+    array('data' => t('Current Roles')),
+    array('data' => t('Applying For'), 'field' => 'rid'),
+    array('data' => t('Applied'), 'field' => 'apply_date', 'sort' => 'desc'),
+    array('data' => t('Status'), 'field' => 'approved'),
+    array('data' => t('Processed'), 'field' => 'approve_date'),
+    array('data' => t('Operations'), 'colspan' => 2),
+  );
+  $form['#header'] = $header;
+
+  $roles = user_roles(TRUE);
+
+  $result = db_query("SELECT * FROM {users_roles_apply} a LEFT JOIN {users} u ON u.uid = a.uid ". tablesort_sql($header));
+  while ($row = db_fetch_object($result)) {
+    $user = user_load(array('uid' => $row->uid));
+
+    $form['apps'][$row->uid][$row->rid]['user'] = array(
+      '#value' => theme('username', $user),
+    );
+    $form['apps'][$row->uid][$row->rid]['role'] = array(
+      '#value' => isset($roles[$row->rid]) ? check_plain($roles[$row->rid]) : t('<em>Invalid Role</em>'),
+    );
+    $current_roles = array_slice($user->roles, 1);
+    $form['apps'][$row->uid][$row->rid]['current_roles'] = array(
+      '#value' => filter_xss_admin(implode('<br />', $current_roles)),
+    );
+    $form['apps'][$row->uid][$row->rid]['apply_date'] = array(
+      '#value' => format_date($row->apply_date),
+    );
+    $form['apps'][$row->uid][$row->rid]['status'] = array(
+      '#value' => theme('apply_for_role_status', $row->approved),
+    );
+    $form['apps'][$row->uid][$row->rid]['approve_date'] = array(
+      '#value' => empty($row->approve_date) ? '' : format_date($row->approve_date),
+    );
+    if (!empty($row->uid) && isset($roles[$row->rid])) {
+      if ($row->approved != APPLY_FOR_ROLE_APPROVED) {
+        $form['apps'][$row->uid][$row->rid]['approve'] = array(
+          '#value' => l(t('Approve'), 'admin/user/apply_for_role/approve/'. $row->uid .'/'. $row->rid, array('title' => t('Approve this user'))),
+        );
+      }
+      if ($row->approved != APPLY_FOR_ROLE_DENIED) {
+        $form['apps'][$row->uid][$row->rid]['deny'] = array(
+          '#value' => l(t('Deny'), 'admin/user/apply_for_role/deny/'. $row->uid .'/'. $row->rid, array('title' => t('Deny this user'))),
+        );
+      }
+    }
+    $form['apps'][$row->uid][$row->rid]['delete'] = array(
+      '#value' => l(t('Delete'), 'admin/user/apply_for_role/remove/'. $row->uid .'/'. $row->rid, array('title' => t('Remove application'))),
+    );
+  }
+
+  return $form;
+}
+
+function theme_apply_for_role_admin_form($form) {
+  $rows = array();
+  foreach (element_children($form['apps']) as $uid) {
+    foreach (element_children($form['apps'][$uid]) as $rid) {
+      $rows[] = array(
+        array('data' => drupal_render($form['apps'][$uid][$rid]['user'])),
+        array('data' => drupal_render($form['apps'][$uid][$rid]['current_roles'])),
+        array('data' => drupal_render($form['apps'][$uid][$rid]['role'])),
+        array('data' => drupal_render($form['apps'][$uid][$rid]['apply_date'])),
+        array('data' => drupal_render($form['apps'][$uid][$rid]['status'])),
+        array('data' => drupal_render($form['apps'][$uid][$rid]['approve_date'])),
+        array('data' => drupal_render($form['apps'][$uid][$rid]['approve'])),
+        array('data' => drupal_render($form['apps'][$uid][$rid]['deny'])),
+        array('data' => drupal_render($form['apps'][$uid][$rid]['delete'])),
+      );
+    }
+  }
+
+  if (empty($rows)) {
+    $rows[] = array(array('data' => '<em>'. t('There are currently no applications.') .'</em>', 'colspan' => 9));
+  }
+
+  return theme('table', $form['#header'], $rows) . drupal_render($form);
+}
+
+
+function apply_for_role_approve_form(&$form_state, $user, $rid) {
+  $form['user'] = array('#type' => 'value', '#value' => $user);
+  $form['rid'] = array('#type' => 'value', '#value' => $rid);
+
+  $roles = user_roles(TRUE);
+  return confirm_form($form, t('Do you want to approve the application from user %username for role %role', array('%username' => $user->name, '%role' => $roles[$rid])), 'admin/user/apply_for_role', t('The role will be automatically assigned to the user.'), t('Approve'));
+}
+
+function apply_for_role_approve_form_submit($form, &$form_state) {
+  if (apply_for_role_approve_apply($form_state['values']['user'], $form_state['values']['rid'])) {
+    drupal_set_message(t('The application was approved and the user was added to the role.'));
+  }
+  else {
+    drupal_set_message(t('Error approving application. Please try again!'), 'error');
+  }
+  cache_clear_all();
+  $form_state['redirect'] = 'admin/user/apply_for_role';
+}
+
+function apply_for_role_deny_form(&$form_state, $user, $rid) {
+  $form['user'] = array('#type' => 'value', '#value' => $user);
+  $form['rid'] = array('#type' => 'value', '#value' => $rid);
+
+  $roles = user_roles(TRUE);
+  return confirm_form($form, t('Do you want to deny the application from user %username for role %role', array('%username' => $user->name, '%role' => $roles[$rid])), 'admin/user/apply_for_role', t('The role will not be assigned to the user and the user will not be able to apply again.'), t('Deny'));
+}
+
+function apply_for_role_deny_form_submit($form, &$form_state) {
+  if (apply_for_role_deny_apply($form_state['values']['user'], $form_state['values']['rid'])) {
+    drupal_set_message(t('The application was denied .'));
+  }
+  else {
+    drupal_set_message(t('Error denying application. Please try again!'), 'error');
+  }
+  cache_clear_all();
+  $form_state['redirect'] = 'admin/user/apply_for_role';
+}
+
+function apply_for_role_remove_form(&$form_state, $user, $rid) {
+  $form['user'] = array('#type' => 'value', '#value' => $user);
+  $form['rid'] = array('#type' => 'value', '#value' => $rid);
+
+  $roles = user_roles(TRUE);
+  return confirm_form($form, t('Do you want to remove the application from user %username for role %role', array('%username' => $user->name, '%role' => isset($roles[$rid]) ? $roles[$rid] : t('Invalid role'))), 'admin/user/apply_for_role', t('The role will be automatically deleted from the user and they will be allowed to re-apply for the role.'), t('Delete'));
+}
+
+function apply_for_role_remove_form_submit($form, &$form_state) {
+  if (apply_for_role_remove_apply($form_state['values']['user'], $form_state['values']['rid'])) {
+    drupal_set_message(t('The application was deleted.'));
+  }
+  else {
+    drupal_set_message(t('Error deleting application. Please try again!'), 'error');
+  }
+  cache_clear_all();
+  $form_state['redirect'] = 'admin/user/apply_for_role';
+}
\ No newline at end of file
Index: apply_for_role.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/apply_for_role/apply_for_role.module,v
retrieving revision 1.29
diff -u -p -r1.29 apply_for_role.module
--- apply_for_role.module	5 Aug 2008 19:06:07 -0000	1.29
+++ apply_for_role.module	8 Aug 2008 01:08:09 -0000
@@ -6,11 +6,23 @@
  * Allows users to apply for roles.
  */
 
+define('APPLY_FOR_ROLE_PENDING', 0);
+define('APPLY_FOR_ROLE_APPROVED', 1);
+define('APPLY_FOR_ROLE_DENIED', 2);
+
+if (module_exists('token')) {
+  module_load_include('inc', 'apply_for_role', 'apply_for_role.token');
+}
+
 /**
  * Implementation of hook_help().
  */
 function apply_for_role_help($path, $arg) {
   switch ($path) {
+    case 'admin/build/trigger/apply_for_role':
+      $explanation = '<p>'. t('Triggers are system events, such as when new content is added or when a user logs in. Trigger module combines these triggers with actions (functional tasks), such as unpublishing content or e-mailing an administrator. The <a href="@url">Actions settings page</a> contains a list of existing actions and provides the ability to create and configure additional actions.', array('@url' => url('admin/settings/actions'))) .'</p>';
+      return $explanation .'<p>'. t('Below you can assign actions to run when certain role application related triggers happen. For example, you could email a user when their application is approved.') .'</p>';
+
     case 'admin/help#apply_for_role':
       return $explanation = '<p>'. t('The <em>Apply for roles</em> module allows users to apply for roles from their user page and allows administrators to easily view, approve and delete role applications.', array('@url' => url('admin/settings/apply_for_role'))) .'</p>';
   }
@@ -24,10 +36,15 @@ function apply_for_role_perm() {
 }
 
 /**
- * Implementation of hook_access().
+ * Check that the user is allowed to create applications and that there are
+ * roles that they don't yet belong to.
  */
-function apply_for_role_access($account) {
-  return $account && $account->uid && (($GLOBALS['user']->uid == $account->uid) && user_access('apply for roles'));
+function apply_for_role_apply_access($account) {
+  return !empty($account->uid)
+    && $GLOBALS['user']->uid == $account->uid
+    && user_access('apply for roles', $account)
+    && count(apply_for_role_available_roles($account)
+  );
 }
 
 /**
@@ -40,13 +57,16 @@ function apply_for_role_menu() {
     'page callback' => 'drupal_get_form',
     'page arguments' => array('apply_for_role_settings_form'),
     'access arguments' => array('administer apply for role'),
+    'file' => 'apply_for_role.admin.inc',
   );
 
   $items['admin/user/apply_for_role'] = array(
     'title' => t('Manage role applications'),
     'description' => t('View, approve and delete role applications.'),
-    'page callback' => 'apply_for_role_approve',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('apply_for_role_admin_form'),
     'access arguments' => array('approve role applications'),
+    'file' => 'apply_for_role.admin.inc',
   );
 
   $items['admin/user/apply_for_role/approve/%user/%'] = array(
@@ -55,6 +75,16 @@ function apply_for_role_menu() {
     'page arguments' => array('apply_for_role_approve_form', 4, 5),
     'access arguments' => array('approve role applications'),
     'type' => MENU_CALLBACK,
+    'file' => 'apply_for_role.admin.inc',
+  );
+
+  $items['admin/user/apply_for_role/deny/%user/%'] = array(
+    'title' => t('Deny role application'),
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('apply_for_role_deny_form', 4, 5),
+    'access arguments' => array('approve role applications'),
+    'type' => MENU_CALLBACK,
+    'file' => 'apply_for_role.admin.inc',
   );
 
   $items['admin/user/apply_for_role/remove/%user/%'] = array(
@@ -63,13 +93,14 @@ function apply_for_role_menu() {
     'page arguments' => array('apply_for_role_remove_form', 4, 5),
     'access arguments' => array('approve role applications'),
     'type' => MENU_CALLBACK,
+    'file' => 'apply_for_role.admin.inc',
   );
 
   $items['user/%user/apply_for_role'] = array(
     'title' => t('Apply for role'),
     'page callback' => 'drupal_get_form',
     'page arguments' => array('apply_for_role_apply_form', 1),
-    'access callback' => 'apply_for_role_access',
+    'access callback' => 'apply_for_role_apply_access',
     'access arguments' => array(1),
     'type' => MENU_LOCAL_TASK,
   );
@@ -77,201 +108,29 @@ function apply_for_role_menu() {
   return $items;
 }
 
-/**
- * Administration
- */
-
-function apply_for_role_settings_form() {
-  $selected_roles = variable_get('users_apply_roles', array());
-  foreach ((array)$selected_roles as $rid => $value) {
-    if ($rid > 2) {
-      $selected_rids[] = $rid;
-    }
-  }
-  $form['options'] = array(
-    '#type' => 'fieldset',
-    '#title' => t('Apply for role options'),
-  );
-  $form['options']['multiple'] = array(
-    '#type' => 'radios',
-    '#title' => t('Allow multiple roles per application'),
-    '#options' => array(t('No'), t('Yes')),
-    '#default_value' => variable_get('apply_for_role_multiple', 0),
-    '#description' => t("Chosing 'no' will limit users to applying for only one role per role application. Choosing 'yes' will allow users to apply for multiple roles per role application."),
-    '#required' => TRUE,
-  );
-  $form['options']['register'] = array(
-    '#type' => 'radios',
-    '#title' => t('Allow users to apply for roles on registration'),
-    '#options' => array(t('No'), t('Yes')),
-    '#default_value' => variable_get('apply_for_role_register', 0),
-    '#description' => t("Choosing 'yes' will allow users to apply for roles when creating a new account."),
-    '#required' => TRUE,
-  );
-  $roles = (user_roles(TRUE));
-  unset($roles[DRUPAL_AUTHENTICATED_RID]);
-  $form['roles'] = array(
-    '#type' => 'checkboxes',
-    '#title' => t('Roles'),
-    '#default_value' => isset($selected_rids) ? $selected_rids : array(2),
-    '#options' => $roles,
-    '#description' => t('Select the roles that users will be able to apply for.'),
-    '#required' => TRUE,
-  );
-  $form['submit'] = array(
-    '#type' => 'submit',
-    '#value' => t('Update'),
+function theme_apply_for_role_status($status) {
+  $statuses = array(
+   -1 => t('Removed'),
+    APPLY_FOR_ROLE_PENDING => t('Pending'),
+    APPLY_FOR_ROLE_APPROVED => t('Approved'),
+    APPLY_FOR_ROLE_DENIED => t('Denied'),
   );
-
-  return $form;
+  return isset($statuses[$status]) ? $statuses[$status] : t('Unknown status');
 }
 
-function apply_for_role_settings_form_submit($form, &$form_state) {
-  $roles = user_roles(TRUE);
-  foreach ($form_state['values']['roles'] as $key => $value) {
-    if ($value) {
-      $selected_roles[$value] = $roles[$value];
-    }
-  }
-
-  variable_set('users_apply_roles', $selected_roles);
-  variable_set('apply_for_role_multiple', $form['options']['multiple']['#value']);
-  variable_set('apply_for_role_register', $form['options']['register']['#value']);
-  drupal_set_message(t('Apply for role settings have been saved.'));
-
-  $form_state['redirect'] = 'admin/settings/apply_for_role';
-  return;
-}
-
-/**
- * User management
- */
-function apply_for_role_approve() {
-  $header = array(
-    array('data' => t('Username'), 'field' => 'u.name'),
-    array('data' => t('Current Roles')),
-    array('data' => t('Apply for role'), 'field' => 'rid'),
-    array('data' => t('Apply Date'), 'field' => 'apply_date', 'sort' => 'desc'),
-    array('data' => t('Approved'), 'field' => 'approved'),
-    array('data' => t('Approve Date'), 'field' => 'approve_date'),
-    array('data' => t('Delete')),
-  );
-
-  $result = db_query("SELECT * FROM {users_roles_apply} a LEFT JOIN {users} u ON u.uid = a.uid ". tablesort_sql($header));
-
-  $roles = user_roles(TRUE);
-  $rows = array();
-
-  while ($row = db_fetch_object($result)) {
-    $user = user_load(array('uid' => $row->uid));
-    foreach ($user->roles as $rid => $role) {
-      $user_roles .= $role .'<br />';
-    }
-
-    if ($row->approved == 0) {
-      $active = '<div>'. l(t('Approve'), 'admin/user/apply_for_role/approve/'. $row->uid .'/'. $row->rid, array('title' => t('Approve this user'))) .'</div>'."\n";
-    }
-    else {
-      $active = '<div>'. t('Approved') .'</div>'."\n";
-    }
-
-    $delete = '<div>'. l(t('Delete'), 'admin/user/apply_for_role/remove/'. $row->uid .'/'. $row->rid, array('title' => t('Remove application'))) .'</div>'."\n";
-
-    $rows[] = array(
-      array('data' => '<div>'. l($row->name, 'user/'. $row->uid) .'</div>'."\n", 'class' => 'title'),
-      array('data' => '<div class="name">'. $user_roles .'</div>'."\n"),
-      array('data' => $roles[$row->rid]),
-      array('data' => format_date($row->apply_date)),
-      array('data' => $active, 'class' => 'icon'),
-      array('data' => (!empty($row->approve_date) ? format_date($row->approve_date) : '')),
-      array('data' => $delete, 'class' => 'icon'),
-    );
-
-    unset($user_roles);
-  }
-
-  if (count($rows) == 0) {
-    $rows[] = array(array('data' => '<em>'. t('There are currently no applications.') .'</em>', 'colspan' => 7));
-  }
-
-  $output .= theme('table', $header, $rows);
-
-  return $output;
-}
-
-function apply_for_role_approve_form(&$form_state, $user, $rid) {
-  $form['uid'] = array(
-    '#type' => 'value',
-    '#value' => $user->uid,
-  );
-  $form['rid'] = array(
-    '#type' => 'value',
-    '#value' => $rid,
-  );
-
-  $roles = user_roles(TRUE);
-  return confirm_form($form, t('Do you want to approve the application from user %username for role %role', array('%username' => $user->name, '%role' => $roles[$rid])), 'admin/user/apply_for_role', t('The role will be automatically assigned to the user'), t('Approve'));
-}
-
-function apply_for_role_approve_form_submit($form, &$form_state) {
-  if (apply_for_role_approve_apply($form_state['values']['uid'], $form_state['values']['rid'])) {
-    drupal_set_message(t('The application was approved.'));
-  }
-  else {
-    drupal_set_message(t('Error approving application. Please try again!'), 'error');
-  }
-  cache_clear_all();
-  $form_state['redirect'] = 'admin/user/apply_for_role';
-}
-
-function apply_for_role_remove_form(&$form_state, $user, $rid) {
-  $form['uid'] = array(
-    '#type' => 'value',
-    '#value' => $user->uid,
-  );
-  $form['rid'] = array(
-    '#type' => 'value',
-    '#value' => $rid,
-  );
-
-  $roles = user_roles(TRUE);
-  return confirm_form($form, t('Do you want to remove the application from user %username for role %role', array('%username' => $user->name, '%role' => isset($roles[$rid]) ? $roles[$rid] : t('Invalid role'))), 'admin/user/apply_for_role', t('The role will be automatically deleted from the user'), t('Delete'));
-}
-
-function apply_for_role_remove_form_submit($form, &$form_state) {
-  if (apply_for_role_remove_apply($form_state['values']['uid'], $form_state['values']['rid'])) {
-    drupal_set_message(t('The application was deleted.'));
-  }
-  else {
-    drupal_set_message(t('Error deleting application. Please try again!'), 'error');
-  }
-  cache_clear_all();
-  $form_state['redirect'] = 'admin/user/apply_for_role';
-}
 
 /**
  * User interface
  */
 
-function apply_for_role_apply_form(&$form_state) {
-  global $user;
-
-  $roles = user_roles(TRUE);
-
-  // Figure out all the roles they've applied for.
-  $applied = array();
-  $result = db_query("SELECT rid FROM {users_roles_apply} WHERE uid = %d", $user->uid, $rid);
-  while ($row = db_fetch_object($result)) {
-    $applied[$row->rid] = isset($roles[$row->rid]) ? $roles[$row->rid] : t('Invalid role');
-  }
-  
-  // Figure out which roles are enabled, figure out which roles they have or
-  // have applied for, then take the remainder.
-  $enabled = variable_get('users_apply_roles', array());
-  $used = $user->roles + $applied;
-  $filter_roles = array_diff($enabled, $used);
+function apply_for_role_apply_form(&$form_state, $user) {
+  $filter_roles = apply_for_role_available_roles($user);
 
   if (count($filter_roles)) {
+    $form['user'] = array(
+      '#type' => 'value',
+      '#value' => $user,
+    );
     $form['rid'] = array(
       '#type' => variable_get('apply_for_role_multiple', 0) ? 'checkboxes' : 'radios',
       '#title' => variable_get('apply_for_role_multiple', 0) ? t('Select the roles you want to apply for') : t('Select the role you want to apply for'),
@@ -280,68 +139,36 @@ function apply_for_role_apply_form(&$for
     $form['submit'] = array(
       '#type' => 'submit',
       '#value' => t('Apply')
-    );    
+    );
   }
   else {
-    drupal_set_message(t('No roles are available at this time.'), 'notice');
+    drupal_set_message(t('No roles are available at this time.'));
   }
 
   return $form;
 }
 
 function apply_for_role_apply_form_submit($form, &$form_state) {
-  global $user;
-  $received = array();
-  $not_received = array();
-  if (is_array($form_state['values']['rid'])) {
-    foreach ($form_state['values']['rid'] as $rid => $value) {
-      if (!empty($value)) {
-        if (apply_for_role_add_apply($user->uid, $value)) {
-          $received[] = db_result(db_query('SELECT name FROM {role} WHERE rid = %d', $value));
-        }
-        else {
-          $not_received[] = db_result(db_query('SELECT name FROM {role} WHERE rid = %d', $value));
-        }
-      }
-    }
-  }
-  else {
-    if (apply_for_role_add_apply($user->uid, $form_state['values']['rid'])) {
-       $received[] = db_result(db_query('SELECT name FROM {role} WHERE rid = %d', $form_state['values']['rid']));
-    }
-    else {
-       $not_received[] = db_result(db_query('SELECT name FROM {role} WHERE rid = %d', $form_state['values']['rid']));
-    }
-  }
-
-  $count_received = count($received);
-  $count_not_received = count($not_received);
-  if (!empty($count_received)) {
-    drupal_set_message(format_plural($count_received, 'Your application was received for the following role: %roles', 'Your applications were received for the following roles: %roles', array('%roles' => implode(', ', $received))));
-  }
-  if (!empty($count_not_received)) {
-    drupal_set_message(format_plural($count_received, 'There was a problem processing your application for the following role: %roles', 'There was a problem processing your application for the following role: %roles', array('%roles' => implode(', ', $not_received))));
-  }
+  apply_for_role_process_apps($form_state['values']['user'], $form_state['values']['rid']);
   $form_state['redirect'] = 'user/'. $user->uid;
-  return;
 }
 
+
 /**
  * Implementation of hook_theme()
  */
 function apply_for_role_theme() {
   return array(
-    'apply_for_role_apply_form' => array(
-      'arguments' => array('form' => NULL)
+    'apply_for_role_admin_form' => array(
+      'arguments' => array('form' => NULL),
+      'file' => 'apply_for_role.admin.inc',
+    ),
+    'apply_for_role_status' => array(
+      'arguments' => array('status' => NULL),
     )
   );
 }
 
-function theme_apply_for_role_apply_form($form) {
-  $output = drupal_render($form);
-  return $output;
-}
-
 /**
  * Implementation of hook_user().
  */
@@ -353,18 +180,18 @@ function apply_for_role_user($op, &$edit
         break;
       }
       if (variable_get('apply_for_role_register', 0)) {
-        $roles = variable_get('users_apply_roles', array());
-        foreach ($roles as $rid => $role) {
+        $filter_roles = array();
+        foreach (variable_get('users_apply_roles', array()) as $rid => $role) {
           if ($rid > 2) {
             $filter_roles[$rid] = $role;
           }
         }
-        $form['apply_for_role'] = array(
-          '#type' => 'fieldset',
-          '#title' => t('Apply for roles'),
-          '#collapsible' => FALSE,
-        );
-        if (is_array($filter_roles)) {
+        if (count($filter_roles)) {
+          $form['apply_for_role'] = array(
+            '#type' => 'fieldset',
+            '#title' => t('Apply for roles'),
+            '#collapsible' => FALSE,
+          );
           if (variable_get('apply_for_role_multiple', 0)) {
             $form['apply_for_role']['rid'] = array(
               '#type' => 'checkboxes',
@@ -373,52 +200,21 @@ function apply_for_role_user($op, &$edit
             );
           }
           else {
-            $filter_roles[0] = t('');
-            ksort($filter_roles);
+            array_unshift($filter_roles, '');
             $form['apply_for_role']['rid'] = array(
               '#type' => 'select',
               '#title' => t('Select the role you want to apply for'),
-              '#default_value' => '',
               '#options' => $filter_roles,
             );
           }
+          return $form;
         }
-        return $form;
       }
       break;
 
     case 'insert':
       if (variable_get('apply_for_role_register', 0)) {
-        if (is_array($edit['rid'])) {
-          foreach ($edit['rid'] as $rid => $value) {
-           if (!empty($value)) {
-              if (apply_for_role_add_apply($user->uid, $value)) {
-                $received[] = db_result(db_query('SELECT name FROM {role} WHERE rid = %d', $value));
-              }
-              else {
-                $not_received[] = db_result(db_query('SELECT name FROM {role} WHERE rid = %d', $value));
-              }
-            }
-          }
-        }
-        else {
-          if (!empty($edit['rid'])) {
-            if (apply_for_role_add_apply($user->uid, $edit['rid'])) {
-               $received[] = db_result(db_query('SELECT name FROM {role} WHERE rid = %d', $edit['rid']));
-            }
-            else {
-               $not_received[] = db_result(db_query('SELECT name FROM {role} WHERE rid = %d', $edit['rid']));
-            }
-          }
-        }
-        $count_received = count($received);
-        $count_not_received = count($not_received);
-        if (!empty($count_received)) {
-          drupal_set_message(format_plural($count_received, 'Your application was received for the following role: %roles', 'Your applications were received for the following roles: %roles', array('%roles' => implode(', ', $received))));
-        }
-        if (!empty($count_not_received)) {
-          drupal_set_message(format_plural($count_received, 'There was a problem processing your application for the following role: %roles', 'There was a problem processing your application for the following role: %roles', array('%roles' => implode(', ', $not_received))));
-        }
+        apply_for_role_process_apps($user, $edit['rid']);
       }
       break;
 
@@ -429,50 +225,231 @@ function apply_for_role_user($op, &$edit
 }
 
 /**
- * Callbacks
+ * Implementation of hook_hook_info().
+ *
+ * Let the Drupal know about our hook_apply_for_role($op, $apply) hook so that
+ * it can be used triggers for actions.
+ *
+ * We provide information on these hooks  This allows admins to do things
+ * like send an email after the user applys for a role.
  */
-function apply_for_role_check_role_exist($uid, $rid) {
-  if ($uid) {
-    $user = user_load(array('uid' => $uid));
-    if ($user->uid) {
-      if ($user->roles[$rid]) {
-      return TRUE;
+function apply_for_role_hook_info() {
+  $info = array(
+    'apply_for_role' => array(
+      'apply_for_role' => array(
+        'apply' => array(
+          'runs when' => t('When a user submits an application for a role'),
+        ),
+        'approve' => array(
+          'runs when' => t("When an admin approves a user's application for a role"),
+        ),
+        'deny' => array(
+          'runs when' => t("When an admin denies a user's application for a role"),
+        ),
+        'remove' => array(
+          'runs when' => t("When an admin deletes a user's application for a role"),
+        ),
+      ),
+    ),
+  );
+  return $info;
+}
+
+/**
+* Implementation of hook_action_info_alter().
+*
+* None of the built-in actions will be enabled for our hook by default. We
+* need to implement hook_action_info_alter() so that we can enable a couple.
+*/
+function apply_for_role_action_info_alter(&$info) {
+  $actions = array(
+    'system_message_action',
+    'system_send_email_action',
+    'token_actions_message_action',
+    'token_actions_send_email_action',
+    'token_actions_goto_action',
+  );
+  $ops = array('apply', 'approve', 'deny', 'remove');
+  foreach ($actions as $action) {
+    if (isset($info[$action])) {
+      $info[$action]['hooks']['apply_for_role'] = $ops;
+    }
+  }
+}
+
+/**
+ * Implementation of hook_apply_for_role().
+ *
+ * We implement our own event to fire triggers.
+ *
+ * @param $op The operation that just occured: 'apply', 'approve', 'deny', 'remove'.
+ * @param $apply A role application object.
+ */
+function apply_for_role_apply_for_role($op, $apply) {
+  // Keep objects for reuse so that changes actions make to objects can persist.
+  static $objects;
+
+  if (!module_exists('trigger')) {
+    return;
+  }
+
+  $user = user_load(array('uid' => $apply->uid));
+  $apply->user = $user;
+
+  $aids = _trigger_get_hook_aids('apply_for_role', $op);
+  $context = array(
+    'hook' => 'apply_for_role',
+    'op' => $op,
+    'user' => $user,
+    'apply_for_role' => $apply,
+  );
+  foreach ($aids as $aid => $action_info) {
+    if ($action_info['type'] != 'user') {
+      if (!isset($objects[$action_info['type']])) {
+        $objects[$action_info['type']] = _trigger_normalize_user_context($action_info['type'], $user);
       }
+      $context['user'] = $user;
+      $context['apply'] = $apply;
+      actions_do($aid, $objects[$action_info['type']], $context);
+    }
+    else {
+      actions_do($aid, $user, $context);
     }
   }
+}
+
+
+/**
+ * Callbacks
+ */
+
+/**
+ * Check if a user has a given role.
+ *
+ * @param $uid User id
+ * @param $rid Role id
+ * @return Boolean
+ */
+function apply_for_role_user_has_role($uid, $rid) {
+  if (!empty($uid) && !empty($rid)) {
+    $user = user_load(array('uid' => $uid));
+    return (!empty($user->uid) && isset($user->roles[$rid]));
+  }
   return FALSE;
 }
 
-function apply_for_role_add_apply($uid, $rid) {
-  if (!apply_for_role_check_role_exist($uid, $rid)) {
+/**
+ * Return an array of the roles that are available for application by a user.
+ *
+ * @param $user User object
+ * @return array keyed by role id with the role names as values.
+ */
+function apply_for_role_available_roles($user) {
+  // Get the complete list of roles (other than anonyous)...
+  $roles = user_roles(TRUE);
+  // ...the roles that can be applied for...
+  $enabled = (array) variable_get('users_apply_roles', array());
+
+  // ...the roles the user has already applied for...
+  $applied = array();
+  $result = db_query("SELECT rid FROM {users_roles_apply} WHERE uid = %d", $user->uid);
+  while ($row = db_fetch_object($result)) {
+    $applied[$row->rid] = isset($roles[$row->rid]) ? $roles[$row->rid] : t('Invalid role');
+  }
+
+  // ... now figure out what's left from the enabled roles list once you remove
+  // those they have and those they've applied for.
+  $used = $user->roles + $applied;
+  return array_diff($enabled, $used);
+}
+
+/**
+ * Store a role application in the database.
+ *
+ * @param $user User object
+ * @param $rid Role id
+ * @return Boolean indicating success
+ */
+function apply_for_role_add_apply($user, $rid) {
+  $uid = $user->uid;
+  if (!apply_for_role_user_has_role($uid, $rid)) {
     // Check if the user has already applied for this role
-    if (db_result(db_query("SELECT uid, rid FROM {users_roles_apply} WHERE uid = %d AND rid = %d", $uid, $rid)) == 0) {
-      db_query("INSERT INTO {users_roles_apply} (uid, rid, approved, apply_date) VALUES (%d, %d, 0, %d)", $uid, $rid, time());
+    if (!db_result(db_query("SELECT COUNT(*) FROM {users_roles_apply} WHERE uid = %d AND rid = %d", $uid, $rid))) {
+      $apply = (object) array('uid' => $uid, 'rid' => $rid, 'approved' => 0, 'apply_date' => time());
+      drupal_write_record('users_roles_apply', $apply);
+
+      module_invoke_all('apply_for_role', 'apply', $apply);
+
       return TRUE;
     }
   }
+  return FALSE;
 }
 
-function apply_for_role_approve_apply($uid, $rid) {
-  if (!apply_for_role_check_role_exist($uid, $rid)) {
-    $result = db_query("SELECT uid, rid, approved FROM {users_roles_apply} WHERE uid = %d AND rid = %d", $uid, $rid);
-    if ($row = db_fetch_object($result)) {
-      if ($row->approved == 0) {
-        apply_for_role_add_role($uid, $rid);
-          db_query("UPDATE {users_roles_apply} SET approved = 1, approve_date = %d WHERE uid = %d AND rid = %d", time(), $uid, $rid);
-          return TRUE;
-      }
-    }
+/**
+ * Approve a role application and put the user into the role.
+ *
+ * @param $user User object
+ * @param $rid Role id
+ * @return Boolean indicating success
+ */
+function apply_for_role_approve_apply($user, $rid) {
+  $uid = $user->uid;
+  if ($apply = db_fetch_object(db_query("SELECT * FROM {users_roles_apply} WHERE uid = %d AND rid = %d AND approved <> %d", $uid, $rid, APPLY_FOR_ROLE_APPROVED))) {
+    apply_for_role_add_role($uid, $rid);
+    $apply->approve_date = time();
+    $apply->approved = APPLY_FOR_ROLE_APPROVED;
+    drupal_write_record('users_roles_apply', $apply, array('uid', 'rid'));
+
+    module_invoke_all('apply_for_role', 'approve', $apply);
+
+    return TRUE;
   }
+  return FALSE;
 }
 
-function apply_for_role_remove_apply($uid, $rid) {
-  $result = db_query("SELECT uid, rid, approved FROM {users_roles_apply} WHERE uid = %d AND rid = %d", $uid, $rid);
-  if (!empty($result)) {
+/**
+ * Deny a role application.
+ *
+ * @param $user User object
+ * @param $rid Role id
+ * @return Boolean indicating success
+ */
+function apply_for_role_deny_apply($user, $rid) {
+  $uid = $user->uid;
+  if ($apply = db_fetch_object(db_query("SELECT * FROM {users_roles_apply} WHERE uid = %d AND rid = %d AND approved <> %d", $uid, $rid, APPLY_FOR_ROLE_DENIED))) {
+    apply_for_role_delete_role($uid, $rid);
+    $apply->approve_date = time();
+    $apply->approved = APPLY_FOR_ROLE_DENIED;
+    drupal_write_record('users_roles_apply', $apply, array('uid', 'rid'));
+
+    module_invoke_all('apply_for_role', 'deny', $apply);
+
+    return TRUE;
+  }
+  return FALSE;
+}
+
+/**
+ * Deletea role application from the database.
+ *
+ * @param $user User object
+ * @param $rid Role id
+ * @return Boolean indicating success
+ */
+
+function apply_for_role_remove_apply($user, $rid) {
+  $uid = $user->uid;
+  if ($apply = db_fetch_object(db_query("SELECT * FROM {users_roles_apply} WHERE uid = %d AND rid = %d", $uid, $rid))) {
     apply_for_role_delete_role($uid, $rid);
     db_query("DELETE FROM {users_roles_apply} WHERE uid = %d AND rid = %d", $uid, $rid);
+
+    $apply->approval = -1;
+    module_invoke_all('apply_for_role', 'remove', $apply);
+
     return TRUE;
   }
+  return FALSE;
 }
 
 function apply_for_role_add_role($uid, $rid) {
@@ -486,3 +463,42 @@ function apply_for_role_delete_role($uid
     db_query('DELETE FROM {users_roles} WHERE uid = %d AND rid = %d', $uid, $rid);
   }
 }
+
+/**
+ * Process an application and store it for admin review.
+ *
+ * @param $user User object.
+ * @param $apps Mixed, either a role id or an array keyed by role id.
+ */
+function apply_for_role_process_apps($user, $apps) {
+  $roles = user_roles(TRUE);
+
+  // They can hand in either an array keyed by role id or single role id.
+  // Ensure we've got an array keyed by role id with the name as the value.
+  if (is_array($apps)) {
+    // Filter out any thing with empty role names. And use the official role
+    // name.
+    $apps = array_intersect_key($roles, array_filter($apps));
+  }
+  else {
+    $apps = array($apps => $roles[$apps]);
+  }
+
+  $received = array();
+  $not_received = array();
+  foreach ($apps as $rid => $role) {
+    if (apply_for_role_add_apply($user, $rid)) {
+      $received[] = $role;
+    }
+    else {
+      $not_received[] = $role;
+    }
+  }
+
+  if (!empty($received)) {
+    drupal_set_message(t('%message %roles', array('%message' => format_plural(count($received), t('Your application was received for the following role:'), t('Your applications were received for the following roles:')), '%roles' => implode(', ', $received))));
+  }
+  if (!empty($not_received)) {
+    drupal_set_message(t('%message %roles', array('%message' => format_plural(count($not_received), t('There was a problem processing your application for the following role:'), t('There was a problem processing your applications for the following roles:')), '%roles' => implode(', ', $not_received))), 'error');
+  }
+}
Index: apply_for_role.token.inc
===================================================================
RCS file: apply_for_role.token.inc
diff -N apply_for_role.token.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ apply_for_role.token.inc	8 Aug 2008 01:08:09 -0000
@@ -0,0 +1,74 @@
+<?php
+// $Id: $
+
+/**
+ * Implementation of hook_token_values().
+ */
+function apply_for_role_token_values($type, $object = NULL, $options = array()) {
+  $values = array();
+  switch ($type) {
+    case 'apply_for_role':
+      // Cast to an object just in case fussy Drupal gave us an array
+      $apply = (object)$object;
+
+      $roles = user_roles();
+      $user = isset($apply->user) ? $apply->user : user_load(array('uid' => $apply->uid));
+
+      $values['apply_for_role-uid']         = $apply->uid;
+      $values['apply_for_role-name']        = check_plain($user->name);
+      $values['apply_for_role-name-raw']    = $user->name;
+      $values['apply_for_role-mail']        = check_plain($user->mail);
+      $values['apply_for_role-mail-raw']    = $user->mail;
+
+      $values['apply_for_role-rid']         = $apply->rid;
+      $values['apply_for_role-role']        = check_plain($roles[$apply->rid]);
+      $values['apply_for_role-status']      = check_plain(theme('apply_for_role_status', $apply->approved));
+
+      $values['apply_for_role-apply_date-yyyy']   = date('Y', $apply->apply_date);
+      $values['apply_for_role-apply_date-yy']     = date('y', $apply->apply_date);
+      $values['apply_for_role-apply_date-month']  = date('F', $apply->apply_date);
+      $values['apply_for_role-apply_date-mon']    = date('M', $apply->apply_date);
+      $values['apply_for_role-apply_date-mm']     = date('m', $apply->apply_date);
+      $values['apply_for_role-apply_date-m']      = date('n', $apply->apply_date);
+      $values['apply_for_role-apply_date-ww']     = date('W', $apply->apply_date);
+      $values['apply_for_role-apply_date-date']   = date('N', $apply->apply_date);
+      $values['apply_for_role-apply_date-day']    = date('l', $apply->apply_date);
+      $values['apply_for_role-apply_date-ddd']    = date('D', $apply->apply_date);
+      $values['apply_for_role-apply_date-dd']     = date('d', $apply->apply_date);
+      $values['apply_for_role-apply_date-d']      = date('j', $apply->apply_date);
+      break;
+  }
+
+  return $values;
+}
+
+/**
+ * Implementation of hook_token_list().
+ */
+function apply_for_role_token_list($type = 'all') {
+  if ($type == 'apply_for_role' || $type == 'all') {
+    $tokens['apply_for_role']['apply_for_role-uid']       = t("Applicant's user ID");
+    $tokens['apply_for_role']['apply_for_role-name']      = t("Applicant's user name");
+    $tokens['apply_for_role']['apply_for_role-name-raw']  = t("Applicant's user name. WARNING - raw user input.");
+    $tokens['apply_for_role']['apply_for_role-mail']      = t("Applicant's e-mail.");
+    $tokens['apply_for_role']['apply_for_role-mail-raw']  = t("Applicant's e-mail. WARNING - raw user input.");
+
+    $tokens['apply_for_role']['apply_for_role-rid']       = t('Application role ID');
+    $tokens['apply_for_role']['apply_for_role-role']      = t('Application role name');
+    $tokens['apply_for_role']['apply_for_role-status']    = t('Application status');
+
+    $tokens['apply_for_role']['apply_for_role-apply_date-yyyy']  = t("Application creation year (four digit)");
+    $tokens['apply_for_role']['apply_for_role-apply_date-yy']    = t("Application creation year (two digit)");
+    $tokens['apply_for_role']['apply_for_role-apply_date-month'] = t("Application creation month (full word)");
+    $tokens['apply_for_role']['apply_for_role-apply_date-mon']   = t("Application creation month (abbreviated)");
+    $tokens['apply_for_role']['apply_for_role-apply_date-mm']    = t("Application creation month (two digit, zero padded)");
+    $tokens['apply_for_role']['apply_for_role-apply_date-m']     = t("Application creation month (one or two digit)");
+    $tokens['apply_for_role']['apply_for_role-apply_date-ww']    = t("Application creation week (two digit)");
+    $tokens['apply_for_role']['apply_for_role-apply_date-date']  = t("Application creation date (day of month)");
+    $tokens['apply_for_role']['apply_for_role-apply_date-day']   = t("Application creation day (full word)");
+    $tokens['apply_for_role']['apply_for_role-apply_date-ddd']   = t("Application creation day (abbreviation)");
+    $tokens['apply_for_role']['apply_for_role-apply_date-dd']    = t("Application creation day (two digit, zero-padded)");
+    $tokens['apply_for_role']['apply_for_role-apply_date-d']     = t("Application creation day (one or two digit)");
+    return $tokens;
+  }
+}
