Creating the Theme's Template VTL
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 -->
#endAt 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")
#endIncluding the Header#
#if($dotThemeLayout.header)
#dotParse("${dotTheme.path}header.vtl")
#endIncluding the Footer#
#if($dotThemeLayout.footer)
#dotParse("${dotTheme.path}footer.vtl")
#endExample: 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>