xref: /webtrees/app/Module/IndividualFactsTabModule.php (revision e4cf93e367ec5647c295019975cd39de03da8c74)
13763c3f2SGreg Roach<?php
23976b470SGreg Roach
33763c3f2SGreg Roach/**
43763c3f2SGreg Roach * webtrees: online genealogy
57fe676e5SGreg Roach * Copyright (C) 2020 webtrees development team
63763c3f2SGreg Roach * This program is free software: you can redistribute it and/or modify
73763c3f2SGreg Roach * it under the terms of the GNU General Public License as published by
83763c3f2SGreg Roach * the Free Software Foundation, either version 3 of the License, or
93763c3f2SGreg Roach * (at your option) any later version.
103763c3f2SGreg Roach * This program is distributed in the hope that it will be useful,
113763c3f2SGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of
123763c3f2SGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
133763c3f2SGreg Roach * GNU General Public License for more details.
143763c3f2SGreg Roach * You should have received a copy of the GNU General Public License
153763c3f2SGreg Roach * along with this program. If not, see <http://www.gnu.org/licenses/>.
163763c3f2SGreg Roach */
17fcfa147eSGreg Roach
18e7f56f2aSGreg Roachdeclare(strict_types=1);
19e7f56f2aSGreg Roach
2076692c8bSGreg Roachnamespace Fisharebest\Webtrees\Module;
2176692c8bSGreg Roach
2263276d8fSGreg Roachuse Fisharebest\Webtrees\Auth;
230e62c4b8SGreg Roachuse Fisharebest\Webtrees\Date;
240e62c4b8SGreg Roachuse Fisharebest\Webtrees\Fact;
250e62c4b8SGreg Roachuse Fisharebest\Webtrees\Family;
260e62c4b8SGreg Roachuse Fisharebest\Webtrees\I18N;
270e62c4b8SGreg Roachuse Fisharebest\Webtrees\Individual;
282adcbd9aSGreg Roachuse Fisharebest\Webtrees\Services\ClipboardService;
294ca7e03cSGreg Roachuse Fisharebest\Webtrees\Services\ModuleService;
3017c50b57SGreg Roachuse Illuminate\Support\Collection;
313763c3f2SGreg Roach
32dec352c1SGreg Roachuse function str_contains;
33dec352c1SGreg Roach
343763c3f2SGreg Roach/**
353763c3f2SGreg Roach * Class IndividualFactsTabModule
363763c3f2SGreg Roach */
3737eb8894SGreg Roachclass IndividualFactsTabModule extends AbstractModule implements ModuleTabInterface
38c1010edaSGreg Roach{
3949a243cbSGreg Roach    use ModuleTabTrait;
4049a243cbSGreg Roach
412adcbd9aSGreg Roach    /** @var ModuleService */
424ca7e03cSGreg Roach    private $module_service;
434ca7e03cSGreg Roach
442adcbd9aSGreg Roach    /** @var ClipboardService */
452adcbd9aSGreg Roach    private $clipboard_service;
462adcbd9aSGreg Roach
474ca7e03cSGreg Roach    /**
480874af26SRichard Cissée     * IndividualFactsTabModule constructor.
494ca7e03cSGreg Roach     *
504ca7e03cSGreg Roach     * @param ModuleService    $module_service
512adcbd9aSGreg Roach     * @param ClipboardService $clipboard_service
524ca7e03cSGreg Roach     */
532adcbd9aSGreg Roach    public function __construct(ModuleService $module_service, ClipboardService $clipboard_service)
545bdbe281SGreg Roach    {
554ca7e03cSGreg Roach        $this->module_service    = $module_service;
562adcbd9aSGreg Roach        $this->clipboard_service = $clipboard_service;
574ca7e03cSGreg Roach    }
584ca7e03cSGreg Roach
594ca7e03cSGreg Roach    /**
600cfd6963SGreg Roach     * How should this module be identified in the control panel, etc.?
61961ec755SGreg Roach     *
62961ec755SGreg Roach     * @return string
63961ec755SGreg Roach     */
6449a243cbSGreg Roach    public function title(): string
65c1010edaSGreg Roach    {
66bbb76c12SGreg Roach        /* I18N: Name of a module/tab on the individual page. */
67bbb76c12SGreg Roach        return I18N::translate('Facts and events');
683763c3f2SGreg Roach    }
693763c3f2SGreg Roach
70961ec755SGreg Roach    /**
71961ec755SGreg Roach     * A sentence describing what this module does.
72961ec755SGreg Roach     *
73961ec755SGreg Roach     * @return string
74961ec755SGreg Roach     */
7549a243cbSGreg Roach    public function description(): string
76c1010edaSGreg Roach    {
77bbb76c12SGreg Roach        /* I18N: Description of the “Facts and events” module */
78bbb76c12SGreg Roach        return I18N::translate('A tab showing the facts and events of an individual.');
793763c3f2SGreg Roach    }
803763c3f2SGreg Roach
8149a243cbSGreg Roach    /**
8249a243cbSGreg Roach     * The default position for this tab.  It can be changed in the control panel.
8349a243cbSGreg Roach     *
8449a243cbSGreg Roach     * @return int
8549a243cbSGreg Roach     */
86cbf4b7faSGreg Roach    public function defaultTabOrder(): int
87cbf4b7faSGreg Roach    {
88fb7a0427SGreg Roach        return 1;
893763c3f2SGreg Roach    }
903763c3f2SGreg Roach
913caaa4d2SGreg Roach    /**
923caaa4d2SGreg Roach     * A greyed out tab has no actual content, but may perhaps have
933caaa4d2SGreg Roach     * options to create content.
943caaa4d2SGreg Roach     *
953caaa4d2SGreg Roach     * @param Individual $individual
963caaa4d2SGreg Roach     *
973caaa4d2SGreg Roach     * @return bool
983caaa4d2SGreg Roach     */
998f53f488SRico Sonntag    public function isGrayedOut(Individual $individual): bool
100c1010edaSGreg Roach    {
1013763c3f2SGreg Roach        return false;
1023763c3f2SGreg Roach    }
1033763c3f2SGreg Roach
1043caaa4d2SGreg Roach    /**
1053caaa4d2SGreg Roach     * Generate the HTML content of this tab.
1063caaa4d2SGreg Roach     *
1073caaa4d2SGreg Roach     * @param Individual $individual
1083caaa4d2SGreg Roach     *
1093caaa4d2SGreg Roach     * @return string
1103caaa4d2SGreg Roach     */
1119b34404bSGreg Roach    public function getTabContent(Individual $individual): string
112c1010edaSGreg Roach    {
113ee727175SGreg Roach        // Only include events of close relatives that are between birth and death
114ee727175SGreg Roach        $min_date = $individual->getEstimatedBirthDate();
115ee727175SGreg Roach        $max_date = $individual->getEstimatedDeathDate();
116ee727175SGreg Roach
1178eaf8709SGreg Roach        // Which facts and events are handled by other modules?
1188eaf8709SGreg Roach        $sidebar_facts = $this->module_service
11987cca37cSGreg Roach            ->findByComponent(ModuleSidebarInterface::class, $individual->tree(), Auth::user())
1200b5fd0a6SGreg Roach            ->map(static function (ModuleSidebarInterface $sidebar): Collection {
1218eaf8709SGreg Roach                return $sidebar->supportedFacts();
1228eaf8709SGreg Roach            });
1238eaf8709SGreg Roach
1248eaf8709SGreg Roach        $tab_facts = $this->module_service
12587cca37cSGreg Roach            ->findByComponent(ModuleTabInterface::class, $individual->tree(), Auth::user())
1260b5fd0a6SGreg Roach            ->map(static function (ModuleTabInterface $sidebar): Collection {
1278eaf8709SGreg Roach                return $sidebar->supportedFacts();
1288eaf8709SGreg Roach            });
1298eaf8709SGreg Roach
1308eaf8709SGreg Roach        $exclude_facts = $sidebar_facts->merge($tab_facts)->flatten();
1318eaf8709SGreg Roach
1323763c3f2SGreg Roach        // The individual’s own facts
13339ca88baSGreg Roach        $indifacts = $individual->facts()
1340b5fd0a6SGreg Roach            ->filter(static function (Fact $fact) use ($exclude_facts): bool {
1357fe676e5SGreg Roach                return !$exclude_facts->contains($fact->getTag());
13639ca88baSGreg Roach            });
1373763c3f2SGreg Roach
1383763c3f2SGreg Roach        // Add spouse-family facts
13939ca88baSGreg Roach        foreach ($individual->spouseFamilies() as $family) {
14030158ae7SGreg Roach            foreach ($family->facts() as $fact) {
1417fe676e5SGreg Roach                if (!$exclude_facts->contains($fact->getTag()) && $fact->getTag() !== 'CHAN') {
14239ca88baSGreg Roach                    $indifacts->push($fact);
1433763c3f2SGreg Roach                }
1443763c3f2SGreg Roach            }
145ee727175SGreg Roach
14639ca88baSGreg Roach            $spouse = $family->spouse($individual);
147ee727175SGreg Roach
148ee727175SGreg Roach            if ($spouse instanceof Individual) {
1498b9cfadbSGreg Roach                $spouse_facts = $this->spouseFacts($individual, $spouse, $min_date, $max_date);
15039ca88baSGreg Roach                $indifacts    = $indifacts->merge($spouse_facts);
1513763c3f2SGreg Roach            }
1523763c3f2SGreg Roach
1538b9cfadbSGreg Roach            $child_facts = $this->childFacts($individual, $family, '_CHIL', '', $min_date, $max_date);
15439ca88baSGreg Roach            $indifacts   = $indifacts->merge($child_facts);
1553763c3f2SGreg Roach        }
156225e381fSGreg Roach
1578b9cfadbSGreg Roach        $parent_facts     = $this->parentFacts($individual, 1, $min_date, $max_date);
1588b9cfadbSGreg Roach        $associate_facts  = $this->associateFacts($individual);
1598b9cfadbSGreg Roach        $historical_facts = $this->historicalFacts($individual);
160225e381fSGreg Roach
16139ca88baSGreg Roach        $indifacts = $indifacts
16239ca88baSGreg Roach            ->merge($parent_facts)
16339ca88baSGreg Roach            ->merge($associate_facts)
16439ca88baSGreg Roach            ->merge($historical_facts);
1653763c3f2SGreg Roach
166580a4d11SGreg Roach        $indifacts = Fact::sortFacts($indifacts);
1673763c3f2SGreg Roach
168a8cd57e1SGreg Roach        return view('modules/personal_facts/tab', [
169225e381fSGreg Roach            'can_edit'             => $individual->canEdit(),
170cd233803SGreg Roach            'clipboard_facts'      => $this->clipboard_service->pastableFacts($individual, new Collection()),
171ddeb3354SGreg Roach            'has_historical_facts' => $historical_facts !== [],
172225e381fSGreg Roach            'individual'           => $individual,
173225e381fSGreg Roach            'facts'                => $indifacts,
174225e381fSGreg Roach        ]);
1753763c3f2SGreg Roach    }
1763763c3f2SGreg Roach
177ee727175SGreg Roach    /**
178ee727175SGreg Roach     * Does a relative event occur within a date range (i.e. the individual's lifetime)?
179ee727175SGreg Roach     *
180ee727175SGreg Roach     * @param Fact $fact
181ee727175SGreg Roach     * @param Date $min_date
182ee727175SGreg Roach     * @param Date $max_date
183ee727175SGreg Roach     *
184ee727175SGreg Roach     * @return bool
185ee727175SGreg Roach     */
1868b9cfadbSGreg Roach    private function includeFact(Fact $fact, Date $min_date, Date $max_date): bool
187b3b1d905SGreg Roach    {
1882decada7SGreg Roach        $fact_date = $fact->date();
189ee727175SGreg Roach
190ee727175SGreg Roach        return $fact_date->isOK() && Date::compare($min_date, $fact_date) <= 0 && Date::compare($fact_date, $max_date) <= 0;
191ee727175SGreg Roach    }
192ee727175SGreg Roach
1933caaa4d2SGreg Roach    /**
1943caaa4d2SGreg Roach     * Is this tab empty? If so, we don't always need to display it.
1953caaa4d2SGreg Roach     *
1963caaa4d2SGreg Roach     * @param Individual $individual
1973caaa4d2SGreg Roach     *
1983caaa4d2SGreg Roach     * @return bool
1993caaa4d2SGreg Roach     */
2008f53f488SRico Sonntag    public function hasTabContent(Individual $individual): bool
201c1010edaSGreg Roach    {
2023763c3f2SGreg Roach        return true;
2033763c3f2SGreg Roach    }
2043763c3f2SGreg Roach
2053caaa4d2SGreg Roach    /**
2063caaa4d2SGreg Roach     * Can this tab load asynchronously?
2073caaa4d2SGreg Roach     *
2083caaa4d2SGreg Roach     * @return bool
2093caaa4d2SGreg Roach     */
2108f53f488SRico Sonntag    public function canLoadAjax(): bool
211c1010edaSGreg Roach    {
21215d603e7SGreg Roach        return false;
2133763c3f2SGreg Roach    }
2143763c3f2SGreg Roach
2153763c3f2SGreg Roach    /**
2167bf6ca81SGreg Roach     * Convert an event into a special "event of a close relative".
2177bf6ca81SGreg Roach     *
2187bf6ca81SGreg Roach     * @param Fact   $fact
2199e65d053SGreg Roach     * @param string $type
2207bf6ca81SGreg Roach     *
2217bf6ca81SGreg Roach     * @return Fact
2227bf6ca81SGreg Roach     */
2239e65d053SGreg Roach    private function convertEvent(Fact $fact, string $type): Fact
22499ed8541SGreg Roach    {
2257bf6ca81SGreg Roach        $gedcom = $fact->gedcom();
2267bf6ca81SGreg Roach        $gedcom = preg_replace('/\n2 TYPE .*/', '', $gedcom);
2277bf6ca81SGreg Roach        $gedcom = preg_replace('/^1 .*/', "1 EVEN CLOSE_RELATIVE\n2 TYPE " . $type, $gedcom);
2287bf6ca81SGreg Roach
2297bf6ca81SGreg Roach        return new Fact($gedcom, $fact->record(), $fact->id());
2307bf6ca81SGreg Roach    }
2317bf6ca81SGreg Roach
2327bf6ca81SGreg Roach    /**
2333763c3f2SGreg Roach     * Spouse facts that are shown on an individual’s page.
2343763c3f2SGreg Roach     *
2353763c3f2SGreg Roach     * @param Individual $individual Show events that occured during the lifetime of this individual
2363763c3f2SGreg Roach     * @param Individual $spouse     Show events of this individual
237ee727175SGreg Roach     * @param Date       $min_date
238ee727175SGreg Roach     * @param Date       $max_date
2393763c3f2SGreg Roach     *
2403763c3f2SGreg Roach     * @return Fact[]
2413763c3f2SGreg Roach     */
2428b9cfadbSGreg Roach    private function spouseFacts(Individual $individual, Individual $spouse, Date $min_date, Date $max_date): array
243c1010edaSGreg Roach    {
244f4afa648SGreg Roach        $SHOW_RELATIVES_EVENTS = $individual->tree()->getPreference('SHOW_RELATIVES_EVENTS');
2453763c3f2SGreg Roach
2467bf6ca81SGreg Roach        $death_of_a_spouse = [
2477bf6ca81SGreg Roach            'DEAT' => [
2487bf6ca81SGreg Roach                'M' => I18N::translate('Death of a husband'),
2497bf6ca81SGreg Roach                'F' => I18N::translate('Death of a wife'),
2507bf6ca81SGreg Roach                'U' => I18N::translate('Death of a spouse'),
2517bf6ca81SGreg Roach            ],
2527bf6ca81SGreg Roach            'BURI' => [
2537bf6ca81SGreg Roach                'M' => I18N::translate('Burial of a husband'),
2547bf6ca81SGreg Roach                'F' => I18N::translate('Burial of a wife'),
2557bf6ca81SGreg Roach                'U' => I18N::translate('Burial of a spouse'),
2567bf6ca81SGreg Roach            ],
2577bf6ca81SGreg Roach            'CREM' => [
2587bf6ca81SGreg Roach                'M' => I18N::translate('Cremation of a husband'),
2597bf6ca81SGreg Roach                'F' => I18N::translate('Cremation of a wife'),
2607bf6ca81SGreg Roach                'U' => I18N::translate('Cremation of a spouse'),
2617bf6ca81SGreg Roach            ],
2627bf6ca81SGreg Roach        ];
2637bf6ca81SGreg Roach
26413abd6f3SGreg Roach        $facts = [];
2657bf6ca81SGreg Roach
266dec352c1SGreg Roach        if (str_contains($SHOW_RELATIVES_EVENTS, '_DEAT_SPOU')) {
2677bf6ca81SGreg Roach            foreach ($spouse->facts(['DEAT', 'BURI', 'CREM']) as $fact) {
2688b9cfadbSGreg Roach                if ($this->includeFact($fact, $min_date, $max_date)) {
2697fe676e5SGreg Roach                    $facts[] = $this->convertEvent($fact, $death_of_a_spouse[$fact->getTag()][$fact->record()->sex()]);
2703763c3f2SGreg Roach                }
2713763c3f2SGreg Roach            }
2723763c3f2SGreg Roach        }
2733763c3f2SGreg Roach
2743763c3f2SGreg Roach        return $facts;
2753763c3f2SGreg Roach    }
2763763c3f2SGreg Roach
2773763c3f2SGreg Roach    /**
2783763c3f2SGreg Roach     * Get the events of children and grandchildren.
2793763c3f2SGreg Roach     *
2803763c3f2SGreg Roach     * @param Individual $person
2813763c3f2SGreg Roach     * @param Family     $family
2823763c3f2SGreg Roach     * @param string     $option
2833763c3f2SGreg Roach     * @param string     $relation
284ee727175SGreg Roach     * @param Date       $min_date
285ee727175SGreg Roach     * @param Date       $max_date
2863763c3f2SGreg Roach     *
2873763c3f2SGreg Roach     * @return Fact[]
2883763c3f2SGreg Roach     */
2898b9cfadbSGreg Roach    private function childFacts(Individual $person, Family $family, $option, $relation, Date $min_date, Date $max_date): array
290c1010edaSGreg Roach    {
291f4afa648SGreg Roach        $SHOW_RELATIVES_EVENTS = $person->tree()->getPreference('SHOW_RELATIVES_EVENTS');
2923763c3f2SGreg Roach
2937bf6ca81SGreg Roach        $birth_of_a_child = [
2947bf6ca81SGreg Roach            'BIRT' => [
2957bf6ca81SGreg Roach                'M' => I18N::translate('Birth of a son'),
2967bf6ca81SGreg Roach                'F' => I18N::translate('Birth of a daughter'),
2977bf6ca81SGreg Roach                'U' => I18N::translate('Birth of a child'),
2987bf6ca81SGreg Roach            ],
2997bf6ca81SGreg Roach            'CHR' => [
3007bf6ca81SGreg Roach                'M' => I18N::translate('Christening of a son'),
3017bf6ca81SGreg Roach                'F' => I18N::translate('Christening of a daughter'),
3027bf6ca81SGreg Roach                'U' => I18N::translate('Christening of a child'),
3037bf6ca81SGreg Roach            ],
3047bf6ca81SGreg Roach            'BAPM' => [
3057bf6ca81SGreg Roach                'M' => I18N::translate('Baptism of a son'),
3067bf6ca81SGreg Roach                'F' => I18N::translate('Baptism of a daughter'),
3077bf6ca81SGreg Roach                'U' => I18N::translate('Baptism of a child'),
3087bf6ca81SGreg Roach            ],
3097bf6ca81SGreg Roach            'ADOP' => [
3107bf6ca81SGreg Roach                'M' => I18N::translate('Adoption of a son'),
3117bf6ca81SGreg Roach                'F' => I18N::translate('Adoption of a daughter'),
3127bf6ca81SGreg Roach                'U' => I18N::translate('Adoption of a child'),
3137bf6ca81SGreg Roach            ],
3147bf6ca81SGreg Roach        ];
3157bf6ca81SGreg Roach
3167bf6ca81SGreg Roach        $birth_of_a_sibling = [
3177bf6ca81SGreg Roach            'BIRT' => [
3187bf6ca81SGreg Roach                'M' => I18N::translate('Birth of a brother'),
3197bf6ca81SGreg Roach                'F' => I18N::translate('Birth of a sister'),
3207bf6ca81SGreg Roach                'U' => I18N::translate('Birth of a sibling'),
3217bf6ca81SGreg Roach            ],
3227bf6ca81SGreg Roach            'CHR' => [
3237bf6ca81SGreg Roach                'M' => I18N::translate('Christening of a brother'),
3247bf6ca81SGreg Roach                'F' => I18N::translate('Christening of a sister'),
3257bf6ca81SGreg Roach                'U' => I18N::translate('Christening of a sibling'),
3267bf6ca81SGreg Roach            ],
3277bf6ca81SGreg Roach            'BAPM' => [
3287bf6ca81SGreg Roach                'M' => I18N::translate('Baptism of a brother'),
3297bf6ca81SGreg Roach                'F' => I18N::translate('Baptism of a sister'),
3307bf6ca81SGreg Roach                'U' => I18N::translate('Baptism of a sibling'),
3317bf6ca81SGreg Roach            ],
3327bf6ca81SGreg Roach            'ADOP' => [
3337bf6ca81SGreg Roach                'M' => I18N::translate('Adoption of a brother'),
3347bf6ca81SGreg Roach                'F' => I18N::translate('Adoption of a sister'),
3357bf6ca81SGreg Roach                'U' => I18N::translate('Adoption of a sibling'),
3367bf6ca81SGreg Roach            ],
3377bf6ca81SGreg Roach        ];
3387bf6ca81SGreg Roach
3397bf6ca81SGreg Roach        $birth_of_a_half_sibling = [
3407bf6ca81SGreg Roach            'BIRT' => [
3417bf6ca81SGreg Roach                'M' => I18N::translate('Birth of a half-brother'),
3427bf6ca81SGreg Roach                'F' => I18N::translate('Birth of a half-sister'),
3437bf6ca81SGreg Roach                'U' => I18N::translate('Birth of a half-sibling'),
3447bf6ca81SGreg Roach            ],
3457bf6ca81SGreg Roach            'CHR' => [
3467bf6ca81SGreg Roach                'M' => I18N::translate('Christening of a half-brother'),
3477bf6ca81SGreg Roach                'F' => I18N::translate('Christening of a half-sister'),
3487bf6ca81SGreg Roach                'U' => I18N::translate('Christening of a half-sibling'),
3497bf6ca81SGreg Roach            ],
3507bf6ca81SGreg Roach            'BAPM' => [
3517bf6ca81SGreg Roach                'M' => I18N::translate('Baptism of a half-brother'),
3527bf6ca81SGreg Roach                'F' => I18N::translate('Baptism of a half-sister'),
3537bf6ca81SGreg Roach                'U' => I18N::translate('Baptism of a half-sibling'),
3547bf6ca81SGreg Roach            ],
3557bf6ca81SGreg Roach            'ADOP' => [
3567bf6ca81SGreg Roach                'M' => I18N::translate('Adoption of a half-brother'),
3577bf6ca81SGreg Roach                'F' => I18N::translate('Adoption of a half-sister'),
3587bf6ca81SGreg Roach                'U' => I18N::translate('Adoption of a half-sibling'),
3597bf6ca81SGreg Roach            ],
3607bf6ca81SGreg Roach        ];
3617bf6ca81SGreg Roach
3627bf6ca81SGreg Roach        $birth_of_a_grandchild = [
3637bf6ca81SGreg Roach            'BIRT' => [
3647bf6ca81SGreg Roach                'M' => I18N::translate('Birth of a grandson'),
3657bf6ca81SGreg Roach                'F' => I18N::translate('Birth of a granddaughter'),
3667bf6ca81SGreg Roach                'U' => I18N::translate('Birth of a grandchild'),
3677bf6ca81SGreg Roach            ],
3687bf6ca81SGreg Roach            'CHR' => [
3697bf6ca81SGreg Roach                'M' => I18N::translate('Christening of a grandson'),
3707bf6ca81SGreg Roach                'F' => I18N::translate('Christening of a granddaughter'),
3717bf6ca81SGreg Roach                'U' => I18N::translate('Christening of a grandchild'),
3727bf6ca81SGreg Roach            ],
3737bf6ca81SGreg Roach            'BAPM' => [
3747bf6ca81SGreg Roach                'M' => I18N::translate('Baptism of a grandson'),
3757bf6ca81SGreg Roach                'F' => I18N::translate('Baptism of a granddaughter'),
3767bf6ca81SGreg Roach                'U' => I18N::translate('Baptism of a grandchild'),
3777bf6ca81SGreg Roach            ],
3787bf6ca81SGreg Roach            'ADOP' => [
3797bf6ca81SGreg Roach                'M' => I18N::translate('Adoption of a grandson'),
3807bf6ca81SGreg Roach                'F' => I18N::translate('Adoption of a granddaughter'),
3817bf6ca81SGreg Roach                'U' => I18N::translate('Adoption of a grandchild'),
3827bf6ca81SGreg Roach            ],
3837bf6ca81SGreg Roach        ];
3847bf6ca81SGreg Roach
3857bf6ca81SGreg Roach        $birth_of_a_grandchild1 = [
3867bf6ca81SGreg Roach            'BIRT' => [
3877bf6ca81SGreg Roach                'M' => I18N::translateContext('daughter’s son', 'Birth of a grandson'),
3887bf6ca81SGreg Roach                'F' => I18N::translateContext('daughter’s daughter', 'Birth of a granddaughter'),
3897bf6ca81SGreg Roach                'U' => I18N::translate('Birth of a grandchild'),
3907bf6ca81SGreg Roach            ],
3917bf6ca81SGreg Roach            'CHR' => [
3927bf6ca81SGreg Roach                'M' => I18N::translateContext('daughter’s son', 'Christening of a grandson'),
3937bf6ca81SGreg Roach                'F' => I18N::translateContext('daughter’s daughter', 'Christening of a granddaughter'),
3947bf6ca81SGreg Roach                'U' => I18N::translate('Christening of a grandchild'),
3957bf6ca81SGreg Roach            ],
3967bf6ca81SGreg Roach            'BAPM' => [
3977bf6ca81SGreg Roach                'M' => I18N::translateContext('daughter’s son', 'Baptism of a grandson'),
3987bf6ca81SGreg Roach                'F' => I18N::translateContext('daughter’s daughter', 'Baptism of a granddaughter'),
3997bf6ca81SGreg Roach                'U' => I18N::translate('Baptism of a grandchild'),
4007bf6ca81SGreg Roach            ],
4017bf6ca81SGreg Roach            'ADOP' => [
4027bf6ca81SGreg Roach                'M' => I18N::translateContext('daughter’s son', 'Adoption of a grandson'),
4037bf6ca81SGreg Roach                'F' => I18N::translateContext('daughter’s daughter', 'Adoption of a granddaughter'),
4047bf6ca81SGreg Roach                'U' => I18N::translate('Adoption of a grandchild'),
4057bf6ca81SGreg Roach            ],
4067bf6ca81SGreg Roach        ];
4077bf6ca81SGreg Roach
4087bf6ca81SGreg Roach        $birth_of_a_grandchild2 = [
4097bf6ca81SGreg Roach            'BIRT' => [
4103d394ce7SGreg Roach                'M' => I18N::translateContext('son’s son', 'Birth of a grandson'),
4113d394ce7SGreg Roach                'F' => I18N::translateContext('son’s daughter', 'Birth of a granddaughter'),
4127bf6ca81SGreg Roach                'U' => I18N::translate('Birth of a grandchild'),
4137bf6ca81SGreg Roach            ],
4147bf6ca81SGreg Roach            'CHR' => [
4153d394ce7SGreg Roach                'M' => I18N::translateContext('son’s son', 'Christening of a grandson'),
4163d394ce7SGreg Roach                'F' => I18N::translateContext('son’s daughter', 'Christening of a granddaughter'),
4177bf6ca81SGreg Roach                'U' => I18N::translate('Christening of a grandchild'),
4187bf6ca81SGreg Roach            ],
4197bf6ca81SGreg Roach            'BAPM' => [
4203d394ce7SGreg Roach                'M' => I18N::translateContext('son’s son', 'Baptism of a grandson'),
4213d394ce7SGreg Roach                'F' => I18N::translateContext('son’s daughter', 'Baptism of a granddaughter'),
4227bf6ca81SGreg Roach                'U' => I18N::translate('Baptism of a grandchild'),
4237bf6ca81SGreg Roach            ],
4247bf6ca81SGreg Roach            'ADOP' => [
4253d394ce7SGreg Roach                'M' => I18N::translateContext('son’s son', 'Adoption of a grandson'),
4263d394ce7SGreg Roach                'F' => I18N::translateContext('son’s daughter', 'Adoption of a granddaughter'),
4277bf6ca81SGreg Roach                'U' => I18N::translate('Adoption of a grandchild'),
4287bf6ca81SGreg Roach            ],
4297bf6ca81SGreg Roach        ];
4307bf6ca81SGreg Roach
4317bf6ca81SGreg Roach        $death_of_a_child = [
4327bf6ca81SGreg Roach            'DEAT' => [
4337bf6ca81SGreg Roach                'M' => I18N::translate('Death of a son'),
4347bf6ca81SGreg Roach                'F' => I18N::translate('Death of a daughter'),
4357bf6ca81SGreg Roach                'U' => I18N::translate('Death of a child'),
4367bf6ca81SGreg Roach            ],
4377bf6ca81SGreg Roach            'BURI' => [
4387bf6ca81SGreg Roach                'M' => I18N::translate('Burial of a son'),
4397bf6ca81SGreg Roach                'F' => I18N::translate('Burial of a daughter'),
4407bf6ca81SGreg Roach                'U' => I18N::translate('Burial of a child'),
4417bf6ca81SGreg Roach            ],
4427bf6ca81SGreg Roach            'CREM' => [
4437bf6ca81SGreg Roach                'M' => I18N::translate('Cremation of a son'),
4447bf6ca81SGreg Roach                'F' => I18N::translate('Cremation of a daughter'),
4457bf6ca81SGreg Roach                'U' => I18N::translate('Cremation of a child'),
4467bf6ca81SGreg Roach            ],
4477bf6ca81SGreg Roach        ];
4487bf6ca81SGreg Roach
4497bf6ca81SGreg Roach        $death_of_a_sibling = [
4507bf6ca81SGreg Roach            'DEAT' => [
4517bf6ca81SGreg Roach                'M' => I18N::translate('Death of a brother'),
4527bf6ca81SGreg Roach                'F' => I18N::translate('Death of a sister'),
4537bf6ca81SGreg Roach                'U' => I18N::translate('Death of a sibling'),
4547bf6ca81SGreg Roach            ],
4557bf6ca81SGreg Roach            'BURI' => [
4567bf6ca81SGreg Roach                'M' => I18N::translate('Burial of a brother'),
4577bf6ca81SGreg Roach                'F' => I18N::translate('Burial of a sister'),
4587bf6ca81SGreg Roach                'U' => I18N::translate('Burial of a sibling'),
4597bf6ca81SGreg Roach            ],
4607bf6ca81SGreg Roach            'CREM' => [
4617bf6ca81SGreg Roach                'M' => I18N::translate('Cremation of a brother'),
4627bf6ca81SGreg Roach                'F' => I18N::translate('Cremation of a sister'),
4637bf6ca81SGreg Roach                'U' => I18N::translate('Cremation of a sibling'),
4647bf6ca81SGreg Roach            ],
4657bf6ca81SGreg Roach        ];
4667bf6ca81SGreg Roach
4677bf6ca81SGreg Roach        $death_of_a_half_sibling = [
4687bf6ca81SGreg Roach            'DEAT' => [
4697bf6ca81SGreg Roach                'M' => I18N::translate('Death of a half-brother'),
4707bf6ca81SGreg Roach                'F' => I18N::translate('Death of a half-sister'),
4717bf6ca81SGreg Roach                'U' => I18N::translate('Death of a half-sibling'),
4727bf6ca81SGreg Roach            ],
4737bf6ca81SGreg Roach            'BURI' => [
4747bf6ca81SGreg Roach                'M' => I18N::translate('Burial of a half-brother'),
4757bf6ca81SGreg Roach                'F' => I18N::translate('Burial of a half-sister'),
4767bf6ca81SGreg Roach                'U' => I18N::translate('Burial of a half-sibling'),
4777bf6ca81SGreg Roach            ],
4787bf6ca81SGreg Roach            'CREM' => [
4797bf6ca81SGreg Roach                'M' => I18N::translate('Cremation of a half-brother'),
4807bf6ca81SGreg Roach                'F' => I18N::translate('Cremation of a half-sister'),
4817bf6ca81SGreg Roach                'U' => I18N::translate('Cremation of a half-sibling'),
4827bf6ca81SGreg Roach            ],
4837bf6ca81SGreg Roach        ];
4847bf6ca81SGreg Roach
4857bf6ca81SGreg Roach        $death_of_a_grandchild = [
4867bf6ca81SGreg Roach            'DEAT' => [
4877bf6ca81SGreg Roach                'M' => I18N::translate('Death of a grandson'),
4887bf6ca81SGreg Roach                'F' => I18N::translate('Death of a granddaughter'),
4897bf6ca81SGreg Roach                'U' => I18N::translate('Death of a grandchild'),
4907bf6ca81SGreg Roach            ],
4917bf6ca81SGreg Roach            'BURI' => [
4927bf6ca81SGreg Roach                'M' => I18N::translate('Burial of a grandson'),
4937bf6ca81SGreg Roach                'F' => I18N::translate('Burial of a granddaughter'),
4947bf6ca81SGreg Roach                'U' => I18N::translate('Burial of a grandchild'),
4957bf6ca81SGreg Roach            ],
4967bf6ca81SGreg Roach            'CREM' => [
4977bf6ca81SGreg Roach                'M' => I18N::translate('Cremation of a grandson'),
4987bf6ca81SGreg Roach                'F' => I18N::translate('Cremation of a granddaughter'),
4997bf6ca81SGreg Roach                'U' => I18N::translate('Baptism of a grandchild'),
5007bf6ca81SGreg Roach            ],
5017bf6ca81SGreg Roach        ];
5027bf6ca81SGreg Roach
5037bf6ca81SGreg Roach        $death_of_a_grandchild1 = [
5047bf6ca81SGreg Roach            'DEAT' => [
5057bf6ca81SGreg Roach                'M' => I18N::translateContext('daughter’s son', 'Death of a grandson'),
5067bf6ca81SGreg Roach                'F' => I18N::translateContext('daughter’s daughter', 'Death of a granddaughter'),
5077bf6ca81SGreg Roach                'U' => I18N::translate('Death of a grandchild'),
5087bf6ca81SGreg Roach            ],
5097bf6ca81SGreg Roach            'BURI' => [
5107bf6ca81SGreg Roach                'M' => I18N::translateContext('daughter’s son', 'Burial of a grandson'),
5117bf6ca81SGreg Roach                'F' => I18N::translateContext('daughter’s daughter', 'Burial of a granddaughter'),
5127bf6ca81SGreg Roach                'U' => I18N::translate('Burial of a grandchild'),
5137bf6ca81SGreg Roach            ],
5147bf6ca81SGreg Roach            'CREM' => [
5157bf6ca81SGreg Roach                'M' => I18N::translateContext('daughter’s son', 'Cremation of a grandson'),
5167bf6ca81SGreg Roach                'F' => I18N::translateContext('daughter’s daughter', 'Cremation of a granddaughter'),
5177bf6ca81SGreg Roach                'U' => I18N::translate('Baptism of a grandchild'),
5187bf6ca81SGreg Roach            ],
5197bf6ca81SGreg Roach        ];
5207bf6ca81SGreg Roach
5217bf6ca81SGreg Roach        $death_of_a_grandchild2 = [
5227bf6ca81SGreg Roach            'DEAT' => [
5233d394ce7SGreg Roach                'M' => I18N::translateContext('son’s son', 'Death of a grandson'),
5243d394ce7SGreg Roach                'F' => I18N::translateContext('son’s daughter', 'Death of a granddaughter'),
5257bf6ca81SGreg Roach                'U' => I18N::translate('Death of a grandchild'),
5267bf6ca81SGreg Roach            ],
5277bf6ca81SGreg Roach            'BURI' => [
5283d394ce7SGreg Roach                'M' => I18N::translateContext('son’s son', 'Burial of a grandson'),
5293d394ce7SGreg Roach                'F' => I18N::translateContext('son’s daughter', 'Burial of a granddaughter'),
5307bf6ca81SGreg Roach                'U' => I18N::translate('Burial of a grandchild'),
5317bf6ca81SGreg Roach            ],
5327bf6ca81SGreg Roach            'CREM' => [
5333d394ce7SGreg Roach                'M' => I18N::translateContext('son’s son', 'Cremation of a grandson'),
5343d394ce7SGreg Roach                'F' => I18N::translateContext('son’s daughter', 'Cremation of a granddaughter'),
5357bf6ca81SGreg Roach                'U' => I18N::translate('Cremation of a grandchild'),
5367bf6ca81SGreg Roach            ],
5377bf6ca81SGreg Roach        ];
5387bf6ca81SGreg Roach
5397bf6ca81SGreg Roach        $marriage_of_a_child = [
5407bf6ca81SGreg Roach            'M' => I18N::translate('Marriage of a son'),
5417bf6ca81SGreg Roach            'F' => I18N::translate('Marriage of a daughter'),
5427bf6ca81SGreg Roach            'U' => I18N::translate('Marriage of a child'),
5437bf6ca81SGreg Roach        ];
5447bf6ca81SGreg Roach
5457bf6ca81SGreg Roach        $marriage_of_a_grandchild = [
5467bf6ca81SGreg Roach            'M' => I18N::translate('Marriage of a grandson'),
5477bf6ca81SGreg Roach            'F' => I18N::translate('Marriage of a granddaughter'),
5487bf6ca81SGreg Roach            'U' => I18N::translate('Marriage of a grandchild'),
5497bf6ca81SGreg Roach        ];
5507bf6ca81SGreg Roach
5517bf6ca81SGreg Roach        $marriage_of_a_grandchild1 = [
5527bf6ca81SGreg Roach            'M' => I18N::translateContext('daughter’s son', 'Marriage of a grandson'),
5537bf6ca81SGreg Roach            'F' => I18N::translateContext('daughter’s daughter', 'Marriage of a granddaughter'),
5547bf6ca81SGreg Roach            'U' => I18N::translate('Marriage of a grandchild'),
5557bf6ca81SGreg Roach        ];
5567bf6ca81SGreg Roach
5577bf6ca81SGreg Roach        $marriage_of_a_grandchild2 = [
5583d394ce7SGreg Roach            'M' => I18N::translateContext('son’s son', 'Marriage of a grandson'),
5593d394ce7SGreg Roach            'F' => I18N::translateContext('son’s daughter', 'Marriage of a granddaughter'),
5607bf6ca81SGreg Roach            'U' => I18N::translate('Marriage of a grandchild'),
5617bf6ca81SGreg Roach        ];
5627bf6ca81SGreg Roach
5637bf6ca81SGreg Roach        $marriage_of_a_sibling = [
5647bf6ca81SGreg Roach            'M' => I18N::translate('Marriage of a brother'),
5657bf6ca81SGreg Roach            'F' => I18N::translate('Marriage of a sister'),
5667bf6ca81SGreg Roach            'U' => I18N::translate('Marriage of a sibling'),
5677bf6ca81SGreg Roach        ];
5687bf6ca81SGreg Roach
5697bf6ca81SGreg Roach        $marriage_of_a_half_sibling = [
5707bf6ca81SGreg Roach            'M' => I18N::translate('Marriage of a half-brother'),
5717bf6ca81SGreg Roach            'F' => I18N::translate('Marriage of a half-sister'),
5727bf6ca81SGreg Roach            'U' => I18N::translate('Marriage of a half-sibling'),
5737bf6ca81SGreg Roach        ];
5747bf6ca81SGreg Roach
57513abd6f3SGreg Roach        $facts = [];
5763763c3f2SGreg Roach
5773763c3f2SGreg Roach        // Deal with recursion.
5783763c3f2SGreg Roach        switch ($option) {
5793763c3f2SGreg Roach            case '_CHIL':
5803763c3f2SGreg Roach                // Add grandchildren
58139ca88baSGreg Roach                foreach ($family->children() as $child) {
58239ca88baSGreg Roach                    foreach ($child->spouseFamilies() as $cfamily) {
58339ca88baSGreg Roach                        switch ($child->sex()) {
5843763c3f2SGreg Roach                            case 'M':
5858b9cfadbSGreg Roach                                foreach ($this->childFacts($person, $cfamily, '_GCHI', 'son', $min_date, $max_date) as $fact) {
5863763c3f2SGreg Roach                                    $facts[] = $fact;
5873763c3f2SGreg Roach                                }
5883763c3f2SGreg Roach                                break;
5893763c3f2SGreg Roach                            case 'F':
5908b9cfadbSGreg Roach                                foreach ($this->childFacts($person, $cfamily, '_GCHI', 'dau', $min_date, $max_date) as $fact) {
5913763c3f2SGreg Roach                                    $facts[] = $fact;
5923763c3f2SGreg Roach                                }
5933763c3f2SGreg Roach                                break;
5943763c3f2SGreg Roach                            default:
5958b9cfadbSGreg Roach                                foreach ($this->childFacts($person, $cfamily, '_GCHI', 'chi', $min_date, $max_date) as $fact) {
5963763c3f2SGreg Roach                                    $facts[] = $fact;
5973763c3f2SGreg Roach                                }
5983763c3f2SGreg Roach                                break;
5993763c3f2SGreg Roach                        }
6003763c3f2SGreg Roach                    }
6013763c3f2SGreg Roach                }
6023763c3f2SGreg Roach                break;
6033763c3f2SGreg Roach        }
6043763c3f2SGreg Roach
6053763c3f2SGreg Roach        // For each child in the family
60639ca88baSGreg Roach        foreach ($family->children() as $child) {
60722d65e5aSGreg Roach            if ($child->xref() === $person->xref()) {
6083763c3f2SGreg Roach                // We are not our own sibling!
6093763c3f2SGreg Roach                continue;
6103763c3f2SGreg Roach            }
6113763c3f2SGreg Roach            // add child’s birth
612dec352c1SGreg Roach            if (str_contains($SHOW_RELATIVES_EVENTS, '_BIRT' . str_replace('_HSIB', '_SIBL', $option))) {
6137bf6ca81SGreg Roach                foreach ($child->facts(['BIRT', 'CHR', 'BAPM', 'ADOP']) as $fact) {
6143763c3f2SGreg Roach                    // Always show _BIRT_CHIL, even if the dates are not known
61522d65e5aSGreg Roach                    if ($option === '_CHIL' || $this->includeFact($fact, $min_date, $max_date)) {
6167bf6ca81SGreg Roach                        switch ($option) {
6177bf6ca81SGreg Roach                            case '_GCHI':
6187bf6ca81SGreg Roach                                switch ($relation) {
6197bf6ca81SGreg Roach                                    case 'dau':
6207fe676e5SGreg Roach                                        $facts[] = $this->convertEvent($fact, $birth_of_a_grandchild1[$fact->getTag()][$fact->record()->sex()]);
6217bf6ca81SGreg Roach                                        break;
6227bf6ca81SGreg Roach                                    case 'son':
6237fe676e5SGreg Roach                                        $facts[] = $this->convertEvent($fact, $birth_of_a_grandchild2[$fact->getTag()][$fact->record()->sex()]);
6247bf6ca81SGreg Roach                                        break;
6257bf6ca81SGreg Roach                                    case 'chil':
6267fe676e5SGreg Roach                                        $facts[] = $this->convertEvent($fact, $birth_of_a_grandchild[$fact->getTag()][$fact->record()->sex()]);
6277bf6ca81SGreg Roach                                        break;
6287bf6ca81SGreg Roach                                }
6297bf6ca81SGreg Roach                                break;
6307bf6ca81SGreg Roach                            case '_SIBL':
6317fe676e5SGreg Roach                                $facts[] = $this->convertEvent($fact, $birth_of_a_sibling[$fact->getTag()][$fact->record()->sex()]);
6327bf6ca81SGreg Roach                                break;
6337bf6ca81SGreg Roach                            case '_HSIB':
6347fe676e5SGreg Roach                                $facts[] = $this->convertEvent($fact, $birth_of_a_half_sibling[$fact->getTag()][$fact->record()->sex()]);
6357bf6ca81SGreg Roach                                break;
6367bf6ca81SGreg Roach                            case '_CHIL':
6377fe676e5SGreg Roach                                $facts[] = $this->convertEvent($fact, $birth_of_a_child[$fact->getTag()][$fact->record()->sex()]);
6387bf6ca81SGreg Roach                                break;
6393763c3f2SGreg Roach                        }
6403763c3f2SGreg Roach                    }
6413763c3f2SGreg Roach                }
6423763c3f2SGreg Roach            }
6433763c3f2SGreg Roach            // add child’s death
644dec352c1SGreg Roach            if (str_contains($SHOW_RELATIVES_EVENTS, '_DEAT' . str_replace('_HSIB', '_SIBL', $option))) {
6457bf6ca81SGreg Roach                foreach ($child->facts(['DEAT', 'BURI', 'CREM']) as $fact) {
6468b9cfadbSGreg Roach                    if ($this->includeFact($fact, $min_date, $max_date)) {
6477bf6ca81SGreg Roach                        switch ($option) {
6487bf6ca81SGreg Roach                            case '_GCHI':
6497bf6ca81SGreg Roach                                switch ($relation) {
6507bf6ca81SGreg Roach                                    case 'dau':
6517fe676e5SGreg Roach                                        $facts[] = $this->convertEvent($fact, $death_of_a_grandchild1[$fact->getTag()][$fact->record()->sex()]);
6527bf6ca81SGreg Roach                                        break;
6537bf6ca81SGreg Roach                                    case 'son':
6547fe676e5SGreg Roach                                        $facts[] = $this->convertEvent($fact, $death_of_a_grandchild2[$fact->getTag()][$fact->record()->sex()]);
6557bf6ca81SGreg Roach                                        break;
6567bf6ca81SGreg Roach                                    case 'chi':
6577fe676e5SGreg Roach                                        $facts[] = $this->convertEvent($fact, $death_of_a_grandchild[$fact->getTag()][$fact->record()->sex()]);
6587bf6ca81SGreg Roach                                        break;
6597bf6ca81SGreg Roach                                }
6607bf6ca81SGreg Roach                                break;
6617bf6ca81SGreg Roach                            case '_SIBL':
6627fe676e5SGreg Roach                                $facts[] = $this->convertEvent($fact, $death_of_a_sibling[$fact->getTag()][$fact->record()->sex()]);
6637bf6ca81SGreg Roach                                break;
6647bf6ca81SGreg Roach                            case '_HSIB':
6657fe676e5SGreg Roach                                $facts[] = $this->convertEvent($fact, $death_of_a_half_sibling[$fact->getTag()][$fact->record()->sex()]);
6667bf6ca81SGreg Roach                                break;
667718b3bc2SGreg Roach                            case '_CHIL':
6687fe676e5SGreg Roach                                $facts[] = $this->convertEvent($fact, $death_of_a_child[$fact->getTag()][$fact->record()->sex()]);
6697bf6ca81SGreg Roach                                break;
6703763c3f2SGreg Roach                        }
6713763c3f2SGreg Roach                    }
6723763c3f2SGreg Roach                }
6733763c3f2SGreg Roach            }
6747bf6ca81SGreg Roach
6753763c3f2SGreg Roach            // add child’s marriage
676dec352c1SGreg Roach            if (str_contains($SHOW_RELATIVES_EVENTS, '_MARR' . str_replace('_HSIB', '_SIBL', $option))) {
67739ca88baSGreg Roach                foreach ($child->spouseFamilies() as $sfamily) {
6788d0ebef0SGreg Roach                    foreach ($sfamily->facts(['MARR']) as $fact) {
6798b9cfadbSGreg Roach                        if ($this->includeFact($fact, $min_date, $max_date)) {
6807bf6ca81SGreg Roach                            switch ($option) {
6817bf6ca81SGreg Roach                                case '_GCHI':
6827bf6ca81SGreg Roach                                    switch ($relation) {
6837bf6ca81SGreg Roach                                        case 'dau':
6847bf6ca81SGreg Roach                                            $facts[] = $this->convertEvent($fact, $marriage_of_a_grandchild1['F']);
6857bf6ca81SGreg Roach                                            break;
6867bf6ca81SGreg Roach                                        case 'son':
6877bf6ca81SGreg Roach                                            $facts[] = $this->convertEvent($fact, $marriage_of_a_grandchild2['M']);
6887bf6ca81SGreg Roach                                            break;
6897bf6ca81SGreg Roach                                        case 'chi':
6907bf6ca81SGreg Roach                                            $facts[] = $this->convertEvent($fact, $marriage_of_a_grandchild['U']);
6917bf6ca81SGreg Roach                                            break;
6927bf6ca81SGreg Roach                                    }
6937bf6ca81SGreg Roach                                    break;
6947bf6ca81SGreg Roach                                case '_SIBL':
6957bf6ca81SGreg Roach                                    $facts[] = $this->convertEvent($fact, $marriage_of_a_sibling['U']);
6967bf6ca81SGreg Roach                                    break;
6977bf6ca81SGreg Roach                                case '_HSIB':
6987bf6ca81SGreg Roach                                    $facts[] = $this->convertEvent($fact, $marriage_of_a_half_sibling['U']);
6997bf6ca81SGreg Roach                                    break;
7007bf6ca81SGreg Roach                                case '_CHIL':
7017bf6ca81SGreg Roach                                    $facts[] = $this->convertEvent($fact, $marriage_of_a_child['U']);
7027bf6ca81SGreg Roach                                    break;
7033763c3f2SGreg Roach                            }
7043763c3f2SGreg Roach                        }
7053763c3f2SGreg Roach                    }
7063763c3f2SGreg Roach                }
7073763c3f2SGreg Roach            }
7083763c3f2SGreg Roach        }
7093763c3f2SGreg Roach
7103763c3f2SGreg Roach        return $facts;
7113763c3f2SGreg Roach    }
7123763c3f2SGreg Roach
7133763c3f2SGreg Roach    /**
7143763c3f2SGreg Roach     * Get the events of parents and grandparents.
7153763c3f2SGreg Roach     *
7163763c3f2SGreg Roach     * @param Individual $person
717cbc1590aSGreg Roach     * @param int        $sosa
718ee727175SGreg Roach     * @param Date       $min_date
719ee727175SGreg Roach     * @param Date       $max_date
7203763c3f2SGreg Roach     *
7213763c3f2SGreg Roach     * @return Fact[]
7223763c3f2SGreg Roach     */
723054cfd06SGreg Roach    private function parentFacts(Individual $person, int $sosa, Date $min_date, Date $max_date): array
724c1010edaSGreg Roach    {
725f4afa648SGreg Roach        $SHOW_RELATIVES_EVENTS = $person->tree()->getPreference('SHOW_RELATIVES_EVENTS');
7263763c3f2SGreg Roach
7277bf6ca81SGreg Roach        $death_of_a_parent = [
7287bf6ca81SGreg Roach            'DEAT' => [
7297bf6ca81SGreg Roach                'M' => I18N::translate('Death of a father'),
7307bf6ca81SGreg Roach                'F' => I18N::translate('Death of a mother'),
7317bf6ca81SGreg Roach                'U' => I18N::translate('Death of a parent'),
7327bf6ca81SGreg Roach            ],
7337bf6ca81SGreg Roach            'BURI' => [
7347bf6ca81SGreg Roach                'M' => I18N::translate('Burial of a father'),
7357bf6ca81SGreg Roach                'F' => I18N::translate('Burial of a mother'),
7367bf6ca81SGreg Roach                'U' => I18N::translate('Burial of a parent'),
7377bf6ca81SGreg Roach            ],
7387bf6ca81SGreg Roach            'CREM' => [
7397bf6ca81SGreg Roach                'M' => I18N::translate('Cremation of a father'),
7407bf6ca81SGreg Roach                'F' => I18N::translate('Cremation of a mother'),
7417bf6ca81SGreg Roach                'U' => I18N::translate('Cremation of a parent'),
7427bf6ca81SGreg Roach            ],
7437bf6ca81SGreg Roach        ];
7447bf6ca81SGreg Roach
7457bf6ca81SGreg Roach        $death_of_a_grandparent = [
7467bf6ca81SGreg Roach            'DEAT' => [
7477bf6ca81SGreg Roach                'M' => I18N::translate('Death of a grandfather'),
7487bf6ca81SGreg Roach                'F' => I18N::translate('Death of a grandmother'),
7497bf6ca81SGreg Roach                'U' => I18N::translate('Death of a grandparent'),
7507bf6ca81SGreg Roach            ],
7517bf6ca81SGreg Roach            'BURI' => [
7527bf6ca81SGreg Roach                'M' => I18N::translate('Burial of a grandfather'),
7537bf6ca81SGreg Roach                'F' => I18N::translate('Burial of a grandmother'),
7547bf6ca81SGreg Roach                'U' => I18N::translate('Burial of a grandparent'),
7557bf6ca81SGreg Roach            ],
7567bf6ca81SGreg Roach            'CREM' => [
7577bf6ca81SGreg Roach                'M' => I18N::translate('Cremation of a grandfather'),
7587bf6ca81SGreg Roach                'F' => I18N::translate('Cremation of a grandmother'),
7590b73ecfcSGreg Roach                'U' => I18N::translate('Cremation of a grandparent'),
7607bf6ca81SGreg Roach            ],
7617bf6ca81SGreg Roach        ];
7627bf6ca81SGreg Roach
7637bf6ca81SGreg Roach        $death_of_a_maternal_grandparent = [
7647bf6ca81SGreg Roach            'DEAT' => [
7650b73ecfcSGreg Roach                'M' => I18N::translate('Death of a maternal grandfather'),
7660b73ecfcSGreg Roach                'F' => I18N::translate('Death of a maternal grandmother'),
7677bf6ca81SGreg Roach                'U' => I18N::translate('Death of a grandparent'),
7687bf6ca81SGreg Roach            ],
7697bf6ca81SGreg Roach            'BURI' => [
7700b73ecfcSGreg Roach                'M' => I18N::translate('Burial of a maternal grandfather'),
7710b73ecfcSGreg Roach                'F' => I18N::translate('Burial of a maternal grandmother'),
7727bf6ca81SGreg Roach                'U' => I18N::translate('Burial of a grandparent'),
7737bf6ca81SGreg Roach            ],
7747bf6ca81SGreg Roach            'CREM' => [
7750b73ecfcSGreg Roach                'M' => I18N::translate('Cremation of a maternal grandfather'),
7760b73ecfcSGreg Roach                'F' => I18N::translate('Cremation of a maternal grandmother'),
7770b73ecfcSGreg Roach                'U' => I18N::translate('Cremation of a grandparent'),
7787bf6ca81SGreg Roach            ],
7797bf6ca81SGreg Roach        ];
7807bf6ca81SGreg Roach
7817bf6ca81SGreg Roach        $death_of_a_paternal_grandparent = [
7827bf6ca81SGreg Roach            'DEAT' => [
7830b73ecfcSGreg Roach                'M' => I18N::translate('Death of a paternal grandfather'),
7840b73ecfcSGreg Roach                'F' => I18N::translate('Death of a paternal grandmother'),
7857bf6ca81SGreg Roach                'U' => I18N::translate('Death of a grandparent'),
7867bf6ca81SGreg Roach            ],
7877bf6ca81SGreg Roach            'BURI' => [
7880b73ecfcSGreg Roach                'M' => I18N::translate('Burial of a paternal grandfather'),
7890b73ecfcSGreg Roach                'F' => I18N::translate('Burial of a paternal grandmother'),
7907bf6ca81SGreg Roach                'U' => I18N::translate('Burial of a grandparent'),
7917bf6ca81SGreg Roach            ],
7927bf6ca81SGreg Roach            'CREM' => [
7930b73ecfcSGreg Roach                'M' => I18N::translate('Cremation of a paternal grandfather'),
7940b73ecfcSGreg Roach                'F' => I18N::translate('Cremation of a paternal grandmother'),
7957bf6ca81SGreg Roach                'U' => I18N::translate('Cremation of a grandparent'),
7967bf6ca81SGreg Roach            ],
7977bf6ca81SGreg Roach        ];
7987bf6ca81SGreg Roach
7997bf6ca81SGreg Roach        $marriage_of_a_parent = [
8007bf6ca81SGreg Roach            'M' => I18N::translate('Marriage of a father'),
8017bf6ca81SGreg Roach            'F' => I18N::translate('Marriage of a mother'),
8027bf6ca81SGreg Roach            'U' => I18N::translate('Marriage of a parent'),
8037bf6ca81SGreg Roach        ];
8047bf6ca81SGreg Roach
80513abd6f3SGreg Roach        $facts = [];
8063763c3f2SGreg Roach
8070b73ecfcSGreg Roach        if ($sosa === 1) {
80839ca88baSGreg Roach            foreach ($person->childFamilies() as $family) {
8093763c3f2SGreg Roach                // Add siblings
8108b9cfadbSGreg Roach                foreach ($this->childFacts($person, $family, '_SIBL', '', $min_date, $max_date) as $fact) {
8113763c3f2SGreg Roach                    $facts[] = $fact;
8123763c3f2SGreg Roach                }
81339ca88baSGreg Roach                foreach ($family->spouses() as $spouse) {
81439ca88baSGreg Roach                    foreach ($spouse->spouseFamilies() as $sfamily) {
8153763c3f2SGreg Roach                        if ($family !== $sfamily) {
8163763c3f2SGreg Roach                            // Add half-siblings
8178b9cfadbSGreg Roach                            foreach ($this->childFacts($person, $sfamily, '_HSIB', '', $min_date, $max_date) as $fact) {
8183763c3f2SGreg Roach                                $facts[] = $fact;
8193763c3f2SGreg Roach                            }
8203763c3f2SGreg Roach                        }
8213763c3f2SGreg Roach                    }
8223763c3f2SGreg Roach                    // Add grandparents
82322d65e5aSGreg Roach                    foreach ($this->parentFacts($spouse, $spouse->sex() === 'F' ? 3 : 2, $min_date, $max_date) as $fact) {
8243763c3f2SGreg Roach                        $facts[] = $fact;
8253763c3f2SGreg Roach                    }
8263763c3f2SGreg Roach                }
8273763c3f2SGreg Roach            }
8283763c3f2SGreg Roach
829dec352c1SGreg Roach            if (str_contains($SHOW_RELATIVES_EVENTS, '_MARR_PARE')) {
8303763c3f2SGreg Roach                // add father/mother marriages
83139ca88baSGreg Roach                foreach ($person->childFamilies() as $sfamily) {
8328d0ebef0SGreg Roach                    foreach ($sfamily->facts(['MARR']) as $fact) {
8338b9cfadbSGreg Roach                        if ($this->includeFact($fact, $min_date, $max_date)) {
8343763c3f2SGreg Roach                            // marriage of parents (to each other)
8357bf6ca81SGreg Roach                            $facts[] = $this->convertEvent($fact, I18N::translate('Marriage of parents'));
8363763c3f2SGreg Roach                        }
8373763c3f2SGreg Roach                    }
8383763c3f2SGreg Roach                }
83939ca88baSGreg Roach                foreach ($person->childStepFamilies() as $sfamily) {
8408d0ebef0SGreg Roach                    foreach ($sfamily->facts(['MARR']) as $fact) {
8418b9cfadbSGreg Roach                        if ($this->includeFact($fact, $min_date, $max_date)) {
8423763c3f2SGreg Roach                            // marriage of a parent (to another spouse)
8437bf6ca81SGreg Roach                            $facts[] = $this->convertEvent($fact, $marriage_of_a_parent['U']);
8443763c3f2SGreg Roach                        }
8453763c3f2SGreg Roach                    }
8463763c3f2SGreg Roach                }
8473763c3f2SGreg Roach            }
8483763c3f2SGreg Roach        }
8493763c3f2SGreg Roach
85039ca88baSGreg Roach        foreach ($person->childFamilies() as $family) {
85139ca88baSGreg Roach            foreach ($family->spouses() as $parent) {
852dec352c1SGreg Roach                if (str_contains($SHOW_RELATIVES_EVENTS, '_DEAT' . ($sosa === 1 ? '_PARE' : '_GPAR'))) {
8537bf6ca81SGreg Roach                    foreach ($parent->facts(['DEAT', 'BURI', 'CREM']) as $fact) {
854*e4cf93e3SGreg Roach                        // Show death of parent when it happened prior to birth
855*e4cf93e3SGreg Roach                        if ($sosa === 1 && Date::compare($fact->date(), $min_date) < 0 || $this->includeFact($fact, $min_date, $max_date)) {
8563763c3f2SGreg Roach                            switch ($sosa) {
8573763c3f2SGreg Roach                                case 1:
8587fe676e5SGreg Roach                                    $facts[] = $this->convertEvent($fact, $death_of_a_parent[$fact->getTag()][$fact->record()->sex()]);
8593763c3f2SGreg Roach                                    break;
8603763c3f2SGreg Roach                                case 2:
8613763c3f2SGreg Roach                                case 3:
86269c89463SGreg Roach                                    switch ($person->sex()) {
8637bf6ca81SGreg Roach                                        case 'M':
8647fe676e5SGreg Roach                                            $facts[] = $this->convertEvent($fact, $death_of_a_paternal_grandparent[$fact->getTag()][$fact->record()->sex()]);
8653763c3f2SGreg Roach                                            break;
8667bf6ca81SGreg Roach                                        case 'F':
8677fe676e5SGreg Roach                                            $facts[] = $this->convertEvent($fact, $death_of_a_maternal_grandparent[$fact->getTag()][$fact->record()->sex()]);
8687bf6ca81SGreg Roach                                            break;
8697bf6ca81SGreg Roach                                        default:
8707fe676e5SGreg Roach                                            $facts[] = $this->convertEvent($fact, $death_of_a_grandparent[$fact->getTag()][$fact->record()->sex()]);
8717bf6ca81SGreg Roach                                            break;
8727bf6ca81SGreg Roach                                    }
8733763c3f2SGreg Roach                            }
8743763c3f2SGreg Roach                        }
8753763c3f2SGreg Roach                    }
8763763c3f2SGreg Roach                }
8773763c3f2SGreg Roach            }
8783763c3f2SGreg Roach        }
8793763c3f2SGreg Roach
8803763c3f2SGreg Roach        return $facts;
8813763c3f2SGreg Roach    }
8823763c3f2SGreg Roach
8833763c3f2SGreg Roach    /**
8843763c3f2SGreg Roach     * Get any historical events.
8853763c3f2SGreg Roach     *
88617c50b57SGreg Roach     * @param Individual $individual
8873763c3f2SGreg Roach     *
8883763c3f2SGreg Roach     * @return Fact[]
8893763c3f2SGreg Roach     */
8904ca7e03cSGreg Roach    private function historicalFacts(Individual $individual): array
891c1010edaSGreg Roach    {
8924ca7e03cSGreg Roach        return $this->module_service->findByInterface(ModuleHistoricEventsInterface::class)
8930b5fd0a6SGreg Roach            ->map(static function (ModuleHistoricEventsInterface $module) use ($individual): Collection {
89417c50b57SGreg Roach                return $module->historicEventsForIndividual($individual);
89517c50b57SGreg Roach            })
89617c50b57SGreg Roach            ->flatten()
89717c50b57SGreg Roach            ->all();
8983763c3f2SGreg Roach    }
8993763c3f2SGreg Roach
9003763c3f2SGreg Roach    /**
9013763c3f2SGreg Roach     * Get the events of associates.
9023763c3f2SGreg Roach     *
9033763c3f2SGreg Roach     * @param Individual $person
9043763c3f2SGreg Roach     *
9053763c3f2SGreg Roach     * @return Fact[]
9063763c3f2SGreg Roach     */
9078b9cfadbSGreg Roach    private function associateFacts(Individual $person): array
908c1010edaSGreg Roach    {
90913abd6f3SGreg Roach        $facts = [];
9103763c3f2SGreg Roach
911ee727175SGreg Roach        /** @var Individual[] $associates */
912907c1109SGreg Roach        $asso1 = $person->linkedIndividuals('ASSO');
913907c1109SGreg Roach        $asso2 = $person->linkedIndividuals('_ASSO');
914907c1109SGreg Roach        $asso3 = $person->linkedFamilies('ASSO');
915907c1109SGreg Roach        $asso4 = $person->linkedFamilies('_ASSO');
916907c1109SGreg Roach
917907c1109SGreg Roach        $associates = $asso1->merge($asso2)->merge($asso3)->merge($asso4);
918907c1109SGreg Roach
9193763c3f2SGreg Roach        foreach ($associates as $associate) {
92030158ae7SGreg Roach            foreach ($associate->facts() as $fact) {
921907c1109SGreg Roach                if (preg_match('/\n\d _?ASSO @' . $person->xref() . '@/', $fact->gedcom())) {
9223763c3f2SGreg Roach                    // Extract the important details from the fact
9237fe676e5SGreg Roach                    $factrec = '1 ' . $fact->getTag();
924138ca96cSGreg Roach                    if (preg_match('/\n2 DATE .*/', $fact->gedcom(), $match)) {
9253763c3f2SGreg Roach                        $factrec .= $match[0];
9263763c3f2SGreg Roach                    }
927138ca96cSGreg Roach                    if (preg_match('/\n2 PLAC .*/', $fact->gedcom(), $match)) {
9283763c3f2SGreg Roach                        $factrec .= $match[0];
9293763c3f2SGreg Roach                    }
9303763c3f2SGreg Roach                    if ($associate instanceof Family) {
93139ca88baSGreg Roach                        foreach ($associate->spouses() as $spouse) {
932c0935879SGreg Roach                            $factrec .= "\n2 _ASSO @" . $spouse->xref() . '@';
9333763c3f2SGreg Roach                        }
9343763c3f2SGreg Roach                    } else {
935c0935879SGreg Roach                        $factrec .= "\n2 _ASSO @" . $associate->xref() . '@';
9363763c3f2SGreg Roach                    }
9373763c3f2SGreg Roach                    $facts[] = new Fact($factrec, $associate, 'asso');
9383763c3f2SGreg Roach                }
9393763c3f2SGreg Roach            }
9403763c3f2SGreg Roach        }
9413763c3f2SGreg Roach
9423763c3f2SGreg Roach        return $facts;
9433763c3f2SGreg Roach    }
9448eaf8709SGreg Roach
9458eaf8709SGreg Roach    /**
9468eaf8709SGreg Roach     * This module handles the following facts - so don't show them on the "Facts and events" tab.
9478eaf8709SGreg Roach     *
948b5c8fd7eSGreg Roach     * @return Collection<string>
9498eaf8709SGreg Roach     */
9508eaf8709SGreg Roach    public function supportedFacts(): Collection
9518eaf8709SGreg Roach    {
9528eaf8709SGreg Roach        // We don't actually displaye these facts, but they are displayed
9538eaf8709SGreg Roach        // outside the tabs/sidebar systems. This just forces them to be excluded here.
9540666a894SGreg Roach        return new Collection(['NAME', 'SEX', 'OBJE']);
9558eaf8709SGreg Roach    }
9563763c3f2SGreg Roach}
957