Last updated Apr 2, 2024

Confluence Macro Manager

Available:

Confluence 4.0 and later

Introduction

In Confluence 4.0 macros are accessed via a new com.atlassian.confluence.macro.xhtml.MacroManager interface.

1
2
public interface MacroManager
{
    Macro getMacroByName(String macroName);
    void registerMacro(String name, Macro macro);
    void unregisterMacro(String name);
    LazyReference<Macro> createLazyMacroReference(final ModuleDescriptor<?> moduleDescriptor);
}
  • The interface contains methods to register and unregister macros. These are called automatically when plugins are installed, uninstalled, enabled or disabled or individual macros are enabled or disabled.
  • The main method however is getMacroByName. It obtains an instance of a Macro unless the macro does not exist or is disabled in which case null is returned.

The Confluence 3.x com.atlassian.renderer.v2.macro.MacroManager interface still exists in order to support 3.x plugins, to migrate content to the new XHTML storage format and to view content that has not been fully migrated.

Source of macros

The Confluence 4.0 MacroManager is composed of 4 sub managers, one for each source of macros. When requested for a macro, each sub manager is checked in sequence.

  • XhtmlMacroManager is populated with Confluence 4.0 style macros. Plugin developers supply 4.0 macro definitions as <xhtml-macro> elements in atlassin-plugin.xml files. Internally the definitions are held as XhtmlMacroModuleDescriptors.
  • V2CompatibilityMacroManager is populated with bodyless Confluence 3.x style macros automatically wrapped in a V2CompatibilityMacro (a 4.0 macro). Plugin developers supply 3.x macro definitions as <macro> elements in atlassin-plugin.xml files. Internally the definitions are held as CustomMacroModuleDescriptors. Older style macros with bodies are not automatically wrapped, as the 4.0 macro's body type (PLAIN_TEXT or RICH_TEXT) is unknown.
  • UserMacroLibraryMacroManager is populated with user macros added via the admin interface. Internally it delegates to a UserMacroLibrary which keeps track of user macros.
  • UserMacroPluginMacroManager is populated with user macros added via the plugin subsystem. Plugin developers supply user macro definitions as <user-macro> elements in atlassin-plugin.xml files. Internally the definitions are held as UserMacroModuleDescriptors.

Spring autowiring

The Confluence 4.0 MacroManager may be autowired using the name "xhtmlMacroManager"
The Confluence 3.x MacroManager is still available using the name "macroManager".

Example XHTML macro definition

1
2
<xhtml-macro name='album' class='com.atlassian.confluence.plugins.macros.albums.macros.AlbumMacro'
             key='album'
             documentation-url="help.albums.ablum.macro"
             icon="/download/resources/albums/icons/album.png">
    <description>Album of pages, attachments, blog posts and external pages.</description>
    <resource type="velocity" name="help" location="com/atlassian/confluence/plugins/macros/albums/album-help.vm">
        <param name="help-section" value="confluence"/>
    </resource>
    <category name="formatting"/>
    <parameters>
        <parameter name="views" type="string" required="true" default="default"/>
    </parameters>
</xhtml-macro>

Example usage from a macro

The following (rather contrived) example is taken from a macro that outputs only selected nested macros. The output from nested macros are only included if there is a parameter with the nested macro's name. The value of the parameter is supplied to the nested macro. Note the check that the macro returned from the macroManager is not null. It may be null if the macro is disabled or does not exist.

1
2
public class TestMacro implements Macro
{
    private final XhtmlContent xhtmlContent;
    private final MacroManager macroManager;
    public TestMacro(XhtmlContent xhtmlContent, MacroManager macroManager)
    {
        this.xhtmlContent = xhtmlContent;
        this.macroManager = macroManager;
    }

    public String execute(final Map<String, String> parameters, String body, final ConversionContext conversionContext) throws MacroExecutionException
    {
        body = getStorageBody(parameters, conversionContext);
        final StringBuilder stringBuilder = new StringBuilder("");
        final AtomicReference<MacroExecutionException> nestedMacroExecutionException = new AtomicReference<MacroExecutionException>();
        try
        {
            xhtmlContent.handleMacroDefinitions(body, conversionContext, new MacroDefinitionHandler()
            {
                public void handle(MacroDefinition macroDefinition)
                {
                    String testValue = parameters.get(macroDefinition.getName());
                    if (testValue != null && testValue.length() > 0)
                    {
                        Macro macro = macroManager.getMacroByName(macroDefinition.getName());
                        if (macro != null)
                        {
                            try
                            {
                                stringBuilder.append(macro.execute(Collections.<String,String>emptyMap(), testValue, conversionContext));
                            }
                            catch (MacroExecutionException e)
                            {
                                nestedMacroExecutionException.set(e);
                            }
                        }
                    }
                }
            });
            if (nestedMacroExecutionException.get() != null)
            {
                throw nestedMacroExecutionException.get();
            }
            return stringBuilder.toString();
        }
        catch (XhtmlException e)
        {
            throw new MacroExecutionException(e);
        }
    }
    // ...

Rate this page: