Replaces "clear intro after first send", which dropped the message for any subscriber still queued on a slower cadence. - Each non-empty admin save bumps a per-locale version counter on the tree. The dispatcher includes the intro only for recipients whose last-seen version is behind, then advances their watermark after a successful send. Webtrees users get a per-user watermark; external addresses share one tree-level watermark. Re-saving the same text is a no-op. - The preferences page now shows delivery progress per locale: "Delivered to X of Y subscriber(s)" plus a collapsible Pending list with name + email of each subscriber who hasn't received the current intro yet, and a single "External recipients (N)" row when the external watermark is behind. - README rewritten to reflect every feature shipped since the initial commit (BockenTheme skin, embedded avatars, relationship labels, kin-distance filter, per-user cadence, bilingual subject prefix, locale-aware subject date, SiteUser as From, three subscriber sources, Markdown intro with personalisation tokens).
7.3 KiB
webtrees Email Newsletter
A webtrees 2.2+ custom module that sends recurring email newsletters with:
- Upcoming birthdays of still-living individuals (formatted as ordinal age — "45th birthday" / "45. Geburtstag").
- Upcoming marriage anniversaries of intact couples (admin toggle, per tree). Marriages with a divorce or annulment fact are excluded automatically.
- Historical events — births and deaths of deceased individuals whose anniversary falls in the upcoming window.
The look-ahead window and the send cadence are the same number: one "every N days" setting (default 14) drives both the cron interval and how far each issue looks ahead. Issues with nothing to report are silently skipped.
Each recipient gets a per-recipient render — language, relationship labels, detail filter, cadence, and personalisation tokens are all resolved against their webtrees account.
Highlights
- Editorial layout with embedded circular avatars, a left-side timeline rail, and event-type icons (birth / death / marriage).
- BockenTheme light-mode skin — Open Sans, cream background, Nord accent palette. The newsletter and the website read as one product.
- Per-recipient localisation — German for users whose webtrees
language starts with
de, English otherwise. Subject line, body, date strings, and (optionally) a custom subject prefix are all localised. Subject dates useIntlDateFormatterfor the recipient's locale. - Per-recipient relationship labels — "your mother", "4th great-
grandfather", "first cousin twice removed". Uses webtrees' own
RelationshipServiceso the labels match the site. - Kin-distance detail filter — close family get the full card (avatar + timeline + icon); distant kin appear as a single-line bullet at the foot of each section. The "distance" radius is an admin setting (default 3); spouses inherit their partner's distance; recipients with no linked tree record always see the full detailed view.
- Per-recipient cadence — each subscriber can pick weekly,
biweekly, monthly, every-two-months, quarterly, or "use site
default" on their
/my-account/{tree}page. - One-shot intro paragraph — admins can attach a Markdown intro
(bilingual, EN/DE) to the next issue. Supports
{{first_name}},{{last_name}},{{username}},{{email}}personalisation tokens. Rendered alongside the tree-contact user's avatar as an editorial column. Cleared automatically after a successful send. - Cron-only dispatch — the "is it due?" decision is made server- side against stored timestamps. Calling the trigger more often than the cadence is harmless and idempotent.
Requirements
- webtrees ≥ 2.2.0
- PHP ≥ 8.2 with
ext-intl(for locale-aware subject dates) and eitherext-imagickorext-gd(for avatar resizing — falls back to original-size embeds if neither is present). - A working SMTP / sendmail configuration in Control panel → Sending email. This module reuses webtrees' standard mailer and signs with the site's DKIM keys if configured.
- An external scheduler on the host (system
cron,systemdtimer, KubernetesCronJob, …) that can fire an HTTP request at a fixed interval. Newsletter dispatch never runs on visitor page loads.
Installation
-
Copy this directory into the webtrees
modules_v4/folder, renaming it toemail_newsletter:cp -r webtrees_email_newsletter /var/www/webtrees/modules_v4/email_newsletter -
In the webtrees control panel, go to Modules → All modules and enable Email Newsletter.
-
Open Control panel → Modules → Email Newsletter → Preferences and, for each tree:
- Tick Enable newsletter for this tree.
- Set the send-cadence (default 14 days). This same number is the look-ahead window for the next issue.
- Toggle Include marriage anniversaries if desired.
- Set Detailed view distance (default 3). Lower values produce a terser email focused tightly on close kin.
- Optional: set per-locale Subject prefix and a Generic
fallback (e.g.
[Bocken family]). - Optional: tick existing webtrees users in Subscribed users to
subscribe them. Users can still adjust their own subscription
and cadence on
/my-account/{tree}. - Optional: add external (non-user) addresses in Extra recipient email addresses.
-
Copy the Cron URL at the bottom and wire it into your scheduler (see below).
Setting up the scheduler
Why no built-in scheduler? PHP has no daemon, and frameworks like Laravel rely on a once-per-minute system cron to fire their internal scheduler. This module follows the same convention: the host OS owns the timer, the module owns the "is it actually due?" decision.
System cron
# Run every 15 minutes. The module itself decides whether sending is due.
*/15 * * * * curl -fsS --max-time 60 'https://example.com/module/_email_newsletter_/Cron?token=YOUR_TOKEN' > /dev/null
systemd timer
/etc/systemd/system/webtrees-newsletter.service:
[Unit]
Description=webtrees newsletter trigger
[Service]
Type=oneshot
ExecStart=/usr/bin/curl -fsS --max-time 60 "https://example.com/module/_email_newsletter_/Cron?token=YOUR_TOKEN"
/etc/systemd/system/webtrees-newsletter.timer:
[Unit]
Description=Trigger webtrees newsletter dispatch
[Timer]
OnCalendar=*-*-* *:00/15
Persistent=true
[Install]
WantedBy=timers.target
Then systemctl enable --now webtrees-newsletter.timer.
Forcing a one-off send
The admin Preferences page has a Send now button for testing.
For an unattended forced send (bypassing the per-recipient "is it
due?" check), append &force=1 to the cron URL.
Subscribers
Three sources, combined and de-duplicated by email:
- Webtrees users who opt in themselves via the per-tree
Newsletter subscription menu entry on
/my-account/{tree}— visible only to logged-in users on trees where the module is enabled. - Webtrees users an admin subscribes from the preferences page.
- External addresses the admin lists in Extra recipient email addresses.
Only approved and email-verified webtrees accounts will receive the newsletter. External addresses always receive on every run (they have no per-user cadence timer).
Sender identity
To match webtrees' own convention for system-generated email
(registration, password resets, "new version available"), the
From: header is SiteUser — Control panel → Sending email →
Sender name / Sender email (SMTP_FROM_NAME / SMTP_DISP_NAME).
The tree's contact user becomes the Reply-To:, so replies still
reach a human admin.
If SMTP_FROM_NAME isn't set the dispatcher falls back to the tree
contact for From: as well, so the message always has a valid
sender envelope.
Privacy
The dispatch service does not impersonate a webtrees user, so it sees the tree from the visitor access level. Records and facts that your tree settings hide from visitors will be omitted from the newsletter even if a recipient has higher in-app access. This is the safest default for an outbound email — if you need to expose more information, relax the tree's visitor-access settings or hand-curate the Extra recipient list.
License
AGPL-3.0-or-later. See LICENSE for the full text.