Router, tabs and breadcrumbs.

We encourage users to post events happening in the community to the community events group on https://www.drupal.org.
donquixote's picture

A follow-up to chx' Menu needs to be torn apart.
(this linked thing is a wiki page, so no discussion possible over there)

What chx said:

1. Router system with access control.
1. Tabs with access control.
1. Menu links

These three have little to do in common and yet we keep them together.

Chx actually said more than this, but for now all I want is a problem description.

Changes that I hope we can agree on:
- menu_router has very little to do with "menu", so it should just be called "router" or "path_router" or whatever.
- same for hook_menu, which could be renamed "hook_router" or "hook_route".
- the idea of producing a navigation menu tree from router data has always been a major pita.
- the code of router and menu_links should be disentangled from each other and live in different places (files).
- anything with menu_links could be optional in a "smallcore" Drupal.

Things that should be discussed:
- Is it ok if items in menu_router / router / hook_menu / hook_router has meta information like "title" or "type", that are used to build tabs, breadcrumbs, menu links?
- How else can we build tabs and breadcrumbs?
- How else can we build an administration menu, if not from hook_menu / hook_router information?
- Does the administration menu need to live in menu_links?

Thoughts

Convenience for module developers:
Right now (D6/D7) a module developer can build local tasks (tabs) and admin menu links with very little effort. All he needs to do is implement hook_menu(). In most cases this is what the developer wants - especially for admin backend links. In some cases we want to change this behavior, but I don't think this requires to crush the existing system.

Uniqueness of the tab parent.
One path can be linked in an arbitrary number of menu links. We might prefer menus without duplicates, but this is not guaranteed.
This is different for tabs: On a given page, there is only one collection of tabs, and only one of them is "active".
Clicking on one of the sibling tabs should leave us in the same tab collection, just with a different "active" item.

Tabs can be parameterized.
In a typical Drupal menu (living in menu_links), every stored link path must be an explicit path.
With tabs, the link path can depend on the current page: On node/123 you get a node/123/edit tab, but on node/456 you get a node/456/edit tab. Yet, we don't have to store this structure for every node separately.
It would be quite sexy to have something similar for manually created menu links. Cannot be so difficult, can it? Would be interesting to see in action.

Tabs are built from path fragments.
In D6/D7, two pages are displayed as siblings if they share all but the last fragment of their system path. Chopping off the last fragment gives us the parent.
If the router path contains a wildcard, then the same wildcard in sibling paths is filled with the same argument, resulting in the above mentioned paremeterized behavior.
In many cases this is what we want, but sometimes we would like to be more flexible.

Link title vs page title.
Router items contain a "title" and "title callback" that are used to produce a title to display in tabs, breadcrumbs and menu links, but that will also be used as a page title in html head, and displayed in h1 or h2 headline on page.tpl.php.
Often we want a short title for breadcrumbs and tabs, but a longer title for html head and the page headline. In D6 / D7 this can be achieved by having the page title override the title from the router item via drupal_set_title().
We might want to have an additional "long title" in hook_menu / hook_router, that will be used for the page title.

Trail and breadcrumb.
I think crumbs is the way to go. Unstable or not, the idea of this module is a good one and has a future. Given a path, we determine a parent. Repeat until we run into a dead end or the front page. This way we get a trail (for menu visibility) and a breadcrumb.
Ideally, when dealing with tabs, the direct parent should be identical with the tab parent.
Fallback / default: Get the parent path by chopping off the last path fragment.

Access checking.
The access callback in hook_menu / hook_router items allows not just to show "Access denied" pages, but also to hide tabs and menu links if the user has no access to this page.
In most cases this is exactly what we want. For the (rare) exceptions, we could find a way to override this, and hide tabs even if the user has path access, or show them even if he has no access (for whatever reason).

Administration menu?
The Admin menu as we know it in D6 / D7 maintains a structure in menu_links, that is built from menu_router. Very much like the original system navigation menu. This process is problematic, and suffers from issues like #550254: Menu links are sometimes not properly re-parented, and the general conflict of manual manipulation vs machine manipulation.
There is an alternative called DQX AdminMenu, which does not use the menu_links system at all, and fetches directly from menu_router to get a renderable menu structure.

Proposed solution

Here is what I propose:
1. Rename hook_menu and menu_router, none of which have anything to do with "menu".
2. Move anything related to menu_links into an optional module.
3. Move the expensive and problematic _menu_navigation_links_rebuild() into an optional module.
4. Keep the meta information in hook_router items, such as: (link) title, information for tab building, information for breadcrumb building (here I disagree with chx).
5. Provide an additional parent_path or parent_path_callback setting in hook_router items, as an alternative to the usual "chop off the last path fragment".
6. Allow contrib modules to override tab and breadcrumb building for specific pages, while keeping the existing behavior as a default / fallback.