Jira is able to notify users by email of events that occurred on issues, such as state transition or comment posted. These notifications are configured in a Notification Scheme. Each event is associated with a list of recipients : specific users, group members, users associated to a project role , … Each project can have its own Notification Scheme, but they can also be shared among several projects. Unfortunately, if Jira offers a way to configure when and to whom notifications are sent, it does not provide anything to customize the content of these emails. The purpose of this article is to briefly describe how it is possible to customize the content, and to give a very handy pratical example : adding a summary panel in the emails.
Velocity templates and Jira customization
Apache Velocity is a widely adopted template engine written in Java used to render customized content. Jira uses Apache Velocity to render most of its content, and especially the email notifications, therefore it is possible to customize email content by modifying the corresponding templates. These templates are located under directory ‘WEB-INF/classes/templates’, located either in the installation directory if you installed Jira using the automatic installer, or in the WAR file (or unzipped WAR) if you chose WAR installation.
There are two kinds of notifications : HTML and text. Jira sends the correct kind of notification to each user based on his profile settings. The corresponding templates are located respectively under ‘WEB-INF/classes/templates/emails/html’ and ‘WEB-INF/classes/templates/emails/text’. In each location you can find a set of Velocity files (*.vm), each one corresponding to the event that will trigger the sending of the email (e.g. issueassigned.vm, issueclosed.vm, …). As example, here’s the template of the HTML email sent on issue creation extracted from pristine Jira 4.4 :
#parse("templates/email/html/includes/emailconstants.vm") #parse("templates/email/html/includes/header.vm") <tr valign="top"> <td id="email-banner" style="padding:32px 32px 0 32px;"> #if ($commentauthor) #set ($changelogauthorLink = "#authorlink2($commentauthor.name $linkstyle)") #else #set ($changelogauthorLink = "#authorlink2($remoteUser.name $linkstyle)") #end #set ($issueType = $issue.getIssueTypeObject()) #set ($issueLink = "#renderIcon(${issueType.iconUrlHtml} ${issueType.getNameTranslation($i18n)}) <a style='color:{textLinkColour};text-decoration:none;' href='${baseurl}/browse/${issue.getKey()}'>$issue.getKey()</a>") #emailbanner($commentauthor "email.event.activity.comment" $changelogauthorLink $issueLink "") </td> </tr> #if ($comment) <tr valign="top"> <td id="email-fields" style="padding:0 32px 32px 32px;"> <table border="0" cellpadding="0" cellspacing="0" style="padding:0;text-align:left;width:100%;" width="100%"> <tr valign="top"> <td id="email-gutter" style="width:64px;white-space:nowrap;"></td> #parse("templates/email/html/includes/issue-summary.vm") <td> <table border="0" cellpadding="0" cellspacing="0" width="100%"> #parse("templates/email/html/includes/fields/comment.vm") </table> </td> </tr> </table> </td> </tr> #end #parse("templates/email/html/includes/footer.vm")
The lines starting with ‘#‘ are Velocity directives. Main directives are :
- #set : used to set variable values.
- #if : used to include content conditionally.
- #parse : used to parse and include another template in the current one. Note that the directive ‘#include‘ also exits, but it does not parse the included file with the Velocity engine.
- #foreach : used for looping.
It’s also possible to define ‘custom directives’ called Velocimacros. The directive ‘#emailbanner(…)‘ in the previous template is an example of such Velocimacro.
I’s worth to note that Jira comes with a set of reusable components that can be included in email notifications, such as footer and header, located under “includes” directory. Especially, the directory “includes/fields” contains templates that render properly issue field values.
Inside directives, tokens that starts with ‘$‘ are references to variables. Behind variable values, hide Java objects on which methods can be invoked and property values can be retrieved. Finally, along with variables directly created in the template, there’s a set of other variables accessible in the template. This set of variables is called the context and is filled by the Java code before triggering the rendering of the template.
The Velocity context for Jira email templates contains among other things the issue related to the email (‘$issue‘), the parameters of the triggering event (‘$params‘), as well as some utils, such as $userutils, $jirautils, $jirakeyutils, $stringUtils, … All this put together allows a very fine grained level of customization in Jira email notifications.
Example use case : adding a summary panel in jira notifications
Finally, here’s a very handy use case we implemented in our corporate Jira instance. We decided to integrate a small panel containing a summary of the issue on the left of the email. This panel would contain most important field values : the issue type, the priority, the status, the assignee and the due date.
To easily insert this panel in several templates we created a reusable component that includes all desired fields. that we placed in the ‘includes’ directory. Here’s the code for ‘includes/issue-summary.vm’ :
<td style="width: 30%; border: 1px solid #bbbbbb; padding: 10px;"> <table border="0" cellpadding="0" cellspacing="0" width="100%"> <tr> <td colspan="2" style="color:#3c78b5;font-family:Arial,FreeSans,Helvetica,sans-serif;font-size:12px;padding:0 10px 10px 0;white-space:nowrap;"> <strong>Issue Summary</strong> </td> </tr> #parse("templates/email/html/includes/fields/issuetype.vm") #parse("templates/email/html/includes/fields/priority.vm") #parse("templates/email/html/includes/fields/status.vm") #parse("templates/email/html/includes/fields/reporter.vm") #parse("templates/email/html/includes/fields/assignee.vm") #parse("templates/email/html/includes/fields/duedate.vm") </table> </td> <td style="width:10px;white-space:nowrap;"></td>
Notice that we didn’t wrap field values inside table row tags (<tr/>), as the templates we render already does it. Also note that we wrapped our content inside a table cell tag (<td/>), as the main content of the email templates is wrapped inside a table cell too. That way, just adding the directive #parse(“…/includes/issue-summary.vm”) before the main content in an email template will automatically add the summary panel on the left of the email. Note that placing the directive just after the main content will place the summary panel on the right.
Here’s an example of the usage of our issue summary inside issuecommented.vm. Notice the #parse(…) directive in line 25 :
#parse("templates/email/html/includes/emailconstants.vm") #parse("templates/email/html/includes/header.vm") <tr valign="top"> <td id="email-banner" style="padding:32px 32px 0 32px;"> #if ($commentauthor) #set ($changelogauthorLink = "#authorlink2($commentauthor.name $linkstyle)") #else #set ($changelogauthorLink = "#authorlink2($remoteUser.name $linkstyle)") #end #set ($issueType = $issue.getIssueTypeObject()) #set ($issueLink = "#renderIcon(${issueType.iconUrlHtml} ${issueType.getNameTranslation($i18n)}) <a style='color:{textLinkColour};text-decoration:none;' href='${baseurl}/browse/${issue.getKey()}'>$issue.getKey()</a>") #emailbanner($commentauthor "email.event.activity.comment" $changelogauthorLink $issueLink "") </td> </tr> #if ($comment) <tr valign="top"> <td id="email-fields" style="padding:0 32px 32px 32px;"> <table border="0" cellpadding="0" cellspacing="0" style="padding:0;text-align:left;width:100%;" width="100%"> <tr valign="top"> <td id="email-gutter" style="width:64px;white-space:nowrap;"></td> #parse("templates/email/html/includes/issue-summary.vm") <td> <table border="0" cellpadding="0" cellspacing="0" width="100%"> #parse("templates/email/html/includes/fields/comment.vm") </table> </td> </tr> </table> </td> </tr> #end #parse("templates/email/html/includes/footer.vm")
And here’s a sample of our modified notification :
Conclusion
Thanks to Atlassian’s choice to use Velocity templates, a widespread and well documented template framework, it’s possible to easily customize Jira content, even though Atlassian does not provide any tool to do it. This is not limited to the email notifications, every template under ‘WEB-INF/classes/templates‘ can be customized, even though the risk to break something is higher in screen templates than in email templates.
The conditional and looping directives of Velocity combined with the rich context provided by Jira allows a very fined grained customization. The content can be customized based on many things, such as the logged on user and its groups, the concerned project, current state of the issue, … , and the inclusion mechanism of Velocity allows to keep the complexity of the templates under control.
Finally, pay attention that Atlassian does not provide support for this feature. They will not be able to help you if trouble occurs with customized content.