Limit detailed view to lineal kin; rest as summary bullets
Per-recipient: only direct ancestors and direct descendants
within a configurable number of generations (default 3) get the
full row treatment (avatar, icon, timeline). Everyone else falls
through to a compact text-only bullet list at the bottom of the
same section.
- New tree preference NEWSLETTER_LINEAL_DEPTH (range 0–10,
default 3) with a clearly-explained admin input.
- RelationshipPathFinder::linealKin() does two cheap recursive
expansions (ancestors and descendants only — no spouse or
sibling traversal) and returns the xref set. Memoised per
recipient within a dispatch run.
- Avatar attachments are filtered per recipient to only the
embeds actually referenced in their HTML, so summary-only rows
no longer inflate per-email size with unused images.
- Recipients with no PREF_TREE_ACCOUNT_XREF (external admin
addresses, users not linked to a record) see the entire
newsletter in detail — no lineal anchor to filter against.
- German translations for the three new section kickers ("Other
birthdays", etc.) and the admin input help text.
This commit is contained in:
@@ -173,6 +173,15 @@ final class NewsletterDispatchService
|
||||
// record the recipient is linked to in this tree.
|
||||
foreach ($group as $recipient) {
|
||||
$relationships = $this->relationshipMap($tree, $recipient, $featured);
|
||||
$detailed_set = $this->detailedXrefs($tree, $recipient, $featured);
|
||||
|
||||
// Trim the embedded image set to only the avatars
|
||||
// we'll actually reference (detailed rows). Summary
|
||||
// bullets render without pictures.
|
||||
$recipient_avatars = array_intersect_key(
|
||||
$avatars,
|
||||
$this->avatarKeysForXrefs(array_keys($detailed_set)),
|
||||
);
|
||||
|
||||
$html = view($module->name() . '::email', [
|
||||
'tree' => $tree,
|
||||
@@ -186,13 +195,14 @@ final class NewsletterDispatchService
|
||||
'generated_at' => $now,
|
||||
'avatar_cids' => $avatar_cids,
|
||||
'relationships' => $relationships,
|
||||
'detailed_xrefs' => $detailed_set,
|
||||
'account_url' => $account_url,
|
||||
]);
|
||||
|
||||
$text = $this->htmlToText($html);
|
||||
|
||||
try {
|
||||
if ($this->mailer->sendWithEmbeds($from, $recipient, $from, $subject, $text, $html, $avatars)) {
|
||||
if ($this->mailer->sendWithEmbeds($from, $recipient, $from, $subject, $text, $html, $recipient_avatars)) {
|
||||
$sent++;
|
||||
} else {
|
||||
$failures++;
|
||||
@@ -269,6 +279,69 @@ final class NewsletterDispatchService
|
||||
return $individuals;
|
||||
}
|
||||
|
||||
/**
|
||||
* Which featured xrefs deserve the full detailed row (avatar +
|
||||
* timeline) for this recipient?
|
||||
*
|
||||
* If the recipient is unmapped to the tree (external address or a
|
||||
* user with no PREF_TREE_ACCOUNT_XREF), every featured xref counts
|
||||
* as "detailed" — they have no lineal context to filter against.
|
||||
*
|
||||
* Otherwise, only the recipient's direct ancestors and descendants
|
||||
* within Configuration::linealDepth() generations qualify. For
|
||||
* Family records (anniversaries), either spouse being lineal
|
||||
* promotes the row.
|
||||
*
|
||||
* @param array<string,Individual> $featured
|
||||
*
|
||||
* @return array<string,true> Set of featured xrefs to render in detail.
|
||||
*/
|
||||
private function detailedXrefs(Tree $tree, UserInterface $recipient, array $featured): array
|
||||
{
|
||||
$self_xref = $tree->getUserPreference($recipient, UserInterface::PREF_TREE_ACCOUNT_XREF);
|
||||
|
||||
if ($self_xref === '') {
|
||||
// Unmapped recipient — no lineal anchor, show everything.
|
||||
return array_fill_keys(array_keys($featured), true);
|
||||
}
|
||||
|
||||
$self = Registry::individualFactory()->make($self_xref, $tree);
|
||||
|
||||
if (!$self instanceof Individual) {
|
||||
return array_fill_keys(array_keys($featured), true);
|
||||
}
|
||||
|
||||
$depth = Configuration::linealDepth($tree);
|
||||
$lineal_set = $this->relationship_finder->linealKin($self, $depth);
|
||||
|
||||
$detailed = [];
|
||||
|
||||
foreach ($featured as $xref => $_individual) {
|
||||
if (isset($lineal_set[$xref])) {
|
||||
$detailed[$xref] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return $detailed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates a list of xrefs into the cid-name keys used by the
|
||||
* avatar embed map ("avatar-<xref>" => true). Lets us
|
||||
* array_intersect_key the embeds map cheaply.
|
||||
*
|
||||
* @param array<int,string> $xrefs
|
||||
* @return array<string,true>
|
||||
*/
|
||||
private function avatarKeysForXrefs(array $xrefs): array
|
||||
{
|
||||
$keys = [];
|
||||
foreach ($xrefs as $xref) {
|
||||
$keys['avatar-' . $xref] = true;
|
||||
}
|
||||
return $keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a "xref => relationship label" map for one recipient.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user