Display Server Files with the Code Macro
Tutorial
This page builds a snippet source type for the "code" Macro, which displays a file of a snippets directory on the server. Building it shows how to let the source parameter of the Macro read code from any place it doesn't know yet. The Formatting page lists the other ways to extend the Macro.
- Add the Code Macro and the environment API as dependencies of your Maven module:
<dependency> <groupId>org.xwiki.platform</groupId> <artifactId>xwiki-platform-rendering-macro-code</artifactId> <version>${platform.version}</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.xwiki.commons</groupId> <artifactId>xwiki-commons-environment-api</artifactId> <version>${commons.version}</version> <scope>provided</scope> </dependency> - Write a component that implements CodeMacroSourceFactory, with the name of the source type as its hint. This one reads the file named by the reference from the snippets sub-directory of the permanent directory, and returns the extension of the file as the language of the code:
package com.acme.internal; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import jakarta.inject.Inject; import jakarta.inject.Named; import jakarta.inject.Singleton; import org.xwiki.component.annotation.Component; import org.xwiki.environment.Environment; import org.xwiki.rendering.macro.MacroExecutionException; import org.xwiki.rendering.macro.code.source.CodeMacroSource; import org.xwiki.rendering.macro.code.source.CodeMacroSourceFactory; import org.xwiki.rendering.macro.source.MacroContentSourceReference; import org.xwiki.rendering.transformation.MacroTransformationContext; @Component @Singleton @Named("snippet") public class SnippetCodeMacroSourceFactory implements CodeMacroSourceFactory { @Inject private Environment environment; @Override public CodeMacroSource getContent(MacroContentSourceReference reference, MacroTransformationContext context) throws MacroExecutionException { Path directory = this.environment.getPermanentDirectory().toPath().resolve("snippets").normalize(); Path file = directory.resolve(reference.getReference()).normalize(); // Refuse a reference such as "../xwiki.cfg", which points outside the snippets directory. if (!file.startsWith(directory) || !Files.isRegularFile(file)) { throw new MacroExecutionException("Unknown snippet [" + reference.getReference() + "]"); } try { String content = Files.readString(file, StandardCharsets.UTF_8); // The extension of the file (py, java, js...) is a language name that Pygments knows. String fileName = file.getFileName().toString(); int dot = fileName.lastIndexOf('.'); String language = dot > 0 ? fileName.substring(dot + 1) : null; return new CodeMacroSource(reference, content, language); } catch (IOException e) { throw new MacroExecutionException("Failed to read the snippet [" + reference.getReference() + "]", e); } } } - Register the component by adding its class name to src/main/resources/META-INF/components.txt:
com.acme.internal.SnippetCodeMacroSourceFactory - Build the module and deploy its JAR like any other component, as the "Deploying the Component" section of Creating XWiki Components describes.
- Create the snippets directory in the permanent directory and save a file in it, for example snippets/fibonacci.py:
def fibonacci(n): """Return the first n Fibonacci numbers.""" numbers = [0, 1] while len(numbers) < n: numbers.append(numbers[-1] + numbers[-2]) return numbers[:n] - Edit a Page and use the new type in the source parameter of the Macro: {{code source="snippet:fibonacci.py"/}}.
- View the Page. It displays the content of fibonacci.py, highlighted as Python code:

FAQ
Which language is the code highlighted in?
The language returned by the component applies only when the Macro has no language parameter. When it is null, Pygments guesses the language from the code.
Who can display what through a source type?
The Macro checks nothing for a new source type: anyone who can edit a Page can display whatever a reference gives access to. The component has to refuse the references that go beyond what it means to expose, as the example does for files outside its directory, and as the built-in types do for content that the reader may not view.
What does the reader see when the component throws an exception?
The Macro displays its error box with the message of the exception, for example "Unknown snippet [../xwiki.cfg]", instead of the code.