Passed
Push — master ( a2ae7a...86787b )
by Greg
06:02
created

AbstractEditController::updateRest()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 26
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 16
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 26
rs 9.7333
1
<?php
2
3
/**
4
 * webtrees: online genealogy
5
 * Copyright (C) 2019 webtrees development team
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
 * GNU General Public License for more details.
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16
 */
17
18
declare(strict_types=1);
19
20
namespace Fisharebest\Webtrees\Http\Controllers;
21
22
use Fisharebest\Webtrees\Gedcom;
23
use Fisharebest\Webtrees\Tree;
24
use Psr\Http\Message\ServerRequestInterface;
25
26
/**
27
 * Common logic for edit controllers.
28
 */
29
abstract class AbstractEditController extends AbstractBaseController
30
{
31
    /** @var string[] */
32
    protected $glevels = [];
33
34
    /** @var string[] */
35
    protected $tag     = [];
36
37
    /** @var string[] */
38
    protected $islink  = [];
39
40
    /** @var string[] */
41
    protected $text    = [];
42
43
    /** @var string[] */
44
    protected $glevelsSOUR = [];
45
46
    /** @var string[] */
47
    protected $tagSOUR     = [];
48
49
    /** @var string[] */
50
    protected $islinkSOUR  = [];
51
52
    /** @var string[] */
53
    protected $textSOUR    = [];
54
55
    /** @var string[] */
56
    protected $glevelsRest = [];
57
58
    /** @var string[] */
59
    protected $tagRest     = [];
60
61
    /** @var string[] */
62
    protected $islinkRest  = [];
63
64
    /** @var string[] */
65
    protected $textRest    = [];
66
67
    /**
68
     * This function splits the $glevels, $tag, $islink, and $text arrays so that the
69
     * entries associated with a SOUR record are separate from everything else.
70
     *
71
     * Input arrays:
72
     * - $glevels[] - an array of the gedcom level for each line that was edited
73
     * - $tag[] - an array of the tags for each gedcom line that was edited
74
     * - $islink[] - an array of 1 or 0 values to indicate when the text is a link element
75
     * - $text[] - an array of the text data for each line
76
     *
77
     * Output arrays:
78
     * ** For the SOUR record:
79
     * - $glevelsSOUR[] - an array of the gedcom level for each line that was edited
80
     * - $tagSOUR[] - an array of the tags for each gedcom line that was edited
81
     * - $islinkSOUR[] - an array of 1 or 0 values to indicate when the text is a link element
82
     * - $textSOUR[] - an array of the text data for each line
83
     * ** For the remaining records:
84
     * - $glevelsRest[] - an array of the gedcom level for each line that was edited
85
     * - $tagRest[] - an array of the tags for each gedcom line that was edited
86
     * - $islinkRest[] - an array of 1 or 0 values to indicate when the text is a link element
87
     * - $textRest[] - an array of the text data for each line
88
     *
89
     * @return void
90
     */
91
    protected function splitSource(): void
92
    {
93
        $this->glevelsSOUR = [];
94
        $this->tagSOUR     = [];
95
        $this->islinkSOUR  = [];
96
        $this->textSOUR    = [];
97
98
        $this->glevelsRest = [];
99
        $this->tagRest     = [];
100
        $this->islinkRest  = [];
101
        $this->textRest    = [];
102
103
        $inSOUR    = false;
104
        $levelSOUR = 0;
105
106
        // Assume all arrays are the same size.
107
        $count = count($this->glevels);
108
109
        for ($i = 0; $i < $count; $i++) {
110
            if ($inSOUR) {
111
                if ($levelSOUR < $this->glevels[$i]) {
112
                    $dest = 'S';
113
                } else {
114
                    $inSOUR = false;
115
                    $dest   = 'R';
116
                }
117
            } elseif ($this->tag[$i] === 'SOUR') {
118
                $inSOUR    = true;
119
                $levelSOUR = $this->glevels[$i];
120
                $dest      = 'S';
121
            } else {
122
                $dest = 'R';
123
            }
124
125
            if ($dest === 'S') {
126
                $this->glevelsSOUR[] = $this->glevels[$i];
127
                $this->tagSOUR[]     = $this->tag[$i];
128
                $this->islinkSOUR[]  = $this->islink[$i];
129
                $this->textSOUR[]    = $this->text[$i];
130
            } else {
131
                $this->glevelsRest[] = $this->glevels[$i];
132
                $this->tagRest[]     = $this->tag[$i];
133
                $this->islinkRest[]  = $this->islink[$i];
134
                $this->textRest[]    = $this->text[$i];
135
            }
136
        }
137
    }
138
139
    /**
140
     * Add new GEDCOM lines from the $xxxRest interface update arrays, which
141
     * were produced by the splitSOUR() function.
142
     * See the FunctionsEdit::handle_updatesges() function for details.
143
     *
144
     * @param string $inputRec
145
     * @param string $levelOverride
146
     *
147
     * @return string
148
     */
149
    protected function updateRest(string $inputRec): string
150
    {
151
        if (count($this->tagRest) === 0) {
152
            return $inputRec; // No update required
153
        }
154
155
        // Save original interface update arrays before replacing them with the xxxRest ones
156
        $glevelsSave = $this->glevels;
157
        $tagSave     = $this->tag;
158
        $islinkSave  = $this->islink;
159
        $textSave    = $this->text;
160
161
        $this->glevels = $this->glevelsRest;
162
        $this->tag     = $this->tagRest;
163
        $this->islink  = $this->islinkRest;
164
        $this->text    = $this->textRest;
165
166
        $myRecord = $this->handleUpdates($inputRec, 'no'); // Now do the update
167
168
        // Restore the original interface update arrays (just in case ...)
169
        $this->glevels = $glevelsSave;
170
        $this->tag     = $tagSave;
171
        $this->islink  = $islinkSave;
172
        $this->text    = $textSave;
173
174
        return $myRecord;
175
    }
176
177
    /**
178
     * Add new gedcom lines from interface update arrays
179
     * The edit_interface and FunctionsEdit::add_simple_tag function produce the following
180
     * arrays incoming from the $_POST form
181
     * - $glevels[] - an array of the gedcom level for each line that was edited
182
     * - $tag[] - an array of the tags for each gedcom line that was edited
183
     * - $islink[] - an array of 1 or 0 values to tell whether the text is a link element and should be surrounded by @@
184
     * - $text[] - an array of the text data for each line
185
     * With these arrays you can recreate the gedcom lines like this
186
     * <code>$glevel[0].' '.$tag[0].' '.$text[0]</code>
187
     * There will be an index in each of these arrays for each line of the gedcom
188
     * fact that is being edited.
189
     * If the $text[] array is empty for the given line, then it means that the
190
     * user removed that line during editing or that the line is supposed to be
191
     * empty (1 DEAT, 1 BIRT) for example. To know if the line should be removed
192
     * there is a section of code that looks ahead to the next lines to see if there
193
     * are sub lines. For example we don't want to remove the 1 DEAT line if it has
194
     * a 2 PLAC or 2 DATE line following it. If there are no sub lines, then the line
195
     * can be safely removed.
196
     *
197
     * @param string $newged        the new gedcom record to add the lines to
198
     * @param string $levelOverride Override GEDCOM level specified in $glevels[0]
199
     *
200
     * @return string The updated gedcom record
201
     */
202
    protected function handleUpdates(string $newged, $levelOverride = 'no'): string
203
    {
204
        if ($levelOverride === 'no') {
205
            $levelAdjust = 0;
206
        } else {
207
            $levelAdjust = 1;
208
        }
209
210
        // Assert all arrays are the same size.
211
        assert(count($this->glevels) === count($this->tag));
212
        assert(count($this->glevels) === count($this->text));
213
        assert(count($this->glevels) === count($this->islink));
214
215
        $count = count($this->glevels);
216
217
        for ($j = 0; $j < $count; $j++) {
218
            // Look for empty SOUR reference with non-empty sub-records.
219
            // This can happen when the SOUR entry is deleted but its sub-records
220
            // were incorrectly left intact.
221
            // The sub-records should be deleted.
222
            if ($this->tag[$j] === 'SOUR' && ($this->text[$j] === '@@' || $this->text[$j] === '')) {
223
                $this->text[$j] = '';
224
                $k              = $j + 1;
225
                while ($k < $count && $this->glevels[$k] > $this->glevels[$j]) {
226
                    $this->text[$k] = '';
227
                    $k++;
228
                }
229
            }
230
231
            if (trim($this->text[$j]) !== '') {
232
                $pass = true;
233
            } else {
234
                //-- for facts with empty values they must have sub records
235
                //-- this section checks if they have subrecords
236
                $k    = $j + 1;
237
                $pass = false;
238
                while ($k < $count && $this->glevels[$k] > $this->glevels[$j]) {
239
                    if ($this->text[$k] !== '') {
240
                        if (($this->tag[$j] !== 'OBJE') || ($this->tag[$k] === 'FILE')) {
241
                            $pass = true;
242
                            break;
243
                        }
244
                    }
245
                    $k++;
246
                }
247
            }
248
249
            //-- if the value is not empty or it has sub lines
250
            //--- then write the line to the gedcom record
251
            //-- we have to let some emtpy text lines pass through... (DEAT, BIRT, etc)
252
            if ($pass) {
253
                $newline = (int) $this->glevels[$j] + $levelAdjust . ' ' . $this->tag[$j];
254
                if ($this->text[$j] !== '') {
255
                    if ($this->islink[$j]) {
256
                        $newline .= ' @' . $this->text[$j] . '@';
257
                    } else {
258
                        $newline .= ' ' . $this->text[$j];
259
                    }
260
                }
261
                $newged .= "\n" . str_replace("\n", "\n" . (1 + substr($newline, 0, 1)) . ' CONT ', $newline);
262
            }
263
        }
264
265
        return $newged;
266
    }
267
268
    /**
269
     * Create a form to add a new fact.
270
     *
271
     * @param ServerRequestInterface $request
272
     * @param Tree    $tree
273
     * @param string  $fact
274
     *
275
     * @return string
276
     */
277
    protected function addNewFact(ServerRequestInterface $request, Tree $tree, $fact): string
278
    {
279
        $params = $request->getParsedBody();
280
281
        $FACT = $params[$fact];
282
        $DATE = $params[$fact . '_DATE'] ?? '';
283
        $PLAC = $params[$fact . '_PLAC'] ?? '';
284
285
        if ($DATE !== '' || $PLAC !== '' || $FACT !== '' && $FACT !== 'Y') {
286
            if ($FACT !== '' && $FACT !== 'Y') {
287
                $gedrec = "\n1 " . $fact . ' ' . $FACT;
288
            } else {
289
                $gedrec = "\n1 " . $fact;
290
            }
291
            if ($DATE) {
292
                $gedrec .= "\n2 DATE " . $DATE;
293
            }
294
            if ($PLAC) {
295
                $gedrec .= "\n2 PLAC " . $PLAC;
296
297
                if (preg_match_all('/(' . Gedcom::REGEX_TAG . ')/', $tree->getPreference('ADVANCED_PLAC_FACTS'), $match)) {
298
                    foreach ($match[1] as $tag) {
299
                        $TAG = $params[$fact . '_' . $tag];
300
                        if ($TAG !== '') {
301
                            $gedrec .= "\n3 " . $tag . ' ' . $TAG;
302
                        }
303
                    }
304
                }
305
                $LATI = $params[$fact . '_LATI'];
306
                $LONG = $params[$fact . '_LONG'];
307
                if ($LATI !== '' || $LONG !== '') {
308
                    $gedrec .= "\n3 MAP\n4 LATI " . $LATI . "\n4 LONG " . $LONG;
309
                }
310
            }
311
            if ((bool) ($params['SOUR_' . $fact] ?? false)) {
312
                return $this->updateSource($gedrec, 'yes');
313
            }
314
315
            return $gedrec;
316
        }
317
318
        if ($FACT === 'Y') {
319
            if ((bool) ($params['SOUR_' . $fact] ?? false)) {
320
                return $this->updateSource("\n1 " . $fact . ' Y', 'yes');
321
            }
322
323
            return "\n1 " . $fact . ' Y';
324
        }
325
326
        return '';
327
    }
328
329
    /**
330
     * Add new GEDCOM lines from the $xxxSOUR interface update arrays, which
331
     * were produced by the splitSOUR() function.
332
     * See the FunctionsEdit::handle_updatesges() function for details.
333
     *
334
     * @param string $inputRec
335
     * @param string $levelOverride
336
     *
337
     * @return string
338
     */
339
    protected function updateSource(string $inputRec, string $levelOverride = 'no'): string
340
    {
341
        if (count($this->tagSOUR) === 0) {
342
            return $inputRec; // No update required
343
        }
344
345
        // Save original interface update arrays before replacing them with the xxxSOUR ones
346
        $glevelsSave = $this->glevels;
347
        $tagSave     = $this->tag;
348
        $islinkSave  = $this->islink;
349
        $textSave    = $this->text;
350
351
        $this->glevels = $this->glevelsSOUR;
352
        $this->tag     = $this->tagSOUR;
353
        $this->islink  = $this->islinkSOUR;
354
        $this->text    = $this->textSOUR;
355
356
        $myRecord = $this->handleUpdates($inputRec, $levelOverride); // Now do the update
357
358
        // Restore the original interface update arrays (just in case ...)
359
        $this->glevels = $glevelsSave;
360
        $this->tag     = $tagSave;
361
        $this->islink  = $islinkSave;
362
        $this->text    = $textSave;
363
364
        return $myRecord;
365
    }
366
367
    /**
368
     * Create a form to add a sex record.
369
     *
370
     * @param ServerRequestInterface $request
371
     *
372
     * @return string
373
     */
374
    protected function addNewSex(ServerRequestInterface $request): string
375
    {
376
        switch ($request->getParsedBody()['SEX']) {
377
            case 'M':
378
                return "\n1 SEX M";
379
            case 'F':
380
                return "\n1 SEX F";
381
            default:
382
                return "\n1 SEX U";
383
        }
384
    }
385
386
    /**
387
     * Assemble the pieces of a newly created record into gedcom
388
     *
389
     * @param ServerRequestInterface $request
390
     * @param Tree    $tree
391
     *
392
     * @return string
393
     */
394
    protected function addNewName(ServerRequestInterface $request, Tree $tree): string
395
    {
396
        $params = $request->getParsedBody();
397
        $gedrec = "\n1 NAME " . $params['NAME'];
398
399
        $tags = [
400
            'NPFX',
401
            'GIVN',
402
            'SPFX',
403
            'SURN',
404
            'NSFX',
405
            'NICK',
406
        ];
407
408
        if (preg_match_all('/(' . Gedcom::REGEX_TAG . ')/', $tree->getPreference('ADVANCED_NAME_FACTS'), $match)) {
409
            $tags = array_merge($tags, $match[1]);
410
        }
411
412
        // Paternal and Polish and Lithuanian surname traditions can also create a _MARNM
413
        $SURNAME_TRADITION = $tree->getPreference('SURNAME_TRADITION');
414
        if ($SURNAME_TRADITION === 'paternal' || $SURNAME_TRADITION === 'polish' || $SURNAME_TRADITION === 'lithuanian') {
415
            $tags[] = '_MARNM';
416
        }
417
418
        foreach (array_unique($tags) as $tag) {
419
            $TAG = $params[$tag];
420
421
            if ($TAG !== '') {
422
                $gedrec .= "\n2 " . $tag . ' ' . $TAG;
423
            }
424
        }
425
426
        return $gedrec;
427
    }
428
}
429