13763c3f2SGreg Roach<?php 23976b470SGreg Roach 33763c3f2SGreg Roach/** 43763c3f2SGreg Roach * webtrees: online genealogy 58fcd0d32SGreg Roach * Copyright (C) 2019 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; 268d0ebef0SGreg Roachuse Fisharebest\Webtrees\Gedcom; 270e62c4b8SGreg Roachuse Fisharebest\Webtrees\I18N; 280e62c4b8SGreg Roachuse Fisharebest\Webtrees\Individual; 292adcbd9aSGreg Roachuse Fisharebest\Webtrees\Services\ClipboardService; 304ca7e03cSGreg Roachuse Fisharebest\Webtrees\Services\ModuleService; 3117c50b57SGreg Roachuse Illuminate\Support\Collection; 323763c3f2SGreg Roach 333763c3f2SGreg Roach/** 343763c3f2SGreg Roach * Class IndividualFactsTabModule 353763c3f2SGreg Roach */ 3637eb8894SGreg Roachclass IndividualFactsTabModule extends AbstractModule implements ModuleTabInterface 37c1010edaSGreg Roach{ 3849a243cbSGreg Roach use ModuleTabTrait; 3949a243cbSGreg Roach 402adcbd9aSGreg Roach /** @var ModuleService */ 414ca7e03cSGreg Roach private $module_service; 424ca7e03cSGreg Roach 432adcbd9aSGreg Roach /** @var ClipboardService */ 442adcbd9aSGreg Roach private $clipboard_service; 452adcbd9aSGreg Roach 464ca7e03cSGreg Roach /** 474ca7e03cSGreg Roach * UserWelcomeModule constructor. 484ca7e03cSGreg Roach * 494ca7e03cSGreg Roach * @param ModuleService $module_service 502adcbd9aSGreg Roach * @param ClipboardService $clipboard_service 514ca7e03cSGreg Roach */ 522adcbd9aSGreg Roach public function __construct(ModuleService $module_service, ClipboardService $clipboard_service) 535bdbe281SGreg Roach { 544ca7e03cSGreg Roach $this->module_service = $module_service; 552adcbd9aSGreg Roach $this->clipboard_service = $clipboard_service; 564ca7e03cSGreg Roach } 574ca7e03cSGreg Roach 584ca7e03cSGreg Roach /** 590cfd6963SGreg Roach * How should this module be identified in the control panel, etc.? 60961ec755SGreg Roach * 61961ec755SGreg Roach * @return string 62961ec755SGreg Roach */ 6349a243cbSGreg Roach public function title(): string 64c1010edaSGreg Roach { 65bbb76c12SGreg Roach /* I18N: Name of a module/tab on the individual page. */ 66bbb76c12SGreg Roach return I18N::translate('Facts and events'); 673763c3f2SGreg Roach } 683763c3f2SGreg Roach 69961ec755SGreg Roach /** 70961ec755SGreg Roach * A sentence describing what this module does. 71961ec755SGreg Roach * 72961ec755SGreg Roach * @return string 73961ec755SGreg Roach */ 7449a243cbSGreg Roach public function description(): string 75c1010edaSGreg Roach { 76bbb76c12SGreg Roach /* I18N: Description of the “Facts and events” module */ 77bbb76c12SGreg Roach return I18N::translate('A tab showing the facts and events of an individual.'); 783763c3f2SGreg Roach } 793763c3f2SGreg Roach 8049a243cbSGreg Roach /** 8149a243cbSGreg Roach * The default position for this tab. It can be changed in the control panel. 8249a243cbSGreg Roach * 8349a243cbSGreg Roach * @return int 8449a243cbSGreg Roach */ 85cbf4b7faSGreg Roach public function defaultTabOrder(): int 86cbf4b7faSGreg Roach { 87fb7a0427SGreg Roach return 1; 883763c3f2SGreg Roach } 893763c3f2SGreg Roach 903caaa4d2SGreg Roach /** 913caaa4d2SGreg Roach * A greyed out tab has no actual content, but may perhaps have 923caaa4d2SGreg Roach * options to create content. 933caaa4d2SGreg Roach * 943caaa4d2SGreg Roach * @param Individual $individual 953caaa4d2SGreg Roach * 963caaa4d2SGreg Roach * @return bool 973caaa4d2SGreg Roach */ 988f53f488SRico Sonntag public function isGrayedOut(Individual $individual): bool 99c1010edaSGreg Roach { 1003763c3f2SGreg Roach return false; 1013763c3f2SGreg Roach } 1023763c3f2SGreg Roach 1033caaa4d2SGreg Roach /** 1043caaa4d2SGreg Roach * Generate the HTML content of this tab. 1053caaa4d2SGreg Roach * 1063caaa4d2SGreg Roach * @param Individual $individual 1073caaa4d2SGreg Roach * 1083caaa4d2SGreg Roach * @return string 1093caaa4d2SGreg Roach */ 1109b34404bSGreg Roach public function getTabContent(Individual $individual): string 111c1010edaSGreg Roach { 112ee727175SGreg Roach // Only include events of close relatives that are between birth and death 113ee727175SGreg Roach $min_date = $individual->getEstimatedBirthDate(); 114ee727175SGreg Roach $max_date = $individual->getEstimatedDeathDate(); 115ee727175SGreg Roach 1168eaf8709SGreg Roach // Which facts and events are handled by other modules? 1178eaf8709SGreg Roach $sidebar_facts = $this->module_service 11887cca37cSGreg Roach ->findByComponent(ModuleSidebarInterface::class, $individual->tree(), Auth::user()) 1190b5fd0a6SGreg Roach ->map(static function (ModuleSidebarInterface $sidebar): Collection { 1208eaf8709SGreg Roach return $sidebar->supportedFacts(); 1218eaf8709SGreg Roach }); 1228eaf8709SGreg Roach 1238eaf8709SGreg Roach $tab_facts = $this->module_service 12487cca37cSGreg Roach ->findByComponent(ModuleTabInterface::class, $individual->tree(), Auth::user()) 1250b5fd0a6SGreg Roach ->map(static function (ModuleTabInterface $sidebar): Collection { 1268eaf8709SGreg Roach return $sidebar->supportedFacts(); 1278eaf8709SGreg Roach }); 1288eaf8709SGreg Roach 1298eaf8709SGreg Roach $exclude_facts = $sidebar_facts->merge($tab_facts)->flatten(); 1308eaf8709SGreg Roach 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 { 13539ca88baSGreg 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) { 1418eaf8709SGreg 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(), 1702adcbd9aSGreg Roach 'clipboard_facts' => $this->clipboard_service->pastableFacts($individual, $exclude_facts), 171*ddeb3354SGreg 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 /** 2163763c3f2SGreg Roach * Spouse facts that are shown on an individual’s page. 2173763c3f2SGreg Roach * 2183763c3f2SGreg Roach * @param Individual $individual Show events that occured during the lifetime of this individual 2193763c3f2SGreg Roach * @param Individual $spouse Show events of this individual 220ee727175SGreg Roach * @param Date $min_date 221ee727175SGreg Roach * @param Date $max_date 2223763c3f2SGreg Roach * 2233763c3f2SGreg Roach * @return Fact[] 2243763c3f2SGreg Roach */ 2258b9cfadbSGreg Roach private function spouseFacts(Individual $individual, Individual $spouse, Date $min_date, Date $max_date): array 226c1010edaSGreg Roach { 227f4afa648SGreg Roach $SHOW_RELATIVES_EVENTS = $individual->tree()->getPreference('SHOW_RELATIVES_EVENTS'); 2283763c3f2SGreg Roach 22913abd6f3SGreg Roach $facts = []; 2303763c3f2SGreg Roach if (strstr($SHOW_RELATIVES_EVENTS, '_DEAT_SPOU')) { 2318d0ebef0SGreg Roach foreach ($spouse->facts(Gedcom::DEATH_EVENTS) as $fact) { 2328b9cfadbSGreg Roach if ($this->includeFact($fact, $min_date, $max_date)) { 2333763c3f2SGreg Roach // Convert the event to a close relatives event. 234e364afe4SGreg Roach $rela_fact = clone $fact; 2353763c3f2SGreg Roach $rela_fact->setTag('_' . $fact->getTag() . '_SPOU'); 2363763c3f2SGreg Roach $facts[] = $rela_fact; 2373763c3f2SGreg Roach } 2383763c3f2SGreg Roach } 2393763c3f2SGreg Roach } 2403763c3f2SGreg Roach 2413763c3f2SGreg Roach return $facts; 2423763c3f2SGreg Roach } 2433763c3f2SGreg Roach 2443763c3f2SGreg Roach /** 2453763c3f2SGreg Roach * Get the events of children and grandchildren. 2463763c3f2SGreg Roach * 2473763c3f2SGreg Roach * @param Individual $person 2483763c3f2SGreg Roach * @param Family $family 2493763c3f2SGreg Roach * @param string $option 2503763c3f2SGreg Roach * @param string $relation 251ee727175SGreg Roach * @param Date $min_date 252ee727175SGreg Roach * @param Date $max_date 2533763c3f2SGreg Roach * 2543763c3f2SGreg Roach * @return Fact[] 2553763c3f2SGreg Roach */ 2568b9cfadbSGreg Roach private function childFacts(Individual $person, Family $family, $option, $relation, Date $min_date, Date $max_date): array 257c1010edaSGreg Roach { 258f4afa648SGreg Roach $SHOW_RELATIVES_EVENTS = $person->tree()->getPreference('SHOW_RELATIVES_EVENTS'); 2593763c3f2SGreg Roach 26013abd6f3SGreg Roach $facts = []; 2613763c3f2SGreg Roach 2623763c3f2SGreg Roach // Deal with recursion. 2633763c3f2SGreg Roach switch ($option) { 2643763c3f2SGreg Roach case '_CHIL': 2653763c3f2SGreg Roach // Add grandchildren 26639ca88baSGreg Roach foreach ($family->children() as $child) { 26739ca88baSGreg Roach foreach ($child->spouseFamilies() as $cfamily) { 26839ca88baSGreg Roach switch ($child->sex()) { 2693763c3f2SGreg Roach case 'M': 2708b9cfadbSGreg Roach foreach ($this->childFacts($person, $cfamily, '_GCHI', 'son', $min_date, $max_date) as $fact) { 2713763c3f2SGreg Roach $facts[] = $fact; 2723763c3f2SGreg Roach } 2733763c3f2SGreg Roach break; 2743763c3f2SGreg Roach case 'F': 2758b9cfadbSGreg Roach foreach ($this->childFacts($person, $cfamily, '_GCHI', 'dau', $min_date, $max_date) as $fact) { 2763763c3f2SGreg Roach $facts[] = $fact; 2773763c3f2SGreg Roach } 2783763c3f2SGreg Roach break; 2793763c3f2SGreg Roach default: 2808b9cfadbSGreg Roach foreach ($this->childFacts($person, $cfamily, '_GCHI', 'chi', $min_date, $max_date) as $fact) { 2813763c3f2SGreg Roach $facts[] = $fact; 2823763c3f2SGreg Roach } 2833763c3f2SGreg Roach break; 2843763c3f2SGreg Roach } 2853763c3f2SGreg Roach } 2863763c3f2SGreg Roach } 2873763c3f2SGreg Roach break; 2883763c3f2SGreg Roach } 2893763c3f2SGreg Roach 2903763c3f2SGreg Roach // For each child in the family 29139ca88baSGreg Roach foreach ($family->children() as $child) { 29222d65e5aSGreg Roach if ($child->xref() === $person->xref()) { 2933763c3f2SGreg Roach // We are not our own sibling! 2943763c3f2SGreg Roach continue; 2953763c3f2SGreg Roach } 2963763c3f2SGreg Roach // add child’s birth 2973763c3f2SGreg Roach if (strpos($SHOW_RELATIVES_EVENTS, '_BIRT' . str_replace('_HSIB', '_SIBL', $option)) !== false) { 2988d0ebef0SGreg Roach foreach ($child->facts(Gedcom::BIRTH_EVENTS) as $fact) { 2993763c3f2SGreg Roach // Always show _BIRT_CHIL, even if the dates are not known 30022d65e5aSGreg Roach if ($option === '_CHIL' || $this->includeFact($fact, $min_date, $max_date)) { 30122d65e5aSGreg Roach if ($option === '_GCHI' && $relation === 'dau') { 3023763c3f2SGreg Roach // Convert the event to a close relatives event. 303e364afe4SGreg Roach $rela_fact = clone $fact; 3043763c3f2SGreg Roach $rela_fact->setTag('_' . $fact->getTag() . '_GCH1'); 3053763c3f2SGreg Roach $facts[] = $rela_fact; 30622d65e5aSGreg Roach } elseif ($option === '_GCHI' && $relation === 'son') { 3073763c3f2SGreg Roach // Convert the event to a close relatives event. 308e364afe4SGreg Roach $rela_fact = clone $fact; 3093763c3f2SGreg Roach $rela_fact->setTag('_' . $fact->getTag() . '_GCH2'); 3103763c3f2SGreg Roach $facts[] = $rela_fact; 3113763c3f2SGreg Roach } else { 3123763c3f2SGreg Roach // Convert the event to a close relatives event. 313e364afe4SGreg Roach $rela_fact = clone $fact; 3143763c3f2SGreg Roach $rela_fact->setTag('_' . $fact->getTag() . $option); 3153763c3f2SGreg Roach $facts[] = $rela_fact; 3163763c3f2SGreg Roach } 3173763c3f2SGreg Roach } 3183763c3f2SGreg Roach } 3193763c3f2SGreg Roach } 3203763c3f2SGreg Roach // add child’s death 3213763c3f2SGreg Roach if (strpos($SHOW_RELATIVES_EVENTS, '_DEAT' . str_replace('_HSIB', '_SIBL', $option)) !== false) { 3228d0ebef0SGreg Roach foreach ($child->facts(Gedcom::DEATH_EVENTS) as $fact) { 3238b9cfadbSGreg Roach if ($this->includeFact($fact, $min_date, $max_date)) { 32422d65e5aSGreg Roach if ($option === '_GCHI' && $relation === 'dau') { 3253763c3f2SGreg Roach // Convert the event to a close relatives event. 326e364afe4SGreg Roach $rela_fact = clone $fact; 3273763c3f2SGreg Roach $rela_fact->setTag('_' . $fact->getTag() . '_GCH1'); 3283763c3f2SGreg Roach $facts[] = $rela_fact; 32922d65e5aSGreg Roach } elseif ($option === '_GCHI' && $relation === 'son') { 3303763c3f2SGreg Roach // Convert the event to a close relatives event. 331e364afe4SGreg Roach $rela_fact = clone $fact; 3323763c3f2SGreg Roach $rela_fact->setTag('_' . $fact->getTag() . '_GCH2'); 3333763c3f2SGreg Roach $facts[] = $rela_fact; 3343763c3f2SGreg Roach } else { 3353763c3f2SGreg Roach // Convert the event to a close relatives event. 336e364afe4SGreg Roach $rela_fact = clone $fact; 3373763c3f2SGreg Roach $rela_fact->setTag('_' . $fact->getTag() . $option); 3383763c3f2SGreg Roach $facts[] = $rela_fact; 3393763c3f2SGreg Roach } 3403763c3f2SGreg Roach } 3413763c3f2SGreg Roach } 3423763c3f2SGreg Roach } 3433763c3f2SGreg Roach // add child’s marriage 3443763c3f2SGreg Roach if (strstr($SHOW_RELATIVES_EVENTS, '_MARR' . str_replace('_HSIB', '_SIBL', $option))) { 34539ca88baSGreg Roach foreach ($child->spouseFamilies() as $sfamily) { 3468d0ebef0SGreg Roach foreach ($sfamily->facts(['MARR']) as $fact) { 3478b9cfadbSGreg Roach if ($this->includeFact($fact, $min_date, $max_date)) { 34822d65e5aSGreg Roach if ($option === '_GCHI' && $relation === 'dau') { 3493763c3f2SGreg Roach // Convert the event to a close relatives event. 350e364afe4SGreg Roach $rela_fact = clone $fact; 3513763c3f2SGreg Roach $rela_fact->setTag('_' . $fact->getTag() . '_GCH1'); 3523763c3f2SGreg Roach $facts[] = $rela_fact; 35322d65e5aSGreg Roach } elseif ($option === '_GCHI' && $relation === 'son') { 3543763c3f2SGreg Roach // Convert the event to a close relatives event. 355e364afe4SGreg Roach $rela_fact = clone $fact; 3563763c3f2SGreg Roach $rela_fact->setTag('_' . $fact->getTag() . '_GCH2'); 3573763c3f2SGreg Roach $facts[] = $rela_fact; 3583763c3f2SGreg Roach } else { 3593763c3f2SGreg Roach // Convert the event to a close relatives event. 360e364afe4SGreg Roach $rela_fact = clone $fact; 3613763c3f2SGreg Roach $rela_fact->setTag('_' . $fact->getTag() . $option); 3623763c3f2SGreg Roach $facts[] = $rela_fact; 3633763c3f2SGreg Roach } 3643763c3f2SGreg Roach } 3653763c3f2SGreg Roach } 3663763c3f2SGreg Roach } 3673763c3f2SGreg Roach } 3683763c3f2SGreg Roach } 3693763c3f2SGreg Roach 3703763c3f2SGreg Roach return $facts; 3713763c3f2SGreg Roach } 3723763c3f2SGreg Roach 3733763c3f2SGreg Roach /** 3743763c3f2SGreg Roach * Get the events of parents and grandparents. 3753763c3f2SGreg Roach * 3763763c3f2SGreg Roach * @param Individual $person 377cbc1590aSGreg Roach * @param int $sosa 378ee727175SGreg Roach * @param Date $min_date 379ee727175SGreg Roach * @param Date $max_date 3803763c3f2SGreg Roach * 3813763c3f2SGreg Roach * @return Fact[] 3823763c3f2SGreg Roach */ 3838b9cfadbSGreg Roach private function parentFacts(Individual $person, $sosa, Date $min_date, Date $max_date): array 384c1010edaSGreg Roach { 385f4afa648SGreg Roach $SHOW_RELATIVES_EVENTS = $person->tree()->getPreference('SHOW_RELATIVES_EVENTS'); 3863763c3f2SGreg Roach 38713abd6f3SGreg Roach $facts = []; 3883763c3f2SGreg Roach 3893763c3f2SGreg Roach if ($sosa == 1) { 39039ca88baSGreg Roach foreach ($person->childFamilies() as $family) { 3913763c3f2SGreg Roach // Add siblings 3928b9cfadbSGreg Roach foreach ($this->childFacts($person, $family, '_SIBL', '', $min_date, $max_date) as $fact) { 3933763c3f2SGreg Roach $facts[] = $fact; 3943763c3f2SGreg Roach } 39539ca88baSGreg Roach foreach ($family->spouses() as $spouse) { 39639ca88baSGreg Roach foreach ($spouse->spouseFamilies() as $sfamily) { 3973763c3f2SGreg Roach if ($family !== $sfamily) { 3983763c3f2SGreg Roach // Add half-siblings 3998b9cfadbSGreg Roach foreach ($this->childFacts($person, $sfamily, '_HSIB', '', $min_date, $max_date) as $fact) { 4003763c3f2SGreg Roach $facts[] = $fact; 4013763c3f2SGreg Roach } 4023763c3f2SGreg Roach } 4033763c3f2SGreg Roach } 4043763c3f2SGreg Roach // Add grandparents 40522d65e5aSGreg Roach foreach ($this->parentFacts($spouse, $spouse->sex() === 'F' ? 3 : 2, $min_date, $max_date) as $fact) { 4063763c3f2SGreg Roach $facts[] = $fact; 4073763c3f2SGreg Roach } 4083763c3f2SGreg Roach } 4093763c3f2SGreg Roach } 4103763c3f2SGreg Roach 4113763c3f2SGreg Roach if (strstr($SHOW_RELATIVES_EVENTS, '_MARR_PARE')) { 4123763c3f2SGreg Roach // add father/mother marriages 41339ca88baSGreg Roach foreach ($person->childFamilies() as $sfamily) { 4148d0ebef0SGreg Roach foreach ($sfamily->facts(['MARR']) as $fact) { 4158b9cfadbSGreg Roach if ($this->includeFact($fact, $min_date, $max_date)) { 4163763c3f2SGreg Roach // marriage of parents (to each other) 417e364afe4SGreg Roach $rela_fact = clone $fact; 4183763c3f2SGreg Roach $rela_fact->setTag('_' . $fact->getTag() . '_FAMC'); 4193763c3f2SGreg Roach $facts[] = $rela_fact; 4203763c3f2SGreg Roach } 4213763c3f2SGreg Roach } 4223763c3f2SGreg Roach } 42339ca88baSGreg Roach foreach ($person->childStepFamilies() as $sfamily) { 4248d0ebef0SGreg Roach foreach ($sfamily->facts(['MARR']) as $fact) { 4258b9cfadbSGreg Roach if ($this->includeFact($fact, $min_date, $max_date)) { 4263763c3f2SGreg Roach // marriage of a parent (to another spouse) 4273763c3f2SGreg Roach // Convert the event to a close relatives event 428e364afe4SGreg Roach $rela_fact = clone $fact; 4293763c3f2SGreg Roach $rela_fact->setTag('_' . $fact->getTag() . '_PARE'); 4303763c3f2SGreg Roach $facts[] = $rela_fact; 4313763c3f2SGreg Roach } 4323763c3f2SGreg Roach } 4333763c3f2SGreg Roach } 4343763c3f2SGreg Roach } 4353763c3f2SGreg Roach } 4363763c3f2SGreg Roach 43739ca88baSGreg Roach foreach ($person->childFamilies() as $family) { 43839ca88baSGreg Roach foreach ($family->spouses() as $parent) { 4393763c3f2SGreg Roach if (strstr($SHOW_RELATIVES_EVENTS, '_DEAT' . ($sosa == 1 ? '_PARE' : '_GPAR'))) { 4408d0ebef0SGreg Roach foreach ($parent->facts(Gedcom::DEATH_EVENTS) as $fact) { 4418b9cfadbSGreg Roach if ($this->includeFact($fact, $min_date, $max_date)) { 4423763c3f2SGreg Roach switch ($sosa) { 4433763c3f2SGreg Roach case 1: 4443763c3f2SGreg Roach // Convert the event to a close relatives event. 445e364afe4SGreg Roach $rela_fact = clone $fact; 4463763c3f2SGreg Roach $rela_fact->setTag('_' . $fact->getTag() . '_PARE'); 4473763c3f2SGreg Roach $facts[] = $rela_fact; 4483763c3f2SGreg Roach break; 4493763c3f2SGreg Roach case 2: 4503763c3f2SGreg Roach // Convert the event to a close relatives event 451e364afe4SGreg Roach $rela_fact = clone $fact; 4523763c3f2SGreg Roach $rela_fact->setTag('_' . $fact->getTag() . '_GPA1'); 4533763c3f2SGreg Roach $facts[] = $rela_fact; 4543763c3f2SGreg Roach break; 4553763c3f2SGreg Roach case 3: 4563763c3f2SGreg Roach // Convert the event to a close relatives event 457e364afe4SGreg Roach $rela_fact = clone $fact; 4583763c3f2SGreg Roach $rela_fact->setTag('_' . $fact->getTag() . '_GPA2'); 4593763c3f2SGreg Roach $facts[] = $rela_fact; 4603763c3f2SGreg Roach break; 4613763c3f2SGreg Roach } 4623763c3f2SGreg Roach } 4633763c3f2SGreg Roach } 4643763c3f2SGreg Roach } 4653763c3f2SGreg Roach } 4663763c3f2SGreg Roach } 4673763c3f2SGreg Roach 4683763c3f2SGreg Roach return $facts; 4693763c3f2SGreg Roach } 4703763c3f2SGreg Roach 4713763c3f2SGreg Roach /** 4723763c3f2SGreg Roach * Get any historical events. 4733763c3f2SGreg Roach * 47417c50b57SGreg Roach * @param Individual $individual 4753763c3f2SGreg Roach * 4763763c3f2SGreg Roach * @return Fact[] 4773763c3f2SGreg Roach */ 4784ca7e03cSGreg Roach private function historicalFacts(Individual $individual): array 479c1010edaSGreg Roach { 4804ca7e03cSGreg Roach return $this->module_service->findByInterface(ModuleHistoricEventsInterface::class) 4810b5fd0a6SGreg Roach ->map(static function (ModuleHistoricEventsInterface $module) use ($individual): Collection { 48217c50b57SGreg Roach return $module->historicEventsForIndividual($individual); 48317c50b57SGreg Roach }) 48417c50b57SGreg Roach ->flatten() 48517c50b57SGreg Roach ->all(); 4863763c3f2SGreg Roach } 4873763c3f2SGreg Roach 4883763c3f2SGreg Roach /** 4893763c3f2SGreg Roach * Get the events of associates. 4903763c3f2SGreg Roach * 4913763c3f2SGreg Roach * @param Individual $person 4923763c3f2SGreg Roach * 4933763c3f2SGreg Roach * @return Fact[] 4943763c3f2SGreg Roach */ 4958b9cfadbSGreg Roach private function associateFacts(Individual $person): array 496c1010edaSGreg Roach { 49713abd6f3SGreg Roach $facts = []; 4983763c3f2SGreg Roach 499ee727175SGreg Roach /** @var Individual[] $associates */ 500907c1109SGreg Roach $asso1 = $person->linkedIndividuals('ASSO'); 501907c1109SGreg Roach $asso2 = $person->linkedIndividuals('_ASSO'); 502907c1109SGreg Roach $asso3 = $person->linkedFamilies('ASSO'); 503907c1109SGreg Roach $asso4 = $person->linkedFamilies('_ASSO'); 504907c1109SGreg Roach 505907c1109SGreg Roach $associates = $asso1->merge($asso2)->merge($asso3)->merge($asso4); 506907c1109SGreg Roach 5073763c3f2SGreg Roach foreach ($associates as $associate) { 50830158ae7SGreg Roach foreach ($associate->facts() as $fact) { 509907c1109SGreg Roach if (preg_match('/\n\d _?ASSO @' . $person->xref() . '@/', $fact->gedcom())) { 5103763c3f2SGreg Roach // Extract the important details from the fact 5113763c3f2SGreg Roach $factrec = '1 ' . $fact->getTag(); 512138ca96cSGreg Roach if (preg_match('/\n2 DATE .*/', $fact->gedcom(), $match)) { 5133763c3f2SGreg Roach $factrec .= $match[0]; 5143763c3f2SGreg Roach } 515138ca96cSGreg Roach if (preg_match('/\n2 PLAC .*/', $fact->gedcom(), $match)) { 5163763c3f2SGreg Roach $factrec .= $match[0]; 5173763c3f2SGreg Roach } 5183763c3f2SGreg Roach if ($associate instanceof Family) { 51939ca88baSGreg Roach foreach ($associate->spouses() as $spouse) { 520c0935879SGreg Roach $factrec .= "\n2 _ASSO @" . $spouse->xref() . '@'; 5213763c3f2SGreg Roach } 5223763c3f2SGreg Roach } else { 523c0935879SGreg Roach $factrec .= "\n2 _ASSO @" . $associate->xref() . '@'; 5243763c3f2SGreg Roach } 5253763c3f2SGreg Roach $facts[] = new Fact($factrec, $associate, 'asso'); 5263763c3f2SGreg Roach } 5273763c3f2SGreg Roach } 5283763c3f2SGreg Roach } 5293763c3f2SGreg Roach 5303763c3f2SGreg Roach return $facts; 5313763c3f2SGreg Roach } 5328eaf8709SGreg Roach 5338eaf8709SGreg Roach /** 5348eaf8709SGreg Roach * This module handles the following facts - so don't show them on the "Facts and events" tab. 5358eaf8709SGreg Roach * 53654c7f8dfSGreg Roach * @return Collection 5378eaf8709SGreg Roach */ 5388eaf8709SGreg Roach public function supportedFacts(): Collection 5398eaf8709SGreg Roach { 5408eaf8709SGreg Roach // We don't actually displaye these facts, but they are displayed 5418eaf8709SGreg Roach // outside the tabs/sidebar systems. This just forces them to be excluded here. 5428eaf8709SGreg Roach return new Collection(['NAME', 'SEX']); 5438eaf8709SGreg Roach } 5443763c3f2SGreg Roach} 545