Email Builder - An easy way to create custom emails for your groovy scripts

Hello everyone!

In my first (and hopefully not the last) article here on The Community I'd like to share an easy way to construct and send custom emails from your Groovy scripts. Please keep in mind that Scriptrunner add-on is required for this. 

While browsing the various questions here on The Community I've noticed that alot of people are asking how to send a custom email from their scripts. Now we do know that Scriptrunner has a great built-in script for sending custom emails, and most of the time it works fine. Unfortunatly however you can't really use it from within your other scripts, which is especially the case if you store most of your scripts on file system. Nor does it allow you to set various parts of your email conditionally (like subject, body, attachments, recipients, etc).

With that in mind I've developed this rather simple class which allows you once imported into your other scripts to easily construct and send custom email. We've already had a similar builder for creating and updating issues, developed by my colleague and fellow champion @Mark Markov so I figured this logic could be used for emails as well. 

package ru.tovbin.ivan.builders

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.attachment.Attachment
import com.atlassian.jira.util.PathUtils
import com.atlassian.mail.Email
import com.atlassian.mail.server.SMTPMailServer
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import javax.activation.DataHandler
import javax.activation.FileDataSource
import javax.mail.BodyPart
import javax.mail.Multipart
import javax.mail.internet.MimeBodyPart
import javax.mail.internet.MimeMultipart
import com.atlassian.jira.issue.Issue

class EmailBuilder {

private static Logger log = LoggerFactory.getLogger(this.class.getName())
private Email email
private EmailBuilder(){}

public static Builder create(String to){
return new EmailBuilder.Builder(new EmailBuilder()).createEmail(new Email(to))
}

private static File getFilePath(Attachment attachment){
log.debug("Getting path for attachment ${attachment.getFilename()} from issue ${attachment.getIssue().getKey()}...")
int u = Integer.parseInt(attachment.getIssue().getKey().split("-")[1])
int kk = (int)Math.ceil(u/10000)*10000
String path = PathUtils.joinPaths(ComponentAccessor.getAttachmentPathManager().getAttachmentPath(), attachment.getIssue().getProjectObject().getKey(), kk.toString() ,attachment.getIssue().getKey(), attachment.getId().toString())
log.debug("File path for ${attachment.getFilename()}: ${path}")
File filePath = new File(path)
return filePath
}

private static BodyPart getAttachBody(File filePath, String fileName){
log.debug("Building body for file ${fileName} at path ${filePath.toString()}....")
BodyPart attachBody = new MimeBodyPart()
attachBody.setDataHandler(new DataHandler(new FileDataSource(filePath)))
log.debug("Body data handler name:${attachBody.getDataHandler().getName()}")
attachBody.setFileName(fileName)
log.debug("Body filename: ${attachBody.getFileName()}")
return attachBody

}

public class Builder {
private Builder() {}

private Builder createEmail(Email email){
EmailBuilder.this.email = email
return this
}

public Builder setFromName(String fromName){
log.debug("setFromName. value: {}", fromName)
EmailBuilder.this.email.setFromName(fromName)
return this
}

public Builder setFrom(String from){
log.debug("setFrom. value: {}", from)
EmailBuilder.this.email.setFrom(from)
return this
}

public Builder setTo(String to){
log.debug("setTo. value: {}", to)
EmailBuilder.this.email.setTo(to)
return this
}

public Builder setCc(String cc){
log.debug("setCc. value: {}", cc)
EmailBuilder.this.email.setCc(cc)
return this
}

public Builder setSubject(String subject){
log.debug("setSubject. value: {}", subject)
EmailBuilder.this.email.setSubject(subject)
return this
}

public Builder setBody(String body){
log.debug("setBody. value: {}", body)
EmailBuilder.this.email.setBody(body)
return this
}

public Builder setMimeType(String mimeType){
log.debug("setMimeType. value: {}", mimeType)
EmailBuilder.this.email.setMimeType(mimeType)
return this
}

public Builder addAttachments(List<Attachment> attachmentList){
log.debug("Attachments to add: ${attachmentList.toString()}")
Multipart multipart = new MimeMultipart()
for (int i = 0; i < attachmentList.size(); i++){
multipart.addBodyPart(EmailBuilder.getAttachBody(EmailBuilder.getFilePath(attachmentList[i]), attachmentList[i].getFilename()))
}
EmailBuilder.this.email.setMultipart(multipart)
return this
}

public EmailBuilder send() {
SMTPMailServer mailServer = ComponentAccessor.getMailServerManager().getDefaultSMTPMailServer()
Thread.currentThread().setContextClassLoader(javax.mail.Message.class.getClassLoader())
log.debug(EmailBuilder.this.email.toString())
mailServer.send(EmailBuilder.this.email)
return EmailBuilder.this;
}
}
}

 

And here's how you use it in your scripts:

import ru.tovbin.ivan.builders.EmailBuilder

EmailBuilder.create("destination@email.com")
.setFromName("some name")
.setFrom("from@email.com")
.setCc("copy@email.com")
.setSubject("some subject")
.setBody("some body")
.setMimeType("text/html")
.addAttachments(issue.getAttachments())
.send()

Pretty easy, huh?

Well that's about it, I hope this will be helpful. Please feel free to share your thoughts and questions. Any feedback is always welcome!

9 comments

Gonchik Tsymzhitov
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
January 17, 2019

Hi! 

 

Thanks for your article. 

Do you have experience with generating email in different formats like RTF, HTML ? 

And of course, how do you handling styles, footer and some scripts? Also, it will be so interesting how it represent in Office365, G-Suite, Yahoo and Zimbra. 

 

Thanks 

 

Cheers,

Gonchik Tsymzhitov

Ivan Tovbin
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
January 17, 2019

@Gonchik Tsymzhitov

We pass "text/html" to setMimeType() method and simply use HTML tags in email body, like so:

setBody("<font face=\"Calibri\" size=\"3\" color=\"black\"><b>some text:</b> <a href=\"$baseUrl/browse/$key\">ABC-123</a>some text<br><br><b>some text</b> some more text<br><b>some more text</b> some text<br><b>some tesxt</b></font>"
Gonchik Tsymzhitov
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
January 17, 2019

@Ivan Tovbin Thanks for your awesome clarify. 

BTW, have you check it on related platrforms? Or what kind of mail service or server do you using?

Ivan Tovbin
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
January 17, 2019

@Gonchik Tsymzhitov

We've been usuing IMB Notes 9 so far, but are looking at MS Exchange right now. Didn't really have any problems with either.

Gonchik Tsymzhitov
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
January 17, 2019

Thanks for opening my eyes about  IBM Notes 9 :)

Evgeniy Buturlia January 17, 2019

@Ivan Tovbin How can you here generate valid urls to jira tickets and other objects from java-objects in code? :)

 

For example - you need to mention $issue and username. How can I do it with this class? 

Ivan Tovbin
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
January 17, 2019

@Evgeniy Buturlia

I suppose something like this should do the trick:

import com.atlassian.jira.component.ComponentAccessor

String baseurl = ComponentAccessor.getApplicationProperties().getString("jira.baseurl")
String issueUrl = "${baseurl}/browse/${issue.getKey()}"
Matt Doar
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
January 17, 2019

Useful, thanks

VVor April 24, 2020

Finally I've found The Best Script to email attachments!

I've spent hours googling for it, I believed all that time that it must be exist :)

I'll use it in my Listener later, but now I'm so happy and wanna thank you for it!

More over I want to share some useful changes I made:

PathUtils.joinPaths(ComponentAccessor.getAttachmentPathManager().getAttachmentPath(), attachment.getIssue().getProjectObject().getKey(), kk.toString() ,attachment.getIssue().getKey(), attachment.getId().toString())

it would be better to use here getOriginalKey() - I have a project, which was renamed, but it's folder named is still the named with it's original name...

 

And one more. The kk variable had a bad value in my Jira instance. So, I've found and read here info about "grouping numbers" which lead to next change:

int kk = (int) Math.round(Math.floor((double) attachment.id / 10000)) * 10000

  As you can see, now kk doesn't depends on Issue number.

И ещё раз Спасибо! :)

Comment

Log in or Sign up to comment
TAGS
AUG Leaders

Atlassian Community Events