Hi community,
Just posting a question here since I've been having trouble finding resources for this.
While using scriptrunner to create a page, I can add macros to it. I do it through XML like this:
xml.'ac:structured-macro'('ac:name':"section) {
'ac:rich-text-body'("here are some values to be shown in the body of the macro")
}
xml.'ac:structured-macro'('ac:name':"section") {
'ac:inline'('ac:structured-macro'('ac:name':"column") {
'ac:rich-text-body'("there should be some value in here")
})
}
Posting the solution here. Bear in mind I'm working with groovy in the scriptrunner console in Jira, and not a python file to run on the backend with a curl request:
-Create the page
-Retrieve the page body in storage format. It will be in an XHTML document object.
-Make necessary alterations to the document object
-execute PUT request - see below
Resources that were very helpful!
https://docs.atlassian.com/ConfluenceServer/rest/7.15.0/#api/content-update
Document (jsoup Java HTML Parser 1.15.2-SNAPSHOT API) also see 'Element'
And many more forum posts that may not have been directly helpful but gave me a better idea of what I was looking for
def params = [
type : "page",
title: pageTitle, // <------- grab the original page title if not changing or specify a new one
version: [
number: "2" // <----- I hard incremented during testing. Grab the original page version and + 1 it
],
status: "current", // <------------ you can make it a draft if you so please
body : [
storage: [
value : new_bod, //<-------- literally just take the modified xhtml document and .toString() it
representation: "storage"
]
],
]
In your original post you mentioned you are able to embed a macro while creating a page via xml.
Could you please provide an example of code how this would work?
I got stuck on how to add the xml/macro content to the page content:
import com.atlassian.confluence.pages.Page
import com.atlassian.confluence.spaces.SpaceManager
import com.atlassian.confluence.pages.PageManager
import com.atlassian.sal.api.component.ComponentLocator
def pageManager = ComponentLocator.getComponent(PageManager)
def spaceManager = ComponentLocator.getComponent(SpaceManager)
def spaceKey = "STS"
def space = spaceManager.getSpace("${spaceKey}")
def homePage = space.getHomePage()
Page parentPage = pageManager.getPage(homePage.id);
Page page = new Page();
page.setSpace(space);
page.setParentPage(parentPage);
page.setTitle("Created by Scriptrunner");
page.setBodyAsString('');
parentPage.addChild(page);
pageManager.saveContentEntity(page, null, null);
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
This appears to be the Confluence api, for this post I was generating the page from our Jira instance. Basically I created the confluence page template manually, then copied the page source into my code and edited certain lines to use variables instead of whatever was in the template.
Whatever you pull from the template page source, try pasting that into the 'page.setBodyAsString('')' function
I tested what you posted in my console and after editing the string page source stuff I had from my original script (basically just removed the variables I had in) and it worked just fine
Here is an example you can paste in that spot, just replace the issue key with some one from your systems (shows up twice, I bolded and underlined them)
"""<p class="auto-cursor-target">
<ac:structured-macro ac:name="jira" ac:schema-version="1" ac:macro-id="02d7303d-bc19-42c7-b22a-0e8b3a1a5a7b">
<ac:parameter ac:name="server">Jira</ac:parameter>
<ac:parameter ac:name="columnIds">
issuekey,summary,issuetype,created,updated,assignee,reporter,priority,status,resolution
</ac:parameter>
<ac:parameter ac:name="columns">
Key,Summary,Type,Created,Updated,Assignee,Reporter,Priority,Status,Resolution
</ac:parameter><ac:parameter ac:name="jqlQuery">key = JTES-2256</ac:parameter>
<ac:parameter ac:name="serverId">
08c4b063-58d0-3ea5-906a-60932f95f843
</ac:parameter><ac:parameter ac:name="key">
JTES-2256
</ac:parameter>
</ac:structured-macro>
</p>
<p><strong>General Ticket Information</strong></p>
<p>Program: </p>
<p>Test Engineers: </p>
<p>Manager Approving: </p>
<p>Created: </p>
<p>Due: </p>
<p>Location: </p>
<p class="auto-cursor-target"><br /></p>
<p><strong>Table Of Contents</strong></p>
<p class="auto-cursor-target"><ac:structured-macro ac:name="toc" ac:schema-version="1" ac:macro-id="1e1c437a-eabb-4530-9862-661b0ba20b75" /></p>
<hr />
<h1>General</h1>
<h2>Purpose/Background</h2>
<p></p>
<p><br /></p>
<p><br /></p>
<hr />
<h1>Testing Info</h1>
<h2>Procedure</h2>
<h2>Required Materials and Equipment</h2>
<h2>Post Testing Info Comparison</h2>
<p><br /></p>
<p><br /></p>
<hr />
<h1>Post Testing Info</h1>
<h2>Additional Info</h2>
<h2>Test Results</h2>
<h2>Conclusion/Takeaways</h2>
<p><br /></p>
<p><br /></p>
<hr />
<h2>Attachments</h2>
<p><ac:structured-macro ac:name="attachments" ac:schema-version="1" ac:macro-id="5be11d15-d16e-49a2-9f57-f9a13f73bbd2" /></p>
<p class="auto-cursor-target"><br /></p>
<p class="auto-cursor-target"><br /></p>
<p><br /></p>
<p><br /></p>"""
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi @Jordan Hauser ,
This is a very interesting idea! Can you please share with us an example on how you retrieve the storage format of the confluence page you are creating with ScriptRunner? I have a similar request but my solution is not working when I try to create the confluence page in one go with more macros.
Thank you!
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi @Alexandra-Mihaela Apostol
When I was updating the page at a later point in the workflow, I have to use a GET request for using the following endpoint:
/plugins/viewstorage/viewpagestorage.action?pageId={page id you want}
The request was structured like this:
//Outside the primary method I'm using in this project found in here Interacting with Confluence from JIRA (adaptavist.com)
//Most packages I used for this, might be missing a couple
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.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 getPrimaryConfluenceLink() {
def applicationLinkService = ComponentLocator.getComponent(ApplicationLinkService.class)
final ApplicationLink confLink = applicationLinkService.getPrimaryApplicationLink(ConfluenceApplicationType.class)
confLink
}
Within the method I created for this project, I had to do the GET request.
(1) confluenceLink.createAuthenticatedRequestFactory()
(2) .createRequest(Request.MethodType.GET, """plugins/viewstorage/viewpagestorage.action?pageId=""" + holder)
(3) .addHeader("Content-Type", "application/json")
(4) .execute(new ResponseHandler<Response>() {
(5) @Override
(6) void handle(Response response) throws ResponseException {
(7) if (response.statusCode != HttpURLConnection.HTTP_OK) {
(8) throw new Exception(response.getResponseBodyAsString())
(9) } else {
(10) def xhtml_parser = Jsoup.parseBodyFragment(response.responseBodyAsString)
(11) xhtml_parser.outputSettings.prettyPrint(false)
}
}
})
It uses the confluence application link and sets the structure for the request with the factory (1), then creates the GET request referencing the endpoint for the page storage and using a variable for the pageId which you could hardcode if your just doing one (2). I needed to add a header for the request type (3) and you may need to add in some auth if the instance is more secure using another .addHeader("Authorization", "{basic or bearer auth depending on what you need}").
Then it executes the request (4)and says to use a certain variant of the response handler class (5 and 6). 7 and 8 are what to do in case it fails, 9 tells it what to do when it succeeds. 10 is to define a variable thats the body of the page, parsed from the xhtml its formatted in using the jsoup library. 11 sets it to the working format for sending it back when I update the page, if you need to read it in the logs while testing I suggest setting prettyPrint(true) and converting it back to false later.
Hope that helps!
-Jordan
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi @Jordan Hauser , this worked perfectly for me!
Thank you for your explanation!
Cheers,
Alexandra-Mihaela
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Online forums and learning are now in one easy-to-use experience.
By continuing, you accept the updated Community Terms of Use and acknowledge the Privacy Policy. Your public name, photo, and achievements may be publicly visible and available in search engines.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.