How to obtain a list of all macros in the current page?

I’m working on a Confluence plugin that allows users to embed some content into a page via a macro. There can be multiple macro instances in a single page and I would like to get a list of them all (including their parameter settings).

I found out that I can get the HTML code of the current page with getBodyAsString():

ContentEntityObject curPage = context.getPageContext().getEntity();
logger.info(curPage.getBodyAsString());

And I could parse this code for the “imgId” that I’m interested in:

<ac:structured-macro ac:name="My Macro" ac:macro-id="be129df2-2e6b-4664-98f4-8613c1a4521c">
	<ac:parameter ac:name="imgId">EB5C24F1-54B6-4c7b-93FF-4B36FAA4212</ac:parameter>
</ac:structured-macro>

But this seems too “fragile” and I feel like I would be reinventing the wheel. Is there maybe a Manager or Service that could easily return me a list of macros in a page object?

Regards
Dominik

I now found that there is something called a MacroInstanceFinder. I can create one, but I have no clue how this could be used to get a list of all macros in a given page.

ContentMacroService.MacroInstanceFinder f = 
				contentMacroService.findInContent(ContentId.of(curPage.getId()));

Does anyone know how to obtain a list of macros in a given page?

Hello hello !

You can use the XhtmlContent interface for this purpose , take a look at the handleMacroDefinitions method , see javadoc available here XhtmlContent (Atlassian Confluence 7.4.9 API)

You can use it in combination with the ContentService as follow to collect the macros that are parsed out of a content body.

From what I recall , MacroInstanceFinder was made to look up macros by ID/hash only (so it’s a single fetcher), and needs some extra love to add support for a fetchMany method which would return a collection of macros … (hint hint @ggautam might want to add that sometime soon ?)

    @GET
    @Path("/{id}/macros")
    @Produces("application/json")
    public Response getMacros(@PathParam("id") final long contentId) {
        final String contentBody = contentService.find(ExpansionsParser.parse("body.storage")).withId(ContentId.of(contentId)).fetch()
                .map(content -> content.getBody().get(ContentRepresentation.STORAGE).getValue())
                .orElseThrow(notFound("content not found for id: " + contentId));

        final Collection<Map<String, Object>> macroDefinitions = new ArrayList<>();
        try {
            xhtmlContent.handleMacroDefinitions(
                    contentBody,
                    new DefaultConversionContext(new RenderContext()),
                    (macro) -> macroDefinitions.add(
                            ImmutableMap.<String, Object>builder()
                                    .put("name", macro.getName())
                                    .put("macroId", macro.getMacroIdentifier().orElse(MacroId.fromString("none")).getId())
                                    .put("parameters", macro.getParameters())
                                    .put("body.text", macro.getBodyText())
                                    .put("body.type", macro.getBodyType())
                                    .put("valid", macro.isValid())
                                    .put("schema.version", macro.getSchemaVersion())
                                    .build()
                    )
            );
            return Response.ok(macroDefinitions).build();
        } catch (XhtmlException exception) {
            return Response.serverError().entity(exception.getMessage()).build();
        }
    }

Code example available here https://github.com/viqueen/atlassian-devbox/commit/f999e5ee2281714c46837f430b8fd6e2450c24b6

I hope this helps
Cheers

Hasnae R.
(former Confluence person)

6 Likes