Skip to main content
Example scripts
arrow icon
To homepage
Confluence
Data centre icon
Data Center

Automate the Creation of Complex Page Structures within Confluence

Created 1 year ago, Updated 2 month(s) ago
App in script
ScriptRunner For Confluence
ScriptRunner For Confluence
by Adaptavist
Compatibility
compatibility bullet
Confluence (7.15 - 8.6)
compatibility bullet
ScriptRunner For Confluence (7.10.0)
Language |
groovy
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
}
Having an issue with this script?
Report it here