If you have ever programatically sent an e-mail from Content Server you would have certainly encountered the
$Kernel.SMTPClient library. The library provides the groundwork for sending an e-mail, but has a rather unfriendly API. In a nutshell, to send an e-mail you must:
- read the SMTP configuration from the notification settings (or get other settings from elsewhere);
- instantiate an instance of
$Kernel.SMTPClientwith these settings;
- render the body of the message (which isn't trivial with HTML e-mails);
.SendMessage()with the details of the e-mail (which can be seven or more parameters);
- implement extensions to support:
- carbon-copy (CC) and blind carbon-copy (BCC);
- binary attachments;
- multiple attachments; and
- Content Server documents as attachments.
With these limitations and this lack of abstraction I decided to take a fresh look at sending e-mail from Content Server. The result is the
EMailer class, which is now a part of RHCore.
# Introducing EMailer
EMailer class is a direct subclass of
$Kernel.SMTPClient. The class abstracts away the complexity of sending an e-mail and provides a simplified programming interface to the developer. Let's look at an example of how one can send a short plain text e-mail with
Frame emailer = $RHCore.EMailer.New( prgCtx ) emailer.addRecipients("Admin") emailer.setSubject("Welcome to RHCore") emailer.setBody("We hope you're enjoying this blog post.") emailer.send()
Let's review line-by-line.
The first line constructs an instance of
EMailer and sets a few defaults based on the settings in the "Configure Notification" admin pages. Namely:
- the SMTP settings (i.e., server, port, & host name);
- the default from address; and
- the default reply-to address.
Any of these settings can be changed by calling
setReplyTo on the instance. Furthermore, the body and subject are defaulted to blank and the body mimetype is defaulted to
The second line adds a recipient to the e-mail. The recipient can be a user id, user name, e-mail address, RHUser, or list. The method can be called multiple times, and provisions are in place to prevent the same user or e-mail address from being added more than once.
The remaining lines should be self explanatory.
Each of these methods returns the
EMailer instance, which allows the methods to be chained. That is, our example could be written as follows:
$RHCore.EMailer.New(prgCtx) \ .addRecipients("Admin") \ .setSubject("Welcome to RHCore") \ .setBody("We hope you're enjoying this blog post.") \ .send()
That's it! The e-mail gets sent and renders as follows:
Of course, nobody wants to look at plain text e-mails. Let's see what we can do about this.
# Rendering HTML E-Mails
HTML e-mails can be rendered by setting the body mimetype to
text/html. For example, we could change the previous example to the following:
$RHCore.EMailer.New(prgCtx) \ .addRecipients("Admin") \ .setSubject("Welcome to RHCore") \ .setBodyMimeType('text/html') \ .setBody("<p>We hope you're enjoying this <strong>blog post</strong>.</p>") \ .send()
As you would expect, the mail client renders this with the text "blog post" in bold:
No developer likes to write inline HTML, so the
setBody method also accepts markdown. This can be converted to HTML by calling the
renderMD method. For example, the following creates the same e-mail as in the previous example:
$RHCore.EMailer.New(prgCtx) \ .addRecipients("Admin") \ .setSubject("Welcome to RHCore") \ .setBodyMimeType('text/html') \ // set the mimetype to text/html .setBody("We hope you're enjoying this **blog post**.") \ .renderMD() \ // render the markdown as HTML .send()
Things get difficult as the HTML gets more complex and you consider the many variations in e-mail clients. What renders fine in GMail or Thunderbird may look terrible in Outlook or on an iPhone. There are a number of blog posts that describe how to write generic HTML e-mails, but I won't get into that here.
However, being able to wrap a message into a mail-friendly HTML template is very useful and has some advantages. Specifically, it:
- provides a consistent look to e-mails (header, footer, colour scheme, etc.);
- offers some guarantees the e-mail will render nicely on the mail client; and
- saves the developer a lot of time.
EMailer class supports this with the
setTemplate method, which takes the current body and embeds it within a template. For example:
$RHCore.EMailer.New(prgCtx) \ .addRecipients("Admin") \ .setSubject("Welcome to RHCore") \ .setBodyMimeType('text/html') \ .setBody("We hope you're enjoying this **blog post**.") \ .renderMD() \ .setTemplate() \ // wrap the message in the default template .send()
This renders as follows:
I've tested the default template with a few clients and it seems to work well. Of course, if you don't like the template you can create your own and pass it as a parameter to the
How about that? In eight lines of code (which is actually just one line) we've generated and sent an HTML e-mail from Content Server.
So what else?
EMailer class supports multiple attachments, which can come from the file system or Content Server. These are easy to add:
// add a file from the filesystem emailer.addAttachmentFile("C:/temp/somepicture.png") // add the document with DataID 12345 emailer.addAttachmentNode(12345)
The current user must have
See Contents permission to attach a document from Content Server. Furthermore, additional parameters are available to control:
- the display name of the attachment;
- the mimetype; and
- the document version (including renditions).
# Queueing Mail
E-mails can also be queued and sent later using agents. This serves four purposes:
- you want to audit the generation and send status of each e-mail;
- you don't want to tie up the current thread sending potentially thousands of e-mails;
- prevents an e-mail from being sent if the transaction is rolled back due to a runtime error; and
- allows each recipient to receive a personalized copy of the e-mail.
The queueing of e-mails depends on the RHTaskQueue module, which is a small extension to RHCore and allows a task to be deferred to the agent (this module may become part of RHCore in the future). Usage is identical to sending a regular e-mail with the exception of the last line:
$RHCore.EMailer.New(prgCtx) \ .addRecipients("Admin") \ .addRecipients("cmeyer") \ .setSubject("Welcome to RHCore") \ .setBody("We hope you're enjoying this blog post.") \ .queue() // queue the e-mail instead of sending it
This inserts the e-mail into the queue and will be sent out the next time the five minute agent runs. Failed attempts to send an e-mail (e.g., the SMTP server is offline) are repeated five times before giving up. The status of each e-mail is audited and accessible from the admin pages:
queue method accepts an optional boolean parameter (defaults to
false) to generate a single and unique e-mail for each recipient. This has a few advantages:
- each e-mail can be personalized when wrapped with the
setTemplatemethod (i.e., the recipient is personally addressed in the e-mail body); and
- the e-mail won't be overloaded with potentially thousands of e-mail addresses.
The one caveat is that carbon-copy (CC) and blind carbon-copy (BCC) are not supported when sending individual e-mails. It's a small detail, but makes sense.
# Wrapping Up
EMailer class breathes new life into programatically sending e-mails from Content Server. I'm using it in a few projects with success as I can now send rich HTML e-mails with just a few lines of code. I couldn't imagine still doing it the old way.
Questions, comments, or interested in a private demo? Please contact me by e-mail or leave a comment below.