Confluence has been updated to version 6.15.9

Page tree

Versions Compared


  • This line was added.
  • This line was removed.
  • Formatting was changed.



Use TGE as a storage of simple sub-tasks and get email notification on change of the task assignee

This article details out how to setup a table grid to track 'sub' tasks, and be notified when something is assigned to yourself.  It is using Script Listeners (from ScriptRunner) to send out the notifications.


Create the TGE Listener

Install latest ScriptRunner add-on, create a file "TgeListener.groovy" with the following content in the "$JIRA_HOME/scripts/com/idalko/scripts" directory.

Code Block
import com.atlassian.crowd.embedded.api.User
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.event.issue.AbstractIssueEventListener
import com.atlassian.jira.event.issue.IssueEvent
import com.atlassian.jira.issue.CustomFieldManager
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.fields.CustomField
import com.atlassian.jira.user.ApplicationUser
import com.atlassian.jira.user.UserUtils
import com.atlassian.mail.Email
import com.atlassian.mail.queue.MailQueue
import com.atlassian.mail.queue.SingleMailQueueItem
import com.atlassian.plugin.PluginAccessor
import org.apache.commons.lang3.ObjectUtils
import org.apache.log4j.Logger
import org.ofbiz.core.entity.GenericValue
class TgeListener extends AbstractIssueEventListener {

    // configuration
    private static final String TGE_FIELD_NAME = "TGE";
    private static final String TRACKED_USERLIST_COLUMNS_NAME = "iassignee";
    private static final String PREVIOUS_USERLIST_COLUMNS_NAME = "previousiassignee";

    private static final Logger LOG = Logger.getLogger("com.idalko.scripts");

    private final CustomFieldManager customFieldManager;
    private final PluginAccessor pluginAccessor;
    private final JiraAuthenticationContext jiraAuthenticationContext;
    private final ApplicationProperties applicationProperties;
    private final MailQueue mailQueue;
    private final def tgeGridDataManager;

    public TgeListener() {
        customFieldManager = get(CustomFieldManager.class);
        pluginAccessor = get(PluginAccessor.class);
        jiraAuthenticationContext = get(JiraAuthenticationContext.class);
        Class dataManagerClass = pluginAccessor.getClassLoader().findClass("");
        tgeGridDataManager = get(dataManagerClass)
        applicationProperties = get(ApplicationProperties.class);
        mailQueue = get(MailQueue.class);

    private <T> T get(Class<T> clazz) {
        return ObjectUtils.firstNonNull(

    void issueUpdated(IssueEvent event) {
        Set<Long> updatedCustomFieldIds = getUpdatedCustomFieldsIds(event);
        LOG.debug("Updated custom fields IDs are: " + updatedCustomFieldIds);
        CustomField tgeCustomField = customFieldManager.getCustomFieldObjectByName(TGE_FIELD_NAME);
        Long tgeCustomFieldId = tgeCustomField.getIdAsLong();
        LOG.debug("TGE custom fields ID is: " + tgeCustomFieldId);
        if (updatedCustomFieldIds.contains(tgeCustomFieldId)) {
            Issue issue = event.getIssue()
            processTgeFieldChange(issue, tgeCustomFieldId);

    private Set<Long> getUpdatedCustomFieldsIds(IssueEvent event) {
        List<GenericValue> changeItems = event.getChangeLog().getRelated("ChildChangeItem");
        Set<Long> updatedCustomFields = new HashSet<Long>();
        for (GenericValue changeItem : changeItems) {
            String changeType = changeItem.getString("fieldtype");
            if (changeType.equalsIgnoreCase("custom")) {
                String customFieldName = changeItem.getString("field");
                CustomField customFieldObject = customFieldManager.getCustomFieldObjectByName(customFieldName);
                Long customFieldId = customFieldObject.getIdAsLong()
        return updatedCustomFields;

    private void processTgeFieldChange(Issue issue, long tgeCustomFieldId) {
        User user = getCurrentUser();
        Long issueId = issue.getId();
        def result = tgeGridDataManager.readGridData(issueId, tgeCustomFieldId, null, null, 0, 10, user);
        List<Map<String, Object>> gridData = result.getValues();
        for (Map<String, Object> row : gridData) {
            String selectedUserName = getSelectedUserNameFromRow(row);
            String previousUserName = getPreviousUserNameFromRow(row);
            if (selectedUserName != null && !selectedUserName.equals(previousUserName)) {
                sendEmailNotificationToUser(selectedUserName, issue);
                setPreviousUserEqualToSelected(row, issueId, tgeCustomFieldId, user);

    private User getCurrentUser() {
        Object userObject = jiraAuthenticationContext.getLoggedInUser();
        User user = userObject instanceof ApplicationUser ? ((ApplicationUser) userObject).getDirectoryUser() : (User) userObject;
        return user;

    private String getSelectedUserNameFromRow(Map<String, Object> row) {
        Map<String, String> selectedUser = (Map<String, String>) row.get(TRACKED_USERLIST_COLUMNS_NAME)
        String selectedUserName = selectedUser.get("value");
        return selectedUserName;

    private String getPreviousUserNameFromRow(Map<String, Object> row) {
        return row.get(PREVIOUS_USERLIST_COLUMNS_NAME);

    private void sendEmailNotificationToUser(String userName, Issue issue) {
        // prepare data
        User user = UserUtils.getUser(userName);
        String userEmail = user.getEmailAddress();
        String baseUrl = applicationProperties.getString(APKeys.JIRA_BASEURL);
        String issueKey = issue.getKey();
        String issueLink = baseUrl + "/browse/" + issueKey;
        String userDisplayName = user.getDisplayName();
        // build email
        Email email = new Email(userEmail);
        email.setSubject("TGE notification");
        email.setBody("Hi " + userDisplayName + "! A new task has been assigned to you in issue " + issueKey
                + "! Please visit <a href=\"" + issueLink + "\">" + issueKey + "</a>.");
        // send it
        SingleMailQueueItem mailItem = new SingleMailQueueItem(email);

    private void setPreviousUserEqualToSelected(Map<String, Object> row, Long issueId, Long tgeCustomFieldId, User user) {
        List changes = new ArrayList();
        // get row values
        Long rowId = (Long) row.get("id");
        String selectedUserName = getSelectedUserNameFromRow(row);
        // prepare new values
        Map<String, Object> newRowValues = new HashMap<String, Object>();
        newRowValues.put(PREVIOUS_USERLIST_COLUMNS_NAME, selectedUserName);
        // create changeset
        Class changeSetClass = pluginAccessor.getClassLoader().loadClass("");
        def changeSet = changeSetClass.newInstance();
        // update row
        tgeGridDataManager.applyChanges(issueId, tgeCustomFieldId, changes, user);

Then Configure Scripted Listener


Create new rows in the grid, edit existing ones. On the change of the value of the "Assignee" column new Assignee will get a TGE email notification like this

Code Block
Subject: TGE notification
Body: Hi Samuel Stone! A new task has been assigned to you in issue TGE-1! Please visit