External Renderers

Pepperminty Wiki supports external renders, should you choose to enable with the parser_ext_renderers_enabled setting - though it's enabled by default. The way these function is very powerful - by registering an external renderer, a subshell will be spawned that will execute the given command, and serve the output back to the user as an image. Text is specified via code-fencing like so:

insert text here

The output of external renderers is cached in the render_ext subdirectory of the main cache directory (usually called ._cache in the data storage directory alongside your pages), to avoid the computational load of re-rendering things every time.

Note that anonymous users, by default, are not allowed to invoke external renderers, unless the parser_ext_allow_anon setting is set to true - though they can still recall pre-rendered items from the cache. This protects against potentially malicious content being rendered and causing nasty things like infinite loops.

If you've got anonymous edits enabled, you may want to carefully analyse the external renderers you've got enabled, as an anonymous user could save a malicious image to a page, which would then be unwittingly loaded a user at a later time.

External renderers are registered in an object map in the parser_ext_renderers setting, like so:

    "language_code": "definition_here",
    "another_language_code": "definition_here"

...where "definition_here" is an object like this:

    "name": "nomnoml",
    "description": "The nomnoml UML diagram renderer. Requires the 'nomnoml' npm package to be globally installed.",
    "url": "http:\/\/nomnoml.com\/",
    "cli": "nomnoml {input_file} {output_file} 0",
    "cli_mode": "file",
    "output_format": "image\/svg+xml",
    "output_classes": [ "invert-when-dark" ]

Above is a definition for nomnoml, which is one of the 4 default external renderers registered. There's quite a lot here, so we'll look at each property in turn. First, the simpler properties:

Although the output of external renderers is cached, the hash used as the filename in the cache is computed from not only the source text input, but a hash of the external renderer options too. If any of the options in the definition of the external renderer change, then the hash will be altered too and all previous cache entries automatically invalidated. In these instances it may be worth deleting the cache directory too, as it might get quite large if the external renderer definition is changed in between many invocations.

cli_mode requires additional explanation. It supports 3 values:

Each value causes the command specified in cli to be executed slightly differently and the output of the command to be handled differently too. The intention here is to allow integration with the widest possible range of external programs without the need for a wrapper script.

Pipe Mode

In pipe mode, the input source text is made available via the standard input, and everything on the standard output is considered to be the output image, which is both cached and sent to the user.

If the exit code of the command is non-zero, then the output on the standard error is converted into an error image and served instead. Example:


Substitution-Pipe Mode

In substitution-pipe mode, the special token {input_text} in the cli that is to be executed is replaced with the shell-escaped text (using PHP's escapeshellarg) that should be rendered. The standard input is empty, and the standard output is treated as the output image, as in pipe mode. The standard error is handled as in pipe mode too.


command {input_text}

Note that {input_text} is not in quotes - these are added automatically. Care should be taken that the input text is note handled as an argument though, as this could cause unintended side-effects if the input text starts with a dash. Many commands support the double-dash syntax to stop parsing argument flags like so:

command --some-options -a -b -c 3 -- {input_text}

File Mode

The last mode supported is file mode. Unlike the pipe and substitution-pipe modes, any content pushed to the standard output is ignored. The tokens {input_file} and {output_file} are replaced with the path to files that contain the input text and that the output should be written to respectively. If the exit code is non-zero, the standard error is handled as normal - just like pipe and substitution-pipe modes.


command --input {input_file} --output {output_file}

As with substitution-pipe mode, note that the tokens are not in quotes - these are added automatically.

As with substitution-pipe mode, care should be taken to ensure that the file paths aren't handled as arguments - but Pepperminty Wiki will attempt to avoid this. The input file will be an absolute path to an empty file in the system's temporary directory, and the output file will be a file (that may or may not yet exist) in the external renderer cache directory.