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