<?php
// $Id: profile_role.module,v 1.5.2.3 2010/01/19 05:18:01 boombatower Exp $
/**
 * @file
 * Define profile categories by role.
 *
 * Copyright 2008 by Jimmy Berry ("boombatower", http://drupal.org/user/214218)
 */

/**
 * Implementation of hook_menu().
 */
function profile_role_menu() {
  $items = array();

  $items['admin/user/profile/manage'] = array(
    'title' => 'Manage',
    'description' => 'Manage profiles.',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'file path' => drupal_get_path('module', 'profile')
  );
  $items['admin/user/profile/role'] = array(
    'title' => 'Role',
    'description' => 'Define profile categories by role.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('profile_role_restrict_form'),
    'access arguments' => array('administer users'),
    'type' => MENU_LOCAL_TASK
  );

  return $items;
}

/**
 * Implementation of hook_menu_alter().
 */
function profile_role_menu_alter(&$callbacks) {
  $result = db_query('SELECT DISTINCT(category)
                      FROM {profile_fields}');

  while ($category = db_result($result)) {
    $key = 'user/%user_category/edit/' . $category;
    $callbacks[$key]['access callback'] = 'profile_role_access_category';
    $callbacks[$key]['access arguments'] = array(1, $category);
  }
}

/**
 * Access callback -- ensure that the user being editted is of a role that
 * uses the field category, if so then ensure that the editting user has
 * permission to edit the fields.
 *
 * @param object $user User being editted.
 * @param string $category Profile field category.
 * @return boolean Access.
 */
function profile_role_access_category($user, $category) {
  $category_roles = profile_role_get_roles();
  if (isset($category_roles[$category])) {
    foreach ($category_roles[$category] as $rid) {
      if (array_key_exists($rid, $user->roles)) {
        // User has fields, check if editting user has edit access.
        return user_edit_access($user);
      }
    }
  }
  return FALSE;
}

/**
 * Restrict the user registration form.
 */
function profile_role_form_user_register_alter(&$form, $form_state) {
  if ($form['#action'] == '/user/register') {
    // No way to determine what role user will be, so leave form alone.
    return;
  }

  $roles = isset($form_state['post']['roles']) ? $form_state['post']['roles'] : array();
  $category_roles = profile_role_get_roles();
  $result = db_query('SELECT DISTINCT(category)
                      FROM {profile_fields}');
  while ($category = db_result($result)) {
    if (isset($form[$category])) {
      // Category displayed, check to see if it should.
      $remove = TRUE;

      if (isset($category_roles[$category])) {
        foreach ($category_roles[$category] as $rid) {
          if (array_key_exists($rid, $roles)) {
            $remove = FALSE;
          }
        }
      }

      if ($remove) {
        unset($form[$category]);
      }
    }
  }

  // Submit the form so that any additional fields display.
  $form['account']['roles']['#attributes']['onchange'] = 'submit()';
}

/**
 * Define profile categories by role.
 */
function profile_role_restrict_form(&$form_state) {
  $form = array();
  $roles = profile_role_get_roles();
  $result = db_query('SELECT DISTINCT(category)
                      FROM {profile_fields}');

  $form['_categories'] = array(
    '#type' => 'value',
    '#value' => array()
  );
  $i = 0;
  while ($category = db_result($result)) {
    $form[$i] = array(
      '#type' => 'fieldset',
      '#title' => $category,
      '#tree' => TRUE
    );
    $form[$i]['roles'] = array(
      '#type' => 'select',
      '#title' => t('Roles'),
      '#description' => t('The roles that use this category.'),
      '#multiple' => TRUE,
      '#options' => user_roles(),
      '#default_value' => (isset($roles[$category]) ? $roles[$category] : array())
    );
    $form['_categories']['#value'][$i] = $category;
    $i++;
  }

  if (!$form['_categories']['#value']) {
    drupal_set_message(t('At least one category must exists. Please assign fields to categories.'), 'notice');
    drupal_goto('admin/user/profile');
  }

  $form['op'] = array(
    '#type' => 'submit',
    '#value' => t('Save configuration')
  );

  return $form;
}

/**
 * Save category roles.
 */
function profile_role_restrict_form_submit($form, &$form_state) {
  db_query('DELETE FROM {profile_role}');

  foreach ($form_state['values']['_categories'] as $key => $category) {
    foreach ($form_state['values'][$key]['roles'] as $rid) {
      db_query("INSERT INTO {profile_role}
                VALUES (%d, '%s')", $rid, $category);
    }
  }

  drupal_set_message(t('The configuration options have been saved.'));
}

/**
 * Get the roles that use each profile categories.
 *
 * @return array Associative array of roles.
 */
function profile_role_get_roles() {
  $result = db_query('SELECT rid, category
                      FROM {profile_role}');
  $roles = array();
  while ($role = db_fetch_object($result)) {
    $roles[$role->category][] = $role->rid;
  }
  return $roles;
}

/**
 * Implementation of hook_form_alter(): profile_field_form.
 */
function profile_role_form_profile_field_form_alter(&$form, $form_state) {
  $form['#submit'][] = 'profile_role_prune_roles';
}

/**
 * Implementation of hook_form_alter(): profile_field_delete.
 */
function profile_role_form_profile_field_delete_alter(&$form, $form_state) {
  $form['#submit'][] = 'profile_role_prune_roles';
}

/**
 * Prune the role restrictions for orphaned records.
 *
 * Whenever the user can edit the categories either via deleting a field or
 * editting the category a field is in the category may no longer exist. After
 * either operation this function is used to prune the records.
 */
function profile_role_prune_roles() {
  // Must delete records in loop since DELETE WHERE IN (SELECT) does not work.
  $result = db_query('SELECT r.category
                      FROM {profile_role} r
                      LEFT JOIN {profile_fields} f
                        ON f.category = r.category
                      WHERE f.category IS NULL');
  while ($category = db_result($result)) {
    db_query("DELETE FROM {profile_role} WHERE category = '%s'", $category);
  }
}
