Create
cancel
Showing results for 
Search instead for 
Did you mean: 
Sign up Log in

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

Script runner post function to create confluence page FROM TEMPLATE from JIRA

brbojorque Community Leader Jul 11, 2018

Hello, has anyone done this before? Create confluence page from template from jira using scriptrunner post function

6 answers

1 accepted

3 votes
Answer accepted

Hey,

Here is how to send rest API from Jira to confluence:

https://scriptrunner.adaptavist.com/latest/jira/interacting-with-confluence-from-jira.html

and maybe here you can find how to create a page from a template with rest :

https://jira.atlassian.com/browse/CRA-818

brbojorque Community Leader Jul 17, 2018

@Neta Elyakim, I have tried the scriptrunner documentation, sure I can create confluence page. But not with the use of template. Some ppl made it but it surprise me that no one is showing some code to back it up.

Like Jorj likes this

Do you have ScriptRunner for Confluence?

If so you can create custom rest that takes with groovy the template body and creates a page with it:

import com.onresolve.scriptrunner.runner.rest.common.CustomEndpointDelegate
import groovy.transform.BaseScript
import org.codehaus.jackson.map.ObjectMapper
import com.atlassian.confluence.spaces.Space
import com.atlassian.confluence.pages.Page
import com.atlassian.confluence.spaces.SpaceManager
import com.atlassian.confluence.pages.PageManager
import com.atlassian.confluence.core.DefaultSaveContext
import com.atlassian.confluence.api.impl.sal.ConfluenceApplicationProperties
import javax.ws.rs.core.Response
import com.atlassian.sal.api.component.ComponentLocator
import com.atlassian.confluence.pages.templates.PageTemplateManager

import javax.servlet.http.HttpServletRequest
import javax.ws.rs.core.MultivaluedMap
import javax.ws.rs.core.Response

import static com.atlassian.user.security.password.Credential.unencrypted

@BaseScript CustomEndpointDelegate delegate

def confluenceApplicationProperties = ComponentLocator.getComponent(ConfluenceApplicationProperties)
createPageFromTitle(
httpMethod: "POST", groups: ["confluence-administrators"]
) {MultivaluedMap queryParams, String body ->

def mapper = new ObjectMapper()
def params = mapper.readValue(body, Map)
assert params.title // must provide title for the page
assert params.tamplateid // must provide tamplateid


def pageTemplateManager = ComponentLocator.getComponent(PageTemplateManager)
def spaceManager = ComponentLocator.getComponent(SpaceManager)
def pageManager = ComponentLocator.getComponent(PageManager)
Space space = spaceManager.getSpace("STAR")
Page HomePage = space.getHomePage()

def NewParentPage
try {
def pageTemplate = pageTemplateManager.getPageTemplate(params.tamplateid as Long)
String tamplateAsString = pageTemplate.getContent()
String pageTitle= params.title

NewParentPage = new Page(title: pageTitle, bodyAsString: tamplateAsString, space: space, parentPage: HomePage) //create the new page (copy)
pageManager.saveContentEntity(NewParentPage, DefaultSaveContext.DEFAULT)
HomePage.addChild(NewParentPage)
pageManager.saveContentEntity(HomePage, DefaultSaveContext.MINOR_EDIT)

} catch (e) {
return Response.serverError().entity([error: e.message]).build()
}

Response.ok().build()
}

Your request that you need to send from JIRA: (you can take the params from some JIRA ticket- maybe get the issue summary to be the page title, you can even set the template id on confluence side if it should be the same template every time)

import com.atlassian.applinks.api.ApplicationLink
import com.atlassian.applinks.api.ApplicationLinkService
import com.atlassian.applinks.api.application.confluence.ConfluenceApplicationType
import com.atlassian.sal.api.component.ComponentLocator
import com.atlassian.sal.api.net.Request
import com.atlassian.sal.api.net.Response
import com.atlassian.sal.api.net.ResponseException
import com.atlassian.sal.api.net.ResponseHandler
import groovy.json.JsonBuilder
import groovy.json.JsonSlurper

def ApplicationLink getPrimaryConfluenceLink() {
def applicationLinkService = ComponentLocator.getComponent(ApplicationLinkService.class)
final ApplicationLink conflLink = applicationLinkService.getPrimaryApplicationLink(ConfluenceApplicationType.class)
conflLink
}
def confluenceLink = getPrimaryConfluenceLink()
assert confluenceLink // must have a working app link set up

def params = [
title: "Blabla",
tamplateid : 9469953
]

def authenticatedRequestFactory = confluenceLink.createImpersonatingAuthenticatedRequestFactory()
authenticatedRequestFactory
.createRequest(Request.MethodType.POST, "/rest/scriptrunner/latest/custom/createPageFromTitle")
.addHeader("Content-Type", "application/json")
.setRequestBody(new JsonBuilder(params).toString())
.execute(new ResponseHandler<Response>() {
@Override
void handle(Response response) throws ResponseException {
if(response.statusCode != HttpURLConnection.HTTP_OK) {
throw new Exception(response.getResponseBodyAsString())
}
else {
def webUrl = new JsonSlurper().parseText(response.responseBodyAsString)["_links"]["webui"]
}
}
})

 

If you don't have ScriptRunner for Confluence, I manage to find a rest commend to get the template-

/rest/create-dialog/1.0/templates?id=9469953

 9469953- is the template id

You can also try to set the body with it but I think that the solution with the custom rest on confleucen side is better. 

Like # people like this
brbojorque Community Leader Jul 18, 2018

Omg dude, this is good stuff. It actually works!

I noticed incorrect spelling in this case tamplate instead of template.

Also I noticed that I get this everytime I execute the script.

But overall it works and gets the job done!! Cheers mate.

 

2018-07-19 09:57:32,096 WARN [common.UserScriptEndpoint]: Script console script failed: java.lang.IllegalArgumentException: Text must not be null or empty at Script43$1.handle(Script43.groovy:37) at com.atlassian.applinks.oauth.auth.OAuthResponseHandler.handle(OAuthResponseHandler.java:44) at com.atlassian.plugins.rest.module.jersey.JerseyRequest$1.handle(JerseyRequest.java:115) at com.atlassian.plugins.rest.module.jersey.JerseyRequest$1.handle(JerseyRequest.java:113) at com.atlassian.plugins.rest.module.jersey.JerseyRequest$2.handle(JerseyRequest.java:134) at com.atlassian.sal.core.net.HttpClientRequest.executeAndReturn(HttpClientRequest.java:104) at com.atlassian.plugins.rest.module.jersey.JerseyRequest.executeAndReturn(JerseyRequest.java:131) at com.atlassian.plugins.rest.module.jersey.JerseyRequest.execute(JerseyRequest.java:113) at com.atlassian.applinks.core.auth.ApplicationLinkRequestAdaptor.execute(ApplicationLinkRequestAdaptor.java:47) at com.atlassian.applinks.oauth.auth.OAuthRequest.execute(OAuthRequest.java:71) at com.atlassian.sal.api.net.Request$execute$1.call(Unknown Source) at Script43.run(Script43.groovy:30)

I'm glad I manage to help you! BTY I'm a woman :)

Check your application link- it needs to be set like this:

 

1.PNG

brbojorque Community Leader Jul 19, 2018

Oh apologies @Neta Elyakim, hihi. I don't think authentication is the culprit. Because I can connect fine and the script is properly executed.

That error is from the Script Console. Not really sure what is the context of the text is missing.

 

See you arround :D

Hey,

The application link needs to be set OAuth with impersonation, you can see more info for this in scriptRunner docs- https://scriptrunner.adaptavist.com/latest/jira/interacting-with-other-apps-via-applinks.html

About the log error, remove this line from the script-  

def webUrl = new JsonSlurper().parseText(response.responseBodyAsString)["_links"]["webui"]

The problem is that we don't return any response from confluence - 

Response.ok().build()

This last line on confluence rest return null.

When we get the response we try to parse it, so just comment out the webUrl line (on Jira side)

brbojorque Community Leader Jul 23, 2018

@Neta Elyakim, this is well noted. It make sense to have that error. I will apply it.

Thanks alot :D

@brbojorque , @Neta Elyakim , do you by any chance know how to convert this to the script runner cloud versions?

I have both Jira and Confluence Cloud script runners, and I already can:

  1. create (empty) page in confluence cloud, from either Jira or Confluence
  2. get the content of a template by Id
  3. inspect the content of the retrieved content to see that the storage value is as expected.

However, using the content post api, I tried to set the body.storage.value property, but that does not end up in the new page. (Only title and empty content.)

Do I need to set a different property?

Do I need an extra round trip to get the page Id and put some content there?

Hi @Pieter Kuppens ,

Have you had any luck with your implementation?

I'm running into a similar situation and you might have found a solution by now? :D

Hi @Bruno Lelateur , I'm able to create a page but not using the template.

link

Please do share with me if you find any script using a template to create a page.

Hi @Pieter Kuppens 

Can you please share the solution with me?

I want to create a page in confluence in a post function (cloud instance), but very simple, using scriptrunner.

Like Ash likes this

Hi @brbojorque and @[deleted]  @Neta Elyakim 

Thank you for the solution above, I used the script and was able to make it work. Now my question is instead of creating the new page in the Home page I want to create it in a Ancestor page. Do you have any idea how can I do this?

Thanks!

I was able to figure out my question. I just replaced

Page HomePage = space.getHomePage()

with

Page HomePage = pageManager.getPage(<Parent ID>)
Like Patrik Grönvall likes this

Hi @Neta Elyakim can you please help me to search for a sensitive information such as password in confluence using groovy. 

Hi,

I used your script to create auto page in Jira confluence after creating new issue in Jira. It worked to create new page but not yet create auto link with jira macro in script. Maybe I filled wrong value for server name and serverID in macro as below

// write storage format using an XML builder
def writer = new StringWriter()
def xml = new MarkupBuilder(writer)
xml.'ac:structured-macro'('ac:name': "jira") {
'ac:parameter'('ac:name': "key", issue.key)
'ac:parameter'('ac:name': "server", "10.6.130.108:8081")
'ac:parameter'('ac:name': "serverId", "B93S-O1GZ-1N2R-CUET")
}
// add more paragraphs etc
xml.p("Some additional info here.")

 

I tried with serverID of Jira sofware (got from system information) and aslo tried with Jira Confluence serverid but all dinot work. Server Name I input Jira sofware as my app link "10.6.130.108:8081"

Pls help me  what is the exact value for them?

 

Thanks,

Nhung

Hello,

Very helpful all the above posts!

Is there a way to copy also the attachments from a Jira issue to the created confluence page? 

Thank you,

Anna

Hey there,

in case this is still a matter you can also try this app from the atlassian marketplace.

It is a little easier to handle than the script runner coding you have to do and automatically links pages back to your issue, or creates them during a postfunction.

Cheers,

Andrej

@brbojorque , @Neta Elyakim Do you guys know how i can get the script to populate custom field from the Jira issue it is creating the page from?

Right now it seems i can only get issue.summary, issue.key. issue.description.

Help is very much appreciated.  My goal here is to have the page display a table with various fields and their corresponding values

Here is the script i used to create a new page. In it, i construct my own tables instead of trying to populate a predefined template. I did that by creating an example page and using the Storage format view as a guide. I hope this is helpful for you.

import com.atlassian.applinks.api.ApplicationLink
import com.atlassian.applinks.api.ApplicationLinkService
import com.atlassian.applinks.api.application.confluence.ConfluenceApplicationType
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.issue.IssueManager
import com.atlassian.sal.api.component.ComponentLocator
import com.atlassian.sal.api.net.Request
import com.atlassian.sal.api.net.Response
import com.atlassian.sal.api.net.ResponseException
import com.atlassian.sal.api.net.ResponseHandler
import groovy.json.JsonBuilder
import groovy.json.JsonSlurper
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.CustomFieldManager
import groovy.xml.MarkupBuilder

//for testing only
// the issue provided to us in the binding
def issueManager = ComponentAccessor.getIssueManager()
MutableIssue issue = issueManager.getIssueObject('XSUP-2994')

def customFieldManager = ComponentAccessor.getComponent(CustomFieldManager)
def corpNameCF = customFieldManager.getCustomFieldObjectByName("Corporation Name")
def corpName = issue.getCustomFieldValue(corpNameCF)

def supportEmailCF = customFieldManager.getCustomFieldObjectByName("Technical Support Email (must be a corporation email)")
def supportEmail = issue.getCustomFieldValue(supportEmailCF)

def websiteCF = customFieldManager.getCustomFieldObjectByName("Website")
def website = issue.getCustomFieldValue(websiteCF)

def corpAddressCF = customFieldManager.getCustomFieldObjectByName("Registered Corporate Address")
def corpAddress = issue.getCustomFieldValue(corpAddressCF)

def primaryContactNameCF = customFieldManager.getCustomFieldObjectByName("Contact Name")
def primaryContactName = issue.getCustomFieldValue(primaryContactNameCF)

def primaryContactEmailCF = customFieldManager.getCustomFieldObjectByName("Contact Email")
def primaryContactEmail = issue.getCustomFieldValue(primaryContactEmailCF)

def primaryContactPhoneCF = customFieldManager.getCustomFieldObjectByName("Contact Phone Number")
def primaryContactPhone = issue.getCustomFieldValue(primaryContactPhoneCF)

def primaryContactAddressCF = customFieldManager.getCustomFieldObjectByName("Contact Address")
def primaryContactAddress = issue.getCustomFieldValue(primaryContactAddressCF)


def salesPersonCF = customFieldManager.getCustomFieldObjectByName("Salesperson")
def salesPerson = issue.getCustomFieldValue(salesPersonCF).getDisplayName() //ingnore error

def stpAcctIdCF = customFieldManager.getCustomFieldObjectByName("STP Account ID")
def stpAcctID = issue.getCustomFieldValue(stpAcctIdCF)

def ddAcctIdCF = customFieldManager.getCustomFieldObjectByName("DD Account ID")
def ddAcctID = issue.getCustomFieldValue(ddAcctIdCF)

def leverageCF = customFieldManager.getCustomFieldObjectByName("Leverage")
def leverage = issue.getCustomFieldValue(leverageCF)

def priceTierCF = customFieldManager.getCustomFieldObjectByName("Price Tier")
def priceTier = issue.getCustomFieldValue(priceTierCF)

def platformCF = customFieldManager.getCustomFieldObjectByName("Server Platform")
def platform = issue.getCustomFieldValue(platformCF)

def dedicatedSymbolCF = customFieldManager.getCustomFieldObjectByName("Dedicated Symbol Set")
def dedicatedSymbol = issue.getCustomFieldValue(dedicatedSymbolCF)

def mobileCF = customFieldManager.getCustomFieldObjectByName("Mobile Platform")
def mobile = issue.getCustomFieldValue(mobileCF)

def serverNameCF = customFieldManager.getCustomFieldObjectByName("Server Name")
def serverName = issue.getCustomFieldValue(serverNameCF)

def dataFeedCF = customFieldManager.getCustomFieldObjectByName("Data Feed Name(s)")
def dataFeed = issue.getCustomFieldValue(dataFeedCF)

def groupPrefixCF = customFieldManager.getCustomFieldObjectByName("Group Prefix")
def groupPrefix = issue.getCustomFieldValue(groupPrefixCF)

def symbolSuffixCF = customFieldManager.getCustomFieldObjectByName("Symbol Suffix")
def symbolSuffix = issue.getCustomFieldValue(symbolSuffixCF)

def maxGroupsCF = customFieldManager.getCustomFieldObjectByName("Max Groups")
def maxGroups = issue.getCustomFieldValue(maxGroupsCF)

def maxManagersCF = customFieldManager.getCustomFieldObjectByName("Max Managers")
def maxManagers = issue.getCustomFieldValue(maxManagersCF)

def webTraderUnameCF = customFieldManager.getCustomFieldObjectByName("WebTrader Username")
def webTraderUname = issue.getCustomFieldValue(webTraderUnameCF)

def webTraderPwCF= customFieldManager.getCustomFieldObjectByName("WebTrader Password")
def webTraderPw = issue.getCustomFieldValue(webTraderPwCF)

def bPortalUnameCF = customFieldManager.getCustomFieldObjectByName("Broker Portal Username")
def bPortalUname = issue.getCustomFieldValue(bPortalUnameCF)

def bPortalPwCF = customFieldManager.getCustomFieldObjectByName("Broker Portal Password")
def bPortalPw = issue.getCustomFieldValue(bPortalPwCF)

/**
* Retrieve the primary confluence application link
* @return confluence app link
*/
def ApplicationLink getPrimaryConfluenceLink() {
def applicationLinkService = ComponentLocator.getComponent(ApplicationLinkService.class)
final ApplicationLink conflLink = applicationLinkService.getPrimaryApplicationLink(ConfluenceApplicationType.class)
conflLink
}


// if you don't want to create confluence pages based on some criterion like issue type, handle this, eg:
/*
if (! issue.issueType.name == "Dispute") {
return
}
*/

def confluenceLink = getPrimaryConfluenceLink()
assert confluenceLink // must have a working app link set up

def authenticatedRequestFactory = confluenceLink.createImpersonatingAuthenticatedRequestFactory()

// write storage format using an XML builder
def writer = new StringWriter()
def xml = new MarkupBuilder(writer)
xml.'ac:structured-macro'('ac:name': "jira") {
'ac:parameter'('ac:name': "key", issue.key)
'ac:parameter'('ac:name': "server", "[SERVER NAME HERE]")
'ac:parameter'('ac:name': "serverId", "[SERVER ID HERE]")
}

// 1st table: General Info
xml.p(){
br()
xml.strong("General Information")
}
xml.table('class':"fixed-table wrapped", 'style':"width:38%;"){
colgroup {
col('style':"width:35%;")
col('style':"width:65%;")
}
tbody{
tr{
th('class':"highlight-grey", 'colspan':"1", 'data-highlight-colour':"grey",'title':"Background colour : Grey", "Account Name")
td(corpName)
}
tr{
th("Status")
td("[PUT SOMETHING HERE]")
}
tr{
th("Sales Person")
td(salesPerson)
}
tr{
th("Website")
td(website)
}
tr{
th("Support Email")
td(supportEmail)
}
tr{
th("Corporate Address")
td(corpAddress)
}
}
}

// 2nd table: Contact Info
xml.p(){
br()
xml.strong("Contact Information")
}
xml.table('class':"fixed-table wrapped"){
colgroup {
col('style':"width:155.0px;")
col('style':"width:240.0px;")
col('style':"width:240.0px;")
col('style':"width:240.0px;")
col('style':"width:240.0px;")
}
tbody{
tr{
th("")
th("Name")
th("Email")
th("Phone")
th("Address")
}
tr{
th("Primary Contact")
td(primaryContactName)
td(primaryContactEmail)
td(primaryContactPhone)
td(primaryContactAddress)
}
tr{
th("Additional Contact")
td()
td()
td()
td()
}
tr{
th("Additional Contact")
td()
td()
td()
td()
}
}
}

// 3rd table: Account Info
xml.p(){
br()
strong("Account Information")
}
xml.table('class':"fixed-table wrapped", 'style':"width:38%;"){
colgroup {
col('style':"width:35%;")
col('style':"width:65%;")
}
tbody{
tr{
th("STP Account ID")
td(stpAcctID)
}
tr{
th("DD Account ID")
td(ddAcctID)
}
tr{
th("Price Tier")
td(priceTier)
}
tr{
th("Leverage")
td(leverage)
}
tr{
th("Mobile Platform (Yes/No)")
td(mobile)
}
}
}

// 4th table: Server Details
xml.p(){
br()
strong("Server Details")
}
xml.table('class':"fixed-table wrapped", 'style':"width:38%;"){
colgroup {
col('style':"width:35%;")
col('style':"width:65%;")
}
tbody{
tr{
th("Server Platform (MT4/MT5)")
td(platform)
}
tr{
th("Server Name")
td(serverName)
}
tr{
th("Data Feed Name")
td(dataFeed)
}
tr{
th("Group Prefix")
td(groupPrefix)
}
tr{
th("Symbol Suffix")
td(symbolSuffix)
}
tr{
th("Maximum Groups")
td(maxGroups)
}
tr{
th("Maximum Managers")
td(maxManagers)
}
}
}

// 5th table: Login Info
xml.p(){
br()
xml.strong("Login Information")
}
xml.table('class':"fixed-table wrapped", 'style':"width:38%;"){
colgroup {
col('style':"width:35%;")
col()
col()
}
tbody{
tr{
th("")
th("Username")
th("Password")
}
tr{
th("WebTrader")
td(webTraderUname)
td(webTraderPw)
}
tr{
th("Broker Portal")
td(bPortalUname)
td(bPortalPw)
}
}
}


// print the storage that will be the content of the page
log.debug(writer.toString())

// set the page title - this should be unique in the space or page creation will fail
def pageTitle = issue.key + " Test 2"

def params = [
type : "page",
title: pageTitle,
space: [
key: "TEST" // set the space key - or calculate it from the project or something
],
body : [
storage: [
value : writer.toString(),
representation: "storage"
]
]
]

authenticatedRequestFactory
.createRequest(Request.MethodType.POST, "rest/api/content")
.addHeader("Content-Type", "application/json")
.setRequestBody(new JsonBuilder(params).toString())
.execute(new ResponseHandler<Response>() {
@Override
void handle(Response response) throws ResponseException {
if (response.statusCode != HttpURLConnection.HTTP_OK) {
throw new Exception(response.getResponseBodyAsString())
} else {
def webUrl = new JsonSlurper().parseText(response.responseBodyAsString)["_links"]["webui"]
}
}
})

 



sorry for the formatting issues!

@Ken Lucio  My apologies for the late reply. Thank you so much for this. I really appreciate it. The only probel m i am running into with this now is that, it won't automatically update the fields as they change in the JIRA issue

Hi @Neta Elyakim 

I am creating confluence page from JIRA using your code.

But After the successful creation of page is not linking back to the JIRA Server.

I mean in JIRA Server under the IssueLinks I am not able to find "metioned in" link

to navigate to the "Confluence Page".

Do you know how to get it ?

Please help me ....

Thanks in advance

Kind Regards

Sushma

@Sushma  the use account that is creating te confluence page, needs to have access to the jira project and the confluence space. Also the JIRA issue must be mentioned on the page (JIRA issue macro) and lastly , you have to make sure the link between confluence and jira is working

Hi @Hamdy Atakora 

Thanks for the reply :)

I had a proper access in jira also in confluence I have permission to access the space.

Both Jira and confluence are connected as below :

connection.PNG

@Sushma  So when the page is created, is the JIRA issue mentioned on it, using the JIRA Issue Macro? Without that, it will not link

Hi @Hamdy Atakora 

Yes .when page is created it is creating the Jiraissue macro in it.

page.PNG 

In the script runner I found the below information :

docs.PNGBut in my case why it not creating a pagelink in the jira server ?

In confluence If I click on the "jira macro issue" it is redirecting me to the jira server.

But In jira under the issuelinks I am not able to find the page link.

Please help m,e to resolve this

Hi All,

Any response ?

Regards

Sushma

@Sushma check the macro in the confluence page. Edit it and chec k to see if the issue is bein g linked as a table ort as a single issue. Confluence will not display it , if it is adding the issue in the macro as a table..

Hi @Hamdy Atakora 

The storage format of jira issue macro is as follows

storageformat.PNG

@Sushma  this is the main difference . In confluence, try editing the macro and c heck it. It should not be displaying as a table. It should be as a single issue

JIRAissuesMacro_DisplayOptions.png

Like Sushma likes this

Hi @Hamdy Atakora 

Thanks alot

Regards

Sushma

Suggest an answer

Log in or Sign up to answer
TAGS
Community showcase
Published in Apps & Integrations

🍻🍂Apptoberfest Update: Upcoming Virtual Events 🎉

Hello Community! I hope you've been enjoying the 🍂Apptoberfestivities🍂 (I know I have!) The event is heating up next week with a series of virtual events that we're calling the 🍻🍂Partner App ...

79 views 1 6
Read article

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