You are here

menu_item_visibility.module in Menu Item Visibility 7

Same filename and directory in other branches
  1. 8 menu_item_visibility.module

Alters the menu item form to specify roles who are allowed to view them.

File

menu_item_visibility.module
View source
<?php

/**
 * @file
 * Alters the menu item form to specify roles who are allowed to view them.
 */

/**
 * Implements hook_menu_link_alter().
 *
 * In order to have our menu_item_visibility_translated_menu_link_alter()
 * function called by _menu_link_translate(), we have to manually set this
 * menu link as 'altered'. Unfortunately this alters all menu links and we need
 * to figure out a better solution in order to not globally alter all links.
 */
function menu_item_visibility_menu_link_alter(&$item) {
  $item['options']['alter'] = TRUE;

  // Because menu_link_save() may skip calling hook_menu_link_update(), we need
  // to force it to be invoked. See http://drupal.org/node/1013856.
  if (!empty($item['mlid'])) {
    _menu_item_visibility_menu_link_update($item);
  }
}

/**
 * Implements hook_translated_menu_link_alter().
 */
function menu_item_visibility_translated_menu_link_alter(&$item, $map) {
  global $user;
  if (!empty($item['access'])) {

    // Menu administrators can see all links within admin paths.
    if (user_access('administer menu') && path_is_admin(current_path())) {
      return;
    }

    // @todo Convert this into a proper hook so modules can extend visibility.
    $item['visibility'] = menu_item_visibility_load($item['mlid']);
    if (!empty($item['visibility']['roles']) && !array_intersect($item['visibility']['roles'], array_keys($user->roles))) {
      $item['access'] = FALSE;
    }
  }
}

/**
 * Implements hook_module_implements_alter().
 */
function menu_item_visibility_module_implements_alter(&$implementations, $hook) {
  if ($hook == 'translated_menu_link_alter') {

    // Move menu_item_visibility_translated_menu_link_alter() to the end of the
    // list. This is mainly to deal with menu_token resetting the menu item
    // access back to what is returned by menu_get_item().
    $group = $implementations['menu_item_visibility'];
    unset($implementations['menu_item_visibility']);
    $implementations['menu_item_visibility'] = $group;
  }
}

/**
 * Load all visibility data for a menu link.
 */
function menu_item_visibility_load($mlid, $reset = FALSE) {

  // Use the advanced drupal_static() pattern, since this is called very often.
  static $drupal_static_fast;
  if (!isset($drupal_static_fast)) {
    $drupal_static_fast['visibilities'] =& drupal_static(__FUNCTION__);
  }
  $visibilities =& $drupal_static_fast['visibilities'];

  // If we haven't stored them all in the static build them from the database.
  if (!isset($visibilities) || $reset) {

    // Select all menu link ids and their role ids.
    $result = db_select('menu_links_visibility_role', 'r')
      ->fields('r', array(
      'mlid',
      'rid',
    ))
      ->execute()
      ->fetchAll(PDO::FETCH_ASSOC);
    $visibilities = array();
    foreach ($result as $record) {
      $visibilities[$record['mlid']][] = $record['rid'];
    }
  }
  $visibility['roles'] = !empty($visibilities[$mlid]) ? $visibilities[$mlid] : array();
  if (!empty($mlid)) {
    drupal_alter('menu_item_visibility_load', $visibility, $mlid);
  }
  return $visibility;
}

/**
 * Implements hook_menu_link_insert().
 */
function menu_item_visibility_menu_link_insert($link) {
  if (!empty($link['roles']) && ($roles = array_filter($link['roles']))) {
    $query = db_insert('menu_links_visibility_role');
    $query
      ->fields(array(
      'mlid',
      'rid',
    ));
    foreach ($roles as $rid) {
      $query
        ->values(array(
        'mlid' => $link['mlid'],
        'rid' => $rid,
      ));
    }
    $query
      ->execute();
  }
}

/**
 * Implements hook_menu_link_update().
 *
 * Disabled as a hook until http://drupal.org/node/1013856 is fixed.
 */
function _menu_item_visibility_menu_link_update($link) {

  // http://drupal.org/node/1369480
  // Only delete the roles if the $link object contains actual role information
  // otherwise role information will be lost when moving menu items.
  if (isset($link['roles'])) {
    db_delete('menu_links_visibility_role')
      ->condition('mlid', $link['mlid'])
      ->execute();
  }
  menu_item_visibility_menu_link_insert($link);
}

/**
 * Implements hook_menu_link_delete().
 */
function menu_item_visibility_menu_link_delete($link) {
  db_delete('menu_links_visibility_role')
    ->condition('mlid', $link['mlid'])
    ->execute();
}

/**
 * Implements hook_user_role_delete().
 */
function menu_item_visibility_user_role_delete($role) {
  db_delete('menu_links_visibility_role')
    ->condition('rid', $role->rid)
    ->execute();
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function menu_item_visibility_form_menu_edit_item_alter(&$form, &$form_state) {

  // Visibility settings.
  $form['visibility_title'] = array(
    '#type' => 'item',
    '#title' => t('Visibility settings'),
  );
  $form['visibility'] = array(
    '#type' => 'vertical_tabs',
    '#attached' => array(
      'js' => array(
        'vertical-tabs' => drupal_get_path('module', 'menu_item_visibility') . '/menu_item_visibility.js',
      ),
    ),
  );
  $visibility = menu_item_visibility_load($form['mlid']['#value'], TRUE);

  // Per-role visibility.
  $form['visibility']['role'] = array(
    '#type' => 'fieldset',
    '#title' => t('Roles'),
    '#collapsible' => TRUE,
    '#collapsed' => TRUE,
    '#group' => 'visibility',
    '#weight' => 10,
  );
  $form['visibility']['role']['roles'] = array(
    '#type' => 'checkboxes',
    '#title' => t('Show menu link for specific roles'),
    '#default_value' => isset($visibility['roles']) ? $visibility['roles'] : array(),
    '#options' => array_map('check_plain', user_roles()),
    '#description' => t('Show this menu link only for the selected role(s). If you select no roles, the menu link will be visible to all users.'),
  );
}