How to add the value of a custom field in the email notifications in JIRA 5.0

I have a custom field of the type Select List . I wan to add the value of this field in the email notifications that are sent to the user.

Please let me know how can I do this. I have already had this implemented for JIRA 4.3.4. Is it the same for JIRA 5.0 also.

Amit.

3 answers

1 accepted

This widget could not be displayed.

Almost! If you are using the getCustomFieldValue, it retruns Option object in JIRA5. You need to do a getValue or toString on it.

Did you test it?

Thanks Jobin for the quick response. I did try this out. I made the changes to the atlassian-jira\WEB-INF\classes\templates\email\text\issuecreated. This is the change:

#if ($issue.getCustomFieldValue("customfield_10703"))
$stringUtils.leftPad($issue.getCustomField("customfield_10703").name, $padSize):$issue.getCustomFieldValue("customfield_10703").getValue()
$stringUtils.leftPad($issue.getCustomField("customfield_10703").name, $padSize):$issue.getCustomFieldValue("customfield_10703").toString()

#end

where customfield_10703 is the custom field created by me of type single select selectlist.

The same changes were made to the issuesummary.vm file located within includes folder at the above location.

Please let me know if this is correct.

Thanks.

is it a select list or Version picker?

Jobin,

Yes I have tested what u advices, but it is not working. I did tried both, getValue and toString. but it did not brought the Version value in the email notification.

Please help me out here.

Thanks.

Its a single version picker.

That is not a select list as you mentioned in the question! I don't think the version manager is available in templates and so you might not get the version name. toString should get you the version id, right?

Oh!! my bad... Actually .toString does not give anything in the email. Is there any way I can get the Version value in the email notification. I do have this implemented this for JIRA 4.3.4 this way:

$stringUtils.leftPad($issue.getCustomField("customfield_10703").name, $padSize):$issue.getCustomFieldValue("customfield_10703")

Any Suggestions Jobin??

Are these the only files that need to modified: issuecreated.vm and issuesummary.vm. I could see that the email that I received follows a different template (I mean that the ordering of the fields is different).

Amit.

Jobin,

I hope we can achieve this in JIRA 5.0.

What event are you trying to get emails for? Do you have html or text format? It is still possible in JIRA but I am not sure what is different for you.

Please let me know if the code written above is correct or not?

JIRA 5.0

JIRA 4.3.4

Jobin,

The event that I am using is "issuecreated". The format is text. I have made changes to issuecreated.vm and includes\issuesummary.vm. If you been able to get this working at your end, then probably I might be doing something wroong. I have got this configured in exactly the same manner as in JIRA 4.3.4. In the above screenshot, the first one is JIRA 5.0 (does not include any version info). The second screenshot is JIRA 4.3.4 includes version information.

This widget could not be displayed.

I am unable to configure the default value for custom field of type selectlist in jira 4.4.5. I tested this in 4.1.2 it is working fine. Also the default value it is showing me is null. I have attached the code below . The execute method is throwing the following error

2012-11-27 13:56:00,011 QuartzWorker-0 INFO ServiceRunner [plugins.calculate

dfields.scheduler.CalculatedFieldsJob] >> Appfire Custom Field Ext Plugin: Defau

lt value is not configured for custom field:Days In Progress

2012-11-27 13:56:00,011 QuartzWorker-0 ERROR ServiceRunner [org.quartz.core.

JobRunShell] Job DEFAULT.CalculatedFieldsJob threw an unhandled Exception:

java.lang.NullPointerException

at com.appfire.jira.plugins.calculatedfields.scheduler.CalculatedFieldsJ

ob.execute(CalculatedFieldsJob.java:153)

at org.quartz.core.JobRunShell.run(JobRunShell.java:195)

at com.atlassian.multitenant.quartz.MultiTenantThreadPool$MultiTenantRun

nable.run(MultiTenantThreadPool.java:72)

at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.j

ava:520)

2012-11-27 13:56:00,011 QuartzWorker-0 ERROR ServiceRunner [org.quartz.core.

ErrorLogger] Job (DEFAULT.CalculatedFieldsJob threw an exception.

org.quartz.SchedulerException: Job threw an unhandled exception. [See nested exc

eption: java.lang.NullPointerException]

at org.quartz.core.JobRunShell.run(JobRunShell.java:206)

at com.atlassian.multitenant.quartz.MultiTenantThreadPool$MultiTenantRun

nable.run(MultiTenantThreadPool.java:72)

at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.j

ava:520)

* Nested Exception (Underlying Cause) ---------------

java.lang.NullPointerException

at com.appfire.jira.plugins.calculatedfields.scheduler.CalculatedFieldsJ

ob.execute(CalculatedFieldsJob.java:153)

at org.quartz.core.JobRunShell.run(JobRunShell.java:195)

at com.atlassian.multitenant.quartz.MultiTenantThreadPool$MultiTenantRun

nable.run(MultiTenantThreadPool.java:72)

at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.j

ava:520)

//////////////////////////////////////////////////////////////////// Code ////
public class CalculatedFieldsJob implements Job{
private static Logger log = Logger.getLogger(CalculatedFieldsJob.class);
public final static long ONE_SECOND = 1000;
public final static long SECONDS = 60;
public final static long ONE_MINUTE = ONE_SECOND * 60;
public final static long MINUTES = 60;
public final static long ONE_HOUR = ONE_MINUTE * 60;
public final static long HOURS = 24;
public final static long ONE_DAY = ONE_HOUR * 24;
private CustomFieldManager customFieldManager;
private static final String AF_PROPERTIES_FILE = "appfirecalculatedfields.properties";
private static final String AF_USER_KEY_DEFAULT = "calculatedfieldsuser";
private static final String AF_USER_DEFAULT = "admin";
public final String CUSTOMFIELD_KEY = "com.appfire.jira.plugins.customfield.ext.appfire-customfields-ext:af-daysinstatus";
private static Properties clientProperties = null;
public CustomFieldManager getCustomFieldManager() {
return customFieldManager;
}
public void setCustomFieldManager(CustomFieldManager customFieldManager) {
this.customFieldManager = customFieldManager;
}
public void execute(JobExecutionContext context) throws JobExecutionException {
try {
final URL sampleConfigurationFile = ClassLoaderUtils.getResource(AF_PROPERTIES_FILE, CalculatedFieldsJob.class);
clientProperties = new Properties();
if (sampleConfigurationFile == null) {
log.error("<< appfire properties file could not be found ");
}
clientProperties.load(sampleConfigurationFile.openStream());
log.debug("<< Loaded file " + AF_PROPERTIES_FILE);
if (clientProperties == null) {
log.error("<< clientProperties is null ");
}
} catch (Exception e) {
log.error("<< Error loading " + AF_PROPERTIES_FILE + " " + e.getMessage());
e.printStackTrace();
}
String serviceUser = clientProperties.get(AF_USER_KEY_DEFAULT).toString();
try {
if(serviceUser == null || UserUtils.getUser(serviceUser) == null){
log.error(">> Appfire Custom Field Ext Plugin: Configured user in appfirecalculatedfields.properties is not valid user");
log.info("Reverting to 'admin' user");
serviceUser = AF_USER_DEFAULT;
}
} catch (EntityNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
customFieldManager = (CustomFieldManager)ComponentManager.getComponentInstanceOfType(CustomFieldManager.class);
GenericConfigManager genericConfigManager = (GenericConfigManager)ComponentManager.getComponentInstanceOfType(GenericConfigManager.class);
List<CustomField> fieldList = customFieldManager.getCustomFieldObjects();
boolean wasIndexing = ImportUtils.isIndexIssues();
ImportUtils.setIndexIssues(true);
long jobStartTime = context.getFireTime().getTime();
log.info(">> Appfire Custom Field Ext Plugin: Calculated Field job trigerred at: " + context.getFireTime());
for(Iterator itr = fieldList.iterator(); itr.hasNext();){
CustomField cf = (CustomField)itr.next();
CustomFieldType cft = cf.getCustomFieldType();
if(cft.getKey().equals(CUSTOMFIELD_KEY))
{
FieldConfigSchemeManager sm = (FieldConfigSchemeManager) ComponentManager.getComponentInstanceOfType(FieldConfigSchemeManager.class);
long fieldConfigId = ((FieldConfigSchemeImpl) ((List) sm.getConfigSchemesForField(cf)).get(0)).getId();
Object list = genericConfigManager.retrieve(CustomFieldType.DEFAULT_VALUE_TYPE, Long.toString(fieldConfigId));
if(list == null)
{
log.info(">> Appfire Custom Field Ext Plugin: Default value is not configured for custom field:" + cf.getNameKey());
}
//check if the default value is a valid status
ConstantsManager constantsManager = (ConstantsManager) ComponentManager.getComponentInstanceOfType(ConstantsManager.class);
Status status = constantsManager.getStatusByName(list.toString());
if(status == null){
log.info(">> Appfire Custom Field Ext Plugin: Default value configured for custom field:" + cf.getNameKey() + " is not a valid status");
}else{
log.info(">> Appfire Custom Field Ext Plugin: calculating for custom field: " + cf.getName() );
}
long startCFTime = System.currentTimeMillis();
final JqlQueryBuilder builder = JqlQueryBuilder.newBuilder();
// builder.where().status().eq("Open").and().assignee().eq(getRemoteUser().getName());
builder.where().status().eq(status.getName());
//builder.orderBy().createdDate(SortOrder.DESC);
Query query = builder.buildQuery();
SearchService searchService = ComponentManager.getInstance()
.getSearchService();
SearchResults results = null;
try {
User user = UserUtils.getUser(serviceUser);
JiraServiceContextImpl jiraServiceContextImpl = new JiraServiceContextImpl(user);
results = searchService.search(jiraServiceContextImpl.getUser(), query, PagerFilter.getUnlimitedFilter());
List<Issue> issues = results.getIssues();
log.debug(">> Appfire Custom Field Ext Plugin: Number of issues retreived :" + issues.size());
for (int i = 0; i < issues.size(); i++) {
Issue issue = (Issue) issues.get(i);
List<Transition> transList = getTransitionsList(issue);
long cfValue = getDurantionInStatus(issue,transList);
double secs = (double)cfValue/1000;
double days = ((double)secs)/86400d;
NumberFormat formatter = new DecimalFormat("#0.00");
String numDays = formatter.format(days) + " Days";
MutableIssue missue = ComponentManager.getInstance().getIssueManager().getIssueObject(issue.getId());
DefaultIssueChangeHolder issueChangeHolder = new DefaultIssueChangeHolder();
// final ModifiedValue modifiedValue = (ModifiedValue) modifiedFields.get(cf.getId());
cft.updateValue(cf, missue, numDays);
if(i%100 == 0){
log.debug(">> Appfire Custom Field Ext Plugin: updated issues "+ i + ", custom field value for " + cf.getName() + " calculated as:" + numDays + " current time " + new Date());
}
}
}catch (SearchException e) {
log.error(">> Appfire Custom Field Ext Plugin: Error while executing the search query" );
e.printStackTrace();
} catch (EntityNotFoundException e) {
log.error(">> Appfire Custom Field Ext Plugin: Error rereiving the issse " );
e.printStackTrace();
}
//end for a custom field
long timetakenForCF = System.currentTimeMillis() - startCFTime;
String timeforCF = CalculatedFieldsJob.millisToLongDHMS(timetakenForCF);
log.info(">> Appfire Custom Field Ext Plugin: time to complete values for all issues for Custom Field:" + timeforCF );
}
else
continue;
}
long timetakenForCF = System.currentTimeMillis() - jobStartTime;
String timeforJob = CalculatedFieldsJob.millisToLongDHMS(timetakenForCF);
log.info(">> Appfire Custom Field Ext Plugin: calculated fields job finsihed at :" + timeforJob );
}
private long getDurantionInStatus(Issue issue, List<Transition> transList) {
long timeinMS = 0;
Timestamp tChanged = null;
if(transList.size() > 0 )
{
for(Iterator<Transition> iterator = transList.iterator(); iterator.hasNext();){
Transition tran = iterator.next();
if(tran.getFromStatus().getName().equals(issue.getStatusObject().getName())){
timeinMS = timeinMS + tran.getDurationInMillis();
}
if(tran.getToStatus().getName().equals(issue.getStatusObject().getName()))
tChanged = tran.getChangedAt();
}
}
else{
//No transitions, so initial status is being tracked. get the durationfrom the issue created
timeinMS = ( System.currentTimeMillis() - issue.getCreated().getTime());
}
if( tChanged != null)
timeinMS = timeinMS + ( System.currentTimeMillis() - ( tChanged != null ? tChanged.getTime() : 0 ) );
return timeinMS;
}
private List<Transition> getTransitionsList(Issue issue) {
ChangeHistoryManager changeHistoryManager =
(ChangeHistoryManager)ComponentManager.getComponentInstanceOfType(ChangeHistoryManager.class);
List<ChangeHistory> changeHistoryOfIssue = changeHistoryManager.getChangeHistoriesForUser(issue,null);
ConstantsManager constantsManager = (ConstantsManager) ComponentManager.getComponentInstanceOfType(ConstantsManager.class);
Timestamp tsStartDate = issue.getCreated();
List<Transition> transList1 = new ArrayList<Transition>();
for(ChangeHistory changeHistory : changeHistoryOfIssue) {
//long timeCreated = issue.getCreated().getTime();
for(Object o : changeHistory.getChangeItems()) {
GenericValue gv = (GenericValue) o;
if(gv.get("field").equals("status")){
String prevStatus = (String)gv.get("oldstring");
String nextStatus = (String)gv.get("newstring");
Transition tran = new Transition();
tran.setChangedAt(changeHistory.getTimePerformed());
tran.setFromStatus(constantsManager.getStatusByName(prevStatus));
tran.setToStatus(constantsManager.getStatusByName(nextStatus));
tran.setStartAt(tsStartDate);
transList1.add(tran);
//used to calculate the start of next tranistion
tsStartDate = changeHistory.getTimePerformed();
}
}
}
return transList1;
}
public static String millisToLongDHMS(long duration) {
StringBuffer res = new StringBuffer();
long temp = 0;
if (duration >= ONE_SECOND) {
temp = duration / ONE_DAY;
if (temp > 0) {
duration -= temp * ONE_DAY;
res.append(temp).append(" day").append(temp > 1 ? "s" : "")
.append(duration >= ONE_MINUTE ? ", " : "");
}
temp = duration / ONE_HOUR;
if (temp > 0) {
duration -= temp * ONE_HOUR;
res.append(temp).append(" hour").append(temp > 1 ? "s" : "")
.append(duration >= ONE_MINUTE ? ", " : "");
}
temp = duration / ONE_MINUTE;
if (temp > 0) {
duration -= temp * ONE_MINUTE;
res.append(temp).append(" minute").append(temp > 1 ? "s" : "");
}
if (!res.toString().equals("") && duration >= ONE_SECOND) {
res.append(" and ");
}
temp = duration / ONE_SECOND;
if (temp > 0) {
res.append(temp).append(" second").append(temp > 1 ? "s" : "");
}
return res.toString();
} else {
return "0 second";
}
}
}
This widget could not be displayed.

Hi Amit,

I have customized the Email body message to include the custom fields in the email body.

I have done in Jira 4.4.5

Please check that the custom field IDs are correct.

I have modified 2 files ;

i. /<jira-installation-folder>/atlassian-jira-4.4.5-standalone/atlassian-jira/WEB-INF/classes/templates/email/text/issuecreated.vm

ii. / <jira-installation-folder>/atlassian-jira-4.4.5-standalone/atlassian-jira/WEB-INF/classes/templates/email/text/includes/issuesummary.vm

Added the below lines of code in the issuecreated.vm and issuesummary.vm files :

#if ($issue.getCustomFieldValue("customfield_10006"))

$stringUtils.leftPad($issue.getCustomField("customfield_10006").name, $padSize): $issue.getCustomFieldValue("customfield_10006")

#end

This is working for me. Hope it helps you. :-)

Thank you so much guys. I have got this resolved. The email notification is showing me the version value.

Thank you for all your effort.

Just one more thing. Would you guys know how to do the same thing in the html format. How can I bring the value of the custom field in the html format.

Thanks.

Is it also possible to add the Issue Created date to the custom notifications?

Regards,

Rahul

Anyone know which file one has to edit to customise the email when sharing an issue with somebody? I can't figure out what event that corresponds to or where else that might be configured

Suggest an answer

Log in or Sign up to answer
Atlassian Summit 2018

Meet the community IRL

Atlassian Summit is an excellent opportunity for in-person support, training, and networking.

Learn more
Community showcase
Posted Wednesday in Teamwork

What teamwork quotes inspire you?

Hey everyone! My name is Natalie and I'm an editor of the Atlassian Blog and I've got a question for you: What's your favorite quote about teamwork?  We've compiled a list here, along with...

125 views 15 7
Join discussion

Atlassian User Groups

Connect with like-minded Atlassian users at free events near you!

Find a group

Connect with like-minded Atlassian users at free events near you!

Find my local user group

Unfortunately there are no AUG chapters near you at the moment.

Start an AUG

You're one step closer to meeting fellow Atlassian users at your local meet up. Learn more about AUGs

Groups near you