taxonomy_menu_trails.inc in Taxonomy Menu Trails 7.2
Same filename and directory in other branches
Processing functions for taxonomy_menu_trails.
@author Dmitriy.trt <http://drupal.org/user/329125>
File
taxonomy_menu_trails.incView source
<?php
/**
* @file
* Processing functions for taxonomy_menu_trails.
*
* @author Dmitriy.trt <http://drupal.org/user/329125>
*/
/**
* Returns default taxonomy term path.
*
* @param int $tid
*
* @return string
*/
function _taxonomy_menu_trails_default_term_path($tid) {
return 'taxonomy/term/' . $tid;
}
/**
* Returns paths for specified term IDs.
*
* @param array $tids
* @param object $entity
* @param array $settings
*
* @return array
* Paths for passed tids.
*/
function _taxonomy_menu_trails_get_paths($tids, $entity, $settings) {
$paths = array();
switch ($settings['term_path']) {
case 'custom':
foreach ($tids as $tid) {
$term_paths = str_replace('[tid]', $tid, $settings['term_path_patterns']);
$paths = array_merge($paths, $term_paths);
}
break;
case 'api':
// We have to keep tids order for the final paths array.
// First fill paths array with "tid => array()" map.
$paths = array_combine($tids, array_fill(0, count($tids), array()));
// Call hook implementations and fill paths array for each term.
$hook = 'taxonomy_menu_trails_get_paths';
foreach (module_implements($hook) as $module) {
$function = $module . '_' . $hook;
if (function_exists($function)) {
// Use call_user_func() to force passing arguments by value, not by
// reference.
$results = call_user_func($function, $tids, $settings['entity_type'], $entity, $settings);
if (empty($results) || !is_array($results)) {
continue;
}
foreach ($results as $tid => $term_paths) {
if (empty($term_paths)) {
continue;
}
if (is_string($term_paths)) {
$paths[$tid][] = $term_paths;
}
elseif (is_array($term_paths)) {
$paths[$tid] = array_merge($paths[$tid], $term_paths);
}
}
}
}
// For terms with empty mappings fallback to default path. For the rest
// make sure paths are unique.
foreach ($paths as $tid => $term_paths) {
if (empty($term_paths)) {
$paths[$tid][] = _taxonomy_menu_trails_default_term_path($tid);
}
else {
$paths[$tid] = array_unique($paths[$tid]);
}
}
// Merge paths into flat array. The order of tids will be kept.
$paths = call_user_func_array('array_merge', $paths);
break;
case 'tm':
if (module_exists('taxonomy_menu')) {
/**
* @var string|null $function
*/
$function = NULL;
$candidate_functions = array(
// 7.x-1.x
'taxonomy_menu_create_path',
// 7.x-2.x
'taxonomy_menu_path_get',
);
foreach ($candidate_functions as $candidate) {
if (function_exists($candidate)) {
$function = $candidate;
}
}
if (isset($function)) {
// Unfortunately, there is no way to get tids-vids map without DB query.
// If you know one, please create issue and describe method.
$vids = db_select('taxonomy_term_data', 'ttd')
->fields('ttd', array(
'tid',
'vid',
))
->condition('ttd.tid', $tids, 'IN')
->execute()
->fetchAllKeyed();
foreach ($tids as $tid) {
if (isset($vids[$tid])) {
$paths[] = $function($vids[$tid], $tid);
}
else {
$paths[] = _taxonomy_menu_trails_default_term_path($tid);
}
}
break;
}
}
// Fallback to default path when Taxonomy Menu module not exists anymore
// or the function returning term path is not defined.
default:
foreach ($tids as $tid) {
$paths[] = _taxonomy_menu_trails_default_term_path($tid);
}
}
return $paths;
}
/**
* Set active trail to entitie's term selected by method specified in settings.
*
* @param object $entity
* @param array $settings
*/
function _taxonomy_menu_trails_process($entity, $settings) {
$instances = $settings['instances'];
$tids = array();
//TODO figure out: maybe we have to choose language in term reference fields
foreach ($instances as $field) {
if (!empty($entity->{$field})) {
foreach ($entity->{$field} as $lang => $items) {
foreach ($items as $item) {
$tids[] = isset($item['tid']) ? $item['tid'] : $item['target_id'];
}
}
}
}
// Filter out terms with different language if this option is enabled.
if (module_exists('i18n_taxonomy') && $settings['terms_with_current_language']) {
$terms = taxonomy_term_load_multiple($tids);
$allowed_languages = array(
'und',
$GLOBALS['language_content']->language,
);
foreach ($tids as $index => $tid) {
if (empty($terms[$tid]) || !in_array($terms[$tid]->language, $allowed_languages)) {
unset($tids[$index]);
}
}
// Exit in case no valid terms left.
if (empty($tids)) {
return;
}
}
if ($settings['selection_method'] == 'last') {
$tids = array_reverse($tids);
}
// Get paths for each tid.
$paths = _taxonomy_menu_trails_get_paths($tids, $entity, $settings);
// Retrieve the list of menu names sorted by preference. This list is used
// by menu system to get order of menus and not as a filter, we do the same.
// See menu_link_get_preferred().
$menu_names = menu_get_active_menu_names();
$query = db_select('menu_links', 'ml', array(
'fetch' => PDO::FETCH_ASSOC,
));
$query
->leftJoin('menu_router', 'm', 'm.path = ml.router_path');
$query
->fields('ml');
// Weight must be taken from {menu_links}, not {menu_router}.
$query
->addField('ml', 'weight', 'link_weight');
$query
->fields('m');
$query
->condition('ml.link_path', $paths, 'IN');
$query
->condition('ml.hidden', '0');
if ($settings['selection_method'] == 'deepest-in-menu') {
$query
->orderBy('ml.depth', 'DESC');
}
$query
->orderBy('ml.weight', 'ASC');
$results = $query
->execute();
$selected_items = array();
$vars = compact('menu_names', 'results', 'paths', 'selected_items');
$vars['trail_per_menu'] = $settings['trail_per_menu'];
switch ($settings['selection_method']) {
case 'first':
case 'last':
$selected_items = _taxonomy_menu_trails_fetch_link_simple($vars);
break;
case 'deepest-in-menu':
$selected_items = _taxonomy_menu_trails_fetch_link_deepest($vars);
break;
}
if (empty($selected_items)) {
return;
}
// Set selected menu item as an active path for its menu.
// Function menu_tree_set_path() available since Drupal 7.9, so we should
// check it exists.
if (function_exists('menu_tree_set_path')) {
foreach ($selected_items as $selected_item) {
menu_tree_set_path($selected_item['menu_name'], $selected_item['link_path']);
}
}
// Save selected item and "set_breadcrumb" setting for
// taxonomy_menu_trails_menu_breadcrumb_alter().
if (!empty($settings['set_breadcrumb'])) {
$selected_item = reset($selected_items);
$selected_item['set_breadcrumb'] = $settings['set_breadcrumb'];
taxonomy_menu_trails_selected_item_for_breadcrumb($selected_item);
}
// TODO: Remove workaround when Menu Block will remove its
// _menu_link_get_preferred(). But it's safe to leave it here even after
// Menu Block update to keep compatibility with old Menu Block releases.
// So, probably it should be here forever or until the next major release.
if (module_exists('menu_block')) {
// Also set preferred item for Menu Block module
$mb_preferred_links =& drupal_static('_menu_link_get_preferred');
foreach ($selected_items as $selected_item) {
$mb_preferred_links[$_GET['q']][$selected_item['menu_name']] = $selected_item;
}
}
}
/**
* Selects menu item using "first" or "last" selection method. There is no
* difference in these two methods because terms array is reversed in
* _taxonomy_menu_trails_process() if "last" is used.
*
* @param array $vars
* Variables map:
* - menu_names - active menu names
* - results - menu items fetching query results
* - paths - paths of terms, order is important
* @return array
* Selected menu item.
*/
function _taxonomy_menu_trails_fetch_link_simple($vars) {
extract($vars);
// Sort candidates by link path and menu name.
$candidates = array();
foreach ($results as $candidate) {
// Prefer the lightest of items with the same menu and path.
if (!isset($candidates[$candidate['link_path']][$candidate['menu_name']])) {
$candidate['weight'] = $candidate['link_weight'];
$candidates[$candidate['link_path']][$candidate['menu_name']] = $candidate;
// Add any menus not already in the menu name search list.
if (!in_array($candidate['menu_name'], $menu_names)) {
$menu_names[] = $candidate['menu_name'];
}
}
}
if (!empty($candidates)) {
// Pick the most specific link, in the most preferred menu.
foreach ($paths as $link_path) {
if (!isset($candidates[$link_path])) {
continue;
}
foreach ($menu_names as $menu_name) {
if (!isset($candidates[$link_path][$menu_name])) {
continue;
}
$candidate_item = $candidates[$link_path][$menu_name];
$map = explode('/', $link_path);
_menu_translate($candidate_item, $map);
if ($candidate_item['access']) {
if (!isset($selected_items[$menu_name])) {
$selected_items[$menu_name] = $candidate_item;
}
if (!$trail_per_menu) {
break 2;
}
}
}
}
}
return $selected_items;
}
/**
* Selects deepest menu item.
*
* @param array $vars
* @see _taxonomy_menu_trails_fetch_link_simple()
* @return array
* Selected menu item.
*/
function _taxonomy_menu_trails_fetch_link_deepest($vars) {
extract($vars);
$candidates = array();
foreach ($results as $candidate) {
// Prefer the lightest of items with the same menu and path.
if (!isset($candidates[$candidate['menu_name']][$candidate['link_path']])) {
$candidate['weight'] = $candidate['link_weight'];
$candidates[$candidate['menu_name']][$candidate['link_path']] = $candidate;
// Add any menus not already in the menu name search list.
if (!in_array($candidate['menu_name'], $menu_names)) {
$menu_names[] = $candidate['menu_name'];
}
}
}
if (!empty($candidates)) {
foreach ($menu_names as $menu_name) {
if (!empty($candidates[$menu_name])) {
foreach ($candidates[$menu_name] as $candidate_item) {
$map = explode('/', $candidate_item['link_path']);
_menu_translate($candidate_item, $map);
if ($candidate_item['access']) {
$selected_items[$menu_name] = $candidate_item;
if ($trail_per_menu) {
// Break only the loop of current menu candidates.
break;
}
else {
// Finish the search.
break 2;
}
}
}
}
}
}
return $selected_items;
}
/**
* Returns list of possible front page paths.
*
* @return array
*/
function _taxonomy_menu_trails_homepage_paths() {
return array(
'<front>',
variable_get('site_frontpage', 'node'),
);
}
/**
* Returns TRUE when passed breadcrumb is empty.
*
* "Empty" here means there are no items between "Home" (or breadcrumb start)
* and current tab root menu item.
*
* @param array $active_trail
*
* @return bool
*/
function _taxonomy_menu_trails_menu_breadcrumb_is_empty($active_trail) {
if (empty($active_trail)) {
return TRUE;
}
// Skip first item if it has homepage href.
$first_item = reset($active_trail);
$frontpage_hrefs = _taxonomy_menu_trails_homepage_paths();
if (in_array($first_item['href'], $frontpage_hrefs)) {
array_shift($active_trail);
}
// Look into the next item (if any) and check if it has current tab root href.
$next_item = reset($active_trail);
if (empty($next_item)) {
return TRUE;
}
$menu_item = menu_get_item();
if ($next_item['href'] == $menu_item['tab_root_href']) {
return TRUE;
}
return FALSE;
}
/**
* Injects selected item and its parents into the breadcrumb.
*
* @param array $active_trail
* @param array $selected_item
*
* @todo Simplify function code, probably menu_build_tree() is not necessary
* here and we could just load parents from {menu_links} and translate them.
*/
function _taxonomy_menu_trails_menu_breadcrumb_alter(&$active_trail, $selected_item) {
if ($selected_item['set_breadcrumb'] == 'if_empty' && !_taxonomy_menu_trails_menu_breadcrumb_is_empty($active_trail)) {
return;
}
// Iterate over menu item parents and collect mlids of current active trail.
$active_trail_mlids = array();
for ($i = 1; $i <= $selected_item['depth']; $i++) {
$parent = $selected_item["p{$i}"];
$active_trail_mlids[$parent] = $parent;
}
// Build menu tree containing only selected menu item itself and its parents.
$parameters = array(
'active_trail' => $active_trail_mlids,
'only_active_trail' => TRUE,
);
$tree = menu_build_tree($selected_item['menu_name'], $parameters);
// Flatten menu tree into injection array. This part was copied
// from menu_set_active_trail().
$injection = array();
list($key, $curr) = each($tree);
while ($curr) {
$link = $curr['link'];
if ($link['in_active_trail']) {
// Add the link to the trail, unless it links to its parent.
if (!($link['type'] & MENU_LINKS_TO_PARENT)) {
// The menu tree for the active trail may contain additional links
// that have not been translated yet, since they contain dynamic
// argument placeholders (%). Such links are not contained in regular
// menu trees, and have only been loaded for the additional
// translation that happens here, so as to be able to display them in
// the breadcumb for the current page.
// @see _menu_tree_check_access()
// @see _menu_link_translate()
if (strpos($link['href'], '%') !== FALSE) {
_menu_link_translate($link, TRUE);
}
if ($link['access']) {
$injection[] = $link;
}
}
$tree = $curr['below'] ? $curr['below'] : array();
}
list($key, $curr) = each($tree);
}
// Inject our active trail to the passed active trail right after the "Home"
// link (if any).
$trail_start = array();
if (!empty($active_trail)) {
$first_item = reset($active_trail);
$frontpage_hrefs = _taxonomy_menu_trails_homepage_paths();
if (in_array($first_item['href'], $frontpage_hrefs)) {
$trail_start[] = array_shift($active_trail);
}
}
$active_trail = array_merge($trail_start, $injection, $active_trail);
}
Functions
Name | Description |
---|---|
_taxonomy_menu_trails_default_term_path | Returns default taxonomy term path. |
_taxonomy_menu_trails_fetch_link_deepest | Selects deepest menu item. |
_taxonomy_menu_trails_fetch_link_simple | Selects menu item using "first" or "last" selection method. There is no difference in these two methods because terms array is reversed in _taxonomy_menu_trails_process() if "last" is used. |
_taxonomy_menu_trails_get_paths | Returns paths for specified term IDs. |
_taxonomy_menu_trails_homepage_paths | Returns list of possible front page paths. |
_taxonomy_menu_trails_menu_breadcrumb_alter | Injects selected item and its parents into the breadcrumb. |
_taxonomy_menu_trails_menu_breadcrumb_is_empty | Returns TRUE when passed breadcrumb is empty. |
_taxonomy_menu_trails_process | Set active trail to entitie's term selected by method specified in settings. |