How to Clear a User Custom Field using Groovy

Hi,

when a feedback was given I want to clear the field with a post function:

CustomFieldManager customFieldManager = componentManager.getCustomFieldManager()
IssueChangeHolder changeHolder = new DefaultIssueChangeHolder();
Issue issue  = issue

def cfNFF = customFieldManager.getCustomFieldObjects(issue).find {it.name == 'Need Feedback From'}
def cfNFFval = issue.getCustomFieldValue(cfNFF)
log.warn "Need Feedback From: '"+cfNFFval+"'"
if ( cfNFFval ) {
   log.warn "Need Feedback From: '"+cfNFFval+"' cleared to"
   cfNFF.updateValue(null, issue, new ModifiedValue(cfNFFval, new UserCFType(null)), changeHolder); // ???
   log.warn "Need Feedback From: '"+issue.getCustomFieldValue(cfNFF)+"'"
} else {
   log.info "Need Feedback From: '"+cfNFFval+"' already cleared"
}

At the line marked I get an error because

new UserCFType(null)

is not allowed here. I tried 'null' but this also did not work

a) How do I have to insert an empty UserCFType value here?

b) Is there a better solution to clear the field?

Thank you

4 answers

1 accepted

Hi,

using setFieldValue() solves the issue. Here the complete Groovy code of the post function:

import org.apache.log4j.Category
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.ComponentManager
import com.atlassian.jira.issue.ModifiedValue
import com.googlecode.jsu.util.WorkflowUtils

log = Category.getInstance("post-clear-need_feedback_from.groovy")
log.warn "begin"

// issue is provided by environment
log.warn "issue "+issue.getKey()
cfm    = componentManager.getCustomFieldManager()
ch     = new DefaultIssueChangeHolder();
nff   = cfm.getCustomFieldObjects(issue).find {it.name == 'Need Feedback From'}
cfv   = issue.getCustomFieldValue(nff)
log.warn "Need Feedback From = '"+cfv+"' cleared"
// mv    = new ModifiedValue(nff, null)  // only for updateValue()
// nff.updateValue(null, issue, mv, ch)  // does not work when called as a post function
WorkflowUtils.setFieldValue(issue, nff, null, ch) // does work!
log.warn "end"

Does also work with Ruby.

You need the jira-plugin-pack which includes WorkflowUtils. I downloaded it from http://code.google.com/p/jira-plugin-pack/downloads/detail?name=jira-plugin-pack-1.0.0.jar .

Did you try issue.setCustomFieldValue()? You should not need JSU.

No, look at http://docs.atlassian.com/software/jira/docs/api/4.4.4/ :

 void 	setCustomFieldValue(CustomField customField, Object value)
          Sets a custom field value on this Issue Object, but does not write it to the database.

Yes, but then you call issue.store() to write to the database. This is all that JSU is doing.

Hi Jamie,

this does _not_ work:

...
mv  = new ModifiedValue(nff, null)
nff.updateValue(null, issue, mv, ch)
issue.store()

I was suggesting to try setCustomFieldValue, store.

Sorry, my fault. Now it works:

...
log.warn "Need Feedback From = '"+cfv+"' cleared"
issue.setCustomFieldValue(nff, null)
issue.store()

For me that's the smallest and best code to solve the problem.

Thank you.

0 vote

Just use null, not "new UserCFType(null)".

I already used null. Then the code does not throw an error but the field content will not deleted. The old content remains.

It should work, try reindexing to see if it;s an indexing problem. It is definitely *not* "new UserCFType(null)".

Reindex is done by the standard process of post-functions:

Set issue status to the linked status of the destination workflow step.
— THEN
Script /home/jira/SR/post-clear-need_feedback_from.groovy will be run.
— THEN
Add a comment to an issue if one is entered during a transition.
— THEN
Update change history for an issue and store the issue in the database.
— THEN
Re-index an issue to keep indexes in sync with the database.
— THEN
Fire a Allgemeines Ereignis event that can be processed by the listeners.

I cannot reindex the whole jira database at the moment.

Is there another way to clear the field?

Another quastion: Is there a docu where I will find the right import names for groovy programming? Some tries result in groovy.lang.MissingMethodException errors.

Here we have late evening. It would be great if there is an alternative solution. Thank you for help until now.

Hi,

a half day work with the result, that this function works well when written in Ruby!?? Example tested with ScriptRunner:

require 'java'
java_import org.apache.log4j.Category
java_import com.atlassian.jira.issue.ModifiedValue

log = Category.getInstance("script logger")
log.warn "start ruby script"

issue = componentManager.getIssueManager().get_issue_object(97328)
cfm   = componentManager.getCustomFieldManager()
ch    = DefaultIssueChangeHolder.new
nff   = cfm.getCustomFieldObjects(issue).find {|it| it.name == 'Need Feedback From'}
cfv   = issue.get_custom_field_value(nff)
mv    = ModifiedValue.new(cfv, nil)
nff.updateValue(nil, issue, mv, ch)

log.warn "stop  ruby script"

It for test only working with a specific issue id?97328 - but there is a strange message in log:

2012-02-29 18:08:24,167 http-8443-5 ERROR akt 1088x224525x1 oopz0j 192.168.210.28 /secure/admin/groovy/GroovyRunner.jspa [onresolve.jira.groovy.GroovyRunner] The script failed : org.jruby.exceptions.RaiseException: (TypeError) cannot convert instance of class org.jruby.java.proxies.ConcreteJavaProxy to class org.ofbiz.core.entity.GenericValue
2012-02-29 18:08:24,168 http-8443-5 ERROR akt 1088x224525x1 oopz0j 192.168.210.28 /secure/admin/groovy/GroovyRunner.jspa [onresolve.jira.groovy.GroovyRunner] The script failed : org.jruby.exceptions.RaiseException: (TypeError) cannot convert instance of class org.jruby.java.proxies.ConcreteJavaProxy to class org.ofbiz.core.entity.GenericValue
org.jruby.exceptions.RaiseException: (TypeError) cannot convert instance of class org.jruby.java.proxies.ConcreteJavaProxy to class org.ofbiz.core.entity.GenericValue

When I use a slightly adapted version as a post function, similar messages arise (too long for comment, but's nearly the same).

-- but the script does not work, there is no "script logger" message and no function - strange.

Do you have an idea? Do I have to change something to use Ruby for post functions?

Thank you in advance

I don't know, I'm not very familiar with ruby. However what you've put is the moral equivalent of the groovy so it's impossible that that could work and groovy does not. Why don't you post the groovy you think should work, you posted stuff above but saying you knew it was incorrect.

Hi Jamie,

well, I'm more familiar with ruby so I preferred this language in script runner. Groovy does work well as a post function so I transferred and shorened the ruby to groovy in script runner - it worked:

import org.apache.log4j.Category
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.ComponentManager
import com.atlassian.jira.issue.ModifiedValue
   
log   = Category.getInstance("script runner")
log.warn "begin"
cfm   = componentManager.getCustomFieldManager()
ch    = new DefaultIssueChangeHolder()
issue = componentManager.getIssueManager().getIssueObject(97328)
nff   = cfm.getCustomFieldObjects(issue).find {it.name == 'Need Feedback From'}
cfv   = issue.getCustomFieldValue(nff)
log.warn "Need Feedback From = '"+cfv+"' cleared"
mv    = new ModifiedValue(nff, null)
nff.updateValue(null, issue, mv, ch)
log.warn "end"

With only one line difference + one more import, this _should_ work as a post function - but it does _NOT_!??? The custom field is _not_ cleared.

import org.apache.log4j.Category
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.ComponentManager
import com.atlassian.jira.issue.ModifiedValue

log = Category.getInstance("post-clear-need_feedback_from.groovy")
log.warn "begin"
cfm    = componentManager.getCustomFieldManager()
ch     = new DefaultIssueChangeHolder();
Issue issue  = issue    // changed //
nff   = cfm.getCustomFieldObjects(issue).find {it.name == 'Need Feedback From'}
cfv   = issue.getCustomFieldValue(nff)
log.warn "Need Feedback From = '"+cfv+"' cleared"
mv    = new ModifiedValue(nff, null)
nff.updateValue(null, issue, mv, ch)
log.warn "end"

Any idea?

PS: The strange error message I reported yesterday is related to any ruby script running, maybe a jira restart will help. Still I would prefer ruby - are there any hints regarding ruby integration into jira?

Not really, sorry. Seems to be a few hits. The code that I put in my answer appears to work for you, can you mark the answer as correct to help other people? ta.

You asked me "The code that I put ... can you mark the answer as correct" - sorry, the question was 'using groovy' and this does not work, not as a post function.

Hi,

after a jira restart ruby scripting does work as expected.

Neither ruby nor groovy scripts are able to clear the custom field content within a post function using updateValue(). Neither without FieldLayoutItem or with in updateValue.

- Is this a general limitation?

- If not, is it necessary to follow another way to catch the object for updateValue()?

The Java code you linked above does handle system fields only but no custom field. I found a sample code using WorkflowUtils.setFieldValue() for a custom field: https://studio.plugins.atlassian.com/wiki/display/GRV/Miscellaneous+Groovy+Scripts?focusedCommentId=9961492#comment-9961492

- Where/how do I have to install jira-plugin-pack-1.0.0.jar (com.googlecode.jsu.util.WorkflowUtils) to be used for script programming?

Thank you

Any idea why the debug log would indicate the nullifying code was working, and yet the UI always shows the last value?

 

// com.FactSet.CloneAndLinkTemplateWorkItem.groovy
package com.FactSet
import com.atlassian.crowd.embedded.api.User
import com.atlassian.jira.issue.fields.CustomField
import com.atlassian.jira.issue.CustomFieldManager
import com.atlassian.jira.user.ApplicationUser
import com.atlassian.jira.user.ApplicationUsers
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.IssueFactory
import com.atlassian.jira.issue.IssueManager
import com.atlassian.jira.issue.attachment.Attachment
import com.atlassian.jira.issue.link.IssueLinkManager
import com.atlassian.jira.issue.link.IssueLinkTypeManager
import com.atlassian.jira.issue.security.IssueSecurityLevel
import com.atlassian.jira.issue.AttachmentManager
import com.atlassian.jira.issue.security.IssueSecuritySchemeManager
import com.atlassian.jira.issue.link.IssueLinkType
import com.atlassian.jira.issue.fields.config.manager.IssueTypeSchemeManager
import com.atlassian.jira.issue.Issue 
import com.atlassian.jira.event.issue.IssueEvent
import com.atlassian.jira.issue.link.IssueLink
import com.atlassian.jira.issue.security.IssueSecurityLevelManager
import com.atlassian.jira.workflow.WorkflowFunctionUtils
import com.atlassian.jira.project.ProjectManager
import com.atlassian.jira.user.UserUtils
import com.atlassian.jira.util.ImportUtils
import com.opensymphony.workflow.WorkflowContext
import com.atlassian.jira.project.Project
import com.atlassian.jira.issue.security.IssueSecurityLevelScheme
import org.ofbiz.core.entity.GenericValue
import org.apache.log4j.Category
import com.atlassian.jira.ComponentManager
import com.atlassian.jira.security.JiraAuthenticationContext

def Category log = Category.getInstance("com.onresolve.jira.groovy.PostFunction")
log.setLevel(org.apache.log4j.Level.DEBUG)
log.debug "DBG: Begin postfunction"
def wasIndexing = ImportUtils.indexIssues
def issueManager = ComponentAccessor.getComponent(IssueManager.class)
def projectManager = ComponentAccessor.getComponent(ProjectManager.class)
def linkManager = ComponentAccessor.getComponent(IssueLinkManager.class)
def linkTypeManager = ComponentAccessor.getComponent(IssueLinkTypeManager.class)
def issueSecuritySchemeManager = ComponentAccessor.getComponent(IssueSecuritySchemeManager.class)
def issueSecurityLevelManager = ComponentAccessor.getComponent(IssueSecurityLevelManager.class)
def attachmentManager = ComponentAccessor.getComponent(AttachmentManager.class)
def customFieldManager = ComponentAccessor.getComponent(CustomFieldManager.class)
ComponentManager componentManager = ComponentManager.getInstance()
// testing
//def Issue thisIssue = issueManager.getIssueObject("PRTNRSDEV-3")    
def Issue issueToClone = issueManager.getIssueObject("RESOURCES-1")    
def issueToCloneKey =  issueToClone.getKey()
def thisIssueKey = issue.getKey()
def thisIssueProject = issue.getProjectObject()
log.debug "DBG: this issue key " + thisIssueKey
log.debug "DBG: issue to clone key " + issueToCloneKey
log.debug "DBG: this issue's project key " + thisIssueProject.getKey()

def newWorkItemNameField = customFieldManager.getCustomFieldObjectByName("New Work Item Name")
def newWorkItemName = issue.getCustomFieldValue(newWorkItemNameField)
log.debug "DBG: starting value of new work item name field: " + newWorkItemName
JiraAuthenticationContext authContext = ComponentAccessor.getJiraAuthenticationContext();
ApplicationUser currentUser = authContext.getLoggedInUser();
def issueFactory = ComponentAccessor.getComponent(IssueFactory.class)
def newissue = issueFactory.getIssue()
newissue.setSummary (newWorkItemName.toString())
newissue.setIssueTypeId("10204") //10204 == Work Item
newissue.setProjectObject (thisIssueProject)
newissue.description = issueToClone.description
newissue.reporter = currentUser 
//copy parent Work Item's basic data into new Work Item
List<String> syncWorkFieldsToNewWork = ["11100"];  // this string array of fields to copy will grow and grow.
for (String fieldId : syncWorkFieldsToNewWork) {
    CustomField customField_name = customFieldManager.getCustomFieldObject("customfield_" + fieldId)
    def sourceFieldValue = issueToClone.getCustomFieldValue(customField_name)
    newissue.setCustomFieldValue(customField_name, sourceFieldValue)
}
log.debug "DBG: create new issue"
params = ["issue":newissue]
newWorkItem = issueManager.createIssueObject(currentUser, params)

def sequence = 0
for (IssueLink link in linkManager.getInwardLinks(issue.id)) {
	if ("Include" == link.issueLinkType.name) {
		sequence++;
	}
}

Collection<IssueLinkType> issueLinkTypes = linkTypeManager.getIssueLinkTypes()
String linkID=null;
for (IssueLinkType issueLinkType : issueLinkTypes) {
	String name=issueLinkType.getName();
	if(name.equals("Include")){
		linkID=issueLinkType.getId();
		break;
	}
}
linkManager.createIssueLink (issue.id, newissue.id, Long.parseLong(linkID), sequence, currentUser)

ImportUtils.indexIssues = wasIndexing

// blank out the New Work Item Name field
CustomField fieldToNullify = customFieldManager.getCustomFieldObjectByName("New Work Item Name")
issue.setCustomFieldValue(fieldToNullify, null)
issue.store()
def blankedOutNewWorkItemName = issue.getCustomFieldValue(fieldToNullify)
log.debug "DBG: ending value of new work item name field: " + blankedOutNewWorkItemName

ImportUtils.indexIssues = wasIndexing
/* clone attachments */
// attachmentManager.copyAttachments(issue, currentAppUser, newissue.getKey())

found out issue.store() has been deprecated and no longer persists the data (at least in my particular setup) the key is:

updIssue = issueManager.updateIssue(currentUser, issue, UpdateIssueRequest.builder().build())


with some context:

import com.atlassian.jira.issue.UpdateIssueRequest;
// blank out the New Work Item Name field
CustomField fieldToNullify = customFieldManager.getCustomFieldObject(11013)
issue.setCustomFieldValue(fieldToNullify, "")
updIssue = issueManager.updateIssue(currentUser, issue, UpdateIssueRequest.builder().build())
def blankedOutNewWorkItemName = issue.getCustomFieldValue(fieldToNullify)
log.debug "DBG: ending value of new work item name field: " + blankedOutNewWorkItemName

ImportUtils.indexIssues = wasIndexing

Suggest an answer

Log in or Sign up to answer
Atlassian Community Anniversary

Happy Anniversary, Atlassian Community!

This community is celebrating its one-year anniversary and Atlassian co-founder Mike Cannon-Brookes has all the feels.

Read more
Community showcase
Bridget Sauer
Published Thursday in Marketplace Apps

Calling all developers––You're invited to Atlas Camp 2018

 Atlas Camp   is our developer event which will take place in Barcelona, Spain  from the 6th -7th of   September . This is a great opportunity to meet other developers and get n...

359 views 0 6
Read article

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