Module: Cms::MenuHelper

Defined in:
app/helpers/cms/menu_helper.rb

Instance Method Summary collapse

Instance Method Details

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" }
    ] }
]


106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'app/helpers/cms/menu_helper.rb', line 106

def menu_items(options = {})
  # Intialize parameters
  selected_page = options[:page] || @page
  return nil unless selected_page
  
  # Path to the section
  if options.has_key?(:path)
    section_for_path = Section.find_by_path(options[:path])
    raise "Could not find section for path '#{options[:path]}'" unless section_for_path
    ancestors = section_for_path.ancestors(:include_self => true)
  else
    ancestors = selected_page.ancestors
  end
  
  if options.has_key?(:from_top)
    ancestors = ancestors[options[:from_top].to_i..-1] || []
  end
  
  depth = options.has_key?(:depth) ? options[:depth].to_i : 1.0/0
  show_all_siblings = options[:show_all_siblings] || false
  
  # We are defining a recursive lambda that takes the top-level sections
  fn = lambda do |section_nodes, current_depth|
    section_nodes.map do |section_node|
      node = section_node.node
      
      item = {}
      item[:id] = "#{section_node.node_type.underscore}_#{section_node.node_id}"
      
      # If we are showing a section item, we want to use the path for the first page
      page = section_node.section? ? node.first_page_or_link : node
      if section_node.section? && page
        item[:selected] = true if page.hidden? && selected_page == page
      else
        item[:selected] = true if selected_page == page
      end
     
      item[:url] = page && page.path || '#'
      item[:name] = node.name
      item[:target] = "_blank" if page.respond_to?(:new_window?) && page.new_window?
      
      # Now if this is a section, we do the child nodes, 
      # but only if the show_all_siblings parameter is true, 
      # or if this section is one of the current page's ancestors
      # and also if the current depth is less than the target depth
      if section_node.section? &&
         current_depth < depth &&
         (show_all_siblings || ancestors.include?(node)) &&
         !node.visible_child_nodes.empty?
        item[:children] = fn.call(node.visible_child_nodes, current_depth + 1)
      end
      
      item
    end
  end
  
  if ancestors.empty?
    []
  else
    fn.call(ancestors.first.visible_child_nodes(:limit => options[:limit]), 1)
  end
end

#render_menu(options = {}) ⇒ Object

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.


29
30
31
32
33
34
35
36
37
# File 'app/helpers/cms/menu_helper.rb', line 29

def render_menu(options = {})
  options[:items] ||= menu_items(options)
  return "" unless options[:items]

  options[:partial] ||= "cms/menus/menu"
  options[:id] ||= "menu"
  options[:class] ||= "menu"
  render :partial => options[:partial], :locals => { :items => options[:items], :css_id => options[:id], :css_class => options[:class] }
end