Index: admin_menu.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/admin_menu/admin_menu.inc,v
retrieving revision 1.47
diff -u -p -r1.47 admin_menu.inc
--- admin_menu.inc	9 Jun 2009 18:21:03 -0000	1.47
+++ admin_menu.inc	11 Jun 2009 19:13:23 -0000
@@ -109,24 +109,6 @@ function admin_menu_admin_menu(&$deleted
   $links = array();
   $icon_path = '<front>';
 
-  // Add link to manually run cron.
-  $links[] = array(
-    'title' => 'Run cron',
-    'path' => 'admin/reports/status/run-cron',
-    'weight' => 50,
-    'parent_path' => $icon_path,
-  );
-  // Add link to run update.php.
-  $links[] = array(
-    'title' => 'Run updates',
-    'path' => 'update.php',
-    'weight' => 50,
-    'parent_path' => $icon_path,
-    'options' => array(
-      'external' => TRUE,
-    ),
-  );
-
   // Move 'By module' item into Site configuration.
   if (isset($deleted['admin/by-module'])) {
     $deleted['admin/by-module']['parent_path'] = 'admin/settings';
@@ -135,38 +117,6 @@ function admin_menu_admin_menu(&$deleted
     unset($deleted['admin/by-module']);
   }
 
-  // Add link to drupal.org.
-  $links[] = array(
-    'title' => 'Drupal.org',
-    'path' => 'http://drupal.org',
-    'weight' => 100,
-    'parent_path' => $icon_path,
-  );
-  // Add links to project issue queues.
-  $links[] = array(
-    'title' => 'Drupal issue queue',
-    'path' => 'http://drupal.org/project/issues/drupal',
-    'weight' => -10,
-    'parent_path' => 'http://drupal.org',
-  );
-  $projects = array();
-  foreach (module_list(FALSE, TRUE) as $module) {
-    $info = drupal_parse_info_file(drupal_get_path('module', $module) . '/' . $module . '.info');
-    if (!isset($info['project']) || (isset($info['project']) && ($info['project'] == 'drupal' || isset($projects[$info['project']])))) {
-      continue;
-    }
-    $projects[$info['project']] = 1;
-    $url = 'http://drupal.org/project/issues/' . $info['project'];
-    // Filtering project versions via query string is not yet supported.
-    // @see http://drupal.org/node/97569
-    // $url .= !empty($info['version']) ? '/'. $info['version'] : '';
-    $links[] = array(
-      'title' => check_plain($info['name']) . ' issue queue',
-      'path' => $url,
-      'parent_path' => 'http://drupal.org',
-    );
-  }
-
   // Add 'Create <content-type>' items to Content management menu.
   if (isset($deleted['node/add'])) {
     $deleted['node/add']['parent_path'] = 'admin/content';
@@ -211,167 +161,150 @@ function admin_menu_admin_menu(&$deleted
     }
   }
 
+  return $links;
+}
+
+/**
+ * Add some hard-coded features for better user experience.
+ *
+ * @param $menu_links
+ *   An array containing the complete administration menu structure, passed by
+ *   reference.
+ *
+ * @return
+ *   An array of links that were removed from $menu_links.
+ */
+function admin_menu_adjust_items() {
+  // Add icon containing special links.
+  $links['icon'] = array(
+    '#title' => theme('admin_menu_icon'),
+    '#attributes' => array('class' => array('admin-menu-icon')),
+    '#href' => '<front>',
+    '#options' => array(
+      'html' => TRUE,
+    ),
+  );
+  // Add link to manually run cron.
+  $links['icon']['cron'] = array(
+    '#title' => t('Run cron'),
+    '#weight' => 50,
+    '#href' => 'admin/reports/status/run-cron',
+  );
+  // Add link to run update.php.
+  $links['icon']['update'] = array(
+    '#title' => t('Run updates'),
+    '#weight' => 50,
+    '#href' => 'update.php',
+    '#options' => array(
+      'external' => TRUE,
+    ),
+  );
+  // Add link to drupal.org.
+  $links['icon']['drupal.org'] = array(
+    '#title' => 'Drupal.org',
+    '#weight' => 100,
+    '#href' => 'http://drupal.org',
+  );
+  // Add links to project issue queues.
+  $links['icon']['drupal.org']['drupal'] = array(
+    '#title' => t('@project issue queue', array('@project' => 'Drupal')),
+    '#weight' => -10,
+    '#href' => 'http://drupal.org/project/issues/drupal',
+  );
+  $projects = array();
+  foreach (module_list(FALSE, TRUE) as $module) {
+    $info = drupal_parse_info_file(drupal_get_path('module', $module) . '/' . $module . '.info');
+    if (!isset($info['project']) || (isset($info['project']) && ($info['project'] == 'drupal' || isset($projects[$info['project']])))) {
+      continue;
+    }
+    $projects[$info['project']] = 1;
+    // @todo Add version filter.
+    $links['icon']['drupal.org'][$info['project']] = array(
+      '#title' => t('@project issue queue', array('@project' => 'Drupal')),
+      '#href' => 'http://drupal.org/project/issues/' . $info['project'],
+    );
+  }
   // Add items to flush caches.
-  $links[] = array(
-    'title' => 'Flush all caches',
-    'path' => 'admin_menu/flush-cache',
-    'options' => array(
+  $links['icon']['flush-cache'] = array(
+    '#title' => t('Flush all caches'),
+    '#weight' => 20,
+    '#href' => 'admin_menu/flush-cache',
+    '#options' => array(
       'query' => drupal_get_destination(),
     ),
-    'weight' => 20,
-    'parent_path' => $icon_path,
   );
   $caches = array(
-    'admin_menu' => 'Administration menu',
-    'cache' => 'Cache tables',
-    'menu' => 'Menu',
-    'requisites' => 'Page requisites',
-    'registry' => 'Registry',
-    'theme' => 'Theme registry',
-  );
-  foreach ($caches as $name => $title) {
-    $links[] = array(
-      'title' => $title,
-      'path' => 'admin_menu/flush-cache/' . $name,
-      'options' => array(
+    'admin_menu' => t('Administration menu'),
+    'cache' => t('Cache tables'),
+    'menu' => t('Menu'),
+    'requisites' => t('Page requisites'),
+    'registry' => t('Registry'),
+    'theme' => t('Theme registry'),
+  );
+  foreach ($caches as $arg => $title) {
+    $links['icon']['flush-cache'][$arg] = array(
+      '#title' => $title,
+      '#href' => 'admin_menu/flush-cache/' . $arg,
+      '#options' => array(
         'query' => drupal_get_destination(),
       ),
-      'parent_path' => 'admin_menu/flush-cache',
-    );
-  }
-
-  // Add devel module links
-  if (module_exists('devel')) {
-    // Add variable editor.
-    $links[] = array(
-      'title' => 'Variable editor',
-      'path' => 'devel/variable',
-      'weight' => 20,
-      'parent_path' => $icon_path,
     );
-    // Add switch_user items.
-    if ($devel_user_links = module_invoke('devel', 'switch_user_list')) {
-      foreach ($devel_user_links as $link) {
-        if (is_array($link)) {
-          $links[] = array(
-            'title' => $link['title'],
-            'description' => $link['attributes']['title'],
-            'path' => $link['href'],
-            'options' => array(
-              'query' => $link['query'],
-              'html' => !empty($link['html']),
-            ),
-            'parent_path' => 'user/%',
-          );
-        }
-        // @todo Remove when Devel 6.x-1.16 has been released.
-        elseif (preg_match('!href="' . base_path() . '([^\?]+)\?([^"]+)" title="([^"]+)">((<em>)?[^<]+(</em>)?)!', $link, $match)) {
-          $links[] = array(
-            'title' => $match[4],
-            'description' => $match[3],
-            'path' => urldecode($match[1]),
-            'options' => array(
-              'query' => drupal_get_destination(),
-              'html' => TRUE,
-            ),
-            'weight' => 20,
-            'parent_path' => 'user/%',
-          );
-        }
-      }
-    }
   }
-  // Add developer modules toggle link.
+  // Add link to toggle developer modules (performance).
   $saved_state = variable_get('admin_menu_devel_modules_enabled', NULL);
-  $links[] = array(
-    'title' => isset($saved_state) ? t('Enable developer modules') : t('Disable developer modules'),
-    'path' => 'admin_menu/toggle-modules',
-    'weight' => 88,
-    'parent_path' => $icon_path,
-    'options' => array(
+  $links['icon']['toggle-modules'] = array(
+    '#title' => isset($saved_state) ? t('Enable developer modules') : t('Disable developer modules'),
+    '#weight' => 88,
+    '#href' => 'admin_menu/toggle-modules',
+    '#options' => array(
       'query' => drupal_get_destination(),
     ),
   );
 
-  return $links;
-}
+  // Add link to show current authenticated/anonymous users.
+  $links['user-counter'] = array(
+    '#title' => admin_menu_get_user_count(),
+    '#description' => t('Current anonymous / authenticated users'),
+    '#weight' => -90,
+    '#attributes' => array('class' => array('admin-menu-action', 'admin-menu-users')),
+    '#href' => 'user',
+  );
+  $links['account'] = array(
+    '#title' => $GLOBALS['user']->name,
+    '#weight' => -99,
+    '#attributes' => array('class' => array('admin-menu-action')),
+    '#href' => 'user/' . $GLOBALS['user']->uid,
+  );
+  $links['logout'] = array(
+    '#title' => 'Log out',
+    '#weight' => -100,
+    '#attributes' => array('class' => array('admin-menu-action')),
+    '#href' => 'user/logout',
+  );
 
-/**
- * Add some hard-coded features for better user experience.
- *
- * @param $menu_links
- *   An array containing the complete administration menu structure, passed by
- *   reference.
- * @param $sort
- *   An array containing the # parts of each link - must be updated if a link
- *   is added.
- *
- * @return
- *   An array of links that were removed from $menu_links.
- */
-function admin_menu_adjust_items(&$menu_links, &$sort) {
-  global $user;
-  $links = array();
-  $deleted = array();
-
-  // Change or remove items, or add new top-level items.
-  $deleted['admin/by-module'] = $menu_links['admin/by-module'];
-  unset($menu_links['admin/by-module'], $sort['admin/by-module']);
-  $deleted['admin/by-task'] = $menu_links['admin/by-task'];
-  unset($menu_links['admin/by-task'], $sort['admin/by-task']);
-
-  // Remove certain links to re-position them in admin_menu_admin_menu().
-  foreach ($menu_links as $path => $link) {
-    // Remove links below
-    // - admin/build/node-type/*
-    // - node/add*
-    if (strpos($path, 'admin/build/node-type/') !== FALSE || strpos($path, 'node/add') !== FALSE) {
-      $deleted[$path] = $link;
-      unset($menu_links[$path], $sort[$path]);
+  // Add Devel module links.
+  if (module_exists('devel')) {
+    // Add variable editor.
+    $links['icon']['devel-variables'] = array(
+      '#title' => t('Variable editor'),
+      '#weight' => 20,
+      '#href' => 'devel/variable',
+    );
+    // Add switch user links.
+    foreach (module_invoke('devel', 'switch_user_list') as $link) {
+      $links['account'][$link['title']] = array(
+        '#title' => $link['title'],
+        '#description' => $link['attributes']['title'],
+        '#href' => $link['href'],
+        '#options' => array(
+          'query' => $link['query'],
+          'html' => !empty($link['html']),
+        ),
+      );
     }
   }
-  // Add the icon containing special links.
-  $links[] = array(
-    'title' => theme('admin_menu_icon'),
-    'path' => '<front>',
-    'weight' => -100,
-    'parent_path' => '<root>',
-    'options' => array('extra class' => 'admin-menu-icon', 'html' => TRUE),
-  );
-  // Add link to show current authenticated/anonymous users - we will add the
-  // data dynamically in the _alter hook.
-  $links[] = array(
-    'title' => admin_menu_get_user_count(),
-    'description' => t('Current anonymous / authenticated users'),
-    'path' => 'user',
-    'weight' => -90,
-    'parent_path' => '<root>',
-    'options' => array('extra class' => 'admin-menu-action admin-menu-users'),
-  );
-  $links[] = array(
-    'title' => '@username',
-    'path' => 'user/%',
-    'weight' => -99,
-    'parent_path' => '<root>',
-    // Note: @username is dynamically replaced by default, we just invoke
-    // replacement by setting the 't' key here.
-    'options' => array('extra class' => 'admin-menu-action', 't' => array()),
-  );
-  $links[] = array(
-    'title' => 'Log out',
-    'path' => 'user/logout',
-    'weight' => -100,
-    'parent_path' => '<root>',
-    'options' => array('extra class' => 'admin-menu-action'),
-  );
-  foreach ($links as $item) {
-    $path = $item['path'];
-    $item = admin_menu_link_build($item);
-    $menu_links[$path] = $item;
-    $sort[$path] = 1;
-  }
 
-  return $deleted;
+  return $links;
 }
 
 /**
@@ -418,10 +351,6 @@ function admin_menu_theme_settings() {
  * Wipe the menu so it can be rebuilt from scratch.
  */
 function admin_menu_flush_caches() {
-  // Flush cached menu tree.
-  db_delete('menu_links')->condition('menu_name', 'admin_menu')->execute();
-  menu_cache_clear('admin_menu');
-  variable_set('admin_menu_rebuild_links', TRUE);
   // Flush cached output of admin_menu.
   cache_clear_all('admin_menu:', 'cache_menu', TRUE);
   // Flush client-side cache hashes.
@@ -533,7 +462,13 @@ function admin_menu_toggle_modules() {
 function admin_menu_flush_cache($name = NULL) {
   switch ($name) {
     case 'admin_menu':
-      admin_menu_flush_caches();
+      // Flush cached menu tree.
+      db_delete('menu_links')
+        ->condition('menu_name', 'admin_menu')
+        ->condition('customized', 0)
+        ->execute();
+      menu_cache_clear('admin_menu');
+      menu_rebuild();
       break;
 
     case 'cache':
@@ -547,7 +482,7 @@ function admin_menu_flush_cache($name = 
       break;
 
     case 'menu':
-      module_invoke('menu', 'rebuild');
+      menu_rebuild();
       break;
 
     case 'registry':
Index: admin_menu.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/admin_menu/admin_menu.install,v
retrieving revision 1.15
diff -u -p -r1.15 admin_menu.install
--- admin_menu.install	18 Apr 2009 17:24:56 -0000	1.15
+++ admin_menu.install	11 Jun 2009 00:56:12 -0000
@@ -16,6 +16,7 @@ function admin_menu_schema() {
 function admin_menu_install() {
   // Create cache table.
   drupal_install_schema('admin_menu');
+  db_query("INSERT INTO {menu_custom} (menu_name, title, description) VALUES ('admin_menu', 'Administration menu', '')");
 }
 
 /**
@@ -83,3 +84,16 @@ function admin_menu_update_7300() {
   db_create_table($ret, 'cache_admin_menu', $schema);
   return $ret;
 }
+
+/**
+ * Add "admin_menu" menu.
+ */
+function admin_menu_update_7301() {
+  $ret = array();
+  if (db_query("SELECT menu_name FROM {menu_custom} WHERE menu_name = 'admin_menu'")->fetchField()) {
+    return $ret;
+  }
+  $ret[] = update_sql("INSERT INTO {menu_custom} (menu_name, title, description) VALUES ('admin_menu', 'Administration menu', '')");
+  return $ret;
+}
+
Index: admin_menu.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/admin_menu/admin_menu.module,v
retrieving revision 1.73
diff -u -p -r1.73 admin_menu.module
--- admin_menu.module	11 Jun 2009 01:39:20 -0000	1.73
+++ admin_menu.module	11 Jun 2009 19:21:54 -0000
@@ -48,6 +48,9 @@ function admin_menu_perm() {
  */
 function admin_menu_theme() {
   return array(
+    'admin_menu_links' => array(
+      'arguments' => array('elements' => array()),
+    ),
     'admin_menu_icon' => array(
       'arguments' => array(),
     ),
@@ -59,6 +62,7 @@ function admin_menu_theme() {
  */
 function admin_menu_menu() {
   // AJAX callback.
+  // @see http://drupal.org/project/js
   $items['js/admin_menu/cache'] = array(
     'page callback' => 'admin_menu_js_cache',
     'access arguments' => array('access administration menu'),
@@ -87,6 +91,44 @@ function admin_menu_menu() {
 }
 
 /**
+ * Implementation of hook_menu_alter().
+ */
+function admin_menu_menu_alter(&$items) {
+  // Move all items below admin/* into administration menu.
+  foreach ($items as $path => $item) {
+    if (strpos($path, 'admin') === 0) {
+      if (!isset($item['type'])) {
+        $items[$path]['menu_name'] = 'admin_menu';
+      }
+      else {
+        if ($item['type'] & MENU_IS_LOCAL_TASK) {
+          // Work around a core bug, which defines local tasks below menu
+          // callbacks (e.g. for admin/build/node-type/article/edit).
+          $parent = explode('/', $path);
+          array_pop($parent);
+          $parent = implode('/', $parent);
+          if (isset($items[$parent]['type']) && $items[$parent]['type'] & MENU_CALLBACK) {
+            continue;
+          }
+          // Trick this item into the visible menu tree.
+          $items[$path]['_visible'] = TRUE;
+        }
+        if ($item['type'] & MENU_CALLBACK) {
+          continue;
+        }
+        $items[$path]['menu_name'] = 'admin_menu';
+      }
+    }
+  }
+
+  // Remove 'admin', so children appear on the top-level.
+  $items['admin']['_visible'] = FALSE;
+  // Remove local tasks on 'admin'.
+  $items['admin/by-task']['_visible'] = FALSE;
+  $items['admin/by-module']['_visible'] = FALSE;
+}
+
+/**
  * Implementation of hook_init().
  *
  * We can't move this into admin_menu_footer(), because PHP-only based themes
@@ -174,67 +216,9 @@ function admin_menu_suppress($set = TRUE
 
 /**
  * Implementation of hook_footer().
- *
- * @todo Since admin_menu is rebuilt in the same request, we should be able
- *   to use a helper function instead of a variable to remind us to rebuild
- *   (variable_set() is slow).
  */
 function admin_menu_footer($main = 0) {
-  if (!user_access('access administration menu') || admin_menu_suppress(FALSE)) {
-    return;
-  }
-  global $user, $language;
-
-  $cache_server_enabled = variable_get('admin_menu_cache_server', TRUE);
-
-  // Determine whether we need to rebuild.
-  $rebuild = variable_get('admin_menu_rebuild_links', FALSE);
-  $cid = 'admin_menu:' . $user->uid . ':' . $language->language;
-
-  // Do nothing at all here if the client supports client-side caching, no
-  // rebuild is needed, the user has a hash, and is NOT requesting the cache
-  // update path. Consult the hash cache last, since it requires a DB request.
-  // @todo Implement a sanity-check to prevent permanent double requests; i.e.
-  //   what if the client-side cache fails for any reason and performs a second
-  //   request on every page?
-  if (!empty($_COOKIE['has_js']) && !$rebuild && strpos($_GET['q'], 'js/admin_menu/cache') !== 0) {
-    if (admin_menu_cache_get($cid)) {
-      return;
-    }
-  }
-
-  // Check for the flag indicating that we need to rebuild the menu.
-  if ($rebuild) {
-    module_load_include('inc', 'admin_menu');
-    _admin_menu_rebuild_links();
-    variable_del('admin_menu_rebuild_links');
-  }
-  // Try to load and output administration menu from server-side cache.
-  elseif ($cache_server_enabled) {
-    $cache = cache_get($cid, 'cache_menu');
-    if ($cache && isset($cache->data)) {
-      $content = $cache->data;
-    }
-  }
-
-  // Rebuild the output.
-  if (!isset($content)) {
-    $content  = '<div id="admin-menu">';
-    $content .= admin_menu_tree_output(menu_tree_all_data('admin_menu'));
-    $content .= '</div>';
-
-    // Cache the menu for this user.
-    if ($cache_server_enabled) {
-      cache_set($cid, $content, 'cache_menu');
-    }
-  }
-
-  // Store the new hash for this user.
-  if (!empty($_COOKIE['has_js'])) {
-    admin_menu_cache_set($cid, md5($content));
-  }
-
-  return $content;
+  return admin_menu_output();
 }
 
 /**
@@ -284,8 +268,8 @@ function admin_menu_cache_set($cid, $dat
  * Menu callback; Output administration menu for HTTP caching via AJAX request.
  */
 function admin_menu_js_cache($hash = NULL) {
-  // Fetch the menu.
-  $content = admin_menu_footer();
+  // Get the rendered menu.
+  $content = admin_menu_output();
 
   // @todo According to http://www.mnot.net/blog/2006/05/11/browser_caching,
   //   IE will only cache the content when it is compressed.
@@ -341,6 +325,125 @@ function admin_menu_get_user_count() {
 }
 
 /**
+ * Build the administration menu output.
+ *
+ * @todo Since admin_menu is rebuilt in the same request, we should be able
+ *   to use a helper function instead of a variable to remind us to rebuild
+ *   (variable_set() is slow).
+ */
+function admin_menu_output() {
+  if (!user_access('access administration menu') || admin_menu_suppress(FALSE)) {
+    return;
+  }
+  global $user, $language;
+
+  $cache_server_enabled = variable_get('admin_menu_cache_server', TRUE);
+
+  // Determine whether we need to rebuild.
+  $rebuild = variable_get('admin_menu_rebuild_links', FALSE);
+  $cid = 'admin_menu:' . $user->uid . ':' . $language->language;
+
+  // Do nothing at all here if the client supports client-side caching, no
+  // rebuild is needed, the user has a hash, and is NOT requesting the cache
+  // update path. Consult the hash cache last, since it requires a DB request.
+  // @todo Implement a sanity-check to prevent permanent double requests; i.e.
+  //   what if the client-side cache fails for any reason and performs a second
+  //   request on every page?
+  if (!empty($_COOKIE['has_js']) && !$rebuild && strpos($_GET['q'], 'js/admin_menu/cache') !== 0) {
+    if (admin_menu_cache_get($cid)) {
+      return;
+    }
+  }
+
+  // Check for the flag indicating that we need to rebuild the menu.
+  if (0 && $rebuild) {
+    module_load_include('inc', 'admin_menu');
+    _admin_menu_rebuild_links();
+    variable_del('admin_menu_rebuild_links');
+  }
+  // Try to load and output administration menu from server-side cache.
+  elseif ($cache_server_enabled) {
+    $cache = cache_get($cid, 'cache_menu');
+    if ($cache && isset($cache->data)) {
+      $content = $cache->data;
+    }
+  }
+
+  // Rebuild the output.
+  if (!isset($content)) {
+    // @todo Always output container to harden JS-less support.
+    $content['#prefix'] = '<div id="admin-menu">';
+    $content['#suffix'] = '</div>';
+
+    module_load_include('inc', 'admin_menu');
+    $content['links'] = array(
+      '#theme' => 'admin_menu_links',
+    );
+    $content['links'] += admin_menu_adjust_items();
+
+    // @todo How can we use #theme here?
+    $content['menu'] = array(
+      '#markup' => admin_menu_tree_output(menu_tree_all_data('admin_menu')),
+    );
+
+    drupal_alter('admin_menu_output', $content);
+    $content = drupal_render($content);
+
+    // Cache the menu for this user.
+    if ($cache_server_enabled) {
+      cache_set($cid, $content, 'cache_menu');
+    }
+  }
+
+  // Store the new hash for this user.
+  if (!empty($_COOKIE['has_js'])) {
+    admin_menu_cache_set($cid, md5($content));
+  }
+
+  return $content;
+}
+
+/**
+ * Render a themed list of links.
+ *
+ * @param $elements
+ *   A structured drupal_render()-array of links using the following keys:
+ *   - '#attributes': Optional array of attributes for the list item, processed
+ *     via drupal_attributes().
+ *     Note that we use an array for 'class'.
+ *   - '#title': Title of the link, passed to l().
+ *   - '#href': Path of the link, passed to l().
+ *   - '#description': Optional alternative text for the link, passed to l().
+ *   - '#options': Optional alternative text for the link, passed to l().
+ *
+ *   The array key of each child element itself is passed as path for l().
+ */
+function theme_admin_menu_links(&$elements) {
+  $output = '';
+  foreach (element_children($elements, TRUE) as $path) {
+    $elements[$path] += array(
+      '#attributes' => array(),
+      '#options' => array(),
+    );
+    // Render children to determine whether this link is expandable.
+    $children = theme('admin_menu_links', $elements[$path]);
+    if (!empty($children)) {
+      $elements[$path]['#attributes']['class'][] = 'expandable';
+    }
+    if (isset($elements[$path]['#attributes']['class'])) {
+      $elements[$path]['#attributes']['class'] = implode(' ', $elements[$path]['#attributes']['class']);
+    }
+
+    $output .= '<li' . drupal_attributes($elements[$path]['#attributes']) . '>';
+    $output .= l($elements[$path]['#title'], $elements[$path]['#href'], $elements[$path]['#options']);
+    $output .= $children;
+    $output .= '</li>';
+  }
+  // @todo #attributes probably required for UL, but already used for LI.
+  return $output ? '<ul>' . $output . '</ul>' : '';
+}
+
+/**
  * Return a rendered menu tree.
  *
  * @param $tree
@@ -354,6 +457,11 @@ function admin_menu_tree_output($tree) {
   $output = '';
 
   foreach ($tree as $data) {
+    // Skip dynamic items.
+    if (isset($data['link']['type']) && $data['link']['type'] == MENU_CALLBACK) {
+      continue;
+    }
+    // @todo Obsolete.
     $extra_class = isset($data['link']['localized_options']['extra class']) ? $data['link']['localized_options']['extra class'] : NULL;
     $link = admin_menu_item_link($data['link']);
 
@@ -434,13 +542,6 @@ function theme_admin_menu_item($link, $h
 }
 
 /**
- * Implementation of hook_menu_alter().
- */
-function admin_menu_menu_alter() {
-  variable_set('admin_menu_rebuild_links', TRUE);
-}
-
-/**
  * Implementation of hook_translated_menu_link_alter().
  *
  * Here is where we make changes to links that need dynamic information such
