Skip to content

Instantly share code, notes, and snippets.

@evildmp
Created October 31, 2011 17:14
Show Gist options
  • Save evildmp/1328038 to your computer and use it in GitHub Desktop.
Save evildmp/1328038 to your computer and use it in GitHub Desktop.
Attempt to understand the menu system
=========================
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