views_menu_reference.module in Views Menu Reference 7
views_menu_reference module core implementations.
File
views_menu_reference.moduleView 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;
}