Create
cancel
Showing results for 
Search instead for 
Did you mean: 
Sign up Log in

Confluence 4.x: Why can built-in macros still be inserted using wiki markup, but apparently not my custom macro?

Michael Podvinec May 10, 2012

I am seeing a difference in behavior between the macros provided by Confluence, and a macro I am trying to develop.

For simplicity's sake, I have based this explanation on the Confluence XhtmlMacroDemo, with the addition of a parameter "foo" that is just copied to the macro body (see below). Tested using Confluence 4.1.6.

In the WYSIWYG editor, standard plugins can be entered/parametrized using wiki markup, but not a custom macro, like the XhtmlMacroDemo.

Example:

If I enter {status:colour=green}, the editor will immediately display a green status bar (I understand that there's additional magic involved in getting the WYSIWYG preview). The point is that the macro and its parameters can be fully specified using the old wiki markup.

(If I enter {xhtml-macro-demo} and press return on the autocomplete, I can parametrize the macro using the pop-up screen, but that's not my goal)

If I enter {xhtml-macro-demo:foo=bar}, the code will be put into a "wiki markup" macro block upon closing the curly brace. However, when I save or preview the page, it isn't interpreted correctly, and I get "Unknown macro: {xhtml-macro-tutorial}"

Motivation (why go through the trouble and not just use the macro dialog?):

I need to be able to specify a parametrized instance of my macro in the wiki markup language, since this is to be included in a menu bar created with Adaptavist ThemeBuilder. As far as I ahev understood, you cannot use the storage format in this circumstance (but I would be glad to be proved wrong).

Why is wiki markup entry of macros treated differently between "built-in" and "user-contributed" macros, and is there a way how I can get the built-in behavior?

atlassian-plugin-xml:

<atlassian-plugin key="${project.groupId}.${project.artifactId}" name="${project.name}" plugins-version="2">
    <plugin-info>
        <description>${project.description}</description>
        <version>${project.version}</version>
        <vendor name="${project.organization.name}" url="${project.organization.url}" />
    </plugin-info>

    <xhtml-macro name="xhtml-macro-tutorial" class="tutorial.XhtmlMacroDemo" key="xhtml-macro-tutorial">
        <parameters>
        	<parameter name="foo" type="string"/>
        </parameters>
        <category name ="development"/>
    </xhtml-macro>
</atlassian-plugin>
XhtmlMacroDemo.java:
package tutorial;

import com.atlassian.confluence.content.render.xhtml.ConversionContext;
import com.atlassian.confluence.content.render.xhtml.DefaultConversionContext;
import com.atlassian.confluence.content.render.xhtml.XhtmlException;
import com.atlassian.confluence.macro.Macro;
import com.atlassian.confluence.macro.MacroExecutionException;
import com.atlassian.confluence.renderer.PageContext;
import com.atlassian.confluence.xhtml.api.MacroDefinition;
import com.atlassian.confluence.xhtml.api.MacroDefinitionHandler;
import com.atlassian.confluence.xhtml.api.XhtmlContent;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class XhtmlMacroDemo implements Macro
{
   private final XhtmlContent xhtmlUtils;

   public XhtmlMacroDemo(XhtmlContent xhtmlUtils)
   {
       this.xhtmlUtils = xhtmlUtils;
   }

   @Override
   public BodyType getBodyType()
   {
       return BodyType.NONE;
   }

   @Override
   public OutputType getOutputType()
   {
       return OutputType.BLOCK;
   }

   @Override
   public String execute(Map<String, String> parameters, String bodyContent, ConversionContext conversionContext) throws MacroExecutionException
   {
       
	   String parameterEntered; 
	   if (parameters.containsKey("foo")) {
		   parameterEntered = "You have passed the parameter: " + parameters.get("foo");
	   }
	   else {
		   parameterEntered = "You have not parametrized the macro";
	   }
	   
	   // We will eventually be able to get the page out of the conversion context, for the time being we have to do it this way.
       if (!(conversionContext instanceof DefaultConversionContext))
       {
           return "";
       }
       DefaultConversionContext defaultConversionContext = (DefaultConversionContext) conversionContext;
       String body = ((PageContext) defaultConversionContext.getRenderContext()).getEntity().getBodyAsString();

       final List<MacroDefinition> macros = new ArrayList<MacroDefinition>();

       try
       {
           xhtmlUtils.handleMacroDefinitions(body, conversionContext, new MacroDefinitionHandler()
           {
               @Override
               public void handle(MacroDefinition macroDefinition)
               {
                   macros.add(macroDefinition);
               }
           });
       }
       catch (XhtmlException e)
       {
           throw new MacroExecutionException(e);
       }

       StringBuilder builder = new StringBuilder();
       builder.append("<p>");
       if (!macros.isEmpty())
       {
           builder.append("<table width=\"50%\">");
           builder.append("<tr><th>Macro Name</th><th>Has Body?</th></tr>");
           for (MacroDefinition defn : macros)
           {
               builder.append("<tr>");
               builder.append("<td>").append(defn.getName()).append("</td><td>").append(defn.hasBody()).append("</td>");
               builder.append("</tr>");
           }
           builder.append("</table>");
       }
       else
       {
           builder.append("How did this happen - I am a macro, where am I?!?!?!");
       }
       builder.append("</p>");

       return builder.toString() + "<p><b>" + parameterEntered +"</b></p>";
   }
}

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>tutorial</groupId>
    <artifactId>xhtml-macro-tutorial</artifactId>
    <version>1.0-SNAPSHOT</version>

    <organization>
        <name>Example Company</name>
        <url>http://www.example.com/</url>
    </organization>

    <name>xhtml-macro-tutorial</name>
    <description>This is the tutorial:xhtml-macro-tutorial plugin for Atlassian Confluence.</description>
    <packaging>atlassian-plugin</packaging>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.6</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.atlassian.confluence</groupId>
            <artifactId>confluence</artifactId>
            <version>${confluence.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.atlassian.confluence.plugin</groupId>
            <artifactId>func-test</artifactId>
            <version>2.3</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>net.sourceforge.jwebunit</groupId>
            <artifactId>jwebunit-htmlunit-plugin</artifactId>
            <version>2.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>net.sourceforge.nekohtml</groupId>
            <artifactId>nekohtml</artifactId>
            <version>1.9.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>com.atlassian.maven.plugins</groupId>
                <artifactId>maven-confluence-plugin</artifactId>
                <version>${amps.version}</version>
                <extensions>true</extensions>
                <configuration>
                    <productVersion>${confluence.version}</productVersion>
                    <productDataVersion>${confluence.data.version}</productDataVersion>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <properties>
        <confluence.version>4.1.6</confluence.version>
        <confluence.data.version>3.5</confluence.data.version>
        <amps.version>3.10.2</amps.version>
    </properties>

</project>

4 answers

1 accepted

Comments for this post are closed

Community moderators have prevented the ability to post new answers.

Post a new question

1 vote
Answer accepted
Bob Swift OSS (Bob Swift Atlassian Apps)
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
May 10, 2012

You will need to add a legacy (ie 3.x style) macro in your plugin to get it to work in wiki markup.

Alain Moran
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
May 15, 2012

Bob is correct - to use your xhtml macro with the wiki-markup renderer then you will need to declare a 3.x style macro.

0 votes
Paul Curren
Atlassian Team
Atlassian Team members are employees working across the company in a wide variety of roles.
June 20, 2012

In my opinion, it is a bug that autoformatting of macros relies on us having a "legacy" macro definition.

It is covered in this issue.

0 votes
Johannes Schwarz
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
June 10, 2012
0 votes
Johannes Schwarz
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
June 7, 2012

how can this be done?

Bob Swift OSS (Bob Swift Atlassian Apps)
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
June 7, 2012

For instance, this is how to declare a 3.x style macro in atlassian-plugin.xml - https://bitbucket.org/bob_swift/confluence-wiki-plugin/src/a06aa7b1fc20/src/main/resources/atlassian-plugin.xml#cl-8 . Then create a implementation that follows the 3.x interfaces - usually just map that to your xhtml implementation.

Johannes Schwarz
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
June 7, 2012

thanks! I have tried out the example, exactly how your post states. However, the macro is not found in the macro browser on Confluence 4.2. Source Code:

atlassian-plugin.xml

<macro name="wiki" key="wiki" class="path....macros.WikiMacro" state="enabled">
        <description>
            Useful for testing legacy macro support and for those that need to hang onto wiki markup for a bit longer even on Confluence 4.x.
        </description>
        <category name="content"/>
    </macro>

WikiMacro.java

import java.util.Map;

import com.atlassian.renderer.RenderContext;
import com.atlassian.renderer.v2.RenderMode;
import com.atlassian.renderer.v2.macro.BaseMacro;
import com.atlassian.renderer.v2.macro.MacroException;

/**
 * Simple wiki markup that will not be migrated on Confluence 4.x
 */
public class WikiMacro extends BaseMacro {

    @Override
    public String execute(@SuppressWarnings("rawtypes") Map parameters, String body, RenderContext renderContext) throws MacroException {
        return body;
    }

    @Override
    public RenderMode getBodyRenderMode() {
        return RenderMode.ALL;
    }

    @Override
    public boolean hasBody() {
        return true;
    }

}

Bob Swift OSS (Bob Swift Atlassian Apps)
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
June 7, 2012

It is an example of how to declare a legacy macro in your plugin so that it is valid using insert wiki markup. Legacy macros do not appear in the macro browser, you need a xhtml macro for that.

Johannes Schwarz
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
June 9, 2012

ok, but how to create a macro which can be used from the macro browser AND in a space or global template?

Bob Swift OSS (Bob Swift Atlassian Apps)
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
June 9, 2012

So, include both macro (legacy) and xhtml (new) definitions in your atlassian-plugin.xml. Use the same macro name for both, but, the key needs to be different.

Johannes Schwarz
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
June 10, 2012

thanks Bob! Atm the Macro is shown as markup within a "Wiki Markup" Macro. What I want, is to get a template with all (custom) macros already rendered. Do you know, how to do this?

Bob Swift OSS (Bob Swift Atlassian Apps)
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
June 10, 2012

It is not clear what you are asking about now. Perhaps you need to ask another question.

Johannes Schwarz
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
June 10, 2012

Comments for this post are closed

Community moderators have prevented the ability to post new answers.

Post a new question

TAGS
AUG Leaders

Atlassian Community Events