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