1<?php 2/** 3 * webtrees: online genealogy 4 * Copyright (C) 2017 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() { 50 return false; 51 } 52 53 /** {@inheritdoc} */ 54 public function getTabContent() { 55 global $controller; 56 $EXPAND_HISTO_EVENTS = false; 57 58 $indifacts = []; 59 // The individual’s own facts 60 foreach ($controller->record->getFacts() as $fact) { 61 switch ($fact->getTag()) { 62 case 'SEX': 63 case 'NAME': 64 case 'SOUR': 65 case 'OBJE': 66 case 'NOTE': 67 case 'FAMC': 68 case 'FAMS': 69 break; 70 default: 71 if (!array_key_exists('extra_info', Module::getActiveSidebars($controller->record->getTree())) || !ExtraInformationModule::showFact($fact)) { 72 $indifacts[] = $fact; 73 } 74 break; 75 } 76 } 77 78 // Add spouse-family facts 79 foreach ($controller->record->getSpouseFamilies() as $family) { 80 foreach ($family->getFacts() as $fact) { 81 switch ($fact->getTag()) { 82 case 'SOUR': 83 case 'NOTE': 84 case 'OBJE': 85 case 'CHAN': 86 case '_UID': 87 case 'RIN': 88 case 'HUSB': 89 case 'WIFE': 90 case 'CHIL': 91 break; 92 default: 93 $indifacts[] = $fact; 94 break; 95 } 96 } 97 $spouse = $family->getSpouse($controller->record); 98 if ($spouse) { 99 foreach (self::spouseFacts($controller->record, $spouse) as $fact) { 100 $indifacts[] = $fact; 101 } 102 } 103 foreach (self::childFacts($controller->record, $family, '_CHIL', '') as $fact) { 104 $indifacts[] = $fact; 105 } 106 } 107 108 foreach (self::parentFacts($controller->record, 1) as $fact) { 109 $indifacts[] = $fact; 110 } 111 foreach (self::historicalFacts($controller->record) as $fact) { 112 $indifacts[] = $fact; 113 } 114 foreach (self::associateFacts($controller->record) as $fact) { 115 $indifacts[] = $fact; 116 } 117 118 Functions::sortFacts($indifacts); 119 120 ob_start(); 121 ?> 122 <table class="facts_table"> 123 <colgroup> 124 <col class="width20"> 125 <col class="width80"> 126 </colgroup> 127 <tbody> 128 <tr> 129 <td colspan="2" class="descriptionbox"> 130 <?php if ($controller->record->getTree()->getPreference('SHOW_RELATIVES_EVENTS')) : ?> 131 <label> 132 <input id="show-relatives-facts" type="checkbox"> 133 <?= I18N::translate('Events of close relatives') ?> 134 </label> 135 <?php endif ?> 136 <?php if (file_exists(Site::getPreference('INDEX_DIRECTORY') . 'histo.' . WT_LOCALE . '.php')) : ?> 137 <label> 138 <input id="show-historical-facts" type="checkbox"> 139 <?= I18N::translate('Historical facts') ?> 140 </label> 141 <?php endif ?> 142 </td> 143 </tr> 144 <?php 145 146 if (!$indifacts) { 147 echo '<tr><td colspan="2" class="facts_value">', I18N::translate('There are no facts for this individual.'), '</td></tr>'; 148 } 149 150 foreach ($indifacts as $fact) { 151 FunctionsPrintFacts::printFact($fact, $controller->record); 152 } 153 154 //-- new fact link 155 if ($controller->record->canEdit()) { 156 FunctionsPrint::printAddNewFact($controller->record->getXref(), $indifacts, 'INDI'); 157 } 158 159 ?> 160 </tbody> 161 </table> 162 <script> 163 //persistent_toggle("show-relatives-facts", "tr.rela"); 164 //persistent_toggle("show-historical-facts", "tr.histo"); 165 </script> 166 <?php 167 168 return '<div id="' . $this->getName() . '_content">' . ob_get_clean() . '</div>'; 169 } 170 171 /** {@inheritdoc} */ 172 public function hasTabContent() { 173 return true; 174 } 175 176 /** {@inheritdoc} */ 177 public function canLoadAjax() { 178 return false; 179 } 180 181 /** {@inheritdoc} */ 182 public function getPreLoadContent() { 183 return ''; 184 } 185 186 /** 187 * Spouse facts that are shown on an individual’s page. 188 * 189 * @param Individual $individual Show events that occured during the lifetime of this individual 190 * @param Individual $spouse Show events of this individual 191 * 192 * @return Fact[] 193 */ 194 private static function spouseFacts(Individual $individual, Individual $spouse) { 195 $SHOW_RELATIVES_EVENTS = $individual->getTree()->getPreference('SHOW_RELATIVES_EVENTS'); 196 197 $facts = []; 198 if (strstr($SHOW_RELATIVES_EVENTS, '_DEAT_SPOU')) { 199 // Only include events between birth and death 200 $birt_date = $individual->getEstimatedBirthDate(); 201 $deat_date = $individual->getEstimatedDeathDate(); 202 203 foreach ($spouse->getFacts(WT_EVENTS_DEAT) as $fact) { 204 205 $fact_date = $fact->getDate(); 206 if ($fact_date->isOK() && Date::compare($birt_date, $fact_date) <= 0 && Date::compare($fact_date, $deat_date) <= 0) { 207 // Convert the event to a close relatives event. 208 $rela_fact = clone($fact); 209 $rela_fact->setTag('_' . $fact->getTag() . '_SPOU'); 210 $facts[] = $rela_fact; 211 } 212 } 213 } 214 215 return $facts; 216 } 217 218 /** 219 * Get the events of children and grandchildren. 220 * 221 * @param Individual $person 222 * @param Family $family 223 * @param string $option 224 * @param string $relation 225 * 226 * @return Fact[] 227 */ 228 private static function childFacts(Individual $person, Family $family, $option, $relation) { 229 global $controller; 230 231 $SHOW_RELATIVES_EVENTS = $person->getTree()->getPreference('SHOW_RELATIVES_EVENTS'); 232 233 $facts = []; 234 235 // Only include events between birth and death 236 $birt_date = $controller->record->getEstimatedBirthDate(); 237 $deat_date = $controller->record->getEstimatedDeathDate(); 238 239 // Deal with recursion. 240 switch ($option) { 241 case '_CHIL': 242 // Add grandchildren 243 foreach ($family->getChildren() as $child) { 244 foreach ($child->getSpouseFamilies() as $cfamily) { 245 switch ($child->getSex()) { 246 case 'M': 247 foreach (self::childFacts($person, $cfamily, '_GCHI', 'son') as $fact) { 248 $facts[] = $fact; 249 } 250 break; 251 case 'F': 252 foreach (self::childFacts($person, $cfamily, '_GCHI', 'dau') as $fact) { 253 $facts[] = $fact; 254 } 255 break; 256 default: 257 foreach (self::childFacts($person, $cfamily, '_GCHI', 'chi') as $fact) { 258 $facts[] = $fact; 259 } 260 break; 261 } 262 } 263 } 264 break; 265 } 266 267 // For each child in the family 268 foreach ($family->getChildren() as $child) { 269 if ($child->getXref() == $person->getXref()) { 270 // We are not our own sibling! 271 continue; 272 } 273 // add child’s birth 274 if (strpos($SHOW_RELATIVES_EVENTS, '_BIRT' . str_replace('_HSIB', '_SIBL', $option)) !== false) { 275 foreach ($child->getFacts(WT_EVENTS_BIRT) as $fact) { 276 $sgdate = $fact->getDate(); 277 // Always show _BIRT_CHIL, even if the dates are not known 278 if ($option == '_CHIL' || $sgdate->isOK() && Date::compare($birt_date, $sgdate) <= 0 && Date::compare($sgdate, $deat_date) <= 0) { 279 if ($option == '_GCHI' && $relation == 'dau') { 280 // Convert the event to a close relatives event. 281 $rela_fact = clone($fact); 282 $rela_fact->setTag('_' . $fact->getTag() . '_GCH1'); 283 $facts[] = $rela_fact; 284 } elseif ($option == '_GCHI' && $relation == 'son') { 285 // Convert the event to a close relatives event. 286 $rela_fact = clone($fact); 287 $rela_fact->setTag('_' . $fact->getTag() . '_GCH2'); 288 $facts[] = $rela_fact; 289 } else { 290 // Convert the event to a close relatives event. 291 $rela_fact = clone($fact); 292 $rela_fact->setTag('_' . $fact->getTag() . $option); 293 $facts[] = $rela_fact; 294 } 295 } 296 } 297 } 298 // add child’s death 299 if (strpos($SHOW_RELATIVES_EVENTS, '_DEAT' . str_replace('_HSIB', '_SIBL', $option)) !== false) { 300 foreach ($child->getFacts(WT_EVENTS_DEAT) as $fact) { 301 $sgdate = $fact->getDate(); 302 if ($sgdate->isOK() && Date::compare($birt_date, $sgdate) <= 0 && Date::compare($sgdate, $deat_date) <= 0) { 303 if ($option == '_GCHI' && $relation == 'dau') { 304 // Convert the event to a close relatives event. 305 $rela_fact = clone($fact); 306 $rela_fact->setTag('_' . $fact->getTag() . '_GCH1'); 307 $facts[] = $rela_fact; 308 } elseif ($option == '_GCHI' && $relation == 'son') { 309 // Convert the event to a close relatives event. 310 $rela_fact = clone($fact); 311 $rela_fact->setTag('_' . $fact->getTag() . '_GCH2'); 312 $facts[] = $rela_fact; 313 } else { 314 // Convert the event to a close relatives event. 315 $rela_fact = clone($fact); 316 $rela_fact->setTag('_' . $fact->getTag() . $option); 317 $facts[] = $rela_fact; 318 } 319 } 320 } 321 } 322 // add child’s marriage 323 if (strstr($SHOW_RELATIVES_EVENTS, '_MARR' . str_replace('_HSIB', '_SIBL', $option))) { 324 foreach ($child->getSpouseFamilies() as $sfamily) { 325 foreach ($sfamily->getFacts(WT_EVENTS_MARR) as $fact) { 326 $sgdate = $fact->getDate(); 327 if ($sgdate->isOK() && Date::compare($birt_date, $sgdate) <= 0 && Date::compare($sgdate, $deat_date) <= 0) { 328 if ($option == '_GCHI' && $relation == 'dau') { 329 // Convert the event to a close relatives event. 330 $rela_fact = clone($fact); 331 $rela_fact->setTag('_' . $fact->getTag() . '_GCH1'); 332 $facts[] = $rela_fact; 333 } elseif ($option == '_GCHI' && $relation == 'son') { 334 // Convert the event to a close relatives event. 335 $rela_fact = clone($fact); 336 $rela_fact->setTag('_' . $fact->getTag() . '_GCH2'); 337 $facts[] = $rela_fact; 338 } else { 339 // Convert the event to a close relatives event. 340 $rela_fact = clone($fact); 341 $rela_fact->setTag('_' . $fact->getTag() . $option); 342 $facts[] = $rela_fact; 343 } 344 } 345 } 346 } 347 } 348 } 349 350 return $facts; 351 } 352 353 /** 354 * Get the events of parents and grandparents. 355 * 356 * @param Individual $person 357 * @param int $sosa 358 * 359 * @return Fact[] 360 */ 361 private static function parentFacts(Individual $person, $sosa) { 362 global $controller; 363 364 $SHOW_RELATIVES_EVENTS = $person->getTree()->getPreference('SHOW_RELATIVES_EVENTS'); 365 366 $facts = []; 367 368 // Only include events between birth and death 369 $birt_date = $controller->record->getEstimatedBirthDate(); 370 $deat_date = $controller->record->getEstimatedDeathDate(); 371 372 if ($sosa == 1) { 373 foreach ($person->getChildFamilies() as $family) { 374 // Add siblings 375 foreach (self::childFacts($person, $family, '_SIBL', '') as $fact) { 376 $facts[] = $fact; 377 } 378 foreach ($family->getSpouses() as $spouse) { 379 foreach ($spouse->getSpouseFamilies() as $sfamily) { 380 if ($family !== $sfamily) { 381 // Add half-siblings 382 foreach (self::childFacts($person, $sfamily, '_HSIB', '') as $fact) { 383 $facts[] = $fact; 384 } 385 } 386 } 387 // Add grandparents 388 foreach (self::parentFacts($spouse, $spouse->getSex() == 'F' ? 3 : 2) as $fact) { 389 $facts[] = $fact; 390 } 391 } 392 } 393 394 if (strstr($SHOW_RELATIVES_EVENTS, '_MARR_PARE')) { 395 // add father/mother marriages 396 foreach ($person->getChildFamilies() as $sfamily) { 397 foreach ($sfamily->getFacts(WT_EVENTS_MARR) as $fact) { 398 if ($fact->getDate()->isOK() && Date::compare($birt_date, $fact->getDate()) <= 0 && Date::compare($fact->getDate(), $deat_date) <= 0) { 399 // marriage of parents (to each other) 400 $rela_fact = clone($fact); 401 $rela_fact->setTag('_' . $fact->getTag() . '_FAMC'); 402 $facts[] = $rela_fact; 403 } 404 } 405 } 406 foreach ($person->getChildStepFamilies() as $sfamily) { 407 foreach ($sfamily->getFacts(WT_EVENTS_MARR) as $fact) { 408 if ($fact->getDate()->isOK() && Date::compare($birt_date, $fact->getDate()) <= 0 && Date::compare($fact->getDate(), $deat_date) <= 0) { 409 // marriage of a parent (to another spouse) 410 // Convert the event to a close relatives event 411 $rela_fact = clone($fact); 412 $rela_fact->setTag('_' . $fact->getTag() . '_PARE'); 413 $facts[] = $rela_fact; 414 } 415 } 416 } 417 } 418 } 419 420 foreach ($person->getChildFamilies() as $family) { 421 foreach ($family->getSpouses() as $parent) { 422 if (strstr($SHOW_RELATIVES_EVENTS, '_DEAT' . ($sosa == 1 ? '_PARE' : '_GPAR'))) { 423 foreach ($parent->getFacts(WT_EVENTS_DEAT) as $fact) { 424 if ($fact->getDate()->isOK() && Date::compare($birt_date, $fact->getDate()) <= 0 && Date::compare($fact->getDate(), $deat_date) <= 0) { 425 switch ($sosa) { 426 case 1: 427 // Convert the event to a close relatives event. 428 $rela_fact = clone($fact); 429 $rela_fact->setTag('_' . $fact->getTag() . '_PARE'); 430 $facts[] = $rela_fact; 431 break; 432 case 2: 433 // Convert the event to a close relatives event 434 $rela_fact = clone($fact); 435 $rela_fact->setTag('_' . $fact->getTag() . '_GPA1'); 436 $facts[] = $rela_fact; 437 break; 438 case 3: 439 // Convert the event to a close relatives event 440 $rela_fact = clone($fact); 441 $rela_fact->setTag('_' . $fact->getTag() . '_GPA2'); 442 $facts[] = $rela_fact; 443 break; 444 } 445 } 446 } 447 } 448 } 449 } 450 451 return $facts; 452 } 453 454 /** 455 * Get any historical events. 456 * 457 * @param Individual $person 458 * 459 * @return Fact[] 460 */ 461 private static function historicalFacts(Individual $person) { 462 $SHOW_RELATIVES_EVENTS = $person->getTree()->getPreference('SHOW_RELATIVES_EVENTS'); 463 464 $facts = []; 465 466 if ($SHOW_RELATIVES_EVENTS) { 467 // Only include events between birth and death 468 $birt_date = $person->getEstimatedBirthDate(); 469 $deat_date = $person->getEstimatedDeathDate(); 470 471 if (file_exists(Site::getPreference('INDEX_DIRECTORY') . 'histo.' . WT_LOCALE . '.php')) { 472 $histo = []; 473 require Site::getPreference('INDEX_DIRECTORY') . 'histo.' . WT_LOCALE . '.php'; 474 foreach ($histo as $hist) { 475 $fact = new Fact($hist, $person, 'histo'); 476 $sdate = $fact->getDate(); 477 if ($sdate->isOK() && Date::compare($birt_date, $sdate) <= 0 && Date::compare($sdate, $deat_date) <= 0) { 478 $facts[] = $fact; 479 } 480 } 481 } 482 } 483 484 return $facts; 485 } 486 487 /** 488 * Get the events of associates. 489 * 490 * @param Individual $person 491 * 492 * @return Fact[] 493 */ 494 private static function associateFacts(Individual $person) { 495 $facts = []; 496 497 $associates = array_merge( 498 $person->linkedIndividuals('ASSO'), 499 $person->linkedIndividuals('_ASSO'), 500 $person->linkedFamilies('ASSO'), 501 $person->linkedFamilies('_ASSO') 502 ); 503 foreach ($associates as $associate) { 504 foreach ($associate->getFacts() as $fact) { 505 $arec = $fact->getAttribute('_ASSO'); 506 if (!$arec) { 507 $arec = $fact->getAttribute('ASSO'); 508 } 509 if ($arec && trim($arec, '@') === $person->getXref()) { 510 // Extract the important details from the fact 511 $factrec = '1 ' . $fact->getTag(); 512 if (preg_match('/\n2 DATE .*/', $fact->getGedcom(), $match)) { 513 $factrec .= $match[0]; 514 } 515 if (preg_match('/\n2 PLAC .*/', $fact->getGedcom(), $match)) { 516 $factrec .= $match[0]; 517 } 518 if ($associate instanceof Family) { 519 foreach ($associate->getSpouses() as $spouse) { 520 $factrec .= "\n2 _ASSO @" . $spouse->getXref() . '@'; 521 } 522 } else { 523 $factrec .= "\n2 _ASSO @" . $associate->getXref() . '@'; 524 } 525 $facts[] = new Fact($factrec, $associate, 'asso'); 526 } 527 } 528 } 529 530 return $facts; 531 } 532} 533