Velocity Template

Overview

The Velocity Template service allows you to create and upload velocity templates that can be rendered later by making a simple REST call. The service provides flexible template management using Velocity-based templates. It also allows you to specify placeholders inside the template for population with custom data later.

Use the Velocity Template service to:

  • Create and manage templates for any use case.
  • Render templates using the Velocity Template Language.
  • Create multiple variants of Velocity templates that you can use, for example, for different languages and locations.

An example use case for the Velocity template service is to create and render a template for an order confirmation email to a customer that lists their order details in a specified language.

Velocity Template Service uses Velocity Engine version 1.6.x and Velocity Tools version 2.0.x.


API Reference

/{tenant}/templates

Manages the templates for the rendering.

/{tenant}/templates

get

Returns a list with all of the templates and their metadata for a given project. The caller must have the hybris.velocitytemplate_view, hybris.velocitytemplate_manage or hybris.velocitytemplate_admin scope assigned. The hybris.velocitytemplate_admin will be able to view all of its clients tempalates. This method uses a paging mechanism. If no paging parameters are provided, the default values are used.

post

Creates a new empty template by specifying its code and optionally a name and/or a description The caller must have the hybris.velocitytemplate_manage or hybris.velocitytemplate_admin scope assigned.

/{tenant}/{client}/templates

Manages templates of a particular client.

/{tenant}/{client}/templates

get

Returns a list with all of the templates and their metadata for a given project and client. The caller must have the hybris.velocitytemplate_view, hybris.velocitytemplate_manage or hybris.velocitytemplate_admin scope assigned. This method uses a paging mechanism. If no paging parameters are provided, the default values are used.

/{tenant}/{client}/templates/{code}

Manages a single template for rendering.

get

Retrieves the specified template metadata including a list of its variants. The caller must have the hybris.velocitytemplate_view, hybris.velocitytemplate_manage or hybris.velocitytemplate_admin scope assigned.

put

Updates a template definition. The caller must have the hybris.velocitytemplate_manage or hybris.velocitytemplate_admin scope assigned. In case the template contains any variants, they will not be removed.

delete

Deletes a template. The caller must have the hybris.velocitytemplate_manage and be the template owner or must have the hybris.velocitytemplate_admin scope assigned.

/{tenant}/{client}/templates/{code}/variants/{variant}

Manages a velocity template variant, for example one of several localized variants.

post

Creates the variant for the template if it does not already exist. If a template with the specified code does not exist it also automatically created. The caller must have the hybris.velocitytemplate_manage or hybris.velocitytemplate_admin scope assigned. Content over 50MB cannot be uploaded.

put

Replaces the existing variant of the template. The caller must have the hybris.velocitytemplate_manage or hybris.velocitytemplate_admin scope assigned. Content over 50MB cannot be uploaded.

get

Retrieves the requested (unrendered) variant of this template. The caller must have the hybris.velocitytemplate_view, hybris.velocitytemplate_manage or hybris.velocitytemplate_admin scope assigned.

delete

Deletes a template variant, and unassigns it from the template. The caller must have the hybris.velocitytemplate_manage assigned and be the template owner or have the hybris.velocitytemplate_admin scope assigned.

/{tenant}/{client}/templates/{code}/render

post

Return the requested template variant or a fallback variant, rendered using the given attributes. The variant in the request's variant field will be first searched to be used for rendering. If it is not found following attempts will be made to find the variants specified in the fallbackVariants field in the specified order. The attributes must be given as JSON properties of the attributes field of the request's JSON body and are passed as Velocity variables while rendering the chosen template variant. The caller must have the hybris.velocitytemplate_manage assigned and be the template owner or have the hybris.velocitytemplate_admin scope assigned.


Using Velocity Templates

The Velocity Template service renders templates using the Velocity Template Language, also known as VTL, or Velocity for short. This allows for simple variable replacement as well as more advanced templating techniques like string operations, numeric operations, conditionals, and loops.

For more information about the Velocity Template Language, see the Velocity User Guide.

Variables available in Velocity templates

In Velocity templating, Velocity variables pass values to Velocity templates. Because Velocity itself is implemented in Java, the available variable types are modeled closely after Java types. Velocity variables can hold any of these types:

  • Simple types, such as strings and numbers
  • Collection types, such as lists and maps
  • Arbitrary Java objects, which can store data and provide additional functionality for templating

To avoid namespace cluttering, the Velocity Template service only uses a small number of top-level Velocity variables. Currently, these top-level variables are data, mailContext, and tools. Each of these variables is a map that provides access to specific values, as described in the section, The data variable.

The data variable

The top-level variable data. aliased to d, holds the values of all defined attributes that the caller passes when invoking the Velocity template. Assuming the template defines the attributes foo and bar, these VTL expressions can access their respective values:

  ${data.foo}
  ${d.foo}
  ${data.bar}
  ${d.bar}

Each defined attribute can pass arbitrary JSON values to the Velocity template. This is not limited to strings or numbers. It also includes more complex JSON objects and JSON arrays. All of these JSON types map to Velocity types, which makes it easy to access structured data in your Velocity template.

This table includes example data passed in a defined attribute called val. It summarizes the mapping from JSON to Velocity:

JSON typeJSON exampleJava / Velocity typeVelocity access example
string"YaaS we can!"String${d.val}
${d.val.length}
number-3.14Double${d.val}
${d.val.isInfinite()}
true, falsetrueBoolean${d.val}
nullnullnull${d.val}
object{
"sku" : "1337",
"name" : "Leet"
}
Map${d.val}
${d.val.sku}
${d.val.name}
${d.val["name"]}
${d.val.keys}
array[1, "two", 3.0]List${d.val}
{$d.val[0]}
${d.val[2]}
${d.val.size()}

JSON objects and JSON arrays can be nested within each other. You can access their contents in your Velocity template the same way. For more detailed examples, see the advanced templating tutorial on Leveraging Structured Data.

The tools variable

The top-level variable tools, aliased to t, exposes useful Velocity Tools. The current version of the exposed Velocity Tools is v2.0.

These Velocity Tools are available:

  • ${tools.number}:


    The NumberTool provides number-formatting functionality with internationalization support.
    The exposed NumberTool uses en_US as its default locale.


    Example 1: For formatting a number use:

    ${tools.number.format(1142.66666666666666)
    

    For default locale 'en_US' the output will be: 1,142.667


    Example 2: For formatting a number using a custom format use:

    ${tools.number.format("#.00", 1142.66666666666666)
    

    For default locale 'en_US' the output will be: 1142.67


    Example 3: For formatting a number with a currency use:

    ${tools.number.format("¤#.00", 1142.66666666666666)
    

    For default locale 'en_US' the output will be: $1142.67


  • ${tools.convert}:


    The ConversionTool provides explicit data-type conversion functionality.
    The exposed ConversionTool uses en_US as its default locale,
    and it uses yyyy-MM-dd'T'HH:mm:ss.SSSXXX as its default dateFormat.

    While the date format yyyy-MM-dd'T'HH:mm:ss.SSSXXX might seem a little bulky, it provides good compatibility with ISO 8601 and RFC 3339. Also, it is less ambiguous than most natural-language date formats and is well-suited for data transfer and programmatic processing. For more information, see Date and Time Attributes.


    Example 1: For obtaining a locale instance from a locale string use:

    $tools.convert.toLocale("es_ES")
    


    Example 2: You can use the 'toLocale' method in combination with various other Velocity Tools. For formatting a number with a currency and using the locale stored as a string in the 'myLocale' render data attribute, you can write the following:

    ${tools.number.format("¤#.00", 1142.66666666666666, $tools.convert.toLocale(${data.mylocale}))}
    

    If the attribute mylocale contains 'es_ES' the output will be: €1142,67.

  • ${tools.date}:


    The DateTool provides date and time formatting functionality with internationalization support.
    The exposed DateTool uses en_US as its default locale,
    Java's short date format as its default format,
    and GMT as its default timezone.


    Example 1: To output the current date in the default locale format write:

    ${tools.date}
    


    Example 2: To output the current date in the yyyy-MM-dd'T'HH:mm:ss.SSSXXX (ISO 8601) format write:

    ${tools.date.get("yyyy-MM-dd'T'HH:mm:ss.SSSXXX")}
    


  • ${tools.esc}:


    The EscapeTool provides escaping functionality such as HTML and URL escaping.


    Example: To escape html code for avoid running javascript code write:

    $tools.esc.html("\t<!@#$%^&*(()<script>alert(`boom`);</script>{{\b")
    

    The escaped output will be:

    &lt;!@#$%^&amp;*(()&lt;script&gt;alert(`boom`);&lt;/script&gt;{{
    
  • Overriding the locale:

    To override the locale used for initializing the Velocity Tools, set the locale attribute of the render request.

    For example:

    • Method: POST
    • Request URL: https://api.beta.yaas.io/hybris/velocity-template/v1/{project}/{client}/templates/myTemplate/render
    • Content Type: application/json
    • Request Body:
    {
      "variant": "myLocalizedContent",
      "locale" : "de_DE" 
    }
    

    The set locale is also be available in the template as a string in the following variable:

      ${tools.locale}
    

For more examples on how to use these tools in the context of the Velocity Template service, see the advanced templating tutorials on leveraging structured data and securing your templates.

Velocity template restrictions

In addition to plain access to Velocity variables, the Velocity Template service supports most other features of the Velocity Template Language. Therefore, you can utilize Velocity directives such as #set, #if, #foreach, #evaluate, and #define to structure your Velocity template code.

However, there are some Velocity features that the Velocity Template service disregards for security reasons. These are:

  • Inclusion Directives:
    The Velocity Template service ignores the Velocity directives #include and #parse. When found in a Velocity template, they always evaluate to an empty string.
  • Java Reflection API:
    A Velocity template can call arbitrary Java methods on all Velocity variables. However, methods that provide access to the Java Reflection API are explicitly blocked. In particular, you cannot use the getClass() method and related functionality. Attempts to do so evaluate to null.


Template Localization

The Velocity Template service supports maintaining several variants of each of your templates. You can use this functionality to conveniently manage multiple language variants for your templates.

Managing localized templates

The Velocity Template service can manage multiple variants of the templates. You can use this functionality to add localization to your templates.

To add new localizations, you can issue PUT requests to this URL:

https://api.beta.yaas.io/hybris/velocity-template/v1/{project}/{client}/templates/{code}/variants/{variant}

In this case, the {variant} suffix is a locale identifier, which is a formal reference to a natural language, dialect, or similar variant. Examples of valid locale identifiers are pl (Polish), en-US (American English), or de-AT (Austrian German). A PUT request to these URLs uploads a body template for those locales:

https://api.beta.yaas.io/hybris/velocity-template/v1/{project}/{client}/templates/{code}/variants/pl
https://api.beta.yaas.io/hybris/velocity-template/v1/{project}/{client}/templates/{code}/variants/en-US
https://api.beta.yaas.io/hybris/velocity-template/v1/{project}/{client}/templates/{code}/variants/de-AT

Render with different variants of Velocity templates

When rendering based on a Velocity template with multiple variants, you can specify which variants to use, as well as the fallback order of those variants. For example, to use the template with the locale variant de-AT for rendering the template, include the JSON property "variants" : ["de-AT"] in the render request.

There are fallback mechanisms if no template is found for the desired locale variant. The Velocity Template service first tries to find a template for the variants that are specified in the variant request attribute, and if this variant is not found, the service tries to find the variants specified in the fallbackVariants array attribute, in the order they appear there. If the service cannot find a template that matches the first variant fallback, it tries to use the second variant, and so forth. If there is no template for the requested variant or any of the fallback variants, the render request returns a 404 response.

For example, if you request to render a Velocity template with the variant "de-AT" and the fallback variant ["de-DE"], the Velocity Template service performs these checks:

  1. Is there a template with variant "de-AT"? If yes, use it.
  2. Is there a template with variant "de-DE"? If yes, use it.
  3. If none of these variants exist, return a 404 response.


Securing The Templates

Velocity templates enable injecting specific values when rendering a template. For more information about creating Velocity templates, refer to the Create Your Template tutorial.

This topic describes how Velocity template authors can make sure that all dynamically-created HTML outputs of a Velocity template are sanitized. This helps to prevent Cross-Site Scripting (XSS) attacks and makes sure that attribute values passed into template variants do not harm the user of the rendered template. Luckily, the Velocity Template service and Velocity itself support this sanitizing, which helps to protect your reputation, as well.

Sanitize your outputs with the Velocity EscapeTool

The key to sanitizing output in the Velocity Template service is Velocity EscapeTool, which is always available in the ${tools.esc} variable of the Velocity context. It provides methods to escape HTML code and URL parts, amongst other functionality.

When templating HTML content, pass all your outputs through the html method of the Escape Tool. For instance, instead of just writing ${data.customerName}, use the syntax shown:

${tools.esc.html(${data.customerName})}

Moreover, when you want to construct a URL dynamically in your Velocity template, use the url method of the Escape Tool. For instance, when adding a coupon code to a URL, do not use ${data.couponCode}. Instead, construct your URL like this example:

https://virtually4free.example.net/?couponCode=${tools.esc.url(${data.couponCode})}

When applying these principles to the body template for win notification emails, you might end up with something like this:

<html>
<body>
  <h1>Win notification!</h1>
  <p>Dear ${tools.esc.html(${data.customerName})},</p>
  <p>
     Thank you for participating in our super-fabulous new customer competition!
     You're one of the lucky winners!
  </p>
  <p>
    We hereby present you with your coupon worth
    <strong>${tools.esc.html(${data.couponValue})}</strong>,
    to make your next purchase at the Virtually-4-Free shop even cheaper.
    To redeem your coupon, simply enter the following coupon code during checkout:
    <div class="coupon">${tools.esc.html(${data.couponCode})}<div>
  </p>
  #if( ${data.products} && ${data.products.size()} > 0 )
    <p>
      Or, how about you check out one of our ${data.products.size()} latest products right now?
    </p>
    <ul>
    #foreach( $product in ${data.products} )
      #set( $createdAt = ${tools.convert.toDate(${product.createdAt})} )
      <li>
        <strong>
          <a href="https://virtually4free.example.net/products/${tools.esc.url(${product.sku})}?couponCode=${tools.esc.url(${data.couponCode})}">
            ${tools.esc.html(${product.name})}
          </a>
        </strong>
        <br>
        New since ${tools.date.format("EEEEE MMMMM d, yyyy", ${createdAt})} and recommended by 
        ${tools.number.percent(${product.score})} of our customers :-)
        <br>
        <em>Only ${tools.number.format("¤#.00", ${product.price})}!</em>
      </li>
    #end
    </ul>
  #else
    <p>
      Or, how about starting your shopping spree at 
      <a href="https://virtually4free.example.net/?couponCode=${tools.esc.url(${data.couponCode})}">Virtually-4-Free</a>,
      right now?
    </p>
  #end
</body>
</html>

After uploading this secured template, you can test it by rendering a template that contains malicious data. For example, you can pass a customerName attribute value that contains undesirable HTML code. The HTML displays as-is and does not break the overall HTML structure of your Velocity template.

Is it ever OK to omit escaping?

When looking at the secured template shown above, you might notice that it does not apply HTML escaping in all situations. Here are some situations and examples.

Predictable outputs of Velocity Tool methods

For instance, the expression ${tools.number.percent(${product.score})} does not strictly require additional escaping in HTML. Its output is always a percentage, for example 98%, which never contains any HTML special characters.

That said, it is good practice to apply HTTP escaping even in such situations:

${tools.esc.html(${tools.number.percent(${product.score})})}

While that expression is slightly longer, it makes your Velocity template code more robust in case of future changes. Also, there might be locales that use unusual characters as currency symbols, for number formatting, or for date formatting. So try to apply proper output sanitization all the time, even when you think it is not necessary.

Preformatted HTML values

Another situation when you should not apply HTML escaping is when outputting the value of a defined attribute that is intended to contain preformatted HTML code. Here it is the responsibility of the template renderer, that is, the YaaS service or client that triggers the render request, to ensure that the HTML does not contain any malicious code.

The Velocity Template service does not formally distinguish between preformatted HTML and other inputs. Such situations are subject to informal agreements between the template author and potential template renderers. As a template author, you might want to use the description of your template object to state that your Velocity template expects preformatted HTML in certain defined attributes.


Introduction to Tutorials

This tutorial demonstrates how to prepare and render templates.

In this tutorial, you will render a template for sending notification emails to contest winners using a fictional storefront called Virtually-4-Free as the basis for all examples shown.

The template in this tutorial uses HTML formatting so that you can use your corporate design in the email. Over time, you will send similar emails to numerous winners. You don't want to do all the repetitive formatting work each time, but you do want to customize each email for the individual contest winners. The email should contain a personalized salutation as well as the prize itself: a coupon that can be redeemed at your example shop.

Basic tutorial

The first three parts of this tutorial focus on getting you started quickly. Perform them in the order indicated. They cover all the steps that are necessary to send basic win-notification emails.

  1. Create your templates:

    The templates for the email body and subject contain HTML code including a few snippets of Velocity template code.

  2. Upload your email template:

    Because email templates are created in the Velocity Template service, you must upload the two Velocity templates you created in the previous step to the Velocity Template service.

  3. Render a template:

    When rendering a template, specify which template to use, and personalize the rendered contents by passing data into the template.

Advanced templating tutorial

The subsequent tutorials build on the basics and introduce several more advanced topics. Perform them in the order indicated.

  1. Leverage Structured Data:

    Pass JSON data when rendering templates, and process the data using Velocity directives and tools.

  2. Secure your templates:

    Use the Velocity Template Tool to sanitize your outputs and prevent cross-site scripting.

Preparations

You can execute all the steps in this tutorial using the API Console of the Velocity Template service. For more information about the API Console, see First Steps with APIs. In particular, you need to know how to:

  1. Set up a project. See Getting Started Guide > Set Up a Project.
  2. Obtain an access token for an authenticated client of your project. See Getting Started Guide > Get an Access Token.
  3. Make API calls using that access token. See Getting Started Guide > Make a First API Call.

This tutorial uses the placeholders listed. You can replace these placeholders with values from your own project.

DetailPlaceholderExample Value
Project ID{project}virtually4free
Application's/Service's YaaS Client Identifier{client}hybris.order


Create Your Template

This section of the tutorial shows you how to write the contents of the subject and body templates that comprise your email template.

Step 1: Prepare the template for the email subject

In this use case, you are rendering notification emails to the winners of the Virtually-4-Free storefront contest. The subject of the emails should be customizable for the individual winners. This customization is done using simple Velocity code in the template.

To create the template for the subject of the email, paste this text into a text editor:

Hello ${data.customerName}! You have won a coupon for the Virtually-4-Free shop

The variable expression that serves as a placeholder for the customerName attribute is replaced with a value when the email is sent using the email template.

All attribute names must be prepended with data, which is a hash array that the Velocity Template service uses to make the attributes available to Velocity. For details on how to work with attributes, see the Velocity Templates for the Velocity Template service section.

Save your file as subject.vm. The suffix .vm is the standard file extension for Velocity files. You can save the file to refer to it later, although you will only upload the contents of the file and not the file itself.

Step 2: Prepare the template for the email body

As indicated in the use case, you are using HTML formatting for your notification emails. Therefore, you must use a template that includes both HTML code and Velocity code.

To create the template for the body of the email, paste the text shown into a text editor:

<html>
<body>
  <h1>Win notification!</h1>
  <p>Dear ${data.customerName},</p>
  <p>
     Thank you for participating in our super-fabulous new-customer competition!
     You're one of the lucky winners!
  </p>
  <p>
    We hereby present you with your coupon worth
    <strong>${data.couponValue}</strong>,
    to make your next purchase at the Virtually-4-Free shop even cheaper.
    To redeem your coupon, simply enter the following coupon code during checkout:
    <div class="coupon">${data.couponCode}<div>
  </p>
  <p>
    Or, start your next shopping spree from 
    <a href="https://virtually4free.example.net/coupon?code=${data.couponCode}">this link</a>,
    and have the coupon code conveniently pre-filled!
  </p>
</body>
</html>

In this template file, use the same customerName placeholder that you used in the subject template file. In addition, there are two new placeholders named couponValue and couponCode, also prepended by the data hash array.

Note how the template file uses the couponCode attribute in both the visible contents of the HTML and in a hyperlink URL. You can refer to attributes anywhere in the HTML code of your template file. This gives you a great deal of flexibility and control when authoring your own template files for the Email service.

The great flexibility of templating can introduce security concerns. While the example body template file in this tutorial is suitable for your first experiments with the Email service, take care to ensure proper HTML formatting and prevent code-injection attacks for your production template files. For more information on this subject, see the advanced templating tutorial about securing your templates.

When you are done adjusting the template, save the file as body.vm. The next step is to upload your template to the Velocity Template service.


Upload Your Template

In the Create Your Template tutorial, you prepared your subject and body templates. Now it's time to upload the templates to the Velocity Template service. You can use the API Console to define two templates with the subject and body information in the Velocity Template service: one for the email subject and one for the body.

Step 1: Upload the content for Velocity templates

You have now created the email templates, but you still did not provide any information about how the content should look. You can upload this information using these steps.

To upload the subject of the email, issue this request to the Velocity Template service:

  • Method: PUT
  • Request URL: https://api.beta.yaas.io/hybris/velocity-template/v1/{project}/{client}/templates/win-notification_subject/variants/en-US
  • Content Type: application/octet-stream
  • Body: The body content is the content of the subject.vm file created in the Create Your Template tutorial.

You must specify the variant of your email in case you decide in the future to manage several variants for a single template. The common use case for this is maintaining templates in several languages. In the preceding example, the locale identifier is the variant that specifies the language of the template.

A successful response looks like this example:

  • Status code: 200
  • Content type: application/json
  • Body example:
    {
        "sizeInBytes": 85,
        "type": "body",
        "url": "https://api.beta.yaas.io/hybris/velocity-template/v1/{project}/{client}/templates/win-notification_subject/variants/en-US"
    }
    
The content of the template passes through a validator. If there is a syntactic error in the template, the upload fails.

Step 2: Upload the Velocity template for the body

To upload a Velocity template for the email body, issue this request:

  • Method: PUT
  • Request URL: https://api.beta.yaas.io/hybris/velocity-template/v1/{project}/{client}/templates/win-notification_body/variants/en-US
  • Content Type: application/octet-stream
  • Body: The body content is the content of the body.vm file created in the Create Your Template tutorial.

A successful response looks like this example:

  • Status code: 200
  • Content type: application/json
  • Body example:
    {
        "sizeInBytes": 749,
        "type": "body",
        "url": "https://api.beta.yaas.io/hybris/velocity-template/v1/{project}/{client}/templates/win-notification_body/variants/en-US"
    }
    

Troubleshooting

Make sure that you are using a suitable authorization token.

Be aware that even if you successfully upload a template file, the Velocity code might not be properly validated. This is because Velocity is often lenient when evaluating template files. If the system finds a syntactical error, the template file evaluation is likely to succeed anyway because Velocity simply outputs that part of the template, verbatim.

Congratulations! You have created and uploaded your first template. To check how the uploaded template will render, you must use it to send an email with the subject specified in the Send an Email tutorial.


Render a Template

This part of the tutorial shows how to use the templates that you wrote and uploaded in the "Upload Your Template" tutorial, and specifically, how to render templates for notification emails. The tutorial also shows how to pass custom values to defined attributes in order to personalize the contents of the email for the individual contest winner.

When sending an email, you must provide additional inputs to the Velocity Template service. These inputs are explained in detail here.

You can use the API Console to send emails using these inputs.

To render the template for the subject, issue this request to the Velocity Template service:

  • Method: POST
  • Request URL: https://api.beta.yaas.io/hybris/velocity-template/v1/{project}/{client}/templates/win-notification_subject/render
  • Content Type: application/json
  • Request Body:
    {
        "attributes": {
            "myVelocityKey": "myValue",
            "name": "Joe"
        },
        "variant": "en-US" 
    }
    

A successful response looks like this:

  • Status code: 200
  • Response Body: The rendered subject

The body is rendered in the same way. Remember to include values for the couponCode and couponValue attributes.

Troubleshooting

If you receive a 400 response status code, read the error message provided. Here are some common problems that you might need to fix if your send request yielded an error response:

  • Verify that there are no typos in the JSON object that you sent. The object must be syntactically-correct JSON, and its structure must conform to the example shown above. Look at the error message in the response body, which might help to identify the problem.

  • If you encounter authorization problems, verify that you are using a suitable access token. Also, verify that you replaced the {client} in your JSON with your actual Client value.

  • Verify that the referenced template is present both for the subject and the body. Note that the owner and code must exactly match an existing email template and that both are case-sensitive. Also make sure that variants for the template are present.

  • The Velocity templates must be syntactically-correct in terms of the Velocity Template Language (VCL). While VCL is very lenient about syntax, and usually falls back to rendering the source code verbatim, there are some constructs that might cause Velocity template rendering to fail completely. Read the Velocity user guide, or start from the Velocity templates in this guide, and modify them step-by-step to get to the root of any such problems.


Leverage Structured Data

As described previously, the Velocity Template service uses Velocity to allow you to create flexible and reusable templates. So far, the example template for win notification emails has been fairly simple.

In this tutorial, you will learn how to add flexible product recommendations to the example email template you created in the previous tutorial. This tutorial demonstrates more of the advanced templating features that the Velocity Template service provides.

Pass structured product data in your send request

When you render a template using the Velocity Template service, the defined attribute values that you pass are not constrained to be mere strings. Instead, you can use more complex, structured data – basically anything that can be expressed in JSON. For product recommendations, you might use a defined attribute called products with a value similar to the example shown:

[
  {
    "sku" : "395879346",
    "name" : "Canned Unicorn Meat",
    "price" : 279.5,
    "score" : 0.98,
    "createdAt" : "2015-07-05T15:32:12.345Z"
  },
  {
    "sku" : "NCC-1701",
    "name" : "Vintage Tricorder",
    "price" : 17.01,
    "score" : 0.86,
    "createdAt" : "2015-02-23T07:45:04.235Z"
  }
]

Step 1: Loop through a list of products

In your Velocity template, you can access the aforementioned products through the VTL expression ${data.products}, which results in this output:

[{sku=395879346, name=Canned Unicorn Meat, price=279.5, score=0.98, createdAt=2015-07-05T15:32:12.345Z},
{sku=NCC-1701, name=Vintage Tricorder, price=17.01, score=0.86, createdAt=2015-07-03T07:45:04.235Z}]

That formatting is not easy to read, though. To obtain more pleasant output, you have to loop over the products and format each of them separately, such as in an HTML list. Use Velocity's #foreach directive, which loops over the contents of ${data.products} and makes each of them available in a new variable ${product}:

<ul>
#foreach( $product in ${data.products} )
  <li>
    ${product}
  </li>
#end
</ul>

This yields the output:

<ul>
  <li>
    {sku=395879346, name=Canned Unicorn Meat, price=279.5, score=0.98, createdAt=2015-07-05T15:32:12.345Z}
  </li>
  <li>
    {sku=NCC-1701, name=Vintage Tricorder, price=17.01, score=0.86, createdAt=2015-07-03T07:45:04.235Z}
  </li>
</ul>

Each ${product} now appears as a separate list item of an HTML list.

Step 2: Access the fields of a product

To further improve formatting, you can break down the projects themselves. Use Velocity references to access the fields of a product separately and format them in HTML. This example also constructs a link back to the Virtually-4-Free storefront based upon the product's stock keeping unit (SKU):

<li>
  <strong>
    <a href="https://virtually4free.example.net/products/${product.sku}">
      ${product.name}
    </a>
  </strong>
  <br>
  New since ${product.createdAt} and recommended by ${product.score} of our customers :-)
  <br>
  <em>Only ${product.price}!</em>
</li>

The result is a formatted representation of a product:

<li>
  <strong>
    <a href="https://virtually4free.example.net/products/395879346">
      Canned Unicorn Meat
    </a>
  </strong>
  <br>
  New since 2015-07-05T15:32:12.345Z and recommended by 0.98 of our customers :-)
  <br>
  <em>Only 279.5!</em>
</li>

Step 3: Format dates and numbers

While overall formatting of the product is under control now, the output of dates, numbers, and prices still requires some work. The Velocity Template service provides several Velocity Tools, which are excellent for this purpose.

First, have a look at the calendar date in ${product.createdAt}, which up until now has been rendered in ISO 8601 format, such as 2015-07-05T15:32:12.345Z in the preceding example. It is a good idea to stick to such a machine-readable date format in your JSON data. However, in an email message, you should use a format that is more pleasant to the human reader. You can achieve both using this approach:

  1. Use the Velocity Conversion Tool to convert the ISO 8601 String to a Velocity / Java Date object, and store it in a new Velocity variable:
    #set( $createdAt = ${tools.convert.toDate(${product.createdAt})} )
  2. Use the Velocity DateTool to format the Date object according to a custom date format pattern:
    ${tools.date.format("EEEEE MMMMM d, yyyy", ${createdAt})}

Next, have a look at the numbers in ${product.score} and ${product.price}. You can format both in a more meaningful way using the Velocity NumberTool:

  • Use it to format the score as a percentage: ${tools.number.percent(${product.score})}
  • Use it to format the price as a currency according to a custom format, taking the passed-in locale into account: ${tools.number.format("¤#.00", ${product.price})}

Altogether, you can use this Velocity template for your products:

#set( $createdAt = ${tools.convert.toDate(${product.createdAt})} )
<li>
  <strong>
    <a href="https://virtually4free.example.net/products/${product.sku}">
      ${product.name}
    </a>
  </strong>
  <br>
  New since ${tools.date.format("EEEEE MMMMM d, yyyy", ${createdAt})} and recommended by 
  ${tools.number.percent(${product.score})} of our customers :-)
  <br>
  <em>Only ${tools.number.format("¤#.00", ${product.price})}!</em>
</li>

Which results in nicely-formatted output like this:

<li>
  <strong>
    <a href="https://virtually4free.example.net/products/395879346">
      Canned Unicorn Meat
    </a>
  </strong>
  <br>
  New since Sunday July 5, 2015 and recommended by
  98% of our customers :-)
  <br>
  <em>Only $279.50!</em>
</li>

Step 4: Put it all together

Time to integrate the preceding code snippets into the overall body Velocity template for win notification emails.

Because product recommendations are not always be available, you might want to make that part of the email optional. With Velocity's #if directive, you can render the list of products, plus a short introductory sentence, when recommendations are available. Otherwise, you can render a generic paragraph with a link to to your shop.

Last but not least, you can extend the product links and pass the coupon code along with them, which might be evaluated by the fictional storefront. This is what it all might look like:

<html>
<body>
  <h1>Win notification!</h1>
  <p>Dear ${data.customerName},</p>
  <p>
     Thank you for participating in our super-fabulous new customer competition!
     You're one of the lucky winners!
  </p>
  <p>
    We hereby present you with your coupon worth
    <strong>${data.couponValue}</strong>,
    to make your next purchase at Virtually-4-Free shop even cheaper.
    To redeem your coupon, simply enter the following coupon code during checkout:
    <div class="coupon">${data.couponCode}<div>
  </p>
  #if( ${data.products} && ${data.products.size()} > 0 )
    <p>
      Or, how about you check out one of our ${data.products.size()} latest products right now?
    </p>
    <ul>
    #foreach( $product in ${data.products} )
      #set( $createdAt = ${tools.convert.toDate(${product.createdAt})} )
      <li>
        <strong>
          <a href="https://virtually4free.example.net/products/${product.sku}?couponCode=${data.couponCode}">
            ${product.name}
          </a>
        </strong>
        <br>
        New since ${tools.date.format("EEEEE MMMMM d, yyyy", ${createdAt})} and recommended by 
        ${tools.number.percent(${product.score})} of our customers :-)
        <br>
        <em>Only ${tools.number.format("¤#.00", ${product.price})}!</em>
      </li>
    #end
    </ul>
  #else
    <p>
      Or, how about starting your shopping spree at 
      <a href="https://virtually4free.example.net/?couponCode=${data.couponCode}">Virtually-4-Free</a>,
      right now?
    </p>
  #end
</body>
</html>

Now try to upload this template and use it to render a template. Remember to pass an additional attribute value that holds the recommended products.

Feel free to experiment a little! For instance, see what happens when you don't pass any products. Or, try to pass a different locale in your send request, such as de-DE. Watch how dates and numbers automatically get adjusted to local customs. (Hint: Germans use a comma as a decimal separator rather than a period.) For more information about fully localizing your template, see template localization.


Security

Templates stored in the Velocity Template service may contain personal data. Only you as a data controller know whether the template contains personal data and which data subject the data relates to. Therefore, to meet the data privacy requirements, you must map documents containing personal data with the corresponding data subjects, and implement a logic to serve the data subject's requests related to its personal data, such as requests for information or deletion. To meet these goals, follow the instructions provided in the Developer Guidelines for Data Privacy.


  • Send feedback

    If you find any information that is unclear or incorrect, please let us know so that we can improve the Dev Portal content.

  • Get Help

    Use our private help channel. Receive updates over email and contact our specialists directly.

  • hybris Experts

    If you need more information about this topic, visit hybris Experts to post your own question and interact with our community and experts.