

Creating Custom Cloud Macros 101
Can’t find a macro you need on Confluence Cloud? Join Bobby and Jessie to learn the easiest way to create your own, using ScriptRunner.

Available on-demand
If there's a macro you're craving that doesn't exist in Confluence Cloud: you're in the right place.
In this session, you'll get a walkthrough of how to create your own custom macros, using ScriptRunner.
Get a guided look at:
- What the Custom Macro feature on ScriptRunner is and how you can use it.
- Live scripting and a guided walk-through to build 2-3 useful macros from real customer use cases.
- A copy of the macro scripts so you can use them on your own instance.
Watch this can't-miss session now!
Please update your cookie preferences below to view this content.
Who's leading this 101?

Jessie Wang
Senior Product Marketing Manager

Bobby Bailey
Senior Customer Success Manager, ScriptRunner
Bobby is a Senior Customer Success Manager and a ScriptRunner extraordinaire!
Resources
Here are all the resources mentioned in the demo.

Script library
Discover even more ready-to-use scripts in our script library.
User Data: Provides the User ID and Email Address of the user selected in the parameters.
def userAccountId = parameters['User'] as String
def userDataURI = "/wiki/rest/api/user?accountId=${userAccountId}"
def userData = get(userDataURI)
.header("Accept", "application/json")
.asObject(Map).body
def macroOutput = ("<p><strong>Data for : </strong>${userData.publicName}</p>"
+ "<p><strong>User ID : </strong>${userData.accountId}</p>"
+ "<p><strong>Email Address : </strong>${userData.email}</p>")
macroOutput = ('<ac:structured-macro ac:name="info" ac:schema-version="1" ac:macro-id="59b88725-71ee-4e2a-903f-7860cdbac9a9"><ac:rich-text-body>'
+ macroOutput +
'</ac:rich-text-body></ac:structured-macro>')
return macroOutput
Label Report: Outputs a table with a count of the specified labels in an instance.
def label = parameters.Label
def labelDataURI = "/wiki/rest/api/label?name=${label}"
def labelData = get(labelDataURI)
.header("Accept", "application/json")
.asObject(Map).body
def labelId = labelData['label']['id']
def pageDataURI = "/wiki/api/v2/labels/${labelId}/pages"
def pageData = get(pageDataURI)
.header("Accept", "application/json")
.asObject(Map).body
def numberOfPages = 0
def macroOutput = ('<table data-layout=\"default\" ac:local-id=\"57400373-37bd-4d1a-a65d-c1385048e5e9\"><colgroup><col style=\"\" />'
+'</h3><table data-layout=\"default\" ac:local-id=\"57400373-37bd-4d1a-a65d-c1385048e5e9\"><colgroup><col style=\"\" />'
+ '<col style=\"\" /><col style=\"\" /></colgroup>'
+ '<tbody><tr><th><p><strong>Page Title</strong></p></th><th><p><strong>Space</strong></p></th><th><p><strong>Owner</strong></p></th></tr>')
pageData.results.each{ curPage ->
def ownerId = curPage['ownerId'] as String
def spaceId = curPage['spaceId']
logger.info(ownerId)
def spaceInfoUri = "/wiki/api/v2/spaces/${spaceId}"
def spaceInfo = get(spaceInfoUri)
.header("Accept", "application/json")
.asObject(Map).body
def userInfoUri = "/wiki/rest/api/user?accountId=${ownerId}"
def userInfo = get(userInfoUri)
.header("Accept", "application/json")
.asObject(Map).body
macroOutput = macroOutput + '<tr>'
macroOutput = macroOutput + ("<td><ac:link><ri:page ri:content-title='${curPage['title']}'/>${curPage['title']}</ac:link></td>"
+"<td>${spaceInfo['name']}</td>"
+"<td>${userInfo['publicName']}</td>")
macroOutput = macroOutput + '</tr>'
++numberOfPages
}
macroOutput = (macroOutput + '</tbody></table>')
macroOutput = ("<p>Number of Pages: ${numberOfPages}</p>" + macroOutput)
macroOutput = ("<p>Label report for label : ${parameters.Label}</p>" + macroOutput)
return macroOutput
Page Info V2: Outputs metadata of a page (e.g. Title, Created Date, Status, Owner), with the option to specify which page the metadata comes from.
import java.text.SimpleDateFormat;
import java.text.DateFormat;
def pageOption = parameters['Page'] as String
def informationType = parameters['Information Type'] as String
def curPageId = parameters.pageId as String
def pageToReadId = ''
boolean directId = false
def macroOutput = ''
logger.info(pageOption)
logger.info(informationType)
logger.info(curPageId)
// Check for ID
if(pageOption.isNumber()){
pageToReadId = pageOption
directId = true
} else {
// Check for other options
switch(pageOption){
case '$parent':
def curPageUri = "/wiki/api/v2/pages/$curPageId"
def curPageInfo = get("$curPageUri")
.header('Content-Type', 'application/json')
.asObject(Map).body
pageToReadId = curPageInfo['parentId']
break;
default:
macroOutput = 'Page option not recognised'
break;
}
}
logger.info(pageToReadId)
def pageUri = "/wiki/api/v2/pages/$pageToReadId"
def pageInfo = get("$pageUri")
.header('Content-Type', 'application/json')
.asObject(Map).body
switch(informationType){
case 'Title':
macroOutput = pageInfo.title as String
break;
case 'Created Date':
def date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").parse(pageInfo.createdAt as String)
def changed = new SimpleDateFormat("dd/MM/yyyy").format(date)
macroOutput = changed
break;
case 'Status':
macroOutput = pageInfo['status'] as String
break;
case 'Owner':
def ownerId = pageInfo['ownerId']
def ownerUri = "/wiki/rest/api/user?accountId=${ownerId}"
def ownerInfo = get("$ownerUri")
.header('Content-Type', 'application/json')
.asObject(Map).body
macroOutput = "<ac:link><ri:user ri:account-id='${ownerInfo.accountId}'/>${ownerInfo.publicName}</ac:link>"
break;
default:
macroOutput = "Information Type option not recognised"
break;
}
return """
<p> ${macroOutput} </p>
"""