Note

We've packed up and moved from Confluence to Discourse to bring you a better, more interactive space. Out of courtesy we didn't migrate your user account so - you will have to signup again

The Exalate team will be on holiday for the coming days - returning Jan 4
Enjoy & stay safe

Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Introduction


Hi all,


Jira processes data using Jira Wiki, while Azure DevOps utilizes HTML for data processing.


One limitation we face out-of-the-box is the inability to synchronize images between these systems.

However, with Exalate this will be possible. By utilizing the script mode, we can modify the language used by these systems to interpret and handle data.


To learn more about this solution, I added a video tutorial.


The code:


Code Block
titleOutgoing Sync Jira On Prem
linenumberstrue
collapsetrue
import com.atlassian.jira.component.ComponentAccessor

class WikiToHtml {
	static String transform(String wikiFormat) {
		if (!wikiFormat) {
			return null
		}

		// access the correct services
		def jcl = ComponentAccessor.classLoader
		def app = ComponentAccessor.getApplicationProperties()
		def epubClass = jcl.loadClass("com.atlassian.event.api.EventPublisher")
		def epub = ComponentAccessor.getOSGiComponentInstanceOfType(epubClass)
		def fmanClass = jcl.loadClass("com.atlassian.jira.config.FeatureManager")
		def fman = ComponentAccessor.getOSGiComponentInstanceOfType(fmanClass)
		def vreqClass = jcl.loadClass("com.atlassian.jira.util.velocity.VelocityRequestContextFactory")
		def vreq = ComponentAccessor.getOSGiComponentInstanceOfType(vreqClass)
		def wrenderClass = jcl.loadClass("com.atlassian.jira.issue.fields.renderer.wiki.AtlassianWikiRenderer")
		def wrender = wrenderClass.newInstance(epub, app, vreq, fman)


		def fixImage = wikiFormat?.replaceAll(/\!(\S+)\|\S+\!/, '<!-- inline image filename=#$1# -->')
		fixImage = fixImage.replaceAll(/\!\^(\S+)\|\S+\!/, '<!-- inline image filename=#$1# -->')
		fixImage = fixImage.replaceAll(/\!\^(\S+)\!/, '<!-- inline image filename=#$1# -->')
		fixImage = fixImage.replaceAll(/\!(\S+)\!/, '<!-- inline image filename=#$1# -->')

		// wiki text can also contain files
		fixImage = fixImage.replaceAll(/\[(\S+)\|\^(\S+)\]/, '<!-- inline file filename=#$2# -->')
		fixImage = fixImage.replaceAll(/\[\^(\S+)\]/, '<!-- inline file filename=#$1# -->')
		return wrender.render(fixImage, null)

	}

}

replica.description = WikiToHtml.transform(issue.description)
replica.labels         = issue.labels
replica.comments       = issue.comments.collect {
    comment -> 
    comment.body = WikiToHtml.transform (comment.body)
    comment
}
Code Block
titleIncoming Sync Azure Devops
linenumberstrue
collapsetrue
def//change 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 =~ /<the projectKey!

if(firstSync){
   // Set type name from source entity, if not found set a default
   workItem.projectKey  =  "Mathieu"
   workItem.typeName = nodeHelper.getIssueType(replica.type?.name)?.name ?: "Task";
}

workItem.summary      = replica.summary
workItem.attachments  = attachmentHelper.mergeAttachments(workItem, replica)
workItem.labels       = replica.labels
workItem.priority     = replica.priority

def setInlineImage(def comment){
    // List of possible regex paterns
    def patternList = [
        /<p><!-- inline image filename=#(([^#]+)|(([^#]+)#([^#]+)))# -->/).*?)#<\/a> --><\/p>/,
            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]}, /(?s)<!-- inline image filename=(.*?)-->/ ,
           known names: ${replica.attachments.filename}, 
           match: ${replica.attachments.find { it.filename?.equals(match[1]) }}
       """)
                str = str.replace(match[0], """</<p><!-- 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\".*?)#<\/a> --><\/p>/,
        // 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;filename=#(.+?)# -->/
    ]

    def pattern       }
      = null
      def matchmatcher = matcher[0]null
            if (match.size() < 2) {
                break;patternList.each{ p ->
            }
            def filenamematcher = match[2]
            def attId = replica.attachments.find { it.filename?.equals(filename) }?.remoteId
   comment =~ p
         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 (matcher.find()){
                def tmpStrmatcher = str.replace(match[0], """<img src="/secure/attachment/${attId}/${attId}_${filename}" />""".toString())
                if (tmpStr == str) {
                    break;
                }
         comment =~ p
       str = tmpStr
            }
            counter++
        }
        str
    }
    def processImgTagsWithIdsinlineImage = {
        //"<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.sizefind() < 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())it.filename?.equals(match[1]) }?.remoteId
            } else {
                def tmpStr = strinlineImage = comment.replace(match[0], """<img src="/secure/attachment/${attId}/${attId}_${attachment.filenamematch[1]}" />""".toString())
                if (tmpStr == str) }else{
                    break;
            return comment 
    }
                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 $strinlineImage = inlineImage.replaceAll("<!-- inline image", "").replaceAll("<!-- inline image", "")
    str = processImgTagsWithIds()
    log.error("#processimages $str")
    str
}
            return inlineImage
}

workItem.comments     = commentHelper.mergeComments(workItem, replica, {
    comment ->
def attrAuthor = comment.author?.displayName ?: "Default-"
    comment.body =  "<b> ${attrAuthor} said:</b> " + comment.body
    comment.body = processInlineImages setInlineImage(comment.body) 
comment
})




workItem.description = processInlineImages(replica.description)



Thank you.

Kind regards,
Mathieu Lepoutre



Widget Connector
urlhttp://youtube.com/watch?v=uVUT12K87z8

Questions

Questions List
filterrecent
asktrue
include-topicsany
spaceexacom

...