xref: /webtrees/resources/views/edit/new-individual.phtml (revision bedf83da798d605df6e0cd217b04ee39d614702d)
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 isnt initially formed from the components in a standard way,
509    // then dont 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