xref: /webtrees/app/Module/IndividualFactsTabModule.php (revision d2681c37325a35ab01be82034f4afd3b58010fb8)
1<?php
2/**
3 * webtrees: online genealogy
4 * Copyright (C) 2018 webtrees development team
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16namespace Fisharebest\Webtrees\Module;
17
18use Fisharebest\Webtrees\Date;
19use Fisharebest\Webtrees\Fact;
20use Fisharebest\Webtrees\Family;
21use Fisharebest\Webtrees\Functions\Functions;
22use Fisharebest\Webtrees\Functions\FunctionsPrint;
23use Fisharebest\Webtrees\Functions\FunctionsPrintFacts;
24use Fisharebest\Webtrees\I18N;
25use Fisharebest\Webtrees\Individual;
26use Fisharebest\Webtrees\Module;
27use Fisharebest\Webtrees\Site;
28
29/**
30 * Class IndividualFactsTabModule
31 */
32class IndividualFactsTabModule extends AbstractModule implements ModuleTabInterface {
33	/** {@inheritdoc} */
34	public function getTitle() {
35		return /* I18N: Name of a module/tab on the individual page. */ I18N::translate('Facts and events');
36	}
37
38	/** {@inheritdoc} */
39	public function getDescription() {
40		return /* I18N: Description of the “Facts and events” module */ I18N::translate('A tab showing the facts and events of an individual.');
41	}
42
43	/** {@inheritdoc} */
44	public function defaultTabOrder() {
45		return 10;
46	}
47
48	/** {@inheritdoc} */
49	public function isGrayedOut(Individual $individual) {
50		return false;
51	}
52
53	/** {@inheritdoc} */
54	public function getTabContent(Individual $individual) {
55		$indifacts = [];
56		// The individual’s own facts
57		foreach ($individual->getFacts() as $fact) {
58			switch ($fact->getTag()) {
59				case 'SEX':
60				case 'NAME':
61				case 'SOUR':
62				case 'OBJE':
63				case 'NOTE':
64				case 'FAMC':
65				case 'FAMS':
66					break;
67				default:
68					if (!array_key_exists('extra_info', Module::getActiveSidebars($individual->getTree())) || !ExtraInformationModule::showFact($fact)) {
69						$indifacts[] = $fact;
70					}
71					break;
72			}
73		}
74
75		// Add spouse-family facts
76		foreach ($individual->getSpouseFamilies() as $family) {
77			foreach ($family->getFacts() as $fact) {
78				switch ($fact->getTag()) {
79					case 'SOUR':
80					case 'NOTE':
81					case 'OBJE':
82					case 'CHAN':
83					case '_UID':
84					case 'RIN':
85					case 'HUSB':
86					case 'WIFE':
87					case 'CHIL':
88						break;
89					default:
90						$indifacts[] = $fact;
91						break;
92				}
93			}
94			$spouse = $family->getSpouse($individual);
95			if ($spouse) {
96				foreach (self::spouseFacts($individual, $spouse) as $fact) {
97					$indifacts[] = $fact;
98				}
99			}
100			foreach (self::childFacts($individual, $family, '_CHIL', '') as $fact) {
101				$indifacts[] = $fact;
102			}
103		}
104
105		foreach (self::parentFacts($individual, 1) as $fact) {
106			$indifacts[] = $fact;
107		}
108
109		foreach (self::associateFacts($individual) as $fact) {
110			$indifacts[] = $fact;
111		}
112
113		$has_historical_facts = false;
114		foreach (self::historicalFacts($individual) as $fact) {
115			$has_historical_facts = true;
116			$indifacts[] = $fact;
117		}
118
119		Functions::sortFacts($indifacts);
120
121		return view('tabs/facts', [
122			'can_edit'             => $individual->canEdit(),
123			'has_historical_facts' => $has_historical_facts,
124			'individual'           => $individual,
125			'facts'                => $indifacts,
126		]);
127	}
128
129	/** {@inheritdoc} */
130	public function hasTabContent(Individual $individual) {
131		return true;
132	}
133
134	/** {@inheritdoc} */
135	public function canLoadAjax() {
136		return false;
137	}
138
139	/**
140	 * Spouse facts that are shown on an individual’s page.
141	 *
142	 * @param Individual $individual Show events that occured during the lifetime of this individual
143	 * @param Individual $spouse     Show events of this individual
144	 *
145	 * @return Fact[]
146	 */
147	private static function spouseFacts(Individual $individual, Individual $spouse) {
148		$SHOW_RELATIVES_EVENTS = $individual->getTree()->getPreference('SHOW_RELATIVES_EVENTS');
149
150		$facts = [];
151		if (strstr($SHOW_RELATIVES_EVENTS, '_DEAT_SPOU')) {
152			// Only include events between birth and death
153			$birt_date = $individual->getEstimatedBirthDate();
154			$deat_date = $individual->getEstimatedDeathDate();
155
156			foreach ($spouse->getFacts(WT_EVENTS_DEAT) as $fact) {
157				$fact_date = $fact->getDate();
158				if ($fact_date->isOK() && Date::compare($birt_date, $fact_date) <= 0 && Date::compare($fact_date, $deat_date) <= 0) {
159					// Convert the event to a close relatives event.
160					$rela_fact = clone($fact);
161					$rela_fact->setTag('_' . $fact->getTag() . '_SPOU');
162					$facts[] = $rela_fact;
163				}
164			}
165		}
166
167		return $facts;
168	}
169
170	/**
171	 * Get the events of children and grandchildren.
172	 *
173	 * @param Individual $person
174	 * @param Family     $family
175	 * @param string     $option
176	 * @param string     $relation
177	 *
178	 * @return Fact[]
179	 */
180	private static function childFacts(Individual $person, Family $family, $option, $relation) {
181		global $controller;
182
183		$SHOW_RELATIVES_EVENTS = $person->getTree()->getPreference('SHOW_RELATIVES_EVENTS');
184
185		$facts = [];
186
187		// Only include events between birth and death
188		$birt_date = $person->getEstimatedBirthDate();
189		$deat_date = $person->getEstimatedDeathDate();
190
191		// Deal with recursion.
192		switch ($option) {
193			case '_CHIL':
194				// Add grandchildren
195				foreach ($family->getChildren() as $child) {
196					foreach ($child->getSpouseFamilies() as $cfamily) {
197						switch ($child->getSex()) {
198							case 'M':
199								foreach (self::childFacts($person, $cfamily, '_GCHI', 'son') as $fact) {
200									$facts[] = $fact;
201								}
202								break;
203							case 'F':
204								foreach (self::childFacts($person, $cfamily, '_GCHI', 'dau') as $fact) {
205									$facts[] = $fact;
206								}
207								break;
208							default:
209								foreach (self::childFacts($person, $cfamily, '_GCHI', 'chi') as $fact) {
210									$facts[] = $fact;
211								}
212								break;
213						}
214				}
215			}
216			break;
217		}
218
219		// For each child in the family
220		foreach ($family->getChildren() as $child) {
221			if ($child->getXref() == $person->getXref()) {
222				// We are not our own sibling!
223				continue;
224			}
225			// add child’s birth
226			if (strpos($SHOW_RELATIVES_EVENTS, '_BIRT' . str_replace('_HSIB', '_SIBL', $option)) !== false) {
227				foreach ($child->getFacts(WT_EVENTS_BIRT) as $fact) {
228					$sgdate = $fact->getDate();
229					// Always show _BIRT_CHIL, even if the dates are not known
230					if ($option == '_CHIL' || $sgdate->isOK() && Date::compare($birt_date, $sgdate) <= 0 && Date::compare($sgdate, $deat_date) <= 0) {
231						if ($option == '_GCHI' && $relation == 'dau') {
232							// Convert the event to a close relatives event.
233							$rela_fact = clone($fact);
234							$rela_fact->setTag('_' . $fact->getTag() . '_GCH1');
235							$facts[] = $rela_fact;
236						} elseif ($option == '_GCHI' && $relation == 'son') {
237							// Convert the event to a close relatives event.
238							$rela_fact = clone($fact);
239							$rela_fact->setTag('_' . $fact->getTag() . '_GCH2');
240							$facts[] = $rela_fact;
241						} else {
242							// Convert the event to a close relatives event.
243							$rela_fact = clone($fact);
244							$rela_fact->setTag('_' . $fact->getTag() . $option);
245							$facts[] = $rela_fact;
246						}
247					}
248				}
249			}
250			// add child’s death
251			if (strpos($SHOW_RELATIVES_EVENTS, '_DEAT' . str_replace('_HSIB', '_SIBL', $option)) !== false) {
252				foreach ($child->getFacts(WT_EVENTS_DEAT) as $fact) {
253					$sgdate = $fact->getDate();
254					if ($sgdate->isOK() && Date::compare($birt_date, $sgdate) <= 0 && Date::compare($sgdate, $deat_date) <= 0) {
255						if ($option == '_GCHI' && $relation == 'dau') {
256							// Convert the event to a close relatives event.
257							$rela_fact = clone($fact);
258							$rela_fact->setTag('_' . $fact->getTag() . '_GCH1');
259							$facts[] = $rela_fact;
260						} elseif ($option == '_GCHI' && $relation == 'son') {
261							// Convert the event to a close relatives event.
262							$rela_fact = clone($fact);
263							$rela_fact->setTag('_' . $fact->getTag() . '_GCH2');
264							$facts[] = $rela_fact;
265						} else {
266							// Convert the event to a close relatives event.
267							$rela_fact = clone($fact);
268							$rela_fact->setTag('_' . $fact->getTag() . $option);
269							$facts[] = $rela_fact;
270						}
271					}
272				}
273			}
274			// add child’s marriage
275			if (strstr($SHOW_RELATIVES_EVENTS, '_MARR' . str_replace('_HSIB', '_SIBL', $option))) {
276				foreach ($child->getSpouseFamilies() as $sfamily) {
277					foreach ($sfamily->getFacts(WT_EVENTS_MARR) as $fact) {
278						$sgdate = $fact->getDate();
279						if ($sgdate->isOK() && Date::compare($birt_date, $sgdate) <= 0 && Date::compare($sgdate, $deat_date) <= 0) {
280							if ($option == '_GCHI' && $relation == 'dau') {
281								// Convert the event to a close relatives event.
282								$rela_fact = clone($fact);
283								$rela_fact->setTag('_' . $fact->getTag() . '_GCH1');
284								$facts[] = $rela_fact;
285							} elseif ($option == '_GCHI' && $relation == 'son') {
286								// Convert the event to a close relatives event.
287								$rela_fact = clone($fact);
288								$rela_fact->setTag('_' . $fact->getTag() . '_GCH2');
289								$facts[] = $rela_fact;
290							} else {
291								// Convert the event to a close relatives event.
292								$rela_fact = clone($fact);
293								$rela_fact->setTag('_' . $fact->getTag() . $option);
294								$facts[] = $rela_fact;
295							}
296						}
297					}
298				}
299			}
300		}
301
302		return $facts;
303	}
304
305	/**
306	 * Get the events of parents and grandparents.
307	 *
308	 * @param Individual $person
309	 * @param int        $sosa
310	 *
311	 * @return Fact[]
312	 */
313	private static function parentFacts(Individual $person, $sosa) {
314		global $controller;
315
316		$SHOW_RELATIVES_EVENTS = $person->getTree()->getPreference('SHOW_RELATIVES_EVENTS');
317
318		$facts = [];
319
320		// Only include events between birth and death
321		$birt_date = $person->getEstimatedBirthDate();
322		$deat_date = $person->getEstimatedDeathDate();
323
324		if ($sosa == 1) {
325			foreach ($person->getChildFamilies() as $family) {
326				// Add siblings
327				foreach (self::childFacts($person, $family, '_SIBL', '') as $fact) {
328					$facts[] = $fact;
329				}
330				foreach ($family->getSpouses() as $spouse) {
331					foreach ($spouse->getSpouseFamilies() as $sfamily) {
332						if ($family !== $sfamily) {
333							// Add half-siblings
334							foreach (self::childFacts($person, $sfamily, '_HSIB', '') as $fact) {
335								$facts[] = $fact;
336							}
337						}
338					}
339					// Add grandparents
340					foreach (self::parentFacts($spouse, $spouse->getSex() == 'F' ? 3 : 2) as $fact) {
341						$facts[] = $fact;
342					}
343				}
344			}
345
346			if (strstr($SHOW_RELATIVES_EVENTS, '_MARR_PARE')) {
347				// add father/mother marriages
348				foreach ($person->getChildFamilies() as $sfamily) {
349					foreach ($sfamily->getFacts(WT_EVENTS_MARR) as $fact) {
350						if ($fact->getDate()->isOK() && Date::compare($birt_date, $fact->getDate()) <= 0 && Date::compare($fact->getDate(), $deat_date) <= 0) {
351							// marriage of parents (to each other)
352							$rela_fact = clone($fact);
353							$rela_fact->setTag('_' . $fact->getTag() . '_FAMC');
354							$facts[] = $rela_fact;
355						}
356					}
357				}
358				foreach ($person->getChildStepFamilies() as $sfamily) {
359					foreach ($sfamily->getFacts(WT_EVENTS_MARR) as $fact) {
360						if ($fact->getDate()->isOK() && Date::compare($birt_date, $fact->getDate()) <= 0 && Date::compare($fact->getDate(), $deat_date) <= 0) {
361							// marriage of a parent (to another spouse)
362							// Convert the event to a close relatives event
363							$rela_fact = clone($fact);
364							$rela_fact->setTag('_' . $fact->getTag() . '_PARE');
365							$facts[] = $rela_fact;
366						}
367					}
368				}
369			}
370		}
371
372		foreach ($person->getChildFamilies() as $family) {
373			foreach ($family->getSpouses() as $parent) {
374				if (strstr($SHOW_RELATIVES_EVENTS, '_DEAT' . ($sosa == 1 ? '_PARE' : '_GPAR'))) {
375					foreach ($parent->getFacts(WT_EVENTS_DEAT) as $fact) {
376						if ($fact->getDate()->isOK() && Date::compare($birt_date, $fact->getDate()) <= 0 && Date::compare($fact->getDate(), $deat_date) <= 0) {
377							switch ($sosa) {
378								case 1:
379									// Convert the event to a close relatives event.
380									$rela_fact = clone($fact);
381									$rela_fact->setTag('_' . $fact->getTag() . '_PARE');
382									$facts[] = $rela_fact;
383									break;
384								case 2:
385									// Convert the event to a close relatives event
386									$rela_fact = clone($fact);
387									$rela_fact->setTag('_' . $fact->getTag() . '_GPA1');
388									$facts[] = $rela_fact;
389									break;
390								case 3:
391									// Convert the event to a close relatives event
392									$rela_fact = clone($fact);
393									$rela_fact->setTag('_' . $fact->getTag() . '_GPA2');
394									$facts[] = $rela_fact;
395									break;
396							}
397						}
398					}
399				}
400			}
401		}
402
403		return $facts;
404	}
405
406	/**
407	 * Get any historical events.
408	 *
409	 * @param Individual $person
410	 *
411	 * @return Fact[]
412	 */
413	private static function historicalFacts(Individual $person) {
414		$SHOW_RELATIVES_EVENTS = $person->getTree()->getPreference('SHOW_RELATIVES_EVENTS');
415
416		$facts = [];
417
418		if ($SHOW_RELATIVES_EVENTS) {
419			// Only include events between birth and death
420			$birt_date = $person->getEstimatedBirthDate();
421			$deat_date = $person->getEstimatedDeathDate();
422
423			if (file_exists(Site::getPreference('INDEX_DIRECTORY') . 'histo.' . WT_LOCALE . '.php')) {
424				$histo = [];
425				require Site::getPreference('INDEX_DIRECTORY') . 'histo.' . WT_LOCALE . '.php';
426				foreach ($histo as $hist) {
427					$fact  = new Fact($hist, $person, 'histo');
428					$sdate = $fact->getDate();
429					if ($sdate->isOK() && Date::compare($birt_date, $sdate) <= 0 && Date::compare($sdate, $deat_date) <= 0) {
430						$facts[] = $fact;
431					}
432				}
433			}
434		}
435
436		return $facts;
437	}
438
439	/**
440	 * Get the events of associates.
441	 *
442	 * @param Individual $person
443	 *
444	 * @return Fact[]
445	 */
446	private static function associateFacts(Individual $person) {
447		$facts = [];
448
449		$associates = array_merge(
450			$person->linkedIndividuals('ASSO'),
451			$person->linkedIndividuals('_ASSO'),
452			$person->linkedFamilies('ASSO'),
453			$person->linkedFamilies('_ASSO')
454		);
455		foreach ($associates as $associate) {
456			foreach ($associate->getFacts() as $fact) {
457				$arec = $fact->getAttribute('_ASSO');
458				if (!$arec) {
459					$arec = $fact->getAttribute('ASSO');
460				}
461				if ($arec && trim($arec, '@') === $person->getXref()) {
462					// Extract the important details from the fact
463					$factrec = '1 ' . $fact->getTag();
464					if (preg_match('/\n2 DATE .*/', $fact->getGedcom(), $match)) {
465						$factrec .= $match[0];
466					}
467					if (preg_match('/\n2 PLAC .*/', $fact->getGedcom(), $match)) {
468						$factrec .= $match[0];
469					}
470					if ($associate instanceof Family) {
471						foreach ($associate->getSpouses() as $spouse) {
472							$factrec .= "\n2 _ASSO @" . $spouse->getXref() . '@';
473						}
474					} else {
475						$factrec .= "\n2 _ASSO @" . $associate->getXref() . '@';
476					}
477					$facts[] = new Fact($factrec, $associate, 'asso');
478				}
479			}
480		}
481
482		return $facts;
483	}
484}
485