I have no useful feedback on the changes (haven't had a chance to test them, probably won't soon) - just wanted to say thanks for taking point on this Benedict.
On Tue, Apr 16, 2019 at 6:23 AM Benedict Elliott Smith <bened...@apache.org> wrote: > Some exciting news from the Jira changes (maybe). > > We’re done! We’ve even achieved the stretch goals. > > Does anyone have any further suggestions from the new workflow, after > having tried it out for a bit? Speak up while we have the easy capability > to make changes! > > - Somebody has proposed making Change Category a multi-select list, which > would require recreating this. Does anybody have an opinion on this? > - Would anybody prefer that one of the new fields be recreated as a > project-specific select-list, like Platform and Impacts (see below), so we > can manage the options? > > Gavin McDonald kindly set us up with two plugins. With the ScriptRunner > plugin, I’ve setup scripted listeners to maintain the Authors field (based > on Assignee) and Priority field (based on updates to Severity), as well as > initialising both of these for all issues. Try it out - it works > surprisingly well, but please let me know ASAP if there are any bugs. > > The Authors management may be slightly unintuitive, in that if assignee is > updated we always overwrite Authors to the value of the assignee. We could > maybe do something more sophisticated by checking what the prior value of > Assignee was, but this looked very fiddly. I think in the vast majority of > circumstances this will work as we expect; if there are multiple authors > actively working I expect the assignee not to change; if the assignee is > cleared we probably want to clear the Authors field; and if it is set to a > new value it’s probably the new author. If somebody has suggestions for a > better approach, please chime in. > > The other plugin provided us with project-specific select lists - which > we’ve used for Platform and Impacts. These can be modified by any project > admin, so we can maintain them ourselves. Impacts and Platform have a > default value of None and All respectively, to avoid UX ugliness with this > multi-select list; don’t ask me why this makes them prettier, but it does. > I had originally proposed using these values so that we could force the > user to specify a value, but opted to not make the UX ugly, while > supporting project ownership of the options. Opinions on this decision > welcome. > > For posterity, here are the ScriptRunner scripts I used and setup: > > > ===== LISTENER TO SET PRIORITY WHEN SEVERITY UPDATED ==== > > import com.atlassian.jira.component.ComponentAccessor > import com.atlassian.jira.event.type.EventDispatchOption > import com.atlassian.jira.issue.MutableIssue > import com.atlassian.jira.util.ImportUtils > import com.atlassian.jira.user.ApplicationUser; > import com.atlassian.jira.issue.util.DefaultIssueChangeHolder > import com.atlassian.jira.issue.util.IssueChangeHolder > import com.atlassian.jira.issue.priority.Priority > import com.atlassian.jira.issue.context.IssueContext > import com.atlassian.jira.issue.customfields.option.Option > import com.atlassian.jira.issue.customfields.option.Options > import com.atlassian.jira.issue.customfields.manager.OptionsManager > > def issue = event.issue as MutableIssue > def severityChange = null != > event?.getChangeLog()?.getRelated("ChildChangeItem")?.find {it.field == > "Severity"} > if (issue.issueType.name == "Bug" && severityChange) { > def issueManager = ComponentAccessor.getIssueManager() > def customFieldManager = ComponentAccessor.getCustomFieldManager() > def cf = > customFieldManager.getCustomFieldObject("customfield_12313820" ); > > def constantsManager = ComponentAccessor.getConstantsManager() > Priority pUrgent = constantsManager.getPriorities().find {it.name == > "Urgent"} > Priority pHigh = constantsManager.getPriorities().find {it.name == > "High"} > Priority pNormal = constantsManager.getPriorities().find {it.name == > "Normal"} > Priority pLow = constantsManager.getPriorities().find {it.name == > "Low"} > > def optManager = ComponentAccessor.getOptionsManager(); > Options severities = > optManager.getOptions(cf.getRelevantConfig(IssueContext.GLOBAL)) > Option sCritical = severities.getOptionForValue("Critical", null) > Option sNormal = severities.getOptionForValue("Normal", null) > Option sLow = severities.getOptionForValue("Low", null) > > def sdUser = ComponentAccessor.getUserManager().getUserByKey('robot') > def priority = issue.getPriority() > def severity = issue.getCustomFieldValue(cf) > > if (severity != null) { > def newPriority = priority; > if (severity == sCritical) { > newPriority = pUrgent > } else if (severity == sNormal) { > newPriority = pNormal > } else if (severity == sLow) { > newPriority = pLow > } > if (newPriority != priority) { > issue.setPriority(newPriority) > issueManager.updateIssue(sdUser, issue, > EventDispatchOption.DO_NOT_DISPATCH, false) > } > } > } > > > ===== LISTENER TO SET AUTHORS WHEN ASSIGNEE UPDATED ==== > > import com.atlassian.jira.issue.Issue > import com.atlassian.jira.issue.ModifiedValue > import com.atlassian.jira.issue.util.DefaultIssueChangeHolder > import com.atlassian.jira.component.ComponentAccessor > import com.atlassian.jira.user.ApplicationUser; > import com.atlassian.jira.issue.MutableIssue > import com.atlassian.jira.event.type.EventDispatchOption > import com.atlassian.jira.issue.index.IssueIndexingService > > def sdUser = ComponentAccessor.getUserManager().getUserByKey('robot') > def issueManager = ComponentAccessor.getIssueManager() > def customFieldManager = ComponentAccessor.getCustomFieldManager() > def cf = customFieldManager.getCustomFieldObjectByName( "Authors" ); > > def issue = event.issue as MutableIssue > def assignee = issue.getAssignee() > if (assignee != null) { > def authors = (List<ApplicationUser>)issue.getCustomFieldValue(cf) > if (authors == null || !authors.contains(assignee)) { > authors = new ArrayList<ApplicationUser>() > authors.add(assignee) > issue.setCustomFieldValue(cf, authors) > issueManager.updateIssue(sdUser, issue, > EventDispatchOption.DO_NOT_DISPATCH, false) > } > } else { > def authors = (List<ApplicationUser>)issue.getCustomFieldValue(cf) > if (authors != null) { > issue.setCustomFieldValue(cf, new ArrayList<ApplicationUser>()) > issueManager.updateIssue(sdUser, issue, > EventDispatchOption.DO_NOT_DISPATCH, false) > } > } > > > ===== SCRIPT RO COPY ASSIGNEE TO AUTHORS ==== > > import com.atlassian.jira.component.ComponentAccessor > import com.atlassian.jira.issue.search.SearchProvider > import com.atlassian.jira.jql.parser.JqlQueryParser > import com.atlassian.jira.web.bean.PagerFilter > import com.atlassian.jira.event.type.EventDispatchOption > import com.atlassian.jira.issue.MutableIssue > import com.atlassian.jira.issue.index.IssueIndexingService > import com.atlassian.jira.util.ImportUtils > import com.atlassian.jira.user.ApplicationUser; > import com.atlassian.jira.issue.util.DefaultIssueChangeHolder > import com.atlassian.jira.issue.util.IssueChangeHolder > > def issueManager = ComponentAccessor.getIssueManager() > def customFieldManager = ComponentAccessor.getCustomFieldManager() > def cf = customFieldManager.getCustomFieldObjectByName( "Authors" ); > > def sdUser = ComponentAccessor.getUserManager().getUserByKey('robot') > def jqlQueryParser = ComponentAccessor.getComponent(JqlQueryParser) > def searchProvider = ComponentAccessor.getComponent(SearchProvider) > def query1 = jqlQueryParser.parseQuery("project = CASSANDRA") > def results1 = searchProvider.search(query1, sdUser, > PagerFilter.getUnlimitedFilter()) > log.warn("All issues=" + results1.total) > results1.getIssues().each() { > documentIssue -> > def issue = issueManager.getIssueObject(documentIssue.id) > def assignee = issue.getAssignee() > if (assignee != null) { > def authors = > (List<ApplicationUser>)issue.getCustomFieldValue(cf) > if (authors == null) { > authors = new ArrayList<ApplicationUser>() > } else { > authors = new ArrayList<ApplicationUser>(authors) > } > if (!authors.contains(assignee)) { > authors.add(assignee) > issue.setCustomFieldValue(cf, authors) > issueManager.updateIssue(sdUser, issue, > EventDispatchOption.DO_NOT_DISPATCH, false) > log.warn(documentIssue.id) > } > } > } > > > ===== SCRIPT TO COPY PRIORITY TO SEVERITY ==== > > import com.atlassian.jira.component.ComponentAccessor > import com.atlassian.jira.issue.search.SearchProvider > import com.atlassian.jira.jql.parser.JqlQueryParser > import com.atlassian.jira.web.bean.PagerFilter > import com.atlassian.jira.event.type.EventDispatchOption > import com.atlassian.jira.issue.MutableIssue > import com.atlassian.jira.issue.index.IssueIndexingService > import com.atlassian.jira.util.ImportUtils > import com.atlassian.jira.user.ApplicationUser; > import com.atlassian.jira.issue.util.DefaultIssueChangeHolder > import com.atlassian.jira.issue.util.IssueChangeHolder > import com.atlassian.jira.issue.priority.Priority > import com.atlassian.jira.issue.context.IssueContext > import com.atlassian.jira.issue.customfields.option.Option > import com.atlassian.jira.issue.customfields.option.Options > import com.atlassian.jira.issue.customfields.manager.OptionsManager > > def issueManager = ComponentAccessor.getIssueManager() > def customFieldManager = ComponentAccessor.getCustomFieldManager() > def cf = customFieldManager.getCustomFieldObject("customfield_12313820" ); > > def constantsManager = ComponentAccessor.getConstantsManager() > Priority pUrgent = constantsManager.getPriorities().find {it.name == > "Urgent"} > Priority pHigh = constantsManager.getPriorities().find {it.name == "High"} > Priority pNormal = constantsManager.getPriorities().find {it.name == > "Normal"} > Priority pLow = constantsManager.getPriorities().find {it.name == "Low"} > > def optManager = ComponentAccessor.getOptionsManager(); > Options severities = > optManager.getOptions(cf.getRelevantConfig(IssueContext.GLOBAL)) > Option sCritical = severities.getOptionForValue("Critical", null) > Option sNormal = severities.getOptionForValue("Normal", null) > Option sLow = severities.getOptionForValue("Low", null) > > def sdUser = ComponentAccessor.getUserManager().getUserByKey('robot') > def jqlQueryParser = ComponentAccessor.getComponent(JqlQueryParser) > def searchProvider = ComponentAccessor.getComponent(SearchProvider) > def query1 = jqlQueryParser.parseQuery("project = CASSANDRA and priority = > High") > def results1 = searchProvider.search(query1, sdUser, > PagerFilter.getUnlimitedFilter()) > log.warn("All issues=" + results1.total) > results1.getIssues().each() { > documentIssue -> > def issue = issueManager.getIssueObject(documentIssue.id) > def priority = issue.getPriority() > if (priority != null) { > def severity = issue.getCustomFieldValue(cf) > def newSeverity = severity; > if (priority == pUrgent) { > newSeverity = sCritical > } else if (priority == pHigh) { > newSeverity = sNormal > } else if (priority == pNormal) { > newSeverity = sNormal > } else if (priority == pLow) { > newSeverity = sLow > } else { > log.warn(priority) > } > if (newSeverity != severity) { > issue.setCustomFieldValue(cf, newSeverity) > issueManager.updateIssue(sdUser, issue, > EventDispatchOption.DO_NOT_DISPATCH, false) > } > } > }