Chain Menu Access API is has no functionality on its own — install it only if another module requests it.
The remainder of this page is directed towards module developers:
Chain Menu Access API allows your module to chain its own menu access callback functions into other modules' menu router entries.
In Drupal 7 menu access is determined very early during the page loading process. If your module wants to alter the access to some other other module's paths, then the cleanest way to do this is to implement hook_menu_alter() and to change the values of the "access callback" and "access argument" keys of the corresponding menu router items (see hook_menu()).
The trivial approach is to simply replace the values of those two keys with your own and take over the access checking completely. However, this is rarely appropriate. The usual case is that your module wants to either restrict or extend access, and the resulting behavior should be a joint effort between your module and the module that owns the menu item.
Even more generally, there may be additional third-party modules that are interested in restricting or extending access, so simply replacing the values is a no-no! Rather, you want to install your callback in such a way that Drupal calls both your callback and the original one(s) and that it figures out the final result from the two votes.
The original callback doesn't know about any of this, so you have to take the lead and manage it. This is called chaining: In the general case, several modules have already installed their callbacks, then your module inserts its own callback in the front of the existing chain, and some other modules may come after you and add even more callbacks in front of you.
This is done by implementing hook_menu_alter() and changing the "access callback" and "access argument" values of the desired menu items. For each item you need to save the old values, install your own callback, that when the callback is called, you need to evaluate the old access and the new one defined by your module, and then merge the two.
This can certainly be done 'manually,' but it's not quite easy to get right in all cases, and if you need to do it for multiple menu items, then it can become tedious. This is the one thing that Chain Menu Access API does, and it does it very well.
I needed this functionality and chx was kind enough to implement a chaining function on the fly (in 3.5 minutes). It turned out that this first version didn't work quite right in every situation, and in an extended chat session, he fixed and generalized it to what we have now. My role was to act as a catalyzer, to add comments and some tweaks, and to turn it into this module.
Chain Menu Access API provides not only a useful working function, which will hopefully find its way into core some day, it's also an interesting piece of code with a number of useful techniques that you can learn from.
How to Use
Implement hook_menu_alter(), and in the simplest case just call
chain_menu_access_chain($menu, 'the/existing/path', '_mymodule_new_access_callback');
where _mymodule_new_access_callback is an access callback function just like you would implement for your own menu items. If the function requires access arguments, then pass them in an array as the fourth argument:
chain_menu_access_chain($menu, 'the/existing/path', '_mymodule_new_access_callback', array('arg1', 'arg2'));
The syntax for 6.x-1.x and the obsolete 7.x-1.x is
chain_menu_access_chain($menu['the/existing/path'], '_mymodule_new_access_callback', array('arg1', 'arg2'));
The results of your access callback and of the one already present in the menu item are combined using the AND operator by default, i.e. if either returns FALSE, then the access check fails. This means your new callback restricts access to the given path. Pass TRUE as the fifth ($or) argument to combine the results using OR instead, meaning that your new callback loosens access to the given path.
Chain Menu Access API supports all possible value combinations for 'access callback' and 'access arguments' as documented in hook_menu(), both for the old and the new access callbacks. Thus, you can take any 'access callback' and 'access arguments' pair exactly as you would put into your own menu items and chain it in front of the 'access callback' of any existing menu router item, and Chain Menu Access API does all the rest.
Finally, the fifth parameter is overloaded — you can pass a numeric index ($pass_index). This causes the old callback to be called first and its result passed to your new callback at the given position in the new callback arguments array. In this case your new access callback must accept the additional parameter and its return value will be the result of the access check.
Starting with the 2012 versions, the handling of MENU_DEFAULT_LOCAL_TASK items has changed (see #1186208-35: Unable to chain in front of some system paths for details). These items should not specify their own access keys but rather inherit them from their parent item.
To be on the safe side, you should call
chain_menu_access_chain() on the MENU_DEFAULT_LOCAL_TASK item as well as on its parent item. The call on the child item will not actually chain your callback but instead just remove the access properties (and return FALSE), to restore sane behavior.
Starting with 7.x-2.x (and 8.x-1.x), calling
chain_menu_access_chain() on the MENU_DEFAULT_LOCAL_TASK item is not necessary anymore, but the arguments have changed, which is why we started a new branch. See #1757616: Handle MENU_DEFAULT_LOCAL_TASKs automatically.
Developers: Please switch to 2.x.
Note: Literally switching a client module to 2.x, i.e. removing the support for 1.x, is problematic, because automated update tools won't realize that CMA needs to be upgraded at the same time. #1775204: Avoid crashing the site if FA 1.1 encounters CMA 1.x shows how to do this in a friendly way by supporting both 1.x and 2.x.
2.x is now the recommended branch, so please do not delay updating your modules.
D6 is at the same level as 7.x-1.x. We believe it should be stable, but it has received very little real-world testing. See #1243362: Chain Menu Access API for D6 — BETA4.
D7 is solid and productive. However, we need additional automated tests. See #1243374: Wanted: Tests for Chain Menu Access API for D7.
The 2.x branch provides increased robustness against misbehaving third-party modules.
D8 follows core development, which is a moving target. See
- the issues queue for the current state,
- #1243342: Chain Menu Access API for D8 — BETA5 for the BETA release history, and
- the change log for the latest changes in the development snapshot.