Confluence has been updated to version 6.15.9

Page tree
Skip to end of metadata
Go to start of metadata


Introduction


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.



The table grid where the subtasks are created

 


The Notifications sent by the Script Listener

Setup the environment

 

  • Configure a grid to track tasks
  • Prepare a script to send out notifications
  • Configure a script listener to send out notifications

 

 

Configure a grid

This configuration makes use of such features of TGE as userlists, column hiding.

gd.columns=isummary,iassignee,previousiassignee,istatus,idue
gd.tablename=actions
gd.ds=jira

col.isummary=Summary
col.isummary.name=summary
col.isummary.type=string
col.isummary.required=true
col.isummary.maxLength=128
col.isummary.width=400

col.iassignee=Assignee
col.iassignee.type=userlist
col.iassignee.required=true
col.iassignee.autocomplete=true
col.iassignee.width=100

col.previousiassignee=previousiassignee
col.previousiassignee.type=string
col.previousiassignee.hidden=true

col.istatus=Status
col.istatus.type=list
col.istatus.list.size=2
col.istatus.name1=Open
col.istatus.value1=O
col.istatus.name2=Done
col.istatus.value2=D
col.istatus.width=60
col.istatus.defaultValue=O

col.idue=Date due
col.idue.type=date
col.idue.defaultDate = +1w

 

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.
 

TgeListener.groovy
import com.atlassian.crowd.embedded.api.User
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.config.properties.APKeys
import com.atlassian.jira.config.properties.ApplicationProperties
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.security.JiraAuthenticationContext
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("com.idalko.jira.plugins.igrid.api.data.TGEGridTableDataManager");
        tgeGridDataManager = get(dataManagerClass)
        applicationProperties = get(ApplicationProperties.class);
        mailQueue = get(MailQueue.class);
    }

    private <T> T get(Class<T> clazz) {
        return ObjectUtils.firstNonNull(
                ComponentAccessor.getComponent(clazz),
                ComponentAccessor.getComponentOfType(clazz),
                ComponentAccessor.getOSGiComponentInstanceOfType(clazz)
        );
    }

    @Override
    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()
                updatedCustomFields.add(customFieldId);
            }
        }
        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>.");
        email.setMimeType("text/html");
        // send it
        SingleMailQueueItem mailItem = new SingleMailQueueItem(email);
        mailQueue.addItem(mailItem);
    }

    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("com.idalko.jira.plugins.igrid.api.data.dto.TGEChangeSet");
        def changeSet = changeSetClass.newInstance();
        changeSet.setRowId(rowId);
        changeSet.setValues(newRowValues);
        changes.add(changeSet);
        // update row
        tgeGridDataManager.applyChanges(issueId, tgeCustomFieldId, changes, user);
    }
}

Then Configure Scripted Listener

Usage

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

Notification
Subject: TGE notification
Body: Hi Samuel Stone! A new task has been assigned to you in issue TGE-1! Please visit http://seriouscopr.com/browse/TGE-1.
  • No labels