- Installing Extensions
- Event & Filter Hooks
- Rendering & Parsing Engines
An extension (aka a 'plugin') is a Python module or package that extends Ivy's functionality. You can install plugins for a site in one of two ways.
You can add an extensions directory named
extto your site's root directory. Extension modules placed in this
extdirectory will be loaded automatically by Ivy.
If an extension module has been installed on Python's standard import path you can activate it for a particular site by adding its name to an
extensionslist in the site's configuration file:
extensions = [ 'extension_one', 'extension_two', ]
This second method can be used to enable extensions installed from the Python package index using
Event & Filter Hooks
Ivy exports a flexible framework of event and filter hooks. Plugins can extend Ivy by registering callback functions on these hooks.
Most of Ivy's default functionality — e.g. support for Jinja templates or Markdown files — is implemented by a set of bundled plugins which make use of this hook system. If you want to extend Ivy yourself you'll probably want to start by taking at look at how they work.
You can find these bundled plugins in the
ivy/extensions/ directory which you can view on Github.
Event callbacks accept zero or more arguments depending on the specific hook. They may modify their arguments in place but have no return value.
Here's a simple event callback that prints a count of the number of pages that have been written to disk:
import ivy @ivy.hooks.register('exit') def print_page_count(): print(ivy.site.written())
This callback is registered on the
exit event hook which fires just before the application exits. (The
exit hook can be found in the
Filter callbacks accept at least one argument — the value to be filtered. They may accept additional arguments depending on the specific hook. Filter callbacks modify and return the value of their first argument.
Here's a simple filter callback that changes every instance of the word foo in node content to bar:
import ivy @ivy.hooks.register('node_text') def foo_to_bar(text, node): return text.replace('foo', 'bar')
This callback is registered on the
node_text filter hook which fires just before a node's text is rendered into HTML. (The
node_text hook can be found in the
Note that this hook supplies us with the
Node instance itself as an additional argument which we here ignore.
Rendering & Parsing Engines
Ivy relies for most of its functionality on a suite of pluggable rendering and parsing engines, e.g. the Jinja template-engine for handling
.jinja template files. Plugins can register support for additional rendering and parsing engines using a system of
Ivy has builtin support for Jinja and Ibis templates. Plugins can register support for additional formats using the
@templates.register() decorator. Template-engine callbacks are registered per file-extension, e.g.
import ivy @ivy.templates.register('jinja') def jinja_callback(page, filename): ... return html
A template-engine callback should accept a
Page object and a template filename and return a string of HTML.
Ivy has builtin support for node files written in Markdown and Syntext. Plugins can register support for additional formats using the
@renderers.register() decorator. Rendering-engine callbacks are registered per file-extension, e.g.
import ivy @ivy.renderers.register('md') def markdown_callback(text): ... return html
A rendering-engine callback should accept a single string argument containing plain text and return a string of HTML.
Ivy has builtin support for YAML file headers. Plugins can add support for additional formats by preprocessing file content on the
file_text filter hook.
import ivy @ivy.hooks.register('file_text') def parse_toml(text, meta): ... return text
This filter fires each time a node file is loaded; it passes the raw file text along with a metadata dictionary. Callbacks can check the text for an appropriate header marker, process the header if found, and update the dictionary. They should return the text with the header stripped.
file_text hook can be found in the
Ivy has builtin support for WordPress-style shortcodes with space-separated positional and keyword arguments:
[% tag arg1 key=arg2 %] ... [% endtag %]
Shortcodes are powerful; you can use them to inject content into a node's text or to customize the formatting of a block of content.
Ivy ships with a sample shortcode plugin which registers an
[% include %] shortcode. You can use this shortcode to inject the content of files from your site's
inc directory directly into a node's text, e.g.
[% include foobar.md %]
Plugins can register custom shortcode tags using the
import shortcodes @shortcodes.register('tag') def handler(node, content, pargs, kwargs): ... return replacement_text
Specifying a closing tag in the decorator gives the new shortcode block scope:
@shortcodes.register('tag', 'endtag') def handler(node, content, pargs, kwargs): ... return replacement_text
Handler functions should accept four arguments:
Nodeinstance containing the shortcode.
- A string containing the shortcode's content, if the shortcode has block scope.
- A list of positional arguments.
- A dictionary of keyword arguments.
Positional and keyword arguments are passed as strings. The handler function itself should return a string which will replace the shortcode in the text.
Note that shortcodes are processed before node text is rendered into HTML.
See the shortcodes documentation for further details.