Skip to content

Mail content using content helpers

Sergi Baila edited this page May 31, 2017 · 6 revisions

Motivation

The basic usage of this library allows you to send messages throwing simple strings as the text and/or html content at the respective methods. Sometimes, though, you may want to use some basic formatting that HTML provides but don't care about a full fledged complex HTML email template. Examples: add some bold text, create a link, format a basic table.

The classes on the package net.sargue.mailgun.content are designed to easily build basic HTML messages. It's not supposed to be used for building cutting edge responsive modern HTML messages. It's just for simple cases where you need to send a message and you want to use some basic HTML like tables and some formatting.

Basic examples

Some self explanatory examples:

Mail.using(configuration)
    .body()
    .h1("This is a heading")
    .p("And this some text")
    .mail()
    .to("[email protected]")
    .subject("This is the subject")
    .build()
    .send();
Mail.using(configuration)
    .body()
    .h3("Monthly report")
    .p("Report of the number of time travels this month")
    .table()
        .row("Marty", "5")
        .row("Doc", "7")
        .row("Einstein", "0")
    .end()
    .mail()
    .to("[email protected]")
    .subject("Monthly Delorean usage")
    .build()
    .send();

Of course you can keep the body content and mail building separated.

Body body = Body.builder()
                .h1("This is a heading")
                .p("And this some text")
                .build();

Mail.using(configuration)
    .to("[email protected]")
    .subject("This is the subject")
    .content(body)
    .build()
    .send();

Usage

The main class is the Builder which you use to construct a Body using a fluent interface. Like this:

Body body = Body.builder()
                .h1("This is a heading")
                .p("And this some text")
                .build();

A Body is really just a tuple with two strings: the text version and the HTML version. When you are building your mail using MailBuilder you have the MailBuilder#content() method which accepts a Body. Completing the example:

Mail.using(configuration)
    .to("[email protected]")
    .subject("This is the subject")
    .content(body)
    .build()
    .send();

Most of the time you would probably build the content, the email and send it. So you can chain it an get a nice fluent conversation.

Mail.using(configuration)
    .body()
    .h1("This is a heading")
    .p("And this some text")
    .mail()
    .to("[email protected]")
    .subject("This is the subject")
    .build()
    .send();

How it works

As said before this is for very simple formatted messages. Most email clients can read basic HTML but the proper way is to include also a text only version. Even if it, probably, won't be used. These classes take care of that just by generating both text and HTML versions of the orders you give. Most of the time the text version is just the content. You ask for emphasis of a text? The HTML will go <em>Hello world</em> but the text version will go just like Hello world. The aim is to be readable not an accurate representation of the formatting. No fancy ASCII tricks here.

Basic formatting

You will find several common HTML tags in the Builder class. Most accept content directly or can be called without parameters if you need more complex content or to nest other directives. Remember to close your tags calling end().

Example of heading with direct content and nested content. They produce the same result:

Body.builder()
    .h1("This is the heading")
    .build();
Body.builder()
    .h1()
    .text("This is the heading")
    .end()
    .build();

Tables

There is some basic support for tabular data. The basic expected tags for table, tr, and td are there along with some shortcuts. There are shortcuts for rows of size up to 4 columns.

A simple example:

Mail.using(configuration)
    .body()
    .h3("Monthly report")
    .p("Report of the number of time travels this month")
    .table()
        .row("Marty", "5")
        .row("Doc", "7")
        .row("Einstein", "0")
    .end()
    .mail()
    .to("[email protected]")
    .subject("Monthly Delorean usage")
    .build()
    .send();

There is also the method rowh(String label, T data) which I've found particularly useful when building basic HTML messages to output tabular data. For example, you have a customer, Michael, and inside an HTML email you want to show data about him.

You expect to create something like this:

Label Data
ID 2356
Name Michael
Address 9303 Lyon Drive, Lyon Estates

So you can build it in a compact way like this:

Mail.using(configuration)
    .body()
    .table()
    .rowh("ID", 2356)
    .rowh("Name", "Michael")
    .rowh("Address", "9303 Lyon Drive, Lyon Estates")
    .end()
    .mail()
    .to("[email protected]")
    .subject("Come see my home!")
    .build();

Low level HTML

If you really need specific HTML written to the output you can use the tag methods. Just remember to always close every tag invokation with a end invokation.

An example to write some colored text using the style attribute.

Body.builder()
    .p()
        .text("Hello world ")
        .tag("span", "style='color:red'")
            .text("in color")
        .end()
    .end()
    .build();

This will render this HTML.

<p>Hello world <span style='color:red'>in color</span></p>

Content converters

All the content we are formatting and putting together in an email is basically text. But most data won't be directly text so we need to format it. Numbers, dates, currency amounts...

You certainly can format it when appending it into the message but you can also use the content converter facility.

Content converters allows you to register any number of content converters associated with a Java class. That converter will be used when calling the generic method Builder#text(T value).

Sample usage:

Date date = new GregorianCalendar(1985, 9, 26, 1, 22).getTime();

Configuration cfg = new Configuration();
ContentConverter<Date> converter = new ContentConverter<Date>() {
    SimpleDateFormat df = new SimpleDateFormat("dd/MM/yyyy");

    @Override
    public String toString(Date value) {
        return df.format(value);
    }
};

cfg.registerConverter(converter, Date.class);

Body body = Body.builder(cfg)
    .text(date)
    .build();

assertEquals("26/10/1985" +  emptyText, body.text());

As you can see the converters are registered to the Configuration. Internally, they are matched in declaration order checking for assignability (inheritance). So you can have a a Date converter which will be applied to a Timestamp object. You will want to register the more specific converters first.

The default converter is always the last one and accepts any Object. It just calls Object#toString().