diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsAddPageStrategy.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsAddPageStrategy.java
index 036d314e2..3d546f96a 100644
--- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsAddPageStrategy.java
+++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsAddPageStrategy.java
@@ -57,8 +57,6 @@ public abstract class UserAccountsAddPageStrategy extends UserAccountsPage {
private static class EmailStrategy extends UserAccountsAddPageStrategy {
public static final String CREATE_PASSWORD_URL = "/accounts/createPassword";
- private static final String EMAIL_TEMPLATE_WITH_PASSWORD = "userAccounts-acctCreatedEmail.ftl";
- private static final String EMAIL_TEMPLATE_NO_PASSWORD = "userAccounts-acctCreatedExternalOnlyEmail.ftl";
private boolean sentEmail;
@@ -100,15 +98,19 @@ public abstract class UserAccountsAddPageStrategy extends UserAccountsPage {
body.put("userAccount", page.getAddedAccount());
body.put("passwordLink", buildCreatePasswordLink());
body.put("siteName", getSiteName());
-
FreemarkerEmailMessage email = FreemarkerEmailFactory
.createNewMessage(vreq);
email.addRecipient(TO, page.getAddedAccount().getEmailAddress());
- email.setSubject(i18n.text("account_created_subject", getSiteName()));
+ String subject = i18n.text("account_created_subject", getSiteName());
+ email.setSubject(subject);
if (page.isExternalAuthOnly()) {
- email.setTemplate(EMAIL_TEMPLATE_NO_PASSWORD);
+ body.put("subject", subject);
+ body.put("textMessage", i18n.text("acct_created_external_only_email_plain_text"));
+ body.put("htmlMessage", i18n.text("acct_created_external_only_email_html_text"));
} else {
- email.setTemplate(EMAIL_TEMPLATE_WITH_PASSWORD);
+ body.put("subject", subject);
+ body.put("textMessage", i18n.text("acct_created_email_plain_text"));
+ body.put("htmlMessage", i18n.text("acct_created_email_html_text"));
}
email.setBodyMap(body);
email.processTemplate();
diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsEditPageStrategy.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsEditPageStrategy.java
index d9ee8aa14..be364fb75 100644
--- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsEditPageStrategy.java
+++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsEditPageStrategy.java
@@ -56,8 +56,6 @@ public abstract class UserAccountsEditPageStrategy extends UserAccountsPage {
private static class EmailStrategy extends UserAccountsEditPageStrategy {
private static final String PARAMETER_RESET_PASSWORD = "resetPassword";
- private static final String EMAIL_TEMPLATE = "userAccounts-passwordResetPendingEmail.ftl";
-
public static final String RESET_PASSWORD_URL = "/accounts/resetPassword";
private boolean resetPassword;
@@ -107,11 +105,13 @@ public abstract class UserAccountsEditPageStrategy extends UserAccountsPage {
body.put("userAccount", page.getUpdatedAccount());
body.put("passwordLink", buildResetPasswordLink());
body.put("siteName", getSiteName());
+ body.put("subject", i18n.text("password_reset_pending_email_subject"));
+ body.put("textMessage",i18n.text("password_reset_pending_email_plain_text"));
+ body.put("htmlMessage", i18n.text("password_reset_pending_email_html_text"));
FreemarkerEmailMessage email = FreemarkerEmailFactory
.createNewMessage(vreq);
email.addRecipient(TO, page.getUpdatedAccount().getEmailAddress());
- email.setTemplate(EMAIL_TEMPLATE);
email.setBodyMap(body);
email.processTemplate();
email.send();
diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsCreatePasswordPage.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsCreatePasswordPage.java
index 68daa2d67..5ba3aba76 100644
--- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsCreatePasswordPage.java
+++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsCreatePasswordPage.java
@@ -26,8 +26,6 @@ public class UserAccountsCreatePasswordPage extends
.getLog(UserAccountsCreatePasswordPage.class);
private static final String TEMPLATE_NAME = "userAccounts-createPassword.ftl";
- private static final String EMAIL_TEMPLATE = "userAccounts-passwordCreatedEmail.ftl";
-
public UserAccountsCreatePasswordPage(VitroRequest vreq) {
super(vreq);
}
@@ -73,8 +71,11 @@ public class UserAccountsCreatePasswordPage extends
FreemarkerEmailMessage email = FreemarkerEmailFactory
.createNewMessage(vreq);
email.addRecipient(TO, userAccount.getEmailAddress());
- email.setSubject(i18n.text("password_created_subject", getSiteName()));
- email.setTemplate(EMAIL_TEMPLATE);
+ final String subject = i18n.text("password_created_subject", getSiteName());
+ email.setSubject(subject);
+ body.put("subject", subject);
+ body.put("textMessage", i18n.text("password_created_email_plain_text"));
+ body.put("htmlMessage", i18n.text("password_created_email_html_text"));
email.setBodyMap(body);
email.processTemplate();
email.send();
diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsFirstTimeExternalPageStrategy.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsFirstTimeExternalPageStrategy.java
index 447d2d223..1b9486e78 100644
--- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsFirstTimeExternalPageStrategy.java
+++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsFirstTimeExternalPageStrategy.java
@@ -52,8 +52,6 @@ public abstract class UserAccountsFirstTimeExternalPageStrategy extends
public static class EmailStrategy extends
UserAccountsFirstTimeExternalPageStrategy {
- private static final String EMAIL_TEMPLATE = "userAccounts-firstTimeExternalEmail.ftl";
-
public EmailStrategy(VitroRequest vreq,
UserAccountsFirstTimeExternalPage page) {
super(vreq, page);
@@ -73,8 +71,11 @@ public abstract class UserAccountsFirstTimeExternalPageStrategy extends
FreemarkerEmailMessage email = FreemarkerEmailFactory
.createNewMessage(vreq);
email.addRecipient(TO, ua.getEmailAddress());
- email.setSubject(i18n.text("account_created_subject", getSiteName()));
- email.setTemplate(EMAIL_TEMPLATE);
+ final String subject = i18n.text("account_created_subject", getSiteName());
+ email.setSubject(subject);
+ body.put("subject", subject);
+ body.put("textMessage", i18n.text("first_time_external_email_plain_text"));
+ body.put("htmlMessage", i18n.text("first_time_external_email_html_text"));
email.setBodyMap(body);
email.processTemplate();
email.send();
diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsMyAccountPageStrategy.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsMyAccountPageStrategy.java
index ca895cab8..34481efbe 100644
--- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsMyAccountPageStrategy.java
+++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsMyAccountPageStrategy.java
@@ -108,8 +108,6 @@ public abstract class UserAccountsMyAccountPageStrategy extends
private static final String ERROR_WRONG_PASSWORD_LENGTH = "errorPasswordIsWrongLength";
private static final String ERROR_PASSWORDS_DONT_MATCH = "errorPasswordsDontMatch";
- private static final String EMAIL_TEMPLATE = "userAccounts-confirmEmailChangedEmail.ftl";
-
private final String originalEmail;
private String newPassword;
@@ -179,8 +177,11 @@ public abstract class UserAccountsMyAccountPageStrategy extends
FreemarkerEmailMessage email = FreemarkerEmailFactory
.createNewMessage(vreq);
email.addRecipient(TO, page.getUserAccount().getEmailAddress());
- email.setSubject(i18n.text("email_changed_subject", getSiteName()));
- email.setTemplate(EMAIL_TEMPLATE);
+ final String subject = i18n.text("email_changed_subject", getSiteName());
+ email.setSubject(subject);
+ body.put("subject", subject);
+ body.put("textMessage", i18n.text("confirm_email_changed_email_plain_text"));
+ body.put("htmlMessage", i18n.text("confirm_email_changed_email_html_text"));
email.setBodyMap(body);
email.processTemplate();
email.send();
diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsResetPasswordPage.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsResetPasswordPage.java
index f865cbe94..8e22ae043 100644
--- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsResetPasswordPage.java
+++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsResetPasswordPage.java
@@ -26,8 +26,6 @@ public class UserAccountsResetPasswordPage extends UserAccountsPasswordBasePage
private static final String TEMPLATE_NAME = "userAccounts-resetPassword.ftl";
- private static final String EMAIL_TEMPLATE = "userAccounts-passwordResetCompleteEmail.ftl";
-
protected UserAccountsResetPasswordPage(VitroRequest vreq) {
super(vreq);
}
@@ -68,14 +66,16 @@ public class UserAccountsResetPasswordPage extends UserAccountsPasswordBasePage
private void notifyUser() {
Map body = new HashMap();
+ FreemarkerEmailMessage email = FreemarkerEmailFactory.createNewMessage(vreq);
+ final String subject = i18n.text("password_changed_subject");
+ email.setSubject(subject);
+
body.put("userAccount", userAccount);
body.put("siteName", getSiteName());
-
- FreemarkerEmailMessage email = FreemarkerEmailFactory
- .createNewMessage(vreq);
+ body.put("subject", subject);
+ body.put("textMessage", i18n.text("password_reset_complete_email_plain_text"));
+ body.put("htmlMessage", i18n.text("password_reset_complete_email_html_text"));
email.addRecipient(TO, userAccount.getEmailAddress());
- email.setSubject(i18n.text("password_changed_subject"));
- email.setTemplate(EMAIL_TEMPLATE);
email.setBodyMap(body);
email.processTemplate();
email.send();
diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/FreemarkerHttpServlet.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/FreemarkerHttpServlet.java
index 50f54e2e0..cd416b931 100644
--- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/FreemarkerHttpServlet.java
+++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/FreemarkerHttpServlet.java
@@ -8,7 +8,7 @@ import static javax.mail.Message.RecipientType.TO;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
-import java.nio.charset.Charset;
+import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
@@ -41,6 +41,8 @@ import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.Tem
import edu.cornell.mannlib.vitro.webapp.email.FreemarkerEmailFactory;
import edu.cornell.mannlib.vitro.webapp.email.FreemarkerEmailMessage;
import edu.cornell.mannlib.vitro.webapp.freemarker.config.FreemarkerConfiguration;
+import edu.cornell.mannlib.vitro.webapp.i18n.I18n;
+import edu.cornell.mannlib.vitro.webapp.i18n.I18nBundle;
import edu.cornell.mannlib.vitro.webapp.web.templatemodels.Tags;
import edu.cornell.mannlib.vitro.webapp.web.templatemodels.User;
import edu.cornell.mannlib.vitro.webapp.web.templatemodels.menu.MainMenu;
@@ -67,7 +69,6 @@ public class FreemarkerHttpServlet extends VitroHttpServlet {
// error templates
ERROR_DISPLAY("error-display.ftl"),
- ERROR_EMAIL("error-email.ftl"),
ERROR_MESSAGE("error-message.ftl"),
STANDARD_ERROR("error-standard.ftl"),
TITLED_ERROR_MESSAGE("error-titled.ftl"),
@@ -161,7 +162,8 @@ public class FreemarkerHttpServlet extends VitroHttpServlet {
}
adminErrorData.put("cause", cause);
- adminErrorData.put("datetime", new Date());
+ SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
+ adminErrorData.put("datetime", dateformat.format(new Date()));
templateMap.put("errorOnHomePage", this instanceof HomePageController);
@@ -175,7 +177,11 @@ public class FreemarkerHttpServlet extends VitroHttpServlet {
} else if (FreemarkerEmailFactory.isConfigured(vreq)) {
FreemarkerEmailMessage email = FreemarkerEmailFactory.createNewMessage(vreq);
email.addRecipient(TO, email.getReplyToAddress());
- email.setTemplate(Template.ERROR_EMAIL.toString());
+ I18nBundle i18n = I18n.bundle(vreq);
+ addSiteName(vreq, adminErrorData);
+ adminErrorData.put("subject", i18n.text("application_error_email_subject"));
+ adminErrorData.put("textMessage", i18n.text("application_error_email_plain_text"));
+ adminErrorData.put("htmlMessage", i18n.text("application_error_email_html_text"));
email.setBodyMap(adminErrorData);
email.processTemplate();
sentEmail = email.send();
@@ -193,6 +199,16 @@ public class FreemarkerHttpServlet extends VitroHttpServlet {
}
}
+ private void addSiteName(VitroRequest vreq, Map adminErrorData) {
+ try {
+ ApplicationBean appBean = vreq.getAppBean();
+ String appName = appBean.getApplicationName();
+ adminErrorData.put("siteName", appName);
+ } catch (Exception e) {
+ log.error(e,e);
+ }
+ }
+
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/email/FreemarkerEmailMessage.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/email/FreemarkerEmailMessage.java
index ee5a9ef17..c361955ec 100644
--- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/email/FreemarkerEmailMessage.java
+++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/email/FreemarkerEmailMessage.java
@@ -24,24 +24,19 @@ import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
+import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
-import edu.cornell.mannlib.vitro.webapp.web.directives.EmailDirective;
import freemarker.template.Configuration;
+import freemarker.template.Template;
import freemarker.template.TemplateException;
/**
* A framework that makes it simpler to send email messages with a body built
* from a Freemarker template.
*
- * The template must call the @email directive, which may provide the subject
- * line, the HTML content, and the plain text content. If these values are not
- * provided by the directive, they default to empty strings, or to values that
- * were set by the controller.
- *
- * @see EmailDirective
*/
public class FreemarkerEmailMessage {
private static final Log log = LogFactory
@@ -56,7 +51,6 @@ public class FreemarkerEmailMessage {
private InternetAddress fromAddress = null;
private String subject = "";
- private String templateName = "";
private String htmlContent = "";
private String textContent = "";
private Map bodyMap = Collections.emptyMap();
@@ -124,10 +118,6 @@ public class FreemarkerEmailMessage {
this.textContent = nonNull(textContent, "");
}
- public void setTemplate(String templateName) {
- this.templateName = nonNull(templateName, "");
- }
-
public void setBodyMap(Map body) {
if (body == null) {
this.bodyMap = Collections.emptyMap();
@@ -137,16 +127,52 @@ public class FreemarkerEmailMessage {
}
public void processTemplate() {
- bodyMap.put("email", new EmailDirective(this));
-
try {
- config.getTemplate(templateName).process(bodyMap,
- new StringWriter());
+ addDefaultBodyMapValues();
+ StringWriter writer = new StringWriter();
+ new Template(null, getInlineVariable("subject"), config).process(bodyMap, writer);
+ subject = writer.toString();
+ writer.getBuffer().setLength(0);
+ new Template(null, getEmailTemplate(), config).process(bodyMap, writer);
+ htmlContent = writer.toString();
+ writer.getBuffer().setLength(0);
+ new Template(null, getInlineVariable("textMessage"), config).process(bodyMap, writer);
+ textContent = writer.toString();
} catch (TemplateException | IOException e) {
log.error(e, e);
}
}
+ private void addDefaultBodyMapValues() {
+ if (!bodyMap.containsKey("subject")) {
+ if (StringUtils.isBlank(subject)) {
+ bodyMap.put("subject", "No subject defined");
+ }
+ bodyMap.put("subject", subject);
+ }
+ if (!bodyMap.containsKey("textMessage")) {
+ bodyMap.put("textMessage", "No text message defined");
+ }
+ if (!bodyMap.containsKey("htmlMessage")) {
+ bodyMap.put("htmlMessage", "No html message defined");
+ }
+ }
+
+ private String getInlineVariable(String name) {
+ return "<@" + name + "?interpret />";
+ }
+
+ private String getEmailTemplate() {
+ return "\n"
+ + " \n"
+ + " <@subject?interpret />\n"
+ + " \n"
+ + " \n"
+ + " <@htmlMessage?interpret />\n"
+ + " \n"
+ + "";
+ }
+
public boolean send() {
try {
MimeMessage msg = new MimeMessage(mailSession);
@@ -182,8 +208,7 @@ public class FreemarkerEmailMessage {
}
msg.setSentDate(new Date());
-
- Transport.send(msg);
+ sendMessage(msg);
return true;
} catch (MessagingException e) {
log.error("Failed to send message.", e);
@@ -191,6 +216,19 @@ public class FreemarkerEmailMessage {
}
}
+ private void sendMessage(MimeMessage msg) {
+ Thread thread = new Thread() {
+ public void run() {
+ try {
+ Transport.send(msg);
+ } catch (MessagingException e) {
+ log.error(e, e);
+ }
+ }
+ };
+ thread.start();
+ }
+
private void addBodyPart(MimeMultipart content, String textBody, String type)
throws MessagingException {
MimeBodyPart bodyPart = new MimeBodyPart();
diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/directives/EmailDirective.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/directives/EmailDirective.java
deleted file mode 100644
index 4b31d7d77..000000000
--- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/directives/EmailDirective.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/* $This file is distributed under the terms of the license in LICENSE$ */
-
-package edu.cornell.mannlib.vitro.webapp.web.directives;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-import edu.cornell.mannlib.vitro.webapp.email.FreemarkerEmailMessage;
-import freemarker.core.Environment;
-import freemarker.template.TemplateDirectiveBody;
-import freemarker.template.TemplateException;
-import freemarker.template.TemplateModel;
-import freemarker.template.TemplateModelException;
-
-/**
- * Process the inputs for a FreemarkerEmailMessage.
- *
- * @see FreemarkerEmailMessage
- */
-public class EmailDirective extends BaseTemplateDirectiveModel {
-
- private static final Log log = LogFactory.getLog(EmailDirective.class);
-
- private final FreemarkerEmailMessage message;
-
- public EmailDirective(FreemarkerEmailMessage message) {
- this.message = message;
- }
-
- @Override
- public void execute(Environment env, Map params, TemplateModel[] loopVars,
- TemplateDirectiveBody body) throws TemplateException, IOException {
-
- String subject = getOptionalSimpleScalarParameter(params, "subject");
- if (subject != null) {
- message.setSubject(subject);
- }
-
- String htmlContent = getOptionalSimpleScalarParameter(params, "html");
- if (htmlContent != null) {
- message.setHtmlContent(htmlContent);
- }
-
- String textContent = getOptionalSimpleScalarParameter(params, "text");
- if (textContent != null) {
- message.setTextContent(textContent);
- }
-
- if ((htmlContent == null) && (textContent == null)) {
- throw new TemplateModelException("The email directive must have "
- + "either a 'html' parameter or a 'text' parameter.");
- }
- }
-
- @Override
- public Map help(String name) {
- Map map = new LinkedHashMap();
-
- map.put("effect",
- "Create an email message from the parameters set in the invoking template.");
-
- Map params = new HashMap();
- params.put("subject", "email subject (optional)");
- params.put("html", "HTML version of email message (optional)");
- params.put("text", "Plain text version of email message (optional)");
- map.put("parameters", params);
-
- List examples = new ArrayList();
- examples.add("<email subject=\"Password reset confirmation\" html=html text=text>");
- examples.add("<email html=html text=text>");
- map.put("examples", examples);
-
- return map;
- }
-}
diff --git a/webapp/src/main/webapp/templates/freemarker/body/error/error-email.ftl b/webapp/src/main/webapp/templates/freemarker/body/error/error-email.ftl
deleted file mode 100644
index fa804266e..000000000
--- a/webapp/src/main/webapp/templates/freemarker/body/error/error-email.ftl
+++ /dev/null
@@ -1,62 +0,0 @@
-<#-- $This file is distributed under the terms of the license in LICENSE$ -->
-
-<#-- Template for email message sent to site administrator when an error occurs on the site. -->
-
-<#assign subject = "${i18n().error_occurred(siteName!)}" />
-
-<#assign datetime = datetime?string("yyyy-MM-dd HH:mm:ss zzz")>
-
-<#assign html>
-
-
- ${subject!}
-
-
-
- ${i18n().error_occurred_at(siteName!,datetime!)}
-
-
-
- ${i18n().requested_url}: ${requestedUrl!}
-
-
-
- <#if errorMessage?has_content>
- ${i18n().error_message}: ${errorMessage!}
- #if>
-
-
-
- ${i18n().stack_trace} (${i18n().trace_available(siteName!)}):
-
${stackTrace!}
-
-
- <#if cause?has_content>
- ${i18n().caused_by}:
-
${cause!}
-
- #if>
-
-
-
-#assign>
-
-<#assign text>
-${i18n().error_occurred_at(siteName!,datetime!)}
-
-${i18n().requested_url}: ${requestedUrl!}
-
-<#if errorMessage?has_content>
- ${i18n().error_message}: ${errorMessage!}
-#if>
-
-${i18n().stack_trace} (${i18n().trace_available(siteName!)}):
-${stackTrace!}
-
-<#if cause?has_content>
-${i18n().caused_by}:
-${cause!}
-#if>
-#assign>
-
-<@email subject=subject html=html text=text />