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