Plugins

Plugins allow you to write a server-side executed Python script that can modify or generate pages. The plugins are found in:

/services/http/kondo/plugins

If you use the following directive:

@plugin censor-bad-words

Then the following plugin will be loaded:

/services/http/kondo/plugins/censor-bad-words.py

The plugin name must only have unaccented latin letters, digits, and - and _.

A plugin is just a Python 3 script with a filter_markdown function, which takes a single context argument containing the Markdown body. Plugins can modify the body variable to change any part of the page:

import re

def filter_markdown(context):
    context.body = re.sub(r"[Ee]macs", "VIM", context.body)

This plugin is actually running on this page, so I can tell you that the two best text editors are VIM and VIM. (I tried to be neutral in the editor holy war, but the plugin wouldn't let meā€”check the source of /services/http/kondo/index.ucc if you don't believe me!)

You can use multiple @plugin directives, and each plugin will run in the order that the directives are listed in.

The properties and methods available on the context are currently:

Property Description
file_path The absolute file system path of the .ucc file being rendered.
styles The list of styles added by the @style directive. You can manipulate this list to add plugin specific styles, which should be stored in /services/http/kondo/styles/for-plugins.
get_data(path) Parses a YAML file given a path that is relative to the /data directory found in the web site root directory. Changes to this file will trigger hot reloading.
render_string_template(data, template) Given a dictionary of variables data, and a Jinja2 template as a string in template, returns the rendered template.
render_markdown(text) Renders Markdown into HTML.

The plugin .py file is reloaded each time a page is generated, and runs under a user that has the webmasters group. While it doesn't get any input from remote browsers, other than in the path variable, it still could in theory be poisoned by something in the .ucc file, so be conservative about handling potentially tainted data.

Syntax Tree Plugins

Instead of a filter_markdown function, a plugin can also implement a filter_ast function (or both) that is given a context.body that is the root of the Mistletoe Abstract Syntax Tree (AST). The root Document node can be replaced, or the child nodes in the tree can be manipulated:

from mistletoe.block_tokens import Paragraph
from mistletoe.span_tokens import RawText

def filter_ast(context):
    context.body.children.append(
        Paragraph(children = [
            RawText("This paragraph was added by a plugin.")
        ])
    )

There is an existing plugin, dump-markdown-ast, that converts the entire page into a listing of the AST notes. Since plugins are executed in the order they are defined in the page header, you can place this plugin before or after your plugin to test document manipulations.

Also, the above example plugin is running on this page:

This paragraph was added by a plugin.