system, and templates Add content system documentation Add newsletter plugin documentation Add petition system documentation Add templates documentation
3.4 KiB
3.4 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') ?>
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', string $formId = 'newsletter'): string
$introText- Displayed above the form$theme-'hero'or'small'$formId- HTML id for the section (for anchor links)
How It Works
- Form submits via JavaScript
fetch()(AJAX) - this is the ONE place JS is used on the site - Server validates: CSRF token, rate limit (30s), name/email
- Calls Listmonk public subscription API
- Returns JSON response
- 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.
Anti-Spam
- CSRF token - Separate from petition (
newsletter_csrf_token) - Rate limit - 30 seconds between submissions (session-based)
- 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_placeholdernewsletter.email_label,newsletter.email_placeholdernewsletter.notice(e.g., "We send about 12 emails per year")newsletter.submit_buttonnewsletter.success_message,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
- AJAX submission - The form uses
fetch()with JSON response. ThenewsletterHandleSubmission()function runs early (before any output) and callsexitafter sending JSON. - CSRF separate from petition - Uses
newsletter_csrf_tokensession key, not the petition'scsrf_token. - Static inclusion guards -
$stylesIncludedand$scriptIncludedstatic vars prevent duplicate CSS/JS. Do not reset these. - Button states - CSS classes
is-loading,is-success,is-errorcontrol button feedback. The JS depends on these class names anddata-*attributes. escapeclass on hero - The hero section uses classescapeto break out of the content container width. This is defined inbase.css.
Currently Used On
- Homepage (
content/index.php) - hero theme with custom intro text