NIHVIVO-2299 Improve the way Email templates are handled - specify HTML and plain text in the same template. Refine the EmailDirective to permit optional parameters.

This commit is contained in:
j2blake 2011-06-15 18:23:07 +00:00
parent d42acf48d8
commit 5d587265fa
20 changed files with 237 additions and 387 deletions

View file

@ -57,6 +57,7 @@ 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 = "userAccounts-acctCreatedEmail.ftl";
private boolean sentEmail;
@ -91,15 +92,14 @@ public abstract class UserAccountsAddPageStrategy extends UserAccountsPage {
Map<String, Object> body = new HashMap<String, Object>();
body.put("userAccount", page.getAddedAccount());
body.put("passwordLink", buildCreatePasswordLink());
body.put("subjectLine", "Your VIVO account has been created.");
FreemarkerEmailMessage email = FreemarkerEmailFactory
.createNewMessage(vreq);
email.addRecipient(TO, page.getAddedAccount().getEmailAddress());
email.setSubject("Your VIVO account has been created.");
email.setHtmlTemplate("userAccounts-acctCreatedEmail-html.ftl");
email.setTextTemplate("userAccounts-acctCreatedEmail-text.ftl");
email.setTemplate(EMAIL_TEMPLATE);
email.setBodyMap(body);
email.processTemplate();
email.send();
sentEmail = true;

View file

@ -103,22 +103,15 @@ public abstract class UserAccountsEditPageStrategy extends UserAccountsPage {
Map<String, Object> body = new HashMap<String, Object>();
body.put("userAccount", page.getUpdatedAccount());
body.put("passwordLink", buildResetPasswordLink());
String siteName = getSiteName();
body.put("siteName", siteName);
body.put("siteName", getSiteName());
FreemarkerEmailMessage email = FreemarkerEmailFactory
.createNewMessage(vreq);
email.addRecipient(TO, page.getUpdatedAccount().getEmailAddress());
email.setTemplate(EMAIL_TEMPLATE);
email.setBodyMap(body);
email.setDefaultSubject(getDefaultSubject(siteName));
email.setDefaultHtml(getDefaultHtml());
email.setDefaultText(getDefaultText());
vreq.setAttribute("email", email);
email.processTemplate(vreq);
email.processTemplate();
email.send();
sentEmail = true;
}
@ -128,18 +121,6 @@ public abstract class UserAccountsEditPageStrategy extends UserAccountsPage {
return appBean.getApplicationName();
}
private String getDefaultSubject(String siteName) {
return siteName + " reset password request";
}
private String getDefaultHtml() {
return "";
}
private String getDefaultText() {
return "Default text for user accounts edit page";
}
private String buildResetPasswordLink() {
try {
String email = page.getUpdatedAccount().getEmailAddress();

View file

@ -26,6 +26,7 @@ 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);
@ -56,15 +57,14 @@ public class UserAccountsCreatePasswordPage extends
private void notifyUser() {
Map<String, Object> body = new HashMap<String, Object>();
body.put("userAccount", userAccount);
body.put("subjectLine", "Password successfully created.");
FreemarkerEmailMessage email = FreemarkerEmailFactory
.createNewMessage(vreq);
email.addRecipient(TO, userAccount.getEmailAddress());
email.setSubject("Password successfully created.");
email.setHtmlTemplate("userAccounts-passwordCreatedEmail-html.ftl");
email.setTextTemplate("userAccounts-passwordCreatedEmail-text.ftl");
email.setTemplate(EMAIL_TEMPLATE);
email.setBodyMap(body);
email.processTemplate();
email.send();
}
}

View file

@ -52,6 +52,8 @@ 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);
@ -66,15 +68,14 @@ public abstract class UserAccountsFirstTimeExternalPageStrategy extends
public void notifyUser(UserAccount ua) {
Map<String, Object> body = new HashMap<String, Object>();
body.put("userAccount", ua);
body.put("subjectLine", "Your VIVO account has been created.");
FreemarkerEmailMessage email = FreemarkerEmailFactory
.createNewMessage(vreq);
email.addRecipient(TO, ua.getEmailAddress());
email.setSubject("Your VIVO account has been created.");
email.setHtmlTemplate("userAccounts-firstTimeExternalEmail-html.ftl");
email.setTextTemplate("userAccounts-firstTimeExternalEmail-text.ftl");
email.setTemplate(EMAIL_TEMPLATE);
email.setBodyMap(body);
email.processTemplate();
email.send();
}

View file

@ -108,6 +108,8 @@ 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;
@ -167,15 +169,14 @@ public abstract class UserAccountsMyAccountPageStrategy extends
Map<String, Object> body = new HashMap<String, Object>();
body.put("userAccount", page.getUserAccount());
body.put("subjectLine", "Your VIVO email account has been changed.");
FreemarkerEmailMessage email = FreemarkerEmailFactory
.createNewMessage(vreq);
email.addRecipient(TO, page.getUserAccount().getEmailAddress());
email.setSubject("Your VIVO email account has been changed.");
email.setHtmlTemplate("userAccounts-confirmEmailChangedEmail-html.ftl");
email.setTextTemplate("userAccounts-confirmEmailChangedEmail-text.ftl");
email.setTemplate(EMAIL_TEMPLATE);
email.setBodyMap(body);
email.processTemplate();
email.send();
emailSent = true;

View file

@ -26,6 +26,8 @@ public class UserAccountsResetPasswordPage extends UserAccountsPasswordBasePage
private static final String TEMPLATE_NAME = "userAccounts-resetPassword.ftl";
private static final String EMAIL_TEMPLATE = "userAccounts-passwordResetEmail.ftl";
protected UserAccountsResetPasswordPage(VitroRequest vreq) {
super(vreq);
}
@ -55,15 +57,14 @@ public class UserAccountsResetPasswordPage extends UserAccountsPasswordBasePage
private void notifyUser() {
Map<String, Object> body = new HashMap<String, Object>();
body.put("userAccount", userAccount);
body.put("subjectLine", "Password changed.");
FreemarkerEmailMessage email = FreemarkerEmailFactory
.createNewMessage(vreq);
email.addRecipient(TO, userAccount.getEmailAddress());
email.setSubject("Password changed.");
email.setHtmlTemplate("userAccounts-passwordResetEmail-html.ftl");
email.setTextTemplate("userAccounts-passwordResetEmail-text.ftl");
email.setTemplate(EMAIL_TEMPLATE);
email.setBodyMap(body);
email.processTemplate();
email.send();
}

View file

@ -23,17 +23,13 @@ import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang.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.controller.freemarker.FreemarkerHttpServlet;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.TemplateProcessingHelper;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.TemplateProcessingHelper.TemplateProcessingException;
import edu.cornell.mannlib.vitro.webapp.web.directives.EmailDirective;
import freemarker.core.Environment;
import freemarker.template.Configuration;
import freemarker.template.Template;
@ -43,8 +39,14 @@ import freemarker.template.TemplateException;
* A framework that makes it simpler to send email messages with a body built
* from a Freemarker template.
*
* In fact, the body can be plain text from a template, HTML from a template, or
* both.
* The template must contain 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.
*
* The directive also calls the send() method here.
*
* @see EmailDirective
*/
public class FreemarkerEmailMessage {
private static final Log log = LogFactory
@ -55,23 +57,17 @@ public class FreemarkerEmailMessage {
private final HttpServletRequest req;
private final Session session;
private final Configuration config;
private final ServletContext ctx;
private final List<Recipient> recipients = new ArrayList<Recipient>();
private final InternetAddress replyToAddress;
private InternetAddress fromAddress = null;
private String subject = "";
private String defaultSubject = "";
private String defaultHtml = "";
private String defaultText = "";
private String templateName;
private String templateName = "";
private String htmlContent = "";
private String textContent = "";
private Map<String, Object> bodyMap = Collections.emptyMap();
// TO BE REMOVED
private String htmlTemplateName;
private String textTemplateName;
/**
* Package access - should only be created by the factory.
*/
@ -81,8 +77,6 @@ public class FreemarkerEmailMessage {
this.session = session;
this.replyToAddress = replyToAddress;
this.ctx = req.getSession().getServletContext();
Object o = req.getAttribute(ATTRIBUTE_NAME);
if (!(o instanceof Configuration)) {
String oClass = (o == null) ? "null" : o.getClass().getName();
@ -129,10 +123,6 @@ public class FreemarkerEmailMessage {
try {
recipients.add(new Recipient(type, emailAddress, personalName));
} catch (AddressException e) {
log.warn("invalid recipient address: " + type + ", '"
+ emailAddress + "', personal name '" + personalName + "'");
return;
} catch (UnsupportedEncodingException e) {
log.warn("invalid recipient address: " + type + ", '"
+ emailAddress + "', personal name '" + personalName + "'");
@ -140,36 +130,22 @@ public class FreemarkerEmailMessage {
}
}
// TO BE REMOVED
public void setHtmlTemplate(String templateName) {
this.htmlTemplateName = nonNull(templateName, "");
}
// TO BE REMOVED
public void setTextTemplate(String templateName) {
this.textTemplateName = nonNull(templateName, "");
}
public void setSubject(String subject) {
this.subject = nonNull(subject, "");
}
public void setHtmlContent(String htmlContent) {
this.htmlContent = nonNull(htmlContent, "");
}
public void setTextContent(String textContent) {
this.textContent = nonNull(textContent, "");
}
public void setTemplate(String templateName) {
this.templateName = nonNull(templateName, "");
}
public void setDefaultSubject(String defaultSubject) {
this.defaultSubject = nonNull(defaultSubject, "");
}
public void setDefaultHtml(String defaultHtml) {
this.defaultHtml = nonNull(defaultHtml, "");
}
public void setDefaultText(String defaultText) {
this.defaultText = nonNull(defaultText, "");
}
public void setBodyMap(Map<String, Object> body) {
if (body == null) {
this.bodyMap = Collections.emptyMap();
@ -178,18 +154,15 @@ public class FreemarkerEmailMessage {
}
}
public void processTemplate(VitroRequest vreq) {
vreq.setAttribute("emailMessage", this);
public void processTemplate() {
bodyMap.putAll(FreemarkerHttpServlet.getDirectivesForAllEnvironments());
bodyMap.put("email", new edu.cornell.mannlib.vitro.webapp.web.directives.EmailDirective());
bodyMap.put("email", new EmailDirective(this));
try {
Template template = config.getTemplate(templateName);
StringWriter writer = new StringWriter();
Environment env = template.createProcessingEnvironment(bodyMap, writer);
env.setCustomAttribute("request", vreq);
Environment env = template.createProcessingEnvironment(bodyMap,
new StringWriter());
env.setCustomAttribute("request", req);
env.process();
} catch (TemplateException e) {
log.error(e, e);
@ -198,66 +171,7 @@ public class FreemarkerEmailMessage {
}
}
public void send(String subject, String html, String text) {
try {
MimeMessage msg = new MimeMessage(session);
msg.setReplyTo(new Address[] { replyToAddress });
if (fromAddress == null) {
msg.addFrom(new Address[] { replyToAddress });
} else {
msg.addFrom(new Address[] { fromAddress });
}
for (Recipient recipient : recipients) {
msg.addRecipient(recipient.type, recipient.address);
}
if (subject == null) {
log.debug("No email subject specified in template. Using default subject.");
subject = defaultSubject;
}
msg.setSubject(subject);
if (html == null) {
log.debug("No html email specified in template. Using default html.");
html = defaultHtml;
}
if (text == null) {
log.debug("No plain text email specified in template. Using default html.");
text = defaultText;
}
if (StringUtils.isEmpty(text)) {
if (StringUtils.isEmpty(html)) {
log.error("Message has neither text body nor HTML body");
} else {
msg.setContent(html, "text/html");
}
} else if (StringUtils.isEmpty(html)) {
msg.setContent(text, "text/plain");
} else {
MimeMultipart content = new MimeMultipart("alternative");
addBodyPart(content, text, "text/plain");
addBodyPart(content, html, "text/html");
msg.setContent(content);
}
msg.setSentDate(new Date());
Transport.send(msg);
} catch (MessagingException e) {
log.error("Failed to send message.", e);
}
}
// TO BE REMOVED
public void send() {
String textBody = figureMessageBody(textTemplateName);
String htmlBody = figureMessageBody(htmlTemplateName);
try {
MimeMessage msg = new MimeMessage(session);
msg.setReplyTo(new Address[] { replyToAddress });
@ -274,19 +188,19 @@ public class FreemarkerEmailMessage {
msg.setSubject(subject);
if (textBody.isEmpty()) {
if (htmlBody.isEmpty()) {
if (textContent.isEmpty()) {
if (htmlContent.isEmpty()) {
log.error("Message has neither text body nor HTML body");
} else {
msg.setContent(htmlBody, "text/html");
msg.setContent(htmlContent, "text/html");
}
} else {
if (htmlBody.isEmpty()) {
msg.setContent(textBody, "text/plain");
if (htmlContent.isEmpty()) {
msg.setContent(textContent, "text/plain");
} else {
MimeMultipart content = new MimeMultipart("alternative");
addBodyPart(content, textBody, "text/plain");
addBodyPart(content, htmlBody, "text/html");
addBodyPart(content, textContent, "text/plain");
addBodyPart(content, htmlContent, "text/html");
msg.setContent(content);
}
}
@ -300,27 +214,6 @@ public class FreemarkerEmailMessage {
}
}
/**
* Process the template. If there is no template name or if there is a
* problem with the process, return an empty string.
*/
// TO BE REMOVED
private String figureMessageBody(String templateName) {
if (templateName.isEmpty()) {
return "";
}
try {
TemplateProcessingHelper helper = new TemplateProcessingHelper(
config, req, ctx);
return helper.processTemplate(templateName, bodyMap).toString();
} catch (TemplateProcessingException e) {
log.warn("Exception while processing email template '"
+ templateName + "'", e);
return "";
}
}
private void addBodyPart(MimeMultipart content, String textBody, String type)
throws MessagingException {
MimeBodyPart bodyPart = new MimeBodyPart();
@ -343,7 +236,7 @@ public class FreemarkerEmailMessage {
}
public Recipient(RecipientType type, String address, String personalName)
throws AddressException, UnsupportedEncodingException {
throws UnsupportedEncodingException {
this.type = type;
this.address = new InternetAddress(address, personalName);
}

View file

@ -9,8 +9,6 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@ -22,58 +20,67 @@ 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 {
HttpServletRequest request = (HttpServletRequest) env.getCustomAttribute("request");
FreemarkerEmailMessage email = null;
Object paramValue = (FreemarkerEmailMessage) request.getAttribute("emailMessage");
if ( paramValue == null) {
throw new TemplateModelException(
"No email message object found in the request.");
}
if ( ! (paramValue instanceof FreemarkerEmailMessage)) {
throw new TemplateModelException(
"Invalid value for request email attribute");
}
email = (FreemarkerEmailMessage) paramValue;
// Read in parameter values. If a value is undefined by the template, the
// default values defined by the email object will be used.
String subject = null;
paramValue = params.get("subject");
if (paramValue != null && paramValue instanceof SimpleScalar) {
subject = paramValue.toString();
String subject = getOptionalSimpleScalarParameter(params, "subject");
if (subject != null) {
message.setSubject(subject);
}
String html = null;
paramValue = params.get("html");
if (paramValue != null && paramValue instanceof SimpleScalar) {
html = paramValue.toString();
String htmlContent = getOptionalSimpleScalarParameter(params, "html");
if (htmlContent != null) {
message.setHtmlContent(htmlContent);
}
String text = null;
paramValue = params.get("text");
if (paramValue != null && paramValue instanceof SimpleScalar) {
text = paramValue.toString();
String textContent = getOptionalSimpleScalarParameter(params, "text");
if (textContent != null) {
message.setTextContent(textContent);
}
email.send(subject, html, text);
if ((htmlContent == null) && (textContent == null)) {
throw new TemplateModelException("The email directive must have "
+ "either a 'html' parameter or a 'text' parameter.");
}
}
private String getOptionalSimpleScalarParameter(Map<?, ?> params,
String name) throws TemplateModelException {
Object o = params.get(name);
if (o == null) {
return null;
}
if (!(o instanceof SimpleScalar)) {
throw new TemplateModelException("The '" + name + "' parameter "
+ "for the email directive must be a string value.");
}
return o.toString();
}
@Override
public Map<String, Object> help(String name) {
Map<String, Object> map = new LinkedHashMap<String, Object>();
map.put("effect", "Create an email message from the parameters set in the invoking template.");
map.put("comment", "Parameter values undefined by the template will be provided by controller default values.");
map.put("effect",
"Create an email message from the parameters set in the invoking template.");
Map<String, String> params = new HashMap<String, String>();
params.put("subject", "email subject (optional)");

View file

@ -1,20 +0,0 @@
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
<#-- Confirmation that an account has been created. -->
${userAccount.firstName} ${userAccount.lastName}
Congratulations!
We have created your new VIVO account associated with
${userAccount.emailAddress}.
If you did not request this new account you can safely ignore this email.
This request will expire if not acted upon for 30 days.
Paste the link below into your browser's address bar to create your password
for your new account using our secure server.
${passwordLink}
Thanks!

View file

@ -2,9 +2,12 @@
<#-- Confirmation that an account has been created. -->
<#assign subject = "Your VIVO account has been created." />
<#assign html>
<html>
<head>
<title>${subjectLine}</title>
<title>${subject}</title>
</head>
<body>
<p>
@ -41,3 +44,25 @@
</p>
</body>
</html>
</#assign>
<#assign text>
${userAccount.firstName} ${userAccount.lastName}
Congratulations!
We have created your new VIVO account associated with
${userAccount.emailAddress}.
If you did not request this new account you can safely ignore this email.
This request will expire if not acted upon for 30 days.
Paste the link below into your browser's address bar to create your password
for your new account using our secure server.
${passwordLink}
Thanks!
</#assign>
<@email subject=subject html=html text=text />

View file

@ -1,10 +0,0 @@
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
<#-- Confirmation that the user has changed his email account. -->
Hi, ${userAccount.firstName} ${userAccount.lastName}
You recently changed the email address associated with
${userAccount.firstName} ${userAccount.lastName}
Thank you.

View file

@ -2,9 +2,12 @@
<#-- Confirmation that the user has changed his email account. -->
<#assign subject = "Your VIVO email account has been changed." />
<#assign html>
<html>
<head>
<title>${subjectLine}</title>
<title>${subject}</title>
</head>
<body>
<p>
@ -21,3 +24,15 @@
</p>
</body>
</html>
</#assign>
<#assign text>
Hi, ${userAccount.firstName} ${userAccount.lastName}
You recently changed the email address associated with
${userAccount.firstName} ${userAccount.lastName}
Thank you.
</#assign>
<@email subject=subject html=html text=text />

View file

@ -1,12 +0,0 @@
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
<#-- Confirmation that an account has been created for an externally-authenticated user. -->
${userAccount.firstName} ${userAccount.lastName}
Congratulations!
We have created your new VIVO account associated with
${userAccount.emailAddress}.
Thanks!

View file

@ -2,9 +2,12 @@
<#-- Confirmation that an account has been created for an externally-authenticated user. -->
<#assign subject = "Your VIVO account has been created." />
<#assign html>
<html>
<head>
<title>${subjectLine}</title>
<title>${subject}</title>
</head>
<body>
<p>
@ -24,3 +27,17 @@
</p>
</body>
</html>
</#assign>
<#assign text>
${userAccount.firstName} ${userAccount.lastName}
Congratulations!
We have created your new VIVO account associated with
${userAccount.emailAddress}.
Thanks!
</#assign>
<@email subject=subject html=html text=text />

View file

@ -1,12 +0,0 @@
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
<#-- Confirmation that a password has been created. -->
${userAccount.firstName} ${userAccount.lastName}
Password successfully created.
Your new password associated with ${userAccount.emailAddress}
has been created.
Thank you.

View file

@ -2,9 +2,12 @@
<#-- Confirmation that an password has been created. -->
<#assign subject = "Password successfully created." />
<#assign html>
<html>
<head>
<title>${subjectLine}</title>
<title>${subject}</title>
</head>
<body>
<p>
@ -24,3 +27,17 @@
</p>
</body>
</html>
</#assign>
<#assign text>
${userAccount.firstName} ${userAccount.lastName}
Password successfully created.
Your new password associated with ${userAccount.emailAddress}
has been created.
Thank you.
</#assign>
<@email subject=subject html=html text=text />

View file

@ -1,12 +0,0 @@
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
<#-- Confirmation that a password has been reset. -->
${userAccount.firstName} ${userAccount.lastName}
Password successfully changed.
Your new password associated with ${userAccount.emailAddress}
has been changed.
Thank you.

View file

@ -2,9 +2,12 @@
<#-- Confirmation that a password has been reset. -->
<#assign subject = "Password changed." />
<#assign html>
<html>
<head>
<title>${subjectLine}</title>
<title>${subject}</title>
</head>
<body>
<p>
@ -24,3 +27,17 @@
</p>
</body>
</html>
</#assign>
<#assign text>
${userAccount.firstName} ${userAccount.lastName}
Password successfully changed.
Your new password associated with ${userAccount.emailAddress}
has been changed.
Thank you.
</#assign>
<@email subject=subject html=html text=text />

View file

@ -1,40 +0,0 @@
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
<#-- Notification that your password has been reset. -->
<html>
<head>
<title>${subjectLine}</title>
</head>
<body>
<p>
${userAccount.firstName} ${userAccount.lastName}
</p>
<p>
We received a request to reset the password for your account (${userAccount.emailAddress}).
Please follow the instructions below to proceed with your password reset.
</p>
<p>
If you did not request this new account you can safely ignore this email.
This request will expire if not acted upon for 30 days.
</p>
<p>
Click the link below to reset your password using our secure server.
</p>
<p>
<a href="${passwordLink}">${passwordLink}</a>
</p>
<p>
If the link above doesn't work, you can copy and paste the link directly into your browser's address bar.
</p>
<p>
Thank you!
</p>
</body>
</html>

View file

@ -1,19 +0,0 @@
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
<#-- Notification that your password has been reset. -->
${userAccount.firstName} ${userAccount.lastName}
We received a request to reset the password for your account
(${userAccount.emailAddress}).
Please follow the instructions below to proceed with your password reset.
If you did not request this new account you can safely ignore this email.
This request will expire if not acted upon for 30 days.
Paste the link below into your browser's address bar to reset your password
using our secure server.
${passwordLink}
Thank you!