xref: /webtrees/app/Services/GedcomEditService.php (revision c8183f294aedbaa1f62c070929381961858fb914)
17c7d1e03SGreg Roach<?php
27c7d1e03SGreg Roach
37c7d1e03SGreg Roach/**
47c7d1e03SGreg Roach * webtrees: online genealogy
589f7189bSGreg Roach * Copyright (C) 2021 webtrees development team
67c7d1e03SGreg Roach * This program is free software: you can redistribute it and/or modify
77c7d1e03SGreg Roach * it under the terms of the GNU General Public License as published by
87c7d1e03SGreg Roach * the Free Software Foundation, either version 3 of the License, or
97c7d1e03SGreg Roach * (at your option) any later version.
107c7d1e03SGreg Roach * This program is distributed in the hope that it will be useful,
117c7d1e03SGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of
127c7d1e03SGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
137c7d1e03SGreg Roach * GNU General Public License for more details.
147c7d1e03SGreg Roach * You should have received a copy of the GNU General Public License
1589f7189bSGreg Roach * along with this program. If not, see <https://www.gnu.org/licenses/>.
167c7d1e03SGreg Roach */
177c7d1e03SGreg Roach
187c7d1e03SGreg Roachdeclare(strict_types=1);
197c7d1e03SGreg Roach
207c7d1e03SGreg Roachnamespace Fisharebest\Webtrees\Services;
217c7d1e03SGreg Roach
227c7d1e03SGreg Roachuse Fisharebest\Webtrees\Gedcom;
23c2ed51d1SGreg Roachuse Fisharebest\Webtrees\Registry;
247c7d1e03SGreg Roachuse Fisharebest\Webtrees\Tree;
257c7d1e03SGreg Roachuse Psr\Http\Message\ServerRequestInterface;
267c7d1e03SGreg Roach
277c7d1e03SGreg Roachuse function array_merge;
287c7d1e03SGreg Roachuse function array_unique;
297c7d1e03SGreg Roachuse function assert;
307c7d1e03SGreg Roachuse function count;
317c7d1e03SGreg Roachuse function preg_match_all;
327c7d1e03SGreg Roachuse function str_replace;
337c7d1e03SGreg Roachuse function trim;
347c7d1e03SGreg Roach
357c7d1e03SGreg Roach/**
367c7d1e03SGreg Roach * Utilities to edit/save GEDCOM data.
377c7d1e03SGreg Roach */
387c7d1e03SGreg Roachclass GedcomEditService
397c7d1e03SGreg Roach{
407c7d1e03SGreg Roach    /** @var string[] */
417c7d1e03SGreg Roach    public $glevels = [];
427c7d1e03SGreg Roach
437c7d1e03SGreg Roach    /** @var string[] */
447c7d1e03SGreg Roach    public $tag = [];
457c7d1e03SGreg Roach
467c7d1e03SGreg Roach    /** @var string[] */
477c7d1e03SGreg Roach    public $islink = [];
487c7d1e03SGreg Roach
497c7d1e03SGreg Roach    /** @var string[] */
507c7d1e03SGreg Roach    public $text = [];
517c7d1e03SGreg Roach
527c7d1e03SGreg Roach    /** @var string[] */
537c7d1e03SGreg Roach    protected $glevelsSOUR = [];
547c7d1e03SGreg Roach
557c7d1e03SGreg Roach    /** @var string[] */
567c7d1e03SGreg Roach    protected $tagSOUR = [];
577c7d1e03SGreg Roach
587c7d1e03SGreg Roach    /** @var string[] */
597c7d1e03SGreg Roach    protected $islinkSOUR = [];
607c7d1e03SGreg Roach
617c7d1e03SGreg Roach    /** @var string[] */
627c7d1e03SGreg Roach    protected $textSOUR = [];
637c7d1e03SGreg Roach
647c7d1e03SGreg Roach    /** @var string[] */
657c7d1e03SGreg Roach    protected $glevelsRest = [];
667c7d1e03SGreg Roach
677c7d1e03SGreg Roach    /** @var string[] */
687c7d1e03SGreg Roach    protected $tagRest = [];
697c7d1e03SGreg Roach
707c7d1e03SGreg Roach    /** @var string[] */
717c7d1e03SGreg Roach    protected $islinkRest = [];
727c7d1e03SGreg Roach
737c7d1e03SGreg Roach    /** @var string[] */
747c7d1e03SGreg Roach    protected $textRest = [];
757c7d1e03SGreg Roach
767c7d1e03SGreg Roach    /**
777c7d1e03SGreg Roach     * This function splits the $glevels, $tag, $islink, and $text arrays so that the
787c7d1e03SGreg Roach     * entries associated with a SOUR record are separate from everything else.
797c7d1e03SGreg Roach     *
807c7d1e03SGreg Roach     * Input arrays:
817c7d1e03SGreg Roach     * - $glevels[] - an array of the gedcom level for each line that was edited
827c7d1e03SGreg Roach     * - $tag[] - an array of the tags for each gedcom line that was edited
837c7d1e03SGreg Roach     * - $islink[] - an array of 1 or 0 values to indicate when the text is a link element
847c7d1e03SGreg Roach     * - $text[] - an array of the text data for each line
857c7d1e03SGreg Roach     *
867c7d1e03SGreg Roach     * Output arrays:
877c7d1e03SGreg Roach     * ** For the SOUR record:
887c7d1e03SGreg Roach     * - $glevelsSOUR[] - an array of the gedcom level for each line that was edited
897c7d1e03SGreg Roach     * - $tagSOUR[] - an array of the tags for each gedcom line that was edited
907c7d1e03SGreg Roach     * - $islinkSOUR[] - an array of 1 or 0 values to indicate when the text is a link element
917c7d1e03SGreg Roach     * - $textSOUR[] - an array of the text data for each line
927c7d1e03SGreg Roach     * ** For the remaining records:
937c7d1e03SGreg Roach     * - $glevelsRest[] - an array of the gedcom level for each line that was edited
947c7d1e03SGreg Roach     * - $tagRest[] - an array of the tags for each gedcom line that was edited
957c7d1e03SGreg Roach     * - $islinkRest[] - an array of 1 or 0 values to indicate when the text is a link element
967c7d1e03SGreg Roach     * - $textRest[] - an array of the text data for each line
977c7d1e03SGreg Roach     *
987c7d1e03SGreg Roach     * @return void
997c7d1e03SGreg Roach     */
1007c7d1e03SGreg Roach    public function splitSource(): void
1017c7d1e03SGreg Roach    {
1027c7d1e03SGreg Roach        $this->glevelsSOUR = [];
1037c7d1e03SGreg Roach        $this->tagSOUR     = [];
1047c7d1e03SGreg Roach        $this->islinkSOUR  = [];
1057c7d1e03SGreg Roach        $this->textSOUR    = [];
1067c7d1e03SGreg Roach
1077c7d1e03SGreg Roach        $this->glevelsRest = [];
1087c7d1e03SGreg Roach        $this->tagRest     = [];
1097c7d1e03SGreg Roach        $this->islinkRest  = [];
1107c7d1e03SGreg Roach        $this->textRest    = [];
1117c7d1e03SGreg Roach
1127c7d1e03SGreg Roach        $inSOUR    = false;
1137c7d1e03SGreg Roach        $levelSOUR = 0;
1147c7d1e03SGreg Roach
1157c7d1e03SGreg Roach        // Assume all arrays are the same size.
1167c7d1e03SGreg Roach        $count = count($this->glevels);
1177c7d1e03SGreg Roach
1187c7d1e03SGreg Roach        for ($i = 0; $i < $count; $i++) {
1197c7d1e03SGreg Roach            if ($inSOUR) {
1207c7d1e03SGreg Roach                if ($levelSOUR < $this->glevels[$i]) {
1217c7d1e03SGreg Roach                    $dest = 'S';
1227c7d1e03SGreg Roach                } else {
1237c7d1e03SGreg Roach                    $inSOUR = false;
1247c7d1e03SGreg Roach                    $dest   = 'R';
1257c7d1e03SGreg Roach                }
1267c7d1e03SGreg Roach            } elseif ($this->tag[$i] === 'SOUR') {
1277c7d1e03SGreg Roach                $inSOUR    = true;
1287c7d1e03SGreg Roach                $levelSOUR = $this->glevels[$i];
1297c7d1e03SGreg Roach                $dest      = 'S';
1307c7d1e03SGreg Roach            } else {
1317c7d1e03SGreg Roach                $dest = 'R';
1327c7d1e03SGreg Roach            }
1337c7d1e03SGreg Roach
1347c7d1e03SGreg Roach            if ($dest === 'S') {
1357c7d1e03SGreg Roach                $this->glevelsSOUR[] = $this->glevels[$i];
1367c7d1e03SGreg Roach                $this->tagSOUR[]     = $this->tag[$i];
1377c7d1e03SGreg Roach                $this->islinkSOUR[]  = $this->islink[$i];
1387c7d1e03SGreg Roach                $this->textSOUR[]    = $this->text[$i];
1397c7d1e03SGreg Roach            } else {
1407c7d1e03SGreg Roach                $this->glevelsRest[] = $this->glevels[$i];
1417c7d1e03SGreg Roach                $this->tagRest[]     = $this->tag[$i];
1427c7d1e03SGreg Roach                $this->islinkRest[]  = $this->islink[$i];
1437c7d1e03SGreg Roach                $this->textRest[]    = $this->text[$i];
1447c7d1e03SGreg Roach            }
1457c7d1e03SGreg Roach        }
1467c7d1e03SGreg Roach    }
1477c7d1e03SGreg Roach
1487c7d1e03SGreg Roach    /**
1497c7d1e03SGreg Roach     * Add new GEDCOM lines from the $xxxRest interface update arrays, which
1507c7d1e03SGreg Roach     * were produced by the splitSOUR() function.
1517c7d1e03SGreg Roach     * See the FunctionsEdit::handle_updatesges() function for details.
1527c7d1e03SGreg Roach     *
1537c7d1e03SGreg Roach     * @param string $inputRec
1547c7d1e03SGreg Roach     *
1557c7d1e03SGreg Roach     * @return string
1567c7d1e03SGreg Roach     */
1577c7d1e03SGreg Roach    public function updateRest(string $inputRec): string
1587c7d1e03SGreg Roach    {
1597c7d1e03SGreg Roach        if (count($this->tagRest) === 0) {
1607c7d1e03SGreg Roach            return $inputRec; // No update required
1617c7d1e03SGreg Roach        }
1627c7d1e03SGreg Roach
1637c7d1e03SGreg Roach        // Save original interface update arrays before replacing them with the xxxRest ones
1647c7d1e03SGreg Roach        $glevelsSave = $this->glevels;
1657c7d1e03SGreg Roach        $tagSave     = $this->tag;
1667c7d1e03SGreg Roach        $islinkSave  = $this->islink;
1677c7d1e03SGreg Roach        $textSave    = $this->text;
1687c7d1e03SGreg Roach
1697c7d1e03SGreg Roach        $this->glevels = $this->glevelsRest;
1707c7d1e03SGreg Roach        $this->tag     = $this->tagRest;
1717c7d1e03SGreg Roach        $this->islink  = $this->islinkRest;
1727c7d1e03SGreg Roach        $this->text    = $this->textRest;
1737c7d1e03SGreg Roach
1747c7d1e03SGreg Roach        $myRecord = $this->handleUpdates($inputRec, 'no'); // Now do the update
1757c7d1e03SGreg Roach
1767c7d1e03SGreg Roach        // Restore the original interface update arrays (just in case ...)
1777c7d1e03SGreg Roach        $this->glevels = $glevelsSave;
1787c7d1e03SGreg Roach        $this->tag     = $tagSave;
1797c7d1e03SGreg Roach        $this->islink  = $islinkSave;
1807c7d1e03SGreg Roach        $this->text    = $textSave;
1817c7d1e03SGreg Roach
1827c7d1e03SGreg Roach        return $myRecord;
1837c7d1e03SGreg Roach    }
1847c7d1e03SGreg Roach
1857c7d1e03SGreg Roach    /**
1867c7d1e03SGreg Roach     * Add new gedcom lines from interface update arrays
1877c7d1e03SGreg Roach     * The edit_interface and FunctionsEdit::add_simple_tag function produce the following
1887c7d1e03SGreg Roach     * arrays incoming from the $_POST form
1897c7d1e03SGreg Roach     * - $glevels[] - an array of the gedcom level for each line that was edited
1907c7d1e03SGreg Roach     * - $tag[] - an array of the tags for each gedcom line that was edited
1917c7d1e03SGreg Roach     * - $islink[] - an array of 1 or 0 values to tell whether the text is a link element and should be surrounded by @@
1927c7d1e03SGreg Roach     * - $text[] - an array of the text data for each line
1937c7d1e03SGreg Roach     * With these arrays you can recreate the gedcom lines like this
1947c7d1e03SGreg Roach     * <code>$glevel[0].' '.$tag[0].' '.$text[0]</code>
1957c7d1e03SGreg Roach     * There will be an index in each of these arrays for each line of the gedcom
1967c7d1e03SGreg Roach     * fact that is being edited.
1977c7d1e03SGreg Roach     * If the $text[] array is empty for the given line, then it means that the
1987c7d1e03SGreg Roach     * user removed that line during editing or that the line is supposed to be
1997c7d1e03SGreg Roach     * empty (1 DEAT, 1 BIRT) for example. To know if the line should be removed
2007c7d1e03SGreg Roach     * there is a section of code that looks ahead to the next lines to see if there
2017c7d1e03SGreg Roach     * are sub lines. For example we don't want to remove the 1 DEAT line if it has
2027c7d1e03SGreg Roach     * a 2 PLAC or 2 DATE line following it. If there are no sub lines, then the line
2037c7d1e03SGreg Roach     * can be safely removed.
2047c7d1e03SGreg Roach     *
2057c7d1e03SGreg Roach     * @param string $newged        the new gedcom record to add the lines to
2067c7d1e03SGreg Roach     * @param string $levelOverride Override GEDCOM level specified in $glevels[0]
2077c7d1e03SGreg Roach     *
2087c7d1e03SGreg Roach     * @return string The updated gedcom record
2097c7d1e03SGreg Roach     */
210*c8183f29SGreg Roach    public function handleUpdates(string $newged, string $levelOverride = 'no'): string
2117c7d1e03SGreg Roach    {
2127c7d1e03SGreg Roach        if ($levelOverride === 'no') {
2137c7d1e03SGreg Roach            $levelAdjust = 0;
2147c7d1e03SGreg Roach        } else {
2157c7d1e03SGreg Roach            $levelAdjust = 1;
2167c7d1e03SGreg Roach        }
2177c7d1e03SGreg Roach
2187c7d1e03SGreg Roach        // Assert all arrays are the same size.
2197c7d1e03SGreg Roach        assert(count($this->glevels) === count($this->tag));
2207c7d1e03SGreg Roach        assert(count($this->glevels) === count($this->text));
2217c7d1e03SGreg Roach        assert(count($this->glevels) === count($this->islink));
2227c7d1e03SGreg Roach
2237c7d1e03SGreg Roach        $count = count($this->glevels);
2247c7d1e03SGreg Roach
2257c7d1e03SGreg Roach        for ($j = 0; $j < $count; $j++) {
2267c7d1e03SGreg Roach            // Look for empty SOUR reference with non-empty sub-records.
2277c7d1e03SGreg Roach            // This can happen when the SOUR entry is deleted but its sub-records
2287c7d1e03SGreg Roach            // were incorrectly left intact.
2297c7d1e03SGreg Roach            // The sub-records should be deleted.
2307c7d1e03SGreg Roach            if ($this->tag[$j] === 'SOUR' && ($this->text[$j] === '@@' || $this->text[$j] === '')) {
2317c7d1e03SGreg Roach                $this->text[$j] = '';
2327c7d1e03SGreg Roach                $k              = $j + 1;
2337c7d1e03SGreg Roach                while ($k < $count && $this->glevels[$k] > $this->glevels[$j]) {
2347c7d1e03SGreg Roach                    $this->text[$k] = '';
2357c7d1e03SGreg Roach                    $k++;
2367c7d1e03SGreg Roach                }
2377c7d1e03SGreg Roach            }
2387c7d1e03SGreg Roach
2397c7d1e03SGreg Roach            if (trim($this->text[$j]) !== '') {
2407c7d1e03SGreg Roach                $pass = true;
2417c7d1e03SGreg Roach            } else {
2427c7d1e03SGreg Roach                //-- for facts with empty values they must have sub records
2437c7d1e03SGreg Roach                //-- this section checks if they have subrecords
2447c7d1e03SGreg Roach                $k    = $j + 1;
2457c7d1e03SGreg Roach                $pass = false;
2467c7d1e03SGreg Roach                while ($k < $count && $this->glevels[$k] > $this->glevels[$j]) {
2477c7d1e03SGreg Roach                    if ($this->text[$k] !== '') {
248c2ed51d1SGreg Roach                        if ($this->tag[$j] !== 'OBJE' || $this->tag[$k] === 'FILE') {
2497c7d1e03SGreg Roach                            $pass = true;
2507c7d1e03SGreg Roach                            break;
2517c7d1e03SGreg Roach                        }
2527c7d1e03SGreg Roach                    }
2537c7d1e03SGreg Roach                    $k++;
2547c7d1e03SGreg Roach                }
2557c7d1e03SGreg Roach            }
2567c7d1e03SGreg Roach
2577c7d1e03SGreg Roach            //-- if the value is not empty or it has sub lines
2587c7d1e03SGreg Roach            //--- then write the line to the gedcom record
2597c7d1e03SGreg Roach            //-- we have to let some emtpy text lines pass through... (DEAT, BIRT, etc)
2607c7d1e03SGreg Roach            if ($pass) {
2617c7d1e03SGreg Roach                $newline = (int) $this->glevels[$j] + $levelAdjust . ' ' . $this->tag[$j];
2627c7d1e03SGreg Roach                if ($this->text[$j] !== '') {
2637c7d1e03SGreg Roach                    if ($this->islink[$j]) {
26465de9aa7SGreg Roach                        $newline .= ' @' . trim($this->text[$j], '@') . '@';
2657c7d1e03SGreg Roach                    } else {
2667c7d1e03SGreg Roach                        $newline .= ' ' . $this->text[$j];
2677c7d1e03SGreg Roach                    }
2687c7d1e03SGreg Roach                }
2697c7d1e03SGreg Roach                $next_level = 1 + (int) $this->glevels[$j] + $levelAdjust;
2707c7d1e03SGreg Roach
2717c7d1e03SGreg Roach                $newged .= "\n" . str_replace("\n", "\n" . $next_level . ' CONT ', $newline);
2727c7d1e03SGreg Roach            }
2737c7d1e03SGreg Roach        }
2747c7d1e03SGreg Roach
2757c7d1e03SGreg Roach        return $newged;
2767c7d1e03SGreg Roach    }
2777c7d1e03SGreg Roach
2787c7d1e03SGreg Roach    /**
2797c7d1e03SGreg Roach     * Create a form to add a new fact.
2807c7d1e03SGreg Roach     *
2817c7d1e03SGreg Roach     * @param ServerRequestInterface $request
2827c7d1e03SGreg Roach     * @param Tree                   $tree
2837c7d1e03SGreg Roach     * @param string                 $fact
2847c7d1e03SGreg Roach     *
2857c7d1e03SGreg Roach     * @return string
2867c7d1e03SGreg Roach     */
28724f2a3afSGreg Roach    public function addNewFact(ServerRequestInterface $request, Tree $tree, string $fact): string
2887c7d1e03SGreg Roach    {
2897c7d1e03SGreg Roach        $params = (array) $request->getParsedBody();
2907c7d1e03SGreg Roach
2917c7d1e03SGreg Roach        $FACT = $params[$fact];
2927c7d1e03SGreg Roach        $DATE = $params[$fact . '_DATE'] ?? '';
2937c7d1e03SGreg Roach        $PLAC = $params[$fact . '_PLAC'] ?? '';
2947c7d1e03SGreg Roach
2957c7d1e03SGreg Roach        if ($DATE !== '' || $PLAC !== '' || $FACT !== '' && $FACT !== 'Y') {
2967c7d1e03SGreg Roach            if ($FACT !== '' && $FACT !== 'Y') {
2977c7d1e03SGreg Roach                $gedrec = "\n1 " . $fact . ' ' . $FACT;
2987c7d1e03SGreg Roach            } else {
2997c7d1e03SGreg Roach                $gedrec = "\n1 " . $fact;
3007c7d1e03SGreg Roach            }
3017c7d1e03SGreg Roach            if ($DATE !== '') {
3027c7d1e03SGreg Roach                $gedrec .= "\n2 DATE " . $DATE;
3037c7d1e03SGreg Roach            }
3047c7d1e03SGreg Roach            if ($PLAC !== '') {
3057c7d1e03SGreg Roach                $gedrec .= "\n2 PLAC " . $PLAC;
3067c7d1e03SGreg Roach
3077c7d1e03SGreg Roach                if (preg_match_all('/(' . Gedcom::REGEX_TAG . ')/', $tree->getPreference('ADVANCED_PLAC_FACTS'), $match)) {
3087c7d1e03SGreg Roach                    foreach ($match[1] as $tag) {
3097c7d1e03SGreg Roach                        $TAG = $params[$fact . '_' . $tag];
3107c7d1e03SGreg Roach                        if ($TAG !== '') {
3117c7d1e03SGreg Roach                            $gedrec .= "\n3 " . $tag . ' ' . $TAG;
3127c7d1e03SGreg Roach                        }
3137c7d1e03SGreg Roach                    }
3147c7d1e03SGreg Roach                }
31550f35038SGreg Roach                $LATI = $params[$fact . '_LATI'] ?? '';
31650f35038SGreg Roach                $LONG = $params[$fact . '_LONG'] ?? '';
3177c7d1e03SGreg Roach                if ($LATI !== '' || $LONG !== '') {
3187c7d1e03SGreg Roach                    $gedrec .= "\n3 MAP\n4 LATI " . $LATI . "\n4 LONG " . $LONG;
3197c7d1e03SGreg Roach                }
3207c7d1e03SGreg Roach            }
3217c7d1e03SGreg Roach            if ((bool) ($params['SOUR_' . $fact] ?? false)) {
3227c7d1e03SGreg Roach                return $this->updateSource($gedrec, 'yes');
3237c7d1e03SGreg Roach            }
3247c7d1e03SGreg Roach
3257c7d1e03SGreg Roach            return $gedrec;
3267c7d1e03SGreg Roach        }
3277c7d1e03SGreg Roach
3287c7d1e03SGreg Roach        if ($FACT === 'Y') {
3297c7d1e03SGreg Roach            if ((bool) ($params['SOUR_' . $fact] ?? false)) {
3307c7d1e03SGreg Roach                return $this->updateSource("\n1 " . $fact . ' Y', 'yes');
3317c7d1e03SGreg Roach            }
3327c7d1e03SGreg Roach
3337c7d1e03SGreg Roach            return "\n1 " . $fact . ' Y';
3347c7d1e03SGreg Roach        }
3357c7d1e03SGreg Roach
3367c7d1e03SGreg Roach        return '';
3377c7d1e03SGreg Roach    }
3387c7d1e03SGreg Roach
3397c7d1e03SGreg Roach    /**
3407c7d1e03SGreg Roach     * Add new GEDCOM lines from the $xxxSOUR interface update arrays, which
3417c7d1e03SGreg Roach     * were produced by the splitSOUR() function.
3427c7d1e03SGreg Roach     * See the FunctionsEdit::handle_updatesges() function for details.
3437c7d1e03SGreg Roach     *
3447c7d1e03SGreg Roach     * @param string $inputRec
3457c7d1e03SGreg Roach     * @param string $levelOverride
3467c7d1e03SGreg Roach     *
3477c7d1e03SGreg Roach     * @return string
3487c7d1e03SGreg Roach     */
3497c7d1e03SGreg Roach    public function updateSource(string $inputRec, string $levelOverride = 'no'): string
3507c7d1e03SGreg Roach    {
3517c7d1e03SGreg Roach        if (count($this->tagSOUR) === 0) {
3527c7d1e03SGreg Roach            return $inputRec; // No update required
3537c7d1e03SGreg Roach        }
3547c7d1e03SGreg Roach
3557c7d1e03SGreg Roach        // Save original interface update arrays before replacing them with the xxxSOUR ones
3567c7d1e03SGreg Roach        $glevelsSave = $this->glevels;
3577c7d1e03SGreg Roach        $tagSave     = $this->tag;
3587c7d1e03SGreg Roach        $islinkSave  = $this->islink;
3597c7d1e03SGreg Roach        $textSave    = $this->text;
3607c7d1e03SGreg Roach
3617c7d1e03SGreg Roach        $this->glevels = $this->glevelsSOUR;
3627c7d1e03SGreg Roach        $this->tag     = $this->tagSOUR;
3637c7d1e03SGreg Roach        $this->islink  = $this->islinkSOUR;
3647c7d1e03SGreg Roach        $this->text    = $this->textSOUR;
3657c7d1e03SGreg Roach
3667c7d1e03SGreg Roach        $myRecord = $this->handleUpdates($inputRec, $levelOverride); // Now do the update
3677c7d1e03SGreg Roach
3687c7d1e03SGreg Roach        // Restore the original interface update arrays (just in case ...)
3697c7d1e03SGreg Roach        $this->glevels = $glevelsSave;
3707c7d1e03SGreg Roach        $this->tag     = $tagSave;
3717c7d1e03SGreg Roach        $this->islink  = $islinkSave;
3727c7d1e03SGreg Roach        $this->text    = $textSave;
3737c7d1e03SGreg Roach
3747c7d1e03SGreg Roach        return $myRecord;
3757c7d1e03SGreg Roach    }
3767c7d1e03SGreg Roach
3777c7d1e03SGreg Roach    /**
3787c7d1e03SGreg Roach     * Create a form to add a sex record.
3797c7d1e03SGreg Roach     *
3807c7d1e03SGreg Roach     * @param ServerRequestInterface $request
3817c7d1e03SGreg Roach     *
3827c7d1e03SGreg Roach     * @return string
3837c7d1e03SGreg Roach     */
3847c7d1e03SGreg Roach    public function addNewSex(ServerRequestInterface $request): string
3857c7d1e03SGreg Roach    {
3867c7d1e03SGreg Roach        $params = (array) $request->getParsedBody();
3877c7d1e03SGreg Roach
3887c7d1e03SGreg Roach        switch ($params['SEX']) {
3897c7d1e03SGreg Roach            case 'M':
3907c7d1e03SGreg Roach                return "\n1 SEX M";
3917c7d1e03SGreg Roach            case 'F':
3927c7d1e03SGreg Roach                return "\n1 SEX F";
3937c7d1e03SGreg Roach            default:
3947c7d1e03SGreg Roach                return "\n1 SEX U";
3957c7d1e03SGreg Roach        }
3967c7d1e03SGreg Roach    }
3977c7d1e03SGreg Roach
3987c7d1e03SGreg Roach    /**
3997c7d1e03SGreg Roach     * Assemble the pieces of a newly created record into gedcom
4007c7d1e03SGreg Roach     *
4017c7d1e03SGreg Roach     * @param ServerRequestInterface $request
4027c7d1e03SGreg Roach     * @param Tree                   $tree
4037c7d1e03SGreg Roach     *
4047c7d1e03SGreg Roach     * @return string
4057c7d1e03SGreg Roach     */
4067c7d1e03SGreg Roach    public function addNewName(ServerRequestInterface $request, Tree $tree): string
4077c7d1e03SGreg Roach    {
4087c7d1e03SGreg Roach        $params = (array) $request->getParsedBody();
4097c7d1e03SGreg Roach        $gedrec = "\n1 NAME " . $params['NAME'];
4107c7d1e03SGreg Roach
4117c7d1e03SGreg Roach        $tags = [
4127c7d1e03SGreg Roach            'NPFX',
4137c7d1e03SGreg Roach            'GIVN',
4147c7d1e03SGreg Roach            'SPFX',
4157c7d1e03SGreg Roach            'SURN',
4167c7d1e03SGreg Roach            'NSFX',
4177c7d1e03SGreg Roach            'NICK',
4187c7d1e03SGreg Roach        ];
4197c7d1e03SGreg Roach
4207c7d1e03SGreg Roach        if (preg_match_all('/(' . Gedcom::REGEX_TAG . ')/', $tree->getPreference('ADVANCED_NAME_FACTS'), $match)) {
4217c7d1e03SGreg Roach            $tags = array_merge($tags, $match[1]);
4227c7d1e03SGreg Roach        }
4237c7d1e03SGreg Roach
4247c7d1e03SGreg Roach        // Paternal and Polish and Lithuanian surname traditions can also create a _MARNM
4257c7d1e03SGreg Roach        $SURNAME_TRADITION = $tree->getPreference('SURNAME_TRADITION');
4267c7d1e03SGreg Roach        if ($SURNAME_TRADITION === 'paternal' || $SURNAME_TRADITION === 'polish' || $SURNAME_TRADITION === 'lithuanian') {
4277c7d1e03SGreg Roach            $tags[] = '_MARNM';
4287c7d1e03SGreg Roach        }
4297c7d1e03SGreg Roach
4307c7d1e03SGreg Roach        foreach (array_unique($tags) as $tag) {
4317c7d1e03SGreg Roach            $TAG = $params[$tag];
4327c7d1e03SGreg Roach
4337c7d1e03SGreg Roach            if ($TAG !== '') {
4347c7d1e03SGreg Roach                $gedrec .= "\n2 " . $tag . ' ' . $TAG;
4357c7d1e03SGreg Roach            }
4367c7d1e03SGreg Roach        }
4377c7d1e03SGreg Roach
4387c7d1e03SGreg Roach        return $gedrec;
4397c7d1e03SGreg Roach    }
440c2ed51d1SGreg Roach
441c2ed51d1SGreg Roach    /**
442c2ed51d1SGreg Roach     * Reassemble edited GEDCOM fields into a GEDCOM fact/event string.
443c2ed51d1SGreg Roach     *
444c2ed51d1SGreg Roach     * @param string        $record_type
445c2ed51d1SGreg Roach     * @param array<string> $levels
446c2ed51d1SGreg Roach     * @param array<string> $tags
447c2ed51d1SGreg Roach     * @param array<string> $values
448c2ed51d1SGreg Roach     *
449c2ed51d1SGreg Roach     * @return string
450c2ed51d1SGreg Roach     */
451c2ed51d1SGreg Roach    public function editLinesToGedcom(string $record_type, array $levels, array $tags, array $values): string
452c2ed51d1SGreg Roach    {
453c2ed51d1SGreg Roach        // Assert all arrays are the same size.
454c2ed51d1SGreg Roach        $count = count($levels);
455c2ed51d1SGreg Roach        assert($count > 0);
456c2ed51d1SGreg Roach        assert(count($tags) === $count);
457c2ed51d1SGreg Roach        assert(count($values) === $count);
458c2ed51d1SGreg Roach
459c2ed51d1SGreg Roach        $gedcom_lines = [];
460c2ed51d1SGreg Roach        $hierarchy    = [$record_type];
461c2ed51d1SGreg Roach
462c2ed51d1SGreg Roach        for ($i = 0; $i < $count; $i++) {
463c2ed51d1SGreg Roach            $hierarchy[$levels[$i]] = $tags[$i];
464c2ed51d1SGreg Roach
465c2ed51d1SGreg Roach            $full_tag   = implode(':', array_slice($hierarchy, 0, 1 + (int) $levels[$i]));
466c2ed51d1SGreg Roach            $element    = Registry::elementFactory()->make($full_tag);
467c2ed51d1SGreg Roach            $values[$i] = $element->canonical($values[$i]);
468c2ed51d1SGreg Roach
469c2ed51d1SGreg Roach            // If "1 FACT Y" has a DATE or PLAC, then delete the value of Y
470c2ed51d1SGreg Roach            if ($levels[$i] === '1' && $values[$i] === 'Y') {
471c2ed51d1SGreg Roach                for ($j = $i + 1; $j < $count && $levels[$j] > $levels[$i]; ++$j) {
472c2ed51d1SGreg Roach                    if ($levels[$j] === '2' && ($tags[$j] === 'DATE' || $tags[$j] === 'PLAC') && $values[$j] !== '') {
473c2ed51d1SGreg Roach                        $values[$i] = '';
474c2ed51d1SGreg Roach                        break;
475c2ed51d1SGreg Roach                    }
476c2ed51d1SGreg Roach                }
477c2ed51d1SGreg Roach            }
478c2ed51d1SGreg Roach
479c2ed51d1SGreg Roach            // Include this line if there is a value - or if there is a child record with a value.
480c2ed51d1SGreg Roach            $include = $values[$i] !== '';
481c2ed51d1SGreg Roach
482c2ed51d1SGreg Roach            for ($j = $i + 1; !$include && $j < $count && $levels[$j] > $levels[$i]; $j++) {
483c2ed51d1SGreg Roach                $include = $values[$j] !== '';
484c2ed51d1SGreg Roach            }
485c2ed51d1SGreg Roach
486c2ed51d1SGreg Roach            if ($include) {
487c2ed51d1SGreg Roach                if ($values[$i] === '') {
488c2ed51d1SGreg Roach                    $gedcom_lines[] = $levels[$i] . ' ' . $tags[$i];
489c2ed51d1SGreg Roach                } else {
490f7c88e25SGreg Roach                    if ($tags[$i] === 'CONC') {
491f7c88e25SGreg Roach                        $next_level = (int) $levels[$i];
492f7c88e25SGreg Roach                    } else {
493c2ed51d1SGreg Roach                        $next_level = 1 + (int) $levels[$i];
494f7c88e25SGreg Roach                    }
495c2ed51d1SGreg Roach
496c2ed51d1SGreg Roach                    $gedcom_lines[] = $levels[$i] . ' ' . $tags[$i] . ' ' . str_replace("\n", "\n" . $next_level . ' CONT ', $values[$i]);
497c2ed51d1SGreg Roach                }
498c2ed51d1SGreg Roach            }
499c2ed51d1SGreg Roach        }
500c2ed51d1SGreg Roach
501c2ed51d1SGreg Roach        return implode("\n", $gedcom_lines);
502c2ed51d1SGreg Roach    }
5037c7d1e03SGreg Roach}
504