package com.jobhive.sakimonkey.data.request; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.codec.binary.Base64; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.jobhive.sakimonkey.exception.IORuntimeException; import com.jobhive.sakimonkey.utils.Assert; import com.jobhive.sakimonkey.utils.IOUtils; import com.jobhive.sakimonkey.utils.Literal; /** * the information on the message to send * * @author Hussachai * */ public class Message { /** * the full HTML content to be sent */ private String html; /** * optional full text content to be sent */ private String text; /** * the message subject */ private String subject; /** * the sender email address */ private String fromEmail; /** * optional from name to be used */ private String fromName; /** * an array of recipient information. */ @JsonProperty("to") private Set<Recipient> recipients; /** * optional extra headers to add to the message (most headers are allowed) */ private Map<String, String> headers; /** * whether or not this message is important, and should be delivered ahead * of non-important messages */ private Boolean important; /** * whether or not to turn on open tracking for the message */ private Boolean trackOpens; /** * whether or not to turn on click tracking for the message */ private Boolean trackClicks; /** * whether or not to automatically generate a text part for messages that * are not given text */ private Boolean autoText; /** * whether or not to automatically generate an HTML part for messages that * are not given HTML */ private Boolean autoHtml; /** * whether or not to automatically inline all CSS styles provided in the * message HTML - only for HTML documents less than 256KB in size */ private Boolean inlineCss; /** * whether or not to strip the query string from URLs when aggregating * tracked URL data */ private Boolean urlStripQs; /** * whether or not to expose all recipients in to "To" header for each email */ private Boolean preserveRecipients; /** * set to false to remove content logging for sensitive emails */ private Boolean viewContentLink; /** * an optional address to receive an exact copy of each recipient's email * email */ private String bccAddress; /** * a custom domain to use for tracking opens and clicks instead of * mandrillapp.com */ private String trackingDomain; /** * a custom domain to use for SPF/DKIM signing instead of mandrill (for * "via" or "on behalf of" in email clients) */ private String signingDomain; /** * a custom domain to use for the messages's return-path */ private String returnPathDomain; /** * whether to evaluate merge tags in the message. Will automatically be set * to true if either merge_vars or global_merge_vars are provided. */ private Boolean merge; /** * the merge tag language to use when evaluating merge tags, either * mailchimp or handlebars oneof(mailchimp, handlebars) */ private MergeLanguage mergeLanguage = MergeLanguage.Handlebars; /** * global merge variables to use for all recipients. You can override these * per recipient. */ @JsonProperty("global_merge_vars") private Set<Var> globalVars; /** * per-recipient merge variables, which override global merge variables with * the same name. */ @JsonProperty("merge_vars") private Set<RecipientVars> recipientVars; /** * an array of string to tag the message with. Stats are accumulated using * tags, though we only store the first 100 we see, so this should not be * unique or change frequently. Tags should be 50 characters or less. Any * tags starting with an underscore are reserved for internal use and will * cause errors. * * a single tag - must not start with an underscore */ private Set<String> tags; /** * the unique id of a subaccount for this message - must already exist or * will fail with an error */ private String subaccount; /** * an array of strings indicating for which any matching URLs will * automatically have Google Analytics parameters appended to their query * string automatically. */ private Set<String> googleAnalyticsDomains; /** * optional string indicating the value to set for the utm_campaign tracking * parameter. If this isn't provided the email's from address will be used * instead. */ private String googleAnalyticsCampaign; /** * metadata an associative array of user metadata. Mandrill will store this * metadata and make it available for retrieval. In addition, you can select * up to 10 metadata fields to index and make searchable using the Mandrill * search api. */ private Map<String, String> metadata; /** * Per-recipient metadata that will override the global values specified in * the metadata parameter. */ private Set<RecipientMetaData> recipientMetadata; /** * an array of supported attachments to add to the message */ private Set<Attachment> attachments; /** * an array of embedded images to add to the message */ private Set<EmbeddedImage> images; public String getHtml() { return html; } public Message setHtml(String html) { this.html = html; return this; } public Message setHtml(File htmlFile){ this.html = IOUtils.readFileToString(htmlFile); return this; } public String getText() { return text; } public Message setText(String text) { this.text = text; return this; } public String getSubject() { return subject; } public Message setSubject(String subject) { this.subject = subject; return this; } public String getFromEmail() { return fromEmail; } public Message setFromEmail(String fromEmail) { this.fromEmail = fromEmail; return this; } public String getFromName() { return fromName; } public Message setFromName(String fromName) { this.fromName = fromName; return this; } public Set<Recipient> getRecipients() { return recipients; } public Message setRecipients(Set<Recipient> recipients) { this.recipients = recipients; if (recipients != null) { this.recipientVars = new HashSet<>(); for (Recipient recipient : recipients) { this.recipientVars.add(new RecipientVars(recipient.getEmail(), recipient.getVars())); } } return this; } public Message addRecipient(Recipient recipient) { Assert.notNull(recipient, "recipient"); if (this.recipients == null) { this.recipients = new HashSet<>(); this.recipientVars = new HashSet<>(); } this.recipients.add(recipient); this.recipientVars.add(new RecipientVars(recipient.getEmail(), recipient.getVars())); return this; } public Map<String, String> getHeaders() { return headers; } public Message setHeaders(Map<String, String> headers) { this.headers = headers; return this; } public Message addHeader(String name, String value) { Assert.notNull(name, "name"); if (this.headers == null) { this.headers = new HashMap<>(); } this.headers.put(name, value); return this; } public Boolean getImportant() { return important; } public Message setImportant(Boolean important) { this.important = important; return this; } public Boolean getTrackOpens() { return trackOpens; } public Message setTrackOpens(Boolean trackOpens) { this.trackOpens = trackOpens; return this; } public Boolean getTrackClicks() { return trackClicks; } public Message setTrackClicks(Boolean trackClicks) { this.trackClicks = trackClicks; return this; } public Boolean getAutoText() { return autoText; } public Message setAutoText(Boolean autoText) { this.autoText = autoText; return this; } public Boolean getAutoHtml() { return autoHtml; } public Message setAutoHtml(Boolean autoHtml) { this.autoHtml = autoHtml; return this; } public Boolean getInlineCss() { return inlineCss; } public Message setInlineCss(Boolean inlineCss) { this.inlineCss = inlineCss; return this; } public Boolean getUrlStripQs() { return urlStripQs; } public Message setUrlStripQs(Boolean urlStripQs) { this.urlStripQs = urlStripQs; return this; } public Boolean getPreserveRecipients() { return preserveRecipients; } public Message setPreserveRecipients(Boolean preserveRecipients) { this.preserveRecipients = preserveRecipients; return this; } public Boolean getViewContentLink() { return viewContentLink; } public Message setViewContentLink(Boolean viewContentLink) { this.viewContentLink = viewContentLink; return this; } public String getBccAddress() { return bccAddress; } public Message setBccAddress(String bccAddress) { this.bccAddress = bccAddress; return this; } public String getTrackingDomain() { return trackingDomain; } public Message setTrackingDomain(String trackingDomain) { this.trackingDomain = trackingDomain; return this; } public String getSigningDomain() { return signingDomain; } public Message setSigningDomain(String signingDomain) { this.signingDomain = signingDomain; return this; } public String getReturnPathDomain() { return returnPathDomain; } public Message setReturnPathDomain(String returnPathDomain) { this.returnPathDomain = returnPathDomain; return this; } public Boolean getMerge() { return merge; } public Message setMerge(Boolean merge) { this.merge = merge; return this; } public MergeLanguage getMergeLanguage() { return mergeLanguage; } public Message setMergeLanguage(MergeLanguage mergeLanguage) { this.mergeLanguage = mergeLanguage; return this; } public Set<Var> getGlobalVars() { return globalVars; } public Message setGlobalVars(Set<Var> globalVars) { this.globalVars = globalVars; return this; } public Message addGlobalVar(Var globalVar) { Assert.notNull(globalVar, "globalVar"); if (this.globalVars == null) { this.globalVars = new HashSet<>(); } this.globalVars.add(globalVar); return this; } public Set<String> getTags() { return tags; } public Message setTags(Set<String> tags) { this.tags = tags; return this; } public Message setTags(String... tags) { this.tags = Literal.set(tags); return this; } public String getSubaccount() { return subaccount; } public Message setSubaccount(String subaccount) { this.subaccount = subaccount; return this; } public Set<String> getGoogleAnalyticsDomains() { return googleAnalyticsDomains; } public Message setGoogleAnalyticsDomains(Set<String> googleAnalyticsDomains) { this.googleAnalyticsDomains = googleAnalyticsDomains; return this; } public Message setGoogleAnalyticsDomains(String... googleAnalyticsDomains) { this.googleAnalyticsDomains = Literal.set(googleAnalyticsDomains); return this; } public String getGoogleAnalyticsCampaign() { return googleAnalyticsCampaign; } public Message setGoogleAnalyticsCampaign(String googleAnalyticsCampaign) { this.googleAnalyticsCampaign = googleAnalyticsCampaign; return this; } public Map<String, String> getMetadata() { return metadata; } public Message setMetadata(Map<String, String> metadata) { this.metadata = metadata; return this; } public Message addMetadata(String name, String value) { Assert.notNull(name, "name"); if (this.metadata == null) { this.metadata = new HashMap<>(); } this.metadata.put(name, value); return this; } public Set<RecipientMetaData> getRecipientMetadata() { return recipientMetadata; } public Message setRecipientMetadata( Set<RecipientMetaData> recipientMetadata) { this.recipientMetadata = recipientMetadata; return this; } public Message addRecipientMetadata(RecipientMetaData recipientMetadata) { Assert.notNull(recipientMetadata, "recipientMetadata"); if (this.recipientMetadata == null) { this.recipientMetadata = new HashSet<>(); } this.recipientMetadata.add(recipientMetadata); return this; } public Set<Attachment> getAttachments() { return attachments; } public Message setAttachments(Set<Attachment> attachments) { this.attachments = attachments; return this; } public Message addAttachment(Attachment attachment) { Assert.notNull(attachment, "attachment"); if (this.attachments == null) { this.attachments = new HashSet<>(); } this.attachments.add(attachment); return this; } public Set<EmbeddedImage> getImages() { return images; } public Message setImages(Set<EmbeddedImage> images) { this.images = images; return this; } public Message addImage(EmbeddedImage image) { Assert.notNull(image, "image"); if (this.images == null) { this.images = new HashSet<>(); } this.images.add(image); return this; } /** * * @author Hussachai * */ public enum MergeLanguage { MailChimp, Handlebars; private String value; private MergeLanguage() { this.value = name().toLowerCase(); } @Override public String toString() { return value; } } /** * * @author Hussachai * */ public static class Recipient { /** * the email address of the recipient */ private String email; /** * the optional display name to use for the recipient */ private String name; /** * the header type to use for the recipient, defaults to "to" if not * provided oneof(to, cc, bcc) */ private RecipientType type = RecipientType.To; @JsonIgnore private List<Var> vars; public Recipient() { } public Recipient(String email, String name, RecipientType type) { setEmail(email); setName(name); setType(type); } public Recipient(String email, String name) { this(email, name, RecipientType.To); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((email == null) ? 0 : email.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Recipient other = (Recipient) obj; if (email == null) { if (other.email != null) return false; } else if (!email.equals(other.email)) return false; return true; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getName() { return name; } public Recipient setName(String name) { this.name = name; return this; } public RecipientType getType() { return type; } public Recipient setType(RecipientType type) { this.type = type; return this; } public List<Var> getVars() { return vars; } public Recipient setVars(List<Var> vars) { this.vars = vars; return this; } public Recipient addVar(Var var) { Assert.notNull(var, "var"); if (this.vars == null) { this.vars = new ArrayList<>(); } this.vars.add(var); return this; } public Recipient addVar(String name, Object content) { Var var = new Var(name, content); return addVar(var); } } /** * * @author Hussachai * */ public static enum RecipientType { To, CC, BCC; private String value; private RecipientType(){ this.value = name().toLowerCase(); } @Override public String toString() { return value; } } /** * * @author Hussachai * */ public static class RecipientMetaData { /** * the email address of the recipient that the metadata is associated with */ private String rcpt; /** * a map containing the recipient's unique metadata. If a key exists in both * the per-recipient metadata and the global metadata, the per-recipient * metadata will be used. */ @JsonProperty("values") private Map<String, String> metadata; @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((rcpt == null) ? 0 : rcpt.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; RecipientMetaData other = (RecipientMetaData) obj; if (rcpt == null) { if (other.rcpt != null) return false; } else if (!rcpt.equals(other.rcpt)) return false; return true; } public String getRcpt() { return rcpt; } public RecipientMetaData setRcpt(String rcpt) { this.rcpt = rcpt; return this; } public Map<String, String> getMetadata() { return metadata; } public RecipientMetaData setMetadata(Map<String, String> metadata) { this.metadata = metadata; return this; } public RecipientMetaData addMetadata(String name, String value) { Assert.notNull(name, "name"); if (this.metadata == null) { this.metadata = new HashMap<>(); } this.metadata.put(name, value); return this; } } /** * * @author Hussachai * */ public static class Var { /** * the merge variable's name. Merge variable names are case-insensitive and * may not start with _ */ private String name; /** * the merge variable's content */ private Object content; @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Var other = (Var) obj; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } public Var(String name, Object content) { setName(name); setContent(content); } public String getName() { return name; } public Var setName(String name) { this.name = name; return this; } public Object getContent() { return content; } public Var setContent(Object content) { this.content = content; return this; } } /** * Per-recipient merge variables, which override global merge variables with the * same name. * * @author Hussachai * */ public static class RecipientVars { /** * the email address of the recipient that the merge variables should apply * to */ private String rcpt; /** * the recipient's merge variables */ private List<Var> vars; public RecipientVars(String rcpt, List<Var> vars) { setRcpt(rcpt); setVars(vars); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((rcpt == null) ? 0 : rcpt.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; RecipientVars other = (RecipientVars) obj; if (rcpt == null) { if (other.rcpt != null) return false; } else if (!rcpt.equals(other.rcpt)) return false; return true; } public String getRcpt() { return rcpt; } public RecipientVars setRcpt(String rcpt) { Assert.notNull(rcpt, "rcpt"); this.rcpt = rcpt; return this; } public List<Var> getVars() { return vars; } public RecipientVars setVars(List<Var> vars) { Assert.notNull(vars, "vars"); this.vars = vars; return this; } public RecipientVars addVar(String name, Object content) { if (this.vars == null) { this.vars = new ArrayList<Var>(); } this.vars.add(new Var(name, content)); return this; } } /** * * @author Hussachai * */ public static class Attachment { /** * the MIME type of the attachment */ private String type; /** * the file name of the attachment */ private String name; /** * the content of the attachment as a base64-encoded string */ private String content; @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Attachment other = (Attachment) obj; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } public String getType() { return type; } public Attachment setType(String type) { this.type = type; return this; } public String getName() { return name; } public Attachment setName(String name) { this.name = name; return this; } public String getContent() { return content; } public Attachment setContent(String content) { this.content = content; return this; } public Attachment setContent(File file){ try{ Path path = Paths.get(file.getPath()); if(type == null){ this.type = Files.probeContentType(path); } byte[] data = Files.readAllBytes(path); this.content = Base64.encodeBase64String(data); }catch(IOException e){ throw new IORuntimeException(e); } return this; } } /** * * @author Hussachai * */ public static class EmbeddedImage extends Attachment { public EmbeddedImage(){} public EmbeddedImage(String name, String filePath){ setName(name); setContent(new File(filePath)); } /** * the MIME type of the image - must start with "image/" */ @Override public EmbeddedImage setType(String type) { if (type == null || !type.toLowerCase().startsWith("image/")) { throw new IllegalArgumentException("type must start with 'image/'"); } super.setType(type); return this; } /** * the Content ID of the image - use <img src="cid:THIS_VALUE"> to reference * the image in your HTML content */ @Override public EmbeddedImage setName(String name) { super.setName(name); return this; } } }