1<?php 2 3use Fisharebest\Webtrees\Auth; 4use Fisharebest\Webtrees\Fact; 5use Fisharebest\Webtrees\Functions\FunctionsEdit; 6use Fisharebest\Webtrees\Gedcom; 7use Fisharebest\Webtrees\GedcomTag; 8use Fisharebest\Webtrees\Http\RequestHandlers\EditRawFactPage; 9use Fisharebest\Webtrees\I18N; 10use Fisharebest\Webtrees\Individual; 11use Fisharebest\Webtrees\SurnameTradition; 12use Fisharebest\Webtrees\View; 13 14/** 15 * @var Individual|null $individual 16 * @var Fact|null $name_fact 17 */ 18 19?> 20 21<?php 22if ($individual instanceof Individual) { 23 $xref = $individual->xref(); 24 $cancel_url = $individual->url(); 25} elseif ($family !== null) { 26 $xref = $family->xref(); 27 $cancel_url = $family->url(); 28} else { 29 $cancel_url = route('manage-trees', ['tree' => $tree->name()]); 30 $xref = 'new'; 31} 32 33// Different cultures do surnames differently 34$surname_tradition = SurnameTradition::create($tree->getPreference('SURNAME_TRADITION')); 35 36if ($name_fact instanceof Fact) { 37 // Editing an existing name 38 $name_fact_id = $name_fact->id(); 39 $namerec = $name_fact->gedcom(); 40 $name_fields = [ 41 'NAME' => $name_fact->value(), 42 'TYPE' => $name_fact->attribute('TYPE'), 43 'NPFX' => $name_fact->attribute('NPFX'), 44 'GIVN' => $name_fact->attribute('GIVN'), 45 'NICK' => $name_fact->attribute('NICK'), 46 'SPFX' => $name_fact->attribute('SPFX'), 47 'SURN' => $name_fact->attribute('SURN'), 48 'NSFX' => $name_fact->attribute('NSFX'), 49 ]; 50} else { 51 // Creating a new name 52 $name_fact_id = ''; 53 $namerec = ''; 54 $name_fields = [ 55 'NAME' => '', 56 'TYPE' => '', 57 'NPFX' => '', 58 'GIVN' => '', 59 'NICK' => '', 60 'SPFX' => '', 61 'SURN' => '', 62 'NSFX' => '', 63 ]; 64 65 // Inherit surname from parents, spouse or child 66 if ($family) { 67 $father = $family->husband(); 68 if ($father instanceof Individual && $father->facts(['NAME'])->isNotEmpty()) { 69 $father_name = $father->facts(['NAME'])->first()->value(); 70 } else { 71 $father_name = ''; 72 } 73 $mother = $family->wife(); 74 if ($mother instanceof Individual && $mother->facts(['NAME'])->isNotEmpty()) { 75 $mother_name = $mother->facts(['NAME'])->first()->value(); 76 } else { 77 $mother_name = ''; 78 } 79 } else { 80 $father = null; 81 $mother = null; 82 $father_name = ''; 83 $mother_name = ''; 84 } 85 if ($individual && $individual->facts(['NAME'])->isNotEmpty()) { 86 $indi_name = $individual->facts(['NAME'])->first()->value(); 87 } else { 88 $indi_name = ''; 89 } 90 91 switch ($next_action) { 92 case 'add-child-to-family-action': 93 $name_fields = array_merge($name_fields, $surname_tradition->newChildNames($father_name, $mother_name, $gender)); 94 break; 95 case 'add-child-to-individual-action': 96 if ($individual->sex() === 'F') { 97 $name_fields = array_merge($name_fields, $surname_tradition->newChildNames('', $indi_name, $gender)); 98 } else { 99 $name_fields = array_merge($name_fields, $surname_tradition->newChildNames($indi_name, '', $gender)); 100 } 101 break; 102 case 'add-parent-to-individual-action': 103 $name_fields = array_merge($name_fields, $surname_tradition->newParentNames($indi_name, $gender)); 104 break; 105 case 'add-spouse-to-family-action': 106 if ($father) { 107 $name_fields = array_merge($name_fields, $surname_tradition->newSpouseNames($father_name, $gender)); 108 } else { 109 $name_fields = array_merge($name_fields, $surname_tradition->newSpouseNames($mother_name, $gender)); 110 } 111 break; 112 case 'add-spouse-to-individual-action': 113 $name_fields = array_merge($name_fields, $surname_tradition->newSpouseNames($indi_name, $gender)); 114 break; 115 case 'add-unlinked-individual-action': 116 case 'add-name-action': 117 case 'edit-name-action': 118 if ($surname_tradition->hasSurnames()) { 119 $name_fields['NAME'] = '//'; 120 } 121 break; 122 } 123} 124 125$bdm = ''; // used to copy '1 SOUR' to '2 SOUR' for BIRT DEAT MARR 126 127?> 128<h2 class="wt-page-title"><?= $title ?></h2> 129 130<form method="post" action="<?= e(route($next_action, ['tree' => $tree->name()])) ?>" onsubmit="return checkform();"> 131 <input type="hidden" name="tree" value="<?= e($tree->name()) ?>"> 132 <input type="hidden" name="fact_id" value="<?= e($name_fact_id) ?>"> 133 <input type="hidden" name="xref" value="<?= e($xref) ?>"> 134 <input type="hidden" name="famtag" value="<?= e($famtag) ?>"> 135 <input type="hidden" name="gender" value="<?= $gender ?>"> 136 <?= csrf_field() ?> 137 138 <?php if ($next_action === 'add-child-to-family-action' || $next_action === 'add-child-to-individual-action') : ?> 139 <?= FunctionsEdit::addSimpleTag($tree, '0 PEDI') ?> 140 <?php endif ?> 141 142 <?php 143 // First - standard name fields 144 foreach ($name_fields as $tag => $value) { 145 if (substr_compare($tag, '_', 0, 1) !== 0) { 146 echo FunctionsEdit::addSimpleTag($tree, '0 ' . $tag . ' ' . $value, '', ''); 147 } 148 } 149 150 // Second - advanced name fields 151 if ($surname_tradition->hasMarriedNames() || preg_match('/\n2 _MARNM /', $namerec)) { 152 $adv_name_fields = ['_MARNM' => '']; 153 } else { 154 $adv_name_fields = []; 155 } 156 if (preg_match_all('/(' . Gedcom::REGEX_TAG . ')/', $tree->getPreference('ADVANCED_NAME_FACTS'), $match)) { 157 foreach ($match[1] as $tag) { 158 // Ignore advanced facts that duplicate standard facts 159 if (!in_array($tag, ['TYPE', 'NPFX', 'GIVN', 'NICK', 'SPFX', 'SURN', 'NSFX'])) { 160 $adv_name_fields[$tag] = ''; 161 } 162 } 163 } 164 165 foreach (array_keys($adv_name_fields) as $tag) { 166 // Edit existing tags, grouped together 167 if (preg_match_all('/2 ' . $tag . ' (.+)/', $namerec, $match)) { 168 foreach ($match[1] as $value) { 169 echo FunctionsEdit::addSimpleTag($tree, '2 ' . $tag . ' ' . $value, '', GedcomTag::getLabel('NAME:' . $tag, $individual)); 170 if ($tag === '_MARNM') { 171 preg_match_all('/\/([^\/]*)\//', $value, $matches); 172 echo FunctionsEdit::addSimpleTag($tree, '2 _MARNM_SURN ' . implode(',', $matches[1])); 173 } 174 } 175 } 176 // Allow a new tag to be entered 177 if (!array_key_exists($tag, $name_fields)) { 178 echo FunctionsEdit::addSimpleTag($tree, '0 ' . $tag, '', GedcomTag::getLabel('NAME:' . $tag, $individual)); 179 if ($tag === '_MARNM') { 180 echo FunctionsEdit::addSimpleTag($tree, '0 _MARNM_SURN'); 181 } 182 } 183 } 184 185 // Third - new/existing custom name fields 186 foreach ($name_fields as $tag => $value) { 187 if (substr_compare($tag, '_', 0, 1) === 0) { 188 echo FunctionsEdit::addSimpleTag($tree, '0 ' . $tag . ' ' . $value); 189 if ($tag === '_MARNM') { 190 preg_match_all('/\/([^\/]*)\//', $value, $matches); 191 echo FunctionsEdit::addSimpleTag($tree, '2 _MARNM_SURN ' . implode(',', $matches[1])); 192 } 193 } 194 } 195 196 // Fourth - SOUR, NOTE, _CUSTOM, etc. 197 if ($namerec !== '') { 198 $gedlines = explode("\n", $namerec); // -- find the number of lines in the record 199 $fields = explode(' ', $gedlines[0]); 200 $glevel = $fields[0]; 201 $level = $glevel; 202 $type = $fields[1]; 203 $tags = []; 204 $i = 0; 205 do { 206 if ($type !== 'TYPE' && !array_key_exists($type, $name_fields) && !array_key_exists($type, $adv_name_fields)) { 207 $text = ''; 208 for ($j = 2; $j < count($fields); $j++) { 209 if ($j > 2) { 210 $text .= ' '; 211 } 212 $text .= $fields[$j]; 213 } 214 while (($i + 1 < count($gedlines)) && (preg_match('/' . ($level + 1) . ' CONT ?(.*)/', $gedlines[$i + 1], $cmatch) > 0)) { 215 $text .= "\n" . $cmatch[1]; 216 $i++; 217 } 218 echo FunctionsEdit::addSimpleTag($tree, $level . ' ' . $type . ' ' . $text); 219 } 220 $tags[] = $type; 221 $i++; 222 if (isset($gedlines[$i])) { 223 $fields = explode(' ', $gedlines[$i]); 224 $level = $fields[0]; 225 if (isset($fields[1])) { 226 $type = $fields[1]; 227 } 228 } 229 } while (($level > $glevel) && ($i < count($gedlines))); 230 } 231 232 // If we are adding a new individual, add the basic details 233 if ($next_action !== 'add-name-action' && $next_action !== 'edit-name-action') { 234 echo '</table><br><table class="table wt-facts-table">'; 235 // 1 SEX 236 if ($famtag === 'HUSB' || $gender === 'M') { 237 echo FunctionsEdit::addSimpleTag($tree, '0 SEX M'); 238 } elseif ($famtag === 'WIFE' || $gender === 'F') { 239 echo FunctionsEdit::addSimpleTag($tree, '0 SEX F'); 240 } else { 241 echo FunctionsEdit::addSimpleTag($tree, '0 SEX U'); 242 } 243 $bdm = 'BD'; 244 if (preg_match_all('/(' . Gedcom::REGEX_TAG . ')/', $tree->getPreference('QUICK_REQUIRED_FACTS'), $matches)) { 245 foreach ($matches[1] as $match) { 246 if (!in_array($match, Gedcom::DEATH_EVENTS, true)) { 247 FunctionsEdit::addSimpleTags($tree, $match); 248 } 249 } 250 } 251 //-- if adding a spouse add the option to add a marriage fact to the new family 252 if ($next_action === 'add-spouse-to-individual-action' || $next_action === 'add-spouse-to-family-action') { 253 $bdm .= 'M'; 254 if (preg_match_all('/(' . Gedcom::REGEX_TAG . ')/', $tree->getPreference('QUICK_REQUIRED_FAMFACTS'), $matches)) { 255 foreach ($matches[1] as $match) { 256 FunctionsEdit::addSimpleTags($tree, $match); 257 } 258 } 259 } 260 if (preg_match_all('/(' . Gedcom::REGEX_TAG . ')/', $tree->getPreference('QUICK_REQUIRED_FACTS'), $matches)) { 261 foreach ($matches[1] as $match) { 262 if (in_array($match, Gedcom::DEATH_EVENTS, true)) { 263 FunctionsEdit::addSimpleTags($tree, $match); 264 } 265 } 266 } 267 } 268 269 echo '</table>'; 270 if ($next_action === 'edit-name-action' || $next_action === 'add-name-action') { 271 // GEDCOM 5.5.1 spec says NAME doesn’t get a OBJE 272 echo view('cards/add-source-citation', [ 273 'level' => 2, 274 'full_citations' => $tree->getPreference('FULL_SOURCES'), 275 'tree' => $tree, 276 ]); 277 echo view('cards/add-note', [ 278 'level' => 2, 279 'tree' => $tree, 280 ]); 281 echo view('cards/add-shared-note', [ 282 'level' => 2, 283 'tree' => $tree, 284 ]); 285 echo view('cards/add-restriction', [ 286 'level' => 2, 287 'tree' => $tree, 288 ]); 289 } else { 290 echo view('cards/add-source-citation', [ 291 'bdm' => $bdm, 292 'level' => 1, 293 'full_citations' => $tree->getPreference('FULL_SOURCES'), 294 'prefer_level2_sources' => $tree->getPreference('PREFER_LEVEL2_SOURCES'), 295 'quick_required_facts' => $tree->getPreference('QUICK_REQUIRED_FACTS'), 296 'quick_required_famfacts' => $tree->getPreference('QUICK_REQUIRED_FAMFACTS'), 297 'tree' => $tree, 298 ]); 299 echo view('cards/add-note', [ 300 'level' => 1, 301 'tree' => $tree, 302 ]); 303 echo view('cards/add-shared-note', [ 304 'level' => 1, 305 'tree' => $tree, 306 ]); 307 echo view('cards/add-restriction', [ 308 'level' => 1, 309 'tree' => $tree, 310 ]); 311 } 312 313 ?> 314 <div class="row form-group"> 315 <div class="col-sm-9 offset-sm-3"> 316 <button class="btn btn-primary" type="submit"> 317 <?= view('icons/save') ?> 318 <?= /* I18N: A button label. */ 319 I18N::translate('save') ?> 320 </button> 321 <?php if (preg_match('/^add-(child|spouse|parent|unlinked-individual)/', $next_action)) : ?> 322 <button class="btn btn-primary" type="submit" name="goto" value="<?= $xref ?>"> 323 <?= view('icons/save') ?> 324 <?= /* I18N: A button label. */ 325 I18N::translate('go to new individual') ?> 326 </button> 327 <?php endif ?> 328 <a class="btn btn-secondary" href="<?= e($cancel_url) ?>"> 329 <?= view('icons/cancel') ?> 330 <?= /* I18N: A button label. */ 331 I18N::translate('cancel') ?> 332 </a> 333 334 <?php if ($name_fact instanceof Fact && (Auth::isAdmin() || $tree->getPreference('SHOW_GEDCOM_RECORD'))) : ?> 335 <a class="btn btn-link" href="<?= e(route(EditRawFactPage::class, ['xref' => $xref, 'fact_id' => $name_fact->id(), 'tree' => $tree->name()])) ?>"> 336 <?= I18N::translate('Edit the raw GEDCOM') ?> 337 </a> 338 <?php endif ?> 339 </div> 340 </div> 341</form> 342 343<?= view('modals/on-screen-keyboard') ?> 344<?= view('modals/ajax') ?> 345<?= view('edit/initialize-calendar-popup') ?> 346 347<?php View::push('javascript') ?> 348<script> 349 var SURNAME_TRADITION = <?= json_encode($tree->getPreference('SURNAME_TRADITION')) ?>; 350 351 var NAME = $("[name=NAME]"); 352 353 // Generate a full name from the name components 354 function generate_name() { 355 var npfx = document.querySelector("[name=NPFX]").value; 356 var givn = document.querySelector("[name=GIVN]").value; 357 var spfx = document.querySelector("[name=SPFX]").value; 358 var surn = document.querySelector("[name=SURN]").value; 359 var nsfx = document.querySelector("[name=NSFX]").value; 360 var sex_input = document.querySelector("[name=SEX]:checked"); 361 var sex = sex_input ? sex_input.value : "U"; 362 363 return webtrees.buildNameFromParts(npfx, givn, spfx, surn, nsfx, sex); 364 } 365 366 // Update the NAME and _MARNM fields from the name components 367 // and also display the value in read-only "gedcom" format. 368 function updatewholename() { 369 // Don’t update the name if the user manually changed it 370 if (manualChange) { 371 return; 372 } 373 374 var npfx = document.querySelector("[name=NPFX]").value; 375 var givn = document.querySelector("[name=GIVN]").value; 376 var spfx = document.querySelector("[name=SPFX]").value; 377 var nsfx = document.querySelector("[name=NSFX]").value; 378 var name = generate_name(); 379 380 var display_id = NAME.attr("id") + "_display"; 381 382 NAME.val(name); 383 $("#" + display_id).text(name); 384 385 // Married names inherit some NSFX values, but not these 386 nsfx = nsfx.replace(/^(I|II|III|IV|V|VI|Junior|Jr\.?|Senior|Sr\.?)$/i, ""); 387 388 // Update _MARNM field from _MARNM_SURN field and display it 389 var ip = document.getElementsByTagName("input"); 390 var marnm_id = ""; 391 var romn = ""; 392 var heb = ""; 393 var i; 394 395 for (i = 0; i < ip.length; i++) { 396 if (ip[i].id.indexOf("_HEB") === 0) { 397 // Remember this field - we might need it later 398 heb = val; 399 } 400 if (ip[i].id.indexOf("ROMN") === 0) { 401 // Remember this field - we might need it later 402 romn = val; 403 } 404 } 405 406 for (i = 0; i < ip.length; i++) { 407 var val = ip[i].value; 408 409 if (ip[i].id.indexOf("_MARNM") === 0) { 410 if (ip[i].id.indexOf("_MARNM_SURN") === 0) { 411 var msurn = ""; 412 if (val !== "") { 413 if (webtrees.detectScript(val) === webtrees.detectScript(name)) { 414 // Same script as NAME field? 415 msurn = webtrees.buildNameFromParts(npfx, givn, spfx, val, nsfx); 416 } else if (heb !== "" && webtrees.detectScript(val) === webtrees.detectScript(heb)) { 417 // Same script as _HEB field? 418 msurn = heb.replace(/\/.*\//, "/" + val + "/"); 419 } else if (romn !== "" && webtrees.detectScript(val) === webtrees.detectScript(romn)) { 420 //. Same script as ROMN field 421 msurn = romn.replace(/\/.*\//, "/" + val + "/"); 422 } 423 } 424 document.getElementById(marnm_id).value = msurn; 425 document.getElementById(marnm_id + "_display").innerHTML = msurn; 426 } else { 427 marnm_id = ip[i].id; 428 } 429 } 430 } 431 } 432 433 // Toggle the name editor fields between 434 // <input type="hidden"> <span style="display:inline"> 435 // <input type="text"> <span style="display:none"> 436 437 var oldName = ""; 438 439 // Calls to generate_name() trigger an update - hence need to 440 // set the manual change to true first. We are probably 441 // listening to the wrong events on the input fields... 442 var manualChange = generate_name() !== NAME.val(); 443 444 function convertHidden(eid) { 445 var input1 = $("#" + eid); 446 var input2 = $("#" + eid + "_display"); 447 448 if (input1.attr("type") === "hidden") { 449 input1.attr("type", "text"); 450 input2.hide(); 451 } else { 452 input1.attr("type", "hidden"); 453 input2.show(); 454 } 455 } 456 457 /** 458 * if the user manually changed the NAME field, then update the textual 459 * HTML representation of it 460 * If the value changed set manualChange to true so that changing 461 * the other fields doesn’t change the NAME line 462 */ 463 function updateTextName(eid) { 464 var element = document.getElementById(eid); 465 if (element) { 466 if (element.value !== oldName) { 467 manualChange = true; 468 } 469 var delement = document.getElementById(eid + "_display"); 470 if (delement) { 471 delement.innerHTML = element.value; 472 } 473 } 474 } 475 476 function checkform() { 477 var ip = document.getElementsByTagName("input"); 478 for (var i = 0; i < ip.length; i++) { 479 // ADD slashes to _HEB and _AKA names 480 if (ip[i].id.indexOf("_AKA") === 0 || ip[i].id.indexOf("_HEB") === 0 || ip[i].id.indexOf("ROMN") === 0) 481 if (ip[i].value.indexOf("/") < 0 && ip[i].value !== "") 482 ip[i].value = ip[i].value.replace(/([^\s]+)\s*$/, "/$1/"); 483 // Blank out temporary _MARNM_SURN 484 if (ip[i].id.indexOf("_MARNM_SURN") === 0) 485 ip[i].value = ""; 486 // Convert "xxx yyy" and "xxx y yyy" surnames to "xxx,yyy" 487 if ((SURNAME_TRADITION === "spanish" || "SURNAME_TRADITION" === "portuguese") && ip[i].id.indexOf("SURN") === 0) { 488 ip[i].value = document.forms[0].SURN.value.replace(/^\s*([^\s,]{2,})\s+([iIyY] +)?([^\s,]{2,})\s*$/, "$1,$3"); 489 } 490 } 491 return true; 492 } 493 494 // If the name isn’t initially formed from the components in a standard way, 495 // then don’t automatically update it. 496 if (NAME.val() !== generate_name() && NAME.val() !== "//") { 497 convertHidden(NAME.attr("id")); 498 } 499</script> 500<?php View::endpush() ?> 501 502