Templates – lo básico
El HTML de la página, el Head, el título SEO, la descripción y los Snippets son templates. La sintaxis se corresponde en gran medida con Twig o Django (pongo2).
Salida
{{ page.title }}
{{ post.titel|upper }}
{{ preis|floatformat:2 }} €
{{ beschreibung|default:"Sin descripción" }}
Las salidas se escapan automáticamente como HTML: < se convierte en <. Eso protege contra marcado roto y código inyectado. Para HTML que realmente deba aparecer como HTML:
- Campos HTML:
{{ post.inhalt|safe }} - Campos Markdown:
{{ post.text|markdown }}
Condiciones y bucles
{% if page.slug == "kontakt" %}
…
{% elif route.query.seite %}
…
{% else %}
…
{% endif %}
{% for p in data("team") %}
<li class="{% if forloop.First %}erste{% endif %}">{{ forloop.Counter }}. {{ p.name }}</li>
{% empty %}
<li>Todavía no hay nadie.</li>
{% endfor %}
En los bucles están disponibles forloop.Counter (desde 1), forloop.Counter0 (desde 0), forloop.First, forloop.Last y forloop.Revcounter.
Definir variables
{% with post=item("blog", "slug", route.slug) %}
<h1>{{ post.titel }}</h1>
{% endwith %}
Comentarios
{# No se muestra #}
Espacios en blanco
Las etiquetas de template no modifican los espacios ni los saltos de línea: el texto se emite exactamente tal como está (igual que en Twig). Hallo {% if name %}{{ name }}{% endif %}! conserva el espacio después de «Hallo».
Mostrar sintaxis de template: verbatim
Si necesitas {{ … }} de forma literal, por ejemplo para Vue, Alpine o Handlebars, envuelve esa zona:
{% verbatim %}<div id="app">{{ nachricht }}</div>{% endverbatim %}
El importador añade verbatim automáticamente si el original contiene esas secuencias.
Variables disponibles
| Variable | Contenido |
|---|---|
page.title, page.meta_title, page.slug, page.path, page.description, page.id, page.parent_id |
Página actual (meta_title y description ya están evaluados) |
page.noindex, page.nofollow |
Ajustes de robots de la página (true/false) |
page.lang |
Idioma efectivo de la página – propio o heredado, si no de (consulta Sitios multilingües) |
route.path |
Ruta solicitada |
route.slug |
Subruta en páginas Catch-All, p. ej. "mein-artikel" o "2025/rueckblick" |
route.rest |
Subruta como lista: route.rest.0, route.rest.1 … |
route.query.x |
Parámetro de consulta ?x=… |
today |
Fecha de hoy como AAAA-MM-DD, a juego con los campos de fecha, p. ej. data("events", "datum__gte", today) para las citas futuras |
now |
Momento actual para el filtro date: {{ now|date:"2006" }} (año en el copyright) |
site.title, site.id, site.domain, site.domains, site.favicon |
El sitio web actual (su página de inicio): site.domain es el primer dominio registrado y site.favicon, la URL del icono del sitio web |
preview |
true en la vista previa del admin |
Funciones
| Función | Resultado |
|---|---|
nav() |
Subpáginas publicadas de la página de inicio del sitio web actual (menú principal), sin las páginas con Ocultar en menús; nav(true) con ellas |
children() / children(12) |
Subpáginas publicadas de la página actual o de la página 12, sin las ocultas; children(true) / children(12, true) con ellas |
lang_root() / lang_root(12) |
Página de inicio del árbol de idioma de la página actual o de la página 12: la propia página o la más cercana por encima con su propio idioma; si no, la página de inicio del sitio. Para menús por idioma: children(lang_root().id) |
data("slug", …) |
Entradas de una Collection o resultado de una fuente REST (consulta Datos) |
item("slug", "feld", wert) |
Primera entrada con feld == wert, o nada |
workflow("pfad", …) |
Resultado de un Workflow con entrada HTTP en esa ruta |
image(id, sizes, klasse) |
<img> responsivo con srcset, dimensiones y texto alternativo; en JPEG/PNG, dentro de <picture> con WebP |
file(id) |
Información del archivo: .url, .name, .alt, .width, .height, .srcset, .webp_srcset, .mime, .size |
asset_url(id) |
URL versionada de un archivo CSS/JS |
translations() / translations(entrada) |
Versiones de idioma publicadas de la página o de una entrada, ordenadas por código de idioma: cada una con lang, title, url, current – para selectores de idioma |
language_name("fr") |
Nombre de un idioma en ese mismo idioma («Français», «Deutsch» …); los códigos desconocidos se quedan como están |
not_found() |
Termina el renderizado con la página 404 (estado 404) |
Cada página de nav()/children() (y lang_root()) tiene title, path, slug, id, description y lang, además de hidden (Ocultar en menús). Ejemplo de pie de página con aviso legal y privacidad, que no aparecen en el menú principal:
<footer>{% for p in nav(true) %}{% if p.hidden %}<a href="{{ p.path }}">{{ p.title }}</a> {% endif %}{% endfor %}</footer>
Filtros útiles
| Filtro | Ejemplo |
|---|---|
default |
{{ x|default:"–" }} |
upper, lower, title, capfirst |
{{ name|upper }} |
truncatechars, truncatewords |
{{ text|truncatechars:120 }} |
floatformat |
{{ preis|floatformat:2 }} |
length |
{{ liste|length }} |
join |
{{ tags|join:", " }} |
safe |
Emitir HTML sin filtrar |
markdown |
Markdown a HTML (markdown:"html" deja pasar el HTML incrustado; ver más abajo) |
json |
Valor como JSON, p. ej. para JavaScript: <script>const cfg = {{ cfg|json }};</script> |
urlencode |
<a href="/suche?q={{ q|urlencode }}"> |
Markdown directamente en el HTML
Los textos largos se escriben más cómodamente en Markdown, también en medio del HTML de una página o de un Snippet. Envuelve la zona con {% filter markdown %}:
<main class="container">
{% filter markdown %}
# Sobre nosotros
Diseñamos y desarrollamos **sitios web con personalidad**, desde la idea hasta la puesta en marcha.
Más información en [Servicios](/leistungen): el equipo de {{ site.title }} te espera.
- Asesoramiento
- Implementación
{% endfilter %}
</main>
- Las variables y etiquetas de template funcionan dentro del bloque.
- Los títulos reciben anclas para enlaces de salto, y las diéresis se transcriben: «Über uns» →
#ueber-uns, «Größe» →#groesse. - HTML dentro del Markdown: el filtro normal descarta el HTML incrustado (
<div …>), porque también procesa contenidos de Collections y fuentes REST que pueden venir de fuera. Para los textos que escribes tú mismo en el editor,{% filter markdown:"html" %}deja pasar el HTML, por ejemplo para un<div class="hinweis">entre párrafos. Para datos externos,|markdownsigue siendo la opción segura.
Pens – zonas de pruebas
En Pens creas pequeñas zonas de pruebas, como en CodePen: HTML (con todas las posibilidades de los templates: data(), item(), workflow(), filtros …), CSS y JS uno junto a otro, y debajo el resultado, en vivo mientras escribes. Muy útil para probar templates con datos reales, para prototipos o para micrositios pequeños.
- Cada Pen tiene su propia dirección imposible de adivinar:
/cms-admin/pen/<uuid>. Con la sesión iniciada abre el editor (pantalla completa) y Ctrl+S guarda. Arriba cambias el diseño: Código arriba (uno junto a otro) o Código al lado (apilado a la izquierda, resultado a la derecha); Pantalla completa muestra solo el resultado ocupando toda la pantalla (Esc para salir). - Público (interruptor de arriba): sin iniciar sesión, la misma dirección muestra el resultado y, al lado, el código, solo para lectura, en pestañas HTML/CSS/JS. Con Mostrar código desactivado, la dirección es una página independiente: solo el resultado, con el título del Pen; práctico para páginas sencillas rápidas. Los Pens no públicos no existen para los visitantes (404). Como administrador ves la vista de los visitantes con Vista de visitante (pestaña nueva,
?view=1). - Los Pens son siempre
noindex, nofollow(metaetiqueta y cabeceraX-Robots-Tag) y no aparecen en ningún sitemap. - El resultado se ejecuta en un sandbox con origen propio: los scripts del Pen no llegan a tu sesión de administración.
- Para los visitantes solo se usan entradas de Collection publicadas; en el editor ves también los borradores, como en la vista previa.
siteynav()se refieren al sitio web del dominio solicitado. - Los Pens forman parte de los Bundles; no tienen historial de versiones.