Saturday, October 14, 2017

Build an user macro in Confluence

As a tech writer, I often find myself using a good collection of Confluence macros in my documentation. So of the most commonly used macros are the table of contents, style, Jira issues, align, attachment, children display, html, and live search. There's actually dozens of macros that I utilize on a regular basis but I commonly use these in some in some combination with each other. For example, I use the table of contents macro nested within the align macro so I can avoid the dreaded long list of anchor links at the top of a document and have the table of contents listed in a floating (left) element so the content of the page can flow around it.

Wouldn't it be nice to combine some of these macros into one "uber macro"? In this tutorial, I'll walk you through the process to do just that: create a table of contents nested within a float left element.

User macro anatomy

Writing an user macro isn't for everyone. It requires some basic knowledge of how HTML and Confluence XML macros works. Plus any skills with CSS, JavaScript, jQuery, and Velocity would be to your benefit to create more advanced user macros. If you don't feel comfortable getting started with this tutorial, you can check out Guide to User Macro Templates by Atlassian for a good overview of what user macros templates are and how to write a basic user macro.

The typical anatomy of an user macro in Confluence looks something like this:
  • Macro settings
  • Macro definition
    • In-macro comments
    • Declaration of parameter usage (or not)
    • Template (macro code)
  • Detailed documentation (external to the macro)

Macro settings

Every macro must declare the following items:
  • Macro Name
  • Macro Title
These parameters of the user macro are required and the Macro Name should be all lowercase and unique. The Macro Title should reflect the Macro Name but you should be write it so it's human readable as this is the name of the macro that will appear in the macro browser.

The following macro settings are optional but highly encouraged to be filled out:
  • Visibility
    • You must decide if this macro will be available to everyone or to just the system admins
  • Description
    • This is the description of the macro that your users will see when selecting this macro from the macro browser
  • Categories
    • The macro browser can be sorted by different categories of macro. Choose yours accordingly. 
  • Icon URL
    • If an icon is not defined, Confluence will assign it a generic icon. For faster load times, the macro should be internal to your system. Historically, an icon can be hosted somewhere within the server as long as it is reachable by the standard user. Or, it could be hosted on a wiki page as an attachment.
  • Documentation URL
    • To say the least, this macro should be documented somewhere on the wiki

Macro definition

The Definition of User Macro is the section of the user macro that allows you to define the Macro Body Processing and it's Template.

Macro Body Processing options

The Macro Body Processing allows you to instruct Confluence how the macro should process the body before passing it your macro. The macro body is what gets display on the Confluence page, if the macro has a body that is.

Option for processing includes:
  • No macro body
    • Use this option if your macro does not utilize a body
  • Escaped
    • Use this option to render the contents of the body as HTML markup
  • Unrendered
    • HTML content will be processed before being rendered
  • Rendered
    • HTML content is rendered but doesn't guarantee that it will be rendered before the page finishes rendering. 
For our macro, we'll set this parameter to No macro body as we won't be utilizing any body elements.

Template

The Template is where you provide you macro with the code that it will utilize to render the desired results. You can use a combination of HTML and Confluence-specific XML elements, and Velocity.

In-macro comments

As an good code is well commented, user macros should utilize comments as well. To add a comment, you must use the #* comment *# format.

While it's not 100% necessary, it is helpful to include the two comments at the top of your macro: one sentence description (if the description is not filled out) and a link to it's documentation (if no link is provided for the Documentation URL). For the macro we will build in this tutorial, our comment should look something like this:

#* floatLeftToC is a left floating table of content *#
#* for more info, see <tiny url> *#

Parameters

Parameters allows the users to pass in options that the macro may use. Discussing the different options and features of macro parameters can be a whole tutorial in itself.

For the macro we are going to build, it won't take any options or parameters. Therefore, the parameter line in our code should look something like this:

## @noparams desc=Creates a left aligned floating table of contents

Code

For this macro, we want to wrap whatever content into a div element so we can float the element and it's content to the left of the page. To accomplish these, we can do the following:

<div style="float: left; margin: 0 10px 10px 0; padding: 5px 10px 5px 0; min-width: 50px; max-width: 250px; border: solid thin black; border-radius: 5px;">
  <ac:structured-macro ac:name="toc"/>
</div>

Ideally, the CSS would not be inline to the element we are using to wrap around the ToC. In my experience, the CSS should be a globally declared rule that Confluence makes available throughout your wiki.
Note: the border attributes in the style attribute is complete superficial. I like to use this when developing and/or debugging user macros that act as a container.

The nested structured-macro handles the table of contents thanks to a native Confluence macro.

Documentation

The documentation should include the following information:
  • Purpose
  • Usage
    • Intent
    • Parameters
  • Change history
  • Creator info
    • Name, email, etc.
  • Known issues and/or limitations
  • Links to related content
  • Attachment of the code
The purpose of the macro should be clearly defined with it's intent. One doesn't need to justify it's existence (as one assumes this macro was created out of a request or to fulfill an existing need).

The usage section should plainly define it's intended usage, and the parameters (if any) are used, and provide a few examples of the macro in use.

Depending on your work environment, you can include the change history of this macro (if you're not using Github or similar services). The same is true with the creator info and attachments of the code sections.

Depending on it's usage, you may want to include a section about known issues or limitations. While the macro should be designed to work with everything in the Confluence environment, it may not due to a multitude of reasons. Sometimes, the most innocent macro make work perfectly by itself but when combined with another, it may break the other itself or other macros. One recent example that comes to mind is a macro that wrapped a div element around an existing Confluence macro that changes the appearance of it via CSS. While this macro was written to only affect that one macro wrapped within it, it caused several issues with the non-wrapped macro on the same page.

Gotchas

It should be noted that if you have a typo anywhere in your macro's template, it will either fail to render the macro, display an error message, or at the worse, cause the page where the user macro is utilized to fail to render completely.
As a happy accident, while I was exploring which macro I should create, I failed to include the closing parentheses for some CSS rules. This caused the macro to not render and Confluence didn't inform me of anything wrong with the macro.