# Need a solution for a bi-directional sync, of embedded (in-line) images in the description/comments/Text fields etc.) and their attachments

**URL:** <https://community.exalate.com/t/need-a-solution-for-a-bi-directional-sync-of-embedded-in-line-images-in-the-description-comments-text-fields-etc-and-their-attachments/5582>\
**Category:** General Questions\
**Tags:** jira, azure, migrated\_question\
**Created:** [November 4, 2024, 8:14am UTC](https://community.exalate.com/t/need-a-solution-for-a-bi-directional-sync-of-embedded-in-line-images-in-the-description-comments-text-fields-etc-and-their-attachments/5582 "2024-11-04T08:14:31Z")\
**Posts on this page:** 14\
**Page:** 1

<div class="post-metadata">

**Author:** ![xl8bot](https://dub1.discourse-cdn.com/flex018/user_avatar/community.exalate.com/xl8bot/32/324_2.png) [@xl8bot](https://community.exalate.com/u/xl8bot)\
**Post date:** [November 4, 2024, 8:14am UTC](https://community.exalate.com/t/need-a-solution-for-a-bi-directional-sync-of-embedded-in-line-images-in-the-description-comments-text-fields-etc-and-their-attachments/5582/1 "2024-11-04T08:14:31Z")

</div>

_Originally asked by Harold Oconitrillo on 14 September 2023 [(original question)](https://oldcommunity.exalate.com/questions/88246570)_

* * *

Hello support.

Main goal is to be able to create a bi-directional sync. from Azure Devops Board to Jira.

Here the first issues that I have so far:

Context: using Exalate in Script mode From Azure Work item(Outgoing sync ) to Jira Cloud Issue (Incoming sync) ( note: as a start we need Azure to Jira sync. but our main goal is to have bi-directional sync )

To reproduce my situation:

- From Azure Board create a work item
  - in the Description add some text
    - Add an image by pasting from a **print screen**
    - Save

  - Reopen the same work item
  - add a Comment
    - Add an new image by pasting from a new **print screen**
    - Save

Azure DevOps Board - Exalate outbound

```auto
// Azure DevOps Board - Exalate outbound

replica.description = workItem.description	// Note description will contain HTML

replica.attachments = workItem.attachments

replica.comments = workItem.comments // Note comment will contain HTML

```

NOTE from the Jira Exalate connection information of the Remote replica entity section about the Attachments

```auto
"attachments": [
      {
        "id": "5729801f-8a84-4dd6-adc7-7521854a47d2",
        "filename": "image.png",
        "internal": false,
        "zip": false
      },
      {
        "id": "fbc509af-2794-4766-b347-99a7fd7bd688",
        "filename": "image.png",
        "internal": false,
        "zip": false
      }
    ],

```

Jira Cloud - Exalate Incoming sync

```auto
// Jira Cloud - Exalate Incoming sync

import com.exalate.transform.HtmlToWiki

issue.attachments = attachmentHelper.mergeAttachments(issue, replica)

HtmlToWiki htwDesc = new HtmlToWiki(replica.attachments)
String wikiDescriptionText = htwDesc.transform(replica.description) // replica as HTML Jira understands wiki
issue.description = wikiDescriptionText 

HtmlToWiki htwComment = new HtmlToWiki(replica.attachments) // // replica as HTML Jira understands wiki 
issue.comments = commentHelper.mergeComments(issue, replica, {
    comment ->    
    comment.body = htwComment.transform(comment.body)
    comment
})

```

The result in the Jira issue, both the description and comment text fields have the same image (The last image added to the comment, the second image.png).

I did try the suggested solution from : [https://community.exalate.com/display/exacom/questions/20124163/exalating-attachments-pasted-images-on-azuredevops-result-in-same-name-files-on-receiving-jira-side](https://community.exalate.com/display/exacom/questions/20124163/exalating-attachments-pasted-images-on-azuredevops-result-in-same-name-files-on-receiving-jira-side "Follow link")

Solution to rename the filename values in replica.attachments but the HtmlToWiki::processImage methode/function base it self on the HTML content only to scans for filename in the Image ‘src’ attribute. Giving me a broken in-line image link on the Jira side.

Since the Field content (HTML) (description and/or comments) in-line images information need/must fit the attachment information in order to resolve the image information to display in the proper context. There is a missing part to the solution.

**Hypotheses** :

So by renaming the images from azure to Jira …in Jira new filename ….when will do the other sync Jira to Azure inbound script even using the attachmentHelper.mergeAttachments tool … are those images are added to the work item making duplicates because their is different filenames.

Main goal is to be able to create a bi-directional sync. from

- Azure Devops Board
- Jira Cloud

Help would be appreciated,

* * *

---

<div class="post-metadata">

**Author:** ![xl8bot](https://dub1.discourse-cdn.com/flex018/user_avatar/community.exalate.com/xl8bot/32/324_2.png) [@xl8bot](https://community.exalate.com/u/xl8bot)\
**Post date:** [November 4, 2024, 8:14am UTC](https://community.exalate.com/t/need-a-solution-for-a-bi-directional-sync-of-embedded-in-line-images-in-the-description-comments-text-fields-etc-and-their-attachments/5582/2 "2024-11-04T08:14:32Z")

</div>

_Answer by Javier Pozuelo on 26 September 2023_

Hello,

Add the following code on your Outgoing Sync on Jira Cloud

```auto
replica.comments = nodeHelper.getHtmlComments(issue)

```

Incoming Sync Azure DevOps

```auto
def processInlineImages = { str ->
    def processUnescapedLtGtTags = {
        def counter = 0
        while (counter < 1000) {
            def matcher = (str =~ /<!-- inline image filename=#(([^#]+)|(([^#]+)#([^#]+)))# -->/)
            if (matcher.size() < 1) {
                break;
            }
            def match = matcher[0]
            if (match.size() < 2) {
                break;
            }
            //log.error("replica.attachments=${replica.attachments}")
            def attId = replica.attachments.find { it.filename?.equals(match[1]) }?.remoteId
            if (!attId) {
                log.error("""Could not find attachment with name ${match[1]},
           known names: ${replica.attachments.filename},
           match: ${replica.attachments.find { it.filename?.equals(match[1]) }}
       """)
                str = str.replace(match[0], """<!-- inline processed image filename=#${match[1]}# -->""".toString())
            } else {
                def tmpStr = str.replace(match[0], """<img src="/secure/attachment/${attId}/${attId}_${match[1]}" />""".toString())
                if (tmpStr == str) {
                    break;
                }
                str = tmpStr
            }
            counter++
        }
        str
    }
    def processLtGtTags = {
        def counter = 0
        while (counter < 1000) {
            def matcher = (str =~ /<!-- inline image filename=#(([^#]+)|(([^#]+)#([^#]+)))# -->/)
            if (matcher.size() < 1) {
                break;
            }
            def match = matcher[0]
            if (match.size() < 2) {
                break;
            }
            def attId = replica.attachments.find { it.filename?.equals(match[1]) }?.remoteId
            if (!attId) {
                log.error("""Could not find attachment with name ${match[1]},
           known names: ${replica.attachments.filename},
           match: ${replica.attachments.find { it.filename?.equals(match[1]) }}
       """)
                str = str.replace(match[0], """<!-- inline processed image filename=#${match[1]}# -->""".toString())
            } else {
                def tmpStr = str.replace(match[0], """<img src="/secure/attachment/${attId}/${attId}_${match[1]}" />""".toString())
                if (tmpStr == str) {
                    break;
                }
                str = tmpStr
            }
            counter++
        }
        str
    }
    def processNoImage = {
        //"<p><img
        // src=\"https://jira.smartodds.co.uk/images/icons/attach/noimage.png\"
        // imagetext=\"Screenshot from 2022-11-18 11-09-25.png|thumbnail\"
        // align=\"absmiddle\"
        // border=\"0\" /></p>"
        def counter = 0
        while (counter < 1000) {
            def matcher = (str =~ /<img src="[^"]+" imagetext="(([^"]+)\|thumbnail)" align="absmiddle" border="0" \/>/)
            if (matcher.size() < 1) {
                break;
            }
            def match = matcher[0]
            if (match.size() < 2) {
                break;
            }
            def filename = match[2]
            def attId = replica.attachments.find { it.filename?.equals(filename) }?.remoteId
            if (!attId) {
                log.error("""Could not find attachment with name ${filename},
           known names: ${replica.attachments.filename},
           match: ${replica.attachments.find { it.filename?.equals(filename) }}
       """)
                str = str.replace(match[0], """<img src="/images/icons/attach/noimage.png" processed imagetext="$filename|thumbnail" align="absmiddle" border="0" />""".toString())
            } else {
                def tmpStr = str.replace(match[0], """<img src="/secure/attachment/${attId}/${attId}_${filename}" />""".toString())
                if (tmpStr == str) {
                    break;
                }
                str = tmpStr
            }
            counter++
        }
        str
    }
    def processImgTagsWithIds = {
        //"<p>TEST DECS23456 </p> \n
        //<p><span class=\"image-wrap\" style=\"\"><img src=\"/rest/api/3/attachment/content/36820\"></span></p> \n
        //<p>TESt </p> \n
        //<p><span class=\"image-wrap\" style=\"\"><img src=\"/rest/api/3/attachment/content/36821\"></span></p> \n
        //<p>and more</p>"
        def counter = 0
        while (counter < 1000) {
            def matcher = (str =~ /<img src="\/rest\/api\/3\/attachment\/content\/(\d+)">/)
            if (matcher.size() < 1) {
                return str
            }
            def match = matcher[0]
            //println("match[1]=$match[1]")
            if (match.size() < 2) { // match[0]=<img src="/rest/api/3/attachment/content/36820"> match[1]=36820
                return str
            }
            def attId = match[1]
            def attachment = replica.attachments.find { (it.remoteId as String) == ( attId as String ) }
            if (!attachment) {
                log.error("""Could not find attachment with id ${attId},
           known ids: ${replica.attachments.remoteId},
           match: ${replica.attachments.find { (it.remoteId as String) == ( attId as String ) }}
       """)
                str = str.replace(match[0], """<img src="/rest/api/3/attachment/content/${attId}" processed />""".toString())
            } else {
                def tmpStr = str.replace(match[0], """<img src="/secure/attachment/${attId}/${attId}_${attachment.filename}" />""".toString())
                if (tmpStr == str) {
                    break;
                }
                str = tmpStr
            }
            counter++
        }
        str
    }
    //log.error("#processimages 0 $str")
    str = processUnescapedLtGtTags()
    //log.error("#processimages 1 $str")
    str = processLtGtTags()
    //log.error("#processimages 2 $str")
    str = processNoImage()
    //log.error("#processimages 3 $str")
    str = processImgTagsWithIds()
    log.error("#processimages $str")
    str
}
             
workItem.comments = commentHelper.mergeComments(workItem, replica, {
    comment ->
def attrAuthor = comment.author?.displayName ?: "Default-"
    comment.body = "<b> ${attrAuthor} said:</b> " + comment.body
    comment.body = processInlineImages (comment.body)
comment
})
 
 
 
 
workItem.description = processInlineImages(replica.description)

```

Kind Regards,

Javier Pozuelo

* * *

---

<div class="post-metadata">

**Author:** ![Angel\_Hidalgo](https://dub1.discourse-cdn.com/flex018/user_avatar/community.exalate.com/angel_hidalgo/32/458_2.png) [@Angel\_Hidalgo](https://community.exalate.com/u/Angel_Hidalgo)\
**Post date:** [March 13, 2025, 5:58pm UTC](https://community.exalate.com/t/need-a-solution-for-a-bi-directional-sync-of-embedded-in-line-images-in-the-description-comments-text-fields-etc-and-their-attachments/5582/3 "2025-03-13T17:58:00Z")

</div>

I’m experiencing an issue with Exalate when syncing images from Azure DevOps to Jira. All images appear as “image.png” instead of retaining their original names.

Has anyone encountered this issue before? Any suggestions on how to resolve it?

Thanks in advance!

---

<div class="post-metadata">

**Author:** ![Javier](https://dub1.discourse-cdn.com/flex018/user_avatar/community.exalate.com/javier/32/338_2.png) [@Javier](https://community.exalate.com/u/Javier)\
**Post date:** [April 2, 2025, 8:41pm UTC](https://community.exalate.com/t/need-a-solution-for-a-bi-directional-sync-of-embedded-in-line-images-in-the-description-comments-text-fields-etc-and-their-attachments/5582/5 "2025-04-02T20:41:24Z")

</div>

Hi @Angel_Hidalgo,

Welcome to the Exalate community!

How are you synchronizing the images? Are you sending it from ADO as attachments or as in-line images in the description/comments? Also, is it a Jira On-premise or a Jira Cloud?

Looking forward to assisting you further!

---

<div class="post-metadata">

**Author:** ![Angel\_Hidalgo](https://dub1.discourse-cdn.com/flex018/user_avatar/community.exalate.com/angel_hidalgo/32/458_2.png) [@Angel\_Hidalgo](https://community.exalate.com/u/Angel_Hidalgo)\
**Post date:** [April 3, 2025, 6:50am UTC](https://community.exalate.com/t/need-a-solution-for-a-bi-directional-sync-of-embedded-in-line-images-in-the-description-comments-text-fields-etc-and-their-attachments/5582/6 "2025-04-03T06:50:45Z")

</div>

Hi Javier.

My DevOps and Jira are cloud.

This is my **Outgoing Sync** from **DevOps** connection:

```
replica.comments = workItem.comments
replica.attachments = workItem.attachments

```

An this if my **Incoming Sync** from **Jira** connection:

```
HtmlToWiki htw = new HtmlToWiki()
issue.comments = commentHelper.mergeComments(issue, replica, {
    comment ->
    comment.body = "[" + comment.author.displayName + "] commented: \n" +
             htw.transform(comment.body)
})
issue.attachments = attachmentHelper.mergeAttachments(issue, replica)

```

Also, keep in mind that all images used in DevOps comments appear as **image.png** in Jira, with the same text in the image file. In Jira attachments, all images from DevOps comments are named **image.png**.

Here’s an example with an image from Jira and an image from the DevOps comments.  
image-20250204-140418.png → Jira  
image.png → DevOps

```
  {
    "id": "1bc830e8-5afa-4cfd-9103-c2d36dce7651",
    "filename": "image-20250204-140418.png",
    "filesize": 117752,
    "internal": false,
    "zip": false
  },
  {
    "id": "fc66170e-71fc-4d04-a90f-56770e90c8b3",
    "filename": "image.png",
    "internal": false,
    "zip": false
  },

```

Thanks so much for your help.

---

<div class="post-metadata">

**Author:** ![Javier](https://dub1.discourse-cdn.com/flex018/user_avatar/community.exalate.com/javier/32/338_2.png) [@Javier](https://community.exalate.com/u/Javier)\
**Post date:** [April 4, 2025, 1:10am UTC](https://community.exalate.com/t/need-a-solution-for-a-bi-directional-sync-of-embedded-in-line-images-in-the-description-comments-text-fields-etc-and-their-attachments/5582/7 "2025-04-04T01:10:49Z")

</div>

Thanks for the update!

I will try to reproduce this, and I’ll give you an update as soon as possible.

---

<div class="post-metadata">

**Author:** ![Angel\_Hidalgo](https://dub1.discourse-cdn.com/flex018/user_avatar/community.exalate.com/angel_hidalgo/32/458_2.png) [@Angel\_Hidalgo](https://community.exalate.com/u/Angel_Hidalgo)\
**Post date:** [April 4, 2025, 9:22am UTC](https://community.exalate.com/t/need-a-solution-for-a-bi-directional-sync-of-embedded-in-line-images-in-the-description-comments-text-fields-etc-and-their-attachments/5582/8 "2025-04-04T09:22:55Z")

</div>

Hi Javier.

Thank you very much, Javier, for your time. Additionally, I include this Jira screenshot where you can see what’s happening with the images:

 ![2025-04-04_111943](https://europe1.discourse-cdn.com/flex018/uploads/exalate1/original/1X/490efb685dea742969c322365d8045d65bc85095.png)

---

<div class="post-metadata">

**Author:** ![Angel\_Hidalgo](https://dub1.discourse-cdn.com/flex018/user_avatar/community.exalate.com/angel_hidalgo/32/458_2.png) [@Angel\_Hidalgo](https://community.exalate.com/u/Angel_Hidalgo)\
**Post date:** [April 11, 2025, 12:27pm UTC](https://community.exalate.com/t/need-a-solution-for-a-bi-directional-sync-of-embedded-in-line-images-in-the-description-comments-text-fields-etc-and-their-attachments/5582/9 "2025-04-11T12:27:53Z")

</div>

Hi Javier,

Do you need any additional information?  
I’m happy to provide whatever you need—just let me know!

Best regards,  
Ángel.

---

<div class="post-metadata">

**Author:** ![Javier](https://dub1.discourse-cdn.com/flex018/user_avatar/community.exalate.com/javier/32/338_2.png) [@Javier](https://community.exalate.com/u/Javier)\
**Post date:** [April 14, 2025, 1:53am UTC](https://community.exalate.com/t/need-a-solution-for-a-bi-directional-sync-of-embedded-in-line-images-in-the-description-comments-text-fields-etc-and-their-attachments/5582/10 "2025-04-14T01:53:41Z")

</div>

Hi @Angel_Hidalgo,

I tested the attachment sync from ADO to JC using the script you shared, but I wasn’t able to reproduce the issue. Could you please let me know which Exalate version you’re using on each instance?

Thanks!

---

<div class="post-metadata">

**Author:** ![Angel\_Hidalgo](https://dub1.discourse-cdn.com/flex018/user_avatar/community.exalate.com/angel_hidalgo/32/458_2.png) [@Angel\_Hidalgo](https://community.exalate.com/u/Angel_Hidalgo)\
**Post date:** [April 14, 2025, 7:33am UTC](https://community.exalate.com/t/need-a-solution-for-a-bi-directional-sync-of-embedded-in-line-images-in-the-description-comments-text-fields-etc-and-their-attachments/5582/11 "2025-04-14T07:33:41Z")

</div>

Hi @Javier

Exalate for Azure DevOps Integration  
Installed version: 1.2.8  
Last updated: 16 dic 2024 at 11:21 CET

Exalate for Jira Cloud  
Jira Cloud - Version 5.23.3  
Released for Jira Cloud on March 26th, 2025

---

<div class="post-metadata">

**Author:** ![Javier](https://dub1.discourse-cdn.com/flex018/user_avatar/community.exalate.com/javier/32/338_2.png) [@Javier](https://community.exalate.com/u/Javier)\
**Post date:** [April 15, 2025, 2:57pm UTC](https://community.exalate.com/t/need-a-solution-for-a-bi-directional-sync-of-embedded-in-line-images-in-the-description-comments-text-fields-etc-and-their-attachments/5582/12 "2025-04-15T14:57:42Z")

</div>

Hi @Angel_Hidalgo,

Could we schedule a short call to review this? I’m interested in seeing who you are able to sync images (event with the naming issue), since this is currently not working at the latest version. Here is my Calendly link so you can pick a slot

---

<div class="post-metadata">

**Author:** ![Varsha\_Duhoon](https://dub1.discourse-cdn.com/flex018/user_avatar/community.exalate.com/varsha_duhoon/32/757_2.png) [@Varsha\_Duhoon](https://community.exalate.com/u/Varsha_Duhoon)\
**Post date:** [July 2, 2026, 10:27am UTC](https://community.exalate.com/t/need-a-solution-for-a-bi-directional-sync-of-embedded-in-line-images-in-the-description-comments-text-fields-etc-and-their-attachments/5582/13 "2026-07-02T10:27:01Z")

</div>

Hi Team,

I would like to follow up on the case here and understand the issue and best possible solution?

Thanks!

Varsha duhoon

---

<div class="post-metadata">

**Author:** ![Javier](https://dub1.discourse-cdn.com/flex018/user_avatar/community.exalate.com/javier/32/338_2.png) [@Javier](https://community.exalate.com/u/Javier)\
**Post date:** [July 6, 2026, 6:33pm UTC](https://community.exalate.com/t/need-a-solution-for-a-bi-directional-sync-of-embedded-in-line-images-in-the-description-comments-text-fields-etc-and-their-attachments/5582/14 "2026-07-06T18:33:42Z")

</div>

Hi @Varsha_Duhoon,

Welcome to the community!

The long custom script that was shared initially on this thread is no longer needed to sync inline images in the description field of Jira Cloud and ADO. The following soltution works:

> [@DevOps \> Jira Attachments](https://community.exalate.com/t/devops-jira-attachments/4369/9):
>
> Hi @Angel_Hidalgo / @ashar.iqbal , This should fully work bi-directionally between Jira Cloud and Azure DevOps since [Exalate 5.12.0](https://docs.exalate.com/docs/azure-devops-version-5120). The key is to use the correct [nodeHelper](https://docs.exalate.com/docs/nodehelper-in-script-helpers) methods to do the transformations. The scripts you basically need are the following: JC Outgoing: replica.description = nodeHelper.getHtmlField(issue, "description") replica.comments = nodeHelper.getHtmlComments(issue) JC Incoming: issue.description = nodeHelper.toMarkDownFromHtml(replica.description) issue.comments …

The issue Angel was experiencing was specific to his integration. Let me know if you have any questions

---

<div class="post-metadata">

**Author:** ![Sonal\_Otwani](https://dub1.discourse-cdn.com/flex018/user_avatar/community.exalate.com/sonal_otwani/32/392_2.png) [@Sonal\_Otwani](https://community.exalate.com/u/Sonal_Otwani)\
**Post date:** [July 15, 2026, 1:54pm UTC](https://community.exalate.com/t/need-a-solution-for-a-bi-directional-sync-of-embedded-in-line-images-in-the-description-comments-text-fields-etc-and-their-attachments/5582/15 "2026-07-15T13:54:46Z")

</div>

Hi @Varsha_Duhoon

Can you please update us if the solution provided by @Javier worked or not?

Thanks,

Sonal
