001: /*
002: * Copyright 2005-2007 The Kuali Foundation.
003: *
004: *
005: * Licensed under the Educational Community License, Version 1.0 (the "License");
006: * you may not use this file except in compliance with the License.
007: * You may obtain a copy of the License at
008: *
009: * http://www.opensource.org/licenses/ecl1.php
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: // Created on Mar 21, 2007
018: package edu.iu.uis.eden.mail;
019:
020: import java.io.StringWriter;
021: import java.util.ArrayList;
022: import java.util.Collection;
023:
024: import javax.xml.parsers.DocumentBuilder;
025: import javax.xml.parsers.DocumentBuilderFactory;
026: import javax.xml.parsers.ParserConfigurationException;
027: import javax.xml.transform.Templates;
028: import javax.xml.transform.TransformerConfigurationException;
029: import javax.xml.transform.TransformerException;
030: import javax.xml.transform.TransformerFactory;
031: import javax.xml.transform.dom.DOMResult;
032: import javax.xml.transform.dom.DOMSource;
033: import javax.xml.transform.stream.StreamResult;
034: import javax.xml.transform.stream.StreamSource;
035: import javax.xml.xpath.XPath;
036: import javax.xml.xpath.XPathConstants;
037: import javax.xml.xpath.XPathExpressionException;
038: import javax.xml.xpath.XPathFactory;
039:
040: import org.apache.log4j.Logger;
041: import org.w3c.dom.Document;
042: import org.w3c.dom.Element;
043: import org.w3c.dom.Node;
044:
045: import edu.iu.uis.eden.EdenConstants;
046: import edu.iu.uis.eden.actionitem.ActionItem;
047: import edu.iu.uis.eden.doctype.DocumentType;
048: import edu.iu.uis.eden.edl.StyleService;
049: import edu.iu.uis.eden.exception.WorkflowRuntimeException;
050: import edu.iu.uis.eden.feedback.web.FeedbackForm;
051: import edu.iu.uis.eden.plugin.attributes.CustomEmailAttribute;
052: import edu.iu.uis.eden.user.WorkflowUser;
053: import edu.iu.uis.eden.util.Utilities;
054: import edu.iu.uis.eden.util.XmlHelper;
055:
056: /**
057: * EmailContentService that serves EmailContent customizable via XSLT style sheets
058: * The global email style name is: kew.email.style
059: * If this style is not found, the resource 'defaultEmailStyle.xsl' will be retrieved
060: * relative to this class.
061: * @author Aaron Hamid (arh14 at cornell dot edu)
062: */
063: public class StyleableEmailContentServiceImpl extends
064: BaseEmailContentServiceImpl {
065: private static final Logger LOG = Logger
066: .getLogger(StyleableEmailContentServiceImpl.class);
067:
068: protected final String DEFAULT_EMAIL_STYLESHEET_RESOURCE_LOC = "defaultEmailStyle.xsl";
069:
070: protected StyleService styleService;
071: protected String globalEmailStyleSheet = EdenConstants.EMAIL_STYLESHEET_NAME;
072:
073: public void setStyleService(StyleService styleService) {
074: this .styleService = styleService;
075: }
076:
077: public void setGlobalEmailStyleSheet(String globalEmailStyleSheet) {
078: this .globalEmailStyleSheet = globalEmailStyleSheet;
079: }
080:
081: protected static DocumentBuilder getDocumentBuilder(boolean coalesce) {
082: try {
083: DocumentBuilderFactory dbf = DocumentBuilderFactory
084: .newInstance();
085: dbf.setCoalescing(coalesce);
086: return dbf.newDocumentBuilder();
087: } catch (ParserConfigurationException e) {
088: String message = "Error constructing document builder";
089: LOG.error(message, e);
090: throw new WorkflowRuntimeException(message, e);
091: }
092: }
093:
094: protected static void addObjectXML(Document doc, Object o,
095: Node node, String name) throws Exception {
096: Element element = XmlHelper.propertiesToXml(doc, o, name);
097:
098: LOG.info(XmlHelper.jotNode(element));
099:
100: if (node == null) {
101: node = doc;
102: }
103:
104: node.appendChild(element);
105: }
106:
107: protected static void addActionItem(Document doc,
108: ActionItem actionItem, WorkflowUser user, Node node)
109: throws Exception {
110: if (node == null) {
111: node = doc;
112: }
113:
114: Element root = doc.createElement("actionItem");
115:
116: // append the custom body and subject if they exist
117: try {
118: CustomEmailAttribute customEmailAttribute = getCustomEmailAttribute(
119: user, actionItem);
120: if (customEmailAttribute != null) {
121: String customBody = customEmailAttribute
122: .getCustomEmailBody();
123: if (!Utilities.isEmpty(customBody)) {
124: Element element = doc.createElement("customBody");
125: element.appendChild(doc.createTextNode(customBody));
126: root.appendChild(element);
127: }
128: String customEmailSubject = customEmailAttribute
129: .getCustomEmailSubject();
130: if (!Utilities.isEmpty(customEmailSubject)) {
131: Element element = doc
132: .createElement("customSubject");
133: element.appendChild(doc
134: .createTextNode(customEmailSubject));
135: root.appendChild(element);
136: }
137: }
138: } catch (Exception e) {
139: LOG
140: .error(
141: "Error when checking for custom email body and subject.",
142: e);
143: }
144:
145: // keep adding stuff until we have all the xml we need to formulate the message :/
146:
147: addObjectXML(doc, actionItem, root, "actionItem");
148:
149: addObjectXML(doc, actionItem.getUser(), root, "actionItemUser");
150:
151: addObjectXML(doc, actionItem.getUser()
152: .getAuthenticationUserId(), root,
153: "actionItemAuthenticationUserId");
154:
155: addObjectXML(doc, actionItem.getRouteHeader(), root, "doc");
156:
157: addObjectXML(doc, actionItem.getRouteHeader()
158: .getInitiatorUser(), root, "docInitiator");
159:
160: DocumentType docType = actionItem.getRouteHeader()
161: .getDocumentType();
162: addObjectXML(doc, docType, root, "documentType");
163:
164: node.appendChild(root);
165: }
166:
167: protected static String transform(Templates style, Document doc) {
168: StringWriter writer = new StringWriter();
169: StreamResult result = new StreamResult(writer);
170:
171: try {
172: style.newTransformer()
173: .transform(new DOMSource(doc), result);
174: return writer.toString();
175: } catch (TransformerException te) {
176: String message = "Error transforming DOM";
177: LOG.error(message, te);
178: throw new WorkflowRuntimeException(message, te);
179: }
180: }
181:
182: protected EmailContent generateEmailContent(String styleName,
183: Document doc) {
184: Templates style = null;
185: try {
186: style = styleService.getStyleAsTranslet(styleName);
187: } catch (TransformerConfigurationException tce) {
188: String message = "Error obtaining style '" + styleName
189: + "', using default";
190: LOG.error(message, tce);
191: // throw new WorkflowRuntimeException("Error obtaining style '" + styleName + "'", tce);
192: }
193:
194: if (style == null) {
195: LOG.warn("Could not find specified style, " + styleName
196: + ", using default");
197: try {
198: style = TransformerFactory
199: .newInstance()
200: .newTemplates(
201: new StreamSource(
202: getClass()
203: .getResourceAsStream(
204: DEFAULT_EMAIL_STYLESHEET_RESOURCE_LOC)));
205: } catch (TransformerConfigurationException tce) {
206: String message = "Error obtaining default style from resource: "
207: + DEFAULT_EMAIL_STYLESHEET_RESOURCE_LOC;
208: LOG.error(message, tce);
209: throw new WorkflowRuntimeException(
210: "Error obtaining style '" + styleName + "'",
211: tce);
212: }
213: }
214:
215: DOMResult result = new DOMResult();
216:
217: LOG.debug("Input document: "
218: + XmlHelper.jotNode(doc.getDocumentElement(), true));
219: try {
220: style.newTransformer()
221: .transform(new DOMSource(doc), result);
222: } catch (TransformerException te) {
223: String message = "Error transforming immediate reminder DOM";
224: LOG.error(message, te);
225: throw new WorkflowRuntimeException(message, te);
226: }
227:
228: Node node = result.getNode();
229:
230: LOG.debug("Email document: " + XmlHelper.jotNode(doc));
231: XPathFactory xpf = XPathFactory.newInstance();
232: XPath xpath = xpf.newXPath();
233: try {
234: String subject = (String) xpath.evaluate("/email/subject",
235: node, XPathConstants.STRING);
236: String body = (String) xpath.evaluate("/email/body", node,
237: XPathConstants.STRING);
238: // simple heuristic to determine whether content is HTML
239: return new EmailContent(subject, body, body
240: .matches("(?msi).*<(\\w+:)?html.*"));
241: } catch (XPathExpressionException xpee) {
242: throw new WorkflowRuntimeException(
243: "Error evaluating generated email content", xpee);
244: }
245: }
246:
247: protected EmailContent generateReminderForActionItems(
248: WorkflowUser user, Collection<ActionItem> actionItems,
249: String name, String style) {
250: DocumentBuilder db = getDocumentBuilder(false);
251: Document doc = db.newDocument();
252: Element element = doc.createElement(name);
253: setStandardAttributes(element);
254: doc.appendChild(element);
255:
256: try {
257: addObjectXML(doc, user, element, "user");
258: for (ActionItem actionItem : actionItems) {
259: try {
260: addActionItem(doc, actionItem, user, element);
261: } catch (Exception e) {
262: String message = "Error generating XML for action item: "
263: + actionItem;
264: LOG.error(message, e);
265: throw new WorkflowRuntimeException(e);
266: }
267: }
268:
269: } catch (Exception e) {
270: String message = "Error generating XML for action items: "
271: + actionItems;
272: LOG.error(message, e);
273: throw new WorkflowRuntimeException(e);
274: }
275:
276: return generateEmailContent(style, doc);
277: }
278:
279: protected void setStandardAttributes(Element e) {
280: e.setAttribute("env", getDeploymentEnvironment());
281: e.setAttribute("applicationEmailAddress",
282: getApplicationEmailAddress());
283: e.setAttribute("actionListUrl", getActionListUrl());
284: e.setAttribute("preferencesUrl", getPreferencesUrl());
285: }
286:
287: public EmailContent generateImmediateReminder(WorkflowUser user,
288: ActionItem actionItem, DocumentType documentType) {
289: Collection<ActionItem> actionItems = new ArrayList<ActionItem>(
290: 1);
291: actionItems.add(actionItem);
292:
293: // change style name based on documentType when configurable email style on document is implemented...
294: String styleSheet = documentType.getCustomEmailStylesheet();
295: LOG.error(documentType.getName() + " style: " + styleSheet);
296: if (styleSheet == null) {
297: styleSheet = globalEmailStyleSheet;
298: }
299:
300: LOG.error("generateImmediateReminder: style: " + styleSheet);
301: return generateReminderForActionItems(user, actionItems,
302: "immediateReminder", styleSheet);
303: }
304:
305: public EmailContent generateWeeklyReminder(WorkflowUser user,
306: Collection<ActionItem> actionItems) {
307: return generateReminderForActionItems(user, actionItems,
308: "weeklyReminder", globalEmailStyleSheet);
309: }
310:
311: public EmailContent generateDailyReminder(WorkflowUser user,
312: Collection<ActionItem> actionItems) {
313: return generateReminderForActionItems(user, actionItems,
314: "dailyReminder", globalEmailStyleSheet);
315: }
316:
317: public EmailContent generateFeedback(FeedbackForm form) {
318: DocumentBuilder db = getDocumentBuilder(true);
319: Document doc = db.newDocument();
320: String styleSheet = globalEmailStyleSheet;
321:
322: // if the doc type is specified, see if that doc has a custom email stylesheet and use it
323: // NOTE: do we need to do this for feedback? presumably feedback will be going back to admins
324: /*String docTypeName = form.getDocumentType();
325: if (!StringUtils.isBlank(docTypeName)) {
326: DocumentType docType = KEWServiceLocator.getDocumentTypeService().findByName(docTypeName);
327: if (docType == null) {
328: LOG.error("User specified document type '" + docTypeName + "' in feedback form, but the document type was not found in the system");
329: } else {
330: if (docType.getCustomEmailStylesheet() != null) {
331: styleSheet = docType.getCustomEmailStylesheet();
332: }
333: }
334: }*/
335: LOG.info("form: " + form.getRouteHeaderId());
336: try {
337: addObjectXML(doc, form, null, "feedback");
338: } catch (Exception e) {
339: String message = "Error generating XML for feedback form: "
340: + form;
341: LOG.error(message, e);
342: throw new WorkflowRuntimeException(message, e);
343: }
344: setStandardAttributes(doc.getDocumentElement());
345:
346: return generateEmailContent(styleSheet, doc);
347: }
348: }
|