Alexander 105b09c4c5 Fix kin-distance metric: shortest descent from direct lineage
Replaces the previous "depth in generations along the strict
lineal chain" definition (which excluded siblings, aunts, cousins
entirely) with the metric the user actually wants: the number of
descent-steps separating the target from the recipient's closest
direct ancestor or descendant.

Examples relative to the recipient:
- sibling:        1  (parent → sibling)
- great-aunt:     1  (great-grandparent → great-aunt)
- nephew:         2  (parent → sibling → nephew)
- first cousin:   2  (grandparent → aunt → cousin)
- second cousin:  3
- ego, parents, grandparents, ..., children, ..., great-greats: 0
- own spouse, step-parents, brothers-in-law: inherit partner's
  distance (so spouse-of-distance-1 is also distance 1)

Implementation:
- Anchor set seeded with R's direct ancestors + R + direct
  descendants (capped at 25 generations to bound runaway data).
- Multi-source BFS expanding by descent only.
- Spouse propagation at every level so a person and their
  spouse always share the same distance.
- Memoised per (recipient xref, max distance).

Tree preference key and range kept (NEWSLETTER_LINEAL_DEPTH,
0–10, default 3); only the semantics and the user-facing label
+ help text change, with concrete examples in both English and
German.
2026-05-15 13:10:38 +02:00

webtrees Email Newsletter

A webtrees 2.2+ custom module that sends recurring email newsletters with:

  • Upcoming birthdays of still-living individuals.
  • Upcoming marriage anniversaries of intact couples (optional — admin toggle, per tree). Marriages with a divorce or annulment fact are excluded automatically.
  • Once-per-month historical section: births and deaths of deceased individuals whose anniversary falls in the upcoming window.

The decision to actually send is made by comparing a stored "last sent" timestamp to the configured frequency, so the dispatch run is idempotent — calling the trigger more often than the frequency simply does nothing extra.

Requirements

  • webtrees ≥ 2.2.0
  • PHP ≥ 8.2
  • A working SMTP / sendmail configuration in webtrees → Control panel → Sending email (this module reuses webtrees' standard mailer).
  • An external scheduler on the host: system cron, a systemd timer, a Kubernetes CronJob, or anything else that can fire an HTTP request at a fixed interval. Newsletter dispatch never runs on visitor page loads — it only runs when the scheduler triggers it.

Installation

  1. Copy this directory into the webtrees modules_v4/ folder, renaming it to email_newsletter (the folder name determines the internal module identifier — the registered name will be _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:

    • Enable newsletter dispatch per tree.
    • Pick a frequency (default: 14 days).
    • Optionally toggle marriage anniversaries and add any extra external email addresses.
    • Copy the Cron URL at the bottom — this is the secret-token URL your scheduler must hit.

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 one-off send, append &force=1 to the cron URL — that bypasses the "is it due?" check.

Subscribers

Two sources, combined:

  1. Logged-in webtrees users who opt in via the per-tree Newsletter subscription menu entry (visible only to logged-in users on trees where the module is enabled). Only approved and email-verified accounts will receive the newsletter.
  2. External addresses the tree administrator lists in the Extra recipient email addresses textarea (one per line).

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%