The Apache Velocity translator uses the formal reference notation for all commands, to minimize the risks of running into parsing conflicts. In general, all generated commands should be compatible from Apache Velocity 2.0 and upwards.
The following tools have to be enabled in the Velocity templates:
- [DateTool][]
- [EscapeTool][]
- [StringUtils][]
- [LibPhoneNumber][] (see below for details)
- NumericTool (custom tool, see below)
- Map command for lists (custom command, see below)
- TrackingTool (custom tool, see below)
- TextTool (custom tool)
- TimeTool (custom tool)
- DictionaryTool (custom tool)
These tools can be added to the context of templates like this:
context.put("date", new DateTool());
context.put("esc", new EscapeTool());
context.put("StringUtils", new StringUtils());NOTE: The names are case-sensitive. There is a mix of cases here - they have to stay this way to be backwards compatible.
Commands that require these tools:
- ShowDate (TimeTool)
- ShowSnippet (EscapeTool, DictionaryTool)
- ShowXXX commands, for URL encoding support (EscapeTool)
- ShowXXX commands, for IDN encoding support (TextTool)
- If Empty / If Not Empty (StringUtils)
- ElseIf Empty / ElseIf Not Empty (StringUtils)
- List contains / List not contains (Map command)
- ShowPhone (LibPhoneNumber)
- ShowNumber (NumericTool)
- ShowURL (TrackingTool)
If these tools are not available, these commands will throw errors if they are used in a template.
This library is required for converting phone numbers to the E164 format, to create tel: URLs
for clickable phone number links. The library is used to convert the localized phone number
syntax to E164.
The showphone command expects the tool to be available in the $phone variable. The
following is an example implementation for making this available in a Velocity template:
private static final PhoneNumberUtil PHONE_NUMBER_UTIL = PhoneNumberUtil.getInstance();
public String e164(String phoneNumber, String country) {
try {
return PHONE_NUMBER_UTIL.format(PHONE_NUMBER_UTIL.parse(phoneNumber, country), PhoneNumberUtil.PhoneNumberFormat.E164);
} catch (Exception e) {
return phoneNumber;
}
}Its usage is expected to look like this:
${phone.e164($PHONE.NUMBER, 'FR')}
The commands list-contains and list-not-contains depend on a custom command which we implemented on the Velocity side for our project.
This basically provides the following Velocity command, assuming that $PRODUCTS is a list of records:
#if($map.hasElement($PRODUCTS.list(), "NAME", "(?s)Value") )The matching java code then looks like this:
return list.stream().anyMatch(map -> map.get("NAME").matches("(?s)Value"));To use those commands, this has to be implemented in your Velocity engine.
The shownumber command relies on the numeric tool class to be present
in the $numeric variable in templates. This class is a custom implementation
used to convert numbers, so they can be formatted as prices. It is
basically just a wrapper around Java functions for number formatting. to
make the code in the template easier to read and maintain.
Due to copyright issues, the source code cannot be posted here.
The generated command looks like this:
${numeric.format($FOO.BAR, 2, ',', ' ')}And the absolute number variant:
${numeric.format(${numeric.abs($FOO.BAR)}, 2, ',', ' ')}The numeric tool expects all numbers to be specified without thousands separator, and dots as decimal separator.
The showurl command relies on a custom implementation on the Velocity
side, which handles all the functionality.
Due to copyright issues, the source code cannot be posted here.
Consider the following Mailcode command:
{showurl: "TrackingID" "foo=bar"}
{if variable: $COUNTRY == "fr"}
https://mistralys.fr
{else}
https://mistralys.eu
{end}
{showurl}
Velocity code generated by the command:
#{define}($url_tpl001)
#if($COUNTRY == "fr")
https://mistralys.fr
#{else}
https://mistralys.eu
#{end}
#{end}
${tracking}
.url(${url_tpl001})
.lt(${tracking_host}, ${envelope.hash}, "TrackingID")
.query("foo", "bar")This has been indented for readability. The actual generated command does not contain any line breaks or indentation.
The url() method is used to set the target URL. To allow complex
statements, the URL is wrapped in a #define call, and specified
as argument via a variable.
The lt() method sets up and activates the tracking URL generation.
We are using the preexisting variables $tracking_host and $envelope.hash
in our system, which contain the tracking URL host (e.g. mistralys.com)
and a unique identifier of the request in the form of a hash string.
Each call to query adds or replaces a single query parameter
intended to be added to the final URL.
This tool offers the IDN encoding support for the following Velocity functions:
$text.idn("Subject")- IDN encode the subject$text.unidn("Subject")- Remove IDN encoding from the subject
These two functions use an IDN encoding library to process the subject values.
Mailcode:
{showencoded: "öäü.com" idnencode:}
{showvar: $DOMAIN.NAME idnencode:}
Velocity:
${text.idn("öäü.com")}
${text.idn(${DOMAIN.NAME})}
When working with dates, the generated velocity statement will assume the date to be provided in the default internal format:
yyyy-MM-dd'T'HH:mm:ss.SSSXXX
If the variable source data does not match this format, the date commands will fail.
To change this, the internal format can be specified on a per-command basis, using translation parameters:
use \Mailcode\Mailcode_Factory;
$var = Mailcode_Factory::show()->date('ORDER.DATE');
$var->setTranslationParam('internal_format', 'yyyy-MM-dd');The translator will automatically use the specified format instead.
Note: The internal format must be a valid Java
SimpleDateFormatpattern. Optional-section brackets ([and]) fromDateTimeFormatterare not supported and will cause aMailcode_Translator_Exceptionto be thrown during translation.
To adjust the format of dates in a safeguarded string, the shortest way is to set the translation parameter for relevant date variables.
use \Mailcode\Mailcode;
$sourceString = '(Mailcode commands here)';
$internalFormat = 'yyyy-MM-dd';
// Create the translator and the safeguard
$mailcode = Mailcode::create();
$syntax = $mailcode->createTranslator()->createApacheVelocity();
$safeguard = Mailcode::create()->createSafeguard($sourceString);
// Configure all date commands, as needed
$dateCommands = $safeguard->getCollection()->getShowDateCommands();
foreach($dateCommands as $dateCommand)
{
$dateCommand->setTranslationParam('internal_format', $internalFormat);
}
// Translate the string
$result = $syntax->translateSafeguard($safeguard);