The 5.7 branch of Magnolia reached End-of-Life on December 31, 2023, as specified in our End-of-life policy. This means the 5.7 branch is no longer maintained or supported. Please upgrade to the latest Magnolia release. By upgrading, you will get the latest release of Magnolia featuring significant improvements to the author and developer experience. For a successful upgrade, please consult our Magnolia 6.2 documentation. If you need help, please contact info@magnolia-cms.com.

Template prototype is like a master page template. Anything you configure in the prototype is applied to all page templates. The prototype makes configuration efficient as you only need to do it once. For example, add an area to the prototype to make it available on all pages. You can override the prototype at the page definition level if needed.

Best practice

Put commonly used things in the template prototype. If most pages on your site have the same areas then define those areas in the prototype.

Do I need a prototype?

No, it is not necessary to use a prototype. You can configure a separate page definition for each page template instead. This works fine if you have a small number of templates that are very different from each other.

Prototype is an optional templating mechanism that offers a number of benefits:

  • Ensures uniformity across templates
  • Avoids repeating and duplicating configuration
  • Creates similar templates quickly

Prerequisites

Configuring a prototype

The prototype definition is a page template definition that must use a special class: PrototypeTemplateDefinition . You can use all the page template properties.

You can configure a prototype in:

 If you have the Multisite module (EE Pro) you can configure a different prototype for each site or use the same prototype for all sites.

Configuring a prototype in JCR

In the site definition configure the prototype in the /templates/prototype node.

Example: Prototype in the site definition in the Site app.

Node nameDescription

 
templates

 

 
prototype

 

 
areas

 

 
navigation

 

 
footer

 

 
main

 

 
htmlHeader

 

 
class

info.magnolia.my-module.definition.PageTemplateDefinition

 
templateScript

/my-module/templates/pages/main.ftl

 
renderType

site

Configuring a prototype in YAML

While it is not possible to configure a site definition completely in YAML, you can configure the prototype in YAML and reference it in the site definition. The prototype definition can reside in any module.

To configure the prototype in YAML:

  1. In the site definition in /<site name>/templates :
    1. Set the: 
      1. class property to info.magnolia.module.site.templates.ReferencingPrototypeTemplateSettings . This class allows you to use a prototype defined in YAML.
      2. prototypeId to the location of the YAML prototype definition in the <module name>:<relative path to definition> format, for example, travel-demo:pages/prototype .
  2. Remove the /prototype node, if it exists.

    Exampletravel site definition in the Site app.

    Node nameValue

     
    travel

     

     
    templates

     

     
    availability

     

     
    class

    info.magnolia.module.site.templates.ReferencingPrototypeTemplateSettings

     
    prototypeId

    travel-demo:pages/prototype

     
    theme

     

     
    i18n

     
  3. Create the prototype YAML file in the location defined in the prototypeId property and set the class property to info.magnolia.module.site.templates.PrototypeTemplateDefinition (or a subclass).


     <!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><title>Log in - Magnolia Bitbucket</title><script>
    window.WRM=window.WRM||{};window.WRM._unparsedData=window.WRM._unparsedData||{};window.WRM._unparsedErrors=window.WRM._unparsedErrors||{};
    WRM._unparsedData["com.atlassian.bitbucket.server.bitbucket-webpack-INTERNAL:user-keyboard-shortcuts-enabled.data"]="true";
    WRM._unparsedData["com.atlassian.analytics.analytics-client:programmatic-analytics-init.programmatic-analytics-data-provider"]="false";
    WRM._unparsedData["com.atlassian.plugins.atlassian-plugins-webresource-plugin:context-path.context-path"]="\u0022\u0022";
    WRM._unparsedData["com.atlassian.plugins.atlassian-clientside-extensions-runtime:runtime.atlassianDevMode"]="false";
    WRM._unparsedData["com.atlassian.bitbucket.server.bitbucket-webpack-INTERNAL:date-format-preference.data"]="\u0022\u0022";
    WRM._unparsedData["com.atlassian.bitbucket.server.feature-wrm-data:bitbucket.global.theme.data"]="false";
    WRM._unparsedData["com.atlassian.analytics.analytics-client:policy-update-init.policy-update-data-provider"]="false";
    WRM._unparsedData["com.atlassian.bitbucket.plugins.bitbucket-slack-server-integration-plugin:slack-link-error-resources.slack-link-error"]="{}";
    WRM._unparsedData["com.atlassian.bitbucket.server.feature-wrm-data:user.time.zone.onboarding.data"]="true";
    if(window.WRM._dataArrived)window.WRM._dataArrived();</script>
    <link rel="stylesheet" href="/s/c2e7fb6cb0fa3d99cfd1ed5573a5b732-CDN/1015515914/fdb5904/1h5ouvp/a93e8b54a53a5453c42e8c7cfb35551a/_/download/contextbatch/css/_super/batch.css" data-wrm-key="_super" data-wrm-batch-type="context" media="all">
    <link rel="stylesheet" href="/s/c3b4ddaba8d3dce9466950696444d252-CDN/1015515914/fdb5904/1h5ouvp/80af30e6da65e29f7038393c9f70794e/_/download/contextbatch/css/bitbucket.page.login,-_super/batch.css" data-wrm-key="bitbucket.page.login,-_super" data-wrm-batch-type="context" media="all">
    <link rel="stylesheet" href="/s/a5886b3e3e45b4adb9b9991e2cf18803-CDN/1015515914/fdb5904/1h5ouvp/f4992ec124d5384cd0091fae42c3ca97/_/download/contextbatch/css/bitbucket.layout.focused,bitbucket.layout.base,atl.general,bitbucket.internal.feature.theme,-_super/batch.css?slack-enabled=true" data-wrm-key="bitbucket.layout.focused,bitbucket.layout.base,atl.general,bitbucket.internal.feature.theme,-_super" data-wrm-batch-type="context" media="all">
    <script src="/s/2fb1765c7438a1e383facff8dd19270a-CDN/1015515914/fdb5904/1h5ouvp/a93e8b54a53a5453c42e8c7cfb35551a/_/download/contextbatch/js/_super/batch.js?locale=en-US" data-wrm-key="_super" data-wrm-batch-type="context" data-initially-rendered></script>
    <script src="/s/8fd0897a356f9fee979922482537ec9e-CDN/1015515914/fdb5904/1h5ouvp/80af30e6da65e29f7038393c9f70794e/_/download/contextbatch/js/bitbucket.page.login,-_super/batch.js?locale=en-US" data-wrm-key="bitbucket.page.login,-_super" data-wrm-batch-type="context" data-initially-rendered></script>
    <script src="/s/b26a251d5dcc697b8b37326544044035-CDN/1015515914/fdb5904/1h5ouvp/f4992ec124d5384cd0091fae42c3ca97/_/download/contextbatch/js/bitbucket.layout.focused,bitbucket.layout.base,atl.general,bitbucket.internal.feature.theme,-_super/batch.js?locale=en-US&amp;slack-enabled=true" data-wrm-key="bitbucket.layout.focused,bitbucket.layout.base,atl.general,bitbucket.internal.feature.theme,-_super" data-wrm-batch-type="context" data-initially-rendered></script>
    <meta name="application-name" content="Bitbucket"><link rel="shortcut icon" type="image/x-icon" href="/s/1015515914/fdb5904/1h5ouvp/1.0/_/download/resources/com.atlassian.bitbucket.server.bitbucket-webpack-INTERNAL:favicon/favicon.ico" /><link rel="search" href="https://git.magnolia-cms.com/plugins/servlet/opensearch-descriptor" type="application/opensearchdescription+xml" title="Bitbucket code search"/></head><body class="aui-page-focused aui-page-focused-small aui-page-size-small bitbucket-theme user-login"><script type="text/javascript">require('@bitbucket/internal/feature/theme').init();</script><ul id="assistive-skip-links" class="assistive"><li><a href="#content">Skip to content</a></li></ul><div id="page"><!-- start #header --><header id="header" role="banner"><section class="notifications"></section><nav class="aui-header aui-dropdown2-trigger-group" aria-label="site"><div class="aui-header-inner"><div class="aui-header-before"><button class=" aui-dropdown2-trigger app-switcher-trigger aui-dropdown2-trigger-arrowless" aria-controls="app-switcher" aria-haspopup="true" role="button" data-aui-trigger href="#app-switcher"><span class="aui-icon aui-icon-small aui-iconfont-appswitcher">Linked Applications</span></button><div id="app-switcher" class="aui-dropdown2 aui-style-default" role="menu" hidden data-is-user-admin="false" data-is-switcher="true"><div class="app-switcher-loading">Loading&hellip;</div></div></div><div class="aui-header-primary"><span id="logo" class="aui-header-logo bitbucket-header-logo"><a href="https://git.magnolia-cms.com"><img src="/s/1015515914/fdb5904/1h5ouvp/1.0/_/download/resources/com.atlassian.bitbucket.server.bitbucket-webpack-INTERNAL:bitbucket-logo/images/logo/bitbucket.svg" alt="Bitbucket"/></a></span><ul class="aui-nav"></ul></div><div class="aui-header-secondary"><ul class="aui-nav"><li class=" help-link"title="Help"><a class=" aui-dropdown2-trigger aui-dropdown2-trigger-arrowless" aria-controls="com.atlassian.bitbucket.server.bitbucket-server-web-fragments-help-menu" aria-haspopup="true" role="button" tabindex="0" data-aui-trigger><span class="aui-icon aui-icon-small aui-icon-small aui-iconfont-question-filled">Help</span></a><div id="com.atlassian.bitbucket.server.bitbucket-server-web-fragments-help-menu" class="aui-dropdown2 aui-style-default" role="menu" hidden data-aui-dom-container="body"><div class="aui-dropdown2-section help-items-section"><ul class="aui-list-truncate" role="presentation"><li role="presentation"><a href="https://docs.atlassian.com/bitbucketserver/docs-0814/Bitbucket+Data+Center+and+Server+documentation?utm_campaign=in-app-help&amp;amp;utm_medium=in-app-help&amp;amp;utm_source=stash" title="Go to the online documentation for Bitbucket" data-web-item-key="com.atlassian.bitbucket.server.bitbucket-server-web-fragments:general-help">Online help</a></li><li role="presentation"><a href="https://www.atlassian.com/git?utm_campaign=learn-git&amp;utm_medium=in-app-help&amp;utm_source=stash" title="Learn about Git commands &amp; workflows" data-web-item-key="com.atlassian.bitbucket.server.bitbucket-server-web-fragments:learn-git">Learn Git</a></li><li role="presentation"><a href="/getting-started"class="getting-started-page-link" title="Overview of Bitbucket features" data-web-item-key="com.atlassian.bitbucket.server.bitbucket-server-web-fragments:getting-started-page-help-link">Welcome to Bitbucket</a></li><li role="presentation"><a href="/#"class="keyboard-shortcut-link" title="Discover keyboard shortcuts in Bitbucket" data-web-item-key="com.atlassian.bitbucket.server.bitbucket-server-web-fragments:keyboard-shortcuts-help-link">Keyboard shortcuts</a></li><li role="presentation"><a href="https://go.atlassian.com/bitbucket-server-whats-new?utm_campaign=in-app-help&amp;utm_medium=in-app-help&amp;utm_source=stash" title="Learn about what&#39;s new in Bitbucket" data-web-item-key="com.atlassian.bitbucket.server.bitbucket-server-web-fragments:whats-new-link">What&#39;s new</a></li><li role="presentation"><a href="https://go.atlassian.com/bitbucket-server-community?utm_campaign=in-app-help&amp;utm_medium=in-app-help&amp;utm_source=stash" title="Explore the Atlassian community" data-web-item-key="com.atlassian.bitbucket.server.bitbucket-server-web-fragments:community-link">Community</a></li><li role="presentation"><a href="/about" title="About Bitbucket" data-web-item-key="com.atlassian.bitbucket.server.bitbucket-server-web-fragments:about">About</a></li></ul></div></div></li><li class=" alerts-menu"title="View system alerts"><a href="#alerts" id="alerts-trigger"class="alerts-menu" title="View system alerts" data-web-item-key="com.atlassian.bitbucket.server.bitbucket-server-web-fragments:global-alerts-menu-item">Alerts</a></li></ul></div></div> <!-- End .aui-header-inner --></nav> <!-- End .aui-header --></header><!-- End #header --><!-- Start #content --><section id="content" role="main" tabindex="-1" data-timezone="-120" ><div class="aui-page-panel content-body" ><div class="aui-page-panel-inner"><main role="main" id="main" class="aui-page-panel-content" ><h2>Log in</h2><form class="aui top-label prevent-double-submit " action="/j_atl_security_check" method="post" accept-charset="UTF-8"><div class="field-group"><label for="j_username" >Username</label><input class="text long-field" type="text" id="j_username"  name="j_username"  autofocus accesskey="u"/></div><div class="field-group"><label for="j_password" >Password</label><input class="text long-field" type="password" id="j_password"  name="j_password"  accesskey="p"/></div><div class="aui-group"><fieldset class="group checkbox"><div class="checkbox"><input class="checkbox" type="checkbox" id="_atl_remember_me"  name="_atl_remember_me"  checked="checked"  accesskey="r"/><label for="_atl_remember_me" >Keep me logged in</label></div></fieldset></div><div class="aui-group"><input name="next" type="hidden" value="/projects/MODULES/repos/demo-projects/raw/community/magnolia-travel-demo/src/main/resources/travel-demo/templates/pages/prototype.yaml?at=refs%2Fheads%2Fmaster"/><input name="queryString" type="hidden" value="next=%2Fprojects%2FMODULES%2Frepos%2Fdemo-projects%2Fraw%2Fcommunity%2Fmagnolia-travel-demo%2Fsrc%2Fmain%2Fresources%2Ftravel-demo%2Ftemplates%2Fpages%2Fprototype.yaml%3Fat%3Drefs%252Fheads%252Fmaster"/><input class="aui-button aui-button-primary" type="submit" id="submit"  name="submit"  value="Log in" accesskey="s"/><a id="forgot" class="aui-button aui-button-link"  name="forgot" href="/passwordreset" autocomplete="off" tabindex="0">Unable to access your account?</a></div></form></main></div></div></section><!-- End #content --><!-- Start #footer --><footer id="footer" role="contentinfo"><section class="notifications"></section><section class="footer-body"><ul><li data-key="footer.license.free.eval">Git repository management powered by a free <a href="https://www.atlassian.com/software/bitbucket/">Atlassian Bitbucket</a> evaluation license</li></ul><ul><li>Atlassian Bitbucket <span title="fdb5904e84ca72e8d62953007cf5cf6cfcdfebd8" id="product-version" data-commitid="fdb5904e84ca72e8d62953007cf5cf6cfcdfebd8" data-system-build-number="fdb5904"> v8.14.0</span></li><li data-key="footer.links.documentation"><a href="https://docs.atlassian.com/bitbucketserver/docs-0814/Bitbucket+Data+Center+and+Server+documentation?utm_campaign=in-app-help&amp;utm_medium=in-app-help&amp;utm_source=stash" target="_blank">Documentation</a></li><li data-key="footer.links.jac"><a href="https://jira.atlassian.com/browse/BSERV?utm_campaign=in-app-help&amp;utm_medium=in-app-help&amp;utm_source=stash" target="_blank">Request a feature</a></li><li data-key="footer.links.about"><a href="/about">About</a></li><li data-key="footer.links.contact.atlassian"><a href="https://www.atlassian.com/company/contact?utm_campaign=in-app-help&amp;utm_medium=in-app-help&amp;utm_source=stash" target="_blank">Contact Atlassian</a></li></ul><div id="footer-logo"><a href="https://www.atlassian.com/" target="_blank">Atlassian</a></div></section></footer><!-- End #footer --></div><script>require('bitbucket/internal/layout/base/base').onReady(null, "Magnolia Bitbucket" ); require('bitbucket/internal/widget/keyboard-shortcuts/keyboard-shortcuts').onReady();</script><script type="text/javascript">require('bitbucket/internal/page/login/login').onReady();</script></body></html>

Prototype template properties

Prototype is a TemplateDefinition which means it supports the same properties as page definition, including common template properties. However, typically you don't use them all. Here are the typical use cases:

areas

optional

Common areas such as footer or navigation that are used on most pages. Each child node is an area definition. Define the areas in the prototype so you don't have to repeat them in each page definition.

templateScript

optional

The templateScript property allows you to define a common page template script for the whole site. For a page to inherit its template script from the prototype you must also define renderType=site in the template definition.

A page template script typically starts with an opening <html> element and ends with the closing </html> element when rendering HTML output. See main.ftl in the Travel Demo.

Example: The travel demo demo defines a template script in the prototype. This common script is used for all pages. It is also the reason why no YAML page definition in the demo has an explicit templateScript property. The page definitions set renderType=site to inherit the script from the prototype, see home.yaml for example.

class

optional

A custom definition class which extends ConfiguredTemplateDefinition, an implementation of TemplateDefinition. You only need a custom class if you want to add your own nodes and properties in the prototype. Implement corresponding methods to operate on those properties in the definition class.

Example: PageTemplateDefinition (Git) is a custom definition class used in the Travel Demo. It adds support for a jsFiles node which allows you to define JavaScript files in the prototype the same way a theme defines them.

Merging a prototype with a page definition

When a user requests a page Magnolia merges the prototype with a page definition. The result is a merged template definition which is then used to render the page.

The merged definition is virtual. You won't find its configuration anywhere. It is created dynamically at the time of rendering. However, you can access the merged definition in a template script using the  def rendering context object .

Example: Merging a prototype (configured in JCR) with a home page definition (configured in YAML)

  1. Prototype defines the main area type as list . Page definition adds availableComponents for the area. The result contains both.
  2. Prototype defines a templateScript . Page definition says nothing about script. The result is that the home page uses the script defined in the prototype.
  3. Prototype defines the footer area as not editable. Page definition overrides this decision by allowing footer editing on the home page. The result is that the footer can be edited on the home page only.


Before mergeAfter merge

Prototype:

Node nameDescription

 
prototype

 

 
areas

 

 
footer

 

 
availableComponents

 

 
textImage

 

 
id

mtk:components/textImage

 
linkList

 

 
id

mtk:components/linkList

 
editable

false

 
type

list

 
main

 

 
type

list

 
templateScript

/example/templates/pages/main.ftl

Page template definition:

home.yaml
renderType: site
areas:
  footer:
    editable: true
  main:
    availableComponents:
      textImage:
        id: mtk:components/textImage
      teaserInternal:
        id: mtk:components/teaser

Merged template definition:

Node nameDescription

 
prototype

 

 
areas

 

 
footer

 

 
availableComponents

 

 
textImage

 

 
id

mtk:components/textImage

 
linkList

 

 
id

mtk:components/linkList

 
editable

true

 
type

list

 
main

 

 
availableComponents

 

 
textImage

 

 
id

mtk:components/textImage

 
teaser

 

 
id

mtk:components/teaser

 
type

list

 
templateScript

/example/templates/pages/main.ftl

12 Comments

  1. I'm trying to use a yaml prototype with a page template definition but I'm missing something thus getting an error message on trying to edit the home page.

    Here are the related yaml files and the error messages: https://pastebin.com/4YYatYd5

    Here the site definition:

     

    1. Can you enlarge that screenshot in width?  I'd like to see the values of

      • /capitaly/templates@class
      • /capitaly/templates@prototypeId


      1. Christoph Meier, sure:

        • class: info.magnolia.module.site.templates.ReferencingPrototypeTemplateSettings
        • prototypeId: presentation:pages/main

        and actually there is a thing with which I"m not sure if is right in my prototype main.yaml file I have a `dialog: mte:pages/pageProperties` It is a CE bundle but until changing it to be in accordance with the samples here it has its own dialog file created with the cli. But both ways it gives the template error: No template script defined for the template definition 

        [info.magnolia.rendering.template.configured.ConfiguredTemplateDefinition@2ea44d02[id=presentation:pages/home,name=home,renderType=site,description=<null>,title=<null>,templateScript=<null>,autoGeneration=info.magnolia.rendering.template.configured.ConfiguredAutoGeneration@1558bcc4[content={},generatorClass=<null>]

        Have found a trace of others having this same issue, and Stef te Winkel 's proposed solution was: 

        - using class info.magnolia.module.site.templates.ReferencingPrototypeTemplateSettings in Site Module
             that's right

        - using enableAll true with availability and empty templates content node
             I don't have enableAll as neither has this documentation

        - using class: info.magnolia.module.site.templates.PrototypeTemplateDefinition and renderType:site in prototype template
             that's right

        - using class: info.magnolia.module.site.templates.PageTemplateDefinition in Page template definition with renderType: site
             I wasn't using class as the home.yaml here doesn't uses it neither, tried with it, didn't make difference
        - when switching from JCR setup to Yaml setup, don't forget to delete the Prototype node
             I wasn't using JCR setup, no prototype node

        So I still couldn't solve it.


        1. Obeliksz  - Note that you will have a bigger audience when asking questions at the Magnolia google groups

          1. Right, have posted there.

        2. I'm confused here. The CE bundle only allows for a single site definition. So having the node capitaly would be incorrect.

          1. Please see the comments on Site definition , where my question was if there is only one (the first) content node it will be considered to contain the site definition... So having a top node is error if not in multisite mode?

            1. Exactly, when the multisite module is not installed there is no need for that top node. This node config:/modules/site/config/site is your top level node. Configure off of that.

              1. first I tried to just move every node out of `capitaly` and delete the `capitaly` node, that way no template was found, then moved all the nodes inside a `site` content node, so no I have the same error as I had before. I guess it doesn't matter what name it has that single top content node - but it did cause some confusion for me too.

                1. If you are using CE it expects to find the site configuration off the site node. It must be called site. If you configure another node, such as capitaly, in between the site node and your configuration the system will not load the configuration. Have a look here. You can see the path is mentioned there.

                  If you are still having an issue, I would say it's related to template availability. 

                  1. Richard Gange I've moved the items from the config:/modules/site/config/site/capitaly cn into the config:/modules/site/config/site cn and this way it does not see any page definition: when going to the pages app it alerts that none are found. When putting them inside the cn config:/modules/site/config/site/capitaly or config:/modules/site/config/site/site it works. Revisiting the source code that you've linked it should be working from config:/modules/site/config/site as you said... weird issue.

                    In both case in the resource files app I see my light module and its template files to be registered.

                    1. Have started it all over from scratch and it works now...