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

1

Automate the Creation of Complex Page Structures within Confluence

Overview

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.

Example

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.

Requirements

  • Confluence Confluence (7.9 - 7.20)
  • ScriptRunner for Confluence ScriptRunner for Confluence (6.18.0)
    
import com.atlassian.confluence.content.service.PageService 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 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.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 @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") @Field PageService pageService = ComponentLocator.getComponent(PageService) // 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 parentPageLocator = pageService.getIdPageLocator(parentPageId) def parentPage = parentPageLocator?.getPage() ?: 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 ${parentPage.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) parentPage.addChild(createdPage) // Save this page pageManager.saveContentEntity(createdPage, DefaultSaveContext.SUPPRESS_NOTIFICATIONS) def createdPageLocator = pageService.getIdPageLocator(createdPage.id) if (createdPageLocator.getPage()) { log.debug("Created page ${testPageTitle} successfully") } else { throw new IllegalStateException("Unable to create page ${testPageTitle}") } // Build the children if (pageSpec.page) { pageSpec.page.each { 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 }
Discovered an issue? Report it here

Suggested for you