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