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