Make a Macro Editable Inline

Last modified by Eleni Cojocariu on 2026/10/13 15:11

Steps

Declare part of a Java macro's content, or one of its parameters, as editable inline, so that the user edits it directly in the WYSIWYG editor instead of reopening the macro dialog.

Two things have to be declared: the type of the content or of the parameter, and which parts of what the macro returns are editable.

  1. Declare the type of the macro content in the DefaultContentDescriptor the macro passes to its descriptor; Rendering Macros shows where that descriptor goes in the macro's constructor. Its third parameter is the type, and the type of content that can be edited inline is List<Block>, for which Block.LIST_BLOCK_TYPE is the constant to pass:
    new DefaultContentDescriptor("The text displayed beside the logo", true, Block.LIST_BLOCK_TYPE)
  2. Declare the type of a parameter, if a parameter is to be edited inline rather than the content, with a dedicated annotation on its setter in the macro's parameters bean:
    @PropertyDisplayType({ List.class, Block.class })

    The annotation takes the class followed by its type parameters, so this is the same List<Block> written as an annotation value.

  3. Mark the editable blocks in the macro's execute method, by wrapping them in a MetaDataBlock carrying the metadata for non-generated content. Everything the macro returns outside that block stays read-only, so a macro can mix fixed decoration with editable content:
    @Override
    public List<Block> execute(MyMacroParameters parameters, String content,
        MacroTransformationContext context) throws MacroExecutionException
    {
        // A fixed logo, which the user cannot edit.
        Block logoBlock = new ImageBlock(this.logoReference, true);
    
        // The macro content, parsed into blocks and marked as editable inline.
        List<Block> contentBlocks =
            this.contentParser.parse(content, context, false, context.isInline()).getChildren();
        Block editableContent = new MetaDataBlock(contentBlocks, getNonGeneratedContentMetaData());
    
        return List.of(logoBlock, editableContent);
    }

    getNonGeneratedContentMetaData() is a protected helper of AbstractMacro; it reads the type back from the content descriptor declared in step 1, so the two declarations cannot drift apart.

  4. For a parameter, use the overload that takes its name, which is how the editor knows which parameter the blocks belong to. For a parameter named title:
    new MetaDataBlock(titleBlock, getNonGeneratedContentMetaData("title"));
  5. Open a page calling the macro in the WYSIWYG editor. The parts you marked are no longer part of the read-only macro widget: the cursor goes into them, and what the user types there is edited as normal content. It is saved back into the macro, not into the page around it, so the page source still holds one macro call:
    {{mymacro title="Edited in place too"}}
    The text the user typed directly in the page.
    {{/mymacro}}

FAQ

What does getNonGeneratedContentMetaData() actually store?

A MetaData holding MetaData.NON_GENERATED_CONTENT, whose value is the declared type serialized as a string; the parameter overload adds MetaData.PARAMETER_NAME. The same thing written by hand:

MetaData metaData = new MetaData();
metaData.addMetaData(MetaData.NON_GENERATED_CONTENT, "java.util.List<org.xwiki.rendering.block.Block>");

Can I declare which syntax the editable content is written in?

Yes, with a MetaData.SYNTAX entry on the same MetaData. Its value is a Syntax object, not a syntax id.

Related

Get Connected