Hi,
I created custom scheduled job with ScriptRunner that is checking all our projects and sending out emails to responsible for those projects. An email is basically a HTML table with relevant data for the responsible.
My problem here is that I can't seem to find the solution to format table the way I want. I can add borders, and change the background colors of the table rows and columns, but I cant make the table columns to have fixed width. It always has adjustable size (it takes the size of the text).
Is there a workaround for this maybe? Am I doing something wrong here? Take a look at the "table" part of my code bellow.
if(mapResponsible.get(responsible2.toString()))
{
//style='border:1px solid black'
emailBody =
"<tr>"+
"<td style='border-bottom: 2px solid #e8e3e3; border-right: 2px solid #e8e3e3; width: 500px; overflow: scroll'>" + issueTypeName + "</td>"+
"<td style='border-bottom: 2px solid #e8e3e3; border-right: 2px solid #e8e3e3; width: 500px; overflow: scroll'>${qe_cs.displayName}</td>"+
"</tr>"
mapResponsible.put(responsible2.toString(), mapResponsible.get(responsible2.toString()) + emailBody);
} else // If responsible is not in the list: add responsible, add email body
{
emailBody =
"<table style='max-width: 8500px;'>"+ //style='width: 500px; table-layout:fixed; overflow: hidden;'
"<thead style='background-color: #dddddd'>"+
"<tr>"+
"<th style='width: 500px; overflow: scroll'> T </th>"+
"<th style='width: 500px; overflow: scroll'> QE-CS </th>"+
"</tr>"+
"</div>"+
"</thead>"+
"<tr>"+
"<td style='border-bottom: 2px solid #e8e3e3; border-right: 2px solid #e8e3e3; width: 500px; overflow: scroll'>" + issueTypeName + "</td>"+
"<td style='border-bottom: 2px solid #e8e3e3; border-right: 2px solid #e8e3e3; width: 500px; overflow: scroll'>${qe_cs.displayName}</td>"+
"</tr>"
mapResponsible.put(responsible2.toString(), emailBody);
if(mapCheck.get(responsible2.toString())){
mapCheck.put(responsible2.toString(), mapCheck.get(responsible2.toString()) + "f")
}else{
mapCheck.put(responsible2.toString(), "f")
}
log.debug(responsible2)
}
And it's also not working when I add width to for e.g. <tr> or anywhere.
Thanks in advance.
Hi @kukabgd,
Could you please share the full code that you are using for review?
In regards to the HTML text, could you please try and use Groovy's MarkupBuilder?
Thank you and Kind Regards,
Ram
Hi Ram,
I'm now trying to tune up the code with Markup Builder, meanwhile here is the whole code.
I know I've probably made it overly complicated. Hopefully you can understand some parts:
All of this is done with Custom Scheduled Job:
import com.atlassian.jira.component.ComponentAccessor;
import com.atlassian.jira.issue.CustomFieldManager;
import com.atlassian.jira.issue.Issue;
import com.atlassian.jira.issue.IssueManager;
import com.atlassian.jira.issue.fields.CustomField;
import com.atlassian.jira.issue.link.IssueLink;
import java.text.SimpleDateFormat;
import com.atlassian.jira.mail.Email
import com.atlassian.mail.server.SMTPMailServer
import java.util.Map;
import java.util.Map.Entry;
import com.atlassian.jira.user.ApplicationUser;
import org.apache.log4j.Logger
import org.apache.log4j.Level
import com.atlassian.jira.issue.search.SearchProvider
import com.atlassian.jira.bc.issue.search.SearchService
import com.atlassian.jira.jql.parser.JqlQueryParser
import com.atlassian.jira.web.bean.PagerFilter
//if logs are needed use the code below
log.setLevel(Level.DEBUG)
//log.debug("message")
// Create new map for "Responsible for Findings" field. Here will all responsibles be saved, so that they can get emails with only their findings
HashMap<String, String> mapResponsible = new HashMap<String, String>();
HashMap<String, String> mapCheck = new HashMap<String, String>();
//JQL query for only open projects
def jqlQueryParser = ComponentAccessor.getComponent(JqlQueryParser)
def searchProvider = ComponentAccessor.getComponent(SearchProvider)
def user = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()
def searchService = ComponentAccessor.getComponent(SearchService)
def jqlSearch = "project = XY AND issuetype = 'XY Project' AND Status = open"
def query = jqlQueryParser.parseQuery(jqlSearch)
def results = searchService.search(user, query, PagerFilter.getUnlimitedFilter());
results.getResults().each { documentIssue ->
// if you need a mutable issue you can do:
IssueManager issueManager = ComponentAccessor.getIssueManager();
def issue = issueManager.getIssueObject(documentIssue.id)
// do something to the issue...
String emailBody =""
//IssueManager issueManager = ComponentAccessor.getIssueManager();
def issueLinkManager = ComponentAccessor.getIssueLinkManager()
//def issue = event.getIssue()
// Get fields for filter
def projectNumberField = ComponentAccessor.customFieldManager.getCustomFieldObject("customfield_13021"); // Project Number
def projectNumber = issue.getCustomFieldValue(projectNumberField);
def reviewMsField = ComponentAccessor.customFieldManager.getCustomFieldObject("customfield_13032"); // Review Milestone
def msReviewTypeField = ComponentAccessor.customFieldManager.getCustomFieldObject("customfield_13015"); // Ms Review Type
def commentFindingField = ComponentAccessor.customFieldManager.getCustomFieldObject("customfield_13005"); // Comment/Finding
def responsibleForFindingField = ComponentAccessor.customFieldManager.getCustomFieldObject("customfield_18604"); // Responsible for findings
def overDueField = ComponentAccessor.customFieldManager.getCustomFieldObject("customfield_17004"); // OverDue
// Gets all subtasks of the projects.
final Collection < Issue > subTaskObjects = issue.getSubTaskObjects();
if(subTaskObjects != null && subTaskObjects.size() != 0)
{
for (Issue subTaskObject : subTaskObjects)
{
if(subTaskObject.issueType.name == "XY: Subtask")
{
if(subTaskObject != null)
{
// Gets all findings (links) of the subtasks
final List <IssueLink> issuelinks = issueLinkManager.getInwardLinks(subTaskObject.getId());
if(issuelinks != null && issuelinks.size() != 0)
{
for (IssueLink issueLink : issuelinks)
{
def mutablelink = issueManager.getIssueObject(issueLink.getSourceId())
// get the values of the fields
def reviewMs = mutablelink.getCustomFieldValue(reviewMsField);
def msReviewType = mutablelink.getCustomFieldValue(msReviewTypeField);
def commentFinding = mutablelink.getCustomFieldValue(commentFindingField);
def responsible = mutablelink.getCustomFieldValue(responsibleForFindingField);
def overDue = mutablelink.getCustomFieldValue(overDueField);
if (issueLink.getIssueLinkType().name == "XY Subtask finding" && issueLink != null && mutablelink.status.name == "open")
{
ApplicationUser responsible_2 = (ApplicationUser)mutablelink.getCustomFieldValue(responsibleForFindingField)
if(responsible_2){
def responsible2 = responsible_2.getEmailAddress()
}else{
def responsible2 = "SampleEmail@address.com"
}
// If responsible is already in the list: for that responsible, update the email body
if(mapResponsible.get(responsible2.toString()))
{
//style='border:1px solid black'
emailBody =
"<tr>"+
"<td style='border-bottom: 2px solid #e8e3e3; border-right: 2px solid #e8e3e3; width: 500px'>${mutablelink.key}</td>"+
"<td style='border-bottom: 2px solid #e8e3e3; border-right: 2px solid #e8e3e3; width: 500px'>${projectNumber}</td>"+
"<td style='border-bottom: 2px solid #e8e3e3; border-right: 2px solid #e8e3e3; width: 500px'>${reviewMs}</td>"+
"<td style='border-bottom: 2px solid #e8e3e3; border-right: 2px solid #e8e3e3; width: 500px'>${msReviewType}</td>"+
"<td style='border-bottom: 2px solid #e8e3e3; border-right: 2px solid #e8e3e3; width: 500px'>${commentFinding}</td>"+
"<td style='border-bottom: 2px solid #e8e3e3; border-right: 2px solid #e8e3e3; width: 500px'>${responsible_2.displayName}</td>"+
"<td style='border-bottom: 2px solid #e8e3e3; border-right: 2px solid #e8e3e3; width: 500px'>${overDue}</td>"+
"</tr>"
mapResponsible.put(responsible2.toString(), mapResponsible.get(responsible2.toString()) + emailBody);
} else // If responsible is not in the list: add responsible, add email body
{
emailBody =
"<table style='max-width: 100%;'>"+
"<thead style='background-color: #dddddd'>"+
"<tr>"+
"<th> Key </th>"+
"<th> Project Number </th>"+
"<th> Review MS </th>"+
"<th> MS Review Type </th>"+
"<th> Comment/Finding </th>"+
"<th> Responsible for findings </th>"+
"<th> Overdue </th>"+
"</tr>"+
"</div>"+
"</thead>"+
"<tr>"+
"<td style='border-bottom: 2px solid #e8e3e3; border-right: 2px solid #e8e3e3; width: 500px'>${mutablelink.key}</td>"+
"<td style='border-bottom: 2px solid #e8e3e3; border-right: 2px solid #e8e3e3; width: 500px'>${projectNumber}</td>"+
"<td style='border-bottom: 2px solid #e8e3e3; border-right: 2px solid #e8e3e3; width: 500px'>${reviewMs}</td>"+
"<td style='border-bottom: 2px solid #e8e3e3; border-right: 2px solid #e8e3e3; width: 500px'>${msReviewType}</td>"+
"<td style='border-bottom: 2px solid #e8e3e3; border-right: 2px solid #e8e3e3; width: 500px'>${commentFinding}</td>"+
"<td style='border-bottom: 2px solid #e8e3e3; border-right: 2px solid #e8e3e3; width: 500px'>${responsible_2.displayName}</td>"+
"<td style='border-bottom: 2px solid #e8e3e3; border-right: 2px solid #e8e3e3; width: 500px'>${overDue}</td>"+
"</tr>"
mapResponsible.put(responsible2.toString(), emailBody);
}
}
}
}
}
}
}
}
log.debug(issue.summary);
}
def sendEmail(String emailAddr, String subject, String body)
{
SMTPMailServer mailServer = ComponentAccessor.getMailServerManager().getDefaultSMTPMailServer()
if (mailServer) {
Email email = new Email(emailAddr)
email.setSubject(subject)
email.setBody(body)
email.setMimeType("text/html")// this line is necessary to format email body as HTML
mailServer.send(email)
log.debug("Mail sent")
} else {
log.warn("Please make sure that a valid mailServer is configured")
}
}
def emailSubject = "Your open XY Subtask findings for " + projectNumber
for(String i : mapResponsible.keySet()){
// Close findings table
mapResponsible.put(i.toString(), mapResponsible.get(i.toString()) + "</table>"); // this was necessary to format table in Outlook. this way we only have one table, instead of create new table with new finding
sendEmail("SampleEmail@address.com", emailSubject, mapResponsible.get(i))
}
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi @kukabgd,
Thank you for sharing your code.
Below is an example working code:-
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.mail.Email
import groovy.xml.MarkupBuilder
def customFieldManager = ComponentAccessor.customFieldManager
def mailServerManager = ComponentAccessor.mailServerManager
def mailServer = mailServerManager.defaultSMTPMailServer
def issueManager = ComponentAccessor.issueManager
def issue = issueManager.getIssueByCurrentKey("COM-3")
def subTasks = issue.subTaskObjects
def sampleList = customFieldManager.getCustomFieldObjectsByName("Sample List")[0]
def writer = new StringWriter()
def markup = new MarkupBuilder(writer)
def builder = new StringBuilder()
def emailSubject = "Sample Mail"
if(subTasks!=null) {
markup.html {
head {
title: "Example table"
}
body() {
table("style":"border: 0.5px solid black;") {
tr {
th ("style":"text-align: left; border: 0.5px solid black;", "class":"header","Issue Key" )
th ("style":"text-align: left; border: 0.5px solid black;", "class":"header","Sample List")
}
subTasks.each {
def key = it.key
def customFieldValue = it.getCustomFieldValue(sampleList)
tr {
td ("style":"text-align: left; border: 0.5px solid black;", "class":"row","${key.toString()}")
td ("style":"text-align: left; border: 0.5px solid black;", "class":"row","${customFieldValue.toString()}")
}
}
}
}
}
builder.append(writer)
}
if (mailServer) {
def email = new Email("rkumar@adaptavist.com")
email.setSubject(emailSubject)
email.setMimeType("text/html")
email.setBody(builder.toString())
def threadClassLoader = Thread.currentThread().contextClassLoader
Thread.currentThread().contextClassLoader = mailServer.class.classLoader
mailServer.send(email)
Thread.currentThread().contextClassLoader = threadClassLoader
}
Please note, this sample code is not 100% exact to your environment. Hence, you will need to make the required modifications.
Below is a print screen of the Custom Job Configuration:-
And below is a print screen of the sample output produced:-
I hope this helps to answer your question. :)
Thank you and Kind Regards,
Ram
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi Ram,
thank you so much for the code, it helped me a lot with Email formatting. However now I can't quite get the grouping of issue links to work as I wanted.
So the idea was to go through all projects, get open sub-tasks and get the issue links in those sub-tasks.
The issue links should be sorted according to responsible person for those links (see picture Escalation Service Explained.png).
Then each responsible shall get an Email with only his/hers issue links (this is why I needed MarkupBuilder, so that I can format the table in the email).
Now for my previous solution, I have used HashMaps, and the sorting was working correctly, however now I cant get the sorting to work, probably when it comes to MarkupBuilder it is not working as in the example before.
So instead to get the email as shown above, both responsible persons get the same Email (see bellow Escalation Service Email Not Working Correctly.png)
Notice the counter numbers and the way it is saving them in the table.
And finally, here is my code that I am using at the moment:
import com.atlassian.jira.component.ComponentAccessor;
import com.atlassian.jira.issue.CustomFieldManager;
import com.atlassian.jira.issue.Issue;
import com.atlassian.jira.issue.IssueManager;
import com.atlassian.jira.issue.fields.CustomField;
import com.atlassian.jira.issue.link.IssueLink;
import java.text.SimpleDateFormat;
import com.atlassian.jira.mail.Email
import com.atlassian.mail.server.SMTPMailServer
import java.util.Map;
import java.util.Map.Entry;
import com.atlassian.jira.user.ApplicationUser;
import org.apache.log4j.Logger
import org.apache.log4j.Level
import com.atlassian.jira.issue.search.SearchProvider
import com.atlassian.jira.bc.issue.search.SearchService
import com.atlassian.jira.jql.parser.JqlQueryParser
import com.atlassian.jira.web.bean.PagerFilter
import groovy.xml.MarkupBuilder
//if logs are needed use the code below
log.setLevel(Level.DEBUG)
log.debug("message")
// Create new map for "Responsible for Findings" field. Here will all responsibles be saved, so that they can get subscriptions with only their findings
HashMap<String, String> mapResponsible = new HashMap<String, String>();
HashMap<String, String> mapResponsibleCountermeasure = new HashMap<String, String>();
HashMap<String, String> mapCheck = new HashMap<String, String>();
def jqlQueryParser = ComponentAccessor.getComponent(JqlQueryParser)
def searchProvider = ComponentAccessor.getComponent(SearchProvider)
def user = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()
def searchService = ComponentAccessor.getComponent(SearchService)
def jqlSearch = "project = QTOOL AND issuetype = 'QT Project' AND Status = open and key = QTOOL-10587"
def query = jqlQueryParser.parseQuery(jqlSearch)
def writer = new StringWriter()
def markup = new MarkupBuilder(writer)
def builder = new StringBuilder()
//def query = jqlQueryParser.parseQuery("project = QTOOL AND issuetype = 'QT Project' AND Status = open" )// project = QTOOL AND issuetype = "QT Project" AND Status = open
def results = searchService.search(user, query, PagerFilter.getUnlimitedFilter());
log.debug("Total issues: ${results.total}")
results.getResults().each { documentIssue ->
log.debug(documentIssue.key)
// if you need a mutable issue you can do:
IssueManager issueManager = ComponentAccessor.getIssueManager();
def issueLinkManager = ComponentAccessor.getIssueLinkManager()
def issue = issueManager.getIssueObject(documentIssue.id)
// Get fields for filter
def projectNumberField = ComponentAccessor.customFieldManager.getCustomFieldObject("customfield_13021"); // Project Number
def projectNumber = issue.getCustomFieldValue(projectNumberField);
def reviewMsField = ComponentAccessor.customFieldManager.getCustomFieldObject("customfield_13032"); // Review Milestone
def msReviewTypeField = ComponentAccessor.customFieldManager.getCustomFieldObject("customfield_13015"); // Ms Review Type
def commentFindingField = ComponentAccessor.customFieldManager.getCustomFieldObject("customfield_13005"); // Comment/Finding
def responsibleForFindingField = ComponentAccessor.customFieldManager.getCustomFieldObject("customfield_18604"); // Responsible for findings [still needs to be implemented for Countermeasure]
def overDueField = ComponentAccessor.customFieldManager.getCustomFieldObject("customfield_17004"); // OverDue
def count = 1
int size = 0;
// Gets all subtasks of the projects.
final Collection < Issue > subTaskObjects = issue.getSubTaskObjects();
if(subTaskObjects != null && subTaskObjects.size() != 0)
{
for (Issue subTaskObject : subTaskObjects)
{
if(subTaskObject.issueType.name == "QT: Milestone Review")
{
if(subTaskObject != null)
{
// Gets all findings of the subtasks
final List <IssueLink> issuelinks = issueLinkManager.getInwardLinks(subTaskObject.getId());
if(issuelinks != null && issuelinks.size() != 0)
{
for (IssueLink issueLink : issuelinks)
{
def mutablelink = issueManager.getIssueObject(issueLink.getSourceId())
def issueTypeName = issueLink.getIssueLinkType().name;
def reviewMs = mutablelink.getCustomFieldValue(reviewMsField);
def msReviewType = mutablelink.getCustomFieldValue(msReviewTypeField);
def commentFinding = mutablelink.getCustomFieldValue(commentFindingField);
def responsible = mutablelink.getCustomFieldValue(responsibleForFindingField);
def overDue = mutablelink.getCustomFieldValue(overDueField);
/**************************************** START MARKUP HTML ****************************************/
markup.html {
head {
title: "Example table"
}
/**************************************** IF MS REVIEW FINDING ****************************************/
if (issueLink.getIssueLinkType().name == "MS Review finding" && issueLink != null && mutablelink.status.name == "open")
{
//SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy")
// def dueDateFormated = sdf.format(mutablelink.dueDate)
ApplicationUser responsible_2 = (ApplicationUser)mutablelink.getCustomFieldValue(responsibleForFindingField)
def responsible2
if(responsible_2){
responsible2 = responsible_2.getEmailAddress()
}else{
responsible2 = "test@test.com"
}
// If responsible is already in the list: for that responsible, update the email body
if(mapResponsible.get(responsible2.toString()))
{
body()
{
table("style":"border: 0.5px solid black;")
{
tr {
//td ("style":"text-align: left; border: 0.5px solid black; width: 100px", "class":"row","${issueTypeName.toString()}")
td ("style":"text-align: left; border: 0.5px solid black; width: 100px", "class":"row","${count++}")
td ("style":"text-align: left; border: 0.5px solid black; width: 100px", "class":"row","${mutablelink.key.toString()}")
td ("style":"text-align: left; border: 0.5px solid black; width: 200px", "class":"row","${projectNumber.toString()}")//responsible_2.displayName
td ("style":"text-align: left; border: 0.5px solid black; width: 200px", "class":"row","${responsible_2.displayName.toString()}")
}
}
}
log.debug("KEY 2")
log.debug(mutablelink.key.toString())
//mapResponsible.put(responsible2.toString(), mapResponsible.get(responsible2.toString()) + builder.append(writer));
mapResponsible.put(responsible2.toString(), mapResponsible.get(responsible2.toString()) + builder.append(writer));
writer.buffer.setLength(0)
log.debug("not first" + mapResponsible.get(responsible2.toString()))
} // If responsible is not in the list: add responsible, add email body*/
else{
body()
{
table("style":"border: 0.5px solid black;")
{
tr {
//th ("style":"text-align: left; border: 0.5px solid black; width: 100px", "class":"header","Issue Type" )
th ("style":"text-align: left; border: 0.5px solid black; width: 100px", "class":"header","Counter" )
th ("style":"text-align: left; border: 0.5px solid black; width: 100px", "class":"header","Issue Key" )
th ("style":"text-align: left; border: 0.5px solid black; width: 200px", "class":"header","Project Number")
th ("style":"text-align: left; border: 0.5px solid black; width: 200px", "class":"header","Responsible")
}
tr {
//td ("style":"text-align: left; border: 0.5px solid black; width: 100px", "class":"row","${issueTypeName.toString()}")
td ("style":"text-align: left; border: 0.5px solid black; width: 100px", "class":"row","${count++}")
td ("style":"text-align: left; border: 0.5px solid black; width: 100px", "class":"row","${mutablelink.key.toString()}")
td ("style":"text-align: left; border: 0.5px solid black; width: 200px", "class":"row","${projectNumber.toString()}")
td ("style":"text-align: left; border: 0.5px solid black; width: 200px", "class":"row","${responsible_2.displayName.toString()}")
}
}
log.debug("KEY 1")
log.debug(mutablelink.key.toString())
}
mapResponsible.put(responsible2.toString(), builder.append(writer));
log.debug("first" + mapResponsible.get(responsible2.toString()))
//log.debug(builder.append(writer))
writer.buffer.setLength(0)
if(mapCheck.get(responsible2.toString())){
mapCheck.put(responsible2.toString(), mapCheck.get(responsible2.toString()) + "f")
}else{
mapCheck.put(responsible2.toString(), "f")
}
log.debug(responsible2)
}
} /**************************************** END IF MS REVIEW FINDING ****************************************/
} /**************************************** END MARKUP HTML ****************************************/
}
}
}
}
}
}
log.debug(issue.summary);
} /**************************************** END OF QUERY ****************************************/
def sendEmail(String emailAddr, String subject, String body)
{
SMTPMailServer mailServer = ComponentAccessor.getMailServerManager().getDefaultSMTPMailServer()
if (mailServer) {
Email email = new Email(emailAddr)
email.setSubject(subject)
email.setBody(body)
email.setMimeType("text/html")// this line is necessary to format email body as HTML
mailServer.send(email)
log.debug("Mail sent")
} else {
log.warn("Please make sure that a valid mailServer is configured")
}
}
def emailSubject = "Your open MS Review findings for "
//Send Email to responsibles
for(String i : mapCheck.keySet()){
sendEmail(i, "Testing Subscriptions", mapResponsible.get(i).toString())
}
Any tips maybe? :)
Again, thanks a lot for the solution above, it is exactly what I needed.
Best regards,
Stefan
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi @kukabgd
Glad to hear the solution worked for you. :)
For your requirement, you could try to modify your code slightly as shown below:-
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.mail.Email
import groovy.xml.MarkupBuilder
def mailServerManager = ComponentAccessor.mailServerManager
def mailServer = mailServerManager.defaultSMTPMailServer
def issueManager = ComponentAccessor.issueManager
def issue = issueManager.getIssueByCurrentKey("MOCK-3")
def subTasks = issue.subTaskObjects
def writer = new StringWriter()
def markup = new MarkupBuilder(writer)
def mailBodyList = [] as ArrayList<String>
def emailSubject = "Sample Mail"
if(subTasks!=null) {
markup.html {
head {
title: "Example table"
}
body() {
table("style":"border: 0.5px solid black;") {
tr {
th ("style":"text-align: left; border: 0.5px solid black;", "class":"header","Counter" )
th ("style":"text-align: left; border: 0.5px solid black;", "class":"header","Parent Issue Key" )
th ("style":"text-align: left; border: 0.5px solid black;", "class":"header","Sub Tasks")
th ("style":"text-align: left; border: 0.5px solid black;", "class":"header","Sub Tasks Assignee")
}
subTasks.eachWithIndex{ Issue i, int index ->
def key = i.key
def parent = i.parentObject
def reporter = i.reporter.name
tr {
td ("style":"text-align: left; border: 0.5px solid black;", "class":"row","${index+1}")
td ("style":"text-align: left; border: 0.5px solid black;", "class":"row","${parent.key}")
td ("style":"text-align: left; border: 0.5px solid black;", "class":"row","${key}")
td ("style":"text-align: left; border: 0.5px solid black;", "class":"row","${reporter}")
}
}
}
}
}
}
mailBodyList.addAll(writer.toString())
mailBodyList.findAll {
if (mailServer) {
def email = new Email("rkumar@adaptavist.com")
email.setSubject(emailSubject)
email.setMimeType("text/html")
email.setBody(it)
def threadClassLoader = Thread.currentThread().contextClassLoader
Thread.currentThread().contextClassLoader = mailServer.class.classLoader
mailServer.send(email)
Thread.currentThread().contextClassLoader = threadClassLoader
}
}
Please note, this sample code is not 100% exact to your environment. Hence, you will need to make the required modifications.
So the modified code, I am not able to display both the Parent issue as well as the sub-tasks, the reporter of the Sub-Task and also a counter to get the row numbers.
Below is a print screen of the output produced:-
I hope this helps to answer your question. :)
Thank you and Kind Regards,
Ram
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi,
I was hopping to achieve this without any additional add-ons. Hopefully someone has some other solution for this problem, since I am still trying to resolve this.
BR,
Stefan
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
If you decide that re-building the internal logic of a notification plugin isn't a great use of your time, check out Notification Assistant for Jira where you could set this up. The only item we won't have is the Counter column.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Online forums and learning are now in one easy-to-use experience.
By continuing, you accept the updated Community Terms of Use and acknowledge the Privacy Policy. Your public name, photo, and achievements may be publicly visible and available in search engines.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.