You can now vote for scripts to help people know if they're useful or not. Login or create an account to vote!


Automate the Creation of Complex Page Structures within Confluence


Create complex page structures automatically within Confluence using this custom REST endpoint. Save time when creating projects or pieces of work that require the same page structures and templates.


As a project manager, I need to create multiple pages with the same taxonomy. Creating each page manually is time consuming. Using this script I can create a reusable set of templates for a project, and with a single click I can create multiple pages in a structured hierarchy.

Good to Know

  • There are several different ways in which you can utilise this script. These include, but are not limited to:
    • Using a custom button (Script Fragment > Custom Web Item) to allow users to trigger the action.
    • Calling the endpoint from another Atlassian application or 3rd party system, allowing you to create page structures within Confluence.
  • Security concerns should be evaluated as part of implementing your own version of this endpoint, here are a few:
    • Consider which groups should have access to your rest endpoint and restrict as required.
    • Check to ensure users have the correct Confluence permissions for the work being done by your endpoint.
  • You can get more info in this blog post.


  • Confluence Confluence (7.4 - 7.19)
  • ScriptRunner for Confluence ScriptRunner for Confluence (6.18.0)
import com.atlassian.confluence.core.BodyContent import com.atlassian.confluence.core.BodyType import com.atlassian.confluence.core.DefaultSaveContext 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 import 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 import groovy.json.JsonOutput import groovy.transform.BaseScript import groovy.transform.Field import org.apache.log4j.Logger import import @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") // The specified setup - Specify the page titles and hierarchy here // This example setup contains one page with 2 child pages // The template value takes the id of the template you want to use def spec = [ [ title : "Planning", templateName : "template1", templateSpaceKey: "PS", page : [ [ title : "Review", templateName : "template2", templateSpaceKey: "global-template" ], [ title : "Social", templateName : "template1", templateSpaceKey: "PS" ] ] ] ] @BaseScript CustomEndpointDelegate delegate createProjectPages(httpMethod: "GET", groups: ["confluence-administrators", "confluence-users"]) { MultivaluedMap queryParams, String body -> // This is the space key specified in the custom script fragment def spaceKey = queryParams.getFirst("spaceKey") as String // This is the parent page id specified in the custom script fragment // The first page created in this script will use the page associated with the parent page id as its parent def parentPageId = queryParams.getFirst("parentPageId") as Long def mainPageTitle = spec.get(0).get("title") // Flag shown to user on successful or failed creation of new project structure pages def flag = [ type : 'success', title: "Pages created", close: 'auto', body : "Refresh this page to see the newly created page (${mainPageTitle}) and its children in the page tree" ] try { createPages(spaceKey, parentPageId, spec) } catch (IllegalStateException | DuplicateDataRuntimeException e) { log.error("There was a problem trying to create the project structure", e) flag = [ type : 'failure', title: "An error occurred", close: 'manual', body : "There was an error trying to create project structure pages" ] } Response.ok(JsonOutput.toJson(flag)).build() } /** * Create the desired page structure * * @param spaceKey The Key of the space to add pages for. * @param parentPageId The page id of the parent page. * @param spec The specification for the pages to be created. */ void createPages(String spaceKey, Long parentPageId, List spec) throws IllegalStateException, Exception { def space = spaceManager.getSpace(spaceKey) as Space def parentPage = pageManager.getPage(parentPageId) as Page ?: spaceManager.getSpace(spaceKey).homePage if (!parentPage) { throw new IllegalStateException("The specified parent page for new project structure pages does not exist") } if (!userHasPageViewPermission(parentPage)) { throw new IllegalStateException("User does not have the required permission to create child pages on page with id ${}") } spec.each { pageSpec -> createPage(parentPage, space, pageSpec as Map) } } /** * Check if the user clicking the fragment button has the relevant permission to create child pages. * @param parentPage * @return user permission view status */ boolean userHasPageViewPermission(Page parentPage) { def user = AuthenticatedUserThreadLocal.get() permissionManager.hasPermission(user, Permission.VIEW, parentPage) } /** * Create a page using the given page specification (pageSpec). This spec dictates the title of the page, the template * to be used to populate it (if any) and if required, the specification of any child pages. * * @param parentPage The parent page for the page we're about to create. * @param space The space that the page should be created in. * @param pageSpec The specification for the pages to be created. */ void createPage(Page parentPage, Space space, Map pageSpec) throws IllegalStateException { def testPageTitle = pageSpec.title as String def templateName = pageSpec.templateName as String def templateSpaceKey = pageSpec.templateSpaceKey as String def content = getTemplateContent(templateName, templateSpaceKey) def createdPage = createBasicPage(space, testPageTitle, content) linkPages(parentPage, createdPage) // Save this page pageManager.saveContentEntity(createdPage, DefaultSaveContext.SUPPRESS_NOTIFICATIONS) if (pageManager.getPage(space.key, createdPage.title)) { log.debug("Created page ${testPageTitle} successfully") } else { throw new IllegalStateException("Unable to create page ${testPageTitle}") } // Build the children if ( { { childPageSpec -> createPage(createdPage, space, childPageSpec as Map) } } } /** * Get the content from the template to populate the page with. * * @param templateName The name of the template we wish to use. * @param templateSpace The space associated with the template we wish to use. * @return The template body content. */ String getTemplateContent(String templateName, String templateSpaceKey) { def pageTemplate = templateSpaceKey == "global-template" ? pageTemplateManager.getGlobalPageTemplate(templateName) : pageTemplateManager.getPageTemplate(templateName, spaceManager.getSpace(templateSpaceKey)) if (!pageTemplate) { throw new IllegalStateException("Unable to retrieve specified template") } pageTemplate.content } /** * 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. */ Page createBasicPage(Space space, String title, String content) { 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()) } page } /** * 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) }
Discovered an issue? Report it here

Suggested for you