Files
KantSites/templates/admin/mail.html.twig
T
kantarciandClaude Opus 5.5 b982cb8944 Give mail delivery its own admin page and move the origin probe off /.well-known
The mail page under System tries SMTP, PHP mail() and both sendmail
modes one by one or all at once, and saves the chosen way to .env
without ever showing the password. IONOS answers everything under
/.well-known/ itself with 403, so the address and HTTPS checks never
reached the installation; the probe now lives at /kantsites-origin
(ADR 122).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 21:07:27 +02:00

133 lines
8.3 KiB
Twig
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% 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 %}
<div class="card" id="mail">
<div class="card-head"><h2>Versandweg</h2><span class="muted">gilt für alle Formulare</span></div>
<div class="card-body">
<dl class="facts">
<dt>Eingestellt</dt>
<dd>
{% if mail.gueltig %}
<strong>{{ mail.art }}</strong>{% if mail.ziel %} <code>{{ mail.ziel }}</code>{% endif %}{% if mail.benutzer %} <span class="muted">· angemeldet als</span> <code>{{ mail.benutzer }}</code>{% endif %}
{% else %}
<span class="check-bad"><strong>Die Einstellung ist ungültig.</strong></span>
{% endif %}
<br><span class="muted">Gespeichert in <code>{{ mail.quelle }}</code> als <code>MAILER_DSN</code>.{% if mail.quelle != '.env' %} Sie hat dort Vorrang; speichern lässt sich hier nichts, testen schon.{% endif %}</span>
</dd>
</dl>
{% if mailNotice %}
<p class="notice {{ mailNotice.art == 'ok' ? 'ok' : 'error' }} mail-result" role="{{ mailNotice.art == 'ok' ? 'status' : 'alert' }}">{{ mailNotice.text }}</p>
{% endif %}
{% 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 %} <span>({{ mailResult.weg }})</span>{% 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><strong>Alle Wege ausprobiert</strong> <span class="muted">– an {{ (f.an ?? '') }}. Was angenommen wurde, kann gespeichert werden; angekommen ist es damit noch nicht, bitte im Postfach nachsehen.</span></p>
<ul class="log-list">
{% for r in mailResults %}
<li class="{{ r.ok ? 'check-ok' : 'check-bad' }}">
<strong>{{ r.ok ? 'Angenommen' : 'Fehlgeschlagen' }}</strong> · {{ r.weg }}
{% if not r.ok %}<br><code>{{ r.meldung }}</code>{% if r.hinweis %}<br><span class="muted">{{ r.hinweis }}</span>{% endif %}{% endif %}
</li>
{% endfor %}
</ul>
</div>
{% endif %}
<form class="mail-form" action="{{ path('admin_mail_save') }}" method="post">
<input type="hidden" name="_csrf_token" value="{{ csrf_token('system') }}">
<fieldset>
<legend>Versandweg</legend>
<label class="check-label" for="weg-smtp"><input id="weg-smtp" type="radio" name="weg" value="smtp"{% if weg == 'smtp' %} checked{% endif %}> <span>SMTP mit Postfach <span class="muted">– empfohlen, unabhängig vom Hoster</span></span></label>
{% for key, w in mail.wege %}
<label class="check-label" for="weg-{{ key }}"><input id="weg-{{ key }}" type="radio" name="weg" value="{{ key }}"{% if weg == key %} checked{% endif %}> {{ w.label }}</label>
{% endfor %}
</fieldset>
<fieldset class="smtp-fields">
<legend>SMTP</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>
<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>
</fieldset>
<div class="mail-actions">
<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>
{% if mail.quelle == '.env' %}<button type="submit" name="aktion" value="speichern">Diesen Weg speichern</button>{% endif %}
</div>
<p class="field-help">„Alle Wege testen“ schickt je Weg eine Testmail – bis zu vier. Gespeichert wird {{ mail.quelle == '.env' ? 'nur mit „Diesen Weg speichern“' : 'hier nichts, weil MAILER_DSN in der Server-Umgebung steht' }}.</p>
</form>
</div>
</div>
<div class="card">
<div class="card-head"><h2>Letzte Versuche</h2><span class="muted">var/log/mail.log</span></div>
<div class="card-body">
{% if mail.protokoll is empty %}
<p class="muted">Noch nichts versendet – weder Testmail noch Formular.</p>
{% else %}
<ul class="log-list">
{% for eintrag in mail.protokoll %}
<li class="{{ eintrag.ok ? 'check-ok' : 'check-bad' }}">
<strong>{{ eintrag.ok ? 'Angenommen' : 'Fehlgeschlagen' }}</strong> · {{ eintrag.zeit }} UTC · {{ eintrag.anlass }} an <code>{{ eintrag.an }}</code>{% if eintrag.von %} von <code>{{ eintrag.von }}</code>{% endif %}{% if eintrag.weg is defined and eintrag.weg %} · {{ eintrag.weg }}{% endif %}
{% if not eintrag.ok %}<br><code>{{ eintrag.meldung }}</code>{% if eintrag.hinweis %}<br><span class="muted">{{ eintrag.hinweis }}</span>{% endif %}{% endif %}
</li>
{% endfor %}
</ul>
{% endif %}
</div>
</div>
<p class="footnote">KantSites · Arbeitsstand</p>
{% endblock %}