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