We are building out our library of bundled macros. Prior to release, this is driven mostly by what we require. Let us know what would be useful to you.

Longer-term, the intention is that you can build on these macros yourself.

Include Version Macro

This macro is similar to the include page macro, but allows you to select a specific version of the page to include.

This is great for reports and proposals, because you can create a space that consists only of boilerplate, and if you’re lucky your report will not be much more than a combination of includes.

The problem with this macro is that people may not be aware that the page they are editing is being included in several other pages, and may make inappropriate edits. This macro, along with the includes report macro, lets you easily control that.

By default, when including a page using this macro it will default to the number of the latest version. There is an additional option Latest, which simply tracks the latest version. When this option is used it becomes identical to the standard Include Page macro.

include version macro config

You can use the macro browser preview functionality to view the results of including the different versions.

Converting Pages

If you have many instances of the include page macro on a page, it would be painful to manually update them. A REST endpoint is included which will convert all instances of include page to include version. The version number is specified to be the current latest version, so the page contents should be identical (until one of the includes is modified).

You can easily hook up this endpoint using a web item:

convert includes web item

The condition code is the Current page contains particular macros sample, with cheese changed to include.

The link address is:

/rest/scriptrunner-confluence/latest/content-info/convert-includes?pageId=${page.id}&#url_xsrfToken()

Once configured it will appear as below, but only on those pages that contain the include page macro.

convert includes disp

Includes Report Macro

This macro is used in conjunction with the include version macro. It will show you which includes have later versions, with links to the diffs so that you can easily see what version you want to update to, if any.

It also provides a button to Update All, which will update all includes to the version number of the latest version (check the diffs first to make sure this is what you want). When used it will create and save a new version of the current page - you can always roll it back if it wasn’t the result you wanted.

includes report view

To display this macro best you could put it in a panel macro. If you are writing reports for customers, you probably don’t want them to see this information - so put the panel in a NoPrint user macro.

Example source:

includes report source

CQL Search Macro

This macro executes the provided CQL query and returns the results as links to pages. It can useful to show the results of complex queries on pages, or labels etc.

Various improvements are expected in this macro in the near future.

Markdown Macro

This macro allows you to insert your own Markdown inline tags or render them from a URL. Using external URLs will require whitelisting, as described below.

Typically you would want to include some Markdown from a Gist or a repository in Github or Bitbucket.

In GitHub & Bitbucket you need to get the Raw content URL, which links to the original Markdown file.

Github

Bitbucket

github raw
bitbucket raw

If you want to display Markdown from another Atlassian product, for example Bitbucket Server, then the macro will automatically detect if you have an application link setup for the url you have entered and use it to retrieve the content.

You may also place Markdown directly into the body of the macro:

markdownBody
markdownBody2
If you place Markdown in the body of the macro and you provide a URL, the URL will be ignored.
This macro only supports the CommonMark specification of Markdown, which means that different flavours of the Markdown specification may not render correctly.

Whitelisting URLs

To use URLs to fetch Markdown documents from external sources, the Markdown Macro requires you to add them to the whitelist for outgoing requests.

If the Confluence whitelist is disabled, you will not be able to use URLs with the Markdown Macro at all, though inline Markdown will still work. This is necessary in order to prevent SSRF vulnerabilities.

Linked Atlassian applications are whitelisted by default, but you’ll likely want to whitelist common Markdown sources such as the following:

You may want to be more restrictive and, for example, only allow data from your company’s Bitbucket project. For that, you could use a wildcard expression rule like https://bitbucket.org/MyCompany/*.

Confluence’s whitelist tester may still display a red mark for the "incoming" URLs depending on your company’s firewall settings. The Markdown Macro does not require incoming whitelisting, so this should not pose a problem.
url whitelist test incoming red okay

Files, FTP, and other data

You may have Markdown files in places that can’t be accessed over HTTP that you still want to render in Confluence. For example, you may have a network file share on your Confluence server where permitted users are adding Markdown documents.

Previous versions of ScriptRunner for Confluence allowed specifying file & ftp URLs for the Markdown Macro. In order to support the added security of the url whitelist, this is no longer supported out of the box. Fortunately, you can still use a Scripted REST Endpoint to serve up those documents.

Files

This example shows how to read markdown files from a directory in your Confluence server’s home directory.

First, create a REST Endpoint with code like this, with appropriate changes for your environment:

import com.onresolve.scriptrunner.runner.ScriptRunnerImpl
import com.onresolve.scriptrunner.runner.diag.ClusterHomeLocatorService
import com.onresolve.scriptrunner.runner.rest.common.CustomEndpointDelegate
import groovy.transform.BaseScript

import javax.ws.rs.core.MediaType
import javax.ws.rs.core.MultivaluedMap
import javax.ws.rs.core.Response

@BaseScript CustomEndpointDelegate delegate

getMarkdownFile( (1)
    httpMethod: "GET", (2)
    groups: ["confluence-users"] (3)
) { MultivaluedMap queryParams ->
    def fileName = queryParams.getFirst("filename") as String (4)

    if (!fileName) {
        return Response.status(Response.Status.BAD_REQUEST)
            .type(MediaType.TEXT_PLAIN_TYPE)
            .entity("Must specify a markdown file.").build()
    }

    def homeDirectory = ScriptRunnerImpl.getOsgiService(ClusterHomeLocatorService).sharedHomeDir
    def markdownFiles = new File(homeDirectory, "markdown-files/") (5)
    def file = new File(markdownFiles, fileName) (6)

    boolean pathIsCanonical = file.path == file.canonicalPath (7)
    boolean fileIsMarkdown = [".md", ".markdown", ".mdx"].any { extension -> fileName.endsWith(extension) }

    if (!pathIsCanonical || !fileIsMarkdown || !file.exists()) {
        return Response.status(Response.Status.BAD_REQUEST)
            .type(MediaType.TEXT_PLAIN_TYPE)
            .entity("You may only specify markdown files in the appropriate directory.").build()
    }

    String fileContents = file.getText()
    return Response.status(Response.Status.ACCEPTED)
        .type(MediaType.TEXT_PLAIN_TYPE)
        .entity(fileContents).build()
}
1 The name of the REST endpoint, which forms part of the URL, in this example getMarkdownFile
2 The httpMethod must be GET in order for the endpoint to work with the Markdown macro.
3 This line should be used to restrict use of the REST Endpoint to specific groups of authenticated users. Don’t allow access unless it is definitely needed.
4 Retrieve the filename from the URL’s query parameters
5 Here, we assume that the directory where the markdown files live is inside your Confluence home directory. If it is somewhere else on the server, you can specify a path as a String (e.g. new File("/opt/path/to/markdown-files/").
6 This variable points to the individual markdown file itself
7 Basic checks against directory traversal attacks. This checks the path against the canonical path.
Be extremely careful that your endpoint doesn’t let users read arbitrary files from the Confluence server!

Once you have configured the above REST Endpoint (with appropriate changes for your environment), your users can configure the Markdown Macro to point to a URL like https://confluence.mycompany.com/rest/scriptrunner/latest/custom/getMarkdownFile?filename=somefile.md. Just replace confluence.mycompany.com with your Confluence instance’s base URL.

FTP

Alternately, you may want to setup a REST Endpoint that can read files from an FTP, SFTP, or FTPS server.

import com.onresolve.scriptrunner.runner.rest.common.CustomEndpointDelegate
import groovy.transform.BaseScript

import javax.ws.rs.core.MediaType
import javax.ws.rs.core.MultivaluedMap
import javax.ws.rs.core.Response

@BaseScript CustomEndpointDelegate delegate

markdownSftp( (1)
    httpMethod: "GET", (2)
    groups: ["confluence-users"] (3)
) { MultivaluedMap queryParams ->
    def fileName = queryParams.getFirst("filename") as String (4)

    if (!fileName) {
        return Response.status(Response.Status.BAD_REQUEST)
            .type(MediaType.TEXT_PLAIN_TYPE)
            .entity("Must specify a markdown file.").build()
    }

    def sftpRootUrl = "ftp://myftp.host.com/root/allowed" (5)
    def pathToFile = "$sftpRootUrl/$fileName" (6)

    boolean pathIsPermissible = ['../', '$', '*'].every { !pathToFile.contains(it) } (7)
    boolean fileIsMarkdown = [".md", ".markdown", ".mdx"].any {
        extension -> fileName.endsWith(extension) (8)
    }

    if (!pathIsPermissible || !fileIsMarkdown) {
        return Response.status(Response.Status.BAD_REQUEST)
            .type(MediaType.TEXT_PLAIN_TYPE)
            .entity("You may only specify markdown files in the appropriate directory.").build()
    }

    def url = new URL(pathToFile) (9)
    String fileContents
    try {
        fileContents = url.text
    } catch (Exception e) {
        return Response.status(Response.Status.BAD_REQUEST)
            .type(MediaType.TEXT_PLAIN_TYPE)
            .entity("File $fileName does not exist or is unreadable.".toString()).build()
    }
    return Response.status(Response.Status.ACCEPTED)
        .type(MediaType.TEXT_PLAIN_TYPE)
        .entity(fileContents).build()
}
1 The name of the REST endpoint, which forms part of the URL, in this example getMarkdownFile
2 The httpMethod must be GET in order for the endpoint to work with the Markdown macro.
3 This line should be used to restrict use of the REST Endpoint to specific groups of authenticated users. Don’t allow access unless it is definitely needed.
4 Retrieve the filename from the URL’s query parameters
5 The FTP URL points to both the server and subdirectory you wish to access. Change this as appropriate for your environment.
6 Here, we combine the root path to the FTP site with the file name.
7 Basic checks against directory traversal attacks. This checks that the path doesn’t contain characters which might permit users to add malicious attempts to access the FTP server or other URLs.
8 Validate the file type acessed by extension. Here, we are only allowing markdown files.
9 Note that we only create the URL once all validation is passed. This is important, as java.net.URL instances will perform a DNS lookup on creation.

As in the previous example, once you have configured the above REST Endpoint (with appropriate changes for your environment), your users can configure the Markdown Macro to point to a URL like https://confluence.mycompany.com/rest/scriptrunner/latest/custom/markdownSftp?filename=somefile.md. Just replace confluence.mycompany.com with your Confluence instance’s base URL.

Currency Converter Macro

This simple macro converts an amount of money to multiple target currencies based on the last close of day exchange rate. It uses European Central Bank exchange rates.

Previously (v5.2.10 and earlier) Yahoo was used but their service was retired.
The reference rates are usually updated around 16:00 CET on every working day, except on TARGET closing days. They are based on a regular daily concertation procedure between central banks across Europe, which normally takes place at 14:15 CET.

To get the last daily exchange rate just provide a valid amount and select the From and To currencies:

currency converter

Imagine you want to show the photos of group members in a page. This macro will help you achieve this easily. It shows profile photos of group members in tabular format for a group. Simply add the macro to a page, provide the group name, and specify the number of profile pictures you want to see in a row.

mugshot gallery
The macro shows first 200 members of a group returned by confluence API.

Lock Content Macro

Let’s say as a page author you want to add a page status or information and do not want the information to be removed, modified or viewed by specific users or groups. This is not achievable straight away as Confluence does not support the concept of partial restrictions. Lock Content Macro helps you achieve this by restricting users or groups editing or viewing its content. Simply add the user name in Restricted Users or group in Restricted Groups you want to restrict. The user or the users in the group won’t be able to edit the content of the macro. Additionally, if you want to hide the content of the macro from the restricted users, tick the "Hide Content" box. In the following screenshot all the users in the "painters" group and the user "dali" won’t be able to modify the content of the Lock Content Macro.

lock content
The macro does not restrict Confluence administrators and space administrators from editing the contents.
There are few cases where the macro cannot restrict user from updating its contents. For an example using API it is possible to change a page content. It is advisable to use this macro considering this cases.

Have questions? Visit the Atlassian Community to connect, share, and learn with other Atlassian users and experts, including Adaptavist staff.

Ask a question about ScriptRunner for JIRA, Bitbucket Server, or Confluence.

Want to learn more? Check out courses on Adaptavist Learn, an online platform to onboard and train new users for Atlassian solutions.