Text in admin cards had no size of its own, so 16px browser default sat next to 13px notes; cards now use 13px throughout while fields stay at 16px for iOS. The mail page gets option cards, a 2x2 SMTP grid, a styled save button in the card foot and a log with status pills and collapsed server messages, plus "Protokoll leeren". The package card uses the same sections and a styled file picker. The E-Mail-Versand subitem only shows inside System and sits beside it in the horizontal strip. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
148 lines
8.4 KiB
Twig
148 lines
8.4 KiB
Twig
{% extends 'admin/shell.html.twig' %}
|
||
{% set nav = 'mail' %}
|
||
{% block title %}E-Mail-Versand{% endblock %}
|
||
{% block heading %}E-Mail-Versand{% endblock %}
|
||
{% block lead %}Wie Formulare ihre Mails verschicken – ausprobieren, speichern und sehen, woran es hakt.{% endblock %}
|
||
{% block content %}
|
||
|
||
{# E-Mail-Versand (ADR 121, 122): Weg wählen und ausprobieren, alle Wege des Servers testen,
|
||
speichern - und die letzten Versuche mit Ursache. Das SMTP-Passwort steht nie im Formular. #}
|
||
{% set f = mailForm ?? {} %}
|
||
{% set e = mail.einstellung %}
|
||
{% set weg = f.weg ?? e.weg %}
|
||
{% set beschreibung = {
|
||
smtp: 'Empfohlen. Ein echtes Postfach, unabhängig vom Hoster.',
|
||
native: 'Über den Server. Der Absender muss zu einer Domain beim Hoster gehören.',
|
||
'sendmail-t': 'Über den Server, falls mail() fehlt.',
|
||
'sendmail-bs': 'Über den Server, bei manchen Hostern gesperrt.',
|
||
} %}
|
||
|
||
{% if mailNotice %}
|
||
<p class="notice {{ mailNotice.art == 'ok' ? 'ok' : 'error' }}" role="{{ mailNotice.art == 'ok' ? 'status' : 'alert' }}">{{ mailNotice.text }}</p>
|
||
{% endif %}
|
||
|
||
<form class="card mail-form" id="mail" action="{{ path('admin_mail_save') }}" method="post">
|
||
<input type="hidden" name="_csrf_token" value="{{ csrf_token('system') }}">
|
||
<div class="card-head"><h2>Versandweg</h2><span class="muted">gilt für alle Formulare</span></div>
|
||
<div class="card-body">
|
||
<div class="mail-current">
|
||
<span class="muted">Eingestellt</span>
|
||
{% if mail.gueltig %}
|
||
<strong>{{ mail.art }}</strong>{% if mail.ziel %} <code>{{ mail.ziel }}</code>{% endif %}{% if mail.benutzer %} <span class="muted">als</span> <code>{{ mail.benutzer }}</code>{% endif %}
|
||
{% else %}
|
||
<strong class="danger-text">Die Einstellung ist ungültig.</strong>
|
||
{% endif %}
|
||
<span class="muted mail-current-source">in <code>{{ mail.quelle }}</code> als <code>MAILER_DSN</code>{% if mail.quelle != '.env' %} – hat Vorrang, speichern lässt sich hier nichts, testen schon{% endif %}</span>
|
||
</div>
|
||
|
||
<fieldset class="card-section">
|
||
<legend>Weg wählen</legend>
|
||
<div class="option-list">
|
||
<label class="option" for="weg-smtp"><input id="weg-smtp" type="radio" name="weg" value="smtp"{% if weg == 'smtp' %} checked{% endif %}><span><strong>SMTP mit Postfach</strong><span>{{ beschreibung.smtp }}</span></span></label>
|
||
{% for key, w in mail.wege %}
|
||
<label class="option" for="weg-{{ key }}"><input id="weg-{{ key }}" type="radio" name="weg" value="{{ key }}"{% if weg == key %} checked{% endif %}><span><strong>{{ w.label }}</strong><span>{{ beschreibung[key] ?? '' }}</span></span></label>
|
||
{% endfor %}
|
||
</div>
|
||
</fieldset>
|
||
|
||
<fieldset class="card-section smtp-fields">
|
||
<legend>SMTP-Zugang</legend>
|
||
<div class="mail-grid">
|
||
<div>
|
||
<label for="mail-server">Server</label>
|
||
<input id="mail-server" name="server" type="text" inputmode="url" autocomplete="off" spellcheck="false" maxlength="200" placeholder="smtp.ionos.de" value="{{ f.server ?? e.server }}">
|
||
</div>
|
||
<div>
|
||
<label for="mail-port">Port</label>
|
||
<input id="mail-port" name="port" type="number" min="1" max="65535" value="{{ f.port ?? e.port }}" aria-describedby="mail-port-help">
|
||
</div>
|
||
<div>
|
||
<label for="mail-user">Benutzer</label>
|
||
<input id="mail-user" name="benutzer" type="text" autocomplete="off" spellcheck="false" maxlength="200" placeholder="formular@meine-firma.de" value="{{ f.benutzer ?? e.benutzer }}">
|
||
</div>
|
||
<div>
|
||
<label for="mail-pass">Passwort</label>
|
||
<input id="mail-pass" name="passwort" type="password" autocomplete="new-password" maxlength="500" placeholder="{{ e.passwort ? 'gespeichert – leer lassen zum Behalten' : '' }}">
|
||
</div>
|
||
</div>
|
||
<p class="field-help" id="mail-port-help">587 mit STARTTLS ist üblich (IONOS, Microsoft 365: <code>smtp.office365.com</code>), 465 verschlüsselt von Anfang an. Das Passwort wird nie angezeigt und landet nur in <code>.env</code>.</p>
|
||
</fieldset>
|
||
|
||
<fieldset class="card-section">
|
||
<legend>Testmail</legend>
|
||
<div class="mail-grid">
|
||
<div>
|
||
<label for="mail-to">An</label>
|
||
<input id="mail-to" name="to" type="email" autocomplete="email" maxlength="200" placeholder="name@beispiel.de" value="{{ f.an ?? (mailResult.an ?? '') }}">
|
||
</div>
|
||
<div>
|
||
<label for="mail-from">Absender</label>
|
||
<input id="mail-from" name="from" type="email" maxlength="200" placeholder="leer: dieselbe Adresse" value="{{ f.von ?? mail.absender }}" aria-describedby="mail-from-help">
|
||
</div>
|
||
</div>
|
||
<p class="field-help" id="mail-from-help">Derselbe Absender wie bei den Formularen. Über den Server (mail(), sendmail) muss er zu einer eigenen Domain beim Hoster gehören, bei SMTP ist es meist das angemeldete Postfach.</p>
|
||
<div class="button-row">
|
||
<button class="secondary" type="submit" name="aktion" value="test">Diesen Weg testen</button>
|
||
<button class="secondary" type="submit" name="aktion" value="alle">Alle Wege testen</button>
|
||
</div>
|
||
<p class="field-help">„Alle Wege testen“ schickt je Weg eine Testmail – bis zu vier.</p>
|
||
|
||
{% if mailResult %}
|
||
<div class="notice {{ mailResult.ok ? 'ok' : 'error' }} mail-result" role="{{ mailResult.ok ? 'status' : 'alert' }}">
|
||
<p><strong>{{ mailResult.ok ? 'Testmail an ' ~ mailResult.an ~ ' vom Mailserver angenommen' : 'Testmail an ' ~ mailResult.an ~ ' nicht versendet' }}</strong>{% if mailResult.weg is defined %} ({{ mailResult.weg }}){% endif %}.{% if not mailResult.ok %} {{ mailResult.meldung }}{% endif %}</p>
|
||
{% if mailResult.hinweis %}<p>{{ mailResult.hinweis }}</p>{% endif %}
|
||
{% if mailResult.dialog %}<details><summary>Antworten des Mailservers</summary><pre>{{ mailResult.dialog }}</pre></details>{% endif %}
|
||
</div>
|
||
{% endif %}
|
||
|
||
{% if mailResults %}
|
||
<div class="mail-result">
|
||
<p class="mail-result-head"><strong>Alle Wege ausprobiert</strong> <span class="muted">– an {{ f.an ?? '' }}. Angenommen heißt noch nicht angekommen: bitte im Postfach nachsehen.</span></p>
|
||
<ul class="mail-log">
|
||
{% for r in mailResults %}
|
||
{% include 'admin/_mail_entry.html.twig' with {eintrag: r, titel: r.weg} only %}
|
||
{% endfor %}
|
||
</ul>
|
||
</div>
|
||
{% endif %}
|
||
</fieldset>
|
||
</div>
|
||
<div class="card-foot form-foot">
|
||
{% if mail.quelle == '.env' %}
|
||
<button class="primary" type="submit" name="aktion" value="speichern">Diesen Weg speichern</button>
|
||
<span class="muted">Erst mit „Speichern“ versenden die Formulare auf dem gewählten Weg.</span>
|
||
{% else %}
|
||
<span class="muted">MAILER_DSN steht in der Server-Umgebung – speichern lässt sich hier nichts.</span>
|
||
{% endif %}
|
||
</div>
|
||
</form>
|
||
|
||
<div class="card">
|
||
<div class="card-head">
|
||
<h2>Letzte Versuche</h2>
|
||
{% if mail.protokoll is empty %}
|
||
<span class="muted">var/log/mail.log</span>
|
||
{% else %}
|
||
<form action="{{ path('admin_mail_clear') }}" method="post">
|
||
<input type="hidden" name="_csrf_token" value="{{ csrf_token('system') }}">
|
||
<button class="text-button" type="submit">Protokoll leeren</button>
|
||
</form>
|
||
{% endif %}
|
||
</div>
|
||
<div class="card-body">
|
||
{% if mail.protokoll is empty %}
|
||
<p class="muted">Noch nichts versendet – weder Testmail noch Formular.</p>
|
||
{% else %}
|
||
<ul class="mail-log">
|
||
{% for eintrag in mail.protokoll %}
|
||
{% include 'admin/_mail_entry.html.twig' with {eintrag: eintrag} only %}
|
||
{% endfor %}
|
||
</ul>
|
||
<p class="field-help">Die letzten zehn Versuche aus <code>var/log/mail.log</code>, neueste zuerst. Zeiten in UTC.</p>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
|
||
<p class="footnote">KantSites · Arbeitsstand</p>
|
||
{% endblock %}
|