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