29 Jul 2013 01:11:07

Drupal . Lightweight highlight menu links by path mask, entity type & bundle rule functional.

I used menu_position Drupal contrib module for this task but it has a lot of buggs related to features transfer across multiple server instances. So we decided to implement lightweight logic with hardcoded menu rules to highlight specified menu link regarding path mask, entity type and bundle rule settings.
We reused some menu position's module source parts & implement needed logic.
To tigger this code you should implement hook_delivery_callback_alter:
 
/**
 * Implements hook_page_delivery_callback_alter().
 * To highlight menu links by custom rules.
 */
function your_module_name_page_delivery_callback_alter() {
  // Check if some rule should be triggered & activate it.
  module_excute_rules();
}

This alter will trigger below logic, you can move it to separate module inc file:
 
<?php
/**
 * @file
 * Provides logic to highlight specified menu items by path mask.
 */

/**
 * Hardcoded menu position rules.
 */
function module_rules() {
  // Rules represent as array with several keys: paths of menu items
  // to be highlighted & pages paths where this items will be
  // highlight. Also related properties such as entity type, bundle.
  $rules = array(
    array(
      // This gives opportunity to apply mask rule for several
      // menu items.
      'menu_item_to_highlight' => array(
        'admin/content/static-pages',
      ),
      'page_mask' => array(
        'node/*/delete',
        'node/*/edit',
        'node/*/publish',
        'node/*/unpublish',
      ),
      // Optional.
      'type' => array(
        'entity_types' => array(
          'node',
        ),
        'bundles' => array(
          'static_page',
        ),
      ),
    ),
  );

  return $rules;
}

/**
 * Triggered from delivery callback alter to check & execute
 * validated rules.
 */
function module_excute_rules() {
  if ($rules = module_rules()) {
    $context = module_build_context();
    foreach ($rules as $rule) {
      foreach ($rule['menu_item_to_highlight']
         as $menu_item_path_to_highlight) {
        // Search link within admin menu, if it isn't exist check
        // across all menus.
        $item = menu_link_get_preferred($menu_item_path_to_highlight,
         'menu-admin-menu');

        if (empty($item['mlid'])) {
          $item = menu_link_get_preferred($menu_item_path_to_highlight);
        }
        if (!empty($item['mlid']) && !empty($rule['page_mask'])) {
          // Check is type settings are filled & check are they valid.
          if (!empty($rule['type'])) {
            $valid_type = module_check_types(
              $rule['type'], $context
            );
          }
          else {
            $valid_type = TRUE;
          }

          if ($valid_type) {
            // Check if current path ($_GET['q']) belongs to some
            // rule mask.
            foreach ($rule['page_mask'] as $path) {
              if (module_check_page_conditions($path)) {
                // Set rule menu mode to highlight.
                module_activate_rule($item, $context);
                break;
              }
            }
          }
        }
      }
    }
  }
}

/**
 * Context callback.
 */
function module_build_context() {
  // Build a small context.
  $context = array(
    'path' => $_GET['q'],
    'entity_type' => NULL,
    'bundle_name' => NULL,
  );

  // Determine what kind of entity page this is.
  list($arg0, $arg1) = explode('/', $_GET['q'] . '//');
  if ($arg0 == 'node' && is_numeric($arg1)) {
    $context['node'] = node_load($arg1);
    // Don't evaluate the rules on a 404 page.
    if (!$context['node']) {
      return;
    }
    $context['entity_type'] = 'node';
    $context['bundle_name'] = $context['node']->type;
  }
  elseif ($arg0 == 'user' && is_numeric($arg1)) {
    $context['user'] = user_load($arg1);
    // Don't evaluate the rules on a 404 page.
    if (!$context['user']) {
      return;
    }
    $context['entity_type'] = 'user';
    $context['bundle_name'] = 'user';
  }
  elseif (preg_match('/taxonomy\/term\/([0-9]+)/i', $_GET['q'],
    $matches)) {
    
    $context['taxonomy_term'] = taxonomy_term_load($matches[1]);
    // Don't evaluate the rules on a 404 page.
    if (!$context['taxonomy_term']) {
      return;
    }
    $context['entity_type'] = 'taxonomy_term';
    $context['bundle_name'] = $context['taxonomy_term']
      ->vocabulary_machine_name;
  }

  return $context;
}

/**
 * Menu position module fork - check are there some valid menu
 * rules to activate it.
 */
function module_check_page_conditions($pages) {
  // Convert the Drupal path to lowercase.
  $path = drupal_strtolower(drupal_get_path_alias($_GET['q']));
  // Compare the lowercase internal and lowercase path alias (if any).
  $page_match = drupal_match_path($path, $pages);
  if (!$page_match && $path != $_GET['q']) {
    $page_match = drupal_match_path($_GET['q'], $pages);
  }

  return $page_match;
}

/**
 * Entity type & bundle check callback.
 */
function module_check_types($type_settings, $context) {
  if (!empty($context['entity_type'])
    && !empty($type_settings['entity_types'])
    && in_array(
      $context['entity_type'],
      $type_settings['entity_types'], TRUE)
    ) {
    
    $entity_type = TRUE;
  }
  if (!empty($context['bundle_name'])
    && !empty($type_settings['bundles'])
    && in_array(
      $context['bundle_name'],
      $type_settings['bundles'], TRUE)
    ) {
    
    $bundle = TRUE;
  }
  return !empty($entity_type) && !empty($bundle);
}

/**
 * Menu position module fork - activate rule's link if it matched
 * to page rules.
 */
function module_activate_rule($item, $context = NULL,
  $set_breadcrumb = TRUE) {
  
  // Retrieve menu item specified in the rule.
  $menu_item = menu_link_load($item['mlid']);

  // Sanity check: if the menu link doesn't exist abort processing
  // the rule.
  if (!$menu_item) {
    return FALSE;
  }

  // Reset the menu trail that views may have set.
  $original_router_item = menu_get_item();
  if ($original_router_item['page_callback'] == 'views_page') {
    $preferred = & drupal_static('menu_link_get_preferred');
      unset($preferred[$context['path']]);
  }

  // Set the active path for the rule's menu.
  menu_tree_set_path($menu_item['menu_name'],
    $menu_item['link_path']
  );

  // Allow the rule's parent menu item to show "expanded" status.
  module_expand_parent_link($item['plid']);

  // Alter the active trail if breadcrumbs still need to be set.
  if ($set_breadcrumb) {
    // Manually set the preferred link for this path so that
    // menu_get_active_trail() returns the proper trail.
    $preferred_links = & drupal_static('menu_link_get_preferred');
    $preferred_links[$_GET['q']][MENU_PREFERRED_LINK]
       = menu_link_get_preferred($menu_item['link_path']);

    // Remove the menu position router from the end of the trail.
    $active_trail = menu_set_active_trail();
    array_pop($active_trail);
    menu_set_active_trail($active_trail);
  }

  return TRUE;
}

/**
 * Menu position fork.
 * Dynamically expands the parent menu item for a rule.
 *
 * @param $plid
 *   The parent menu item's mlid.
 */
function module_expand_parent_link($plid = NULL) {
  $link_id = &drupal_static(_FUNCTION_, NULL);

  if (isset($plid)) {
    $link_id = $plid;
  }
  return $link_id;
}



 So, after implementing this you can hardcode some rule to highlight needed menu link, there is rule example:
 
    array(
      // This gives opportunity to apply mask rule for several menu items.
      'menu_item_to_highlight' => array(
        'admin/content/static-pages',
      ),
      'page_mask' => array(
        'node/*/delete',
        'node/*/edit',
        'node/*/publish',
        'node/*/unpublish',
      ),
      // Optional.
      'type' => array(
        'entity_types' => array(
          'node',
        ),
        'bundles' => array(
          'static_page',
        ),
      ),
    ),
   

This rule highlight menu link with path "admin/content/static-pages" for static_page bundle at all page mask :
        'node/*/delete',
        'node/*/edit',
        'node/*/publish',
        'node/*/unpublish'
i.e. node/3/edit

Comments:

аноним
Продаю грибы
10 июл 2013 в 20:49
Pavel Ruban
какие нынче в ходу?
10 июл 2013 в 22:49
аноним
Can you please send an e-mail to me the code for this script or please tell me in detail concerning this script? michael kors http://www.tundraforum.org/product/michael-kors-outlet.html
21 июл 2013 в 20:06

add comment