/** * Performs a search and replace * * @param Tree $tree */ private function searchAndReplace(Tree $tree) { $this->generalSearch(); //-- don't try to make any changes if nothing was found if (!$this->myindilist && !$this->myfamlist && !$this->mysourcelist && !$this->mynotelist) { return; } Log::addEditLog("Search And Replace old:" . $this->query . " new:" . $this->replace); $adv_name_tags = preg_split("/[\\s,;: ]+/", $tree->getPreference('ADVANCED_NAME_FACTS')); $name_tags = array_unique(array_merge(Config::standardNameFacts(), $adv_name_tags)); $name_tags[] = '_MARNM'; $records_updated = 0; foreach ($this->myindilist as $id => $record) { $old_record = $record->getGedcom(); $new_record = $old_record; if ($this->replaceAll) { $new_record = preg_replace("~" . $this->query . "~i", $this->replace, $new_record); } else { if ($this->replaceNames) { foreach ($name_tags as $tag) { $new_record = preg_replace("~(\\d) " . $tag . " (.*)" . $this->query . "(.*)~i", "\$1 " . $tag . " \$2" . $this->replace . "\$3", $new_record); } } if ($this->replacePlaces) { if ($this->replacePlacesWord) { $new_record = preg_replace('~(\\d) PLAC (.*)([,\\W\\s])' . $this->query . '([,\\W\\s])~i', "\$1 PLAC \$2\$3" . $this->replace . "\$4", $new_record); } else { $new_record = preg_replace("~(\\d) PLAC (.*)" . $this->query . "(.*)~i", "\$1 PLAC \$2" . $this->replace . "\$3", $new_record); } } } //-- if the record changed replace the record otherwise remove it from the search results if ($new_record !== $old_record) { $record->updateRecord($new_record, true); $records_updated++; } else { unset($this->myindilist[$id]); } } if ($records_updated) { FlashMessages::addMessage(I18N::plural('%s individual has been updated.', '%s individuals have been updated.', $records_updated, I18N::number($records_updated))); } $records_updated = 0; foreach ($this->myfamlist as $id => $record) { $old_record = $record->getGedcom(); $new_record = $old_record; if ($this->replaceAll) { $new_record = preg_replace("~" . $this->query . "~i", $this->replace, $new_record); } else { if ($this->replacePlaces) { if ($this->replacePlacesWord) { $new_record = preg_replace('~(\\d) PLAC (.*)([,\\W\\s])' . $this->query . '([,\\W\\s])~i', "\$1 PLAC \$2\$3" . $this->replace . "\$4", $new_record); } else { $new_record = preg_replace("~(\\d) PLAC (.*)" . $this->query . "(.*)~i", "\$1 PLAC \$2" . $this->replace . "\$3", $new_record); } } } //-- if the record changed replace the record otherwise remove it from the search results if ($new_record !== $old_record) { $record->updateRecord($new_record, true); $records_updated++; } else { unset($this->myfamlist[$id]); } } if ($records_updated) { FlashMessages::addMessage(I18N::plural('%s family has been updated.', '%s families have been updated.', $records_updated, I18N::number($records_updated))); } $records_updated = 0; foreach ($this->mysourcelist as $id => $record) { $old_record = $record->getGedcom(); $new_record = $old_record; if ($this->replaceAll) { $new_record = preg_replace("~" . $this->query . "~i", $this->replace, $new_record); } else { if ($this->replaceNames) { $new_record = preg_replace("~(\\d) TITL (.*)" . $this->query . "(.*)~i", "\$1 TITL \$2" . $this->replace . "\$3", $new_record); $new_record = preg_replace("~(\\d) ABBR (.*)" . $this->query . "(.*)~i", "\$1 ABBR \$2" . $this->replace . "\$3", $new_record); } if ($this->replacePlaces) { if ($this->replacePlacesWord) { $new_record = preg_replace('~(\\d) PLAC (.*)([,\\W\\s])' . $this->query . '([,\\W\\s])~i', "\$1 PLAC \$2\$3" . $this->replace . "\$4", $new_record); } else { $new_record = preg_replace("~(\\d) PLAC (.*)" . $this->query . "(.*)~i", "\$1 PLAC \$2" . $this->replace . "\$3", $new_record); } } } //-- if the record changed replace the record otherwise remove it from the search results if ($new_record !== $old_record) { $record->updateRecord($new_record, true); $records_updated++; } else { unset($this->mysourcelist[$id]); } } if ($records_updated) { FlashMessages::addMessage(I18N::plural('%s source has been updated.', '%s sources have been updated.', $records_updated, I18N::number($records_updated))); } $records_updated = 0; foreach ($this->mynotelist as $id => $record) { $old_record = $record->getGedcom(); $new_record = $old_record; if ($this->replaceAll) { $new_record = preg_replace("~" . $this->query . "~i", $this->replace, $new_record); } //-- if the record changed replace the record otherwise remove it from the search results if ($new_record != $old_record) { $record->updateRecord($new_record, true); $records_updated++; } else { unset($this->mynotelist[$id]); } } if ($records_updated) { FlashMessages::addMessage(I18N::plural('%s note has been updated.', '%s notes have been updated.', $records_updated, I18N::number($records_updated))); } }
/** * 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>'; }