linderp / sulu-mailing-list-bundle
Mailing List Bundle for Sulu CMS
Installs: 37
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
Type:symfony-bundle
pkg:composer/linderp/sulu-mailing-list-bundle
Requires
- php: ^8.2
- jackalope/jackalope-doctrine-dbal: ^2.0
- linderp/sulu-base-bundle: ^0.1 || dev-main
- linderp/sulu-form-save-contact-bundle: ^0.1
- sulu/sulu: ^2.6
- symfony/config: ^6.2 | ^7.0
- symfony/dependency-injection: ^6.2 | ^7.0
- symfony/framework-bundle: ^6.2 | ^7.0
- symfony/http-foundation: ^6.2 | ^7.0
- symfony/http-kernel: ^6.2 | ^7.0
- symfony/intl: ^6.2 | ^7.0
- symfony/security-core: ^6.3 | ^7.0
- symfony/translation: ^6.2 | ^7.0
Requires (Dev)
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^9.6
- symfony/phpunit-bridge: ^7.2
README
Sulu bundle to manage mailing lists, subscribe contacts (including via Sulu Forms), and send MJML-based newsletter mails from the admin UI.
Features
- Newsletter management with translatable content and double-opt-in templates.
- Newsletter subscriptions with confirm/unsubscribe flow and locale-aware subscriptions.
- Admin UI resources and lists for newsletters, newsletter mails, and subscriptions.
- Newsletter mails can target newsletter subscribers and optionally filter by selected contacts.
- MJML + Twig email templates with configurable wrappers/components and optional caching.
- Optional form integration via
linderp/sulu-form-save-contact-bundleto auto-subscribe contacts.
Requirements
- PHP 8.2+
- Sulu 2.6+
linderp/sulu-form-save-contact-bundlelinderp/sulu-base-bundle
Installation
- Install the bundle:
composer require linderp/sulu-mailing-list-bundle
- If you are not using Symfony Flex, add the bundle to
config/bundles.php:
return [ // ... Linderp\SuluMailingListBundle\SuluMailingListBundle::class => ['all' => true], ];
- Register routes:
config/routes_admin.yaml
SuluMailingListBundle: resource: "@SuluMailingListBundle/Resources/config/routes_admin.yml"
config/routes_website.yaml
SuluMailingListBundle: resource: "@SuluMailingListBundle/Resources/config/routes.yml"
- Configure MJML and sender settings in
config/packages/sulu_mailing_list.yaml:
sulu_mailing_list: mjml: app_id: <api-id> secret_key: <secret-key> caching: true socials: facebook: facebookNoShare: twitter: twitterNoShare: x: xNoShare: google: googleNoShare: pinterest: pinterestNoShare: linkedin: linkedinNoShare: tumblr: tumblrNoShare: xing: xingNoShare: github: instagram: web: snapchat: youtube: vimeo: medium: soundcloud: dribbble: no_reply_email: <no-reply-mail>
- Add the admin UI assets dependency (admin app):
assets/admin/package.json
"dependencies": { "sulu-mailing-list-bundle": "file:../../vendor/linderp/sulu-mailing-list-bundle/Resources/js" }
- Import the admin UI bundle:
assets/admin/app.js
import "sulu-mailing-list-bundle";
- Build admin assets:
cd assets/admin
npm run build
Admin UI
The bundle registers admin resources and lists for:
newslettersnewsletters_mailsnewsletters_subscriptionsfiltered_contacts(used by the newsletter mail contact selector)
Toolbar actions are provided for:
- Subscribe / Unsubscribe on newsletter subscription lists.
- Send on newsletter mail detail view (disabled if already sent or translations are missing).
Website routes (unsubscribe / confirm)
The website routes are handled by NewsletterWebsiteController:
/{locale}/newsletter/unsubscribe/{newsletterId}/{token}/{locale}/newsletter/confirm/{newsletterId}/{token}
Both routes redirect to the configured unsubscribe/confirmation pages on the newsletter entity.
Sending newsletters
- Mails are sent only once (
sent = trueafter send). - Only confirmed and not-unsubscribed subscriptions are included.
- If contacts are selected, subscribers are filtered to those contacts.
- Duplicates are removed per contact.
Sulu Forms integration
The bundle provides two dynamic field types:
newsletter(checkbox)hidden_newsletter
When a form submission is saved via linderp/sulu-form-save-contact-bundle, selected newsletters are attached to the saved contact and handled by the subscription service.
Email templates
MJML + Twig templates live in Resources/views/mails/:
base_email.mjml.twig(base layout)wrappers/for layout sectionscomponents/for individual blocks
Templates are rendered through the MJML API and can be cached via sulu_mailing_list.mjml.caching.
Extending mail editor resources
The mail editor is built from four building blocks: resources, wrappers, contexts, and fields.
When you add one, you must wire it in the correct places. The exact locations differ per type.
Where things live:
- Resources: form XML in
Resources/config/forms/*.xml+ type class inMail/Resource/Types/ - Wrappers: XML in
Resources/config/mail/wrappers/*.xml+ MJML inResources/views/mails/wrappers/+ type class inMail/Wrapper/Types/ - Contexts: XML in
Resources/config/mail/contexts/*.xml+ type class inMail/Context/Types/(no MJML template) - Fields: XML in
Resources/config/mail/types/*.xml+ MJML inResources/views/mails/components/<type>/<type>.mjml.twig+ type class inMail/Field/Types/
Notes:
- Keep XML names/keys aligned with the PHP type name and Twig template name.
- Update translations in
Resources/translations/if you add new labels. - PHP types are auto-registered via
#[AutoconfigureTag(...)]on the corresponding interface (no manual pool registration needed).
Example: add a new field type quote
- XML:
Resources/config/mail/types/quote.xml
<?xml version="1.0" ?> <properties xmlns="http://schemas.sulu.io/template/template" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://schemas.sulu.io/template/template http://schemas.sulu.io/template/properties-1.0.xsd"> <property name="text" type="text_line"> <meta> <title>mailingListMail.props.quote.text</title> </meta> </property> </properties>
- MJML:
Resources/views/mails/components/quote/quote.mjml.twig
<mj-text font-style="italic">{{ item.text }}</mj-text>
- PHP type:
Mail/Field/Types/QuoteMailFieldType.php
final class QuoteMailFieldType implements MailFieldTypeInterface { public function getConfiguration(): MailFieldTypeConfiguration { return new MailFieldTypeConfiguration( 'quote', 'mailingListMail.types.quote.label', __DIR__ . '/../../../Resources/config/mail/types/quote.xml', __DIR__ . '/../../../Resources/views/mails/components/quote/quote.mjml.twig' ); } }
Admin icon PNG generation
The bundle includes a helper script to generate PNGs from Font Awesome solid SVGs, with optional color variants.
Script location:
vendor/linderp/sulu-mailing-list-bundle/scripts/generate-fontawesome-pngs.js
Default output folder:
icons/ (in the project root, when run from the project root)
Default Font Awesome SVG folder:
assets/admin/node_modules/@fortawesome/fontawesome-free/svgs/solid
Additional SVG folder included:
vendor/linderp/sulu-mailing-list-bundle/Resources/social (e.g. social brand icons)
Examples (run from the project root):
# Use COLOR_PICKER_COLORS from .env node vendor/linderp/sulu-mailing-list-bundle/scripts/generate-fontawesome-pngs.js # Custom output folder node vendor/linderp/sulu-mailing-list-bundle/scripts/generate-fontawesome-pngs.js --out /path/to/output # Custom Font Awesome SVG folder node vendor/linderp/sulu-mailing-list-bundle/scripts/generate-fontawesome-pngs.js --svg-dir /path/to/solid-svgs # Custom colors (defaults to output in ./icons) node vendor/linderp/sulu-mailing-list-bundle/scripts/generate-fontawesome-pngs.js "#F7F7F7" "#A6A6A6" # Custom output + custom colors node vendor/linderp/sulu-mailing-list-bundle/scripts/generate-fontawesome-pngs.js --out /path/to/output "#F7F7F7" "#A6A6A6"
Notes:
- The script reads solid SVGs from
assets/admin/node_modules/@fortawesome/fontawesome-free/svgs/solidby default, but you can override with--svg-dir. - It also includes SVGs from
Resources/social/(e.g.facebook.svg,facebook-noshare.svg). - It skips PNGs that already exist, so you can rerun it to resume.
- PNGs are named
{svg-name}.pngand{svg-name}-{color}.png.
Additional Setup
Fonts
To add a font for the admin mail editor, create a class implementing MailFontInterface:
class MomoMailFont implements MailFontInterface { public function getConfiguration(): MailFontConfiguration { return new MailFontConfiguration( '<url to momo.css>', // css file in /public 'Momo Trust Sans', 'Momo Trust Sans, sans-serif', true ); } }
Either provide the CSS yourself or use a service like Google Fonts. Example CSS:
@font-face { font-family: 'Momo Trust Sans'; font-style: normal; font-weight: 400; src: url(momo-latin.woff2) format('woff2'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }