Total CMS Twig Adapter
The Total CMS Twig Adapter provides access to all CMS data and functionality through the global cms variable in Twig templates.
Configuration & Environment
Section titled “Configuration & Environment”{{ cms.env }} {# Current environment (development, production) #}{{ cms.config('key') }} {# Get config value by key #}{{ cms.config('key', 'setting') }} {# Get nested config setting #}{{ cms.api }} {# API base URL #}{{ cms.dashboard }} {# Admin dashboard URL #}{{ cms.domain }} {# Current domain name #}{{ cms.clearcache }} {# Emergency cache clear URL #}Authentication & Access Control
Section titled “Authentication & Access Control”{# Login #}{{ cms.auth.login() }} {# Default login URL with current page redirect #}{{ cms.auth.login('collection') }} {# Collection login URL with current page redirect #}{{ cms.auth.login('', '') }} {# Default login URL with no redirect #}{{ cms.auth.login('', '/redirect/url') }} {# Default login with custom redirect URL #}{{ cms.auth.login('collection', '/redirect/url') }} {# Collection login with custom redirect URL #}
{# Logout #}{{ cms.auth.logout() }} {# Logout URL #}{{ cms.auth.logout('/redirect/url') }} {# Logout with redirect URL #}{{ cms.auth.userData() }} {# Get current user data array #}{{ cms.auth.userLoggedIn() }} {# Check if user is logged in (boolean) #}{{ cms.auth.userLoggedIn('collection') }} {# Check login for specific collection #}{{ cms.auth.userHasAccess('group') }} {# Check if user has access to group #}{{ cms.auth.userHasAccess(['group1', 'group2']) }} {# Check multiple groups #}{{ cms.auth.sessionData('key') }} {# Get session data by key #}{{ cms.auth.verifyFilePassword(password, collection, id, property) }} {# Verify file password #}Schemas
Section titled “Schemas”{{ cms.schema.list() }} {# Get all schemas #}{{ cms.schema.reserved() }} {# Get built-in schemas #}{{ cms.schema.custom() }} {# Get custom schemas #}{{ cms.schema.byCategory() }} {# Get schemas grouped by category #}{{ cms.schema.get('schemaName') }} {# Get specific schema definition #}{{ cms.schema.forCollection('collection') }} {# Get schema for a collection #}Collections
Section titled “Collections”{{ cms.collection.list() }} {# Get all collections #}{{ cms.collection.byCategory() }} {# Get collections grouped by category #}{{ cms.collection.get('collectionName') }} {# Get collection metadata #}{{ cms.collection.objectCount('collectionName') }} {# Get number of objects in collection #}{{ cms.collection.objects('collectionName') }} {# Get all objects from collection #}{{ cms.collection.property('collection', 'property') }} {# Get unique values from property #}{{ cms.collection.objectUrl('collection', 'id') }} {# Get URL for an object #}Object Data
Section titled “Object Data”{{ cms.collection.object('collection', 'id') }} {# Get complete object data #}{{ cms.data.raw('collection', 'id', 'property') }} {# Get specific property value #}Search
Section titled “Search”{{ cms.collection.search('collection', 'query', 'property') }} {# Search with single property #}{{ cms.collection.search('collection', 'query', ['prop1', 'prop2']) }} {# Search multiple properties #}Text Content
Section titled “Text Content”{{ cms.data.text('id') }} {# Get text (default collection: text) #}{{ cms.data.text('id', {collection: 'custom'}) }} {# Custom collection #}{{ cms.data.text('id', {property: 'content'}) }} {# Custom property #}
{{ cms.data.code('id') }} {# Get code snippet (default collection: code) #}{{ cms.data.code('id', {collection: 'custom'}) }} {# Custom collection #}{{ cms.data.code('id', {property: 'snippet'}) }} {# Custom property #}
{{ cms.data.styledtext('id') }} {# Get styled text (HTML) #}{{ cms.data.styledtext('id', {collection: 'custom', property: 'html'}) }}Simple Data Types
Section titled “Simple Data Types”{{ cms.data.toggle('id') }} {# Get boolean toggle value #}{{ cms.data.toggle('id', {collection: 'settings', property: 'enabled'}) }}
{{ cms.data.date('id') }} {# Get date string #}{{ cms.data.date('id', {collection: 'events', property: 'eventDate'}) }}
{{ cms.data.number('id') }} {# Get number value #}{{ cms.data.number('id', {collection: 'stats', property: 'count'}) }}
{{ cms.data.url('id') }} {# Get URL #}{{ cms.data.url('id', {collection: 'links', property: 'href'}) }}
{{ cms.data.email('id') }} {# Get email address #}{{ cms.data.email('id', {}, true) }} {# Get email with HTML encoding (anti-spam) #}
{{ cms.data.svg('id') }} {# Get SVG content #}{{ cms.data.svg('id', {collection: 'icons', property: 'svgData'}) }}Colors
Section titled “Colors”{% set myColor = cms.data.color('id') %} {# Get color data array #}{% set myColor = cms.data.colour('id') %} {# British spelling alias #}
{# Color has 'hex' and 'oklch' properties #}{{ myColor.hex }} {# Hex value: #ff0000 #}{{ myColor.oklch.l }} {# Lightness #}{{ myColor.oklch.c }} {# Chroma #}{{ myColor.oklch.h }} {# Hue #}
{# Use with color filters #}{{ myColor|hex }} {# Output: #ff0000 #}{{ myColor|oklch }} {# Output: oklch(62.8% 0.25768 29.234) #}{{ myColor|rgb }} {# Output: rgb(255 0 0) #}{{ myColor|hsl }} {# Output: hsl(0 100% 50%) #}Images
Section titled “Images”{# Basic image output #}{{ cms.render.image('id') }} {# Returns complete <img> HTML #}{{ cms.media.imagePath('id') }} {# Returns image URL only #}{{ cms.render.alt('id') }} {# Get alt text #}
{# With ImageWorks transformations #}{{ cms.render.image('id', {w: 800, h: 600, fit: 'crop'}) }}{{ cms.media.imagePath('id', {w: 400, blur: 20, fm: 'webp'}) }}
{# Custom collections and properties #}{{ cms.render.image('id', {}, {collection: 'products', property: 'photo'}) }}
{# Pass object directly (recommended) #}{{ cms.render.image(object, {w: 600}, {collection: 'products', property: 'image'}) }}
{# Loading options #}{{ cms.render.image('id', {}, {loading: 'eager'}) }} {# Default is 'lazy' #}Galleries
Section titled “Galleries”Standard Gallery (Grid with Lightbox)
Section titled “Standard Gallery (Grid with Lightbox)”{# Complete gallery with lightbox #}{{ cms.render.gallery('id') }} {# Default 300x200 thumbs #}{{ cms.render.gallery('id', {w: 150, h: 150}) }} {# Custom thumb size #}{{ cms.render.gallery('id', {w: 150}, {w: 1200}) }} {# Thumb and full size settings #}
{# Gallery with options #}{{ cms.render.gallery('id', {w: 200}, {}, { maxVisible: 8, viewAllText: 'Show all photos', loop: true, download: false, captions: true, gridCaptions: true, sort: 'name'}) }}
{# Individual gallery images #}{{ cms.render.galleryImage('id', 'filename.jpg') }} {# Get specific image HTML #}{{ cms.media.galleryPath('id', 'filename.jpg', {w: 800}) }} {# Get image URL #}{{ cms.render.galleryAlt('id', 'filename.jpg') }} {# Get alt text #}{{ cms.media.galleryImageData('id', 'filename.jpg') }} {# Get complete image data #}
{# Dynamic gallery images #}{{ cms.render.galleryImage('id', 'first') }} {# First image #}{{ cms.render.galleryImage('id', 'last') }} {# Last image #}{{ cms.render.galleryImage('id', 'random') }} {# Random image #}{{ cms.render.galleryImage('id', 'featured') }} {# Featured image #}Note: cms.galleryImage automatically includes data-gallery and data-gallery-image attributes, making it easy to use with cms.galleryLauncher.
Gallery Launcher (Trigger-Based Lightbox)
Section titled “Gallery Launcher (Trigger-Based Lightbox)”The gallery launcher allows you to open a lightbox from custom trigger elements like buttons, links, or thumbnails. Perfect for launching galleries without showing a visible grid.
{# Basic gallery launcher #}{{ cms.render.galleryLauncher('id') }} {# Outputs hidden template with gallery data #}<button data-gallery="gallery-id">View Photos</button>
{# Custom thumbnail and full-size settings #}{{ cms.render.galleryLauncher('id', {w: 300, h: 200}, {w: 1920, fit: 'contain'}) }}
{# With options #}{{ cms.render.galleryLauncher('id', {w: 300}, {w: 1920}, { collection: 'gallery', property: 'gallery', captions: true, {# Show captions in lightbox #} speed: 600, loop: true, download: false, plugins: ['zoom', 'thumbnail', 'fullscreen']}) }}Triggering Gallery Launchers
Section titled “Triggering Gallery Launchers”Method 1: Using data-gallery attribute (recommended)
{{ cms.render.galleryLauncher('vacation') }}<button data-gallery="gallery-vacation">View All Photos</button>Method 2: Using CSS selector
{{ cms.render.galleryLauncher('vacation', {}, {}, { trigger: '.open-gallery-btn' {# Any element with this class triggers gallery #}}) }}<button class="open-gallery-btn">View Gallery</button><a class="open-gallery-btn">See Photos</a>Method 3: Combined (both data-gallery and CSS selector)
{{ cms.render.galleryLauncher('vacation', {}, {}, { trigger: '.gallery-thumb'}) }}<button data-gallery="gallery-vacation">View All</button><img class="gallery-thumb" src="thumb1.jpg"><img class="gallery-thumb" src="thumb2.jpg">Opening at Specific Image
Section titled “Opening at Specific Image”By image filename (recommended):
{{ cms.render.galleryLauncher('vacation') }}
{# These thumbnails open gallery at their specific image #}<img data-gallery="gallery-vacation" data-gallery-image="sunset.jpg" src="sunset-thumb.jpg">
<img data-gallery="gallery-vacation" data-gallery-image="beach.jpg" src="beach-thumb.jpg">By index (1-based):
{{ cms.render.galleryLauncher('vacation') }}<button data-gallery="gallery-vacation" data-gallery-index="1">First Image</button><button data-gallery="gallery-vacation" data-gallery-index="6">Image #6</button>Using galleryImage (recommended):
{{ cms.render.galleryLauncher('vacation') }}
{# galleryImage automatically includes data-gallery and data-gallery-image attributes #}{{ cms.render.galleryImage('vacation', 'sunset.jpg', {w: 300, h: 200}) }}{{ cms.render.galleryImage('vacation', 'beach.jpg', {w: 300, h: 200}) }}
{# Works with dynamic selectors too #}{{ cms.render.galleryImage('vacation', 'first', {w: 400}) }}{{ cms.render.galleryImage('vacation', 'featured', {w: 400}) }}Complete Gallery Launcher Example
Section titled “Complete Gallery Launcher Example”{# Create the gallery launcher #}{{ cms.render.galleryLauncher('products', {w: 400, h: 300}, {# Thumbnail settings #} {w: 1920, fit: 'contain'}, {# Full-size settings #} { captions: true, speed: 400, loop: true, trigger: '.product-image', plugins: ['zoom', 'fullscreen'] }) }}
{# Display product thumbnails that trigger the gallery #}<div class="product-grid"> <img class="product-image" data-gallery-image="front-view.jpg" src="thumb-front.jpg" alt="Front view">
<img class="product-image" data-gallery-image="side-view.jpg" src="thumb-side.jpg" alt="Side view">
<img class="product-image" data-gallery-image="detail.jpg" src="thumb-detail.jpg" alt="Close-up detail">
<button data-gallery="gallery-products">View All Images</button></div>Gallery Launcher Options:
trigger- CSS selector for trigger elements (in addition to data-gallery)captions- Lightbox captions:truefor default, or a Twig template string (see Gallery Captions)galleryId- Custom gallery ID (default:{collection}-{id})sort- Sort images by property before rendering (see Sorting Gallery Images)- All standard LightGallery options:
speed,loop,download,counter,plugins, etc. - Note: Grid-specific options (
maxVisible,viewAllText) don’t apply to gallery launchers
When to Use Gallery Launchers:
- Custom thumbnail layouts that don’t fit the grid pattern
- Opening galleries from buttons or text links
- Multiple different triggers for the same gallery
- Launching galleries from specific images in your custom layout
- Image-based navigation where clicking a product photo opens full gallery
Gallery Captions
Section titled “Gallery Captions”Galleries support two independent caption options:
captions- Captions inside the lightbox overlaygridCaptions- Captions below thumbnails in the grid
Both accept either true for default behavior or a Twig template string for custom formatting.
Default Captions
Section titled “Default Captions”When set to true, captions use this fallback chain: alt text → EXIF title → EXIF description. If none are available, no caption is shown. Filenames are never used as captions.
{{ cms.render.gallery('id', {w: 300}, {w: 1500}, { captions: true, gridCaptions: true}) }}Caption Templates
Section titled “Caption Templates”Pass a template string to customize caption content. Use single curly braces {variable} for template variables — they are automatically converted to Twig syntax before rendering. Standard Twig features (conditionals, filters) work inside the braces.
{# Simple alt text caption #}{% set caption = "{alt}" %}{{ cms.render.gallery('id', {}, {}, {captions: caption}) }}
{# Photography captions with EXIF data #}{% set caption %}<h4>{alt}</h4><p>{exif.camera} · {exif.lens}</p><p>f/{exif.aperture} · {exif.shutterSpeed} · ISO {exif.iso}</p>{% endset %}{{ cms.render.gallery('id', {}, {}, {captions: caption}) }}
{# Different templates for grid and lightbox #}{% set gridCaption = "{alt}" %}{% set lightboxCaption %}<h4>{alt}</h4><p>{exif.description}</p><p>{exif.camera} — f/{exif.aperture}</p>{% endset %}
{{ cms.render.gallery('id', {w: 300}, {w: 1500}, { gridCaptions: gridCaption, captions: lightboxCaption}) }}When a template string is used, HTML is preserved (not escaped), so you can include markup like <h4>, <p>, <span>, etc. When set to true, plain text captions are HTML-escaped.
If all template variables resolve to empty, the caption is suppressed entirely (empty HTML tags are not shown).
Available Template Variables
Section titled “Available Template Variables”Image fields:
| Variable | Description |
|---|---|
alt | Alt text |
name | Filename |
width | Image width in pixels |
height | Image height in pixels |
mime | MIME type |
size | File size in bytes |
link | Optional link URL |
tags | Array of tags |
uploadDate | Upload date (ISO 8601) |
EXIF metadata (available when the image contains EXIF data):
| Variable | Description |
|---|---|
exif.title | Image title |
exif.description | Image description |
exif.camera | Camera model |
exif.make | Camera manufacturer |
exif.lens | Lens model |
exif.aperture | f-number (e.g., 1.8) |
exif.shutterSpeed | Shutter speed (e.g., 1/125) |
exif.iso | ISO sensitivity |
exif.focalLength | Focal length in mm |
exif.date | Photo capture date |
exif.author | Photographer name |
exif.copyright | Copyright notice |
exif.city | City |
exif.state | State/Province |
exif.country | Country |
exif.sublocation | Specific location |
exif.latitude | GPS latitude |
exif.longitude | GPS longitude |
exif.altitude | GPS altitude |
Gallery Grid Styling
Section titled “Gallery Grid Styling”The gallery grid uses CSS Grid with a customizable minimum column size via the --cms-gallery-min-size CSS variable (default: 150px). Smaller values produce more columns; larger values produce fewer.
/* Change the minimum thumbnail size globally */:root { --cms-gallery-min-size: 250px;}
/* Or override per gallery using the class option */.large-thumbs { --cms-gallery-min-size: 300px;}{{ cms.render.gallery('id', {}, {}, {class: 'large-thumbs'}) }}Sorting Gallery Images
Section titled “Sorting Gallery Images”By default, gallery images display in their stored order. Use the sort option to sort images by any image property. This works with both cms.render.gallery() and cms.render.galleryLauncher().
Simple Sort (Single Property)
Section titled “Simple Sort (Single Property)”{# Sort alphabetically by filename #}{{ cms.render.gallery('id', {}, {}, {sort: 'name'}) }}
{# Sort by upload date (oldest first) #}{{ cms.render.gallery('id', {}, {}, {sort: 'uploadDate'}) }}
{# Sort by EXIF capture date (oldest first) #}{{ cms.render.gallery('id', {}, {}, {sort: 'exif.date'}) }}Reverse Sort (Descending)
Section titled “Reverse Sort (Descending)”Prefix the property name with - to sort in descending order:
{# Newest photos first (by EXIF date) #}{{ cms.render.gallery('id', {}, {}, {sort: '-exif.date'}) }}
{# Most recently uploaded first #}{{ cms.render.gallery('id', {}, {}, {sort: '-uploadDate'}) }}
{# Largest files first #}{{ cms.render.gallery('id', {}, {}, {sort: '-size'}) }}Multi-Criteria Sort
Section titled “Multi-Criteria Sort”Pass an array of rule objects for advanced sorting with multiple criteria:
{# Featured images first, then sorted by EXIF date (newest first) #}{{ cms.render.gallery('id', {}, {}, {sort: [ {property: 'featured', reverse: true}, {property: 'exif.date', reverse: true}]}) }}
{# Sort by camera model, then by filename within each group #}{{ cms.render.gallery('id', {}, {}, {sort: [ {property: 'exif.camera'}, {property: 'name'}]}) }}With Gallery Launcher
Section titled “With Gallery Launcher”{{ cms.render.galleryLauncher('vacation', {w: 300}, {w: 1920}, { sort: '-exif.date', captions: true, loop: true}) }}Sortable Properties
Section titled “Sortable Properties”| Property | Description |
|---|---|
name | Filename (natural sort) |
uploadDate | When the image was uploaded (ISO 8601) |
size | File size in bytes |
width | Image width in pixels |
height | Image height in pixels |
featured | Boolean — useful for sorting featured images first |
exif.date | Date photo was taken |
exif.camera | Camera model |
exif.focalLength | Focal length |
exif.aperture | Aperture value |
exif.iso | ISO sensitivity |
File Downloads & Streaming
Section titled “File Downloads & Streaming”Downloads (attachment; forces download)
Section titled “Downloads (attachment; forces download)”{# Single file download #}{{ cms.media.download('id') }} {# Default from 'file' collection #}{{ cms.media.download('id', {collection: 'documents', property: 'pdf'}) }}{{ cms.media.download('id', {pwd: 'secret123'}) }} {# Password-protected file #}
{# Depot (multiple files) #}{% set files = cms.media.depot('id') %} {# Get files array #}{% for file in files %} <a href="{{ cms.media.depotDownload('id', file.name) }}">{{ file.name }}</a>{% endfor %}
{# Depot with folders #}{{ cms.media.depotDownload('id', 'document.pdf', {path: 'folder/subfolder'}) }}{{ cms.media.depotDownload('id', 'folder/document.pdf') }} {# Path in filename #}{{ cms.media.depotDownload('id', 'file.zip', {pwd: 'pass123'}) }}Streaming (inline; plays in browser)
Section titled “Streaming (inline; plays in browser)”{# Single file streaming (ideal for video/audio) #}{{ cms.media.stream('id') }} {# Default from 'file' collection #}{{ cms.media.stream('id', {collection: 'videos', property: 'video'}) }}{{ cms.media.stream('id', {pwd: 'secret123'}) }} {# Password-protected file #}
{# Depot file streaming #}{{ cms.media.depotStream('id', 'video.mp4') }} {# Stream specific file #}{{ cms.media.depotStream('id', 'movie.mp4', {path: 'folder/subfolder'}) }}{{ cms.media.depotStream('id', 'folder/video.mp4') }} {# Path in filename #}{{ cms.media.depotStream('id', 'audio.mp3', {pwd: 'pass123'}) }}
{# HTML5 video/audio examples #}<video controls> <source src="{{ cms.media.stream('video-id') }}" type="video/mp4"></video>
<audio controls> <source src="{{ cms.media.depotStream('audio-id', 'song.mp3') }}" type="audio/mpeg"></audio>Stream vs Download:
- Stream: Content-Disposition: inline, supports HTTP range requests, ideal for media files
- Download: Content-Disposition: attachment, forces download dialog
- Both support password protection and automatic encryption
Pagination
Section titled “Pagination”{# Simple pagination (Previous/Next only) #}{{ cms.render.paginationSimple(totalObjects, currentPage, pageLimit) }}{{ cms.render.paginationSimple(items|length, page, 10, 'page', 'Prev', 'Next') }}
{# Full pagination with page numbers #}{{ cms.render.paginationFull(totalObjects, currentPage, pageLimit) }}{{ cms.render.paginationFull(items|length, page, 10, 'p', '← Previous', 'Next →', {sort: 'date'}) }}URL Helpers
Section titled “URL Helpers”{{ cms.collection.prettyUrl('/blog/post.php') }} {# Convert to pretty URL #}{{ cms.admin.apacheRule(currentUrl, 'Blog Posts') }} {# Generate .htaccess rules #}{{ cms.admin.nginxRule(currentUrl, 'Products') }} {# Generate nginx rules #}Form Builder Integration
Section titled “Form Builder Integration”{{ cms.form.render('formId') }} {# Render complete form #}{{ cms.form.field('fieldType', 'name', 'value', {options}) }} {# Individual field #}Grid Renderer
Section titled “Grid Renderer”The grid renderer provides helper methods for content grids:
{{ cms.render.grid.date(item, 'M j, Y') }} {# Format date with fallback #}{{ cms.render.grid.tags(item, '/blog/tag') }} {# Render tag list with links #}{{ cms.render.grid.excerpt(item, 160) }} {# Generate excerpt #}{{ cms.render.grid.price(item) }} {# Format price #}{{ cms.render.grid.meta(item) }} {# Render metadata (author, date, etc) #}Render Helpers
Section titled “Render Helpers”{# Load more items from a collection (HTMX-powered pagination) #}{{ cms.render.loadMore('blog', {template: 'blog/card.twig', limit: 10}) }}
{# Load more items from a DataView #}{{ cms.render.loadMoreDataView('recent-posts', {template: 'blog/card.twig', limit: 10}) }}See Load More Documentation for full options and examples.
Server & Diagnostics
Section titled “Server & Diagnostics”{{ cms.admin.checker.serverInfo() }} {# Server information array #}{{ cms.admin.checker.checkRequiredSoftware() }} {# Required software check #}{{ cms.admin.checker.checkOptionalSoftware() }} {# Optional software check #}{{ cms.admin.checker.getVersion() }} {# Total CMS version #}
{{ cms.admin.cacheReporter.getStatus() }} {# Cache status #}{{ cms.admin.logAnalyzer.getRecentErrors(10) }} {# Recent error logs #}Job Queue
Section titled “Job Queue”{{ cms.admin.processJobQueueCommand() }} {# Get CLI command for processing jobs #}{{ cms.admin.jobQueuePendingInfo() }} {# HTML table of pending jobs #}{{ cms.admin.jobQueueFailedInfo() }} {# HTML table of failed jobs #}Logging
Section titled “Logging”Log messages from your templates to help with debugging. Messages are written to twig.log and viewable in the admin log analyzer.
{{ cms.log('Something unexpected happened') }}{{ cms.log('Missing image for product', 'error') }}{{ cms.log('Debug info', 'debug', { id: object.id }) }}Parameters
Section titled “Parameters”| Parameter | Type | Default | Description |
|---|---|---|---|
message | string | required | The message to log |
level | string | 'warning' | Log level: debug, info, warning, error |
context | object | {} | Additional context data to include in the log entry |
Utility Functions
Section titled “Utility Functions”{{ cms.collection.redirectIfNotFound(object) }} {# Redirect if object is empty #}{{ cms.locale.languages() }} {# Get supported languages array #}ImageWorks Parameters
Section titled “ImageWorks Parameters”Common parameters for image transformations:
Basic Image Controls
Section titled “Basic Image Controls”w- Width in pixelsh- Height in pixelsfit- How to fit image:contain,max,fill,stretch,cropcrop- Crop position:top-left,top,top-right,left,center,right,bottom-left,bottom,bottom-rightfm- Output format:jpg,png,gif,webp,avifq- Quality (1-100)
Effects & Filters
Section titled “Effects & Filters”blur- Blur amount (0-100)sharp- Sharpen amount (0-100)pixel- Pixelate amount (0-100)filt- Filter:greyscale,sepia
Image Watermarks
Section titled “Image Watermarks”mark- Watermark image pathmarkw- Watermark widthmarkh- Watermark heightmarkpos- Watermark positionmarkpad- Watermark paddingmarkalpha- Watermark opacity (0-100)
Text Watermarks
Section titled “Text Watermarks”marktext- Text to display as watermarkmarktextfont- Font family name (TTF/OTF fonts from watermark-fonts depot)marktextsize- Text size in pixels (default: 500)marktextcolor- Text color as hex (without #, e.g., ‘ffffff’ for white)marktextbg- Background color as hex (optional, transparent if not set)marktextpad- Padding around text in pixels (default: 10)marktextangle- Text rotation angle in degrees (-360 to 360, default: 0)marktextpos- Text position:top-left,top,top-right,left,center,right,bottom-left,bottom,bottom-rightmarktextw- Maximum text width in pixels or relative (e.g., ‘50w’ for 50% of image width)marktextalpha- Text transparency (0-100, where 100 is fully opaque)
Examples
Section titled “Examples”Display a blog post
Section titled “Display a blog post”{% set post = cms.collection.object('blog', 'my-post-id') %}<article> <h1>{{ post.title }}</h1> <time>{{ post.date|dateRelative }}</time> {{ post.content|markdown }} {{ cms.render.image(post.id, {w: 800, h: 400, fit: 'crop'}) }}</article>Create an image gallery
Section titled “Create an image gallery”{% set product = cms.collection.object('products', 'widget-pro') %}<div class="product-gallery"> {{ cms.render.gallery(product.id, {w: 100, h: 100}, {w: 1200}, { maxVisible: 4, viewAllText: 'View all images' }) }}</div>Create a gallery launcher with custom triggers
Section titled “Create a gallery launcher with custom triggers”{% set product = cms.collection.object('products', 'widget-pro') %}
{# Output the gallery launcher (hidden template) #}{{ cms.render.galleryLauncher(product.id, {w: 300, h: 300}, {w: 1920}, { captions: true, trigger: '.product-thumb', plugins: ['zoom', 'fullscreen']}) }}
{# Display custom layout with gallery triggers #}<div class="product-images"> <img class="product-thumb main-image" data-gallery-image="front.jpg" src="{{ cms.media.galleryPath(product.id, 'front.jpg', {w: 600, h: 400}) }}">
<div class="thumbnail-strip"> <img class="product-thumb" data-gallery-image="side.jpg" src="{{ cms.media.galleryPath(product.id, 'side.jpg', {w: 100, h: 100}) }}"> <img class="product-thumb" data-gallery-image="detail.jpg" src="{{ cms.media.galleryPath(product.id, 'detail.jpg', {w: 100, h: 100}) }}"> </div>
<button data-gallery="gallery-{{ product.id }}">View All Photos</button></div>Protected downloads
Section titled “Protected downloads”{% if cms.auth.verifyFilePassword(password, 'documents', docId, 'file') %} <a href="{{ cms.media.download(docId, {pwd: password}) }}">Download Document</a>{% else %} <p>Invalid password</p>{% endif %}Display object counts efficiently
Section titled “Display object counts efficiently”{# Efficient: Uses cached collection metadata (no index loading) #}<p>{{ cms.collection.objectCount('blog') }} blog posts</p><p>{{ cms.collection.objectCount('products') }} products available</p>
{# Avoid: Loads entire index just to count objects #}<p>{{ cms.collection.objects('blog')|length }} blog posts</p>Search with pagination
Section titled “Search with pagination”{% set results = cms.collection.search('blog', query, ['title', 'content', 'tags']) %}{% set page = app.request.get('page', 1) %}{% set perPage = 10 %}{% set paged = results|paginate(perPage, page) %}
{% for item in paged %} <article>{{ item.title }}</article>{% endfor %}
{{ cms.render.paginationFull(results|length, page, perPage) }}Text watermark examples
Section titled “Text watermark examples”{# Simple text watermark #}{{ cms.media.imagePath('hero-image', { w: 1200, h: 600, marktext: 'Copyright 2024'}) }}
{# Styled text watermark with custom font #}{{ cms.media.imagePath('product-photo', { w: 800, marktext: 'Premium Quality', marktextfont: 'Dorsa-Regular', marktextsize: 120, marktextcolor: 'ffffff', marktextbg: '000000', marktextpad: 20, marktextpos: 'bottom-right', marktextalpha: 80}) }}
{# Rotated watermark #}{{ cms.media.imagePath('landscape', { marktext: 'DRAFT', marktextsize: 200, marktextangle: -45, marktextcolor: 'ff0000', marktextpos: 'center', marktextalpha: 50}) }}
{# Responsive text width #}{{ cms.media.imagePath('banner', { w: 1200, marktext: 'This is a very long watermark text that will wrap', marktextw: '80w', {# 80% of image width #} marktextpos: 'top'}) }}