Confluence User Macros: An Introduction

24 september 2020
Confluence User Macros can help you out in lots of ways. In this blog post we will guide you through the process of creating and adding Confluence User Macros to your Confluence.

Confluence User Macros are basically macros that Confluence admins can create themselves without the need of buying, installing or even engineering an app. They can have several use cases, but since it is a macro, they always provide the user with some type of functionality that can be added to a Confluence page.

Here are a few examples:

  • Change the format and/or layout of a Confluence page. E.g., hide the comments section.
  • Display Confluence data, like the reader’s username or a page’s current version information.
  • Perform actions from a page, such as adding a button to save the page as PDF.
  • Provide the ability to frame an external website on a Confluence page without having to enable the HTML macro.
  • Display media, like embedding a Microsoft Streams video on a Confluence page.

As you can imagine, there are lots of ways Confluence User Macros can help you out. Did any of the examples listed above pique your interest? If yes, then you’re in luck, because in this blog post we will guide you through the process of creating and adding all of these to your Confluence instance.

Do note that User Macros are only available on Confluence Server and Data Center and unfortunately not on the Cloud version of Confluence.


Managing Confluence User Macros

We already mentioned this, but it is good to know that only users with Confluence administrative permissions are able to create and manage Confluence User Macros.

From Confluence’s General configuration navigate to User Macros:

Here you can add new and manage existing User Macros. By default, Confluence comes without User Macros.

Creating a User Macro

Creating a User Macro can be pretty simple, depending on what you want to achieve, but you can not do it without writing a few lines of codes. You do not need to be a software engineer though. Some basic knowledge of HTML and CSS will definitely help and if you know a bit of Java that’s even better. Don’t be discouraged if you don’t. Try to keep on reading and if you do not get it, you can always just copy-paste the examples discussed here  . Of course you can also always contact TMC ALM if you need professional assistance or guidance.

To create a new User Macro, navigate to the User Macros page and hit the Create a User Macro link. You will be presented with a screen that basically consists of 2 parts:

  1. The metadata for the User Macro. Think of the name, title, description, category, etc, which are used by Confluence’s macro Browser. The following image should be self-explanatory.
  1. The Definition of the User Macro. This is where you define what the macro should do and if users can put text or other macros inside the macro. If so, the macro should have a so-called body. You can choose how this body should be processed. Furthermore, you have to define the macro Template, which is the code that gets executed to provide the macro with functionality.


The Macro Body

A macro without a body (no macro body) looks like this when editing a page:

And a macro with a body looks like this, you can enter text inside the macro which will be processed by the macro. This text will be available to the macro in the $body variable.

Options for processing the macro body include:

  • No macro body
    Select this option if your macro does not have a body.
  • Escaped
    Confluence will add escape characters to the HTML markup in the macro body. Use this if you want to show actual HTML markup in the rendered page. For example, if the body is <b>Hello World</b> it will render as “<b>Hello World</b>” instead of Hello World.
  • Unrendered
    HTML in the body will be processed within the template before being output. Ensure that HTML is ultimately output by the template.
  • Rendered
    Confluence will recognize HTML in the macro body, and render it appropriately. For example, if the body is <b>Hello World</b> it will render as Hello World.


The Macro Template

The Template is basically the code that determines what the macro should do.

  • The template should output HTML and Confluence-specific XML elements.
  • You can make use of the Velocity templating language. This allows you to add logic to the code.
  • If the macro has a body, your template can refer to the macro body text by specifying ‘$body‘. E.g. if the user using the macro enters “Hello World” in the body and the Template is as follows:
    <b>$body</b>
    The macro will print Hello World on the location where the macro was inserted in the Confluence page.
  • You can use parameters. These are basically the options users can set when they edit the macro. The parameter creates a variable, like ‘$body‘ that can be used in the Template. Use @param to define metadata for your macro parameters. E.g. if the Template were as follows:
    @param Name:title=Name|type=string|required=true|desc=Your name
    Hello, <b>$paramName</b>!

    In edit mode the macro would like this:

    When inserted in a page in edit mode it will look like:

    And when a user views the page, the macro will print “Hello, Rick!” on the location where the macro was inserted in the Confluence page.
  • When using the information passed using parameters, refer to your parameters as $paramXXX where ‘XXX‘ is the parameter name that you specified in the @param metadata definition. E.g. like the previous example where the parameter Name was added and in the template we referred to it as @paramName.
  • If you do not have any macro parameters, you must indicate this in the Template by adding @noparams before any other code.

See User Macro Template Syntax for more information. Let’s write some example macros!

Examples

Hide the comments section on a page

With this User Macro, we can hide the comments section on the page where it is inserted in. When hiding the comments section on a page, you hide the existing comments and the ability to create new comments for visitors on that page. It is hidden and not removed nor deleted, because when you remove the macro again, everything will be back as it was, including any already existing comments.
This can be really useful for landing pages in spaces where comments are allowed for example.

This is a macro without any parameters, so we must at @noparams before any other code.

Next, we need to hide the comments section. This is done by adding the following bit of JavaScript:

<script type="text/javascript">
AJS.toInit(function(){ AJS.$('#comments-section').hide(); }); </script>

This basically tells the visitors browser to not show the HTML that is marked with #comments-section.

To learn more about using JavaScript in Confluence, you can start with this Atlassian Knowledgebase article: How to use JavaScript in Confluence

That is all there is to it, so the Template should look as follows:

## @noparams
<script type="text/javascript">
AJS.toInit(function(){ AJS.$('#comments-section').hide(); });
</script>

Name the macro hidecomments, give it the title Hide Comments and make sure to select No macro body for the macro Body Processing option.

To add the macro to a page, open the macro browser or start typing {hide when editing the page to select the Hide Comments macro. Save the page and gone is the comments section.

Show the current version information on a page

In this example, we retrieve some page information with a User Macro and display the version information on a page when the User Macro gets inserted in a page. The version information consists of the current version and the last modification date.

Again this is a macro without a body, so start the Template with @noparams.

To display the date correctly, we need to retrieve a Java function, this is done with the following code:

#set($dateFormatter = $action.getDateFormatter())

Next, we need to retrieve the latest version number and its modification date. You can do this by referring to the $content object that Confluence provides. For readers experienced with Java, check out Confluence’s JavaDoc for more information.

So $content.version gives is the current version number and $content.lastModificationDate gives us the last modification date. Do note that we need to previously create dateFormatter to display the date correctly. So we must use it as follows: $dateFormatter.format($content.lastModificationDate).

When putting it all together, the Template should look as follows:

## @noparams
#set($dateFormatter = $action.getDateFormatter())
This is version <b>$content.version</b> and was saved on <b>$dateFormatter.format($content.lastModificationDate)</b>.

When adding this User Macro to a page, it will output “This is version 1 and was saved on Sep 18, 2020.”

Show the visitor’s username

Very similar to the above, we can create a User Macro to display the visitor’s username (actually Confluence login name). If you have user authentication set up via an Active Directory, this can be really useful when writing work instructions and such.

The only thing we would have to do is retrieve the user’s login name from the $action object. Use $action.authenticatedUser.name to retrieve the user’s login name.

The Template should look like this:

## @noparams
$action.authenticatedUser.name

Name the User Macro username for example and you can do the following:

Resulting in the following line to be displayed on the Confluence page:

“Your Documents folder is located at C:\Users\Rick.van.Twillert\Documents”

Now users can just copy-paste the line of text without having to replace some placeholder with their username.

Download this page as PDF

This is also a very simple macro which allows to add a button on a page to download it in PDF format.

To download a page we need to follow this link: <CONFLUENCE_BASE_URL>/spaces/flyingpdf/pdfpageexport.action?pageId=<PAGE_ID>

As we are already on Confluence, we do not need the base URL (e.g. https://confluence.yourcompany.com). We do need the current page id, which we can retrieve with the $content object which we already used in one of the examples above. To get the page id of the current page, use $content.getIdAsString().

Now, all we need is a button that opens this link when the user clicks it. This is just plain HTML, but we add some JavaScript to open the link and we use some styling to align with Atlassian’s guidelines. This results in the following Template code:

## @noparams
<input type="button" onclick="location.href='/spaces/flyingpdf/pdfpageexport.action?pageId=$content.getIdAsString()';" class="aui-button aui-button-primary" value="Download as PDF" style="display: inline;" />

The button on the page looks like this, though the colour depends on the global or space colour scheme you are using.

Frame an external website on a Confluence page

A common question we get asked is if it’s possible to display another website on a Confluence page. This is definitely possible if that website allows framing. Most websites do NOT allow this these days! To make this available to all Confluence users, you can enable the HTML macro so they can use the so-called iframe HTML tag. The downside of this is that you expose Confluence to all sorts of code injection. So a better alternative is to create a User Macro that gives the user a friendly way of framing a website on a Confluence page.

Here’s how to do it! We basically need to ask the user three things:

  1. What is the URL of the website they want to frame?
  2. What should be the frame width?
  3. What should be the frame height?

This we do with User Macro parameters. Next, we need to use the data provided with the parameters to create an HTML frame. We will also add some code that the maximum width of the frame is that fo what the user set using the parameters, but that the frame scales depending on the size of the website. This all sounds complicated, but it’s just some basic HTML and CSS.

The resulting Template code is as follows:

## @param Url:title=URL|type=string|required=true|desc=URL of website you want to frame
## @param Width:title=Frame width|type=int|required=true|desc=Width of the frame in which the website is shown
## @param Height:title=Frame height|type=int|required=true|desc=Height of the frame in which the website is shown
<div style='max-width: ${paramWidth}px'>
    <div style='position: relative; padding-bottom: 50%; height: 0; overflow: hidden;'>
       <iframe width="${paramWidth}" height="${paramHeigth}" src="${paramUrl}" style="border:none; position: absolute; top: 0; left: 0; right: 0; bottom: 0; height: 100%; max-width: 100%;"></iframe>
     </div>
</div>

Embed a Microsoft Streams video on a Confluence page

Last, but not least, we will show you how to create a User Macro for embedding a Microsoft Stream video on a Confluence page. This example is a bit more advanced as we will apply some Velocity logic in the User Macro.

Let’s begin! First, we will provide the same options as Microsoft does to user’s who want to embed Streams videos. But, as we don’t know the video resolution, we will let the user choose between the most common aspect ratios used on the web instead of letting them define the width and height. To do this we create an aspect ratio parameter of type enum. This creates a dropdown with a limited set of options we can define ourselves.

By default, we set the video to 360p, which means 640×360 pixels. But if the user chooses another aspect ratio, we have to set a different width and height for the video. This we do with a so-called if-else statement:

## Set video width and height based on selected aspect ratio
#if( $paramSize == "1080p" )
    #set( $width = "1920px" ) #set( $height = "1080px" )
#elseif( $paramSize == "720p" )
    #set( $width = "1280px" ) #set( $height = "720px" )
#elseif( $paramSize == "480p" )
    #set( $width = "8540px" ) #set( $height = "480px" )
#elseif( $paramSize == "240p" )
    #set( $width = "426px" ) #set( $height = "240px" )
#elseif( $paramSize == "160p" )
    #set( $width = "184px" ) #set( $height = "160px" )
#else
    #set( $width = "640px" ) #set( $height = "360px" )
#end

What this does is check if the user selected the 1080p aspect ratio. If it did, the width and height are set to the corresponding pixels. If not, it does the same for all other options. With the last else, we make sure that it always defaults to the 360p aspect ratio.

Next, we need to get the Microsoft Stream video id from the URL provided by the user. Since the URL for embedding videos is different from the one for sharing video’s we do not want to rely on the user providing the correct URL. With the following code, we split the URL into several parts and add them to a list. We set the separator to the forward-slash character (“/“). This means that for example, the URL https://web.microsoftstream.com/video/123456789 gets split into the following parts:

  1. https:
  2. web.microsoftstream.com
  3. video
  4. 123456789 ← Streams video ID!

We need the last part of the Microsoft Streams URL, which is the video id. In the example, part 4. To do this, you normally would take the last part of the list, but unfortunately, this doesn’t work correctly so we need to loop through the list until we get the last item. If you start using Velocity logic for User Macros you will encounter more of these caveats for which you need to develop smart workarounds. This can be fun but also frustrating .

The code looks as follows:

## Get video id from given video url
#set( $parts = $paramUrl.split("/") )
#foreach( $part in $parts )
    #set( $videoId = $part )
#end

Last, we need to display the embedded video according to the aspect ratio the user chose. But, if the user also enabled the Responsive option, we need different HTML. We use another if-else statement for this and we can reuse some of the HTML we used in the previous example with the iframe. The final Template code looks as follows:

## @param Url:title=Stream URL|type=string|required=true|desc=URL to the Microsoft Stream video that needs to be embedded on this Confluence page
## @param Size:title=Aspect ratio|type=enum|enumValues=1080p,720p,480p,360p,240p,160p|default=360p|desc=Sets the aspect ratio (size) of the embedded Microsoft Stream on the Confluence page.
## @param Autoplay:title=Autoplay|type=boolean|required=false|desc=Check to start playing the video automatically
## @param Responsive:title=Responsive|type=boolean|required=false|desc=Check to scale the video to fit the browser window size
## @param Showinfo:title=Show info|type=boolean|required=false|default=true|Uncheck to hide controls and video information

## Set video width and height based on selected aspect ratio
#if( $paramSize == "1080p" )
    #set( $width = "1920px" ) #set( $height = "1080px" )
#elseif( $paramSize == "720p" )
    #set( $width = "1280px" ) #set( $height = "720px" )
#elseif( $paramSize == "480p" )
    #set( $width = "8540px" ) #set( $height = "480px" )
#elseif( $paramSize == "240p" )
    #set( $width = "426px" ) #set( $height = "240px" )
#elseif( $paramSize == "160p" )
    #set( $width = "184px" ) #set( $height = "160px" )
#else
    #set( $width = "640px" ) #set( $height = "360px" )
#end

## Get video id from given video url
#set( $parts = $paramUrl.split("/") )
#foreach( $part in $parts )
    #set( $videoId = $part )
#end

#if ( $paramResponsive )
    <div style='max-width: ${width}px'><div style='position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;'>
        <iframe width="${width}" height="${height}" src="https://web.microsoftstream.com/embed/video/${videoId}?autoplay=${paramAutoplay}&amp;showinfo=${paramShowinfo}" allowfullscreen style="border:none; position: absolute; top: 0; left: 0; right: 0; bottom: 0; height: 100%; max-width: 100%;"></iframe>
    </div></div>
#else
    <iframe width="${width}" height="${height}" src="https://web.microsoftstream.com/embed/video/${videoId}?autoplay=${paramAutoplay}&amp;showinfo=${paramShowinfo}" allowfullscreen style="border:none;"></iframe>
#end

Make sure you provide a nice macro title for this macro, something like Embed Microsoft Stream and select the macro categories Media and External Content. The result is as follows:


More information

We hope this blog post gave you some basic understanding of what can be achieved with User Macros. Listed below are a few sources to fall back on when writing your own User Macros:

  • There’s the Atlassian Knowledgebase which has a few good articles to get you started:
  • Also, the Atlassian Community is a great place to ask for help if you get stuck or need some directions. It’s a very active, friendly and helpful community.
  • User Macros are written in a templating language called Velocity which is an Apache project. Apache published a really useful user guide here.
  • If you want to keep in line with Confluence’s design guidelines and/or use Confluence style buttons, icons, etc. in your User Macro, visit Atlassian’s AUI documentation.
  • JavaScript can be used in a User Macro, but you will have to use Atlassian’s JavaScript library. Read all about it in this knowledge base article: How to use JavaScript in Confluence
  • When you are ready to dive a little deeper into developing User Macros for Confluence, Atlassian has a site dedicated to developers where you can find in-depth details about almost everything related to Confluence: The Confluence Server Developer site.

One last, but important, piece of advice; please test your User Macro on a non-production environment first! Malfunctioning User Macro code can have a big impact on the availability of Confluence.

TMC can help you out as well!

Over the years we’ve written dozens of User Macros to create all sorts of functionality. Maybe we already have something lying around that can do exactly what you are looking for. Do not hesitate to get in touch! We can help you out with anything Atlassian related!

Good luck and thanks for reading!