* binding is used from here
* meta data helps formatting plain text
Rendering e-Mails
A common problem in different types of applications is the rendering of e-mails. Of course it's a really solvable task. However, I want to present how an order confirmation mail might be created using the Snippetory Template Engine. To make it not too simple I will create a plain text and a HTML variant, based on user preference. Let's start with the plain text template:
$salutation $name,
$text
$name(pad='40') $quant(pad='10' pad.align='right') $price(pad='12' pad.align='right') $sum(pad='14' pad.align='right')
$line(pad='87' pad.fill='=')
$cart_entry{
$name(pad='40') $quant(pad='10' pad.align='right') $price(pad='12' pad.align='right') $sum(pad='14' pad.align='right') $curr
}$
$line(pad='87' pad.fill='-')
$total-label( pad='62' pad.align='right') $total(pad='14' pad.align='right') $curr
$footer
This shows a number of interesting facts. The most obvious is the direct support for formatting the cart table. The formats stretch and shorten give you convenient control over the length of strings. Another thing to mention is the white space handling on region delimiters i.e. The t:cart_entry tag elements. Such an element will span the entire line if the line only contains this element and white space. No additional line breaks will be added by the demonstrated construct.
Now, that we have the template, we need some code to bind the data. But luckily, we have written a part of it in the page pattern example. Of course, I'll reuse this! And the rest is quite the same as any mail.
The code for creating an email to a user might look like this:
public class MailRenderer {
ResourceBundle msg;
public MailRenderer(Locale locale) {
msg = ResourceBundle.getBundle("messages",locale);
}
public void renderMail(Template mail, User user) {
mail
.set("salutation", getSalutation(user))
.set("name", user.getDisplayName());
renderFooter(mail);
}
protected String getSalutation(User user) {
String key = "user.salutation.male";
if (user.getGender() == Gender.FEMALE) key = "user.salutation.female";
return msg.getString(key);
}
protected void renderFooter(Template mail) {
mail.set("footer", msg.getString("mail.footer")):
}
}
Now we just have to load the right template, set the main text (which is different per mail) and call our mail and cart rendering methods and send the result. That's all.
public class OrderConfirmationRenderer {
protected final ResourceBundle msg;
protected final Locale locale;
protected final MailRenderer mailRenderer;
public OrderConfirmationRenderer(Locale l) {
msg = ResourceBundle.getBundle("messages", l);
locale = l;
mailRenderer = new MailRenderer(l);
}
public Template render(User user, Cart cart) {
Template mail = user.isPlainTextMail() ? getPlainTemplate(user, cart) : getHtmlTemplate(user, cart);
mailRenderer.renderMail(mail, user);
new CartPage(locale, cart).renderContent(mail);
return mail;
}
private Template getHtmlTemplate( User user, Cart cart ) {
return Repo.readResource("org/jproggy/examples/minishop/OrderConfirmation.html")
.locale(locale).encoding(Encodings.html).parse();
}
private Template getPlainTemplate( User user, Cart cart ) {
return Repo.readResource("org/jproggy/examples/minishop/OrderConfirmation.txt")
.syntax(Syntaxes.FLUYT).locale(locale).encoding(Encodings.plain).parse();
}
}
Sometimes this is really all. Sometimes we do it the generous way and provide the choice between a plain text and a html version of the mail. In that case there's still some work to do. Let's start with the html template. This is easy. As we can reuse the cart template, too, we don't need too much:
<html>
<body>
<p>$salutation $name,</p>
<p>$text</p>
$content
<p>$footer</p>
</body>
</html>
The last effort is to modify our java code. In fact this is split into two parts. First we obviously didn't provide an HTML footer. We implement a class that's a little more sophisticated:
$target
Finally the routine to send the mail needs some enhancement. First we have to select the template to load and the mail rendering method to call. And in the end we call the other methods:
We can see that the Snippetory Template Engine provides a number of interesting features:
- a simple approach for plain text formatting
- pretty fine-grained code reuse
- the possibility to organize the logic in methods and classes
- an abstraction layer that frees the logic from technical issues of the output format.
That's all for now. Have fun.