1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.kuali.rice.kew.mail.service.impl;
19
20 import org.apache.commons.lang.StringUtils;
21 import org.apache.log4j.Logger;
22 import org.kuali.rice.core.api.style.StyleService;
23 import org.kuali.rice.core.mail.EmailContent;
24 import org.kuali.rice.core.util.RiceConstants;
25 import org.kuali.rice.core.util.xml.XmlHelper;
26 import org.kuali.rice.core.util.xml.XmlJotter;
27 import org.kuali.rice.kew.actionitem.ActionItem;
28 import org.kuali.rice.kew.api.WorkflowRuntimeException;
29 import org.kuali.rice.kew.doctype.bo.DocumentType;
30 import org.kuali.rice.kew.feedback.web.FeedbackForm;
31 import org.kuali.rice.kew.mail.CustomEmailAttribute;
32 import org.kuali.rice.kew.mail.EmailStyleHelper;
33 import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue;
34 import org.kuali.rice.kew.routeheader.service.RouteHeaderService;
35 import org.kuali.rice.kew.service.KEWServiceLocator;
36 import org.kuali.rice.kew.user.UserUtils;
37 import org.kuali.rice.kew.util.KEWConstants;
38 import org.kuali.rice.kim.api.identity.principal.Principal;
39 import org.kuali.rice.kim.api.services.KimApiServiceLocator;
40 import org.kuali.rice.kim.bo.Person;
41 import org.kuali.rice.krad.util.GlobalVariables;
42 import org.springframework.core.io.DefaultResourceLoader;
43 import org.w3c.dom.Document;
44 import org.w3c.dom.Element;
45 import org.w3c.dom.Node;
46
47 import javax.xml.parsers.DocumentBuilder;
48 import javax.xml.parsers.DocumentBuilderFactory;
49 import javax.xml.parsers.ParserConfigurationException;
50 import javax.xml.transform.Templates;
51 import javax.xml.transform.TransformerConfigurationException;
52 import javax.xml.transform.TransformerException;
53 import javax.xml.transform.TransformerFactory;
54 import javax.xml.transform.dom.DOMSource;
55 import javax.xml.transform.stream.StreamResult;
56 import javax.xml.transform.stream.StreamSource;
57 import java.io.StringWriter;
58 import java.sql.Timestamp;
59 import java.util.Collection;
60 import java.util.Map;
61
62
63
64
65
66
67
68
69
70
71 public class StyleableEmailContentServiceImpl extends BaseEmailContentServiceImpl {
72 private static final Logger LOG = Logger.getLogger(StyleableEmailContentServiceImpl.class);
73
74 protected final String DEFAULT_EMAIL_STYLESHEET_RESOURCE_LOC = "defaultEmailStyle.xsl";
75
76 protected StyleService styleService;
77 protected EmailStyleHelper styleHelper = new EmailStyleHelper();
78 protected String globalEmailStyleSheet = KEWConstants.EMAIL_STYLESHEET_NAME;
79
80 protected RouteHeaderService routeHeaderService;
81
82 public void setStyleService(StyleService styleService) {
83 this.styleService = styleService;
84 }
85
86 public void setGlobalEmailStyleSheet(String globalEmailStyleSheet) {
87 this.globalEmailStyleSheet = globalEmailStyleSheet;
88 }
89
90 protected static DocumentBuilder getDocumentBuilder(boolean coalesce) {
91 try {
92 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
93 dbf.setCoalescing(coalesce);
94 return dbf.newDocumentBuilder();
95 } catch (ParserConfigurationException e) {
96 String message = "Error constructing document builder";
97 LOG.error(message, e);
98 throw new WorkflowRuntimeException(message, e);
99 }
100 }
101
102 protected static void addObjectXML(Document doc, Object o, Node node, String name) throws Exception {
103 Element element = XmlHelper.propertiesToXml(doc, o, name);
104
105 if (LOG.isDebugEnabled()) {
106 LOG.debug(XmlJotter.jotNode(element));
107 }
108
109 if (node == null) {
110 node = doc;
111 }
112
113 node.appendChild(element);
114 }
115
116 protected static void addTextElement(Document doc, Element baseElement, String elementName, Object elementData) {
117 Element element = doc.createElement(elementName);
118 String dataValue = "";
119 if (elementData != null) {
120 dataValue = elementData.toString();
121 }
122 element.appendChild(doc.createTextNode(dataValue));
123 baseElement.appendChild(element);
124 }
125
126 protected static void addCDataElement(Document doc, Element baseElement, String elementName, Object elementData) {
127 Element element = doc.createElement(elementName);
128 String dataValue = "";
129 if (elementData != null) {
130 dataValue = elementData.toString();
131 }
132 element.appendChild(doc.createCDATASection(dataValue));
133 baseElement.appendChild(element);
134 }
135
136 protected static void addTimestampElement(Document doc, Element baseElement, String elementName, Timestamp elementData) {
137 addTextElement(doc, baseElement, elementName, RiceConstants.getDefaultDateFormat().format(elementData));
138 }
139
140 protected static void addDelegatorElement(Document doc, Element baseElement, ActionItem actionItem) {
141 Element delegatorElement = doc.createElement("delegator");
142 if ( (actionItem.getDelegatorWorkflowId() != null) && (actionItem.getDelegatorWorkflowId() != null) ) {
143
144 baseElement.appendChild(delegatorElement);
145 return;
146 }
147 String delegatorType = "";
148 String delegatorId = "";
149 String delegatorDisplayValue = "";
150 if (actionItem.getDelegatorWorkflowId() != null) {
151 delegatorType = "user";
152 delegatorId = actionItem.getDelegatorWorkflowId();
153 Principal delegator = KimApiServiceLocator.getIdentityService().getPrincipal(delegatorId);
154
155 if (delegator == null) {
156 LOG.error("Cannot find user for id " + delegatorId);
157 delegatorDisplayValue = "USER NOT FOUND";
158 } else {
159 delegatorDisplayValue = UserUtils.getTransposedName(GlobalVariables.getUserSession(), delegator);
160 }
161 } else if (actionItem.getDelegatorWorkflowId() != null) {
162 delegatorType = "workgroup";
163 delegatorId = actionItem.getDelegatorGroupId().toString();
164 delegatorDisplayValue = KimApiServiceLocator.getGroupService().getGroup(actionItem.getDelegatorGroupId()).getName();
165 }
166 delegatorElement.setAttribute("type", delegatorType);
167
168 Element idElement = doc.createElement("id");
169 idElement.appendChild(doc.createTextNode(delegatorId));
170 delegatorElement.appendChild(idElement);
171
172 Element displayValElement = doc.createElement("displayValue");
173 displayValElement.appendChild(doc.createTextNode(delegatorDisplayValue));
174 delegatorElement.appendChild(displayValElement);
175 baseElement.appendChild(delegatorElement);
176 }
177
178 protected static void addWorkgroupRequestElement(Document doc, Element baseElement, ActionItem actionItem) {
179 Element workgroupElement = doc.createElement("workgroupRequest");
180 if (actionItem.isWorkgroupItem()) {
181
182 Element idElement = doc.createElement("id");
183 idElement.appendChild(doc.createTextNode(actionItem.getGroupId()));
184 workgroupElement.appendChild(idElement);
185
186 Element displayValElement = doc.createElement("displayValue");
187 displayValElement.appendChild(doc.createTextNode(actionItem.getGroupId()));
188 workgroupElement.appendChild(displayValElement);
189 }
190 baseElement.appendChild(workgroupElement);
191 }
192
193
194
195
196
197
198
199
200
201
202
203 protected void addSummarizedActionItem(Document doc, ActionItem actionItem, Person user, Node node, DocumentRouteHeaderValue routeHeader) throws Exception {
204 if (node == null) {
205 node = doc;
206 }
207
208 Element root = doc.createElement("summarizedActionItem");
209
210
211 addTextElement(doc, root, "documentId", actionItem.getDocumentId());
212 addTextElement(doc, root, "docName", actionItem.getDocName());
213 addCDataElement(doc, root, "docLabel", actionItem.getDocLabel());
214 addCDataElement(doc, root, "docTitle", actionItem.getDocTitle());
215
216 addTextElement(doc, root, "docRouteStatus", routeHeader.getDocRouteStatus());
217 addCDataElement(doc, root, "routeStatusLabel", routeHeader.getRouteStatusLabel());
218 addTextElement(doc, root, "actionRequestCd", actionItem.getActionRequestCd());
219 addTextElement(doc, root, "actionRequestLabel", actionItem.getActionRequestLabel());
220 addDelegatorElement(doc, root, actionItem);
221 addTimestampElement(doc, root, "createDate", routeHeader.getCreateDate());
222 addWorkgroupRequestElement(doc, root, actionItem);
223 addTimestampElement(doc, root, "dateAssigned", actionItem.getDateAssigned());
224
225 node.appendChild(root);
226 }
227
228 public DocumentRouteHeaderValue getRouteHeader(ActionItem actionItem) {
229 if (routeHeaderService == null) {
230 routeHeaderService = KEWServiceLocator.getRouteHeaderService();
231 }
232 return routeHeaderService.getRouteHeader(actionItem.getDocumentId());
233 }
234
235 protected Map<String,DocumentRouteHeaderValue> getRouteHeaders(Collection<ActionItem> actionItems) {
236 if (routeHeaderService == null) {
237 routeHeaderService = KEWServiceLocator.getRouteHeaderService();
238 }
239 return routeHeaderService.getRouteHeadersForActionItems(actionItems);
240 }
241
242 protected static String transform(Templates style, Document doc) {
243 StringWriter writer = new StringWriter();
244 StreamResult result = new StreamResult(writer);
245
246 try {
247 style.newTransformer().transform(new DOMSource(doc), result);
248 return writer.toString();
249 } catch (TransformerException te) {
250 String message = "Error transforming DOM";
251 LOG.error(message, te);
252 throw new WorkflowRuntimeException(message, te);
253 }
254 }
255
256
257
258
259
260
261
262
263 protected Templates getStyle(String styleName) {
264 Templates style = null;
265 try {
266 style = styleService.getStyleAsTranslet(styleName);
267 } catch (TransformerConfigurationException tce) {
268 String message = "Error obtaining style '" + styleName + "', using default";
269 LOG.error(message, tce);
270
271 }
272
273 if (style == null) {
274 LOG.warn("Could not find specified style, " + styleName + ", using default");
275 try {
276
277 style = TransformerFactory.newInstance().newTemplates(new StreamSource(new DefaultResourceLoader().getResource("classpath:org/kuali/rice/kew/mail/" + DEFAULT_EMAIL_STYLESHEET_RESOURCE_LOC).getInputStream()));
278 } catch (Exception tce) {
279 String message = "Error obtaining default style from resource: " + DEFAULT_EMAIL_STYLESHEET_RESOURCE_LOC;
280 LOG.error(message, tce);
281 throw new WorkflowRuntimeException("Error obtaining style '" + styleName + "'", tce);
282 }
283 }
284 return style;
285 }
286
287 protected EmailContent generateEmailContent(String styleName, Document doc) {
288 Templates style = getStyle(styleName);
289 return styleHelper.generateEmailContent(style, doc);
290 }
291
292 protected EmailContent generateReminderForActionItems(Person user, Collection<ActionItem> actionItems, String name, String style) {
293 DocumentBuilder db = getDocumentBuilder(false);
294 Document doc = db.newDocument();
295 Element element = doc.createElement(name);
296 Map<String,DocumentRouteHeaderValue> routeHeaders = getRouteHeaders(actionItems);
297
298 setStandardAttributes(element);
299 doc.appendChild(element);
300
301 try {
302 addObjectXML(doc, user, element, "user");
303 for (ActionItem actionItem: actionItems) {
304 try {
305 addSummarizedActionItem(doc, actionItem, user, element, routeHeaders.get(actionItem.getDocumentId()));
306 } catch (Exception e) {
307 String message = "Error generating XML for action item: " + actionItem;
308 LOG.error(message, e);
309 throw new WorkflowRuntimeException(e);
310 }
311 }
312
313 } catch (Exception e) {
314 String message = "Error generating XML for action items: " + actionItems;
315 LOG.error(message, e);
316 throw new WorkflowRuntimeException(e);
317 }
318
319 return generateEmailContent(style, doc);
320 }
321
322 protected void setStandardAttributes(Element e) {
323 e.setAttribute("env", getDeploymentEnvironment());
324 e.setAttribute("applicationEmailAddress", getApplicationEmailAddress());
325 e.setAttribute("actionListUrl", getActionListUrl());
326 e.setAttribute("preferencesUrl", getPreferencesUrl());
327 }
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348 @Override
349 public EmailContent generateImmediateReminder(Person user, ActionItem actionItem, DocumentType documentType) {
350
351 LOG.info("Starting generation of immediate email reminder...");
352 LOG.info("Action Id: " + actionItem.getActionItemId() +
353 "; ActionRequestId: " + actionItem.getActionRequestId() +
354 "; Action Item Principal Name: " + actionItem.getPerson().getPrincipalName());
355 LOG.info("User Principal Name: " + user.getPrincipalName());
356
357 String styleSheet = documentType.getCustomEmailStylesheet();
358 LOG.debug(documentType.getName() + " style: " + styleSheet);
359 if (styleSheet == null) {
360 styleSheet = globalEmailStyleSheet;
361 }
362
363 LOG.info("generateImmediateReminder using style sheet: "+ styleSheet + " for Document Type " + documentType.getName());
364
365 DocumentBuilder db = getDocumentBuilder(false);
366 Document doc = db.newDocument();
367 Element element = doc.createElement("immediateReminder");
368 setStandardAttributes(element);
369 doc.appendChild(element);
370
371 try {
372 addObjectXML(doc, user, element, "user");
373
374 Node node = element;
375 if (node == null) {
376 node = doc;
377 }
378
379 Element root = doc.createElement("actionItem");
380
381 try {
382 CustomEmailAttribute customEmailAttribute = getCustomEmailAttribute(user, actionItem);
383 if (customEmailAttribute != null) {
384 String customBody = customEmailAttribute.getCustomEmailBody();
385 if (!org.apache.commons.lang.StringUtils.isEmpty(customBody)) {
386 Element bodyElement = doc.createElement("customBody");
387 bodyElement.appendChild(doc.createTextNode(customBody));
388 root.appendChild(bodyElement);
389 }
390 String customEmailSubject = customEmailAttribute.getCustomEmailSubject();
391 if (!org.apache.commons.lang.StringUtils.isEmpty(customEmailSubject)) {
392 Element subjectElement = doc.createElement("customSubject");
393 subjectElement.appendChild(doc.createTextNode(customEmailSubject));
394 root.appendChild(subjectElement);
395 }
396 }
397 } catch (Exception e) {
398 LOG.error("Error when checking for custom email body and subject.", e);
399 }
400 Person person = actionItem.getPerson();
401 DocumentRouteHeaderValue header = getRouteHeader(actionItem);
402
403 addObjectXML(doc, actionItem, root, "actionItem");
404 addObjectXML(doc, person, root, "actionItemPerson");
405 addTextElement(doc, root, "actionItemPrincipalId", person.getPrincipalId());
406 addTextElement(doc, root, "actionItemPrincipalName", person.getPrincipalName());
407 addDocumentHeaderXML(doc, header, root, "doc");
408 addObjectXML(doc, header.getInitiatorPrincipal(), root, "docInitiator");
409 addTextElement(doc, root, "docInitiatorDisplayName", header.getInitiatorDisplayName());
410 addObjectXML(doc, header.getDocumentType(), root, "documentType");
411
412 node.appendChild(root);
413 } catch (Exception e) {
414 String message = "Error generating immediate reminder XML for action item: " + actionItem;
415 LOG.error(message, e);
416 throw new WorkflowRuntimeException(e);
417 }
418 LOG.info("Leaving generation of immeidate email reminder...");
419
420
421
422 return generateEmailContent(styleSheet, doc);
423 }
424
425
426
427
428
429
430
431
432 protected void addDocumentHeaderXML(Document document, DocumentRouteHeaderValue documentHeader, Node node, String elementName) throws Exception {
433 Element element = XmlHelper.propertiesToXml(document, documentHeader, elementName);
434
435 Element docContentElement = (Element)element.getElementsByTagName("docContent").item(0);
436 String documentContent = docContentElement.getTextContent();
437
438 if (!StringUtils.isBlank(documentContent) && documentContent.startsWith("<")) {
439 Document documentContentXML = XmlHelper.readXml(documentContent);
440 Element documentContentElement = documentContentXML.getDocumentElement();
441 documentContentElement = (Element)document.importNode(documentContentElement, true);
442
443
444 docContentElement.removeChild(docContentElement.getFirstChild());
445
446
447 docContentElement.appendChild(documentContentElement);
448 } else {
449
450
451
452
453
454 docContentElement.removeChild(docContentElement.getFirstChild());
455 }
456
457 if (LOG.isDebugEnabled()) {
458 LOG.debug(XmlJotter.jotNode(element));
459 }
460
461 node.appendChild(element);
462 }
463
464 @Override
465 public EmailContent generateWeeklyReminder(Person user, Collection<ActionItem> actionItems) {
466 return generateReminderForActionItems(user, actionItems, "weeklyReminder", globalEmailStyleSheet);
467 }
468
469 @Override
470 public EmailContent generateDailyReminder(Person user, Collection<ActionItem> actionItems) {
471 return generateReminderForActionItems(user, actionItems, "dailyReminder", globalEmailStyleSheet);
472 }
473
474 @Override
475 public EmailContent generateFeedback(FeedbackForm form) {
476 DocumentBuilder db = getDocumentBuilder(true);
477 Document doc = db.newDocument();
478 String styleSheet = globalEmailStyleSheet;
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493 LOG.info("form: " + form.getDocumentId());
494 try {
495 addObjectXML(doc, form, null, "feedback");
496 } catch (Exception e) {
497 String message = "Error generating XML for feedback form: " + form;
498 LOG.error(message, e);
499 throw new WorkflowRuntimeException(message, e);
500 }
501 setStandardAttributes(doc.getDocumentElement());
502
503 return generateEmailContent(styleSheet, doc);
504 }
505 }