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