Skip to content

Templates Module

The templates module provides powerful template rendering capabilities using Liquid templating syntax, with support for dynamic data processing through Lua functions. It enables creation of dynamic documents, emails, reports, and any text-based content with sophisticated templating features.

Functions

template.render(id_or_handle, data)

Renders a pre-configured template by its ID or handle, with optional function preprocessing.

Parameters:

  • id_or_handle (string): Template UUID or handle identifier
  • data (table): Data context for template rendering

Returns:

  • string: Rendered template output
  • false: On error (template not found, rendering failure, or permission denied)

Basic Examples:

lua
-- Simple template rendering
local output = template.render("welcome-email", {
    name = "John Doe",
    company = "Acme Corp",
    signup_date = "2024-01-15"
})

if output then
    print("Email content generated")
    notifiers.recipient(user_id, "Welcome!", output)
else
    print("Failed to render template")
end

-- Complex data structure
local report_data = {
    report_date = "2024-01-15",
    metrics = {
        total_orders = 150,
        revenue = 12500.00,
        average_order = 83.33
    },
    top_products = {
        {name = "Widget A", sales = 45},
        {name = "Widget B", sales = 38},
        {name = "Widget C", sales = 29}
    }
}

local report = template.render("daily-report", report_data)

template.render_raw(template_string, data)

Renders a raw Liquid template string without requiring a pre-configured template.

Parameters:

  • template_string (string): Liquid template markup
  • data (table): Data context for template rendering

Returns:

  • string: Rendered template output
  • false: On rendering error

Examples:

lua
-- Simple variable substitution
local output = template.render_raw(
    "Hello, {{ name }}! Welcome to {{ company }}.",
    {name = "Jane Smith", company = "CoCoCo"}
)
-- Output: "Hello, Jane Smith! Welcome to CoCoCo."

-- Conditional rendering
local template_str = [[
{% if user.premium %}
    Welcome back, {{ user.name }}! You have premium access.
{% else %}
    Hello {{ user.name }}! Consider upgrading to premium.
{% endif %}
]]

local output = template.render_raw(template_str, {
    user = {name = "John", premium = true}
})

-- Loops and filters
local template_str = [[
Order Summary:
{% for item in items %}
- {{ item.name | upcase }}: ${{ item.price | round: 2 }}
{% endfor %}
Total: ${{ total | round: 2 }}
]]

local output = template.render_raw(template_str, {
    items = {
        {name = "Print Job A", price = 25.556},
        {name = "Print Job B", price = 30.234}
    },
    total = 55.79
})

Liquid Template Features

Variables and Filters

lua
local template_str = [[
{{ name | upcase }}
{{ price | round: 2 }}
{{ description | truncate: 50 }}
{{ date | date: "%Y-%m-%d" }}
{{ text | strip_html | escape }}
{{ number | plus: 10 | times: 2 }}
]]

local output = template.render_raw(template_str, {
    name = "John Doe",
    price = 99.996,
    description = "This is a very long description that will be truncated",
    date = "2024-01-15T10:30:00Z",
    text = "<p>HTML content</p>",
    number = 5
})

Control Flow

lua
-- If/Else statements
local template_str = [[
{% if order.status == "pending" %}
    Your order is being processed.
{% elsif order.status == "shipped" %}
    Your order has been shipped!
{% else %}
    Order status: {{ order.status }}
{% endif %}
]]

-- Case statements
local template_str = [[
{% case product.type %}
{% when "book" %}
    This is a printed book.
{% when "poster" %}
    This is a poster print.
{% when "card" %}
    This is a greeting card.
{% else %}
    Unknown product type.
{% endcase %}
]]

-- Unless (negative if)
local template_str = [[
{% unless user.verified %}
    Please verify your email address.
{% endunless %}
]]

Loops and Iterations

lua
-- Basic for loop
local template_str = [[
{% for product in products %}
    {{ forloop.index }}. {{ product.name }} - ${{ product.price }}
{% endfor %}
]]

-- Loop with limit and offset
local template_str = [[
{% for item in items limit:5 offset:10 %}
    {{ item.name }}
{% endfor %}
]]

-- Loop variables
local template_str = [[
{% for item in items %}
    {% if forloop.first %}First item: {% endif %}
    {{ item.name }}
    {% if forloop.last %}(Last item){% endif %}
    {% unless forloop.last %},{% endunless %}
{% endfor %}
]]

-- TableRow for HTML tables
local template_str = [[
<table>
{% tablerow product in products cols:3 %}
    {{ product.name }}
{% endtablerow %}
</table>
]]

Function-Enhanced Templates

Templates can be bound to Lua functions for dynamic data preprocessing. When a template has an associated function:

  1. The function's main(args) is called with the provided data
  2. The function's return value becomes the template context
  3. The template is rendered with the processed data
lua
-- Template function (configured separately in the system):
function main(args)
    -- Process input arguments
    local order_id = args.order_id

    -- Fetch additional data
    local success, response = graphql.query([[
        query($id: ID!) {
            getOrder(id: $id) {
                id
                customer { name, email }
                items { name, quantity, price }
                total
                status
            }
        }
    ]], { id = order_id })

    if not success then
        return { error = "Failed to fetch order" }
    end

    local order = response.data.getOrder

    -- Calculate additional fields
    local item_count = 0
    for _, item in ipairs(order.items) do
        item_count = item_count + item.quantity
    end

    -- Return enriched data for template
    return {
        order = order,
        item_count = item_count,
        formatted_total = string.format("$%.2f", order.total),
        status_message = get_status_message(order.status),
        generated_at = os.date("%Y-%m-%d %H:%M:%S")
    }
end

-- Template content:
-- "Order #{{ order.id }} contains {{ item_count }} items. Total: {{ formatted_total }}"

-- Usage in Lua:
local output = template.render("order-summary", { order_id = "12345" })

Advanced Template Patterns

Email Templates

lua
function send_order_confirmation(order_id, customer_email)
    -- Template with HTML and plain text versions
    local html_template = [[
<!DOCTYPE html>
<html>
<head>
    <style>
        .header { background: #007bff; color: white; padding: 20px; }
        .item { border-bottom: 1px solid #eee; padding: 10px; }
        .total { font-weight: bold; font-size: 1.2em; }
    </style>
</head>
<body>
    <div class="header">
        <h1>Order Confirmation</h1>
    </div>
    <p>Dear {{ customer.name }},</p>
    <p>Thank you for your order #{{ order.id }}!</p>

    <h2>Order Details:</h2>
    {% for item in order.items %}
    <div class="item">
        {{ item.name }} x {{ item.quantity }} @ ${{ item.price | round: 2 }}
    </div>
    {% endfor %}

    <div class="total">
        Total: ${{ order.total | round: 2 }}
    </div>
</body>
</html>
    ]]

    local plain_template = [[
Order Confirmation

Dear {{ customer.name }},

Thank you for your order #{{ order.id }}!

Order Details:
{% for item in order.items %}
- {{ item.name }} x {{ item.quantity }} @ ${{ item.price | round: 2 }}
{% endfor %}

Total: ${{ order.total | round: 2 }}
    ]]

    -- Fetch order data
    local order_data = fetch_order_data(order_id)

    -- Render both versions
    local html_output = template.render_raw(html_template, order_data)
    local plain_output = template.render_raw(plain_template, order_data)

    -- Send multipart email
    return send_multipart_email(customer_email, "Order Confirmation", {
        html = html_output,
        plain = plain_output
    })
end

Report Generation

lua
function generate_monthly_report(month, year)
    local report_template = [[
# Monthly Report - {{ month }}/{{ year }}

## Executive Summary
Total Revenue: ${{ summary.revenue | round: 2 }}
Total Orders: {{ summary.order_count }}
Average Order Value: ${{ summary.avg_order_value | round: 2 }}

## Top Products
{% for product in top_products %}
{{ forloop.index }}. **{{ product.name }}**
   - Units Sold: {{ product.units }}
   - Revenue: ${{ product.revenue | round: 2 }}
{% endfor %}

## Customer Metrics
New Customers: {{ metrics.new_customers }}
Returning Customers: {{ metrics.returning_customers }}
Customer Retention Rate: {{ metrics.retention_rate | times: 100 | round: 1 }}%

## Trends
{% if trends.revenue_change > 0 %}
📈 Revenue increased by {{ trends.revenue_change | round: 1 }}%
{% else %}
📉 Revenue decreased by {{ trends.revenue_change | abs | round: 1 }}%
{% endif %}

---
Generated on {{ generated_date }}
    ]]

    -- Collect report data
    local report_data = collect_monthly_metrics(month, year)
    report_data.month = month
    report_data.year = year
    report_data.generated_date = os.date("%Y-%m-%d %H:%M:%S")

    return template.render_raw(report_template, report_data)
end

Dynamic Form Letters

lua
function generate_invoice(invoice_data)
    local invoice_template = [[
{% assign subtotal = 0 %}

INVOICE
=======

Invoice #: {{ invoice.number }}
Date: {{ invoice.date | date: "%B %d, %Y" }}
Due Date: {{ invoice.due_date | date: "%B %d, %Y" }}

Bill To:
{{ customer.name }}
{{ customer.address.street }}
{{ customer.address.city }}, {{ customer.address.state }} {{ customer.address.zip }}

Items:
------
{% for item in invoice.items %}
{% assign line_total = item.quantity | times: item.unit_price %}
{% assign subtotal = subtotal | plus: line_total %}
{{ item.description }}
  Qty: {{ item.quantity }} × ${{ item.unit_price | round: 2 }} = ${{ line_total | round: 2 }}
{% endfor %}

Subtotal: ${{ subtotal | round: 2 }}
{% if invoice.discount > 0 %}
Discount ({{ invoice.discount }}%): -${{ subtotal | times: invoice.discount | divided_by: 100 | round: 2 }}
{% assign subtotal = subtotal | times: 100 | minus: invoice.discount | divided_by: 100 %}
{% endif %}
Tax ({{ invoice.tax_rate }}%): ${{ subtotal | times: invoice.tax_rate | divided_by: 100 | round: 2 }}
{% assign total = subtotal | times: 100 | plus: invoice.tax_rate | divided_by: 100 %}

TOTAL DUE: ${{ total | round: 2 }}

Payment Terms: {{ invoice.payment_terms }}
    ]]

    return template.render_raw(invoice_template, invoice_data)
end

Best Practices

  1. Validate template data: Ensure all required fields exist

    lua
    function safe_template_render(template_id, data)
        -- Validate required fields
        local required_fields = {"name", "email", "order_id"}
    
        for _, field in ipairs(required_fields) do
            if not data[field] then
                print("Missing required field: " .. field)
                return false
            end
        end
    
        return template.render(template_id, data)
    end
  2. Handle missing variables gracefully: Use default filters

    lua
    local template_str = [[
    Hello {{ name | default: "Guest" }}!
    Your balance is ${{ balance | default: 0 | round: 2 }}
    ]]
  3. Escape user content: Prevent XSS in HTML templates

    lua
    local template_str = [[
    <p>Comment: {{ user_comment | escape }}</p>
    <div>{{ user_html | strip_html }}</div>
    ]]
  4. Cache compiled templates: Improve performance

    lua
    function cached_template_render(template_id, data)
        local cache_key = "template:compiled:" .. template_id
        local compiled = cache.get(cache_key)
    
        if not compiled then
            -- Fetch and compile template
            compiled = fetch_and_compile_template(template_id)
            cache.put_ttl(cache_key, compiled, 3600)
        end
    
        return render_compiled_template(compiled, data)
    end

Security and Performance

Security:

  • Requires actor permissions to access templates
  • Function execution runs in sandboxed Lua environment
  • User input is automatically escaped in HTML context
  • Template size limits prevent memory exhaustion

Performance:

  • Templates are compiled once and cached
  • Large datasets should be paginated
  • Complex calculations should be done in preprocessing functions
  • Avoid deeply nested loops in templates

Connect. Combine. Collaborate.
The pioneering open integration platform, dedicated to transforming connectivity in the printing industry.