Wiki source code of Make a Macro Editable Inline

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

Hide last authors
Eleni Cojocariu 2.1 1 Declare part of a [[Java macro>>doc:documentation.xs.dev.rendering-macros.write-java-macro.WebHome]]'s content, or one of its parameters, as **editable inline**, so that the user edits it directly in the [[WYSIWYG editor>>doc:extensions:Extension.CKEditor Integration.WebHome]] instead of reopening the macro dialog.
2
3 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.
4
Eleni Cojocariu 7.1 5 1. Declare the type of the macro content in the ##DefaultContentDescriptor## the macro passes to its descriptor; [[Rendering Macros>>doc:documentation.xs.dev.rendering-macros.WebHome]] 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: (((
Eleni Cojocariu 2.1 6 {{code language="java"}}
Eleni Cojocariu 7.1 7 new DefaultContentDescriptor("The text displayed beside the logo", true, Block.LIST_BLOCK_TYPE)
Eleni Cojocariu 2.1 8 {{/code}}
9 )))
10 1. 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: (((
11 {{code language="java"}}
12 @PropertyDisplayType({ List.class, Block.class })
13 {{/code}}
14
15 The annotation takes the class followed by its type parameters, so this is the same ##List~<Block~>## written as an annotation value.
16 )))
17 1. 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: (((
18 {{code language="java"}}
19 @Override
20 public List<Block> execute(MyMacroParameters parameters, String content,
21 MacroTransformationContext context) throws MacroExecutionException
22 {
23 // A fixed logo, which the user cannot edit.
24 Block logoBlock = new ImageBlock(this.logoReference, true);
25
26 // The macro content, parsed into blocks and marked as editable inline.
27 List<Block> contentBlocks =
28 this.contentParser.parse(content, context, false, context.isInline()).getChildren();
29 Block editableContent = new MetaDataBlock(contentBlocks, getNonGeneratedContentMetaData());
30
31 return List.of(logoBlock, editableContent);
32 }
33 {{/code}}
34
35 ##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.
36 )))
37 1. 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##: (((
38 {{code language="java"}}
39 new MetaDataBlock(titleBlock, getNonGeneratedContentMetaData("title"));
40 {{/code}}
41 )))
Eleni Cojocariu 5.1 42 1. 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: (((
43 {{code language="xwiki"}}
44 {{mymacro title="Edited in place too"}}
45 The text the user typed directly in the page.
46 {{/mymacro}}
47 {{/code}}
48 )))

Get Connected