--- version: "1.0" date: "2025-01-15" author: "DWS Foldsite Team" title: "Template Helper Functions" description: "Complete reference for all Jinja2 helper functions in Foldsite" summary: "Comprehensive API documentation for Foldsite's template helper functions - discover content, navigate your site, and build dynamic features." quick_tips: - "All helpers are automatically available in every template" - "Helpers return Python objects you can loop over and filter" - "Most helpers are cached for performance - safe to call multiple times" --- # Template Helper Functions Foldsite provides powerful helper functions you can use in any template. These functions access your content dynamically, enabling features like recent posts, navigation menus, breadcrumbs, and more. ## Content Discovery ### get_folder_contents(path) Get all files and folders in a directory with rich metadata. **Parameters:** - `path` (str, optional) - Relative path from content root. Defaults to current directory. **Returns:** List of `TemplateFile` objects with attributes: - `name` (str) - Filename with extension - `path` (str) - Relative path from content root - `proper_name` (str) - Filename without extension - `extension` (str) - File extension (`.md`, `.jpg`, etc.) - `categories` (list[str]) - File categories (`['document']`, `['image']`, etc.) - `date_modified` (str) - Last modified date (`YYYY-MM-DD`) - `date_created` (str) - Creation date (`YYYY-MM-DD`) - `size_kb` (int) - File size in kilobytes - `metadata` (dict | None) - Markdown frontmatter or image EXIF data - `dir_item_count` (int) - Number of items if it's a directory - `is_dir` (bool) - True if it's a directory **Example:** ```jinja

Files in this folder:

``` **Sort and filter:** ```jinja {# Sort by date, newest first #} {% for file in get_folder_contents()|sort(attribute='date_created', reverse=True) %} ... {% endfor %} {# Filter to only documents #} {% for file in get_folder_contents() %} {% if 'document' in file.categories %} {{ file.proper_name }} {% endif %} {% endfor %} ``` ### get_sibling_content_files(path) Get files in the same directory as the current page. **Parameters:** - `path` (str, optional) - Relative path. Defaults to current directory. **Returns:** List of tuples `(filename, relative_path)` **Example:** ```jinja ``` ### get_sibling_content_folders(path) Get folders in the same directory as the current page. **Parameters:** - `path` (str, optional) - Relative path. Defaults to current directory. **Returns:** List of tuples `(folder_name, relative_path)` **Example:** ```jinja ``` ### get_text_document_preview(path) Get a preview (first 100 characters) of a text document. **Parameters:** - `path` (str) - Relative path to the document **Returns:** String (first 100 characters of the file) **Example:** ```jinja {% for file in get_folder_contents() %} {% if 'document' in file.categories %}

{{ file.proper_name }}

{{ get_text_document_preview(file.path) }}...

{% endif %} {% endfor %} ``` ### get_rendered_markdown(path) Get fully rendered markdown content without Jinja2 templating. Perfect for displaying markdown files (like `index.md`) within folder views or embedding content from one markdown file into a template. **Parameters:** - `path` (str) - Relative path to the markdown file **Returns:** Dictionary with: - `html` (str | None) - Rendered HTML content from markdown - `metadata` (dict | None) - Frontmatter metadata from the markdown file - `exists` (bool) - True if file was found and rendered successfully **Example:** ```jinja {# Display index.md in a folder view #} {% set index_path = (currentPath + '/index.md') if currentPath else 'index.md' %} {% set index = get_rendered_markdown(index_path) %} {% if index.exists %}
{{ index.html | safe }} {% if index.metadata.author %}

By {{ index.metadata.author }}

{% endif %}
{% endif %} ``` **Use in folder templates:** ```jinja {# Show index.md content at the top of a folder listing #}
{% set index = get_rendered_markdown(currentPath + '/index.md') %} {% if index.exists %}
{{ index.html | safe }}
{% endif %} {# Then show other files in the folder #}
{% for file in get_folder_contents(currentPath) %} {% if file.name != 'index.md' %} {{ file.proper_name }} {% endif %} {% endfor %}
``` **Embed content from another file:** ```jinja {# Include shared content from another markdown file #} {% set about = get_rendered_markdown('about/team-bio.md') %} {% if about.exists %} {% endif %} ``` ### get_markdown_metadata(path) Get metadata (frontmatter) from a markdown file **without rendering the content**. Perfect for displaying static markdown metadata in any location - like showing post titles, descriptions, or custom fields without the overhead of rendering the full markdown. **Parameters:** - `path` (str) - Relative path to the markdown file **Returns:** Dictionary with: - `metadata` (dict | None) - Frontmatter metadata from the markdown file - `exists` (bool) - True if file was found successfully - `error` (str, optional) - Error message if reading failed **Example:** ```jinja {# Display metadata from a specific post without rendering it #} {% set post_meta = get_markdown_metadata('blog/my-awesome-post.md') %} {% if post_meta.exists %}

{{ post_meta.metadata.title }}

{{ post_meta.metadata.description }}

Published: {{ post_meta.metadata.date }}

{% for tag in post_meta.metadata.tags %} {{ tag }} {% endfor %}
Read more →
{% endif %} ``` **Build a custom post grid using metadata only:** ```jinja {# Create cards showing metadata from multiple posts #}
{% for post_path in ['blog/intro.md', 'blog/tutorial.md', 'blog/advanced.md'] %} {% set meta = get_markdown_metadata(post_path) %} {% if meta.exists %}

{{ meta.metadata.title }}

{% if meta.metadata.author %}

By {{ meta.metadata.author }}

{% endif %} {% if meta.metadata.excerpt %}

{{ meta.metadata.excerpt }}

{% endif %} Read full post
{% endif %} {% endfor %}
``` **Show custom metadata fields:** ```jinja {# Display reading time, difficulty, or any custom frontmatter field #} {% set meta = get_markdown_metadata('tutorials/python-basics.md') %} {% if meta.exists %}

{{ meta.metadata.title }}

{% if meta.metadata.difficulty %} {{ meta.metadata.difficulty }} {% endif %} {% if meta.metadata.reading_time %} ⏱ {{ meta.metadata.reading_time }} min read {% endif %} {% if meta.metadata.prerequisites %}
Prerequisites:
{% endif %}
{% endif %} ``` **Performance tip:** Use `get_markdown_metadata` instead of `get_rendered_markdown` when you only need frontmatter data. It's much faster since it doesn't process the markdown content. ## Blog Functions ### get_recent_posts(limit, folder) Get recent blog posts sorted by date (newest first). **Parameters:** - `limit` (int, optional) - Maximum number of posts. Default: 5 - `folder` (str, optional) - Search within specific folder. Default: "" (search everywhere) **Returns:** List of post dictionaries with: - `title` (str) - From frontmatter or filename - `date` (str) - From frontmatter - `path` (str) - Relative path to post - `url` (str) - Full URL to post - `metadata` (dict) - Full frontmatter including tags, description, etc. **Example:** ```jinja

Recent Posts

{% for post in get_recent_posts(limit=5, folder='blog') %}

{{ post.title }}

{% if post.metadata.description %}

{{ post.metadata.description }}

{% endif %} {% if post.metadata.tags %}
{% for tag in post.metadata.tags %} {{ tag }} {% endfor %}
{% endif %}
{% endfor %}
``` **Search in specific folder:** ```jinja {# Only posts from blog/tutorials/ #} {% for post in get_recent_posts(limit=10, folder='blog/tutorials') %} {{ post.title }} {% endfor %} ``` ### get_posts_by_tag(tag, limit) Get posts filtered by a specific tag. **Parameters:** - `tag` (str) - Tag to filter by (case-insensitive) - `limit` (int, optional) - Maximum posts. Default: 10 **Returns:** List of post dictionaries (same format as `get_recent_posts`) **Example:** ```jinja

Python Posts

{% for post in get_posts_by_tag('python', limit=10) %}

{{ post.title }}

{% endfor %} ``` **Dynamic tag pages:** ```jinja {# If currentPath is 'tags/python.md' #} {% set tag = currentPath.split('/')[-1].replace('.md', '') %}

Posts tagged: {{ tag }}

{% for post in get_posts_by_tag(tag) %} ... {% endfor %} ``` ### get_related_posts(current_post_path, limit) Find posts related to the current post based on shared tags. **Parameters:** - `current_post_path` (str) - Path to current post - `limit` (int, optional) - Maximum related posts. Default: 3 **Returns:** List of post dictionaries with an additional `overlap_score` field (number of shared tags) **Example:** ```jinja {% set related = get_related_posts(currentPath, limit=3) %} {% if related %} {% endif %} ``` ### get_all_tags() Get all tags used across the site with post counts. **Returns:** List of tag dictionaries with: - `name` (str) - Tag name - `count` (int) - Number of posts with this tag **Example:** ```jinja
{% for tag in get_all_tags() %} {{ tag.name }} ({{ tag.count }}) {% endfor %}
``` **Sort by popularity:** ```jinja {% for tag in get_all_tags()|sort(attribute='count', reverse=True) %} {{ tag.name }} {{ tag.count }} {% endfor %} ``` ## Navigation ### generate_breadcrumbs(current_path) Generate breadcrumb navigation based on URL path. **Parameters:** - `current_path` (str) - Current page path **Returns:** List of breadcrumb dictionaries with: - `title` (str) - Display title (from metadata or derived from path) - `url` (str) - URL to this level - `is_current` (bool) - True if this is the current page **Example:** ```jinja ``` **Styled example:** ```jinja ``` ### get_navigation_items(max_items) Get top-level pages and folders for site navigation. **Parameters:** - `max_items` (int, optional) - Maximum items to return. Default: 10 **Returns:** List of navigation dictionaries with: - `title` (str) - Display title (from metadata or filename) - `url` (str) - URL to page/folder - `path` (str) - Relative path - `is_folder` (bool, optional) - True if it's a folder **Example:** ```jinja ``` **With icons for folders:** ```jinja {% for item in get_navigation_items() %} {% if item.is_folder %}📁{% endif %} {{ item.title }} {% endfor %} ``` ## Gallery Functions ### get_photo_albums() Get all photo gallery directories (folders containing mostly images). **Returns:** List of album dictionaries with: - `name` (str) - Album name - `path` (str) - Relative path - `url` (str) - Full URL - `image_count` (int) - Number of images - `total_files` (int) - Total files in album **Example:** ```jinja

Photo Galleries

{% for album in get_photo_albums() %}

{{ album.name }}

{{ album.image_count }} photos

{% endfor %}
``` ## Built-in Jinja2 Filters In addition to Foldsite helpers, you can use standard Jinja2 filters: ### String Filters ```jinja {{ "hello"|upper }} {# HELLO #} {{ "HELLO"|lower }} {# hello #} {{ "hello world"|title }} {# Hello World #} {{ "hello world"|capitalize }} {# Hello world #} {{ " text "|trim }} {# text #} {{ "hello"|reverse }} {# olleh #} ``` ### List Filters ```jinja {{ items|length }} {# Count items #} {{ items|first }} {# First item #} {{ items|last }} {# Last item #} {{ items|join(', ') }} {# Join with comma #} {{ items|sort }} {# Sort list #} {{ items|sort(attribute='date') }} {# Sort by attribute #} {{ items|reverse }} {# Reverse list #} {{ items|unique }} {# Remove duplicates #} ``` ### Other Useful Filters ```jinja {{ value|default('N/A') }} {# Default if falsy #} {{ html|safe }} {# Don't escape HTML #} {{ number|round(2) }} {# Round to 2 decimals #} {{ date|replace('-', '/') }} {# Replace strings #} ``` ## Common Patterns ### Pattern: Blog Homepage with Recent Posts ```jinja

Welcome to My Blog

{% for post in get_recent_posts(limit=10) %}

{{ post.title }}

{% if post.metadata.description %}

{{ post.metadata.description }}

{% endif %} {% if post.metadata.tags %}
{% for tag in post.metadata.tags %} {{ tag }} {% endfor %}
{% endif %}
{% endfor %}
``` ### Pattern: Folder Index with Previews ```jinja

{{ currentPath.split('/')[-1]|title }}

{# Show index.md content if it exists #} {% set index = get_rendered_markdown(currentPath + '/index.md') %} {% if index.exists %}
{{ index.html | safe }}
{% endif %}
{% for file in get_folder_contents(currentPath)|sort(attribute='date_created', reverse=True) %} {% if 'document' in file.categories and file.name != 'index.md' %}

{{ file.proper_name }}

{{ file.date_created }}

{% if file.metadata and file.metadata.description %}

{{ file.metadata.description }}

{% endif %}
{% endif %} {% endfor %}
``` ### Pattern: Photo Gallery with EXIF Data ```jinja ``` ### Pattern: Documentation with Sibling Pages ```jinja
{{ content|safe }} {% set related = get_related_posts(currentPath, limit=3) %} {% if related %} {% endif %}
``` ## Performance Tips ### Caching Most helper functions are cached automatically. It's safe to call them multiple times: ```jinja {# These don't cause multiple filesystem scans #} {% set posts = get_recent_posts(limit=5) %} ... use posts ... {% set posts_again = get_recent_posts(limit=5) %} {# Cached result #} ``` ### Filtering vs. Multiple Calls Filter results in templates rather than calling helpers multiple times: ```jinja ✓ Efficient: {% set all_files = get_folder_contents() %} {% set docs = all_files|selectattr('categories', 'contains', 'document') %} {% set images = all_files|selectattr('categories', 'contains', 'image') %} ✗ Less efficient: {% for file in get_folder_contents() %} {% if 'document' in file.categories %}...{% endif %} {% endfor %} {% for file in get_folder_contents() %} {# Second call #} {% if 'image' in file.categories %}...{% endif %} {% endfor %} ``` ## Debugging Enable debug mode to see which templates and helpers are being used: ```toml # config.toml [server] debug = true ``` Add debug output to templates: ```jinja {# Show what get_recent_posts returns #} {% set posts = get_recent_posts(limit=3) %}
{{ posts|pprint }}
{# Show current variables #}
currentPath: {{ currentPath }}
metadata: {{ metadata|pprint }}
``` ## Next Steps - **[Template Recipes](../recipes/)** - See these helpers in complete working examples - **[Template System Overview](index.md)** - Understand how templates work - **[Template Discovery](template-discovery.md)** - Learn how Foldsite finds templates With these helpers, you can build sophisticated, dynamic sites while keeping your content as simple files and folders!