Overview

This tutorial shows you how to write a simple macro for Confluence. It prints a greeting message on the page using a parameter supplied by the user.

Your completed plugin will consist of the following components:

Java classes encapsulating the plugin logic.

Resources for display of the plugin UI.

A plugin descriptor (XML file) to enable the plugin module in the Atlassian application.

When you are done, all these components will be packaged in a single JAR file.

About Pre-4.0 Macros

This tutorial shows how to build a pre-4.0 macro plugin. There are many differences between 3.x and 4.x plugins. A difference particularly relevant to macro development is the introduction of the XHTML-based output format. For a tutorial on creating 4.x version plugins, see Creating a New Confluence Macro.

Also, this plugin happens to work in both 3.x and 4.x versions of Confluence. Some legacy 3.x plugins will need to be modified in order to work in 4.x. For more information, see Plugin Development Upgrade FAQ for 4.0.

Prerequisite Knowledge

To complete this tutorial, you must already understand the basics of Java development: classes, interfaces, methods, how to use the compiler, and so on. You should also understand:

Atlassian's security standards. Since all plugin code is essentially injected into the Confluence executable, we recommend that you review an AtlasCamp 2010 presentation Securing your plugin before proceeding. In addition, have a look at Enabling XSS Protection in Plugins, as it has been updated since AtlasCamp 2010.

Plugin Source

We encourage you to work through this tutorial. If you want to skip ahead or check your work when you are done, you can find the plugin source code on Atlassian Bitbucket. Bitbucket serves a public Git repository containing the tutorial's code. To clone the repository, issue the following command:

You can use any supported combination of OS and IDE to construct this plugin. These instructions were written using Eclipse Indigo on Windows 7. If you are using another combination, you should use the equivalent operations for your specific environment.

Step 1. Create the plugin project

In this step, you'll use two atlas- commands to generate stub code for your plugin and set up the stub code as an Eclipse project. The atlas- commands are part of the Atlassian Plugin SDK, and automate much of the work of plugin development for you.

Open a terminal and navigate to your Eclipse workspace directory.

Enter the following command to create a Confluence plugin skeleton:atlas-create-confluence-pluginWhen prompted, enter the following information to identify your plugin:

Filter for Existing Projects into Workspace (or expand the General folder tree).

Press Next and enter the root directory of your workspace.Your Atlassian plugin folder should appear under Projects.

Select your plugin and click Finish.

Eclipse imports your project.

Step 2. Review and tweak the generated stub code

It's a good idea to familiarise yourself with the initial project files. In this section, we'll modify information about your plugin and check the product version value. Open your plugin project in Eclipse and follow along with the next sections.

Add plugin metadata to the POM

The POM (Project Object Model definition file) is located at the root of your project and declares the project dependencies and other information.

Add some metadata about your plugin.

Edit the pom.xml file in the root folder of your plugin.

Add a description of your macro to the following <description> element:

Save the file.

Verify your Confluence version

When you generated the stub files, a default Confluence version was included in your pom.xml file. Take a moment and examine the Confluence dependency:

Open the pom.xml file.

Scroll to the bottom of the file.

Find the <properties> element. This section lists the version of Confluence you are using. It should look something like this:

Step 4. Write Java classes

You have already generated the stubs for your plugin modules. Now you will write some code that will make your plugin do something. In this example, we are writing a simple Confluence macro that greets the user by name and displays the total number of spaces in the instance, along with the home page title and creator of a randomly chosen space. Once you're comfortable with the process, you can use the same steps to create a macro to do pretty much anything.

Update imports

Before we implement the macro logic, we'll make some preliminary changes to the generated ExampleMacro class.

In Eclipse or from the command line, open the ExampleMacro.java file at the following directory path: src/main/java/com/example/plugins/tutorial/confluence/TutorialMacro.

Replace the existing import statements with the following. (You may need to expand the import statements if they are collapsed in your source view.)

Build a skeleton to extend BaseMacro

A macro is a Java class that implements the com.atlassian.renderer.v2.macro.Macro interface, which lives in a renderer module shared between Atlassian applications. To simplify our task, we'll extend a convenience class that Confluence provides for macro authors, BaseMacro.

Replace the existing ExampleMacro class body with the following skeleton:

Here we see all four methods that a macro is required to implement:

isInline() tells the macro how to display. If the macro output should stand apart from surrounding text (like, for example, the built-in {info} macro), this should return false. If the output should be interleaved into the surrounding text (like a HTML <a> tag, because usually occurs within some other tag like <p> or <div>), this should return true. Since the example macro output will be a block of standalone HTML, return false.

hasBody() is a Boolean value that indicates whether the macro returns body text.

getBodyRenderMode() specifies what rendering, if any, should be applied to the text returned from execute(). If a macro generates wiki markup, returning RenderMode.ALL or RenderMode.INLINE would be appropriate. Since this example macro will render directly to HTML through a Velocity template, you don't want the renderer to mess with it. Return RenderMode.NO_RENDER.

execute() is the crucial method in BaseMacro. It returns a String, which is just the macro-generated text. In short, this is where you tell your macro what to do.

Create a Velocity template

Like good software engineers, we'll separate our controller from our view and delegate the HTML rendering to a Velocity template, passing a model object from the former to the latter. In src/main/resources, create a new folder named templates and save the following Velocity template as tutorial-macro.vm:

The tokens that start with $ are Velocity references. They correspond to the objects inserted into the context by ExampleMacro. At render time, Velocity replaces these references with the corresponding values.

Be careful of context parameter names

By default, Velocity doesn't distinguish between references in the context that have no value and those that don't exist at all. If your rendered template has stuff like $myVariable, make sure you've spelled the reference exactly the same in both the template and the macro class.

Now that you have defined your template, return to your ExampleMacro class skeleton and at the top, where you had previously omitted the constructor and instance variables, direct the macro to reference your Velocity template:

Now refresh the project in Eclipse.

Tell the macro what to do

Now comes the fun part. At render time, macros are invoked through the execute() method with the following parameters:

params is a Map of macro parameter names and values.

body contains the text inside the macro opening and closing tags. Macros that don't have a body (like ours) don't require closing tags, so this parameter will be null in such cases.

context is the RenderContext object which supplies information about the renderer being used to render the current macro. Some macros can take advantage of this information, but this one will not need to.

In summary, you want to create a Velocity context and place several things into it, including the value of the greeting parameter (you make one yourself if none is supplied), the total number of spaces in the installation, a randomly chosen space, and the title and creator of that space's home page. The return statement gets the rendered Velocity template and sends it back. Since our implementation of getBodyRenderMode() returns RenderMode.NO_RENDER, the HTML from Velocity will be inserted into the page with no further processing.

To make this happen, go to your ExampleMacro class and replace the skeleton execute() method with the following:

Step 5. Register the Plugin Module in the Plugin Descriptor

In pre-4.0 Confluence, the generated Notation Guide provided information on macros, including user-created macros. We can document our macro in the Notation Guide, again using a Velocity template.

Go to src/main/resources/templates and create a new file named tutorial-macro-help.vm with this content:

After you create the template, refresh the package in Eclipse.

Now that the macro class is written, you need to register the plugin module in the plugin descriptor, atlassian-plugin.xml, which is located at src/main/resources.

The Atlassian plugin descriptor file describes your plugin to Confluence. Use the <plugin-info> element to add a description and version number for your macro. Also add a <macro> element that identifies the macro along with the parameters it takes.

When you're done, atlassian-plugin.xml should look like this:

Step 6. Build, install and run the plugin

That's it! Now you can test your code. Open a command window and go to the plugin root folder (where the pom.xml is located). Enter the atlas-run command. When it's done, you should see something like the following:

To use the macro:

In your favorite browser, open this Confluence instance and log in as admin (the password is also admin).

Add a page or edit an existing one.

In the edit screen, click the Edit/Insert macro icon.

Scroll down and select the macro named Macro tutorial.

Optionally, type a greeting in the greeting field and click Insert.Alternatively, add this to the page in the edit screen: