Java tutorial
/* * (C) Copyright 2014 Nuxeo SA (http://nuxeo.com/) and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Lesser General Public License * (LGPL) version 2.1 which accompanies this distribution, and is available at * http://www.gnu.org/licenses/lgpl-2.1.html * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * Contributors: * Benjamin JALON <bjalon@nuxeo.com> */ package org.nuxeo.labs.operations.notification; import java.io.InputStream; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import javax.mail.Address; import javax.mail.MessagingException; import javax.mail.internet.InternetAddress; import org.apache.commons.lang.StringEscapeUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.nuxeo.common.utils.StringUtils; import org.nuxeo.ecm.automation.OperationContext; import org.nuxeo.ecm.automation.core.Constants; import org.nuxeo.ecm.automation.core.annotations.Context; import org.nuxeo.ecm.automation.core.annotations.Operation; import org.nuxeo.ecm.automation.core.annotations.OperationMethod; import org.nuxeo.ecm.automation.core.annotations.Param; import org.nuxeo.ecm.automation.core.collectors.DocumentModelCollector; import org.nuxeo.ecm.automation.core.mail.Composer; import org.nuxeo.ecm.automation.core.mail.Mailer.Message; import org.nuxeo.ecm.automation.core.operations.notification.MailTemplateHelper; import org.nuxeo.ecm.automation.core.scripting.Scripting; import org.nuxeo.ecm.automation.core.util.StringList; import org.nuxeo.ecm.core.api.Blob; import org.nuxeo.ecm.core.api.ClientException; import org.nuxeo.ecm.core.api.DocumentModel; import org.nuxeo.ecm.core.api.NuxeoPrincipal; import org.nuxeo.ecm.core.api.model.Property; import org.nuxeo.ecm.core.api.model.PropertyException; import org.nuxeo.ecm.core.api.model.impl.ListProperty; import org.nuxeo.ecm.platform.ec.notification.service.NotificationServiceHelper; import org.nuxeo.ecm.platform.usermanager.UserManager; import org.nuxeo.runtime.api.Framework; /** * @author <a href="mailto:bjalon@nuxeo.com">Benjamin JALON</a> * @since 5.7 */ @Operation(id = AdvancedSendEmail.ID, category = Constants.CAT_NOTIFICATION, label = "AdvancedSendEmail", description = "Send Email according parameters set. <p> EMail Address resolution is as following if you check strict : <ul><li> if you set a string that starts with 'user:xxxx' will resolve email of username xxxx. </li><li> if you set a string that starts with 'group:xxxx, will resolve all users and add their email. </li><li> if not these 2 conditions, if the value contains an '@' then will concider it as an email directly. </li><li> If there is no match then throw an exception. </li></ul><p> if you uncheck the strict checkbox, you have the same behavior explain above, but the exception is not throw and will try to concider the value as a username <ul><li> if no match then will concider a group </li><li> and if no match again then empty string</li></ul>. <p>You can also put in values : <ul><li>a NuxeoPrincipal </li><li> or a list of NuxeoPrincipal </li><li> or a list of String.</li><li> or a list of mixed value.</li></ul> <p>For list of string, the resolution is the same as explain previously.<p> If you check 'rollbackOnError' then error will be catch if one is thrown. <p>For the file value just put the xpath value of field that stores the file (file:content, files:/files/0/content, myschema:myfield, myschema:/myfield/0/content)") public class AdvancedSendEmail { public static final String ID = "AdvancedSendEmail"; protected static final Log log = LogFactory.getLog(AdvancedSendEmail.class); private static final String LIST_ITEM_TYPE_ERROR_MESSAGE = "Field type \"%s\" store a list but an element in this list is not neither a String, nor a NuxeoPrincipal, please check the Studio Project Configuration: %s"; private static final String TYPE_ERROR_MESSAGE = "Field type \"%s\" doesn't store neither String, nor a List, nor a NuxeoPrincipal, please check the Studio Project Configuration: %s"; private static final String WARN_ON_SEND_EXCEPTION = "An error occured while trying to execute the %s operation, see complete stack trace below. Continuing chain since 'rollbackOnError' was set to false."; public static final Composer COMPOSER = new Composer(); @Context protected OperationContext ctx; @Context protected UserManager umgr; @Param(name = "subject", required = false) protected String subject; @Param(name = "message", required = false, widget = Constants.W_MULTILINE_TEXT) protected String message; @Param(name = "HTML", required = false) protected boolean asHtml = false; @Param(name = "from") protected String from; @Param(name = "to") protected Object to; @Param(name = "cc", required = false) protected Object cc; @Param(name = "bcc", required = false) protected Object bcc; @Param(name = "replyto", required = false) protected Object replyto; @Param(name = "files", required = false) protected StringList blobXpath; @Param(name = "viewId", required = false, values = { "view_documents" }) protected String viewId = "view_documents"; @Param(name = "rollbackOnError", required = false, values = { "true" }) protected boolean rollbackOnError = true; @Param(name = "Strict User Resolution", required = false) protected boolean isStrict = false; @OperationMethod(collector = DocumentModelCollector.class) public DocumentModel run(DocumentModel doc) throws Exception { send(doc); return doc; } protected void send(DocumentModel doc) throws Exception { try { Message msg = createContentMessage(doc); addToEmails(msg); addFromEmails(msg); addCcEmails(msg); addBccEmails(msg); addReplyToEmails(msg); msg.setSubject(subject); msg.send(); } catch (Exception e) { if (rollbackOnError) { throw e; } else { log.warn(String.format(WARN_ON_SEND_EXCEPTION, ID), e); } } } protected Message createContentMessage(DocumentModel doc) throws Exception { Map<String, Object> ctx = createContext(doc); String content = StringEscapeUtils.unescapeHtml(message); if (blobXpath == null) { if (asHtml) { return COMPOSER.newHtmlMessage(content, ctx); } else { return COMPOSER.newTextMessage(content, ctx); } } else { ArrayList<Blob> blobs = new ArrayList<Blob>(); for (String xpath : blobXpath) { Property p = doc.getProperty(xpath); if (p instanceof ListProperty) { for (Property pp : p) { Object o = pp.getValue(); if (o instanceof Blob) { blobs.add((Blob) o); } } } else { Object o = p.getValue(); if (o instanceof Blob) { blobs.add((Blob) o); } } } return COMPOSER.newMixedMessage(content, ctx, asHtml ? "html" : "plain", blobs); } } private Map<String, Object> createContext(DocumentModel doc) throws Exception { Map<String, Object> map = Scripting.initBindings(ctx); map.put("Document", doc); map.put("docUrl", MailTemplateHelper.getDocumentUrl(doc, viewId)); map.put("subject", subject); map.put("to", to); map.put("from", from); map.put("cc", cc); map.put("bcc", bcc); map.put("replyTo", replyto); map.put("viewId", viewId); map.put("baseUrl", NotificationServiceHelper.getNotificationService().getServerUrlPrefix()); map.put("Runtime", Framework.getRuntime()); return map; } protected void addToEmails(Message msg) throws ClientException, MessagingException { for (String email : getEmails(to, "TO")) { msg.addTo(email); } } protected void addFromEmails(Message msg) throws ClientException, MessagingException { for (String email : getEmails(from, "FROM")) { msg.addFrom(email); } } protected void addCcEmails(Message msg) throws ClientException, MessagingException { for (String email : getEmails(cc, "CC")) { msg.addCc(email); } } protected void addBccEmails(Message msg) throws ClientException, MessagingException { for (String email : getEmails(bcc, "BCC")) { msg.addBcc(email); } } protected void addReplyToEmails(Message msg) throws ClientException, MessagingException { if (replyto == null) { return; } if ("".equals(replyto)) { return; } List<String> emails = getEmails(replyto, "ReplyTo"); Address[] replyToValue = new InternetAddress[emails.size()]; for (int i = 0; i < emails.size(); i++) { replyToValue[i] = new InternetAddress(emails.get(i)); } msg.setReplyTo(replyToValue); } @SuppressWarnings("unchecked") protected List<String> getEmails(Object value, String fieldType) throws ClientException { List<String> result = new ArrayList<String>(); if (value == null) { return result; } if ("".equals(value)) { return result; } if (value instanceof String) { result.addAll(getEmailFromString((String) value)); return result; } if (value instanceof List) { for (Object user : (List<Object>) value) { if (user instanceof String) { result.addAll(getEmailFromString((String) user)); } else { if (user instanceof NuxeoPrincipal) { result.add(((NuxeoPrincipal) user).getEmail()); } else { throw new ClientException( String.format(LIST_ITEM_TYPE_ERROR_MESSAGE, fieldType, user.getClass().getName())); } } } return result; } if (value instanceof NuxeoPrincipal) { result.add(((NuxeoPrincipal) value).getEmail()); return result; } throw new ClientException(String.format(TYPE_ERROR_MESSAGE, fieldType, value.getClass().getName())); } protected List<String> getEmailFromString(String value) throws ClientException { List<String> result = null; if (value.startsWith("user:")) { String userId = value.substring("user:".length()); result = getEmailUserFromUserId(userId); log.debug("User email found from (username) " + value + " was " + result.get(0)); return result; } if (value.startsWith("group:")) { String groupId = value.substring("group:".length()); result = getEmailsFromGroupId(groupId); log.debug("User emails found from (groupId) " + value + " was [" + StringUtils.join(result, ",") + "]"); return result; } if (!isStrict) { result = getEmailUserFromUserId(value); if (result != null && !result.isEmpty()) { log.debug("User email found from (username) " + value + " was " + result.get(0)); return result; } result = getEmailsFromGroupId(value); if (result != null && !result.isEmpty()) { log.debug("User emails found from (groupId) " + value + " was [" + StringUtils.join(result, ",") + "]"); return result; } } if (value.contains("@")) { log.debug("User email (esasily) found from (email) " + value + " was " + value); return Collections.singletonList(value); } if (isStrict) { throw new ClientException("User or group not found and not an email " + value); } log.debug("User emails found from (groupId) " + value + " was [" + StringUtils.join(result, ",") + "]"); return null; } private List<String> getEmailUserFromUserId(String userId) throws ClientException, PropertyException { DocumentModel user = umgr.getUserModel(userId); if (user != null && user.getPropertyValue("email") != null && ((String) user.getPropertyValue("email")).contains("@")) { return Collections.singletonList(((String) user.getPropertyValue("email"))); } return null; } private List<String> getEmailsFromGroupId(String groupId) throws ClientException, PropertyException { if (umgr.getGroup(groupId) == null) { return null; } List<String> users = umgr.getUsersInGroup(groupId); if (users != null) { List<String> result = new ArrayList<String>(); for (String userId : users) { DocumentModel user = umgr.getUserModel(userId); if (user != null && user.getPropertyValue("email") != null && ((String) user.getPropertyValue("email")).contains("@")) { result.add((String) user.getPropertyValue("email")); } } return result; } return null; } }