taxonomy_menu.module in Taxonomy menu 7
Same filename and directory in other branches
Adds links to taxonomy terms into a menu.
File
taxonomy_menu.moduleView source
<?php
/**
* @file
* Adds links to taxonomy terms into a menu.
*/
// Include the database layer.
module_load_include('inc', 'taxonomy_menu', 'taxonomy_menu.database');
// Include the batch functions.
module_load_include('inc', 'taxonomy_menu', 'taxonomy_menu.batch');
/**
* Implements hook_help().
*/
function taxonomy_menu_help($path, $arg) {
switch ($path) {
case 'admin/help#taxonomy_menu':
$output = '';
$output .= '<p>' . t('The Taxonomy Menu module transforms your taxonomy vocabularies into menus.') . '</p>';
$output .= '<p>' . t('For more information, please visit the official <a href="@url">project page</a> on Drupal.org.', array(
'@url' => 'https://www.drupal.org/project/taxonomy_menu',
)) . '</p>';
return $output;
}
}
/**
* Implements hook_form_alter().
*
* Modify the form at admin/content/taxonomy/edit/vocabulary/xx. We add
* our taxonomy_menu options in here on a per-vocab basis.
*/
function taxonomy_menu_form_alter(&$form, &$form_state, $form_id) {
if ($form_id == 'taxonomy_form_vocabulary') {
// Do not alter on deletion.
if (isset($form_state['confirm_delete']) && isset($form_state['values']['vid'])) {
return;
}
// Choose a menu to add link items to.
$menus = menu_get_menus();
array_unshift($menus, t('- Disabled -'));
// Options for path if tokens are not enabled.
$paths = _taxonomy_menu_get_paths();
$form['taxonomy_menu'] = array(
'#type' => 'fieldset',
'#collapsible' => TRUE,
'#title' => t('Taxonomy menu'),
'#weight' => 10,
'#tree' => TRUE,
);
// This turns the vocab terms into menu items.
$item['mlid'] = 0;
$menu_items = menu_parent_options(menu_get_menus(), $item);
array_unshift($menu_items, '= DISABLED =');
// The vid isn't set when a new vocabulary is being created.
if (isset($form['vid']['#value'])) {
$default = variable_get(_taxonomy_menu_build_variable('vocab_menu', $form['vid']['#value']), NULL) . ':' . variable_get(_taxonomy_menu_build_variable('vocab_parent', $form['vid']['#value']), NULL);
if (!isset($menu_items[$default])) {
$default = 0;
}
}
else {
$default = 0;
}
$form['taxonomy_menu']['vocab_parent'] = array(
'#type' => 'select',
'#title' => t('Menu location'),
'#default_value' => $default,
'#options' => $menu_items,
'#description' => t('Taxonomy menu items will be inserted below the item selected here.'),
'#attributes' => array(
'class' => array(
'menu-title-select',
),
),
);
$form['taxonomy_menu']['path'] = array(
'#type' => 'select',
'#title' => t('Menu path type'),
'#default_value' => isset($form['vid']['#value']) ? variable_get(_taxonomy_menu_build_variable('path', $form['vid']['#value']), 0) : 0,
'#options' => $paths,
'#description' => t('The path will be taxonomy/term/tid if <em>Default</em> has been selected.<br />The menu path will be passed through drupal_get_path_alias() function so all aliases will be applied.'),
);
// Get taxonomy menu form options.
if (isset($form['vid']) && $form['vid']['#value']) {
$vid = $form['vid']['#value'];
}
else {
$vid = 0;
}
$form['taxonomy_menu']['options'] = _taxonomy_menu_create_options($vid);
// Rebuild the menu.
$form['taxonomy_menu']['options']['rebuild'] = array(
'#type' => 'checkbox',
'#title' => t('Rebuild the menu on submit'),
'#default_value' => 0,
'#weight' => 20,
'#description' => t('<strong>Warning</strong>: This will delete then re-create all of the menu items. Only use this option if you are experiencing issues like missing menu items or other inconsistencies.'),
);
// Move the buttons to the bottom of the form.
$form['submit']['#weight'] = 49;
$form['delete']['#weight'] = 50;
// Add an extra submit handler to save these settings.
$form['#submit'][] = 'taxonomy_menu_vocab_submit';
}
elseif ($form_id == "taxonomy_overview_terms") {
// Add an extra submit handler to sync the rearranged terms with menu.
// @ TODO: using hook_taxonomy_vocabulary_update is nicer then callback,
// But gives less info and does not always fire.
$form['#submit'][] = 'taxonomy_menu_overview_submit';
}
}
/**
* Submit handler for the extra settings added to the taxonomy vocab form.
*
* Check to see if the user has selected a different menu, and only rebuild
* if this is the case.
*/
function taxonomy_menu_vocab_submit($form, &$form_state) {
$vid = $form_state['values']['vid'];
$changed = FALSE;
if (is_numeric($form_state['values']['taxonomy_menu']['vocab_parent'])) {
// Menu location has been set to disabled, don't want to throw notices.
$form_state['values']['taxonomy_menu']['vocab_parent'] = '0:0';
}
// Split the menu location into menu name and menu item id.
list($vocab_parent['vocab_menu'], $vocab_parent['vocab_parent']) = explode(':', $form_state['values']['taxonomy_menu']['vocab_parent']);
// Init flag variables to avoid notices if changes haven't happened.
$changed_menu = FALSE;
$change_vocab_item = FALSE;
$changed_path = FALSE;
// Set the menu name and check for changes.
$variable_name = _taxonomy_menu_build_variable('vocab_menu', $vid);
if (_taxonomy_menu_check_variable($variable_name, $vocab_parent['vocab_menu'])) {
$changed_menu = TRUE;
}
variable_set($variable_name, $vocab_parent['vocab_menu']);
// Set the menu parent item and check for changes.
$variable_name = _taxonomy_menu_build_variable('vocab_parent', $vid);
if (_taxonomy_menu_check_variable($variable_name, $vocab_parent['vocab_parent'])) {
$changed_menu = TRUE;
}
variable_set($variable_name, $vocab_parent['vocab_parent']);
// Set the path and check for changes.
$variable_name = _taxonomy_menu_build_variable('path', $vid);
if (_taxonomy_menu_check_variable($variable_name, $form_state['values']['taxonomy_menu']['path'])) {
$changed_path = TRUE;
}
variable_set($variable_name, $form_state['values']['taxonomy_menu']['path']);
foreach ($form_state['values']['taxonomy_menu']['options'] as $key => $value) {
// Create the variable name
$variable_name = _taxonomy_menu_build_variable($key, $vid);
// Check to see if the vocab enable options has changed.
if ($key == 'voc_item') {
if (_taxonomy_menu_check_variable($variable_name, $value)) {
$change_vocab_item = TRUE;
}
}
// If $changed is already set to true, then don't bother checking any others.
if (!$changed) {
// Check to see if the variable has changed.
if (_taxonomy_menu_check_variable($variable_name, $value)) {
$changed = TRUE;
}
}
// Save variable.
variable_set($variable_name, $value);
}
// If the menu hasn't changed and the menu is disabled then do not do anything else.
if ($form_state['values']['taxonomy_menu']['options']['rebuild'] || $changed_menu || !$changed_menu && variable_get(_taxonomy_menu_build_variable('vocab_menu', $vid), FALSE) == 0) {
// Rebuild if rebuild is selected, menu has changed or vocabulary option changed.
if ($form_state['values']['taxonomy_menu']['options']['rebuild'] || $changed_menu || $change_vocab_item || $changed_path) {
$message = _taxonomy_menu_rebuild($vid);
}
elseif ($changed && variable_get(_taxonomy_menu_build_variable('vocab_menu', $vid), FALSE)) {
$message = _taxonomy_menu_update_link_items($vid);
}
// Do a full menu rebuild in case we have removed the menu or moved it between menus.
variable_set('menu_rebuild_needed', TRUE);
// Only send a message if one has been created.
if (isset($message) && $message) {
// $message is sanitized coming out of its source function,
// No need to reclean it here.
drupal_set_message($message, 'status');
}
}
}
/**
* Submit handler, reacting on form ID: taxonomy_overview_terms.
*/
function taxonomy_menu_overview_submit(&$form, &$form_state) {
// Only sync if taxonomy_menu is enabled for this vocab and the 'sync'
// option has been checked.
// This form has the following flow of buttons:
// 1. [Save] --> rebuild taxonomy_menu
// 2. [Reset to alphabetical] --> no rebuild yet
// 3. [Reset to alphabetical][Reset to alphabetical] --> rebuild
// 4. [Reset to alphabetical][Cancel] --> no rebuild
// The code below avoids rebuilding after situation 2.
if ($form_state['rebuild'] == FALSE && isset($form['#vocabulary']->vid)) {
// Try to catch the 'Save' button.
$vid = $form['#vocabulary']->vid;
}
elseif ($form_state['rebuild'] == TRUE && isset($form['#vocabulary']->vid)) {
// Try to catch the 'Reset to alphabetical' button.
$vid = NULL;
}
elseif ($form_state['rebuild'] == FALSE && isset($form['vid']['#value'])) {
// Try to catch the second (confirming) 'Reset to alphabetical' button.
$vid = $form['vid']['#value'];
}
else {
// The button [Reset to alphabetical] [Cancel] does not call this page.
$vid = NULL;
}
if (isset($vid)) {
$menu_name = variable_get(_taxonomy_menu_build_variable('vocab_menu', $vid), 0);
$sync = variable_get(_taxonomy_menu_build_variable('sync', $vid), 0);
if ($menu_name && $sync) {
// Update all menu items (do not rebuild the menu).
$message = _taxonomy_menu_update_link_items($vid);
// Report status.
if (isset($message)) {
// Message is sanitized coming out of _taxonomy_menu_update_link_items
// no need to reclean it here.
drupal_set_message($message, 'status');
}
// Rebuild the menu.
menu_cache_clear($menu_name);
}
}
}
/**
* Rebuilds a menu.
*
* @param int $vid
* Vocabulary ID.
*
* @return string
* Message that is displayed.
*/
function _taxonomy_menu_rebuild($vid) {
// Remove all of the menu items for this vocabulary.
_taxonomy_menu_delete_all($vid);
// Only insert the links if a menu is set.
if (variable_get(_taxonomy_menu_build_variable('vocab_menu', $vid), FALSE)) {
_taxonomy_menu_insert_link_items($vid);
menu_rebuild();
return t('The Taxonomy Menu has been rebuilt.');
}
menu_rebuild();
return t('The Taxonomy Menu has been removed.');
}
/**
* Checks to see if the variable has changed.
*
* @param string $variable
* Name of variable.
*
* @return bool
* TRUE if it has changed
*/
function _taxonomy_menu_check_variable($variable, $new_value) {
if ($new_value != variable_get($variable, FALSE)) {
return TRUE;
}
return FALSE;
}
/**
* Updates the menu items.
*
* @param $vid
* Vocabulary ID.
*/
function _taxonomy_menu_update_link_items($vid) {
$menu_name = variable_get(_taxonomy_menu_build_variable('vocab_menu', $vid), FALSE);
$depth = variable_get(_taxonomy_menu_build_variable('max_depth', $vid), 0);
// Get a list of the current tid - menu_link combinations.
$menu_links = _taxonomy_menu_get_menu_items($vid);
// Cycle through the menu links.
foreach ($menu_links as $tid => $mlid) {
if (!_taxonomy_menu_term_too_deep($tid, $depth)) {
// $args must be reset each time through.
$args = array(
'menu_name' => $menu_name,
'mlid' => $mlid,
);
}
if ($tid == 0) {
$args['vid'] = $vid;
}
else {
$args['term'] = taxonomy_term_load($tid);
}
// Update the menu link.
taxonomy_menu_handler('update', $args);
}
return t('The Taxonomy Menu %menu_name has been updated.', array(
'%menu_name' => $menu_name,
));
}
/**
* Creates new link items for the vocabulary.
*
* @param $vid
* Vocabulary ID.
*/
function _taxonomy_menu_insert_link_items($vid) {
$menu_name = variable_get(_taxonomy_menu_build_variable('vocab_menu', $vid), FALSE);
// Check to see if we should had a vocab item.
if (variable_get(_taxonomy_menu_build_variable('voc_item', $vid), FALSE)) {
$args = array(
'vid' => $vid,
'menu_name' => $menu_name,
);
taxonomy_menu_handler('insert', $args);
}
// Let batch api take care of inserting the menu items.
_taxonomy_menu_insert_link_items_batch($vid);
}
/**
* Implements hook_taxonomy_vocabulary_delete().
*/
function taxonomy_menu_taxonomy_vocabulary_delete($vocabulary) {
// Delete the menu items for this vocab.
_taxonomy_menu_delete_all($vocabulary->vid);
$menu_name = variable_get(_taxonomy_menu_build_variable('vocab_menu', $vocabulary->vid), 0);
menu_cache_clear($menu_name);
// Delete all the variables for this vocab.
$variable_prefixes = array(
'taxonomy_menu_vocab_menu_',
'taxonomy_menu_vocab_parent_',
'taxonomy_menu_voc_item_',
'taxonomy_menu_display_num_',
'taxonomy_menu_hide_empty_terms_',
'taxonomy_menu_expanded_',
'taxonomy_menu_rebuild_',
'taxonomy_menu_path_',
'taxonomy_menu_menu_end_all_',
'taxonomy_menu_display_descendants_',
'taxonomy_menu_voc_name_',
'taxonomy_menu_sync_',
);
foreach ($variable_prefixes as $prefix) {
variable_del($prefix . $vocabulary->name);
}
}
/**
* Implements hook_taxonomy_term_insert($term).
*/
function taxonomy_menu_taxonomy_term_insert($term) {
_taxonomy_menu_taxonomy_termapi_helper($term, 'insert');
}
/**
* Implements hook_taxonomy_term_update().
*/
function taxonomy_menu_taxonomy_term_update($term) {
_taxonomy_menu_taxonomy_termapi_helper($term, 'update');
}
/**
* Implements hook_taxonomy_term_delete().
*/
function taxonomy_menu_taxonomy_term_delete($term) {
_taxonomy_menu_taxonomy_termapi_helper($term, 'delete');
}
/**
* Implements hook_node_insert().
*/
function taxonomy_menu_node_insert($node) {
$terms_old =& drupal_static('taxonomy_menu_terms_old');
// We use this direct table pull to avoid the cache and because
// free tags are not formatted in a matter where extrating the
// tid's is easy.
$terms_new = _taxonomy_menu_get_node_terms($node);
// Merge current terms and previous terms to update both menu items.
$terms = array_unique(array_merge((array) $terms_new, (array) $terms_old));
_taxonomy_menu_nodeapi_helper('insert', $terms, $node);
}
/**
* Implements hook_node_update().
*/
function taxonomy_menu_node_update($node) {
if (isset($node->original->status) and $node->original->status != $node->status) {
$terms_old =& drupal_static('taxonomy_menu_terms_old');
// We use this direct table pull to avoid the cache and because
// free tags are not formatted in a matter where extracting the
// tid's is easy.
$terms_new = _taxonomy_menu_get_node_terms($node);
// Merge current terms and previous terms to update both menu items.
$terms = array_unique(array_merge((array) $terms_new, (array) $terms_old));
_taxonomy_menu_nodeapi_helper('update', $terms, $node);
}
}
/**
* Implements hook_node_presave().
*/
function taxonomy_menu_node_presave($node) {
$terms_old =& drupal_static('taxonomy_menu_terms_old');
// Get the terms from the database before the changes are made. These will be
// used to update the menu item's name if needed we go directly to the db to
// bypass any caches.
if (isset($node->nid)) {
$node_old = node_load($node->nid);
$terms_old = _taxonomy_menu_get_node_terms($node_old);
}
else {
$terms_old = array();
}
}
/**
* Implements hook_node_delete().
*/
function taxonomy_menu_node_delete($node) {
// Since the delete operation is run after the data is deleted pull the terms
// from the node object.
$terms = _taxonomy_menu_get_node_terms($node);
_taxonomy_menu_nodeapi_helper('delete', $terms, $node);
}
/**
* Abstraction of hook_taxonomy_term_<operation>()
*/
function _taxonomy_menu_taxonomy_termapi_helper($term, $operation) {
// Only sync if taxonomy_menu is enabled for this vocab and the 'sync'
// option has been checked.
$menu_name = variable_get(_taxonomy_menu_build_variable('vocab_menu', $term->vid), 0);
$sync = variable_get(_taxonomy_menu_build_variable('sync', $term->vid), 0);
$depth = variable_get(_taxonomy_menu_build_variable('max_depth', $term->vid), 0);
if ($menu_name && $sync && !_taxonomy_menu_term_too_deep($term->tid, $depth)) {
$item = array(
'tid' => $term->tid,
'vid' => $term->vid,
'term' => $term,
'menu_name' => $menu_name,
'mlid' => _taxonomy_menu_get_mlid($term->tid, $term->vid),
);
switch ($operation) {
case 'insert':
$text = 'Added term %term to taxonomy menu %menu_name.';
break;
case 'update':
$text = 'Updated term %term in taxonomy menu %menu_name.';
break;
case 'delete':
$text = 'Deleted term %term from taxonomy menu %menu_name.';
break;
}
$message = t($text, array(
'%term' => $term->name,
'%menu_name' => $menu_name,
));
// Run function.
taxonomy_menu_handler($operation, $item);
// Report status.
drupal_set_message($message, 'status');
// Rebuild the menu.
menu_cache_clear($menu_name);
}
}
/**
* Builds argument arrays calls taxonomy_menu_handler.
*
* @param string $op
* The operation to be performed [update|insert|delete]
* @param array $terms
* The taxonomy terms.
* @param $node
* The node object.
*/
function _taxonomy_menu_nodeapi_helper($op, $terms = array(), $node) {
foreach ($terms as $key => $tid) {
// If taxonomy $term is false, then go to the next $term.
// taxonomy_term_load($tid) returns FALSE if the term was not found.
if (!($term = taxonomy_term_load($tid))) {
continue;
}
// Update the menu for each term if necessary.
$menu_name = variable_get(_taxonomy_menu_build_variable('vocab_menu', $term->vid), FALSE);
$vocb_sync = variable_get(_taxonomy_menu_build_variable('sync', $term->vid), TRUE);
$menu_num = variable_get(_taxonomy_menu_build_variable('display_num', $term->vid), TRUE);
$menu_num_inc_children = variable_get(_taxonomy_menu_build_variable('display_num_incl_children', $term->vid), TRUE);
$hide_empty = variable_get(_taxonomy_menu_build_variable('hide_empty_terms', $term->vid), FALSE);
if ($menu_name && $vocb_sync && ($menu_num || $hide_empty)) {
// Build argument array to save menu_item.
$args = array(
'tid' => $term->tid,
'vid' => $term->vid,
'term' => $term,
'menu_name' => $menu_name,
'mlid' => _taxonomy_menu_get_mlid($term->tid, $term->vid),
);
if ($op == 'delete') {
/* Turn the op to 'update' here since we really do want to update the item
* and not delete/recreate it, since the latter will break hierarchy and
* customizations.
*/
$op = 'update';
}
taxonomy_menu_handler($op, $args, $node);
if ($hide_empty || $menu_num && $menu_num_inc_children) {
_taxonomy_menu_update_all_parents($term, $menu_name);
}
}
}
}
/**
* Updates all parent items.
*
* @param string $op
* options are 'insert', 'update', 'delete' or path.
* @param $node
* The node object.
*/
function _taxonomy_menu_update_all_parents($term, $menu_name) {
$parents = taxonomy_get_parents($term->tid);
if ($parents) {
foreach ($parents as $parent) {
$parent->parents = array_keys(taxonomy_get_parents($parent->tid));
$item = array(
'term' => $parent,
'menu_name' => $menu_name,
'mlid' => _taxonomy_menu_get_mlid($parent->tid, $parent->vid),
'remove' => FALSE,
);
taxonomy_menu_handler('update', $item);
_taxonomy_menu_update_all_parents($parent, $menu_name);
}
}
}
/**
* Taxonomy Menu Handler: Creates a menu item for each taxonomy term.
*
* @param $op
* options are 'insert', 'update', 'delete' or path.
* @param $args
* if $op == 'insert' then args is an array with the following key/value pairs:
* 'term': taxonomy term object,
* 'menu_name' : menu that the item is set to apply to
* if $op == 'delete' then then args is an array with the following key/value pairs:
* 'tid': TermID
* 'mlid': Menu ID
* if $op == 'update' then then args is an array with the following key/value pairs:
* 'term': term object,
* 'menu_name': menu that the item is set to apply to
* 'mlid': Menu ID
* @param $node
* The node object.
* @param $item array
* Taxonomy menu item.
*
* @return array
* Menu link ID for the taxonomy menu item.
*/
function taxonomy_menu_handler($op, $args = array(), $node = NULL, $item = array()) {
// Get the initial $item.
if (empty($item)) {
$item = _taxonomy_menu_create_item($args, $node);
}
// Let other modules make edits.
$hook = 'taxonomy_menu_' . $op;
foreach (module_implements($hook) as $module) {
$function = $module . '_' . $hook;
$function($item);
}
// Update the menu and return the mlid if the remove element is not true.
if ($op != 'delete') {
return _taxonomy_menu_save($item);
}
}
/**
* Adds/Updates a taxonomy menu item.
*
* We use a custom data array $item as a parameter, instead of using a
* standard taxonomy $term object. This is because this function is also
* called from hook_taxonomy(), which doesn't have a $term object. Rather
* have one consistent method of passing the data.
*
* @param $item
* array with the following key/value pairs:
* 'tid' => the term id (if 0 then adding the vocab as an item)
* 'name' => the term's name
* 'description' => term description, used as to build the title attribute
* 'weight' => term weight
* (This will be overriden by the order created from taxonomy_get_tree which respects the correct wight)
* 'vid' => the vocabulary's id
* 'ptid' => the term's parent's term id
* 'menu_name' => the menu that the link item will be inserted into
* 'mlid' => if this is filled in then the mlid will be updated
*/
function _taxonomy_menu_save($item) {
if (empty($item)) {
return;
}
$insert = TRUE;
$flatten_menu = variable_get(_taxonomy_menu_build_variable('flat', $item['vid']));
// Child items should appear around the parent/root, so set their weight
// equal to the root term's weight.
if ($flatten_menu) {
$item['weight'] = $item['root_term_weight'];
}
// create the path.
$path = taxonomy_menu_create_path($item['vid'], $item['tid']);
// Get the parent mlid: this is either:
// - the parent tid's mlid
// - the vocab menu item's mlid
// - the menu parent setting for this vocab
$plid = _taxonomy_menu_get_mlid($item['ptid'], $item['vid']);
if (!$plid || $flatten_menu) {
$plid = variable_get(_taxonomy_menu_build_variable('vocab_parent', $item['vid']), NULL);
}
// Make sure the path has less then 256 characters.
if (drupal_strlen($path) > 256) {
preg_match('/(.{256}.*?)\\b/', $path, $matches);
$path = rtrim($matches[1]);
}
$link = array(
'link_title' => $item['name'],
'menu_name' => $item['menu_name'],
'plid' => $plid,
'weight' => $item['weight'],
'module' => 'taxonomy_menu',
'expanded' => variable_get(_taxonomy_menu_build_variable('expanded', $item['vid']), TRUE),
'link_path' => $path,
);
// Be sure to load the original menu link to preserve non-standard properties.
if (isset($item['mlid']) && !empty($item['mlid']) && ($original_link = menu_link_load($item['mlid']))) {
$link = array_merge($original_link, $link);
}
else {
$link['options'] = array(
'attributes' => array(
'title' => trim($item['description']) ? $item['description'] : $item['name'],
),
);
}
// Add setup the query paramater in the URL correctly.
if (strpos($path, '?') !== FALSE) {
$split = explode('?', $path);
if (strpos($split[1], '?') !== FALSE) {
// the query split didn't work, too many question marks
// error?
}
else {
parse_str($split[1], $link['options']['query']);
$link['link_path'] = $split[0];
}
}
// If passed a mlid then add it.
if (isset($item['mlid']) && $item['mlid']) {
$link['mlid'] = $item['mlid'];
$insert = FALSE;
$menu_link = menu_link_load($item['mlid']);
$is_hidden = !empty($menu_link['hidden']) ? $menu_link['hidden'] : 0;
}
else {
$is_hidden = 0;
}
// @todo i18nmenu needs to be cleaned up to allow translation from other menu modules.
if (module_exists('i18n_menu')) {
$link['options']['alter'] = TRUE;
$link['language'] = $item['language'];
$link['customized'] = 1;
}
// Set the has_children property.
// If tid=0 then adding a vocab item and had children.
// If the term has any children then set it to true.
if ($item['tid'] == 0) {
$link['has_children'] = 1;
}
else {
$children = taxonomy_get_children($item['tid']);
if (!empty($children)) {
$link['has_children'] = 1;
}
}
// If remove is true then set hidden to 1.
$link['hidden'] = isset($item['remove']) && $item['remove'] ? 1 : $is_hidden;
// Save the menu item.
if ($mlid = menu_link_save($link)) {
// if inserting a new menu item then insert a record into the table.
if ($insert) {
_taxonomy_menu_insert_menu_item($mlid, $item['tid'], $item['vid']);
}
return $mlid;
}
else {
drupal_set_message(t('Could not save the menu link for the taxonomy menu.'), 'error');
return FALSE;
}
}
/**
* Create the path to use in the menu item.
*
* @return array
* Path selections.
*/
function _taxonomy_menu_get_paths() {
return module_invoke_all('taxonomy_menu_path');
}
/**
* Creates the path for the vid/tid combination.
*
* @param $vid
* Vocablary ID.
* @param $tid
* Taxonomy term ID.
*
* @return string
* Path
*/
function taxonomy_menu_create_path($vid, $tid) {
// Get the path function for this vocabulary.
$function = variable_get(_taxonomy_menu_build_variable('path', $vid), 'taxonomy_menu_path_default');
// Run the function.
return $function($vid, $tid);
}
/**
* Implements hook_taxonomy_menu_path().
*
* @return array
* A list of the path options in the form: function_name => Display Title.
*
* @see _taxonomy_menu_get_paths().
*/
function taxonomy_menu_taxonomy_menu_path() {
$output = array(
'taxonomy_menu_path_default' => t('Default'),
);
return $output;
}
/**
* Callback for hook_taxonomy_menu_path.
*/
function taxonomy_menu_path_default($vid, $tid) {
// If tid = 0 then we are creating the vocab menu item format will be taxonomy/term/$tid+$tid+$tid...
if ($tid == 0) {
// Get all of the terms for the vocab.
$vtids = _taxonomy_menu_get_terms($vid);
$end = implode('/', $vtids);
$path = "taxonomy/term/{$end}";
}
else {
$path = 'taxonomy/term/' . $tid;
if (variable_get(_taxonomy_menu_build_variable('display_descendants', $vid), FALSE)) {
// Use 'all' at the end of the path.
if (variable_get(_taxonomy_menu_build_variable('end_all', $vid), FALSE)) {
$path .= '/all';
}
else {
// We wait to run this instead of during the if above because we only
// want to run it once.
$terms = taxonomy_get_tree($vid, $tid);
foreach ($terms as $term) {
$tids[] = $term->tid;
}
if (isset($tids)) {
$end = implode('/', $tids);
$path .= '/ ' . $end;
}
}
}
}
return $path;
}
/**
* Implements hook_taxonomy_menu_delete().
*
* @param array $item
* Taxonomy menu item array, containing the following key/value pairs:
* 'vid': Vocabulary ID.
* 'tid': Taxonomy term ID.
* 'mlid': Menu link ID.
*/
function taxonomy_menu_taxonomy_menu_delete(&$item) {
menu_link_delete($item['mlid']);
_taxonomy_menu_delete_item($item['vid'], $item['tid']);
unset($item['mlid']);
}
/**
* Creates the initial $item array.
*
* @param $args
* array with the following key/value pairs:
* 'term': Taxonomy term object, if updating a term.
* 'menu_name': menu that the item is set to apply to.
* 'vid': Vocabuary id, if editing vocab item.
* 'mlid': Menu link id.
*
* @param $node
* The node object.
*/
function _taxonomy_menu_create_item($args = array(), $node) {
// If tid = 0, then we are creating a vocab item.
if (isset($args['tid']) && isset($args['vid']) && $args['tid'] == 0 && variable_get(_taxonomy_menu_build_variable('voc_item', $args['vid']), 0)) {
$vocab = taxonomy_vocabulary_load($args['vid']);
$item = array(
'tid' => 0,
'name' => $vocab->name,
'description' => variable_get(_taxonomy_menu_build_variable('voc_item_description', $args['vid']), 0) ? $vocab->description : '',
'weight' => $vocab->weight,
'vid' => $args['vid'],
'ptid' => 0,
'root_term_weight' => $vocab->weight,
'menu_name' => $args['menu_name'],
'language' => $vocab->language,
);
}
else {
// If tid <> 0 then we are creating a term item.
$term = $args['term'];
// Sometimes $term->parents is not set so we find it.
if (empty($term->parents)) {
$term->parents = _taxonomy_menu_get_parents($term->tid);
if (empty($term->parents)) {
// Even without parents, create one with $ptid = 0.
$term->parents = array(
0 => '0',
);
}
}
// Find the weight of the root taxonomy term; we'll need it in case we want
// a flat taxonomy menu.
if (is_object($term)) {
$term_parents = taxonomy_get_parents_all($term->tid);
$root_term_weight = $term_parents ? $term_parents[count($term_parents) - 1]->weight : 0;
}
else {
$root_term_weight = 0;
}
foreach ($term->parents as $parent) {
$ptid = $parent;
// Turn the term into the correct $item array form.
$item = array(
'term' => $term,
'tid' => $term->tid,
'name' => $term->name,
'description' => variable_get(_taxonomy_menu_build_variable('term_item_description', $term->vid), 0) ? $term->description : '',
'weight' => !empty($term->weight) ? $term->weight : 0,
'vid' => $term->vid,
'ptid' => $ptid,
'root_term_weight' => $root_term_weight,
'menu_name' => $args['menu_name'],
'language' => isset($term->language) ? $term->language : ($node ? $node->language : $GLOBALS['language']->language),
);
if (isset($args['mlid'])) {
$item['mlid'] = $args['mlid'];
}
// Mutiple parents are not supported yet. Without the break, the item is
// inserted multiple under one parent, instead of once under each parent.
break;
}
}
return $item;
}
/**
* Helper function: See if any of the children have any nodes.
*
* @param $tid
* Taxonomy term ID.
* @param $vid
* Vocabulary ID.
*
* @return boolean
*/
function _taxonomy_menu_children_has_nodes($tid, $vid, $return = FALSE) {
$children = taxonomy_get_children($tid, $vid);
foreach ($children as $tid => $term) {
if (_taxonomy_menu_term_count($tid) > 0) {
$return = TRUE;
}
else {
$return = _taxonomy_menu_children_has_nodes($tid, $vid, $return);
}
}
return $return;
}
/**
* Helper function: Inserts and updates menu along with taxonomy changes.
*
* @param array $item
* Taxonomy menu item.
*
* @return array
* Taxonomy menu item.
*/
function _taxonomy_menu_item($item) {
if (empty($item)) {
return;
}
// If tid is 0 then do not change any settings.
if ($item['tid'] > 0) {
// Get the number of node attached to this term.
$num = _taxonomy_menu_term_count($item['tid']);
// If hide menu is selected and the term count is 0 and the term has no
// children then do not create the menu item.
if ($num == 0 && variable_get(_taxonomy_menu_build_variable('hide_empty_terms', $item['vid']), FALSE) && !_taxonomy_menu_children_has_nodes($item['tid'], $item['vid'])) {
$item['remove'] = TRUE;
return $item;
}
// If display number is selected and $num > 0 then change the title.
if (variable_get(_taxonomy_menu_build_variable('display_num', $item['vid']), FALSE)) {
// If number > 0 and display include children num, then count all of the children.
if (variable_get(_taxonomy_menu_build_variable('display_num_incl_children', $item['vid']), TRUE)) {
$num = taxonomy_menu_term_count_nodes($item['tid'], $item['vid']);
}
$item['name'] .= " ({$num})";
}
}
elseif ($item['tid'] == 0) {
// If custom name is provided, use that name.
$custom_name = variable_get(_taxonomy_menu_build_variable('voc_name', $item['vid']), '');
if (!empty($custom_name)) {
$item['name'] = $custom_name;
}
}
return $item;
}
/**
* Calculates the number of nodes linked to the term and all children.
*
* @param int $tid
* Taxonomy term ID.
* @param int $vid
* Vocabulary ID.
*
* @return int
* Count for how many nodes are in that taxonomy category.
*/
function taxonomy_menu_term_count_nodes($tid, $vid, $count = 0) {
$count += _taxonomy_menu_term_count($tid);
$children = taxonomy_get_children($tid, $vid);
foreach ($children as $tid => $term) {
$count = taxonomy_menu_term_count_nodes($term->tid, $term->vid, $count);
}
return $count;
}
/**
* Implements hook_taxonomy_menu_insert().
*
* @param array $item
* Taxonomy menu item array with the following key/value pairs:
* 'tid': The term id (if 0 then updating the vocab as an item).
* 'name': New menu name.
* 'description': New menu description, used for the title attribute.
* 'weight': New menu weight.
* 'vid': The new vocabulary's id.
* 'ptid': The new parent tid.
* 'remove': If this is set to TRUE then the $item is not added as a menu.
*
* @return array $item
* Taxonomy menu item.
*/
function taxonomy_menu_taxonomy_menu_insert(&$item) {
$item = _taxonomy_menu_item($item);
}
/**
* Implements hook_taxonomy_menu_update().
*
* @param array $item
* Taxonomy menu item array with the following key/value pairs:
* 'tid': The term id (if 0 then updating the vocab as an item).
* 'name': New menu name.
* 'description': New menu description, used for the title attribute.
* 'weight': New menu weight.
* 'vid': The new vocabulary's id.
* 'ptid': The new parent tid.
* 'remove': If this is set to TRUE then the $item is not added as a menu.
*/
function taxonomy_menu_taxonomy_menu_update(&$item) {
$item = _taxonomy_menu_item($item);
}
/**
*Creates a form array of taxonomy menu options.
* Invokes hook_taxonomy_menu_options().
*
* @param $vid
* Vocabulary ID.
*
* @return array
* Form array.
*/
function _taxonomy_menu_create_options($vid) {
$options = module_invoke_all('taxonomy_menu_options');
// Cycle through field.
foreach ($options as $field_name => $field_elements) {
// Cycle through each value of the field.
$variable_name = _taxonomy_menu_build_variable($field_name, $vid);
// If the variable is set then use that, if the default key is set then use that,
// otherwise use FALSE.
$options[$field_name]['#default_value'] = variable_get($variable_name, !empty($options[$field_name]['default']) ? $options[$field_name]['default'] : FALSE);
// Set the type to checkbox if it is empty.
if (empty($options[$field_name]['#type'])) {
$options[$field_name]['#type'] = 'checkbox';
}
// Set the options.
$options['#type'] = 'container';
// Remove the default value from the array so we don't pass it to the form.
unset($options[$field_name]['default']);
}
return $options;
}
/**
* Builds a variable from the supplied name and machine name of the vocabulary.
*
* @param string $name
* Name to be added to the returned variable.
* @param $vid
* VID of the vocabulary from which the machine name will be taken.
*
* @return bool|string
*/
function _taxonomy_menu_build_variable($name, $vid) {
$vocabulary = taxonomy_vocabulary_load($vid);
if ($vocabulary) {
return 'taxonomy_menu_' . $name . '_' . $vocabulary->machine_name;
}
else {
return FALSE;
}
}
/**
* Implements hook_taxonomy_menu_options().
*/
function taxonomy_menu_taxonomy_menu_options() {
$options['sync'] = array(
'#title' => t('Synchronise changes to this vocabulary'),
'#description' => t('Every time a term is added/deleted/modified, the corresponding menu link will be altered too.'),
'default' => TRUE,
);
$options['max_depth'] = array(
'#type' => 'select',
'#title' => t('Max depth'),
'#description' => t('Limit how many levels of the taxonomy tree to process. Useful if you have a very large tree of taxonomy terms, and only want to provide a menu for the first several levels.'),
'#options' => array(
0 => t('All'),
1 => 1,
2 => 2,
3 => 3,
4 => 4,
5 => 5,
6 => 6,
7 => 7,
8 => 8,
9 => 9,
),
'default' => 0,
);
$options['display_num'] = array(
'#title' => t('Display the number of items in each taxonomy term.'),
'default' => FALSE,
);
$options['display_num_incl_children'] = array(
'#title' => t('Include items belonging to child terms in number counts'),
'#description' => t('Items belonging to the child terms will be counted when performing item count calculations.'),
'#states' => array(
'visible' => array(
':input[name="taxonomy_menu[options][display_num]"]' => array(
'checked' => TRUE,
),
),
),
'default' => TRUE,
);
$options['hide_empty_terms'] = array(
'#title' => t('Do not add a menu link for taxonomy terms with no items.'),
'default' => FALSE,
);
$options['voc_item'] = array(
'#title' => t('Include the vocabulary name as the top level menu item of the taxonomy menu.'),
'default' => FALSE,
'#disabled' => TRUE,
);
$options['voc_item_description'] = array(
'#title' => t('Add the vocabulary description to the vocabulary menu item.'),
'default' => FALSE,
'#access' => FALSE,
);
$options['term_item_description'] = array(
'#title' => t('Add the taxonomy term description to the term menu item.'),
'default' => FALSE,
);
$options['expanded'] = array(
'#title' => t('Automatically show all menu items as expanded.'),
'default' => TRUE,
);
$options['flat'] = array(
'#title' => t('Add all menu items to the same level rather than retaining term hierarchy.'),
'default' => FALSE,
);
$options['voc_name'] = array(
'#type' => 'textfield',
'#title' => t('Custom name for vocabulary item'),
'#description' => t('Changes the name of the vocabulary item (if enabled above). Leave blank to use the name of the vocabulary.'),
'default' => '',
'#disabled' => TRUE,
);
$options['display_descendants'] = array(
'#title' => t('Display descendants: change the path to taxonomy/term/tid+tid+tid for all terms thave have child terms.'),
'default' => FALSE,
);
$options['end_all'] = array(
'#title' => t("Use 'all' at the end of URL"),
'default' => FALSE,
'#description' => t('This changes tid+tid+tid to "All" in term when <em>Display descendants</em> has been selected.<br />Only used if <em>Menu path type</em> is "Default path".<br />Works with default taxonomy page.'),
'#disabled' => TRUE,
);
return $options;
}
/**
* Implements hook_translated_menu_link_alter().
*
* Translate menu links on the fly by using term translations.
*/
function taxonomy_menu_translated_menu_link_alter(&$item, $map) {
if (module_exists('i18n_taxonomy')) {
// In case of localized terms, use term translation for menu title.
if ($item['module'] == 'taxonomy_menu') {
$t = _taxonomy_menu_get_item($item['mlid']);
// Only translate when term exist (may occur with stray menu item).
if ($t) {
// Only translate when translation mode is set to localize.
if (i18n_taxonomy_vocabulary_mode($t->vid, I18N_MODE_LOCALIZE)) {
// This is a term.
if ($t->tid > 0) {
$term = taxonomy_term_load($t->tid);
$display_num = '';
$menu_num_inc_children = variable_get(_taxonomy_menu_build_variable('display_num_incl_children', $term->vid), TRUE);
$menu_display_descendants = variable_get(_taxonomy_menu_build_variable('display_descendants', $t->vid), FALSE);
if ($menu_num_inc_children || $menu_display_descendants) {
$num = taxonomy_menu_term_count_nodes($t->tid, $t->vid);
}
else {
$num = _taxonomy_menu_term_count($t->tid);
}
// If hide menu is selected and the term count is 0 and the term
// has no children then do not create the menu item.
if ($num == 0 && variable_get(_taxonomy_menu_build_variable('hide_empty_terms', $t->vid), FALSE) && !_taxonomy_menu_children_has_nodes($t->tid, $t->vid)) {
$display_num = '';
}
elseif (variable_get(_taxonomy_menu_build_variable('display_num', $t->vid), FALSE)) {
$display_num = " ({$num})";
}
if ($item['title'] != $term->name . $display_num) {
// Should not happen.
watchdog('error', 'Menu and taxonomy name mismatch: @title != @name', array(
'@title' => $item['title'],
'@name' => $term->name . $display_num,
));
}
$term = i18n_taxonomy_localize_terms($term);
$item['title'] = $item['link_title'] = $term->name . $display_num;
if (variable_get(_taxonomy_menu_build_variable('term_item_description', $t->vid), FALSE)) {
$item['options']['attributes']['title'] = $term->description;
}
}
else {
$vocab = taxonomy_vocabulary_load($t->vid);
$item['title'] = i18n_string('taxonomy:vocabulary:' . $vocab->vid . ':name', $vocab->name);
}
}
}
else {
watchdog('taxonomy_menu', 'Error with menu entry "%me" in menu "%mt"', array(
'%me' => $item['title'],
'%mt' => $item['menu_name'],
));
}
}
}
}
/**
* Gets term depth from a tid.
*
* @param $tid
* Taxonomy term ID.
* @param $max_depth
* Maximum depth.
*
* @return bool
* Whether or not the term is too deep to include.
*/
function _taxonomy_menu_term_too_deep($tid, $max_depth) {
if ($max_depth) {
$depth = 0;
while ($parent = db_select('taxonomy_term_hierarchy', 't')
->condition('tid', $tid, '=')
->fields('t')
->execute()
->fetchAssoc()) {
$depth++;
$tid = $parent['parent'];
if ($depth > $max_depth) {
return TRUE;
}
}
}
return FALSE;
}
Functions
Name | Description |
---|---|
taxonomy_menu_create_path | Creates the path for the vid/tid combination. |
taxonomy_menu_form_alter | Implements hook_form_alter(). |
taxonomy_menu_handler | Taxonomy Menu Handler: Creates a menu item for each taxonomy term. |
taxonomy_menu_help | Implements hook_help(). |
taxonomy_menu_node_delete | Implements hook_node_delete(). |
taxonomy_menu_node_insert | Implements hook_node_insert(). |
taxonomy_menu_node_presave | Implements hook_node_presave(). |
taxonomy_menu_node_update | Implements hook_node_update(). |
taxonomy_menu_overview_submit | Submit handler, reacting on form ID: taxonomy_overview_terms. |
taxonomy_menu_path_default | Callback for hook_taxonomy_menu_path. |
taxonomy_menu_taxonomy_menu_delete | Implements hook_taxonomy_menu_delete(). |
taxonomy_menu_taxonomy_menu_insert | Implements hook_taxonomy_menu_insert(). |
taxonomy_menu_taxonomy_menu_options | Implements hook_taxonomy_menu_options(). |
taxonomy_menu_taxonomy_menu_path | Implements hook_taxonomy_menu_path(). |
taxonomy_menu_taxonomy_menu_update | Implements hook_taxonomy_menu_update(). |
taxonomy_menu_taxonomy_term_delete | Implements hook_taxonomy_term_delete(). |
taxonomy_menu_taxonomy_term_insert | Implements hook_taxonomy_term_insert($term). |
taxonomy_menu_taxonomy_term_update | Implements hook_taxonomy_term_update(). |
taxonomy_menu_taxonomy_vocabulary_delete | Implements hook_taxonomy_vocabulary_delete(). |
taxonomy_menu_term_count_nodes | Calculates the number of nodes linked to the term and all children. |
taxonomy_menu_translated_menu_link_alter | Implements hook_translated_menu_link_alter(). |
taxonomy_menu_vocab_submit | Submit handler for the extra settings added to the taxonomy vocab form. |
_taxonomy_menu_build_variable | Builds a variable from the supplied name and machine name of the vocabulary. |
_taxonomy_menu_check_variable | Checks to see if the variable has changed. |
_taxonomy_menu_children_has_nodes | Helper function: See if any of the children have any nodes. |
_taxonomy_menu_create_item | Creates the initial $item array. |
_taxonomy_menu_create_options | Creates a form array of taxonomy menu options. Invokes hook_taxonomy_menu_options(). |
_taxonomy_menu_get_paths | Create the path to use in the menu item. |
_taxonomy_menu_insert_link_items | Creates new link items for the vocabulary. |
_taxonomy_menu_item | Helper function: Inserts and updates menu along with taxonomy changes. |
_taxonomy_menu_nodeapi_helper | Builds argument arrays calls taxonomy_menu_handler. |
_taxonomy_menu_rebuild | Rebuilds a menu. |
_taxonomy_menu_save | Adds/Updates a taxonomy menu item. |
_taxonomy_menu_taxonomy_termapi_helper | Abstraction of hook_taxonomy_term_<operation>() |
_taxonomy_menu_term_too_deep | Gets term depth from a tid. |
_taxonomy_menu_update_all_parents | Updates all parent items. |
_taxonomy_menu_update_link_items | Updates the menu items. |