? 0-token-menu-load.txt
? patches
? token_profile.inc
Index: token.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/token/token.module,v
retrieving revision 1.7.4.46
diff -u -p -r1.7.4.46 token.module
--- token.module	13 Dec 2010 18:31:23 -0000	1.7.4.46
+++ token.module	13 Dec 2010 19:28:09 -0000
@@ -39,7 +39,7 @@ function token_help($path, $arg) {
  * Return an array of the core modules supported by token.module.
  */
 function _token_core_supported_modules() {
-  return array('node', 'user', 'taxonomy', 'comment');
+  return array('node', 'user', 'taxonomy', 'comment', 'menu', 'book');
 }
 
 /**
@@ -767,3 +767,237 @@ function token_find_duplicate_tokens() {
 
   return $all_tokens;
 }
+
+/**
+ * Get a menu link by its mlid, access checked and link translated for rendering.
+ *
+ * This function is a copy of menu_link_load() but with its own cache and a
+ * simpler query to load the link.
+ *
+ * @param $mlid
+ *   The mlid of the menu item.
+ *
+ * @return
+ *   A menu link translated for rendering.
+ *
+ * @see menu_link_load()
+ * @see _token_menu_link_translate()
+ */
+function token_menu_link_load($mlid) {
+  static $cache = array();
+
+  if (!is_numeric($mlid)) {
+    return FALSE;
+  }
+
+  if (!isset($cache[$mlid])) {
+    $item = db_fetch_array(db_query("SELECT * FROM {menu_links} ml LEFT JOIN {menu_router} m ON m.path = ml.router_path WHERE ml.mlid = %d", $mlid));
+    if (!empty($item)) {
+      _token_menu_link_translate($item);
+    }
+    $cache[$mlid] = $item;
+  }
+
+  return $cache[$mlid];
+}
+
+function token_book_link_load($mlid) {
+  static $cache = array();
+
+  if (!is_numeric($mlid)) {
+    return FALSE;
+  }
+
+  if (!isset($cache[$mlid])) {
+    $item = db_fetch_array(db_query("SELECT * FROM {menu_links} ml INNER JOIN {book} b ON b.mlid = ml.mlid LEFT JOIN {menu_router} m ON m.path = ml.router_path WHERE ml.mlid = %d", $mlid));
+    if (!empty($item)) {
+      _token_menu_link_translate($item);
+    }
+    $cache[$mlid] = $item;
+  }
+
+  return $cache[$mlid];
+}
+
+function _token_menu_link_translate(&$item) {
+  $map = array();
+
+  if (!is_array($item['options'])) {
+    $item['options'] = unserialize($item['options']);
+  }
+
+  if ($item['external']) {
+    $item['access'] = 1;
+    $item['href'] = $item['link_path'];
+    $item['title'] = $item['link_title'];
+    $item['localized_options'] = $item['options'];
+  }
+  else {
+    $map = explode('/', $item['link_path']);
+    _menu_link_map_translate($map, $item['to_arg_functions']);
+    $item['href'] = implode('/', $map);
+
+    // Note - skip callbacks without real values for their arguments.
+    if (strpos($item['href'], '%') !== FALSE) {
+      $item['access'] = FALSE;
+      return FALSE;
+    }
+
+    $item['access'] = TRUE;
+    _menu_item_localize($item, $map, TRUE);
+  }
+
+  // Allow other customizations - e.g. adding a page-specific query string to the
+  // options array. For performance reasons we only invoke this hook if the link
+  // has the 'alter' flag set in the options array.
+  if (!empty($item['options']['alter'])) {
+    drupal_alter('translated_menu_link', $item, $map);
+  }
+
+  return $map;
+}
+
+function token_menu_tree_all_data($menu_name = 'navigation', $item = NULL) {
+  static $tree = array();
+
+  // Use $mlid as a flag for whether the data being loaded is for the whole tree.
+  $mlid = isset($item['mlid']) ? $item['mlid'] : 0;
+  // Generate a cache ID (cid) specific for this $menu_name and $item.
+  $cid = 'links:' . $menu_name . ':all-cid:' . $mlid;
+
+  if (!isset($tree[$cid])) {
+    // If the static variable doesn't have the data, check {cache_menu}.
+    $cache = cache_get($cid, 'cache_menu');
+    if ($cache && isset($cache->data)) {
+      // If the cache entry exists, it will just be the cid for the actual data.
+      // This avoids duplication of large amounts of data.
+      $cache = cache_get($cache->data, 'cache_menu');
+      if ($cache && isset($cache->data)) {
+        $data = $cache->data;
+      }
+    }
+    // If the tree data was not in the cache, $data will be NULL.
+    if (!isset($data)) {
+      // Build and run the query, and build the tree.
+      if ($mlid) {
+        // The tree is for a single item, so we need to match the values in its
+        // p columns and 0 (the top level) with the plid values of other links.
+        $args = array(0);
+        for ($i = 1; $i < MENU_MAX_DEPTH; $i++) {
+          $args[] = $item["p$i"];
+        }
+        $args = array_unique($args);
+        $placeholders = implode(', ', array_fill(0, count($args), '%d'));
+        $where = ' AND ml.plid IN (' . $placeholders . ')';
+        $parents = $args;
+        $parents[] = $item['mlid'];
+      }
+      else {
+        // Get all links in this menu.
+        $where = '';
+        $args = array();
+        $parents = array();
+      }
+      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.
+      $data['tree'] = menu_tree_data(db_query("
+        SELECT 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);
+      $data['node_links'] = array();
+      menu_tree_collect_node_links($data['tree'], $data['node_links']);
+      // Cache the data, if it is not already in the cache.
+      $tree_cid = _menu_tree_cid($menu_name, $data);
+      if (!cache_get($tree_cid, 'cache_menu')) {
+        cache_set($tree_cid, $data, 'cache_menu');
+      }
+      // Cache the cid of the (shared) data using the menu and item-specific cid.
+      cache_set($cid, $tree_cid, 'cache_menu');
+    }
+    // Check access for the current user to each item in the tree.
+    token_menu_tree_check_access($data['tree'], $data['node_links']);
+    $tree[$cid] = $data['tree'];
+  }
+
+  return $tree[$cid];
+}
+
+function token_menu_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;
+      }
+    }
+  }
+  _token_menu_tree_check_access($tree);
+  return;
+}
+
+function _token_menu_tree_check_access(&$tree) {
+  $new_tree = array();
+  foreach ($tree as $key => $v) {
+    $item = &$tree[$key]['link'];
+    _token_menu_link_translate($item);
+    if ($item['access']) {
+      if ($tree[$key]['below']) {
+        _token_menu_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];
+    }
+  }
+  // Sort siblings in the tree based on the weights and localized titles.
+  ksort($new_tree);
+  $tree = $new_tree;
+}
+
+/**
+ * Return an array of titles for a menu, from the top down to the specified node
+ *
+ * @param $menu_link
+ *   Fully loaded menu link, of node to which a path is desired.
+ * @param $nid
+ *   Id of node.
+ * @return
+ *   An array of titles through the specified node
+ */
+function _menu_titles($menu_link, $nid) {
+  $tree = token_menu_tree_all_data($menu_link['menu_name'], $menu_link);
+
+  // Get mlid of all nodes in path - top-most parent to leaf node.
+  $parents = array();
+  for ($i = 1; $i < MENU_MAX_DEPTH; $i++) {
+    if ($menu_link["p$i"]) {
+      $parents[] = $menu_link["p$i"];
+    }
+  }
+
+  // Build the titles in this hierarchy.
+  $titles = array();
+  $current = array_shift($tree);
+  while ($current) {
+    if (in_array($current['link']['mlid'], $parents)) {
+      $titles[] = $current['link']['title'];
+      if ($current['link']['href'] == "node/". $nid) {
+        break;
+      }
+      // Go deeper in tree hierarchy.
+      $tree = $current['below'];
+    }
+    // Go to next sibling at same level in tree hierarchy.
+    $current = $tree ? array_shift($tree) : NULL;
+  }
+
+  return $titles;
+}
Index: token.test
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/token/token.test,v
retrieving revision 1.1.2.19
diff -u -p -r1.1.2.19 token.test
--- token.test	30 Nov 2010 22:55:27 -0000	1.1.2.19
+++ token.test	13 Dec 2010 19:28:09 -0000
@@ -317,8 +317,8 @@ class TokenMenuTestCase extends TokenTes
 
   function testMenuTokens() {
     $root_link = array(
-      'link_path' => '<front>',
-      'link_title' => 'Front link',
+      'link_path' => 'root',
+      'link_title' => 'Root link',
       'menu_name' => 'primary-links',
     );
     menu_link_save($root_link);
@@ -338,6 +338,8 @@ class TokenMenuTestCase extends TokenTes
       'menu-link-mlid' => $node->menu['mlid'],
       'menu-link-plid' => $node->menu['plid'],
       'menu-link-plid' => $root_link['mlid'],
+      'menupath' => 'Root link/Node link',
+      'menupath-raw' => 'Root link/Node link',
     );
     $this->assertTokens('node', $node, $tokens);
   }
Index: token_node.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/token/Attic/token_node.inc,v
retrieving revision 1.5.4.35
diff -u -p -r1.5.4.35 token_node.inc
--- token_node.inc	13 Dec 2010 18:31:23 -0000	1.5.4.35
+++ token_node.inc	13 Dec 2010 19:28:09 -0000
@@ -64,41 +64,6 @@ function node_token_values($type, $objec
         $values += token_get_date_token_values($node->changed, 'mod-');
       }
 
-      // Try to get the menu data.
-      $mlid = db_result(db_query("SELECT mlid FROM {menu_links} WHERE link_path = '%s'", 'node/'. $node->nid));
-
-      // Now get the menu related information.
-      if (!empty($mlid) || !empty($node->menu['mlid']) || !empty($node->menu['plid'])) {
-        $menu_link = menu_link_load($mlid);
-        $menus = menu_get_menus();
-        $menu = isset($menus[$menu_link['menu_name']]) ? $menus[$menu_link['menu_name']] : '';
-        $trail_raw = _menu_titles($menu_link, $node->nid);
-
-        $trail = array();
-        foreach ($trail_raw as $title) {
-          $trail[] = check_plain($title);
-        }
-
-        $values['menupath']            = !empty($options['pathauto']) ? $trail : implode('/', $trail);
-        $values['menupath-raw']        = !empty($options['pathauto']) ? $trail_raw : implode('/', $trail_raw);
-        $values['menu']                = check_plain($menu);
-        $values['menu-raw']            = $menu;
-        $values['menu-link-title']     = check_plain($menu_link['title']);
-        $values['menu-link-title-raw'] = $menu_link['link_title'];
-        $values['menu-link-mlid']      = $menu_link['mlid'];
-        $values['menu-link-plid']      = $menu_link['plid'];
-      }
-      else {
-        $values['menu']                = '';
-        $values['menu-raw']            = '';
-        $values['menupath']            = '';
-        $values['menupath-raw']        = '';
-        $values['menu-link-title']     = '';
-        $values['menu-link-title-raw'] = '';
-        $values['menu-link-mlid']      = '';
-        $values['menu-link-plid']      = '';
-      }
-
       // And now taxonomy, which is a bit more work. This code is adapted from
       // pathauto's handling code; it's intended for compatibility with it.
       if (module_exists('taxonomy') && !empty($node->taxonomy) && is_array($node->taxonomy)) {
@@ -209,106 +174,126 @@ function node_token_list($type = 'all') 
       // $tokens['node']['catpath']        = t("Full taxonomy tree for the topmost term");
     }
 
-    if (module_exists('menu')) {
-      $tokens['node']['menu']                = t("The name of the menu the node belongs to.");
-      $tokens['node']['menu-raw']            = t("The name of the menu the node belongs to.");
-      $tokens['node']['menupath']            = t("The menu path (as reflected in the breadcrumb), not including Home or [menu]. Separated by /.");
-      $tokens['node']['menupath-raw']        = t("The unfiltered menu path (as reflected in the breadcrumb), not including Home or [menu]. Separated by /.");
-      $tokens['node']['menu-link-title']     = t("The text used in the menu as link text for this item.");
-      $tokens['node']['menu-link-title-raw'] = t("The unfiltered text used in the menu as link text for this item.");
-      $tokens['node']['menu-link-mlid']      = t("The unique ID of the node's menu link.");
-      $tokens['node']['menu-link-plid']      = t("The unique ID of the node's menu link parent.");
-    }
-
     return $tokens;
   }
 }
 
 /**
- * Return an array of titles for a menu, from the top down to the specified node
- *
- * @param $menu_link
- *   Fully loaded menu link, of node to which a path is desired.
- * @param $nid
- *   Id of node.
- * @return
- *   An array of titles through the specified node
+ * Implements hook_token_list() on behalf of menu.module.
  */
-function _menu_titles($menu_link, $nid) {
-  $tree = menu_tree_all_data($menu_link['menu_name'], $menu_link);
+function menu_token_list($type = 'all') {
+  $tokens = array();
 
-  // Get mlid of all nodes in path - top-most parent to leaf node.
-  $parents = array();
-  for ($i = 1; $i < MENU_MAX_DEPTH; $i++) {
-    if ($menu_link["p$i"]) {
-      $parents[] = $menu_link["p$i"];
-    }
+  if ($type == 'node' || $type == 'all') {
+    $tokens['node']['menu']                = t("The name of the menu the node belongs to.");
+    $tokens['node']['menu-raw']            = t("The name of the menu the node belongs to.");
+    $tokens['node']['menupath']            = t("The menu path (as reflected in the breadcrumb), not including Home or [menu]. Separated by /.");
+    $tokens['node']['menupath-raw']        = t("The unfiltered menu path (as reflected in the breadcrumb), not including Home or [menu]. Separated by /.");
+    $tokens['node']['menu-link-title']     = t("The text used in the menu as link text for this item.");
+    $tokens['node']['menu-link-title-raw'] = t("The unfiltered text used in the menu as link text for this item.");
+    $tokens['node']['menu-link-mlid']      = t("The unique ID of the node's menu link.");
+    $tokens['node']['menu-link-plid']      = t("The unique ID of the node's menu link parent.");
   }
 
-  // Build the titles in this hierarchy.
-  $titles = array();
-  $current = array_shift($tree);
-  while ($current) {
-    if (in_array($current['link']['mlid'], $parents)) {
-      $titles[] = $current['link']['title'];
-      if ($current['link']['href'] == "node/". $nid) {
-        break;
-      }
-      // Go deeper in tree hierarchy.
-      $tree = $current['below'];
+  return $tokens;
+}
+
+/**
+ * Implements hook_token_values() on behalf of menu.module.
+ */
+function menu_token_values($type, $object = NULL, $options = array()) {
+  $values = array();
+
+  if ($type == 'node' && !empty($object)) {
+    $node = $object;
+
+    // Initialize tokens to empty strings.
+    $values['menu']                = '';
+    $values['menu-raw']            = '';
+    $values['menupath']            = '';
+    $values['menupath-raw']        = '';
+    $values['menu-link-title']     = '';
+    $values['menu-link-title-raw'] = '';
+    $values['menu-link-mlid']      = '';
+    $values['menu-link-plid']      = '';
+
+    if (!isset($node->menu)) {
+      // Nodes do not have their menu links loaded via menu_nodeapi($node, 'load').
+      menu_nodeapi($node, 'prepare');
+    }
+
+    // Now get the menu related information.
+    if (!empty($node->menu['mlid'])) {
+      $menu_link = token_menu_link_load($node->menu['mlid']);
+      $menus = menu_get_menus();
+      $menu = isset($menus[$menu_link['menu_name']]) ? $menus[$menu_link['menu_name']] : '';
+      $trail_raw = _menu_titles($menu_link, $node->nid);
+      $trail = array_map('check_plain', $trail_raw);
+
+      $values['menu']                = check_plain($menu);
+      $values['menu-raw']            = $menu;
+      $values['menupath']            = !empty($options['pathauto']) ? $trail : implode('/', $trail);
+      $values['menupath-raw']        = !empty($options['pathauto']) ? $trail_raw : implode('/', $trail_raw);
+      $values['menu-link-title']     = check_plain($menu_link['title']);
+      $values['menu-link-title-raw'] = $menu_link['link_title'];
+      $values['menu-link-mlid']      = $menu_link['mlid'];
+      $values['menu-link-plid']      = $menu_link['plid'];
     }
-    // Go to next sibling at same level in tree hierarchy.
-    $current = $tree ? array_shift($tree) : NULL;
   }
-  return $titles;
+
+  return $values;
+}
+
+/**
+ * Implements hook_token_list() on behalf of book.module.
+ */
+function book_token_list($type) {
+  $tokens = array();
+
+  if ($type == 'node' || $type == 'all') {
+    $tokens['book']['book']           = t("The title of the node's book parent.");
+    $tokens['book']['book_id']        = t("The id of the node's book parent.");
+    $tokens['book']['bookpath']       = t("The titles of all parents in the node's book hierarchy.");
+    $tokens['book']['book-raw']       = t("The unfiltered title of the node's book parent.");
+    $tokens['book']['bookpath-raw']   = t("The unfiltered titles of all parents in the node's book hierarchy.");
+  }
+
+  return $tokens;
 }
 
 /**
- * Implementation of hook_token_values() for book nodes
+ * Implements hook_token_values() on behalf of book.module.
  */
 function book_token_values($type, $object = NULL, $options = array()) {
-  if ($type == 'node') {
-    $node = $object;
+  $values = array();
 
-    // Initialize some variables to empty.
-    $tokens['book'] = '';
-    $tokens['book-raw'] = '';
-    $tokens['book_id'] = '';
-    $tokens['bookpath'] = '';
-    $tokens['bookpath-raw'] = '';
+  if ($type == 'node' && !empty($object)) {
+    $node = $object;
 
-    if (!empty($node->book['menu_name'])) {
-      $trail_raw = _menu_titles($node->book, $node->nid);
+    // Initialize tokens to empty strings.
+    $values['book'] = '';
+    $values['book-raw'] = '';
+    $values['book_id'] = '';
+    $values['bookpath'] = '';
+    $values['bookpath-raw'] = '';
+
+    if (!empty($node->book['mlid'])) {
+      $link = token_book_link_load($node->book['mlid']);
+      $trail_raw = _menu_titles($link, $node->nid);
       $book_raw = $trail_raw[0];
       $book = check_plain($book_raw);
       // For book paths, we don't include the current node's title (last in
       // the array) in the trail.
       array_pop($trail_raw);
-      $trail = array();
-      foreach ($trail_raw as $title) {
-        $trail[] = check_plain($title);
-      }
-      $tokens = array();
-      $tokens['book'] = $book;
-      $tokens['book-raw'] = $book_raw;
-      $tokens['book_id'] = $node->book['bid'];
-      $tokens['bookpath'] = !empty($options['pathauto']) ? $trail : implode('/', $trail);
-      $tokens['bookpath-raw'] = !empty($options['pathauto']) ? $trail_raw : implode('/', $trail_raw);
-    }
+      $trail = array_map('check_plain', $trail_raw);
 
-    return $tokens;
+      $values['book'] = $book;
+      $values['book-raw'] = $book_raw;
+      $values['book_id'] = $node->book['bid'];
+      $values['bookpath'] = !empty($options['pathauto']) ? $trail : implode('/', $trail);
+      $values['bookpath-raw'] = !empty($options['pathauto']) ? $trail_raw : implode('/', $trail_raw);
+    }
   }
-}
-
-function book_token_list($type) {
-  if ($type == 'node' || $type == 'all') {
-    $list['book']['book']           = t("The title of the node's book parent.");
-    $list['book']['book_id']        = t("The id of the node's book parent.");
-    $list['book']['bookpath']       = t("The titles of all parents in the node's book hierarchy.");
-
-    $list['book']['book-raw']       = t("The unfiltered title of the node's book parent.");
-    $list['book']['bookpath-raw']   = t("The unfiltered titles of all parents in the node's book hierarchy.");
 
-    return $list;
-  }
+  return $values;
 }

