| 1 |
module StandardTags |
|---|
| 2 |
|
|---|
| 3 |
include Radiant::Taggable |
|---|
| 4 |
include LocalTime |
|---|
| 5 |
|
|---|
| 6 |
class TagError < StandardError; end |
|---|
| 7 |
|
|---|
| 8 |
desc %{ |
|---|
| 9 |
Causes the tags referring to a page's attributes to refer to the current page. |
|---|
| 10 |
|
|---|
| 11 |
*Usage:* |
|---|
| 12 |
<pre><code><r:page>...</r:page></code></pre> |
|---|
| 13 |
} |
|---|
| 14 |
tag 'page' do |tag| |
|---|
| 15 |
tag.locals.page = tag.globals.page |
|---|
| 16 |
tag.expand |
|---|
| 17 |
end |
|---|
| 18 |
|
|---|
| 19 |
[:breadcrumb, :slug, :title].each do |method| |
|---|
| 20 |
desc %{ |
|---|
| 21 |
Renders the @#{method}@ attribute of the current page. |
|---|
| 22 |
} |
|---|
| 23 |
tag method.to_s do |tag| |
|---|
| 24 |
tag.locals.page.send(method) |
|---|
| 25 |
end |
|---|
| 26 |
end |
|---|
| 27 |
|
|---|
| 28 |
desc %{ |
|---|
| 29 |
Renders the @url@ attribute of the current page. |
|---|
| 30 |
} |
|---|
| 31 |
tag 'url' do |tag| |
|---|
| 32 |
relative_url_for(tag.locals.page.url, tag.globals.page.request) |
|---|
| 33 |
end |
|---|
| 34 |
|
|---|
| 35 |
desc %{ |
|---|
| 36 |
Gives access to a page's children. |
|---|
| 37 |
|
|---|
| 38 |
*Usage:* |
|---|
| 39 |
<pre><code><r:children>...</r:children></code></pre> |
|---|
| 40 |
} |
|---|
| 41 |
tag 'children' do |tag| |
|---|
| 42 |
tag.locals.children = tag.locals.page.children |
|---|
| 43 |
tag.expand |
|---|
| 44 |
end |
|---|
| 45 |
|
|---|
| 46 |
desc %{ |
|---|
| 47 |
Renders the total number of children. |
|---|
| 48 |
} |
|---|
| 49 |
tag 'children:count' do |tag| |
|---|
| 50 |
tag.locals.children.count |
|---|
| 51 |
end |
|---|
| 52 |
|
|---|
| 53 |
desc %{ |
|---|
| 54 |
Returns the first child. Inside this tag all page attribute tags are mapped to |
|---|
| 55 |
the first child. Takes the same ordering options as @<r:children:each>@. |
|---|
| 56 |
|
|---|
| 57 |
*Usage:* |
|---|
| 58 |
<pre><code><r:children:first>...</r:children:first></code></pre> |
|---|
| 59 |
} |
|---|
| 60 |
tag 'children:first' do |tag| |
|---|
| 61 |
options = children_find_options(tag) |
|---|
| 62 |
children = tag.locals.children.find(:all, options) |
|---|
| 63 |
if first = children.first |
|---|
| 64 |
tag.locals.page = first |
|---|
| 65 |
tag.expand |
|---|
| 66 |
end |
|---|
| 67 |
end |
|---|
| 68 |
|
|---|
| 69 |
desc %{ |
|---|
| 70 |
Returns the last child. Inside this tag all page attribute tags are mapped to |
|---|
| 71 |
the last child. Takes the same ordering options as @<r:children:each>@. |
|---|
| 72 |
|
|---|
| 73 |
*Usage:* |
|---|
| 74 |
<pre><code><r:children:last>...</r:children:last></code></pre> |
|---|
| 75 |
} |
|---|
| 76 |
tag 'children:last' do |tag| |
|---|
| 77 |
options = children_find_options(tag) |
|---|
| 78 |
children = tag.locals.children.find(:all, options) |
|---|
| 79 |
if last = children.last |
|---|
| 80 |
tag.locals.page = last |
|---|
| 81 |
tag.expand |
|---|
| 82 |
end |
|---|
| 83 |
end |
|---|
| 84 |
|
|---|
| 85 |
desc %{ |
|---|
| 86 |
Cycles through each of the children. Inside this tag all page attribute tags |
|---|
| 87 |
are mapped to the current child page. |
|---|
| 88 |
|
|---|
| 89 |
*Usage:* |
|---|
| 90 |
<pre><code><r:children:each [offset="number"] [limit="number"] [by="attribute"] [order="asc|desc"] |
|---|
| 91 |
[status="draft|reviewed|published|hidden|all"]> |
|---|
| 92 |
... |
|---|
| 93 |
</r:children:each> |
|---|
| 94 |
</code></pre> |
|---|
| 95 |
} |
|---|
| 96 |
tag 'children:each' do |tag| |
|---|
| 97 |
options = children_find_options(tag) |
|---|
| 98 |
result = [] |
|---|
| 99 |
children = tag.locals.children |
|---|
| 100 |
tag.locals.previous_headers = {} |
|---|
| 101 |
children.find(:all, options).each do |item| |
|---|
| 102 |
tag.locals.child = item |
|---|
| 103 |
tag.locals.page = item |
|---|
| 104 |
result << tag.expand |
|---|
| 105 |
end |
|---|
| 106 |
result |
|---|
| 107 |
end |
|---|
| 108 |
|
|---|
| 109 |
desc %{ |
|---|
| 110 |
Page attribute tags inside of this tag refer to the current child. This is occasionally |
|---|
| 111 |
useful if you are inside of another tag (like <r:find>) and need to refer back to the |
|---|
| 112 |
current child. |
|---|
| 113 |
|
|---|
| 114 |
*Usage:* |
|---|
| 115 |
<pre><code><r:children:each> |
|---|
| 116 |
<r:child>...</r:child> |
|---|
| 117 |
</r:children:each> |
|---|
| 118 |
</code></pre> |
|---|
| 119 |
} |
|---|
| 120 |
tag 'children:each:child' do |tag| |
|---|
| 121 |
tag.locals.page = tag.locals.child |
|---|
| 122 |
tag.expand |
|---|
| 123 |
end |
|---|
| 124 |
|
|---|
| 125 |
desc %{ |
|---|
| 126 |
Renders the tag contents only if the contents do not match the previous header. This |
|---|
| 127 |
is extremely useful for rendering date headers for a list of child pages. |
|---|
| 128 |
|
|---|
| 129 |
If you would like to use several header blocks you may use the @name@ attribute to |
|---|
| 130 |
name the header. When a header is named it will not restart until another header of |
|---|
| 131 |
the same name is different. |
|---|
| 132 |
|
|---|
| 133 |
Using the @restart@ attribute you can cause other named headers to restart when the |
|---|
| 134 |
present header changes. Simply specify the names of the other headers in a semicolon |
|---|
| 135 |
separated list. |
|---|
| 136 |
|
|---|
| 137 |
*Usage:* |
|---|
| 138 |
<pre><code><r:children:each> |
|---|
| 139 |
<r:header [name="header_name"] [restart="name1[;name2;...]"]> |
|---|
| 140 |
... |
|---|
| 141 |
</r:header> |
|---|
| 142 |
</r:children:each> |
|---|
| 143 |
</code></pre> |
|---|
| 144 |
} |
|---|
| 145 |
tag 'children:each:header' do |tag| |
|---|
| 146 |
previous_headers = tag.locals.previous_headers |
|---|
| 147 |
name = tag.attr['name'] || :unnamed |
|---|
| 148 |
restart = (tag.attr['restart'] || '').split(';') |
|---|
| 149 |
header = tag.expand |
|---|
| 150 |
unless header == previous_headers[name] |
|---|
| 151 |
previous_headers[name] = header |
|---|
| 152 |
unless restart.empty? |
|---|
| 153 |
restart.each do |n| |
|---|
| 154 |
previous_headers[n] = nil |
|---|
| 155 |
end |
|---|
| 156 |
end |
|---|
| 157 |
header |
|---|
| 158 |
end |
|---|
| 159 |
end |
|---|
| 160 |
|
|---|
| 161 |
desc %{ |
|---|
| 162 |
Page attribute tags inside this tag refer to the parent of the current page. |
|---|
| 163 |
|
|---|
| 164 |
*Usage:* |
|---|
| 165 |
<pre><code><r:parent>...</r:parent></code></pre> |
|---|
| 166 |
} |
|---|
| 167 |
tag "parent" do |tag| |
|---|
| 168 |
parent = tag.locals.page.parent |
|---|
| 169 |
tag.locals.page = parent |
|---|
| 170 |
tag.expand if parent |
|---|
| 171 |
end |
|---|
| 172 |
|
|---|
| 173 |
desc %{ |
|---|
| 174 |
Renders the contained elements only if the current contextual page has a parent, i.e. |
|---|
| 175 |
is not the root page. |
|---|
| 176 |
|
|---|
| 177 |
*Usage:* |
|---|
| 178 |
<pre><code><r:if_parent>...</r:if_parent></code></pre> |
|---|
| 179 |
} |
|---|
| 180 |
tag "if_parent" do |tag| |
|---|
| 181 |
parent = tag.locals.page.parent |
|---|
| 182 |
tag.expand if parent |
|---|
| 183 |
end |
|---|
| 184 |
|
|---|
| 185 |
desc %{ |
|---|
| 186 |
Renders the contained elements only if the current contextual page has no parent, i.e. |
|---|
| 187 |
is the root page. |
|---|
| 188 |
|
|---|
| 189 |
*Usage:* |
|---|
| 190 |
<pre><code><r:unless_parent>...</r:unless_parent></code></pre> |
|---|
| 191 |
} |
|---|
| 192 |
tag "unless_parent" do |tag| |
|---|
| 193 |
parent = tag.locals.page.parent |
|---|
| 194 |
tag.expand unless parent |
|---|
| 195 |
end |
|---|
| 196 |
|
|---|
| 197 |
desc %{ |
|---|
| 198 |
Renders the contained elements only if the current contextual page has one or |
|---|
| 199 |
more child pages. The @status@ attribute limits the status of found child pages |
|---|
| 200 |
to the given status, the default is @"published"@. @status="all"@ includes all |
|---|
| 201 |
non-virtual pages regardless of status. |
|---|
| 202 |
|
|---|
| 203 |
*Usage:* |
|---|
| 204 |
<pre><code><r:if_children [status="published"]>...</r:if_children></code></pre> |
|---|
| 205 |
} |
|---|
| 206 |
tag "if_children" do |tag| |
|---|
| 207 |
children = tag.locals.page.children.count(:conditions => children_find_options(tag)[:conditions]) |
|---|
| 208 |
tag.expand if children > 0 |
|---|
| 209 |
end |
|---|
| 210 |
|
|---|
| 211 |
desc %{ |
|---|
| 212 |
Renders the contained elements only if the current contextual page has no children. |
|---|
| 213 |
The @status@ attribute limits the status of found child pages to the given status, |
|---|
| 214 |
the default is @"published"@. @status="all"@ includes all non-virtual pages |
|---|
| 215 |
regardless of status. |
|---|
| 216 |
|
|---|
| 217 |
*Usage:* |
|---|
| 218 |
<pre><code><r:unless_children [status="published"]>...</r:unless_children></code></pre> |
|---|
| 219 |
} |
|---|
| 220 |
tag "unless_children" do |tag| |
|---|
| 221 |
children = tag.locals.page.children.count(:conditions => children_find_options(tag)[:conditions]) |
|---|
| 222 |
tag.expand unless children > 0 |
|---|
| 223 |
end |
|---|
| 224 |
|
|---|
| 225 |
desc %{ |
|---|
| 226 |
Renders one of the passed values based on a global cycle counter. Use the @reset@ |
|---|
| 227 |
attribute to reset the cycle to the beginning. Use the @name@ attribute to track |
|---|
| 228 |
multiple cycles; the default is @cycle@. |
|---|
| 229 |
|
|---|
| 230 |
*Usage:* |
|---|
| 231 |
<pre><code><r:cycle values="first, second, third" [reset="true|false"] [name="cycle"] /></code></pre> |
|---|
| 232 |
} |
|---|
| 233 |
tag 'cycle' do |tag| |
|---|
| 234 |
raise TagError, "`cycle' tag must contain a `values' attribute." unless tag.attr['values'] |
|---|
| 235 |
cycle = (tag.globals.cycle ||= {}) |
|---|
| 236 |
values = tag.attr['values'].split(",").collect(&:strip) |
|---|
| 237 |
cycle_name = tag.attr['name'] || 'cycle' |
|---|
| 238 |
current_index = (cycle[cycle_name] ||= 0) |
|---|
| 239 |
current_index = 0 if tag.attr['reset'] == 'true' |
|---|
| 240 |
cycle[cycle_name] = (current_index + 1) % values.size |
|---|
| 241 |
values[current_index] |
|---|
| 242 |
end |
|---|
| 243 |
|
|---|
| 244 |
desc %{ |
|---|
| 245 |
Renders the main content of a page. Use the @part@ attribute to select a specific |
|---|
| 246 |
page part. By default the @part@ attribute is set to body. Use the @inherit@ |
|---|
| 247 |
attribute to specify that if a page does not have a content part by that name that |
|---|
| 248 |
the tag should render the parent's content part. By default @inherit@ is set to |
|---|
| 249 |
@false@. Use the @contextual@ attribute to force a part inherited from a parent |
|---|
| 250 |
part to be evaluated in the context of the child page. By default 'contextual' |
|---|
| 251 |
is set to true. |
|---|
| 252 |
|
|---|
| 253 |
*Usage:* |
|---|
| 254 |
<pre><code><r:content [part="part_name"] [inherit="true|false"] [contextual="true|false"] /></code></pre> |
|---|
| 255 |
} |
|---|
| 256 |
tag 'content' do |tag| |
|---|
| 257 |
page = tag.locals.page |
|---|
| 258 |
part_name = tag_part_name(tag) |
|---|
| 259 |
boolean_attr = proc do |attribute_name, default| |
|---|
| 260 |
attribute = (tag.attr[attribute_name] || default).to_s |
|---|
| 261 |
raise TagError.new(%{`#{attribute_name}' attribute of `content' tag must be set to either "true" or "false"}) unless attribute =~ |
|---|
| 262 |
(attribute.downcase == 'true') ? true : false |
|---|
| 263 |
end |
|---|
| 264 |
inherit = boolean_attr['inherit', false] |
|---|
| 265 |
part_page = page |
|---|
| 266 |
if inherit |
|---|
| 267 |
while (part_page.part(part_name).nil? and (not part_page.parent.nil?)) do |
|---|
| 268 |
part_page = part_page.parent |
|---|
| 269 |
end |
|---|
| 270 |
end |
|---|
| 271 |
contextual = boolean_attr['contextual', true] |
|---|
| 272 |
part = part_page.part(part_name) |
|---|
| 273 |
tag.locals.page = part_page unless contextual |
|---|
| 274 |
tag.globals.page.render_snippet(part) unless part.nil? |
|---|
| 275 |
end |
|---|
| 276 |
|
|---|
| 277 |
desc %{ |
|---|
| 278 |
Renders the containing elements only if the part exists on a page. By default the |
|---|
| 279 |
@part@ attribute is set to @body@. |
|---|
| 280 |
|
|---|
| 281 |
*Usage:* |
|---|
| 282 |
<pre><code><r:if_content [part="part_name"]>...</r:if_content></code></pre> |
|---|
| 283 |
} |
|---|
| 284 |
tag 'if_content' do |tag| |
|---|
| 285 |
page = tag.locals.page |
|---|
| 286 |
part_name = tag_part_name(tag) |
|---|
| 287 |
unless page.part(part_name).nil? |
|---|
| 288 |
tag.expand |
|---|
| 289 |
end |
|---|
| 290 |
end |
|---|
| 291 |
|
|---|
| 292 |
desc %{ |
|---|
| 293 |
The opposite of the @if_content@ tag. |
|---|
| 294 |
|
|---|
| 295 |
*Usage:* |
|---|
| 296 |
<pre><code><r:unless_content [part="part_name"]>...</r:unless_content></code></pre> |
|---|
| 297 |
} |
|---|
| 298 |
tag 'unless_content' do |tag| |
|---|
| 299 |
page = tag.locals.page |
|---|
| 300 |
part_name = tag_part_name(tag) |
|---|
| 301 |
if page.part(part_name).nil? |
|---|
| 302 |
tag.expand |
|---|
| 303 |
end |
|---|
| 304 |
end |
|---|
| 305 |
|
|---|
| 306 |
desc %{ |
|---|
| 307 |
Renders the containing elements only if the page's url matches the regular expression |
|---|
| 308 |
given in the @matches@ attribute. If the @ignore_case@ attribute is set to false, the |
|---|
| 309 |
match is case sensitive. By default, @ignore_case@ is set to true. |
|---|
| 310 |
|
|---|
| 311 |
*Usage:* |
|---|
| 312 |
<pre><code><r:if_url matches="regexp" [ignore_case="true|false"]>...</if_url></code></pre> |
|---|
| 313 |
} |
|---|
| 314 |
tag 'if_url' do |tag| |
|---|
| 315 |
raise TagError.new("`if_url' tag must contain a `matches' attribute.") unless tag.attr.has_key?('matches') |
|---|
| 316 |
regexp = build_regexp_for(tag, 'matches') |
|---|
| 317 |
unless tag.locals.page.url.match(regexp).nil? |
|---|
| 318 |
tag.expand |
|---|
| 319 |
end |
|---|
| 320 |
end |
|---|
| 321 |
|
|---|
| 322 |
desc %{ |
|---|
| 323 |
The opposite of the @if_url@ tag. |
|---|
| 324 |
|
|---|
| 325 |
*Usage:* |
|---|
| 326 |
<pre><code><r:unless_url matches="regexp" [ignore_case="true|false"]>...</unless_url></code></pre> |
|---|
| 327 |
} |
|---|
| 328 |
tag 'unless_url' do |tag| |
|---|
| 329 |
raise TagError.new("`unless_url' tag must contain a `matches' attribute.") unless tag.attr.has_key?('matches') |
|---|
| 330 |
regexp = build_regexp_for(tag, 'matches') |
|---|
| 331 |
if tag.locals.page.url.match(regexp).nil? |
|---|
| 332 |
tag.expand |
|---|
| 333 |
end |
|---|
| 334 |
end |
|---|
| 335 |
|
|---|
| 336 |
desc %{ |
|---|
| 337 |
Renders the contained elements if the current contextual page is either the actual page or one of its parents. |
|---|
| 338 |
|
|---|
| 339 |
This is typically used inside another tag (like <r:children:each>) to add conditional mark-up if the child element is or descends from the current page. |
|---|
| 340 |
|
|---|
| 341 |
*Usage:* |
|---|
| 342 |
<pre><code><r:if_ancestor_or_self>...</if_ancestor_or_self></code></pre> |
|---|
| 343 |
} |
|---|
| 344 |
tag "if_ancestor_or_self" do |tag| |
|---|
| 345 |
tag.expand if (tag.globals.page.ancestors + [tag.globals.page]).include?(tag.locals.page) |
|---|
| 346 |
end |
|---|
| 347 |
|
|---|
| 348 |
desc %{ |
|---|
| 349 |
Renders the contained elements if the current contextual page is also the actual page. |
|---|
| 350 |
|
|---|
| 351 |
This is typically used inside another tag (like <r:children:each>) to add conditional mark-up if the child element is the current page. |
|---|
| 352 |
|
|---|
| 353 |
*Usage:* |
|---|
| 354 |
<pre><code><r:if_self>...</if_self></code></pre> |
|---|
| 355 |
} |
|---|
| 356 |
tag "if_self" do |tag| |
|---|
| 357 |
tag.expand if tag.locals.page == tag.globals.page |
|---|
| 358 |
end |
|---|
| 359 |
|
|---|
| 360 |
desc %{ |
|---|
| 361 |
Renders the name of the author of the current page. |
|---|
| 362 |
} |
|---|
| 363 |
tag 'author' do |tag| |
|---|
| 364 |
page = tag.locals.page |
|---|
| 365 |
if author = page.created_by |
|---|
| 366 |
author.name |
|---|
| 367 |
end |
|---|
| 368 |
end |
|---|
| 369 |
|
|---|
| 370 |
desc %{ |
|---|
| 371 |
Renders the date based on the current page (by default when it was published or created). |
|---|
| 372 |
The format attribute uses the same formating codes used by the Ruby @strftime@ function. By |
|---|
| 373 |
default it's set to @%A, %B %d, %Y@. The @for@ attribute selects which date to render. Valid |
|---|
| 374 |
options are @published_at@, @created_at@, @updated_at@, and @now@. @now@ will render the |
|---|
| 375 |
current date/time, regardless of the page. |
|---|
| 376 |
|
|---|
| 377 |
*Usage:* |
|---|
| 378 |
<pre><code><r:date [format="%A, %B %d, %Y"] [for="published_at"]/></code></pre> |
|---|
| 379 |
} |
|---|
| 380 |
tag 'date' do |tag| |
|---|
| 381 |
page = tag.locals.page |
|---|
| 382 |
format = (tag.attr['format'] || '%A, %B %d, %Y') |
|---|
| 383 |
time_attr = tag.attr['for'] |
|---|
| 384 |
date = if time_attr |
|---|
| 385 |
case |
|---|
| 386 |
when time_attr == 'now' |
|---|
| 387 |
Time.now |
|---|
| 388 |
when ['published_at', 'created_at', 'updated_at'].include?(time_attr) |
|---|
| 389 |
page[time_attr] |
|---|
| 390 |
else |
|---|
| 391 |
raise TagError, "Invalid value for 'for' attribute." |
|---|
| 392 |
end |
|---|
| 393 |
else |
|---|
| 394 |
page.published_at || page.created_at |
|---|
| 395 |
end |
|---|
| 396 |
adjust_time(date).strftime(format) |
|---|
| 397 |
end |
|---|
| 398 |
|
|---|
| 399 |
desc %{ |
|---|
| 400 |
Renders a link to the page. When used as a single tag it uses the page's title |
|---|
| 401 |
for the link name. When used as a double tag the part in between both tags will |
|---|
| 402 |
be used as the link text. The link tag passes all attributes over to the HTML |
|---|
| 403 |
@a@ tag. This is very useful for passing attributes like the @class@ attribute |
|---|
| 404 |
or @id@ attribute. If the @anchor@ attribute is passed to the tag it will |
|---|
| 405 |
append a pound sign (<code>#</code>) followed by the value of the attribute to |
|---|
| 406 |
the @href@ attribute of the HTML @a@ tag--effectively making an HTML anchor. |
|---|
| 407 |
|
|---|
| 408 |
*Usage:* |
|---|
| 409 |
<pre><code><r:link [anchor="name"] [other attributes...] /></code></pre> |
|---|
| 410 |
or |
|---|
| 411 |
<pre><code><r:link [anchor="name"] [other attributes...]>link text here</r:link></code></pre> |
|---|
| 412 |
} |
|---|
| 413 |
tag 'link' do |tag| |
|---|
| 414 |
options = tag.attr.dup |
|---|
| 415 |
anchor = options['anchor'] ? "##{options.delete('anchor')}" : '' |
|---|
| 416 |
attributes = options.inject('') { |s, (k, v)| s << %{#{k.downcase}="#{v}" } }.strip |
|---|
| 417 |
attributes = " #{attributes}" unless attributes.empty? |
|---|
| 418 |
text = tag.double? ? tag.expand : tag.render('title') |
|---|
| 419 |
%{<a href="#{tag.render('url')}#{anchor}"#{attributes}>#{text}</a>} |
|---|
| 420 |
end |
|---|
| 421 |
|
|---|
| 422 |
desc %{ |
|---|
| 423 |
Renders a trail of breadcrumbs to the current page. The separator attribute |
|---|
| 424 |
specifies the HTML fragment that is inserted between each of the breadcrumbs. By |
|---|
| 425 |
default it is set to @>@. The boolean nolinks attribute can be specified to render |
|---|
| 426 |
breadcrumbs in plain text, without any links (useful when generating title tag). |
|---|
| 427 |
|
|---|
| 428 |
*Usage:* |
|---|
| 429 |
<pre><code><r:breadcrumbs [separator="separator_string"] [nolinks="true"] /></code></pre> |
|---|
| 430 |
} |
|---|
| 431 |
tag 'breadcrumbs' do |tag| |
|---|
| 432 |
page = tag.locals.page |
|---|
| 433 |
breadcrumbs = [page.breadcrumb] |
|---|
| 434 |
nolinks = (tag.attr['nolinks'] == 'true') |
|---|
| 435 |
page.ancestors.each do |ancestor| |
|---|
| 436 |
tag.locals.page = ancestor |
|---|
| 437 |
if nolinks |
|---|
| 438 |
breadcrumbs.unshift tag.render('breadcrumb') |
|---|
| 439 |
else |
|---|
| 440 |
breadcrumbs.unshift %{<a href="#{tag.render('url')}">#{tag.render('breadcrumb')}</a>} |
|---|
| 441 |
end |
|---|
| 442 |
end |
|---|
| 443 |
separator = tag.attr['separator'] || ' > ' |
|---|
| 444 |
breadcrumbs.join(separator) |
|---|
| 445 |
end |
|---|
| 446 |
|
|---|
| 447 |
desc %{ |
|---|
| 448 |
Renders the snippet specified in the @name@ attribute within the context of a page. |
|---|
| 449 |
|
|---|
| 450 |
*Usage:* |
|---|
| 451 |
<pre><code><r:snippet name="snippet_name" /></code></pre> |
|---|
| 452 |
} |
|---|
| 453 |
tag 'snippet' do |tag| |
|---|
| 454 |
if name = tag.attr['name'] |
|---|
| 455 |
if snippet = Snippet.find_by_name(name.strip) |
|---|
| 456 |
tag.globals.page.render_snippet(snippet) |
|---|
| 457 |
else |
|---|
| 458 |
raise TagError.new('snippet not found') |
|---|
| 459 |
end |
|---|
| 460 |
else |
|---|
| 461 |
raise TagError.new("`snippet' tag must contain `name' attribute") |
|---|
| 462 |
end |
|---|
| 463 |
end |
|---|
| 464 |
|
|---|
| 465 |
desc %{ |
|---|
| 466 |
Inside this tag all page related tags refer to the page found at the @url@ attribute. |
|---|
| 467 |
@url@s may be relative or absolute paths. |
|---|
| 468 |
|
|---|
| 469 |
*Usage:* |
|---|
| 470 |
<pre><code><r:find url="value_to_find">...</r:find></code></pre> |
|---|
| 471 |
} |
|---|
| 472 |
tag 'find' do |tag| |
|---|
| 473 |
url = tag.attr['url'] |
|---|
| 474 |
raise TagError.new("`find' tag must contain `url' attribute") unless url |
|---|
| 475 |
|
|---|
| 476 |
found = Page.find_by_url(absolute_path_for(tag.locals.page.url, url)) |
|---|
| 477 |
if page_found?(found) |
|---|
| 478 |
tag.locals.page = found |
|---|
| 479 |
tag.expand |
|---|
| 480 |
end |
|---|
| 481 |
end |
|---|
| 482 |
|
|---|
| 483 |
desc %{ |
|---|
| 484 |
Randomly renders one of the options specified by the @option@ tags. |
|---|
| 485 |
|
|---|
| 486 |
*Usage:* |
|---|
| 487 |
<pre><code><r:random> |
|---|
| 488 |
<r:option>...</r:option> |
|---|
| 489 |
<r:option>...</r:option> |
|---|
| 490 |
... |
|---|
| 491 |
<r:random> |
|---|
| 492 |
</code></pre> |
|---|
| 493 |
} |
|---|
| 494 |
tag 'random' do |tag| |
|---|
| 495 |
tag.locals.random = [] |
|---|
| 496 |
tag.expand |
|---|
| 497 |
options = tag.locals.random |
|---|
| 498 |
option = options[rand(options.size)] |
|---|
| 499 |
option.call if option |
|---|
| 500 |
end |
|---|
| 501 |
tag 'random:option' do |tag| |
|---|
| 502 |
items = tag.locals.random |
|---|
| 503 |
items << tag.block |
|---|
| 504 |
end |
|---|
| 505 |
|
|---|
| 506 |
desc %{ |
|---|
| 507 |
Nothing inside a set of comment tags is rendered. |
|---|
| 508 |
|
|---|
| 509 |
*Usage:* |
|---|
| 510 |
<pre><code><r:comment>...</r:comment></code></pre> |
|---|
| 511 |
} |
|---|
| 512 |
tag 'comment' do |tag| |
|---|
| 513 |
end |
|---|
| 514 |
|
|---|
| 515 |
desc %{ |
|---|
| 516 |
Escapes angle brackets, etc. for rendering in an HTML document. |
|---|
| 517 |
|
|---|
| 518 |
*Usage:* |
|---|
| 519 |
<pre><code><r:escape_html>...</r:escape_html></code></pre> |
|---|
| 520 |
} |
|---|
| 521 |
tag "escape_html" do |tag| |
|---|
| 522 |
CGI.escapeHTML(tag.expand) |
|---|
| 523 |
end |
|---|
| 524 |
|
|---|
| 525 |
desc %{ |
|---|
| 526 |
Outputs the published date using the format mandated by RFC 1123. (Ideal for RSS feeds.) |
|---|
| 527 |
|
|---|
| 528 |
*Usage:* |
|---|
| 529 |
<pre><code><r:rfc1123_date /></code></pre> |
|---|
| 530 |
} |
|---|
| 531 |
tag "rfc1123_date" do |tag| |
|---|
| 532 |
page = tag.locals.page |
|---|
| 533 |
if date = page.published_at || page.created_at |
|---|
| 534 |
CGI.rfc1123_date(date.to_time) |
|---|
| 535 |
end |
|---|
| 536 |
end |
|---|
| 537 |
|
|---|
| 538 |
desc %{ |
|---|
| 539 |
Renders a list of links specified in the @urls@ attribute according to three |
|---|
| 540 |
states: |
|---|
| 541 |
|
|---|
| 542 |
* @normal@ specifies the normal state for the link |
|---|
| 543 |
* @here@ specifies the state of the link when the url matches the current |
|---|
| 544 |
page's URL |
|---|
| 545 |
* @selected@ specifies the state of the link when the current page matches |
|---|
| 546 |
is a child of the specified url |
|---|
| 547 |
|
|---|
| 548 |
The @between@ tag specifies what should be inserted in between each of the links. |
|---|
| 549 |
|
|---|
| 550 |
*Usage:* |
|---|
| 551 |
<pre><code><r:navigation urls="[Title: url | Title: url | ...]"> |
|---|
| 552 |
<r:normal><a href="<r:url />"><r:title /></a></r:normal> |
|---|
| 553 |
<r:here><strong><r:title /></strong></r:here> |
|---|
| 554 |
<r:selected><strong><a href="<r:url />"><r:title /></a></strong></r:selected> |
|---|
| 555 |
<r:between> | </r:between> |
|---|
| 556 |
</r:navigation> |
|---|
| 557 |
</code></pre> |
|---|
| 558 |
} |
|---|
| 559 |
tag 'navigation' do |tag| |
|---|
| 560 |
hash = tag.locals.navigation = {} |
|---|
| 561 |
tag.expand |
|---|
| 562 |
raise TagError.new("`navigation' tag must include a `normal' tag") unless hash.has_key? :normal |
|---|
| 563 |
result = [] |
|---|
| 564 |
pairs = tag.attr['urls'].to_s.split('|').map do |
|---|