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>
83 lines
5.8 KiB
Twig
83 lines
5.8 KiB
Twig
{% extends 'admin/shell.html.twig' %}
|
||
{% set nav = 'plugins' %}
|
||
{% block title %}Plugins{% endblock %}
|
||
{% block heading %}Plugins{% endblock %}
|
||
{% block lead %}Erweiterungen bringen eigene Bausteine, Vorlagen und Adressen mit. Sie liegen als Ordner unter <code>plugins/</code>; hier schaltest du sie ein und aus und stellst sie ein. Ein ausgeschaltetes Plugin lässt seine Bausteine in den Seiten stehen, zeigt sie aber nicht mehr.{% endblock %}
|
||
|
||
{# Ein Feld aus dem Schema eines Plugins - dieselben sieben Arten wie im Editor. #}
|
||
{% macro feld(plugin, key, schema, value) %}
|
||
{% set id = 'plugin-' ~ plugin ~ '-' ~ key %}
|
||
{% set name = 'settings[' ~ key ~ ']' %}
|
||
{% if schema.kind == 'boolean' %}
|
||
<div><label class="check-label" for="{{ id }}"><input id="{{ id }}" type="checkbox" name="{{ name }}" value="1"{{ value ? ' checked' : '' }}> {{ schema.label }}</label>{% if schema.help %}<p class="field-help">{{ schema.help }}</p>{% endif %}</div>
|
||
{% else %}
|
||
<div>
|
||
<label for="{{ id }}">{{ schema.label }}</label>
|
||
{% if schema.kind == 'textarea' %}<textarea id="{{ id }}" name="{{ name }}" rows="3" maxlength="{{ schema.maxLength }}">{{ value }}</textarea>
|
||
{% elseif schema.kind == 'select' %}<select id="{{ id }}" name="{{ name }}">{% for wert, text in schema.choices %}<option value="{{ wert }}"{{ value == wert ? ' selected' : '' }}>{{ text }}</option>{% endfor %}</select>
|
||
{% elseif schema.kind == 'number' %}<input id="{{ id }}" name="{{ name }}" type="number" step="any" min="{{ schema.min }}" max="{{ schema.max }}" value="{{ value }}">
|
||
{% elseif schema.kind == 'media' %}<input id="{{ id }}" name="{{ name }}" value="{{ value }}" placeholder="Kennung aus der Mediathek" pattern="[a-f0-9]{32}">
|
||
{% else %}<input id="{{ id }}" name="{{ name }}" value="{{ value }}" maxlength="{{ schema.maxLength }}">{% endif %}
|
||
{% if schema.help %}<p class="field-help">{{ schema.help }}</p>{% endif %}
|
||
</div>
|
||
{% endif %}
|
||
{% endmacro %}
|
||
|
||
{% block content %}
|
||
{% import _self as f %}
|
||
{% if plugins is empty %}
|
||
<div class="empty"><h3>Keine Plugins gefunden.</h3><p>Ein Plugin ist ein Ordner unter <code>{{ directory }}</code> mit einer <code>plugin.json</code>. Wie eine aussieht, steht in <code>docs/PLUGINS.md</code>.</p></div>
|
||
{% else %}
|
||
<div class="rows">
|
||
{% for plugin in plugins %}
|
||
<article class="row row-top">
|
||
<div class="row-main">
|
||
<h2>{{ plugin.title }} <span class="count">{{ plugin.version }}</span></h2>
|
||
{% if plugin.description %}<p>{{ plugin.description }}</p>{% endif %}
|
||
{% if plugin.error %}
|
||
<p class="field-error">Nicht ladbar: {{ plugin.error }}</p>
|
||
{% else %}
|
||
<p class="muted">Ordner <code>plugins/{{ plugin.name }}</code>{% if plugin.requires %} · braucht KantSites {{ plugin.requires }}{% endif %}{% if plugin.active and plugin.activatedVersion and plugin.activatedVersion != plugin.version %} · seit dem Einschalten aktualisiert (war {{ plugin.activatedVersion }}){% endif %}</p>
|
||
<dl class="submission-data">
|
||
{% for block in plugin.blocks %}
|
||
<dt>{{ block.icon }} {{ block.label }}</dt>
|
||
<dd><code>{{ block.type }}</code>{% if block.description %} – {{ block.description }}{% endif %} · {{ block.props|length }} {{ block.props|length == 1 ? 'Eigenschaft' : 'Eigenschaften' }}{% if block.container %} · Container{% if block.children %} für {{ block.children|join(', ') }}{% endif %}{% endif %}{% if block.css %} · Stylesheet{% endif %}{% if block.script %} · Skript{% endif %}</dd>
|
||
{% endfor %}
|
||
{% for preset in plugin.presets %}
|
||
<dt>⬡ Vorlage „{{ preset.label }}“</dt>
|
||
<dd>{{ preset.description ?: 'Seitenvorlage für den Editor' }} · {{ preset.blocks|length }} {{ preset.blocks|length == 1 ? 'Baustein' : 'Bausteine' }}</dd>
|
||
{% endfor %}
|
||
{% for route in plugin.routes %}
|
||
<dt>↗ Adresse</dt>
|
||
<dd>{% if plugin.active %}<a href="{{ route.path }}">{{ route.path }}</a>{% else %}<code>{{ route.path }}</code>{% endif %} · {{ route.type }}</dd>
|
||
{% endfor %}
|
||
</dl>
|
||
{% if plugin.active and plugin.settings is not empty %}
|
||
<form method="post" action="{{ path('admin_plugin_settings', {name: plugin.name}) }}" class="plugin-settings">
|
||
<input type="hidden" name="_csrf_token" value="{{ csrf_token('plugins') }}">
|
||
<h3 class="form-part">Einstellungen</h3>
|
||
<div class="fields">
|
||
{% for key, schema in plugin.settings %}{{ f.feld(plugin.name, key, schema, plugin.values[key]|default('')) }}{% endfor %}
|
||
</div>
|
||
<button class="secondary" type="submit">Einstellungen speichern</button>
|
||
</form>
|
||
{% endif %}
|
||
{% endif %}
|
||
</div>
|
||
<div class="row-side">
|
||
<span class="pill {{ plugin.active ? 'pill-live' : 'pill-off' }}">{{ plugin.active ? 'Eingeschaltet' : 'Aus' }}</span>
|
||
{% if not plugin.error %}
|
||
<form method="post" action="{{ path('admin_plugin_toggle', {name: plugin.name, state: plugin.active ? 'aus' : 'an'}) }}">
|
||
<input type="hidden" name="_csrf_token" value="{{ csrf_token('plugins') }}">
|
||
<button class="{{ plugin.active ? 'secondary' : 'primary' }}" type="submit">{{ plugin.active ? 'Ausschalten' : 'Einschalten' }}</button>
|
||
</form>
|
||
{% endif %}
|
||
</div>
|
||
</article>
|
||
{% endfor %}
|
||
</div>
|
||
{% endif %}
|
||
<p class="field-help">Plugin-Vorlagen laufen mit den Rechten der Website – es gibt keinen Sandkasten. Installiere nur, was du gelesen hast. Farben, Schriften und Buttons änderst du ausschließlich unter Design – ein Plugin fasst sie nicht an.</p>
|
||
<p class="footnote">KantSites · Arbeitsstand</p>
|
||
{% endblock %}
|