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.
ScriptRunner for Confluence has a built-in script for copying a tree of pages that's available for Confluence Administrators and Space Administrators, but what if you wanted to create something like that for regular Confluence users?
The answer actually requires two ScriptRunner features to work together.
First, you would need a custom REST Endpoint to copy a page. Second, you'd need a custom Web Item to add a button that would call to your REST Endpoint.
There are many use cases around this, but let's consider one specific one: you want to let people copy a tree of pages to their personal space.
The code below demonstrates how a REST Endpoint could copy a tree of pages. It's deliberately oversimplified (it has minimal validation, it doesn't copy labels, attachments, comments, etc.), but it's a useful starter.
import com.atlassian.confluence.core.DefaultSaveContext import com.atlassian.confluence.pages.Page import com.atlassian.confluence.pages.PageManager import com.atlassian.confluence.setup.settings.SettingsManager import com.atlassian.confluence.spaces.SpaceManager import com.atlassian.confluence.user.AuthenticatedUserThreadLocal import com.atlassian.sal.api.component.ComponentLocator import com.atlassian.sal.api.transaction.TransactionTemplate import com.onresolve.scriptrunner.runner.ScriptRunnerImpl import com.onresolve.scriptrunner.runner.rest.common.CustomEndpointDelegate import groovy.transform.BaseScript import groovy.transform.Field import javax.ws.rs.core.MultivaluedMap import javax.ws.rs.core.Response @BaseScript CustomEndpointDelegate delegate @Field PageManager pageManager = ComponentLocator.getComponent(PageManager) @Field SpaceManager spaceManager = ComponentLocator.getComponent(SpaceManager) @Field SettingsManager settingsManager = ComponentLocator.getComponent(SettingsManager) copyPageTree( httpMethod: "GET", groups: ["confluence-users"] ) { MultivaluedMap queryParams, String body -> def pageId = queryParams.getFirst("page")?.toString()?.toInteger() def sourcePage = pageManager.getPage(pageId) def user = AuthenticatedUserThreadLocal.get() def personalSpace = spaceManager.getPersonalSpace(user) def newPage = copyPage(sourcePage, personalSpace.homePage) copyPages(sourcePage.children, newPage) def baseUrl = settingsManager.getGlobalSettings().getBaseUrl() def redirect = new URI("${baseUrl}${newPage.urlPath}") log.debug "Redirect: ${redirect}" return Response.temporaryRedirect(redirect).build() //to new page } void copyPages(List pages, Page targetPage) { log.debug "Copying pages: ${pages*.title} to ${targetPage.title}" pages.each { sourcePage -> def clonePage = copyPage(sourcePage, targetPage) copyPages(sourcePage.children, clonePage) } } Page copyPage(Page sourcePage, Page targetPage) { def transactionTemplate = ScriptRunnerImpl.scriptRunner.getBean(TransactionTemplate) transactionTemplate.execute { def extantPage = pageManager.getPage(targetPage.space.key, sourcePage.title) if (!extantPage) { def newPage = new Page() newPage.with { setTitle(sourcePage.title) setSpace(targetPage.space) setParentPage(targetPage) setBodyAsString(sourcePage.bodyAsString) setCreator(sourcePage.creator) setContentStatus(sourcePage.getContentStatus()) setCreationDate(sourcePage.getCreationDate()) } targetPage.addChild(newPage) pageManager.saveContentEntity(newPage, DefaultSaveContext.BULK_OPERATION) return newPage } else { return extantPage } } }
The Web Item's configuration would look something like the attached image.
Two important bits of that configuration are worth a special mention: the URL and the section.
The section selected (system.content.action/modify) will add our custom web item to the three-dot menu on the upper right corner of a Confluence page.
The URL will point to the custom REST Endpoint we setup earlier and provide the current page ID as a parameter. /rest/scriptrunner/latest/custom/copyPageTree?page=${page.id}
Jonny Carter
Engineering Team Lead
Adaptavist
Springfield, MO
55 accepted answers
2 comments