Creating the Template VTL File

The template VTL (Velocity Template Language) file is a theme's backbone, and the most basic requirement for the creation of a new theme. Simply place a folder containing a template.vtl file into a site's application/themes directory, and dotCMS will detect a new theme, taking the theme's name from the folder.

The quickest way to get started building your own custom template.vtl is to use the default System theme's as a starting point, adding VTL includes to replace layout placeholders. An example of one way this might look can be found at the bottom of the page.

Velocity Variables#


Template VTLs are able to call two important pre-defined Velocity variables, pointing to theme and layout data:

  • $dotTheme
  • $dotThemeLayout

Discussion of these two variables will be broken up into four subsections, in view of important nested properties.

Key Paths and More: $dotTheme#

$dotTheme contains basic information about the theme:

PropertyData
$dotTheme.titleThe theme's name, drawn from its folder.
$dotTheme.pathThe directory containing the theme — e.g. /application/themes/THEME_NAME/.
$dotTheme.htmlHeadtrue or false depending on whether the folder contains html_head.vtl.
$dotTheme.templatePathThe path to the template.vtl file asset.

The Big Picture: $dotThemeLayout#

$dotThemeLayout contains various layout properties.

PropertyData
$dotThemeLayout.titleThe title of the template or custom layout.
$dotThemeLayout.headertrue or false depending on the template or layout's settings.
$dotThemeLayout.footertrue or false depending on the template or layout's settings.
$dotThemeLayout.bodyContains body layout data; see below.
$dotThemeLayout.sidebarContains sidebar layout data; see below.

The two properties .body and .sidebar are special; both contain additional nested properties. In the system template.vtl, these two are given aliases for ease of reference:

## Main column #set( $mainColumn = $dotThemeLayout.body ) ## Sidebar #set( $sidebar = $dotThemeLayout.sidebar )

We shall here consider these aliases to be a matter of convention.

The Body: $dotThemeLayout.body or $mainColumn#

$mainColumn contains only a single array: $mainColumn.rows. Each individual $row within contains the $row.columns property, an array of individual $column elements.

A pair of nested #foreach loops can iterate through the whole structure:

#foreach( $row in $mainColumn.rows ) <!-- row html --> #foreach( $column in $row.columns ) <!-- column html --> $render.eval($column.draw()) <!-- column html after content --> #end <!-- html concluding the row --> #end

At the innermost loop lies a series of $column structures, which have the following properties:

PropertyData
$column.leftA number from 0 to 11, indicating horizontal position based on $mainColumn's 12 divisions, counting from the left.
$column.leftOffsetSame as $column.left, but indexed from 1 to 12 instead of 0 to 11.
$column.widthA value from 1 to 12, indicating the column's width in terms of the number of $mainColumn's 12 horizontal divisions used.
$column.widthPercentSimilar to .width, but represented as a percentage of the $mainColumn.
$column.styleClassLists HTML class attributes assigned through the layout editor.
$column.previewEquivalent to $column.isPreview(); yields true or false.
$column.draw()Generates a #parsecontainer macro for the column, which can be read by $render.eval() — a method of the RenderTool Viewtool.
$column.containersContains additional object data; see below.

Each $container in $column.containers possesses:

PropertyData
$container.identifierA reference assigned to each element on the basis of its publishing bundle.
$container.uuidAn index number to distinguish between elements with the same identifier.

$sidebar contains the following properties:

PropertyData
$sidebar.widthA value of small, medium, or large, as selected in the template editor.
$sidebar.widthPercentA percentage of the viewport space, corresponding to $sidebar.width — either 20, 30, or 40.
$sidebar.locationEither left or right, as defined in the template editor.
$sidebar.previewEquivalent to $sidebar.isPreview(); yields true or false.
$sidebar.draw()Generates a #parsecontainer macro for the sidebar, which can be read by $render.eval() — a method of the RenderTool Viewtool.
$sidebar.containersContains additional object data; identical to $column.containers, in the previous section.

Including Other VTLs in template.vtl#


As with any case of including a VTL file, your html_head.vtl, header.vtl, and footer.vtl can be included with a call to the #dotParse macro, albeit with one small difference: They will require the $dotTheme.path variable in their file path.

Here are examples:

Including the HTML Head#

#if($dotTheme.htmlHead) #dotParse("${dotTheme.path}html_head.vtl") #end

Including the Header#

#if($dotThemeLayout.header) #dotParse("${dotTheme.path}header.vtl") #end

Including the Footer#

#if($dotThemeLayout.footer) #dotParse("${dotTheme.path}footer.vtl") #end

Example: Complete template.vtl#


Below is an example of a functional template.vtl file, based on the System theme VTL. Bootstrap-style classes used throughout correspond to styles defined in the original's head; in the example below, those definitions — and other head elements — are assumed to have been moved to a html_head.vtl file in order to illustrate its inclusion and reduce screen clutter.

<!doctype html> <html lang="en"> <head> ############################ ## HTML_HEAD (IF PRESENT) ## ############################ #if($dotTheme.htmlHead) #dotParse("${dotTheme.path}html_head.vtl") #end #set($utilClass = $dotPageContent.title.toLowerCase().replace(' ', '-')) </head> <body id="$utilClass" #if($EDIT_MODE)class="edit-mode"#end> <div class="body-wrapper"> ######################### ## HEADER (IF PRESENT) ## ######################### #if($dotThemeLayout.header) <header> #dotParse("${dotTheme.path}header.vtl") </header> #end ############# ## ALIASES ## ############# ## Main column #set($mainColumn = $dotThemeLayout.body) ## Sidebar #set($sidebar = $dotThemeLayout.sidebar) ############################### ## LEFT SIDEBAR (IF PRESENT) ## ############################### #if ($sidebar && $sidebar.location != "") #if ($sidebar.width == 'small') #set ($sidebarColumn1Span = "col-sm-2") #set ($sidebarColumn2Span = "col-sm-10") #elseif ($sidebar.width == 'medium') #set ($sidebarColumn1Span = "col-sm-3") #set ($sidebarColumn2Span = "col-sm-9") #elseif ($sidebar.width == 'large') #set ($sidebarColumn1Span = "col-sm-4") #set ($sidebarColumn2Span = "col-sm-8") #else #set ($sidebarColumn1Span = "") #set ($sidebarColumn2Span = "") #end <div class="container"> <div class="grid"> #if ($sidebar.location == "left") <div class="$sidebarColumn1Span"> ## Draw the column content $render.eval($sidebar.draw()) </div><!--/div sidebar left--> <div class="$sidebarColumn2Span"> #else <div class="$sidebarColumn2Span"> #end #end ######################################### ## ADDING THE ROWS FOR THE MAIN COLUMN ## ######################################### #if ($mainColumn.rows) #set($rowCount = 0) #foreach($row in $mainColumn.rows) #set($rowCount = $rowCount + 1) #set($rowLeftOffset = 1) ## Each row has a number of columns, using bootstrap grid layout #foreach($column in $row.columns) #if($velocityCount == 1) <section id="section-$!{rowCount}" class="section $!{row.styleClass}"> <div class="container"> <div class="grid"> #end ## Define width by the column settings #set ($currentColumnSpan = "col-lg-${column.width}") #set ($offset = 0) ## Set the bootstrap offset of each column based on the row's left offset. #if ($rowLeftOffset == $column.leftOffset) #set($columnOffset = "") #else #set($offset = $column.leftOffset - $rowLeftOffset) #set($columnOffset = "offset-lg-${offset}") #end #set($rowLeftOffset = $rowLeftOffset + $column.width + $offset) <div class="$currentColumnSpan $columnOffset $!{column.styleClass}"> ## Draw the column content $render.eval($column.draw()) </div><!--/Column--> #if($velocityCount == $row.columns.size()) </div><!--/row--> </div><!--/container--> </section><!-- /row-wrapper--> #end #end #end #end ################################ ## RIGHT SIDEBAR (IF PRESENT) ## ################################ #if ($sidebar && $sidebar.location != "") #if ($sidebar.location == "left") </div><!--/div columns--> #else </div><!--/div columns--> <div class="$sidebarColumn1Span"> ## Draw the column content $render.eval($sidebar.draw()) </div><!--/div sidebar right--> #end </div><!--/div row--> </div><!-- /container--> #end </div><!-- /body-wrapper --> ######################### ## FOOTER (IF PRESENT) ## ######################### #if($dotThemeLayout.footer) <footer> #dotParse("${dotTheme.path}footer.vtl") </footer> #end </body> </html>