Module Cms::MenuHelper
In: app/helpers/cms/menu_helper.rb

Methods

Public Instance methods

This will render generate an array-of-hashes tree structure based on the page, which can be passed to render_menu in order to generate a menu.

With no options passed, it will generate a structure that includes all the child sections of the root and then it will include the path of decendent sections all the way to the current page.

Hidden pages will not be included, but if the first page in a Section is hidden, it will be used as the URL for that Section. This is commonly done to have a page for a Section and avoid having duplicates in the navigation.

You can change the behavior with the following options, all of these are optional:

Options

  • :page - What page should be used as the current page. If this value is omitted, the value in @page will be used.
  • :path - This will be used to look up a section and that section will used to generate the menu structure. The current page will still be the value of the page option or @page. Note that this is the path to a section, not a path to a page.
  • :from_top - How many below levels from the root the tree should start at. All sections at this level will be shown. The default is 0, which means show all nodes that are direct children of the root
  • :depth - How many levels deep should the tree go, relative to from_top. If no value is supplied, the tree will go all the way down to the current page. If a value is supplied, the tree will be that many levels underneath from_top deep.
  • :limit - Limits the number of top-level elements that will be included in the list
  • :show_all_siblings - Passing true for this option will make all sibilings appear in the tree. The default is false, in which case only the siblings of nodes within the open path will appear.

Examples

Assume you have the structure the NFL, which is NFL > Conference > Division > Team, with teams being a Page, everything else a Section. Also, assume we are on the Baltimore Ravens page. If you‘re not a footbal fan, see sports.yahoo.com/nfl/teams

  menu_items
  # => [
    { :id => "section_2", :url => "/buf", :name => "AFC", :children => [
      { :id => "section_3", :url => "/buf", :name => "East" },
      { :id => "section_4", :url => "/bal", :name => "North", :children => [
        { :id => "page_5", :selected => true, :url => "/bal", :name => "Baltimore Ravens" },
        { :id => "page_6", :url => "/cin", :name => "Cincinnati Bengals" },
        { :id => "page_7", :url => "/cle", :name => "Cleveland Browns" },
        { :id => "page_8", :url => "/pit", :name => "Pittsburgh Steelers" }
      ] },
      { :id => "section_9", :url => "/hou", :name => "South" },
      { :id => "section_10}", :url => "/den", :name => "West" }
      ] },
    { :id => "section_11", :url => "/dal", :name => "NFC" }
  ]

  menu_items(:depth => 2, :show_all_siblings => true)
  # => [
    { :id => "section_2", :url => "/buf", :name => "AFC", :children => [
        { :id => "section_3", :url => "/buf", :name => "East" },
        { :id => "section_4", :url => "/bal", :name => "North" },
        { :id => "section_5", :url => "/hou", :name => "South" },
        { :id => "section_6", :url => "/den", :name => "West" }
      ] },
    { :id => "section_7", :url => "/dal", :name => "NFC", :children => [
        { :id => "section_8", :url => "/dal", :name => "East" },
        { :id => "section_9", :url => "/chi", :name => "North" },
        { :id => "section_10", :url => "/atl", :name => "South" },
        { :id => "section_11", :url => "/ari", :name => "West" }
      ] }
  ]

[Source]

     # File app/helpers/cms/menu_helper.rb, line 106
106:     def menu_items(options = {})
107:       # Intialize parameters
108:       selected_page = options[:page] || @page
109:       return nil unless selected_page
110:       
111:       # Path to the section
112:       if options.has_key?(:path)
113:         section_for_path = Section.find_by_path(options[:path])
114:         raise "Could not find section for path '#{options[:path]}'" unless section_for_path
115:         ancestors = section_for_path.ancestors(:include_self => true)
116:       else
117:         ancestors = selected_page.ancestors
118:       end
119:       
120:       if options.has_key?(:from_top)
121:         ancestors = ancestors[options[:from_top].to_i..-1] || []
122:       end
123:       
124:       depth = options.has_key?(:depth) ? options[:depth].to_i : 1.0/0
125:       show_all_siblings = options[:show_all_siblings] || false
126:       
127:       # We are defining a recursive lambda that takes the top-level sections
128:       fn = lambda do |section_nodes, current_depth|
129:         section_nodes.map do |section_node|
130:           node = section_node.node
131:           
132:           item = {}
133:           item[:id] = "#{section_node.node_type.underscore}_#{section_node.node_id}"
134:           
135:           # If we are showing a section item, we want to use the path for the first page
136:           page = section_node.section? ? node.first_page_or_link : node
137:           if section_node.section? && page
138:             item[:selected] = true if page.hidden? && selected_page == page
139:           else
140:             item[:selected] = true if selected_page == page
141:           end
142:          
143:           item[:url] = page && page.path || '#'
144:           item[:name] = node.name
145:           item[:target] = "_blank" if page.respond_to?(:new_window?) && page.new_window?
146:           
147:           # Now if this is a section, we do the child nodes, 
148:           # but only if the show_all_siblings parameter is true, 
149:           # or if this section is one of the current page's ancestors
150:           # and also if the current depth is less than the target depth
151:           if section_node.section? &&
152:              current_depth < depth &&
153:              (show_all_siblings || ancestors.include?(node)) &&
154:              !node.visible_child_nodes.empty?
155:             item[:children] = fn.call(node.visible_child_nodes, current_depth + 1)
156:           end
157:           
158:           item
159:         end
160:       end
161:       
162:       if ancestors.empty?
163:         []
164:       else
165:         fn.call(ancestors.first.visible_child_nodes(:limit => options[:limit]), 1)
166:       end
167:     end

Renders a menu. There are two options, neither are required:

Options

  • :items - The items which should appear in the menu. This defaults to calling menu_items which generates items automatically based on the current page. But you can use this option to pass in a custom menu structure.
  • :partial - The partial used to render the menu. By default this is "partials/menu", which can be customised through the CMS. The partial gets a local variable items.

Structure of items

The items should be an array of hashes, in a tree. Each hash can have the following keys (name and url are required, others are optional):

  • :name - The name which appears in the menu
  • :url - The URL to link to
  • :id - The id for the menu item
  • :selected - Boolean value to indicate whether the menu item is the current page
  • :target - The target attribute for the link
  • :children - An array of hashes containing the child menu items. This is where the tree structure comes in.

Edge Cases:

  If both @page and :items are nil/empty, this will return an empty string. This might happen if used on an CMS
  rendered page, where @page isn't set.

[Source]

    # File app/helpers/cms/menu_helper.rb, line 29
29:     def render_menu(options = {})
30:       options[:items] ||= menu_items(options)
31:       return "" unless options[:items]
32: 
33:       options[:partial] ||= "cms/menus/menu"
34:       options[:id] ||= "menu"
35:       options[:class] ||= "menu"
36:       render :partial => options[:partial], :locals => { :items => options[:items], :css_id => options[:id], :css_class => options[:class] }
37:     end

[Validate]