You are here

menu_per_role.module in Menu Per Role 7

Same filename and directory in other branches
  1. 8 menu_per_role.module
  2. 5 menu_per_role.module
  3. 6 menu_per_role.module

Allows restricting access to menu items per role.

File

menu_per_role.module
View source
<?php

/**
 * @file
 * Allows restricting access to menu items per role.
 */

/**
 * Implements hook_help().
 */
function menu_per_role_help($path, $arg) {
  switch ($path) {
    case 'admin/help#menu_per_role':
      $output = '';
      $output .= '<h3>' . t('About') . '</h3>';
      $output .= '<p>' . t('The Menu Per Role module allows you to restrict access of menu items per roles.') . '</p>';
      $output .= '<h3>' . t('Configuration') . '</h3>';
      $output .= '<p>' . t('Just activate the Menu Per Role module and edit a menu item as usual. There will be one or two fieldsets, depending on the configuration of the module, that allows you to restrict access by role.') . '</p>';
      $output .= '<p>' . t("If you don't check any roles the default access permissions will be kept. Otherwise the module will additionally restrict access to the chosen user roles.") . '</p>';
      return $output;
  }
}

/**
 * Implements hook_permission().
 */
function menu_per_role_permission() {
  $permissions = array();
  $permissions['administer menu_per_role'] = array(
    'title' => t('Administer Menu Per Role'),
    'description' => t('Permission for who can access the Menu Per Role forms.'),
  );
  return $permissions;
}

/**
 * Implements hook_menu().
 */
function menu_per_role_menu() {
  $menu = array();
  $menu['admin/config/system/menu_per_role'] = array(
    'title' => 'Menu Per Role',
    'description' => 'Settings for the Menu Per Role module',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'menu_per_role_settings_form',
    ),
    'access arguments' => array(
      'administer menu_per_role',
    ),
    'file' => 'menu_per_role.admin.inc',
  );
  return $menu;
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function menu_per_role_form_menu_edit_item_alter(&$form, &$form_state, $form_id) {
  if (!user_access('administer menu_per_role')) {
    return;
  }
  $default_value_roles = $form['mlid']['#value'] ? _menu_per_role_get_roles($form['mlid']['#value'], 0) : array();
  $default_value_hide_from_roles = $form['mlid']['#value'] ? _menu_per_role_get_roles($form['mlid']['#value'], 1) : array();
  $form['menu_per_role'] = array(
    '#type' => 'fieldset',
    '#title' => t('Restrict item visibility'),
    '#collapsible' => TRUE,
    '#collapsed' => count($default_value_roles) + count($default_value_hide_from_roles) == 0,
    '#weight' => 5,
    '#description' => t('Check to know whether the user has proper visibility permissions to see this menu item. Note that both checks are always performed.'),
    '#tree' => TRUE,
  );
  $form['menu_per_role']['menu_per_role_roles'] = array(
    '#type' => 'checkboxes',
    '#title' => t('Show menu item only to selected roles'),
    '#options' => user_roles(),
    '#default_value' => $default_value_roles,
    '#description' => t('Check no role to leave the access permission to the default. A user who is not part of at least one of the selected roles will not see this menu item.'),
  );
  $form['menu_per_role']['menu_per_role_hide_from_roles'] = array(
    '#type' => 'checkboxes',
    '#title' => t('Hide menu item from selected roles'),
    '#options' => user_roles(),
    '#default_value' => $default_value_hide_from_roles,
    '#description' => t('Check no role to leave the access permission to the default. A user who is part of any one of these roles will not see this menu item.'),
  );
}

/**
 * Implements hook_menu_link_insert().
 */
function menu_per_role_menu_link_insert($link) {
  if (isset($link['menu_per_role']) && user_access('administer menu_per_role')) {

    // Hide but to those roles.
    $rids_str = _menu_per_role_serialize_rids($link['menu_per_role']['menu_per_role_roles']);

    // Show but to those roles.
    $hrids_str = _menu_per_role_serialize_rids($link['menu_per_role']['menu_per_role_hide_from_roles']);
    $fields = array(
      'rids' => $rids_str,
      'hrids' => $hrids_str,
      'mlid' => $link['mlid'],
    );
    drupal_write_record('menu_per_role', $fields);
  }
}

/**
 * Implements hook_menu_link_update().
 */
function menu_per_role_menu_link_update($link) {
  if (isset($link['menu_per_role']) && user_access('administer menu_per_role')) {

    // Hide but to those roles.
    $rids_str = _menu_per_role_serialize_rids($link['menu_per_role']['menu_per_role_roles']);

    // Show but to those roles.
    $hrids_str = _menu_per_role_serialize_rids($link['menu_per_role']['menu_per_role_hide_from_roles']);
    $fields = array(
      'rids' => $rids_str,
      'hrids' => $hrids_str,
    );
    $count = db_select('menu_per_role')
      ->condition('mlid', $link['mlid'])
      ->countQuery()
      ->execute()
      ->fetchField();
    if ($count == 0) {
      $fields['mlid'] = $link['mlid'];
      db_insert('menu_per_role')
        ->fields($fields)
        ->execute();
    }
    else {
      db_update('menu_per_role')
        ->fields($fields)
        ->condition('mlid', $link['mlid'])
        ->execute();
    }
  }
}

/**
 * Implements hook_menu_link_delete().
 */
function menu_per_role_menu_link_delete($link) {

  // Delete the record from our table.
  db_delete('menu_per_role')
    ->condition('mlid', $link['mlid'])
    ->execute();
}

/**
 * Implements hook_menu_link_alter().
 *
 * By catching that function, we can set the special alter option
 * that will let our module receive a call whenever the menu is
 * ready for display but was not yet displayed. At that time we
 * can mark the access as FALSE.
 */
function menu_per_role_menu_link_alter(&$item) {

  // TODO: The following marks ALL menu items as alterable. Any time a menu item
  // is saved, it is marked as such. I have no clue, at this time, of a way to
  // avoid such nonsense. Hints welcome!
  menu_per_role_menu_link_update($item);
  $item['options']['alter'] = TRUE;
}

/**
 * Implements hook_translated_menu_link_alter().
 *
 * Before a menu item gets displayed, the core calls the hook:
 * hook_translated_menu_link_alter(&$item, $map);
 * (but only if $item['options']['alter'] is TRUE)
 *
 * This function is used to alter the access right based on
 * the role definition of the item.
 */
function menu_per_role_translated_menu_link_alter(&$item, $map) {

  // Avoid checking the role if the item access is already false.
  if ($item['access'] && _menu_per_role_access($item['mlid']) === FALSE) {
    $item['access'] = FALSE;
  }
}

/**
 * Determines access for a given menu item id.
 *
 * @param int $mlid
 *   The menu item identifier.
 *
 * @return bool|null
 *   NULL if this module does not forbid the viewing of this menu item, FALSE
 *   otherwise.
 */
function _menu_per_role_access($mlid) {
  global $user;
  if (empty($mlid)) {
    return NULL;
  }

  // If menu is being edited allow user to see it in full.
  if (arg(0) == 'admin' && arg(1) == 'structure' && arg(2) == 'menu') {
    return NULL;
  }

  // Check whether this role has visibility access (must be present).
  $rids = _menu_per_role_get_roles($mlid, 0);
  if (!empty($rids) && count(array_intersect($rids, array_keys($user->roles))) == 0) {

    // Not permitted by the rids...
    return FALSE;
  }

  // Check whether this role has visibility access (must not be present).
  $hrids = _menu_per_role_get_roles($mlid, 1);
  if (!empty($hrids) && count(array_intersect($hrids, array_keys($user->roles))) > 0) {

    // Not permitted by the hrids...
    return FALSE;
  }

  // This module is not preventing user from seeing this menu entry.
  return NULL;
}

/**
 * Turn the given roles into a serialized string of rids or hrids.
 *
 * @param array $roles
 *   An array of roles as passed from the appropriate keys from
 *   $form_state['values'] during a form submit.
 *
 * @return string
 *   A string of the serialized rids.
 */
function _menu_per_role_serialize_rids(array $roles) {
  $rids = array();
  foreach ($roles as $rid => $checked) {
    if ($checked) {
      $rids[] = $rid;
    }
  }
  return implode(',', $rids);
}

/**
 * Turn a serialized string of rids or hrids into an array of rids or hrids.
 *
 * @param string $rids
 *   The serialized string to unserialize.
 *
 * @return array
 *   An array of rids.
 */
function _menu_per_role_unserialize_rids($rids) {
  return explode(',', $rids);
}

/**
 * Gets all roles with access to the specified menu item.
 *
 * No roles mean that access is granted by this module.
 *
 * @param int $mlid
 *   The menu link id.
 * @param int $show
 *   Set to 0 for show to roles, 1 for hide from roles.
 *
 * @return array
 *   An array of roles.
 */
function _menu_per_role_get_roles($mlid, $show) {
  static $menu_per_role;
  if (!isset($menu_per_role)) {

    // Read all the data ONCE, it is likely very small.
    $menu_per_role = array();
    $result = db_select('menu_per_role', 'mpr')
      ->fields('mpr')
      ->execute()
      ->fetchAll();
    foreach ($result as $row) {
      if ($row->rids || $row->hrids) {
        if ($row->rids) {
          $menu_per_role[$row->mlid][0] = _menu_per_role_unserialize_rids($row->rids);
        }
        else {
          $menu_per_role[$row->mlid][0] = array();
        }
        if ($row->hrids) {
          $menu_per_role[$row->mlid][1] = _menu_per_role_unserialize_rids($row->hrids);
        }
        else {
          $menu_per_role[$row->mlid][1] = array();
        }
      }
    }
  }
  if (isset($menu_per_role[$mlid])) {
    return $menu_per_role[$mlid][$show];
  }

  // Not defined, everyone has the right to use it.
  return array();
}

Functions

Namesort descending Description
menu_per_role_form_menu_edit_item_alter Implements hook_form_FORM_ID_alter().
menu_per_role_help Implements hook_help().
menu_per_role_menu Implements hook_menu().
menu_per_role_menu_link_alter Implements hook_menu_link_alter().
menu_per_role_menu_link_delete Implements hook_menu_link_delete().
menu_per_role_menu_link_insert Implements hook_menu_link_insert().
menu_per_role_menu_link_update Implements hook_menu_link_update().
menu_per_role_permission Implements hook_permission().
menu_per_role_translated_menu_link_alter Implements hook_translated_menu_link_alter().
_menu_per_role_access Determines access for a given menu item id.
_menu_per_role_get_roles Gets all roles with access to the specified menu item.
_menu_per_role_serialize_rids Turn the given roles into a serialized string of rids or hrids.
_menu_per_role_unserialize_rids Turn a serialized string of rids or hrids into an array of rids or hrids.