Extensions
Installing Extensions
An extension (also known as a plugin) is a Python module or package that extends Ivy's functionality. You can install extensions for a site in one of two ways.
-
You can add an extensions directory named
ext
to your site's root directory. Extension modules placed in thisext
directory 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
extensions
list 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 pip
.
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/ext/
directory which you can view on Github.
Events
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:
from ivy.events import Event @ivy.events.register(Event.EXIT) def print_page_count(): print(ivy.site.pages_written())
This callback is registered on the EXIT
event hook which fires just before the application exits. (The EXIT
event hook can be found in the ivy/__init__.py
file.)
Filters
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:
from ivy.filters import Filter @ivy.filters.register(Filter.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
filter hook can be found in the ivy/nodes.py
file).
Note that this hook supplies us with the Node
instance itself as an additional argument which in this case we 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. Extensions can register support for additional rendering and parsing engines using a system of @register
decorators.
Template Engines
Template-engines produce the output HTML for finished .html
pages in the site.
Ivy has builtin support for Jinja and Ibis templates. Extensions can register support for additional template-engines using the @ivy.templates.register()
decorator. Template-engine callbacks are registered per template-file-extension, e.g.
@ivy.templates.register('jinja') def jinja_callback(page_data, template_filename): ... return html
A template-engine callback should accept a dictionary of page data and a template filename and return a string of HTML.
Rendering Engines
Rendering-engines convert node content into HTML which can then be poured into a template to produce the finished .html
output page.
Ivy has builtin support for node files written in Markdown and Syntext. Extensions can register support for additional input formats using the @ivy.renderers.register()
decorator. Rendering-engine callbacks are registered per file-extension, e.g.
@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.
Note that if you register a custom callback for .md
files, this will override the default Markdown renderer.
Node Metadata
Ivy has builtin support for YAML file headers. Extensions can add support for additional metadata formats by preprocessing file content on the file_text
filter hook.
from ivy.filters import Filter @ivy.filters.register(Filter.FILE_TEXT) def parse_toml_header(raw_text, meta_dict): ... return filtered_text
This filter fires each time a node file is loaded from disk; 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.
The FILE_TEXT
filter hook can be found in the ivy/utils.py
file.
Bundled Extension Settings
Markdown
Ivy uses the Markdown package to render node files with a .md
extension. You can add a
dictionary of keyword arguments for the Markdown renderer to your site configuration file via a
markdown_settings
attribute, e.g.
markdown_settings = { 'extensions': ['markdown.extensions.extra'] }
See the Markdown package's documentation for details of the available options.
Note that you can register a custom handler for .md
files to use an alternative Markdown library of your choice.
Syntext
Ivy uses the Syntext package to render node files with a .stx
extension. You can add a dictionary of keyword arguments for the Syntext renderer to your site configuration file via a syntext_settings
attribute, e.g.
syntext_settings = { 'pygmentize': False, }
Jinja
Ivy uses the Jinja package to render template files with a .jinja
extension. You can add a
dictionary of keyword arguments for the Jinja environment to your site configuration file via a
jinja_settings
attribute.
Shortcodes
Ivy uses the Shortcodes package to process shortcodes in node files. You can add a dictionary of
keyword arguments for the shortcode parser to your site configuration file via a shortcode_settings
attribute.
Automenu
The bundled Automenu extension automatically generates a menu containing links to every node in the site. The menu can be accessed in templates via the automenu
attribute. This menu can be customized in three ways:
-
If a node has a
menu_title
attribute, its value will be used in the menu in place of the node'stitle
. -
By default entries are ordered alphabetically by filename. Entry order can be customized by giving nodes an integer
menu_order
attribute (positive or negative) with lower numbers coming first. The default order value is 0. (Note that the homepage is an exception and will always be the first entry in the menu.) -
If a node has a
menu_exclude
attribute set to true it (and its children) will be omitted from the menu.
Only nodes which have a menu_title
or title
attribute are included in the menu.