Ivy

A static website generator for people who enjoy the simpler things in life.

Version 6.5.0

Shortcodes


Ivy has builtin support for WordPress-style shortcodes in node content. These shortcodes can have space-separated positional and keyword arguments:

[% tag arg1 key=arg2 %]

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.

Registering Shortcodes

Shortcodes are implemented by the shortcodes package, an external library. An Ivy extension can register a new shortcode tag using the shortcode package's @register() decorator:

import shortcodes

@shortcodes.register('tag')
def handler(pargs, kwargs, node):
    ...
    return replacement_text

The handler function should accept three arguments:

  1. A list of positional arguments.
  2. A dictionary of keyword arguments.
  3. The Node instance containing the shortcode.

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 so any content injected by a shortcode should be compatible with the existing text's format (Markdown, Syntext, etc.).

See the shortcode package's documentation for further details.

Shortcode Errors

Note that when the shortcode parser reports syntax or rendering errors it quotes line numbers within the node's content. These line numbers don't include the node file's metadata header which is never seen by the shortcode parser.

Example — include_raw

Here's a sample shortcode you could use to inject the raw content of a file from the site's includes directory, inc, directly into a node file:

import ivy
import shortcodes

@shortcodes.register('include_raw')
def handler(pargs, kwargs, node):
    path = ivy.site.inc(pargs[0])
    with open(path) as file:
        return file.read()

To use this shortcode, just supply the name of the file you want to include, e.g.

[% include_raw menu.md %]

The shortcode will be replaced by the content of the file.

Example — include_rendered

Ivy already loads and renders the content of files from the includes directory to make it available in template files. What if you want to include this pre-rendered content in a node file?

import ivy
import shortcodes

@shortcodes.register('include_rendered')
def handler(pargs, kwargs, node):
    includes = ivy.site.includes()
    return includes[pargs[0]]

To use this shortcode, just supply the name of the file you want to include, leaving off the file extension, e.g.

[% include_rendered menu %]

The shortcode will be replaced by the rendered content of the file.

Example — list_children

This is a handy shortcode for building sitemaps — it assembles a list of links to all the children of a target node:

import ivy
import shortcodes

@shortcodes.register('list_children')
def handler(pargs, kwargs, node):
    links = []
    target_node = ivy.nodes.node(pargs[0])
    for child in target_node.children:
        title = child.get('title') or 'Missing Title'
        links.append(f'* [{title}]({child.url})')
    return '\n'.join(links)

To use this shortcode, just supply the @root/ url of the target node, e.g.

[% list_children @root/foo/bar// %]

The shortcode will be replaced by the list of links.

Example — quote

Here's an example of a block-level shortcode wrapping a block of content. Imagine we want to add special styling to quotes with the name of the quote's author attached:

import ivy
import shortcodes

@shortcodes.register('quote', 'endquote')
def handler(pargs, kwargs, node, content):
    content = content.strip()
    output = f'<blockquote>{content}</blockquote>\n'
    output += f'<p class="author">{pargs[0]}</p>'
    return output

The content argument gives us the content of the shortcode as a string.

We can use this shortcode in a source file by supplying the author's name as an argument:

[% quote "Oscar Wilde" %]
    Work is the curse of the drinking classes.
[% endquote %]

The output will look like this:

<blockquote>Work is the curse of the drinking classes.</blockquote>
<p class="author">Oscar Wilde</p>

We can style this output to suit with a little CSS.