/** * Function checkFact * * Does this fact meet the search criteria? * * @todo This function is public to support the PHP5.3 closure workaround. * * @param Fact $fact * * @return bool */ public function checkFact(Fact $fact) { $valid = !in_array($fact->getTag(), $this->nonfacts); if ($valid && $this->place_obj) { $valid = stripos($fact->getPlace()->getGedcomName(), $this->place_obj->getGedcomName()) !== false; } if ($valid && $this->startDate) { if ($this->strictDate && $this->calendar !== $this->defaultCalendar) { $valid = stripos($fact->getAttribute('DATE'), $this->calendar) !== false; } if ($valid) { $date = $fact->getDate(); $valid = $date->isOK() && Date::compare($date, $this->startDate) >= 0 && Date::compare($date, $this->endDate) <= 0; } } return $valid; }
/** * Print fact DATE/TIME * * @param Fact $event event containing the date/age * @param GedcomRecord $record the person (or couple) whose ages should be printed * @param bool $anchor option to print a link to calendar * @param bool $time option to print TIME value * * @return string */ public static function formatFactDate(Fact $event, GedcomRecord $record, $anchor, $time) { global $pid; $factrec = $event->getGedcom(); $html = ''; // Recorded age if (preg_match('/\\n2 AGE (.+)/', $factrec, $match)) { $fact_age = $match[1]; } else { $fact_age = ''; } if (preg_match('/\\n2 HUSB\\n3 AGE (.+)/', $factrec, $match)) { $husb_age = $match[1]; } else { $husb_age = ''; } if (preg_match('/\\n2 WIFE\\n3 AGE (.+)/', $factrec, $match)) { $wife_age = $match[1]; } else { $wife_age = ''; } // Calculated age if (preg_match('/\\n2 DATE (.+)/', $factrec, $match)) { $date = new Date($match[1]); $html .= ' ' . $date->display($anchor); // time if ($time && preg_match('/\\n3 TIME (.+)/', $factrec, $match)) { $html .= ' – <span class="date">' . $match[1] . '</span>'; } $fact = $event->getTag(); if ($record instanceof Individual) { if ($fact === 'BIRT' && $record->getTree()->getPreference('SHOW_PARENTS_AGE')) { // age of parents at child birth $html .= self::formatParentsAges($record, $date); } elseif ($fact !== 'CHAN' && $fact !== '_TODO') { // age at event $birth_date = $record->getBirthDate(); // Can't use getDeathDate(), as this also gives BURI/CREM events, which // wouldn't give the correct "days after death" result for people with // no DEAT. $death_event = $record->getFirstFact('DEAT'); if ($death_event) { $death_date = $death_event->getDate(); } else { $death_date = new Date(''); } $ageText = ''; if (Date::compare($date, $death_date) <= 0 || !$record->isDead() || $fact == 'DEAT') { // Before death, print age $age = Date::getAgeGedcom($birth_date, $date); // Only show calculated age if it differs from recorded age if ($age != '') { if ($fact_age != '' && $fact_age != $age || $fact_age == '' && $husb_age == '' && $wife_age == '' || $husb_age != '' && $record->getSex() == 'M' && $husb_age != $age || $wife_age != '' && $record->getSex() == 'F' && $wife_age != $age) { if ($age != "0d") { $ageText = '(' . I18N::translate('Age') . ' ' . FunctionsDate::getAgeAtEvent($age, false) . ')'; } } } } if ($fact != 'DEAT' && Date::compare($date, $death_date) >= 0) { // After death, print time since death $age = FunctionsDate::getAgeAtEvent(Date::getAgeGedcom($death_date, $date), true); if ($age != '') { if (Date::getAgeGedcom($death_date, $date) == "0d") { $ageText = '(' . I18N::translate('on the date of death') . ')'; } else { $ageText = '(' . $age . ' ' . I18N::translate('after death') . ')'; // Family events which occur after death are probably errors if ($event->getParent() instanceof Family) { $ageText .= '<i class="icon-warning"></i>'; } } } } if ($ageText) { $html .= ' <span class="age">' . $ageText . '</span>'; } } } elseif ($record instanceof Family) { $indi = Individual::getInstance($pid, $record->getTree()); if ($indi) { $birth_date = $indi->getBirthDate(); $death_date = $indi->getDeathDate(); $ageText = ''; if (Date::compare($date, $death_date) <= 0) { $age = Date::getAgeGedcom($birth_date, $date); // Only show calculated age if it differs from recorded age if ($age != '' && $age > 0) { if ($fact_age != '' && $fact_age != $age || $fact_age == '' && $husb_age == '' && $wife_age == '' || $husb_age != '' && $indi->getSex() == 'M' && $husb_age != $age || $wife_age != '' && $indi->getSex() == 'F' && $wife_age != $age) { $ageText = '(' . I18N::translate('Age') . ' ' . FunctionsDate::getAgeAtEvent($age, false) . ')'; } } } if ($ageText) { $html .= ' <span class="age">' . $ageText . '</span>'; } } } } else { // 1 DEAT Y with no DATE => print YES // 1 BIRT 2 SOUR @S1@ => print YES // 1 DEAT N is not allowed // It is not proper GEDCOM form to use a N(o) value with an event tag to infer that it did not happen. $factdetail = explode(' ', trim($factrec)); if (isset($factdetail) && (count($factdetail) == 3 && strtoupper($factdetail[2]) == 'Y') || count($factdetail) == 4 && $factdetail[2] == 'SOUR') { $html .= I18N::translate('yes'); } } // print gedcom ages foreach (array(GedcomTag::getLabel('AGE') => $fact_age, GedcomTag::getLabel('HUSB') => $husb_age, GedcomTag::getLabel('WIFE') => $wife_age) as $label => $age) { if ($age != '') { $html .= ' <span class="label">' . $label . ':</span> <span class="age">' . FunctionsDate::getAgeAtEvent($age, false) . '</span>'; } } return $html; }
/** * Does this module display a particular fact * * @param Fact $fact * * @return bool */ public static function showFact(Fact $fact) { switch ($fact->getTag()) { case 'AFN': case 'CHAN': case 'IDNO': case 'REFN': case 'RFN': case 'RIN': case 'SSN': case '_UID': return true; default: return false; } }
/** * Print a fact record, for the individual/family/source/repository/etc. pages. * * Although a Fact has a parent object, we also need to know * the GedcomRecord for which we are printing it. For example, * we can show the death of X on the page of Y, or the marriage * of X+Y on the page of Z. We need to know both records to * calculate ages, relationships, etc. * * @param Fact $fact * @param GedcomRecord $record */ public static function printFact(Fact $fact, GedcomRecord $record) { static $n_chil = 0, $n_gchi = 0; $parent = $fact->getParent(); // Some facts don't get printed here ... switch ($fact->getTag()) { case 'NOTE': self::printMainNotes($fact, 1); return; case 'SOUR': self::printMainSources($fact, 1); return; case 'OBJE': self::printMainMedia($fact, 1); return; case 'FAMC': case 'FAMS': case 'CHIL': case 'HUSB': case 'WIFE': // These are internal links, not facts return; case '_WT_OBJE_SORT': // These links are used internally to record the sort order. return; default: // Hide unrecognized/custom tags? if ($fact->getParent()->getTree()->getPreference('HIDE_GEDCOM_ERRORS') && !GedcomTag::isTag($fact->getTag())) { return; } break; } // Who is this fact about? Need it to translate fact label correctly if ($parent instanceof Family && $record instanceof Individual) { // Family event $label_person = $fact->getParent()->getSpouse($record); } else { // Individual event $label_person = $parent; } // New or deleted facts need different styling $styleadd = ''; if ($fact->isPendingAddition()) { $styleadd = 'new'; } if ($fact->isPendingDeletion()) { $styleadd = 'old'; } // Event of close relative if (preg_match('/^_[A-Z_]{3,5}_[A-Z0-9]{4}$/', $fact->getTag())) { $styleadd = trim($styleadd . ' rela'); } // Event of close associates if ($fact->getFactId() == 'asso') { $styleadd = trim($styleadd . ' rela'); } // historical facts if ($fact->getFactId() == 'histo') { $styleadd = trim($styleadd . ' histo'); } // Does this fact have a type? if (preg_match('/\\n2 TYPE (.+)/', $fact->getGedcom(), $match)) { $type = $match[1]; } else { $type = ''; } switch ($fact->getTag()) { case 'EVEN': case 'FACT': if (GedcomTag::isTag($type)) { // Some users (just Meliza?) use "1 EVEN/2 TYPE BIRT". Translate the TYPE. $label = GedcomTag::getLabel($type, $label_person); $type = ''; // Do not print this again } elseif ($type) { // We don't have a translation for $type - but a custom translation might exist. $label = I18N::translate(Filter::escapeHtml($type)); $type = ''; // Do not print this again } else { // An unspecified fact/event $label = $fact->getLabel(); } break; case 'MARR': // This is a hack for a proprietory extension. Is it still used/needed? $utype = strtoupper($type); if ($utype == 'CIVIL' || $utype == 'PARTNERS' || $utype == 'RELIGIOUS') { $label = GedcomTag::getLabel('MARR_' . $utype, $label_person); $type = ''; // Do not print this again } else { $label = $fact->getLabel(); } break; default: // Normal fact/event $label = $fact->getLabel(); break; } echo '<tr class="', $styleadd, '">'; echo '<td class="descriptionbox width20">'; if ($fact->getParent()->getTree()->getPreference('SHOW_FACT_ICONS')) { echo Theme::theme()->icon($fact), ' '; } if ($fact->getFactId() != 'histo' && $fact->canEdit()) { ?> <a href="#" title="<?php echo I18N::translate('Edit'); ?> " onclick="return edit_record('<?php echo $parent->getXref(); ?> ', '<?php echo $fact->getFactId(); ?> ');" ><?php echo $label; ?> </a> <div class="editfacts"> <div class="editlink"> <a href="#" title="<?php echo I18N::translate('Edit'); ?> " class="editicon" onclick="return edit_record('<?php echo $parent->getXref(); ?> ', '<?php echo $fact->getFactId(); ?> ');" ><span class="link_text"><?php echo I18N::translate('Edit'); ?> </span></a> </div> <div class="copylink"> <a href="#" title="<?php echo I18N::translate('Copy'); ?> " class="copyicon" onclick="return copy_fact('<?php echo $parent->getXref(); ?> ', '<?php echo $fact->getFactId(); ?> ');" ><span class="link_text"><?php echo I18N::translate('Copy'); ?> </span></a> </div> <div class="deletelink"> <a href="#" title="<?php echo I18N::translate('Delete'); ?> " class="deleteicon" onclick="return delete_fact('<?php echo I18N::translate('Are you sure you want to delete this fact?'); ?> ', '<?php echo $parent->getXref(); ?> ', '<?php echo $fact->getFactId(); ?> ');" ><span class="link_text"><?php echo I18N::translate('Delete'); ?> </span></a> </div> </div> <?php } else { echo $label; } switch ($fact->getTag()) { case '_BIRT_CHIL': echo '<br>', I18N::translate('#%s', ++$n_chil); break; case '_BIRT_GCHI': case '_BIRT_GCH1': case '_BIRT_GCH2': echo '<br>', I18N::translate('#%s', ++$n_gchi); break; } echo '</td><td class="optionbox ', $styleadd, ' wrap">'; // Event from another record? if ($parent !== $record) { if ($parent instanceof Family) { foreach ($parent->getSpouses() as $spouse) { if ($record !== $spouse) { echo '<a href="', $spouse->getHtmlUrl(), '">', $spouse->getFullName(), '</a> — '; } } echo '<a href="', $parent->getHtmlUrl(), '">', I18N::translate('View family'), '</a><br>'; } elseif ($parent instanceof Individual) { echo '<a href="', $parent->getHtmlUrl(), '">', $parent->getFullName(), '</a><br>'; } } // Print the value of this fact/event switch ($fact->getTag()) { case 'ADDR': echo $fact->getValue(); break; case 'AFN': echo '<div class="field"><a href="https://familysearch.org/search/tree/results#count=20&query=afn:', rawurlencode($fact->getValue()), '" target="new">', Filter::escapeHtml($fact->getValue()), '</a></div>'; break; case 'ASSO': // we handle this later, in format_asso_rela_record() break; case 'EMAIL': case 'EMAI': case '_EMAIL': echo '<div class="field"><a href="mailto:', Filter::escapeHtml($fact->getValue()), '">', Filter::escapeHtml($fact->getValue()), '</a></div>'; break; case 'FILE': if (Auth::isEditor($fact->getParent()->getTree())) { echo '<div class="field">', Filter::escapeHtml($fact->getValue()), '</div>'; } break; case 'RESN': echo '<div class="field">'; switch ($fact->getValue()) { case 'none': // Note: "1 RESN none" is not valid gedcom. // However, webtrees privacy rules will interpret it as "show an otherwise private record to public". echo '<i class="icon-resn-none"></i> ', I18N::translate('Show to visitors'); break; case 'privacy': echo '<i class="icon-class-none"></i> ', I18N::translate('Show to members'); break; case 'confidential': echo '<i class="icon-confidential-none"></i> ', I18N::translate('Show to managers'); break; case 'locked': echo '<i class="icon-locked-none"></i> ', I18N::translate('Only managers can edit'); break; default: echo Filter::escapeHtml($fact->getValue()); break; } echo '</div>'; break; case 'PUBL': // Publication details might contain URLs. echo '<div class="field">', Filter::expandUrls($fact->getValue()), '</div>'; break; case 'REPO': if (preg_match('/^@(' . WT_REGEX_XREF . ')@$/', $fact->getValue(), $match)) { self::printRepositoryRecord($match[1]); } else { echo '<div class="error">', Filter::escapeHtml($fact->getValue()), '</div>'; } break; case 'URL': case '_URL': case 'WWW': echo '<div class="field"><a href="', Filter::escapeHtml($fact->getValue()), '">', Filter::escapeHtml($fact->getValue()), '</a></div>'; break; case 'TEXT': // 0 SOUR / 1 TEXT echo '<div class="field">', nl2br(Filter::escapeHtml($fact->getValue()), false), '</div>'; break; default: // Display the value for all other facts/events switch ($fact->getValue()) { case '': // Nothing to display break; case 'N': // Not valid GEDCOM echo '<div class="field">', I18N::translate('No'), '</div>'; break; case 'Y': // Do not display "Yes". break; default: if (preg_match('/^@(' . WT_REGEX_XREF . ')@$/', $fact->getValue(), $match)) { $target = GedcomRecord::getInstance($match[1], $fact->getParent()->getTree()); if ($target) { echo '<div><a href="', $target->getHtmlUrl(), '">', $target->getFullName(), '</a></div>'; } else { echo '<div class="error">', Filter::escapeHtml($fact->getValue()), '</div>'; } } else { echo '<div class="field"><span dir="auto">', Filter::escapeHtml($fact->getValue()), '</span></div>'; } break; } break; } // Print the type of this fact/event if ($type) { $utype = strtoupper($type); // Events of close relatives, e.g. _MARR_CHIL if (substr($fact->getTag(), 0, 6) == '_MARR_' && ($utype == 'CIVIL' || $utype == 'PARTNERS' || $utype == 'RELIGIOUS')) { // Translate MARR/TYPE using the code that supports MARR_CIVIL, etc. tags $type = GedcomTag::getLabel('MARR_' . $utype); } else { // Allow (custom) translations for other types $type = I18N::translate($type); } echo GedcomTag::getLabelValue('TYPE', Filter::escapeHtml($type)); } // Print the date of this fact/event echo FunctionsPrint::formatFactDate($fact, $record, true, true); // Print the place of this fact/event echo '<div class="place">', FunctionsPrint::formatFactPlace($fact, true, true, true), '</div>'; // A blank line between the primary attributes (value, date, place) and the secondary ones echo '<br>'; $addr = $fact->getAttribute('ADDR'); if ($addr) { echo GedcomTag::getLabelValue('ADDR', $addr); } // Print the associates of this fact/event if ($fact->getFactId() !== 'asso') { echo self::formatAssociateRelationship($fact); } // Print any other "2 XXXX" attributes, in the order in which they appear. preg_match_all('/\\n2 (' . WT_REGEX_TAG . ') (.+)/', $fact->getGedcom(), $matches, PREG_SET_ORDER); foreach ($matches as $match) { switch ($match[1]) { case 'DATE': case 'TIME': case 'AGE': case 'PLAC': case 'ADDR': case 'ALIA': case 'ASSO': case '_ASSO': case 'DESC': case 'RELA': case 'STAT': case 'TEMP': case 'TYPE': case 'FAMS': case 'CONT': // These were already shown at the beginning break; case 'NOTE': case 'OBJE': case 'SOUR': // These will be shown at the end break; case 'EVEN': // 0 SOUR / 1 DATA / 2 EVEN / 3 DATE / 3 PLAC $events = array(); foreach (preg_split('/ *, */', $match[2]) as $event) { $events[] = GedcomTag::getLabel($event); } if (count($events) == 1) { echo GedcomTag::getLabelValue('EVEN', $event); } else { echo GedcomTag::getLabelValue('EVEN', implode(I18N::$list_separator, $events)); } if (preg_match('/\\n3 DATE (.+)/', $fact->getGedcom(), $date_match)) { $date = new Date($date_match[1]); echo GedcomTag::getLabelValue('DATE', $date->display()); } if (preg_match('/\\n3 PLAC (.+)/', $fact->getGedcom(), $plac_match)) { echo GedcomTag::getLabelValue('PLAC', $plac_match[1]); } break; case 'FAMC': // 0 INDI / 1 ADOP / 2 FAMC / 3 ADOP $family = Family::getInstance(str_replace('@', '', $match[2]), $fact->getParent()->getTree()); if ($family) { echo GedcomTag::getLabelValue('FAM', '<a href="' . $family->getHtmlUrl() . '">' . $family->getFullName() . '</a>'); if (preg_match('/\\n3 ADOP (HUSB|WIFE|BOTH)/', $fact->getGedcom(), $match)) { echo GedcomTag::getLabelValue('ADOP', GedcomCodeAdop::getValue($match[1], $label_person)); } } else { echo GedcomTag::getLabelValue('FAM', '<span class="error">' . $match[2] . '</span>'); } break; case '_WT_USER': $user = User::findByIdentifier($match[2]); // may not exist if ($user) { echo GedcomTag::getLabelValue('_WT_USER', $user->getRealNameHtml()); } else { echo GedcomTag::getLabelValue('_WT_USER', Filter::escapeHtml($match[2])); } break; case 'RESN': switch ($match[2]) { case 'none': // Note: "2 RESN none" is not valid gedcom. // However, webtrees privacy rules will interpret it as "show an otherwise private fact to public". echo GedcomTag::getLabelValue('RESN', '<i class="icon-resn-none"></i> ' . I18N::translate('Show to visitors')); break; case 'privacy': echo GedcomTag::getLabelValue('RESN', '<i class="icon-resn-privacy"></i> ' . I18N::translate('Show to members')); break; case 'confidential': echo GedcomTag::getLabelValue('RESN', '<i class="icon-resn-confidential"></i> ' . I18N::translate('Show to managers')); break; case 'locked': echo GedcomTag::getLabelValue('RESN', '<i class="icon-resn-locked"></i> ' . I18N::translate('Only managers can edit')); break; default: echo GedcomTag::getLabelValue('RESN', Filter::escapeHtml($match[2])); break; } break; case 'CALN': echo GedcomTag::getLabelValue('CALN', Filter::expandUrls($match[2])); break; case 'FORM': // 0 OBJE / 1 FILE / 2 FORM / 3 TYPE echo GedcomTag::getLabelValue('FORM', $match[2]); if (preg_match('/\\n3 TYPE (.+)/', $fact->getGedcom(), $type_match)) { echo GedcomTag::getLabelValue('TYPE', GedcomTag::getFileFormTypeValue($type_match[1])); } break; case 'URL': case '_URL': case 'WWW': $link = '<a href="' . Filter::escapeHtml($match[2]) . '">' . Filter::escapeHtml($match[2]) . '</a>'; echo GedcomTag::getLabelValue($fact->getTag() . ':' . $match[1], $link); break; default: if (!$fact->getParent()->getTree()->getPreference('HIDE_GEDCOM_ERRORS') || GedcomTag::isTag($match[1])) { if (preg_match('/^@(' . WT_REGEX_XREF . ')@$/', $match[2], $xmatch)) { // Links $linked_record = GedcomRecord::getInstance($xmatch[1], $fact->getParent()->getTree()); if ($linked_record) { $link = '<a href="' . $linked_record->getHtmlUrl() . '">' . $linked_record->getFullName() . '</a>'; echo GedcomTag::getLabelValue($fact->getTag() . ':' . $match[1], $link); } else { echo GedcomTag::getLabelValue($fact->getTag() . ':' . $match[1], Filter::escapeHtml($match[2])); } } else { // Non links echo GedcomTag::getLabelValue($fact->getTag() . ':' . $match[1], Filter::escapeHtml($match[2])); } } break; } } echo self::printFactSources($fact->getGedcom(), 2); echo FunctionsPrint::printFactNotes($fact->getGedcom(), 2); self::printMediaLinks($fact->getGedcom(), 2); echo '</td></tr>'; }
/** * Static method to compare two events by their type. * * @param Fact $a Fact one * @param Fact $b Fact two * * @return int */ public static function compareType(Fact $a, Fact $b) { global $factsort; if (empty($factsort)) { $factsort = array_flip(array('BIRT', '_HNM', 'ALIA', '_AKA', '_AKAN', 'ADOP', '_ADPF', '_ADPF', '_BRTM', 'CHR', 'BAPM', 'FCOM', 'CONF', 'BARM', 'BASM', 'EDUC', 'GRAD', '_DEG', 'EMIG', 'IMMI', 'NATU', '_MILI', '_MILT', 'ENGA', 'MARB', 'MARC', 'MARL', '_MARI', '_MBON', 'MARR', 'MARR_CIVIL', 'MARR_RELIGIOUS', 'MARR_PARTNERS', 'MARR_UNKNOWN', '_COML', '_STAT', '_SEPR', 'DIVF', 'MARS', '_BIRT_CHIL', 'DIV', 'ANUL', '_BIRT_', '_MARR_', '_DEAT_', '_BURI_', 'CENS', 'OCCU', 'RESI', 'PROP', 'CHRA', 'RETI', 'FACT', 'EVEN', '_NMR', '_NMAR', 'NMR', 'NCHI', 'WILL', '_HOL', '_????_', 'DEAT', '_FNRL', 'CREM', 'BURI', '_INTE', '_YART', '_NLIV', 'PROB', 'TITL', 'COMM', 'NATI', 'CITN', 'CAST', 'RELI', 'SSN', 'IDNO', 'TEMP', 'SLGC', 'BAPL', 'CONL', 'ENDL', 'SLGS', 'ADDR', 'PHON', 'EMAIL', '_EMAIL', 'EMAL', 'FAX', 'WWW', 'URL', '_URL', 'FILE', 'AFN', 'REFN', '_PRMN', 'REF', 'RIN', '_UID', 'OBJE', 'NOTE', 'SOUR', 'CHAN', '_TODO')); } // Facts from same families stay grouped together // Keep MARR and DIV from the same families from mixing with events from other FAMs // Use the original order in which the facts were added if ($a->parent instanceof Family && $b->parent instanceof Family && $a->parent !== $b->parent) { return $a->sortOrder - $b->sortOrder; } $atag = $a->getTag(); $btag = $b->getTag(); // Events not in the above list get mapped onto one that is. if (!array_key_exists($atag, $factsort)) { if (preg_match('/^(_(BIRT|MARR|DEAT|BURI)_)/', $atag, $match)) { $atag = $match[1]; } else { $atag = "_????_"; } } if (!array_key_exists($btag, $factsort)) { if (preg_match('/^(_(BIRT|MARR|DEAT|BURI)_)/', $btag, $match)) { $btag = $match[1]; } else { $btag = "_????_"; } } // - Don't let dated after DEAT/BURI facts sort non-dated facts before DEAT/BURI // - Treat dated after BURI facts as BURI instead if ($a->getAttribute('DATE') !== null && $factsort[$atag] > $factsort['BURI'] && $factsort[$atag] < $factsort['CHAN']) { $atag = 'BURI'; } if ($b->getAttribute('DATE') !== null && $factsort[$btag] > $factsort['BURI'] && $factsort[$btag] < $factsort['CHAN']) { $btag = 'BURI'; } $ret = $factsort[$atag] - $factsort[$btag]; // If facts are the same then put dated facts before non-dated facts if ($ret == 0) { if ($a->getAttribute('DATE') !== null && $b->getAttribute('DATE') === null) { return -1; } if ($b->getAttribute('DATE') !== null && $a->getAttribute('DATE') === null) { return 1; } // If no sorting preference, then keep original ordering $ret = $a->sortOrder - $b->sortOrder; } return $ret; }
/** * Display an icon for this fact. * * @param Fact $fact * * @return string */ public function icon(Fact $fact) { $icon = 'images/facts/' . $fact->getTag() . '.png'; $dir = substr($this->assetUrl(), strlen(WT_STATIC_URL)); if (file_exists($dir . $icon)) { return '<img src="' . $this->assetUrl() . $icon . '" title="' . GedcomTag::getLabel($fact->getTag()) . '">'; } elseif (file_exists($dir . 'images/facts/NULL.png')) { // Spacer image - for alignment - until we move to a sprite. return '<img src="' . Theme::theme()->assetUrl() . 'images/facts/NULL.png">'; } else { return ''; } }
/** * Create a form to edit a Fact object. * * @param GedcomRecord $record * @param Fact $fact * * @return string */ public static function createEditForm(GedcomRecord $record, Fact $fact) { global $tags, $WT_TREE; $pid = $record->getXref(); $tags = array(); $gedlines = explode("\n", $fact->getGedcom()); $linenum = 0; $fields = explode(' ', $gedlines[$linenum]); $glevel = $fields[0]; $level = $glevel; $type = $fact->getTag(); $parent = $fact->getParent(); $level0type = $parent::RECORD_TYPE; $level1type = $type; $i = $linenum; $inSource = false; $levelSource = 0; $add_date = true; // List of tags we would expect at the next level // NB add_missing_subtags() already takes care of the simple cases // where a level 1 tag is missing a level 2 tag. Here we only need to // handle the more complicated cases. $expected_subtags = array('SOUR' => array('PAGE', 'DATA'), 'DATA' => array('TEXT'), 'PLAC' => array('MAP'), 'MAP' => array('LATI', 'LONG')); if ($record->getTree()->getPreference('FULL_SOURCES')) { $expected_subtags['SOUR'][] = 'QUAY'; $expected_subtags['DATA'][] = 'DATE'; } if (preg_match_all('/(' . WT_REGEX_TAG . ')/', $record->getTree()->getPreference('ADVANCED_PLAC_FACTS'), $match)) { $expected_subtags['PLAC'] = array_merge($match[1], $expected_subtags['PLAC']); } $stack = array(0 => $level0type); // Loop on existing tags : while (true) { // Keep track of our hierarchy, e.g. 1=>BIRT, 2=>PLAC, 3=>FONE $stack[(int) $level] = $type; // Merge them together, e.g. BIRT:PLAC:FONE $label = implode(':', array_slice($stack, 1, $level)); $text = ''; for ($j = 2; $j < count($fields); $j++) { if ($j > 2) { $text .= ' '; } $text .= $fields[$j]; } $text = rtrim($text); while ($i + 1 < count($gedlines) && preg_match("/" . ($level + 1) . ' CONT ?(.*)/', $gedlines[$i + 1], $cmatch) > 0) { $text .= "\n" . $cmatch[1]; $i++; } if ($type === 'SOUR') { $inSource = true; $levelSource = $level; } elseif ($levelSource >= $level) { $inSource = false; } if ($type !== 'DATA' && $type !== 'CONT') { $tags[] = $type; $person = Individual::getInstance($pid, $WT_TREE); $subrecord = $level . ' ' . $type . ' ' . $text; if ($inSource && $type === 'DATE') { self::addSimpleTag($subrecord, '', GedcomTag::getLabel($label, $person)); } elseif (!$inSource && $type === 'DATE') { self::addSimpleTag($subrecord, $level1type, GedcomTag::getLabel($label, $person)); if ($level === '2') { // We already have a date - no need to add one. $add_date = false; } } elseif ($type === 'STAT') { self::addSimpleTag($subrecord, $level1type, GedcomTag::getLabel($label, $person)); } elseif ($level0type === 'REPO') { $repo = Repository::getInstance($pid, $WT_TREE); self::addSimpleTag($subrecord, $level0type, GedcomTag::getLabel($label, $repo)); } else { self::addSimpleTag($subrecord, $level0type, GedcomTag::getLabel($label, $person)); } } // Get a list of tags present at the next level $subtags = array(); for ($ii = $i + 1; isset($gedlines[$ii]) && preg_match('/^\\s*(\\d+)\\s+(\\S+)/', $gedlines[$ii], $mm) && $mm[1] > $level; ++$ii) { if ($mm[1] == $level + 1) { $subtags[] = $mm[2]; } } // Insert missing tags if (!empty($expected_subtags[$type])) { foreach ($expected_subtags[$type] as $subtag) { if (!in_array($subtag, $subtags)) { if (!$inSource || $subtag !== 'DATA') { self::addSimpleTag($level + 1 . ' ' . $subtag, '', GedcomTag::getLabel($label . ':' . $subtag)); } if (!empty($expected_subtags[$subtag])) { foreach ($expected_subtags[$subtag] as $subsubtag) { self::addSimpleTag($level + 2 . ' ' . $subsubtag, '', GedcomTag::getLabel($label . ':' . $subtag . ':' . $subsubtag)); } } } } } // Awkward special cases if ($level == 2 && $type === 'DATE' && in_array($level1type, Config::dateAndTime()) && !in_array('TIME', $subtags)) { self::addSimpleTag('3 TIME'); // TIME is NOT a valid 5.5.1 tag } if ($level == 2 && $type === 'STAT' && GedcomCodeTemp::isTagLDS($level1type) && !in_array('DATE', $subtags)) { self::addSimpleTag('3 DATE', '', GedcomTag::getLabel('STAT:DATE')); } $i++; if (isset($gedlines[$i])) { $fields = explode(' ', $gedlines[$i]); $level = $fields[0]; if (isset($fields[1])) { $type = trim($fields[1]); } else { $level = 0; } } else { $level = 0; } if ($level <= $glevel) { break; } } if ($level1type !== '_PRIM') { self::insertMissingSubtags($level1type, $add_date); } return $level1type; }