moklett / radiant-nested-layouts-extension

An extension for Radiant CMS which provides tags for creating nested layouts.

radiant-nested-layouts-extension / lib / nested_layout_tags.rb
100644 98 lines (80 sloc) 3.134 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
module NestedLayoutTags
  include Radiant::Taggable
  
  class TagError < StandardError; end
 
  desc %{
Renders the contents of the tag inside of a "parent" layout, which is selected via the +name+
attribute. The contents of this tag are placed at a corresponding <r:content_for_layout/> tag
within the parent layout. This tag is intended to be used within your layouts, and should
only appear once per layout.
*Usage:*
 
<r:inside_layout name="master">
<div id="main-column">
<r:content_for_layout/>
</div>
</r:inside_layout>
}
  tag 'inside_layout' do |tag|
    if name = tag.attr['name']
      # Prepare the stacks
      tag.globals.nested_layouts_content_stack ||= []
      tag.globals.nested_layouts_layout_stack ||= []
 
      # Remember the original layout to support the +layout+ tag
      tag.globals.page_original_layout ||= tag.globals.page.layout # Remember the original layout
      
      # Find the layout
      name.strip!
      if layout = Layout.find_by_name(name)
        # Track this layout on the stack
        tag.globals.nested_layouts_layout_stack << name
        
        # Save contents of inside_layout for later insertion
        tag.globals.nested_layouts_content_stack << tag.expand
        
        # Set the page layout that Radiant should use for rendering, which is different than the actual
        # page's layout when layouts are nested. The final/highest +inside_layout+ tag will set or
        # overwrite this value for the last time.
        tag.globals.page.layout = layout
        tag.globals.page.render
      else
        raise TagError.new(%{Error (nested_layouts): Parent layout "#{name.strip}" not found for "inside_layout" tag})
      end
    else
      raise TagError.new(%{Error (nested_layouts): "inside_layout" tag must contain a "name" attribute})
    end
  end
 
  desc %{
Allows nested layouts to target this layout. The contents of <r:inside_layout> tag blocks in another
layout will have their contents inserted at the location given by this tag (if they target this
layout). This tag also behaves like a standard <r:content/> tag if this layout is specified directly
by a page.
This tag is intended to be used inside layouts.
*Usage:*
 
<html>
<body>
<r:content_for_layout/>
</body>
</html>
}
  tag 'content_for_layout' do |tag|
    tag.globals.nested_layouts_content_stack ||= []
    
    # return the saved content if any, or mimic a default +<r:content/>+ tag (render the body part)
    tag.globals.nested_layouts_content_stack.pop || tag.globals.page.render_snippet(tag.locals.page.part('body'))
  end
  
  desc %{
Return the layout name of the current page.
*Usage:*
<html>
<body id="<r:layout/>"
My body tag has an id corresponding to the layout I use. Sweet!
</body>
</html>
}
  tag 'layout' do |tag|
    if layout = tag.globals.page_original_layout
      layout.name
    else
      if layout = tag.globals.page.layout
        layout.name
      else
        ''
      end
    end
  end
end