Templates in Fragmenta

Below is an up to date list of the methods available in templates. This is generated from the Ruby source of Fragmenta, by Fragmenta.

FieldHelper

Copyright© 2007 Kenny Grant – released under MIT licence (see Licence.txt)

Any item with fields should inherit from this module

Do we have a field with this name?

field?(in_name)

def field?(in_name)

 @fields.include?(in_name.downcase)

end

Do we have a field with this name and if so is it set to /Yes/i ?

field_true?(in_name)

def field_true?(in_name)

 test_name = in_name.downcase

 (@fields.include?(test_name) and @fields[test_name] =~ /yes/i)

end

Do we either have no field with this name, or a field saying /no/i

field_false?(in_name)

def field_false?(in_name)

 test_name = in_name.downcase

 (@fields.include?(test_name) or @fields[test_name] =~ /no/i)

end

Returns the specified field (if we have one) or a blank string

Fields are not case sensitive

field(in_name)

def field(in_name)

 if render_context and render_field?(in_name)

  # should we run through textile too?

  # doing this escaping just means we cannot put formatting into fields…

  execute_eruby(raw_field(in_name),render_context) 

 else

dates are special cased – we return the date unprocessed by eruby

  raw_field(in_name)

end

Make web safe

h in_string

def h in_string

 in_string.gsub(”<”,”<”) if in_string

end

Generate an array from a comma separated list stored in a field

used to generate an array of names from a field,

useful for generating icons/images from a csv list

field_list(in_name)

def field_list(in_name)

 the_field = field(in_name)

 if the_field.length > 0

  CSV.parse_line(the_field,’, ‘)

 else

  Array.new

end

Return the view path,

If full path exists, return that

otherwise we append a .erb or .rhtml extension

view_path

def view_path

 view_path = File.join(app.style_directory,”views”,@fields[“view”])

 if File.exists? view_path

  view_path

 elsif File.exists? view_path + ’.erb’

  view_path + ’.erb’

 elsif File.exists? view_path + ’.rhtml’

  view_path + ’.rhtml’

 else

  puts “Invalid viewpath #{@fields[‘view’]} in page #{folder_name}”

  ’page’ # fall back on standard page

end

Return the iso date of our date field

iso_date

def iso_date

 @fields[‘date’].strftime(“%Y-%m-%d”) if field? ‘date’

end

Add to our fields (replacing if necessary)

NB These fields must be lowercase

add_fields(in_fields)

def add_fields(in_fields)

 @fields.merge! in_fields

end

Munge the string so that it is safe for ID fields

NOTE ID fields shoudl start id, as we’re not allowed numbers at the start (bork bork bork)

web_safe_str(in_string)

def web_safe_str(in_string)

replace all non character stuff with a safe underscore

 in_string.gsub(/[0-9A-Za-z\\-\:\.]/,’‘)

end

Return a field safe for web consumption (so no a tags etc)

web_safe_field(in_name)

def web_safe_field(in_name)

 the_field = field(in_name)

quick and dirty strip of a tags which may appear in titles

 the_field.gsub(/<([>]*)\>/,”“)

 return web_safe_str(in_name)

end

FragmentHTML

Copyright© 2007 Kenny Grant – released under MIT licence (see Licence.txt)

Included by the fragment base class to generate html from fragments

These methods can be replaced by methods in Style/controllers/fragment.rb

on a per-website basis

Return a string representation of date object

Date fields should be of the form 17 Dec 2007

date

def date

 date_obj.strftime(“%A, %d %B %Y”) if date_obj

end

Returns the actual date object stored in field

Not a string!

date_obj

def date_obj

 @fields[“date”]

end

Produce formatted html text from the text field

What we do to our text depends on our file type

text_html

def text_html

 return unless render_context

 raw_text = @fields[“text”] || ””

we must process ERB stuff before running the text through redcloth

otherwise all commands will be escaped

 raw_text = execute_eruby(raw_text,render_context)

 if use_textile? # see use_textile for details

  redcloth = RedCloth.new(raw_text)

  redcloth.no_span_caps = true

  redcloth.to_html

 else

  raw_text

end

Returns the HTML text of the item, but trimmed to the number of lines specified

text_html_summary(max_chars = 100)

def text_html_summary(max_chars = 100)

 output = String.new

 text_html.each(” “) do | word |

  if output.length + word.length > max_chars

  break

  else

  output << word

 return output << ”...”

end

Returns the plain text of the item, but trimmed to the number of lines specified

text_summary(max_chars = 100)

def text_summary(max_chars = 100)

 raw_text = @fields[“text”] || ””

 raw_text = execute_eruby(raw_text,render_context)

 output = String.new

 raw_text.each(” “) do | word |

  if output.length + word.length > max_chars

  break

  else

  output << word

must strip html as we may have unbalanced tags

 output.gsub!( /\s?<\/?(\w)[^\n]?\/?>\s?\s?/,”\n”)

strip common textile tags which don’t make sense in a summary

could convert these instead to markdown?

 output.gsub!(/h\d\.\s?/,” “)

 return output << ”...”

end

Spits out an HTML tag for our image (if we have one)

image_html

def image_html

 if image and render_context

  @owner_page.img(:src => image_url, :class => ‘fragment_image’, :alt => web_safe_field(‘title’))

 else

  ”“

end

Spits out an HTML tag for our preview image (if we have one)

preview_image_html

def preview_image_html

 if image and render_context

  preview_image_html = @owner_page.img(:src => small_image_url, :class => ‘fragment_image_preview’, :alt => field(‘title’))

  # return a link which contains the preview img tag

  @owner_page.link(:href => image_url, :text => preview_image_html)

 else

  ”“

end

Returns the url of the fragment image

image_url

def image_url

 rendering_page.root_url + @owner_page.page_url_web + encode_str(image) if image

end

Returns the url of the small fragment image

small_image_url

def small_image_url

 if image

  preview_name  = File.basename(image,”.*”) + ”.jpg”

  preview_dir_name = image_model.image_dir_name(:small)

  rendering_page.root_url + @owner_page.page_url_web + preview_dir_name + ”/” + encode_str(preview_name)

end

 alias preview_image_url small_image_url

The HTML id which should be used as an anchor for the fragment (if that’s defined in template)

html_anchor

def html_anchor

 ”#” + field(“id”)

end

Generate a table from the table_rows field (if we have one)

called from fragment/table.erb template

table_html(options = {})

def table_html(options = {})

 return unless field?(‘table_rows’)

  output = “

 table_rows = @fields[‘table_rows’]

 cols = table_rows.first.length

 table_rows.each_with_index do | row, i |

  cell_type = row == 0 ? “th” : “td”

  output << “

\n”

   # add any extra cols we need with blank cells

   (cols – row.length).times {row << ””} if (row.length < cols )

   row.each_with_index do |cell, c_i|

   cell_text = RedCloth.new(cell).to_html

   output << ”<#{cell_type} class=\”cell_#{c_i+1}\”>#{cell_text}</#{cell_type}>”

  output << ”\n

\n”

 output << “

 output

end

PageHTML

Copyright© 2007 Kenny Grant – released under MIT licence (see Licence.txt)

Included by the page controller to generate html snippets

These methods can be replaced by methods in Style/controllers/page.rb

on a per-website basis

We should probably check doctype and close/open tags like img accordingly

 for micro-id stuff

Return the preferred name for home, try various choices, ending on default

home_name

def home_name

 return app.field(‘home_name’) if app.field?(‘home_name’)

 return app.root.field(‘short_title’) if app.root and app.root.field?(‘short_title’) and app.root.field(‘short_title’) != ‘Content’

 return “Home”

end

Returns a link to one or more .css files in the root level folder ‘stylesheets’

Input is an array of names, which can optionally be followed by a hash of options

We also append the site version to url to avoid caching bugs in Safari

stylesheet_link_tag(✹in_names)

def stylesheet_link_tag(✹in_names)

set up the default options for stylesheets

 options = {:rel => ‘stylesheet’, :href => ’’, :type => “text/css”, :media => ‘all’, :open => true}

 options.merge! in_names.pop if in_names.last.is_a?(Hash)

 if options[:generate_titles] 

  generate_titles = true

  options.delete(:generate_titles)

 in_names.inject(””) do | output, sheet_name | 

  # set title to last component of stylesheet path

  options[:title] = sheet_name.gsub(/\//,”“) if generate_titles

  options[:href] = ”#{root_url}stylesheets/#{sheet_name}.css?version=#{app.version}”

  output << tag(“link”,options) << ”\n\t”

end

Returns a link to one or more .js files in the root level folder ‘javascripts’

Input is an array of names, which can optionally be followed by a hash of options

javascript_link_tag(✹in_names)

def javascript_link_tag(✹in_names)

set up the default options for javascripts

 options = {:src => ’’, :type => “text/javascript”, :open => true}

 options.merge! in_names.pop if in_names.last.is_a?(Hash)

 in_names.inject(””) do | output, script_name | 

  options[:src] = ”#{root_url}javascripts/#{script_name}”

  options[:src] << ’.js’ if options[:src] !~ /\.js/

  options[:open] = true

  output << tag(“script”,options) << tag_end(“script”) << ”\n\t”

end

Returns a list of links to js files included in javascript_includes field (optional field)

If no field, returns empty string

javascript_includes deprecated, use javascripts

javascript_includes

def javascript_includes

 output = ”<!—Page specific javascripts—>\n”

 now check the javascripts field

 javascripts_list = @fields[‘javascripts’] || @fields[‘javascript_includes’]

 if javascripts_list

  javascripts_list.split(’, ‘).each do | script |

  output << javascript_link_tag(script, :defer => :defer)

next try for a javascript named after the page

 page_script = “site/#{folder_name_web.downcase}”

 page_script_file = ”#{app.website_directory}/javascripts/#{page_script}.js”

 output << javascript_link_tag(page_script, :defer => :defer) if File.exists? page_script_file 

 output

end

Returns a list of links to js files included in javascript_includes field (optional field)

If no field, returns empty string

stylesheet_includes

def stylesheet_includes

 output = ”<!—Page specific stylesheets—>\n”

first try for a stylesheet named after the page

 page_style = “site/#{folder_name_web.downcase}”

 page_style_file = ”#{app.website_directory}/stylesheets/#{page_style}.css”

 output << stylesheet_link_tag(page_style) if File.exists? page_style_file

 now check the stylesheets entry

 stylesheets_list = @fields[‘stylesheets’] || @fields[‘stylesheet_includes’]

 if stylesheets_list

  stylesheets_list.split(’, ‘).each do | script |

  output << stylesheet_link_tag(script)

 output

end

Returns an unordered list containing navigation links

Ignores pages which have a field ‘list’ set to ‘no’

Examples of use :

navigation :depth => 1 – produces a list of top level pages

navigation – produces a list of all pages

navigation :exclude => ‘about,missing,contact’ – excludes these three pages from the listings

navigation(options = {})

def navigation(options = {})

 options[:id]     ||= ‘navigation’ # id for list element

 options[:page_list]   ||= ‘root’   # by default lists from root

 options[:depth]    ||= 0     # by default goes all the way down

 options[:order]    ||= ‘order’   # by default order on ‘order’ field of pages

navigation includes the root page as well

 don’t override options passed in

 options[:include_root]  = true unless options.include?(:include_root)

only show related pages

 don’t override options passed in

 options[:related]    = true unless options.include?(:related)

 render(options)

end

Returns an unordered list containing navigation links

Ignores pages which have a field ‘list’ set to ‘no’

Examples of use :

sub_navigation :level => 3 – produces a list of all pages in the current branch of the hierarchy at level 3

sub_navigation(options = {})

def sub_navigation(options = {})

 return if self.root? # don’t render subnav for root page

 options[:id]     ||= ‘sub_navigation’ # by default this is id of ul element for subnav

 options[:depth]    ||= 1     # by default goes 1 down

 options[:order]    ||= ‘order’   # by default order on ‘order’ field of pages

 options[:level]    ||= 2     # by default we only show pages from level 2

find the page at this level which we want to show children of

by default lists from parent of current page at the correct level

 options[:page_list]   ||= parent_at_level(options[:level]).folder_name 

sub navigation does not include the root page as well

 don’t override options passed in

 options[:include_root]  = false unless options.include?(:include_root)

don’t only show related pages

 don’t override options passed in

 options[:related]    = false unless options.include?(:related)

 render(options)

end

Return all the pages as a vast (flat) array

all_pages

def all_pages

 app.all_pages

end

Produce a list tag for a navigation list

Uses id or class if available (only on root of list though)

page_list_start_tag(in_options)

def page_list_start_tag(in_options)

 options = {:open => true}

 only add class or id on root of listing

 if (in_options[:root_page] == @parent_page)

  options[:id] = in_options[:id] if in_options[:id]

  options[:class] = in_options[:class] if in_options[:class]

 tag(“ul”,options)

end

Produce a list item for a page listing in navigation

We close list tag if finish_tag = true

Should spit out <li><a></a></li>

Will give the tag class selected if in_page is this page or related page (parent or child)

 uses a relative url to page of form root/parent/page/

to be sure we link to the right page

page_list_item(in_page,in_options = {},finish_tag = false)

def page_list_item(in_page,in_options = {},finish_tag = false)

 options = {:open => true}

 options[:class] = ‘selected’ if related?(in_page.folder_name)

 options[:class] = ’’ if in_page.root? and in_page != @page # don’t select if checking root and we are not root

 options[:class] = ”#{options[:class]}#{in_options[:li_class]}” if in_options[:li_class]

 options[:id] = in_options[:li_id] if in_options[:li_id]

if this is a .page folder, treat it differently

 page_path = in_page.page? ? in_page.page_url + in_page.folder_name : in_page.page_url

 tag(“li”,options) + link(:page =>page_path) + (finish_tag ? tag_end(“li”) : ””)

end

Produce a list item for a page listing in navigation with more detail

We close list tag if finish_tag = true

Should spit out <li><a></a></li>

Will give the tag class selected if in_page is this page or related page (parent or child)

 uses a relative url to page of form root/parent/page/

to be sure we link to the right page

page_list_item_verbose(in_page,in_options = {},finish_tag = false)

def page_list_item_verbose(in_page,in_options = {},finish_tag = false)

 options = {:open => true}

 options[:class] = ‘selected’if related?(in_page.folder_name)

 options[:class] = ”#{options[:class]}#{in_options[:li_class]}” if in_options[:li_class]

 options[:id] = in_options[:li_id] if in_options[:li_id]

if this is a .page folder, treat it differently

 page_path = in_page.page? ? in_page.page_url + in_page.folder_name : in_page.page_url

 tag(“li”,options) + link(:page =>page_path, :text => in_page.page_title) + (finish_tag ? tag_end(“li”) : ””)

end

Generate breadcrumbs for this page

breadcrumbs(options = {:separator => ’ > ‘})

def breadcrumbs(options = {:separator => ’ > ‘})

  # Note class variable

  @_@depth = distance_to_root + 1

  if @parent

  @parent.breadcrumbs_to_page(options) + options[:separator] + @fields[“short_title”]

  else

  home_name

end

Returns the url of a page from this page

Following options accepted

* :page – the page to link to

* :fragment – an optional fragment file name to find

* :google => ‘text for google search’

p_url(options)

def p_url(options)

 the_page  = app.page(options[:page]) if options[:page]

 the_fragment = app.fragment(options) if options[:fragment]

 google_url  = “http://www.google.com/search?q=#{options[:google].gsub(’ ’,’+‘)}” if options[:google]

puts “linking to #{the_page.page_html_url}” if options[:page] == “deviceinfo”

 if the_page

  f_anchor = the_fragment ? the_fragment.html_anchor : ””

  root_url + the_page.page_html_url + f_anchor

 elsif google_url

  google_url

 else

  nil

end

Get the text of the named fragment

f_text(in_name)

def f_text(in_name)

 f = fragment(:find => in_name)

 f.text_html if f

end

Returns a snippet of HTML for a named image

*If :src supplied, uses that, if not and :name supplied

adds png extension if no extension given, and locates in images folder at root

img(options)

def img(options)

 if options[:src].nil? and options[:name]

  # override image url if it wasn’t supplied and we have a name

  options[:src] = ”#{root_url}images/#{options[:name]}” << (File.extname(options[:name]).length > 0 ? ”” : ”.png”)

  options[:name] = File.basename(options[:name],”.“)

 if (options[:src])

  image_name = File.basename(options[:src],”.“).gsub(“%20”,”_“)

  image_id = options[:src].gsub(”/”,”-“).gsub(/\.[.]*$/,”“).gsub(/\.\.-/,”“).gsub(“%20”,”_“)

not valid attribute for images

 options.delete(:name)

 tag(“img”, {:id => image_id, :alt => image_name, :open => true}.merge(options))

end

Generate a link to the requested page or fragment

Following options accepted

* :page – the page to link to

* :fragment – an optional fragment file name to find

* :google => ‘text for google search’

link(options)

def link(options)

 if options[:fragment]

  the_fragment  = app.fragment(options[:fragment]) 

  return options[:text] if the_fragment.nil?

  the_page = the_fragment.owner_page

  options[:page] = the_page.page_url

 elsif options[:page]

  # interpret blank page string as root – folder_url returns blank for root page

  options[:page] = “root” if options[:page] == ””

  the_page   = app.page(options[:page]) if options[:page] 

 the_page_title = the_page.field(‘short_title’) if the_page

we don’t want these tags to end up in anchor tag so delete them from options which we pass in to tag

 tag_options = { :open => true, :href => p_url(options)}.merge(options)

 tag_options.reject!(){ | key, value | key.to_s =~ /text|page|fragment|find|field|google/ }

 if it’s a google search, replace href with the google link

 options[:href] = “http://www.google.com/search?q=#{options[:google].gsub(’ ’,’+‘)}” if options[:google]

get hold of the text for use inside link

 tag_text = options[:text] || options[:fragment] || the_page_title || options[:google] || tag_options[:href] || options.to_s

 link_text = tag(‘a’,tag_options)

 link_text << tag_text + tag_end(‘a’) unless options[:open]

 link_text

end

Return the name of the page for display

page_title

def page_title

 field(“title”)

end

Return the name of the page for display

Used for navigation, links and Site maps

short_title

def short_title

 field(“short_title”)

end

Return the path to our output page

returns path for html, rss or other output file (without file name appended)

root_url

def root_url

 ”../” * distance_to_root

end

Returns the url (safely encoded) to our page

page_url_web

def page_url_web

 if page?

  @parent.root? ? ”” : @parent.page_url_web 

 else

  output_folder_name = page? ? @parent.folder_name_web : folder_name_web

  root? ? ”” : @parent.page_url_web + output_folder_name + ”/”

end

Full url for HTML page, including file name, but not site url

only to be used in web pages as it is encoded

page_html_url

def page_html_url

 html_name_web = encode_str(field(“html_name”))

 @parent ? page_url_web + html_name_web : html_name_web

end

Full url for HTML page, including file name, and site url

Only to be used in web pages as it is encoded

absolute_url

def absolute_url

 ”#{site_url}/#{page_html_url}”

end

The mtime of this page as ISO date time string

(Updated from mtime of from fragments)

Can be used for outputting on page

iso_mtime

def iso_mtime

dates expected in format 1997-07-16T19:20:30+01:00

this outputs 1997-07-16T19:20:30+0100, so we add separator manually

 @fields[“mtime”].strftime(“%Y-%m-%dT%T%z”).gsub(/(\d{2})(\d{2})$/,”\\1:\\2”)

end

The mtime of the output file for this page as ISO date string

(Uses later of date of output file or mtime) – not yet done

Use for outputting on sitemap

iso_mdate

def iso_mdate

 not yet done, just use mtime for now and outputs date only

 @fields[“mtime”].strftime(“%Y-%m-%d”)

end

Get the first item from a page’s fragments

order can be set by caller

first_fragment options

def first_fragment options

 options[:collection] ||= @fragments

 options[:limit]  ||= 1 

 return ordered_collection(options)[0] 

end