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
{{ 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.login }} {# Login URL #}
{{ cms.logout }} {# Logout URL #}
{{ cms.domain }} {# Current domain name #}
{{ cms.clearcache }} {# Emergency cache clear URL #}
Authentication & Access Control
{{ cms.login() }} {# Default login URL with current page redirect #}
{{ cms.login('collection') }} {# Collection login URL with current page redirect #}
{{ cms.login('', '') }} {# Default login URL with no redirect #}
{{ cms.login('', '/redirect/url') }} {# Default login with custom redirect URL #}
{{ cms.login('collection', '/redirect/url') }} {# Collection login with custom redirect URL #}
{{ cms.userData() }} {# Get current user data array #}
{{ cms.userLoggedIn() }} {# Check if user is logged in (boolean) #}
{{ cms.userLoggedIn('collection') }} {# Check login for specific collection #}
{{ cms.userHasAccess('group') }} {# Check if user has access to group #}
{{ cms.userHasAccess(['group1', 'group2']) }} {# Check multiple groups #}
{{ cms.sessionData('key') }} {# Get session data by key #}
{{ cms.verifyFilePassword(password, collection, id, property) }} {# Verify file password #}
Schemas
{{ cms.schemas() }} {# Get all schemas #}
{{ cms.reservedSchemas() }} {# Get built-in schemas #}
{{ cms.customSchemas() }} {# Get custom schemas #}
{{ cms.schemasByCategory() }} {# Get schemas grouped by category #}
{{ cms.schema('schemaName') }} {# Get specific schema definition #}
{{ cms.schemaForCollection('collection') }} {# Get schema for a collection #}
Collections
{{ cms.collections() }} {# Get all collections #}
{{ cms.collectionsByCategory() }} {# Get collections grouped by category #}
{{ cms.collection('collectionName') }} {# Get collection metadata #}
{{ cms.objectCount('collectionName') }} {# Get number of objects in collection #}
{{ cms.objects('collectionName') }} {# Get all objects from collection #}
{{ cms.property('collection', 'property') }} {# Get unique values from property #}
{{ cms.objectUrl('collection', 'id') }} {# Get URL for an object #}
Object Data
{{ cms.object('collection', 'id') }} {# Get complete object data #}
{{ cms.data('collection', 'id', 'property') }} {# Get specific property value #}
Search
{{ cms.search('collection', 'query', 'property') }} {# Search with single property #}
{{ cms.search('collection', 'query', ['prop1', 'prop2']) }} {# Search multiple properties #}
Text Content
{{ cms.text('id') }} {# Get text (default collection: text) #}
{{ cms.text('id', {collection: 'custom'}) }} {# Custom collection #}
{{ cms.text('id', {property: 'content'}) }} {# Custom property #}
{{ cms.code('id') }} {# Get code snippet (default collection: code) #}
{{ cms.code('id', {collection: 'custom'}) }} {# Custom collection #}
{{ cms.code('id', {property: 'snippet'}) }} {# Custom property #}
{{ cms.styledtext('id') }} {# Get styled text (HTML) #}
{{ cms.styledtext('id', {collection: 'custom', property: 'html'}) }}
Simple Data Types
{{ cms.toggle('id') }} {# Get boolean toggle value #}
{{ cms.toggle('id', {collection: 'settings', property: 'enabled'}) }}
{{ cms.date('id') }} {# Get date string #}
{{ cms.date('id', {collection: 'events', property: 'eventDate'}) }}
{{ cms.number('id') }} {# Get number value #}
{{ cms.number('id', {collection: 'stats', property: 'count'}) }}
{{ cms.url('id') }} {# Get URL #}
{{ cms.url('id', {collection: 'links', property: 'href'}) }}
{{ cms.email('id') }} {# Get email address #}
{{ cms.email('id', {}, true) }} {# Get email with HTML encoding (anti-spam) #}
{{ cms.svg('id') }} {# Get SVG content #}
{{ cms.svg('id', {collection: 'icons', property: 'svgData'}) }}
Colors
{% set myColor = cms.color('id') %} {# Get color data array #}
{% set myColor = cms.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
{# Basic image output #}
{{ cms.image('id') }} {# Returns complete <img> HTML #}
{{ cms.imagePath('id') }} {# Returns image URL only #}
{{ cms.alt('id') }} {# Get alt text #}
{# With ImageWorks transformations #}
{{ cms.image('id', {w: 800, h: 600, fit: 'crop'}) }}
{{ cms.imagePath('id', {w: 400, blur: 20, fm: 'webp'}) }}
{# Custom collections and properties #}
{{ cms.image('id', {}, {collection: 'products', property: 'photo'}) }}
{# Pass object directly (recommended) #}
{{ cms.image(object, {w: 600}, {collection: 'products', property: 'image'}) }}
{# Loading options #}
{{ cms.image('id', {}, {loading: 'eager'}) }} {# Default is 'lazy' #}
Galleries
Standard Gallery (Grid with Lightbox)
{# Complete gallery with lightbox #}
{{ cms.gallery('id') }} {# Default 300x200 thumbs #}
{{ cms.gallery('id', {w: 150, h: 150}) }} {# Custom thumb size #}
{{ cms.gallery('id', {w: 150}, {w: 1200}) }} {# Thumb and full size settings #}
{# Gallery with options #}
{{ cms.gallery('id', {w: 200}, {}, {
maxVisible: 8,
viewAllText: 'Show all photos',
loop: true,
download: false
}) }}
{# Individual gallery images #}
{{ cms.galleryImage('id', 'filename.jpg') }} {# Get specific image HTML #}
{{ cms.galleryPath('id', 'filename.jpg', {w: 800}) }} {# Get image URL #}
{{ cms.galleryAlt('id', 'filename.jpg') }} {# Get alt text #}
{{ cms.galleryImageData('id', 'filename.jpg') }} {# Get complete image data #}
{# Dynamic gallery images #}
{{ cms.galleryImage('id', 'first') }} {# First image #}
{{ cms.galleryImage('id', 'last') }} {# Last image #}
{{ cms.galleryImage('id', 'random') }} {# Random image #}
{{ cms.galleryImage('id', 'featured') }} {# Featured image #}
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.galleryLauncher('id') }} {# Outputs hidden template with gallery data #}
<button data-gallery="gallery-id">View Photos</button>
{# Custom thumbnail and full-size settings #}
{{ cms.galleryLauncher('id', {w: 300, h: 200}, {w: 1920, fit: 'contain'}) }}
{# With options #}
{{ cms.galleryLauncher('id', {w: 300}, {w: 1920}, {
collection: 'gallery',
property: 'gallery',
captions: true, {# Show alt text as captions #}
speed: 600,
loop: true,
download: false,
plugins: ['zoom', 'thumbnail', 'fullscreen']
}) }}
Triggering Gallery Launchers
Method 1: Using data-gallery attribute (recommended)
{{ cms.galleryLauncher('vacation') }}
<button data-gallery="gallery-vacation">View All Photos</button>
Method 2: Using CSS selector
{{ cms.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.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
By image filename (recommended):
{{ cms.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 (zero-based):
{{ cms.galleryLauncher('vacation') }}
<button data-gallery="gallery-vacation" data-gallery-index="0">First Image</button>
<button data-gallery="gallery-vacation" data-gallery-index="5">Image #6</button>
Complete Gallery Launcher Example
{# Create the gallery launcher #}
{{ cms.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- Show image alt text as captions (true/false)galleryId- Custom gallery ID (default:{collection}-{id})- 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
File Downloads & Streaming
Downloads (attachment; forces download)
{# Single file download #}
{{ cms.download('id') }} {# Default from 'file' collection #}
{{ cms.download('id', {collection: 'documents', property: 'pdf'}) }}
{{ cms.download('id', {pwd: 'secret123'}) }} {# Password-protected file #}
{# Depot (multiple files) #}
{% set files = cms.depot('id') %} {# Get files array #}
{% for file in files %}
<a href="{{ cms.depotDownload('id', file.name) }}">{{ file.name }}</a>
{% endfor %}
{# Depot with folders #}
{{ cms.depotDownload('id', 'document.pdf', {path: 'folder/subfolder'}) }}
{{ cms.depotDownload('id', 'folder/document.pdf') }} {# Path in filename #}
{{ cms.depotDownload('id', 'file.zip', {pwd: 'pass123'}) }}
Streaming (inline; plays in browser)
{# Single file streaming (ideal for video/audio) #}
{{ cms.stream('id') }} {# Default from 'file' collection #}
{{ cms.stream('id', {collection: 'videos', property: 'video'}) }}
{{ cms.stream('id', {pwd: 'secret123'}) }} {# Password-protected file #}
{# Depot file streaming #}
{{ cms.depotStream('id', 'video.mp4') }} {# Stream specific file #}
{{ cms.depotStream('id', 'movie.mp4', {path: 'folder/subfolder'}) }}
{{ cms.depotStream('id', 'folder/video.mp4') }} {# Path in filename #}
{{ cms.depotStream('id', 'audio.mp3', {pwd: 'pass123'}) }}
{# HTML5 video/audio examples #}
<video controls>
<source src="{{ cms.stream('video-id') }}" type="video/mp4">
</video>
<audio controls>
<source src="{{ cms.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
{# Simple pagination (Previous/Next only) #}
{{ cms.paginationSimple(totalObjects, currentPage, pageLimit) }}
{{ cms.paginationSimple(items|length, page, 10, 'page', 'Prev', 'Next') }}
{# Full pagination with page numbers #}
{{ cms.paginationFull(totalObjects, currentPage, pageLimit) }}
{{ cms.paginationFull(items|length, page, 10, 'p', '← Previous', 'Next →', {sort: 'date'}) }}
URL Helpers
{{ cms.prettyUrl('/blog/post.php') }} {# Convert to pretty URL #}
{{ cms.apacheRule(currentUrl, 'Blog Posts') }} {# Generate .htaccess rules #}
{{ cms.nginxRule(currentUrl, 'Products') }} {# Generate nginx rules #}
Form Builder Integration
{{ cms.form.render('formId') }} {# Render complete form #}
{{ cms.form.field('fieldType', 'name', 'value', {options}) }} {# Individual field #}
Grid Renderer
The grid renderer provides helper methods for content grids:
{{ cms.grid.date(item, 'M j, Y') }} {# Format date with fallback #}
{{ cms.grid.tags(item, '/blog/tag') }} {# Render tag list with links #}
{{ cms.grid.excerpt(item, 160) }} {# Generate excerpt #}
{{ cms.grid.price(item) }} {# Format price #}
{{ cms.grid.meta(item) }} {# Render metadata (author, date, etc) #}
Server & Diagnostics
{{ cms.checker.serverInfo() }} {# Server information array #}
{{ cms.checker.checkRequiredSoftware() }} {# Required software check #}
{{ cms.checker.checkOptionalSoftware() }} {# Optional software check #}
{{ cms.checker.getVersion() }} {# Total CMS version #}
{{ cms.cacheReporter.getStatus() }} {# Cache status #}
{{ cms.logger.getRecentErrors(10) }} {# Recent error logs #}
Job Queue
{{ cms.processJobQueueCommand() }} {# Get CLI command for processing jobs #}
{{ cms.jobQueuePendingInfo() }} {# HTML table of pending jobs #}
{{ cms.jobQueueFailedInfo() }} {# HTML table of failed jobs #}
Utility Functions
{{ cms.redirectIfNotFound(object) }} {# Redirect if object is empty #}
{{ cms.languages() }} {# Get supported languages array #}
ImageWorks Parameters
Common parameters for image transformations:
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
blur- Blur amount (0-100)sharp- Sharpen amount (0-100)pixel- Pixelate amount (0-100)filt- Filter:greyscale,sepia
Image Watermarks
mark- Watermark image pathmarkw- Watermark widthmarkh- Watermark heightmarkpos- Watermark positionmarkpad- Watermark paddingmarkalpha- Watermark opacity (0-100)
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
Display a blog post
{% set post = cms.object('blog', 'my-post-id') %}
<article>
<h1>{{ post.title }}</h1>
<time>{{ post.date|dateRelative }}</time>
{{ post.content|markdown }}
{{ cms.image(post.id, {w: 800, h: 400, fit: 'crop'}) }}
</article>
Create an image gallery
{% set product = cms.object('products', 'widget-pro') %}
<div class="product-gallery">
{{ cms.gallery(product.id, {w: 100, h: 100}, {w: 1200}, {
maxVisible: 4,
viewAllText: 'View all images'
}) }}
</div>
Create a gallery launcher with custom triggers
{% set product = cms.object('products', 'widget-pro') %}
{# Output the gallery launcher (hidden template) #}
{{ cms.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.galleryPath(product.id, 'front.jpg', {w: 600, h: 400}) }}">
<div class="thumbnail-strip">
<img class="product-thumb"
data-gallery-image="side.jpg"
src="{{ cms.galleryPath(product.id, 'side.jpg', {w: 100, h: 100}) }}">
<img class="product-thumb"
data-gallery-image="detail.jpg"
src="{{ cms.galleryPath(product.id, 'detail.jpg', {w: 100, h: 100}) }}">
</div>
<button data-gallery="gallery-{{ product.id }}">View All Photos</button>
</div>
Protected downloads
{% if cms.verifyFilePassword(password, 'documents', docId, 'file') %}
<a href="{{ cms.download(docId, {pwd: password}) }}">Download Document</a>
{% else %}
<p>Invalid password</p>
{% endif %}
Display object counts efficiently
{# Efficient: Uses cached collection metadata (no index loading) #}
<p>{{ cms.objectCount('blog') }} blog posts</p>
<p>{{ cms.objectCount('products') }} products available</p>
{# Avoid: Loads entire index just to count objects #}
<p>{{ cms.objects('blog')|length }} blog posts</p>
Search with pagination
{% set results = cms.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.paginationFull(results|length, page, perPage) }}
Text watermark examples
{# Simple text watermark #}
{{ cms.imagePath('hero-image', {
w: 1200,
h: 600,
marktext: 'Copyright 2024'
}) }}
{# Styled text watermark with custom font #}
{{ cms.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.imagePath('landscape', {
marktext: 'DRAFT',
marktextsize: 200,
marktextangle: -45,
marktextcolor: 'ff0000',
marktextpos: 'center',
marktextalpha: 50
}) }}
{# Responsive text width #}
{{ cms.imagePath('banner', {
w: 1200,
marktext: 'This is a very long watermark text that will wrap',
marktextw: '80w', {# 80% of image width #}
marktextpos: 'top'
}) }}