[ Index ]
 

Code source de Drupal 5.3

Accédez au Source d'autres logiciels libres

Classes | Fonctions | Variables | Constantes | Tables

title

Body

[fermer]

/includes/ -> menu.inc (source)

   1  <?php
   2  // $Id: menu.inc,v 1.146.2.1 2007/06/17 01:50:50 drumm Exp $
   3  
   4  /**
   5   * @file
   6   * API for the Drupal menu system.
   7   */
   8  
   9  /**
  10   * @defgroup menu Menu system
  11   * @{
  12   * Define the navigation menus, and route page requests to code based on URLs.
  13   *
  14   * The Drupal menu system drives both the navigation system from a user
  15   * perspective and the callback system that Drupal uses to respond to URLs
  16   * passed from the browser. For this reason, a good understanding of the
  17   * menu system is fundamental to the creation of complex modules.
  18   *
  19   * Drupal's menu system follows a simple hierarchy defined by paths.
  20   * Implementations of hook_menu() define menu items and assign them to
  21   * paths (which should be unique). The menu system aggregates these items
  22   * and determines the menu hierarchy from the paths. For example, if the
  23   * paths defined were a, a/b, e, a/b/c/d, f/g, and a/b/h, the menu system
  24   * would form the structure:
  25   * - a
  26   *   - a/b
  27   *     - a/b/c/d
  28   *     - a/b/h
  29   * - e
  30   * - f/g
  31   * Note that the number of elements in the path does not necessarily
  32   * determine the depth of the menu item in the tree.
  33   *
  34   * When responding to a page request, the menu system looks to see if the
  35   * path requested by the browser is registered as a menu item with a
  36   * callback. If not, the system searches up the menu tree for the most
  37   * complete match with a callback it can find. If the path a/b/i is
  38   * requested in the tree above, the callback for a/b would be used.
  39   *
  40   * The found callback function is called with any arguments specified
  41   * in the "callback arguments" attribute of its menu item. The
  42   * attribute must be an array. After these arguments, any remaining
  43   * components of the path are appended as further arguments. In this
  44   * way, the callback for a/b above could respond to a request for
  45   * a/b/i differently than a request for a/b/j.
  46   *
  47   * For an illustration of this process, see page_example.module.
  48   *
  49   * Access to the callback functions is also protected by the menu system.
  50   * The "access" attribute of each menu item is checked as the search for a
  51   * callback proceeds. If this attribute is TRUE, then access is granted; if
  52   * FALSE, then access is denied. The first found "access" attribute
  53   * determines the accessibility of the target. Menu items may omit this
  54   * attribute to use the value provided by an ancestor item.
  55   *
  56   * In the default Drupal interface, you will notice many links rendered as
  57   * tabs. These are known in the menu system as "local tasks", and they are
  58   * rendered as tabs by default, though other presentations are possible.
  59   * Local tasks function just as other menu items in most respects. It is
  60   * convention that the names of these tasks should be short verbs if
  61   * possible. In addition, a "default" local task should be provided for
  62   * each set. When visiting a local task's parent menu item, the default
  63   * local task will be rendered as if it is selected; this provides for a
  64   * normal tab user experience. This default task is special in that it
  65   * links not to its provided path, but to its parent item's path instead.
  66   * The default task's path is only used to place it appropriately in the
  67   * menu hierarchy.
  68   */
  69  
  70  /**
  71   * @name Menu flags
  72   * @{
  73   * Flags for use in the "type" attribute of menu items.
  74   */
  75  
  76  define('MENU_IS_ROOT', 0x0001);
  77  define('MENU_VISIBLE_IN_TREE', 0x0002);
  78  define('MENU_VISIBLE_IN_BREADCRUMB', 0x0004);
  79  define('MENU_VISIBLE_IF_HAS_CHILDREN', 0x0008);
  80  define('MENU_MODIFIABLE_BY_ADMIN', 0x0010);
  81  define('MENU_MODIFIED_BY_ADMIN', 0x0020);
  82  define('MENU_CREATED_BY_ADMIN', 0x0040);
  83  define('MENU_IS_LOCAL_TASK', 0x0080);
  84  define('MENU_EXPANDED', 0x0100);
  85  define('MENU_LINKS_TO_PARENT', 0x0200);
  86  
  87  /**
  88   * @} End of "Menu flags".
  89   */
  90  
  91  /**
  92   * @name Menu item types
  93   * @{
  94   * Menu item definitions provide one of these constants, which are shortcuts for
  95   * combinations of the above flags.
  96   */
  97  
  98  /**
  99   * Normal menu items show up in the menu tree and can be moved/hidden by
 100   * the administrator. Use this for most menu items. It is the default value if
 101   * no menu item type is specified.
 102   */
 103  define('MENU_NORMAL_ITEM', MENU_VISIBLE_IN_TREE | MENU_VISIBLE_IN_BREADCRUMB | MENU_MODIFIABLE_BY_ADMIN);
 104  
 105  /**
 106   * Item groupings are used for pages like "node/add" that simply list
 107   * subpages to visit. They are distinguished from other pages in that they will
 108   * disappear from the menu if no subpages exist.
 109   */
 110  define('MENU_ITEM_GROUPING', MENU_VISIBLE_IF_HAS_CHILDREN | MENU_VISIBLE_IN_BREADCRUMB | MENU_MODIFIABLE_BY_ADMIN);
 111  
 112  /**
 113   * Callbacks simply register a path so that the correct function is fired
 114   * when the URL is accessed. They are not shown in the menu.
 115   */
 116  define('MENU_CALLBACK', MENU_VISIBLE_IN_BREADCRUMB);
 117  
 118  /**
 119   * Dynamic menu items change frequently, and so should not be stored in the
 120   * database for administrative customization.
 121   */
 122  define('MENU_DYNAMIC_ITEM', MENU_VISIBLE_IN_TREE | MENU_VISIBLE_IN_BREADCRUMB);
 123  
 124  /**
 125   * Modules may "suggest" menu items that the administrator may enable. They act
 126   * just as callbacks do until enabled, at which time they act like normal items.
 127   */
 128  define('MENU_SUGGESTED_ITEM', MENU_MODIFIABLE_BY_ADMIN | MENU_VISIBLE_IN_BREADCRUMB);
 129  
 130  /**
 131   * Local tasks are rendered as tabs by default. Use this for menu items that
 132   * describe actions to be performed on their parent item. An example is the path
 133   * "node/52/edit", which performs the "edit" task on "node/52".
 134   */
 135  define('MENU_LOCAL_TASK', MENU_IS_LOCAL_TASK);
 136  
 137  /**
 138   * Every set of local tasks should provide one "default" task, that links to the
 139   * same path as its parent when clicked.
 140   */
 141  define('MENU_DEFAULT_LOCAL_TASK', MENU_IS_LOCAL_TASK | MENU_LINKS_TO_PARENT);
 142  
 143  /**
 144   * Custom items are those defined by the administrator. Reserved for internal
 145   * use; do not return from hook_menu() implementations.
 146   */
 147  define('MENU_CUSTOM_ITEM', MENU_VISIBLE_IN_TREE | MENU_VISIBLE_IN_BREADCRUMB | MENU_CREATED_BY_ADMIN | MENU_MODIFIABLE_BY_ADMIN);
 148  
 149  /**
 150   * Custom menus are those defined by the administrator. Reserved for internal
 151   * use; do not return from hook_menu() implementations.
 152   */
 153  define('MENU_CUSTOM_MENU', MENU_IS_ROOT | MENU_VISIBLE_IN_TREE | MENU_CREATED_BY_ADMIN | MENU_MODIFIABLE_BY_ADMIN);
 154  
 155  /**
 156   * @} End of "Menu item types".
 157   */
 158  
 159  /**
 160   * @name Menu status codes
 161   * @{
 162   * Status codes for menu callbacks.
 163   */
 164  
 165  define('MENU_FOUND', 1);
 166  define('MENU_NOT_FOUND', 2);
 167  define('MENU_ACCESS_DENIED', 3);
 168  define('MENU_SITE_OFFLINE', 4);
 169  
 170  /**
 171   * @} End of "Menu status codes".
 172   */
 173  
 174  /**
 175   * Return the menu data structure.
 176   *
 177   * The returned structure contains much information that is useful only
 178   * internally in the menu system. External modules are likely to need only
 179   * the ['visible'] element of the returned array. All menu items that are
 180   * accessible to the current user and not hidden will be present here, so
 181   * modules and themes can use this structure to build their own representations
 182   * of the menu.
 183   *
 184   * $menu['visible'] will contain an associative array, the keys of which
 185   * are menu IDs. The values of this array are themselves associative arrays,
 186   * with the following key-value pairs defined:
 187   * - 'title' - The displayed title of the menu or menu item. It will already
 188   *   have been translated by the locale system.
 189   * - 'description' - The description (link title attribute) of the menu item.
 190   *   It will already have been translated by the locale system.
 191   * - 'path' - The Drupal path to the menu item. A link to a particular item
 192   *   can thus be constructed with
 193   *   l($item['title'], $item['path'], array('title' => $item['description'])).
 194   * - 'children' - A linear list of the menu ID's of this item's children.
 195   *
 196   * Menu ID 0 is the "root" of the menu. The children of this item are the
 197   * menus themselves (they will have no associated path). Menu ID 1 will
 198   * always be one of these children; it is the default "Navigation" menu.
 199   */
 200  function menu_get_menu() {
 201    global $_menu;
 202    global $user;
 203    global $locale;
 204  
 205    if (!isset($_menu['items'])) {
 206      // _menu_build() may indirectly call this function, so prevent infinite loops.
 207      $_menu['items'] = array();
 208  
 209      $cid = "$user->uid:$locale";
 210      if ($cached = cache_get($cid, 'cache_menu')) {
 211        $_menu = unserialize($cached->data);
 212      }
 213      else {
 214        _menu_build();
 215        // Cache the menu structure for this user, to expire after one day.
 216        cache_set($cid, 'cache_menu', serialize($_menu), time() + (60 * 60 * 24));
 217      }
 218  
 219      // Make sure items that cannot be cached are added.
 220      _menu_append_contextual_items();
 221  
 222      // Reset the cached $menu in menu_get_item().
 223      menu_get_item(NULL, NULL, TRUE);
 224    }
 225  
 226    return $_menu;
 227  }
 228  
 229  /**
 230   * Return the local task tree.
 231   *
 232   * Unlike the rest of the menu structure, the local task tree cannot be cached
 233   * nor determined too early in the page request, because the user's current
 234   * location may be changed by a menu_set_location() call, and the tasks shown
 235   * (just as the breadcrumb trail) need to reflect the changed location.
 236   */
 237  function menu_get_local_tasks() {
 238    global $_menu;
 239  
 240    // Don't cache the local task tree, as it varies by location and tasks are
 241    // allowed to be dynamically determined.
 242    if (!isset($_menu['local tasks'])) {
 243      // _menu_build_local_tasks() may indirectly call this function, so prevent
 244      // infinite loops.
 245      $_menu['local tasks'] = array();
 246      $pid = menu_get_active_nontask_item();
 247      if (!_menu_build_local_tasks($pid)) {
 248        // If the build returned FALSE, the tasks need not be displayed.
 249        $_menu['local tasks'][$pid]['children'] = array();
 250      }
 251    }
 252  
 253    return $_menu['local tasks'];
 254  }
 255  
 256  /**
 257   * Retrieves the menu item specified by $mid, or by $path if $mid is not given.
 258   *
 259   * @param $mid
 260   *   The menu ID of the menu item to retrieve.
 261   * @param $path
 262   *   The internal path of the menu item to retrieve. Defaults to NULL. Only
 263   *   used if $mid is not set.
 264   * @param $reset
 265   *   Optional flag that resets the static variable cache of the menu tree, if
 266   *   set to TRUE. Default is FALSE.
 267   *
 268   * @return
 269   *   The menu item found in the site menu, or an empty array if none could be
 270   *   found.
 271   */
 272  function menu_get_item($mid, $path = NULL, $reset = FALSE) {
 273    static $menu;
 274  
 275    if (!isset($menu) || $reset) {
 276      $menu = menu_get_menu();
 277    }
 278  
 279    if (isset($mid)) {
 280      return $menu['items'][$mid];
 281    }
 282  
 283    if (isset($path)) {
 284      return $menu['items'][$menu['path index'][$path]];
 285    }
 286  
 287    return array();
 288  }
 289  
 290  /**
 291   * Retrieves the menu ID and title of all root menus.
 292   *
 293   * @return
 294   *   Array containing all menus (but not menu items), in the form mid => title.
 295   */
 296  function menu_get_root_menus() {
 297    $menu = menu_get_menu();
 298    $root_menus = array();
 299  
 300    foreach ($menu['items'][0]['children'] as $mid) {
 301      $root_menus[$mid] = $menu['items'][$mid]['title'];
 302    }
 303  
 304    return $root_menus;
 305  }
 306  
 307  /**
 308   * Change the current menu location of the user.
 309   *
 310   * Frequently, modules may want to make a page or node act as if it were
 311   * in the menu tree somewhere, even though it was not registered in a
 312   * hook_menu() implementation. If the administrator has rearranged the menu,
 313   * the newly set location should respect this in the breadcrumb trail and
 314   * expanded/collapsed status of menu items in the tree. This function
 315   * allows this behavior.
 316   *
 317   * @param $location
 318   *   An array specifying a complete or partial breadcrumb trail for the
 319   *   new location, in the same format as the return value of hook_menu().
 320   *   The last element of this array should be the new location itself.
 321   *
 322   * This function will set the new breadcrumb trail to the passed-in value,
 323   * but if any elements of this trail are visible in the site tree, the
 324   * trail will be "spliced in" to the existing site navigation at that point.
 325   */
 326  function menu_set_location($location) {
 327    global $_menu;
 328    $temp_id = min(array_keys($_menu['items'])) - 1;
 329    $prev_id = 0;
 330  
 331    // Don't allow this function to change the actual current path, just the
 332    // position in the menu tree.
 333    $location[count($location) - 1]['path'] = $_GET['q'];
 334  
 335    foreach (array_reverse($location) as $item) {
 336      if (isset($_menu['path index'][$item['path']])) {
 337        $mid = $_menu['path index'][$item['path']];
 338        if (isset($_menu['visible'][$mid])) {
 339          // Splice in the breadcrumb at this location.
 340          if ($prev_id) {
 341            $_menu['items'][$prev_id]['pid'] = $mid;
 342          }
 343          $prev_id = 0;
 344          break;
 345        }
 346        else {
 347          // A hidden item; show it, but only temporarily.
 348          $_menu['items'][$mid]['type'] |= MENU_VISIBLE_IN_BREADCRUMB;
 349          if ($prev_id) {
 350            $_menu['items'][$prev_id]['pid'] = $mid;
 351          }
 352          $prev_id = $mid;
 353        }
 354      }
 355      else {
 356        $item['type'] |= MENU_VISIBLE_IN_BREADCRUMB;
 357        if ($prev_id) {
 358          $_menu['items'][$prev_id]['pid'] = $temp_id;
 359        }
 360        $_menu['items'][$temp_id] = $item;
 361        $_menu['path index'][$item['path']] = $temp_id;
 362  
 363        $prev_id = $temp_id;
 364        $temp_id--;
 365      }
 366    }
 367  
 368    if ($prev_id) {
 369      // Didn't find a home, so attach this to the main navigation menu.
 370      $_menu['items'][$prev_id]['pid'] = 1;
 371    }
 372  
 373    $final_item = array_pop($location);
 374    menu_set_active_item($final_item['path']);
 375  }
 376  
 377  /**
 378   * Execute the handler associated with the active menu item.
 379   *
 380   * This is called early in the page request. The active menu item is at
 381   * this point determined exclusively by the URL. The handler that is called
 382   * here may, as a side effect, change the active menu item so that later
 383   * menu functions (that display the menus and breadcrumbs, for example)
 384   * act as if the user were in a different location on the site.
 385   */
 386  function menu_execute_active_handler() {
 387    if (_menu_site_is_offline()) {
 388      return MENU_SITE_OFFLINE;
 389    }
 390  
 391    $menu = menu_get_menu();
 392  
 393    // Determine the menu item containing the callback.
 394    $path = $_GET['q'];
 395    while ($path && !isset($menu['callbacks'][$path])) {
 396      $path = substr($path, 0, strrpos($path, '/'));
 397    }
 398  
 399    if ($path === '' || !isset($menu['callbacks'][$path])) {
 400      return MENU_NOT_FOUND;
 401    }
 402  
 403    if (!function_exists($menu['callbacks'][$path]['callback'])) {
 404      return MENU_NOT_FOUND;
 405    }
 406  
 407    if (!_menu_item_is_accessible(menu_get_active_item())) {
 408      return MENU_ACCESS_DENIED;
 409    }
 410  
 411    // We found one, and are allowed to execute it.
 412    $arguments = isset($menu['callbacks'][$path]['callback arguments']) ? $menu['callbacks'][$path]['callback arguments'] : array();
 413    $arg = substr($_GET['q'], strlen($path) + 1);
 414    if (strlen($arg)) {
 415      $arguments = array_merge($arguments, explode('/', $arg));
 416    }
 417  
 418    return call_user_func_array($menu['callbacks'][$path]['callback'], $arguments);
 419  }
 420  
 421  /**
 422   * Returns the ID of the active menu item.
 423   */
 424  function menu_get_active_item() {
 425    return menu_set_active_item();
 426  }
 427  
 428  /**
 429   * Sets the path of the active menu item.
 430   */
 431  function menu_set_active_item($path = NULL) {
 432    static $stored_mid;
 433  
 434    if (!isset($stored_mid) || isset($path)) {
 435      if (!isset($path)) {
 436        $path = $_GET['q'];
 437      }
 438      else {
 439        $_GET['q'] = $path;
 440      }
 441      $menu = menu_get_menu();
 442  
 443      while ($path && !isset($menu['path index'][$path])) {
 444        $path = substr($path, 0, strrpos($path, '/'));
 445      }
 446      $stored_mid = isset($menu['path index'][$path]) ? $menu['path index'][$path] : 0;
 447  
 448      // Search for default local tasks to activate instead of this item.
 449      $continue = TRUE;
 450      while ($continue) {
 451        $continue = FALSE;
 452        if (isset($menu['items'][$stored_mid]['children'])) {
 453          foreach ($menu['items'][$stored_mid]['children'] as $cid) {
 454            if ($menu['items'][$cid]['type'] & MENU_LINKS_TO_PARENT) {
 455              $stored_mid = $cid;
 456              $continue = TRUE;
 457            }
 458          }
 459        }
 460      }
 461  
 462      // Reset the cached $menu in menu_get_item().
 463      menu_get_item(NULL, NULL, TRUE);
 464    }
 465  
 466    return $stored_mid;
 467  }
 468  
 469  /**
 470   * Returns the ID of the current menu item or, if the current item is a
 471   * local task, the menu item to which this task is attached.
 472   */
 473  function menu_get_active_nontask_item() {
 474    $mid = menu_get_active_item();
 475  
 476    // Find the first non-task item:
 477    while ($mid) {
 478      $item = menu_get_item($mid);
 479  
 480      if (!($item['type'] & MENU_IS_LOCAL_TASK)) {
 481        return $mid;
 482      }
 483  
 484      $mid = $item['pid'];
 485    }
 486  }
 487  
 488  /**
 489   * Returns the title of the active menu item.
 490   */
 491  function menu_get_active_title() {
 492    if ($mid = menu_get_active_nontask_item()) {
 493      $item = menu_get_item($mid);
 494      return $item['title'];
 495    }
 496  }
 497  
 498  /**
 499   * Returns the help associated with the active menu item.
 500   */
 501  function menu_get_active_help() {
 502    $path = $_GET['q'];
 503    $output = '';
 504  
 505    if (!_menu_item_is_accessible(menu_get_active_item())) {
 506      // Don't return help text for areas the user cannot access.
 507      return;
 508    }
 509  
 510    foreach (module_list() as $name) {
 511      if (module_hook($name, 'help')) {
 512        if ($temp = module_invoke($name, 'help', $path)) {
 513          $output .= $temp . "\n";
 514        }
 515        if (module_hook('help', 'page')) {
 516          if (arg(0) == "admin") {
 517            if (module_invoke($name, 'help', 'admin/help#'. arg(2)) && !empty($output)) {
 518              $output .= theme("more_help_link", url('admin/help/'. arg(2)));
 519            }
 520          }
 521        }
 522      }
 523    }
 524    return $output;
 525  }
 526  
 527  /**
 528   * Returns an array of rendered menu items in the active breadcrumb trail.
 529   */
 530  function menu_get_active_breadcrumb() {
 531  
 532    // No breadcrumb for the front page.
 533    if (drupal_is_front_page()) {
 534      return array();
 535    }
 536  
 537    // We do *not* want to use "variable_get('site_frontpage', 'node)" here
 538    // as that will create the link '/node'. This is unsightly and creates
 539    // a second URL for the homepage ('/' *and* '/node').
 540    $links[] = l(t('Home'), '');
 541  
 542    $trail = _menu_get_active_trail();
 543    foreach ($trail as $mid) {
 544      $item = menu_get_item($mid);
 545      if ($item['type'] & MENU_VISIBLE_IN_BREADCRUMB) {
 546        $links[] = menu_item_link($mid);
 547      }
 548    }
 549  
 550    // The last item in the trail is the page title; don't display it here.
 551    array_pop($links);
 552  
 553    return $links;
 554  }
 555  
 556  /**
 557   * Returns TRUE when the menu item is in the active trail.
 558   */
 559  function menu_in_active_trail($mid) {
 560    $trail = _menu_get_active_trail();
 561  
 562    return in_array($mid, $trail);
 563  }
 564  
 565  /**
 566   * Returns TRUE when the menu item is in the active trail within a
 567   * specific subsection of the menu tree.
 568   *
 569   * @param $mid
 570   *   The menu item being considered.
 571   * @param $pid
 572   *   The root of the subsection of the menu tree in which to look.
 573   */
 574  function menu_in_active_trail_in_submenu($mid, $pid) {
 575    $trail = _menu_get_active_trail_in_submenu($pid);
 576  
 577    if (!$trail) {
 578      return FALSE;
 579    }
 580  
 581    return in_array($mid, $trail);
 582  }
 583  
 584  /**
 585   * Populate the database representation of the menu.
 586   *
 587   * This need only be called at the start of pages that modify the menu.
 588   */
 589  function menu_rebuild() {
 590    // Clear the page cache, so that changed menus are reflected for anonymous users.
 591    cache_clear_all('*', 'cache_page', TRUE);
 592    // Also clear the menu cache.
 593    cache_clear_all('*', 'cache_menu', TRUE);
 594  
 595    _menu_build();
 596  
 597    if (module_exists('menu')) {
 598      $menu = menu_get_menu();
 599  
 600      // Fill a queue of new menu items which are modifiable.
 601      $new_items = array();
 602      foreach ($menu['items'] as $mid => $item) {
 603        if ($mid < 0 && ($item['type'] & MENU_MODIFIABLE_BY_ADMIN)) {
 604          $new_items[$mid] = $item;
 605        }
 606      }
 607  
 608      $old_count = -1;
 609      // Save the new items updating the pids in each iteration
 610      while (($c = count($new_items)) && ($c != $old_count)) {
 611        $old_count = count($new_items);
 612        foreach ($new_items as $mid => $item) {
 613          // If the item has a valid parent, save it
 614          if ($item['pid'] >= 0) {
 615            // The new menu ID gets passed back by reference as $item['mid']
 616            menu_save_item($item);
 617            // Fix parent IDs for the children of the menu item just saved
 618            if ($item['children']) {
 619              foreach ($item['children'] as $child) {
 620                if (isset($new_items[$child])) {
 621                  $new_items[$child]['pid'] = $item['mid'];
 622                }
 623              }
 624            }
 625            // remove the item
 626            unset($new_items[$mid]);
 627          }
 628        }
 629      }
 630      // Rebuild the menu to account for the changes.
 631      _menu_build();
 632    }
 633  
 634    // Reset the cached $menu in menu_get_item().
 635    menu_get_item(NULL, NULL, TRUE);
 636  
 637  }
 638  
 639  /**
 640   * Generate the HTML for a menu tree.
 641   *
 642   * @param $pid
 643   *   The parent id of the menu.
 644   *
 645   * @ingroup themeable
 646   */
 647  function theme_menu_tree($pid = 1) {
 648    if ($tree = menu_tree($pid)) {
 649      return "\n<ul class=\"menu\">\n". $tree ."\n</ul>\n";
 650    }
 651  }
 652  
 653  /**
 654   * Returns a rendered menu tree.
 655   *
 656   * @param $pid
 657   *   The parent id of the menu.
 658   */
 659  function menu_tree($pid = 1) {
 660    $menu = menu_get_menu();
 661    $output = '';
 662  
 663    if (isset($menu['visible'][$pid]) && $menu['visible'][$pid]['children']) {
 664      foreach ($menu['visible'][$pid]['children'] as $mid) {
 665        $type = isset($menu['visible'][$mid]['type']) ? $menu['visible'][$mid]['type'] : NULL;
 666        $children = isset($menu['visible'][$mid]['children']) ? $menu['visible'][$mid]['children'] : NULL;
 667        $output .= theme('menu_item', $mid, menu_in_active_trail($mid) || ($type & MENU_EXPANDED) ? theme('menu_tree', $mid) : '', count($children) == 0);
 668      }
 669    }
 670  
 671    return $output;
 672  }
 673  
 674  /**
 675   * Generate the HTML output for a single menu item.
 676   *
 677   * @param $mid
 678   *   The menu id of the item.
 679   * @param $children
 680   *   A string containing any rendered child items of this menu.
 681   * @param $leaf
 682   *   A boolean indicating whether this menu item is a leaf.
 683   *
 684   * @ingroup themeable
 685   */
 686  function theme_menu_item($mid, $children = '', $leaf = TRUE) {
 687    return '<li class="'. ($leaf ? 'leaf' : ($children ? 'expanded' : 'collapsed')) .'">'. menu_item_link($mid) . $children ."</li>\n";
 688  }
 689  
 690  /**
 691   * Generate the HTML representing a given menu item ID.
 692   *
 693   * @param $item
 694   *   The menu item to render.
 695   * @param $link_item
 696   *   The menu item which should be used to find the correct path.
 697   *
 698   * @ingroup themeable
 699   */
 700  function theme_menu_item_link($item, $link_item) {
 701    return l($item['title'], $link_item['path'], !empty($item['description']) ? array('title' => $item['description']) : array(), isset($item['query']) ? $item['query'] : NULL);
 702  }
 703  
 704  /**
 705   * Returns the rendered link to a menu item.
 706   *
 707   * @param $mid
 708   *   The menu item id to render.
 709   * @param $theme
 710   *   Whether to return a themed link or the link as an array
 711   */
 712  function menu_item_link($mid, $theme = TRUE) {
 713    $item = menu_get_item($mid);
 714    $link_item = $item;
 715    $link = '';
 716  
 717    while ($link_item['type'] & MENU_LINKS_TO_PARENT) {
 718      $link_item = menu_get_item($link_item['pid']);
 719    }
 720  
 721    if ($theme) {
 722      $link = theme('menu_item_link', $item, $link_item);
 723    }
 724    else {
 725      $link = array(
 726        'title' => $item['title'],
 727        'href' => $link_item['path'],
 728        'attributes' => !empty($item['description']) ? array('title' => $item['description']) : array()
 729      );
 730    }
 731  
 732    return $link;
 733  }
 734  
 735  /**
 736   * Returns the rendered local tasks. The default implementation renders
 737   * them as tabs.
 738   *
 739   * @ingroup themeable
 740   */
 741  function theme_menu_local_tasks() {
 742    $output = '';
 743  
 744    if ($primary = menu_primary_local_tasks()) {
 745      $output .= "<ul class=\"tabs primary\">\n". $primary ."</ul>\n";
 746    }
 747    if ($secondary = menu_secondary_local_tasks()) {
 748      $output .= "<ul class=\"tabs secondary\">\n". $secondary ."</ul>\n";
 749    }
 750  
 751    return $output;
 752  }
 753  
 754  /**
 755   * Returns the rendered HTML of the primary local tasks.
 756   */
 757  function menu_primary_local_tasks() {
 758    $local_tasks = menu_get_local_tasks();
 759    $pid = menu_get_active_nontask_item();
 760    $output = '';
 761  
 762    if (count($local_tasks[$pid]['children'])) {
 763      foreach ($local_tasks[$pid]['children'] as $mid) {
 764        $output .= theme('menu_local_task', $mid, menu_in_active_trail($mid), TRUE);
 765      }
 766    }
 767  
 768    return $output;
 769  }
 770  
 771  /**
 772   * Returns the rendered HTML of the secondary local tasks.
 773   */
 774  function menu_secondary_local_tasks() {
 775    $local_tasks = menu_get_local_tasks();
 776    $pid = menu_get_active_nontask_item();
 777    $output = '';
 778  
 779    if (count($local_tasks[$pid]['children'])) {
 780      foreach ($local_tasks[$pid]['children'] as $mid) {
 781        if (menu_in_active_trail($mid) && count($local_tasks[$mid]['children']) > 1) {
 782          foreach ($local_tasks[$mid]['children'] as $cid) {
 783            $output .= theme('menu_local_task', $cid, menu_in_active_trail($cid), FALSE);
 784          }
 785        }
 786      }
 787    }
 788  
 789    return $output;
 790  }
 791  
 792  /**
 793   * Generate the HTML representing a given menu item ID as a tab.
 794   *
 795   * @param $mid
 796   *   The menu ID to render.
 797   * @param $active
 798   *   Whether this tab or a subtab is the active menu item.
 799   * @param $primary
 800   *   Whether this tab is a primary tab or a subtab.
 801   *
 802   * @ingroup themeable
 803   */
 804  function theme_menu_local_task($mid, $active, $primary) {
 805    if ($active) {
 806      return '<li class="active">'. menu_item_link($mid) ."</li>\n";
 807    }
 808    else {
 809      return '<li>'. menu_item_link($mid) ."</li>\n";
 810    }
 811  }
 812  
 813  /**
 814   * Returns an array containing the primary links.
 815   * Can optionally descend from the root of the Primary links menu towards the
 816   * current node for a specified number of levels and return that submenu.
 817   * Used to generate a primary/secondary menu from different levels of one menu.
 818   *
 819   * @param $start_level
 820   *   This optional parameter can be used to retrieve a context-sensitive array
 821   *   of links at $start_level levels deep into the Primary links menu.
 822   *   The default is to return the top-level links.
 823   * @param $pid
 824   *   The parent menu ID from which to search for children. Defaults to the
 825   *   menu_primary_menu setting.
 826   * @return A nested array of links and their properties. The keys of
 827   *   the array contain some extra encoded information about the results.
 828   *   The format of the key is {level}-{num}{-active}.
 829   *   level is the depth within the menu tree of this list.
 830   *   num is the number within this array, used only to make the key unique.
 831   *   -active is appended if this element is in the active trail.
 832   */
 833  function menu_primary_links($start_level = 1, $pid = 0) {
 834    if (!module_exists('menu')) {
 835      return NULL;
 836    }
 837    if (!$pid) {
 838      $pid = variable_get('menu_primary_menu', 0);
 839    }
 840    if (!$pid) {
 841      return NULL;
 842    }
 843  
 844    if ($start_level < 1) {
 845      $start_level = 1;
 846    }
 847  
 848    if ($start_level > 1) {
 849      $trail = _menu_get_active_trail_in_submenu($pid);
 850      if (!$trail) {
 851        return NULL;
 852      }
 853      else {
 854        $pid = $trail[$start_level - 1];
 855      }
 856    }
 857  
 858    $menu = menu_get_menu();
 859    $links = array();
 860    if ($pid && is_array($menu['visible'][$pid]) && isset($menu['visible'][$pid]['children'])) {
 861      $count = 1;
 862      foreach ($menu['visible'][$pid]['children'] as $cid) {
 863        $index = "menu-$start_level-$count-$pid";
 864        if (menu_in_active_trail_in_submenu($cid, $pid)) {
 865          $index .= "-active";
 866        }
 867        $links[$index] = menu_item_link($cid, FALSE);
 868        $count++;
 869      }
 870    }
 871  
 872    // Special case - provide link to admin/build/menu if primary links is empty.
 873    if (empty($links) && $start_level == 1 && $pid == variable_get('menu_primary_menu', 0) && user_access('administer menu')) {
 874      $links['1-1'] = array(
 875        'title' => t('Edit primary links'),
 876        'href' => 'admin/build/menu'
 877      );
 878    }
 879  
 880    return $links;
 881  }
 882  
 883  /**
 884   * Returns an array containing the secondary links.
 885   * Secondary links can be either a second level of the Primary links
 886   * menu or generated from their own menu.
 887   */
 888  function menu_secondary_links() {
 889    $msm = variable_get('menu_secondary_menu', 0);
 890    if ($msm == 0) {
 891      return NULL;
 892    }
 893  
 894    if ($msm == variable_get('menu_primary_menu', 0)) {
 895      return menu_primary_links(2, $msm);
 896    }
 897  
 898    return menu_primary_links(1, $msm);
 899  }
 900  
 901  /**
 902   * Returns the themed HTML for primary and secondary links.
 903   * Note that this function is overridden by most core themes because
 904   * those themes display links in "link | link" format, not from a list.
 905   * Also note that by default links rendered with this function are
 906   * displayed with the same CSS as is used for the local tasks.
 907   * If a theme wishes to render links from a ul it is expected that
 908   * the theme will provide suitable CSS.
 909   *
 910   * @param $links
 911   *   An array containing links to render.
 912   * @return
 913   *   A string containing the themed links.
 914   *
 915   * @ingroup themeable
 916   */
 917  function theme_menu_links($links) {
 918    if (!count($links)) {
 919      return '';
 920    }
 921    $level_tmp = explode('-', key($links));
 922    $level = $level_tmp[0];
 923    $output = "<ul class=\"links-$level\">\n";
 924    foreach ($links as $index => $link) {
 925      $output .= '<li';
 926      if (stristr($index, 'active')) {
 927        $output .= ' class="active"';
 928      }
 929      $output .= ">". l($link['title'], $link['href'], $link['attributes'], $link['query'], $link['fragment']) ."</li>\n";
 930    }
 931    $output .= '</ul>';
 932  
 933    return $output;
 934  }
 935  
 936  /**
 937   * @} End of "defgroup menu".
 938   */
 939  
 940  /**
 941   * Returns an array with the menu items that lead to the current menu item.
 942   */
 943  function _menu_get_active_trail() {
 944    static $trail;
 945  
 946    if (!isset($trail)) {
 947      $trail = array();
 948  
 949      $mid = menu_get_active_item();
 950  
 951      // Follow the parents up the chain to get the trail.
 952      while ($mid && ($item = menu_get_item($mid))) {
 953        array_unshift($trail, $mid);
 954        $mid = $item['pid'];
 955      }
 956    }
 957    return $trail;
 958  }
 959  
 960  /**
 961   * Find the active trail through a specific subsection of the menu tree.
 962   *
 963   * @param $pid
 964   *   The root item from which the active trail must descend.
 965   */
 966  function _menu_get_active_trail_in_submenu($pid) {
 967    static $trails;
 968  
 969    if (!isset($trails)) {
 970      // Find all menu items which point to the current node and for each
 971      // follow the parents up the chain to build an active trail.
 972      $trails = array();
 973      $menu = menu_get_menu();
 974      $path = $_GET['q'];
 975      $count = 0;
 976      while ($path && !$count) {
 977        foreach ($menu['items'] as $key => $item) {
 978          if (isset($item['path']) && $item['path'] == $path) {
 979            $trails[$count] = array();
 980            $mid = $key;
 981            while ($mid && $menu['items'][$mid]) {
 982              array_unshift($trails[$count], $mid);
 983              $mid = $menu['items'][$mid]['pid'];
 984            }
 985            $count ++;
 986          }
 987        }
 988        $path = substr($path, 0, strrpos($path, '/'));
 989      }
 990    }
 991  
 992    if ($trails) {
 993      foreach ($trails as $trail) {
 994        $count_trail = count($trail);
 995        for ($i = 0; $i < $count_trail; $i++) {
 996          if ($trail[$i] == $pid) {
 997            // Return a trail from $pid down to the current page inclusive.
 998            for ( ; $i < $count_trail; $i++) {
 999              $subtrail[] = $trail[$i];
1000            }
1001            return $subtrail;
1002          }
1003        }
1004      }
1005    }
1006  
1007    return NULL;
1008  }
1009  
1010  /**
1011   * Comparator routine for use in sorting menu items.
1012   */
1013  function _menu_sort($a, $b) {
1014    $menu = menu_get_menu();
1015  
1016    $a = $menu['items'][$a];
1017    $b = $menu['items'][$b];
1018  
1019    if ($a['weight'] < $b['weight']) {
1020      return -1;
1021    }
1022    elseif ($a['weight'] > $b['weight']) {
1023      return 1;
1024    }
1025    elseif (isset($a['title']) && isset($b['title'])) {
1026      return strnatcasecmp($a['title'], $b['title']);
1027    }
1028    else {
1029      return 1;
1030    }
1031  }
1032  
1033  /**
1034   * Build the menu by querying both modules and the database.
1035   */
1036  function _menu_build() {
1037    global $_menu;
1038    global $user;
1039  
1040    // Start from a clean slate.
1041    $_menu = array();
1042  
1043    $_menu['path index'] = array();
1044    // Set up items array, including default "Navigation" menu.
1045    $_menu['items'] = array(
1046      0 => array('path' => '', 'title' => '', 'type' => MENU_IS_ROOT),
1047      1 => array('pid' => 0, 'path' => '', 'title' => t('Navigation'), 'weight' => -50, 'access' => TRUE, 'type' => MENU_IS_ROOT | MENU_VISIBLE_IN_TREE)
1048      );
1049    $_menu['callbacks'] = array();
1050  
1051    // Build a sequential list of all menu items.
1052    $menu_item_list = module_invoke_all('menu', TRUE);
1053  
1054    // Menu items not in the DB get temporary negative IDs.
1055    $temp_mid = -1;
1056  
1057    foreach ($menu_item_list as $item) {
1058      if (!isset($item['path'])) {
1059        $item['path'] = '';
1060      }
1061      if (!isset($item['type'])) {
1062        $item['type'] = MENU_NORMAL_ITEM;
1063      }
1064      if (!isset($item['weight'])) {
1065        $item['weight'] = 0;
1066      }
1067      $mid = $temp_mid;
1068      if (isset($_menu['path index'][$item['path']])) {
1069        // Newer menu items overwrite older ones.
1070        unset($_menu['items'][$_menu['path index'][$item['path']]]);
1071      }
1072      if (isset($item['callback'])) {
1073        $_menu['callbacks'][$item['path']] = array('callback' => $item['callback']);
1074        if (isset($item['callback arguments'])) {
1075          $_menu['callbacks'][$item['path']]['callback arguments'] = $item['callback arguments'];
1076          unset($item['callback arguments']);
1077        }
1078        unset($item['callback']);
1079      }
1080      $_menu['items'][$mid] = $item;
1081      $_menu['path index'][$item['path']] = $mid;
1082  
1083      $temp_mid--;
1084    }
1085  
1086    // Now fetch items from the DB, reassigning menu IDs as needed.
1087    if (module_exists('menu')) {
1088      $result = db_query(db_rewrite_sql('SELECT m.mid, m.* FROM {menu} m ORDER BY m.mid ASC', 'm', 'mid'));
1089      while ($item = db_fetch_object($result)) {
1090        // Handle URL aliases if entered in menu administration.
1091        if (!isset($_menu['path index'][$item->path])) {
1092          $item->path = drupal_get_normal_path($item->path);
1093        }
1094        if (isset($_menu['path index'][$item->path])) {
1095          // The path is already declared.
1096          $old_mid = $_menu['path index'][$item->path];
1097          if ($old_mid < 0) {
1098            // It had a temporary ID, so use a permanent one.
1099            $_menu['items'][$item->mid] = $_menu['items'][$old_mid];
1100            if ($_menu['items'][$item->mid]['type'] & $item->type) {
1101              // If the item is of the same type, delete the old item.
1102              unset($_menu['items'][$old_mid]);
1103              $_menu['path index'][$item->path] = $item->mid;
1104            }
1105            // The new menu item gets all the custom type flags from the database
1106            $_menu['items'][$item->mid]['type'] &= $item->type;
1107          }
1108          else {
1109            // It has a permanent ID. Only replace with non-custom menu items.
1110            if ($item->type & MENU_CREATED_BY_ADMIN) {
1111              $_menu['items'][$item->mid] = array('path' => $item->path);
1112            }
1113            else {
1114              // Leave the old item around as a shortcut to this one.
1115              $_menu['items'][$item->mid] = $_menu['items'][$old_mid];
1116              $_menu['path index'][$item->path] = $item->mid;
1117            }
1118          }
1119        }
1120        else {
1121          // The path was not declared, so this is a custom item or an orphaned one.
1122          if ($item->type & MENU_CREATED_BY_ADMIN) {
1123            $_menu['items'][$item->mid] = array('path' => $item->path);
1124            if (!empty($item->path)) {
1125              $_menu['path index'][$item->path] = $item->mid;
1126            }
1127          }
1128        }
1129  
1130        // If the administrator has changed the item, reflect the change.
1131        if ($item->type & MENU_MODIFIED_BY_ADMIN) {
1132          $_menu['items'][$item->mid]['title'] = $item->title;
1133          $_menu['items'][$item->mid]['description'] = $item->description;
1134          $_menu['items'][$item->mid]['pid'] = $item->pid;
1135          $_menu['items'][$item->mid]['weight'] = $item->weight;
1136          $_menu['items'][$item->mid]['type'] = $item->type;
1137        }
1138      }
1139    }
1140  
1141    // Associate parent and child menu items.
1142    _menu_find_parents($_menu['items']);
1143  
1144    // Prepare to display trees to the user as required.
1145    _menu_build_visible_tree();
1146  }
1147  
1148  /**
1149   * Determine whether the given menu item is accessible to the current user.
1150   *
1151   * Use this instead of just checking the "access" property of a menu item
1152   * to properly handle items with fall-through semantics.
1153   */
1154  function _menu_item_is_accessible($mid) {
1155    $menu = menu_get_menu();
1156  
1157    // Follow the path up to find the first "access" attribute.
1158    $path = isset($menu['items'][$mid]['path']) ? $menu['items'][$mid]['path'] : NULL;
1159    while ($path && (!isset($menu['path index'][$path]) || !isset($menu['items'][$menu['path index'][$path]]['access']))) {
1160      $path = substr($path, 0, strrpos($path, '/'));
1161    }
1162    if (empty($path)) {
1163      // Items without any access attribute up the chain are denied, unless they
1164      // were created by the admin. They most likely point to non-Drupal directories
1165      // or to an external URL and should be allowed.
1166      return $menu['items'][$mid]['type'] & MENU_CREATED_BY_ADMIN;
1167    }
1168    return $menu['items'][$menu['path index'][$path]]['access'];
1169  }
1170  
1171  /**
1172   * Find all visible items in the menu tree, for ease in displaying to user.
1173   *
1174   * Since this is only for display, we only need title, path, and children
1175   * for each item.
1176   */
1177  function _menu_build_visible_tree($pid = 0) {
1178    global $_menu;
1179  
1180    if (isset($_menu['items'][$pid])) {
1181      $parent = $_menu['items'][$pid];
1182  
1183      $children = array();
1184      if (isset($parent['children'])) {
1185        usort($parent['children'], '_menu_sort');
1186        foreach ($parent['children'] as $mid) {
1187          $children = array_merge($children, _menu_build_visible_tree($mid));
1188        }
1189      }
1190      $visible = ($parent['type'] & MENU_VISIBLE_IN_TREE) ||
1191        ($parent['type'] & MENU_VISIBLE_IF_HAS_CHILDREN && count($children) > 0);
1192      $allowed = _menu_item_is_accessible($pid);
1193  
1194      if (($parent['type'] & MENU_IS_ROOT) || ($visible && $allowed)) {
1195        $_menu['visible'][$pid] = array('title' => $parent['title'], 'path' => $parent['path'], 'children' => $children, 'type' => $parent['type']);
1196        foreach ($children as $mid) {
1197          $_menu['visible'][$mid]['pid'] = $pid;
1198        }
1199        return array($pid);
1200      }
1201      else {
1202        return $children;
1203      }
1204    }
1205  
1206    return array();
1207  }
1208  
1209  /**
1210   * Account for menu items that are only defined at certain paths, so will not
1211   * be cached.
1212   *
1213   * We don't support the full range of menu item options for these menu items. We
1214   * don't support MENU_VISIBLE_IF_HAS_CHILDREN, and we require parent items to be
1215   * declared before their children.
1216   */
1217  function _menu_append_contextual_items() {
1218    global $_menu;
1219  
1220    // Build a sequential list of all menu items.
1221    $menu_item_list = module_invoke_all('menu', FALSE);
1222  
1223    // Menu items not in the DB get temporary negative IDs.
1224    $temp_mid = min(array_keys($_menu['items'])) - 1;
1225    $new_items = array();
1226  
1227    foreach ($menu_item_list as $item) {
1228      if (isset($item['callback'])) {
1229        $_menu['callbacks'][$item['path']] = array('callback' => $item['callback']);
1230        if (isset($item['callback arguments'])) {
1231          $_menu['callbacks'][$item['path']]['callback arguments'] = $item['callback arguments'];
1232          unset($item['callback arguments']);
1233        }
1234        unset($item['callback']);
1235      }
1236      if (!isset($_menu['path index'][$item['path']])) {
1237        if (!isset($item['path'])) {
1238          $item['path'] = '';
1239        }
1240        if (!isset($item['type'])) {
1241          $item['type'] = MENU_NORMAL_ITEM;
1242        }
1243        if (!isset($item['weight'])) {
1244          $item['weight'] = 0;
1245        }
1246        $_menu['items'][$temp_mid] = $item;
1247        $_menu['path index'][$item['path']] = $temp_mid;
1248        $new_items[$temp_mid] = $item;
1249        $temp_mid--;
1250      }
1251      else {
1252        $mid = $_menu['path index'][$item['path']];
1253        if ($_menu['items'][$mid]['type'] & MENU_CREATED_BY_ADMIN) {
1254          $_menu['items'][$mid]['access'] = $item['access'];
1255          if (isset($_menu['items'][$mid]['callback'])) {
1256            $_menu['items'][$mid]['callback'] = $item['callback'];
1257          }
1258          if (isset($_menu['items'][$mid]['callback arguments'])) {
1259            $_menu['items'][$mid]['callback arguments'] = $item['callback arguments'];
1260          }
1261        }
1262        if ($item['type'] & MENU_LOCAL_TASK && !($_menu['items'][$mid]['type'] & MENU_LOCAL_TASK)) {
1263          // A local task is in the menu table and the path is already present
1264          $_menu['items'][$mid]['type'] = MENU_LOCAL_TASK;
1265          $new_items[$mid] = $item;
1266        }
1267      }
1268    }
1269  
1270    // Establish parent-child relationships.
1271    _menu_find_parents($new_items);
1272  
1273    // Add new items to the visible tree if necessary.
1274    foreach ($new_items as $mid => $item) {
1275      $item = $_menu['items'][$mid];
1276      if (($item['type'] & MENU_VISIBLE_IN_TREE) && _menu_item_is_accessible($mid)) {
1277        $pid = $item['pid'];
1278        while ($pid && !isset($_menu['visible'][$pid])) {
1279          $pid = $_menu['items'][$pid]['pid'];
1280        }
1281        $_menu['visible'][$mid] = array('title' => $item['title'], 'path' => $item['path'], 'pid' => $pid);
1282        $_menu['visible'][$pid]['children'][] = $mid;
1283        usort($_menu['visible'][$pid]['children'], '_menu_sort');
1284      }
1285    }
1286  }
1287  
1288  /**
1289   * Establish parent-child relationships.
1290   */
1291  function _menu_find_parents(&$items) {
1292    global $_menu;
1293  
1294    foreach ($items as $mid => $item) {
1295      if (!isset($item['pid'])) {
1296        // Parent's location has not been customized, so figure it out using the path.
1297        $parent = $item['path'];
1298        if ($parent) {
1299          do {
1300            $parent = substr($parent, 0, strrpos($parent, '/'));
1301          }
1302          while ($parent && !isset($_menu['path index'][$parent]));
1303        }
1304  
1305        $pid = $parent ? $_menu['path index'][$parent] : 1;
1306        $_menu['items'][$mid]['pid'] = $pid;
1307      }
1308      else {
1309        $pid = $item['pid'];
1310      }
1311  
1312      // Don't make root a child of itself.
1313      if ($mid) {
1314        if (isset ($_menu['items'][$pid])) {
1315          $_menu['items'][$pid]['children'][] = $mid;
1316        }
1317        else {
1318          // If parent is missing, it is a menu item that used to be defined
1319          // but is no longer. Default to a root-level "Navigation" menu item.
1320          $_menu['items'][1]['children'][] = $mid;
1321        }
1322      }
1323    }
1324  }
1325  
1326  /**
1327   * Find all the items in the current local task tree.
1328   *
1329   * Since this is only for display, we only need title, path, and children
1330   * for each item.
1331   *
1332   * At the close of this function, $_menu['local tasks'] is populated with the
1333   * menu items in the local task tree.
1334   *
1335   * @return
1336   *   TRUE if the local task tree is forked. It does not need to be displayed
1337   *   otherwise.
1338   */
1339  function _menu_build_local_tasks($pid) {
1340    global $_menu;
1341  
1342    $forked = FALSE;
1343  
1344    if (isset($_menu['items'][$pid])) {
1345      $parent = $_menu['items'][$pid];
1346  
1347      $children = array();
1348      if (isset($parent['children'])) {
1349        foreach ($parent['children'] as $mid) {
1350          if (($_menu['items'][$mid]['type'] & MENU_IS_LOCAL_TASK) && _menu_item_is_accessible($mid)) {
1351            $children[] = $mid;
1352            // Beware short-circuiting || operator!
1353            $forked = _menu_build_local_tasks($mid) || $forked;
1354          }
1355        }
1356      }
1357      usort($children, '_menu_sort');
1358      $forked = $forked || count($children) > 1;
1359  
1360      $_menu['local tasks'][$pid] = array('title' => $parent['title'], 'path' => $parent['path'], 'children' => $children);
1361      foreach ($children as $mid) {
1362        $_menu['local tasks'][$mid]['pid'] = $pid;
1363      }
1364    }
1365  
1366    return $forked;
1367  }
1368  
1369  /**
1370   * Returns TRUE if the site is off-line for maintenance.
1371   */
1372  function _menu_site_is_offline() {
1373    // Check if site is set to off-line mode
1374    if (variable_get('site_offline', 0)) {
1375      // Check if the user has administration privileges
1376      if (!user_access('administer site configuration')) {
1377        // Check if this is an attempt to login
1378        if (drupal_get_normal_path($_GET['q']) != 'user') {
1379          return TRUE;
1380        }
1381      }
1382      else {
1383        $offline_message = t('Operating in off-line mode.');
1384        $messages = drupal_set_message();
1385        // Ensure that the off-line message is displayed only once [allowing for page redirects].
1386        if (!isset($messages) || !isset($messages['status']) || !in_array($offline_message, $messages['status'])) {
1387          drupal_set_message($offline_message);
1388        }
1389      }
1390    }
1391    return FALSE;
1392  }


Généré le : Fri Nov 30 16:20:15 2007 par Balluche grâce à PHPXref 0.7
  Clicky Web Analytics