1*a25f0a04SGreg Roach<?php 2*a25f0a04SGreg Roachnamespace Webtrees; 3*a25f0a04SGreg Roach 4*a25f0a04SGreg Roach/** 5*a25f0a04SGreg Roach * webtrees: online genealogy 6*a25f0a04SGreg Roach * Copyright (C) 2015 webtrees development team 7*a25f0a04SGreg Roach * This program is free software: you can redistribute it and/or modify 8*a25f0a04SGreg Roach * it under the terms of the GNU General Public License as published by 9*a25f0a04SGreg Roach * the Free Software Foundation, either version 3 of the License, or 10*a25f0a04SGreg Roach * (at your option) any later version. 11*a25f0a04SGreg Roach * This program is distributed in the hope that it will be useful, 12*a25f0a04SGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of 13*a25f0a04SGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14*a25f0a04SGreg Roach * GNU General Public License for more details. 15*a25f0a04SGreg Roach * You should have received a copy of the GNU General Public License 16*a25f0a04SGreg Roach * along with this program. If not, see <http://www.gnu.org/licenses/>. 17*a25f0a04SGreg Roach */ 18*a25f0a04SGreg Roach 19*a25f0a04SGreg Roach/** 20*a25f0a04SGreg Roach * Class Fact - Class that defines an event details object 21*a25f0a04SGreg Roach */ 22*a25f0a04SGreg Roachclass Fact { 23*a25f0a04SGreg Roach /** @var string Unique identifier for this fact (currently implemented as a hash of the raw data). */ 24*a25f0a04SGreg Roach private $fact_id; 25*a25f0a04SGreg Roach 26*a25f0a04SGreg Roach /** @var GedcomRecord The GEDCOM record from which this fact is taken */ 27*a25f0a04SGreg Roach private $parent; 28*a25f0a04SGreg Roach 29*a25f0a04SGreg Roach /** @var string The raw GEDCOM data for this fact */ 30*a25f0a04SGreg Roach private $gedcom; 31*a25f0a04SGreg Roach 32*a25f0a04SGreg Roach /** @var string The GEDCOM tag for this record */ 33*a25f0a04SGreg Roach private $tag; 34*a25f0a04SGreg Roach 35*a25f0a04SGreg Roach /** @var boolean Is this a recently deleted fact, pending approval? */ 36*a25f0a04SGreg Roach private $pending_deletion = false; 37*a25f0a04SGreg Roach 38*a25f0a04SGreg Roach /** @var boolean Is this a recently added fact, pending approval? */ 39*a25f0a04SGreg Roach private $pending_addition = false; 40*a25f0a04SGreg Roach 41*a25f0a04SGreg Roach /** @var Date The date of this fact, from the “2 DATE …” attribute */ 42*a25f0a04SGreg Roach private $date; 43*a25f0a04SGreg Roach 44*a25f0a04SGreg Roach /** @var Place The place of this fact, from the “2 PLAC …” attribute */ 45*a25f0a04SGreg Roach private $place; 46*a25f0a04SGreg Roach 47*a25f0a04SGreg Roach /** @var integer Temporary(!) variable Used by sort_facts() */ 48*a25f0a04SGreg Roach public $sortOrder; 49*a25f0a04SGreg Roach 50*a25f0a04SGreg Roach /** 51*a25f0a04SGreg Roach * Create an event object from a gedcom fragment. 52*a25f0a04SGreg Roach * We need the parent object (to check privacy) and a (pseudo) fact ID to 53*a25f0a04SGreg Roach * identify the fact within the record. 54*a25f0a04SGreg Roach * 55*a25f0a04SGreg Roach * @param string $gedcom 56*a25f0a04SGreg Roach * @param GedcomRecord $parent 57*a25f0a04SGreg Roach * @param string $fact_id 58*a25f0a04SGreg Roach * 59*a25f0a04SGreg Roach * @throws \InvalidArgumentException 60*a25f0a04SGreg Roach */ 61*a25f0a04SGreg Roach public function __construct($gedcom, GedcomRecord $parent, $fact_id) { 62*a25f0a04SGreg Roach if (preg_match('/^1 (' . WT_REGEX_TAG . ')/', $gedcom, $match)) { 63*a25f0a04SGreg Roach $this->gedcom = $gedcom; 64*a25f0a04SGreg Roach $this->parent = $parent; 65*a25f0a04SGreg Roach $this->fact_id = $fact_id; 66*a25f0a04SGreg Roach $this->tag = $match[1]; 67*a25f0a04SGreg Roach } else { 68*a25f0a04SGreg Roach throw new \InvalidArgumentException('Invalid GEDCOM data passed to Fact::_construct(' . $gedcom . ')'); 69*a25f0a04SGreg Roach } 70*a25f0a04SGreg Roach } 71*a25f0a04SGreg Roach 72*a25f0a04SGreg Roach /** 73*a25f0a04SGreg Roach * Get the value of level 1 data in the fact 74*a25f0a04SGreg Roach * Allow for multi-line values 75*a25f0a04SGreg Roach * 76*a25f0a04SGreg Roach * @return string|null 77*a25f0a04SGreg Roach */ 78*a25f0a04SGreg Roach public function getValue() { 79*a25f0a04SGreg Roach if (preg_match('/^1 (?:' . $this->tag . ') ?(.*(?:(?:\n2 CONT ?.*)*))/', $this->gedcom, $match)) { 80*a25f0a04SGreg Roach return preg_replace("/\n2 CONT ?/", "\n", $match[1]); 81*a25f0a04SGreg Roach } else { 82*a25f0a04SGreg Roach return null; 83*a25f0a04SGreg Roach } 84*a25f0a04SGreg Roach } 85*a25f0a04SGreg Roach 86*a25f0a04SGreg Roach /** 87*a25f0a04SGreg Roach * Get the record to which this fact links 88*a25f0a04SGreg Roach * 89*a25f0a04SGreg Roach * @return GedcomRecord|null 90*a25f0a04SGreg Roach */ 91*a25f0a04SGreg Roach public function getTarget() { 92*a25f0a04SGreg Roach $xref = trim($this->getValue(), '@'); 93*a25f0a04SGreg Roach switch ($this->tag) { 94*a25f0a04SGreg Roach case 'FAMC': 95*a25f0a04SGreg Roach case 'FAMS': 96*a25f0a04SGreg Roach return Family::getInstance($xref, $this->getParent()->getGedcomId()); 97*a25f0a04SGreg Roach case 'HUSB': 98*a25f0a04SGreg Roach case 'WIFE': 99*a25f0a04SGreg Roach case 'CHIL': 100*a25f0a04SGreg Roach return Individual::getInstance($xref, $this->getParent()->getGedcomId()); 101*a25f0a04SGreg Roach case 'SOUR': 102*a25f0a04SGreg Roach return Source::getInstance($xref, $this->getParent()->getGedcomId()); 103*a25f0a04SGreg Roach case 'OBJE': 104*a25f0a04SGreg Roach return Media::getInstance($xref, $this->getParent()->getGedcomId()); 105*a25f0a04SGreg Roach case 'REPO': 106*a25f0a04SGreg Roach return Repository::getInstance($xref, $this->getParent()->getGedcomId()); 107*a25f0a04SGreg Roach case 'NOTE': 108*a25f0a04SGreg Roach return Note::getInstance($xref, $this->getParent()->getGedcomId()); 109*a25f0a04SGreg Roach default: 110*a25f0a04SGreg Roach return GedcomRecord::getInstance($xref, $this->getParent()->getGedcomId()); 111*a25f0a04SGreg Roach } 112*a25f0a04SGreg Roach } 113*a25f0a04SGreg Roach 114*a25f0a04SGreg Roach /** 115*a25f0a04SGreg Roach * Get the value of level 2 data in the fact 116*a25f0a04SGreg Roach * 117*a25f0a04SGreg Roach * @param string $tag 118*a25f0a04SGreg Roach * 119*a25f0a04SGreg Roach * @return string|null 120*a25f0a04SGreg Roach */ 121*a25f0a04SGreg Roach public function getAttribute($tag) { 122*a25f0a04SGreg Roach if (preg_match('/\n2 (?:' . $tag . ') ?(.*(?:(?:\n3 CONT ?.*)*)*)/', $this->gedcom, $match)) { 123*a25f0a04SGreg Roach return preg_replace("/\n3 CONT ?/", "\n", $match[1]); 124*a25f0a04SGreg Roach } else { 125*a25f0a04SGreg Roach return null; 126*a25f0a04SGreg Roach } 127*a25f0a04SGreg Roach } 128*a25f0a04SGreg Roach 129*a25f0a04SGreg Roach /** 130*a25f0a04SGreg Roach * Do the privacy rules allow us to display this fact to the current user 131*a25f0a04SGreg Roach * 132*a25f0a04SGreg Roach * @param integer $access_level 133*a25f0a04SGreg Roach * 134*a25f0a04SGreg Roach * @return boolean 135*a25f0a04SGreg Roach */ 136*a25f0a04SGreg Roach public function canShow($access_level = WT_USER_ACCESS_LEVEL) { 137*a25f0a04SGreg Roach // TODO - use the privacy settings for $this->gedcom_id, not the default gedcom. 138*a25f0a04SGreg Roach global $person_facts, $global_facts; 139*a25f0a04SGreg Roach 140*a25f0a04SGreg Roach // Does this record have an explicit RESN? 141*a25f0a04SGreg Roach if (strpos($this->gedcom, "\n2 RESN confidential")) { 142*a25f0a04SGreg Roach return WT_PRIV_NONE >= $access_level; 143*a25f0a04SGreg Roach } 144*a25f0a04SGreg Roach if (strpos($this->gedcom, "\n2 RESN privacy")) { 145*a25f0a04SGreg Roach return WT_PRIV_USER >= $access_level; 146*a25f0a04SGreg Roach } 147*a25f0a04SGreg Roach if (strpos($this->gedcom, "\n2 RESN none")) { 148*a25f0a04SGreg Roach return true; 149*a25f0a04SGreg Roach } 150*a25f0a04SGreg Roach 151*a25f0a04SGreg Roach // Does this record have a default RESN? 152*a25f0a04SGreg Roach $xref = $this->parent->getXref(); 153*a25f0a04SGreg Roach if (isset($person_facts[$xref][$this->tag])) { 154*a25f0a04SGreg Roach return $person_facts[$xref][$this->tag] >= $access_level; 155*a25f0a04SGreg Roach } 156*a25f0a04SGreg Roach if (isset($global_facts[$this->tag])) { 157*a25f0a04SGreg Roach return $global_facts[$this->tag] >= $access_level; 158*a25f0a04SGreg Roach } 159*a25f0a04SGreg Roach 160*a25f0a04SGreg Roach // No restrictions - it must be public 161*a25f0a04SGreg Roach return true; 162*a25f0a04SGreg Roach } 163*a25f0a04SGreg Roach 164*a25f0a04SGreg Roach /** 165*a25f0a04SGreg Roach * Check whether this fact is protected against edit 166*a25f0a04SGreg Roach * 167*a25f0a04SGreg Roach * @return boolean 168*a25f0a04SGreg Roach */ 169*a25f0a04SGreg Roach public function canEdit() { 170*a25f0a04SGreg Roach // Managers can edit anything 171*a25f0a04SGreg Roach // Members cannot edit RESN, CHAN and locked records 172*a25f0a04SGreg Roach return 173*a25f0a04SGreg Roach $this->parent->canEdit() && !$this->isPendingDeletion() && ( 174*a25f0a04SGreg Roach WT_USER_GEDCOM_ADMIN || 175*a25f0a04SGreg Roach WT_USER_CAN_EDIT && strpos($this->gedcom, "\n2 RESN locked") === false && $this->getTag() != 'RESN' && $this->getTag() != 'CHAN' 176*a25f0a04SGreg Roach ); 177*a25f0a04SGreg Roach } 178*a25f0a04SGreg Roach 179*a25f0a04SGreg Roach /** 180*a25f0a04SGreg Roach * The place where the event occured. 181*a25f0a04SGreg Roach * 182*a25f0a04SGreg Roach * @return Place 183*a25f0a04SGreg Roach */ 184*a25f0a04SGreg Roach public function getPlace() { 185*a25f0a04SGreg Roach if ($this->place === null) { 186*a25f0a04SGreg Roach $this->place = new Place($this->getAttribute('PLAC'), $this->getParent()->getGedcomId()); 187*a25f0a04SGreg Roach } 188*a25f0a04SGreg Roach 189*a25f0a04SGreg Roach return $this->place; 190*a25f0a04SGreg Roach } 191*a25f0a04SGreg Roach 192*a25f0a04SGreg Roach /** 193*a25f0a04SGreg Roach * Get the date for this fact. 194*a25f0a04SGreg Roach * We can call this function many times, especially when sorting, 195*a25f0a04SGreg Roach * so keep a copy of the date. 196*a25f0a04SGreg Roach * 197*a25f0a04SGreg Roach * @return Date 198*a25f0a04SGreg Roach */ 199*a25f0a04SGreg Roach public function getDate() { 200*a25f0a04SGreg Roach if ($this->date === null) { 201*a25f0a04SGreg Roach $this->date = new Date($this->getAttribute('DATE')); 202*a25f0a04SGreg Roach } 203*a25f0a04SGreg Roach 204*a25f0a04SGreg Roach return $this->date; 205*a25f0a04SGreg Roach } 206*a25f0a04SGreg Roach 207*a25f0a04SGreg Roach /** 208*a25f0a04SGreg Roach * The raw GEDCOM data for this fact 209*a25f0a04SGreg Roach * 210*a25f0a04SGreg Roach * @return string 211*a25f0a04SGreg Roach */ 212*a25f0a04SGreg Roach public function getGedcom() { 213*a25f0a04SGreg Roach return $this->gedcom; 214*a25f0a04SGreg Roach } 215*a25f0a04SGreg Roach 216*a25f0a04SGreg Roach /** 217*a25f0a04SGreg Roach * Get a (pseudo) primary key for this fact. 218*a25f0a04SGreg Roach * 219*a25f0a04SGreg Roach * @return string 220*a25f0a04SGreg Roach */ 221*a25f0a04SGreg Roach public function getFactId() { 222*a25f0a04SGreg Roach return $this->fact_id; 223*a25f0a04SGreg Roach } 224*a25f0a04SGreg Roach 225*a25f0a04SGreg Roach // What sort of fact is this? 226*a25f0a04SGreg Roach /** 227*a25f0a04SGreg Roach * What is the tag (type) of this fact, such as BIRT, MARR or DEAT. 228*a25f0a04SGreg Roach * 229*a25f0a04SGreg Roach * @return string 230*a25f0a04SGreg Roach */ 231*a25f0a04SGreg Roach public function getTag() { 232*a25f0a04SGreg Roach return $this->tag; 233*a25f0a04SGreg Roach } 234*a25f0a04SGreg Roach 235*a25f0a04SGreg Roach /** 236*a25f0a04SGreg Roach * Used to convert a real fact (e.g. BIRT) into a close-relative’s fact (e.g. _BIRT_CHIL) 237*a25f0a04SGreg Roach * 238*a25f0a04SGreg Roach * @param string $tag 239*a25f0a04SGreg Roach */ 240*a25f0a04SGreg Roach public function setTag($tag) { 241*a25f0a04SGreg Roach $this->tag = $tag; 242*a25f0a04SGreg Roach } 243*a25f0a04SGreg Roach 244*a25f0a04SGreg Roach // 245*a25f0a04SGreg Roach /** 246*a25f0a04SGreg Roach * The Person/Family record where this Fact came from 247*a25f0a04SGreg Roach * 248*a25f0a04SGreg Roach * @return GedcomRecord 249*a25f0a04SGreg Roach */ 250*a25f0a04SGreg Roach public function getParent() { 251*a25f0a04SGreg Roach return $this->parent; 252*a25f0a04SGreg Roach } 253*a25f0a04SGreg Roach 254*a25f0a04SGreg Roach /** 255*a25f0a04SGreg Roach * Get the name of this fact type, for use as a label. 256*a25f0a04SGreg Roach * 257*a25f0a04SGreg Roach * @return string 258*a25f0a04SGreg Roach */ 259*a25f0a04SGreg Roach public function getLabel() { 260*a25f0a04SGreg Roach switch ($this->tag) { 261*a25f0a04SGreg Roach case 'EVEN': 262*a25f0a04SGreg Roach case 'FACT': 263*a25f0a04SGreg Roach if ($this->getAttribute('TYPE')) { 264*a25f0a04SGreg Roach // Custom FACT/EVEN - with a TYPE 265*a25f0a04SGreg Roach return I18N::translate(Filter::escapeHtml($this->getAttribute('TYPE'))); 266*a25f0a04SGreg Roach } 267*a25f0a04SGreg Roach // no break - drop into next case 268*a25f0a04SGreg Roach default: 269*a25f0a04SGreg Roach return WT_Gedcom_Tag::getLabel($this->tag, $this->parent); 270*a25f0a04SGreg Roach } 271*a25f0a04SGreg Roach } 272*a25f0a04SGreg Roach 273*a25f0a04SGreg Roach /** 274*a25f0a04SGreg Roach * This is a newly deleted fact, pending approval. 275*a25f0a04SGreg Roach */ 276*a25f0a04SGreg Roach public function setPendingDeletion() { 277*a25f0a04SGreg Roach $this->pending_deletion = true; 278*a25f0a04SGreg Roach $this->pending_addition = false; 279*a25f0a04SGreg Roach } 280*a25f0a04SGreg Roach 281*a25f0a04SGreg Roach /** 282*a25f0a04SGreg Roach * Is this a newly deleted fact, pending approval. 283*a25f0a04SGreg Roach * 284*a25f0a04SGreg Roach * @return boolean 285*a25f0a04SGreg Roach */ 286*a25f0a04SGreg Roach public function isPendingDeletion() { 287*a25f0a04SGreg Roach return $this->pending_deletion; 288*a25f0a04SGreg Roach } 289*a25f0a04SGreg Roach 290*a25f0a04SGreg Roach /** 291*a25f0a04SGreg Roach * This is a newly added fact, pending approval. 292*a25f0a04SGreg Roach */ 293*a25f0a04SGreg Roach public function setPendingAddition() { 294*a25f0a04SGreg Roach $this->pending_addition = true; 295*a25f0a04SGreg Roach $this->pending_deletion = false; 296*a25f0a04SGreg Roach } 297*a25f0a04SGreg Roach 298*a25f0a04SGreg Roach /** 299*a25f0a04SGreg Roach * Is this a newly added fact, pending approval. 300*a25f0a04SGreg Roach * 301*a25f0a04SGreg Roach * @return boolean 302*a25f0a04SGreg Roach */ 303*a25f0a04SGreg Roach public function isPendingAddition() { 304*a25f0a04SGreg Roach return $this->pending_addition; 305*a25f0a04SGreg Roach } 306*a25f0a04SGreg Roach 307*a25f0a04SGreg Roach /** 308*a25f0a04SGreg Roach * Source citations linked to this fact 309*a25f0a04SGreg Roach * 310*a25f0a04SGreg Roach * @return string[] 311*a25f0a04SGreg Roach */ 312*a25f0a04SGreg Roach public function getCitations() { 313*a25f0a04SGreg Roach preg_match_all('/\n(2 SOUR @(' . WT_REGEX_XREF . ')@(?:\n[3-9] .*)*)/', $this->getGedcom(), $matches, PREG_SET_ORDER); 314*a25f0a04SGreg Roach $citations = array(); 315*a25f0a04SGreg Roach foreach ($matches as $match) { 316*a25f0a04SGreg Roach $source = Source::getInstance($match[2], $this->getParent()->getGedcomId()); 317*a25f0a04SGreg Roach if ($source->canShow()) { 318*a25f0a04SGreg Roach $citations[] = $match[1]; 319*a25f0a04SGreg Roach } 320*a25f0a04SGreg Roach } 321*a25f0a04SGreg Roach 322*a25f0a04SGreg Roach return $citations; 323*a25f0a04SGreg Roach } 324*a25f0a04SGreg Roach 325*a25f0a04SGreg Roach /** 326*a25f0a04SGreg Roach * Notes (inline and objects) linked to this fact 327*a25f0a04SGreg Roach * 328*a25f0a04SGreg Roach * @return string[]|Note[] 329*a25f0a04SGreg Roach */ 330*a25f0a04SGreg Roach public function getNotes() { 331*a25f0a04SGreg Roach $notes = array(); 332*a25f0a04SGreg Roach preg_match_all('/\n2 NOTE ?(.*(?:\n3.*)*)/', $this->getGedcom(), $matches); 333*a25f0a04SGreg Roach foreach ($matches[1] as $match) { 334*a25f0a04SGreg Roach $note = preg_replace("/\n3 CONT ?/", "\n", $match); 335*a25f0a04SGreg Roach if (preg_match('/@(' . WT_REGEX_XREF . ')@/', $note, $nmatch)) { 336*a25f0a04SGreg Roach $note = Note::getInstance($nmatch[1], $this->getParent()->getGedcomId()); 337*a25f0a04SGreg Roach if ($note && $note->canShow()) { 338*a25f0a04SGreg Roach // A note object 339*a25f0a04SGreg Roach $notes[] = $note; 340*a25f0a04SGreg Roach } 341*a25f0a04SGreg Roach } else { 342*a25f0a04SGreg Roach // An inline note 343*a25f0a04SGreg Roach $notes[] = $note; 344*a25f0a04SGreg Roach } 345*a25f0a04SGreg Roach } 346*a25f0a04SGreg Roach 347*a25f0a04SGreg Roach return $notes; 348*a25f0a04SGreg Roach } 349*a25f0a04SGreg Roach 350*a25f0a04SGreg Roach /** 351*a25f0a04SGreg Roach * Media objects linked to this fact 352*a25f0a04SGreg Roach * 353*a25f0a04SGreg Roach * @return Media[] 354*a25f0a04SGreg Roach */ 355*a25f0a04SGreg Roach public function getMedia() { 356*a25f0a04SGreg Roach $media = array(); 357*a25f0a04SGreg Roach preg_match_all('/\n2 OBJE @(' . WT_REGEX_XREF . ')@/', $this->getGedcom(), $matches); 358*a25f0a04SGreg Roach foreach ($matches[1] as $match) { 359*a25f0a04SGreg Roach $obje = Media::getInstance($match, $this->getParent()->getGedcomId()); 360*a25f0a04SGreg Roach if ($obje->canShow()) { 361*a25f0a04SGreg Roach $media[] = $obje; 362*a25f0a04SGreg Roach } 363*a25f0a04SGreg Roach } 364*a25f0a04SGreg Roach 365*a25f0a04SGreg Roach return $media; 366*a25f0a04SGreg Roach } 367*a25f0a04SGreg Roach 368*a25f0a04SGreg Roach /** 369*a25f0a04SGreg Roach * A one-line summary of the fact - for charts, etc. 370*a25f0a04SGreg Roach * 371*a25f0a04SGreg Roach * @return string 372*a25f0a04SGreg Roach */ 373*a25f0a04SGreg Roach public function summary() { 374*a25f0a04SGreg Roach global $SHOW_PARENTS_AGE; 375*a25f0a04SGreg Roach 376*a25f0a04SGreg Roach $attributes = array(); 377*a25f0a04SGreg Roach $target = $this->getTarget(); 378*a25f0a04SGreg Roach if ($target) { 379*a25f0a04SGreg Roach $attributes[] = $target->getFullName(); 380*a25f0a04SGreg Roach } else { 381*a25f0a04SGreg Roach $value = $this->getValue(); 382*a25f0a04SGreg Roach if ($value && $value != 'Y') { 383*a25f0a04SGreg Roach $attributes[] = '<span dir="auto">' . Filter::escapeHtml($value) . '</span>'; 384*a25f0a04SGreg Roach } 385*a25f0a04SGreg Roach $date = $this->getDate(); 386*a25f0a04SGreg Roach if ($this->getTag() == 'BIRT' && $SHOW_PARENTS_AGE && $this->getParent() instanceof Individual) { 387*a25f0a04SGreg Roach $attributes[] = $date->display() . format_parents_age($this->getParent(), $date); 388*a25f0a04SGreg Roach } else { 389*a25f0a04SGreg Roach $attributes[] = $date->display(); 390*a25f0a04SGreg Roach } 391*a25f0a04SGreg Roach $place = $this->getPlace()->getShortName(); 392*a25f0a04SGreg Roach if ($place) { 393*a25f0a04SGreg Roach $attributes[] = $place; 394*a25f0a04SGreg Roach } 395*a25f0a04SGreg Roach } 396*a25f0a04SGreg Roach $html = WT_Gedcom_Tag::getLabelValue($this->getTag(), implode(' — ', $attributes), $this->getParent()); 397*a25f0a04SGreg Roach if ($this->isPendingAddition()) { 398*a25f0a04SGreg Roach return '<div class="new">' . $html . '</div>'; 399*a25f0a04SGreg Roach } elseif ($this->isPendingDeletion()) { 400*a25f0a04SGreg Roach return '<div class="old">' . $html . '</div>'; 401*a25f0a04SGreg Roach } else { 402*a25f0a04SGreg Roach return $html; 403*a25f0a04SGreg Roach } 404*a25f0a04SGreg Roach } 405*a25f0a04SGreg Roach 406*a25f0a04SGreg Roach /** 407*a25f0a04SGreg Roach * Static Helper functions to sort events 408*a25f0a04SGreg Roach * 409*a25f0a04SGreg Roach * @param Fact $a Fact one 410*a25f0a04SGreg Roach * @param Fact $b Fact two 411*a25f0a04SGreg Roach * 412*a25f0a04SGreg Roach * @return integer 413*a25f0a04SGreg Roach */ 414*a25f0a04SGreg Roach public static function compareDate(Fact $a, Fact $b) { 415*a25f0a04SGreg Roach if ($a->getDate()->isOK() && $b->getDate()->isOK()) { 416*a25f0a04SGreg Roach // If both events have dates, compare by date 417*a25f0a04SGreg Roach $ret = Date::Compare($a->getDate(), $b->getDate()); 418*a25f0a04SGreg Roach 419*a25f0a04SGreg Roach if ($ret == 0) { 420*a25f0a04SGreg Roach // If dates are the same, compare by fact type 421*a25f0a04SGreg Roach $ret = self::compareType($a, $b); 422*a25f0a04SGreg Roach 423*a25f0a04SGreg Roach // If the fact type is also the same, retain the initial order 424*a25f0a04SGreg Roach if ($ret == 0) { 425*a25f0a04SGreg Roach $ret = $a->sortOrder - $b->sortOrder; 426*a25f0a04SGreg Roach } 427*a25f0a04SGreg Roach } 428*a25f0a04SGreg Roach 429*a25f0a04SGreg Roach return $ret; 430*a25f0a04SGreg Roach } else { 431*a25f0a04SGreg Roach // One or both events have no date - retain the initial order 432*a25f0a04SGreg Roach return $a->sortOrder - $b->sortOrder; 433*a25f0a04SGreg Roach } 434*a25f0a04SGreg Roach } 435*a25f0a04SGreg Roach 436*a25f0a04SGreg Roach /** 437*a25f0a04SGreg Roach * Static method to compare two events by their type. 438*a25f0a04SGreg Roach * 439*a25f0a04SGreg Roach * @param Fact $a Fact one 440*a25f0a04SGreg Roach * @param Fact $b Fact two 441*a25f0a04SGreg Roach * 442*a25f0a04SGreg Roach * @return integer 443*a25f0a04SGreg Roach */ 444*a25f0a04SGreg Roach public static function compareType(Fact $a, Fact $b) { 445*a25f0a04SGreg Roach global $factsort; 446*a25f0a04SGreg Roach 447*a25f0a04SGreg Roach if (empty($factsort)) { 448*a25f0a04SGreg Roach $factsort = array_flip( 449*a25f0a04SGreg Roach array( 450*a25f0a04SGreg Roach 'BIRT', 451*a25f0a04SGreg Roach '_HNM', 452*a25f0a04SGreg Roach 'ALIA', '_AKA', '_AKAN', 453*a25f0a04SGreg Roach 'ADOP', '_ADPF', '_ADPF', 454*a25f0a04SGreg Roach '_BRTM', 455*a25f0a04SGreg Roach 'CHR', 'BAPM', 456*a25f0a04SGreg Roach 'FCOM', 457*a25f0a04SGreg Roach 'CONF', 458*a25f0a04SGreg Roach 'BARM', 'BASM', 459*a25f0a04SGreg Roach 'EDUC', 460*a25f0a04SGreg Roach 'GRAD', 461*a25f0a04SGreg Roach '_DEG', 462*a25f0a04SGreg Roach 'EMIG', 'IMMI', 463*a25f0a04SGreg Roach 'NATU', 464*a25f0a04SGreg Roach '_MILI', '_MILT', 465*a25f0a04SGreg Roach 'ENGA', 466*a25f0a04SGreg Roach 'MARB', 'MARC', 'MARL', '_MARI', '_MBON', 467*a25f0a04SGreg Roach 'MARR', 'MARR_CIVIL', 'MARR_RELIGIOUS', 'MARR_PARTNERS', 'MARR_UNKNOWN', '_COML', 468*a25f0a04SGreg Roach '_STAT', 469*a25f0a04SGreg Roach '_SEPR', 470*a25f0a04SGreg Roach 'DIVF', 471*a25f0a04SGreg Roach 'MARS', 472*a25f0a04SGreg Roach '_BIRT_CHIL', 473*a25f0a04SGreg Roach 'DIV', 'ANUL', 474*a25f0a04SGreg Roach '_BIRT_', '_MARR_', '_DEAT_', '_BURI_', // other events of close relatives 475*a25f0a04SGreg Roach 'CENS', 476*a25f0a04SGreg Roach 'OCCU', 477*a25f0a04SGreg Roach 'RESI', 478*a25f0a04SGreg Roach 'PROP', 479*a25f0a04SGreg Roach 'CHRA', 480*a25f0a04SGreg Roach 'RETI', 481*a25f0a04SGreg Roach 'FACT', 'EVEN', 482*a25f0a04SGreg Roach '_NMR', '_NMAR', 'NMR', 483*a25f0a04SGreg Roach 'NCHI', 484*a25f0a04SGreg Roach 'WILL', 485*a25f0a04SGreg Roach '_HOL', 486*a25f0a04SGreg Roach '_????_', 487*a25f0a04SGreg Roach 'DEAT', 488*a25f0a04SGreg Roach '_FNRL', 'CREM', 'BURI', '_INTE', 489*a25f0a04SGreg Roach '_YART', 490*a25f0a04SGreg Roach '_NLIV', 491*a25f0a04SGreg Roach 'PROB', 492*a25f0a04SGreg Roach 'TITL', 493*a25f0a04SGreg Roach 'COMM', 494*a25f0a04SGreg Roach 'NATI', 495*a25f0a04SGreg Roach 'CITN', 496*a25f0a04SGreg Roach 'CAST', 497*a25f0a04SGreg Roach 'RELI', 498*a25f0a04SGreg Roach 'SSN', 'IDNO', 499*a25f0a04SGreg Roach 'TEMP', 500*a25f0a04SGreg Roach 'SLGC', 'BAPL', 'CONL', 'ENDL', 'SLGS', 501*a25f0a04SGreg Roach 'ADDR', 'PHON', 'EMAIL', '_EMAIL', 'EMAL', 'FAX', 'WWW', 'URL', '_URL', 502*a25f0a04SGreg Roach 'FILE', // For media objects 503*a25f0a04SGreg Roach 'AFN', 'REFN', '_PRMN', 'REF', 'RIN', '_UID', 504*a25f0a04SGreg Roach 'OBJE', 'NOTE', 'SOUR', 505*a25f0a04SGreg Roach 'CHAN', '_TODO', 506*a25f0a04SGreg Roach ) 507*a25f0a04SGreg Roach ); 508*a25f0a04SGreg Roach } 509*a25f0a04SGreg Roach 510*a25f0a04SGreg Roach // Facts from same families stay grouped together 511*a25f0a04SGreg Roach // Keep MARR and DIV from the same families from mixing with events from other FAMs 512*a25f0a04SGreg Roach // Use the original order in which the facts were added 513*a25f0a04SGreg Roach if ($a->parent instanceof Family && $b->parent instanceof Family && $a->parent !== $b->parent) { 514*a25f0a04SGreg Roach return $a->sortOrder - $b->sortOrder; 515*a25f0a04SGreg Roach } 516*a25f0a04SGreg Roach 517*a25f0a04SGreg Roach $atag = $a->getTag(); 518*a25f0a04SGreg Roach $btag = $b->getTag(); 519*a25f0a04SGreg Roach 520*a25f0a04SGreg Roach // Events not in the above list get mapped onto one that is. 521*a25f0a04SGreg Roach if (!array_key_exists($atag, $factsort)) { 522*a25f0a04SGreg Roach if (preg_match('/^(_(BIRT|MARR|DEAT|BURI)_)/', $atag, $match)) { 523*a25f0a04SGreg Roach $atag = $match[1]; 524*a25f0a04SGreg Roach } else { 525*a25f0a04SGreg Roach $atag = "_????_"; 526*a25f0a04SGreg Roach } 527*a25f0a04SGreg Roach } 528*a25f0a04SGreg Roach 529*a25f0a04SGreg Roach if (!array_key_exists($btag, $factsort)) { 530*a25f0a04SGreg Roach if (preg_match('/^(_(BIRT|MARR|DEAT|BURI)_)/', $btag, $match)) { 531*a25f0a04SGreg Roach $btag = $match[1]; 532*a25f0a04SGreg Roach } else { 533*a25f0a04SGreg Roach $btag = "_????_"; 534*a25f0a04SGreg Roach } 535*a25f0a04SGreg Roach } 536*a25f0a04SGreg Roach 537*a25f0a04SGreg Roach // - Don't let dated after DEAT/BURI facts sort non-dated facts before DEAT/BURI 538*a25f0a04SGreg Roach // - Treat dated after BURI facts as BURI instead 539*a25f0a04SGreg Roach if ($a->getAttribute('DATE') != null && $factsort[$atag] > $factsort['BURI'] && $factsort[$atag] < $factsort['CHAN']) { 540*a25f0a04SGreg Roach $atag = 'BURI'; 541*a25f0a04SGreg Roach } 542*a25f0a04SGreg Roach 543*a25f0a04SGreg Roach if ($b->getAttribute('DATE') != null && $factsort[$btag] > $factsort['BURI'] && $factsort[$btag] < $factsort['CHAN']) { 544*a25f0a04SGreg Roach $btag = 'BURI'; 545*a25f0a04SGreg Roach } 546*a25f0a04SGreg Roach 547*a25f0a04SGreg Roach $ret = $factsort[$atag] - $factsort[$btag]; 548*a25f0a04SGreg Roach 549*a25f0a04SGreg Roach // If facts are the same then put dated facts before non-dated facts 550*a25f0a04SGreg Roach if ($ret == 0) { 551*a25f0a04SGreg Roach if ($a->getAttribute('DATE') != null && $b->getAttribute('DATE') == null) { 552*a25f0a04SGreg Roach return -1; 553*a25f0a04SGreg Roach } 554*a25f0a04SGreg Roach 555*a25f0a04SGreg Roach if ($b->getAttribute('DATE') != null && $a->getAttribute('DATE') == null) { 556*a25f0a04SGreg Roach return 1; 557*a25f0a04SGreg Roach } 558*a25f0a04SGreg Roach 559*a25f0a04SGreg Roach // If no sorting preference, then keep original ordering 560*a25f0a04SGreg Roach $ret = $a->sortOrder - $b->sortOrder; 561*a25f0a04SGreg Roach } 562*a25f0a04SGreg Roach 563*a25f0a04SGreg Roach return $ret; 564*a25f0a04SGreg Roach } 565*a25f0a04SGreg Roach 566*a25f0a04SGreg Roach /** 567*a25f0a04SGreg Roach * Allow native PHP functions such as array_unique() to work with objects 568*a25f0a04SGreg Roach * 569*a25f0a04SGreg Roach * @return string 570*a25f0a04SGreg Roach */ 571*a25f0a04SGreg Roach public function __toString() { 572*a25f0a04SGreg Roach return $this->fact_id . '@' . $this->parent->getXref(); 573*a25f0a04SGreg Roach } 574*a25f0a04SGreg Roach} 575