Created
October 31, 2011 17:14
-
-
Save evildmp/1328038 to your computer and use it in GitHub Desktop.
Attempt to understand the menu system
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
========================= | |
How the menu system works | |
========================= | |
Basic concepts | |
============== | |
Registration | |
------------ | |
Parts of the menu system, which can be from the menus application, or from another application (cms, or some other application entirely) are registered with the menu system. | |
Then, when a menu is built, the system allows the registered menu generators and modifiers to work on it. | |
The Menu class, base.Menu | |
------------------------- | |
A class based on Menu - such as cms.menu.CMSMenu(Menu) - adds nodes. | |
The Modifier class, base.Modifier | |
--------------------------------- | |
A class based on Modifier - such as cms.menu.NavExtender or cms.menu.SoftRootCutter - examines the nodes that have been assembled, and modifies them according to its requirements (adding, removing or otherwise marking them as it sees fit). | |
Each Modifer is called *twice*: | |
* first, by menu_pool.MenuPool.get_nodes(), with the argument post_cut = False | |
* later, by the templatetag, with the argument post_cut = True | |
This corresponds to the state of the nodes list before and after menus.templatetags.cut_levels(), which removes nodes from the menu according to the arguments provided by the templatetag. | |
NavigationNode, base.NavigationNode | |
----------------------------------- | |
Each node is a NavigationNode, with attributes such as URL, title, parent and children - as one would expect in a navigation tree. | |
Tracing the logic of the menu system | |
==================================== | |
Let's look at an example using the {% show_menu %} templatetag. | |
An blank line separates methods; an indentation represents a similar Python/logical indentation. | |
Each of the methods below passes a big list of nodes to the ones it calls, and returns them to the one that it was in turn called by. | |
{% show_menu %}: | |
menu_tags.ShowMenu.get_context(): | |
menu_pool.MenuPool.get_nodes(): | |
menu_pool.MenuPool.discover_menus(): | |
[this loops over every application, checking the menu.py file; it registers: | |
* unregistered Menu classes, placing them in the self.menus dict | |
* unregistered Modifier classes, placing them in the self.modifiers list] | |
menu_pool.MenuPool._build_nodes(): | |
[this first checks the to see if it should return cached nodes] | |
[then, it loops over the Menus in self.menus - by default the only one is: | |
* cms.menu.CMSMenu]: | |
cms.menu.CMSMenu.get_nodes() [the menu's own method for getting nodes] | |
menu_pool._build_nodes_inner_for_one_menu() [I don't really understand what this does] | |
adds all nodes into a big list | |
] | |
menu_pool.MenuPool.apply_modifiers(): | |
menu_pool.MenuPool._mark_selected(): | |
[loops over each node, comparing its URL with the request.path, and marks the best match as selected] | |
[loops over the Modifiers in self.modifiers, in the order that their applications were loaded, | |
followed by the order in whch they were registered; by default, these are: | |
* cms.menu.NavExtender | |
* cms.menu.SoftRootCutter | |
* menus.modifiers.Marker | |
* menus.modifiers.AuthVisibility | |
* menus.modifiers.Level]: | |
cms.menu.NavExtender.modify() [needs a description] | |
cms.menu.SoftRootCutter.modify() [needs a description] | |
menus.modifiers.Marker.modify(): | |
loops over all nodes | |
once it has found the selected node, marks all its ancestors, siblings and children | |
menus.modifiers.AuthVisibility.modify() [removes nodes that require authorisation] | |
menus.modifiers.Level.modify(): | |
if post_cut = False, loops over all nodes; for each one that is a root node (level = 0) passes it to: | |
menus.modifiers.Level.mark_levels(): | |
[recurses over a node's descendants marking their levels until it has reached them all] | |
[we are now back in menu_tags.ShowMenu.render() again] | |
if we have been provided a root_id, get rid of any nodes other than its descendants] | |
menus.templatetags.cut_levels() [removes nodes from the menu according to the arguments provided by the templatetag] | |
menu_pool.MenuPool.apply_modifiers(post_cut = True) [remember we did these earlier with post_cut = False] | |
returns the nodes to the context |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment