We had a client who wanted the site's menu system to be used as the sole organizing principle. To allow editorial access based on menu items, however, we had to figure out how to write JOINs from the {menu_links} table to the {node} table, which is currently not supported.
We created the Menu Node API to address this problem. However, it works mainly by altering and watching form submissions, since there is no direct menu save hook (something similar to hook_nodeapi).
Would having a {menu_node} table in core be useful? If so, we can craft a patch to allow the maintenance of this table without hook_form_alter().
Comment | File | Size | Author |
---|---|---|---|
#56 | 457450-followup.patch | 2.3 KB | agentrickard |
#45 | 457450-menu-link-hooks.patch | 7.7 KB | agentrickard |
#43 | 457450-menu-item-hooks.patch | 7.75 KB | agentrickard |
#41 | 457450-menu-objects-ext.patch | 15.71 KB | agentrickard |
#36 | 457450-menu-objects.patch | 14.13 KB | agentrickard |
Comments
Comment #1
simeI think this is a useful contrib module, I have this request a lot.
I agree it's difficult that you can't see where links were created (either from menu admin or the node form), this would also solve that problem (unless I've missed something about the menu system).
Comment #2
martidau CreditAttribution: martidau commentedThe ability to join the menu_links table from a view was one of the first things I needed to do after using drupal for 2 days. Should definitely be core functionality.
Comment #3
agentrickard@martidau
Off topic: Did you manage to do it? Can you post something over at http://drupal.org/project/menu_node?
Comment #4
Mac Clemmens CreditAttribution: Mac Clemmens commented+1 on this.
Comment #5
ronan CreditAttribution: ronan commented+1 from me also. I've implemented the same functionality in the Node Hierarchy module. I'm sure these aren't the only two implementations of this in contrib.
Comment #6
agentrickardHere's a patch to get started with. Requires running update.php.
I put the code into menu.inc, on the theory that you can create node menu items without menu module turned on. If this is a false assumption, then we simply move the code into menu.module.
This feature should do the following:
-- CRUD storage of the {menu_node} table, storing MLID and NID.
-- Fire internal hooks notifying modules of menu node changes.
These hooks are currently menu_node_insert($item, $node), menu_node_update($item, $node) and menu_node_delete($item, $node).
TODO:
-- Tests.
-- Clean up the update hook.
-- Decide on hook names.
-- Decide if this belongs in menu.inc or menu.module.
Comment #7
sunWhy can't you use http://api.drupal.org/api/function/hook_menu_link_alter/7 in your module?
If at all, this is node-related, and thereby should go into node.module.
Comment #8
agentrickard@sun
Hmmmm. That is an excellent point and may work in the D6 version so I can ignore all the form_alter nonsense currently used in Menu Node API.
That's why the extra eyeballs are a good thing.
But hook_menu_links_alter() only works for save. What about delete? I suppose we can get that in hook_nodeapi() and hook_node_delete().
/me thinks about the re-roll....
Comment #9
sunWell, yes, that part of the menu system is still a bit confusing.
We have http://api.drupal.org/api/function/menu_link_maintain/7, but that's only ever called from aggregator.module. Additionally, my first assumption on this function was that it's rather a hook, or it (also) invokes a hook that allows other modules to perform additional actions whenever a menu link is altered. But no-go. That part we have to improve for sure.
Comment #10
agentrickardWell, I started looking at moving this into the node module. The problem is that you can create and delete menu items that point to nodes from _outside_ the node module. (And, in fact, that logic is currently handled by menu.module using node hooks.)
For example, if you delete an entire menu, then the {menu_node} table will not get updated because hook_menu_item_alter() doesn't fire on delete, just on save. (Which is why the D6 module uses hook_form_alter() to watch multiple forms).
I also would prefer to fire a menu_node specific hook, and don't really want to do that inside hook_menu_item_alter().
I think we have to place this as high up in the menu call stack as possible, which is why I originally targeted menu.inc.
So I think this has to stay in menu.inc or menu.module.
Comment #11
agentrickardUpdated patch corrects the hook_update_N function a little.
We still need review on where this should live, and then tests.
Comment #12
agentrickardCross post. I need to review #9.
Comment #13
agentrickardOK, I read #9 and menu_link_maintain() is just an odd function. It looks like someone wrote it for general use and then it sits unused. Technically, I think menu.module should be using it for its current node handling, but it doesn't.
Perhaps we need a more generic hook in menu.inc that is then implemented in node.module or menu.module. The hook would respond to menu link CRUD.
The issue there is that, for this API to work properly, we need all the information about the link before it is actually deleted.
Comment #14
agentrickardAfter a discussiuon with sun in IRC, we decided to try tracking the object's foreign keys directly in the {menu_links} table. This approach will let us run direct JOINs on that table, and treats other items as first-class objects (not just nodes).
To test:
-- Run update.php to update {menu_links}.
-- Add some node-based menu items
Since menu items can be created from the menu module and from nodes, we need to declare a hook_menu_object() function to derive the object type. Otherwise, we can pass the object data when calling menu_link_save() directly from a module.
The patch also introduces hook_menu_item_insert() hook_menu_item_update() and hook_menu_item_delete().
TODO:
-- Tests
-- Logic checking
-- Conversion of other menu_link_save() calls to add object data. (Though node may be the only one right now.)
-- Documentation
Comment #15
Damien Tournoud CreditAttribution: Damien Tournoud commented- hook_menu_object() seems unneeded. Considering a wildcard loader like '%node', 'node' is the object type, and its value in the path is the object id
- hook_menu_item_insert() and hook_menu_item_update() looks like scope creep, especially because they are not used in the current patch
Comment #17
sunhook_menu_link_save() or hook_menu_link_maintain() is the part of this patch we want to backport to D6, if possible. As agentrickard mentioned, modules have to hack their way through all possible forms that may lead to a menu link update to track potential updates.
Comment #18
agentrickardI agree that hook_menu_object() is not needed -- we can do this in hook_menu_link_alter() instead. But I don't think we can deduce the object from the router path reliably. (We can for core, but since modules can alter the router path, that makes the whole mess unstable.)
But I can roll a version that reads the router path.
I actually have use-cases in contrib that use the hook_menu_item_update() functions, which are currently very hard to implement.
Comment #19
agentrickardHere is a patch that reads the menu object type and id based on the router_path and its associated loader function. Note, however, that this deliberately fails for menu item with multiple loaders.
TODO:
-- Tests
-- What to do (if anything) about multiple loader arguments.
Comment #20
Dries CreditAttribution: Dries commented- I agree that menu_link_maintain() is odd -- maybe it needs to be reconsidered?
-
It is cleaner and more readable to write that as a regular if-else.
- Elsewhere in Drupal, we write ID instead of id -- at least in comments and PHPdoc.
- Should we write tests for this?
Comment #21
Damien Tournoud CreditAttribution: Damien Tournoud commentedStoring the object_type and object_id in the menu link is really odd. Those belong to the menu router (and there is direct relation from the menu link to a menu router via themenu_link.router_path
column).The information about the object_type is already in the menu_router table by the way (in the load_functions serialized blob), but it makes sense to make that a proper column if we want to be able to join on this.Comment #22
agentrickard@Damien
I see no use-case currently where someone would want to query for the object type of a router item. What we want is information about the items displayed by the menus, which is stored in {menu_links}.
The entire point to the patch is to be able to write efficient JOINs against {menu_links} directly. I suppose we could store this data in {menu_router}, but that adds a layer of indirection that we may not want, particularly when using modules like Views to write queries.
Since we cannot store the object id in the router table, it makes sense to me (from an optimization standpoint) to store the object type directly in {menu_links}, which is where we need to run the queries. Compare:
The primary use-case of this patch is to allow Views of menu items and to allow Node Access systems to leverage the menu hierarchy.
Comment #23
Damien Tournoud CreditAttribution: Damien Tournoud commented@agentrickard: yes, sorry, scratch #21 completely, I should not try to do several things at the same time.
Comment #24
agentrickardNot a problem.
What do people think about the problem of multiple loader functions? Is taking the first match acceptable?
And, yes, we should write some tests that cover:
-- Do the three hooks fire as expected?
-- Are node items added to the menu identified correctly?
-- Are taxonomy term paths added to the menu identified correctly?
That should be enough, I suppose.
Comment #25
agentrickardMinor patch revision, in light of #20.
Now for tests....
Comment #26
agentrickardRevised with one set of tests.
Any ideas for how to test the three new hook implementations?
Comment #28
agentrickardI suppose tests have to be packaged separately?
Comment #29
agentrickardNope, there was a change.
Here is the current patch, with tests, but the update test fails because menu_link_maintain is inconsistent.
#423690: menu_link_maintain(), $op update critically broken.
Comment #31
agentrickardTestbot is wrong. Patch applies.
Now with API docs.
Comment #32
agentrickardBah. Left in a debug message.
Comment #34
agentrickardOne more time, robot overlord!
Comment #36
agentrickardTwo versions of the revised patch.
The first is just this issue.
The second incorporates the latest patch against #423690: menu_link_maintain(), $op update critically broken.
Who knows how testbot will respond, but all tests pass on my dev.
Comment #38
agentrickardYea. testbot is just f'ing wrong.
/me goes to do something more productive.
Comment #39
pwolanin CreditAttribution: pwolanin commentedI see possible good and bad points here - the good might include getting away fromt he current hack of parsing th epaths to find node links for an optimized access check.
Comment #40
agentrickard@pwolanin
If we have to scale back the patch, I would argue for the 'hook_menu_item_X' elements of the patch. Having those will allow me to do the object tracking separately. (In D6 I have to hook_form_alter() something like 5 forms to track the data I need.)
The original intent was for tracking nodes for use by node access modules and Views, so that we can use the menu system as a site hierarchy. I have no vision of this patch in terms of optimizing the menu system itself.
Comment #41
agentrickardBumping back to testbot.
Comment #43
agentrickardOK. I have backed off making any changes to the {menu_links} table and storing the menu object type in the db.
This is a more limited patch which does 4 things:
1) Introduces hook_menu_item_insert()
2) Introduces hook_menu_item_update()
3) Introduces hook_menu_item_delete()
4) Cleans up menu_link_maintain to use menu_link_save() on the update op. (See http://drupal.org/node/423690#comment-1775258 for reference.)
I would _really_ like to see this get in..... Tests all pass. There is API documentation.
What else do we need, if anything?
Comment #44
pwolanin CreditAttribution: pwolanin commentedAny function relating to a menu link should use "link" not "item" (which is ambiguous and should be removed where-ever possible).
Comment #45
agentrickardRe-rolled.
pwolanin and I just discussed in IRC splitting off the object tracking to a new issue, as well. But this has to go in first, IMO. #557356: Track object types for menu links
Comment #46
pwolanin CreditAttribution: pwolanin commentedthis seems to make changes to menu_lnk_maintain that I thought were already in another patch
Comment #47
agentrickard@pwolanin
I merged it here, since that issue had been marked 'fixed' once and people didn't seem to want to re-open it.
Comment #48
pwolanin CreditAttribution: pwolanin commentedAside from hook proliferation, this seems pretty reasonable
we already have a menu_link_alter hook, which is essentially the "pre-save" step.
Comment #49
pwolanin CreditAttribution: pwolanin commentedtagging
Comment #50
pwolanin CreditAttribution: pwolanin commentedSimple enough change that makes the menu link API richer. They allow potentially much broader use of the menu hierarchy functions for other hierarchies.
Comment #51
agentrickardMy understanding at DrupalCON Paris was that this was held up pending some performance concerns.
If this is true, can someone explain what those might be, so I can whip up a test or two?
Comment #52
webchickOh. I think Peter voiced some concern about how this would impact a menu_rebuild(), but those are pretty slow anyway. :P I see he's marked it RTBC despite these concerns.
I checked this over once more, and it looks good. I've run into a situation where I wanted to keep menu items and taxonomy terms in sync and this would've made it much easier. Even comes with tests. :D
Committed to HEAD. Marking needs work for documentation.
Comment #53
agentrickardWhat other documentation do you want? The patch contains elements for menu.api.php already...
Module upgrade docs?
BTW: I think that static variable testing trick might be useful in more places...
Comment #54
agentrickardThere are some typos in the API docs. So will need a follow-up.
Comment #55
webchickUpgrade docs, yeah. We should inform developers about new APIs as well as changes to existing ones.
Comment #56
agentrickardFixed errors in the documentation.
Comment #57
agentrickardUpgrade docs added, too --> http://drupal.org/node/224333#menu-link-hooks
Comment #58
webchickCommitted to HEAD.
Comment #63
David_Rothstein CreditAttribution: David_Rothstein commented