xref: /webtrees/app/Module/IndividualFactsTabModule.php (revision 911f5683b68340b4bad2bbca6e1ab7a3a15f46a7)
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('modules/personal_facts/tab', [
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		$SHOW_RELATIVES_EVENTS = $person->getTree()->getPreference('SHOW_RELATIVES_EVENTS');
182
183		$facts = [];
184
185		// Only include events between birth and death
186		$birt_date = $person->getEstimatedBirthDate();
187		$deat_date = $person->getEstimatedDeathDate();
188
189		// Deal with recursion.
190		switch ($option) {
191			case '_CHIL':
192				// Add grandchildren
193				foreach ($family->getChildren() as $child) {
194					foreach ($child->getSpouseFamilies() as $cfamily) {
195						switch ($child->getSex()) {
196							case 'M':
197								foreach (self::childFacts($person, $cfamily, '_GCHI', 'son') as $fact) {
198									$facts[] = $fact;
199								}
200								break;
201							case 'F':
202								foreach (self::childFacts($person, $cfamily, '_GCHI', 'dau') as $fact) {
203									$facts[] = $fact;
204								}
205								break;
206							default:
207								foreach (self::childFacts($person, $cfamily, '_GCHI', 'chi') as $fact) {
208									$facts[] = $fact;
209								}
210								break;
211						}
212				}
213			}
214			break;
215		}
216
217		// For each child in the family
218		foreach ($family->getChildren() as $child) {
219			if ($child->getXref() == $person->getXref()) {
220				// We are not our own sibling!
221				continue;
222			}
223			// add child’s birth
224			if (strpos($SHOW_RELATIVES_EVENTS, '_BIRT' . str_replace('_HSIB', '_SIBL', $option)) !== false) {
225				foreach ($child->getFacts(WT_EVENTS_BIRT) as $fact) {
226					$sgdate = $fact->getDate();
227					// Always show _BIRT_CHIL, even if the dates are not known
228					if ($option == '_CHIL' || $sgdate->isOK() && Date::compare($birt_date, $sgdate) <= 0 && Date::compare($sgdate, $deat_date) <= 0) {
229						if ($option == '_GCHI' && $relation == 'dau') {
230							// Convert the event to a close relatives event.
231							$rela_fact = clone($fact);
232							$rela_fact->setTag('_' . $fact->getTag() . '_GCH1');
233							$facts[] = $rela_fact;
234						} elseif ($option == '_GCHI' && $relation == 'son') {
235							// Convert the event to a close relatives event.
236							$rela_fact = clone($fact);
237							$rela_fact->setTag('_' . $fact->getTag() . '_GCH2');
238							$facts[] = $rela_fact;
239						} else {
240							// Convert the event to a close relatives event.
241							$rela_fact = clone($fact);
242							$rela_fact->setTag('_' . $fact->getTag() . $option);
243							$facts[] = $rela_fact;
244						}
245					}
246				}
247			}
248			// add child’s death
249			if (strpos($SHOW_RELATIVES_EVENTS, '_DEAT' . str_replace('_HSIB', '_SIBL', $option)) !== false) {
250				foreach ($child->getFacts(WT_EVENTS_DEAT) as $fact) {
251					$sgdate = $fact->getDate();
252					if ($sgdate->isOK() && Date::compare($birt_date, $sgdate) <= 0 && Date::compare($sgdate, $deat_date) <= 0) {
253						if ($option == '_GCHI' && $relation == 'dau') {
254							// Convert the event to a close relatives event.
255							$rela_fact = clone($fact);
256							$rela_fact->setTag('_' . $fact->getTag() . '_GCH1');
257							$facts[] = $rela_fact;
258						} elseif ($option == '_GCHI' && $relation == 'son') {
259							// Convert the event to a close relatives event.
260							$rela_fact = clone($fact);
261							$rela_fact->setTag('_' . $fact->getTag() . '_GCH2');
262							$facts[] = $rela_fact;
263						} else {
264							// Convert the event to a close relatives event.
265							$rela_fact = clone($fact);
266							$rela_fact->setTag('_' . $fact->getTag() . $option);
267							$facts[] = $rela_fact;
268						}
269					}
270				}
271			}
272			// add child’s marriage
273			if (strstr($SHOW_RELATIVES_EVENTS, '_MARR' . str_replace('_HSIB', '_SIBL', $option))) {
274				foreach ($child->getSpouseFamilies() as $sfamily) {
275					foreach ($sfamily->getFacts(WT_EVENTS_MARR) as $fact) {
276						$sgdate = $fact->getDate();
277						if ($sgdate->isOK() && Date::compare($birt_date, $sgdate) <= 0 && Date::compare($sgdate, $deat_date) <= 0) {
278							if ($option == '_GCHI' && $relation == 'dau') {
279								// Convert the event to a close relatives event.
280								$rela_fact = clone($fact);
281								$rela_fact->setTag('_' . $fact->getTag() . '_GCH1');
282								$facts[] = $rela_fact;
283							} elseif ($option == '_GCHI' && $relation == 'son') {
284								// Convert the event to a close relatives event.
285								$rela_fact = clone($fact);
286								$rela_fact->setTag('_' . $fact->getTag() . '_GCH2');
287								$facts[] = $rela_fact;
288							} else {
289								// Convert the event to a close relatives event.
290								$rela_fact = clone($fact);
291								$rela_fact->setTag('_' . $fact->getTag() . $option);
292								$facts[] = $rela_fact;
293							}
294						}
295					}
296				}
297			}
298		}
299
300		return $facts;
301	}
302
303	/**
304	 * Get the events of parents and grandparents.
305	 *
306	 * @param Individual $person
307	 * @param int        $sosa
308	 *
309	 * @return Fact[]
310	 */
311	private static function parentFacts(Individual $person, $sosa) {
312		$SHOW_RELATIVES_EVENTS = $person->getTree()->getPreference('SHOW_RELATIVES_EVENTS');
313
314		$facts = [];
315
316		// Only include events between birth and death
317		$birt_date = $person->getEstimatedBirthDate();
318		$deat_date = $person->getEstimatedDeathDate();
319
320		if ($sosa == 1) {
321			foreach ($person->getChildFamilies() as $family) {
322				// Add siblings
323				foreach (self::childFacts($person, $family, '_SIBL', '') as $fact) {
324					$facts[] = $fact;
325				}
326				foreach ($family->getSpouses() as $spouse) {
327					foreach ($spouse->getSpouseFamilies() as $sfamily) {
328						if ($family !== $sfamily) {
329							// Add half-siblings
330							foreach (self::childFacts($person, $sfamily, '_HSIB', '') as $fact) {
331								$facts[] = $fact;
332							}
333						}
334					}
335					// Add grandparents
336					foreach (self::parentFacts($spouse, $spouse->getSex() == 'F' ? 3 : 2) as $fact) {
337						$facts[] = $fact;
338					}
339				}
340			}
341
342			if (strstr($SHOW_RELATIVES_EVENTS, '_MARR_PARE')) {
343				// add father/mother marriages
344				foreach ($person->getChildFamilies() as $sfamily) {
345					foreach ($sfamily->getFacts(WT_EVENTS_MARR) as $fact) {
346						if ($fact->getDate()->isOK() && Date::compare($birt_date, $fact->getDate()) <= 0 && Date::compare($fact->getDate(), $deat_date) <= 0) {
347							// marriage of parents (to each other)
348							$rela_fact = clone($fact);
349							$rela_fact->setTag('_' . $fact->getTag() . '_FAMC');
350							$facts[] = $rela_fact;
351						}
352					}
353				}
354				foreach ($person->getChildStepFamilies() as $sfamily) {
355					foreach ($sfamily->getFacts(WT_EVENTS_MARR) as $fact) {
356						if ($fact->getDate()->isOK() && Date::compare($birt_date, $fact->getDate()) <= 0 && Date::compare($fact->getDate(), $deat_date) <= 0) {
357							// marriage of a parent (to another spouse)
358							// Convert the event to a close relatives event
359							$rela_fact = clone($fact);
360							$rela_fact->setTag('_' . $fact->getTag() . '_PARE');
361							$facts[] = $rela_fact;
362						}
363					}
364				}
365			}
366		}
367
368		foreach ($person->getChildFamilies() as $family) {
369			foreach ($family->getSpouses() as $parent) {
370				if (strstr($SHOW_RELATIVES_EVENTS, '_DEAT' . ($sosa == 1 ? '_PARE' : '_GPAR'))) {
371					foreach ($parent->getFacts(WT_EVENTS_DEAT) as $fact) {
372						if ($fact->getDate()->isOK() && Date::compare($birt_date, $fact->getDate()) <= 0 && Date::compare($fact->getDate(), $deat_date) <= 0) {
373							switch ($sosa) {
374								case 1:
375									// Convert the event to a close relatives event.
376									$rela_fact = clone($fact);
377									$rela_fact->setTag('_' . $fact->getTag() . '_PARE');
378									$facts[] = $rela_fact;
379									break;
380								case 2:
381									// Convert the event to a close relatives event
382									$rela_fact = clone($fact);
383									$rela_fact->setTag('_' . $fact->getTag() . '_GPA1');
384									$facts[] = $rela_fact;
385									break;
386								case 3:
387									// Convert the event to a close relatives event
388									$rela_fact = clone($fact);
389									$rela_fact->setTag('_' . $fact->getTag() . '_GPA2');
390									$facts[] = $rela_fact;
391									break;
392							}
393						}
394					}
395				}
396			}
397		}
398
399		return $facts;
400	}
401
402	/**
403	 * Get any historical events.
404	 *
405	 * @param Individual $person
406	 *
407	 * @return Fact[]
408	 */
409	private static function historicalFacts(Individual $person) {
410		$SHOW_RELATIVES_EVENTS = $person->getTree()->getPreference('SHOW_RELATIVES_EVENTS');
411
412		$facts = [];
413
414		if ($SHOW_RELATIVES_EVENTS) {
415			// Only include events between birth and death
416			$birt_date = $person->getEstimatedBirthDate();
417			$deat_date = $person->getEstimatedDeathDate();
418
419			if (file_exists(Site::getPreference('INDEX_DIRECTORY') . 'histo.' . WT_LOCALE . '.php')) {
420				$histo = [];
421				require Site::getPreference('INDEX_DIRECTORY') . 'histo.' . WT_LOCALE . '.php';
422				foreach ($histo as $hist) {
423					$fact  = new Fact($hist, $person, 'histo');
424					$sdate = $fact->getDate();
425					if ($sdate->isOK() && Date::compare($birt_date, $sdate) <= 0 && Date::compare($sdate, $deat_date) <= 0) {
426						$facts[] = $fact;
427					}
428				}
429			}
430		}
431
432		return $facts;
433	}
434
435	/**
436	 * Get the events of associates.
437	 *
438	 * @param Individual $person
439	 *
440	 * @return Fact[]
441	 */
442	private static function associateFacts(Individual $person) {
443		$facts = [];
444
445		$associates = array_merge(
446			$person->linkedIndividuals('ASSO'),
447			$person->linkedIndividuals('_ASSO'),
448			$person->linkedFamilies('ASSO'),
449			$person->linkedFamilies('_ASSO')
450		);
451		foreach ($associates as $associate) {
452			foreach ($associate->getFacts() as $fact) {
453				$arec = $fact->getAttribute('_ASSO');
454				if (!$arec) {
455					$arec = $fact->getAttribute('ASSO');
456				}
457				if ($arec && trim($arec, '@') === $person->getXref()) {
458					// Extract the important details from the fact
459					$factrec = '1 ' . $fact->getTag();
460					if (preg_match('/\n2 DATE .*/', $fact->getGedcom(), $match)) {
461						$factrec .= $match[0];
462					}
463					if (preg_match('/\n2 PLAC .*/', $fact->getGedcom(), $match)) {
464						$factrec .= $match[0];
465					}
466					if ($associate instanceof Family) {
467						foreach ($associate->getSpouses() as $spouse) {
468							$factrec .= "\n2 _ASSO @" . $spouse->getXref() . '@';
469						}
470					} else {
471						$factrec .= "\n2 _ASSO @" . $associate->getXref() . '@';
472					}
473					$facts[] = new Fact($factrec, $associate, 'asso');
474				}
475			}
476		}
477
478		return $facts;
479	}
480}
481