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>