/** * Format an anniversary display. * * @param Fact $fact * @param bool $show_places * * @return string */ function calendar_fact_text(Fact $fact, $show_places) { $text = $fact->getLabel() . ' — ' . $fact->getDate()->display(true, null, false); if ($fact->anniv) { $text .= ' (' . I18N::translate('%s year anniversary', $fact->anniv) . ')'; } if ($show_places && $fact->getAttribute('PLAC')) { $text .= ' — ' . $fact->getAttribute('PLAC'); } return $text; }
/** * 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 a form to add an individual or edit an individual’s name * * @param string $nextaction * @param Individual $person * @param Family $family * @param Fact $name_fact * @param string $famtag * @param string $gender */ function print_indi_form($nextaction, Individual $person = null, Family $family = null, Fact $name_fact = null, $famtag = 'CHIL', $gender = 'U') { global $WT_TREE, $bdm, $controller; if ($person) { $xref = $person->getXref(); } elseif ($family) { $xref = $family->getXref(); } else { $xref = 'new'; } // Different cultures do surnames differently $surname_tradition = SurnameTradition::create($WT_TREE->getPreference('SURNAME_TRADITION')); $name_fields = array(); if ($name_fact) { // Editing an existing name $name_fact_id = $name_fact->getFactId(); $name_type = $name_fact->getAttribute('TYPE'); $namerec = $name_fact->getGedcom(); foreach (Config::standardNameFacts() as $tag) { if ($tag === 'NAME') { $name_fields[$tag] = $name_fact->getValue(); } else { $name_fields[$tag] = $name_fact->getAttribute($tag); } } // Populate any missing 2 XXXX fields from the 1 NAME field $npfx_accept = implode('|', Config::namePrefixes()); if (preg_match('/(((' . $npfx_accept . ')\\.? +)*)([^\\n\\/"]*)("(.*)")? *\\/(([a-z]{2,3} +)*)(.*)\\/ *(.*)/i', $name_fields['NAME'], $name_bits)) { if (empty($name_fields['NPFX'])) { $name_fields['NPFX'] = $name_bits[1]; } if (empty($name_fields['SPFX']) && empty($name_fields['SURN'])) { $name_fields['SPFX'] = trim($name_bits[7]); // For names with two surnames, there will be four slashes. // Turn them into a list $name_fields['SURN'] = preg_replace('~/[^/]*/~', ',', $name_bits[9]); } if (empty($name_fields['GIVN'])) { $name_fields['GIVN'] = $name_bits[4]; } if (empty($name_fields['NICK']) && !empty($name_bits[6]) && !preg_match('/^2 NICK/m', $namerec)) { $name_fields['NICK'] = $name_bits[6]; } } } else { // Creating a new name $name_fact_id = null; $name_type = null; $namerec = null; // Populate the standard NAME field and subfields foreach (Config::standardNameFacts() as $tag) { $name_fields[$tag] = ''; } // Inherit surname from parents, spouse or child if ($family) { $father = $family->getHusband(); if ($father && $father->getFirstFact('NAME')) { $father_name = $father->getFirstFact('NAME')->getValue(); } else { $father_name = ''; } $mother = $family->getWife(); if ($mother && $mother->getFirstFact('NAME')) { $mother_name = $mother->getFirstFact('NAME')->getValue(); } else { $mother_name = ''; } } else { $father = null; $mother = null; $father_name = ''; $mother_name = ''; } if ($person && $person->getFirstFact('NAME')) { $indi_name = $person->getFirstFact('NAME')->getValue(); } else { $indi_name = ''; } switch ($nextaction) { case 'add_child_to_family_action': $name_fields = $surname_tradition->newChildNames($father_name, $mother_name, $gender) + $name_fields; break; case 'add_child_to_individual_action': if ($person->getSex() === 'F') { $name_fields = $surname_tradition->newChildNames('', $indi_name, $gender) + $name_fields; } else { $name_fields = $surname_tradition->newChildNames($indi_name, '', $gender) + $name_fields; } break; case 'add_parent_to_individual_action': $name_fields = $surname_tradition->newParentNames($indi_name, $gender) + $name_fields; break; case 'add_spouse_to_family_action': if ($father) { $name_fields = $surname_tradition->newSpouseNames($father_name, $gender) + $name_fields; } else { $name_fields = $surname_tradition->newSpouseNames($mother_name, $gender) + $name_fields; } break; case 'add_spouse_to_individual_action': $name_fields = $surname_tradition->newSpouseNames($indi_name, $gender) + $name_fields; break; case 'add_unlinked_indi_action': case 'update': if ($surname_tradition->hasSurnames()) { $name_fields['NAME'] = '//'; } break; } } $bdm = ''; // used to copy '1 SOUR' to '2 SOUR' for BIRT DEAT MARR echo '<div id="edit_interface-page">'; echo '<h4>', $controller->getPageTitle(), '</h4>'; FunctionsPrint::initializeCalendarPopup(); echo '<form method="post" name="addchildform" onsubmit="return checkform();">'; echo '<input type="hidden" name="ged" value="', $WT_TREE->getNameHtml(), '">'; echo '<input type="hidden" name="action" value="', $nextaction, '">'; echo '<input type="hidden" name="fact_id" value="', $name_fact_id, '">'; echo '<input type="hidden" name="xref" value="', $xref, '">'; echo '<input type="hidden" name="famtag" value="', $famtag, '">'; echo '<input type="hidden" name="gender" value="', $gender, '">'; echo '<input type="hidden" name="goto" value="">'; // set by javascript echo Filter::getCsrf(); echo '<table class="facts_table">'; switch ($nextaction) { case 'add_child_to_family_action': case 'add_child_to_individual_action': // When adding a new child, specify the pedigree FunctionsEdit::addSimpleTag('0 PEDI'); break; case 'update': // When adding/editing a name, specify the type FunctionsEdit::addSimpleTag('0 TYPE ' . $name_type, '', '', null, $person); break; } // First - new/existing standard name fields foreach ($name_fields as $tag => $value) { if (substr_compare($tag, '_', 0, 1) !== 0) { FunctionsEdit::addSimpleTag('0 ' . $tag . ' ' . $value); } } // Second - new/existing advanced name fields if ($surname_tradition->hasMarriedNames() || preg_match('/\\n2 _MARNM /', $namerec)) { $adv_name_fields = array('_MARNM' => ''); } else { $adv_name_fields = array(); } if (preg_match_all('/(' . WT_REGEX_TAG . ')/', $WT_TREE->getPreference('ADVANCED_NAME_FACTS'), $match)) { foreach ($match[1] as $tag) { $adv_name_fields[$tag] = ''; } } foreach (array_keys($adv_name_fields) as $tag) { // Edit existing tags, grouped together if (preg_match_all('/2 ' . $tag . ' (.+)/', $namerec, $match)) { foreach ($match[1] as $value) { FunctionsEdit::addSimpleTag('2 ' . $tag . ' ' . $value, '', GedcomTag::getLabel('NAME:' . $tag, $person)); if ($tag === '_MARNM') { preg_match_all('/\\/([^\\/]*)\\//', $value, $matches); FunctionsEdit::addSimpleTag('2 _MARNM_SURN ' . implode(',', $matches[1])); } } } // Allow a new tag to be entered if (!array_key_exists($tag, $name_fields)) { FunctionsEdit::addSimpleTag('0 ' . $tag, '', GedcomTag::getLabel('NAME:' . $tag, $person)); if ($tag === '_MARNM') { FunctionsEdit::addSimpleTag('0 _MARNM_SURN'); } } } // Third - new/existing custom name fields foreach ($name_fields as $tag => $value) { if (substr_compare($tag, '_', 0, 1) === 0) { FunctionsEdit::addSimpleTag('0 ' . $tag . ' ' . $value); if ($tag === '_MARNM') { preg_match_all('/\\/([^\\/]*)\\//', $value, $matches); FunctionsEdit::addSimpleTag('2 _MARNM_SURN ' . implode(',', $matches[1])); } } } // Fourth - SOUR, NOTE, _CUSTOM, etc. if ($namerec) { $gedlines = explode("\n", $namerec); // -- find the number of lines in the record $fields = explode(' ', $gedlines[0]); $glevel = $fields[0]; $level = $glevel; $type = trim($fields[1]); $tags = array(); $i = 0; do { if ($type !== 'TYPE' && !array_key_exists($type, $name_fields) && !array_key_exists($type, $adv_name_fields)) { $text = ''; for ($j = 2; $j < count($fields); $j++) { if ($j > 2) { $text .= ' '; } $text .= $fields[$j]; } while ($i + 1 < count($gedlines) && preg_match('/' . ($level + 1) . ' CONT ?(.*)/', $gedlines[$i + 1], $cmatch) > 0) { $text .= "\n" . $cmatch[2]; $i++; } FunctionsEdit::addSimpleTag($level . ' ' . $type . ' ' . $text); } $tags[] = $type; $i++; if (isset($gedlines[$i])) { $fields = explode(' ', $gedlines[$i]); $level = $fields[0]; if (isset($fields[1])) { $type = $fields[1]; } } } while ($level > $glevel && $i < count($gedlines)); } // If we are adding a new individual, add the basic details if ($nextaction !== 'update') { echo '</table><br><table class="facts_table">'; // 1 SEX if ($famtag === 'HUSB' || $gender === 'M') { FunctionsEdit::addSimpleTag("0 SEX M"); } elseif ($famtag === 'WIFE' || $gender === 'F') { FunctionsEdit::addSimpleTag('0 SEX F'); } else { FunctionsEdit::addSimpleTag('0 SEX'); } $bdm = 'BD'; if (preg_match_all('/(' . WT_REGEX_TAG . ')/', $WT_TREE->getPreference('QUICK_REQUIRED_FACTS'), $matches)) { foreach ($matches[1] as $match) { if (!in_array($match, explode('|', WT_EVENTS_DEAT))) { FunctionsEdit::addSimpleTags($match); } } } //-- if adding a spouse add the option to add a marriage fact to the new family if ($nextaction === 'add_spouse_to_individual_action' || $nextaction === 'add_spouse_to_family_action') { $bdm .= 'M'; if (preg_match_all('/(' . WT_REGEX_TAG . ')/', $WT_TREE->getPreference('QUICK_REQUIRED_FAMFACTS'), $matches)) { foreach ($matches[1] as $match) { FunctionsEdit::addSimpleTags($match); } } } if (preg_match_all('/(' . WT_REGEX_TAG . ')/', $WT_TREE->getPreference('QUICK_REQUIRED_FACTS'), $matches)) { foreach ($matches[1] as $match) { if (in_array($match, explode('|', WT_EVENTS_DEAT))) { FunctionsEdit::addSimpleTags($match); } } } } echo keep_chan($person); echo '</table>'; if ($nextaction === 'update') { // GEDCOM 5.5.1 spec says NAME doesn’t get a OBJE FunctionsEdit::printAddLayer('SOUR'); FunctionsEdit::printAddLayer('NOTE'); FunctionsEdit::printAddLayer('SHARED_NOTE'); FunctionsEdit::printAddLayer('RESN'); } else { FunctionsEdit::printAddLayer('SOUR', 1); FunctionsEdit::printAddLayer('NOTE', 1); FunctionsEdit::printAddLayer('SHARED_NOTE', 1); FunctionsEdit::printAddLayer('RESN', 1); } // If we are editing an existing name, allow raw GEDCOM editing if ($name_fact && (Auth::isAdmin() || $WT_TREE->getPreference('SHOW_GEDCOM_RECORD'))) { echo '<br><br><a href="edit_interface.php?action=editrawfact&xref=', $xref, '&fact_id=', $name_fact->getFactId(), '&ged=', $WT_TREE->getNameUrl(), '">', I18N::translate('Edit raw GEDCOM'), '</a>'; } echo '<p id="save-cancel">'; echo '<input type="submit" class="save" value="', I18N::translate('save'), '">'; if (preg_match('/^add_(child|spouse|parent|unlinked_indi)/', $nextaction)) { echo '<input type="submit" class="save" value="', I18N::translate('go to new individual'), '" onclick="document.addchildform.goto.value=\'new\';">'; } echo '<input type="button" class="cancel" value="', I18N::translate('close'), '" onclick="window.close();">'; echo '</p>'; echo '</form>'; $controller->addInlineJavascript(' SURNAME_TRADITION="' . $WT_TREE->getPreference('SURNAME_TRADITION') . '"; gender="' . $gender . '"; famtag="' . $famtag . '"; function trim(str) { str=str.replace(/\\s\\s+/g, " "); return str.replace(/(^\\s+)|(\\s+$)/g, ""); } function lang_class(str) { if (str.match(/[\\u0370-\\u03FF]/)) return "greek"; if (str.match(/[\\u0400-\\u04FF]/)) return "cyrillic"; if (str.match(/[\\u0590-\\u05FF]/)) return "hebrew"; if (str.match(/[\\u0600-\\u06FF]/)) return "arabic"; return "latin"; // No matched text implies latin :-) } // Generate a full name from the name components function generate_name() { var npfx = jQuery("#NPFX").val(); var givn = jQuery("#GIVN").val(); var spfx = jQuery("#SPFX").val(); var surn = jQuery("#SURN").val(); var nsfx = jQuery("#NSFX").val(); if (SURNAME_TRADITION === "polish" && (gender === "F" || famtag === "WIFE")) { surn = surn.replace(/ski$/, "ska"); surn = surn.replace(/cki$/, "cka"); surn = surn.replace(/dzki$/, "dzka"); surn = surn.replace(/żki$/, "żka"); } // Commas are used in the GIVN and SURN field to separate lists of surnames. // For example, to differentiate the two Spanish surnames from an English // double-barred name. // Commas *may* be used in other fields, and will form part of the NAME. if (WT_LOCALE=="vi" || WT_LOCALE=="hu") { // Default format: /SURN/ GIVN return trim(npfx+" /"+trim(spfx+" "+surn).replace(/ *, */g, " ")+"/ "+givn.replace(/ *, */g, " ")+" "+nsfx); } else if (WT_LOCALE=="zh") { // Default format: /SURN/GIVN return trim(npfx+" /"+trim(spfx+" "+surn).replace(/ *, */g, " ")+"/"+givn.replace(/ *, */g, " ")+" "+nsfx); } else { // Default format: GIVN /SURN/ return trim(npfx+" "+givn.replace(/ *, */g, " ")+" /"+trim(spfx+" "+surn).replace(/ *, */g, " ")+"/ "+nsfx); } } // Update the NAME and _MARNM fields from the name components // and also display the value in read-only "gedcom" format. function updatewholename() { // Don’t update the name if the user manually changed it if (manualChange) { return; } var npfx = jQuery("#NPFX").val(); var givn = jQuery("#GIVN").val(); var spfx = jQuery("#SPFX").val(); var surn = jQuery("#SURN").val(); var nsfx = jQuery("#NSFX").val(); var name = generate_name(); jQuery("#NAME").val(name); jQuery("#NAME_display").text(name); // Married names inherit some NSFX values, but not these nsfx = nsfx.replace(/^(I|II|III|IV|V|VI|Junior|Jr\\.?|Senior|Sr\\.?)$/i, ""); // Update _MARNM field from _MARNM_SURN field and display it // Be careful of mixing latin/hebrew/etc. character sets. var ip = document.getElementsByTagName("input"); var marnm_id = ""; var romn = ""; var heb = ""; for (var i = 0; i < ip.length; i++) { var val = trim(ip[i].value); if (ip[i].id.indexOf("_HEB") === 0) heb = val; if (ip[i].id.indexOf("ROMN") === 0) romn = val; if (ip[i].id.indexOf("_MARNM") === 0) { if (ip[i].id.indexOf("_MARNM_SURN") === 0) { var msurn = ""; if (val !== "") { var lc = lang_class(document.getElementById(ip[i].id).value); if (lang_class(name) === lc) msurn = trim(npfx + " " + givn + " /" + val + "/ " + nsfx); else if (lc === "hebrew") msurn = heb.replace(/\\/.*\\//, "/" + val + "/"); else if (lang_class(romn) === lc) msurn = romn.replace(/\\/.*\\//, "/" + val + "/"); } document.getElementById(marnm_id).value = msurn; document.getElementById(marnm_id+"_display").innerHTML = msurn; } else { marnm_id = ip[i].id; } } } } // Toggle the name editor fields between // <input type="hidden"> <span style="display:inline"> // <input type="text"> <span style="display:hidden"> var oldName = ""; // Calls to generate_name() trigger an update - hence need to // set the manual change to true first. We are probably // listening to the wrong events on the input fields... var manualChange = true; manualChange = generate_name() !== jQuery("#NAME").val(); function convertHidden(eid) { var input1 = jQuery("#" + eid); var input2 = jQuery("#" + eid + "_display"); // Note that IE does not allow us to change the type of an input, so we must create a new one. if (input1.attr("type")=="hidden") { input1.replaceWith(input1.clone().attr("type", "text")); input2.hide(); } else { input1.replaceWith(input1.clone().attr("type", "hidden")); input2.show(); } } /** * if the user manually changed the NAME field, then update the textual * HTML representation of it * If the value changed set manualChange to true so that changing * the other fields doesn’t change the NAME line */ function updateTextName(eid) { var element = document.getElementById(eid); if (element) { if (element.value!=oldName) manualChange = true; var delement = document.getElementById(eid+"_display"); if (delement) { delement.innerHTML = element.value; } } } function checkform() { var ip=document.getElementsByTagName("input"); for (var i=0; i<ip.length; i++) { // ADD slashes to _HEB and _AKA names if (ip[i].id.indexOf("_AKA")==0 || ip[i].id.indexOf("_HEB")==0 || ip[i].id.indexOf("ROMN")==0) if (ip[i].value.indexOf("/")<0 && ip[i].value!="") ip[i].value=ip[i].value.replace(/([^\\s]+)\\s*$/, "/$1/"); // Blank out temporary _MARNM_SURN if (ip[i].id.indexOf("_MARNM_SURN")==0) ip[i].value=""; // Convert "xxx yyy" and "xxx y yyy" surnames to "xxx,yyy" if ((SURNAME_TRADITION=="spanish" || "SURNAME_TRADITION"=="portuguese") && ip[i].id.indexOf("SURN")==0) { ip[i].value=document.forms[0].SURN.value.replace(/^\\s*([^\\s,]{2,})\\s+([iIyY] +)?([^\\s,]{2,})\\s*$/, "$1,$3"); } } return true; } // If the name isn’t initially formed from the components in a standard way, // then don’t automatically update it. if (document.getElementById("NAME").value!=generate_name() && document.getElementById("NAME").value!="//") { convertHidden("NAME"); } '); echo '</div>'; }
/** * 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; }