# 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`:
```ini
plugins = "newsletter-signup"
```

Then call from PHP content files:
```php
<?= 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

```php
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`)

```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
