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