You are here

apply_for_role.module in Apply for role 7

Allows users to apply for roles.

File

apply_for_role.module
View source
<?php

/**
 * @file
 * Allows users to apply for roles.
 */

/**
 * The role application is removed.
 */
define('APPLY_FOR_ROLE_REMOVED', -1);

/**
 * The role application is pending.
 */
define('APPLY_FOR_ROLE_PENDING', 0);

/**
 * The role application is approved.
 */
define('APPLY_FOR_ROLE_APPROVED', 1);

/**
 * The role application is denied.
 */
define('APPLY_FOR_ROLE_DENIED', 2);

/**
 * Includes.
 */
if (module_exists('token')) {
  module_load_include('inc', 'apply_for_role', 'apply_for_role.token');
}

/**
 * Implements hook_views_api().
 */
function apply_for_role_views_api() {
  return array(
    'api' => 3,
  );
}

/**
 * Implements hook_help().
 */
function apply_for_role_help($path, $arg) {
  switch ($path) {
    case 'admin/help#apply_for_role':
      $output = '<h3>' . t('About') . '</h3>';
      $output .= '<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/config/people/apply_for_role'),
      )) . '</p>';
      $output .= '<h3>' . t('Uses') . '</h3>';
      $output .= '<dl>';
      $output .= '</dl>';
      return $output;
  }
}

/**
 * Implements hook_menu().
 */
function apply_for_role_menu() {
  $items['admin/config/people/apply_for_role'] = array(
    'title' => 'Apply for role administration',
    'description' => 'Administer which roles users can apply for.',
    '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/people/apply_for_role'] = array(
    'title' => 'Role applications',
    'description' => 'View, approve, deny and delete role applications.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'apply_for_role_admin_form',
    ),
    'access arguments' => array(
      'view role applications',
    ),
    'file' => 'apply_for_role.admin.inc',
    'type' => MENU_LOCAL_TASK,
    'weight' => 10,
  );
  $items['admin/people/apply_for_role/approve/%user/%'] = array(
    'title' => 'Approve role application',
    'page callback' => 'drupal_get_form',
    '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/people/apply_for_role/deny/%user/%'] = array(
    'title' => 'Deny role application',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'apply_for_role_deny_form',
      4,
      5,
    ),
    'access arguments' => array(
      'deny role applications',
    ),
    'type' => MENU_CALLBACK,
    'file' => 'apply_for_role.admin.inc',
  );
  $items['admin/people/apply_for_role/remove/%user/%'] = array(
    'title' => 'Remove role application',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'apply_for_role_remove_form',
      4,
      5,
    ),
    'access arguments' => array(
      'delete role applications',
    ),
    'type' => MENU_CALLBACK,
    'file' => 'apply_for_role.admin.inc',
  );
  $items['user/%user/apply_for_role'] = array(
    'title' => 'Apply for role',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'apply_for_role_apply_form',
      1,
    ),
    'access callback' => '_apply_for_role_apply_access',
    'access arguments' => array(
      1,
    ),
    'type' => MENU_LOCAL_TASK,
  );
  return $items;
}

/**
 * Implements hook_menu_alter().
 */
function apply_for_role_menu_alter(&$callbacks) {
  if (module_exists('trigger') & isset($callbacks['admin/build/trigger/apply_for_role'])) {
    $callbacks['admin/config/trigger/apply_for_role']['access callback'] = 'trigger_access_check';
  }
}

/**
 * Implements hook_permission().
 */
function apply_for_role_permission() {
  return array(
    'administer apply for role' => array(
      'title' => t('Administer Apply for Role'),
      'description' => t(''),
    ),
    'view role applications' => array(
      'title' => t('View role applications'),
      'description' => t(''),
    ),
    'approve role applications' => array(
      'title' => t('Approve role applications'),
      'description' => t(''),
    ),
    'deny role applications' => array(
      'title' => t('Deny role applications'),
      'description' => t(''),
    ),
    'delete role applications' => array(
      'title' => t('Delete role applications'),
      'description' => t(''),
    ),
    'apply for roles' => array(
      'title' => t('Apply for roles'),
      'description' => t(''),
    ),
  );
}

/**
 * Access callback for people/%user/apply_for_role.
 */
function _apply_for_role_apply_access($account) {

  // This path is only allowed for authenticated users looking at their own profile who have permission to apply for roles.
  return $account->uid && $GLOBALS['user']->uid == $account->uid && user_access('apply for roles', $account);
}

/**
 * Admin interface.
 */

/**
 * Implements hook_theme().
 */
function apply_for_role_theme() {
  return array(
    'apply_for_role_admin_form' => array(
      'render element' => 'element',
      'arguments' => array(
        'form' => NULL,
      ),
      'file' => 'apply_for_role.admin.inc',
    ),
    'apply_for_role_status' => array(
      'render element' => 'element',
      'arguments' => array(
        'status' => NULL,
      ),
    ),
    'views_view_table__apply_for_role_admin' => array(
      'arguments' => array(
        'view' => NULL,
      ),
      'template' => 'views-view-table--apply-for-role-admin--apply-for-role-admin',
      'base hook' => 'views_view_table',
      'path' => drupal_get_path('module', 'apply_for_role') . '/templates',
    ),
  );
}

/**
 * Returns a traslatable string for a constant.
 *
 * @param $status
 *   An associative array containing a name for each constant.
 *
 * @return
 *   A traslatable string for a constant.
 */
function theme_apply_for_role_status($variables) {
  $status = is_numeric($variables['status']) ? $variables['status'] : NULL;
  $statuses = array(
    APPLY_FOR_ROLE_REMOVED => t('Removed'),
    APPLY_FOR_ROLE_PENDING => t('Pending'),
    APPLY_FOR_ROLE_APPROVED => t('Approved'),
    APPLY_FOR_ROLE_DENIED => t('Denied'),
  );
  return isset($statuses[$status]) ? $statuses[$status] : t('Unknown status');
}

/**
 * User interface.
 */

/**
 * Callback for the apply for role form.
 */
function apply_for_role_apply_form($form, $form_state, $user) {
  drupal_set_title($user->name);

  // List of roles that were already approved.
  $approved = apply_for_role_approved_roles($user);
  if (variable_get('apply_for_role_display_approved', 0) && count($approved)) {
    $form['approved'] = array(
      '#markup' => theme('item_list', array(
        'title' => t('Approved roles'),
        'items' => $approved,
      )),
    );
  }
  $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' : 'select',
      '#title' => variable_get('apply_for_role_multiple', 0) ? t('Select a role or roles:') : t('Select a role:'),
      '#options' => $filter_roles,
    );
    if (variable_get('apply_for_role_allow_message', 0)) {
      $form['message'] = array(
        '#type' => 'textarea',
        '#title' => t('Application details'),
        '#description' => t('Enter an explanation for how you will use the role, if it is granted.'),
      );
    }
    $form['submit'] = array(
      '#type' => 'submit',
      '#value' => t('Apply'),
    );
  }
  else {
    drupal_set_message(t('No new roles are available to you at this time.'));
  }
  return $form;
}

/**
 * Submit callback for apply for role form.
 *
 * @param
 *   $form
 * @param
 *   &$form_state
 */
function apply_for_role_apply_form_submit($form, &$form_state) {
  global $user;
  if (isset($form_state['values']['message'])) {
    apply_for_role_process_applications($form_state['values']['user'], $form_state['values']['rid'], $form_state['values']['message']);
  }
  else {
    apply_for_role_process_applications($form_state['values']['user'], $form_state['values']['rid']);
  }
  $form_state['redirect'] = 'user/' . $user->uid . '/apply_for_role';
}

/**
 * Registration form.
 */

/**
 * Implements hook_form_alter().
 */
function apply_for_role_form_alter(&$form, &$form_state, $form_id) {
  if (!($form_id == 'user_register_form')) {
    return;
  }

  // Admin created account aren't processed by the module.
  if (user_access('administer users')) {
    return;
  }
  elseif (variable_get('apply_for_role_register', 0)) {
    $filter_roles = array();
    foreach (variable_get('users_apply_roles', array()) as $rid => $role) {
      if ($rid > 2) {
        $filter_roles[$rid] = $role;
      }
    }
    if (count($filter_roles)) {
      $form['apply_for_role'] = array(
        '#type' => 'fieldset',
        '#title' => t('Apply for role'),
        '#collapsible' => FALSE,
      );
      if (variable_get('apply_for_role_multiple', 0) == 0 && variable_get('apply_for_role_register', 0) == 1) {
        $filter_roles[0] = t('--');
        ksort($filter_roles);
      }
      $form['apply_for_role']['rid'] = array(
        '#type' => variable_get('apply_for_role_multiple', 0) ? 'checkboxes' : 'select',
        '#title' => variable_get('apply_for_role_multiple', 0) ? t('Select a role or roles') : t('Select a role'),
        '#options' => $filter_roles,
        '#required' => variable_get('apply_for_role_register', 0) == 2 ? TRUE : FALSE,
      );
    }
  }
}

/**
 * Implements hook_user_insert().
 */
function apply_for_role_user_insert(&$edit, $account, $category) {
  if (variable_get('apply_for_role_register', 0) && !empty($edit['rid'])) {
    apply_for_role_process_applications($account, $edit['rid']);
    $edit['rid'] = NULL;
  }
}

/**
 * Implements hook_user_delete().
 */
function apply_for_role_user_delete($account) {
  if (isset($account->uid) && $account->uid > 2) {
    db_query("DELETE FROM {users_roles_apply} WHERE uid = :uid", array(
      ':uid' => $account->uid,
    ));
  }
}

/**
 * Triggers and actions.
 */

/**
 * Implements hook_trigger_info().
 */
function apply_for_role_trigger_info() {
  return array(
    'apply_for_role' => array(
      'apply_for_role_apply' => array(
        'label' => t('When a user submits an application for a role'),
      ),
      'apply_for_role_approve' => array(
        'label' => t("When an admin approves a user's application for a role"),
      ),
      'apply_for_role_deny' => array(
        'label' => t("When an admin denies a user's application for a role"),
      ),
      'apply_for_role_remove' => array(
        'label' => t("When an admin deletes a user's application for a role"),
      ),
    ),
  );
}

/**
 * Implements hook_apply_for_role().
 *
 * We implement our own event to fire triggers.
 *
 * @param $action
 *   The hook that is being called: 'apply_for_role_apply',
 *   'apply_for_role_approve', 'apply_for_role_deny', 'apply_for_role_remove'.
 * @param $apply
 *   A role application object.
 */
function apply_for_role_apply_for_role($action, $apply) {

  // Keep objects for reuse so that changes actions make to objects can persist.
  static $objects;
  if (!module_exists('trigger')) {
    return;
  }
  $user = user_load($apply->uid);
  $apply->user = $user;
  $context = array(
    'user' => $user,
    'apply_for_role' => $apply,
    'op' => $action,
  );
  foreach (trigger_get_assigned_actions('apply_for_role_' . $action) 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_for_role'] = $apply;
      actions_do($aid, $objects[$action_info['type']], $context);
    }
    elseif (isset($aid)) {
      actions_do($aid, $user, $context);
    }
  }
}

/**
 * API.
 */

/**
 * Check if a user has a given role.
 *
 * @param $uid
 *   A user ID.
 * @param $rid
 *   A role ID.
 *
 * @return
 *   A boolean indicating success.
 */
function apply_for_role_user_has_role($uid, $rid) {
  if (!empty($uid) && !empty($rid)) {
    $user = user_load($uid);
    return !empty($user->uid) && isset($user->roles[$rid]);
  }
  return FALSE;
}

/**
 * Return an array of the roles that are available for application by a user.
 *
 * @param $user
 *   A user object.
 *
 * @return
 *   An 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());

  // Check that all of the enabled roles exist.
  // TODO: Catch these when they are deleted.
  $enabled = array_intersect($roles, $enabled);

  // ...the roles the user has already applied for...
  $applied = array();
  $result = db_query('SELECT rid FROM {users_roles_apply} WHERE uid = :uid', array(
    ':uid' => $user->uid,
  ));
  foreach ($result as $row) {
    $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);
}

/**
 * Stores a role application in the database.
 *
 * @param $user
 *   A user object.
 * @param $rid
 *   A role ID.
 * @param $message
 *   A string containing the applicant's reason for the role request.
 *
 * @return
 *   A boolean indicating success.
 */
function apply_for_role_add_apply($user, $rid, $message = NULL) {
  $uid = $user->uid;
  if (!apply_for_role_user_has_role($uid, $rid)) {

    // Check if the user has already applied for this role
    $has_rows = (bool) db_query_range('SELECT 1 FROM {users_roles_apply} WHERE uid = :uid AND rid = :rid', 0, 1, array(
      ':uid' => $uid,
      ':rid' => $rid,
    ))
      ->fetchField();
    if (!$has_rows) {
      $apply = (object) array(
        'uid' => $uid,
        'rid' => $rid,
        'approved' => 0,
        'apply_date' => REQUEST_TIME,
        'message' => $message,
      );
      drupal_write_record('users_roles_apply', $apply);
      module_invoke_all('apply_for_role', 'apply', $apply);
      return TRUE;
    }
  }
  return FALSE;
}

/**
 * Approve a role application and put the user into the role.
 *
 * @param $user
 *   A user object.
 * @param $rid
 *   A role id.
 *
 * @return
 *   A boolean indicating success.
 */
function apply_for_role_approve_apply($user, $rid) {
  $uid = $user->uid;
  if ($apply = db_query("SELECT * FROM {users_roles_apply} WHERE uid = :uid AND rid = :rid AND approved <> :approved", array(
    ':uid' => $uid,
    ':rid' => $rid,
    ':approved' => APPLY_FOR_ROLE_APPROVED,
  ))
    ->fetchObject()) {
    apply_for_role_add_role($uid, $rid);
    $apply->approve_date = REQUEST_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;
}

/**
 * Deny a role application.
 *
 * @param $user
 *   A user object.
 * @param $rid
 *   A role ID.
 *
 * @return
 *   A boolean indicating success.
 */
function apply_for_role_deny_apply($user, $rid) {
  $uid = $user->uid;
  if ($apply = db_query("SELECT * FROM {users_roles_apply} WHERE uid = :uid AND rid = :rid AND approved <> :approved", array(
    ':uid' => $uid,
    ':rid' => $rid,
    ':approved' => APPLY_FOR_ROLE_DENIED,
  ))
    ->fetchObject()) {
    apply_for_role_delete_role($uid, $rid);
    $apply->approve_date = REQUEST_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;
}

/**
 * Delete a role application from the database.
 *
 * @param $user
 *   A user object.
 * @param $rid
 *   A role ID.
 * @return
 *   A boolean indicating success.
 */
function apply_for_role_remove_apply($user, $rid) {
  $uid = $user->uid;
  if ($apply = db_query("SELECT * FROM {users_roles_apply} WHERE uid = :uid AND rid = :rid", array(
    ':uid' => $uid,
    ':rid' => $rid,
  ))
    ->fetchObject()) {
    apply_for_role_delete_role($uid, $rid);
    db_delete('users_roles_apply')
      ->condition('uid', $uid)
      ->condition('rid', $rid)
      ->execute();
    $apply->approved = APPLY_FOR_ROLE_REMOVED;
    module_invoke_all('apply_for_role', 'remove', $apply);
    return TRUE;
  }
  return FALSE;
}

/**
 * Assigns a role to a user.
 *
 * @param $user
 *   A user object.
 * @param $rid
 *   A role ID.
 *
 * @return
 *   A boolean indicating success.
 */
function apply_for_role_add_role($uid, $rid) {
  if (!in_array($rid, array(
    DRUPAL_ANONYMOUS_RID,
    DRUPAL_AUTHENTICATED_RID,
  ))) {
    $account = user_load($uid);
    $myroles = $account->roles;
    $rolenames = user_roles(TRUE);
    $myroles[$rid] = $rolenames[$rid];
    user_save($account, array(
      'roles' => $myroles,
    ));
  }
}

/**
 * Removes a role from a user.
 *
 * @param $user
 *   A user object.
 * @param $rid
 *   A role ID.
 *
 * @return
 *   A boolean indicating success.
 */
function apply_for_role_delete_role($uid, $rid) {
  if (!in_array($rid, array(
    DRUPAL_ANONYMOUS_RID,
    DRUPAL_AUTHENTICATED_RID,
  ))) {
    $account = user_load($uid);
    $myroles = $account->roles;
    unset($myroles[$rid]);
    user_save($account, array(
      'roles' => $myroles,
    ));
  }
}

/**
 * Process an application and store it for admin review.
 *
 * @param $user
 *   A user object.
 * @param $applications
 *   Mixed, either a role ID or an array keyed by role ID.
 * @param $message
 *   A string containing the applicant's reason for the role request.
 */
function apply_for_role_process_applications($user, $applications, $message = NULL) {
  $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($applications)) {

    // Filter out any thing with empty role names. And use the official role
    // name.
    $applications = array_intersect_key($roles, array_filter($applications));
  }
  else {
    $applications = array(
      $applications => $roles[$applications],
    );
  }
  $received = array();
  $not_received = array();
  foreach ($applications as $rid => $role) {
    if (apply_for_role_add_apply($user, $rid, $message)) {
      $received[] = $role;
    }
    else {
      $not_received[] = $role;
    }
  }
  if (!empty($received)) {
    drupal_set_message(format_plural(count($received), 'Your application was received for: %roles', 'Your applications were received for: %roles', array(
      '%roles' => implode(', ', $received),
    )));
  }
  if (!empty($not_received)) {
    drupal_set_message(format_plural(count($not_received), 'There was a problem processing your application for: %roles', 'There was a problem processing your applications for: %roles', array(
      '%roles' => implode(', ', $not_received),
    )), 'error');
  }
}

/**
 * Return an array of roles that were approved for this user.
 *
 * @param $user
 *   A user object.
 *
 * @return $approved
 *   An array of roles that have been approved for this user.
 */
function apply_for_role_approved_roles(&$user) {
  $approved = array();
  $roles = user_roles(TRUE);
  $result = db_query("SELECT rid FROM {users_roles_apply} WHERE uid = :uid AND approved = :approved", array(
    ':uid' => $user->uid,
    ':approved' => APPLY_FOR_ROLE_APPROVED,
  ))
    ->fetchAll();
  foreach ($result as $row) {
    if (isset($roles[$row->rid]) && isset($user->roles[$row->rid])) {
      $approved[$row->rid] = $roles[$row->rid];
    }
    else {
      continue;
    }
  }
  return $approved;
}

Functions

Namesort descending Description
apply_for_role_add_apply Stores a role application in the database.
apply_for_role_add_role Assigns a role to a user.
apply_for_role_apply_form Callback for the apply for role form.
apply_for_role_apply_form_submit Submit callback for apply for role form.
apply_for_role_apply_for_role Implements hook_apply_for_role().
apply_for_role_approved_roles Return an array of roles that were approved for this user.
apply_for_role_approve_apply Approve a role application and put the user into the role.
apply_for_role_available_roles Return an array of the roles that are available for application by a user.
apply_for_role_delete_role Removes a role from a user.
apply_for_role_deny_apply Deny a role application.
apply_for_role_form_alter Implements hook_form_alter().
apply_for_role_help Implements hook_help().
apply_for_role_menu Implements hook_menu().
apply_for_role_menu_alter Implements hook_menu_alter().
apply_for_role_permission Implements hook_permission().
apply_for_role_process_applications Process an application and store it for admin review.
apply_for_role_remove_apply Delete a role application from the database.
apply_for_role_theme Implements hook_theme().
apply_for_role_trigger_info Implements hook_trigger_info().
apply_for_role_user_delete Implements hook_user_delete().
apply_for_role_user_has_role Check if a user has a given role.
apply_for_role_user_insert Implements hook_user_insert().
apply_for_role_views_api Implements hook_views_api().
theme_apply_for_role_status Returns a traslatable string for a constant.
_apply_for_role_apply_access Access callback for people/%user/apply_for_role.

Constants

Namesort descending Description
APPLY_FOR_ROLE_APPROVED The role application is approved.
APPLY_FOR_ROLE_DENIED The role application is denied.
APPLY_FOR_ROLE_PENDING The role application is pending.
APPLY_FOR_ROLE_REMOVED The role application is removed.