Search by

tenbruggencate / newsletter-lite

guy@tenbruggencatedevelopment.nl

Shopware 6 newsletter signup: GDPR-safe with double opt-in, opaque-token unsubscribe with email erasure, per-IP rate limit.

Package info

bitbucket.org/Bruggencate/sw-plugin-newsletter-lite

Homepage

Issues

Documentation

Type:shopware-platform-plugin

pkg:composer/tenbruggencate/newsletter-lite

Statistics

Installs: 186

Dependents: 0

Suggesters: 0

1.18.1 2026-10-04 23:15 UTC

This package is auto-updated.

Last update: 2026-10-04 23:38:36 UTC


README

Ten Bruggencate Development

Newsletter Lite

Newsletter Lite

Own a clean, GDPR-safe subscriber list. Newsletter Lite is the genuine free tool for capturing and managing newsletter subscribers in Shopware 6: a standalone subscriber table, opaque-token unsubscribe with one-click email erasure, double opt-in by default, and a searchable admin list with CSV export. No third-party dependencies. None of Shopware's built-in newsletter UX-customisation pain.

It does one thing, and does it well — get consent-clean subscribers into a list you control, and let you get them back out again (CSV export) for whatever you send them with.

▲ Newsletter Pro is released. It adds the sending side — a campaign editor, live product blocks from your catalogue and segmentation on your shop's own order data — and installs side-by-side with this plugin. See Part of a Lite/Pro family below, or the Pro overview.

License: MIT · Shopware: 6.7.x · PHP: 8.2 or newer

🇬🇧 English · 🇳🇱 Nederlands · 🇩🇪 Deutsch

📖 More context: Why we built this · How it compares

Screenshots

Newsletter landing page on desktop — branded hero, three-bullet promise, signup form card, subscriber-count transparency footer.

Landing page — desktop

Newsletter landing page on mobile — fully stacked, full-width form fields, 24×24 minimum touch targets.

Landing page — mobile (responsive)

Admin Newsletter subscribers module — searchable subscriber list with Export CSV, showing Confirmed, Pending confirmation, and Unsubscribed status badges.

Admin — subscriber management — searchable list, CSV export, double-opt-in status badges

What it does

Shopware ships a newsletter module, but customising its UX fights the built-in flow. This plugin is deliberately smaller:

  • tenbruggencate_nl_subscriber table — plain id, email, source, brand_name (server-derived — see below), consent_given + audit trail, confirmed + DOI token columns, unsubscribe_token, locale, subscribed_at, unsubscribed_at
  • Signup form partial — drop-in Twig include with compact (footer) and full (landing) variants
  • Landing page at /newsletter — SEO-safe, standalone, no theme dependency
  • Opaque-token unsubscribe — URL carries a CSPRNG token, not the email address. The link opens a confirmation page (opening it changes nothing, so link scanners like Outlook Safe Links can't unsubscribe anyone); one button click scrubs the email to erased-<id>@newsletter.invalid. RFC 8058 one-click unsubscribe for mail clients' own button is built in, the row stays for stats
  • Three locales out of the box — nl-NL, en-GB, de-DE
  • Double opt-in by default — new signups receive a confirmation email; rows only flip to confirmed=1 after the visitor clicks the link. 24h token expiry. Existing pre-DOI subscribers grandfathered as already confirmed. Single-opt-in remains available via the requireDoubleOptIn config toggle if you have a deliberate reason to use it
  • House-style confirmation e-mail — the double-opt-in mail is a regular Shopware e-mail template: wrapped in the sales channel's header and footer, editable per language under Settings → Email templates (preview + test send), never overwritten by the plugin. See "Apply your house style to the confirmation e-mail" below
  • Origin / volume / consent triple-defence on the subscribe endpoint — cross-origin POSTs rejected, per-IP rate limit (5 attempts / 10 min), and DOI for proof-of-consent
  • Admin subscriber list — a "Newsletter subscribers" module under the Marketing menu: search by email, sortable columns, a per-row confirmed / pending / unsubscribed status, and one-click CSV export. Backed by a DAL entity — the opaque tokens and consent IP hash are never exposed through the admin API
  • Flow Builder triggers — subscribed, confirmed and unsubscribed, each with the subscriber's address as mail recipient, so a welcome mail or a CRM webhook is a flow, not code. See "Flow Builder" below

Install

composer require tenbruggencate/newsletter-lite
bin/console plugin:refresh
bin/console plugin:install --activate TenBruggencateNewsletterLite
bin/console database:migrate --all TenBruggencateNewsletterLite
bin/console cache:clear

Configuration

Configurable per sales channel from Extensions → TenBruggencateNewsletterLite.

FieldDefaultPurpose
enabledtrueKill switch — signup form renders nothing when off
brandName(empty)Shown in headings (e.g. "Acme Club"); leave blank for generic wording
consentText(snippet)Checkbox label; HTML allowed — rendered through Shopware's sw_sanitize (links with href, target and title, strong, em, br and the other basic formatting tags survive; scripts, event-handler attributes and javascript: URLs are stripped)
privacyPageUrl/about/privacyLink target in the fineprint
landingPageEnabledtrueToggle the /newsletter landing page route
signupHeadingLevelh2Heading level of the form's own title (h2 / h3 / h4) — keep h2 so the footer form doesn't skip a level under the page's h1 (WCAG 1.3.1 heading order)

Apply your house style to the confirmation e-mail

The double-opt-in confirmation is a regular Shopware e-mail template, so it picks up the same house style as your order confirmations — no code involved.

  1. Header and footer — go to Settings → Email templates → Header and footer. Create or edit a set with your logo, colours and footer text and assign it to your sales channel(s). Every e-mail Shopware sends for that sales channel — this one included — is wrapped in it.
  2. The template itself — go to Settings → Email templates → Email templates and open the type Newsletter confirmation (Newsletter Lite) (technical name tenbruggencate_newsletter_confirm). Edit the subject, the HTML body and the plain-text body per language, use Preview to check the result and Send test email to receive it in your own inbox. Available variables: {{ confirmUrl }} (the confirmation link — keep it in), {{ brand }} (the resolved brand name), {{ hours }} (how long the link stays valid) and {{ salesChannel }} (for example {{ salesChannel.name }}).
  3. Your edits are never overwritten — the plugin creates the template once, when its migrations run, and never touches a template that already exists. Plugin updates keep your version.

If the template cannot be found (for example because database:migrate has not run yet), the plugin falls back to its built-in plain-text e-mail and writes a warning naming tenbruggencate_newsletter_confirm to the log, so no signup is left without a confirmation mail.

Flow Builder

The plugin dispatches three events that appear as triggers in Settings → Flow Builder (in the trigger tree under Ten Bruggencate → Newsletter Lite), next to Shopware's own newsletter triggers. Each one is dispatched only after the subscriber row has been written, so a flow always sees the final state.

TriggerEvent nameClassFires when
Subscribedtenbruggencate.newsletter_lite.subscribedNewsletterLiteSubscribedEventA visitor submitted the form and a new subscriber was created — with double opt-in still pending (confirmed = false) or, with single opt-in, already active (confirmed = true)
Confirmedtenbruggencate.newsletter_lite.confirmedNewsletterLiteConfirmedEventA subscriber clicked a valid, unexpired double-opt-in link (not on a repeat click, not on an expired link)
Unsubscribedtenbruggencate.newsletter_lite.unsubscribedNewsletterLiteUnsubscribedEventA subscriber used their unsubscribe link and the address was erased (not on a repeat visit)

All three are MailAware — the Send email action works out of the box with the subscriber's address as the recipient — and SalesChannelAware, so a flow can branch on the sales channel. The data below is available to actions and e-mail templates as plain variables (for example {{ email }}):

VariableTypeMeaning
emailstringThe subscriber's address. On the unsubscribed trigger this is the address before erasure (the row itself now holds erased-<id>@newsletter.invalid), so a goodbye mail is still possible; the plugin stores it nowhere
brandNamestringThe brand the subscriber signed up under (see "Related plugins")
sourcestringfooter, landing, checkout or other
localestringThe storefront locale at signup, e.g. en-GB
salesChannelIdstringThe sales channel of the request
subscribedAtstringSignup time, ISO 8601 (UTC)
confirmedboolSubscribed only — false while double opt-in is pending
confirmedAtstringConfirmed only — ISO 8601 (UTC)
unsubscribedAtstringUnsubscribed only — ISO 8601 (UTC)

The opaque unsubscribe / confirmation tokens and the consent IP hash are never part of an event. PHP code can listen on the classes in TenBruggencateNewsletterLite\Event\ like on any Symfony event.

The double-opt-in confirmation mail is still sent by the plugin itself (see above). The triggers are for additional automation: a welcome mail after confirmation, a customer tag, a webhook to your CRM.

Routes

RouteMethodPurpose
/newsletterGETLanding page with signup form
/newsletter/subscribePOSTHandles the signup form submit
/newsletter/unsubscribe/{token}GETConfirmation page ("Do you want to unsubscribe j•••@example.com?") — no state change
/newsletter/unsubscribe/{token}POSTThe confirmation button: unsubscribe + email scrub, then redirect
/newsletter/unsubscribe/{token}/one-clickPOSTRFC 8058 one-click (List-Unsubscribe=One-Click body, no CSRF), 200 empty body
/newsletter/unsubscribedGET"You're unsubscribed" page after the button

All routes are registered as storefront routes, so on a domain with a path (https://shop.example/nl) every generated link — the double-opt-in mail, form actions, redirects — keeps that path and the visitor's language.

For your own mails, TenBruggencateNewsletterLite\Service\UnsubscribeUrlGenerator (public service) builds both absolute URLs for a subscriber token and sales channel: unsubscribeUrl() for the visible link, oneClickUrl() for the List-Unsubscribe header together with List-Unsubscribe-Post: List-Unsubscribe=One-Click.

Usage

Embed the signup form anywhere via Twig:

{# Compact variant — for footers #}
{% sw_include '@TenBruggencateNewsletterLite/storefront/component/newsletter/signup-form.html.twig' with {
    source: 'footer',
    compact: true
} only %}

{# Default variant — for landing pages #}
{% sw_include '@TenBruggencateNewsletterLite/storefront/component/newsletter/signup-form.html.twig' %}

The partial reads its heading level from the signupHeadingLevel config field. Override it per include with nlHeadingLevel when your theme wraps the form in a section that already has its own heading — allowed values are h2, h3 and h4; anything else falls back to h2, so an unvalidated value can never end up as a tag name:

{% sw_include '@TenBruggencateNewsletterLite/storefront/component/newsletter/signup-form.html.twig' with {
    source: 'checkout',
    nlHeadingLevel: 'h3'
} only %}

Standards

  • Performance — no JS dependency; plain POST with a 302 redirect on success. Async submission via fetch() is a ~20-line enhancement you ship in your theme if desired.
  • SEO — add /newsletter/unsubscribe/* to your theme's robots.txt disallow list (the landing page is indexable, the unsubscribe endpoint should not be).
  • GDPR — the opaque-token pattern means the unsubscribe URL leaks nothing. Unsubscribing (one button click on the confirmation page, or a mail client's one-click button) also erases the email address from storage; the row stays for stats, PII is gone. Satisfies right-to-erasure without a manual request. Every signup writes a consent audit trail (consent_given_at, consent_text_version, consent_ip_hash) for Article-7 supervisory audits. Full data-flow + subject-rights documentation in GDPR.md.
  • WCAG 2.2 AA — semantic <form>, <label for>, <input type=email required>, keyboard-accessible by construction. Focus state inherited from your theme. Live axe-core audit output + desktop + mobile screenshots + localised-copy evidence: docs/ACCESSIBILITY.md.
  • Security — CSRF-protected via Shopware's default handler; unsubscribe_token is 24 bytes from random_bytes(), hex-encoded to a 48-character string. No user-controllable input in the unsubscribe SQL (token is looked up with a prepared statement).
  • Uninstall — plugin:uninstall --keep-user-data preserves the tenbruggencate_nl_subscriber table AND every TenBruggencateNewsletterLite.config.* row (default, recommended path when you might reinstall). plugin:uninstall without the flag drops the subscriber table entirely + clears config — use this only when you genuinely want to discard all subscribers. There is no middle ground; exports happen before, not during, uninstall.

Production-readiness checklist

A deliberately short list of things to verify before enabling the plugin on a customer-facing storefront. Not legal cover — the MIT license already disclaims warranty — but practical guidance an experienced operator would want anyway.

  • [ ] Database backup before upgrading or uninstalling. The plugin owns one table; a backup makes the destructive uninstall path reversible.
  • [ ] SMTP wired up before flipping requireDoubleOptIn on. DOI is on by default; without working mail transport, visitors submit the form, get the "check your inbox" page, and never receive a confirmation email — they're stuck in confirmed=0 forever. If you can't configure SMTP yet, set requireDoubleOptIn=false until you can.
  • [ ] Privacy policy URL set in the plugin config. The form's fineprint links to it; an empty URL routes to a 404 and trains visitors to ignore privacy notices.
  • [ ] Test on staging before production. Especially the unsubscribe + DOI confirm flows — both rely on per-row tokens that depend on random_bytes() being available (always is, on any modern PHP, but verify).
  • [ ] Trusted proxies configured if you're behind a CDN / load balancer. The plugin rate-limits per client IP; without trusted_proxies set, every visitor shares the same proxy IP and the limit fires too aggressively.

Compatibility

Core platform

ShopwarePHPStatus
6.7.x — tested with Shopware 6.7.158.2 or newerStable
6.6.x—Not supported
6.5.x and earlier—Not supported

Database

EngineVersionNotes
MySQL8.0+Primary target; JSON functions used for config-row manipulation in migrations
MariaDB10.11+Tested end-to-end; earlier versions lack some JSON operator support

Browsers (storefront)

Evergreen browsers only — the two most recent stable releases of each:

BrowserDesktopMobile
Chrome / Chromium✅✅
Firefox✅✅
Safari✅ (macOS)✅ (iOS 16+)
Edge✅—

Internet Explorer and legacy Edge are not supported. The plugin emits no runtime JS (where applicable) so graceful degradation on older browsers usually still renders content, just without progressive enhancements.

Admin browsers

Same evergreen matrix — the Shopware admin is Vue-based and has its own compatibility baseline that this plugin doesn't extend or narrow.

Development

ToolVersionScope
PHP≥ 8.2Runtime + test suite
Composer2.xDependency management
Node.js≥ 18Only needed if you edit SCSS and re-run the theme compile
Python≥ 3.9Only needed for the repo's asset-pipeline scripts (scripts/*.py)

Accessibility

WCAG 2.2 level A + AA — see docs/ACCESSIBILITY.md for axe-core audit output and per-page violations.

What we test before each release

  • Full PHPUnit unit suite against PHP 8.2 (source-inspection tests don't need a kernel)
  • PHPStan level 8 + PHP-CS-Fixer (@PSR12 + @Symfony)
  • Composer validate on every plugin
  • Live-DB smoke tests (plugin install → activate → route render → uninstall cycle)
  • axe-core audit on the primary storefront surfaces (see ACCESSIBILITY.md)

Part of a Lite/Pro family

Newsletter Lite is the free tier of a Lite/Pro plugin family. It is a complete, standalone tool — capturing and managing a clean subscriber list is the whole job, and nothing here is gated or crippled to push an upsell.

Newsletter Pro is released and sold separately. It requires this plugin and installs side-by-side with it, building on the same subscriber table — tenbruggencate/newsletter-pro is not published on Packagist: it is delivered directly today, with a Shopware Store listing in preparation.

What Pro adds:

  • Campaign editor with subject, preheader and content: save, schedule or send now from Marketing → Newsletter Pro
  • Product blocks: new arrivals, a category or bestsellers pulled live from your catalogue into the email, per sales channel
  • Segmentation on your own order data: has ordered, last order older than X days, total spend above an amount
  • Live preview — "matches N subscribers" before every send; only confirmed, active addresses are mailed
  • Reliable sending through Shopware's own mail stack, in throttled background batches, with a status per recipient
  • Export everything (campaigns, segments, send logs and settings) as one JSON file, so you are never locked in

→ Newsletter Pro — features and pricing

If all you need today is a consent-clean subscriber list, Newsletter Lite on its own is the right tool.

Related plugins

Part of the TenBruggencate Suite — small, focused plugins that play well together:

  • MultiBrand — recommended: when MultiBrand is active, brand_name is populated automatically from the resolved brand (priority: MultiBrand resolver → SalesChannelContext → configured brandName → empty). Never read from the form, so tampering is impossible. Per-SC segmentation without separate sales channels.
  • Analytics — signup conversions tracked automatically when Analytics is active and consent is given.
  • LegalPages — provides the /legal/privacy page that privacyPageUrl typically points at.
  • Maintenance — branded maintenance page that cooperates with this plugin's privacy / consent posture.

More from Ten Bruggencate Development

Free Lite plugins for Shopware 6.7, each with a Pro edition for when you need more. Overview: tenbruggencatedevelopment.nl/en/initiatieven/shopware-plugins

  • Analytics — Matomo or Plausible, cookieless, with e-commerce events
  • Legal Pages — terms, privacy, shipping and returns pages from templates · Pro: cookie consent, accessibility statement
  • Maintenance — a branded, SEO-correct (HTTP 503) maintenance page
  • Multi-Brand — several brands on one Shopware, recognised by domain
  • Newsletter — GDPR-safe sign-up with double opt-in · Pro: campaigns and segments
  • Product Encyclopedia — educational content pages linked to your products
  • Seasons — scheduled theme variants (colours, typography, hero)
  • Social Login — passwordless login link, Google and Facebook
  • Stock Alert — "notify me when it's back" with double opt-in

Support

License

MIT © Ten Bruggencate Development