NIHVIVO-2693 Add Freemarker user-defined directive for email and integrate into email messaging framework.
This commit is contained in:
parent
9c9f7dfe57
commit
2fcff042e6
9 changed files with 260 additions and 18 deletions
|
@ -200,7 +200,7 @@ public class UserAccountsEditPage extends UserAccountsPage {
|
||||||
|
|
||||||
userAccountsDao.updateUserAccount(userAccount);
|
userAccountsDao.updateUserAccount(userAccount);
|
||||||
|
|
||||||
strategy.notifyUser();
|
strategy.notifyUser(vreq);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean wasPasswordEmailSent() {
|
public boolean wasPasswordEmailSent() {
|
||||||
|
|
|
@ -47,7 +47,7 @@ public abstract class UserAccountsEditPageStrategy extends UserAccountsPage {
|
||||||
|
|
||||||
protected abstract void setAdditionalProperties(UserAccount u);
|
protected abstract void setAdditionalProperties(UserAccount u);
|
||||||
|
|
||||||
protected abstract void notifyUser();
|
protected abstract void notifyUser(VitroRequest vreq);
|
||||||
|
|
||||||
protected abstract boolean wasPasswordEmailSent();
|
protected abstract boolean wasPasswordEmailSent();
|
||||||
|
|
||||||
|
@ -57,7 +57,8 @@ public abstract class UserAccountsEditPageStrategy extends UserAccountsPage {
|
||||||
|
|
||||||
private static class EmailStrategy extends UserAccountsEditPageStrategy {
|
private static class EmailStrategy extends UserAccountsEditPageStrategy {
|
||||||
private static final String PARAMETER_RESET_PASSWORD = "resetPassword";
|
private static final String PARAMETER_RESET_PASSWORD = "resetPassword";
|
||||||
|
private static final String EMAIL_TEMPLATE = "userAccounts-resetPasswordEmail.ftl";
|
||||||
|
|
||||||
public static final String RESET_PASSWORD_URL = "/accounts/resetPassword";
|
public static final String RESET_PASSWORD_URL = "/accounts/resetPassword";
|
||||||
|
|
||||||
private boolean resetPassword;
|
private boolean resetPassword;
|
||||||
|
@ -94,7 +95,7 @@ public abstract class UserAccountsEditPageStrategy extends UserAccountsPage {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void notifyUser() {
|
protected void notifyUser(VitroRequest vreq) {
|
||||||
if (!resetPassword) {
|
if (!resetPassword) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -102,16 +103,21 @@ public abstract class UserAccountsEditPageStrategy extends UserAccountsPage {
|
||||||
Map<String, Object> body = new HashMap<String, Object>();
|
Map<String, Object> body = new HashMap<String, Object>();
|
||||||
body.put("userAccount", page.getUpdatedAccount());
|
body.put("userAccount", page.getUpdatedAccount());
|
||||||
body.put("passwordLink", buildResetPasswordLink());
|
body.put("passwordLink", buildResetPasswordLink());
|
||||||
body.put("subjectLine", "Reset password request");
|
//body.put("subjectLine", "Reset password request");
|
||||||
|
|
||||||
FreemarkerEmailMessage email = FreemarkerEmailFactory
|
FreemarkerEmailMessage email = FreemarkerEmailFactory
|
||||||
.createNewMessage(vreq);
|
.createNewMessage(vreq);
|
||||||
email.addRecipient(TO, page.getUpdatedAccount().getEmailAddress());
|
email.addRecipient(TO, page.getUpdatedAccount().getEmailAddress());
|
||||||
email.setSubject("Reset password request");
|
|
||||||
email.setHtmlTemplate("userAccounts-resetPasswordEmail-html.ftl");
|
vreq.setAttribute("email", email);
|
||||||
email.setTextTemplate("userAccounts-resetPasswordEmail-text.ftl");
|
|
||||||
email.setBodyMap(body);
|
email.processTemplate(vreq, EMAIL_TEMPLATE, body);
|
||||||
email.send();
|
|
||||||
|
//email.setSubject("Reset password request");
|
||||||
|
//email.setHtmlTemplate("userAccounts-resetPasswordEmail-html.ftl");
|
||||||
|
//email.setTextTemplate("userAccounts-resetPasswordEmail-text.ftl");
|
||||||
|
//email.setBodyMap(body);
|
||||||
|
//email.send();
|
||||||
|
|
||||||
sentEmail = true;
|
sentEmail = true;
|
||||||
}
|
}
|
||||||
|
@ -193,7 +199,7 @@ public abstract class UserAccountsEditPageStrategy extends UserAccountsPage {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void notifyUser() {
|
protected void notifyUser(VitroRequest vreq) {
|
||||||
// Do nothing.
|
// Do nothing.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -306,11 +306,18 @@ public class FreemarkerHttpServlet extends VitroHttpServlet {
|
||||||
*/
|
*/
|
||||||
public static Map<String, Object> getDirectives() {
|
public static Map<String, Object> getDirectives() {
|
||||||
Map<String, Object> map = new HashMap<String, Object>();
|
Map<String, Object> map = new HashMap<String, Object>();
|
||||||
map.put("dump", new freemarker.ext.dump.DumpDirective());
|
map.putAll(getDirectivesForAllEnvironments());
|
||||||
map.put("dumpAll", new freemarker.ext.dump.DumpAllDirective());
|
|
||||||
map.put("help", new freemarker.ext.dump.HelpDirective());
|
|
||||||
map.put("url", new edu.cornell.mannlib.vitro.webapp.web.directives.UrlDirective());
|
map.put("url", new edu.cornell.mannlib.vitro.webapp.web.directives.UrlDirective());
|
||||||
map.put("widget", new edu.cornell.mannlib.vitro.webapp.web.directives.WidgetDirective());
|
map.put("widget", new edu.cornell.mannlib.vitro.webapp.web.directives.WidgetDirective());
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<String, Object> getDirectivesForAllEnvironments() {
|
||||||
|
Map<String, Object> map = new HashMap<String, Object>();
|
||||||
|
map.put("dump", new freemarker.ext.dump.DumpDirective());
|
||||||
|
map.put("dumpAll", new freemarker.ext.dump.DumpAllDirective());
|
||||||
|
map.put("help", new freemarker.ext.dump.HelpDirective());
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
package edu.cornell.mannlib.vitro.webapp.email;
|
package edu.cornell.mannlib.vitro.webapp.email;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.StringWriter;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -27,9 +29,14 @@ import javax.servlet.http.HttpServletRequest;
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
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;
|
||||||
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.TemplateProcessingHelper.TemplateProcessingException;
|
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.TemplateProcessingHelper.TemplateProcessingException;
|
||||||
|
import freemarker.core.Environment;
|
||||||
import freemarker.template.Configuration;
|
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
|
* A framework that makes it simpler to send email messages with a body built
|
||||||
|
@ -146,6 +153,69 @@ public class FreemarkerEmailMessage {
|
||||||
.unmodifiableMap(new HashMap<String, Object>(body));
|
.unmodifiableMap(new HashMap<String, Object>(body));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void processTemplate(VitroRequest vreq, String templateName, Map<String, Object> map) {
|
||||||
|
|
||||||
|
vreq.setAttribute("email", this);
|
||||||
|
|
||||||
|
map.putAll(FreemarkerHttpServlet.getDirectivesForAllEnvironments());
|
||||||
|
map.put("email", new edu.cornell.mannlib.vitro.webapp.web.directives.EmailDirective());
|
||||||
|
|
||||||
|
try {
|
||||||
|
Template template = config.getTemplate(templateName);
|
||||||
|
StringWriter writer = new StringWriter();
|
||||||
|
Environment env = template.createProcessingEnvironment(map, writer);
|
||||||
|
env.setCustomAttribute("request", vreq);
|
||||||
|
env.process();
|
||||||
|
} catch (TemplateException e) {
|
||||||
|
log.error(e, e);
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error(e, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.setSubject(subject);
|
||||||
|
|
||||||
|
if (html.isEmpty()) {
|
||||||
|
if (html.isEmpty()) {
|
||||||
|
log.error("Message has neither text body nor HTML body");
|
||||||
|
} else {
|
||||||
|
msg.setContent(html, "text/html");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (html.isEmpty()) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void send() {
|
public void send() {
|
||||||
String textBody = figureMessageBody(textTemplateName);
|
String textBody = figureMessageBody(textTemplateName);
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
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 javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
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.SimpleScalar;
|
||||||
|
import freemarker.template.TemplateDirectiveBody;
|
||||||
|
import freemarker.template.TemplateException;
|
||||||
|
import freemarker.template.TemplateModel;
|
||||||
|
import freemarker.template.TemplateModelException;
|
||||||
|
|
||||||
|
public class EmailDirective extends BaseTemplateDirectiveModel {
|
||||||
|
|
||||||
|
private static final Log log = LogFactory.getLog(EmailDirective.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(Environment env, Map params, TemplateModel[] loopVars,
|
||||||
|
TemplateDirectiveBody body) throws TemplateException, IOException {
|
||||||
|
|
||||||
|
Object o = params.get("subject");
|
||||||
|
if (o == null) {
|
||||||
|
throw new TemplateModelException(
|
||||||
|
"The email directive requires a value for parameter 'subject'.");
|
||||||
|
}
|
||||||
|
if (! ( o instanceof SimpleScalar)) {
|
||||||
|
throw new TemplateModelException(
|
||||||
|
"The email directive requires a string value for parameter 'subject'.");
|
||||||
|
}
|
||||||
|
String subject = o.toString();
|
||||||
|
|
||||||
|
o = params.get("html");
|
||||||
|
if (o == null) {
|
||||||
|
throw new TemplateModelException(
|
||||||
|
"The email directive requires a value for parameter 'html'.");
|
||||||
|
}
|
||||||
|
if (! ( o instanceof SimpleScalar)) {
|
||||||
|
throw new TemplateModelException(
|
||||||
|
"The email directive requires a string value for parameter 'html'.");
|
||||||
|
}
|
||||||
|
String html = o.toString();
|
||||||
|
|
||||||
|
o = params.get("text");
|
||||||
|
if (o == null) {
|
||||||
|
throw new TemplateModelException(
|
||||||
|
"The email directive requires a value for parameter 'text'.");
|
||||||
|
}
|
||||||
|
if (! ( o instanceof SimpleScalar)) {
|
||||||
|
throw new TemplateModelException(
|
||||||
|
"The email directive requires a string value for parameter 'text'.");
|
||||||
|
}
|
||||||
|
String text = o.toString();
|
||||||
|
|
||||||
|
HttpServletRequest request = (HttpServletRequest) env.getCustomAttribute("request");
|
||||||
|
|
||||||
|
o = (FreemarkerEmailMessage) request.getAttribute("email");
|
||||||
|
if ( o == null) {
|
||||||
|
throw new TemplateModelException(
|
||||||
|
"No email object found in the request.");
|
||||||
|
}
|
||||||
|
if ( ! (o instanceof FreemarkerEmailMessage)) {
|
||||||
|
throw new TemplateModelException(
|
||||||
|
"Invalid value for request email attribute");
|
||||||
|
}
|
||||||
|
FreemarkerEmailMessage email = (FreemarkerEmailMessage) o;
|
||||||
|
email.send(subject, html, text);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@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<String, String> params = new HashMap<String, String>();
|
||||||
|
params.put("subject", "email subject");
|
||||||
|
params.put("html", "HTML version of email message");
|
||||||
|
params.put("text", "Plain text version of email message");
|
||||||
|
map.put("parameters", params);
|
||||||
|
|
||||||
|
List<String> examples = new ArrayList<String>();
|
||||||
|
examples.add("<email subject=\"Password reset confirmation\" html=html text=text>");
|
||||||
|
map.put("examples", examples);
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,6 +15,7 @@ import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder;
|
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder;
|
||||||
import freemarker.core.Environment;
|
import freemarker.core.Environment;
|
||||||
|
import freemarker.template.SimpleScalar;
|
||||||
import freemarker.template.TemplateDirectiveBody;
|
import freemarker.template.TemplateDirectiveBody;
|
||||||
import freemarker.template.TemplateException;
|
import freemarker.template.TemplateException;
|
||||||
import freemarker.template.TemplateModel;
|
import freemarker.template.TemplateModel;
|
||||||
|
@ -44,12 +45,19 @@ public class UrlDirective extends BaseTemplateDirectiveModel {
|
||||||
"The url directive doesn't allow nested content.");
|
"The url directive doesn't allow nested content.");
|
||||||
}
|
}
|
||||||
|
|
||||||
String path = params.get("path").toString();
|
Object o = params.get("path");
|
||||||
if (path == null) {
|
if (o == null) {
|
||||||
throw new TemplateModelException(
|
throw new TemplateModelException(
|
||||||
"The url directive requires a value for parameter 'path'.");
|
"The url directive requires a value for parameter 'path'.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (! ( o instanceof SimpleScalar)) {
|
||||||
|
throw new TemplateModelException(
|
||||||
|
"The url directive requires a string value for parameter 'path'.");
|
||||||
|
}
|
||||||
|
|
||||||
|
String path = o.toString();
|
||||||
|
|
||||||
if (!path.startsWith("/")) {
|
if (!path.startsWith("/")) {
|
||||||
throw new TemplateModelException(
|
throw new TemplateModelException(
|
||||||
"The url directive requires that the value of parameter 'path' is an absolute path starting with '/'.");
|
"The url directive requires that the value of parameter 'path' is an absolute path starting with '/'.");
|
||||||
|
@ -60,6 +68,7 @@ public class UrlDirective extends BaseTemplateDirectiveModel {
|
||||||
out.write(url);
|
out.write(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Map<String, Object> help(String name) {
|
public Map<String, Object> help(String name) {
|
||||||
Map<String, Object> map = new LinkedHashMap<String, Object>();
|
Map<String, Object> map = new LinkedHashMap<String, Object>();
|
||||||
|
|
||||||
|
@ -77,6 +86,5 @@ public class UrlDirective extends BaseTemplateDirectiveModel {
|
||||||
|
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,6 +94,7 @@ public class WidgetDirective extends BaseTemplateDirectiveModel {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Map<String, Object> help(String name) {
|
public Map<String, Object> help(String name) {
|
||||||
Map<String, Object> map = new LinkedHashMap<String, Object>();
|
Map<String, Object> map = new LinkedHashMap<String, Object>();
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
|
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
|
||||||
|
|
||||||
<#-- Confirmation that an password has been reset. -->
|
<#-- Confirmation that a password has been reset. -->
|
||||||
|
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
|
||||||
|
|
||||||
|
<#-- Confirmation email for user account password reset -->
|
||||||
|
|
||||||
|
<#assign subject = "Reset password request" />
|
||||||
|
|
||||||
|
<#assign html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>${subject}</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
${userAccount.firstName} ${userAccount.lastName}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<strong>Password successfully changed.</strong>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Your new password associated with ${userAccount.emailAddress} has been changed.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Thank you.
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</#assign>
|
||||||
|
|
||||||
|
<#assign text>
|
||||||
|
${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!
|
||||||
|
</#assign>
|
||||||
|
|
||||||
|
<@email subject=subject html=html text=text />
|
Loading…
Add table
Reference in a new issue