Die Centralized Notification Suite bündelt den gesamten Benachrichtigungsversand einer Contao-Installation an einer Stelle: Formular-, Mitglieder-, Kommentar- und Newsletter-Mails ebenso wie Meldungen in Slack oder Microsoft Teams – mit Tokens, wiederverwendbaren Layouts, 20 fertigen Designs und einem vollständigen Versandprotokoll.
Diese Seite führt Sie von der Installation bis zur ersten versendeten E-Mail und beschreibt anschließend jede Funktion einzeln, mit Beispielen. Der letzte Teil richtet sich an Entwicklerinnen und Entwickler.
Das Grundprinzip in einem Satz: Eine Benachrichtigung ist das Ereignis („Kontaktformular wurde abgeschickt“), eine Nachricht ist eine konkrete Ausgabe dazu („Bestätigung an den Absender, auf Deutsch“), und ein Absender bestimmt, auf welchem Weg diese Nachricht das Haus verlässt.
Einrichtung in neun Schritten
Die Schritte 1 bis 4 sind einmalig. Danach legen Sie nur noch Benachrichtigungen an.
Systemvoraussetzungen
| Komponente | Voraussetzung |
|---|---|
| Contao | 5.3 oder neuer |
| PHP | 8.1 oder neuer, mit curl, json und sodium |
| Mailversand | SMTP oder ein anderer von Symfony Mailer unterstützter Transport |
| Netzwerk | Ausgehende HTTPS-Verbindungen für Lizenzvorgänge und Webhook-Absender |
| Dateisystem | Schreibrechte im Verzeichnis var/ |
1. Installation
Im Contao Manager das Paket
vtinnovations/centralized-notification-suite suchen, hinzufügen und die Änderungen
übernehmen – die Datenbank-Migration führt der Manager selbst aus.
Alternativ über die Konsole:
composer require vtinnovations/centralized-notification-suite
vendor/bin/contao-console contao:migrate
Optional lassen sich Startpunkte einspielen, damit Sie nicht mit leeren Modulen beginnen:
# Zwei E-Mail-Layouts: "Branded" und "Transactional"
vendor/bin/contao-console notification:install-layouts
# Fertige Beispielnachrichten
vendor/bin/contao-console notification:install-emails
Beide Befehle sind gefahrlos wiederholbar – Vorhandenes wird nicht überschrieben.
2. Lizenz aktivieren
Ohne aktivierte Lizenz versendet die Suite nichts. Es wird auch nichts in die Warteschlange gestellt – die Website verhält sich exakt so, als wäre das Paket nicht installiert. Inhalte und Konfiguration bleiben unangetastet.
Voraussetzung: eine Domain auf der Startseite
Die Lizenz wird an Hostnamen gebunden. Prüfen Sie zuerst unter Seitenstruktur → Startseite bearbeiten, dass im Feld Domainname die Domain steht, unter der die Website läuft. Ohne Eintrag lässt sich keine Lizenz aktivieren.
example.com und www.example.com sind zwei verschiedene Hostnamen.
Die Lizenz gilt genau für die Namen, für die sie ausgestellt wurde – es gibt keine automatische
Übernahme von www, Subdomains oder Aliassen.
Aktivieren
- Einstellungen öffnen.
- Zum Abschnitt V-T.ONE Licence management scrollen. Dort steht eine Karte je V-T.ONE-Paket; suchen Sie die Karte Centralized Notification Suite.
- Den Schlüssel in das Feld Lizenzschlüssel eintragen.
- Lizenz prüfen und aktivieren anklicken.
Danach zeigt die Karte in Grün Lizenz aktiv und darunter Paket, lizenzierten Host, abgedeckte Hosts, Version und Laufzeit.
| Schaltfläche | Wirkung |
|---|---|
| Lizenz prüfen und aktivieren | Prüft den eingegebenen Schlüssel beim Lizenzdienst und aktiviert die Installation |
| Lizenz aktualisieren | Gleicht den gespeicherten Stand mit dem Lizenzdienst ab. Schlüsselfeld leer lassen – der gespeicherte Schlüssel wird wiederverwendet |
| Lizenz entfernen | Setzt die Installation sofort in den nicht lizenzierten Zustand zurück |
Der Schlüssel wird nach dem Speichern nie wieder angezeigt. Das Feld ist bei jedem Seitenaufruf leer, auch direkt nach einer erfolgreichen Aktivierung. Wer ihn ändern möchte, trägt einen neuen ein.
3. SMTP-Zugangsdaten hinterlegen
Über diese Zugangsdaten versendet die gesamte Website, nicht nur die Suite – auch Contao-eigene E-Mails wie „Passwort zurücksetzen“.
Modul SMTP-Konfiguration öffnen (nur für Administratoren) und ausfüllen:
| Feld | Beispiel | Hinweis |
|---|---|---|
| SMTP-Host | smtp.ionos.de | |
| Port | 587 | |
| Verschlüsselung | STARTTLS | meist Port 587; alternativ SSL/TLS (meist 465) oder Keine |
| Benutzername | mail@example.com | |
| Passwort | Leer lassen, um das gespeicherte Passwort beizubehalten | |
| Absenderadresse | noreply@example.com | |
| Testempfänger | ihre@adresse.de | Erhält vor dem Speichern eine Test-E-Mail |
Dann Testen und speichern.
Der Ablauf ist bewusst streng: Es wird zuerst eine Test-E-Mail versendet, und nur wenn sie zugestellt wurde, werden die Zugangsdaten gespeichert und der Cache neu aufgebaut. Falsche Zugangsdaten können also gar nicht gespeichert werden.
4. Branding ausfüllen
Das Branding ist die eine Stelle, aus der sich jedes der 20 Designs bedient. Einmal ausgefüllt, ersparen Sie sich Logo und Farbe in jedem Layout.
Modul Branding öffnen – es springt direkt in den einen Datensatz, es gibt bewusst keine Liste.
| Feld | Empfehlung |
|---|---|
| Logo | PNG, JPG oder GIF. Kein SVG – Outlook und Gmail stellen es nicht dar |
| Logobreite (px) | 120–180 passt für die meisten Logos |
| Markenfarbe | Eine Farbe. Jedes Design leitet daraus Kopfbereiche, Links, getönte Flächen und dunkle Balken automatisch ab |
| Firmenname | Erscheint in der Fußzeile und dient als Alternativtext des Logos |
| Website | Wird in der Fußzeile verlinkt |
| Kontaktadresse | Erscheint in der Fußzeile als mailto:-Link |
| Postanschrift | In vielen Ländern in kommerziellen E-Mails vorgeschrieben |
| Fußnote | Kleingedrucktes, z. B. warum der Empfänger diese E-Mail erhält |
5. Absender anlegen
Ein Absender (Gateway) bestimmt den Versandweg. Modul Absender → Neuer Absender. Für den Anfang genügt einer vom Typ E-Mail:
| Feld | Beispiel |
|---|---|
| Titel | Standard-E-Mail |
| Typ | |
| Absendername | Musterfirma GmbH |
| Absenderadresse | noreply@example.com |
| Standard-Antwortadresse | optional, z. B. info@example.com |
| Veröffentlicht | ✔ |
Veröffentlicht nicht vergessen. Eine Nachricht mit unveröffentlichtem Absender wird nicht versendet; das Nachrichtenformular weist darauf hin.
6. E-Mail-Layout wählen
Ein Layout ist der Rahmen um den Nachrichteninhalt. Modul E-Mail-Layouts → Neues Layout. Der schnellste Weg ist die Layout-Art Fertiges Design:
- Titel vergeben, z. B. „Standard-Layout“.
- Vorschautext ausfüllen – die versteckte Zeile, die die meisten E-Mail-Programme neben dem Betreff im Postfach zeigen.
- Layout-Art auf das fertige Design stellen und unter Design eines der 20 Designs wählen.
- Veröffentlicht setzen.
Farben und Logo kommen aus dem Branding – das Design ist damit sofort im Markenbild.
7. Die erste Benachrichtigung
Modul Benachrichtigungen → Neue Benachrichtigung:
| Feld | Beispiel |
|---|---|
| Titel | Kontaktformular |
| Alias | kontaktformular – leer lassen, um ihn aus dem Titel zu erzeugen |
| Ausgelöst durch | Ein Formularversand |
Speichern, dann über Nachrichten bearbeiten die erste Nachricht anlegen:
| Feld | Beispiel |
|---|---|
| Absender | Standard-E-Mail |
| Sprache | de |
| Fallback-Nachricht | ✔ |
| Betreff | Neue Anfrage von ##name## |
| Inhalt | Aus Bausteinen aufbauen |
| Layout | Standard-Layout |
| Empfänger | buero@example.com |
| Antwort an | ##email## – damit die Antwort direkt an den Absender geht |
| Veröffentlicht | ✔ |
Danach die Bausteine anlegen, zum Beispiel: eine Überschrift, ein Text und ein Generierter Inhalt mit allen übermittelten Formularfeldern. Bausteine lassen sich per Drag & Drop sortieren.
8. Mit einem Formular verbinden
Die Suite ergänzt den Formulargenerator um ein Feld:
- Formulargenerator → das Formular bearbeiten.
- In den Formulareinstellungen die Benachrichtigung Kontaktformular wählen.
- Speichern.
Angeboten werden nur Benachrichtigungen vom Typ Ein Formularversand – ein Registrierungs-Mailing lässt sich also gar nicht versehentlich anhängen. Dasselbe Feld steht bei den Frontend-Modulen für Registrierung, Passwort-Zurücksetzen und Newsletter zur Verfügung.
Woher die Tokens kommen: Die Namen der Formularfelder werden zu
Tokens. Ein Feld mit dem Namen email ist als ##email## verfügbar. Das
Hilfe-Symbol neben Betreff, Nur-Text und HTML listet alle verfügbaren Tokens.
9. Testen
Sie müssen kein echtes Formular abschicken, um das Ergebnis zu sehen.
Vorschau rendert die Nachricht mit Beispielwerten und zeigt sie in Desktop- und Mobil-Breite sowie als Nur-Text.
Test senden lässt Sie die Tokens selbst ausfüllen und an eine Adresse Ihrer Wahl senden. Empfänger, CC und BCC werden durch diese eine Adresse ersetzt – niemand sonst erhält die Nachricht. Das Ergebnis steht anschließend im Versandprotokoll.
Funktionen im Detail
Benachrichtigungen
Eine Benachrichtigung ist das Ereignis, nicht die E-Mail. Sie hat nur drei Felder und enthält selbst keinen Inhalt – der steckt in ihren Nachrichten.
| Typ | Ausgelöst durch | Zusätzliche Tokens |
|---|---|---|
| Ein Formularversand | Formulargenerator | Alle Formularfelder, ##all_fields## und Verwandte |
| Eine Mitglieder-Aktion | Registrierung, Aktivierung, Passwort zurücksetzen, Profiländerung, Kontolöschung | Die Felder des Mitglieds |
| Ein neuer Kommentar | Kommentar-Erweiterung | Autor, Inhalt, Bezugsobjekt |
| Eine Newsletter-Anmeldung | An- und Abmeldung eines Empfängers | Adresse und Kanal |
| Ihr eigener Code | send() aus PHP oder die Konsole | Nur, was Sie selbst übergeben |
Der Typ hat zwei Wirkungen: Er bestimmt, welche Tokens beim Bearbeiten einer Nachricht angeboten werden, und welche Benachrichtigungen ein Auslöser zur Auswahl stellt.
Nachrichten
Eine Benachrichtigung hat beliebig viele Nachrichten. Jede ist eine eigene Ausgabe mit eigenem Absender, eigener Sprache und eigenen Empfängern.
Beispiel: Die Benachrichtigung Kontaktformular hat drei Nachrichten – eine Bestätigung an den Absender, eine Meldung ans Büro mit allen Formularfeldern und eine Nachricht in den Team-Chat. Alle drei entstehen aus demselben Formularversand und teilen dieselben Tokens.
Absender und Sprache
Legen Sie je Sprache eine Nachricht an und markieren Sie die wichtigste als Fallback-Nachricht. Ein Formular auf der englischen Seite bekommt die englische Nachricht; kommt eine Anfrage in einer Sprache, für die es keine Nachricht gibt, greift der Fallback. Ohne Fallback wird in diesem Fall nichts versendet.
Inhalt
| Feld | Bedeutung |
|---|---|
| Betreff | Tokens erlaubt, z. B. Neue Anfrage von ##name## |
| Nur-Text | Der Text-Teil. Leer lassen, um ihn aus dem HTML zu erzeugen |
| Nur-Text aus dem HTML erzeugen | Leitet den Text-Teil aus dem HTML ab. Links werden als „Bezeichnung (URL)“ übernommen |
| Inhalt | Bausteine oder eigenes HTML |
| Layout | Optional in ein wiederverwendbares Layout einbetten |
| Bilder einbetten | Bilder anhängen statt verlinken – sie erscheinen ohne „Externe Inhalte anzeigen“, die Nachricht wird dafür größer |
Bausteine oder HTML? Bausteine sind der Normalfall: sortierbar, ohne HTML-Kenntnisse, automatisch im Markenbild. Eigenes HTML ist für Fälle, in denen ein fertiges Mailing-Markup übernommen wird.
Empfänger
| Feld | Beispiel und Hinweis |
|---|---|
| Empfänger | buero@example.com, ##email## – Komma oder Semikolon trennt, Tokens erlaubt |
| CC | Alle Empfänger sehen diese Adressen |
| BCC | Unsichtbar, z. B. um jede Benachrichtigung zu archivieren |
| Antwort an | Überschreibt die Antwortadresse des Absenders, z. B. ##email## |
| Priorität | Die meisten Programme ignorieren sie; manche Spamfilter bewerten „Höchste“ negativ |
Ungültige Adressen lassen nicht die ganze Nachricht scheitern. Sie werden übersprungen und protokolliert; die übrigen Empfänger erhalten ihre Nachricht trotzdem.
Anhänge und Zeitfenster
Neben Anhängen aus der Dateiverwaltung gibt es Anzeigen ab und Anzeigen bis. Eine Weihnachtsansage kann so ab dem 1. Dezember automatisch mitgesendet und am 27. Dezember automatisch wieder abgeschaltet werden, ohne dass jemand daran denken muss.
Bausteine
Bausteine sind die Inhaltselemente einer Nachricht – vergleichbar mit Contaos Inhaltselementen, aber auf das reduziert, was E-Mail-Programme zuverlässig darstellen. Sie werden per Drag & Drop sortiert. Alle haben Ausrichtung, Abstand unten und Sichtbar.
| Baustein | Wofür, und was zu beachten ist |
|---|---|
| Überschrift | Text plus Größe. Tokens erlaubt, z. B. Willkommen, ##vorname## |
| Text | Fließtext. Eine Leerzeile trennt Absätze, ein einfacher Umbruch erzeugt einen Zeilenumbruch – Sie schreiben normalen Text, kein HTML. Stil: normal, größerer Einleitungsabsatz oder Kleingedrucktes |
| Liste | Aufzählung mit Punkten oder Nummern, eine Zeile je Eintrag |
| Detailtabelle | Zwei Spalten: je Zeile eine Bezeichnung und ein Wert, Tokens in beiden erlaubt. Für Bestellübersichten und Termindaten |
| Generierter Inhalt | Fügt einen automatisch erzeugten Block ein, etwa die Liste aller übermittelten Formularfelder |
| Button | Linktext und Linkziel (URL, Pfad ab /, mailto:, Insert-Tag oder Token), dazu gefüllt oder umrandet |
| Bild | JPG, PNG oder GIF – SVG und WebP werden nicht zuverlässig dargestellt. Maximal 536 px breit. Den Alternativtext bitte ernst nehmen: Viele Programme blockieren Bilder standardmäßig |
| Bild mit Text | Bild und Text nebeneinander; auf schmalen Bildschirmen automatisch untereinander |
| Trennlinie | Eine waagerechte Linie |
| Eigenes HTML | Markup wird unverändert eingefügt. Für E-Mails tabellenbasiertes Markup verwenden. <style>, <script>, <iframe> und on…-Attribute werden entfernt |
Der schnellste Weg zu einer vollständigen Formular-Benachrichtigung: eine Überschrift, ein Satz Text, ein „Generierter Inhalt“ – fertig. Neue Formularfelder erscheinen automatisch mit, ohne dass die Nachricht angefasst werden muss.
Tokens
Tokens sind Platzhalter in der Form ##name##, die beim Versand durch echte Werte
ersetzt werden. Sie funktionieren in Betreff, Nur-Text, HTML, in Baustein-Feldern, in
Empfängerfeldern und in der Webhook-Payload. Das Hilfe-Symbol neben Betreff,
Nur-Text und HTML zeigt die vollständige Liste.
Immer verfügbar
| Token | Enthält |
|---|---|
##admin_email## | Die Administratoradresse aus den Contao-Einstellungen |
##date## | Das heutige Datum im Datumsformat der Website |
##time## | Die aktuelle Uhrzeit |
##datim## | Datum und Uhrzeit |
##host## | Der Hostname, auf dem ausgelöst wurde |
##url## | Die Basis-URL der Website |
##page_id##, ##page_title##, ##page_url## | ID, Titel und absolute URL der auslösenden Seite |
Bei Formularen
Jedes Formularfeld wird unter seinem Namen zum Token. Dazu kommen Sammel-Tokens:
| Token | Enthält |
|---|---|
##all_fields## | Alle übermittelten Felder als Text |
##all_fields_filled## | Dasselbe, aber ohne leere Felder |
##all_fields_html## | Alle übermittelten Felder als Tabelle |
Warum Ihr Markup sicher ist: Token-Werte werden maskiert, ihre
Zeilenumbrüche werden in <br> umgewandelt. Was jemand in ein Formular tippt,
kann Ihr Markup nicht zerstören und keinen Code einschleusen.
Die eine Ausnahme: Tokens, deren Name auf _html endet, werden
als Markup eingefügt. Verwenden Sie im HTML-Teil also ##all_fields_html## und im
Text-Teil ##all_fields##.
E-Mail-Layouts
Ein Layout ist der wiederverwendbare Rahmen um den Nachrichteninhalt. Mehrere Nachrichten teilen sich ein Layout – eine Änderung am Kopfbereich wirkt sich damit auf alle aus.
Den Vorschautext bitte immer ausfüllen. Er ist nach dem Betreff das Zweite, was im Postfach gelesen wird; die Voreinstellung – die ersten Worte Ihres Markups – sieht oft unschön aus.
| Layout-Art | Wofür |
|---|---|
| Fertiges Design | Ein Feld: Design. Farben und Logo kommen aus dem Branding. Der empfohlene Weg für alle, die kein eigenes Mailing-Markup pflegen wollen |
| HTML-Dokument | Ihr komplettes E-Mail-HTML. ##message_body## markiert die Stelle, an die der Nachrichteninhalt gehört |
| Kopf und Fuß | Sie liefern nur Kopf- und Fußbereich, den Rahmen dazwischen erzeugt die Suite |
CSS inline schreiben – bitte aktiviert lassen. Jede Regel wird vor dem
Versand in style-Attribute geschrieben; genau das lässt sie in Outlook und Gmail
funktionieren, die Style-Blöcke teilweise ignorieren. @media-Regeln lassen sich
nicht inline schreiben und bleiben als Style-Block erhalten – so funktioniert responsives
Verhalten dort, wo es unterstützt wird.
Die 20 Designs
Alle Designs beziehen Logo, Markenfarbe und Firmenangaben aus dem Branding. Ein Wechsel des Designs ändert nur die Form, nie Ihre Inhalte.
| Familie | Design | Charakter |
|---|---|---|
| Card | Card – centred logo | Weiße Karte auf grauem Grund, Logo zentriert |
| Card – left logo | Dasselbe mit linksbündigem Logo | |
| Card – square corners | Ohne abgerundete Ecken | |
| Card – outlined | Mit Rahmen statt Schatten | |
| Brand bar | Brand bar – centred logo | Farbiger Balken oben, Logo zentriert |
| Brand bar – left logo | Balken mit linksbündigem Logo | |
| Brand bar – dark footer | Zusätzlich dunkle Fußzeile | |
| Dark | Dark header | Dunkler Kopfbereich |
| Dark header and footer | Dunkel oben und unten | |
| Minimal | Minimal – hairline rule | Nur eine feine Linie als Trennung |
| Minimal – wordmark only | Nur der Firmenname, kein Logo | |
| Minimal – no header | Ganz ohne Kopfbereich | |
| Compact – tight spacing | Enge Abstände, für kurze Hinweise | |
| Transactional | Transactional – plain and narrow | Schlicht und schmal |
| Transactional – tinted panel | Mit getönter Inhaltsfläche | |
| Receipt – boxed, monospaced figures | Beleg-Optik mit Festbreitenschrift für Beträge | |
| Announcement | Announcement – wide, centred | Breit und zentriert |
| Announcement – tinted background | Mit getöntem Hintergrund | |
| Editorial | Newsletter – serif headings | Serifen-Überschriften |
| Editorial – serif, dark footer | Serifen mit dunkler Fußzeile |
Welches wofür? Transactional und Receipt für Bestätigungen, Belege und Passwort-E-Mails; Card und Brand bar für den Alltag; Announcement und Editorial für Newsletter und Ankündigungen; Minimal und Compact für kurze interne Hinweise.
Branding
Die eine Stelle für Logo, Markenfarbe und Firmenangaben – siehe Schritt 4 der Einrichtung. Eine Änderung der Markenfarbe wirkt sich sofort auf alle Designs aus; Sie müssen kein Layout anfassen.
Absender (Gateways)
Absendername, Absenderadresse, Standard-Antwortadresse und optional ein benannter
Symfony-Mailer-Transport aus mailer.yaml.
Mehrere E-Mail-Absender sind sinnvoll, wenn verschiedene Bereiche unterschiedlich auftreten sollen – etwa „Vertrieb“ und „Support“. Der Mailer-Transport erlaubt es zusätzlich, Massenversand über einen anderen Dienst laufen zu lassen als transaktionale Post.
Webhook
Für Team-Chats und Automatisierungsdienste: Slack, Microsoft Teams (Workflows), Google Chat oder ein Zapier-, n8n- bzw. Make-Trigger.
In der JSON-Payload stehen ##subject##, ##text##,
##html##, ##recipients## sowie jedes Token der Benachrichtigung zur
Verfügung. Werte werden maskiert, sodass immer gültiges JSON entsteht – auch wenn jemand ein
Anführungszeichen in ein Formularfeld tippt.
Beispiel Slack:
{"text": "*##subject##*\n\nVon: ##name## (##email##)\n\n##text##"}
Beispiel Microsoft Teams (Workflows):
{"title": "##subject##", "text": "##all_fields##"}
Leer gelassen wird {"text": "##subject##\n\n##text##"} gesendet, was Slack,
Mattermost und Discord akzeptieren. Die Payload-Hilfe unter dem Feld enthält
für jeden gängigen Dienst einen fertigen Body zum Kopieren.
Das Timeout klein halten: Ein langsamer Endpunkt hält die auslösende Besucheraktion auf.
Datei
Schreibt Nachrichten in Dateien, statt sie zu versenden – Voreinstellung
var/notification-mail. Gedacht für Entwicklungs- und Abnahmeumgebungen: Alles
läuft durch, Sie sehen das fertige Ergebnis, aber niemand bekommt versehentlich echte Post.
Versandprotokoll
Das Protokoll beantwortet die Frage, die sonst niemand beantworten kann: Ist die E-Mail rausgegangen – und wenn nicht, warum? Es zeigt Datum, Benachrichtigung, Absendertyp, Empfänger, den aufgelösten Betreff, Status, Fehlertext, Auslöser, Anzahl der Versuche und den Inhalt, wie der Empfänger ihn erhalten hat.
| Status | Bedeutung |
|---|---|
| Wird gesendet | Versand läuft |
| In Warteschlange | Contao versendet E-Mails über eine Warteschlange; die Nachricht wurde übergeben. Das endgültige Ergebnis wird nachgetragen |
| Zugestellt | Vom Mailserver angenommen |
| Fehlgeschlagen | Mit Fehlertext in der Spalte daneben |
| Übersprungen | Bewusst nicht gesendet, z. B. weil ein Ereignis abgebrochen wurde |
Als Ausgelöst durch erscheinen: Formularversand, Code, Testversand, Manuell erneut gesendet oder Automatischer Wiederholversuch.
Erneut senden versendet einen Eintrag unverändert noch einmal – nützlich, wenn der Mailserver kurz nicht erreichbar war. Der Inhalt kommt dabei aus dem Protokoll, es wird nichts neu gerendert.
Ein täglicher Cronjob räumt Einträge weg, die älter als die eingestellte Aufbewahrungsfrist sind – standardmäßig 90 Tage. Protokoll leeren löscht alle Einträge auf einmal.
Datenschutz: Das Protokoll speichert standardmäßig auch den gerenderten
Inhalt, damit Vorschau und erneutes Senden funktionieren. Auf Websites, die keine
personenbezogenen Daten im Protokoll vorhalten dürfen, lässt sich das über
log.store_body: false abschalten.
Vorschau und Testversand
Vorschau rendert die Nachricht mit Beispielwerten und zeigt sie in Desktop- und Mobil-Breite sowie als Nur-Text. Damit sehen Sie Layout, Design und Bausteine, ohne etwas zu versenden.
Testversand: Sie füllen die Tokens selbst aus und geben eine Zieladresse an. Empfänger, CC und BCC werden durch diese eine Adresse ersetzt – niemand sonst erhält die Nachricht, auch wenn im Feld Empfänger die halbe Firma steht.
Berechtigungen
Die Module gehören zur Backend-Gruppe Centralized Notification Suite und werden wie jedes andere Contao-Modul über Benutzergruppen → Module freigegeben. Zwei Dinge bleiben Administratoren vorbehalten: die SMTP-Konfiguration, weil sie die Zugangsdaten der ganzen Website betrifft, und die Lizenzverwaltung.
Eine Redaktion, die Nachrichten pflegen soll, braucht üblicherweise Benachrichtigungen, E-Mail-Layouts und Versandprotokoll; Branding und Absender bleiben oft bei der Administration.
Für Entwickler
Benachrichtigungen aus eigenem Code auslösen
Legen Sie zunächst im Backend eine Benachrichtigung vom Typ Ihr eigener Code an und merken Sie sich ihren Alias.
use VTInnovations\CentralizedNotificationSuite\CentralizedNotificationSuite;
class OrderController
{
public function __construct(
private readonly CentralizedNotificationSuite $notify,
) {
}
public function confirm(Order $order): void
{
$result = $this->notify->send('bestellbestaetigung', [
'kundenname' => $order->customerName,
'bestellnr' => (string) $order->number,
'gesamtsumme' => $order->formattedTotal,
], 'de');
}
}
Die Schlüssel des Arrays sind die Token-Namen: kundenname steht in der Nachricht
als ##kundenname## zur Verfügung.
public function send(
string $alias, // Alias der Benachrichtigung
array $tokens, // Token-Name => Wert
string|null $language = null, // null nimmt die Fallback-Nachricht
array $extraAttachments = [], // zusätzlich zu den konfigurierten Anhängen
string $source = self::SOURCE_API,
): SendResult;
send() wirft eine NotificationException, wenn es
keine Benachrichtigung mit diesem Alias gibt. Fehlgeschlagener Versand wirft
dagegen nicht – er steht im Ergebnis und im Protokoll.
$result->isSuccessful(); // true, wenn nichts fehlgeschlagen ist
$result->hasFailures();
$result->countSent();
$result->getStatuses(); // Nachrichten-ID => Status
$result->getErrors(); // Nachrichten-ID => Fehlertext
Ohne gültige Lizenz versendet send() nichts, wirft aber auch
nicht. Sie erhalten ein leeres SendResult, der Grund steht im Systemlog. Ihre
Anwendung läuft weiter, als wäre das Paket nicht installiert.
Ereignisse
Zwei Symfony-Ereignisse umschließen jeden Versand. PreSendEvent lässt sich
abbrechen:
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
use VTInnovations\CentralizedNotificationSuite\Event\PreSendEvent;
#[AsEventListener]
public function __invoke(PreSendEvent $event): void
{
if ('email' === $event->gatewayType && (int) date('N') >= 6) {
$event->cancel('Kein Versand am Wochenende');
}
}
Ein abgebrochener Versand erscheint im Protokoll als Übersprungen, nicht als fehlgeschlagen – das ist die Unterscheidung zwischen „wir wollten nicht“ und „es ging nicht“.
PostSendEvent läuft danach und trägt zusätzlich status,
throwable, isSuccessful() und wasSkipped().
Konfiguration
Alle Werte sind optional, in config/config.yaml:
centralized_notification_suite:
log:
enabled: true # Jeden Versandversuch protokollieren
store_body: true # Betreff, Inhalt und Token-Werte speichern
retention_days: 90 # 0 behält Einträge dauerhaft
max_attempts: 3 # Wiederholversuche je Nachricht
retry_failed: true # Stündlicher Wiederholversuch
mailer:
php_binary: '' # Leer = automatische Erkennung
process_timeout: 120 # Sekunden bis zum Abbruch des Cache-Neuaufbaus
memory_limit: '-1' # Speicherlimit des Neuaufbau-Subprozesses
Konsolenbefehle
| Befehl | Zweck |
|---|---|
notification:send | Löst eine Benachrichtigung aus. Optionen: -t name=wert, --tokens-json, -l, --dry-run |
notification:install-layouts | Installiert die Layouts „Branded“ und „Transactional“. --list, --force |
notification:install-emails | Installiert Beispielnachrichten. --list, --force |
notification:import-nc | Übernimmt Benachrichtigungen aus dem Notification Center. --dry-run, --force |
vendor/bin/contao-console notification:send bestellbestaetigung \
-t kundenname="Erika Muster" -t bestellnr=2026-001
vendor/bin/contao-console notification:import-nc --dry-run
notification:install-emails --force löscht die Bausteine der betroffenen
Nachricht und legt sie neu an. Eigene Änderungen gehen dabei verloren.
notification:import-nc immer zuerst mit --dry-run laufen lassen.
Cronjobs
| Job | Intervall | Aufgabe |
|---|---|---|
| Wiederholversuch | stündlich | Sendet fehlgeschlagene Nachrichten erneut, bis max_attempts erreicht ist |
| Protokoll aufräumen | täglich | Löscht Einträge älter als retention_days |
| Lizenz erneuern | stündlich | Erneuert den Lizenzdatensatz, sobald die signierte Frist abgelaufen ist |
Erweiterungspunkte
Drei Schnittstellen werden über autoconfigure automatisch registriert – Sie
implementieren die Schnittstelle, mehr ist nicht nötig:
| Schnittstelle | Wofür | Methoden |
|---|---|---|
BlockInterface | Eigener Baustein | getName(), getGroup(), getConfigFields(), getPalette(), render() |
GatewayInterface | Eigener Absender | getName(), getConfigFields(), getPalette(), isAsynchronous(), addressesRecipients(), send() |
TokenProviderInterface | Eigene Tokens | getType(), getDefinitions(), getValues() |
Bei einem eigenen Baustein anschließend contao:migrate ausführen – die Felder
aus getConfigFields() werden zu Spalten in tl_notification_block.
Eigene Token-Definitionen erscheinen automatisch in der Token-Hilfe.
Laufzeitverzeichnisse
| Pfad | Inhalt |
|---|---|
var/notification-state/ | Der Lizenzdatensatz, sein Siegel und das Versions-Wasserzeichen |
var/notification-mail/ | Voreinstellung für den Datei-Absender |
.env.local | MAILER_DSN aus der SMTP-Konfiguration |
var/notification-state/ liegt außerhalb des Webroots und wird nie ausgeliefert.
Der Webserver braucht dort Schreibrechte, sonst lässt sich keine Lizenz aktivieren.
Fehlerbehebung
| Beobachtung | Ursache und Abhilfe |
|---|---|
| „Es wurde nichts versendet: Diese Installation ist nicht aktiviert.“ | Keine gültige Lizenz. Unter Einstellungen aktivieren |
| Lizenz lässt sich nicht aktivieren | Prüfen, ob auf der Startseite eine Domain hinterlegt ist und ob ausgehendes HTTPS möglich ist. Der Schlüssel muss für diesen Hostnamen ausgestellt sein |
| E-Mails bleiben „In Warteschlange“ | Niemand arbeitet die Warteschlange ab – meist läuft der Contao-Cron nicht. Die Nachrichten sind nicht verloren |
| Formular versendet nichts | Im Formular muss die Benachrichtigung ausgewählt, die Nachricht veröffentlicht und für die Sprache vorhanden sein |
| Design wird in Outlook falsch dargestellt | Prüfen, ob das Layout veröffentlicht und der Nachricht zugewiesen ist und ob „CSS inline schreiben“ aktiv ist |
| Webhook meldet einen Fehler | Der Fehlertext des Zielsystems steht im Versandprotokoll |
| „Einstellungen gespeichert, Cache konnte nicht neu aufgebaut werden“ | Die Zugangsdaten stimmen und sind gespeichert, sind aber erst nach einem manuellen Cache-Leeren aktiv. Meist ein zu niedriges memory_limit der PHP-CLI |
Wenn gar nichts versendet wird
Der Reihe nach prüfen:
- Ist die Lizenz aktiv (Einstellungen → V-T.ONE Licence management)?
- Ist die Nachricht veröffentlicht, und liegt das heutige Datum in ihrem Zeitfenster?
- Ist der Absender veröffentlicht?
- Hat die Nachricht Empfänger?
- Was steht im Versandprotokoll?
