Skip to content

Instantly share code, notes, and snippets.

@evildmp
Created October 31, 2011 17:14
Show Gist options
  • Select an option

  • Save evildmp/1328038 to your computer and use it in GitHub Desktop.

Select an option

Save evildmp/1328038 to your computer and use it in GitHub Desktop.

Revisions

  1. @superdmp superdmp revised this gist Nov 2, 2011. 1 changed file with 7 additions and 6 deletions.
    13 changes: 7 additions & 6 deletions gistfile1.txt
    Original file line number Diff line number Diff line change
    @@ -60,19 +60,20 @@ Each of the methods below passes a big list of nodes to the ones it calls, and r
    [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
    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 - by default, these are:
    [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
  2. @superdmp superdmp revised this gist Nov 1, 2011. 1 changed file with 7 additions and 8 deletions.
    15 changes: 7 additions & 8 deletions gistfile1.txt
    Original file line number Diff line number Diff line change
    @@ -26,7 +26,7 @@ A class based on Modifier - such as cms.menu.NavExtender or cms.menu.SoftRootCut
    Each Modifer is called *twice*:

    * first, by menu_pool.MenuPool.get_nodes(), with the argument post_cut = False
    * later, by the templatetage, with the argument post_cut = True
    * 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.

    @@ -46,15 +46,14 @@ Each of the methods below passes a big list of nodes to the ones it calls, and r

    {% show_menu %}:

    menu_tags.ShowMenu.render():
    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 any Menu classes that have not been registered and puts them all
    in the self.menus dict, does the same for Modifier classes, which go into the
    self.modifiers list]
    [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]
    @@ -82,7 +81,7 @@ Each of the methods below passes a big list of nodes to the ones it calls, and r

    cms.menu.NavExtender.modify() [needs a description]

    cms.menu.SoftRootCutter.modify() [decribed below]
    cms.menu.SoftRootCutter.modify() [needs a description]

    menus.modifiers.Marker.modify():
    loops over all nodes
    @@ -96,7 +95,7 @@ Each of the methods below passes a big list of nodes to the ones it calls, and r
    menus.modifiers.Level.mark_levels():
    [recurses over a node's descendants marking their levels until it has reached them all]

    [we are back in menu_tags.ShowMenu.render() again]
    [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]
  3. @superdmp superdmp renamed this gist Oct 31, 2011. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  4. @superdmp superdmp renamed this gist Oct 31, 2011. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  5. @superdmp superdmp created this gist Oct 31, 2011.
    106 changes: 106 additions & 0 deletions gistfile1.txt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,106 @@
    =========================
    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 templatetage, 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.render():

    menu_pool.MenuPool.get_nodes():

    menu_pool.MenuPool.discover_menus():
    [this loops over every application, checking the menu.py file;
    it registers any Menu classes that have not been registered and puts them all
    in the self.menus dict, does the same for Modifier classes, which go into 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 - 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() [decribed below]

    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 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