innhold/docs/newsletter-plugin.md
Ruben 36591e7438 Add per-form Listmonk list UUIDs to newsletter plugin
Add support for specifying Listmonk list UUIDs per form instance
Update petition form to use metadata-defined UUIDs
Add success confirmation message to newsletter forms
Update documentation with new functionality
2026-02-07 15:34:43 +01:00

3.8 KiB

Newsletter Plugin Reference

For LLM agents working on the newsletter signup. Read when modifying newsletter functionality.

Overview

Modular newsletter signup that can be embedded on any page. Uses Listmonk's public subscription API for double opt-in. Located in custom/plugins/page/newsletter-signup.php.

How It Activates

Add to a page's metadata.ini:

plugins = "newsletter-signup"

Then call from PHP content files:

<?= newsletter_signup('Your custom intro text here', 'hero') ?>
<?= newsletter_signup('Short text here', 'small') ?>
<?= newsletter_signup('Text', 'small', ['list-uuid-1', 'list-uuid-2']) ?>

Themes

Theme Description HTML Element
hero Full-width gradient background section (green->blue). Centered form with pill-shaped inputs. <section>
small Compact inline box with green border. Fits between paragraphs. <aside>

Function Signature

newsletter_signup(string $introText, string $theme = 'hero', array $listUuids = [], string $formId = 'newsletter'): string
  • $introText - Displayed above the form
  • $theme - 'hero' or 'small'
  • $listUuids - Listmonk list UUIDs for this form (empty = use global config)
  • $formId - HTML id for the section (for anchor links)

How It Works

  1. Form submits via JavaScript fetch() (AJAX) - this is the ONE place JS is used on the site
  2. Server validates: CSRF token, rate limit (30s), name/email
  3. Calls Listmonk public subscription API
  4. Returns JSON response
  5. Button shows success/error state with animation

Listmonk Integration

Config: custom/listmonk-config.php (not in repo, see listmonk-config.example.php)

return [
    'enabled' => true,
    'url' => 'https://your-listmonk-instance.com',
    'list_uuids' => ['uuid-1', 'uuid-2'],
];

Uses Listmonk's public subscription API (/api/public/subscription). No authentication needed. Listmonk handles its own double opt-in flow.

Per-form list UUIDs can be passed via $listUuids parameter. When empty, falls back to the global config's list_uuids.

Anti-Spam

  1. CSRF token - Separate from petition (newsletter_csrf_token)
  2. Rate limit - 30 seconds between submissions (session-based)
  3. Input validation - Name 2-100 chars, valid email, max 100 chars

Translation Keys

All use newsletter.* prefix in language files:

  • newsletter.name_label, newsletter.name_placeholder
  • newsletter.email_label, newsletter.email_placeholder
  • newsletter.notice (e.g., "We send about 12 emails per year")
  • newsletter.submit_button
  • newsletter.success_message, newsletter.success_confirm, newsletter.error_message

Static Resources

CSS and JS are inlined in the output via static helper functions:

  • newsletterGetStyles() - Included once per page (static flag)
  • newsletterGetScript() - Included once per page (static flag)

This means multiple newsletter_signup() calls on one page share a single copy of CSS/JS.

Critical: Do Not Break

  1. AJAX submission - The form uses fetch() with JSON response. The newsletterHandleSubmission() function runs early (before any output) and calls exit after sending JSON.
  2. CSRF separate from petition - Uses newsletter_csrf_token session key, not the petition's csrf_token.
  3. Static inclusion guards - $stylesIncluded and $scriptIncluded static vars prevent duplicate CSS/JS. Do not reset these.
  4. Button states - CSS classes is-loading, is-success, is-error control button feedback. The JS depends on these class names and data-* attributes.
  5. escape class on hero - The hero section uses class escape to break out of the content container width. This is defined in base.css.

Currently Used On

  • Homepage (content/index.php) - hero theme with custom intro text