This page last changed on Mar 05, 2007 by jnolen.
Macros are written and deployed into Confluence as Macro Plugins. This page describes how to write a macro.
The Macro Class
All macros must implement the com.atlassian.renderer.v2.macro.Macro interface. The Javadoc comments are probably the best place to start:
http://www.atlassian.com/software/confluence/docs/api/latest/com/atlassian/renderer/v2/macro/Macro.html
 | The BaseMacro class While it's not a requirement, your macro should extend the com.atlassian.renderer.v2.macro.BaseMacro abstract class. As of Confluence 1.4 this class does not contain any functionality, but if the Macro interface changes in the future, the BaseMacro class will be maintained in order to ensure backwards compatibility with existing macros. |
Writing Your Macro
When writing a macro, you will need to override the following methods:
Method |
Should return... |
hasBody() |
true if this macro expects to have a body, false otherwise |
getBodyRenderMode() |
The RenderMode under which the body should be processed before being passed into the macro |
isInline() |
false if the macro produces a block element (like a paragraph, table or div) true if it is inline and should be incorporated into surrounding paragraphs |
execute() |
a fragment of HTML that is the rendered macro contents |
Understanding RenderMode
The RenderMode tells the Confluence wiki renderer which wiki-conversion rules should be applied to a piece of text. Once again, the best place to start is the Javadoc:
http://www.atlassian.com/software/confluence/docs/api/latest/com/atlassian/renderer/v2/RenderMode.html
There are a number of pre-defined render modes. The ones that would be useful to macro writers are probably:
Mode |
Description |
RenderMode.ALL |
Render everything |
RenderMode.NO_RENDER |
Don't render anything: just return the raw wiki text |
RenderMode.INLINE |
Render things you'd normally find inside a paragraph, like links, text effects and so on |
RenderMode.SIMPLE_TEXT |
Render text made up only of paragraphs, without images or links |
If you want finer control, RenderMode is implemented as a bit-field. Each constant of RenderMode starting with F_ is a feature of the renderer that can be turned on or off. You can construct a RenderMode by manipulating these bits through the following methods:
Method |
Description |
Example |
RenderMode.allow() |
Allow only the renderings specified |
RenderMode.allow(RenderMode.F_LINKS || RenderMode.F_HTMLESCAPE) will only render links, and escape HTML entities. |
RenderMode.suppress() |
Allow all renderings except those specified |
RenderMode.suppress(RenderMode.F_MACROS || RenderMode.F_TEMPLATE) will render everything except macros and emplate variables |
and() |
Perform a logical AND on an existing render mode |
RenderMode.SIMPLE_TEXT.and(RenderMode.suppress(RenderMode.F_PARAGRAPHS)) will render SIMPLE_TEXT without paragraphs |
or() |
Perform a logical OR on an existing render mode |
RenderMode.SIMPLE_TEXT.and(RenderMode.allow(RenderMode.F_MACROS)) will render SIMPLE_TEXT with macros |
 | Many macros (like this note macro) produce a div. Often, if there's only one line of text within a div, you don't want it surrounded in paragraph tags. For this reason, the RenderMode.F_FIRST_PARA flag controls the first line of wiki text that is rendered. If F_FIRST_PARA is not set, and the first line of text is a paragraph, the paragraph tags will not be rendered. |
Accessing the Rest of the System
Like all Confluence plugin modules, Macros are autowired by the Spring framework. To obtain a manager object through which you can interact with Confluence itself, all you need to do is provide a Javabeans-style setter method for that component on your Macro class. See Accessing Confluence Components From Plugin Modules
Advanced Macro Techniques
Macros are often most powerful when combined with other plugin modules. For example, the {livesearch} macro uses an XWork plugin to perform its server-side duties, and the {userlister} plugin uses a listener plugin to listen for login events and determine who is online. You may also consider using a component plugin to share common code or state between macros.
How Macros are Processed
If you want to know exactly what happens when a macro is processed, the following (slightly overly-detailed) description should help:
Consider the following code in a Wiki page:
- The MacroRendererComponent finds the first {mymacro:blah|width=10|height=20} tag, and asks the MacroManager if a macro is currently active with the name "mymacro". The MacroManager returns a singleton instance of your Macro.
- The MacroRendererComponent calls hasBody() on the Macro.
- If hasBody() returns false, the macro is processed with a 'null' body, and the next {mymacro} tag will be processed as a separate macro.
- If hasBody() returns true, the MacroRendererComponent looks for the closing {mymacro}. Anything between the two becomes the macro body.
- If there is a macro body, the MacroRendererComponent then calls getRenderMode() on the macro to determine how that body should be rendered
- The macro body is processed through the wiki renderer with the given RenderMode before being passed to the macro
- The MacroRendererComponent calls execute on the macro, passing in the macro parameters, the (processed) body, and the current RenderMode
- The execute method should return an HTML string. No further wiki processing is performed on macro output.
- The parameters are a Map of {{String}}s, keyed by parameter name.
- If any parameter is not named, it is keyed by the string representation of its position: so for the above example, parameters.get("0") would return "blah".
- parameters.get(Macro.RAW_PARAMS_KEY) will return the raw parameter string, in this case: "blah|width=10|height=20"
- The MacroRendererComponent calls isInline() on the macro to determine if its results should be inserted into the surrounding page as an inline (i.e. part of a surrounding paragraph) or a block element.
Where do you set the renderMode for a Macro? I didn't see it in the explanation above. I would like to set the renderMode to NO_RENDER to have it pass in the raw wiki text body when calling execute(). Is this controlled by some kind of external setting?

Posted by at Jul 12, 2005 17:01
|
You need to override getRenderMode() on your Macro object to return RenderMode.NO_RENDER

Posted by cmiller at Jul 12, 2005 18:25
|
"The MacroRendererComponent calls execute on the macro, passing in the macro parameters, the (processed) body, and the current RenderMode"
Looking at the signature of AbstractHTMLGeneratingMacro, the signature for execute is:
execute(java.io.Writer writer, org.radeox.macro.parameter.MacroParameter macroParameter)
So, where would the body be? (MacroParameter.getContent() ?)

Posted by at Jul 13, 2005 09:05
|
You should no longer use AbstractHTMLGeneratingMacro. It's part of the previous renderer system and is only kept for backwards compatibility.
There is no longer any need to have a special macro type for generating HTML, becuase V2Renderer macros return HTML by default. Just extend com.atlassian.renderer.v2.macro.BaseMacro.

Posted by cmiller at Jul 13, 2005 19:05
|
The link in the first section "The Macro Class" gives a 500 internal server error.
here's something around there: http://www.atlassian.com/software/confluence/docs/api/latest/com/atlassian/confluence/renderer/package-summary.html

Posted by at Jul 14, 2005 08:20
|
The link in "Understanding RenderMode" also gives a 500 error. If these classes were renamed it should link to the old api not the /latest/

Posted by at Jul 14, 2005 08:22
|
Our apologies, it was an error in our javadoc generation that did not build the com.atlassian.renderer package. This will be fixed shortly.

Posted by daniel@atlassian.com at Jul 14, 2005 19:25
|
The documentation has been fixed now. Sorry for the inconvenience.

Posted by cmiller at Jul 14, 2005 20:16
|
Fixed now. 

Posted by cmiller at Jul 14, 2005 20:17
|
What jar file is com.atlassian.renderer.v2.macro.Macro class in. I am attempting to write my first macro, yet I missed the information that told me the name of the jar file. I am writing my macro in netbeans first, then I'll move it over.

Posted by at Jul 27, 2005 15:03
|
It's under WEB-INF/classes in the Confluence web-app.
You might want to make a jar of the classes directory yourself for convenience, I guess.

Posted by cmiller at Jul 27, 2005 19:52
|
How do you get a macro to be called from any velocity file?
$action.getGlobalHelpre.renderConfluenceMacro(" Unknown macro: {mymacro} ")
This works everywhere but not in the main.vmd or the header and footer vm files?

Posted by ufmemo at Feb 14, 2006 11:47
|
I solve that by using my own WikiStyleRenderer in a VelocityContextItem

Posted by dhardiker@adaptavist.com at Mar 03, 2006 10:18
|
|