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) {
}
}
Ramakrishnan Srinivasan
0 comments