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