You are here

views_menu_reference.module in Views Menu Reference 7

views_menu_reference module core implementations.

File

views_menu_reference.module
View source
<?php

/**
 * @file views_menu_reference module core implementations.
 */

/**
 * Implements hook_views_api().
 */
function views_menu_reference_views_api() {
  return array(
    'api' => '3',
    'path' => drupal_get_path('module', 'views_menu_reference'),
  );
}

/**
 * Returns the <$link_path>'s parent menu hierarchy. It describes the mlids
 * in each depth that may be direct (0) or indirect (1..*) parent.
 * The hierarchy is indexed by the depth and may contain the tree items
 * from more than one menu item, because multiple menu items's may belong
 * to one <$link_path>.
 * The depth index consists of a numeric (0..1..2...) and a plus'sed key
 * (0+..1+..2+) which represent the subtree items.
 *
 * The function is the public variant which makes use of caching to speed things
 * up on a second request.
 *
 * @param string $link_path
 * @return array
 */
function views_menu_reference_get_link_path_parents_hierarchy($link_path) {
  if (empty($link_path)) {

    // Return empty array if no link given.
    return array();
  }
  $cache_key = 'views_menu_reference_get_link_path_parents_hierarchy:' . $link_path;
  $parents_hierarchy =& drupal_static($cache_key);
  if (!isset($parents_hierarchy)) {
    if ($cache = cache_get($cache_key)) {
      $parents_hierarchy = $cache->data;
    }
    else {
      $parents_hierarchy = _views_menu_reference_get_link_path_parents_hierarchy($link_path);
      cache_set($cache_key, $parents_hierarchy, 'cache');
    }
  }
  return $parents_hierarchy;
}

/**
 * Private variant of views_menu_reference_get_link_path_parents_hierarchy().
 * Contains the true logic behind the caching.
 * 
 * @param string $link_path
 * @return array
 */
function _views_menu_reference_get_link_path_parents_hierarchy($link_path) {

  // Use the alias and source pathes because a link can directly link both.
  $link_pathes = array(
    $link_path => $link_path,
  );
  $link_path_alias = drupal_lookup_path('alias', $link_path);
  if ($link_path_alias !== FALSE) {
    $link_pathes[$link_path_alias] = $link_path_alias;
  }
  $link_path_source = drupal_lookup_path('source', $link_path);
  if ($link_path_source !== FALSE) {
    $link_pathes[$link_path_source] = $link_path_source;
  }

  // Special case: Add <front> placeholder if the link path is the frontpage!
  if ($link_path == variable_get('site_frontpage', 'node')) {
    $link_pathes['<front>'] = '<front>';
  }
  if (empty($link_pathes)) {

    // No matching results in the menu tree at all.
    return array();
  }

  // Get the <$link_path> menu items and their properties we need.
  $query = db_select('menu_links', 'ml');
  $query
    ->fields('ml', array(
    'mlid',
    'depth',
    'p1',
    'p2',
    'p3',
    'p4',
    'p5',
    'p6',
    'p7',
    'p8',
    'p9',
  ));
  $query
    ->condition('link_path', $link_pathes, 'IN');
  $result = $query
    ->execute();
  $parents_hierarchy = array();
  foreach ($result as $record) {

    // Process all pX Elements (not including sub menu entries!)
    $depth = $record->depth;
    $i = 0;
    while ($depth > 0) {
      $parent_field = 'p' . $depth;
      $parent_field_value = $record->{$parent_field};
      if (!empty($parent_field_value)) {

        // We index the fields by their value so we ensure that no item is added twice with high performance.
        // This truely is nothing else then $parents_hierarchy[$i][] PLUS unique.
        $parents_hierarchy[$i][$parent_field_value] = $parent_field_value;
      }
      $depth--;
      $i++;
    }

    // Process all pX+ Elements (including sub menu entries!)
    $depth = $record->depth;
    $i = 1;
    while ($i <= $depth) {
      $parent_field = 'p' . $i;
      $parent_field_value = $record->{$parent_field};
      if (!empty($parent_field_value)) {

        // We index the fields by their value so we ensure that no item is added twice with high performance.
        // This truely is nothing else then $parents_hierarchy[$i][] PLUS unique.
        $parents_hierarchy[$i . '+'][$parent_field_value] = $parent_field_value;
      }
      $i++;
    }
  }
  return $parents_hierarchy;
}

/**
 * Implements hook_menu_update().
 */
function views_menu_reference_menu_update($menu) {

  // Clear cache on menu items updates.
  _views_menu_reference_clear_caches();
}

/**
 * Implements hook_node_update().
 */
function views_menu_reference_node_update($node) {

  // Clear cache on node updates.
  _views_menu_reference_clear_caches();
}

/**
 * Helper function to clear the caches of views_menu_reference, when they should
 * be invalidated.
 */
function _views_menu_reference_clear_caches() {

  // Remove all entries starting with the key from this module.
  cache_clear_all('views_menu_reference_get_link_path_parents_hierarchy:', 'cache', TRUE);
}

/**
 * Implements hook_field_info().
 * Provides the description of the field.
 */
function views_menu_reference_field_info() {
  return array(
    'field_views_menu_reference' => array(
      'label' => t('Views Menu Reference Field'),
      'description' => t('Creates a field for views menu references.'),
      'default_widget' => 'field_views_menu_reference',
      'default_formatter' => 'field_views_menu_reference_default',
    ),
  );
}

/**
 * Implements hook_field_widget_info().
 */
function views_menu_reference_field_widget_info() {
  return array(
    'field_views_menu_reference' => array(
      'label' => t('Views Menu Reference Field'),
      'field types' => array(
        'field_views_menu_reference',
      ),
      'settings' => array(
        'menus' => array(),
      ),
    ),
  );
}

/**
 * Implements hook_field_widget_form().
 */
function views_menu_reference_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
  $menus =& $instance['widget']['settings']['menus'];
  $menu_names = menu_get_menus();
  $filtered_menus = array();
  foreach ($menus as $key => $value) {
    if ($value) {
      $filtered_menus[$key] = $menu_names[$key];
    }
  }

  // Get the list of allowed menus and their menu items.
  $items_raw = menu_parent_options($filtered_menus, array(
    'mlid' => 0,
  ));

  // Create the menu list.
  $menu_list = array(
    '' => t('None'),
  );
  $menu_counter = -4;

  // Loop through the list of menu items and process them.
  foreach ($items_raw as $key => $name) {
    $id = explode(':', $key);
    if ($id[1] == 0) {

      // Has no key: Is a menu parent item.
      $menu_list[$menu_counter] = $name;
      $menu_counter--;
    }
    else {
      $menu_list[$id[1]] = $name;
    }
  }

  // The mlid selector.
  $element['mlid'] = array(
    '#type' => 'select',
    '#options' => $menu_list,
    '#title' => t('Menu item'),
    '#default_value' => isset($items[$delta]['mlid']) ? $items[$delta]['mlid'] : '',
  );

  // Create the select list.
  $depth_list = array(
    '0' => t('0 (Only the menu entry itself)'),
  );
  for ($i = 1; $i <= 9; $i++) {
    $depth_list[$i] = t($i . ' (Only the entry)');
    $depth_list[$i . '+'] = t($i . '+ (Incl. subentries)');
  }

  // The depth selector
  $element['depth'] = array(
    '#type' => 'select',
    '#options' => $depth_list,
    '#title' => t('Depth'),
    '#default_value' => isset($items[$delta]['depth']) ? $items[$delta]['depth'] : '0',
    '#description' => t('The depth is being calculated from the selected menu item on. This makes it possible to select a whole menu level X levels under the selected element including or excluding sub menu items.'),
  );

  // To prevent an extra required indicator, disable the required flag on the
  // base element since all the sub-fields are already required if desired.
  $element['#required'] = FALSE;
  return $element;
}

/**
 * Implements hook_field_validate().
 */
function views_menu_reference_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) {
  foreach ($items as $delta => $item) {
    if (!empty($item['mlid'])) {
      if ((int) $item['mlid'] < 0) {
        $errors[$field['field_name']][$langcode][$delta][] = array(
          'error' => 'views_menu_reference_mlid_menuparent',
          'message' => t('%name: The menu item you selected is a whole menu tree. This is not allowed. Please select a menu item.', array(
            '%name' => $instance['label'],
          )),
        );
        return;
      }
      $mlid = $item['mlid'];
      if (menu_link_load($mlid) === FALSE) {
        $errors[$field['field_name']][$langcode][$delta][] = array(
          'error' => 'views_menu_reference_wrong_mlid',
          'message' => t('%name: The menu item you selected does not exist.', array(
            '%name' => $instance['label'],
          )),
        );
      }
    }
  }
}

/**
 * Implements hook_field_is_empty().
 */
function views_menu_reference_field_is_empty($item, $field) {

  // The field is empty if mlid equals an empty string or is less than zerso.
  return $item['mlid'] === '' or (int) $item['mlid'] < 0;
}

/**
 * Implements hook_field_widget_settings_form().
 */
function views_menu_reference_field_widget_settings_form($field, $instance) {
  $form = array();

  // Create these variables by reference. No need to increase memory usage just
  // because we want to write variables in a readable way.
  $widget =& $instance['widget'];
  $settings =& $widget['settings'];
  if ($widget['type'] == 'field_views_menu_reference') {
    $form['menus'] = array(
      '#type' => 'checkboxes',
      '#title' => t('Selectable menus'),
      '#default_value' => $settings['menus'],
      '#options' => menu_get_menus(),
      '#description' => t('Select which menus should be possible to refer to.'),
      '#weight' => -1,
      '#required' => TRUE,
    );
  }
  return $form;
}

/**
 * Implements hook_field_presave().
 */
function views_menu_reference_field_presave($entity_type, $entity, $field, $instance, $langcode, &$items) {
  foreach ($items as $delta => $item) {
    if (isset($item['views_menu_reference']['mlid'])) {
      $items[$delta]['mlid'] = $item['views_menu_reference']['mlid'];
      $items[$delta]['depth'] = $item['views_menu_reference']['depth'];
    }
  }
}

/**
 * Implements hook_field_formatter_info().
 */
function views_menu_reference_field_formatter_info() {
  return array(
    'field_views_menu_reference_default' => array(
      'label' => t('Default'),
      'field types' => array(
        'field_views_menu_reference',
      ),
    ),
  );
}

/**
 * Implements hook_field_formatter_view().
 */
function views_menu_reference_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
  $element = array();
  switch ($display['type']) {
    case 'field_views_menu_reference_default':
      foreach ($items as $delta => $item) {
        if (isset($item['mlid'])) {
          $mlid = $item['mlid'];
          $menu_link = menu_link_load($mlid);
          $element[$delta]['#markup'] = l($menu_link['link_title'], $menu_link['link_path']) . ' (' . check_plain($item['mlid']) . '), ' . t('Depth') . ': ' . check_plain($item['depth']);
        }
      }
      break;
  }
  return $element;
}

Functions

Namesort descending Description
views_menu_reference_field_formatter_info Implements hook_field_formatter_info().
views_menu_reference_field_formatter_view Implements hook_field_formatter_view().
views_menu_reference_field_info Implements hook_field_info(). Provides the description of the field.
views_menu_reference_field_is_empty Implements hook_field_is_empty().
views_menu_reference_field_presave Implements hook_field_presave().
views_menu_reference_field_validate Implements hook_field_validate().
views_menu_reference_field_widget_form Implements hook_field_widget_form().
views_menu_reference_field_widget_info Implements hook_field_widget_info().
views_menu_reference_field_widget_settings_form Implements hook_field_widget_settings_form().
views_menu_reference_get_link_path_parents_hierarchy Returns the <$link_path>'s parent menu hierarchy. It describes the mlids in each depth that may be direct (0) or indirect (1..*) parent. The hierarchy is indexed by the depth and may contain the tree items from more than one menu item,…
views_menu_reference_menu_update Implements hook_menu_update().
views_menu_reference_node_update Implements hook_node_update().
views_menu_reference_views_api Implements hook_views_api().
_views_menu_reference_clear_caches Helper function to clear the caches of views_menu_reference, when they should be invalidated.
_views_menu_reference_get_link_path_parents_hierarchy Private variant of views_menu_reference_get_link_path_parents_hierarchy(). Contains the true logic behind the caching.