2,
//'path' => drupal_get_path('module', 'taxonomy_treemenu'),
'path' => drupal_get_path('module', 'taxonomy_treemenu') . '/views',
);
}
function taxonomy_treemenu_advanced_help_message() {
if (!module_exists('advanced_help')) {
$m = t('The Advanced Help module appears to be uninstalled or disabled. ');
$m .= t('If you install and enable the Advanced Help module from !href, Taxonomy Treemenu will provide help on buttons within the administration interface. ', array('!href' => l('http://drupal.org/project/advanced_help', 'http://drupal.org/project/advanced_help')));
$m .= t('If you prefer not to use the module, you can visit the help pages using the !href which will appear in the naviation menu.', array('!href' => l('help links', 'admin/help/ttm-advanced-help')));
}
else {
$m = t('Taxonomy Treemenu is enabled for Advanced Help. Use the buttons to find help within the administrative interface. The buttons look like this,');
$m .= '
';
$m .= t('Large help buttons look like this,');
$m .= '';
$m .= t('Or go directly to the !href.', array('!href' => l('help links', 'admin/advanced_help/taxonomy_treemenu')));
}
drupal_set_message($m, 'status', FALSE);
};
/**
* Implementation of hook_help().
*/
function taxonomy_treemenu_help($path, $arg) {
$output = '';
switch ($path) {
case 'admin/help#taxonomy_treemenu':
$output = '
'. t("Make a menu from a taxonomy item, autogenerating subitems, with options") .'
';
$output .= '
'. t("On the 'admin/menu' page there is a new link 'Add taxonomy menutree'. Click on it. Looks like a standard menu creation page page, but has a new fieldset for the choosing of a root node. Go ahead, fill in the fields and choose a root node. Note that the root of a treemenu can not be changed. For a menu with a new root, delete the old menu and create a new one.") .'
';
$output .= '
'. t("Has a pager. Try '?q=treemenu/myMenuName'. You can also use ',' or '+' as a separator and get multiple menus on one page. e.g. 'treemenu/cheapcars+stuffedanimals'. Use normal names, as you supplied initially for the 'machine name' field in the 'add menu' form (not proper machine names, if you know what they are). Have a look at 'admin>menu>myTreemenu' if you have forgotten the name, the right URL name is listed there.") .'
';
$output .= '
'. t("Theme the menu using the template, and the links by overriding theme_taxonomy_treemenu_item_link().") .'
';
$output .= '
'. t("The disabled links all have a css class of 'treemenu-disabled-link'.") .'
';
$output .= '
'. t("Read the README or QUICKSTART files for lots of extra help.") .'
';
break;
}
return $output;
}
/**
* Implementation of hook_menu().
*/
function taxonomy_treemenu_menu() {
$items = array();
if (!module_exists('advanced_help')) {
//dpm('menu item for advanced help');
$items['admin/help/ttm-advanced-help'] = array(
'title' => t('Advanced Help for Taxomomy Treemenu'),
'description' => 'Quick links to advanced help pages, if you do not have the module installed.',
'page callback' => 'taxonomy_treemenu_advanced_help_page',
'page arguments' => array('overview'),
'access callback' => 'user_access',
'access arguments' => array('administer menu'),
'type' => MENU_NORMAL_ITEM,
);
$items['admin/help/ttm-advanced-help/%'] = array(
'title' => t('Advanced Help for Taxomomy Treemenu'),
'page callback' => 'taxonomy_treemenu_advanced_help_page',
'page arguments' => array(3),
'access callback' => 'user_access',
'access arguments' => array('administer menu'),
'type' => MENU_CALLBACK,
);
}
$items['taxonomy_treemenu/ahah'] = array(
'title' => 'Advanced Options for Form',
'page callback' => 'taxonomy_treemenu_advanced_options_ahah',
'access arguments' => array('access content'),
'file' => 'taxonomy_treemenu.admin.inc',
'type' => MENU_CALLBACK,
);
// Duff item, never used in Taxonomy Treemenu, but keeps our links
// anchored and existing when using core menu functions.
// Also there is enough here to start the link assessment for access
// and so forth, again with the core functions.
$items['ttprnts/%'] = array(
'title' => t('taxonomy_treemenu_base_item'),
'page callback' => 'drupal_not_found',
'access callback' => 'user_access',
'access arguments' => array('access content'),
'type' => MENU_CALLBACK
);
//leave the /menu part of the path so
//will act as local task
$items['admin/build/menu/treemenu/add'] = array(
'title' => 'Add taxonomy treemenu',
'page callback' => 'drupal_get_form',
'page arguments' => array('taxonomy_treemenu_edit_menu', 'add'),
'access callback' => 'user_access',
'access arguments' => array('administer menu'),
'type' => MENU_LOCAL_TASK,
'file' => 'taxonomy_treemenu.admin.inc',
//'file' => 'menu.admin.inc',
//'file path' => drupal_get_path('module', 'menu'),
);
// For my grumble on this, see taxonomy_treemenu_edit_menu(), taxonomy_treemenu.admin.inc
// We're modifying the menu items by targeting specific
// treemenus, which specific urls over-ride the generalized ones in menu_menu.
// $menu_names = array_keys(variable_get('taxonomy_treemenu_data', array()));
$menu_names = TTMData::allNames();
if ($menu_names) {
foreach ($menu_names as $menu_name) {
// Redirects to our specially extended edit form.
// I'd like to skip this, but I havn't found a way of
// extending the edit form sucessfully, yet. They are big extensions...
$items['admin/build/menu-customize/' . $menu_name . '/edit'] = array(
//Need a custom callback to get the title
'title' => $menu_name,
'title callback' => 'taxonomy_treemenu_menu_title',
'page callback' => 'drupal_get_form',
'page arguments' => array('taxonomy_treemenu_edit_menu', 'edit', 3),
'access callback' => 'user_access',
'access arguments' => array('administer menu'),
'type' => MENU_CALLBACK,
'file' => 'taxonomy_treemenu.admin.inc',
);
//I tried removing the 'add item' button for treemenus
//Well, rejig it as something invisible,
//using MENU_CALLBACK, which is Drupalish, though I'd
//like to remove it completly.
//Truth is, it doesn't, and forum topics suggest this is impossible without theming.
//My guess is that Drupal searches for the local tab urls,
//then reduces them for the page.
//Thus it always finds the menu_menu wildcard entry,
//even if we over-ride it for the page itself.
//We can't use menu links alter, as this is a router item.
// We can do this, though, redirct back with a warning message...
$items['admin/build/menu-customize/' . $menu_name . '/add'] = array(
'title' => 'Add (no do not) item',
'description' => 'You can not do this action.',
'page callback' => 'taxonomy_treemenu_tab_redirect',
'page arguments' => array('admin/build/menu-customize/' . $menu_name),
'access arguments' => array('administer menu'),
'type' => MENU_CALLBACK,
);
}
}
//Put the rebuild link in admin, but if devel is loaded, put it there.
if (module_exists('devel')) {
$items['devel/treemenu-rebuild'] = array(
'title' => 'Rebuild treemenus',
'description' => 'Manually rebuild singular treemenus. Drastic action, for troubled times.',
'page callback' => 'drupal_get_form',
'page arguments' => array('taxonomy_treemenu_menu_update'),
'access arguments' => array('access devel information'),
//dunno what this is exactly, but its needed.
'menu_name' => 'devel',
'file' => 'taxonomy_treemenu.admin.inc',
);
}
else {
$items['admin/treemenu-rebuild'] = array(
'title' => 'Rebuild treemenus',
'description' => 'Manually rebuild singular treemenus. Drastic action, for troubled times.',
'page callback' => 'drupal_get_form',
'page arguments' => array('taxonomy_treemenu_menu_update'),
'access callback' => 'user_access',
'access arguments' => array('administer menu'),
//'type' => MENU_LOCAL_TASK,
//'weight' => 5,
'file' => 'taxonomy_treemenu.admin.inc',
);
}
//The new ones.
// Proposed format is /ttm/[menu-name]/[path]/['node'|'term'/id]
$items['ttm/%taxonomy_treemenu'] = array(
'title' => t('taxonomy_treemenu'),
'page callback' => 'taxonomy_treemenu_page',
'page arguments' => array(1),
'access callback' => 'user_access',
'access arguments' => array('access content'),
'type' => MENU_CALLBACK
);
// The menu system gathers nodes aside from other links.
// We use defined paths so node/term links are easy to identify.
// despite the repetitiveness... despite the repetition...
//$items['ttm/%taxonomy_treemenu/node/%taxonomy_treemenu_id'] = array(
$items['ttm/%taxonomy_treemenu/node/%node'] = array(
'title callback' => t('taxonomy_treemenu_page_title'),
'title arguments' => array(1),
'page callback' => 'taxonomy_treemenu_node_page',
'page arguments' => array(1, 3),
'access callback' => 'node_access',
'access arguments' => array('view', 3),
'type' => MENU_CALLBACK
);
$items['ttm/%taxonomy_treemenu/term/%taxonomy_treemenu_term_str'] = array(
'title callback' => t('taxonomy_treemenu_page_title'),
'title arguments' => array(1),
'page callback' => 'taxonomy_treemenu_term_page',
'page arguments' => array(1, 3),
//'load arguments' => array(1, 3),
'access callback' => 'user_access',
'access arguments' => array('access content'),
'type' => MENU_CALLBACK
);
//end here
// If Views is not loaded, tell the menu router items to load treemenu's
// internal provision, on demand.
//if(!module_exists('views')) {
// $items['taxmenu/%taxonomy_treemenu_ttid/page/%taxmenu_tid_trail/%taxonomy_treemenu_tid']['file'] = '/includes/taxonomy_treemenu.pages.inc';
// $items['taxmenu/%taxonomy_treemenu_ttid/term/%taxmenu_tid_trail/%taxonomy_treemenu_tid']['file'] = '/includes/taxonomy_treemenu.pages.inc';
//}
return $items;
}
/**
* Implementation of menu 'title callback'
*/
//This is just rotten. A title callback - 'edge cases' the documentation calls it.
//But until we can set the titles on our highly modified edit form, which we can't,
//and until we implement a database and loader, which is of marginal use when we can't
//override the main menu, we do this.
//TODO:will get_args do it?
function taxonomy_treemenu_menu_title($menu_name) {
$result = db_fetch_object(db_query("SELECT title FROM {menu_custom} WHERE menu_name = '%s'", $menu_name));
return t($result->title);
}
function taxonomy_treemenu_page_title($ttm) {
return t($ttm['title']);
}
/****************************
* URL verifiers and loaders
***************************/
/**
* Numeric tid loader.
* @param $id
* @return
*/
// No need to load the item, as its only a target.
function taxonomy_treemenu_term_str_load($str_tids) {
$terms = taxonomy_terms_parse_string($str_tids);
if ($terms['operator'] != 'and' && $terms['operator'] != 'or') {
drupal_not_found();
}
return $terms;
return is_numeric($id) ? $id : FALSE;
}
// TOCONSIDER:Following are currently unused?
/**
* We don't load as the user may be using Views, which loads by itself.'
* Since we use further loaders, we don't test for existance.
* @param $nid
* @return
* A verified nid.
*/
function taxonomy_treemenu_nid_load($nid) {
return is_numeric($nid) ? $nid : FALSE;
}
/**
* Verifies a tid exists and is numeric, but doesn't load the associated term data.
*
* @param $tid
* @return
* A verified tid.
*/
function taxonomy_treemenu_tid_load($tid) {
return is_numeric($tid) ? $tid : FALSE;
}
/***************************
* Taxmenu data loaders
**************************/
/**
* Load settings data for a named treemenu.
* Optional argument loads also the descriptive information from custom menu.
*
* @param $menu_name
* @param boolean $custom_menu_data
* @return array of data, or FALSE
*/
function taxonomy_treemenu_load($menu_name, $custom_menu_data = TRUE) {
//dpm('treemnu load');
if (!check_plain($menu_name)) {
return FALSE;
}
if ($custom_menu_data) {
$result = db_query("SELECT * FROM {taxonomy_treemenu} tt INNER JOIN {menu_custom} mc ON tt.menu_name = mc.menu_name WHERE tt.menu_name = '%s'", $menu_name);
}
else {
$result = db_query("SELECT * FROM {taxonomy_treemenu} WHERE menu_name = '%s'", $menu_name);
}
return db_fetch_array($result);
}
/**
* Returns an array, because we like arrays,
* and doesn't cache, unlike taxonomy_get_term($tid).
*
* @param $tid
* @return
*/
function taxonomy_treemenu_get_term($tid) {
return db_fetch_array(db_query('SELECT * FROM {term_data} WHERE tid = %d', $tid));
}
/*********************
*Options retrieval
*********************/
/**
* Treemenu needs to recover many options from the database,
* sometimes with adjustments for zero/type and the like.
* Rather than marking them all as loaders, or using geeralized calls,
* the functions are gathered here.
* And some specialized savers, too.
*/
class TTMData
{
static function depth($menu_name) {
$result = db_query("SELECT depth FROM {taxonomy_treemenu} WHERE menu_name = '%s'", $menu_name);
$depth = db_result($result);
return ($depth == 0) ? MENU_MAX_DEPTH : $depth;
}
static function allNames() {
$names = array();
$result = db_query("SELECT menu_name FROM {taxonomy_treemenu}");
while ($tm = db_fetch_array($result)) {
$names[] = $tm['menu_name'];
}
return $names;
}
static function allNamesDisplay() {
$names = array();
$result = db_query("SELECT tt.menu_name, mc.title FROM {taxonomy_treemenu} tt INNER JOIN {menu_custom} mc ON tt.menu_name = mc.menu_name");
while ($tm = db_fetch_array($result)) {
$names[$tm['menu_name']] = $tm['title'];
}
return $names;
}
// Return FALSE or the check_plained emnu_name.
static function validateMenuName($menu_name) {
$menu_name = check_plain($menu_name);
$result = db_query("SELECT menu_name FROM {taxonomy_treemenu} WHERE menu_name = '%s'", $menu_name);
return db_result($result);
}
//returns FALSE on fail.
static function opts($menu_name) {
$result = db_query("SELECT options FROM {taxonomy_treemenu} WHERE menu_name = '%s'", $menu_name);
$tm = db_fetch_array($result);
return unserialize($tm['options']);
}
/**
* Return an associative array of treemenu names, titles, and dhtml_block flags.
* Quite specific. Used in block calls.
*
* @return
* Associative array with the machine-readable menu names as the keys.
*/
static function block() {
$menu_data = array();
$result = db_query("SELECT tt.menu_name, title, dhtml_blocks FROM {taxonomy_treemenu} tt INNER JOIN {menu_custom} mc ON tt.menu_name = mc.menu_name");
while ($tm = db_fetch_array($result)) {
$menu_data[$tm['menu_name']] = $tm;
}
ksort($menu_data);
return $menu_data;
}
static function getDHTML($menu_name) {
return db_fetch_array(db_query("SELECT dhtml_blocks, dhtml_pages FROM {taxonomy_treemenu} WHERE menu_name = '%s'", $menu_name));
}
static function setDHTML($menu_name, $data) {
db_query("UPDATE {taxonomy_treemenu} SET dhtml_blocks = %d, dhtml_pages = %d WHERE menu_name = '%s'", $data['dhtml_blocks'], $data['dhtml_pages'], $menu_name);
}
static function isMultiple($menu_name) {
$data = db_fetch_array(db_query("SELECT v.hierarchy, v.multiple FROM {taxonomy_treemenu} tt INNER JOIN {vocabulary} v ON tt.vid = v.vid WHERE tt.menu_name = '%s'", $menu_name));
//dpm('multiple:');
//dpm($menu_name);
//dvm($data);
//return (($data['hierarchy'] == 2) || ($data['nodes'] && ($data['multiple'])));
$data['hierarchy'] = ($data['hierarchy'] == 2);
$data['multiple'] = ($data['hierarchy'] || $data['multiple']);
return $data;
}
static function isMultipleStatusData($menu_name) {
$data = db_fetch_array(db_query("SELECT v.hierarchy, v.multiple FROM {taxonomy_treemenu} tt INNER JOIN {vocabulary} v ON tt.vid = v.vid WHERE tt.menu_name = '%s'", $menu_name));
//dpm('multiple:');
//dpm($menu_name);
//dvm($data);
$t = ($data['hierarchy'] == 2) ? 'multiple' : 'single';
$n = ($data['multiple']) ? 'multiple' : 'single';
return array('terms' => $t, 'nodes' => $n);
}
static function vocabularyStatusData($menu_name) {
$data = db_fetch_array(db_query("SELECT v.relations, v.tags FROM {taxonomy_treemenu} tt INNER JOIN {vocabulary} v ON tt.vid = v.vid WHERE tt.menu_name = '%s'", $menu_name));
$data['relations'] =$data['relations'] ? 'yes' : 'no';
$data['tags'] =$data['tags'] ? 'yes' : 'no';
return $data;
}
static function rootDataDisplay($menu_name) {
$vd = db_fetch_array(db_query("SELECT v.name, v.description FROM {taxonomy_treemenu} tt INNER JOIN {vocabulary} v ON tt.vid = v.vid WHERE tt.menu_name = '%s'", $menu_name));
$td = db_fetch_array(db_query("SELECT t.name, t.description FROM {taxonomy_treemenu} tt INNER JOIN {term_data} t ON tt.tid = t.tid WHERE tt.menu_name = '%s'", $menu_name));
if (!$td['name']) {
$td['name'] = $td['description'] = '(menu built from vocab)';
}
return array($vd, $td);
}
static function rootData($menu_name) {
return db_fetch_array(db_query("SELECT vid, tid FROM {taxonomy_treemenu} WHERE menu_name = '%s'", $menu_name));
}
static function rootDataAll() {
$tms = array();
$result = db_query("SELECT menu_name, vid, tid FROM {taxonomy_treemenu}");
while ($tm = db_fetch_array($result)) $tms[] = $tm;
return $tms;
}
// Good for views, maybe on all_data menus. Untested.
static function allTids($menu_name) {
$b = new TTMBranch();
$b->setFromTTMenu($menu_name);
return $b->descendantTids(self::depth($menu_name));
}
}
/**********************
* Branch class
************************/
// Branches always have vids. Maybe we should assert?
class TTMBranch
{
public $vid, $tid;
// Integer cast?
function set($vid, $tid) {
$this->vid = $vid;
$this->tid = $tid;
}
function setFromTTMenu($menu_name) {
$tm = db_fetch_array(db_query("SELECT vid, tid FROM {taxonomy_treemenu} WHERE menu_name = '%s'", $menu_name));
$this->vid = $tm['vid'];
$this->tid = $tm['tid'];
}
function setFromPath($treemenu_path) {
//strrchr($treemenu_path , '/');
//$this->tid = substr($treemenu_path, (strrpos($treemenu_path , '/') + 1));
// Skip 'ttprnts/'
//$this->vid = substr($treemenu_path, (strpos($treemenu_path, '/', 9) + 1), ;
$bits = explode('/', $treemenu_path);
$this->vid = (int) $bits[1];
$this->tid = (int) (count($bits) > 2) ? end($bits) : 0;
}
// Untested.
function getTitle() {
if ($this->tid == '0') {
//vocabulary
$result = db_query('SELECT name FROM {vocabulary} WHERE vid = "%d"', $this->vid);
}
else {
//term
$result = db_query('SELECT name FROM {term_data} WHERE tid = "%d"', $this->tid);
}
$item = db_fetch_array($result);
return $item['name'];
}
// Tested
function getDisplayData() {
if ($this->tid == '0') {
//vocabulary
$result = db_query('SELECT name, description, weight FROM {vocabulary} WHERE vid = %d', $this->vid);
}
else {
//term
$result = db_query('SELECT name, description, weight FROM {term_data} WHERE tid = %d', $this->tid);
}
return db_fetch_array($result);
}
function asRootPath() {
$path = 'ttprnts/' . $this->vid;
if ($this->tid != '0') {
$path = $path . '/' . $this->tid;
};
return $path;
}
// There's some rotten type handling here, as the PHP engine insists on
// making the variables in this class into strings. Won't argue.
// But what is ($this->tid == '0')? Humm. PHP seems to like it?!
// Hence this function, anyhow. Please do not remove - you never know...
function displayBranch() {
dpm('Branch:');
dvm($this->vid);
dvm($this->tid);
}
public function descendantTids($depth) {
//dpm($depth);
//dpm($this->tid);
return $this->_descendantTids($this->tid, -1, $depth);
}
private function _descendantTids($parent = 0, $depth_count = -1, $depth = 0) {
static $children, $parents;
$depth_count++;
// We cache trees, so it's not CPU-intensive to call get_tree() on a term
// and it's children, too.
if (!isset($children[$vid])) {
$children[$vid] = array();
$result = db_query(db_rewrite_sql('SELECT t.tid, parent FROM {term_data} t INNER JOIN {term_hierarchy} h ON t.tid = h.tid WHERE t.vid = %d ORDER BY weight, name', 't', 'tid'), $this->vid);
while ($term = db_fetch_object($result)) {
$children[$vid][$term->parent][] = $term->tid;
$parents[$vid][$term->tid][] = $term->parent;
}
}
$tids = array();
if ($depth > $depth_count && !empty($children[$vid][$parent])) {
foreach ($children[$vid][$parent] as $child) {
$tids[] = $child;
if (!empty($children[$vid][$child])) {
$tids = array_merge($tids, $this->_descendantTids($child, $depth_count, $depth));
}
}
}
return $tids;
}
function childTids() {
$result = db_query(db_rewrite_sql('SELECT t.tid FROM {term_data} t INNER JOIN {term_hierarchy} h ON h.tid = t.tid WHERE t.vid = %d AND h.parent = %d', 't', 'tid'), $this->vid, $this->tid);
$children = array();
while ($children[] = db_fetch_object($result)) {
}
return $children;
}
}
/**
* Implementation of hook_block().
*/
// We can't provoke the system auto-generated treemenus into basic funtionality.
//Which suggests overriding the blocks - which is inmpossible. DHTML menu does this by
// preprocessing the block, tidy, but means the block is built twice (sure they
// are sad about this).
// We use the untidy solution of a separate block, and preprocessing system
// provided system blocks to return a warning message.
function taxonomy_treemenu_block($op = 'list', $delta = 0) {
if ($menus = TTMData::block()) {
if ($op == 'list') {
$blocks = array();
foreach ($menus as $menu_name => $data) {
// Tag with ' - treemenu', as we can't stop the stock listing.
$blocks[$menu_name]['info'] = check_plain($data['title'] . ' - treemenu');
// Menu blocks can't be cached because each menu item can have
// a custom access callback. menu.inc manages its own caching.
$blocks[$menu_name]['cache'] = BLOCK_NO_CACHE;
}
return $blocks;
}
else if ($op == 'view') {
//dvm($delta);
$data['subject'] = check_plain($menus[$delta]['title']);
// If the menu has the DHTML menu option enabled,
// use a staic menu and theme with 'dhtml_menu_tree'.
if (module_exists('dhtml_menu') && $menus[$delta]['dhtml_blocks']) {
//dpm('ttm render dhtml block');
$data['content'] = theme('dhtml_menu_tree', taxonomy_treemenu_tree_data(taxonomy_treemenu_load($delta)));
}
else {
//dpm('ttm render block');
$data['content'] = taxonomy_treemenu_render_block($delta);
}
return $data;
}
}
}
/**
* Parses a comma or plus separated string of term IDs.
*
* @param $str_tids
* A string of term IDs, separated by plus or comma.
* comma (,) means AND
* plus (+) means OR
*
* @return an associative array with an operator key (either 'and'
* or 'or') and a tid key containing an array of the term ids.
*/
//Term data
//Straight lift of taxonomy_terms_parse_string() from taxomony.module
//Makes one zero tid if nothing validates?
function taxonomy_treemenu_terms_parse_string($str_tids) {
$terms = array('operator' => '', 'tids' => array());
if (preg_match('/^([0-9]+[+ ])+[0-9]+$/', $str_tids)) {
$terms['operator'] = 'or';
// The '+' character in a query string may be parsed as ' '.
$terms['tids'] = preg_split('/[+ ]/', $str_tids);
}
else if (preg_match('/^([0-9]+,)*[0-9]+$/', $str_tids)) {
$terms['operator'] = 'and';
$terms['tids'] = explode(',', $str_tids);
}
return $terms;
}
/******************************************************
* Code to redirect a user to the previous page.
* Used in various admin functions and disabled links.
*******************************************************/
/**
* Menu callback; clears all caches, then redirects to the previous page.
*/
function taxonomy_treemenu_tab_redirect($prev_url) {
drupal_set_message('Sorry, you can not add items to a treemenu, which is generated automatically. Try altering the underlying taxonomy. Any changes there will be respected in the menu.');
drupal_goto($prev_url);
}
/**
* Implementation of hook_menu_link_alter().
* Used to redirect a user straight back to the original page.
*/
function taxonomy_treemenu_menu_link_alter(&$item, &$menu) {
//dpm('menu_link_alter');
if (in_array($item['link_path'], taxonomy_treemenu_menu_need_destination())) {
$item['options']['alter'] = TRUE;
}
}
/**
* Implementation of hook_translated_menu_link_alter().
* Used to redirect a user straight back to the original page.
*/
function taxonomy_treemenu_translated_menu_link_alter(&$item) {
if (in_array($item['href'], taxonomy_treemenu_menu_need_destination())) {
$item['localized_options']['query'] = drupal_get_destination();
}
}
/**
* Array of links which should redirect a user back to their original page.
*/
function taxonomy_treemenu_menu_need_destination() {
return array('admin/treemenu-rebuild', 'devel/treemenu-rebuild');
}
/***************************
* DHTML menu compatibility
***************************/
//We could try overriding function by function, but the control ciruitry is
//fairly small, so we duplicate it.
//
/**
* Save the DHTML options
*
* @ingroup form
*/
//See note to function below
/*
function _taxonomy_treemenu_form_block_submit(&$form, $form_state) {
$menu_name = $form_state['values']['delta'];
$data = TTMData::getDHTML($menu_name);
if ($form_state['values']['dhtml_menu']) {
$data['dhtml_pages'] = 0;
}
$data['dhtml_blocks'] = $form_state['values']['dhtml_menu'];
TTMData::setDHTML($menu_name, $data);
}
*/
// TOCONSIDER: as of DHTML Menu 5.3, this is unused. It says put,
// because we do not know where that module is heading.
/**
* Implementation of hook_form_alter().
* @ingroup form
*/
/*
function taxonomy_treemenu_form_block_admin_configure_alter(&$form, $form_state) {
// Modify the form if DHTML Menu is enabled and it is a treemenu.
if (module_exists('dhtml_menu') && $form['module']['#value'] == 'taxonomy_treemenu') {
$menu_name = $form['delta']['#value'];
$data = TTMData::getDHTML($menu_name);
$form['dhtml_menu'] = array(
'#default_value' => $data['dhtml_blocks'],
'#title' => t('Use the DHTML effect on !menu_name in blocks.', array('!menu_name' => $menu_name)),
'#description' => t("If you switch this on, then page DHTML is disabled - the DHTML gets confused if two similar menus share the same page."),
'#type' => 'checkbox',
'#weight' => -3,
);
$form['#submit']['_taxonomy_treemenu_form_block_submit'] = '_taxonomy_treemenu_form_block_submit';
}
}
*/
/**
* Implementation of hook_preprocess_block().
* Puts warning message in place of system-provided taxonomy treemenu blocks.
*
* @ingroup themable
*/
function taxonomy_treemenu_preprocess_block(&$variables) {
$delta = $variables['block']->delta;
$module = $variables['block']->module;
$menu_names = TTMData::allNames();
if ($module == "menu" && in_array($delta, $menu_names)) {
$variables['block']->content = '
' . t("Taxonomy Treemenu overrides menu blocks provided by core. To view this menu, promote the treemenu block of the same name with the suffix ' - treemenu'.") . '
';
$variables['block']->content .= '
' . t("If you see this message we offer our apologies. We use this solution because it is efficient.") . '
';
}
}
/*************************************************
* Various override/hook functions for data admin.
*************************************************/
/**
* Implementation of hook_taxonomy.
*/
/*
This is a hook. All it's data comes from taxonomy calling back.
And I don't think we want it rewritable.
*/
function taxonomy_treemenu_taxonomy($op, $type, $object = NULL) {
//dpm('hook_taxonomy');
//dpm('$op: '. $op .' $type: '.$type);
//dvm($object);
if ($op == 'delete' && $type == 'vocabulary') {
//Well, any treemenu using the vocabulary is history, so...
$ttms = TTMData::rootDataAll();
foreach ($ttms as $ttm) {
if ($ttm['vid'] == $object['vid']) {
_taxonomy_treemenu_delete_menu($ttm['menu_name']);
}
}
}
if ($op == 'delete' && $type == 'term') {
//dpm('tt delete term');
$placeholder = "'ttprnts/". $object['vid'] ."%/". $object['tid'] ."'";
//TODO, shouldn't we properly delete, i.e. shift everything round?
$result = db_query("SELECT mlid FROM {menu_links} WHERE link_path LIKE". $placeholder ." AND module='%s'", 'treemenu');
while($data = db_fetch_array($result)) {
// This is a tree delete.
menu_link_delete($data['mlid']);
}
$ttms = TTMData::rootDataAll();
foreach ($ttms as $ttm) {
if ($ttm['vid'] == $object['vid'] && $ttm['tid'] == $object['tid']) {
//Yow! Term deleted is a menu root.
//The term is gone, let's trash the menu.
_taxonomy_treemenu_delete_menu($ttm['menu_name']);
}
}
}
//Mainly, if we move or create a term.
if (($op == 'insert' || $op == 'update') && $type == 'term') {
taxonomy_treemenu_update_term_links($object);
}
}
/**
* Delete a taxonomy treemenu, including data held by the module.
* @param $menu_name
*/
function _taxonomy_treemenu_delete_menu($menu_name) {
// Reset all the menu links defined by the system via hook_menu. /add and /edit...
// I don't know why they are reset, why not delete? They stay live until a menu rebuild?
// They don't show, though, is this efficient?
$result = db_query("SELECT * FROM {menu_links} ml INNER JOIN {menu_router} m ON ml.router_path = m.path WHERE ml.menu_name = '%s' AND ml.module = 'system' ORDER BY m.number_parts ASC", $menu_name);
while ($item = db_fetch_array($result)) {
menu_reset_item($item);
}
// Delete links to the overview page for this menu.
$result = db_query("SELECT mlid FROM {menu_links} ml WHERE ml.link_path = '%s'", 'admin/build/menu-customize/'. $menu_name);
while ($m = db_fetch_array($result)) {
menu_link_delete($m['mlid']);
}
// Delete all the links in the menu. Note
db_query("DELETE FROM {menu_links} WHERE menu_name = '%s'", $menu_name);
// ...and the menu data from {custom menus}.
db_query("DELETE FROM {menu_custom} WHERE menu_name = '%s'", $menu_name);
// Delete all the blocks for this menu.
db_query("DELETE FROM {blocks} WHERE module = 'menu' AND delta = '%s'", $menu_name);
db_query("DELETE FROM {blocks_roles} WHERE module = 'menu' AND delta = '%s'", $menu_name);
menu_cache_clear_all();
cache_clear_all();
$t_args = array('%title' => $menu_name);
drupal_set_message(t('The custom menu %title has been deleted.', $t_args));
watchdog('menu', 'Deleted custom menu %title and all its menu items.', $t_args, WATCHDOG_NOTICE);
// Delete treemenu data
taxonomy_treemenu_delete_menu_data($menu_name);
}
function taxonomy_treemenu_delete_menu_data($menu_name) {
db_query("DELETE FROM {taxonomy_treemenu} WHERE menu_name = '%s'", $menu_name);
$t_args = array('%title' => $menu_name);
drupal_set_message(t('...and the treemenu data associated with it.'));
watchdog('menu', 'Deleted element %title from treemenu variable.', $t_args, WATCHDOG_NOTICE);
}
/*
* Additional Callbacks
* Here, not in .amin.inc because there is a scoping problem
* with gaining callbacks for re-routed submit calls.
* I suppose the way to go is to reroute,
* which can then pull in the menu functions AND the extra stuff
* but the functions are small, so they are here for now.
*
*/
/**
* Submit to change node count on treemeu controlled term pages.
* @param $form
* @param $form_state
*/
function taxonomy_treemenu_form_node_configure_submit($form, &$form_state) {
variable_set('default_nodes_taxonomy_treemenu_main', $form_state['values']['default_nodes_taxonomy_treemenu_main']);
}
/**
* Implementation of hook_form_alter()
* Sets a vaiable for use by treemeu controlled term pages.
* @param $form
* @param $form_state
*/
function taxonomy_treemenu_form_node_configure_alter(&$form, $form_state) {
$form['default_nodes_taxonomy_treemenu_main'] = array(
'#type' => 'select', '#title' => t('Number of posts on a taxononomy treemenu controlled term page'), '#default_value' => variable_get('default_nodes_taxonomy_treemenu_main', 30),
'#options' => drupal_map_assoc(array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 25, 30, 35, 40, 45, 50)),
'#description' => t('The default maximum number of posts to display per page when using the internal term theme.'),
'#weight' => -1,
);
$form['#submit'][] = 'taxonomy_treemenu_form_node_configure_submit';
}
/**
* Additional confirm deletion for custom menu deletion.
* Deletes all relevant menu data from treemenu variable.
*/
function taxonomy_treemenu_delete_menu_confirm_submit($form, &$form_state) {
$menu_name = $form['#menu']['menu_name'];
taxonomy_treemenu_delete_menu_data($menu_name);
}
/**
* Implementation of hook_theme_registry_alter
* For purposes of re-themeing menu overview pages.
*/
//radical registry redirection!
//seems very clumsy, but I've got to get hold of the javascript before rendering starts.
function taxonomy_treemenu_theme_registry_alter(&$theme_registry) {
// Assuming you have the devel module installed
// dsm($theme_registry);
if (!empty($theme_registry['menu_overview_form'])) {
$theme_registry['menu_overview_form']['function'] = 'taxonomy_treemenu_menu_overview';
$theme_registry['menu_overview_form']['file'] = 'taxonomy_treemenu.module';
$theme_registry['menu_overview_form']['path'] = drupal_get_path('module', 'taxonomy_treemenu');
//$theme_registry['menu_overview_form']['paths'] =
//drupal_rebuild_theme_registry();
}
if (!empty($theme_registry['form_element'])) {
//$theme_registry['form_element']['function'] = 'taxonomy_treemenu_form_element';
}
}
/**
* 'tis a theme alteration by any other name. But dammned drastic.
*/
// Clipped wholesale from theme_menu_overview_form($form) in menu.admin.inc
// If it's commented out, we don't need it.
// TOCONSIDER: Please don't delete commented lines. For example, 'expanded' would
// be used with mlid URLs.
function taxonomy_treemenu_menu_overview($form) {
// Here, we provide our customized version of the
// theme_form_element function from theme.inc...
//dpm($form);
$ttm = TTMData::allNames();
if (in_array($form['#menu']['menu_name'], $ttm)) {
drupal_set_message(t('This menu is a treemenu, automatically generated from a taxonomy.
Some of the usual editing options are removed. To change item order or parentage, change the underlying taxonomy.'), 'warning');
//what follows here is directly clipped from theme_menu_overview_form($form)
//this seems horribly inefficient,(is unDRY WET?) but the javascript needs to be removed,
// and I know no way of tweaking the form to do that.
$header = array(
t('Menu item'),
array('data' => t('Enabled'), 'class' => 'checkbox'),
//array('data' => t('Expanded'), 'class' => 'checkbox'),
//t('Weight'),
//array('data' => t('Operations'), 'colspan' => '3'),
);
$rows = array();
foreach (element_children($form) as $mlid) {
if (isset($form[$mlid]['hidden'])) {
$element = &$form[$mlid];
// Build a list of operations.
//$operations = array();
//foreach (element_children($element['operations']) as $op) {
// $operations[] = drupal_render($element['operations'][$op]);
//}
//while (count($operations) < 2) {
// $operations[] = '';
//}
// Add special classes to be used for tabledrag.js.
//$element['plid']['#attributes']['class'] = 'menu-plid';
//$element['mlid']['#attributes']['class'] = 'menu-mlid';
//$element['weight']['#attributes']['class'] = 'menu-weight';
// Change the parent field to a hidden. This allows any value but hides the field.
$element['plid']['#type'] = 'hidden';
$row = array();
$row[] = theme('indentation', $element['#item']['depth'] - 1) . drupal_render($element['title']);
$row[] = array('data' => drupal_render($element['hidden']), 'class' => 'checkbox');
// NOTE:Expanded may have been hidden by taxonomy_treemenu_form_alter()
$row[] = array('data' => drupal_render($element['expanded']), 'class' => 'hidden');
$row[] = drupal_render($element['weight']) . drupal_render($element['plid']) . drupal_render($element['mlid']);
//$row = array_merge($row, $operations);
$row = array_merge(array('data' => $row), $element['#attributes']);
//$row['class'] = !empty($row['class']) ? $row['class'] .' draggable' : 'draggable';
$rows[] = $row;
}
}
$output = '';
if ($rows) {
$output .= theme('table', $header, $rows, array('id' => 'menu-overview'));
}
$output .= drupal_render($form);
}
else {
require_once(drupal_get_path('module', 'menu') .'/menu.admin.inc');
$output = theme_menu_overview_form($form);
}
//dpr($form);
return $output;
}
/**
* Implementation of hook_form_ID_alter()
* Remove item editing capability from treemenus.
* See also the theme registry hack,
* which removes the javascript drag and drop.
* @param $form
* @param $form_state
*/
function taxonomy_treemenu_form_menu_overview_form_alter(&$form, &$form_state) {
$empty_array = array();
if (in_array($form['#menu']['menu_name'], TTMData::allNames())) {
$form['message'] = array(
'#value' => "
Some attributes can be set,
but if you reparent or update taxomony terms, they may be reset.
",
'#weight' => -1,
);
foreach (element_children($form) as $key) {
$form[$key]['operations'] = $empty_array;
$form[$key]['weight'] = $empty_array;
//if ($form[$key]['#item']['router_path'] == 'node/%') {
// $form[$key]['expanded'] = $empty_array;
//}
// Hide the exapnds, but not remove so we stay API nice.
$form[$key]['expanded']['#type'] = 'hidden';
}
};
}
/**
* Implementation of hook_form_ID_alter()
* Adds extra variable deletion on submit.
* @param $form
* @param $form_state
*/
function taxonomy_treemenu_form_menu_delete_menu_confirm_alter(&$form, &$form_state) {
if (in_array($form['#menu']['menu_name'], TTMData::allNames())) {
//dpm('added submit');
$form['#submit'][] = 'taxonomy_treemenu_delete_menu_confirm_submit';
}
};
/**!
* Implementation of hook_theme().
*/
function taxonomy_treemenu_theme() {
return array(
// 'taxonomy_treemenu_form_element' => array(
// 'arguments' => array('element' => NULL, 'value' => NULL)
// ),
'taxonomy_admin_js_link' => array(
'arguments' => array('title' => NULL, 'id' => NULL)
),
'branch_selector_by_gui' => array(
'arguments' => array('element' => NULL)
),
'branch_selector_by_option' => array(
'arguments' => array('element' => NULL)
),
'ttm_checkbox_raw' => array(
'arguments' => array('element' => NULL)
),
'ttm_div' => array(
'arguments' => array('element' => NULL)
),
'ttm_widget_container' => array(
'arguments' => array('element' => NULL)
),
'ttm_tab_container' => array(
'arguments' => array('element' => NULL)
),
'ttm_tab' => array(
'arguments' => array('element' => NULL)
),
'ttm_tab_display' => array(
'arguments' => array('element' => NULL)
),
'ttm_toggle' => array(
'arguments' => array('element' => NULL)
),
'ttm_checkbox_help' => array(
'arguments' => array('element' => NULL)
),
'depth' => array(
'arguments' => array('element' => NULL)
),
'taxonomy_treemenu_item_link' => array(
'arguments' => array('link' => NULL)
),
'taxonomy_treemenu_root_link' => array(
'arguments' => array('link_title' => NULL)
),
'taxonomy_treemenu_breadcrumbs' => array(
'arguments' => array('breadcrumbs' => NULL),
),
'taxonomy_treemenu_term_page' => array(
'arguments' => array('term' => NULL, 'result' => NULL),
),
'taxonomy_treemenu_term_row' => array(
'arguments' => array('row' => NULL),
),
'taxonomy_treemenu_term_row_data' => array(
'arguments' => array('data' => NULL),
),
'taxonomy_treemenu_term_unformatted_page' => array(
'arguments' => array('term' => NULL, 'result' => NULL),
),
'taxonomy_treemenu_page' => array(
'arguments' => array('ttm' => NULL),
),
'taxonomy_treemenu_view_row_node' => array(
'arguments' => array('node_data' => NULL, 'base_path' => NULL),
'template' => 'taxonomy-treemenu-view-row-node',
'path' => drupal_get_path('module', 'taxonomy_treemenu') . '/themes',
),
'taxonomy_treemenu_view_term_nodes' => array(
'arguments' => array('term' => NULL),
'template' => 'taxonomy-treemenu-view-term-nodes',
'path' => drupal_get_path('module', 'taxonomy_treemenu') . '/themes',
),
);
}
/*******************************************************************
* Menu link handling functions.
* Here follows the heavy lifting of this module.
********************************************************************/
/**
* A good deal friendlier than _taxonomy_treemenu_updatesave_linkroot_raw(),
* as it seeks out items, and can do various root opimisations, which is why it
* is seperate.
*
* The search is quite mild, it looks for items with the same path. So it can't
* agressively reuse links.
*
* While it is at it, asserts a few fields as a form of accident recovery.
*
* @param $menu_name
* @param $link_ops_to_default
* Even if an item exists, still set display options hidden/expanded/options/customized to default.
* @return
*/
function _taxonomy_treemenu_updatesave_linkroot($menu_name, $link_ops_to_default = FALSE) {
//dpm('tt update rootlink:');
$b = new TTMBranch;
$b->setFromTTMenu($menu_name);
//$b->displayBranch();
// Taxonomy data
$data = $b->getDisplayData();
$item['link_title'] = $data['name'];
$item['weight'] = $data['weight'];
$item['router_path'] = 'ttprnts/%';
// Existing link? Recover the data.
$root_path = $b->asRootPath();
//dpm($root_path);
$existing_item = db_fetch_array(db_query("SELECT mlid, options, customized
FROM {menu_links}
WHERE link_path = '%s' AND menu_name = '%s' AND module = '%s'",
$root_path, $menu_name, 'treemenu'));
if (!$exisitng_item['mlid'] || $set_to_default) {
$item['options'] = serialize(array());
$item['customized'] = 0;
}
if(!$exisitng_item['mlid']) {
db_query("INSERT INTO {menu_links} (
menu_name, plid, link_path, router_path,
expanded, weight, depth,
module, link_title, options
) VALUES (
'%s', %d,'%s','%s',
%d, %d, %d,
'%s', '%s', '%s'
)",
$menu_name, 0, $root_path, $item['router_path'],
1, $item['weight'], 1,
'treemenu', $item['link_title'], serialize(array())
);
$mlid = db_last_insert_id('menu_links', 'mlid');
// Set the parent field.
db_query("UPDATE {menu_links} SET p1=%d WHERE mlid = %d", $mlid, $mlid);
return $mlid;
}
else {
db_query("UPDATE {menu_links} SET
plid = %d, router_path = '%s',
expanded = %d, weight = %d, depth = %d,
p1 = %d, p2 = %d, p3 = %d, p4 = %d, p5 = %d, p6 = %d, p7 = %d, p8 = %d, p9 = %d,
link_title = '%s',
options = '%s', customized = %d
WHERE mlid = %d",
0, $item['router_path'],
1, $item['weight'], 1,
$existing_item['mlid'], 0, 0, 0, 0, 0, 0, 0, 0,
$item['link_title'],
$item['options'], $item['customized'],
$existing_item['mlid']);
return $mlid;
}
}
/**
* Update the children of a menu link that's being moved.
*
* The menu name, parents (p1 - p6), and depth are updated for all children of
* the link, and the has_children status of the previous parent is updated.
*/
function _taxonomy_treemenu_link_move_children($new_item, $existing_item) {
//dpm('ttm link move children');
$i = 1;
while ($i <= $existing_item['depth']) {
$p = 'p'. $i++;
$ph[] = "$p = %d";
$args[] = $existing_item[$p];
}
//$where[] = "menu_name = '%s'";
//$args[] = $existing_item['menu_name'];
$result = db_query('SELECT mlid, link_path, depth, p1,p2,p3,p4,p5,p6,p7,p8,p9 FROM {menu_links} WHERE '. implode(' AND ', $ph), $args);
//dpm('SELECT * FROM {menu_links} WHERE '. implode(' AND ', $ph));
//dpm('links searched for args');
//dpm($args);
// Useful info
//$shift = $existing_item['depth'] - $new_item['depth'] + 1;
$redundant_len = strlen($existing_item['link_path']);
$i = 1;
while ($i <= $new_item['depth']) {
$p = 'p'. $i++;
$new_item_set[] = "$p = %d";
$new_item_args[] = $new_item[$p];
}
$base_count = $new_item['depth'] + 1;
//dpm('base count');
//dpm($base_count);
//dvm($new_item_set);
while ($item = db_fetch_array($result)) {
//dpm('got child item for moving');
//dpm($item['mlid']);
$set = $new_item_set;
$args = $new_item_args;
$j = $base_count;
for ($k = $existing_item['depth'] + 1; $k <= $item['depth']; $k++) {
$p = 'p'. ($j++);
$set[] = "$p = %d";
$args[] = $item['p'.$k];
}
// Depth
$args[] = $j-1;
//dpm('depth');
//dpm($j-1);
while ($j <= MENU_MAX_DEPTH) {
$set[] = 'p'. $j++ .' = 0';
}
$args[] = $new_item['link_path'] . substr($item['link_path'], $redundant_len);
$args[] = $item['mlid'];
//db_query('UPDATE {menu_links} SET p1 =%d, p2 =%d, p3 = %d, p4 = %d, p5 = %d, p6 =%d, p7 =%d, p8 = %d, p9 = %d, depth = %d, link_path = "%s" WHERE mlid = %d', $args);
db_query('UPDATE {menu_links} SET '. implode(', ', $set) .', depth = %d, link_path = "%s" WHERE mlid = %d', $args);
//dpm('UPDATE {menu_links} SET '. implode(', ', $set) .', depth = %d, link_path = "%s" WHERE mlid = %d');
//dpm($args);
}
/*
$args[] = $item['menu_name'];
$set[] = "menu_name = '%s'";
$i = 1;
while ($i <= $item['depth']) {
$p = 'p'. $i++;
$set[] = "$p = %d";
$args[] = $item[$p];
}
$j = $existing_item['depth'] + 1;
while ($i <= MENU_MAX_DEPTH && $j <= MENU_MAX_DEPTH) {
$set[] = 'p'. $i++ .' = p'. $j++;
}
while ($i <= MENU_MAX_DEPTH) {
$set[] = 'p'. $i++ .' = 0';
}
$shift = $item['depth'] - $existing_item['depth'];
if ($shift < 0) {
$args[] = -$shift;
$set[] = 'depth = depth - %d';
}
elseif ($shift > 0) {
// The order of $set must be reversed so the new values don't overwrite the
// old ones before they can be used because "Single-table UPDATE
// assignments are generally evaluated from left to right"
// see: http://dev.mysql.com/doc/refman/5.0/en/update.html
$set = array_reverse($set);
$args = array_reverse($args);
$args[] = $shift;
$set[] = 'depth = depth + %d';
}
$where[] = "menu_name = '%s'";
$args[] = $existing_item['menu_name'];
$p = 'p1';
for ($i = 1; $i <= MENU_MAX_DEPTH && $existing_item[$p]; $p = 'p'. ++$i) {
$where[] = "$p = %d";
$args[] = $existing_item[$p];
}
db_query("UPDATE {menu_links} SET ". implode(', ', $set) ." WHERE ". implode(' AND ', $where), $args);
// Check the has_children status of the parent, while excluding this item.
_menu_update_parental_status($existing_item, TRUE);
*/
_menu_update_parental_status($existing_item, TRUE);
}
/**
* Save a treemenu link.
* A brutal function. It doesn't check for existance, you have to do that
* beforehand. It just loads info into what you supply. It can't handle menu
* roots, either. See the wrapper _taxonomy_treemenu_updatesave_linkroot_raw().
*
* You must supply a tid.
*
* If an mlid is supplied it recovers the present data.
*
* If there is no mlid (or mlid is NULL etc.), it will create a new link.
* If there is no mlid you must supply a plid.
*
* If there is an mlid, the plid is optional. Without it, the function
* assumes no parentage is changing. If there is a plid, the parentage
* is moved to the supplied plid and the path updated.
*
* This function is, however and unhappily, necessary. menu_link_save() makes
* certain sensible assumptions, and is hard to coax into certain actions,
* such as preserving old link data. There are several tweaks here, some
* specific to taxonomy treemenu such as auto path building, but some
* quite general, such as optional overwriting of data.
*
* This function does not need/use these fields,
* - module (is set to 'treemenu')
* - router_path (is set to 'ttprnts/%')
* - external (uses system default)
* - updated (uses system default)
* and if the parameters are usefully supplied, it does not need,
* - menu_name (either recovered or passed in)
* - plid (either recovered or passed in)
* - link_path (either recovered or newly generated)
* - has_children (recovered. If you reparent and move children, is preserved, othewise defaulted)
* - depth (either recovered or newly generated)
* - px items (either recovered or newly generated)
* derived from the tid via taxonomy data load
* - link_title
* - weight
* from the original, defaulted or overwritten with defaults
* - hidden
* - expanded
* - options
* - customized
*
* Put it another way. You have to handle the parameters.
*
* @param $tid
* @param $mlid
* Id of existing menu item. If the parameter is present, the item will be moved
* or updated in some way. If not, you get a new item.
* @param $plid
* Optional in the circumstance that you have an existing item
* and it is not being moved. Course, plid can be valid if 0 - a menu root.
* So you have to explicity pass NULL.
* @param $move_children
* If a link is reparented, do children follow?
* @param $set_to_default
* Set display options hidden/expanded/options/customized to default.
* @return mlid of the created/updated link.
*/
function _taxonomy_treemenu_updatesave_link_raw($tid, $mlid = 0, $plid = NULL, $move_children = FALSE, $set_to_default = FALSE) {
//dpm('ttm updatesave link raw');
$item = array();
$item['module'] = 'treemenu';
$item['router_path'] = 'ttprnts/%';
if (($plid === NULL) && !$mlid) {
dpm('We had no parent and no mlid. Abandon ship!');
$set_to_default = TRUE;
return 0;
}
// Existing item data
if ($mlid) {
// JavaBeans can't do heredocs. Interesting and painful.
// Coders: bear in mind you may need some of this for _menu_link_move_children()
$existing_item = db_fetch_array(db_query("
SELECT menu_name, plid, link_path,
hidden, has_children,
expanded, depth,
p1, p2, p3, p4, p5, p6, p7, p8, p9,
options,
customized
FROM {menu_links} WHERE mlid = %d", $mlid));
// The following will be altered if we reparent.
// So we put the value in the line of fire.
$item['plid'] = $existing_item['plid'];
}
if (!$mlid || $set_to_default) {
$item['hidden'] = 0;
$item['expanded'] = 0;
$item['options'] = serialize(array());
$item['customized'] = 0;
}
// Parentage
if ($plid !== NULL) {
// Parentage new or changing.
//dpm('parenting...');
$parent = db_fetch_array(db_query("SELECT link_path, depth, menu_name,
p1, p2, p3, p4, p5, p6, p7, p8, p9
FROM {menu_links} WHERE mlid = %d", $plid));
// Check and set the depth
if ($parent['depth'] > MENU_MAX_DEPTH - 1) {
return FALSE;
}
$item['depth'] = $parent['depth'] + 1;
// Set the new path. No vocabs in this function.
$item['link_path'] = $parent['link_path'] . '/' . $tid;
// And other data fields relating to the parent.
$item['plid'] = $plid;
$item['menu_name'] = $parent['menu_name'];
// Has children.
// If there's an mlid and we move children, the item knows. Otherwise, zero.
if (!($mlid && $move_children)) {
$item['has_children'] = 0;
}
}
// Taxonomy data
$term = taxonomy_treemenu_get_term($tid);
$item['link_title'] = $term['name'];
$item['weight'] = $term['weight'];
if (!$mlid) {
db_query("INSERT INTO {menu_links} (
menu_name, plid, link_path, router_path,
hidden, has_children,
expanded, weight, depth,
module, link_title, options,
customized) VALUES (
'%s', %d, '%s', '%s',
%d, %d,
%d, %d, %d,
'%s', '%s', '%s',
%d)",
$item['menu_name'], $item['plid'], $item['link_path'], $item['router_path'],
$item['hidden'], $item['has_children'],
$item['expanded'], $item['weight'], $item['depth'],
$item['module'], $item['link_title'], $item['options'],
$item['customized']);
$item['mlid'] = db_last_insert_id('menu_links', 'mlid');
//dpm('ttm link raw - creating new item');
//dpm($item['mlid']);
}
else {
//dpm('updating a known link');
$item = array_merge($existing_item, $item);
db_query("UPDATE {menu_links} SET
menu_name = '%s', plid = %d, link_path = '%s', router_path = '%s',
hidden = %d, has_children = %d,
expanded = %d, weight = %d, depth = %d,
module = '%s', link_title = '%s', options = '%s',
customized = %d WHERE mlid = %d",
$item['menu_name'], $item['plid'], $item['link_path'], $item['router_path'],
$item['hidden'], $item['has_children'],
$item['expanded'], $item['weight'], $item['depth'],
$item['module'], $item['link_title'], $item['options'],
$item['customized'], $mlid);
$item['mlid'] = $mlid;
}
if ($plid !== NULL) {
// New link or not, if we re-parent we need to set the parent fields.
_menu_link_parents_set($item, $parent);
db_query("UPDATE {menu_links} SET
p1 = %d, p2 = %d, p3 = %d, p4 = %d, p5 = %d, p6 = %d, p7 = %d, p8 = %d, p9 = %d
WHERE mlid = %d",
$item['p1'], $item['p2'], $item['p3'], $item['p4'], $item['p5'], $item['p6'], $item['p7'], $item['p8'], $item['p9'],
$item['mlid']);
// If there was an item, and we need the children to move, move them.
if ($mlid && $move_children) {
//dpm('move children - item then existing item');
//dpm($item);
//dpm($existing_item);
// This checks the parent status of wherever the link is moved from.
_taxonomy_treemenu_link_move_children($item, $existing_item);
//_menu_link_move_children($item, $existing_item);
}
// Set the parent's flag for children. Only if we must.
//dpm('Set the parent?');
//dvm($existing_item['has_children']);
if (!$existing_item['has_children']) {
db_query("UPDATE {menu_links} SET has_children = 1 WHERE mlid = %d", $existing_item['plid']);
}
}
return $item['mlid'];
}
/**
* Saves/updates all treemenu links when a taxonomy term is updated.
*
* @param object $term_info
* From hook_taxonomy, includes new parent data.
* @return boolean On sucess or failure of function.
*/
function taxonomy_treemenu_update_term_links($term_info) {
//dpm('ttm update term links:');
//dpm($term_info);
$existing_items = array();
$changed_parents = array();
$reusable_bin = array();
$unused_mlids = array();
// Assert $term_info['parent'], as an array, as it can be a lone item.
$new_parents = array();
if (is_array($term_info['parent'])) {
$new_parents = $term_info['parent'];
}
else {
$new_parents = array( (int) $term_info['parent']);
}
//dpm('new_parents');
//dvm($new_parents);
if (empty($new_parents)){
drupal_set_message(t('No parents in function taxonomy_treemenu_update_term_links.'), 'warning');
return FALSE;
}
// Across treemenus, we need any existing items and the mlids. They all need
// considering.
// Matching old links to new links is nigh on impossible
// as the paths have changed, and may have gained/declined multiple status.
// But we do know all links of this term are available to be used.
// Later on, we'll pop them and save new data into the now redundant framework.
// Confusing? The '%d's are wildcards for db_query().
// The central % is a wildcard for the SQL query itself.
$result = db_query("SELECT menu_name, mlid, link_path FROM {menu_links} WHERE link_path LIKE 'ttprnts/%d%/%d' AND module = '%s'", $term_info['vid'], $term_info['tid'], 'treemenu');
// SELECT mlid, link_path FROM menu_links WHERE link_path LIKE 'ttprnts/2%/4' AND module = 'treemenu';
while ($existing_item = db_fetch_array($result)) {
$existing_items[] = $existing_item;
}
// Now, hook_taxonomy() will throw a hook for a internal data (weight/title etc.) change.
// We'd rather these updates didn't reach any link save function because,
// - they are hefty functions (even if we use our own function)
// - there's a lot of these hooks. Many taxonomy changes introduce re-weighing.
// - if we used menu_link_save(), it can add unwanted side changes, like resetting 'has_children'.
// So we catch internal changes here (hook_taxonomy's 'description' info is silently dropped).
// These changes are menu agnostic.
foreach ($new_parents as $parent) {
//dvm($parent);
// Catch the case where a parent is listed as 0 (i.e. is the vocabulary)
$search = (($parent) ? $parent : $term_info['vid']) . '/' . $term_info['tid'];
//dvm($search);
$parent_link_exists = FALSE;
foreach ($existing_items as &$item) {
//dpm('link data of exisitng item');
//dpm($item);
if (strpos($item['link_path'], $search) !== false) {
//dpm('internal change');
// Update the item data. Note that term weights are not link weights!
// But they work ok.
db_query("UPDATE {menu_links} SET
link_title = '%s', weight = %d WHERE mlid = %d",
$term_info['name'], $term_info['weight'], $item['mlid']);
// Falsify the value so we can remove this item later.
$item = FALSE;
//$updated_mlids[] = $item['mlid'];
$parent_link_exists = TRUE;
}
}
if(!$parent_link_exists) $changed_parents[] = $parent;
}
//dpm('existing items');
//dpm($existing_items);
// Construct an array keyed by menu_name. Also filter for mlids
// which are reusable, as they were not simply updated.
// array_walk() has a bizzare non-functionality, and this is a workround, see,
// http://uk3.php.net/manual/en/function.array-walk.php
// http://bugs.php.net/bug.php?id=19699%C2%A0%C2%A0
array_walk($existing_items, create_function('$v, $k, &$bin', 'if($v) $bin[0][$v["menu_name"]][] = $v["mlid"];'), array(&$reusable_bin));
//dpm('resuable_bin');
//dpm($reusable_bin);
//dpm('changed parents: ');
//dvm($changed_parents);
// Check if there is anything to do now, at all.
// array_walk() has a bizzare non-functionality, and this is a workround, see,
// http://uk3.php.net/manual/en/function.array-walk.php
// http://bugs.php.net/bug.php?id=19699%C2%A0%C2%A0
if (empty($changed_parents)) {
// Check if the user has been deleting parents.
foreach ($reusable_bin as $menu_bin) {
if (!empty($menu_bin)) array_walk($menu_bin, create_function('$v, $k, &$u', '$u[0][] = $v;'), array(&$unused_mlids));
}
if (count($unused_mlids)) {
taxonomy_treemenu_linktree_delete($unused_mlids);
}
return TRUE;
}
// Find links for the parent tids in treemenus, as the information is useful.
// A nested placeholder with wildcards makes this difficult to secure
// using usual Drupal methods.
// TODO: Well, try, why don't you?
$path_root = "link_path LIKE 'ttprnts/". $term_info['vid'];
foreach ($changed_parents as $parent) {
if ($parent == 0) {
$parent_searches[] = $path_root . "'";
}
else {
// Wildcard the centre.
$parent_searches[] = $path_root .'%/' . $parent. "'";
}
}
$placeholder = implode(' OR ', $parent_searches);
//dpm($placeholder);
$result = db_query("SELECT menu_name, mlid, link_path, depth FROM {menu_links} WHERE (". $placeholder .") AND module = '%s'", 'treemenu');
// TODO: If you can find a nicer way, contribute!
while ($parent_item = db_fetch_array($result)) {
// Get an existing item, if there is one, for filling with new data.
$reusable_mlid = (!empty($reusable_bin[$parent_item['menu_name']])) ? array_pop($reusable_bin[$parent_item['menu_name']]) : NULL;
if ($reusable_mlid) {
// This ought to be simple. It's a case of reparenting the link, and
// dragging all the sublinks with it (reflecting what happens in the taxonomy).
//dpm('there is a reusable item');
//dpm($reusable_mlid);
_taxonomy_treemenu_updatesave_link_raw($term_info['tid'], $reusable_mlid, $parent_item['mlid'], TRUE, TRUE);
}
else {
// If the parent only moved, we should have the right number of spare links.
// It appears the user has been adding new parents to the term (or the tree
// is corrupt, but that makes little practical difference). We need new links.
//dpm('there is no reusable item - parents added?');
$mlid = _taxonomy_treemenu_updatesave_link_raw($term_info['tid'], NULL, $parent_item['mlid']);
// ...and we need to grow a tree of any children.
_taxonomy_treemenu_linktree_build($mlid);
}
}
// array_walk() has a bizzare non-functionality, and this is a workround, see,
// http://uk3.php.net/manual/en/function.array-walk.php
// http://bugs.php.net/bug.php?id=19699%C2%A0%C2%A0
foreach ($reusable_bin as $menu_bin) {
if (!empty($menu_bin)) array_walk($menu_bin, create_function('$v, $k, $u', '$u[0][] = $v;'), array(&$unused_mlids));
}
//dpm('tail delete');
//dpm($reusable_bin );
//dpm($unused_mlids);
if (count($unused_mlids)) {
//dpm('There were links left over!');
taxonomy_treemenu_linktree_delete($unused_mlids);
}
return TRUE;
}
/**
* Delete trees by term parentage in a treemenu.
* Don't be fooled by the fact it takes mlids. It finds the treemenu path from
* inside the mlid, then trashes all similar terms with similar parentage,
* heedless of menu name, and all sublinks also.
*
* So it's fairly wild, and mainly intended for taxonomy changes.
* @param array $mlids
*/
// TODO: menu_link_delete() to do the grunt work?
function taxonomy_treemenu_linktree_delete($mlids) {
//dpm('tt delete tree:');
$paths = array();
$plids = array();
$placeholders = db_placeholders($mlids);
$result = db_query("SELECT link_path FROM {menu_links} WHERE mlid IN (" . $placeholders . ") AND module = 'treemenu'", $mlids);
while ($item = db_fetch_array($result)) {
$paths[] = $item['link_path'];
}
$paths = array_unique($paths);
//dpm($paths);
// Get the end two elements from the path
foreach ($paths as &$path) {
$e = array_reverse(explode('/', $path));
$path = "link_path LIKE '%" . $e[1] . "/" . $e[0] . "%'";
}
$where = implode(' AND ', $paths);
//dpm($where);
// Get the plids.
$result = db_query("SELECT plid FROM {menu_links} WHERE " . $where . " AND module = 'treemenu'");
while ($item = db_fetch_array($result)) {
$plids[] = $item['plid'];
}
// Trash the links.
db_query("DELETE FROM {menu_links} WHERE " . $where . " AND module = 'treemenu'");
// Update the has_children status of the parent.
$placeholders = db_placeholders($plids);
db_query("UPDATE {menu_links} SET has_children = 0 WHERE mlid IN (" . $placeholders . ") AND module = 'treemenu'", $plids);
menu_cache_clear_all();
_menu_clear_page_cache();
}
/**
* Autobuild a treemenu of correct taxonomy links on top of an existing mlid.
* If there is nothing in the taxonomy this returns, of course, no links. It's
* mainly intended for creating/updating treemenus.
*
* It uses _taxonomy_treemenu_save_submenu_link(), so it will resuse links where
* possible.
*
* @param $mlid
* @param $depth
* 0 ... 9, 0 returning all levels. Note that this is unrestrained.
* @return Numeric array of all mlids updated or created by the build.
*/
function _taxonomy_treemenu_linktree_build($mlid, $link_ops_to_default = FALSE) {
//dpm('tt build');
$parent_stack = array(0);
$mlid_store = array();
// Get data from the mlid item.
$result = db_query("SELECT link_path, menu_name, depth FROM menu_links where mlid = %d", $mlid);
$root_item = db_fetch_array($result);
// Handle depth.
$depth = TTMData::depth($root_item['menu_name']);
$depth -= $root_item['depth'];
// Get the taxonomy tree descending from this branch.
// get_tree doesn't build with root, from a vocabulary, anyhow...
$b = new TTMBranch;
//dpm($root_item['link_path']);
$b->setFromPath($root_item['link_path']);
//$b->displayBranch();
$tree = taxonomy_get_tree($b->vid, $b->tid, -1, ($depth == 0) ? NULL : $depth);
//dpm($tree);
// Let's go to work.
$parent_stack[] = $mlid_store[] = $mlid;
$old_depth = -1;
$old_path = $root_item['link_path'];
foreach ($tree as $term) {
if ($term->depth <= $old_depth) {
$slashes_to_remove = $old_depth - $term->depth + 1;
for ($i = 0; $i < $slashes_to_remove; $i++) {
$old_path = substr($old_path, 0, strrpos($old_path, '/'));
array_pop($parent_stack);
}
}
$path = $old_path .'/'. $term->tid;
$old_depth = $term->depth;
$old_path = $path;
// Mild check for existing items.
// Will double up a db search for existing items with
// _taxonomy_treemenu_updatesave_link_raw, but then so
// would menu_link_save() (and some).
$existing_item = db_fetch_array(db_query("SELECT mlid
FROM {menu_links}
WHERE link_path = '%s' AND menu_name = '%s' AND module = '%s'",
$path, $root_item['menu_name'], 'treemenu'));
// Force parentage by including plid. Parentage is a little safer off this
// stack, than from existing item. Note that there must always be parents,
// root links are handled elsewhere.
$mlid = _taxonomy_treemenu_updatesave_link_raw($term->tid, $existing_item['mlid'], end($parent_stack), FALSE, $link_ops_to_default);
$parent_stack[] = $mlid_store[] = $mlid;
}
// Sometimes useful - used links.
return $mlid_store;
}
/**
* General purpose createupdate of all links in a treemenu.
* Does a cleanup of any unmatched links.
* Uses
* @param $menu_name
* @param $link_ops_to_default
* Ask the link creator to reset display options
* @return
*/
function taxonomy_treemenu_linktree_all_createupdate($menu_name, $link_ops_to_default = FALSE) {
//dpm('tt linktree createupdate:');
$mlid_store = array();
$root_mlid = _taxonomy_treemenu_updatesave_linkroot($menu_name, $link_ops_to_default);
$mlid_store = _taxonomy_treemenu_linktree_build($root_mlid, $link_ops_to_default);
// The mlid_store contains all the items we have touched.
// If requested (on a rebuild, say, rather than a build) remove all other links.
$placeholders = db_placeholders($mlid_store);
// As well as supplying the query, this is a nice bundle of information to return.
$mlid_store[] = $menu_name;
$mlid_store[] = 'treemenu';
db_query("DELETE FROM {menu_links} WHERE mlid NOT IN (". $placeholders .") AND menu_name = '%s' AND module='%s'", $mlid_store);
menu_cache_clear($menu_name);
_menu_clear_page_cache();
return $mlid_store;
}
/**
* Re-build a named treemenu with interface for the user.
*
* @param $menu_name
* @param $link_ops_to_default
* Ask the link creator to reset the link display options also.
* @return boolean, if the menu_name is valid.
*/
function taxonomy_treemenu_rebuild($menu_name, $link_ops_to_default) {
$tm = array();
// Should be display data?
$tm = taxonomy_treemenu_load($menu_name, FALSE);
$t_args = array('%title' => $menu_name);
if (!$tm) {
drupal_set_message(t('Attempted to update a treemenu, but the name %title is not listed in the taxonomy_treemenu data.', $t_args), 'warning');
return FALSE;
}
taxonomy_treemenu_linktree_all_createupdate($menu_name, $link_ops_to_default);
return TRUE;
}
/*
Rendering details
-----------------
menu_tree($menu_name = 'navigation')
menu tree adapted to current path. Calls menu_page_data > menu_tree_output on the output
if tree not hanging round atached to static.
(skim for now, straight to output)
menu_tree_output $tree
pulls out hidden items
(Believe maybe can handle one of our finished trees, no need to rewrite?)
theme_menu_tree
Used in menu_tree_output
wraps completed output in ul tags.
Other stuff done by theme_menu_item.
menu_tree_data $results/parents
organises data into belows and adds user custom stuff
(will be wierd as we provide no parents, yet, but may work as is...)
USED in
menu_all_data
(needs rewriting...)
menu_page_data
*/
/**
* Find treemenu items by supplied taxonomy references
* If it can't find the term, dosn't give up,
* and resorts to the vocabulary.
*/
//TODO: Currently unused?
function taxonomy_treemenu_menu_items($vid, $tid) {
$result = NULL;
if ($tid != 0) {
//rather more complex if we search for a neutered url...
$placeholder = " (link_path LIKE 'category/" . $vid . "/%/" . $tid . "' OR link_path = 'category/" . $vid . "/" . $tid . "')";
$result = db_query("SELECT * FROM {menu_links} WHERE " . $placeholder . " AND module = '%s'", 'treemenu');
}
if (!$result) {
$placeholder = "link_path = 'category/" . $vid ."'";
$result = db_query("SELECT * FROM {menu_links} WHERE " . $placeholder . " AND module = '%s'", 'treemenu');
//dpm($placeholder);
}
//dpm($placeholder);
//$result = db_query("SELECT * FROM {menu_links} WHERE " . $placeholder . " AND module = '%s'", 'treemenu');
return $result;
}
/**
* Find a treemenu item using the menu_name and link_path.
*
* If (and only if) the path is a treemenu path, the result is unambiguous, as
* the path includes the item parents.
*/
// Unused. Unsure about the future?
function taxonomy_treemenu_menu_item($menu_name, $link_path) {
$result = db_query("SELECT * FROM {menu_links} WHERE link_path = '%s' AND menu_name = '%s' AND module = '%s'", $link_path, $menu_name, 'treemenu');
return $result;
}
////////////////////
// Link Renderers //
////////////////////
/**
* Format an internal treemenu link.
*
* @param $text
* @param $path
* @param $options
* @param $page_or_term_link
* Treemenu page and term links get treated seperately. Mainly internal parameter.
* @param $pt_link_active
* Override for page and term links for the 'active' test.
* @return
* HTML of a treemenu link.
*/
function taxonomy_treemenu_l($text, $path, $options = array(), $page_or_term_link = FALSE, $pt_link_active = FALSE) {
// Merge in defaults.
$options += array(
'attributes' => array(),
'html' => FALSE,
);
// Append active class.
if ((!$page_or_term_link && ($path == $_GET['q'])) || $pt_link_active || ($path == '' && drupal_is_front_page())) {
if (isset($options['attributes']['class'])) {
$options['attributes']['class'] .= ' active';
}
else {
$options['attributes']['class'] = 'red active';
}
}
// Remove all HTML and PHP tags from a tooltip. For best performance, we act only
// if a quick strpos() pre-check gave a suspicion (because strip_tags() is expensive).
if (isset($options['attributes']['title']) && strpos($options['attributes']['title'], '<') !== FALSE) {
$options['attributes']['title'] = strip_tags($options['attributes']['title']);
}
return ''. ($options['html'] ? $text : check_plain($text)) .'';
}
/**#
* Generate the HTML output for a single treemenu link.
*
* @param $link
* From a menu tree.
* @param $url_tid_str
* Used for detecting active items in menus. Of form '/[tid]'.
* @return
* HTML of a menu link.
* @ingroup themeable
*/
//TODO: Redundant?
function theme_taxonomy_treemenu_item_link($link, $url_tid_str = "") {
if (empty($link['localized_options'])) {
$link['localized_options'] = array();
}
// This target_tid info is needed because term and page links need to know they
// have the same target (a term). Page links can recognize themselves in the
// URL query, but a term link in a block has to know that a page might be
// targeting the same term. Also nice for avoiding highlighting two links if
// a link is duplicated. So on /term/ links, we look for the tid.
$page_or_term_link = ($link['router_path'] == 'taxmenu/%/term/%/%') || ($link['router_path'] == 'taxmenu/%/page/%/%');
$pt_link_active = (($link['target_tid_str'] == $url_tid_str) && $page_or_term_link) ;
return taxonomy_treemenu_l($link['title'], $link['href'], $link['localized_options'], $page_or_term_link, $pt_link_active);
}
///////////////
// Renderers //
///////////////
/**
* Takes a (tree)menu tree, and renders it,
* handling disabled items,
* and calling the treemenu link themes.
*
* @param $tree
* A data structure representing the tree as returned from menu_tree_data.
* @return
* The rendered HTML of that data structure.
*/
//TODO: still in use, and needs looking at?
//How to do this?
//There is the idea of rewriting the anchor so it goes nowhere.
//Unfortunately, at this point Drupal will filter and rewrite the text, filter_xss_bad_protocol()
//so we can't use the simple bodge of href ="#" (becomes "%23")
//and a null entry become site home! (good on you, Drupal, my pal!)
//So we have to duplicate the menu function structure anyway.
function taxonomy_treemenu_tree_output($tree) {
$output = '';
$items = array();
//For tid link activating.
$url_tid = $_GET['q'];
$url_tag = substr($url_tid, strrpos($url_tid, '/'));
// Pull out just the menu items we are going to render so that we
// get an accurate count for the first/last classes.
foreach ($tree as $data) {
if (!$data['link']['hidden']) {
$items[] = $data;
}
}
$num_items = count($items);
foreach ($items as $i => $data) {
$extra_class = NULL;
if ($i == 0) {
$extra_class = 'first';
}
if ($i == $num_items - 1) {
$extra_class = 'last';
}
$link = theme('taxonomy_treemenu_item_link', $data['link'], $url_tag);
if ($data['below']) {
$output .= theme('menu_item', $link, $data['link']['has_children'], taxonomy_treemenu_tree_output($data['below']), $data['link']['in_active_trail'], $extra_class);
}
else {
$output .= theme('menu_item', $link, $data['link']['has_children'], '', $data['link']['in_active_trail'], $extra_class);
}
}
return $output ? theme('menu_tree', $output) : '';
}
function theme_taxonomy_treemenu_root_link($link_title) {
return $link_title .= " (submenu)";
}
/**
* Modifies tree links. If a link points to the same branch
* as a treemenu base, the link is changed to point straight at the treemenu.
*
* @param $tree
* @param $treemenu_data
* Passed in as it is probably available. Should have the menu data for the
* tree unset(), or it will point links at itself, which is wasteful, if harmless.
*/
//TODO: This needs keeping and moifying to V5
function taxonomy_treemenu_rootpath_mods(&$tree, &$treemenu_data) {
//dpm('link root paths:');
//dpm($treemenu_data);
foreach ($tree as $i => &$data) {
if ($data['link']['router_path'] == 'taxmenu/%/page/%/%') {
$link_path = $data['link']['link_path'];
$link_tid = substr($link_path, strrpos($link_path, '/') + 1);
foreach ($treemenu_data as $menu_name => $menu_data) {
// A term can only be in one vocab, so we don't need to check vid.
if ($link_tid == $menu_data['tid']) {
//The link is also a root item from a menu.
//dpm('linking...');
//dpm($data['link']);
$data['link']['title'] = theme('taxonomy_treemenu_root_link', $data['link']['title']);
$data['link']['href'] = 'taxmenu/' . $menu_name . '/page/';
$data['link']['localized_options']['html'] = TRUE;
}
}
}
if ($data['below']) {
taxonomy_treemenu_rootpath_mods($data['below'], $treemenu_data);
}
}
}
/********************************
Utility funtions for tree modding
*********************************/
function taxonomy_treemenu_tree_level_lockweight_sink(&$tree_level, $i = 0) {
foreach($tree_level as &$item) {
$item['link']['weight'] = $i;
$i++;
}
}
/**
* Count descendant nodes from a branch.
* If tid = 0 then it's a vocabulary.
*/
// TO CONSIDER: Unused. Leave as may be useful later.
function taxonomy_treemenu_count_descendant_nodes($vid, $tid) {
$tids = array();
$nodes = array();
if ($tid == 0) {
//vocab
$tids[0] = $tid;
}
else {
$tids[0] = $tid;
}
$placeholders = db_placeholders($tids, 'int');
//We could use taxonomy_select_nodes(), but it's very heavy for our simple needs.
//stock 'or' query...
$sql = 'SELECT COUNT(DISTINCT(n.nid)) FROM {node} n INNER JOIN {term_node} tn ON n.tid = tn.tid WHERE tn.tid IN ('. $placeholders .') AND n.status = 1';
return db_result(db_query(db_rewrite_sql($sql), $tids));
}
/*just a dev utility function */
function taxonomy_treemenu_dpm_result($result) {
while ($item = db_fetch_object($result)) {
dpm($item);
}
}
/**
* Get descendant nodes of any term.
*
* @param $tid
* If tid = 0 then it's a vocabulary, which will return an empty query.
* @param $options
* Unserialized array of treemenu options.
* @return a query with critical data.
*/
// TODO: leaving for now, but should e move to options class and rewritten?
function taxonomy_treemenu_select_descendant_nodes($tid, $options) {
//dpm('tt select descendant nodes:');
//$sort_option = taxonomy_treemenu_options_get_radios_key($options, 'sort');
//dvm($options);
//$order_tag = taxonomy_treemenu_node_sort_query_tag($options['sort']);
//We could use taxonomy_select_nodes(), but its very heavy for our simple
//needs. Now, anyway.
//stock 'or' query...
$sql = 'SELECT DISTINCT(n.nid), n.sticky, n.title, n.created FROM {node} n INNER JOIN {term_node} tn ON n.vid = tn.vid WHERE tn.tid = %d AND n.status = 1 ORDER BY ' . $order_tag;
//dpm($sql);
return db_query(db_rewrite_sql($sql, 'n', 'nid'), $tid);
}
/**
*
* @param $tid
* @param $opts
* @return
* A resource identifier pointing to the query results.
*/
function taxonomy_treemenu_select_descendant_nodes2($ttm, $tids, $pager = FALSE) {
//dpm('tt select descendant nodes:');
//dvm($opts);
//dvm($tids);
$sql = TTMOptsQuery::nodes($ttm, $tids);
//dpm($args);
//dvm($sql);
if ($pager) {
$items_per_page = $ttm['term_as_links'] ? variable_get('default_nodes_taxonomy_treemenu_main', 30) : variable_get('default_nodes_main', 10);
// $result = pager_query($sql['query'], variable_get('default_nodes_main', 10), 0, $sql['count'], $sql['args']);
$result = pager_query($sql['query'], $items_per_page, 0, $sql['count'], $sql['args']);
}
else {
$result = db_query($sql['query'], $sql['args']);
//$result = db_query_range($sql['query'], $args, 0, variable_get('feed_default_items', 10));
}
//You could cull a node count here, which is efficient...
//dvm($pager_total);
return $result;
}
/**
* Duplicates a menu element into a subbranch of a tree.
* If the renderer uses a link for expansion, this allows an option to go to the
* link itself. DHTML menu has, or had, a similar solution.
* This comes after nodetree modes, so must duplicate /path/ urls.
*
* @param $branch_element
* A sub-branch from a menu tree. ['link_path']s must be /category/ links.
* @param $menu_name
* Needed for link building?
*/
// TO CONSIDER: Unused. May be useful, but requires rebuilding.
function _taxonomy_treemenu_duplicate_element(&$branch_element, $menu_name) {
//dpm('duplicate element:');
//dpm($branch_element);
$tree_element = array();
$dup_element = array();
$dup_element['link'] = $branch_element['link'];
//Adjust the various elements, which are not quite correct.
//Keep the path where it is. This means expansion and breadcrumbs stay as the
//user and functions expect. It ought to wreak havoc on active link
//highlighting, but we search for active links using term tags, not by path.
// taxonomy_treemenu_pathlink_to_termlink($dup_element['link'], taxonomy_treemenu_select_router_item('tax_term'));
//We want the dup element to be active, not the branch element. Removing the
//'target_tid_str' from the branch is a fast solution.
$branch_element['link']['target_tid_str'] = "";
$dup_element['link']['has_children'] = FALSE;
$dup_element['below'] = FALSE;
$new_depth = $dup_element['link']['depth'] + 1;
$dup_element['link']['depth'] = (string) $new_depth;
$dup_element['link']['in_active_trail'] = FALSE;
//$mlid = 'D' . $branch_element['link']['mlid'];
$mlid = $branch_element['link']['mlid'];
$tree_element[$mlid] = $dup_element;
if ($branch_element['below']) {
//dpm('$tree_element');
//dpm($branch_element);
//$branch_element['below'] = array_merge($tree_element, $branch_element['below']);
//TODO:failed attempt to make first in the list?
$branch_element['below'] = $tree_element + $branch_element['below'];
}
else {
$branch_element['below'] = $tree_element;
}
$branch_element['link']['has_children'] = TRUE;
//dpm($dup_element);
}
/**
* Switch on HTML for a tree link.
*
* @param $link_options
* Serialised options element from a tree link.
*/
function taxonomy_treemenu_options_html(&$link_options) {
$ops = unserialize($link_options);
$ops['html'] = TRUE;
$link_options = serialize($ops);
}
/*******************************
* Functions for no-node trees.
*******************************/
/*
Tried amalgamating them, but PHP doesn't seem to like
the extension of the loop inside the foreach()
(though it works with the nodes mod routine?)
*/
/**
* Duplicate items in a menu tree. Produces duplicate /term/ links from /term/
* or /category/ links.
*
* @param $tree
* @param $menu_name
* Needed for link building?
* @param $mlid
* Of item to duplicate. If zero, duplicates all non-leaf items.
*/
// TO CONSIDER: Code from the past, for the furture?
function taxonomy_treemenu_tree_dup_expansion_item(&$tree, $menu_name, $mlid) {
foreach ($tree as &$item) {
if ($item['link']['mlid'] == $mlid) {
//dpm('****Found the target expand item****');
//_taxonomy_treemenu_duplicate_element($item);
_taxonomy_treemenu_duplicate_element($item, $menu_name);
break;
}
elseif ($item['below'] && $mlid == 0) {
_taxonomy_treemenu_duplicate_element($item, $menu_name);
}
if ($item['below']) {
taxonomy_treemenu_tree_dup_expansion_item($item['below'], $menu_name, $mlid);
}
}
}
/********************************************************
* Support functions for modifying taxonomy treemenu trees.
*********************************************************/
/**
* Convert a taxonomy tree item's path/route (/ttprnt) into a
* usable (stock or extended) url.
* @param $item_link
* @param $vocabulary
* @param $tid
* @param $item_base_path
* @param $item_route
*/
function taxonomy_treemenu_modify_term_link(&$item_link, $vocabulary, $tid, $item_base_path, $item_route) {
// Give any module with a hook_term_path() a say.
// Course, it would be good to use taxonomy_term_path(),
// but that returns stock /taxonomy/term/x paths, which is a pain to inject
// with extended URLs.
if ($vocabulary->module != 'taxonomy' && $path = module_invoke($vocabulary->module, 'term_path', taxonomy_get_term($tid))) {
$item_link['link_path'] = $path;
$item_link['router_path'] = _menu_find_router_path($path);
}
else {
$item_link['link_path'] = $item_base_path . $tid;
$item_link['router_path'] = $item_route;
}
}
/************************************
* Functions supporting tree building
************************************/
/**
* Check access and perform other dynamic operations for each link in the tree.
*/
/*
* Note:
* Why do we have to alias these two funtions? Because the originals do a ksort.
* This is based on weight then title then mlid, which we do not want. We have
* our own sort methods.
*
* That's the only difference.
*
* (We could do the sorting here, as it happens, but it's faster, if lacking
* PHP flexibility, to sort in the initial database retrieval, then preserve
* the order here through the access check and translate.)
*/
function taxonomy_treemenu_tree_check_access(&$tree, $node_links = array()) {
if ($node_links) {
// Use db_rewrite_sql to evaluate view access without loading each full node.
$nids = array_keys($node_links);
$placeholders = '%d'. str_repeat(', %d', count($nids) - 1);
$result = db_query(db_rewrite_sql("SELECT n.nid FROM {node} n WHERE n.status = 1 AND n.nid IN (". $placeholders .")"), $nids);
while ($node = db_fetch_array($result)) {
$nid = $node['nid'];
foreach ($node_links[$nid] as $mlid => $link) {
$node_links[$nid][$mlid]['access'] = TRUE;
}
}
}
_taxonomy_treemenu_tree_check_access($tree);
return;
}
/**
* Recursive helper function for menu_tree_check_access()
*/
function _taxonomy_treemenu_tree_check_access(&$tree) {
$new_tree = array();
foreach ($tree as $key => $v) {
$item = &$tree[$key]['link'];
_menu_link_translate($item);
if ($item['access']) {
if ($tree[$key]['below']) {
_taxonomy_treemenu_tree_check_access($tree[$key]['below']);
}
// The weights are made a uniform 5 digits by adding 50000 as an offset.
// After _menu_link_translate(), $item['title'] has the localized link title.
// Adding the mlid to the end of the index insures that it is unique.
//$new_tree[(50000 + $item['weight']) .' '. $item['title'] .' '. $item['mlid']] = $tree[$key];
$new_tree[(50000 + $item['weight']) .' '. $item['mlid']] = $tree[$key];
}
}
// Sort siblings in the tree based on the weights and localized titles.
ksort($new_tree);
$tree = $new_tree;
}
/**
* Get the toplevel mlid of any taxonomy treemenu.
* Treemenus can't use parent 0, as that would return the tree root. This gets
* the visible root mlid. Treemenus are grown from a sole branch, so there is
* only ever one item with p2 = 0.
*
* @param $menu_name
* @return The root mlid(from parent p1)
*/
//TO CONSIDER: I seem to be keeping this because I'm fond of it, or something.
function taxonomy_treemenu_toplevel_mlid($menu_name) {
$mlid = db_result(db_query("SELECT p1 FROM {menu_links} WHERE menu_name = '%s' AND p2 = 0", $menu_name));
return $mlid;
}
/***************
* New link mods
**************/
// Note: I'd prefer this to be non-recursive, i.e. tackle the database return,
// not the tree of links. However, this is not a tidy idea, as core
// retrieves and treebuilds all-in-one, in _menu_tree_data(), so we'd have to
// override all that.
function _taxonomy_treemenu_tree_append_node_links(&$item, $ttm, $tid, $item_base_path, $item_route, $reset = FALSE) {
//dpm("append node items:");
// The purpose of putting the mlid into a key is as a simple id.
// We dont have an mlid, we're inventing these links, which seems an awkward
// problem, until we realise the id doesn't have to be numeric...
static $unique_num = 0;
if ($reset) {$unique_num = 0; return; };
$result = taxonomy_treemenu_select_descendant_nodes2($ttm, array($tid));
$node_element = array();
$tree_elements = array();
$i=0;
while ($node_element = db_fetch_array($result)) {
// Fake an mlid.
$mlid = 'N' . $unique_num++;
// These items are the crutial ones when it comes to rendering.
// (the others are relevant only to
// menu organisation/customisation, which doesn't affect us)
$node_element['mlid'] = $mlid;
$node_element['link_path'] = $item_base_path . $node_element['nid'];
$node_element['router_path'] = $item_route;
$node_element['link_title'] = $node_element['title'];
// Weight is relevant. We want to preseve the link order, which we have
// derived via the custom SQL, so we simply ascend the weight.
$node_element['weight'] = $i;
//Stack them up on the heap.
$tree_elements[$mlid] = array('link' => $node_element, 'below' => FALSE);
$i++;
}
if (!empty($tree_elements)) {
if ($item['below']) {
// Sink the weight of existing links, then append node links.
taxonomy_treemenu_tree_level_lockweight_sink($item['below'], $i);
$item['below'] = array_merge($tree_elements, $item['below']);
}
else {
$item['below'] = $tree_elements;
}
$item['link']['has_children'] = TRUE;
}
}
/**
* Builds some model paths/routes for various treemenu options, then calls the
* recursive helper to modify the links in the tree.
* @param $tree
* @param $ttm
* @param $mlid
*/
function taxonomy_treemenu_tree_modify_links(&$tree, $ttm, $mlid) {
//dpm('tree modify links');
$n_path = '';
$n_route = '';
$t_path = '';
$t_route = '';
if ($ttm['prefix_urls']) {
$n_path = $n_route = 'ttm';
$t_path = $t_route = 'ttm';
}
if ($ttm['menu_urls']) {
$tag = '/'. $ttm['menu_name'];
$n_path .= $tag;
$t_path .= $tag;
$n_route .= '/%taxonomy_treemenu';
$t_route .= '/%taxonomy_treemenu';
}
if ($n_path) {
$n_path .= '/node/';
$t_path .= '/term/';
//$n_route .= '/node/%taxonomy_treemenu_nid';
//$t_route .= '/term/%taxonomy_treemenu_tid';
$n_route .= '/node/%taxonomy_treemenu_id';
$t_route .= '/term/%taxonomy_treemenu_id';
}
else {
// No prefixes, so use stock Drupal paths.
$n_path .= 'node/';
$n_route .= 'node/%';
$t_path .= 'taxonomy/term/';
$t_route .= 'taxonomy/term/%';
}
//dpm($t_path);
//dpm($t_route);
_taxonomy_treemenu_tree_modify_links($tree, $ttm, $mlid, $n_path, $n_route, $t_path, $t_route);
//dpm($tree);
}
/**
* Recursive helper that modifies a tree for cusomized paths and routes.
* @param $tree
* @param $ttm
* Treemenu data, used for pulling options.
* @param $mlid
* ?
* @param $np
* @param $nr
* @param $tp
* @param $tr
*/
function _taxonomy_treemenu_tree_modify_links(&$tree, $ttm, $mlid, $np, $nr, $tp, $tr) {
// Reset the node linkbuilder
//_taxonomy_treemenu_tree_append_stock_nodes(&$tree, "", TRUE);
$voc = taxonomy_vocabulary_load($ttm['vid']);
$depth = ($ttm['depth'] == 0) ? MENU_MAX_DEPTH : $ttm['depth'];
foreach ($tree as &$item) {
/*
if ($item['link']['mlid'] == $mlid) {
//dpm('****Found the target expand item****');
//_taxonomy_treemenu_duplicate_element($item);
_taxonomy_treemenu_duplicate_element($item, $menu_name);
break;
}
elseif ($item['below'] && $mlid == 0) {
//dpm('duplicate element');
//dpm($item['link']['link_path']);
_taxonomy_treemenu_duplicate_element($item, $menu_name);
}
*/
// Extract the tid from the path.
$index_tail = strrpos($item['link']['link_path'], '/');
$index_tail++;
$tid = substr($item['link']['link_path'], $index_tail);
$dts = array();
//taxonomy_treemenu_modify_term_link($item['link'], $voc, $tid, $tp, $tr);
// Modify term links from base data to working links.
// Give any module with a hook_term_path() a say.
if ($voc->module != 'taxonomy' && $path = module_invoke($voc->module, 'term_path', taxonomy_get_term($tid))) {
$item['link']['link_path'] = $path;
$item['link']['router_path'] = _menu_find_router_path($path);
}
else {
$item['link']['link_path'] = $tp . $tid;
$item['link']['router_path'] = $tr;
if ($ttm['show_term_descendants']) {
//dpm('term descendants too!');
$b = new TTMBranch();
$b->set($ttm['vid'], $tid);
//dvm($depth);
//dvm($item['link']['depth']);
//dpm($depth - $item['link']['depth'] + 1);
//dpm($b->descendantTids($depth - $item['link']['depth'] + 1));
//dpm($item['link']['link_title']);
$dts = $b->descendantTids($depth - $item['link']['depth'] + 1);
if (!empty($dts)) {
$item['link']['link_path'] .= '+' . implode('+', $dts);
}
}
}
if ($item['below']) {
_taxonomy_treemenu_tree_modify_links($item['below'], $ttm, $mlid, $np, $nr, $tp, $tr);
}
if ($ttm['nodes']) {
_taxonomy_treemenu_tree_append_node_links($item, $ttm, $tid, $np, $nr);
}
// Add links? add counts? See _taxonomy_treemenu_tree_modify_termlink_fields() below.
// Node counts BUT NOT NODES ON TERMS, add descendant nodes, if enabled.
if ($ttm['node_count']) {
$dts[] = $tid;
$sql = TTMOptsQuery::nodes($ttm, $dts);
$node_count = db_result(db_query($sql['count'], $sql['args']));
$item['link']['link_title'] .= ' (' . $node_count . ')';
taxonomy_treemenu_options_html($item['link']['options']);
}
}
}
// TODO: unfinished function! Currently unused!
function _taxonomy_treemenu_tree_modify_termlink_fields(&$tree, $ttm) {
//add node count display, if enabled
if ($settings['leaf_node_count']) {
$item['link']['link_title'] .= ' (' . $node_count . ')';
taxonomy_treemenu_options_html($item['link']['options']);
}
}
/******************
* Tree Builders
*****************/
/**
* Internal function for adding forcibly expanded link mlids to db_query()
* parameters.
*/
function _taxonomy_treemenu_expanded_links_args($menu_name, &$placeholders, &$args) {
do {
$result = db_query("SELECT mlid FROM {menu_links} WHERE menu_name = '%s' AND expanded = 1 AND has_children = 1 AND plid IN (". $placeholders .') AND mlid NOT IN ('. $placeholders .')', array_merge(array($menu_name), $args, $args));
$num_rows = FALSE;
while ($item = db_fetch_array($result)) {
$args[] = $item['mlid'];
$num_rows = TRUE;
}
$placeholders = db_placeholders ($args, 'int'); //implode(', ', array_fill(0, count($args), '%d'));
} while ($num_rows);
}
/**
* Get a menu tree (a menu tree is the link data, reconstructed into an array tree).
* Not cached
*
* Give it type 0 and it returns a full_data tree.
* Give it type 1 and it returns depending on the existance of a valid item.
* @param $menu_name
* @param $item
* If the $item != 0 returns a tree expanded up a trail, otherwise returns the
* items which are direct children of the root.
* @return
*/
function _taxonomy_treemenu_get_tree($menu_name, $item = NULL) {
//dpm('ttm get tree');
// For a treemenu, parent 0 should not appear in the array,
// (we drop the base item). However, keeping this decalaration
// means array_unique() always places the 0 at the array start,
// so we can simply shift it off afterwards.
$args = array(0);
$parents = array();
//dpm($item);
if (is_null($item)) {
// All_data menu.
// Get all links in this menu.
// i.e. everything, but NOT parent 0
//dpm('All menu data');
$where = ' AND ml.plid NOT IN (%d)';
}
else {
if ($item['mlid']) {
//dpm('mliding:');
//dpm($item);
// Dynamic menu, one which only shows links up a trail.
// We need to match the values in its p columns and 0 (the top level)
// with the plid values of other links.
for ($i = 1; $i < MENU_MAX_DEPTH; $i++) {
$args[] = $item["p$i"];
}
//dvm($item);
//dvm($args);
//$parents is only used for identifying the active trail,
$parents = $args = array_unique($args);
//dvm($parents);
//$parents = array_values($args);
//For nice rebuilding of treemenus, treemenu makes the base item into a link.
//But the user will not want to see that, so we shift the zero out of the args.
array_shift($args);
//dvm($args);
$placeholders = db_placeholders($args,'int');
// Check for forcibly expanded items
//_taxonomy_treemenu_expanded_links_args($menu_name, $placeholders, $args);
//dvm($args);
////$where = ' AND ml.plid IN ('. $placeholders .')';
// so not as critical as it may appear. See _menu_tree_data().
//$parents[] = $item['mlid'];
}
else {
// Default dynamic menu.
// Get all links with parent as the root (i.e.) in this menu.
// i.e. the sole parent NOT = 0
//
// Check for forcibly expanded items
$where = ' AND ml.plid IN (%d)';
$args[0] = $item['mlid'];
$placeholders = '%d';
}
// Check for forcibly expanded items
_taxonomy_treemenu_expanded_links_args($menu_name, $placeholders, $args);
$where = ' AND ml.plid IN ('. $placeholders .')';
//dpm($where);
}
// Prepend the menu name.
array_unshift($args, $menu_name);
// Select the links from the table, and recursively build the tree. We
// LEFT JOIN since there is no match in {menu_router} for an external
// link.
// Treemenu note: We drop the bottom level element of the menu,
// so the menu_tree_data parameter 'depth' is set to 2.
// Not critical, but prevents a useless function call.
//dpm($parents);
//dpm('args:');
//dpm($args);
return menu_tree_data(db_query("
SELECT ml.mlid, m.load_functions, m.to_arg_functions, m.access_callback, m.access_arguments, m.page_callback, m.page_arguments, m.title, m.title_callback, m.title_arguments, m.type, m.description, ml.*
FROM {menu_links} ml LEFT JOIN {menu_router} m ON m.path = ml.router_path
WHERE ml.menu_name = '%s'". $where ."
ORDER BY p1 ASC, p2 ASC, p3 ASC, p4 ASC, p5 ASC, p6 ASC, p7 ASC, p8 ASC, p9 ASC", $args), $parents, 2);
}
/**
* Gather node links.
* The mildest of modifications here adds our own node paths,
* and extracts the nid directly from te link information.
* @param $tree
* @param $node_links
*/
function taxonomy_treemenu_tree_collect_node_links(&$tree, &$node_links) {
foreach ($tree as $key => $v) {
$rp =$tree[$key]['link']['router_path'];
if ($rp == 'node/%' || $rp == '/ttm/node%' || $rp == 'ttm/%taxonomy_treemenu/node/%taxonomy_treemenu_id') {
//$nid = substr($tree[$key]['link']['link_path'], 5);
$nid = $tree[$key]['link']['nid'];
if (is_numeric($nid)) {
$node_links[$nid][$tree[$key]['link']['mlid']] = &$tree[$key]['link'];
$tree[$key]['link']['access'] = FALSE;
}
}
if ($tree[$key]['below']) {
taxonomy_treemenu_tree_collect_node_links($tree[$key]['below'], $node_links);
}
}
}
/**
* Get a link and fields useful for menu targeting.
*
* @param $mlid
* @return
*/
function taxonomy_treemenu_get_link($mlid) {
return db_fetch_array(db_query("
SELECT menu_name, mlid, plid, link_path,
link_title,
p1, p2, p3, p4, p5, p6, p7, p8, p9
FROM {menu_links} WHERE module ='%s' AND mlid = %d", 'treemenu', $mlid));
}
/**
* Cache treemenus. Not much of anything, but cleans up the functions.
*
* Do call sets only after a get, as the class carries the cid.
*/
class _TTMTreeCache
{
static $tree = array();
static $cid;
static function setStatic($tree) {
//dpm('set static:');
//dvm(self::$cid);
$tree[self::$cid] = $tree;
}
static function setCache($tree) {
//dpm('set cache');
//dvm(self::$cid);
cache_set(self::$cid, $tree, 'cache_menu');
}
/**
* Retrieve treemenu tree cache, if any.
*
* If mlid is 0, returns the default menu.
*
* @param $menu_name
* @param $mlid
* @return Cached data, or NULL.
*/
static function get($menu_name, $mlid = NULL) {
// Generate a cache ID (cid) specific for this $menu_name and $mlid.
// Namespace also, though only a hacker would get near it.
$static_tag = (is_null($mlid)) ? 'all-': '';
self::$cid = 'links:'. $menu_name .'-ttm:'. $static_tag .'cid:'. $mlid;
//dpm(self::$cid);
if (!($data = self::$tree[self::$cid])) {
// If the static variable doesn't have the data, check {cache_menu}.
//$data = cache_get(self::$cid, 'cache_menu');
}
return $data;
}
}
/** NEW
* Get the data structure representing a named menu tree. Cached.
*
* Expanding menus default to their root visual items. Full_data menus default to
* the entire menu.
*
* Only applies to block menus.
*
* @param $ttm
* @param $mlid
* If this is there, its an expanded menu. If 0 its a unexpanded menu,
* and if NULL its a full data menu.
* @return
*/
function taxonomy_treemenu_tree_data($ttm, $mlid = NULL) {
//$mlid = 2031;
$item = ($mlid) ? taxonomy_treemenu_get_link($mlid) : $mlid;
$tree = _TTMTreeCache::get($ttm['menu_name'], $mlid);
//dpm($item);
if (empty($tree)) {
//dpm('call');
$tree = _taxonomy_treemenu_get_tree($ttm['menu_name'], $item);
//Then mods...
//dpm('tree pre mods:');
//dvm($ttm);
//dvm($ttm['ttm_paging']);
//if (!$ttm['nodes'] && !$ttm['ttm_paging']) {
//}
//if (!$ttm['ttm_paging']) {
//taxonomy_treemenu_tree_stock_links($tree, $ttm, $mlid);
taxonomy_treemenu_tree_modify_links($tree, $ttm, $mlid);
//}
//if
//dpm('tree after mods:');
//dpm($tree);
taxonomy_treemenu_tree_collect_node_links($tree, $node_links);
//dpm('node links');
//dpm($node_links);
_TTMTreeCache::setCache($tree);
}
//check access
taxonomy_treemenu_tree_check_access($tree, $node_links);
//dpm('tree after access:');
//dpm($tree);
_TTMTreeCache::setStatic($tree);
return $tree;
}
function theme_taxonomy_treemenu_page($ttm) {
$output = '
\n";
$output .= '
'. $ttm['title'] ."
\n";
if ($ttm['description']) {
$output .= '
'. $ttm['description'] ."
\n";
}
$output .= '
'. $ttm['body'] ."
\n
\n";
return $output;
}
// Note:
// In core, Drupal caches tree output through
// menu_tree(), for reasons I am yet to fathom (multiple menus on one page?)
// We don't want to do that, anyhow, as we may have multiple versions of a menu
// on a page.
// TO CONSIDER: This is currently looking redundant, but what of expanding menus?
function taxonomy_treemenu_output($ttm, $mlid = NULL) {
$tree = taxonomy_treemenu_tree_data($ttm, $mlid);
//dpm('output tree:');
//dpm($tree);
return menu_tree_output($tree);
}
function taxonomy_treemenu_render_block($menu_name) {
// Cache this output
static $menu_output = array();
if (!isset($menu_output[$menu_name])) {
$ttm = taxonomy_treemenu_load($menu_name, FALSE);
// Later, we may derive the mlid from the URL,
// or maybe recieve directly. For a static menu...
$mlid = NULL;
$menu_output[$menu_name] = taxonomy_treemenu_output($ttm, $mlid);
//dpm($menu_output[$menu_name]);
}
return $menu_output[$menu_name];
}
function taxonomy_treemenu_render_page($ttm, $mlid = NULL) {
// DHTML menus.
if($ttm['dhtml_pages']) {
$output = theme('dhtml_menu_tree', taxonomy_treemenu_tree_data($ttm));
}
else {
$output = taxonomy_treemenu_output($ttm, $mlid);
}
$ttm['body'] = $output;
return theme('taxonomy_treemenu_page', $ttm);
}
/**
* Parse and load a url page string.
*
* Parses for the basic machine name, does some safety checks,
* if ok, loads the menu custom data.
*/
/*
We're checking for menu names, so we borrow the regexp form from menu_edit_menu_validate().
We then mimic the form of taxonomy_terms_parse_string() for the parsing code.
This is a very hefty check, as the strings are then cross checked against existing menu names
in the variable, which have, of course, been validated through Drupal core.
*/
/*
function taxonomy_treemenu_parse_menu_str(old version)($str_menus) {
$menu_names = array();
$str_parts = array();
//In a url, '+' can become ' ', so we parse for all
if (preg_match('/^([a-z0-9-]+[+, ])*[a-z0-9-]+$/', $str_menus)) {
$str_parts = preg_split('/[+, ]/', $str_menus);
$tmd = variable_get('taxonomy_treemenu_data', array());
foreach ($str_parts as $menu_name) {
$menu_name = 'menu-' . $menu_name;
if (!empty($tmd[$menu_name])) {
$menu_names[] = $menu_name;
}
}
}
return $menu_names;
}
*/
/****************************
* Preprocessing and themes
***************************/
// Testing only
/*
function taxonomy_treemenu_preprocess_page(&$v) {
dpm('ttm preprocess page:');
dpm($v);
}
*/
/*
function taxonomy_treemenu_preprocess_node(&$variables) {
dpm('ttm preprocess node:');
dpm($variables);
}
*/
/**
* Preprocess and validate data for a treemenu.
*
* @param $variables
*/
/*
function template_preprocess_treemenu(&$variables) {
//dpm($variables);
}
*/
/** #
* Theme a taxonomy_treemenu page as HTML output.
*
* @param $menu_content
* rendered treemenu output
*
* @ingroup themeable
*/
/*
function theme_taxonomy_treemenu_menu_page($menu_content) {
//drupal_add_css(drupal_get_path('module', 'taxonomy') .'/taxonomy.css');
//dpm($menus);
$output = '';
// Much the same as theme_taxonomy_term_page
// Only can be one term, though, so we always display a description.
// Check that a description is set - need to do this?
if (count($menus) == 1) {
if (!empty($menus[0]['description'])) {
$output .= '
';
}
}
return $output . $menu_content;
}
*/
/*
function template_preprocess_page_treemenus(&$variables) {
//if this is bad, tell me why and fix it.
drupal_add_css(drupal_get_path('module', 'taxonomy_treemenu') . '/page_treemenu.css', 'module', 'all', FALSE);
$treemenus = $variables['treemenus'];
$variables['treemenus_sane'] = array();
//all this data is already sanitised by theme 'treemenu'
//but we are here collecting the samne stuff away from the insane stuff.
foreach ($treemenus as $name => $treemenu) {
$variables['treemenus_sane'][$name] = array(
'menu_name' => $treemenu['menu_name'],
'body' => $treemenu['body'],
);
}
//dpm($variables);
}
*/
/*******************************
* Pagers, and support functions
******************************/
/** #
* Parse and load a url page string.
*
* Parses for stripped machine names, loads the menu custom data.
* Loader will silently drop bad URL info.
* Returns an array of full menu information, keyed by menu name
* (short version, not full machine name).
*/
//currently redundant, but here if we reenable multiple menus.
/*
function taxonomy_treemenu_parse_menu_str($str_menus) {
$menus = array();
$str_parts = preg_split('/[+, ]/', $str_menus);
foreach ($str_parts as $menu_name) {
$data = taxonomy_treemenu_load('menu-' . $menu_name);
if ($data) {
$menus[$menu_name] = $data;
}
}
return $menus;
}
*/
function taxonomy_treemenu_get_parents_tids($menu_name, $type, $id) {
$parents = array();
if ($type == 'node') {
// Protect against nodes appearing in other vocabularies.
$r = TTMData::rootData($menu_name);
$result = db_query('SELECT tn.tid FROM {term_node} tn JOIN {term_data} td ON tn.tid = td.tid WHERE tn.nid = %d AND td.vid =%d', $id, $r['vid']);
while($data = db_fetch_array($result)) {
$parents[] = $data['tid'];
};
}
else {
$result = db_query('SELECT parent FROM {term_hierarchy} WHERE tid = %d', $id);
while($data = db_fetch_array($result)) {
$parents[] = $data['parent'];
};
}
return $parents;
}
/**
* Find and render a css file from an array of identifiers.
* @param $dir
* To search in. No recursion.
* @param $fids
* An array of partial filenames, to select from.
* The array is reversed and the first match wins.
* @param $filename_prefix
* To add to the fids, for matching against existing files.
* @return
* A rendered css line, ready for a template.
*/
function taxonomy_treemenu_find_css_file($dir, $fids, $filename_prefix = '') {
//dpm('find css files');
//dpm(is_dir($dir));
$output = '';
if (is_null($fids)) $fids = array();
$files = file_scan_directory($dir, '.*\.css$', array(), 0, FALSE, 'name');
//dpm('files:');
//dvm($files);
$fids = array_reverse($fids);
//dpm($fids);
foreach ($fids as $target ) {
$fn = $filename_prefix . $target;
//dpm('search');
//dpm($fn);
if ($files[$fn]) {
$to_load = $files[$fn]->basename;
break;
}
}
if ($to_load) {
$filepath = $dir .'/'. $to_load .'?'. substr(variable_get('css_js_query_string', '0'), 0, 1);
$output = ''."\n";
}
//dpm('output:');
//dpm($output);
return $output;
}
/**
* Find and render a css file with a similar name to the template suggestions.
* @param $dir
* @param $template_suggestions
* @return
* single file from the first matching suggestion, rendered and ready for a template.
*/
/*
function taxonomy_treemenu_find_css_file($dir, $template_suggestions) {
//dpm('find css files');
//dpm(is_dir($dir));
$output = '';
$files = file_scan_directory($dir, '.*\.css$', array(), 0, FALSE, 'name');
//dpm('files:');
//dvm($files);
$template_suggestions = array_reverse($template_suggestions);
//dpm($template_suggestions);
foreach ($template_suggestions as $target ) {
if ($files[$target]) {
$to_load = $files[$target]->basename;
break;
}
}
if ($to_load) {
$filepath = $dir .'/'. $to_load .'.css' .'?'. substr(variable_get('css_js_query_string', '0'), 0, 1);
$output = ''."\n";
}
//dpm('output:');
//dpm($output);
return $output;
}
*/
/**
* Find all ancestor tids from treemenu node or term data.
* Not currently used in the module, but good for preprocessing.
* @param $menu_name
* @param $type
* 'node' or 'term'
* @param $id
* @return
* Unique array of tids. No particular order.
*/
function taxonomy_treemenu_get_tid_ancestry($menu_name, $type, $id) {
$parents = $args = $path_tids = array();
$args = taxonomy_treemenu_get_parents_tids($menu_name, $type, $id);
// Rather than hunt recursively through the taxonomy, we can use the
// treemenu data to grab our trails from the stored paths.
$phs = array_fill(0, count($args), 'link_path LIKE "%/%d"');
$where = implode(' OR ', $phs);
$args[]= $menu_name;
$result = db_query('SELECT link_path FROM {menu_links} WHERE module = "treemenu" AND '. $where .' AND menu_name = "%s"', $args);
while ($data = db_fetch_array($result)) {
$path_tids = explode('/', $data['link_path']);
// get rid of the ttprnts prefix
array_shift($path_tids);
// And the vocab
array_shift($path_tids);
$parents = array_merge($parents, $path_tids);
}
return array_unique($parents);
}
/**
* Find menu mlid parents from node or term data.
* Used for breadcrumbing.
* @param $menu_name
* @param $type
* 'node'/'term'
* @return array of mlids
*/
//TODO: Redundancy. See taxonomy_treemenu_get_parents_tids().
function taxonomy_treemenu_get_parents_mlid($menu_name, $type, $id) {
//dpm('ttm get parents mlid');
$parents = array();
if ($type == 'node') {
// Protect against nodes appearing in other vocabularies.
$r = TTMData::rootData($menu_name);
$result = db_query('SELECT tn.tid FROM {term_node} tn JOIN {term_data} td ON tn.tid = td.tid WHERE tn.nid = %d AND td.vid =%d', $id, $r['vid']);
while($data = db_fetch_array($result)) {
$args[] = $data['tid'];
};
}
else {
$result = db_query('SELECT parent FROM {term_hierarchy} WHERE tid = %d', $id);
while($data = db_fetch_array($result)) {
$args[] = $data['parent'];
};
}
$phs = array_fill(0, count($args), 'link_path LIKE "%/%d"');
$where = implode(' OR ', $phs);
$args[]= $menu_name;
$result = db_query('SELECT mlid FROM {menu_links} WHERE module = "treemenu" AND '. $where .' AND menu_name = "%s"', $args);
//SELECT mlid FROM {menu_links} WHERE link_path LIKE "%/9" AND link_path LIKE "%/19" AND link_path LIKE "%/40" AND menu_name = "menu-indigotree";
while ($data = db_fetch_array($result)) {
$parents[] = $data['mlid'];
}
return $parents;
}
function taxonomy_treemenu_new_base_trail($ttm, $no_root = TRUE) {
$trail = array();
$trail[] = array('title' => t('Home'), 'href' => '', 'localized_options' => array(), 'type' => 0);
if (!$no_root) {
$trail[] = array('title' => t($ttm['title']), 'href' => 'ttm/'. $ttm['menu_name'], 'localized_options' => array(), 'type' => 0);
}
return $trail;
}
/**
* Recursively search for a set of targets in a tree, building the trail for for
* each target.
* The result is an array of trail arrays, placed in the array $trails.
*
* @param $tree
* @param $trails
* An array to load the trail data into.
* @param $curr
* An array which carries the trail as the function recurses.
* Can be pushed with data which should always be at the start of a trail.
* @param $targets
* An array of mlids.
*/
function _taxonomy_treemenu_tree_get_trails(&$tree, &$trails, &$curr, $targets) {
//dpm('set trails');
$curr[] = '';
foreach ($tree as $item) {
$curr[count($curr) - 1] = $item['link'];
if (in_array($item['link']['mlid'], $targets)) {
$trails[] = $curr;
}
if ($item['below']) {
_taxonomy_treemenu_tree_get_trails($item['below'], $trails, $curr, $targets);
}
}
array_pop($curr);
}
/**
* Get all menu trails from node or term data.
* @param $ttm
* @param $type
* @return
*/
function taxonomy_treemenu_get_active_trails_all($ttm, $type, $id) {
$trail = $trails = $parents = array();
// Create base trail with default items.
$trail = taxonomy_treemenu_new_base_trail($ttm, TRUE);
$parents = taxonomy_treemenu_get_parents_mlid($ttm['menu_name'], $type, $id);
//dpm('get active trail all');
$parents = array_unique($parents);
// Call with NULL to get an all data tree.
$tree = taxonomy_treemenu_tree_data($ttm);
_taxonomy_treemenu_tree_get_trails($tree, $trails, $trail, $parents);
// If no parents are mateched in the tree, use the base trail.
if (empty($trails)) {
$trails[] = $trail;
}
return $trails;
}
/** # get parents will do this...
* Find a menu parent mlid from node or term data.
* !WARNING: Only works on single hierarchy menus! Will return minimal otherwise.
* @param $menu_name
* @param $type 'node'/'term'
* @return mlid
*/
function taxonomy_treemenu_get_parent_mlid($menu_name, $type, $id) {
//dpm('get parent');
//dpm($menu_name);
if ($type == 'node') {
// Protect against nodes appearing in other vocabularies.
$r = TTMData::rootData($menu_name);
$parent_tid = db_result(db_query('SELECT tn.tid FROM {term_node} tn JOIN {term_data} td ON tn.tid = td.tid WHERE tn.nid = %d AND td.vid =%d', $id, $r['vid']));
}
else {
$parent_tid = db_result(db_query('SELECT parent FROM {term_hierarchy} WHERE tid = %d', $id));
}
$where = 'link_path LIKE "%/%d"';
return db_result(db_query('SELECT mlid FROM {menu_links} WHERE module = "treemenu" AND '. $where .' AND menu_name = "%s"', $parent_tid, $menu_name));
}
/**
* Get the trail using an expanded version of a targeted tree.
* !WARNING: Only works on single hierarchy menus! Will NULL otherwise?
* @param $ttm
* @param $type
* @return
*/
// Similar to the Drupal method, but that is bundled.
function taxonomy_treemenu_get_active_trail($ttm, $type, $id) {
//dpm('get active trail');
// $id just works as a flag.
$trail = taxonomy_treemenu_new_base_trail($ttm, TRUE);
$mlid = taxonomy_treemenu_get_parent_mlid($ttm['menu_name'], $type, $id);
// Call with the mlid to get a tree with an active trail (expanding, targeted).
$tree = taxonomy_treemenu_tree_data($ttm, $mlid);
list($key, $curr) = each($tree);
while ($curr) {
// Add the link if it's in the active trail, then move to the link below.
if ($curr['link']['in_active_trail']) {
$trail[] = $curr['link'];
$tree = $curr['below'] ? $curr['below'] : array();
}
list($key, $curr) = each($tree);
}
// Make sure the current page is in the trail (needed for the page title),
// TO CONSIDER: Treemenu ignores this form of titling. For now?
//$last = count($trail) - 1;
//if ($trail[$last]['href'] != $item['href']) {
// $trail[] = $item;
//}
return $trail;
}
/**
* Return a themed breadcrumbs trail.
*
* @param $breadcrumbs
* An array of arrays containing the breadcrumb links.
* @return a string containing the breadcrumbs output.
*/
function theme_taxonomy_treemenu_breadcrumbs($breadcrumbs) {
//dpm('theme breadcrumbs');
//dpm($breadcrumbs);
$output = '';
if (!empty($breadcrumbs)) {
$output = '
';
foreach($breadcrumbs as $breadcrumb) {
$output .= '
'. implode(' ยป ', $breadcrumb) .'
';
}
$output .= '
';
}
return $output;
}
/**
* Set/Get multiple breadcrumbs.
* Fill in the parameters to set the internal variable.
*
* @staticvar $stored_breadcrumb
* @param $ttm
* If NULL, returns previous results stored on the static.
* @param $type
* @param $id
* @return
* If $ttm is NULL (the default) an array of arrays of breadcrumb links.
* Otherwise, resets the crumb trail and returns the completed crumb.
*/
function taxonomy_treemenu_get_breadcrumbs($ttm = NULL, $type= NULL, $id = NULL) {
static $stored_breadcrumb;
if (is_null($ttm)) {
return $stored_breadcrumb;
}
// 'Unset' Drupals active breadcrumb.
drupal_set_breadcrumb('');
//drupal_add_css(drupal_get_path('module', 'taxonomy_treemenu') . '/css/taxonomy-treemenu-page.css');
$breadcrumbs = $breadcrumb = array();
// NOTE: taxonomy_treemenu_get_active_trails_all() uses an access checked tree.
$active_trails = taxonomy_treemenu_get_active_trails_all($ttm, $type, $id);
foreach ($active_trails as $trail) {
$breadcrumb = array();
foreach ($trail as $parent) {
$breadcrumb[] = l($parent['title'], $parent['href'], $parent['localized_options']);
}
$breadcrumbs[] = $breadcrumb;
}
$stored_breadcrumb = $breadcrumbs;
return $breadcrumbs;
}
/**
* Get set a singular breadcrumb.
* Uses Drupal search-the-active-path method.
* @param $ttm
* @param $type
* @param $id
* @return
*/
function taxonomy_treemenu_get_active_breadcrumb($ttm, $type, $id) {
$breadcrumb = array();
// NOTE: taxonomy_treemenu_get_active_trail() uses an access checked tree.
$active_trail = taxonomy_treemenu_get_active_trail($ttm, $type, $id);
foreach ($active_trail as $parent) {
$breadcrumb[] = l($parent['title'], $parent['href'], $parent['localized_options']);
}
return $breadcrumb;
}
// NEW
//function taxonomy_treemenu_node_page($ttm, $nid) {
function taxonomy_treemenu_node_page($ttm, $node) {
//dpm('ttm node page:');
//$ttm = $data['menu'];
//$nid = $data['target']['id'];
//dpm($ttm);
//dvm($nid);
$output = '';
// Breadcrumb.
/*
$ms = TTMData::isMultiple($ttm['menu_name']);
if ($ms['multiple'] && $ttm['multiple_breadcrumbs']) {
drupal_set_breadcrumb( taxonomy_treemenu_get_breadcrumbs($ttm, 'node', $nid));
}
else {
drupal_set_breadcrumb(taxonomy_treemenu_get_active_breadcrumb($ttm, 'node', $nid));
}
*/
// Sets the title also.
//$output .= node_page_view(node_load($nid));
// Breadcrumb.
$ms = TTMData::isMultiple($ttm['menu_name']);
if ($ms['multiple'] && $ttm['multiple_breadcrumbs']) {
drupal_set_breadcrumb( taxonomy_treemenu_get_breadcrumbs($ttm, 'node', $node->nid));
}
else {
drupal_set_breadcrumb(taxonomy_treemenu_get_active_breadcrumb($ttm, 'node', $node->nid));
}
$output .= node_page_view($node);
return $output;
}
function taxonomy_treemenu_term_page($ttm, $terms) {
//dpm('ttm term page:');
//dvm($terms);
$tid = $terms['tids'][0];
$output = '';
//return $output;
// Breadcrumb
$mult = TTMData::isMultiple($ttm['menu_name']);
if ($mult['hierarchy'] && $ttm['multiple_breadcrumbs']) {
// $output = theme('taxonomy_treemenu_breadcrumbs', taxonomy_treemenu_get_breadcrumbs($ttm, 'term', $tid));
//dpm('is multiple');
//dpm($output);
drupal_set_breadcrumb( taxonomy_treemenu_get_breadcrumbs($ttm, 'term', $tid));
}
else {
//dpm('not multiple');
drupal_set_breadcrumb(taxonomy_treemenu_get_active_breadcrumb($ttm, 'term', $tid));
}
//dpm('ttm term page:');
//dvm($output);
// Sets the title also.
require_once(drupal_get_path('module', 'taxonomy_treemenu') . '/includes/taxonomy_treemenu.pages.inc');
$output .= taxonomy_treemenu_structured_term_output($ttm, $tid, $terms, $op = 'page');
return $output;
}
// NEW
function taxonomy_treemenu_page($ttm) {
$output = '';
//dpm('ttm page2');
//dvm($ttm);
// Breadcrumb. Can only be 'home', for now, as untargeted, so do nothing.
// Title. This is provided by the theme,
// so set to null here to avoid duplicate titles.
$title = "";
drupal_set_title($title);
// Static menu, default rendering.
$output = taxonomy_treemenu_render_page($ttm);
return $output;
}
function taxonomy_treemenu_advanced_help_page($target) {
$items = array();
$output_items = array();
$output = '
'. t('Quick links to Taxonomy Treemenus Advanced Help pages. ') .'
';
$output .= '
'. t('Please note that this site is reporting that Advanced Help is either not installed or not enabled. ');
$output .= t('These links are provided so users can look through the files using the administration interface. ');
$output .= t('When using these pages and links, the Advanced Help special internal links will return page errors, and the parent structure of the files is not visible.') .'
';
$module_path = drupal_get_path('module', 'taxonomy_treemenu');
$module = 'taxonomy_treemenu';
if (file_exists("$module_path/help/$module.help.ini")) {
$path = "$module_path/help";
$info = parse_ini_file("./$module_path/help/$module.help.ini", TRUE);
}
// Build a simple array of item data.
foreach ($info as $name => $topic) {
$file = !empty($topic['file']) ? $topic['file'] : $name;
$items[$file] = $topic['title'];
}
if ($target == 'overview') {
foreach ($items as $file => $title) {
$output_items[] = l($title, 'admin/help/ttm-advanced-help/'. $file);
}
$output .= theme('item_list', $output_items);
}
if ($target != 'overview') {
$title = $items[$target];
if ($title) {
drupal_set_title(check_plain($title));
$output .= filter_xss_admin(file_get_contents("./$module_path/help/". $target .'.html'));
}
}
return $output;
}
/** #(for the moment)
* Page callback that renders a treemenu (or several treemenus provided in a string).
*/
/*Similar to taxonomy_term_page in taxonomy.pages.inc
except we do not differentiate between symbols
with appropriate checks. We can only append menus with +, \s, or ','
Note that taxonomy_treemenu_parse_menu_str() will return
validated, existing menu names, if anything.
*/
//currently redundant, but here if we reenable multiple menus.
/*
function taxonomy_treemenu_page_menus($str_menus = '') {
//test area!
//dpm('tm paging menus:');
$menus = array();
$titles = array();
$output = '';
$menus = taxonomy_treemenu_parse_menu_str($str_menus);
if (empty($menus)) {
drupal_not_found();
return;
}
//do something with breadcrumbs?
foreach ($menus as $name => $menu) {
//$menu_content .= taxonomy_treemenu_render_menu($menu);
$menus[$name]['body'] = taxonomy_treemenu_render_menu($menu);
$titles[] = $menu['title'];
}
$title = check_plain(implode(', ', $titles));
drupal_set_title($title);
//$output .= theme('taxonomy_treemenu_menu_page', $menu_content);
$output .= theme('page_treemenus', $menus);
return $output;
}
*/