Chris 2pha Brown. Drupal developer Brisbane Australia

Chris Brown

Drupal, Javascript, Three.js, 3D

website blog
image for Drupal Domain Access user 1 menu

Drupal Domain Access user 1 menu

Today when developing a Drupal site with the Domain Access module I had an interesting problem. My site is set up with a custom theme and 2 domains.
My custom theme is using Zurb Foundation and so I had to override the main menu theme functions to add the Zurb classes.
This was easily done by implementing theme_links__system_main_menu, theme_menu_tree__main_menu__submenu and theme_menu_link__main_menu as outlined below (my theme name is nexus).

function nexus_links__system_main_menu($variables) {
  $links = menu_tree_output(menu_tree_all_data(variable_get('menu_main_links_source', 'main-menu')));
  $output = drupal_render($links);
  return $output;
}

function nexus_menu_tree__main_menu__submenu($variables) {
  return '<ul class="menu dropdown">' . $variables['tree'] . '</ul>';
}

function nexus_menu_link__main_menu($variables) {
  $element = $variables['element'];
  $sub_menu = '';
  if ($element['#below']) {
    $element['#below']['#theme_wrappers'][] = array('menu_tree__main_menu__submenu');
    $sub_menu = drupal_render($element['#below']);
    $element['#attributes']['class'][] = 'has-dropdown';
  }
  if (isset($element['#href']) && ($element['#href'] == $_GET['q'] || ($element['#href'] == '<front>' && drupal_is_front_page())) && (empty($element['#language']) || $element['#language']->language == $language_url->language)) {
    $element['#attributes']['class'][] = 'active';
  }
  $output = l($element['#title'], $element['#href'], $element['#localized_options']);
  return '<li' . drupal_attributes($element['#attributes']) . '>' . $output . $sub_menu . "</li>\n";
}

The problem was that all node links assigned to the main menu would show up if you were logged in as user 1, even if they were not assigned to the current domain. Though, if you were not logged in the menu would appear correct and only show links assigned to the current domain.
This is obviously the correct behavior as the domain access module uses Drupal's node access system and the access system is bypassed for user 1.
At this point I could have switched to using a different primary menu for each domain, but I didn't really like the idea of this as the menu was working fine, just not displaying the way I wanted for user 1.

So, what I did was, Made alternate menu generation functions, but just for user 1.

First I changed my main menu theme:

function nexus_links__system_main_menu($variables) {
  global $user;
  if($user->uid == 1){
    // use own custom menu functions so user 1 does not see menu items not on this domain.
    $links = menu_tree_output(_nexus_user1_menu_tree_all_data(variable_get('menu_main_links_source', 'main-menu')));
  }else{
    $links = menu_tree_output(menu_tree_all_data(variable_get('menu_main_links_source', 'main-menu')));
  }
  $output = drupal_render($links);
  return $output;
}

Then I implemented my own menu_tree_all_data function (also caching my menu).

function _nexus_user1_menu_tree_all_data($menu_name, $link = NULL, $max_depth = NULL) {
  $tree = &drupal_static(__FUNCTION__, array());
  $mlid = isset($link['mlid']) ? $link['mlid'] : 0;
  // change the original cache cid (adding "user1")
  $cid = 'user1_links:' . $menu_name . ':all:' . $mlid . ':' . $GLOBALS['language']->language . ':' . (int) $max_depth;

  if (!isset($tree[$cid])) {
    $cache = cache_get($cid, 'cache_menu');
    if ($cache && isset($cache->data)) {
      $tree_parameters = $cache->data;
    }
    if (!isset($tree_parameters)) {
      $tree_parameters = array(
        'min_depth' => 1,
        'max_depth' => $max_depth,
      );
      if ($mlid) {
        $parents = array(0);
        for ($i = 1; $i < MENU_MAX_DEPTH; $i++) {
          if (!empty($link["p$i"])) {
            $parents[] = $link["p$i"];
          }
        }
        $tree_parameters['expanded'] = $parents;
        $tree_parameters['active_trail'] = $parents;
        $tree_parameters['active_trail'][] = $mlid;
      }
      cache_set($cid, $tree_parameters, 'cache_menu');
    }
    // Call my build tree function
    $tree[$cid] = _nexus_user1_menu_build_tree($menu_name, $tree_parameters);
  }

  return $tree[$cid];
}

implemented my own menu_build_tree function

 function _nexus_user1_menu_build_tree($menu_name, $parameters = array()) {
  $data = _menu_build_tree($menu_name, $parameters);
  // call my custom access function
  _nexus_user1_menu_tree_check_access($data['tree'], $data['node_links']);
  return $data['tree'];
}

Then implemented my own access function (remember this only fires for user 1)

function _nexus_user1_menu_tree_check_access(&$tree, $node_links = array()) {
  if ($node_links) {
    $nids = array_keys($node_links);
    $select = db_select('node', 'n');
    $select->addField('n', 'nid');
    $select->condition('n.status', 1);
    $select->condition('n.nid', $nids, 'IN');
    $select->addTag('node_access');
    // Add custom domain access arguments
    global $_domain;
    $select->join('domain_access', 'da', 'n.nid = da.nid');
    $select->condition('da.gid', array(0, $_domain['domain_id']),'IN');

    $nids = $select->execute()->fetchCol();
    foreach ($nids as $nid) {
      foreach ($node_links[$nid] as $mlid => $link) {
        $node_links[$nid][$mlid]['access'] = TRUE;
      }
    }
  }
  _menu_tree_check_access($tree);
}

Now, to be honest, I'm not sure if this is the best way to do this or if there is a simpler way. As I could not find any references on google to others having this problem there may infaact be a better or simpler way to do this... But, it worked for me :)

Add a comment

<b>, <i> and <code> tags are allowed.