1a25f0a04SGreg Roach<?php 2a25f0a04SGreg Roach/** 3a25f0a04SGreg Roach * webtrees: online genealogy 41062a142SGreg Roach * Copyright (C) 2018 webtrees development team 5a25f0a04SGreg Roach * This program is free software: you can redistribute it and/or modify 6a25f0a04SGreg Roach * it under the terms of the GNU General Public License as published by 7a25f0a04SGreg Roach * the Free Software Foundation, either version 3 of the License, or 8a25f0a04SGreg Roach * (at your option) any later version. 9a25f0a04SGreg Roach * This program is distributed in the hope that it will be useful, 10a25f0a04SGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of 11a25f0a04SGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12a25f0a04SGreg Roach * GNU General Public License for more details. 13a25f0a04SGreg Roach * You should have received a copy of the GNU General Public License 14a25f0a04SGreg Roach * along with this program. If not, see <http://www.gnu.org/licenses/>. 15a25f0a04SGreg Roach */ 1676692c8bSGreg Roachnamespace Fisharebest\Webtrees; 1776692c8bSGreg Roach 183d7a8a4cSGreg Roachuse Fisharebest\Webtrees\Functions\FunctionsPrint; 198f5f5da8SGreg Roachuse InvalidArgumentException; 20a25f0a04SGreg Roach 21a25f0a04SGreg Roach/** 2276692c8bSGreg Roach * A GEDCOM fact or event object. 23a25f0a04SGreg Roach */ 24c1010edaSGreg Roachclass Fact 25c1010edaSGreg Roach{ 26*bbe4546fSGreg Roach const FACT_ORDER = [ 27*bbe4546fSGreg Roach 'BIRT', 28*bbe4546fSGreg Roach '_HNM', 29*bbe4546fSGreg Roach 'ALIA', 30*bbe4546fSGreg Roach '_AKA', 31*bbe4546fSGreg Roach '_AKAN', 32*bbe4546fSGreg Roach 'ADOP', 33*bbe4546fSGreg Roach '_ADPF', 34*bbe4546fSGreg Roach '_ADPF', 35*bbe4546fSGreg Roach '_BRTM', 36*bbe4546fSGreg Roach 'CHR', 37*bbe4546fSGreg Roach 'BAPM', 38*bbe4546fSGreg Roach 'FCOM', 39*bbe4546fSGreg Roach 'CONF', 40*bbe4546fSGreg Roach 'BARM', 41*bbe4546fSGreg Roach 'BASM', 42*bbe4546fSGreg Roach 'EDUC', 43*bbe4546fSGreg Roach 'GRAD', 44*bbe4546fSGreg Roach '_DEG', 45*bbe4546fSGreg Roach 'EMIG', 46*bbe4546fSGreg Roach 'IMMI', 47*bbe4546fSGreg Roach 'NATU', 48*bbe4546fSGreg Roach '_MILI', 49*bbe4546fSGreg Roach '_MILT', 50*bbe4546fSGreg Roach 'ENGA', 51*bbe4546fSGreg Roach 'MARB', 52*bbe4546fSGreg Roach 'MARC', 53*bbe4546fSGreg Roach 'MARL', 54*bbe4546fSGreg Roach '_MARI', 55*bbe4546fSGreg Roach '_MBON', 56*bbe4546fSGreg Roach 'MARR', 57*bbe4546fSGreg Roach 'MARR_CIVIL', 58*bbe4546fSGreg Roach 'MARR_RELIGIOUS', 59*bbe4546fSGreg Roach 'MARR_PARTNERS', 60*bbe4546fSGreg Roach 'MARR_UNKNOWN', 61*bbe4546fSGreg Roach '_COML', 62*bbe4546fSGreg Roach '_STAT', 63*bbe4546fSGreg Roach '_SEPR', 64*bbe4546fSGreg Roach 'DIVF', 65*bbe4546fSGreg Roach 'MARS', 66*bbe4546fSGreg Roach '_BIRT_CHIL', 67*bbe4546fSGreg Roach 'DIV', 68*bbe4546fSGreg Roach 'ANUL', 69*bbe4546fSGreg Roach '_BIRT_', 70*bbe4546fSGreg Roach '_MARR_', 71*bbe4546fSGreg Roach '_DEAT_', 72*bbe4546fSGreg Roach '_BURI_', 73*bbe4546fSGreg Roach 'CENS', 74*bbe4546fSGreg Roach 'OCCU', 75*bbe4546fSGreg Roach 'RESI', 76*bbe4546fSGreg Roach 'PROP', 77*bbe4546fSGreg Roach 'CHRA', 78*bbe4546fSGreg Roach 'RETI', 79*bbe4546fSGreg Roach 'FACT', 80*bbe4546fSGreg Roach 'EVEN', 81*bbe4546fSGreg Roach '_NMR', 82*bbe4546fSGreg Roach '_NMAR', 83*bbe4546fSGreg Roach 'NMR', 84*bbe4546fSGreg Roach 'NCHI', 85*bbe4546fSGreg Roach 'WILL', 86*bbe4546fSGreg Roach '_HOL', 87*bbe4546fSGreg Roach '_????_', 88*bbe4546fSGreg Roach 'DEAT', 89*bbe4546fSGreg Roach '_FNRL', 90*bbe4546fSGreg Roach 'CREM', 91*bbe4546fSGreg Roach 'BURI', 92*bbe4546fSGreg Roach '_INTE', 93*bbe4546fSGreg Roach '_YART', 94*bbe4546fSGreg Roach '_NLIV', 95*bbe4546fSGreg Roach 'PROB', 96*bbe4546fSGreg Roach 'TITL', 97*bbe4546fSGreg Roach 'COMM', 98*bbe4546fSGreg Roach 'NATI', 99*bbe4546fSGreg Roach 'CITN', 100*bbe4546fSGreg Roach 'CAST', 101*bbe4546fSGreg Roach 'RELI', 102*bbe4546fSGreg Roach 'SSN', 103*bbe4546fSGreg Roach 'IDNO', 104*bbe4546fSGreg Roach 'TEMP', 105*bbe4546fSGreg Roach 'SLGC', 106*bbe4546fSGreg Roach 'BAPL', 107*bbe4546fSGreg Roach 'CONL', 108*bbe4546fSGreg Roach 'ENDL', 109*bbe4546fSGreg Roach 'SLGS', 110*bbe4546fSGreg Roach 'ADDR', 111*bbe4546fSGreg Roach 'PHON', 112*bbe4546fSGreg Roach 'EMAIL', 113*bbe4546fSGreg Roach '_EMAIL', 114*bbe4546fSGreg Roach 'EMAL', 115*bbe4546fSGreg Roach 'FAX', 116*bbe4546fSGreg Roach 'WWW', 117*bbe4546fSGreg Roach 'URL', 118*bbe4546fSGreg Roach '_URL', 119*bbe4546fSGreg Roach 'AFN', 120*bbe4546fSGreg Roach 'REFN', 121*bbe4546fSGreg Roach '_PRMN', 122*bbe4546fSGreg Roach 'REF', 123*bbe4546fSGreg Roach 'RIN', 124*bbe4546fSGreg Roach '_UID', 125*bbe4546fSGreg Roach 'OBJE', 126*bbe4546fSGreg Roach 'NOTE', 127*bbe4546fSGreg Roach 'SOUR', 128*bbe4546fSGreg Roach 'CHAN', 129*bbe4546fSGreg Roach '_TODO', 130*bbe4546fSGreg Roach ]; 131*bbe4546fSGreg Roach 132a25f0a04SGreg Roach /** @var string Unique identifier for this fact (currently implemented as a hash of the raw data). */ 133a25f0a04SGreg Roach private $fact_id; 134a25f0a04SGreg Roach 135a25f0a04SGreg Roach /** @var GedcomRecord The GEDCOM record from which this fact is taken */ 136a25f0a04SGreg Roach private $parent; 137a25f0a04SGreg Roach 138a25f0a04SGreg Roach /** @var string The raw GEDCOM data for this fact */ 139a25f0a04SGreg Roach private $gedcom; 140a25f0a04SGreg Roach 141a25f0a04SGreg Roach /** @var string The GEDCOM tag for this record */ 142a25f0a04SGreg Roach private $tag; 143a25f0a04SGreg Roach 144cbc1590aSGreg Roach /** @var bool Is this a recently deleted fact, pending approval? */ 145a25f0a04SGreg Roach private $pending_deletion = false; 146a25f0a04SGreg Roach 147cbc1590aSGreg Roach /** @var bool Is this a recently added fact, pending approval? */ 148a25f0a04SGreg Roach private $pending_addition = false; 149a25f0a04SGreg Roach 150a25f0a04SGreg Roach /** @var Date The date of this fact, from the “2 DATE …” attribute */ 151a25f0a04SGreg Roach private $date; 152a25f0a04SGreg Roach 153a25f0a04SGreg Roach /** @var Place The place of this fact, from the “2 PLAC …” attribute */ 154a25f0a04SGreg Roach private $place; 155a25f0a04SGreg Roach 1563d7a8a4cSGreg Roach /** @var int Temporary(!) variable Used by Functions::sortFacts() */ 157a25f0a04SGreg Roach public $sortOrder; 158a25f0a04SGreg Roach 159a25f0a04SGreg Roach /** 160a25f0a04SGreg Roach * Create an event object from a gedcom fragment. 161a25f0a04SGreg Roach * We need the parent object (to check privacy) and a (pseudo) fact ID to 162a25f0a04SGreg Roach * identify the fact within the record. 163a25f0a04SGreg Roach * 164a25f0a04SGreg Roach * @param string $gedcom 165a25f0a04SGreg Roach * @param GedcomRecord $parent 166a25f0a04SGreg Roach * @param string $fact_id 167a25f0a04SGreg Roach * 1688f5f5da8SGreg Roach * @throws InvalidArgumentException 169a25f0a04SGreg Roach */ 170c1010edaSGreg Roach public function __construct($gedcom, GedcomRecord $parent, $fact_id) 171c1010edaSGreg Roach { 172a25f0a04SGreg Roach if (preg_match('/^1 (' . WT_REGEX_TAG . ')/', $gedcom, $match)) { 173a25f0a04SGreg Roach $this->gedcom = $gedcom; 174a25f0a04SGreg Roach $this->parent = $parent; 175a25f0a04SGreg Roach $this->fact_id = $fact_id; 176a25f0a04SGreg Roach $this->tag = $match[1]; 177a25f0a04SGreg Roach } else { 1788f5f5da8SGreg Roach throw new InvalidArgumentException('Invalid GEDCOM data passed to Fact::_construct(' . $gedcom . ')'); 179a25f0a04SGreg Roach } 180a25f0a04SGreg Roach } 181a25f0a04SGreg Roach 182a25f0a04SGreg Roach /** 183a25f0a04SGreg Roach * Get the value of level 1 data in the fact 184a25f0a04SGreg Roach * Allow for multi-line values 185a25f0a04SGreg Roach * 186baacc364SGreg Roach * @return string 187a25f0a04SGreg Roach */ 188c1010edaSGreg Roach public function getValue() 189c1010edaSGreg Roach { 190a25f0a04SGreg Roach if (preg_match('/^1 (?:' . $this->tag . ') ?(.*(?:(?:\n2 CONT ?.*)*))/', $this->gedcom, $match)) { 191a25f0a04SGreg Roach return preg_replace("/\n2 CONT ?/", "\n", $match[1]); 192a25f0a04SGreg Roach } else { 193baacc364SGreg Roach return ''; 194a25f0a04SGreg Roach } 195a25f0a04SGreg Roach } 196a25f0a04SGreg Roach 197a25f0a04SGreg Roach /** 198a25f0a04SGreg Roach * Get the record to which this fact links 199a25f0a04SGreg Roach * 200805a90eaSGreg Roach * @return Individual|Family|Source|Repository|Media|Note|null 201a25f0a04SGreg Roach */ 202c1010edaSGreg Roach public function getTarget() 203c1010edaSGreg Roach { 204a25f0a04SGreg Roach $xref = trim($this->getValue(), '@'); 205a25f0a04SGreg Roach switch ($this->tag) { 206a25f0a04SGreg Roach case 'FAMC': 207a25f0a04SGreg Roach case 'FAMS': 20824ec66ceSGreg Roach return Family::getInstance($xref, $this->getParent()->getTree()); 209a25f0a04SGreg Roach case 'HUSB': 210a25f0a04SGreg Roach case 'WIFE': 211a25f0a04SGreg Roach case 'CHIL': 21224ec66ceSGreg Roach return Individual::getInstance($xref, $this->getParent()->getTree()); 213a25f0a04SGreg Roach case 'SOUR': 21424ec66ceSGreg Roach return Source::getInstance($xref, $this->getParent()->getTree()); 215a25f0a04SGreg Roach case 'OBJE': 21624ec66ceSGreg Roach return Media::getInstance($xref, $this->getParent()->getTree()); 217a25f0a04SGreg Roach case 'REPO': 21824ec66ceSGreg Roach return Repository::getInstance($xref, $this->getParent()->getTree()); 219a25f0a04SGreg Roach case 'NOTE': 22024ec66ceSGreg Roach return Note::getInstance($xref, $this->getParent()->getTree()); 221a25f0a04SGreg Roach default: 22224ec66ceSGreg Roach return GedcomRecord::getInstance($xref, $this->getParent()->getTree()); 223a25f0a04SGreg Roach } 224a25f0a04SGreg Roach } 225a25f0a04SGreg Roach 226a25f0a04SGreg Roach /** 227a25f0a04SGreg Roach * Get the value of level 2 data in the fact 228a25f0a04SGreg Roach * 229a25f0a04SGreg Roach * @param string $tag 230a25f0a04SGreg Roach * 231baacc364SGreg Roach * @return string 232a25f0a04SGreg Roach */ 233c1010edaSGreg Roach public function getAttribute($tag) 234c1010edaSGreg Roach { 235a25f0a04SGreg Roach if (preg_match('/\n2 (?:' . $tag . ') ?(.*(?:(?:\n3 CONT ?.*)*)*)/', $this->gedcom, $match)) { 236a25f0a04SGreg Roach return preg_replace("/\n3 CONT ?/", "\n", $match[1]); 237a25f0a04SGreg Roach } else { 238baacc364SGreg Roach return ''; 239a25f0a04SGreg Roach } 240a25f0a04SGreg Roach } 241a25f0a04SGreg Roach 242a25f0a04SGreg Roach /** 243a25f0a04SGreg Roach * Do the privacy rules allow us to display this fact to the current user 244a25f0a04SGreg Roach * 245cbc1590aSGreg Roach * @param int|null $access_level 246a25f0a04SGreg Roach * 247cbc1590aSGreg Roach * @return bool 248a25f0a04SGreg Roach */ 249c1010edaSGreg Roach public function canShow($access_level = null) 250c1010edaSGreg Roach { 2514b9ff166SGreg Roach if ($access_level === null) { 2524b9ff166SGreg Roach $access_level = Auth::accessLevel($this->getParent()->getTree()); 2534b9ff166SGreg Roach } 2544b9ff166SGreg Roach 255a25f0a04SGreg Roach // Does this record have an explicit RESN? 256a25f0a04SGreg Roach if (strpos($this->gedcom, "\n2 RESN confidential")) { 2574b9ff166SGreg Roach return Auth::PRIV_NONE >= $access_level; 258a25f0a04SGreg Roach } 259a25f0a04SGreg Roach if (strpos($this->gedcom, "\n2 RESN privacy")) { 2604b9ff166SGreg Roach return Auth::PRIV_USER >= $access_level; 261a25f0a04SGreg Roach } 262a25f0a04SGreg Roach if (strpos($this->gedcom, "\n2 RESN none")) { 263a25f0a04SGreg Roach return true; 264a25f0a04SGreg Roach } 265a25f0a04SGreg Roach 266a25f0a04SGreg Roach // Does this record have a default RESN? 267a25f0a04SGreg Roach $xref = $this->parent->getXref(); 268518bbdc1SGreg Roach $fact_privacy = $this->parent->getTree()->getFactPrivacy(); 269518bbdc1SGreg Roach $individual_fact_privacy = $this->parent->getTree()->getIndividualFactPrivacy(); 270518bbdc1SGreg Roach if (isset($individual_fact_privacy[$xref][$this->tag])) { 271518bbdc1SGreg Roach return $individual_fact_privacy[$xref][$this->tag] >= $access_level; 272a25f0a04SGreg Roach } 273518bbdc1SGreg Roach if (isset($fact_privacy[$this->tag])) { 274518bbdc1SGreg Roach return $fact_privacy[$this->tag] >= $access_level; 275a25f0a04SGreg Roach } 276a25f0a04SGreg Roach 277a25f0a04SGreg Roach // No restrictions - it must be public 278a25f0a04SGreg Roach return true; 279a25f0a04SGreg Roach } 280a25f0a04SGreg Roach 281a25f0a04SGreg Roach /** 282a25f0a04SGreg Roach * Check whether this fact is protected against edit 283a25f0a04SGreg Roach * 284cbc1590aSGreg Roach * @return bool 285a25f0a04SGreg Roach */ 286c1010edaSGreg Roach public function canEdit() 287c1010edaSGreg Roach { 288a25f0a04SGreg Roach // Managers can edit anything 289a25f0a04SGreg Roach // Members cannot edit RESN, CHAN and locked records 290a25f0a04SGreg Roach return 291a25f0a04SGreg Roach $this->parent->canEdit() && !$this->isPendingDeletion() && ( 2924b9ff166SGreg Roach Auth::isManager($this->parent->getTree()) || 2934b9ff166SGreg Roach Auth::isEditor($this->parent->getTree()) && strpos($this->gedcom, "\n2 RESN locked") === false && $this->getTag() != 'RESN' && $this->getTag() != 'CHAN' 294a25f0a04SGreg Roach ); 295a25f0a04SGreg Roach } 296a25f0a04SGreg Roach 297a25f0a04SGreg Roach /** 298a25f0a04SGreg Roach * The place where the event occured. 299a25f0a04SGreg Roach * 300a25f0a04SGreg Roach * @return Place 301a25f0a04SGreg Roach */ 302c1010edaSGreg Roach public function getPlace() 303c1010edaSGreg Roach { 304a25f0a04SGreg Roach if ($this->place === null) { 30584caa210SGreg Roach $this->place = new Place($this->getAttribute('PLAC'), $this->getParent()->getTree()); 306a25f0a04SGreg Roach } 307a25f0a04SGreg Roach 308a25f0a04SGreg Roach return $this->place; 309a25f0a04SGreg Roach } 310a25f0a04SGreg Roach 311a25f0a04SGreg Roach /** 312a25f0a04SGreg Roach * Get the date for this fact. 313a25f0a04SGreg Roach * We can call this function many times, especially when sorting, 314a25f0a04SGreg Roach * so keep a copy of the date. 315a25f0a04SGreg Roach * 316a25f0a04SGreg Roach * @return Date 317a25f0a04SGreg Roach */ 318c1010edaSGreg Roach public function getDate() 319c1010edaSGreg Roach { 320a25f0a04SGreg Roach if ($this->date === null) { 321a25f0a04SGreg Roach $this->date = new Date($this->getAttribute('DATE')); 322a25f0a04SGreg Roach } 323a25f0a04SGreg Roach 324a25f0a04SGreg Roach return $this->date; 325a25f0a04SGreg Roach } 326a25f0a04SGreg Roach 327a25f0a04SGreg Roach /** 328a25f0a04SGreg Roach * The raw GEDCOM data for this fact 329a25f0a04SGreg Roach * 330a25f0a04SGreg Roach * @return string 331a25f0a04SGreg Roach */ 332c1010edaSGreg Roach public function getGedcom() 333c1010edaSGreg Roach { 334a25f0a04SGreg Roach return $this->gedcom; 335a25f0a04SGreg Roach } 336a25f0a04SGreg Roach 337a25f0a04SGreg Roach /** 338a25f0a04SGreg Roach * Get a (pseudo) primary key for this fact. 339a25f0a04SGreg Roach * 340a25f0a04SGreg Roach * @return string 341a25f0a04SGreg Roach */ 342c1010edaSGreg Roach public function getFactId() 343c1010edaSGreg Roach { 344a25f0a04SGreg Roach return $this->fact_id; 345a25f0a04SGreg Roach } 346a25f0a04SGreg Roach 347a25f0a04SGreg Roach /** 348a25f0a04SGreg Roach * What is the tag (type) of this fact, such as BIRT, MARR or DEAT. 349a25f0a04SGreg Roach * 350a25f0a04SGreg Roach * @return string 351a25f0a04SGreg Roach */ 352c1010edaSGreg Roach public function getTag() 353c1010edaSGreg Roach { 354a25f0a04SGreg Roach return $this->tag; 355a25f0a04SGreg Roach } 356a25f0a04SGreg Roach 357a25f0a04SGreg Roach /** 358a25f0a04SGreg Roach * Used to convert a real fact (e.g. BIRT) into a close-relative’s fact (e.g. _BIRT_CHIL) 359a25f0a04SGreg Roach * 360a25f0a04SGreg Roach * @param string $tag 361a25f0a04SGreg Roach */ 362c1010edaSGreg Roach public function setTag($tag) 363c1010edaSGreg Roach { 364a25f0a04SGreg Roach $this->tag = $tag; 365a25f0a04SGreg Roach } 366a25f0a04SGreg Roach 367a25f0a04SGreg Roach /** 368a25f0a04SGreg Roach * The Person/Family record where this Fact came from 369a25f0a04SGreg Roach * 3706e59b7c4SGreg Roach * @return Individual|Family|Source|Repository|Media|Note|GedcomRecord 371a25f0a04SGreg Roach */ 372c1010edaSGreg Roach public function getParent() 373c1010edaSGreg Roach { 374a25f0a04SGreg Roach return $this->parent; 375a25f0a04SGreg Roach } 376a25f0a04SGreg Roach 377a25f0a04SGreg Roach /** 378a25f0a04SGreg Roach * Get the name of this fact type, for use as a label. 379a25f0a04SGreg Roach * 380a25f0a04SGreg Roach * @return string 381a25f0a04SGreg Roach */ 382c1010edaSGreg Roach public function getLabel() 383c1010edaSGreg Roach { 384a25f0a04SGreg Roach // Custom FACT/EVEN - with a TYPE 3854e5adf68SGreg Roach if (($this->tag === 'FACT' || $this->tag === 'EVEN') && $this->getAttribute('TYPE') !== '') { 386d53324c9SGreg Roach return I18N::translate(e($this->getAttribute('TYPE'))); 387a25f0a04SGreg Roach } 3884e5adf68SGreg Roach 389764a01d9SGreg Roach return GedcomTag::getLabel($this->tag, $this->parent); 390a25f0a04SGreg Roach } 391a25f0a04SGreg Roach 392a25f0a04SGreg Roach /** 393a25f0a04SGreg Roach * This is a newly deleted fact, pending approval. 394a25f0a04SGreg Roach */ 395c1010edaSGreg Roach public function setPendingDeletion() 396c1010edaSGreg Roach { 397a25f0a04SGreg Roach $this->pending_deletion = true; 398a25f0a04SGreg Roach $this->pending_addition = false; 399a25f0a04SGreg Roach } 400a25f0a04SGreg Roach 401a25f0a04SGreg Roach /** 402a25f0a04SGreg Roach * Is this a newly deleted fact, pending approval. 403a25f0a04SGreg Roach * 404cbc1590aSGreg Roach * @return bool 405a25f0a04SGreg Roach */ 406c1010edaSGreg Roach public function isPendingDeletion() 407c1010edaSGreg Roach { 408a25f0a04SGreg Roach return $this->pending_deletion; 409a25f0a04SGreg Roach } 410a25f0a04SGreg Roach 411a25f0a04SGreg Roach /** 412a25f0a04SGreg Roach * This is a newly added fact, pending approval. 413a25f0a04SGreg Roach */ 414c1010edaSGreg Roach public function setPendingAddition() 415c1010edaSGreg Roach { 416a25f0a04SGreg Roach $this->pending_addition = true; 417a25f0a04SGreg Roach $this->pending_deletion = false; 418a25f0a04SGreg Roach } 419a25f0a04SGreg Roach 420a25f0a04SGreg Roach /** 421a25f0a04SGreg Roach * Is this a newly added fact, pending approval. 422a25f0a04SGreg Roach * 423cbc1590aSGreg Roach * @return bool 424a25f0a04SGreg Roach */ 425c1010edaSGreg Roach public function isPendingAddition() 426c1010edaSGreg Roach { 427a25f0a04SGreg Roach return $this->pending_addition; 428a25f0a04SGreg Roach } 429a25f0a04SGreg Roach 430a25f0a04SGreg Roach /** 431a25f0a04SGreg Roach * Source citations linked to this fact 432a25f0a04SGreg Roach * 433a25f0a04SGreg Roach * @return string[] 434a25f0a04SGreg Roach */ 435c1010edaSGreg Roach public function getCitations() 436c1010edaSGreg Roach { 437a25f0a04SGreg Roach preg_match_all('/\n(2 SOUR @(' . WT_REGEX_XREF . ')@(?:\n[3-9] .*)*)/', $this->getGedcom(), $matches, PREG_SET_ORDER); 43813abd6f3SGreg Roach $citations = []; 439a25f0a04SGreg Roach foreach ($matches as $match) { 44024ec66ceSGreg Roach $source = Source::getInstance($match[2], $this->getParent()->getTree()); 441a25f0a04SGreg Roach if ($source->canShow()) { 442a25f0a04SGreg Roach $citations[] = $match[1]; 443a25f0a04SGreg Roach } 444a25f0a04SGreg Roach } 445a25f0a04SGreg Roach 446a25f0a04SGreg Roach return $citations; 447a25f0a04SGreg Roach } 448a25f0a04SGreg Roach 449a25f0a04SGreg Roach /** 450a25f0a04SGreg Roach * Notes (inline and objects) linked to this fact 451a25f0a04SGreg Roach * 452a25f0a04SGreg Roach * @return string[]|Note[] 453a25f0a04SGreg Roach */ 454c1010edaSGreg Roach public function getNotes() 455c1010edaSGreg Roach { 45613abd6f3SGreg Roach $notes = []; 457a25f0a04SGreg Roach preg_match_all('/\n2 NOTE ?(.*(?:\n3.*)*)/', $this->getGedcom(), $matches); 458a25f0a04SGreg Roach foreach ($matches[1] as $match) { 459a25f0a04SGreg Roach $note = preg_replace("/\n3 CONT ?/", "\n", $match); 460a25f0a04SGreg Roach if (preg_match('/@(' . WT_REGEX_XREF . ')@/', $note, $nmatch)) { 46124ec66ceSGreg Roach $note = Note::getInstance($nmatch[1], $this->getParent()->getTree()); 462a25f0a04SGreg Roach if ($note && $note->canShow()) { 463a25f0a04SGreg Roach // A note object 464a25f0a04SGreg Roach $notes[] = $note; 465a25f0a04SGreg Roach } 466a25f0a04SGreg Roach } else { 467a25f0a04SGreg Roach // An inline note 468a25f0a04SGreg Roach $notes[] = $note; 469a25f0a04SGreg Roach } 470a25f0a04SGreg Roach } 471a25f0a04SGreg Roach 472a25f0a04SGreg Roach return $notes; 473a25f0a04SGreg Roach } 474a25f0a04SGreg Roach 475a25f0a04SGreg Roach /** 476a25f0a04SGreg Roach * Media objects linked to this fact 477a25f0a04SGreg Roach * 478a25f0a04SGreg Roach * @return Media[] 479a25f0a04SGreg Roach */ 480c1010edaSGreg Roach public function getMedia() 481c1010edaSGreg Roach { 48213abd6f3SGreg Roach $media = []; 483a25f0a04SGreg Roach preg_match_all('/\n2 OBJE @(' . WT_REGEX_XREF . ')@/', $this->getGedcom(), $matches); 484a25f0a04SGreg Roach foreach ($matches[1] as $match) { 48524ec66ceSGreg Roach $obje = Media::getInstance($match, $this->getParent()->getTree()); 486a25f0a04SGreg Roach if ($obje->canShow()) { 487a25f0a04SGreg Roach $media[] = $obje; 488a25f0a04SGreg Roach } 489a25f0a04SGreg Roach } 490a25f0a04SGreg Roach 491a25f0a04SGreg Roach return $media; 492a25f0a04SGreg Roach } 493a25f0a04SGreg Roach 494a25f0a04SGreg Roach /** 495a25f0a04SGreg Roach * A one-line summary of the fact - for charts, etc. 496a25f0a04SGreg Roach * 497a25f0a04SGreg Roach * @return string 498a25f0a04SGreg Roach */ 499c1010edaSGreg Roach public function summary() 500c1010edaSGreg Roach { 50113abd6f3SGreg Roach $attributes = []; 502a25f0a04SGreg Roach $target = $this->getTarget(); 503a25f0a04SGreg Roach if ($target) { 504a25f0a04SGreg Roach $attributes[] = $target->getFullName(); 505a25f0a04SGreg Roach } else { 506726d7b07SGreg Roach // Fact value 507a25f0a04SGreg Roach $value = $this->getValue(); 508726d7b07SGreg Roach if ($value !== '' && $value !== 'Y') { 509d53324c9SGreg Roach $attributes[] = '<span dir="auto">' . e($value) . '</span>'; 510a25f0a04SGreg Roach } 511726d7b07SGreg Roach // Fact date 512a25f0a04SGreg Roach $date = $this->getDate(); 513726d7b07SGreg Roach if ($date->isOK()) { 514af459ec4SGreg Roach if (in_array($this->getTag(), explode('|', WT_EVENTS_BIRT)) && $this->getParent() instanceof Individual && $this->getParent()->getTree()->getPreference('SHOW_PARENTS_AGE')) { 5153d7a8a4cSGreg Roach $attributes[] = $date->display() . FunctionsPrint::formatParentsAges($this->getParent(), $date); 516a25f0a04SGreg Roach } else { 517a25f0a04SGreg Roach $attributes[] = $date->display(); 518a25f0a04SGreg Roach } 519726d7b07SGreg Roach } 520726d7b07SGreg Roach // Fact place 521726d7b07SGreg Roach if (!$this->getPlace()->isEmpty()) { 522726d7b07SGreg Roach $attributes[] = $this->getPlace()->getShortName(); 523a25f0a04SGreg Roach } 524a25f0a04SGreg Roach } 5252d8f08abSGreg Roach 5262d8f08abSGreg Roach $class = 'fact_' . $this->getTag(); 527a25f0a04SGreg Roach if ($this->isPendingAddition()) { 5282d8f08abSGreg Roach $class .= ' new'; 529a25f0a04SGreg Roach } elseif ($this->isPendingDeletion()) { 5302d8f08abSGreg Roach $class .= ' old'; 531a25f0a04SGreg Roach } 5322d8f08abSGreg Roach 5332d8f08abSGreg Roach return 5342d8f08abSGreg Roach '<div class="' . $class . '">' . 5352d8f08abSGreg Roach /* I18N: a label/value pair, such as “Occupation: Farmer”. Some languages may need to change the punctuation. */ 5362d8f08abSGreg Roach I18N::translate('<span class="label">%1$s:</span> <span class="field" dir="auto">%2$s</span>', $this->getLabel(), implode(' — ', $attributes)) . 5372d8f08abSGreg Roach '</div>'; 538a25f0a04SGreg Roach } 539a25f0a04SGreg Roach 540a25f0a04SGreg Roach /** 541a25f0a04SGreg Roach * Static Helper functions to sort events 542a25f0a04SGreg Roach * 543a25f0a04SGreg Roach * @param Fact $a Fact one 544a25f0a04SGreg Roach * @param Fact $b Fact two 545a25f0a04SGreg Roach * 546cbc1590aSGreg Roach * @return int 547a25f0a04SGreg Roach */ 548c1010edaSGreg Roach public static function compareDate(Fact $a, Fact $b) 549c1010edaSGreg Roach { 550a25f0a04SGreg Roach if ($a->getDate()->isOK() && $b->getDate()->isOK()) { 551a25f0a04SGreg Roach // If both events have dates, compare by date 552f5b60decSGreg Roach $ret = Date::compare($a->getDate(), $b->getDate()); 553a25f0a04SGreg Roach 554a25f0a04SGreg Roach if ($ret == 0) { 555a25f0a04SGreg Roach // If dates are the same, compare by fact type 556a25f0a04SGreg Roach $ret = self::compareType($a, $b); 557a25f0a04SGreg Roach 558a25f0a04SGreg Roach // If the fact type is also the same, retain the initial order 559a25f0a04SGreg Roach if ($ret == 0) { 560a25f0a04SGreg Roach $ret = $a->sortOrder - $b->sortOrder; 561a25f0a04SGreg Roach } 562a25f0a04SGreg Roach } 563a25f0a04SGreg Roach 564a25f0a04SGreg Roach return $ret; 565a25f0a04SGreg Roach } else { 566a25f0a04SGreg Roach // One or both events have no date - retain the initial order 567a25f0a04SGreg Roach return $a->sortOrder - $b->sortOrder; 568a25f0a04SGreg Roach } 569a25f0a04SGreg Roach } 570a25f0a04SGreg Roach 571a25f0a04SGreg Roach /** 572a25f0a04SGreg Roach * Static method to compare two events by their type. 573a25f0a04SGreg Roach * 574a25f0a04SGreg Roach * @param Fact $a Fact one 575a25f0a04SGreg Roach * @param Fact $b Fact two 576a25f0a04SGreg Roach * 577cbc1590aSGreg Roach * @return int 578a25f0a04SGreg Roach */ 579c1010edaSGreg Roach public static function compareType(Fact $a, Fact $b) 580c1010edaSGreg Roach { 581*bbe4546fSGreg Roach static $factsort = []; 582a25f0a04SGreg Roach 583a25f0a04SGreg Roach if (empty($factsort)) { 584*bbe4546fSGreg Roach $factsort = array_flip(self::FACT_ORDER); 585a25f0a04SGreg Roach } 586a25f0a04SGreg Roach 587a25f0a04SGreg Roach // Facts from same families stay grouped together 588a25f0a04SGreg Roach // Keep MARR and DIV from the same families from mixing with events from other FAMs 589a25f0a04SGreg Roach // Use the original order in which the facts were added 590a25f0a04SGreg Roach if ($a->parent instanceof Family && $b->parent instanceof Family && $a->parent !== $b->parent) { 591a25f0a04SGreg Roach return $a->sortOrder - $b->sortOrder; 592a25f0a04SGreg Roach } 593a25f0a04SGreg Roach 594a25f0a04SGreg Roach $atag = $a->getTag(); 595a25f0a04SGreg Roach $btag = $b->getTag(); 596a25f0a04SGreg Roach 597a25f0a04SGreg Roach // Events not in the above list get mapped onto one that is. 598a25f0a04SGreg Roach if (!array_key_exists($atag, $factsort)) { 599a25f0a04SGreg Roach if (preg_match('/^(_(BIRT|MARR|DEAT|BURI)_)/', $atag, $match)) { 600a25f0a04SGreg Roach $atag = $match[1]; 601a25f0a04SGreg Roach } else { 6027a6ee1acSGreg Roach $atag = '_????_'; 603a25f0a04SGreg Roach } 604a25f0a04SGreg Roach } 605a25f0a04SGreg Roach 606a25f0a04SGreg Roach if (!array_key_exists($btag, $factsort)) { 607a25f0a04SGreg Roach if (preg_match('/^(_(BIRT|MARR|DEAT|BURI)_)/', $btag, $match)) { 608a25f0a04SGreg Roach $btag = $match[1]; 609a25f0a04SGreg Roach } else { 6107a6ee1acSGreg Roach $btag = '_????_'; 611a25f0a04SGreg Roach } 612a25f0a04SGreg Roach } 613a25f0a04SGreg Roach 614a25f0a04SGreg Roach // - Don't let dated after DEAT/BURI facts sort non-dated facts before DEAT/BURI 615a25f0a04SGreg Roach // - Treat dated after BURI facts as BURI instead 616baacc364SGreg Roach if ($a->getAttribute('DATE') !== '' && $factsort[$atag] > $factsort['BURI'] && $factsort[$atag] < $factsort['CHAN']) { 617a25f0a04SGreg Roach $atag = 'BURI'; 618a25f0a04SGreg Roach } 619a25f0a04SGreg Roach 620baacc364SGreg Roach if ($b->getAttribute('DATE') !== '' && $factsort[$btag] > $factsort['BURI'] && $factsort[$btag] < $factsort['CHAN']) { 621a25f0a04SGreg Roach $btag = 'BURI'; 622a25f0a04SGreg Roach } 623a25f0a04SGreg Roach 624a25f0a04SGreg Roach $ret = $factsort[$atag] - $factsort[$btag]; 625a25f0a04SGreg Roach 626a25f0a04SGreg Roach // If facts are the same then put dated facts before non-dated facts 627a25f0a04SGreg Roach if ($ret == 0) { 628baacc364SGreg Roach if ($a->getAttribute('DATE') !== '' && $b->getAttribute('DATE') === '') { 629a25f0a04SGreg Roach return -1; 630a25f0a04SGreg Roach } 631a25f0a04SGreg Roach 632baacc364SGreg Roach if ($b->getAttribute('DATE') !== '' && $a->getAttribute('DATE') === '') { 633a25f0a04SGreg Roach return 1; 634a25f0a04SGreg Roach } 635a25f0a04SGreg Roach 636a25f0a04SGreg Roach // If no sorting preference, then keep original ordering 637a25f0a04SGreg Roach $ret = $a->sortOrder - $b->sortOrder; 638a25f0a04SGreg Roach } 639a25f0a04SGreg Roach 640a25f0a04SGreg Roach return $ret; 641a25f0a04SGreg Roach } 642a25f0a04SGreg Roach 643a25f0a04SGreg Roach /** 644a25f0a04SGreg Roach * Allow native PHP functions such as array_unique() to work with objects 645a25f0a04SGreg Roach * 646a25f0a04SGreg Roach * @return string 647a25f0a04SGreg Roach */ 648c1010edaSGreg Roach public function __toString() 649c1010edaSGreg Roach { 650a25f0a04SGreg Roach return $this->fact_id . '@' . $this->parent->getXref(); 651a25f0a04SGreg Roach } 652a25f0a04SGreg Roach} 653