[V4.0] Templates

Craig Riecke edited this page Aug 3, 2014 · 1 revision
Clone this wiki locally

This guide covers how to set up templates for BrowserCMS. After reading this guide, you should understand:

  • How to create page templates
  • How to take advantage of dynamic menus
  • How to reuse common bits of HTML using page partials
  • How to use tags to add dynamic content to templates and partials

This guide is for BrowserCMS 4.0.

Introduction

One of the primary goals of implementing a Content Management System is to separate Design from Content. This allows designers to provide an attractive design that users can easily apply. A CMS can be thought of as a ‘Web Page’ factory, where designers can create the template (i.e. the molds) that users without design knowledge can use to stamp out new pages.

BrowserCMS supports this concept by the use of page templates. Each page within the site has a single page template that governs it’s layout. Templates are essentially html pages with the editable areas specified. A template can be anything from a basic two column subpage to homepage design with a half dozen separate editable areas. BrowserCMS supports an ‘in-context’ page editor, that allows users to see which areas of the page are editable.

Typical Design Process

When you are developing a site with BrowserCMS, there are a lot of ways to organize and design the site. One of the goals of the project is to support highly graphical designs that pass through multiple team members hands. As such, the template creation process is optimized to mimic the steps that would work for any web development project, without constraining the design to fit the tool.

Here is an example of a web design process to create templates for BrowserCMS.

  1. A designer creates a Photoshop (PSD or JPG) which represents the desire final look of a page.
  2. A developer converts the PSD into a static HTML file which implements that design.
  3. The developer can take this html file, add it to the CMS as a page template just by renaming it from .htm to .html.erb
  4. The developer can add a few helper methods to the template replacing the ‘static’ text to make it editable through the CMS interface.

There are few additional considerations we will get into, but this basic process allows developers to work in a medium they are well trained in (HTML production) with minimal training on BrowserCMS itself. For the purposes of this document, assume there are three separate and distinct roles around creating a CMS site.

  1. Designer – A person in charge of defining the overall look and feel of the pages within a site. Produces a PSD.
  2. Developer – A person in charge of converting the ideal look (PSD) into an HTML page and ultimately a CMS template.
  3. Programmer – A person in charge of coding content types (blocks) and dynamic behavior (portlets).

Design Considerations

There are some considerations that designers/developers should take into account if they want to get the maximum benefit from working w/ BrowserCMS. Here’s a quick list:

1. BrowserCMS can render menus (one or more levels deep) automatically from the site architecture. Developers should understand how menu code is generated when styling them, which happens in a CSS Zen Garden style.
2. The Site Map is how pages and sections are organized. Developers should understand how this will affect auto-generated menus.
3. In addition to static content, the look of some areas of pages will be rendered dynamically by portlets and blocks. Developers may need to work with programmers to determine whether view code will be dynamically generated via portlets or statically placed by users.

Creating a Page Template

There are two ways to create a template, as a file within the project (much like how Rails layouts work) or as content in the database. Let’s start with creating a file based template. Assuming we are starting from a new blank BrowserCMS project, lets create a template that will represent our subpages. Run the following from the command line:


$ rails generate cms:template subpage	

This generates a file called app/views/layouts/templates/subpage.html.erb. For experience Rails developers, you will note that within the traditional layouts directory we have create a subdirectory called templates. All files placed within this directory are available as page templates with the UI. Open up this template file and it should look like this:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
    <title><%= page_title %></title>
    <%= yield :html_head %>
  </head>
  <body style="margin: 0; padding: 0; text-align: center;">
    <%= cms_toolbar %>
    <div id="wrapper" style="width: 700px; margin: 0 auto; text-align: left; padding: 30px">
      Breadcrumbs: <%= render_breadcrumbs %>
      Main Menu: <%= render_menu %>
      <h1><%= page_title %></h1>
      <%= container :main %>
    </div>
  </body>
</html>	

This is a bare bones XHTML template with one editable area (i.e. One Column). It has no CSS stylesheets or javascript. It’s about the simplest possible template we can have to create a renderable page in BrowserCMS.

Files or Content?

Since templates can be stored as either files or database content (or a combination of both), site designers will need to decide which path they want to use. Ultimately, the decision is based on the question of who should be able to modify templates? On projects where site users have some HTML knowledge and want to be able to update templates via the CMS UI, database templates may be appropriate. On other projects, where design is intended to more tightly controlled, file templates might be appropriate.

It’s also possible to start out using file templates for speed of development, and then migrate the content of the templates into the database before launch.

Understanding Containers

There are bits of ruby code (helpers) that our basic template has, but let’s focus on the container helper. Open up the CMS UI and go to /cms/sitemap and Add a New Page. Name it ‘About Us’, and select ‘Subpage’ from the ‘Template’ dropdown to use this new template. Click Save. You should see a mostly white page. Click the ‘Turn On’ button in the upper right to enable the editor, and you should see a single grey box. This box is the container we defined in our template. Users can insert new or existing content into pages, and the containers determine where they go. Add some Text content to the page in this container.

NOTE. Container are placeholders for content. When you have two pages that use the same template, they do not share the content. Putting some Text into the :main container of Page A doesn’t mean it will also appear on Page B just because that page also has a :main container.

Adding A Sidebar Container

Let’s alter our template to add a second column (i.e. Sidebar). Modify the subpage.html to look like the following:

<head>
  <title><%= page_title %> | Our Fancy Site</title>
  <style>
      body, html { margin: 0; padding: 0; text-align: center; }
      #wrapper { width: 700px; margin: 0 auto; text-align: left; padding: 30px }
      #main { float: left; width: 500px; }
      #sidebar { float: right; padding: 0 30px 0 0; width: 150px;}
  </style>
  <%= yield :html_head %>
</head>
<body>
<%= cms_toolbar %>
<div id="wrapper">
  <div id="header">
    Breadcrumbs: <%= render_breadcrumbs %>
    Main Menu: <%= render_menu %>
    <h1><%= page_title %></h1>
  </div>
  <div id="main"><%= container :main %></div>
  <div id="sidebar"><%= container :sidebar %></div>
</div>
</body>

We have added several div elements to the template to clearly define a header, main and sidebar areas of our page. We have also cleaned up the CSS placed in the head element of the page. This CSS could also be moved to an external stylesheet as well. Now refresh the page and you should see an additional column on the right with a new :sidebar container being rendered. The content placed in the :main container will still there, though moved around based on the CSS rules of the template.

Having seen the sample templates, it should be possible to construct HTML templates using typical HTML and CSS, place containers in them where content should appear and get a reasonable looking set of templates.

Tags Quick Reference

This section covers a reference for all the tags (aka Helper Methods) that can be used in Page Templates to render various elements. Here is a quick list of the available tags, their purpose and whether they are required or not.

Name Purpose Required?
container A named area that allows users to add/edit content Yes
container_has_block To hide markup if a container is empty No
cms_toolbar Renders the CMS toolbar controller required for editing and navigation. Should be very first element in body Yes
yield :html_head This allows the CMS to insert javascript and other information into the page. Should be last element in head Yes
page_title Inserts the ‘Name’ of the page (or ‘Title’ if it exists) No
current_page Returns a reference to the Page object that is currently being rendered No
render_breadcrumbs Generates breadcrumbs based on the current page and its location in the sitemap No
render_menu Generates a menu based on the current page and its location in the sitemap No
render_portlet Renders a specific portlet by name No
render_connectable Renders a specific content block No

In addition, since Page Templates are Rails layouts, you can make use of any of the Asset Tags like stylesheet_link_tag or image_tag.

Required Tags

While most of the tags are optional, there are several tags which must be included or page template just won’t work correctly. Here’s the minimum necessary tags required.

1. yield :html_head – Leaving this out will cause problems with in page editing.
2. cms_toolbar – Leaving this out will provide editing and navigation to other CMS pages.
3. container – Having at least one (typically called :main) is required to allow content to be added to pages.

container Tag

This tag is used to allow areas of the page to be editted and render through the CMS. It takes a single argument which is the name of the container. Template developers can name containers whatever they like, but its recommened that the names should generally reflect the area or purpose of a particular container.

<div><%= container :main %></div>
<div><%= container :footer %></div>

Container names are just labels. So if you defined 5 containers on a template (header, footer, left, main, right), you could swap between any two templates which defined all those containers.

NOTE. Be aware that if you add content to a page in a container (like :sidebar) then change a pages template to one which does not have that container, the content will be ‘orphaned’. To remove that content, you would need to swap back to the previous template, remove the content, then change back to the new template again.

container_has_block? Tag

This tag can be used to hide or show containers based on whether a user has placed content in them. While an empty container tag won’t show anything, very frequently template builders will create ‘wrapper’ markup around containers that shouldn’t appear when nothing is placed there. For example, suppose we have a sidebar container that we want to have a styled border on, but we only want it to appear when something is in that container. Here’s how you would do that:

<% if container_has_block? :sidebar %>
<div id="sidebar"><%= container :sidebar %></div>
<% end %>

This tag returns true if the user is in edit mode as well, regardless of whether there is content within the container. This is important, because otherwise, an editor would never be able to select the container to add content to!

cms_toolbar Tag

This tag is how the toolbar is inserted into templates. It should be the first element in the body element of a page. In most cases, you can just use it as follows:

<body>
  <%= cms_toolbar %>
  <h2>About Us</h2>
  <!-- Text goes here -->
</body>

The toolbar is written out as an iframe which makes its less likely that styles/javascript will clash with the page being editted.

HTML Head Tag

This tag is technically a standard yield call, with a particularly named :html_head. This tag must be included in all CMS templates to allow it to insert the necessary javascript and style elements. It should probably be the last element in head element of a page.

<head>
  <title><%= page_title %></title>
  <%= yield :html_head %>
</head>	

page_title Tag

This tag will output the name attribute of the current page. If the title attribute is specified, it will output that instead. This allows users to have one label for a page appear in the navigation (navigation and the sitemap always use name) and another in the header or title bar on a page.

Example:

<title>My Site | <%= page_title %></title>

This is using the page_title tag to title element of a page.

<h1><%= page_title %></h1>

Another common usage of page_title is for the H1 or H2 on a page.

current_page Tag

This doesn’t render anything, but returns a reference to the current Page object that is being rendered. It can be used to access other attributes of the page, like the name of the section or the description of the Page. Here are some common examples:

<%= current_page.name %>

This outputs the name attribute of the page, similar to how page_title works. You might do this if you had specified a title attribute for pages which was different than the name, but wanted to compare names.

<%= current_page.section.name %>	

This outputs the name of the current page’s section.

<% if current_page.top_level_section.name == 'About Us' %>
<%= image_tag "/fancy/about_us_section_image.png" %>
<% end %>	

This renders a specific image if the page is within the top level ‘About Us’ section. The top_level_section method finds the very top level section for a give page, regardless of how far down it is in the sitemap. Useful for making comparisons based on the first level of navigation.

NOTE. Any page or section attribute is reachable in the way. You will need to look at the API documentation for Page and Section to see all the available attributes.

render_portlet Tag

This tag will render a portlet from a template. Normally portlets are placed individually on each page by users. This tag is useful for common elements that might appear on multiple pages throughout the site. An example of this might be a Login Portlet which should appear as part of the header on all pages. Here’s an example:

<%= render_portlet "Nav Login Form" %>	

The only argument to render_portlet is a name. This name will match the name specified in the UI when you add a portlet. You will need to create the portlet and name it appropriately for this tag to work. The portlet will render itself based on its template.

render_connectable Tag

This tag can render a specific block in a page. It takes one argument, the block itself. This could be used to automatically include, say a footer, into a page as part of the template. For instance:

<% footer = Cms::HtmlBlock.find(12) %>
<%= render_connectable(footer) %>

Here we use Active Record to query for a specific HtmlBlock (i.e. Text) with the id of 12. Then we pass it to render_connectable to display itself.

Navigation

Most websites typically features some form of navigation via menus and/or breadcrumbs. Some navigation is highly graphical and changes infrequently. Other’s might use drop downs (via Javascript), while still others have site maps that drill down to the deepest pages. BrowserCMS is designed to support many different navigation choices, including everything from hand coded horizontal navigation to dynamic left side menus.

One of the features that BrowserCMS provides is an menu and breadcrumb generation tags. These tags can automatically generate menus/breadcrumbs based on the current page. For breadcrumbs, they render linkable items for the page and its parents. For menus, it can show a page, all it’s siblings as well as highlight the currently selected sections. The primary advantage of this is for rendering menus which can frequently change, like section navigation. As pages are added and published in the sitemap, they will automatically appear in the menus.

Basic Rules of Menus

Understanding dynamic menus can be tricky things, so you will likely need to experiment with them, but here are some basic rules to remember.

  1. Menus will automatically generate names and URLs based on the name and path attributes of pages in the sitemap.
  2. Hidden pages/section are not shown. If you mark a page or section as ‘hidden’ via the sitemap, it will never be shown in the menu.
  3. Unpublished pages are not shown. If you change a page name or url, it won’t appear until the page is published.
  4. Moving pages and sections around in the sitemap will reorder them in the menus automatically.

Here’s an example of a site map representing part of the National Football League’s conference system. While not all readesr may be familiar with ‘American’ football, it does provide a nice example of a tree structure that has multiple levels (Conferences > Divisions > Teams). We will use this sitemap in the following examples to highlight how to build menus.

Using Menus

To render a menu, you can make use of the render_menu tag. It will generate HTML markup that can be styled via CSS to render either horizontal or vertical navigation.

<%= render_menu %>

This would render a menu starting with all the top level sections, creating unordered lists as far down as the current page. If I navigate to the Dallas page (+/nfc/east/dallas), the following HTML would be rendered:

<div id="menu" class="menu">
  <ul>
    <li id="section_3" class="depth-1 first open">
      <a href="/nfc/east/dallas">NFC</a>
      <ul>
        <li id="section_5" class="depth-2 first open">
          <a href="/nfc/east/dallas">East</a>
          <ul>
            <li id="page_5" class="depth-3 first on">
              <a href="/nfc/east/dallas">Dallas</a>
            </li>
            <li id="page_6" class="depth-3 last">
              <a href="/nfc/east/new-york">New York</a>
            </li>
          </ul>
        </li>
        <li id="section_6" class="depth-2">
          <a href="/nfc/north/chicago">North</a>
        </li>
        <li id="section_7" class="depth-2">
          <a href="/nfc/west">West</a>
        </li>
        <li id="section_11" class="depth-2 last">
          <a href="/nfc/south/tampa-bay">South</a>
        </li>
      </ul>
    </li>
    <li id="section_4" class="depth-1 last">
      <a href="/afc/east">AFC</a>
    </li>
  </ul>
</div>

Things to note:

  • The top level containing element for the menu is marked as id and class = menu. (class can be changed via parameters.)
  • We are currently on the ‘Dallas’ page. So the dallas li is marked with an on class.
  • The ‘NFC’ and ‘East’ sections are open (because they are ancestors of the ‘Dallas’ page). They are marked with an open class.
  • All siblings to ‘open’ sections are shown (i.e. we see ‘AFC’ and ‘North’, but nothing under them). The first and last element of each list is marked with first and last.
  • The depth of each element has a style. For instance, the ‘Dallas’ page is at depth-3.

How Deep Should Menus Be?

Since we have not given any options to render_menu, it will render all levels down to the current page. In a deeply nested site, you could end up with 4 to 5 levels. In general practice, its often best to limit menus to 2-3 levels. You can do this by specifying how deep and when we want to start menus. For instance, the following menu code would behave identically to the previous one.

<%= render_menu :from_top=>0, :depth=>3 %>

This menu will show all items below level 0 (from_top=>0) and showing a total 3 levels of navigation (+depth=>3). Our ‘top level’ sections/pages (like AFC, NFC and _Home__) are all at depth=>1, so they are shown. The East page is second level and the Dallas page is the 3 level, so we see all three levels of navigation.

In general, the :depth option is used to ‘cut off’ navigation after a certain point (since a vertical leftside nav can only render so many levels), while :from_top is used to determine how high to start. Here’s some further examples:

Primary Navigation

Want to show only the first level of navigation? Try this:

<%= render_menu :from_top=>0, :depth=>1 %>	

This will render only a single level of our top level navigation, regardless of how deep our site goes. Here’s the output when we are on the Dallas page.

<div id="menu" class="menu">
  <ul>
    <li id="page_1" class="depth-1 first">
      <a href="/">Home</a>
    </li>
    <li id="section_3" class="depth-1">
      <a href="/nfc/east/dallas">NFC</a>
    </li>
    <li id="section_4" class="depth-1 last">
      <a href="#">AFC</a>
    </li>
  </ul>
</div>
<h1>Dallas</h1>

Sub Navigation

Suppose we wanted to have two menus on a site, a horizontal ‘primary’ navigation and a vertical ‘sub’ navigation. In this case, we want to show our NFL conferences as the primary navigation, and when you drill down the Division would be shown as the left side navigation. Here’s how the left nav can be generated.

<%= render_menu :from_top=>1, :depth=>2, :class=>"leftmenu" %>	

Here is the output when we are on the Dallas page.

<div id="menu" class="leftmenu">
  <ul>
    <li id="section_5" class="depth-1 first open">
      <a href="/nfc/east/dallas">East</a>
      <ul>
        <li id="page_5" class="depth-2 first on">
          <a href="/nfc/east/dallas">Dallas</a>

        </li>
        <li id="page_6" class="depth-2 last">
          <a href="/nfc/east/new-york">New York</a>
        </li>
      </ul>
    </li>
    <li id="section_6" class="depth-1">
      <a href="/nfc/north/chicago">North</a>

    </li>
    <li id="section_7" class="depth-1">
      <a href="/nfc/west/atlanta">West</a>
    </li>
    <li id="section_11" class="depth-1 last">
      <a href="/nfc/south/tampa-bay">South</a>
    </li>
  </ul>

</div>

Site Maps and Flyout Menus

You can also handle sitemaps and flyout menus by specifying some additional attributes. Normally, menus will not show the entire tree, but you can make them by doing the following:

<%= render_menu :show_all_siblings=>true %>

This will render every visible node in the tree, regardless of which page we are currently on. It follows the same :from_top and :depth rules as other menus.

Using Breadcrumbs

Breadcrumbs work very similar fashion to menus, except that they only render a single list which includes the current page and all ancestors.

<%= render_breadcrumbs %>

If we are on the Dallas page, this will render the following HTML:

<ul class="breadcrumbs">
  <li class="first"><a href="/">My Site</a></li>
  <li><a href="/nfc/east/dallas">NFC</a></li>
  <li><a href="/nfc/east/dallas">East</a></li>

  <li>Dallas</li>
</ul>

If we moved to the Detroit page, it would render this:

<ul class="breadcrumbs">
  <li class="first"><a href="/">My Site</a></li>
  <li><a href="/nfc/east/dallas">NFC</a></li>
  <li><a href="/nfc/north/chicago">North</a></li>

  <li>Detroit</li>
</ul>

You can also specify some options to it:

<%= render_breadcrumbs :from_top=>1 %>

This would exclude the top level section (i.e. the name of the site My Site). This would render the following if I was on the Dallas page:

<ul class="breadcrumbs">
  <li class="first"><a href="/nfc/east/dallas">NFC</a></li>
  <li><a href="/nfc/east/dallas">East</a></li>
  <li>Dallas</li>
</ul>

Using Partials

A site which has multiple templates will likely have duplicate HTML. Areas of the page like Headers and Footers can easily be exactly the same between two templates. In order to avoid duplication, it is possible to use partials to extract and reuse view code. Like templates, partials can either be content in the repository or files, depending on how developers want to organize their project. See the Rails Layouts and Views Guide for more information on how partials work.

Let’s extract our common header code from our subpage template. Here’s how the file looked earlier:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
<head>
  <title><%= page_title %> | Our Fancy Site</title>
  <style>
      body, html { margin: 0; padding: 0; text-align: center; }
      #wrapper { width: 700px; margin: 0 auto; text-align: left; padding: 30px }
      #main { float: left; width: 500px; }
      #sidebar { float: right; padding: 0 30px 0 0; width: 150px;}
  </style>
  <%= yield :html_head %>
</head>
<body>
<%= cms_toolbar %>
<div id="wrapper">
  <div id="header">
    Breadcrumbs: <%= render_breadcrumbs %>
    Main Menu: <%= render_menu %>
    <h1><%= page_title %></h1>
  </div>
  <div id="main"><%= container :main %></div>
  <div id="sidebar"><%= container :sidebar %></div>
</div>
</body>
</html>

Create a new file called app/views/partials/_header.html.erb. By placing partials in this directory, they will match where the content repository based templates are written out to. This will allow templates to include partials regardless of where its data is stored. Copy/Paste code from subpage.html.erb so that _header.html.erb looks like this:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
  <title><%= page_title %> | Our Fancy Site</title>
  <style>
      body, html { margin: 0; padding: 0; text-align: center; }
      #wrapper { width: 700px; margin: 0 auto; text-align: left; padding: 30px }
      #main { float: left; width: 500px; }
      #sidebar { float: right; padding: 0 30px 0 0; width: 150px;}
  </style>
  <%= yield :html_head %>
</head>
<body>
<%= cms_toolbar %>

Then update subpage to look like this:

<%= render :partial=>"partials/header" %>
<div id="wrapper">
  <div id="header">
    Breadcrumbs: <%= render_breadcrumbs %>
    Main Menu: <%= render_menu %>
    <h1><%= page_title %></h1>
  </div>
  <div id="main"><%= container :main %></div>
  <div id="sidebar"><%= container :sidebar %></div>
</div>
</body>
</html>

Now when you refresh a page using the Subpage template, it should look exactly the same.