It's not the same without you

Join the community to find out what other Atlassian users are discussing, debating and creating.

Atlassian Community Hero Image Collage

Copy attachments from one issue to another linked issue

Hello,

We are trying to copy attachments from one issue to another. The issues should be linked with the "Parent/Child" issue link, and we would probably keep 2 script listeners to always keep the issues in sync between 2 projects.

What we have: 

import com.atlassian.jira.event.issue.IssueEvent
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.issue.attachment.Attachment
import com.atlassian.jira.issue.AttachmentManager
import com.atlassian.jira.ComponentAccessor
import com.atlassian.jira.issue.comments.Comment
import com.atlassian.jira.issue.comments.CommentManager
import com.atlassian.jira.issue.link.IssueLinkManager
import com.atlassian.jira.issue.link.IssueLink
import com.atlassian.jira.user.ApplicationUser
import org.ofbiz.core.entity.GenericValue
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.Issue;

def linker = ComponentAccessor.getIssueLinkManager()
def commentMgr = ComponentAccessor.getCommentManager()
def issue = event.issue as MutableIssue
def project = issue.getProjectObject().getKey()
def attachmentManager = ComponentAccessor.getAttachmentManager()


def lastAttachment = attachmentManager.getAttachments(issue).last()

def issueLinks = linker.getOutwardLinks(issue.getId())

def subElements = issueLinks.findAll { it.issueLinkType.name.contains('Parent/Child') && it.destinationObject.getProjectObject().getKey() == 'Test'}

def newComment = event.getComment()
def originalAuthor = newComment.getAuthorApplicationUser()
def commentBody = newComment.getBody()

subElements.each {
if( commentBody != "")
{
commentMgr.create(it.getDestinationObject(), originalAuthor, commentBody , false)
}
}

subElements.each {
if(lastAttachment)
{
attachmentManager.copyAttachment(lastAttachment, originalAuthor, it.getDestinationObject().key)
}
}

 

You can ignore the comments part, but since there isn't an issue attachment added event, I'm not sure how else to do this.

 

The script does not give any errors, but attachments are not copied at all.

 

Thank you in advance!

3 answers

1 accepted

1 vote
Answer accepted

Hi @Mihai Mihai 

As @fran garcia gomera mentioned use the Issue Commented event.

I tried trimmed version of your code and it works perfectly with slight change.

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.link.IssueLink
import com.atlassian.jira.issue.comments.Comment
import com.atlassian.jira.issue.comments.CommentManager

def issue = event.issue
def attachmentManager = ComponentAccessor.getAttachmentManager()
def lastAttachment = attachmentManager.getAttachments(issue).last()
def links = ComponentAccessor.getIssueLinkManager().getOutwardLinks(issue.getId())
def subElements = links.findAll { it.issueLinkType.name.contains('Parent/Child') }

def newComment = event.getComment()
def originalAuthor = newComment.getAuthorApplicationUser()

//return subElements*.destinationObject

subElements.each { it ->
if (lastAttachment) {

attachmentManager.copyAttachment(lastAttachment, originalAuthor, it.getDestinationObject().key )
}

}

I hope it helps.

Ravi 

Hello,

 

Thank you for your reply!

 

I used the 'issue commented' event and the script that you added which is much cleaner.

It does not give any errors, but attachments do not get copied to the linked issue. I tried adding an image inside a comment or editing an issue and adding both a comment and a file attachment during that edit.

Do I have to attach the file in a specific way?

Thank you!

Is the name of your Issue Link exactly Parent/Child?

Also check whether the linked issues are outward links or inward links. The code is only checking for outward links.

Hi @Ravi Sagar -Adaptavist- 

Thank you for your replies. Yes, the issue link is "Parent/Child" and I am testing it on an outward link.

 

There aren't any errors, but nothing is added. Is there any other test that I can do?

Thank you!

Hi @Ravi Sagar -Adaptavist- 

 

I found one issue that somehow did not allow me to add attachments on the server: 

2019-10-04 08:22:47,504 file-attachment-store:thread-41 WARN uidq5816 502x8986x1 1fi5fb7 10.208.247.63 /rest/internal/2/AttachTemporaryFile [c.a.j.issue.managers.DefaultAttachmentManager] Unable to copy attachment to issue with key TEST-10407. Exception: Unable to create target directory /opt/ea/atlassian/jira-home/data/attachments/TEST/20000/TEST-10407

 

and I corrected that. Now images added inside comments get copied.

However, by attachments, I also think of txt files and zip files added to an issue. Those do not get copied to the other issue. Is there any way to bring those items over as well?

 

Thank you very much!

Just to be sure you understand what I meant, we would need a listener for this type of attachments, not just the ones added through comments:

 

Screenshot_3.png

Don't understand exactly the problem, if you choose custom listener there is an issue commented event that will launch your script.

listener.png

Hello,

Actually, it was never working. 

The one that was adding the comments with the images was the previous one that I initially added in the description and it was not disabled.

Hello @Ravi Sagar -Adaptavist- 

Correction - it does work, but only with images, not with any other file type.

Like Ravi Sagar -Adaptavist- likes this

Not sure why it is not working for other files. I tried the same code with txt files. I will check.

Like Mihai Mihai likes this

Hi @Ravi Sagar -Adaptavist- 

I think I found something useful.

The problem might be coming from the "last" function, which does not seem to actually look for the last attachment.

For example, if an issue already has multiple attachments, it will only work with images for me.

If on an issue with 0 attachments I add any kind of file, then that one is copied to the linked issue.

Doing some more research I found on a thread that last does not work because attachments are sorted alphabetically.

 

Do you know any work-around for this?

 

Thank you!

Reproduction steps:

 

Issue a > parent of > Issue b

both issue a and issue b have 0 attachments.

add to issue b an attachment named:  "abc" >>> it gets copied to issue a

add another attachment to issue b, named: "xyz" >>> it gets copied to issue a

add any other attachment to issue B, that has the name starting with any letter of the alphabet < x . ("lmn" >>> new attachment does not get copied anymore, xyz still does because it is last.

Hi @Ravi Sagar -Adaptavist- 

 

Please let me know if you know any possible solution for this situation.

 

Thank you!

Like Ravi Sagar -Adaptavist- likes this

Let me try to reproduce your last scenario.

Hi @Mihai Mihai 

I was able to reproduce. The attachments are listed using their file names but we can sort them using their created date before fetching the last one.

Try this code.

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.link.IssueLink
import com.atlassian.jira.issue.comments.Comment
import com.atlassian.jira.issue.comments.CommentManager

def issue = event.issue
def attachmentManager = ComponentAccessor.getAttachmentManager()
def sortAttachment = attachmentManager.getAttachments(issue).sort { it.created }
def lastAttachment = sortAttachment.last()
def links = ComponentAccessor.getIssueLinkManager().getOutwardLinks(issue.getId())
def subElements = links.findAll { it.issueLinkType.name.contains('Parent/Child') }

def newComment = event.getComment()
def originalAuthor = newComment.getAuthorApplicationUser()

//return subElements*.destinationObject

subElements.each { it ->
if (lastAttachment) {

attachmentManager.copyAttachment(lastAttachment, originalAuthor, it.getDestinationObject().key )
}

}

Please note this code will copy attachment based on outward links which is in this case parent to child.

The main change is the following piece of code.

def sortAttachment = attachmentManager.getAttachments(issue).sort { it.created }
def lastAttachment = sortAttachment.last()

I hope it helps.

Let me know.

Ravi 

Like Mihai Mihai likes this

Hi @Ravi Sagar -Adaptavist- 

This works indeed!

It copies the attachment, however, on each new comment, added to the linked issue, and it would be great if it could check if the attachment was either already copied or if it already exists on the other issue.

I think the filename should be a good enough indicator, probably together with the file type.

Please let me know if that is possible.

 

Thank you!

Hi @Mihai Mihai 

Yes it can be done, before copying the attachment fetch the list of attachments of the linked issue, you will get a list. So before copying search for the file name in that list using list.contains("filename.txt"). If the attachment is found then don't copy.

Trying writing the code.

Ravi

Hi @Ravi Sagar -Adaptavist- 

Thank you for your reply.

I am sure I am missing something here, because it still always adds the attachment on each comment:

 

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.link.IssueLink
import com.atlassian.jira.issue.comments.Comment
import com.atlassian.jira.issue.comments.CommentManager

def issue = event.issue
def attachmentManager = ComponentAccessor.getAttachmentManager()
def sortAttachment = attachmentManager.getAttachments(issue).sort { it.created }
def lastAttachment = sortAttachment.last()
def listOfCurrentIssueAttachments = attachmentManager.getAttachments(issue)
def links = ComponentAccessor.getIssueLinkManager().getOutwardLinks(issue.getId())
def subElements = links.findAll { it.issueLinkType.name.contains('Parent/Child') && it.destinationObject.getProjectObject().getKey() == 'TEST'}

def newComment = event.getComment()
def originalAuthor = newComment.getAuthorApplicationUser()

//return subElements*.destinationObject

subElements.each { it ->
if (!listOfCurrentIssueAttachments.contains(lastAttachment.filename)) {

attachmentManager.copyAttachment(lastAttachment, originalAuthor, it.getDestinationObject().key )
}

}

 

What I added is:

 

def listOfCurrentIssueAttachments = attachmentManager.getAttachments(issue)

and the change in the if statement.

 

Thank you.

Hi @Ravi Sagar -Adaptavist- 

Please help with the solution for copying attachments only if they don't already exist on the linked issue.

 

Thank you!

Hi @Ravi Sagar -Adaptavist- 

 

I also tried with:

 


def issue = event.issue
def attachmentManager = ComponentAccessor.getAttachmentManager()
def sortAttachment = attachmentManager.getAttachments(issue).sort { it.created }
def lastAttachment = sortAttachment.last()
def listOfCurrentIssueAttachments = attachmentManager.getAttachments(issue)
def links = ComponentAccessor.getIssueLinkManager().getOutwardLinks(issue.getId())
def subElements = links.findAll { it.issueLinkType.name.contains('Parent/Child') && it.destinationObject.getProjectObject().getKey() == 'TEST'}

def newComment = event.getComment()
def originalAuthor = newComment.getAuthorApplicationUser()

if (listOfCurrentIssueAttachments) {

listOfCurrentIssueAttachments.collect { it.filename }
subElements.each { it ->
if (!listOfCurrentIssueAttachments.contains(lastAttachment.filename)) {

attachmentManager.copyAttachment(lastAttachment, originalAuthor, it.getDestinationObject().key )
}
}
}

 

but it still copies the attachments. I assume I am not getting the list of attachment names in the right way, but I'm not sure how to do it in a different way.

Any help is appreciated, thank you!

Hi @Mihai Mihai 

How are you fetching the attachments of the linked issues in your code?

Ravi

Hi @Ravi Sagar -Adaptavist- 

subElements.each { it ->
if (!listOfCurrentIssueAttachments.contains(lastAttachment.filename)

 

I was thinking that "lastAttachment.filename" in the above section refers to the attachments of the issues that are linked to the current one. 

No lastAttachment is the last attachment of the current issue not the linked issue. Use the same method you used for fetching the attachments but instead of the current issue pass the linked issue object.

Ravi

Hi @Ravi Sagar -Adaptavist- 

I believe now I got the linked issue object, but it is still copying the attachment even if it exists:

 

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.link.IssueLink
import com.atlassian.jira.issue.comments.Comment
import com.atlassian.jira.issue.comments.CommentManager

def issue = event.issue
def attachmentManager = ComponentAccessor.getAttachmentManager()
def sortAttachment = attachmentManager.getAttachments(issue).sort { it.created }
def lastAttachment = sortAttachment.last()
def listOfCurrentIssueAttachments = attachmentManager.getAttachments(issue)
def emptyList = []
def links = ComponentAccessor.getIssueLinkManager().getOutwardLinks(issue.getId())
def subElements = links.findAll { it.issueLinkType.name.contains('Parent/Child') && it.destinationObject.getProjectObject().getKey() == 'TEST'}
def newComment = event.getComment()
def originalAuthor = newComment.getAuthorApplicationUser()

if (listOfCurrentIssueAttachments) {

listOfCurrentIssueAttachments.collect { it.filename }
subElements.each { it ->
def linkedIssue = it.destinationObject
def attachmentsOfLinkedIssues = attachmentManager.getAttachments(linkedIssue).sort { it.created }
def LastAttachmentOfLinkedIssues = attachmentsOfLinkedIssues.last()
if (!listOfCurrentIssueAttachments.contains(LastAttachmentOfLinkedIssues.filename)) {

attachmentManager.copyAttachment(lastAttachment, originalAuthor, it.getDestinationObject().key )
}
}
}

 

Thank you!

Hi @Mihai Mihai 

Here is the complete code that will check whether the filename is already there in the child task (linked) for not.

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.link.IssueLink
import com.atlassian.jira.issue.comments.Comment
import com.atlassian.jira.issue.comments.CommentManager
import org.apache.log4j.Logger
import org.apache.log4j.Level

def log = Logger.getLogger("com.acme.listener")
log.setLevel(Level.DEBUG)


def issue = event.issue
def attachmentManager = ComponentAccessor.getAttachmentManager()
def sortAttachment = attachmentManager.getAttachments(issue).sort { it.created }
def lastAttachment = sortAttachment.last()
def links = ComponentAccessor.getIssueLinkManager().getOutwardLinks(issue.getId())
def subElements = links.findAll { it.issueLinkType.name.contains('Parent/Child') }

def newComment = event.getComment()
def originalAuthor = newComment.getAuthorApplicationUser()

//return subElements*.destinationObject

subElements.each { it ->

def subAttachmentManager = ComponentAccessor.getAttachmentManager()
def subAttachments = attachmentManager.getAttachments(it.getDestinationObject())

log.debug("Last Attachment from parent: " + lastAttachment.filename)
log.debug("Sub Attachments: " + subAttachments*.filename)
log.debug("Last Attachment already there before copy?: " + subAttachments*.filename.contains(lastAttachment.filename))

if (lastAttachment && !subAttachments*.filename.contains(lastAttachment.filename)) {

attachmentManager.copyAttachment(lastAttachment, originalAuthor, it.getDestinationObject().key )
}




}

You were quite close in your code but you were looking at the last attachment in your linked issue whereas you have to look into all the attachments. Also you have to search based on the file name using subAttachments*.filename.

I hope it is clear now.

Have a nice day.

Ravi

Like Mihai Mihai likes this

Suggest an answer

Log in or Sign up to answer
Community showcase
Posted in Jira

Demo Den Ep. 7: New Jira Cloud Reports

Learn how to use two new reports for next-gen projects in Jira Cloud:  Cumulative flow diagram and Sprint burndown chart. Ivan Teong, Product Manager, Jira Software, demos the Cumulative ...

229 views 1 2
Join discussion

Community Events

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

Find an event

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

Unfortunately there are no Community Events near you at the moment.

Host an event

You're one step closer to meeting fellow Atlassian users at your local event. Learn more about Community Events

Events near you