Magnolia 4.5 reached end of life on June 30, 2016. This branch is no longer supported, see End-of-life policy.
This tutorial introduces the Magnolia templating mechanism. It covers creating your first page template, rendering content from the repository and creating a dialog for content entry. You also learn how a page is composed of areas and components.
The goal of templating is to render something. Rendering makes the content stored in the content management system visible and accessible to site visitors. In Magnolia you can render a page, a specific area of the page, or a component that displays editable content or performs a function.
Template gives the rendered element structure. Structure is crucial for presenting content in the correct order and for building a page layout. An example of a simple structure is headers and footers on a page. Header is at the top, footer at the bottom, and content that varies from page to page is in the middle. Templates make it possible to build such structure. And you can be confident that it remains the same across the website.
Templates also allow you to control what is rendered. You can define what components and how many are allowed in a particular area of the page. Without a template it would be difficult to keep the site design consistent.
Think of a typical corporate website. It has carefully designed branding and corporate identity: colors, fonts, images and page layout all contribute to making every page look and feel consistent and part of the whole. You want to safeguard that identity by protecting elements that should not be editable. A template allows you to define which parts of the page are editable and which are not. For example, an area template allows you to define components that are allowed in that area. If only images are allowed, there is no way for editors to introduce text into the area.
Magnolia provides three types of templates:
example.com/products/bicycles
would have at least three pages: home page, products section page, and a bicycles page.All templates need the following common properties:
.dot
file should be opened in Microsoft Word. Similarly, the type property tells Magnolia that a ftl
script should be rendered with a Freemarker renderer and a jsp
script with a JSP renderer.These are not the only properties templates can have. But they are the necessary ones.
This tutorial relies on the basic templating framework included in all versions of Magnolia. There is also a better alternative: Standard Templating Kit (STK) is a set of best practice templates for common use cases. It is an out-of-the-box solution designed to speed up the development. After going through this tutorial and understanding how the templating mechanism works, try the STK. You can adapt and modify STK templates to suit your needs.
Let's start the tutorial with the highest level template which is page. You need three things to create a page template:
Page script renders the content. The script is written in FreeMarker, JSP or a custom templating language. The script instructs the renderer where to place the content on the page and also contains placeholders for content attributes such as headings and images.
In this exercise you will save the script file in the web application's directory on the file system. This is not the only possible place. Freemarker template scripts can also be loaded from the templates
repository or from inside your module's .jar file. JSP templates can only be loaded from the file system since they require pre-compiling by the server before they can be rendered.
To create a page script:
/<CATALINA_HOME>/webapps/<contextPath>/templates
, typically this is /apache-tomcat/webapps/magnoliaAuthor/templates
.helloWorld.ftl
.Freemarker:
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title>Page Title</title> </head> <body> Hello World! </body> </html>
JSP:
<html> <head> <title>Page Title</title> </head> <body> Hello World! </body> </html>
As you can see, declaring the contentType
is unnecessary in JSP. The script is served from the /templates
folder within the webapp
, not from /docroot
, so the content type is established by the servlet. If you use JSP, set the renderType
property in the template definition to jsp
and templateScript
to /templates/helloWorld.jsp
.
The script is very simple and merely displays the text "Hello World!" on the page. Your file system should look like this.
A page definition assigns the page template a name and makes it available to the system. The name is used to reference the template. The page definition also tells the system which dialog is used to edit page properties and which script is used to render the content.
In this tutorial you will create place the page definition in the Templating module. You can find it in Configuration > /modules/templating
. This is the same module that provides the basic templating functionality.
To create a page definition:
/modules/templating
.templates
folder.templates
, create a new child folder pages
. Page definitions are always created in a folder named pages
.pages
, create a new content node helloWorld
. This is the page definition.helloWorld
, create the following data nodes:renderType
defines which renderer is used to render the template. Options are freemarker
, jsp
and stk
. Set value to freemarker
. See About scripting languages.templateScript
is a path to the corresponding page script. The script renders the content. Set value to /templates/helloWorld.ftl
.title
is the title of the template. Editors see it in the Template dropdown when they assign the page template to an actual page on the site. Set value to Hello World
.visible
makes the page template available to editors. Set value to true
.Your definitions do not have to reside in the Templating module. At an advanced level, the best practice is to create your own complete module. To place the configuration into your own module you will need to have a module descriptor that registers the module. In addition you will need a task that bootstraps configurations such as page definitions. This enables you to customize templates independent of other modules.
Freemarker is the most common templating language used with Magnolia. JSP is also popular. You can use both languages on the same site; Freemarker for some pages and JSP for others. You can even use JSP for a page and Freemarker for areas on the page (or vice versa) as long as you define a renderType
for the area separately. Otherwise the area will inherit the page's renderType
.
FreeMarker templates can be loaded from the webapp's classpath, the repository or the file system. JSP templates can be loaded only from the file system since they require pre-compiling by the server before they can be rendered. In this tutorial you find code examples in FreeMarker and JSP. Choose your code of preference.
It does not really matter where you put your template scripts as long as they are within the webapps
folder of your Magnolia instance. The path to the script file is in the template definition. This is how the system finds the script. By default, the templates
folder is in /<CATALINA_HOME>/webapps/<contextPath>/templates
. It is generally a good idea to organize your templates into folders.
To keep things simple, this tutorial does not deal with Cascading Style Sheets (CSS). Your template script can reference a CSS file if you wish. For example, to use the samples.css
file contained in the Templating Samples module from the file system, add the following line in the <head>
section of the script.
<link rel="stylesheet" type="text/css" href="${pageContext.request.contextPath}/docroot/samples/samples.css"/>
CSS files can also be loaded from the repository. You can find samples in Templating Kit > Resources.
Now the page template is defined so the system can find it and editors can assign it to pages. You also have a page script so the system knows how to render content.
To assign the page template to a page:
hello
.The result may seem a little disappointing but this is a very basic page!
Now that basic infrastructure is in place, you can start rendering content from the repository and creating dialogs for content entry.
To learn how to deal with dynamic content, assign the page a title. The title is stored in a property named title
in the repository. You can render it in the browser title bar and on the page.
To assign a title to the page:
Page title is different from template title. Page title ("Hello Magnolia World") is displayed to visitors on the page. Template title ("Hello World") is an internal title of the page template which you defined in the page definition earlier; it is displayed to editors in the Template dropdown in AdminCentral.
The page title is stored with page content and other page properties in the website
repository. You can examine these properties with the JCR Browser . Go to Tools > JCR Browser (Website). Pages, and nodes in general, can have an arbitrary number of properties.
Templating support objects are available to your script implicitly. This means that you can access content and properties stored in the repository using tags in the script. The content
object represents the current content node, in this case the Hello page. Page title is available through this object using the dot notation: content.title
. Properties are exposed as a map like cms:setNode
To display the page title on the page, edit the script.
FreeMarker:
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title>${content.title!}</title> </head> <body> <h1>${content.title!}</h1> </body> </html>
JSP:
<html> <head> <title>${content.title}</title> </head> <body> <h1>${content.title}</h1> </body> </html>
Now, refresh the Hello page. The page title "Hello Magnolia World" is displayed in the window title and in an h1 element on the page.
As you create new pages based on the Hello World template, the window title and h1 element will change dynamically to display each page's unique title.
The exclamation mark after content.title
is the Freemarker default value operator. On the right hand side of the exclamation mark you specify a default value that is substituted if the title
property does not have an actual value. This prevents the script from erroring if it encounters a null value. If you leave the substitute out, the exclamation mark alone sets the value to an empty string.
Each page element has a toolbar that editors use to edit the element's content and move it on the page. The page itself also has a toolbar that allows editors to edit page specific properties such as the title without leaving the page.
Toolbars are added to editable elements automatically. The Tag libraries is a collection of tags that render common elements such as toolbars. Use the cms.init
tag to initialize the tag library.
cms.init
tag in the head element.FreeMarker:
<html> <head> [@cms.init /] <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title>${content.title!}</title> </head> <body> <h1>${content.title!}</h1> <p>My template content</p> </body> </html>
JSP:
<%@ taglib prefix="cms" uri="http://magnolia-cms.com/taglib/templating-components/cms" %> <html> <head> <cms:init/> <title>${content.title}</title> </head> <body> <h1>${content.title}</h1> <p>My template content</p> </body> </html>
Refresh the page.
The page toolbar is a green bar at the top. It has three buttons:
The authoring system uses dialogs to edit content. A dialog is an HTML form with input fields. Editors type content into the fields and the dialog saves it in the repository. It is an agile mechanism in the sense that you can create your own dialogs or customize the standard dialogs that ship with the system.
Like template definitions, dialogs are defined with configuration. The page properties dialog is a very common dialog. You will find it on every STK page for example. It allows editors to edit the page title, navigation title and page metadata without leaving the page.
You could reuse an existing page properties dialog. However, the dialog used in STK pages is not appropriate for the Hello World page because our page will not have navigation or metadata, to keep things simple. Those dialog fields do not make sense in this example. Create a dialog that allows you to edit the page title and enter some page content instead.
Dialogs consist of tabs, which again consist of controls. Each dialog needs to have at least one tab.
To create a page properties dialog:
/modules/templating
.dialogs
.dialogs
, create a content node pageProperties
. This is the dialog definition.pageProperties
, create a content node tabText
. This is the first (and only) tab in the dialog.tabText
, create a data node controlType
and set its value to tab
.tabText
, create a data node label
and set its value to Page Properties
.tabText
, create two content nodes: title
and text
. These are the input fields.title
, create two data nodes:controlType
and set value to edit
. This is a basic text box. For other types see Controls.label
and set value to Page title
.text
, create three data nodes:controlType
and set value to edit
.label
and set value to Page text
.rows
and set value to 5
.Reference the page properties dialog from the page definition so that the system knows which dialog to open when the Properties button is clicked.
/modules/templating/templates/pages/helloWorld
.dialog
and set its value to templating:pageProperties
.The value of the dialog
node has two parts. The first part before the colon (:) is the name of the module folder where the dialog definition resides. The second part is a relative path.
<module name>:<relative path to dialog>
In this case the dialog is defined in the Templating module so the first part is templating
. The relative path always starts from inside the dialogs
folder. Since the pageProperties
dialog definition is right below the dialogs
folder, the name of the content node is enough.
templating:pageProperties
Refresh the Hello page and click Properties. The dialog is displayed.
The Page title box already has a value "Hello Magnolia World". The system read the value from the title
property in the repository. This is the same value you entered in AdminCentral earlier. Controls store their values in properties named after the control: a control named title
stores values in a title
property, a control named text
stores values in a text
property and so on.
For a complete list of properties available in dialog and control definitions, see Dialogs and Controls.
Type some text in the Page text box and save the dialog. The text is stored in a new text
property under the page. You can verify this with the JCR Browser.
To render the text you typed:
<p>
and </p>
in the body.text
property from the content object using ${content.text!
}.Freemarker:
<html> <head> [@cms.init /] <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title>${content.title!}</title> </head> <body> <h1>${content.title!}</h1> <p>${content.text!}</p> </body> </html>
JSP:
<%@ taglib prefix="cms" uri="http://magnolia-cms.com/taglib/templating-components/cms" %> <html> <head> <cms:init/> <title>${content.title}!</title> </head> <body> <h1>${content.title}</h1> <p>${content.text}</p> </body> </html>
The text you typed in the Page text box is now displayed on the page.
Pages are organized into smaller elements called areas. Areas are controllable blocks, rendered as div
elements in the generated HTML. Areas make it possible to have exact control over the page layout and over what content can be placed inside the area.
There are three types of areas. The type defines how many components editors can add inside the area.
single
area can contain one componentlist
can contain many componentsnoComponent
cannot contain any editor-added components. The system creates the components automatically.You can name your areas as you wish but some conventions exist. The main
area is typically where primary content resides. For example, on a news page the actual news story would be in the main
area. Other page components such as headers, footers, and navigation would reside in their own areas.
Areas can be defined globally for the whole site in the site definition or per page template in the template definition.
To create an area definition:
/modules/templating/templates/pages/helloWorld
.helloWorld
, create a content node areas
.areas
, create a content node main
.main
, create two data nodes:type
and set value to list
. This will create a list type area where you can add many components.templateScript
and set value to /templates/areas/mainArea.ftl
. This is a path to the area script. You will write it in a moment.Each area can define what components are available inside it. You do this by creating a list of components that editors can add into the area. Availability is an important control mechanism. It allows you to control page consistency at a very low level. When you control what components editors can add and where, you ensure that information architecture on the page does not get out of hand.
The system creates the component selector dialog automatically. When an editor selects one component that component's own dialog is displayed.
In this example we make a text block component available in the main area. The component will be configured in a moment.
To make components available in the area:
/modules/templating/templates/pages/helloWorld/areas/main
.main
, create a content node availableComponents
.availableComponents
, create a content node textBlock
.textBlock
, create a data node id
and set its value to templating:components/textBlock
.The id
property works the same way the dialog
property you saw above. It locates the component definition. The first part before the colon (:) is the name of the module folder where the component definition resides. The second part is a relative path.
<module name>:<relative path to component>
In this case the component will be defined in the Templating module so the first part is templating
. The relative path starts with a components
folder, followed by a path to the component content node. Since the textBlock
component definition will be right under the components
folder, the path is components/textBlock
.
templating:components/textBlock
To render the main
area in the page script:
cms.area
tag in the body element.name
attribute. In this case the area is main
.Freemarker:
<html> <head> [@cms.init /] <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title>${content.title!}</title> </head> <body> <h1>${content.title!}</h1> <p>${content.text!}</p> [@cms.area name="main" /] </body> </html>
JSP:
<%@ taglib prefix="cms" uri="http://magnolia-cms.com/taglib/templating-components/cms" %> <html> <head> <cms:init/> <title>${content.title}!</title> </head> <body> <h1>${content.title}</h1> <p>${content.text}</p> <cms:area name="main" /> </body> </html>
This instructs the script to render the main area. There is nothing to render yet so we need an area script.
Like pages, areas have their own scripts. The primary purpose of an area script is to render any components inside the area.
/<CATALINA_HOME>/webapps/<contextPath>/templates
, typically this is /apache-tomcat/webapps/magnoliaAuthor/templates
.areas
.areas
, create a new text file mainArea.ftl
and paste the following script in it.Freemarker:
<div id="main"> [#list components as component ] [@cms.component content=component /] [/#list] </div>
JSP:
<%@ taglib prefix="cms" uri="http://magnolia-cms.com/taglib/templating-components/cms" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <div id="main"> <c:forEach items="${components}" var="component"> <cms:component content="${component}" /> </c:forEach> </div>
The script includes two new tags:
list
is a Freemarker directive that evaluates a collection. In this case it is a collection of components inside the area. Any operations defined within the list are executed for each collection item.cms.component
renders content. The content
attribute determines what that content is, in this case a component inside the area.If you just want to render components inside the area, you don't need an area script! An area definition alone is adequate. However, if you want to render any custom HTML like the div
element in this example, then write an area script.
Save the area script and refresh the Hello page. The page now has a main area. The area toolbar shows the name of the area. Areas also have an end marker, a narrower green bar at the bottom. It helps you see the dimensions of the area. In the middle is a gray placeholder for components. You cannot add the text block component into the area yet because we have not defined it yet. Let's do that next.
Component is the smallest block of content that editors can edit, delete and move as a single unit. Think of a component as content that belongs together. When you look at a typical Magnolia page, you can identify components easily with that rule of thumb. At its simplest, a component may be just a heading and some text that belong together.
Each component can be used on several pages and in several areas. Within an area, editors choose the components to add from a list. The list is limited to display only components that are available in that particular area as defined in the availableComponents
node.
The textBlock
component is a very simple example. It renders any text entered by the editor.
To create a component definition:
/modules/templating/templates
.components
.components
, create a content node textBlock
.textBlock
, create the following data nodes:dialog
and set value to templating:textBlock
.renderType
and set value to freemarker
.templateScript
and set value to /templates/components/textBlock.ftl
. This a path to the component script.title
and set value to Text Block
. The title is displayed to editors when they select from a list of components to add.A component script renders the content of the component. For our example component the content is just some text. The script reads the text from the repository and renders it.
/<CATALINA_HOME>/webapps/<contextPath>/templates
, typically this is /apache-tomcat/webapps/magnoliaAuthor/templates
.components
.components
, create a new text file textBlock.ftl
.Freemarker:
<p> ${content.text!} </p>
JSP:
<p> ${content.text} </p>
The script renders the value of the text
property from the current content object. In this case the content object is not the Hello page node but the component node.
A component dialog allows editors to edit the content of the component. You can find many examples of component dialogs in the Standard Templating Kit. Look in Templating Kit > Dialog Definitions > /components
.
To create a component dialog:
/modules/templating/dialogs
.dialogs
, create a content node textBlock
.textBlock
, create a content node tabText
. This it first (and only) tab in the dialog. Remember: you need to define at least one tab for every dialog.tabText
, create two data nodes:controlType
and set value to tab
.label
and set value to Text Block
.tabText
, create a content node text
. This the text box control where the actual content entry happens.text
, create three data nodes:controlType
and set value to edit
.label
and set value to Component text
.rows
and set value to 5
.Now you have all that is required to add a component on the page: the component is defined, has a script and a dialog, and it is available in the main area.
To add a component on the page:
The component is rendered on the page.
Components also have toolbars. Click the containing area toolbar to see them.
Since the main area is of type list
, you can add multiple components to it. Add a few more more and change their order by dragging the component toolbar.
Component content is stored in the website
repository. Each component has a content node and properties under it. You can view them in the JCR Browser.
This introduction explained the basics of the templating mechanism.
A typical Magnolia setup consists of an author instance and one or more public instances. Until now you have been working on the author instance. Activate everything you created to the public instance.
templating
module.magnoliaPublic
webapp.Open the Hello page on the public instance and check that everything works correctly. Content should not be editable but it should be rendered in exactly the same way when you click the Preview button on the author instance.
Install the Standard Templating Kit (STK) and Templating Samples modules. You might already have the STK. Go to Magnolia Store > Installed modules to check.
STK provides a complete best-practice framework with lots of templates, dialogs and scripts. The best way to learn is by example. Look under Templating Kit in AdminCentral. STK scripts are stored in the repository, not on the file system, so you can browse and edit them in Templating Kit > Resources.