Mail Sender API

The new Mail Sender API is an experimental API used by the Mail Application and it is meant to replace the old Mail Sender Plugin. 

For the moment, the Mail Sender API is only used:

  • in the registration email 
  • for password reset

The most important features are:

  • ability to send Multipart emails with any type of mime type content, including html, text and vcalendar
  • ability to embed images in HTML emails
  • ability to send mail templates
  • ability to send mails to list of users or to a group
  • scripting API in order to send mails from wiki pages
  • support for asynchronous email sending
  • support for sending large volume of emails
  • ability to send prepared mime message to multiple users as independent message

Compatibility with the Mail Sender Plugin

File ExtensionOld Mail Sender PluginMail Sender API
exeapplication/octet-streamapplication/x-dosexec
classapplication/octet-streamapplication/java-vm
aiapplication/postscriptapplication/illustrator
pptapplication/powerpointapplication/vnd.ms-powerpoint
cgiapplication/x-httpd-cgitext/x-cgi
skpapplication/x-koanapplication/vnd.koan
skdapplication/x-koanapplication/vnd.koan
sktapplication/x-koanapplication/vnd.koan
skmapplication/x-koanapplication/vnd.koan
mifapplication/x-mifapplication/vnd.mif
tclapplication/x-tcltext/x-tcl
tapplication/x-trofftext/troff
trapplication/x-trofftext/troff
roffapplication/x-trofftext/troff
manapplication/x-troff-mantext/troff
meapplication/x-troff-metext/troff
msapplication/x-troff-mstext/troff
rpmaudio/x-pn-realaudio-pluginapplication/x-rpm
raaudio/x-realaudioaudio/x-pn-realaudio
xyzchemical/x-pdbchemical/x-xyz
sgmltext/x-sgmltext/sgml
sgmtext/x-sgmltext/sgml
wrlx-world/x-vrmlmodel/vrml
vrmlx-world/x-vrmlmodel/vrml
icsapplication/icstext/calendar

Scripting API

The Mail Sender API is exposed to scripts using the MailSenderScriptService: $services.mailsender.*.

Starting with version 6.4, any document using the Scripting API requires Programming Rights by default in order to be allowed to send emails. This is configurable through the mail.sender.scriptServiceCheckerHint parameter of the "WEB-INF/xwiki.properties" file:

#-# [Since 6.4M2]
#-# Defines which authorization checks are done when sending mails using the Mail Sender Script Service.
#-# Example of valid values:
#-# - "programmingrights": the current document must have Programming Rights
#-# - "alwaysallow": no check is performed. This is useful when running XWiki in a secure environment where we
#-#   want to allow all users to be able to send emails through the Script Service.
#-# The default is:
# mail.sender.scriptServiceCheckerHint = programmingrights

When using the Mail Sender Script Service, there is a pluggable permission checker that is necessary in order to know whether the mail should be sent. There are 2 provided implementations, but you can also provide your own, by implementing the ScriptServicePermissionChecker component role as shown below:

@Role
@Unstable
public interface ScriptServicePermissionChecker
{
   /**
     * @param message the message to check for authorization
     * @exception MessagingException if the message is not allowed to be sent
     */

   void check(MimeMessage message) throws MessagingException;
}

Create a Message

The "from" parameter is optional and if not specified it is retrieved from Mail Sender configuration in the following order:

  • the from property is looked in the XWiki.SendMailConfigClass object attached to XWiki.MailConfig document in the current wiki
  • if not found, an admin_email property is looked for in the WebPreferences page for the current space
  • if not found, an admin_email property is looked for in the XWiki.XWikiPreferences page of the current wiki
  • if not found, a mail.sender.from configuration property is looked for in WEB-INF/xwiki.properties configuration file
  • if not found, the "from" parameter will not be set and the mail sending will fail

Examples

#set($message = $services.mailsender.createMessage())

#set($to = "rose.anderson@xwiki.com")
#set($from = "admin@xwiki.com")

$message = $services.mailsender.createMessage($to, 'Test')
$message = $services.mailsender.createMessage($from, $to, 'Test')

Create Pre-Filled Message Objects

It is possible to create a message with its subject automatically computed from a wiki page having an attached XWiki.Mail object. This is done by evaluating the subject property with Velocity using the method: public MimeMessageWrapper createMessage(String hint, Object source, Map<String, Object> parameters) {} where:

  • hint is the component hint of an "org.xwiki.mail.MimeMessageFactory" component.
  • source is the source from which to prefill the Mime Message. When the hint is template, the source represents the Document Reference to a page containing an "XWiki.Mail" object.
  • parameters is an optional generic list of parameters. When the hint is template, it is used to pass Velocity variables and values that will be used when evaluating the subject property of the "XWiki.Mail" object.

Example: #set ($message = $services.mailsender.createMessage(hint, source, parameters))

Add Content to the Message

In the following examples, the first parameter is a Mime Type and it corresponds to a Component Hint for a MimeBodyPartFactory.

Examples 

  • Add simple text to a message with a mail header: $message.addPart("text", "text message", {"headers" : { "Content-Transfer-Encoding" : "quoted-printable"}})
  • Add simple HTML to a message:
    #set($htmlMsg = "The user $userPrettyName ($userName) accepted the invitation to join the wiki
     <a href="$wikiUrl>$wikiName</a>.<br />")
    $message.addPart("text/html", "$htmlMsg")
  • Add HTML and alternate text to a message: $message.addPart("text/html", "$htmlMsg", {"alternate" : "text message"})
  • Add HTML, alternate text, embedded images and attachments to a message:
    #set($attList = $xwiki.getDocument("Sandbox.WebHome").getAttachmentList())
    $message.addPart("text/html", "$htmlMsg", {"alternate" : "text message", "attachments" : $attList})
  • Add HTML and alternate text from a Template Document containing an XWiki.Mail object. Any from Velocity variable is replaced with localhost@xwiki.com. Also, add internationalization support by retrieving the XWiki.Mail object which has a language property that equals to en. Finally, include attachments found in the Mail Template document:
    #set($documentReference = $services.model.createDocumentReference('xwiki', 'XWiki', 'WatchListMessage'))
    #set($attList = $xwiki.getDocument("XWiki.WatchListMessage").getAttachmentList())
    $message.addPart("xwiki/template", $documentReference,
    {"includeTemplateAttachments" : true, "language" : "en", "velocityVariables" :
    {"from" : "localhost@xwiki.com" }, attachments" : $attList})

Set the Email Type

When dealing with a large number of emails, it becomes useful to know the type of the sent emails in order to easily filter them in the "Mail Sending Status" section from the wiki preferences page. 

Example: $message.setType("Some type").

Send the Email

The are 2 ways of sending a message: 

  • synchronously using public ScriptMailResult send(Iterable<? extends MimeMessage> messages) {}
  • asynchronously using public ScriptMailResult sendAsynchronously(Iterable<? extends MimeMessage> messages, String hint) {}

The result can be stored in memory or in the database. The latest is more useful when sending large volume of emails in order to see which mails have succeeded and which mails have failed to be sent.

Examples

  • Send a single message synchronously and store the result in memory: #set ($mailResult = $services.mailsender.send($message))
  • Send N messages synchronously and store the result in memory:

    #set ($mailResult = $services.mailsender.send([$message1, $message2, ...]))
    OR
    #set ($mailResult = $services.mailsender.send([$message1, $message2, ...], 'memory'))

  • Send N messages asynchronously and store the result in memory: #set ($mailResult = $services.mailsender.sendAsynchronously([$message1, $message2, ...], 'memory'))
  • Send N messages asynchronously and store the results in the database: #set ($mailResult = $services.mailsender.sendAsynchronously([$message1, $message2, ...], 'database'))

Check the Sent Emails Status

When mails are sent asynchronously, it is possible to check the status of the sending process by calling: public boolean isProcessed() {} as follows: 

  • $mailResult.isProcessed() for versions prior to 7.1
  • $mailResult.statusResult.isProcessed() for versions starting with 7.1

Example for versions prior to 7.1:

// Returns true when the process is over for the batch (when all mails have been sent or have failed to be sent)
$mailResult.isProcessed()

// Wait 10 seconds till the mails are sent (the passed timeout is expressed in milliseconds)
$mailResult.statusResult.waitTillProcessed(10000L)

Example for versions starting with 7.1:

// Returns true when the process is over for the batch (when all mails have been sent or have failed to be sent)
$mailResult.statusResult.isProcessed()

// Wait 10 seconds till the mails are sent (the passed timeout is expressed in milliseconds)
$mailResult.statusResult.waitTillProcessed(10000L)

To check the status for all email messages, use Iterator<MailStatus> getAll() {}:

#set ($mailStatuses = $mailResult.statusResult.getAll())
#foreach ($mailStatus in $mailStatuses)
  * Mail ($mailStatus.messageId) - Date Sent: $mailStatus.date - State: $mailStatus.state -
Error: $mailStatus.errorSummary
#end

To filter messages by a given state (ready to send, sent successfully or failed to send) use Iterator<MailStatus> getByState(MailState state) {}

Examples

  • Check for mails sent successfully:
    #set ($mailStatuses = $mailResult.statusResult.getByState('SENT'))
    #foreach ($mailStatus in $mailStatuses)
      * Mail ($mailStatus.messageId) - Date Sent: $mailStatus.date
    #end
  • Check for mails that have failed to be sent:
    // For versions prior to 7.1

    #set ($mailStatuses = $mailResult.statusResult.getByState('FAILED'))
    #foreach ($mailStatus in $mailStatuses)

      {{error}}
        $mailStatus.errorSummary
        $mailStatus.errorDescription
      {{/error}}

    #end
    // For versions starting with 7.1

    #set ($mailStatuses = $mailResult.statusResult.getByState('SEND_ERROR'))
    #foreach ($mailStatus in $mailStatuses)

      {{error}}
        $mailStatus.errorSummary
        $mailStatus.errorDescription
      {{/error}}

    #end
Checking statuses using getAll() or getByState() should be used only when a small number of mails are sent at once. When sending a large number of mails, you should always use the database Mail Listener and you should retrieve results using the Storage Script Service.
  • Check for errors that can occur before the mails have been processed:
    #if ($services.mailsender.lastError)
      {{error}}$exceptiontool.getStackTrace($services.mailsender.lastError){{/error}}
    #end

This situation might happen when:

  • an error occurred when creating the message(s) using the $services.mailsender.createMessage(...) APIs
  • the page containing the sending script doesn't have Programming Rights
  • the Mail Listener referenced by the second parameter of $services.mailsender.send(messages, mailListenerHint) doesn't exist

Access the Configuration

To access the Mail Sending configuration, you can use the following example where we define a default from email address in case it is not already defined in the configuration.

#set ($from = $services.mailsender.configuration.fromAddress)
#if ("$!from" == '')
  #set ($from = "no-reply@${request.serverName}")
#end

MimeBodyPartFactory Implementations

When adding content to the message with the addPart() script API, the code first looks for a Component that is implementing MimeBodyPartFactory and using the passed mimeType as a Hint. If no component is found and the source is a String, the it uses the default MimeBodyPartFactory . Otherwise, it throws an exception.

The available implementations are:

  • default - creates a text Message Body Part.
  • text/html - creates an HTML BodyPart that supports:
    • a text alternative 
    • a list of attachments that will be added to the mail as standard attachments and also as embedded images if they are referenced in the passed HTML using the format cid:(attachment name).
  • xwiki/attachment - creates an attachment Body Part from an Attachment object.
  • xwiki/template - creates an Body Part from a Document Reference pointing to a Document that has an attached "XWiki.Mail" XObject (the first one found is used). 

When evaluating Velocity in Mail Templates, the Execution Context used is a clone of the one that was available when the send*() method was called. Thus, all the existing Velocity bindings are available from your Mail Template (request, xwiki, services, Velocity Tools, etc).

Specialized Message Factories

Specialized Message factories are used to create pre-filled Message objects. For instance, you can create a message for which the subject is automatically computed from a template, by evaluating its "subject" property with Velocity. A template is a wiki page with an attached "XWiki.Mail" object. 

Generic API:

#set ($message = $services.mailsender.createMessage(hint, source, parameters))
#set ($messages = $services.mailsender.createMessages(hint, source, parameters))

where:

  • hint is the Component hint of the component implementing the org.xwiki.mail.MimeMessageFactory role.
  • source depends on the hint used - when the hint is template, the source represents the Document Reference to a page containing an "XWiki.Mail" object.
  • parameters also depends on the hint used - when the hint is template, it is used to pass Velocity variables and values to use when evaluating the "subject" property of the "XWiki.Mail" object.

Examples

  

Search this space