CSP-konform, per AJAX, ohne Page-Reload
Wer das Luma-Newsletter-Widget einfach in ein Hyvä-Theme kopiert, kollidiert mit der strikten Content Security Policy und bekommt ein Formular, das im Browser lautlos versagt. Eine saubere Newsletter-Integration braucht stattdessen registerInlineScript(), eine schlanke Alpine.js-Komponente mit fetch(), einen korrekt kommunizierten Double-Opt-in-Flow und eine serverseitig geprüfte DSGVO-Consent-Checkbox, sauber über Layout-XML in Footer, Checkout-Success und CMS-Block platziert.
Inhaltsverzeichnis
- 1. Warum Copy-Paste des Luma-Widgets an Hyväs CSP scheitert
- 2. Hyväs Content Security Policy und registerInlineScript() im Detail
- 3. Die Alpine.js-Komponente für das Signup-Formular
- 4. fetch() gegen den Subscribe-Controller: form_key und Response-Handling
- 5. Der Double-Opt-in-Flow: was Magento serverseitig übernimmt
- 6. DSGVO-Consent: Checkbox als Client-Gate und Server-Revalidierung
- 7. Formular platzieren: Footer, Checkout-Success und CMS-Block per Layout-XML
- 8. ViewModel: Abonnement-Status für eingeloggte Kunden
- 9. CSP-brechende Lösung vs. empfohlenes Hyvä-Pattern
- 10. Zusammenfassung
- 11. FAQ
1. Warum Copy-Paste des Luma-Widgets an Hyväs CSP scheitert
Viele Teams beginnen ihre Newsletter-Integration damit, dass sie Magento_Newsletter/templates/subscribe.phtml aus dem Luma-Theme kopieren und die Klassen auf Tailwind umstellen. Das Formular sieht danach passend aus, funktioniert im Browser aber nicht: Der Klick auf den Button erzeugt in der Konsole eine CSP-Violation, weil das Luma-Template auf onclick-Attribute, jQuery-Ajax-Aufrufe und teils Knockout-Bindings setzt, die Hyväs Content Security Policy grundsätzlich blockiert. Hyvä liefert über Hyvä_Csp und das zugrunde liegende Magento-CSP-Modul eine strikte script-src-Direktive aus, die nur explizit erlaubte Skripte zulässt, alles andere wird stillschweigend verworfen.
Das Ergebnis ist besonders tückisch, weil es keinen sichtbaren Fehler auf der Seite gibt. Der Klick passiert, nichts reagiert, und erst die Browser-Konsole zeigt Refused to execute inline script because it violates the following Content Security Policy directive. In der Praxis fällt das oft erst im Live-Betrieb auf, wenn Support-Anfragen zu einem Newsletter-Formular eintreffen, das angeblich nicht funktioniert. Eine zweite, subtilere Variante des gleichen Problems: Das kopierte Formular funktioniert zwar, löst aber bei jedem Absenden einen vollständigen Seiten-Reload aus, weil kein AJAX-Handler registriert wurde und stattdessen ein klassischer Form-POST mit Redirect greift, was auf der Checkout-Success-Seite besonders störend ist.
Dieser Beitrag beschreibt eine funktionierende Newsletter-Integration von Grund auf: die CSP-Grundlagen und registerInlineScript(), eine Alpine.js-Komponente mit fetch()-basiertem AJAX-Subscribe, den serverseitigen Double-Opt-in-Flow inklusive Frontend-Kommunikation, eine DSGVO-Consent-Checkbox mit Client- und Server-Prüfung, sowie die Platzierung des Formulars über Layout-XML an mehreren Stellen im Theme.
2. Hyväs Content Security Policy und registerInlineScript() im Detail
Magentos CSP-Modul arbeitet mit einer script-src-Direktive, die entweder auf Nonces oder auf Hashes basiert. Bei einer nonce-basierten Policy erhält jeder erlaubte <script>-Block bei jedem Seitenaufruf einen zufälligen, einmaligen nonce-Attributwert, der auch im CSP-Response-Header steht. Stimmen Attribut und Header überein, führt der Browser das Skript aus, jedes andere Inline-Skript ohne passenden Nonce wird verworfen. Hyvä kapselt diese Mechanik über den HyvaCsp-ViewModel und die Methode registerInlineScript(), die intern den korrekten Nonce in den Script-Tag injiziert, ohne dass Template-Autoren die Nonce-Generierung selbst anfassen müssen.
Für eine CSP-konforme Umsetzung bedeutet das konkret: Jeder Inline-<script>-Block in einem phtml-Template muss unmittelbar nach dem schließenden </script>-Tag von einem Aufruf von $hyvaCsp->registerInlineScript() begleitet werden. Fehlt dieser Aufruf, wird das Skript im Browser stillschweigend blockiert, exakt das Verhalten, das ein naiv kopiertes Luma-Widget zeigt. Die Registrierung erfolgt üblicherweise ganz am Ende des Templates, nachdem alle Inline-Skripte im Markup bereits ausgegeben wurden, damit Hyvä den vollständigen Skriptinhalt für die Hash- beziehungsweise Nonce-Zuordnung erfassen kann.
<?php
/** @var \Magento\Framework\View\Element\Template $block */
/** @var \Hyva\Theme\ViewModel\HyvaCsp $hyvaCsp */
$hyvaCsp = $viewModels->require(\Hyva\Theme\ViewModel\HyvaCsp::class);
?>
<div class="my-4">
<button id="scroll-hint" class="text-sm text-orange-700 underline">
Nach oben scrollen
</button>
</div>
<script>
// WITHOUT registerInlineScript() below, the CSP silently blocks this block
document.getElementById('scroll-hint').addEventListener('click', () => {
window.scrollTo({ top: 0, behavior: 'smooth' });
});
</script>
<?php $hyvaCsp->registerInlineScript(); ?>
Wichtig für die Fehlersuche: Der Aufruf von registerInlineScript() muss im selben phtml-Rendering-Durchlauf erfolgen wie der Script-Block selbst, ein Include in einem separaten Template funktioniert nicht zuverlässig. Wer die Policy im laufenden Betrieb debuggen will, kann das CSP-Modul in Magentos Admin auf Report-Only stellen. Dann werden Verstöße nur protokolliert statt blockiert, was besonders hilfreich ist, wenn eine solche Integration Drittanbieter-Skripte wie ein externes Tracking-Pixel für Anmeldungen einbindet und dafür zusätzliche script-src-Hosts über die Datei etc/csp_whitelist.xml freigegeben werden müssen.
3. Die Alpine.js-Komponente für das Signup-Formular
Statt eines Redirect-basierten Form-POST übernimmt eine kleine Alpine.js-Komponente den vollständigen Lebenszyklus des Anmeldeformulars. Die Komponente kennt fünf Zustände: idle vor dem ersten Absenden, loading während der Anfrage, success bei sofortiger Bestätigung, confirmation_pending wenn Double-Opt-in aktiv ist, sowie error für ungültige Eingaben oder Serverfehler. Jeder Zustand steuert, welcher Text im Formular sichtbar ist, ausschließlich über x-show und x-text, niemals über direktes DOM-Manipulieren außerhalb von Alpine.
Die E-Mail-Adresse wird mit x-model gebunden, das Absenden über x-on:submit.prevent abgefangen, damit kein klassischer Form-POST und damit kein Page-Reload ausgelöst wird. Der komplette Zustand lebt in einer Alpine.data()-Definition, die zusammen mit dem Markup im selben Template steht und wie im vorherigen Abschnitt beschrieben über registerInlineScript() freigeschaltet werden muss. Für eine robuste Newsletter-Integration ist wichtig, dass Ladezustand und Fehlertext getrennt gehalten werden, damit ein Nutzer nach einem Fehler erneut absenden kann, ohne dass die Komponente in einem inkonsistenten Zwischenzustand hängen bleibt.
<?php
/** @var \Magento\Framework\View\Element\Template $block */
/** @var \Hyva\Theme\ViewModel\HyvaCsp $hyvaCsp */
/** @var \Hyva\Theme\Model\ViewModelRegistry $viewModels */
$hyvaCsp = $viewModels->require(\Hyva\Theme\ViewModel\HyvaCsp::class);
$formKey = $viewModels->require(\Hyva\Theme\ViewModel\FormKey::class);
$subscribeUrl = $block->getUrl('newsletter/subscriber/new');
?>
<div x-data="newsletterSignup('<?= $block->escapeJs($subscribeUrl) ?>', '<?= $block->escapeJs($formKey->getFormKey()) ?>')"
class="rounded-xl border border-slate-200 p-6 bg-white">
<form x-on:submit.prevent="subscribe()" class="flex flex-col sm:flex-row gap-3">
<input type="email" x-model="email" :disabled="state === 'loading'"
placeholder="deine@email.de" required
class="flex-1 rounded-lg border border-slate-300 px-4 py-2 text-sm" />
<label class="flex items-center gap-2 text-xs text-slate-600">
<input type="checkbox" x-model="consent" class="rounded border-slate-300" />
Ich stimme der Datenverarbeitung laut Datenschutzerklärung zu.
</label>
<button type="submit" :disabled="state === 'loading' || !consent"
class="bg-orange-700 disabled:opacity-50 text-white font-semibold rounded-lg px-5 py-2 text-sm">
<span x-show="state !== 'loading'">Anmelden</span>
<span x-show="state === 'loading'">Wird gesendet …</span>
</button>
</form>
<p class="mt-3 text-sm" :class="state === 'error' ? 'text-red-600' : 'text-green-700'"
x-show="message" x-text="message"></p>
</div>
<script>
document.addEventListener('alpine:init', () => {
Alpine.data('newsletterSignup', (endpoint, formKey) => ({
email: '',
consent: false,
state: 'idle',
message: '',
async subscribe() {
// Client-side gate: no request is sent without explicit consent
if (!this.consent) {
this.state = 'error';
this.message = 'Bitte bestätige zuerst die Datenschutzerklärung.';
return;
}
this.state = 'loading';
this.message = '';
try {
const response = await fetch(endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'X-Requested-With': 'XMLHttpRequest'
},
body: new URLSearchParams({
email: this.email,
form_key: formKey,
consent: this.consent ? '1' : '0'
})
});
const data = await response.json();
this.state = data.status;
this.message = data.message;
} catch (e) {
this.state = 'error';
this.message = 'Verbindungsfehler, bitte später erneut versuchen.';
}
}
}));
});
</script>
<?php $hyvaCsp->registerInlineScript(); ?>
4. fetch() gegen den Subscribe-Controller: form_key und Response-Handling
Der Standard-Controller newsletter/subscriber/new ist ursprünglich für einen klassischen Form-POST mit anschließendem Redirect und Session-Message ausgelegt, nicht für JSON-Antworten. Für eine AJAX-taugliche Newsletter-Integration braucht es daher entweder eine kleine Plugin-Erweiterung, die bei erkanntem X-Requested-With: XMLHttpRequest-Header eine Magento\Framework\Controller\Result\Json-Instanz statt eines Redirects zurückgibt, oder einen dedizierten AJAX-Controller, der intern denselben SubscriberFactory und dieselbe Validierungslogik verwendet. Wichtig ist in beiden Fällen, dass der reguläre Non-JS-Fallback weiterhin funktioniert, falls Alpine aus irgendeinem Grund nicht initialisiert.
Der form_key muss bei jedem POST-Request mitgeschickt werden, da Magentos eingebauter CSRF-Schutz sonst den Request mit einem 302-Redirect zur Formularseite abweist, was im fetch()-Callback als HTML statt JSON ankommt und beim response.json()-Aufruf einen Parse-Fehler erzeugt. Der zuverlässigste Weg, den aktuellen form_key zu erhalten, ist Hyväs FormKey-ViewModel, das im Template einmal aufgelöst und als Parameter an die Alpine-Komponente übergeben wird, statt ihn im Frontend aus einem Cookie zu lesen. Für Fehlerzustände sollte das Handling zwischen HTTP-Fehlern (Status außerhalb 200 bis 299), Netzwerkfehlern im catch-Block und fachlichen Fehlern innerhalb einer erfolgreichen JSON-Antwort unterscheiden, damit der Nutzer bei einer ungültigen E-Mail-Adresse eine andere Meldung sieht als bei einem Serverausfall.
Ein Detail, das in der Praxis häufig übersehen wird: Der Submit-Button muss während state === 'loading' deaktiviert sein, sonst kann ein ungeduldiger Klick doppelte Anfragen und im schlimmsten Fall doppelte Bestätigungsmails auslösen. Die oben gezeigte Komponente löst das über das :disabled-Binding am Button, kombiniert mit der Zustandsvariable state, ohne zusätzliches Debouncing oder externe Bibliotheken.
5. Der Double-Opt-in-Flow: was Magento serverseitig übernimmt
Magento_Newsletter modelliert den Abonnementstatus über die Konstanten STATUS_SUBSCRIBED, STATUS_NOT_ACTIVE, STATUS_UNCONFIRMED und STATUS_UNSUBSCRIBED im Subscriber-Model. Ist die Store-Konfiguration newsletter/subscription/confirm aktiviert, setzt subscribe() den Status nicht direkt auf STATUS_SUBSCRIBED, sondern auf STATUS_UNCONFIRMED, generiert einen zufälligen Bestätigungscode und versendet über den Transactional-Email-Mechanismus eine Bestätigungsmail mit einem Link zum Controller newsletter/subscriber/confirm, der Subscriber-ID und Code als Parameter enthält. Erst der Klick auf diesen Link setzt den Status endgültig auf bestätigt.
Für die Frontend-Seite der Newsletter-Integration bedeutet das: Die Alpine-Komponente darf nach einem erfolgreichen AJAX-Request nicht pauschal "Danke für deine Anmeldung" anzeigen, sondern muss zwischen sofortigem Erfolg (Double-Opt-in deaktiviert) und dem Zwischenzustand confirmation_pending unterscheiden, in dem der Text auf das Postfach verweist, etwa "Bitte bestätige deine Anmeldung über den Link in der E-Mail, die wir dir gerade geschickt haben". Ebenso muss ein Kunde, der sich mit einer bereits vollständig bestätigten Adresse erneut anmeldet, eine eigene Meldung sehen statt eines generischen Fehlers, und ein zuvor abgemeldeter Kunde, der sich reaktiviert, durchläuft je nach Konfiguration erneut den vollständigen Double-Opt-in-Zyklus.
{
"status": "confirmation_pending",
"message": "Bitte bestätige deine Anmeldung über den Link in der E-Mail.",
"subscriber_status": "unconfirmed"
}
// Immediate success: double opt-in disabled in store configuration
{
"status": "success",
"message": "Danke, deine Anmeldung war erfolgreich.",
"subscriber_status": "subscribed"
}
// Already confirmed address tries to subscribe again
{
"status": "already_subscribed",
"message": "Diese E-Mail-Adresse ist bereits für den Newsletter angemeldet.",
"subscriber_status": "subscribed"
}
// Missing GDPR consent: re-validated server side even though the client already gates this
{
"status": "error",
"message": "Bitte bestätige die Datenschutzerklärung, bevor du dich anmeldest.",
"subscriber_status": null
}
6. DSGVO-Consent: Checkbox als Client-Gate und Server-Revalidierung
Eine rechtssichere Newsletter-Integration verlangt eine explizite Einwilligung, bevor personenbezogene Daten überhaupt an den Server übertragen werden. Im Alpine-Component-Code aus Abschnitt 3 prüft die Methode subscribe() den Wert von consent, bevor überhaupt ein fetch()-Aufruf ausgeführt wird: Ist die Checkbox nicht angehakt, bricht die Methode sofort ab und zeigt eine Validierungsmeldung, ohne dass ein einziges Byte an den Server geht. Das ist ein reines Client-Gate und verbessert die Nutzerführung, ersetzt aber keine serverseitige Prüfung.
Da das native Magento_Newsletter-Modul kein Consent-Feld kennt, muss die Zustimmung serverseitig über eine eigene Erweiterung erzwungen werden, entweder als Plugin um den Subscribe-Controller oder als Observer auf ein passendes Event vor dem Speichern des Subscribers. Ein Request ohne den Consent-Parameter oder mit dem Wert 0 muss dort mit einer klaren Fehlermeldung abgelehnt werden, unabhängig davon, was das Frontend anzeigt, denn ein manipulierter oder per Skript automatisierter Request kann das Client-Gate umgehen. Für die Nachweispflicht aus Artikel 7 DSGVO empfiehlt sich zusätzlich, Zeitpunkt und IP-Hash der Einwilligung in einer eigenen Tabelle oder als Subscriber-Attribut zu protokollieren.
<?php
declare(strict_types=1);
namespace Mironsoft\NewsletterConsent\Plugin;
use Magento\Framework\App\RequestInterface;
use Magento\Framework\Exception\LocalizedException;
use Magento\Newsletter\Controller\Subscriber\NewAction;
/**
* Re-validates GDPR consent server side, independent of the client-side gate
* inside the Alpine.js signup component.
*/
class ValidateConsentPlugin
{
/**
* @param RequestInterface $request Current HTTP request instance.
*/
public function __construct(
private readonly RequestInterface $request,
) {
}
/**
* Aborts the subscribe action when consent was not explicitly granted.
*
* @param NewAction $subject Intercepted subscribe controller action.
* @return void
* @throws LocalizedException When the consent flag is missing or falsy.
*/
public function beforeExecute(NewAction $subject): void
{
$consent = (string) $this->request->getParam('consent', '0');
if ($consent !== '1') {
throw new LocalizedException(
__('Bitte bestätige die Datenschutzerklärung, bevor du dich anmeldest.')
);
}
}
}
7. Formular platzieren: Footer, Checkout-Success und CMS-Block per Layout-XML
Damit dieselbe Newsletter-Integration nicht dreifach dupliziert im Theme herumliegt, wird genau ein phtml-Template gebaut, das über Block-Argumente konfigurierbar ist, etwa eine Überschrift, ein Kompakt-Modus für den Footer und ein ausführlicherer Modus für die Checkout-Success-Seite. Layout-XML bindet diesen einen Block anschließend an drei Stellen ein: im default.xml für den Footer, im checkout_onepage_success.xml-Handle direkt nach der Bestellbestätigung, sowie über einen CMS-Widget-Block, der Redakteuren erlaubt, das Formular in beliebigen CMS-Seiten per Widget einzufügen, ohne dass Entwickler dafür zusätzlichen Code schreiben müssen.
Diese Wiederverwendung folgt dem üblichen Hyvä-UI-Component-Muster: Ein Block mit ViewModel-Injektion, ein Template, das ausschließlich über $block->getData() beziehungsweise übergebene Argumente konfiguriert wird, und keine Kopie des Markups an mehreren Stellen im Theme. Änderungen am Formular, etwa ein neuer Consent-Text oder ein zusätzliches Eingabefeld, müssen dadurch nur an einer einzigen Stelle gepflegt werden und wirken sich automatisch auf Footer, Checkout-Success und CMS-Block gleichermaßen aus.
<!-- app/design/frontend/Mironsoft/default/Magento_Theme/layout/default.xml -->
<referenceContainer name="footer-container">
<block class="Magento\Framework\View\Element\Template"
name="footer.newsletter.signup"
template="Magento_Newsletter::signup/form.phtml">
<arguments>
<argument name="heading" xsi:type="string">Newsletter abonnieren</argument>
<argument name="variant" xsi:type="string">compact</argument>
</arguments>
</block>
</referenceContainer>
<!-- app/design/frontend/Mironsoft/default/Magento_Checkout/layout/checkout_onepage_success.xml -->
<referenceContainer name="content">
<block class="Magento\Framework\View\Element\Template"
name="checkout.success.newsletter.signup"
template="Magento_Newsletter::signup/form.phtml"
after="checkout.success">
<arguments>
<argument name="heading" xsi:type="string">Immer informiert bleiben</argument>
<argument name="variant" xsi:type="string">full</argument>
</arguments>
</block>
</referenceContainer>
<!-- Redakteure binden dasselbe Template zusätzlich per CMS-Widget ein,
ohne dass dafür weiterer Code notwendig ist. -->
8. ViewModel: Abonnement-Status für eingeloggte Kunden
Für eingeloggte Kunden soll das Formular nicht bei jedem Seitenaufruf erneut nach der E-Mail-Adresse fragen oder gar dem bereits abonnierten Kunden ein leeres Formular präsentieren. Ein schlankes ViewModel, das ArgumentInterface implementiert und per Constructor Property Promotion den SubscriberFactory sowie eine CustomerSession injiziert, kapselt genau diese Logik: eine Methode isSubscribed(): bool und eine Methode getCustomerEmail(): ?string, die dem Template erlauben, das Formular entweder vorzubefüllen, komplett auszublenden oder direkt im Zustand "bereits angemeldet" zu rendern, ohne auf den ersten AJAX-Roundtrip warten zu müssen.
Dieses ViewModel wird über dieselben Layout-XML-Argumente wie in Abschnitt 7 in den gemeinsamen Block injiziert, sodass Footer, Checkout-Success und CMS-Block dasselbe Verhalten für eingeloggte Kunden zeigen. Im Template initialisiert der Alpine-x-data-Aufruf den Anfangszustand direkt aus PHP heraus, etwa state: '<?= $viewModel->isSubscribed() ? 'already_subscribed' : 'idle' ?>', wodurch die Newsletter-Integration für bereits abonnierte Kunden ohne sichtbares Aufblitzen des leeren Formulars auskommt und die wahrgenommene Ladezeit spürbar sinkt.
9. CSP-brechende Lösung vs. empfohlenes Hyvä-Pattern
Die folgende Übersicht fasst die zentralen Unterschiede zwischen dem naiv kopierten Luma-Ansatz und der in diesem Beitrag beschriebenen Newsletter-Integration zusammen. Jede Zeile entspricht einer konkreten Stelle, an der ein kopiertes Widget typischerweise bricht.
| Aufgabe | CSP-brechender Ansatz | Empfohlenes Hyvä-Pattern | Vorteil |
|---|---|---|---|
| Submit-Handler | onclick="subscribe()" |
x-on:submit.prevent |
CSP-konform, kein Nonce-Handling nötig |
| Formular-Antwort | Full-Page-Reload mit Redirect | fetch() + JSON + x-text |
Kein Reload, sofortiges Feedback |
| Inline-Skript | ohne CSP-Registrierung | $hyvaCsp->registerInlineScript() |
Skript läuft, statt lautlos blockiert zu werden |
| DSGVO-Consent | nur clientseitig geprüft | Client-Gate + Server-Revalidierung | Rechtssicher und auditierbar |
| Formular-Platzierung | pro Seite hart dupliziert | ein Template + Layout-XML-Argumente | Wartbar, DRY über Footer, Checkout, CMS |
In jeder Zeile steckt derselbe Grundgedanke: Hyvä erzwingt, dass Verhalten explizit deklariert wird, anstatt implizit über Inline-Handler oder Redirects zu funktionieren. Das kostet beim ersten Aufbau einer solchen Lösung etwas mehr Sorgfalt, zahlt sich aber in Wartbarkeit, CSP-Konformität und einem messbar besseren Nutzererlebnis ohne Page-Reload aus.
10. Zusammenfassung
Eine CSP-konforme Newsletter-Integration in Hyvä ist kein einzelner Trick, sondern das Zusammenspiel mehrerer sauber getrennter Bausteine: registerInlineScript() schaltet Inline-Skripte innerhalb der strikten Content Security Policy frei, eine Alpine.js-Komponente mit fetch() ersetzt den klassischen Form-POST durch AJAX ohne Page-Reload, und der Double-Opt-in-Flow wird serverseitig von Magento_Newsletter verwaltet, muss aber im Frontend als eigener Zwischenzustand kommuniziert werden. Die DSGVO-Consent-Checkbox fungiert als Client-Gate, ersetzt aber nie die serverseitige Prüfung, denn nur diese ist gegen manipulierte Requests abgesichert.
Wer das Formular zusätzlich als ein einziges wiederverwendbares Template über Layout-XML in Footer, Checkout-Success und CMS-Block einbindet, und den Abonnementstatus für eingeloggte Kunden über ein schlankes ViewModel bereitstellt, erhält eine Newsletter-Integration, die an genau einer Stelle im Code gepflegt wird und an mehreren Stellen im Theme konsistent funktioniert, ganz ohne die CSP-Fallstricke eines kopierten Luma-Widgets.
Newsletter-Integration in Hyvä Themes: Das Wichtigste auf einen Blick
CSP & registerInlineScript()
Jeder Inline-Script-Block braucht den Aufruf direkt danach, sonst wird er von Hyväs strikter Content Security Policy stillschweigend blockiert.
Alpine.js & fetch()
Eine Alpine-Komponente mit fetch() gegen den Subscribe-Controller ersetzt den Form-POST, korrekt mit form_key und JSON-Response-Handling.
Double-Opt-in & Consent
Magento_Newsletter verwaltet den Bestätigungs-Flow serverseitig, das Frontend muss den Zwischenzustand klar kommunizieren, Consent wird client- und serverseitig geprüft.
Layout-XML & ViewModel
Ein Template für Footer, Checkout-Success und CMS-Block, ein ViewModel für den Abonnementstatus eingeloggter Kunden.
11. FAQ: Newsletter-Integration in Hyvä Themes
1Warum funktioniert das Luma-Widget nicht einfach in Hyvä?
2Was macht registerInlineScript() genau?
3Wie vermeide ich form_key-Fehler?
4Single-Opt-in vs. Double-Opt-in?
5Wie zeige ich 'Bitte E-Mail bestätigen' an?
6Muss die DSGVO-Checkbox serverseitig geprüft werden?
7Formular im Footer und auf Checkout-Success?
8Mehrfach-Anmeldung bei doppeltem Klick verhindern?
9Was passiert bei erneuter Anmeldung nach Abmeldung?
10Braucht es zusätzliches JavaScript außerhalb von Alpine.js?
Mironsoft
Hyvä-Theme-Entwicklung und CSP-konforme Magento-2-Integrationen
Newsletter-Integration, die an Hyväs CSP nicht scheitert?
Wir setzen sie CSP-konform mit Alpine.js, korrektem Double-Opt-in und DSGVO-Consent um, sauber über Layout-XML in Footer, Checkout und CMS-Blöcken platziert.
CSP-Audit
Bestehende Theme-Templates auf CSP-Verstöße und fehlende registerInlineScript()-Aufrufe prüfen
Alpine-Komponenten
AJAX-Formulare mit fetch(), sauberen Zuständen und ohne Page-Reload umsetzen
DSGVO-Beratung
Consent-Flows client- und serverseitig rechtssicher absichern und dokumentieren