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:
| Property | Data |
|---|---|
$dotTheme.title | The theme's name, drawn from its folder. |
$dotTheme.path | The directory containing the theme — e.g. /application/themes/THEME_NAME/. |
$dotTheme.htmlHead | true or false depending on whether the folder contains html_head.vtl. |
$dotTheme.templatePath | The path to the template.vtl file asset. |
The Big Picture: $dotThemeLayout#
$dotThemeLayout contains various layout properties.
| Property | Data |
|---|---|
$dotThemeLayout.title | The title of the template or custom layout. |
$dotThemeLayout.header | true or false depending on the template or layout's settings. |
$dotThemeLayout.footer | true or false depending on the template or layout's settings. |
$dotThemeLayout.body | Contains body layout data; see below. |
$dotThemeLayout.sidebar | Contains 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:
| Property | Data |
|---|---|
$column.left | A number from 0 to 11, indicating horizontal position based on $mainColumn's 12 divisions, counting from the left. |
$column.leftOffset | Same as $column.left, but indexed from 1 to 12 instead of 0 to 11. |
$column.width | A value from 1 to 12, indicating the column's width in terms of the number of $mainColumn's 12 horizontal divisions used. |
$column.widthPercent | Similar to .width, but represented as a percentage of the $mainColumn. |
$column.styleClass | Lists HTML class attributes assigned through the layout editor. |
$column.preview | Equivalent 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.containers | Contains additional object data; see below. |
Each $container in $column.containers possesses:
| Property | Data |
|---|---|
$container.identifier | A reference assigned to each element on the basis of its publishing bundle. |
$container.uuid | An index number to distinguish between elements with the same identifier. |
The Sidebar: $dotThemeLayout.sidebar or $sidebar#
$sidebar contains the following properties:
| Property | Data |
|---|---|
$sidebar.width | A value of small, medium, or large, as selected in the template editor. |
$sidebar.widthPercent | A percentage of the viewport space, corresponding to $sidebar.width — either 20, 30, or 40. |
$sidebar.location | Either left or right, as defined in the template editor. |
$sidebar.preview | Equivalent 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.containers | Contains 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>