/src/main/java/com/redradishtech/jira/ccmailer/CCMailerListener.java
Java | 718 lines | 552 code | 65 blank | 101 comment | 69 complexity | abf1e406485d8c8801adf70b70eb1460 MD5 | raw file
- package com.redradishtech.jira.ccmailer;
- import com.atlassian.core.ofbiz.util.OFBizPropertyUtils;
- import com.atlassian.core.util.StringUtils;
- import com.atlassian.jira.ComponentManager;
- import com.atlassian.jira.ManagerFactory;
- import com.atlassian.jira.config.properties.APKeys;
- import com.atlassian.jira.config.properties.ApplicationProperties;
- import com.atlassian.jira.event.issue.AbstractIssueEventListener;
- import com.atlassian.jira.event.issue.IssueEvent;
- import com.atlassian.jira.event.issue.IssueEventListener;
- import com.atlassian.jira.event.type.EventType;
- import com.atlassian.jira.event.type.EventTypeManager;
- import com.atlassian.jira.issue.CustomFieldManager;
- import com.atlassian.jira.issue.Issue;
- import com.atlassian.jira.issue.IssueManager;
- import com.atlassian.jira.issue.RendererManager;
- import com.atlassian.jira.issue.attachment.Attachment;
- import com.atlassian.jira.issue.comments.Comment;
- import com.atlassian.jira.issue.fields.CustomField;
- import com.atlassian.jira.issue.fields.renderer.wiki.AtlassianWikiRenderer;
- import com.atlassian.jira.mail.Email;
- import com.atlassian.jira.mail.JiraMailThreader;
- import com.atlassian.jira.notification.NotificationSchemeManager;
- import com.atlassian.jira.project.ProjectKeys;
- import com.atlassian.jira.security.PermissionManager;
- import com.atlassian.jira.security.roles.ProjectRole;
- import com.atlassian.jira.util.AttachmentUtils;
- import com.atlassian.jira.util.NotNull;
- import com.atlassian.jira.util.URLCodec;
- import com.atlassian.mail.MailThreader;
- import com.atlassian.mail.queue.SingleMailQueueItem;
- import com.atlassian.velocity.VelocityManager;
- import com.opensymphony.user.User;
- import com.redradishtech.jira.ccmailer.util.AttachmentLinkRewriter;
- import com.redradishtech.jira.ccmailer.util.CustomFieldHelperBean;
- import org.apache.commons.lang.builder.ToStringBuilder;
- import org.apache.log4j.Logger;
- import org.apache.log4j.NDC;
- import org.apache.velocity.exception.VelocityException;
- import org.ofbiz.core.entity.GenericEntityException;
- import org.ofbiz.core.entity.GenericValue;
- import javax.activation.DataHandler;
- import javax.activation.FileDataSource;
- import javax.mail.MessagingException;
- import javax.mail.Multipart;
- import javax.mail.Part;
- import javax.mail.internet.InternetAddress;
- import javax.mail.internet.MimeBodyPart;
- import javax.mail.internet.MimeMultipart;
- import javax.mail.internet.MimeUtility;
- import java.io.File;
- import java.io.UnsupportedEncodingException;
- import java.util.*;
- import java.util.concurrent.atomic.AtomicReference;
- import java.util.regex.Matcher;
- import java.util.regex.Pattern;
- import java.util.regex.PatternSyntaxException;
- /**
- * Listener that sends an email notification out to addresses listed in a CC custom field (whose name/ID is configurable).
- * <p/>
- * This listener only triggers if the project's notification scheme notifies All Watchers of the event type.
- * It is possible to prevent CCed users getting a comment update by setting the comment's security level (to anything).
- * <p/>
- * The listener can be configured to only trigger for certain projects, by providing a comma-separated list of project keys.
- * The listener can be disabled altogether with the 'disable.ccmailer.notifications' system property.
- */
- public class CCMailerListener extends AbstractIssueEventListener implements IssueEventListener
- {
- private static final Logger log = Logger.getLogger(CCMailerListener.class);
- private static final String DISABLE_CCMAILER_NOTIFICATIONS_PROPERTY = "disable.ccmailer.notifications";
- private static final boolean CCMAILER_NOTIFICATIONS_DISABLED = "true".equalsIgnoreCase(System.getProperty(DISABLE_CCMAILER_NOTIFICATIONS_PROPERTY, "false"));
- static final String PARAM_SPLIT_REGEX = "(?<!\\\\),"; // split on an unescaped comma. The regexp is a zero-width negative lookbehind assertion.
- static final String CC_CUSTOMFIELD_ID = "CC custom field ID";
- protected static final String PROJECT_KEYS = "Only notify for issues in projects with these keys (default: all projects)";
- protected static final String TRIGGER_EVENTS = "Events triggering message (default: all events)";
- static final String IGNORED_ME = "Ignore events generated by my own changes true/false (default: true)";
- protected Set<String> projectKeys = new HashSet<String>();
- private boolean ignoreMyEvents = true;
- private CustomField ccCustomField;
- protected IssueManager issueManager;
- protected PermissionManager permissionManager;
- private VelocityManager velocityManager;
- private CustomFieldManager customFieldManager;
- private NotificationSchemeManager notificationSchemeManager;
- private RendererManager rendererManager;
- private EventTypeManager eventTypeManager;
- private ApplicationProperties applicationProperties;
- private final String EMAIL_FROM_REGEXP = "templates/ccmailer/notify_sender_regexp.vm";
- private final String EMAIL_TEMPLATE_TEXT = "templates/ccmailer/notify_mailbody_textplain.vm";
- private final String EMAIL_TEMPLATE_HTML = "templates/ccmailer/notify_mailbody_texthtml.vm";
- private final String EMAIL_SUBJECT_TEMPLATE = "templates/ccmailer/notify_subject.vm";
- private HashSet<Long> triggerEventIDs;
- public CCMailerListener()
- {
- this(null, null, null, null, null, null, null, null);
- }
- public CCMailerListener(IssueManager issueManager, PermissionManager permissionManager, VelocityManager velocityManager,
- CustomFieldManager customFieldManager,
- NotificationSchemeManager notificationSchemeManager, RendererManager rendererManager, EventTypeManager eventTypeManager,
- ApplicationProperties applicationProperties)
- {
- this.issueManager = issueManager;
- this.permissionManager = permissionManager;
- this.velocityManager = velocityManager;
- this.customFieldManager = customFieldManager;
- this.notificationSchemeManager = notificationSchemeManager;
- this.rendererManager = rendererManager;
- this.eventTypeManager = eventTypeManager;
- this.applicationProperties = applicationProperties;
- }
- public String[] getAcceptedParams()
- {
- List<String> params = new ArrayList<String>();
- params.addAll(Arrays.asList(getAcceptedListenerParams()));
- return params.toArray(new String[params.size()]);
- }
- public void init(Map params)
- {
- if (CCMAILER_NOTIFICATIONS_DISABLED)
- return;
- initListener(params);
- }
- public String toString()
- {
- return new ToStringBuilder(this).append("ccField", ccCustomField).append("projectKeys", projectKeys).append("triggerEvents", triggerEventIDs).append(DISABLE_CCMAILER_NOTIFICATIONS_PROPERTY, CCMAILER_NOTIFICATIONS_DISABLED).toString();
- }
- /**
- * Initialise listener.
- *
- * @param params Parameters entered by administrator. If a param was left blank we rely on the caller not to pass it through.
- */
- protected void initListener(Map params)
- {
- if (params.containsKey(CC_CUSTOMFIELD_ID))
- {
- String paramStr = (String) params.get(CC_CUSTOMFIELD_ID);
- ccCustomField = new CustomFieldHelperBean(customFieldManager, log).lookupCustomField(paramStr);
- }
- if (params.containsKey(TRIGGER_EVENTS))
- {
- String[] eventStrs = ((String) params.get(TRIGGER_EVENTS)).split(PARAM_SPLIT_REGEX); // any unescaped comma
- if (eventStrs.length > 0) triggerEventIDs = new HashSet<Long>();
- for (String id : eventStrs)
- {
- id = id.trim();
- Long eventId = null;
- try
- {
- eventId = Long.parseLong(id);
- } catch (NumberFormatException nfe)
- {
- for (Object o : eventTypeManager.getEventTypes())
- {
- EventType e = (EventType) o;
- if (id.equals(e.getName())) eventId = e.getId();
- }
- if (eventId == null)
- log.error("CCMailer listener configured to trigger on event '" + id + "', which is not a valid event ID or name.");
- }
- triggerEventIDs.add(eventId);
- }
- }
- if (params.containsKey(PROJECT_KEYS))
- {
- String[] projectKeyStrs = ((String) params.get(PROJECT_KEYS)).split(PARAM_SPLIT_REGEX);
- for (String projectStr : projectKeyStrs)
- {
- if (!"".equals(projectStr.trim()))
- {
- projectKeys.add(projectStr.trim());
- }
- }
- }
- if (params.containsKey(IGNORED_ME))
- {
- // true by default
- ignoreMyEvents = !"false".equals(params.get(IGNORED_ME));
- }
- if (params.size() > 0 && ccCustomField == null)
- {
- log.warn("No CC field set; no-one will be notified from this listener.");
- }
- log.info("Initialized listener " + this);
- // No error handling, as this method is only called when the listener is first triggered (not when it is configured)
- // when no user feedback is possible. Listeners suck..
- }
- public String[] getAcceptedListenerParams()
- {
- return new String[]{CC_CUSTOMFIELD_ID, PROJECT_KEYS, TRIGGER_EVENTS, IGNORED_ME};
- }
- public void workflowEvent(IssueEvent event)
- {
- if (CCMAILER_NOTIFICATIONS_DISABLED)
- return;
- if (event.getIssue() == null)
- {
- log.warn("Event " + event + " has no issue; ignoring");
- return;
- }
- if (ccCustomField == null)
- {
- log.error("Ignoring event, as no CC custom field configured. You need to set the '" + CC_CUSTOMFIELD_ID + "' parameter on the CCMailer Listener.");
- return;
- }
- final String eventKey = "Evnt:" + event.getRemoteUser() + "->" + event.getEventTypeId() + "@" + event.getIssue().getKey();
- NDC.push(eventKey); // Add "(%x)" to your log4j.properties patterns to see this.
- try
- {
- if (meetsTriggerConditions(event))
- {
- Set<InternetAddress> recipients = getRecipients(event.getIssue());
- if (!recipients.isEmpty())
- log.debug("Event matched conditions. Potentially notifying " + recipients.size() + " CC users.");
- for (InternetAddress recipient : recipients)
- {
- if (recipientAlreadyNotifiedAsWatcher(event, recipient))
- {
- log.info("\tUser " + recipient + " is already a watcher; ignoring CC list entry");
- }
- else if (recipientCreatedEvent(event, recipient) && ignoreMyEvents)
- {
- log.info("\tUser " + recipient + " created the event and listener is configured to ignore own events; ignoring CC list entry");
- }
- else
- {
- if (hasPermission(event, recipient))
- {
- log.debug(recipient + " was not already notified as a watcher, is not the creator of the event, and has permission to see it.");
- log.debug("\tNotifying " + recipient);
- String unsubToken = generateUnsubscribeToken(event);
- sendMessage(event, recipient, unsubToken);
- }
- else
- {
- log.info("\tUser " + recipient + " does not have permission to see comment " + event);
- }
- }
- }
- }
- } finally
- {
- NDC.pop();
- }
- }
- /**
- * If an CC email address resulted in the creation of this event's comment, we don't want to then send an email notification
- * back to that email. This method identifies whether a particular email address triggered the event. It does this by
- * parsing the comment. For example if the comment contains 'From: joe@example.com', and recipient is joe@example.com, then
- * this returns true. The actual regexp structure is stored in a velocity template to match the templated nature of the
- * comment format.
- *
- * @param event Event
- * @param recipient Email address
- * @return Whether the recipient email address was the cause of this event.
- */
- boolean recipientCreatedEvent(IssueEvent event, InternetAddress recipient)
- {
- Comment comment = event.getComment();
- if (comment == null) return false; // Eg. when issue is edited and no comment added
- Map<String, Object> params = new HashMap<String, Object>();
- params.put("email", Pattern.quote(recipient.getAddress()));
- try
- {
- String senderRegexp = velocityManager.getBody("", EMAIL_FROM_REGEXP, params);
- try
- {
- Pattern p = Pattern.compile(senderRegexp);
- Matcher m = p.matcher(comment.getBody());
- return m.find();
- } catch (PatternSyntaxException pse)
- {
- log.error("String in " + EMAIL_TEMPLATE_HTML + " is not a valid regular expression after interpolation: " + senderRegexp);
- }
- } catch (VelocityException e)
- {
- log.error("Error parsing " + EMAIL_TEMPLATE_HTML, e);
- }
- return false;
- }
- /**
- * Generates a unique token which we can associate with the event's issue, but will not be guessable. This will be embedded
- * in outgoing emails' unsubscribe links, and also stored in the notificationinstance table of the database. The unsubscribe
- * action then verifies that it is in the database, to validate that the submitter is the email recipient.
- *
- * @param event Issue event
- * @return Token associated with event.
- */
- private String generateUnsubscribeToken(IssueEvent event)
- {
- return "" + System.currentTimeMillis();
- }
- /**
- * Creates and enqueues a CC notification email.
- *
- * @param event Event to notify the CCed user of
- * @param recipient The CC recipient of the email
- * @param unsubToken A non-guessable token uniquely identifying the outgoing email, used to verify the unsubscribe requests.
- */
- private void sendMessage(final IssueEvent event, InternetAddress recipient, String unsubToken)
- {
- // sends the message by email
- String sender = OFBizPropertyUtils.getPropertySet(event.getIssue().getProject()).getString(ProjectKeys.EMAIL_SENDER);
- Map<String, Object> params = new HashMap<String, Object>();
- params.put("issue", event.getIssue());
- String emailSubject = null;
- try
- {
- emailSubject = velocityManager.getBody("", EMAIL_SUBJECT_TEMPLATE, params);
- } catch (VelocityException e)
- {
- log.error("Error rendering CC email subject from template " + EMAIL_SUBJECT_TEMPLATE, e);
- }
- try
- {
- Email email = new Email(recipient.getAddress());
- email.setFrom(sender);
- email.setFromName(getSenderFrom(event));
- email.setSubject(emailSubject == null ? "" : emailSubject);
- email.addHeader("X-JIRA-IssueKey", event.getIssue().getKey());
- final Multipart multipart = new MimeMultipart("alternative");
- final MimeBodyPart textPart = new MimeBodyPart();
- final MimeBodyPart htmlAndAttachmentsPart = new MimeBodyPart();
- final MimeMultipart htmlAndAttachmentsMultipart = new MimeMultipart("related");
- final MimeBodyPart htmlPart = new MimeBodyPart();
- email.setMultipart(multipart);
- multipart.addBodyPart(textPart);
- multipart.addBodyPart(htmlAndAttachmentsPart);
- htmlAndAttachmentsPart.setContent(htmlAndAttachmentsMultipart);
- htmlAndAttachmentsMultipart.addBodyPart(htmlPart);
- final String wikiMsgText = getEmailBodyText(event, recipient, unsubToken, "text");
- final String htmlMsgText = getEmailBodyText(event, recipient, unsubToken, "html");
- textPart.setText(wikiMsgText, "UTF-8");
- final AtomicReference<String> rewrittenHtmlMsgText = new AtomicReference<String>(AttachmentLinkRewriter.rewrite(htmlMsgText, new AttachmentLinkRewriter.URLGenerator()
- {
- /**
- * Generate RFC2392 cid: URL from a JIRA attachment relative URL. As a side-effect, adds the attachment
- * referenced by the cid: URL to the email being generated.
- */
- public String generateURL(long attachmentId) throws Exception
- {
- Attachment att = ComponentManager.getInstance().getAttachmentManager().getAttachment(attachmentId);
- File attFile = AttachmentUtils.getAttachmentFile(att);
- final MimeBodyPart attachmentPart = new MimeBodyPart();
- attachmentPart.setDataHandler(new DataHandler(new FileDataSource(attFile)));
- attachmentPart.setFileName(MimeUtility.encodeText(att.getFilename()));
- attachmentPart.setHeader("Content-Type", att.getMimetype());
- // System.setProperty("mail.mime.encodeparameters", "true");
- attachmentPart.setDisposition(Part.ATTACHMENT);
- String base_url = applicationProperties.getString(APKeys.JIRA_BASEURL);
- String contentID = att.getId() + "@" + base_url;
- attachmentPart.setContentID(contentID);
- htmlAndAttachmentsMultipart.addBodyPart(attachmentPart);
- return "cid:" + contentID;
- }
- }));
- htmlPart.setText(rewrittenHtmlMsgText.get(), "UTF-8", "html");
- SingleMailQueueItem item = new SingleMailQueueItem(email);
- // We need to save unsubToken to the database, so that later we can look it up as a means of verifying the HTTP
- // request of the unsubscribe request
- // As a hack, store it in the notificationinstance table by setting the message-ID, then requesting MailThreader
- // to store it
- email.setMessageId(unsubToken);
- MailThreader mailThreader = new JiraMailThreader(event.getEventTypeId(), event.getIssue().getLong("id"));
- mailThreader.storeSentEmail(email);
- item.setMailThreader(mailThreader);
- ManagerFactory.getMailQueue().addItem(item);
- } catch (MessagingException e)
- {
- throw new RuntimeException("Error setting ccmailer email body", e);
- }
- }
- /**
- * Return from address ('Joe Bloggs (JIRA)' usually).
- *
- * @param event issue event
- * @return sender's address
- */
- private String getSenderFrom(IssueEvent event)
- {
- String from = applicationProperties.getDefaultBackedString(APKeys.EMAIL_FROMHEADER_FORMAT);
- if (from == null)
- {
- return null;
- }
- final User user = event.getRemoteUser();
- String name;
- if (user == null)
- {
- name = "Anonymous";
- }
- else
- {
- try
- {
- String fullName = user.getFullName();
- if (org.apache.commons.lang.StringUtils.isBlank(fullName))
- {
- name = user.getName();
- }
- else
- {
- name = fullName;
- }
- } catch (Exception exception)
- {
- // this should never fail, but incase it does we don't want to imply it was a anonymous user.
- try
- {
- name = user.getName();
- } catch (Exception exception2)
- {
- name = "";
- }
- }
- }
- String email;
- try
- {
- email = (user != null ? user.getEmail() : "");
- } catch (Exception exception)
- {
- email = "";
- }
- final String hostname = (user != null && email != null ? email.substring(email.indexOf("@") + 1) : "");
- from = StringUtils.replaceAll(from, "${fullname}", name);
- from = StringUtils.replaceAll(from, "${email}", email);
- from = StringUtils.replaceAll(from, "${email.hostname}", hostname);
- return from;
- }
- /**
- * Check whether the email recipient should be able to see this event's comment.
- * In this implementation, if a comment security level is set, no comment is sent.
- *
- * @param event
- * @param recipient
- * @return Whether the recipient has permission to see the triggered event.
- */
- protected boolean hasPermission(IssueEvent event, InternetAddress recipient)
- {
- Comment comment = event.getComment();
- if (comment != null)
- {
- String groupLevel = comment.getGroupLevel();
- if (groupLevel != null)
- {
- log.debug("Not emailing " + recipient + " event " + event + " as comment group-level is set, to " + groupLevel);
- return false;
- }
- ProjectRole roleLevel = comment.getRoleLevel();
- if (roleLevel != null)
- {
- log.debug("Not emailing " + recipient + " event " + event + " as comment role-level is set, to " + roleLevel);
- return false;
- }
- }
- return true;
- }
- protected boolean meetsTriggerConditions(IssueEvent event)
- {
- return isTriggerEvent(event) && eventInTriggeringProject(event) && isWatchersNotified(event);
- }
- /**
- * Checks if the event happened in a project we're configured to care about.
- *
- * @param event
- * @return True if this event affects us.
- */
- boolean eventInTriggeringProject(IssueEvent event)
- {
- if (projectKeys.size() > 0 && !projectKeys.contains(event.getIssue().getProjectObject().getKey()))
- {
- log.debug("Ignoring event, as it is not in allowed project(s) " + projectKeys);
- return false;
- }
- return true;
- }
- private boolean isTriggerEvent(IssueEvent event)
- {
- if (triggerEventIDs != null)
- {
- if (!triggerEventIDs.contains(event.getEventTypeId()))
- {
- log.debug("Event is not in trigger set " + triggerEventIDs + "; ignoring");
- return false;
- }
- log.debug("Event matches eventIds " + triggerEventIDs);
- }
- return true;
- }
- /**
- * Checks if the event notifies watchers, and therefore concerns us also.
- *
- * @param event
- * @return True if the event has an All_Watchers notification type.
- */
- boolean isWatchersNotified(IssueEvent event)
- {
- boolean eventHasWatchers = false;
- GenericValue nsGV = notificationSchemeManager.getNotificationSchemeForProject(event.getIssue().getProject());
- if (nsGV != null)
- {
- try
- {
- List entities = notificationSchemeManager.getEntities(nsGV, event.getEventTypeId());
- if (entities != null)
- {
- for (GenericValue gv : (List<GenericValue>) entities)
- {
- if (gv.containsKey("type") && "All_Watchers".equals(gv.getString("type")))
- {
- eventHasWatchers = true;
- break;
- }
- }
- }
- } catch (GenericEntityException e)
- {
- log.error("Unexpected error retrieving entities for notification scheme " + nsGV + ", event " + event);
- }
- }
- else
- {
- log.debug("Not notifying CC emails, as " + event.getIssue() + " has no notification scheme.");
- }
- if (!eventHasWatchers)
- log.debug("Not notifying CC emails (if any), as " + event.getIssue().getProjectObject() + "'s notification scheme does not notify Watchers on event type " + event.getEventTypeId());
- return eventHasWatchers;
- }
- private boolean recipientAlreadyNotifiedAsWatcher(IssueEvent event, InternetAddress recipient)
- {
- for (User u : getWatchers(event.getIssue()))
- {
- if (recipient.equals(u.getEmail()))
- {
- return true;
- }
- }
- return false;
- }
- /**
- * Given an issue, returns a list of watchers
- *
- * @param issue The current issue (used to determine who's in role 'watchers').
- * @return A list of watchers of type User.
- */
- private List<User> getWatchers(Issue issue)
- {
- List<User> myUsers = new ArrayList<User>();
- if (issue != null)
- {
- try
- {
- myUsers = issueManager.getIssueWatchers(issue);
- } catch (Exception e)
- {
- log.debug("Failed to determine watchers from issue: " + e.toString());
- }
- }
- return myUsers;
- }
- /**
- * Get the CC list for an email
- *
- * @param issue The current issue
- * @return A set of email address Strings.
- */
- private Set<InternetAddress> getRecipients(@NotNull Issue issue)
- {
- return new CustomFieldHelperBean(customFieldManager, log).getCustomFieldValueSet(issue, ccCustomField);
- }
- public boolean isInternal()
- {
- return false;
- }
- public boolean isUnique()
- {
- return false;
- }
- public String getDescription()
- {
- return "Sends notifications about issue updates to email addresses in the specified text custom field (the CC field). Parameters are as follows. Note that commas can be escaped with \\." +
- " Note that any parameter with a 'default' can be left blank to have that default take effect.<ul>\n" +
- "<li><b>" + CC_CUSTOMFIELD_ID + "</b> - The name or ID of the 255 character text custom field you wish to be the 'CC' custom field. Its value should be a list of email addresses separated by semicolons." +
- "<li><b>" + PROJECT_KEYS + "</b> - Comma-separated list of project keys (the project key is eg. the 'ABC' in issue key ABC-123). " +
- "<li><b>" + TRIGGER_EVENTS + "</b> - comma-separated list of names or IDs of JIRA events to <b>trigger</b> on. Valid events are:" +
- "<table border=1>" +
- "<tr><th>Event ID</th><th>Event name</th></tr>" +
- getEventsTable() +
- "</table>" +
- "<li><b>" + IGNORED_ME + "</b> - Only trigger if the user performing the action <em>isn't</em> the user being notified. Avoids people being notified on their own changes. Currently this only affects comment notifications, not new issue notifications." +
- "</ul>";
- }
- private String getEventsTable()
- {
- StringBuffer buf = new StringBuffer();
- for (Object o : eventTypeManager.getEventTypes())
- {
- EventType e = (EventType) o;
- buf.append("<tr><td>" + e.getId() + "</td><td>" + e.getName() + "</td></tr>");
- }
- return buf.toString();
- }
- /**
- * Gets the email body text, either html or plaintext depending on the parameter.
- *
- * @param event JIRA trigger event.
- * @param recipient
- * @param unsubscribeToken
- * @param text_or_html "text" or "html", indicating the type of mail body the caller wants.
- * @return The message text.
- */
- protected String getEmailBodyText(IssueEvent event, InternetAddress recipient, String unsubscribeToken, final String text_or_html)
- {
- String result = "";
- Map<String, Object> params = new HashMap<String, Object>();
- Issue issue = event.getIssue();
- params.put("issue", issue);
- params.put("event", event);
- params.put("comment", event.getComment());
- final String commentBody = event.getComment() != null ? event.getComment().getBody() : "";
- params.put("htmlCommentBody", rendererManager.getRenderedContent(AtlassianWikiRenderer.RENDERER_TYPE, commentBody, issue.getIssueRenderContext()));
- String base_url = applicationProperties.getString(APKeys.JIRA_BASEURL);
- params.put("base_url", base_url);
- String unsubURL = null;
- try
- {
- unsubURL = base_url + "/secure/UnsubscribeCCField.jspa?cfId=" +
- (ccCustomField == null ? "null" : ccCustomField.getIdAsLong()) +
- "&unsubToken=" + URLCodec.encode(unsubscribeToken) +
- "&email=" + URLCodec.encode(recipient.getAddress());
- } catch (UnsupportedEncodingException e1)
- {
- log.error("Error encoding unsubscribe URL", e1);
- }
- params.put("unsuburl", unsubURL);
- params.put("unsubtoken", unsubscribeToken);
- params.put("recipient", recipient);
- final String templateFilename = text_or_html.equals("text") ? EMAIL_TEMPLATE_TEXT : EMAIL_TEMPLATE_HTML;
- try
- {
- result = velocityManager.getBody("", templateFilename, params);
- } catch (VelocityException e)
- {
- log.error("Error rendering " + text_or_html + " Cc notification "+templateFilename, e);
- result = "(Error rendering email: see server logs for details)";
- }
- return result;
- }
- String getCcCustomFieldID()
- {
- return "" + ccCustomField;
- }
- }