<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
<channel>
    <title>Uriel Documentation</title>
    <link>https://documentation.uriel.foo/</link>
    <description>Uriel Documentation</description>
    <lastBuildDate>Thu, 11 Jun 2026 18:54:37 -0400</lastBuildDate>
    <image>
        <url>https://documentation.uriel.foo/favicon-32x32.png</url>
        <title>Uriel Documentation</title>
        <link>https://documentation.uriel.foo/</link>
        <width>32</width>
        <height>32</height>
    </image>
    <item>
        <title>VirtualNode Example</title>
        <link>https://documentation.uriel.foo/handlers/virtual/</link>
        <description><![CDATA[<h1>VirtualNode Example</h1>

<p>Hello from a VirtualNode.</p>

<p>See the <a href="https://documentation.uriel.foo/handlers/">Handlers</a> page for more details.</p>

<p>This is a special message for RSS readers only.</p>]]></description>
        <category>user-defined-python-code</category>
        <pubDate>Wed, 10 Jun 2026 23:25:17 -0400</pubDate>
    </item>
    <item>
        <title>Soju</title>
        <link>https://documentation.uriel.foo/soju/</link>
        <description><![CDATA[<h1>Soju</h1>

<p>
    Soju is an extension mechanism that lets you call custom Python code from
    <a href="https://documentation.uriel.foo/parameters/soju-hello-world/">substition parameters</a>.
</p>

<p>
    To run user-defined Python code and have its output appear on a page, you
    need to do two things:
</p>

<ul>
    <li>Define a Soju function</li>
    <li>Reference a Soju function</li>
</ul>

<p>
    This page will show you how to do both.
</p>

<h3>Define a Soju Function</h3>

<p>
    To define a Soju function, you need to add your own user-defined Python
    code into the <i>lib/soju.py</i> file within your project.
</p>

<p>
    This is what the <i>lib/soju.py</i> file looks like when Uriel first
    creates a new project:
</p>

<p><pre>##############################################################################
# soju.py                                                                    #
##############################################################################

# The following symbols are imported using magic:
#
# import uriel
# from uriel import SojuError
# from uriel import log
# from uriel import escape

# The following variables are available to pass to functions:
#
# page
# node
# project_root
# use_canonical_url

# &lbrace;&lbrace;soju:node_title(node)&rbrace;&rbrace;
def node_title(node):
    return escape(node.get_title())</pre></p>

<p>
    You can add your own functions to this file, and
    <a href="https://documentation.uriel.foo/parameters/soju-hello-world/">reference</a>
    them in
    <a href="https://documentation.uriel.foo/directories/nodes/">nodes</a> and
    <a href="https://documentation.uriel.foo/directories/templates/">templates</a>, so the output
    of these functions shows up in pages on your web site.
</p>

<p>
    The <b>uriel</b> program itself is imported into <i>lib/soju.py</i> as a
    module, along with the <b>log()</b> and <b>escape()</b> functions and the
    <b>SojuError</b> exception. See the
    <a href="https://documentation.uriel.foo/uriel/">Uriel API reference</a> for details.
</p>

<p>
    Four variables are present when your functions are called. You can write
    your functions to accept and make use of these variables:
</p>

<table>
    <tr>
        <th>Variable Name</th>
        <th>Type</th>
        <th>Comments</th>
    </tr>
    <tr>
        <td>page</td>
        <td>uriel.Page</td>
        <td><i>uriel.Page</i> instance used to render the current page</td>
    </tr>
    <tr>
        <td>node</td>
        <td><a href="https://documentation.uriel.foo/uriel/node/">uriel.Node</a></td>
        <td>
            The <a href="https://documentation.uriel.foo/uriel/node/">uriel.Node</a> instance for
            the current page
        </td>
    </tr>
    <tr>
        <td>project_root</td>
        <td>str</td>
        <td>Filesystem path to your Uriel project root directory</td>
    </tr>
    <tr>
        <td>use_canonical_url</td>
        <td>bool</td>
        <td>Use canonical URL when generating links?</td>
    </tr>
</table>

<p>
    We won&apos;t discuss <i>page</i> further here. See the
    <a href="https://github.com/ratherlargerobot/uriel/blob/main/uriel">Uriel source code</a>
    if you&apos;re curious.
</p>

<p>
    The <i>node</i> variable is extremely useful. This gives you access to
    dozens of methods that can provide information about the current node,
    find other nodes on the site and get their
    <a href="https://documentation.uriel.foo/uriel/node/">Node</a> instances, etc.
</p>

<p>
    The <i>project_root</i> variable contains the filesystem path to your
    project directory.
</p>

<p>
    The <i>use_canonical_url</i> variable is a boolean that indicates the mode
    that is being used to render the page when your function is called. Normal
    HTML pages are rendered with <i>use_canonical_url</i> set to <i>False</i>.
    But if you are generating an
    <a href="https://documentation.uriel.foo/generated/">RSS feed</a>, then your function can be
    called with <i>use_canonical_url</i> set to <i>True</i> during that
    rendering step. If you are generating your own links to other nodes inside
    of your Soju function, you&apos;ll want to accept this as a function
    argument, and conditionalize your code to work in both conditions.
</p>

<h3>Reference a Soju Function</h3>

<p>
    To reference your custom Soju function, you will need to call it from a
    <a href="https://documentation.uriel.foo/parameters/soju-hello-world/">substition parameter</a>
    in a
    <a href="https://documentation.uriel.foo/directories/nodes/">node</a> or a
    <a href="https://documentation.uriel.foo/directories/templates/">template</a>.
</p>

<p>
    For example, the default <i>lib/soju.py</i> that Uriel generates when it
    creates a new project contains the following function:
</p>

<p><pre># &lbrace;&lbrace;soju:node_title(node)&rbrace;&rbrace;
def node_title(node):
    return escape(node.get_title())</pre></p>

<p>
    We can reference that function by including the
    <b>&lbrace;&lbrace;soju:node_title(node)&rbrace;&rbrace;</b> substitution
    parameter on the node that creates this page. When we do that, here is
    the result of that Soju function call to the <b>node_title(node)</b>
    function:
</p>

<p><pre>Soju</pre></p>

<p>
    The function evaluated to the HTML escaped title of the current node, and
    the string was included in the rendered page.
</p>

<h3>Example Soju Function</h3>

<p>
    Let&apos;s take a look at a non-trivial example function. Here is the
    source code for the <b>example()</b> function in the <i>lib/soju.py</i>
    file for this documentation site:
</p>

<p><pre># &lbrace;&lbrace;soju:example(page, node, project_root, use_canonical_url)&rbrace;&rbrace;
def example(page, node, project_root, use_canonical_url):
    """
    Example Soju function, demonstrating several things in one place.

    Accepts a page, node, project_root, and use_canonical_url.

    Returns an example string to use in the Soju documentation.

    """

    # log a message when the site builds
    log("the example() function is running now")

    # create a list of strings that we will combine and return
    lines = []

    # show the uriel version (which is referenced from the uriel module,
    # along with the string values of each argument passed to this function
    lines.append("Hello from the &lt;b&gt;example()&lt;/b&gt; function!")
    lines.append("")
    lines.append("Generated by uriel version " + escape(uriel.VERSION))
    lines.append("")
    lines.append("page:              " + escape(str(page)))
    lines.append("node:              " + escape(str(node)))
    lines.append("project_root:      " + escape(project_root))
    lines.append("use_canonical_url: " + escape(str(use_canonical_url)))
    lines.append("")

    # find the node for the Uriel API documentation index
    # (the nodes/uriel/index file)
    uriel_api_doc_index_node = node.find_node_by_path("uriel/index")

    # show a link to the node
    lines.append(uriel_api_doc_index_node.get_link())

    # go through each child node under the Uriel API documentation index,
    # sorted by (title + node path). Title is not always guaranteed to
    # be unique, so the unique node path will act as a tie breaker in.
    # case any nodes have the same title.
    for child_node in sorted(uriel_api_doc_index_node.get_children(),
                             key=lambda n: n.get_title() + n.get_path()):

        lines.append("  " + child_node.get_link())

    # if you uncomment this line, it will cause an error when the site builds
    #raise SojuError("oops")

    # combine and return the lines as a string
    return "\n".join(lines)</pre></p>

<p>
    When we call this function, by including it a node or template, using the<br>
    <b>&lbrace;&lbrace;soju:example(page, node, project_root, use_canonical_url)&rbrace;&rbrace;</b><br>
    substitution parameter, this is the output of the function that ends up
    on the rendered page:
</p>

<p><pre>Hello from the <b>example()</b> function!

Generated by uriel version 1.4.1

page:              soju [null]
node:              soju
project_root:      .
use_canonical_url: True

<a href="/uriel/">Uriel API</a>
  <a href="/uriel/constants/">Constants</a>
  <a href="/uriel/exceptions/">Exceptions</a>
  <a href="/uriel/functions/">Functions</a>
  <a href="/uriel/node/">Node Classes</a></pre></p>

<p>
    You can see the output of the <b>log()</b> function when the site builds:
</p>

<p><pre>
copying 'static' to 'public', overwriting previous contents
initializing soju
initializing handlers
reading node files
rendering node content
<b>soju: the example() function is running now</b>
creating pages in 'public' from nodes and templates

... lots of output omitted for brevity ...

created 110 pages (95 file, 15 virtual)
creating 'public/rss.xml'
<b>soju: the example() function is running now</b>
creating 'public/sitemap.xml'
creating 'public/robots.txt'
copying 'static' to 'public'
</pre></p>

<p>
    Notice that the <b>log()</b> message shows up twice? This is because the
    <b>example()</b> function is called twice. Once when the HTML page is
    generated, and again when the
    <a href="https://documentation.uriel.foo/generated/">RSS feed</a> is generated.
</p>

<p>
    The <b>example()</b> function basically collects a variety of values from
    various parts of the <a href="https://documentation.uriel.foo/uriel/">Uriel API</a>, and
    then returns a string that can be displayed in place of the substitution
    parameter on the generated page.
</p>

<p>
    The <b>escape()</b> function performs HTML escaping. As a general rule, it
    should be wrapped around any dynamic values that are returned.
</p>

<p>
    However, any <b>Node</b> methods that return HTML fragments should not be
    escaped (e.g. <b>get_link()</b>).
</p>

<p>
    You should always be aware of HTML escaping when writing custom Soju
    functions.
</p>

<h3>Error Handling</h3>

<p>
    All Soju functions must return a Python <b>str</b> value.
</p>

<p>
    If your function returns a value with a type other than <b>str</b>, has a
    syntax error, or if it raises an exception when it is called, Uriel will
    show an error when the site builds. This error includes detailed
    information about which node and templates were involved, which Soju
    function was called, and what the error was, including a Python traceback.
    This makes it easy to find what caused the build to fail, and where to
    start your debugging efforts.
</p>

<p>
    If you want to avoid the Python traceback portion of the error, you can
    raise a <b>SojuError</b> when you need to raise an expected error.
    This will still result in detailed information about where the error
    originated, but it will skip the Python traceback portion.
</p>

<p>
    If you uncomment the <b>SojuError</b> line from the <b>example()</b>
    function, it will cause the web site build to fail, with a brief error
    message.
</p>

<p>Example <b>SojuError</b> build error message:</p>

<p><pre>copying 'static' to 'public', overwriting previous contents
initializing soju
initializing handlers
reading node files
rendering node content
soju: the example() function is running now
parameter error:
  nodes/soju
    templates/default.html
      nodes/soju
        '&lbrace;&lbrace;soju:example(page, node, project_root, use_canonical_url)&rbrace;&rbrace;'
          'oops'
soju: error in function call to 'soju.example(page, node, project_root, use_canonical_url)': 'oops'</pre></p>]]></description>
        <category>user-defined-python-code</category>
        <pubDate>Wed, 10 Jun 2026 23:23:30 -0400</pubDate>
    </item>
    <item>
        <title>{{node-url:foo/bar}}</title>
        <link>https://documentation.uriel.foo/parameters/node-url-some-other-node/</link>
        <description><![CDATA[<h1>{{node-url:foo/bar}}</h1>

<p>
    The <b>&lbrace;&lbrace;node-url:foo/bar&rbrace;&rbrace;</b>
    parameter evaluates to the URL for the node specified in the rvalue
    portion of the parameter. Nodes are specified as paths starting from the
    <a href="https://documentation.uriel.foo/directories/nodes/">nodes</a> directory.
</p>

<p>
    For example, on this documentation site, we have another node with the
    path <i>parameters/some-other-node</i>.
</p>

<p>
    If we reference it using <i>parameters/some-other-node</i> as the rvalue,
    then
    <b>&lbrace;&lbrace;node-url:parameters/some-other-node&rbrace;&rbrace;</b>
    evaluates to:
</p>

<p><pre>https://documentation.uriel.foo/parameters/some-other-node/</pre></p>

<p>
    Compare this with <a href="https://documentation.uriel.foo/parameters/node-url/">{{node:url}}</a>, which lets you get
    the URL of the current node.
</p>

<h3>URL Representation in HTML vs. RSS</h3>

<p>
    In generated HTML pages, the URL will include the full path to the URL
    from the root of the web site, but will not include the domain name. Like
    this:
</p>

<p><pre>/parameters/some-other-node/</pre></p>

<p>
    In the <a href="https://documentation.uriel.foo/generated/">generated RSS feed</a>, the URL
    will include the <a href="https://documentation.uriel.foo/headers/canonical-url/">Canonical-URL</a>. Like this:
</p>

<p><pre>https://documentation.uriel.foo/parameters/some-other-node/</pre></p>

<p>
    The actual value of
    <b>&lbrace;&lbrace;node-url:parameters/some-other-node&rbrace;&rbrace;</b>
    in the current context evaluates to:
</p>

<p><pre>https://documentation.uriel.foo/parameters/some-other-node/</pre></p>

<p>
    View this in both the <a href="https://documentation.uriel.foo/parameters/node-url-some-other-node/">web page</a> and the
    <a href="https://documentation.uriel.foo/rss.xml">RSS feed</a>, and compare the results.
</p>]]></description>
        <category>parameters</category>
        <pubDate>Sun, 07 Jun 2026 18:01:17 -0400</pubDate>
    </item>
    <item>
        <title>{{node:url}}</title>
        <link>https://documentation.uriel.foo/parameters/node-url/</link>
        <description><![CDATA[<h1>{{node:url}}</h1>

<p>
    The <b>&lbrace;&lbrace;node:url&rbrace;&rbrace;</b> parameter evaluates
    to the URL for the current node.
</p>

<h3>Typical Usage</h3>

<p>Use this when you need to get the URL for the current page.</p>

<p>For example: <a href="https://documentation.uriel.foo/parameters/node-url/">https://documentation.uriel.foo/parameters/node-url/</a></p>

<p>
    Compare this with <a href="https://documentation.uriel.foo/parameters/node-url-some-other-node/">{{node-url:foo/bar}}</a>, which
    lets you get the URL of another node on the web site.
</p>

<h3>URL Representation in HTML vs. RSS</h3>

<p>
    In generated HTML pages, the URL will include the full path to the URL
    from the root of the web site, but will not include the domain name. Like
    this:
</p>

<p><pre>/parameters/node-url/</pre></p>

<p>
    In the <a href="https://documentation.uriel.foo/generated/">generated RSS feed</a>, the URL
    will include the <a href="https://documentation.uriel.foo/headers/canonical-url/">Canonical-URL</a>. Like this:
</p>

<p><pre>https://documentation.uriel.foo/parameters/node-url/</pre></p>

<p>
    The actual value of <b>&lbrace;&lbrace;node:url&rbrace;&rbrace;</b> in the
    current context evaluates to:
</p>

<p><pre>https://documentation.uriel.foo/parameters/node-url/</pre></p>

<p>
    View this in both the <a href="https://documentation.uriel.foo/parameters/node-url/">web page</a> and the
    <a href="https://documentation.uriel.foo/rss.xml">RSS feed</a>, and compare the results.
</p>]]></description>
        <category>parameters</category>
        <pubDate>Sun, 07 Jun 2026 17:16:40 -0400</pubDate>
    </item>
    <item>
        <title>RSS-Include</title>
        <link>https://documentation.uriel.foo/headers/rss-include/</link>
        <description><![CDATA[<h1>RSS-Include</h1>

<p>
    The <b>RSS-Include</b> header controls whether a node is eligible for
    inclusion in the <a href="https://documentation.uriel.foo/generated/">generated RSS feed</a>.
</p>

<p>There are two possible values:</p>

<ul>
    <li><i><a href="https://documentation.uriel.foo/headers/rss-include/true/">true</a></i></li>
    <li><i><a href="https://documentation.uriel.foo/headers/rss-include/false/">false</a></i></li>
</ul>

<p>The default value is <i>false</i>.</p>

<p>
    The <a href="https://documentation.uriel.foo/directories/nodes/">node</a> that generated this
    page has the <b>RSS-Include</b> value set to <i>true</i>. As a result, it
    is included in the generated RSS feed
    (<a href="https://documentation.uriel.foo/rss.xml">https://documentation.uriel.foo/rss.xml</a>).
</p>

<p>
    The usual node
    <a href="https://documentation.uriel.foo/headers/">header inheritance rules</a> apply.
</p>

<p>Here are two example pages highlighting the difference:</p>

<p><a href="https://documentation.uriel.foo/headers/rss-include/true/">RSS-Include: true</a></p>
<p><a href="https://documentation.uriel.foo/headers/rss-include/false/">RSS-Include: false</a></p>]]></description>
        <category>headers</category>
        <category>rss</category>
        <category>rss-headers</category>
        <pubDate>Sat, 06 Jun 2026 17:02:44 -0400</pubDate>
    </item>
    <item>
        <title>RSS-Add-Node-Title-Header</title>
        <link>https://documentation.uriel.foo/headers/rss-add-node-title-header/</link>
        <description><![CDATA[<h1>RSS-Add-Node-Title-Header</h1>

<p>
    Controls whether a title header element is added to the body content of
    <a href="https://documentation.uriel.foo/directories/nodes/">nodes</a> included in a
    <a href="https://documentation.uriel.foo/generated/">generated RSS feed</a>.
</p>

<p>
    There are two possible values:
</p>

<ul>
    <li>
        <i><a href="https://documentation.uriel.foo/headers/rss-add-node-title-header/true/">true</a></i>
    </li>
    <li>
        <i><a href="https://documentation.uriel.foo/headers/rss-add-node-title-header/false/">false</a></i>
    </li>
</ul>

<p>The default value is <i>true</i>.</p>

<p>
    When Uriel generates an RSS feed, it only uses the
    <a href="https://documentation.uriel.foo/directories/nodes/">node</a> contents,
    and completely ignores the
    <a href="https://documentation.uriel.foo/headers/template/">template</a> settings that are
    used to render the HTML pages on the main web site.
</p>

<p>
    However, in most site layouts, the node
    <a href="https://documentation.uriel.foo/headers/title/">title</a> is likely to be displayed
    on the page in a <a href="https://documentation.uriel.foo/headers/template/">template</a>
    using a <a href="https://documentation.uriel.foo/parameters/node-title/">{{node:title}}</a> substitution parameter, and
    not from within the node body contents themselves.
</p>

<p>
    Therefore, when <b>RSS-Add-Node-Title-Header</b> is set to <i>true</i>
    (or left to its default setting), Uriel will add an HTML
    <b>&lt;h1&gt;</b> header tag containing the node
    <a href="https://documentation.uriel.foo/headers/title/">title</a> to the top of its RSS
    entry.
</p>

<p>
    If you want to override this behavior, simply set the
    <b>RSS-Add-Node-Title-Header</b> to <i>false</i>.
</p>

<p>
    All of the normal node
    <a href="https://documentation.uriel.foo/headers/">header inheritance rules</a> apply.
</p>

<p>
    Compare how the following nodes differ in their representation within the
    generated RSS feed (<a href="https://documentation.uriel.foo/rss.xml">https://documentation.uriel.foo/rss.xml</a>).
</p>

<ul>
    <li><a href="https://documentation.uriel.foo/headers/rss-add-node-title-header/true/">RSS-Add-Node-Title-Header: true</a></li>
    <li><a href="https://documentation.uriel.foo/headers/rss-add-node-title-header/false/">RSS-Add-Node-Title-Header: false</a></li>
</ul>]]></description>
        <category>headers</category>
        <category>rss</category>
        <category>rss-headers</category>
        <pubDate>Sat, 06 Jun 2026 17:02:39 -0400</pubDate>
    </item>
    <item>
        <title>RSS-Add-Node-Title-Header: false</title>
        <link>https://documentation.uriel.foo/headers/rss-add-node-title-header/false/</link>
        <description><![CDATA[<p>
    This node has the <a href="https://documentation.uriel.foo/headers/rss-add-node-title-header/">RSS-Add-Node-Title-Header</a>
    header set to <i>false</i>.
</p>

<p>
    As a result, Uriel has not added an additional <b>&lt;h1&gt;</b> title
    header to the top of its
    RSS <i>&lt;description&gt;</i> field in the generated RSS feed
    (<a href="https://documentation.uriel.foo/rss.xml">https://documentation.uriel.foo/rss.xml</a>)
</p>

<p>Compare this with <a href="https://documentation.uriel.foo/headers/rss-add-node-title-header/true/">RSS-Add-Node-Title-Header: true</a></p>]]></description>
        <pubDate>Fri, 05 Jun 2026 22:30:53 -0400</pubDate>
    </item>
    <item>
        <title>RSS-Add-Node-Title-Header: true</title>
        <link>https://documentation.uriel.foo/headers/rss-add-node-title-header/true/</link>
        <description><![CDATA[<h1>RSS-Add-Node-Title-Header: true</h1>

<p>
    This node has the <a href="https://documentation.uriel.foo/headers/rss-add-node-title-header/">RSS-Add-Node-Title-Header</a>
    header set to <i>true</i>.
</p>

<p>
    As a result, Uriel has added the following HTML code to the top of its
    RSS <i>&lt;description&gt;</i> field in the generated RSS feed
    (<a href="https://documentation.uriel.foo/rss.xml">https://documentation.uriel.foo/rss.xml</a>):
</p>

<p><pre>&lt;h1&gt;RSS-Add-Node-Title-Header: true&lt;/h1&gt;</pre></p>

<p>Compare this with <a href="https://documentation.uriel.foo/headers/rss-add-node-title-header/false/">RSS-Add-Node-Title-Header: false</a></p>]]></description>
        <pubDate>Fri, 05 Jun 2026 22:29:01 -0400</pubDate>
    </item>
    <item>
        <title>RSS-Include: true</title>
        <link>https://documentation.uriel.foo/headers/rss-include/true/</link>
        <description><![CDATA[<h1>RSS-Include: true</h1>

<p>
    This node does not have an <b>RSS-Include</b> header defined directly.
    However, it inherited a <i>true</i> value for this header from its
    <a href="https://documentation.uriel.foo/headers/rss-include/">parent node</a>.
</p>

<p>
    As a result, it is eligible for inclusion in the
    generated RSS feed (<a href="https://documentation.uriel.foo/rss.xml">https://documentation.uriel.foo/rss.xml</a>).
</p>

<p>Compare this with <a href="https://documentation.uriel.foo/headers/rss-include/false/">RSS-Include: false</a></p>]]></description>
        <pubDate>Fri, 05 Jun 2026 21:56:09 -0400</pubDate>
    </item>
</channel>
</rss>
