<?php /** * webtrees: online genealogy * Copyright (C) 2016 webtrees development team * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ namespace Fisharebest\Webtrees\Functions; use Fisharebest\Webtrees\Auth; use Fisharebest\Webtrees\Census\Census; use Fisharebest\Webtrees\Census\CensusOfCzechRepublic; use Fisharebest\Webtrees\Census\CensusOfDenmark; use Fisharebest\Webtrees\Census\CensusOfEngland; use Fisharebest\Webtrees\Census\CensusOfFrance; use Fisharebest\Webtrees\Census\CensusOfScotland; use Fisharebest\Webtrees\Census\CensusOfUnitedStates; use Fisharebest\Webtrees\Census\CensusOfWales; use Fisharebest\Webtrees\Config; use Fisharebest\Webtrees\Date; use Fisharebest\Webtrees\Fact; use Fisharebest\Webtrees\Family; use Fisharebest\Webtrees\Filter; use Fisharebest\Webtrees\GedcomCode\GedcomCodeAdop; use Fisharebest\Webtrees\GedcomCode\GedcomCodeName; use Fisharebest\Webtrees\GedcomCode\GedcomCodePedi; use Fisharebest\Webtrees\GedcomCode\GedcomCodeQuay; use Fisharebest\Webtrees\GedcomCode\GedcomCodeRela; use Fisharebest\Webtrees\GedcomCode\GedcomCodeStat; use Fisharebest\Webtrees\GedcomCode\GedcomCodeTemp; use Fisharebest\Webtrees\GedcomRecord; use Fisharebest\Webtrees\GedcomTag; use Fisharebest\Webtrees\I18N; use Fisharebest\Webtrees\Individual; use Fisharebest\Webtrees\Media; use Fisharebest\Webtrees\Module; use Fisharebest\Webtrees\Note; use Fisharebest\Webtrees\Repository; use Fisharebest\Webtrees\Source; use Fisharebest\Webtrees\User; use Rhumsaa\Uuid\Uuid; /** * Class FunctionsEdit - common functions */ class FunctionsEdit { /** * Create a <select> control for a form. * * @param string $name * @param string[] $values * @param string|null $empty * @param string $selected * @param string $extra * * @return string */ public static function selectEditControl($name, $values, $empty, $selected, $extra = '') { if (is_null($empty)) { $html = ''; } else { if (empty($selected)) { $html = '<option value="" selected>' . Filter::escapeHtml($empty) . '</option>'; } else { $html = '<option value="">' . Filter::escapeHtml($empty) . '</option>'; } } // A completely empty list would be invalid, and break various things if (empty($values) && empty($html)) { $html = '<option value=""></option>'; } foreach ($values as $key => $value) { // PHP array keys are cast to integers! Cast them back if ((string) $key === (string) $selected) { $html .= '<option value="' . Filter::escapeHtml($key) . '" selected dir="auto">' . Filter::escapeHtml($value) . '</option>'; } else { $html .= '<option value="' . Filter::escapeHtml($key) . '" dir="auto">' . Filter::escapeHtml($value) . '</option>'; } } if (substr($name, -2) === '[]') { // id attribute is not used for arrays return '<select name="' . $name . '" ' . $extra . '>' . $html . '</select>'; } else { return '<select id="' . $name . '" name="' . $name . '" ' . $extra . '>' . $html . '</select>'; } } /** * Create a set of radio buttons for a form * * @param string $name The ID for the form element * @param string[] $values Array of value=>display items * @param string $selected The currently selected item * @param string $extra Additional markup for the label * * @return string */ public static function radioButtons($name, $values, $selected, $extra = '') { $html = ''; foreach ($values as $key => $value) { $html .= '<label ' . $extra . '>' . '<input type="radio" name="' . $name . '" value="' . Filter::escapeHtml($key) . '"'; // PHP array keys are cast to integers! Cast them back if ((string) $key === (string) $selected) { $html .= ' checked'; } $html .= '>' . Filter::escapeHtml($value) . '</label>'; } return $html; } /** * Print an edit control for a Yes/No field * * @param string $name * @param bool $selected * @param string $extra * * @return string */ public static function editFieldYesNo($name, $selected = false, $extra = '') { return self::radioButtons( $name, array(I18N::translate('no'), I18N::translate('yes')), $selected, $extra ); } /** * Print an edit control for a checkbox. * * @param string $name * @param bool $is_checked * @param string $extra * * @return string */ public static function checkbox($name, $is_checked = false, $extra = '') { return '<input type="checkbox" name="' . $name . '" value="1" ' . ($is_checked ? 'checked ' : '') . $extra . '>'; } /** * Print an edit control for a checkbox, with a hidden field to store one of the two states. * By default, a checkbox is either set, or not sent. * This function gives us a three options, set, unset or not sent. * Useful for dynamically generated forms where we don't know what elements are present. * * @param string $name * @param int $is_checked 0 or 1 * @param string $extra * * @return string */ public static function twoStateCheckbox($name, $is_checked = 0, $extra = '') { return '<input type="hidden" id="' . $name . '" name="' . $name . '" value="' . ($is_checked ? 1 : 0) . '">' . '<input type="checkbox" name="' . $name . '-GUI-ONLY" value="1"' . ($is_checked ? ' checked' : '') . ' onclick="document.getElementById(\'' . $name . '\').value=(this.checked?1:0);" ' . $extra . '>'; } /** * Function edit_language_checkboxes * * @param string $parameter_name * @param array $accepted_languages * * @return string */ public static function editLanguageCheckboxes($parameter_name, $accepted_languages) { $html = ''; foreach (I18N::activeLocales() as $locale) { $html .= '<div class="checkbox">'; $html .= '<label title="' . $locale->languageTag() . '">'; $html .= '<input type="checkbox" name="' . $parameter_name . '[]" value="' . $locale->languageTag() . '"'; $html .= in_array($locale->languageTag(), $accepted_languages) ? ' checked>' : '>'; $html .= $locale->endonym(); $html .= '</label>'; $html .= '</div>'; } return $html; } /** * Print an edit control for access level. * * @param string $name * @param string $selected * @param string $extra * * @return string */ public static function editFieldAccessLevel($name, $selected = '', $extra = '') { $ACCESS_LEVEL = array( Auth::PRIV_PRIVATE => I18N::translate('Show to visitors'), Auth::PRIV_USER => I18N::translate('Show to members'), Auth::PRIV_NONE => I18N::translate('Show to managers'), Auth::PRIV_HIDE => I18N::translate('Hide from everyone'), ); return self::selectEditControl($name, $ACCESS_LEVEL, null, $selected, $extra); } /** * Print an edit control for a RESN field. * * @param string $name * @param string $selected * @param string $extra * * @return string */ public static function editFieldRestriction($name, $selected = '', $extra = '') { $RESN = array( '' => '', 'none' => I18N::translate('Show to visitors'), // Not valid GEDCOM, but very useful 'privacy' => I18N::translate('Show to members'), 'confidential' => I18N::translate('Show to managers'), 'locked' => I18N::translate('Only managers can edit'), ); return self::selectEditControl($name, $RESN, null, $selected, $extra); } /** * Print an edit control for a contact method field. * * @param string $name * @param string $selected * @param string $extra * * @return string */ public static function editFieldContact($name, $selected = '', $extra = '') { // Different ways to contact the users $CONTACT_METHODS = array( 'messaging' => I18N::translate('Internal messaging'), 'messaging2' => I18N::translate('Internal messaging with emails'), 'messaging3' => I18N::translate('webtrees sends emails with no storage'), 'mailto' => I18N::translate('Mailto link'), 'none' => I18N::translate('No contact'), ); return self::selectEditControl($name, $CONTACT_METHODS, null, $selected, $extra); } /** * Print an edit control for a language field. * * @param string $name * @param string $selected * @param string $extra * * @return string */ public static function editFieldLanguage($name, $selected = '', $extra = '') { $languages = array(); foreach (I18N::activeLocales() as $locale) { $languages[$locale->languageTag()] = $locale->endonym(); } return self::selectEditControl($name, $languages, null, $selected, $extra); } /** * Print an edit control for a range of integers. * * @param string $name * @param string $selected * @param int $min * @param int $max * @param string $extra * * @return string */ public static function editFieldInteger($name, $selected = '', $min, $max, $extra = '') { $array = array(); for ($i = $min; $i <= $max; ++$i) { $array[$i] = I18N::number($i); } return self::selectEditControl($name, $array, null, $selected, $extra); } /** * Print an edit control for a username. * * @param string $name * @param string $selected * @param string $extra * * @return string */ public static function editFieldUsername($name, $selected = '', $extra = '') { $users = array(); foreach (User::all() as $user) { $users[$user->getUserName()] = $user->getRealName() . ' - ' . $user->getUserName(); } // The currently selected user may not exist if ($selected && !array_key_exists($selected, $users)) { $users[$selected] = $selected; } return self::selectEditControl($name, $users, '-', $selected, $extra); } /** * Print an edit control for a ADOP field. * * @param string $name * @param string $selected * @param string $extra * @param Individual|null $individual * * @return string */ public static function editFieldAdoption($name, $selected = '', $extra = '', Individual $individual = null) { return self::selectEditControl($name, GedcomCodeAdop::getValues($individual), null, $selected, $extra); } /** * Print an edit control for a PEDI field. * * @param string $name * @param string $selected * @param string $extra * @param Individual|null $individual * * @return string */ public static function editFieldPedigree($name, $selected = '', $extra = '', Individual $individual = null) { return self::selectEditControl($name, GedcomCodePedi::getValues($individual), '', $selected, $extra); } /** * Print an edit control for a NAME TYPE field. * * @param string $name * @param string $selected * @param string $extra * @param Individual|null $individual * * @return string */ public static function editFieldNameType($name, $selected = '', $extra = '', Individual $individual = null) { return self::selectEditControl($name, GedcomCodeName::getValues($individual), '', $selected, $extra); } /** * Print an edit control for a RELA field. * * @param string $name * @param string $selected * @param string $extra * * @return string */ public static function editFieldRelationship($name, $selected = '', $extra = '') { $rela_codes = GedcomCodeRela::getValues(); // The user is allowed to specify values that aren't in the list. if (!array_key_exists($selected, $rela_codes)) { $rela_codes[$selected] = I18N::translate($selected); } return self::selectEditControl($name, $rela_codes, '', $selected, $extra); } /** * Remove all links from $gedrec to $xref, and any sub-tags. * * @param string $gedrec * @param string $xref * * @return string */ public static function removeLinks($gedrec, $xref) { $gedrec = preg_replace('/\n1 ' . WT_REGEX_TAG . ' @' . $xref . '@(\n[2-9].*)*/', '', $gedrec); $gedrec = preg_replace('/\n2 ' . WT_REGEX_TAG . ' @' . $xref . '@(\n[3-9].*)*/', '', $gedrec); $gedrec = preg_replace('/\n3 ' . WT_REGEX_TAG . ' @' . $xref . '@(\n[4-9].*)*/', '', $gedrec); $gedrec = preg_replace('/\n4 ' . WT_REGEX_TAG . ' @' . $xref . '@(\n[5-9].*)*/', '', $gedrec); $gedrec = preg_replace('/\n5 ' . WT_REGEX_TAG . ' @' . $xref . '@(\n[6-9].*)*/', '', $gedrec); return $gedrec; } /** * Generates javascript code for calendar popup in user’s language. * * @param string $id * * @return string */ public static function printCalendarPopup($id) { return ' <a href="#" onclick="cal_toggleDate(\'caldiv' . $id . '\', \'' . $id . '\'); return false;" class="icon-button_calendar" title="' . I18N::translate('Select a date') . '"></a>' . '<div id="caldiv' . $id . '" style="position:absolute;visibility:hidden;background-color:white;z-index:1000;"></div>'; } /** * An HTML link to create a new media object. * * @param string $element_id * * @return string */ public static function printAddNewMediaLink($element_id) { return '<a href="#" onclick="pastefield=document.getElementById(\'' . $element_id . '\'); window.open(\'addmedia.php?action=showmediaform\', \'_blank\', edit_window_specs); return false;" class="icon-button_addmedia" title="' . I18N::translate('Create a media object') . '"></a>'; } /** * An HTML link to create a new repository. * * @param string $element_id * * @return string */ public static function printAddNewRepositoryLink($element_id) { return '<a href="#" onclick="addnewrepository(document.getElementById(\'' . $element_id . '\')); return false;" class="icon-button_addrepository" title="' . I18N::translate('Create a repository') . '"></a>'; } /** * An HTML link to create a new note. * * @param string $element_id * * @return string */ public static function printAddNewNoteLink($element_id) { return '<a href="#" onclick="addnewnote(document.getElementById(\'' . $element_id . '\')); return false;" class="icon-button_addnote" title="' . I18N::translate('Create a shared note') . '"></a>'; } /** * An HTML link to edit a note. * * @param string $note_id * * @return string */ public static function printEditNoteLink($note_id) { return '<a href="#" onclick="edit_note(\'' . $note_id . '\'); return false;" class="icon-button_note" title="' . I18N::translate('Edit the shared note') . '"></a>'; } /** * An HTML link to create a new source. * * @param string $element_id * * @return string */ public static function printAddNewSourceLink($element_id) { return '<a href="#" onclick="addnewsource(document.getElementById(\'' . $element_id . '\')); return false;" class="icon-button_addsource" title="' . I18N::translate('Create a source') . '"></a>'; } /** * add a new tag input field * * called for each fact to be edited on a form. * Fact level=0 means a new empty form : data are POSTed by name * else data are POSTed using arrays : * glevels[] : tag level * islink[] : tag is a link * tag[] : tag name * text[] : tag value * * @param string $tag fact record to edit (eg 2 DATE xxxxx) * @param string $upperlevel optional upper level tag (eg BIRT) * @param string $label An optional label to echo instead of the default * @param string $extra optional text to display after the input field * @param Individual $person For male/female translations * * @return string */ public static function addSimpleTag($tag, $upperlevel = '', $label = '', $extra = null, Individual $person = null) { global $tags, $main_fact, $xref, $bdm, $action, $WT_TREE; // Keep track of SOUR fields, so we can reference them in subsequent PAGE fields. static $source_element_id; $subnamefacts = array('NPFX', 'GIVN', 'SPFX', 'SURN', 'NSFX', '_MARNM_SURN'); preg_match('/^(?:(\d+) (' . WT_REGEX_TAG . ') ?(.*))/', $tag, $match); list(, $level, $fact, $value) = $match; $level = (int) $level; // element name : used to POST data if ($level === 0) { if ($upperlevel) { $element_name = $upperlevel . '_' . $fact; } else { $element_name = $fact; } } else { $element_name = 'text[]'; } if ($level === 1) { $main_fact = $fact; } // element id : used by javascript functions if ($level === 0) { $element_id = $fact; } else { $element_id = $fact . Uuid::uuid4(); } if ($upperlevel) { $element_id = $upperlevel . '_' . $fact . Uuid::uuid4(); } // field value $islink = (substr($value, 0, 1) === '@' && substr($value, 0, 2) !== '@#'); if ($islink) { $value = trim(substr($tag, strlen($fact) + 3), ' @\r'); } else { $value = (string) substr($tag, strlen($fact) + 3); } if ($fact === 'REPO' || $fact === 'SOUR' || $fact === 'OBJE' || $fact === 'FAMC') { $islink = true; } if ($fact === 'SHARED_NOTE_EDIT' || $fact === 'SHARED_NOTE') { $islink = true; $fact = 'NOTE'; } // label echo '<tr id="', $element_id, '_tr"'; if ($fact === 'DATA' || $fact === 'MAP' || ($fact === 'LATI' || $fact === 'LONG') && $value === '') { echo ' style="display:none;"'; } echo '>'; if (in_array($fact, $subnamefacts) || $fact === 'LATI' || $fact === 'LONG') { echo '<td class="optionbox wrap width25">'; } else { echo '<td class="descriptionbox wrap width25">'; } // tag name if ($label) { echo $label; } elseif ($upperlevel) { echo GedcomTag::getLabel($upperlevel . ':' . $fact); } else { echo GedcomTag::getLabel($fact); } // If using GEDFact-assistant window if ($action === 'addnewnote_assisted') { // Do not print on GEDFact Assistant window } else { // Not all facts have help text. switch ($fact) { case 'NAME': if ($upperlevel !== 'REPO' && $upperlevel !== 'UNKNOWN') { echo FunctionsPrint::helpLink($fact); } break; case 'DATE': case 'PLAC': case 'RESN': case 'ROMN': case 'SURN': case '_HEB': echo FunctionsPrint::helpLink($fact); break; } } // tag level if ($level > 0) { echo '<input type="hidden" name="glevels[]" value="', $level, '">'; echo '<input type="hidden" name="islink[]" value="', $islink, '">'; echo '<input type="hidden" name="tag[]" value="', $fact, '">'; } echo '</td>'; // value echo '<td class="optionbox wrap">'; // retrieve linked NOTE if ($fact === 'NOTE' && $islink) { $note1 = Note::getInstance($value, $WT_TREE); if ($note1) { $noterec = $note1->getGedcom(); preg_match('/' . $value . '/i', $noterec, $notematch); $value = $notematch[0]; } } // Show names for spouses in MARR/HUSB/AGE and MARR/WIFE/AGE if ($fact === 'HUSB' || $fact === 'WIFE') { $family = Family::getInstance($xref, $WT_TREE); if ($family) { $spouse_link = $family->getFirstFact($fact); if ($spouse_link) { $spouse = $spouse_link->getTarget(); if ($spouse) { echo $spouse->getFullName(); } } } } if (in_array($fact, Config::emptyFacts()) && ($value === '' || $value === 'Y' || $value === 'y')) { echo '<input type="hidden" id="', $element_id, '" name="', $element_name, '" value="', $value, '">'; if ($level <= 1) { echo '<input type="checkbox" '; if ($value) { echo 'checked'; } echo ' onclick="document.getElementById(\'' . $element_id . '\').value = (this.checked) ? \'Y\' : \'\';">'; echo I18N::translate('yes'); } if ($fact === 'CENS' && $value === 'Y') { echo self::censusDateSelector(WT_LOCALE, $xref); if (Module::getModuleByName('GEDFact_assistant') && GedcomRecord::getInstance($xref, $WT_TREE) instanceof Individual) { echo '<div></div><a href="#" style="display: none;" id="assistant-link" onclick="return activateCensusAssistant();">' . I18N::translate('Create a shared note using the census assistant') . '</a></div>'; } } } elseif ($fact === 'TEMP') { echo self::selectEditControl($element_name, GedcomCodeTemp::templeNames(), I18N::translate('No temple - living ordinance'), $value); } elseif ($fact === 'ADOP') { echo self::editFieldAdoption($element_name, $value, '', $person); } elseif ($fact === 'PEDI') { echo self::editFieldPedigree($element_name, $value, '', $person); } elseif ($fact === 'STAT') { echo self::selectEditControl($element_name, GedcomCodeStat::statusNames($upperlevel), '', $value); } elseif ($fact === 'RELA') { echo self::editFieldRelationship($element_name, strtolower($value)); } elseif ($fact === 'QUAY') { echo self::selectEditControl($element_name, GedcomCodeQuay::getValues(), '', $value); } elseif ($fact === '_WT_USER') { echo self::editFieldUsername($element_name, $value); } elseif ($fact === 'RESN') { echo self::editFieldRestriction($element_name, $value); } elseif ($fact === '_PRIM') { echo '<select id="', $element_id, '" name="', $element_name, '" >'; echo '<option value=""></option>'; echo '<option value="Y" '; if ($value === 'Y') { echo ' selected'; } echo '>', /* I18N: option in list box “always use this image” */ I18N::translate('always'), '</option>'; echo '<option value="N" '; if ($value === 'N') { echo 'selected'; } echo '>', /* I18N: option in list box “never use this image” */ I18N::translate('never'), '</option>'; echo '</select>'; echo '<p class="small text-muted">', I18N::translate('Use this image for charts and on the individual’s page.'), '</p>'; } elseif ($fact === 'SEX') { echo '<select id="', $element_id, '" name="', $element_name, '"><option value="M" '; if ($value === 'M') { echo 'selected'; } echo '>', I18N::translate('Male'), '</option><option value="F" '; if ($value === 'F') { echo 'selected'; } echo '>', I18N::translate('Female'), '</option><option value="U" '; if ($value === 'U' || empty($value)) { echo 'selected'; } echo '>', I18N::translateContext('unknown gender', 'Unknown'), '</option></select>'; } elseif ($fact === 'TYPE' && $level === 3) { //-- Build the selector for the Media 'TYPE' Fact echo '<select name="text[]"><option selected value="" ></option>'; $selectedValue = strtolower($value); if (!array_key_exists($selectedValue, GedcomTag::getFileFormTypes())) { echo '<option selected value="', Filter::escapeHtml($value), '" >', Filter::escapeHtml($value), '</option>'; } foreach (GedcomTag::getFileFormTypes() as $typeName => $typeValue) { echo '<option value="', $typeName, '" '; if ($selectedValue === $typeName) { echo 'selected'; } echo '>', $typeValue, '</option>'; } echo '</select>'; } elseif (($fact === 'NAME' && $upperlevel !== 'REPO' && $upperlevel !== 'UNKNOWN') || $fact === '_MARNM') { // Populated in javascript from sub-tags echo '<input type="hidden" id="', $element_id, '" name="', $element_name, '" onchange="updateTextName(\'', $element_id, '\');" value="', Filter::escapeHtml($value), '" class="', $fact, '">'; echo '<span id="', $element_id, '_display" dir="auto">', Filter::escapeHtml($value), '</span>'; echo ' <a href="#edit_name" onclick="convertHidden(\'', $element_id, '\'); return false;" class="icon-edit_indi" title="' . I18N::translate('Edit the name') . '"></a>'; } else { // textarea if ($fact === 'TEXT' || $fact === 'ADDR' || ($fact === 'NOTE' && !$islink)) { echo '<textarea id="', $element_id, '" name="', $element_name, '" dir="auto">', Filter::escapeHtml($value), '</textarea><br>'; } else { // text // If using GEDFact-assistant window if ($action === 'addnewnote_assisted') { echo '<input type="text" id="', $element_id, '" name="', $element_name, '" value="', Filter::escapeHtml($value), '" style="width:4.1em;" dir="ltr"'; } else { echo '<input type="text" id="', $element_id, '" name="', $element_name, '" value="', Filter::escapeHtml($value), '" dir="ltr"'; } echo ' class="', $fact, '"'; if (in_array($fact, $subnamefacts)) { echo ' onblur="updatewholename();" onkeyup="updatewholename();"'; } // Extra markup for specific fact types switch ($fact) { case 'ALIA': case 'ASSO': case '_ASSO': echo ' data-autocomplete-type="ASSO" data-autocomplete-extra="input.DATE"'; break; case 'DATE': echo ' onblur="valid_date(this);" onmouseout="valid_date(this);"'; break; case 'GIVN': echo ' autofocus data-autocomplete-type="GIVN"'; break; case 'LATI': echo ' onblur="valid_lati_long(this, \'N\', \'S\');" onmouseout="valid_lati_long(this, \'N\', \'S\');"'; break; case 'LONG': echo ' onblur="valid_lati_long(this, \'E\', \'W\');" onmouseout="valid_lati_long(this, \'E\', \'W\');"'; break; case 'NOTE': // Shared notes. Inline notes are handled elsewhere. echo ' data-autocomplete-type="NOTE"'; break; case 'OBJE': echo ' data-autocomplete-type="OBJE"'; break; case 'PAGE': echo ' data-autocomplete-type="PAGE" data-autocomplete-extra="#' . $source_element_id . '"'; break; case 'PLAC': echo ' data-autocomplete-type="PLAC"'; break; case 'REPO': echo ' data-autocomplete-type="REPO"'; break; case 'SOUR': $source_element_id = $element_id; echo ' data-autocomplete-type="SOUR"'; break; case 'SURN': case '_MARNM_SURN': echo ' data-autocomplete-type="SURN"'; break; case 'TIME': echo ' pattern="([0-1][0-9]|2[0-3]):[0-5][0-9](:[0-5][0-9])?" dir="ltr" placeholder="' . /* I18N: Examples of valid time formats (hours:minutes:seconds) */ I18N::translate('hh:mm or hh:mm:ss') . '"'; break; } echo '>'; } $tmp_array = array('TYPE', 'TIME', 'NOTE', 'SOUR', 'REPO', 'OBJE', 'ASSO', '_ASSO', 'AGE'); // split PLAC if ($fact === 'PLAC') { echo '<div id="', $element_id, '_pop" style="display: inline;">'; echo FunctionsPrint::printSpecialCharacterLink($element_id), ' ', FunctionsPrint::printFindPlaceLink($element_id); echo '<span onclick="jQuery(\'tr[id^=', $upperlevel, '_LATI],tr[id^=', $upperlevel, '_LONG],tr[id^=LATI],tr[id^=LONG]\').toggle(\'fast\'); return false;" class="icon-target" title="', GedcomTag::getLabel('LATI'), ' / ', GedcomTag::getLabel('LONG'), '"></span>'; echo '</div>'; if (Module::getModuleByName('places_assistant')) { \PlacesAssistantModule::setup_place_subfields($element_id); \PlacesAssistantModule::print_place_subfields($element_id); } } elseif (!in_array($fact, $tmp_array)) { echo FunctionsPrint::printSpecialCharacterLink($element_id); } } // MARRiage TYPE : hide text field and show a selection list if ($fact === 'TYPE' && $level === 2 && $tags[0] === 'MARR') { echo '<script>'; echo 'document.getElementById(\'', $element_id, '\').style.display=\'none\''; echo '</script>'; echo '<select id="', $element_id, '_sel" onchange="document.getElementById(\'', $element_id, '\').value=this.value;" >'; foreach (array('Unknown', 'Civil', 'Religious', 'Partners') as $key) { if ($key === 'Unknown') { echo '<option value="" '; } else { echo '<option value="', $key, '" '; } $a = strtolower($key); $b = strtolower($value); if ($b !== '' && strpos($a, $b) !== false || strpos($b, $a) !== false) { echo 'selected'; } echo '>', GedcomTag::getLabel('MARR_' . strtoupper($key)), '</option>'; } echo '</select>'; } elseif ($fact === 'TYPE' && $level === 0) { // NAME TYPE : hide text field and show a selection list $onchange = 'onchange="document.getElementById(\'' . $element_id . '\').value=this.value;"'; echo self::editFieldNameType($element_name, $value, $onchange, $person); echo '<script>document.getElementById("', $element_id, '").style.display="none";</script>'; } // popup links switch ($fact) { case 'DATE': echo self::printCalendarPopup($element_id); break; case 'FAMC': case 'FAMS': echo FunctionsPrint::printFindFamilyLink($element_id); break; case 'ALIA': case 'ASSO': case '_ASSO': echo FunctionsPrint::printFindIndividualLink($element_id, $element_id . '_description'); break; case 'FILE': FunctionsPrint::printFindMediaLink($element_id, '0file'); break; case 'SOUR': echo FunctionsPrint::printFindSourceLink($element_id, $element_id . '_description'), ' ', self::printAddNewSourceLink($element_id); //-- checkboxes to apply '1 SOUR' to BIRT/MARR/DEAT as '2 SOUR' if ($level === 1) { echo '<br>'; switch ($WT_TREE->getPreference('PREFER_LEVEL2_SOURCES')) { case '2': // records $level1_checked = 'checked'; $level2_checked = ''; break; case '1': // facts $level1_checked = ''; $level2_checked = 'checked'; break; case '0': // none default: $level1_checked = ''; $level2_checked = ''; break; } if (strpos($bdm, 'B') !== false) { echo ' <label><input type="checkbox" name="SOUR_INDI" ', $level1_checked, ' value="1">', I18N::translate('Individual'), '</label>'; 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))) { echo ' <label><input type="checkbox" name="SOUR_', $match, '" ', $level2_checked, ' value="1">', GedcomTag::getLabel($match), '</label>'; } } } } if (strpos($bdm, 'D') !== false) { 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))) { echo ' <label><input type="checkbox" name="SOUR_', $match, '"', $level2_checked, ' value="1">', GedcomTag::getLabel($match), '</label>'; } } } } if (strpos($bdm, 'M') !== false) { echo ' <label><input type="checkbox" name="SOUR_FAM" ', $level1_checked, ' value="1">', I18N::translate('Family'), '</label>'; if (preg_match_all('/(' . WT_REGEX_TAG . ')/', $WT_TREE->getPreference('QUICK_REQUIRED_FAMFACTS'), $matches)) { foreach ($matches[1] as $match) { echo ' <label><input type="checkbox" name="SOUR_', $match, '"', $level2_checked, ' value="1">', GedcomTag::getLabel($match), '</label>'; } } } } break; case 'REPO': echo FunctionsPrint::printFindRepositoryLink($element_id), ' ', self::printAddNewRepositoryLink($element_id); break; case 'NOTE': // Shared Notes Icons ======================================== if ($islink) { // Print regular Shared Note icons --------------------------- echo ' ', FunctionsPrint::printFindNoteLink($element_id, $element_id . '_description'), ' ', self::printAddNewNoteLink($element_id); if ($value) { echo ' ', self::printEditNoteLink($value); } } break; case 'OBJE': echo FunctionsPrint::printFindMediaLink($element_id, '1media'); if (!$value) { echo ' ', self::printAddNewMediaLink($element_id); $value = 'new'; } break; } echo '<div id="' . $element_id . '_description">'; // current value if ($fact === 'DATE') { $date = new Date($value); echo $date->display(); } if (($fact === 'ASSO' || $fact === '_ASSO') && $value === '') { if ($level === 1) { echo '<p class="small text-muted">' . I18N::translate('An associate is another individual who was involved with this individual, such as a friend or an employer.') . '</p>'; } else { echo '<p class="small text-muted">' . I18N::translate('An associate is another individual who was involved with this fact or event, such as a witness or a priest.') . '</p>'; } } if ($value && $value !== 'new' && $islink) { switch ($fact) { case 'ALIA': case 'ASSO': case '_ASSO': $tmp = Individual::getInstance($value, $WT_TREE); if ($tmp) { echo ' ', $tmp->getFullName(); } break; case 'SOUR': $tmp = Source::getInstance($value, $WT_TREE); if ($tmp) { echo ' ', $tmp->getFullName(); } break; case 'NOTE': $tmp = Note::getInstance($value, $WT_TREE); if ($tmp) { echo ' ', $tmp->getFullName(); } break; case 'OBJE': $tmp = Media::getInstance($value, $WT_TREE); if ($tmp) { echo ' ', $tmp->getFullName(); } break; case 'REPO': $tmp = Repository::getInstance($value, $WT_TREE); if ($tmp) { echo ' ', $tmp->getFullName(); } break; } } // pastable values if ($fact === 'FORM' && $upperlevel === 'OBJE') { FunctionsPrint::printAutoPasteLink($element_id, Config::fileFormats()); } echo '</div>', $extra, '</td></tr>'; return $element_id; } /** * Genearate a <select> element, with the dates/places of all known censuses * * * @param string $locale - Sort the censuses for this locale * @param string $xref - The individual for whom we are adding a census */ public static function censusDateSelector($locale, $xref) { global $controller; // Show more likely census details at the top of the list. switch (WT_LOCALE) { case 'cs': $census_places = array(new CensusOfCzechRepublic); break; case 'en-AU': case 'en-GB': $census_places = array(new CensusOfEngland, new CensusOfWales, new CensusOfScotland); break; case 'en-US': $census_places = array(new CensusOfUnitedStates); break; case 'fr': case 'fr-CA': $census_places = array(new CensusOfFrance); break; case 'da': $census_places = array(new CensusOfDenmark); break; default: $census_places = array(); break; } foreach (Census::allCensusPlaces() as $census_place) { if (!in_array($census_place, $census_places)) { $census_places[] = $census_place; } } $controller->addInlineJavascript(' function selectCensus(el) { var option = jQuery(":selected", el); jQuery("input.DATE", jQuery(el).closest("table")).val(option.val()); jQuery("input.PLAC", jQuery(el).closest("table")).val(option.data("place")); jQuery("input.census-class", jQuery(el).closest("table")).val(option.data("census")); if (option.data("place")) { jQuery("#assistant-link").show(); } else { jQuery("#assistant-link").hide(); } } function set_pid_array(pa) { jQuery("#pid_array").val(pa); } function activateCensusAssistant() { if (jQuery("#newshared_note_img").hasClass("icon-plus")) { expand_layer("newshared_note"); } var field = jQuery("#newshared_note input.NOTE")[0]; var xref = jQuery("input[name=xref]").val(); var census = jQuery(".census-assistant-selector :selected").data("census"); return addnewnote_assisted(field, xref, census); } '); $options = '<option value="">' . I18N::translate('Census date') . '</option>'; foreach ($census_places as $census_place) { $options .= '<option value=""></option>'; foreach ($census_place->allCensusDates() as $census) { $date = new Date($census->censusDate()); $year = $date->minimumDate()->format('%Y'); $place_hierarchy = explode(', ', $census->censusPlace()); $options .= '<option value="' . $census->censusDate() . '" data-place="' . $census->censusPlace() . '" data-census="' . get_class($census) . '">' . $place_hierarchy[0] . ' ' . $year . '</option>'; } } return '<input type="hidden" id="pid_array" name="pid_array" value="">' . '<select class="census-assistant-selector" onchange="selectCensus(this);">' . $options . '</select>'; } /** * Prints collapsable fields to add ASSO/RELA, SOUR, OBJE, etc. * * @param string $tag * @param int $level * @param string $parent_tag */ public static function printAddLayer($tag, $level = 2, $parent_tag = '') { global $WT_TREE; switch ($tag) { case 'SOUR': echo '<a href="#" onclick="return expand_layer(\'newsource\');"><i id="newsource_img" class="icon-plus"></i> ', I18N::translate('Add a source citation'), '</a>'; echo '<br>'; echo '<div id="newsource" style="display: none;">'; echo '<table class="facts_table">'; // 2 SOUR self::addSimpleTag($level . ' SOUR @'); // 3 PAGE self::addSimpleTag(($level + 1) . ' PAGE'); // 3 DATA self::addSimpleTag(($level + 1) . ' DATA'); // 4 TEXT self::addSimpleTag(($level + 2) . ' TEXT'); if ($WT_TREE->getPreference('FULL_SOURCES')) { // 4 DATE self::addSimpleTag(($level + 2) . ' DATE', '', GedcomTag::getLabel('DATA:DATE')); // 3 QUAY self::addSimpleTag(($level + 1) . ' QUAY'); } // 3 OBJE self::addSimpleTag(($level + 1) . ' OBJE'); // 3 SHARED_NOTE self::addSimpleTag(($level + 1) . ' SHARED_NOTE'); echo '</table></div>'; break; case 'ASSO': case 'ASSO2': //-- Add a new ASSOciate if ($tag === 'ASSO') { echo "<a href=\"#\" onclick=\"return expand_layer('newasso');\"><i id=\"newasso_img\" class=\"icon-plus\"></i> ", I18N::translate('Add an associate'), '</a>'; echo '<br>'; echo '<div id="newasso" style="display: none;">'; } else { echo "<a href=\"#\" onclick=\"return expand_layer('newasso2');\"><i id=\"newasso2_img\" class=\"icon-plus\"></i> ", I18N::translate('Add an associate'), '</a>'; echo '<br>'; echo '<div id="newasso2" style="display: none;">'; } echo '<table class="facts_table">'; // 2 ASSO self::addSimpleTag($level . ' _ASSO @'); // 3 RELA self::addSimpleTag(($level + 1) . ' RELA'); // 3 NOTE self::addSimpleTag(($level + 1) . ' NOTE'); // 3 SHARED_NOTE self::addSimpleTag(($level + 1) . ' SHARED_NOTE'); echo '</table></div>'; break; case 'NOTE': //-- Retrieve existing note or add new note to fact echo "<a href=\"#\" onclick=\"return expand_layer('newnote');\"><i id=\"newnote_img\" class=\"icon-plus\"></i> ", I18N::translate('Add a note'), '</a>'; echo '<br>'; echo '<div id="newnote" style="display: none;">'; echo '<table class="facts_table">'; // 2 NOTE self::addSimpleTag($level . ' NOTE'); echo '</table></div>'; break; case 'SHARED_NOTE': echo "<a href=\"#\" onclick=\"return expand_layer('newshared_note');\"><i id=\"newshared_note_img\" class=\"icon-plus\"></i> ", I18N::translate('Add a shared note'), '</a>'; echo '<br>'; echo '<div id="newshared_note" style="display: none;">'; echo '<table class="facts_table">'; // 2 SHARED NOTE self::addSimpleTag($level . ' SHARED_NOTE', $parent_tag); echo '</table></div>'; break; case 'OBJE': if ($WT_TREE->getPreference('MEDIA_UPLOAD') >= Auth::accessLevel($WT_TREE)) { echo "<a href=\"#\" onclick=\"return expand_layer('newobje');\"><i id=\"newobje_img\" class=\"icon-plus\"></i> ", I18N::translate('Add a media object'), '</a>'; echo '<br>'; echo '<div id="newobje" style="display: none;">'; echo '<table class="facts_table">'; self::addSimpleTag($level . ' OBJE'); echo '</table></div>'; } break; case 'RESN': echo "<a href=\"#\" onclick=\"return expand_layer('newresn');\"><i id=\"newresn_img\" class=\"icon-plus\"></i> ", I18N::translate('Add a restriction'), '</a>'; echo '<br>'; echo '<div id="newresn" style="display: none;">'; echo '<table class="facts_table">'; // 2 RESN self::addSimpleTag($level . ' RESN'); echo '</table></div>'; break; } } /** * Add some empty tags to create a new fact. * * @param string $fact */ public static function addSimpleTags($fact) { global $WT_TREE; // For new individuals, these facts default to "Y" if ($fact === 'MARR') { self::addSimpleTag('0 ' . $fact . ' Y'); } else { self::addSimpleTag('0 ' . $fact); } if (!in_array($fact, Config::nonDateFacts())) { self::addSimpleTag('0 DATE', $fact, GedcomTag::getLabel($fact . ':DATE')); } if (!in_array($fact, Config::nonPlaceFacts())) { self::addSimpleTag('0 PLAC', $fact, GedcomTag::getLabel($fact . ':PLAC')); if (preg_match_all('/(' . WT_REGEX_TAG . ')/', $WT_TREE->getPreference('ADVANCED_PLAC_FACTS'), $match)) { foreach ($match[1] as $tag) { self::addSimpleTag('0 ' . $tag, $fact, GedcomTag::getLabel($fact . ':PLAC:' . $tag)); } } self::addSimpleTag('0 MAP', $fact); self::addSimpleTag('0 LATI', $fact); self::addSimpleTag('0 LONG', $fact); } } /** * Assemble the pieces of a newly created record into gedcom * * @return string */ public static function addNewName() { global $WT_TREE; $gedrec = "\n1 NAME " . Filter::post('NAME'); $tags = array('NPFX', 'GIVN', 'SPFX', 'SURN', 'NSFX'); if (preg_match_all('/(' . WT_REGEX_TAG . ')/', $WT_TREE->getPreference('ADVANCED_NAME_FACTS'), $match)) { $tags = array_merge($tags, $match[1]); } // Paternal and Polish and Lithuanian surname traditions can also create a _MARNM $SURNAME_TRADITION = $WT_TREE->getPreference('SURNAME_TRADITION'); if ($SURNAME_TRADITION === 'paternal' || $SURNAME_TRADITION === 'polish' || $SURNAME_TRADITION === 'lithuanian') { $tags[] = '_MARNM'; } foreach (array_unique($tags) as $tag) { $TAG = Filter::post($tag); if ($TAG) { $gedrec .= "\n2 {$tag} {$TAG}"; } } return $gedrec; } /** * Create a form to add a sex record. * * @return string */ public static function addNewSex() { switch (Filter::post('SEX', '[MF]', 'U')) { case 'M': return "\n1 SEX M"; case 'F': return "\n1 SEX F"; default: return "\n1 SEX U"; } } /** * Create a form to add a new fact. * * @param string $fact * * @return string */ public static function addNewFact($fact) { global $WT_TREE; $FACT = Filter::post($fact); $DATE = Filter::post($fact . '_DATE'); $PLAC = Filter::post($fact . '_PLAC'); if ($DATE || $PLAC || $FACT && $FACT !== 'Y') { if ($FACT && $FACT !== 'Y') { $gedrec = "\n1 " . $fact . ' ' . $FACT; } else { $gedrec = "\n1 " . $fact; } if ($DATE) { $gedrec .= "\n2 DATE " . $DATE; } if ($PLAC) { $gedrec .= "\n2 PLAC " . $PLAC; if (preg_match_all('/(' . WT_REGEX_TAG . ')/', $WT_TREE->getPreference('ADVANCED_PLAC_FACTS'), $match)) { foreach ($match[1] as $tag) { $TAG = Filter::post($fact . '_' . $tag); if ($TAG) { $gedrec .= "\n3 " . $tag . ' ' . $TAG; } } } $LATI = Filter::post($fact . '_LATI'); $LONG = Filter::post($fact . '_LONG'); if ($LATI || $LONG) { $gedrec .= "\n3 MAP\n4 LATI " . $LATI . "\n4 LONG " . $LONG; } } if (Filter::postBool('SOUR_' . $fact)) { return self::updateSource($gedrec, 2); } else { return $gedrec; } } elseif ($FACT === 'Y') { if (Filter::postBool('SOUR_' . $fact)) { return self::updateSource("\n1 " . $fact . ' Y', 2); } else { return "\n1 " . $fact . ' Y'; } } else { return ''; } } /** * This function splits the $glevels, $tag, $islink, and $text arrays so that the * entries associated with a SOUR record are separate from everything else. * * Input arrays: * - $glevels[] - an array of the gedcom level for each line that was edited * - $tag[] - an array of the tags for each gedcom line that was edited * - $islink[] - an array of 1 or 0 values to indicate when the text is a link element * - $text[] - an array of the text data for each line * * Output arrays: * ** For the SOUR record: * - $glevelsSOUR[] - an array of the gedcom level for each line that was edited * - $tagSOUR[] - an array of the tags for each gedcom line that was edited * - $islinkSOUR[] - an array of 1 or 0 values to indicate when the text is a link element * - $textSOUR[] - an array of the text data for each line * ** For the remaining records: * - $glevelsRest[] - an array of the gedcom level for each line that was edited * - $tagRest[] - an array of the tags for each gedcom line that was edited * - $islinkRest[] - an array of 1 or 0 values to indicate when the text is a link element * - $textRest[] - an array of the text data for each line */ public static function splitSource() { global $glevels, $tag, $islink, $text; global $glevelsSOUR, $tagSOUR, $islinkSOUR, $textSOUR; global $glevelsRest, $tagRest, $islinkRest, $textRest; $glevelsSOUR = array(); $tagSOUR = array(); $islinkSOUR = array(); $textSOUR = array(); $glevelsRest = array(); $tagRest = array(); $islinkRest = array(); $textRest = array(); $inSOUR = false; for ($i = 0; $i < count($glevels); $i++) { if ($inSOUR) { if ($levelSOUR < $glevels[$i]) { $dest = 'S'; } else { $inSOUR = false; $dest = 'R'; } } else { if ($tag[$i] === 'SOUR') { $inSOUR = true; $levelSOUR = $glevels[$i]; $dest = 'S'; } else { $dest = 'R'; } } if ($dest === 'S') { $glevelsSOUR[] = $glevels[$i]; $tagSOUR[] = $tag[$i]; $islinkSOUR[] = $islink[$i]; $textSOUR[] = $text[$i]; } else { $glevelsRest[] = $glevels[$i]; $tagRest[] = $tag[$i]; $islinkRest[] = $islink[$i]; $textRest[] = $text[$i]; } } } /** * Add new GEDCOM lines from the $xxxSOUR interface update arrays, which * were produced by the splitSOUR() function. * See the FunctionsEdit::handle_updatesges() function for details. * * @param string $inputRec * @param string $levelOverride * * @return string */ public static function updateSource($inputRec, $levelOverride = 'no') { global $glevels, $tag, $islink, $text; global $glevelsSOUR, $tagSOUR, $islinkSOUR, $textSOUR; if (count($tagSOUR) === 0) { return $inputRec; // No update required } // Save original interface update arrays before replacing them with the xxxSOUR ones $glevelsSave = $glevels; $tagSave = $tag; $islinkSave = $islink; $textSave = $text; $glevels = $glevelsSOUR; $tag = $tagSOUR; $islink = $islinkSOUR; $text = $textSOUR; $myRecord = self::handleUpdates($inputRec, $levelOverride); // Now do the update // Restore the original interface update arrays (just in case ...) $glevels = $glevelsSave; $tag = $tagSave; $islink = $islinkSave; $text = $textSave; return $myRecord; } /** * Add new GEDCOM lines from the $xxxRest interface update arrays, which * were produced by the splitSOUR() function. * See the FunctionsEdit::handle_updatesges() function for details. * * @param string $inputRec * @param string $levelOverride * * @return string */ public static function updateRest($inputRec, $levelOverride = 'no') { global $glevels, $tag, $islink, $text; global $glevelsRest, $tagRest, $islinkRest, $textRest; if (count($tagRest) === 0) { return $inputRec; // No update required } // Save original interface update arrays before replacing them with the xxxRest ones $glevelsSave = $glevels; $tagSave = $tag; $islinkSave = $islink; $textSave = $text; $glevels = $glevelsRest; $tag = $tagRest; $islink = $islinkRest; $text = $textRest; $myRecord = self::handleUpdates($inputRec, $levelOverride); // Now do the update // Restore the original interface update arrays (just in case ...) $glevels = $glevelsSave; $tag = $tagSave; $islink = $islinkSave; $text = $textSave; return $myRecord; } /** * Add new gedcom lines from interface update arrays * The edit_interface and FunctionsEdit::add_simple_tag function produce the following * arrays incoming from the $_POST form * - $glevels[] - an array of the gedcom level for each line that was edited * - $tag[] - an array of the tags for each gedcom line that was edited * - $islink[] - an array of 1 or 0 values to tell whether the text is a link element and should be surrounded by @@ * - $text[] - an array of the text data for each line * With these arrays you can recreate the gedcom lines like this * <code>$glevel[0].' '.$tag[0].' '.$text[0]</code> * There will be an index in each of these arrays for each line of the gedcom * fact that is being edited. * If the $text[] array is empty for the given line, then it means that the * user removed that line during editing or that the line is supposed to be * empty (1 DEAT, 1 BIRT) for example. To know if the line should be removed * there is a section of code that looks ahead to the next lines to see if there * are sub lines. For example we don't want to remove the 1 DEAT line if it has * a 2 PLAC or 2 DATE line following it. If there are no sub lines, then the line * can be safely removed. * * @param string $newged the new gedcom record to add the lines to * @param string $levelOverride Override GEDCOM level specified in $glevels[0] * * @return string The updated gedcom record */ public static function handleUpdates($newged, $levelOverride = 'no') { global $glevels, $islink, $tag, $uploaded_files, $text; if ($levelOverride === 'no' || count($glevels) === 0) { $levelAdjust = 0; } else { $levelAdjust = $levelOverride - $glevels[0]; } for ($j = 0; $j < count($glevels); $j++) { // Look for empty SOUR reference with non-empty sub-records. // This can happen when the SOUR entry is deleted but its sub-records // were incorrectly left intact. // The sub-records should be deleted. if ($tag[$j] === 'SOUR' && ($text[$j] === '@@' || $text[$j] === '')) { $text[$j] = ''; $k = $j + 1; while (($k < count($glevels)) && ($glevels[$k] > $glevels[$j])) { $text[$k] = ''; $k++; } } if (trim($text[$j]) !== '') { $pass = true; } else { //-- for facts with empty values they must have sub records //-- this section checks if they have subrecords $k = $j + 1; $pass = false; while (($k < count($glevels)) && ($glevels[$k] > $glevels[$j])) { if ($text[$k] !== '') { if (($tag[$j] !== 'OBJE') || ($tag[$k] === 'FILE')) { $pass = true; break; } } if (($tag[$k] === 'FILE') && (count($uploaded_files) > 0)) { $filename = array_shift($uploaded_files); if (!empty($filename)) { $text[$k] = $filename; $pass = true; break; } } $k++; } } //-- if the value is not empty or it has sub lines //--- then write the line to the gedcom record //-- we have to let some emtpy text lines pass through... (DEAT, BIRT, etc) if ($pass) { $newline = $glevels[$j] + $levelAdjust . ' ' . $tag[$j]; if ($text[$j] !== '') { if ($islink[$j]) { $newline .= ' @' . $text[$j] . '@'; } else { $newline .= ' ' . $text[$j]; } } $newged .= "\n" . str_replace("\n", "\n" . (1 + substr($newline, 0, 1)) . ' CONT ', $newline); } } return $newged; } /** * builds the form for adding new facts * * @param string $fact the new fact we are adding */ public static function createAddForm($fact) { global $tags, $WT_TREE; $tags = array(); // handle MARRiage TYPE if (substr($fact, 0, 5) === 'MARR_') { $tags[0] = 'MARR'; self::addSimpleTag('1 MARR'); self::insertMissingSubtags($fact); } else { $tags[0] = $fact; if ($fact === '_UID') { $fact .= ' ' . GedcomTag::createUid(); } // These new level 1 tags need to be turned into links if (in_array($fact, array('ALIA', 'ASSO'))) { $fact .= ' @'; } if (in_array($fact, Config::emptyFacts())) { self::addSimpleTag('1 ' . $fact . ' Y'); } else { self::addSimpleTag('1 ' . $fact); } self::insertMissingSubtags($tags[0]); //-- handle the special SOURce case for level 1 sources [ 1759246 ] if ($fact === 'SOUR') { self::addSimpleTag('2 PAGE'); self::addSimpleTag('3 TEXT'); if ($WT_TREE->getPreference('FULL_SOURCES')) { self::addSimpleTag('3 DATE', '', GedcomTag::getLabel('DATA:DATE')); self::addSimpleTag('2 QUAY'); } } } } /** * Create a form to edit a Fact object. * * @param Fact $fact * * @return string */ public static function createEditForm(Fact $fact) { global $tags; $record = $fact->getParent(); $tags = array(); $gedlines = explode("\n", $fact->getGedcom()); $linenum = 0; $fields = explode(' ', $gedlines[$linenum]); $glevel = $fields[0]; $level = $glevel; $type = $fact->getTag(); $level0type = $record::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 (GedcomCodeTemp::isTagLDS($level1type)) { $expected_subtags['STAT'] = array('DATE'); } if (in_array($level1type, Config::dateAndTime())) { $expected_subtags['DATE'] = array('TIME'); // TIME is NOT a valid 5.5.1 tag } 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(); // Loop on existing tags : while (true) { // Keep track of our hierarchy, e.g. 1=>BIRT, 2=>PLAC, 3=>FONE $stack[$level] = $type; // Merge them together, e.g. BIRT:PLAC:FONE $label = implode(':', array_slice($stack, 0, $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 !== 'CONT') { $tags[] = $type; $subrecord = $level . ' ' . $type . ' ' . $text; if ($inSource && $type === 'DATE') { self::addSimpleTag($subrecord, '', GedcomTag::getLabel($label, $record)); } elseif (!$inSource && $type === 'DATE') { self::addSimpleTag($subrecord, $level1type, GedcomTag::getLabel($label, $record)); 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, $record)); } else { self::addSimpleTag($subrecord, $level0type, GedcomTag::getLabel($label, $record)); } } // Get a list of tags present at the next level $subtags = array(); for ($ii = $i + 1; isset($gedlines[$ii]) && preg_match('/^(\d+) (\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)) { 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)); } } } } } $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; } /** * Populates the global $tags array with any missing sub-tags. * * @param string $level1tag the type of the level 1 gedcom record * @param bool $add_date */ public static function insertMissingSubtags($level1tag, $add_date = false) { global $tags, $WT_TREE; // handle MARRiage TYPE $type_val = ''; if (substr($level1tag, 0, 5) === 'MARR_') { $type_val = substr($level1tag, 5); $level1tag = 'MARR'; } foreach (Config::levelTwoTags() as $key => $value) { if ($key === 'DATE' && in_array($level1tag, Config::nonDateFacts()) || $key === 'PLAC' && in_array($level1tag, Config::nonPlaceFacts())) { continue; } if (in_array($level1tag, $value) && !in_array($key, $tags)) { if ($key === 'TYPE') { self::addSimpleTag('2 TYPE ' . $type_val, $level1tag); } elseif ($level1tag === '_TODO' && $key === 'DATE') { self::addSimpleTag('2 ' . $key . ' ' . strtoupper(date('d M Y')), $level1tag); } elseif ($level1tag === '_TODO' && $key === '_WT_USER') { self::addSimpleTag('2 ' . $key . ' ' . Auth::user()->getUserName(), $level1tag); } elseif ($level1tag === 'TITL' && strstr($WT_TREE->getPreference('ADVANCED_NAME_FACTS'), $key) !== false) { self::addSimpleTag('2 ' . $key, $level1tag); } elseif ($level1tag === 'NAME' && strstr($WT_TREE->getPreference('ADVANCED_NAME_FACTS'), $key) !== false) { self::addSimpleTag('2 ' . $key, $level1tag); } elseif ($level1tag !== 'TITL' && $level1tag !== 'NAME') { self::addSimpleTag('2 ' . $key, $level1tag); } // Add level 3/4 tags as appropriate switch ($key) { case 'PLAC': if (preg_match_all('/(' . WT_REGEX_TAG . ')/', $WT_TREE->getPreference('ADVANCED_PLAC_FACTS'), $match)) { foreach ($match[1] as $tag) { self::addSimpleTag('3 ' . $tag, '', GedcomTag::getLabel($level1tag . ':PLAC:' . $tag)); } } self::addSimpleTag('3 MAP'); self::addSimpleTag('4 LATI'); self::addSimpleTag('4 LONG'); break; case 'FILE': self::addSimpleTag('3 FORM'); break; case 'EVEN': self::addSimpleTag('3 DATE'); self::addSimpleTag('3 PLAC'); break; case 'STAT': if (GedcomCodeTemp::isTagLDS($level1tag)) { self::addSimpleTag('3 DATE', '', GedcomTag::getLabel('STAT:DATE')); } break; case 'DATE': // TIME is NOT a valid 5.5.1 tag if (in_array($level1tag, Config::dateAndTime())) { self::addSimpleTag('3 TIME'); } break; case 'HUSB': case 'WIFE': self::addSimpleTag('3 AGE'); break; case 'FAMC': if ($level1tag === 'ADOP') { self::addSimpleTag('3 ADOP BOTH'); } break; } } elseif ($key === 'DATE' && $add_date) { self::addSimpleTag('2 DATE', $level1tag, GedcomTag::getLabel($level1tag . ':DATE')); } } // Do something (anything!) with unrecognized custom tags if (substr($level1tag, 0, 1) === '_' && $level1tag !== '_UID' && $level1tag !== '_TODO') { foreach (array('DATE', 'PLAC', 'ADDR', 'AGNC', 'TYPE', 'AGE') as $tag) { if (!in_array($tag, $tags)) { self::addSimpleTag('2 ' . $tag); if ($tag === 'PLAC') { if (preg_match_all('/(' . WT_REGEX_TAG . ')/', $WT_TREE->getPreference('ADVANCED_PLAC_FACTS'), $match)) { foreach ($match[1] as $ptag) { self::addSimpleTag('3 ' . $ptag, '', GedcomTag::getLabel($level1tag . ':PLAC:' . $ptag)); } } self::addSimpleTag('3 MAP'); self::addSimpleTag('4 LATI'); self::addSimpleTag('4 LONG'); } } } } } }