Rebuild DEEG as a fifth installation, add stepped forms and a plugin system, and clear the review backlog
Forms gained steps, conditions, image choices, number fields with units and a summary step; the DEEG price finder uses all of it. Plugins are manifest plus Twig: blocks, settings, containers, routes, presets and theme packs, switched per installation. The review backlog is done: archive pages render through the normal page pipeline, forms and post lists work inside areas and sections, repeated sections get their own ids, breakpoints and colours come from the palette, backup and update work on MySQL and lock each other out. The task worker no longer shortens a process that has no time limit: set_time_limit() sets the limit instead of extending it, which turned an unlimited CLI run into eighty seconds and killed the full test suite far away from the scheduler. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
66ff68abf8
commit
c8a8fa7460
74 files changed
+5352
-243
No files matched your search
@@ -79,11 +79,15 @@
|
||||
<a class="button" href="{{ props.href }}"{% include 'blocks/link-attributes.html.twig' with {newTab: props.newTab|default(false)} only %}>{{ props.buttonText }}</a>
|
||||
</div>
|
||||
{% elseif type == 'contact' %}
|
||||
{% if not (props.phone|default('') or props.email|default('') or props.address|default('')) %}
|
||||
{% if editing|default(false) %}<p id="{{ block.id }}" class="block-unavailable{{ designClasses }}" role="note">Kontaktdaten: Telefon, E-Mail oder Adresse unter Inhalt eintragen.</p>{% endif %}
|
||||
{% else %}
|
||||
<address id="{{ block.id }}" class="block-contact{{ designClasses }}">
|
||||
{% if props.phone|default('') %}<span>{% include 'blocks/icon.html.twig' with {name: 'phone'} only %}<a href="tel:{{ props.phone|replace({' ': '', '(': '', ')': '', '/': '', '-': ''}) }}">{{ props.phone }}</a></span>{% endif %}
|
||||
{% if props.email|default('') %}<span>{% include 'blocks/icon.html.twig' with {name: 'mail'} only %}<a href="mailto:{{ props.email }}">{{ props.email }}</a></span>{% endif %}
|
||||
{% if props.address|default('') %}<span>{% include 'blocks/icon.html.twig' with {name: 'pin'} only %}{{ props.address }}</span>{% endif %}
|
||||
</address>
|
||||
{% endif %}
|
||||
{% elseif type == 'divider' %}
|
||||
<hr id="{{ block.id }}" class="block-divider{{ designClasses }}">
|
||||
{% elseif type == 'spacer' %}
|
||||
@@ -114,7 +118,9 @@
|
||||
{# A linked section: its blocks come from the partial, resolved by the controller. One change
|
||||
there reaches every page that links it, which is the difference to a copied section. #}
|
||||
{% set section = sections[props.partial]|default(null) %}
|
||||
{% if section %}
|
||||
{% if props.partial|default('') == '' %}
|
||||
{% if editing|default(false) %}<p id="{{ block.id }}" class="block-unavailable{{ designClasses }}" role="note">Verknüpfte Sektion: unter Inhalt die Adresse einer Sektion eintragen.</p>{% endif %}
|
||||
{% elseif section %}
|
||||
<div id="{{ block.id }}" class="block-partialref{{ designClasses }}">
|
||||
{% for child in section.blocks %}{% 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 %}
|
||||
</div>
|
||||
@@ -135,9 +141,13 @@
|
||||
</nav>
|
||||
{% endif %}
|
||||
{% elseif type == 'form' %}
|
||||
{% if props.form|default('') == '' %}
|
||||
{% if editing|default(false) %}<p id="{{ block.id }}" class="block-unavailable{{ designClasses }}" role="note">Formular: unter Inhalt die Adresse eines Formulars eintragen (siehe Verwaltung → Formulare).</p>{% endif %}
|
||||
{% else %}
|
||||
<div id="{{ block.id }}" class="block-formwrap{{ designClasses }}">
|
||||
{% include 'blocks/form.html.twig' with (forms[block.id]|default({form: null, sent: false, errors: {}, values: {}, blockId: block.id, page: {path: ''}, issued: 0, issuedSignature: ''})) only %}
|
||||
{% include 'blocks/form.html.twig' with (forms[block.id]|default({form: null, sent: false, errors: {}, values: {}, optionImages: {}, blockId: block.id, page: {path: ''}, issued: 0, issuedSignature: ''})) only %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% elseif type == 'tabs' %}
|
||||
{# Without the script this is an anchor list followed by every panel: complete and readable.
|
||||
tabs.js turns it into a tab widget; it only changes presentation, never content. #}
|
||||
@@ -213,6 +223,9 @@
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% elseif type == 'embed' %}
|
||||
{% if props.url|default('') == '' %}
|
||||
{% if editing|default(false) %}<p id="{{ block.id }}" class="block-unavailable{{ designClasses }}" role="note">Externer Inhalt: unter Inhalt Adresse, Titel und Anbieter eintragen.</p>{% 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.
|
||||
@@ -241,6 +254,7 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% elseif type == 'postlist' %}
|
||||
<div id="{{ block.id }}" class="block-postlist{{ designClasses }}">
|
||||
{% set feed = postLists[block.id]|default([]) %}
|
||||
@@ -264,6 +278,21 @@
|
||||
<div id="{{ block.id }}" class="block-gallery{{ designClasses }}" role="group" aria-label="Bildergalerie"{% if props.lightbox|default(false) %} data-zoom-group{% endif %}>
|
||||
{% 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 %}
|
||||
</div>
|
||||
{% elseif kant_plugin_block(type) %}
|
||||
{# Ein Baustein eines aktiven Plugins. Die Vorlage bekommt nur, was der Kern geprüft hat:
|
||||
die Eigenschaften nach dem Schema des Manifests. Passen sie nicht, bleibt der Platz leer. #}
|
||||
{% set plugin = kant_plugin_block(type) %}
|
||||
{% set pluginProps = kant_plugin_props(block) %}
|
||||
{% if pluginProps is null %}
|
||||
<!-- Block {{ block.id }} ({{ type }}) hat Eigenschaften, die nicht zum Plugin passen, und wird nicht dargestellt. -->
|
||||
{% if editing|default(false) %}<p id="{{ block.id }}" class="block-unavailable{{ designClasses }}" role="note">Baustein „{{ plugin.label }}“: die gespeicherten Eigenschaften passen nicht zum Plugin.</p>{% 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
|
||||
|
||||
Reference in new issue
Block a user