There is 1 comment.
 
 
XWiki Platform / cid:jira-generated-image-avatar-6fa25f05-8c17-46b8-993a-26ee90c4bdd1 XWIKI-24905 Open

Empty {{wikimacrocontent/}} fails to render/edit

 
View issue   ยท   Add comment
 

1 comment

 
cid:jira-generated-image-avatar-a4daea97-5e5b-4de1-affd-92c19e48b7dc Marius Dumitru Florea on 17/Sep/26 13:07
 

Root cause

The problem is not in WikiMacroContentMacro: that macro always outputs a placeholder block (a GroupBlock / FormatBlock carrying data-wikimacro-id=wikimacrocontent), whether or not the wiki macro was called with content.

The placeholder is replaced later, in DefaultWikiMacroRenderer#resolveMacroContent (xwiki-platform-rendering-wikimacro-store):

private Block resolveMacroContent(MacroMarkerBlock macroBlock)
{
    if (this.wikimacro.getDescriptor().getContentDescriptor() != null && this.macroContent != null) {
        MetaData nonGeneratedContentMetaData = getNonGeneratedContentMetaData();
        nonGeneratedContentMetaData.addMetaData(WIKIMACROCONTENT, "true");
        ...
        return new MetaDataBlock(blocks, nonGeneratedContentMetaData);
    }

    return macroBlock;      // <-- taken when the macro is called without content
}

The && this.macroContent != null guard was added by XWIKI-20820 (fixed in 15.3) to avoid an NPE in parseContent(null). But it skips the whole MetaDataBlock, not just the parsing. So when the wiki macro is called without a body:

  • No non-generated-content metadata is produced, so the annotated XHTML renderer doesn't output the <div class="xwiki-metadata-container" data-xwiki-non-generated-content="..."> wrapper. The CKEditor xwiki-macro plugin makes a macro region editable in-place only when it finds that data-xwiki-non-generated-content attribute, so in-place editing of the content is simply not available.
  • The unresolved wikimacrocontent MacroMarkerBlock is left in the output, and cleanMacroMarkers bails out entirely (it keys off the wikimacrocontent=true metadata blocks it no longer finds), so all the inner macro markers survive too.
  • That leftover marker becomes an empty nested macro widget in the editor. Being 0x0, ensureMacroWidgetVisible reveals the macro:<name> placeholder.

Why 15.10 and 17.5 differ

The guard is present in both versions, so the bug is the same. What changed in between is XWIKI-22834 (17.1), which removes the bogus filler BR that CKEditor adds to the empty block elements of a macro output. Before that, the empty widget accidentally had one line of height, so no placeholder was shown and you just had nothing to click on; after it, the widget really is 0x0 and the macro:<name> placeholder shows up. Same root cause, different visible symptom.

Proposed fix

Emit the metadata block whenever there is a content descriptor, and only skip the parsing when the content is null or empty. This keeps the XWIKI-20820 NPE fix intact:

private Block resolveMacroContent(MacroMarkerBlock macroBlock)
{
    if (this.wikimacro.getDescriptor().getContentDescriptor() != null) {
        MetaData nonGeneratedContentMetaData = getNonGeneratedContentMetaData();
        nonGeneratedContentMetaData.addMetaData(WIKIMACROCONTENT, "true");

        List<Block> blocks;
        if (StringUtils.isEmpty(this.macroContent)) {
            // The content is optional and was not provided. The metadata block is still needed, so that the
            // WYSIWYG editor can offer in-place editing of the (empty) macro content.
            blocks = List.of();
        } else {
            try {
                blocks = parseContent(this.macroContent, macroBlock.isInline()).getChildren();
            } catch (RenderingException e) {
                blocks = this.errorBlockGenerator.generateErrorBlocks(macroBlock.isInline(),
                    TM_FAILEDRESOLVECONTENTPLACEHOLDER, "Failed to resolve macro content placeholder", null, e);
            }
        }

        // We don't execute the content to make sure it's executed later in the right context (where it was passed
        // to the wiki macro).
        return new MetaDataBlock(blocks, nonGeneratedContentMetaData);
    }

    return macroBlock;
}

Wanted side effect: with the metadata block back, cleanMacroMarkers runs again and strips the leaked wikimacrocontent and inner macro markers. There is no API change, so no @since / Revapi work.

DefaultWikiMacroTest already asserts the beginMetaData [[non-generated-content]=[...]] events, so the unit test for a content-less call fits right next to the existing ones.

Remaining check on the editor side

After the fix the server outputs an empty <div class="xwiki-metadata-container" data-xwiki-non-generated-content="java.util.List<org.xwiki.rendering.block.Block>" data-xwiki-wikimacrocontent="true"></div>. CKEditor's Widget#initEditable calls editable.setData(editable.getHtml()) on it, which may or may not insert a <p><br></p> filler for empty data. This needs to be checked in a live editor: if no filler is inserted, the macro widget is still 0x0 and the placeholder still wins.

In that case the minimal targeted fix is in xwiki-macro/plugin.js: the rule added by XWIKI-22834 strips bogus BR elements from the macro output so that the visibility of read-only output can be measured, but a nested editable is not read-only. Making isMacroOutput() (or that br rule) return false inside a [data-xwiki-non-generated-content] element mirrors the editable flag the plugin's parser context already tracks, and keeps the filler that makes the empty editable clickable.