Alexander 90ad060421 Per-user intro versioning + admin pending-delivery view
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).
2026-05-15 15:57:16 +02:00

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 use IntlDateFormatter for the recipient's locale.
  • Per-recipient relationship labels"your mother", "4th great- grandfather", "first cousin twice removed". Uses webtrees' own RelationshipService so 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 either ext-imagick or ext-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, systemd timer, Kubernetes CronJob, …) that can fire an HTTP request at a fixed interval. Newsletter dispatch never runs on visitor page loads.

Installation

  1. Copy this directory into the webtrees modules_v4/ folder, renaming it to email_newsletter:

    cp -r webtrees_email_newsletter /var/www/webtrees/modules_v4/email_newsletter
    
  2. In the webtrees control panel, go to Modules → All modules and enable Email Newsletter.

  3. 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.
  4. 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:

  1. 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.
  2. Webtrees users an admin subscribes from the preferences page.
  3. 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 SiteUserControl 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.

S
Description
An email newsletter plugin for upcoming birthdays and marriage anniversaries of living tree members as well as birth and death days for deceased.
Readme 257 KiB
Languages
PHP 50.8%
HTML 49.2%