You're on your way to the next level! Join the Kudos program to earn points and save your progress.
Level 1: Seed
25 / 150 points
Next: Root
1 badge earned
Challenges come and go, but your rewards stay with you. Do more to earn more!
What goes around comes around! Share the love by gifting kudos to your peers.
Keep earning points to reach the top of the leaderboard. It resets every quarter so you always have a chance!
Join now to unlock these features and more
The Atlassian Community can help you and your team get more value out of Atlassian products and practices.
Hi,
I have made some customization of pdf export of a Confluence page. Sharing my approach here for others to try/benefit.
My Setup:
My Approach:
My Configurations
import com.atlassian.sal.api.component.ComponentLocator
import com.atlassian.confluence.spaces.Space
import com.atlassian.confluence.spaces.SpaceManager
import com.atlassian.confluence.core.DefaultDeleteContext
import com.atlassian.confluence.pages.Page
import com.atlassian.confluence.pages.PageManager
def spaceManager = ComponentLocator.getComponent(SpaceManager)
def pageManager = ComponentLocator.getComponent(PageManager)
def parentPage=pageManager.getPage("SOME_SPACE_KEY", "TO DELETE PAGES")
def childPages = pageManager.getDescendants(parentPage)
if(childPages) {
childPages.each { thisChild ->
pageManager.trashPage(thisChild, DefaultDeleteContext.DEFAULT)
}
}
//This script is loaded in Confluence - Script Runner - Rest End Point
//It takes the spaceKey and page title as url parameters
//gets page content
//replacesAll href of jira issue links
//relacesAll Back to Top CDATA
//creates a child page from replaced string
//redirects that child page for pdf download after the page is loaded in browser
/*import org.apache.log4j.Level
import org.apache.log4j.Logger
def logx = Logger.getLogger("com.acme.workflows")
logx.setLevel(Level.DEBUG)*/
import org.apache.http.HttpRequest
import org.apache.http.protocol.HttpContext
import groovy.json.JsonBuilder
import groovy.json.JsonSlurper
import org.apache.http.HttpRequestInterceptor
import groovyx.net.http.HTTPBuilder
import net.sf.json.JSONArray
import groovyx.net.http.RESTClient
import static groovyx.net.http.Method.GET
import static groovyx.net.http.ContentType.JSON
import groovy.transform.Field
import org.codehaus.groovy.runtime.MethodClosure
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest
import java.io.InputStreamReader
import com.atlassian.confluence.setup.settings.SettingsManager
import com.atlassian.confluence.core.BodyContent
import com.atlassian.confluence.core.BodyType
import com.atlassian.confluence.core.DefaultSaveContext
import com.atlassian.confluence.core.DefaultDeleteContext
import com.atlassian.confluence.pages.DuplicateDataRuntimeException
import com.atlassian.confluence.pages.Page
import com.atlassian.confluence.pages.PageManager
import com.atlassian.confluence.pages.templates.PageTemplateManager
import com.atlassian.confluence.security.Permission
import com.atlassian.confluence.security.PermissionManager
import com.atlassian.confluence.spaces.Space
import com.atlassian.confluence.spaces.SpaceManager
import com.atlassian.confluence.user.AuthenticatedUserThreadLocal
import com.atlassian.sal.api.component.ComponentLocator
import com.onresolve.scriptrunner.canned.confluence.utils.PermissionDeniedException
import com.onresolve.scriptrunner.runner.rest.common.CustomEndpointDelegate
import groovy.json.JsonOutput
import groovy.transform.BaseScript
import groovy.transform.Field
import org.apache.log4j.Logger
import javax.ws.rs.core.MultivaluedMap
import javax.ws.rs.core.Response
import java.text.SimpleDateFormat
import groovy.time.TimeCategory
import groovy.time.TimeDuration
Date start = new Date()
@Field SpaceManager spaceManager = ComponentLocator.getComponent(SpaceManager)
@Field PageManager pageManager = ComponentLocator.getComponent(PageManager)
@Field PermissionManager permissionManager = ComponentLocator.getComponent(PermissionManager)
@Field PageTemplateManager pageTemplateManager = ComponentLocator.getComponent(PageTemplateManager)
//@Field Logger log = Logger.getLogger("com.onresolve.scriptrunner.runner.ScriptRunnerImpl")
@BaseScript CustomEndpointDelegate delegate
//End point name is the same as the method's name
//exportPage_toPDF_withoutJira_hyperLinks(httpMethod: "GET", groups: ["confluence-administrators", "confluence-users"]) { MultivaluedMap queryParams, String body ->
exportPage_toPDF_withoutJira_hyperLinks(httpMethod: "GET") { MultivaluedMap queryParams, String body ->
Date date = new Date()
String datePart = date.format("dd/MMM/yyyy")
String timePart = date.format("HH:mm:ss.SSS")
String dateTimeStamp = date.format("dd/MMM/yyyy HH:mm:ss.SSS")
pageManager = ComponentLocator.getComponent(PageManager)
def spaceKey = queryParams.getFirst("spaceKey").toString()
def parentPageTitle = queryParams.getFirst("parentPageTitle").toString()
try {
parentPage=pageManager.getPage(spaceKey, parentPageTitle)
def contentEntityObject = parentPage.getEntity()
def parentPageBody=parentPage.getBodyAsString()
def space = spaceManager.getSpace(spaceKey) as Space
def childPageTitle = parentPageTitle + " - External"
def childPage = pageManager.getPage(spaceKey, childPageTitle)
if(childPage) {
pageManager.trashPage(childPage, DefaultDeleteContext.DEFAULT)
}
def HTML_A_HREF_TAG_PATTERN_jira = "\\s*(?i)href\\s*=\\s*\"([^\"]*jira/browse/[^\"]*\")"
def regex_BacktoTop = "<p><ac:link ac:anchor=\"_wiki_toc\"><ri:content-entity ri:content-id=\"\\d+\" /><ac:plain-text-link-body><!\\[CDATA\\[Back to Top\\]\\]></ac:plain-text-link-body></ac:link></p>"
def childPageContent = parentPageBody.replaceAll(HTML_A_HREF_TAG_PATTERN_jira, "dummy")
childPageContent = childPageContent.replaceAll(regex_BacktoTop, "")
def regex_jiraMacro = "<p style=\"font-size: 15.0px;\"><strong>G3 Issue used to create this Release Page</strong></p><p><ac:structured-macro ac:name=\"jira\" ac:schema-version=\"\\d+\" ac:macro-id=\"([a-zA-Z0-9*--]+)\"><ac:parameter ac:name=\"server\">Jira</ac:parameter><ac:parameter ac:name=\"serverId\">([a-zA-Z0-9*--]+)</ac:parameter><ac:parameter ac:name=\"key\">((?<!([A-Za-z]{1,10})-?)[A-Z]+-\\d+)</ac:parameter></ac:structured-macro></p>"
childPageContent = childPageContent.replaceAll(regex_jiraMacro, "")
def regex_history = "<h1>Document history</h1><p><ac:structured-macro ac:name=\"version-history\" ac:schema-version=\"\\d+\" ac:macro-id=\"([a-zA-Z0-9*--]+)\" /></p>"
childPageContent = childPageContent.replaceAll(regex_history, "")
def toDelete_parentPage=pageManager.getPage(spaceKey, "TO DELETE PAGES")
def createdChildPageId = createBasicPage(space, toDelete_parentPage, childPageTitle, "Temporary Page")
def tableFilterString = "<ac:structured-macro ac:name=\"table-filter\" ac:schema-version=\"1\""
def loadResources = "" //tf-export-ready is Table filter element
if(childPageContent.contains(tableFilterString)) {
loadResources = "<ac:structured-macro ac:name=\"html\">\
<ac:plain-text-body><![CDATA[\
<script type=\"text/javascript\">\
AJS.toInit(function() {\
AJS.\$('#editPageLink').hide();\
AJS.bind('tf-export-ready', function() {\
setTimeout(function() {AJS.\$(location).prop('href', 'https://HOST/wiki/spaces/flyingpdf/pdfpageexport.action?pageId=" + createdChildPageId + "');}, 2000);\
});\
});\
</script>]]>\
</ac:plain-text-body>\
</ac:structured-macro>"
} else {
//removed AJS.bind('tf-export-ready', function() {\
loadResources = "<ac:structured-macro ac:name=\"html\">\
<ac:plain-text-body><![CDATA[\
<script type=\"text/javascript\">\
AJS.toInit(function() {\
AJS.\$('#editPageLink').hide();\
setTimeout(function() {AJS.\$(location).prop('href', 'https://HOST/wiki/spaces/flyingpdf/pdfpageexport.action?pageId=" + createdChildPageId + "');}, 2000);\
});\
</script>]]>\
</ac:plain-text-body>\
</ac:structured-macro>"
}
childPageContent = childPageContent.replaceAll("dummy", "") + loadResources
editPage("${createdChildPageId}", childPageTitle, childPageContent, "2")
pageUrl = "https://HOST/wiki/pages/viewpage.action?pageId=${createdChildPageId}"
return Response.temporaryRedirect(URI.create(pageUrl)).build()
} catch(Exception ex) {
//pageIdMap["Exception"] = "Exception to get page or append page for spaceKey = ${spaceKey} and title = ${title}: ${ex.toString()}"
}
}
/**
* Create a basic page. This is not linked in any hierarchy.
*
* @param space The space that this page belongs to.
* @param title The title of the page we are creating.
* @param content The content of the page we are creating.
*
* @return The create page object.
*/
def createBasicPage(Space space, Page parentPage, String title, String content) {
def pageManager = ComponentLocator.getComponent(PageManager)
def page = new Page()
def bodyContent = new BodyContent(page, content, BodyType.XHTML)
page.with {
setVersion(1)
setSpace(space)
setTitle(title)
setBodyContent(bodyContent)
setCreator(AuthenticatedUserThreadLocal.get())
}
linkPages(parentPage, page)
pageManager.saveContentEntity(page, DefaultSaveContext.SUPPRESS_NOTIFICATIONS)
return page.id
}
/**
* Link a parent and a child page together. This method creates a bi-directional relationship between the two pages.
*
* @param parent The parent page that we wish to link.
* @param child The child page that we wish to link.
*/
void linkPages(Page parent, Page child) {
// Set the parent page on the child
child.setParentPage(parent)
// Set the child page on the parent
parent.addChild(child)
// Set the ancestors on the child page
def ancestors = []
def parentPageAncestors = parent.ancestors as List
if (parentPageAncestors) {
ancestors.addAll(parentPageAncestors)
}
ancestors.add(parent)
child.setAncestors(ancestors)
}
void editPage(String pageId, String pageTitle, String pageContent, String pageVersion) {
//https://community.atlassian.com/t5/Confluence-questions/How-to-edit-the-page-content-using-rest-api/qaq-p/904345
//def logx = Logger.getLogger("com.acme.workflows")
//logx.setLevel(Level.DEBUG)
def pageManager = ComponentLocator.getComponent(PageManager)
def params = [
id: pageId,
type: "page",
title: pageTitle,
body: [storage:[value:pageContent, representation:"storage"]],
version: [number:pageVersion]
]
def jsonString = new JsonBuilder(params).toString()
def rest_api_str = "https://HOST/wiki/rest/api/content/${pageId}"
def http = new HTTPBuilder(rest_api_str)
try { def json = http.request(groovyx.net.http.Method.PUT, groovyx.net.http.ContentType.JSON) { req ->
req.addHeader('Authorization', 'Basic ' + 'USER:PASSWORD'.bytes.encodeBase64().toString())
body = jsonString
response.success = { resp, jsonData ->
assert resp.status == 200
return jsonData
}
response.failure = { resp ->
return resp.status
}
}
} catch(Exception ex) {
}
}