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