{% set type = block.type %} {% set props = block.props|default({}) %} {% set layout = block.layout|default({}) %} {% set designClasses = layout.visibility|default('all') != 'all' ? ' visibility-' ~ layout.visibility : '' %} {% for device, settings in layout.design|default({}) %} {% for key, value in settings %}{% set designClasses = designClasses ~ ' d-' ~ device ~ '-' ~ key ~ '-' ~ value %}{% endfor %} {% endfor %} {% for key, value in layout.effects|default({}) %}{% set designClasses = designClasses ~ ' fx-' ~ key ~ '-' ~ value %}{% endfor %} {% if type in ['section', 'row', 'column'] %} {% set tag = type == 'section' ? 'section' : 'div' %} <{{ tag }} id="{{ block.id }}" class="block-{{ type }} tone-{{ layout.tone|default('paper') }} width-{{ layout.width|default('normal') }}{{ designClasses }}{{ block.anchor|default('') ? ' has-anchor' : '' }}"> {% if block.anchor|default('') %}{% endif %} {% if type == 'section' %}
{{ props.text }}
{% endif %} {% elseif type == 'button' %} {% for device, settings in layout.button|default({}) %}{% for key, value in settings %}{% set designClasses = designClasses ~ ' b-' ~ device ~ '-' ~ key ~ '-' ~ value %}{% endfor %}{% endfor %} {# Schema v25: Symbol wählbar. Ohne Angabe der Pfeil wie bisher; er behält die Klasse button-arrow, damit eigenes CSS älterer Websites (ausblenden, drehen) weiter greift. #} {% set buttonIcon = props.icon|default('arrow-up-right') %} {% set iconMarkup = '' %} {% if buttonIcon != 'none' %}{% set iconMarkup %}{% endset %}{% endif %} {% set iconFirst = props.iconPosition|default('end') == 'start' %} {# Schema v26: Bewegung beim Überfahren und ein Button nur aus Symbol. Der Text steht dann in einer Beschriftung ohne Breite und bleibt der Name des Links für Vorlesesoftware. #} {% set iconOnly = iconMarkup and props.iconOnly|default(false) %} {% if iconMarkup %}{% set designClasses = designClasses ~ (iconFirst ? ' icon-start' : '') ~ (props.iconMotion|default('none') != 'none' ? ' icon-motion-' ~ props.iconMotion : '') ~ (iconOnly ? ' icon-only' : '') %}{% endif %} {% if iconMarkup and iconFirst %}{{ iconMarkup }} {% endif %}{% if iconOnly %}{% else %}{{ props.text }}{% endif %}{% if iconMarkup and not iconFirst %} {{ iconMarkup }}{% endif %} {% elseif type == 'image' %} {# Wie breit das Bild tatsaechlich erscheint. Steht im Block eine feste Breite - ein Logo mit 190px, ein Symbol mit 72px -, dann ist das die Antwort, und der Browser laedt die kleine Variante statt der grossen. Ohne feste Breite bleibt es bei der Annahme nach Spaltenbreite. #} {% set festeBreite = block.style.desktop.maxWidth|default('') %} {% set eager = eagerImage|default(null) == block.id %} {# „auto“ heißt: der Browser misst selbst nach, wie breit das Bild tatsächlich erscheint, und wählt genau dazu die Variante. Das geht nur bei verzögert geladenen Bildern - beim vorgezogenen Bild steht die Größe noch nicht fest. Die Angaben dahinter sind der Rückfall für Browser, die „auto“ noch nicht kennen (heute Safari). #} {% set annahme = '(max-width: 650px) 100vw, (max-width: 1024px) 50vw, 800px' %} {% set imageSizes = festeBreite ends with 'px' ? festeBreite : (eager or inHeader|default(false) ? annahme : 'auto, ' ~ annahme) %} {# Ohne eigene Wahl folgt ein Bild seiner Galerie: ist sie vergrößerbar, ist es das Bild auch. Eine eigene Wahl - auch „Nichts tun“ - geht vor, ebenso ein Link. #} {% set klick = props.click|default(props.href|default('') ? 'link' : (galleryZoom|default(false) ? 'vergroessern' : 'keine')) %} {% set imageLinked = klick == 'link' and props.href|default('') %} {% set grossesBild = props.sources|default([]) ? props.sources|last.src : props.src %}{{ props.value }}{{ props.label }}
{% elseif type == 'quote' %}{{ props.text }}
{{ props.text }}
{% endif %}Kontaktdaten: Telefon, E-Mail oder Adresse unter Inhalt eintragen.
{% endif %} {% else %} {% if props.phone|default('') %}{% include 'blocks/icon.html.twig' with {name: 'phone'} only %}{{ props.phone }}{% endif %} {% if props.email|default('') %}{% include 'blocks/icon.html.twig' with {name: 'mail'} only %}{{ props.email }}{% endif %} {% if props.address|default('') %}{% include 'blocks/icon.html.twig' with {name: 'pin'} only %}{{ props.address }}{% endif %} {% endif %} {% elseif type == 'divider' %}Verknüpfte Sektion: unter Inhalt die Adresse einer Sektion eintragen.
{% endif %} {% elseif section %}Die verknüpfte Sektion „{{ props.partial }}“ gibt es nicht mehr. Der Verweis bleibt gespeichert.
{% else %} {% endif %} {% elseif type == 'breadcrumb' %} {% if breadcrumbs|default([])|length > 1 %} {% endif %} {% elseif type == 'form' %} {% if props.form|default('') == '' %} {% if editing|default(false) %}Formular: unter Inhalt die Adresse eines Formulars eintragen (siehe Verwaltung → Formulare).
{% endif %} {% else %}Noch keine sozialen Profile eingetragen. Sie stehen unter Einstellungen → Unternehmensdaten → Soziale Profile.
{% else %} {% endif %} {% elseif type == 'progress' %} {# A progress bar as a real meter: the number is in the markup, not only in the width of a coloured div, so it is readable without seeing it. #}{{ props.label }}{% if props.showValue %} {{ props.value }} %{% endif %}
Externer Inhalt: unter Inhalt Adresse, Titel und Anbieter eintragen.
{% endif %} {% else %} {# Content from a third party. Until the group is granted, nothing of it is requested - no iframe, no image, no preconnect. That is the whole point of the consent question: the placeholder is not a nicety, it is what keeps the promise. In the editor it always stays a placeholder: the administration's policy forbids foreign frames, and a preview must not load anything from outside either. #} {% if not editing|default(false) and consentGate.granted(app.request, props.group) %} {% else %} {% endif %} {% endif %} {% elseif type == 'html' %} {# Markup des Betreibers, unveraendert. Geprueft wurde es beim Speichern (CustomCode::checkHtml), setzen darf es nur ein Administrator - deshalb steht hier `raw` und keine Filterung. #} {% if props.html|default('')|trim == '' %} {% if editing|default(false) %}Eigenes HTML: unter Inhalt den Code einfügen.
{% endif %} {% else %}Sprachwahl
{% endif %} {% elseif type == 'gallery' %}Baustein „{{ plugin.label }}“: die gespeicherten Eigenschaften passen nicht zum Plugin.
{% endif %} {% else %} {# Ein Container rendert seine Kinder hier, mit demselben Kontext wie jeder Kernbaustein, und reicht sie der Vorlage fertig gerendert als `children` - so kommt eine Plugin-Vorlage nie an Formulare, Beitragslisten oder die Navigation heran. #} {% set childrenHtml %}{% if plugin.container %}{% for child in block.children|default([]) %}{% include 'blocks/block.html.twig' with {block: child, editing: editing|default(false), postLists: postLists|default({}), forms: forms|default({}), breadcrumbs: breadcrumbs|default([]), navigation: navigation|default({}), sections: sections|default({}), richText: richText|default(false), richHeadings: richHeadings|default(false), neighbours: neighbours|default({}), eagerImage: eagerImage|default(null), inHeader: inHeader|default(false)} only %}{% endfor %}{% endif %}{% endset %} {% include plugin.template with {block: block, props: pluginProps, designClasses: designClasses, editing: editing|default(false), plugin: plugin, children: childrenHtml, settings: kant_plugin_settings(plugin.plugin)} only %} {% endif %} {% else %} {# A block whose plugin is not available. Its stored content stays in the document; the core renders nothing from its unvalidated props. Visitors see no broken fragment, while the editor preview shows a placeholder so the block can still be found and moved. #} {% if editing|default(false) %}Baustein „{{ type }}“ ist in dieser Installation nicht verfügbar. Der Inhalt bleibt gespeichert.
{% endif %} {% endif %}