You are here

function menu_position_evaluate_rules in Menu Position 7

Same name and namespace in other branches
  1. 6 menu_position.module \menu_position_evaluate_rules()
  2. 7.2 menu_position.module \menu_position_evaluate_rules()

Evaluate all rules based on a given context.

Multiple rules may be activated, but only if they are all assigned to different menus (no more than one rule will be activated for any given menu).

Existing menu links for the context's path take precedence over menu position rules. Rules assigned to the menus associated with those links will be ignored.

The breadcrumb will be set according to the first matching rule, unless an existing menu link has been found for the context's path in one of the menus considered so far. (n.b. The order of the rules, and each rule's associated menu, determines the order in which menus are considered for this purpose.)

Parameters

array $context: A small context array containing the following data:

  • 'path' => The path of the current page.
  • 'entity_type' => The type of the current page's entity.
  • 'bundle_name' => The bundle of the current page's entity.
  • [entity] => The current page's entity object. The key is the entity type value (i.e. "node", "user", etc).

n.b. Only 'path' is guaranteed to be populated. The other values will be available only if the current page is an "entity" page.

bool $use_static_cache: If TRUE and the specified context has already been processed, then simply return the cached array of rules which were activated for that context. If the context has not yet been processed, normal processing will take place.

bool $use_cache: If TRUE (default) and the specified context has already been processed by a previous request, then use that as a list for rules to evaluate for the context. If the context has not yet been processed, normal processing will take place. Note that this takes effect only when the variable 'menu_position_rules_cache_enabled' is set to 1. By default it is 0. This can be set through the settings.php or the module's settigns form.

Return value

array An array of the rules which were activated for the specified context.

2 calls to menu_position_evaluate_rules()
menu_position_get_activated_rules in ./menu_position.module
Return the activated rule(s) for the specified context.
menu_position_page_delivery_callback_alter in ./menu_position.module
Implements hook_page_delivery_callback_alter().

File

./menu_position.module, line 302
Provides dynamic menu links based on configurable rules.

Code

function menu_position_evaluate_rules($context = array(), $use_static_cache = FALSE, $use_cache = TRUE) {
  if (empty($context)) {
    return array();
  }

  // Use the cache only when needed and it is enabled.
  $use_cache = $use_cache && variable_get('menu_position_rules_cache_enabled', 0);
  $cache =& drupal_static(__FUNCTION__, array());

  // Hash the context array to generate a cache key.
  $cache_key = array(
    'path' => md5($context['path']),
    'entity_type' => '',
    'bundle_name' => '',
    'entity_id' => '',
  );
  if (!empty($context['entity_type'])) {
    $entity_type = $context['entity_type'];
    $entity = $context[$entity_type];
    list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
    $cache_key['entity_type'] = $entity_type;
    $cache_key['bundle_name'] = $bundle;
    $cache_key['entity_id'] = $id;
  }
  $cache_key = implode(':', array_filter(array(
    'menu_position:rules',
    $cache_key['entity_type'],
    $cache_key['bundle_name'],
    $cache_key['entity_id'],
    $cache_key['path'],
  )));

  // If we just want the rules and we have a cached value, return immediately.
  if ($use_static_cache) {
    if (array_key_exists($cache_key, $cache)) {
      return $cache[$cache_key];
    }
  }

  // Otherwise process the context as normal, cache the result, and return that.
  $cache[$cache_key] = array();

  // Sanity check: if there is no existing menu item, Drupal won't display any
  // navigation menus anyway and will error out when we try methods below.
  if (menu_get_item($context['path']) === FALSE) {
    return $cache[$cache_key];
  }

  // Retrieve the list of menus the path is already in.
  $menu_names = db_select('menu_links', 'm')
    ->fields('m', array(
    'menu_name',
  ))
    ->condition('m.link_path', $context['path'], '=')
    ->execute()
    ->fetchCol();

  // Extract a list from cache.
  if ($use_cache && ($cache_entry = cache_get($cache_key, 'cache')) !== FALSE) {
    $rules = $cache_entry->data;
  }
  else {

    // Retrieve the rules from the database. For speed, we don't call
    // menu_position_read_rules() and unserialize the conditions only if needed.
    $rules_query = db_select('menu_position_rules', 'm')
      ->fields('m', array(
      'rid',
      'conditions',
      'menu_name',
      'plid',
      'mlid',
    ))
      ->condition('m.enabled', 1, '=')
      ->orderBy('m.weight')
      ->orderBy('m.rid');

    // Try to additionally filter-out the rules list.
    if (count($menu_names) && !count(module_implements("menu_position_rule_alter"))) {
      $rules_query
        ->condition('m.menu_name', $menu_names, 'NOT IN');
    }
    $rules = $rules_query
      ->execute();
  }

  // Flag that we still need to set the breadcrumb.
  $set_breadcrumb = TRUE;

  // Examine each rule and check its conditions.
  foreach ($rules as $rule) {
    if (in_array($rule->menu_name, $menu_names, TRUE)) {

      // If the page is already placed in the rule's menu, skip the rule.
      $rule_matches = FALSE;
      $set_breadcrumb = FALSE;
    }
    else {

      // A rule with no conditions always matches.
      $rule_matches = TRUE;

      // The ones from cache are already unserialized.
      if (is_string($rule->conditions)) {
        $rule->conditions = unserialize($rule->conditions);
      }

      // Go through each condition, AND-ing each result.
      foreach ($rule->conditions as $plugin => $variables) {

        // Add the current rule and node to the callback's variables.
        $variables['rule'] = $rule;
        $variables['context'] = $context;

        // Find the plugin's callback function.
        $callback = menu_position_get_condition_callback($plugin);
        if ($callback) {

          // Check if this condition matches.
          $rule_matches = $callback($variables);
        }
        else {

          // If the callback cannot be found, the condition has failed.
          $rule_matches = FALSE;
        }

        // No need to check other conditions if this condition failed.
        if (!$rule_matches) {
          break;
        }
      }
    }

    // Let other modules manipulate the rule (or any of the other parameters).
    drupal_alter('menu_position_rule', $rule, $context, $rule_matches, $set_breadcrumb);
    if ($rule_matches && menu_position_activate_rule($rule, $context, $set_breadcrumb)) {
      $rule->set_breadcrumb = $set_breadcrumb;
      $cache[$cache_key][] = $rule;

      // Don't let other rules set the breadcrumb.
      $set_breadcrumb = FALSE;

      // Don't let other rules match against this rule's menu.
      $menu_names[] = $rule->menu_name;
    }
  }
  if ($use_cache && $cache_entry === FALSE) {

    // There was a cache-miss, so iniialize the cache for next request(s).
    cache_set($cache_key, $cache[$cache_key], 'cache', CACHE_PERMANENT);
  }
  return $cache[$cache_key];
}