FunctionsImport   F
last analyzed

Complexity

Total Complexity 253

Size/Duplication

Total Lines 1128
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 811
dl 0
loc 1128
rs 1.789
c 0
b 0
f 0
wmc 253

11 Methods

Rating   Name   Duplication   Size   Complexity  
A convertInlineMedia() 0 13 4
A rejectAllChanges() 0 9 1
F importRecord() 0 159 23
B updateRecord() 0 57 10
A createMediaObject() 0 43 4
F reformatRecord() 0 542 180
A updateLinks() 0 16 5
A updateNames() 0 28 5
B updatePlaces() 0 82 10
B updateDates() 0 37 8
A acceptAllChanges() 0 23 3

How to fix   Complexity   

Complex Class

Complex classes like FunctionsImport often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use FunctionsImport, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * webtrees: online genealogy
4
 * Copyright (C) 2019 webtrees development team
5
 * This program is free software: you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation, either version 3 of the License, or
8
 * (at your option) any later version.
9
 * This program is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
 * GNU General Public License for more details.
13
 * You should have received a copy of the GNU General Public License
14
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15
 */
16
namespace Fisharebest\Webtrees\Functions;
17
18
use Fisharebest\Webtrees\Database;
19
use Fisharebest\Webtrees\Date;
20
use Fisharebest\Webtrees\GedcomRecord;
21
use Fisharebest\Webtrees\GedcomTag;
22
use Fisharebest\Webtrees\I18N;
23
use Fisharebest\Webtrees\Individual;
24
use Fisharebest\Webtrees\Log;
25
use Fisharebest\Webtrees\Media;
26
use Fisharebest\Webtrees\Note;
27
use Fisharebest\Webtrees\Repository;
28
use Fisharebest\Webtrees\Soundex;
29
use Fisharebest\Webtrees\Source;
30
use Fisharebest\Webtrees\Tree;
31
use PDOException;
32
33
/**
34
 * Class FunctionsImport - common functions
35
 */
36
class FunctionsImport
37
{
38
    /**
39
     * Tidy up a gedcom record on import, so that we can access it consistently/efficiently.
40
     *
41
     * @param string $rec
42
     * @param Tree   $tree
43
     *
44
     * @return string
45
     */
46
    public static function reformatRecord($rec, Tree $tree)
47
    {
48
        // Strip out UTF8 formatting characters
49
        $rec = str_replace(array(WT_UTF8_BOM, WT_UTF8_LRM, WT_UTF8_RLM), '', $rec);
50
51
        // Strip out mac/msdos line endings
52
        $rec = preg_replace("/[\r\n]+/", "\n", $rec);
53
54
        // Extract lines from the record; lines consist of: level + optional xref + tag + optional data
55
        $num_matches = preg_match_all('/^[ \t]*(\d+)[ \t]*(@[^@]*@)?[ \t]*(\w+)[ \t]?(.*)$/m', $rec, $matches, PREG_SET_ORDER);
56
57
        // Process the record line-by-line
58
        $newrec = '';
59
        foreach ($matches as $n => $match) {
60
            list(, $level, $xref, $tag, $data) = $match;
61
            $tag                               = strtoupper($tag); // Tags should always be upper case
62
            switch ($tag) {
63
                // Convert PhpGedView tags to WT
64
                case '_PGVU':
65
                    $tag = '_WT_USER';
66
                    break;
67
                case '_PGV_OBJS':
68
                    $tag = '_WT_OBJE_SORT';
69
                    break;
70
                // Convert FTM-style "TAG_FORMAL_NAME" into "TAG".
71
                case 'ABBREVIATION':
72
                    $tag = 'ABBR';
73
                    break;
74
                case 'ADDRESS':
75
                    $tag = 'ADDR';
76
                    break;
77
                case 'ADDRESS1':
78
                    $tag = 'ADR1';
79
                    break;
80
                case 'ADDRESS2':
81
                    $tag = 'ADR2';
82
                    break;
83
                case 'ADDRESS3':
84
                    $tag = 'ADR3';
85
                    break;
86
                case 'ADOPTION':
87
                    $tag = 'ADOP';
88
                    break;
89
                case 'ADULT_CHRISTENING':
90
                    $tag = 'CHRA';
91
                    break;
92
                case 'AFN':
93
                    // AFN values are upper case
94
                    $data = strtoupper($data);
95
                    break;
96
                case 'AGENCY':
97
                    $tag = 'AGNC';
98
                    break;
99
                case 'ALIAS':
100
                    $tag = 'ALIA';
101
                    break;
102
                case 'ANCESTORS':
103
                    $tag = 'ANCE';
104
                    break;
105
                case 'ANCES_INTEREST':
106
                    $tag = 'ANCI';
107
                    break;
108
                case 'ANNULMENT':
109
                    $tag = 'ANUL';
110
                    break;
111
                case 'ASSOCIATES':
112
                    $tag = 'ASSO';
113
                    break;
114
                case 'AUTHOR':
115
                    $tag = 'AUTH';
116
                    break;
117
                case 'BAPTISM':
118
                    $tag = 'BAPM';
119
                    break;
120
                case 'BAPTISM_LDS':
121
                    $tag = 'BAPL';
122
                    break;
123
                case 'BAR_MITZVAH':
124
                    $tag = 'BARM';
125
                    break;
126
                case 'BAS_MITZVAH':
127
                    $tag = 'BASM';
128
                    break;
129
                case 'BIRTH':
130
                    $tag = 'BIRT';
131
                    break;
132
                case 'BLESSING':
133
                    $tag = 'BLES';
134
                    break;
135
                case 'BURIAL':
136
                    $tag = 'BURI';
137
                    break;
138
                case 'CALL_NUMBER':
139
                    $tag = 'CALN';
140
                    break;
141
                case 'CASTE':
142
                    $tag = 'CAST';
143
                    break;
144
                case 'CAUSE':
145
                    $tag = 'CAUS';
146
                    break;
147
                case 'CENSUS':
148
                    $tag = 'CENS';
149
                    break;
150
                case 'CHANGE':
151
                    $tag = 'CHAN';
152
                    break;
153
                case 'CHARACTER':
154
                    $tag = 'CHAR';
155
                    break;
156
                case 'CHILD':
157
                    $tag = 'CHIL';
158
                    break;
159
                case 'CHILDREN_COUNT':
160
                    $tag = 'NCHI';
161
                    break;
162
                case 'CHRISTENING':
163
                    $tag = 'CHR';
164
                    break;
165
                case 'CONCATENATION':
166
                    $tag = 'CONC';
167
                    break;
168
                case 'CONFIRMATION':
169
                    $tag = 'CONF';
170
                    break;
171
                case 'CONFIRMATION_LDS':
172
                    $tag = 'CONL';
173
                    break;
174
                case 'CONTINUED':
175
                    $tag = 'CONT';
176
                    break;
177
                case 'COPYRIGHT':
178
                    $tag = 'COPR';
179
                    break;
180
                case 'CORPORATE':
181
                    $tag = 'CORP';
182
                    break;
183
                case 'COUNTRY':
184
                    $tag = 'CTRY';
185
                    break;
186
                case 'CREMATION':
187
                    $tag = 'CREM';
188
                    break;
189
                case 'DATE':
190
                    // Preserve text from INT dates
191
                    if (strpos($data, '(') !== false) {
192
                        list($date, $text) = explode('(', $data, 2);
193
                        $text              = ' (' . $text;
194
                    } else {
195
                        $date = $data;
196
                        $text = '';
197
                    }
198
                    // Capitals
199
                    $date = strtoupper($date);
200
                    // Temporarily add leading/trailing spaces, to allow efficient matching below
201
                    $date = " {$date} ";
202
                    // Ensure space digits and letters
203
                    $date = preg_replace('/([A-Z])(\d)/', '$1 $2', $date);
204
                    $date = preg_replace('/(\d)([A-Z])/', '$1 $2', $date);
205
                    // Ensure space before/after calendar escapes
206
                    $date = preg_replace('/@#[^@]+@/', ' $0 ', $date);
207
                    // "BET." => "BET"
208
                    $date = preg_replace('/(\w\w)\./', '$1', $date);
209
                    // "CIR" => "ABT"
210
                    $date = str_replace(' CIR ', ' ABT ', $date);
211
                    $date = str_replace(' APX ', ' ABT ', $date);
212
                    // B.C. => BC (temporarily, to allow easier handling of ".")
213
                    $date = str_replace(' B.C. ', ' BC ', $date);
214
                    // "BET X - Y " => "BET X AND Y"
215
                    $date = preg_replace('/^(.* BET .+) - (.+)/', '$1 AND $2', $date);
216
                    $date = preg_replace('/^(.* FROM .+) - (.+)/', '$1 TO $2', $date);
217
                    // "@#ESC@ FROM X TO Y" => "FROM @#ESC@ X TO @#ESC@ Y"
218
                    $date = preg_replace('/^ +(@#[^@]+@) +FROM +(.+) +TO +(.+)/', ' FROM $1 $2 TO $1 $3', $date);
219
                    $date = preg_replace('/^ +(@#[^@]+@) +BET +(.+) +AND +(.+)/', ' BET $1 $2 AND $1 $3', $date);
220
                    // "@#ESC@ AFT X" => "AFT @#ESC@ X"
221
                    $date = preg_replace('/^ +(@#[^@]+@) +(FROM|BET|TO|AND|BEF|AFT|CAL|EST|INT|ABT) +(.+)/', ' $2 $1 $3', $date);
222
                    // Ignore any remaining punctuation, e.g. "14-MAY, 1900" => "14 MAY 1900"
223
                    // (don't change "/" - it is used in NS/OS dates)
224
                    $date = preg_replace('/[.,:;-]/', ' ', $date);
225
                    // BC => B.C.
226
                    $date = str_replace(' BC ', ' B.C. ', $date);
227
                    // Append the "INT" text
228
                    $data = $date . $text;
229
                    break;
230
                case 'DEATH':
231
                    $tag = 'DEAT';
232
                    break;
233
                case '_DEATH_OF_SPOUSE':
234
                    $tag = '_DETS';
235
                    break;
236
                case '_DEGREE':
237
                    $tag = '_DEG';
238
                    break;
239
                case 'DESCENDANTS':
240
                    $tag = 'DESC';
241
                    break;
242
                case 'DESCENDANT_INT':
243
                    $tag = 'DESI';
244
                    break;
245
                case 'DESTINATION':
246
                    $tag = 'DEST';
247
                    break;
248
                case 'DIVORCE':
249
                    $tag = 'DIV';
250
                    break;
251
                case 'DIVORCE_FILED':
252
                    $tag = 'DIVF';
253
                    break;
254
                case 'EDUCATION':
255
                    $tag = 'EDUC';
256
                    break;
257
                case 'EMIGRATION':
258
                    $tag = 'EMIG';
259
                    break;
260
                case 'ENDOWMENT':
261
                    $tag = 'ENDL';
262
                    break;
263
                case 'ENGAGEMENT':
264
                    $tag = 'ENGA';
265
                    break;
266
                case 'EVENT':
267
                    $tag = 'EVEN';
268
                    break;
269
                case 'FACSIMILE':
270
                    $tag = 'FAX';
271
                    break;
272
                case 'FAMILY':
273
                    $tag = 'FAM';
274
                    break;
275
                case 'FAMILY_CHILD':
276
                    $tag = 'FAMC';
277
                    break;
278
                case 'FAMILY_FILE':
279
                    $tag = 'FAMF';
280
                    break;
281
                case 'FAMILY_SPOUSE':
282
                    $tag = 'FAMS';
283
                    break;
284
                case 'FIRST_COMMUNION':
285
                    $tag = 'FCOM';
286
                    break;
287
                case '_FILE':
288
                    $tag = 'FILE';
289
                    break;
290
                case 'FORMAT':
291
                    $tag = 'FORM';
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment if this fall-through is intended.
Loading history...
292
                case 'FORM':
293
                    // Consistent commas
294
                    $data = preg_replace('/ *, */', ', ', $data);
295
                    break;
296
                case 'GEDCOM':
297
                    $tag = 'GEDC';
298
                    break;
299
                case 'GIVEN_NAME':
300
                    $tag = 'GIVN';
301
                    break;
302
                case 'GRADUATION':
303
                    $tag = 'GRAD';
304
                    break;
305
                case 'HEADER':
306
                    $tag = 'HEAD';
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment if this fall-through is intended.
Loading history...
307
                case 'HEAD':
308
                    // HEAD records don't have an XREF or DATA
309
                    if ($level == '0') {
310
                        $xref = '';
311
                        $data = '';
312
                    }
313
                    break;
314
                case 'HUSBAND':
315
                    $tag = 'HUSB';
316
                    break;
317
                case 'IDENT_NUMBER':
318
                    $tag = 'IDNO';
319
                    break;
320
                case 'IMMIGRATION':
321
                    $tag = 'IMMI';
322
                    break;
323
                case 'INDIVIDUAL':
324
                    $tag = 'INDI';
325
                    break;
326
                case 'LANGUAGE':
327
                    $tag = 'LANG';
328
                    break;
329
                case 'LATITUDE':
330
                    $tag = 'LATI';
331
                    break;
332
                case 'LONGITUDE':
333
                    $tag = 'LONG';
334
                    break;
335
                case 'MARRIAGE':
336
                    $tag = 'MARR';
337
                    break;
338
                case 'MARRIAGE_BANN':
339
                    $tag = 'MARB';
340
                    break;
341
                case 'MARRIAGE_COUNT':
342
                    $tag = 'NMR';
343
                    break;
344
                case 'MARRIAGE_CONTRACT':
345
                    $tag = 'MARC';
346
                    break;
347
                case 'MARRIAGE_LICENSE':
348
                    $tag = 'MARL';
349
                    break;
350
                case 'MARRIAGE_SETTLEMENT':
351
                    $tag = 'MARS';
352
                    break;
353
                case 'MEDIA':
354
                    $tag = 'MEDI';
355
                    break;
356
                case '_MEDICAL':
357
                    $tag = '_MDCL';
358
                    break;
359
                case '_MILITARY_SERVICE':
360
                    $tag = '_MILT';
361
                    break;
362
                case 'NAME':
363
                    // Tidy up whitespace
364
                    $data = preg_replace('/  +/', ' ', trim($data));
365
                    break;
366
                case 'NAME_PREFIX':
367
                    $tag = 'NPFX';
368
                    break;
369
                case 'NAME_SUFFIX':
370
                    $tag = 'NSFX';
371
                    break;
372
                case 'NATIONALITY':
373
                    $tag = 'NATI';
374
                    break;
375
                case 'NATURALIZATION':
376
                    $tag = 'NATU';
377
                    break;
378
                case 'NICKNAME':
379
                    $tag = 'NICK';
380
                    break;
381
                case 'OBJECT':
382
                    $tag = 'OBJE';
383
                    break;
384
                case 'OCCUPATION':
385
                    $tag = 'OCCU';
386
                    break;
387
                case 'ORDINANCE':
388
                    $tag = 'ORDI';
389
                    break;
390
                case 'ORDINATION':
391
                    $tag = 'ORDN';
392
                    break;
393
                case 'PEDIGREE':
394
                    $tag = 'PEDI';
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment if this fall-through is intended.
Loading history...
395
                case 'PEDI':
396
                    // PEDI values are lower case
397
                    $data = strtolower($data);
398
                    break;
399
                case 'PHONE':
400
                    $tag = 'PHON';
401
                    break;
402
                case 'PHONETIC':
403
                    $tag = 'FONE';
404
                    break;
405
                case 'PHY_DESCRIPTION':
406
                    $tag = 'DSCR';
407
                    break;
408
                case 'PLACE':
409
                    $tag = 'PLAC';
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment if this fall-through is intended.
Loading history...
410
                case 'PLAC':
411
                    // Consistent commas
412
                    $data = preg_replace('/ *(،|,) */', ', ', $data);
413
                    // The Master Genealogist stores LAT/LONG data in the PLAC field, e.g. Pennsylvania, USA, 395945N0751013W
414
                    if (preg_match('/(.*), (\d\d)(\d\d)(\d\d)([NS])(\d\d\d)(\d\d)(\d\d)([EW])$/', $data, $match)) {
415
                        $data =
416
                        $match[1] . "\n" .
417
                        ($level + 1) . " MAP\n" .
418
                        ($level + 2) . " LATI " . ($match[5] . (round($match[2] + ($match[3] / 60) + ($match[4] / 3600), 4))) . "\n" .
419
                        ($level + 2) . " LONG " . ($match[9] . (round($match[6] + ($match[7] / 60) + ($match[8] / 3600), 4)));
420
                    }
421
                    break;
422
                case 'POSTAL_CODE':
423
                    $tag = 'POST';
424
                    break;
425
                case 'PROBATE':
426
                    $tag = 'PROB';
427
                    break;
428
                case 'PROPERTY':
429
                    $tag = 'PROP';
430
                    break;
431
                case 'PUBLICATION':
432
                    $tag = 'PUBL';
433
                    break;
434
                case 'QUALITY_OF_DATA':
435
                    $tag = 'QUAL';
436
                    break;
437
                case 'REC_FILE_NUMBER':
438
                    $tag = 'RFN';
439
                    break;
440
                case 'REC_ID_NUMBER':
441
                    $tag = 'RIN';
442
                    break;
443
                case 'REFERENCE':
444
                    $tag = 'REFN';
445
                    break;
446
                case 'RELATIONSHIP':
447
                    $tag = 'RELA';
448
                    break;
449
                case 'RELIGION':
450
                    $tag = 'RELI';
451
                    break;
452
                case 'REPOSITORY':
453
                    $tag = 'REPO';
454
                    break;
455
                case 'RESIDENCE':
456
                    $tag = 'RESI';
457
                    break;
458
                case 'RESTRICTION':
459
                    $tag = 'RESN';
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment if this fall-through is intended.
Loading history...
460
                case 'RESN':
461
                    // RESN values are lower case (confidential, privacy, locked, none)
462
                    $data = strtolower($data);
463
                    if ($data == 'invisible') {
464
                        $data = 'confidential'; // From old versions of Legacy.
465
                    }
466
                    break;
467
                case 'RETIREMENT':
468
                    $tag = 'RETI';
469
                    break;
470
                case 'ROMANIZED':
471
                    $tag = 'ROMN';
472
                    break;
473
                case 'SEALING_CHILD':
474
                    $tag = 'SLGC';
475
                    break;
476
                case 'SEALING_SPOUSE':
477
                    $tag = 'SLGS';
478
                    break;
479
                case 'SOC_SEC_NUMBER':
480
                    $tag = 'SSN';
481
                    break;
482
                case 'SEX':
483
                    $data = strtoupper($data);
484
                    break;
485
                case 'SOURCE':
486
                    $tag = 'SOUR';
487
                    break;
488
                case 'STATE':
489
                    $tag = 'STAE';
490
                    break;
491
                case 'STATUS':
492
                    $tag = 'STAT';
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment if this fall-through is intended.
Loading history...
493
                case 'STAT':
494
                    if ($data == 'CANCELLED') {
495
                        // PhpGedView mis-spells this tag - correct it.
496
                        $data = 'CANCELED';
497
                    }
498
                    break;
499
                case 'SUBMISSION':
500
                    $tag = 'SUBN';
501
                    break;
502
                case 'SUBMITTER':
503
                    $tag = 'SUBM';
504
                    break;
505
                case 'SURNAME':
506
                    $tag = 'SURN';
507
                    break;
508
                case 'SURN_PREFIX':
509
                    $tag = 'SPFX';
510
                    break;
511
                case 'TEMPLE':
512
                    $tag = 'TEMP';
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment if this fall-through is intended.
Loading history...
513
                case 'TEMP':
514
                    // Temple codes are upper case
515
                    $data = strtoupper($data);
516
                    break;
517
                case 'TITLE':
518
                    $tag = 'TITL';
519
                    break;
520
                case 'TRAILER':
521
                    $tag = 'TRLR';
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment if this fall-through is intended.
Loading history...
522
                case 'TRLR':
523
                    // TRLR records don't have an XREF or DATA
524
                    if ($level == '0') {
525
                        $xref = '';
526
                        $data = '';
527
                    }
528
                    break;
529
                case 'VERSION':
530
                    $tag = 'VERS';
531
                    break;
532
                case 'WEB':
533
                    $tag = 'WWW';
534
                    break;
535
            }
536
            // Suppress "Y", for facts/events with a DATE or PLAC
537
            if ($data == 'y') {
538
                $data = 'Y';
539
            }
540
            if ($level == '1' && $data == 'Y') {
541
                for ($i = $n + 1; $i < $num_matches - 1 && $matches[$i][1] != '1'; ++$i) {
542
                    if ($matches[$i][3] == 'DATE' || $matches[$i][3] == 'PLAC') {
543
                        $data = '';
544
                        break;
545
                    }
546
                }
547
            }
548
            // Reassemble components back into a single line
549
            switch ($tag) {
550
                default:
551
                    // Remove tabs and multiple/leading/trailing spaces
552
                    if (strpos($data, "\t") !== false) {
553
                        $data = str_replace("\t", ' ', $data);
554
                    }
555
                    if (substr($data, 0, 1) == ' ' || substr($data, -1, 1) == ' ') {
556
                        $data = trim($data);
557
                    }
558
                    while (strpos($data, '  ')) {
559
                        $data = str_replace('  ', ' ', $data);
560
                    }
561
                    $newrec .= ($newrec ? "\n" : '') . $level . ' ' . ($level == '0' && $xref ? $xref . ' ' : '') . $tag . ($data === '' && $tag != "NOTE" ? '' : ' ' . $data);
562
                    break;
563
                case 'NOTE':
564
                case 'TEXT':
565
                case 'DATA':
566
                case 'CONT':
567
                    $newrec .= ($newrec ? "\n" : '') . $level . ' ' . ($level == '0' && $xref ? $xref . ' ' : '') . $tag . ($data === '' && $tag != "NOTE" ? '' : ' ' . $data);
568
                    break;
569
                case 'FILE':
570
                    // Strip off the user-defined path prefix
571
                    $GEDCOM_MEDIA_PATH = $tree->getPreference('GEDCOM_MEDIA_PATH');
572
                    if ($GEDCOM_MEDIA_PATH && strpos($data, $GEDCOM_MEDIA_PATH) === 0) {
573
                        $data = substr($data, strlen($GEDCOM_MEDIA_PATH));
574
                    }
575
                    // convert backslashes in filenames to forward slashes
576
                    $data = preg_replace("/\\\/", "/", $data);
577
578
                    $newrec .= ($newrec ? "\n" : '') . $level . ' ' . ($level == '0' && $xref ? $xref . ' ' : '') . $tag . ($data === '' && $tag != "NOTE" ? '' : ' ' . $data);
579
                    break;
580
                case 'CONC':
581
                    // Merge CONC lines, to simplify access later on.
582
                    $newrec .= ($tree->getPreference('WORD_WRAPPED_NOTES') ? ' ' : '') . $data;
583
                    break;
584
            }
585
        }
586
587
        return $newrec;
588
    }
589
590
    /**
591
     * import record into database
592
     *
593
     * this function will parse the given gedcom record and add it to the database
594
     *
595
     * @param string $gedrec the raw gedcom record to parse
596
     * @param Tree   $tree   import the record into this tree
597
     * @param bool   $update whether or not this is an updated record that has been accepted
598
     */
599
    public static function importRecord($gedrec, Tree $tree, $update)
600
    {
601
        $tree_id = $tree->getTreeId();
602
603
        // Escaped @ signs (only if importing from file)
604
        if (!$update) {
605
            $gedrec = str_replace('@@', '@', $gedrec);
606
        }
607
608
        // Standardise gedcom format
609
        $gedrec = self::reformatRecord($gedrec, $tree);
610
611
        // import different types of records
612
        if (preg_match('/^0 @(' . WT_REGEX_XREF . ')@ (' . WT_REGEX_TAG . ')/', $gedrec, $match)) {
613
            list(, $xref, $type) = $match;
614
            // check for a _UID, if the record doesn't have one, add one
615
            if ($tree->getPreference('GENERATE_UIDS') && !strpos($gedrec, "\n1 _UID ")) {
616
                $gedrec .= "\n1 _UID " . GedcomTag::createUid();
617
            }
618
        } elseif (preg_match('/0 (HEAD|TRLR)/', $gedrec, $match)) {
619
            $type = $match[1];
620
            $xref = $type; // For HEAD/TRLR, use type as pseudo XREF.
621
        } else {
622
            echo I18N::translate('Invalid GEDCOM format'), '<br><pre>', $gedrec, '</pre>';
623
624
            return;
625
        }
626
627
        // If the user has downloaded their GEDCOM data (containing media objects) and edited it
628
        // using an application which does not support (and deletes) media objects, then add them
629
        // back in.
630
        if ($tree->getPreference('keep_media') && $xref) {
631
            $old_linked_media =
632
                Database::prepare("SELECT l_to FROM `##link` WHERE l_from=? AND l_file=? AND l_type='OBJE'")
633
                    ->execute(array($xref, $tree_id))
634
                    ->fetchOneColumn();
635
            foreach ($old_linked_media as $media_id) {
636
                $gedrec .= "\n1 OBJE @" . $media_id . "@";
637
            }
638
        }
639
640
        switch ($type) {
641
            case 'INDI':
642
                // Convert inline media into media objects
643
                $gedrec = self::convertInlineMedia($tree, $gedrec);
644
645
                $record = new Individual($xref, $gedrec, null, $tree);
646
                if (preg_match('/\n1 RIN (.+)/', $gedrec, $match)) {
647
                    $rin = $match[1];
648
                } else {
649
                    $rin = $xref;
650
                }
651
                Database::prepare(
652
                "INSERT INTO `##individuals` (i_id, i_file, i_rin, i_sex, i_gedcom) VALUES (?, ?, ?, ?, ?)"
653
                )->execute(array(
654
                    $xref, $tree_id, $rin, $record->getSex(), $gedrec,
655
                ));
656
                // Update the cross-reference/index tables.
657
                self::updatePlaces($xref, $tree_id, $gedrec);
658
                self::updateDates($xref, $tree_id, $gedrec);
659
                self::updateLinks($xref, $tree_id, $gedrec);
660
                self::updateNames($xref, $tree_id, $record);
661
                break;
662
            case 'FAM':
663
                // Convert inline media into media objects
664
                $gedrec = self::convertInlineMedia($tree, $gedrec);
665
666
                if (preg_match('/\n1 HUSB @(' . WT_REGEX_XREF . ')@/', $gedrec, $match)) {
667
                    $husb = $match[1];
668
                } else {
669
                    $husb = '';
670
                }
671
                if (preg_match('/\n1 WIFE @(' . WT_REGEX_XREF . ')@/', $gedrec, $match)) {
672
                    $wife = $match[1];
673
                } else {
674
                    $wife = '';
675
                }
676
                $nchi = preg_match_all('/\n1 CHIL @(' . WT_REGEX_XREF . ')@/', $gedrec, $match);
677
                if (preg_match('/\n1 NCHI (\d+)/', $gedrec, $match)) {
678
                    $nchi = max($nchi, $match[1]);
679
                }
680
                Database::prepare(
681
                "INSERT INTO `##families` (f_id, f_file, f_husb, f_wife, f_gedcom, f_numchil) VALUES (?, ?, ?, ?, ?, ?)"
682
                )->execute(array(
683
                    $xref, $tree_id, $husb, $wife, $gedrec, $nchi,
684
                ));
685
                // Update the cross-reference/index tables.
686
                self::updatePlaces($xref, $tree_id, $gedrec);
687
                self::updateDates($xref, $tree_id, $gedrec);
688
                self::updateLinks($xref, $tree_id, $gedrec);
689
                break;
690
            case 'SOUR':
691
                // Convert inline media into media objects
692
                $gedrec = self::convertInlineMedia($tree, $gedrec);
693
694
                $record = new Source($xref, $gedrec, null, $tree);
695
                if (preg_match('/\n1 TITL (.+)/', $gedrec, $match)) {
696
                    $name = $match[1];
697
                } elseif (preg_match('/\n1 ABBR (.+)/', $gedrec, $match)) {
698
                    $name = $match[1];
699
                } else {
700
                    $name = $xref;
701
                }
702
                Database::prepare(
703
                "INSERT INTO `##sources` (s_id, s_file, s_name, s_gedcom) VALUES (?, ?, LEFT(?, 255), ?)"
704
                )->execute(array(
705
                    $xref, $tree_id, $name, $gedrec,
706
                ));
707
                // Update the cross-reference/index tables.
708
                self::updateLinks($xref, $tree_id, $gedrec);
709
                self::updateNames($xref, $tree_id, $record);
710
                break;
711
            case 'REPO':
712
                // Convert inline media into media objects
713
                $gedrec = self::convertInlineMedia($tree, $gedrec);
714
715
                $record = new Repository($xref, $gedrec, null, $tree);
716
                Database::prepare(
717
                "INSERT INTO `##other` (o_id, o_file, o_type, o_gedcom) VALUES (?, ?, 'REPO', ?)"
718
                )->execute(array(
719
                    $xref, $tree_id, $gedrec,
720
                ));
721
                // Update the cross-reference/index tables.
722
                self::updateLinks($xref, $tree_id, $gedrec);
723
                self::updateNames($xref, $tree_id, $record);
724
                break;
725
            case 'NOTE':
726
                $record = new Note($xref, $gedrec, null, $tree);
727
                Database::prepare(
728
                "INSERT INTO `##other` (o_id, o_file, o_type, o_gedcom) VALUES (?, ?, 'NOTE', ?)"
729
                )->execute(array(
730
                    $xref, $tree_id, $gedrec,
731
                ));
732
                // Update the cross-reference/index tables.
733
                self::updateLinks($xref, $tree_id, $gedrec);
734
                self::updateNames($xref, $tree_id, $record);
735
                break;
736
            case 'OBJE':
737
                $record = new Media($xref, $gedrec, null, $tree);
738
                Database::prepare(
739
                "INSERT INTO `##media` (m_id, m_ext, m_type, m_titl, m_filename, m_file, m_gedcom) VALUES (?, LEFT(?, 6), LEFT(?, 60), LEFT(?, 255), left(?, 512), ?, ?)"
740
                )->execute(array(
741
                    $xref, $record->extension(), $record->getMediaType(), $record->getTitle(), $record->getFilename(), $tree_id, $gedrec,
742
                ));
743
                // Update the cross-reference/index tables.
744
                self::updateLinks($xref, $tree_id, $gedrec);
745
                self::updateNames($xref, $tree_id, $record);
746
                break;
747
            default: // HEAD, TRLR, SUBM, SUBN, and custom record types.
748
                // Force HEAD records to have a creation date.
749
                if ($type === 'HEAD' && strpos($gedrec, "\n1 DATE ") === false) {
750
                    $gedrec .= "\n1 DATE " . date('j M Y');
751
                }
752
                Database::prepare(
753
                "INSERT INTO `##other` (o_id, o_file, o_type, o_gedcom) VALUES (?, ?, LEFT(?, 15), ?)"
754
                )->execute(array($xref, $tree_id, $type, $gedrec));
755
                // Update the cross-reference/index tables.
756
                self::updateLinks($xref, $tree_id, $gedrec);
757
                break;
758
        }
759
    }
760
761
    /**
762
     * extract all places from the given record and insert them into the places table
763
     *
764
     * @param string $gid
765
     * @param int    $ged_id
766
     * @param string $gedrec
767
     */
768
    public static function updatePlaces($gid, $ged_id, $gedrec)
769
    {
770
        global $placecache;
771
772
        if (!isset($placecache)) {
773
            $placecache = array();
774
        }
775
        $personplace = array();
776
        // import all place locations, but not control info such as
777
        // 0 HEAD/1 PLAC or 0 _EVDEF/1 PLAC
778
        $pt = preg_match_all("/^[2-9] PLAC (.+)/m", $gedrec, $match, PREG_SET_ORDER);
779
        for ($i = 0; $i < $pt; $i++) {
780
            $place    = trim($match[$i][1]);
781
            $lowplace = I18N::strtolower($place);
782
            //-- if we have already visited this place for this person then we don't need to again
783
            if (isset($personplace[$lowplace])) {
784
                continue;
785
            }
786
            $personplace[$lowplace] = 1;
787
            $places                 = explode(',', $place);
788
            //-- reverse the array to start at the highest level
789
            $secalp    = array_reverse($places);
790
            $parent_id = 0;
791
            $search    = true;
792
793
            foreach ($secalp as $place) {
794
                $place = trim($place);
795
                $key   = strtolower(mb_substr($place, 0, 150) . "_" . $parent_id);
796
                //-- if this place has already been added then we don't need to add it again
797
                if (isset($placecache[$key])) {
798
                    $parent_id = $placecache[$key];
799
                    if (!isset($personplace[$key])) {
800
                        $personplace[$key] = 1;
801
                        // Use INSERT IGNORE as a (temporary) fix for https://bugs.launchpad.net/webtrees/+bug/582226
802
                        // It ignores places that utf8_unicode_ci consider to be the same (i.e. accents).
803
                        // For example Québec and Quebec
804
                        // We need a better solution that attaches multiple names to single places
805
                        Database::prepare(
806
                            "INSERT IGNORE INTO `##placelinks` (pl_p_id, pl_gid, pl_file) VALUES (?, ?, ?)"
807
                        )->execute(array(
808
                            $parent_id, $gid, $ged_id,
809
                        ));
810
                    }
811
                    continue;
812
                }
813
814
                //-- only search the database while we are finding places in it
815
                if ($search) {
816
                    //-- check if this place and level has already been added
817
                    $tmp = Database::prepare(
818
                        "SELECT p_id FROM `##places` WHERE p_file = ? AND p_parent_id = ? AND p_place = LEFT(?, 150)"
819
                    )->execute(array(
820
                        $ged_id, $parent_id, $place,
821
                    ))->fetchOne();
822
                    if ($tmp) {
823
                        $p_id = $tmp;
824
                    } else {
825
                        $search = false;
826
                    }
827
                }
828
829
                //-- if we are not searching then we have to insert the place into the db
830
                if (!$search) {
831
                    $std_soundex = Soundex::russell($place);
832
                    $dm_soundex  = Soundex::daitchMokotoff($place);
833
                    Database::prepare(
834
                        "INSERT INTO `##places` (p_place, p_parent_id, p_file, p_std_soundex, p_dm_soundex) VALUES (LEFT(?, 150), ?, ?, ?, ?)"
835
                    )->execute(array(
836
                        $place, $parent_id, $ged_id, $std_soundex, $dm_soundex,
837
                    ));
838
                    $p_id = Database::getInstance()->lastInsertId();
839
                }
840
841
                Database::prepare(
842
                    "INSERT IGNORE INTO `##placelinks` (pl_p_id, pl_gid, pl_file) VALUES (?, ?, ?)"
843
                )->execute(array(
844
                    $p_id, $gid, $ged_id,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $p_id does not seem to be defined for all execution paths leading up to this point.
Loading history...
845
                ));
846
                //-- increment the level and assign the parent id for the next place level
847
                $parent_id         = $p_id;
848
                $placecache[$key]  = $p_id;
849
                $personplace[$key] = 1;
850
            }
851
        }
852
    }
853
854
    /**
855
     * Extract all the dates from the given record and insert them into the database.
856
     *
857
     * @param string $xref
858
     * @param int    $ged_id
859
     * @param string $gedrec
860
     */
861
    public static function updateDates($xref, $ged_id, $gedrec)
862
    {
863
        if (strpos($gedrec, '2 DATE ') && preg_match_all("/\n1 (\w+).*(?:\n[2-9].*)*(?:\n2 DATE (.+))(?:\n[2-9].*)*/", $gedrec, $matches, PREG_SET_ORDER)) {
864
            foreach ($matches as $match) {
865
                $fact = $match[1];
866
                if (($fact == 'FACT' || $fact == 'EVEN') && preg_match("/\n2 TYPE ([A-Z]{3,5})/", $match[0], $tmatch)) {
867
                    $fact = $tmatch[1];
868
                }
869
                $date = new Date($match[2]);
870
                Database::prepare(
871
                    "INSERT INTO `##dates` (d_day,d_month,d_mon,d_year,d_julianday1,d_julianday2,d_fact,d_gid,d_file,d_type) VALUES (?,?,?,?,?,?,?,?,?,?)"
872
                )->execute(array(
873
                    $date->minimumDate()->d,
874
                    $date->minimumDate()->format('%O'),
875
                    $date->minimumDate()->m,
876
                    $date->minimumDate()->y,
877
                    $date->minimumDate()->minJD,
878
                    $date->minimumDate()->maxJD,
879
                    $fact,
880
                    $xref,
881
                    $ged_id,
882
                    $date->minimumDate()->format('%@'),
883
                ));
884
                if ($date->minimumDate() !== $date->maximumDate()) {
885
                    Database::prepare(
886
                        "INSERT INTO `##dates` (d_day,d_month,d_mon,d_year,d_julianday1,d_julianday2,d_fact,d_gid,d_file,d_type) VALUES (?,?,?,?,?,?,?,?,?,?)"
887
                    )->execute(array(
888
                        $date->maximumDate()->d,
889
                        $date->maximumDate()->format('%O'),
890
                        $date->maximumDate()->m,
891
                        $date->maximumDate()->y,
892
                        $date->maximumDate()->minJD,
893
                        $date->maximumDate()->maxJD,
894
                        $fact,
895
                        $xref,
896
                        $ged_id,
897
                        $date->maximumDate()->format('%@'),
898
                    ));
899
                }
900
            }
901
        }
902
    }
903
904
    /**
905
     * Extract all the links from the given record and insert them into the database
906
     *
907
     * @param string $xref
908
     * @param int    $ged_id
909
     * @param string $gedrec
910
     */
911
    public static function updateLinks($xref, $ged_id, $gedrec)
912
    {
913
        if (preg_match_all('/^\d+ (' . WT_REGEX_TAG . ') @(' . WT_REGEX_XREF . ')@/m', $gedrec, $matches, PREG_SET_ORDER)) {
914
            $data = array();
915
            foreach ($matches as $match) {
916
                // Include each link once only.
917
                if (!in_array($match[1] . $match[2], $data)) {
918
                    $data[] = $match[1] . $match[2];
919
                    // Ignore any errors, which may be caused by "duplicates" that differ on case/collation, e.g. "S1" and "s1"
920
                    try {
921
                        Database::prepare(
922
                            "INSERT INTO `##link` (l_from, l_to, l_type, l_file) VALUES (?, ?, ?, ?)"
923
                        )->execute(array(
924
                            $xref, $match[2], $match[1], $ged_id,
925
                        ));
926
                    } catch (PDOException $e) {
927
                        // We could display a warning here....
928
                    }
929
                }
930
            }
931
        }
932
    }
933
934
    /**
935
     * Extract all the names from the given record and insert them into the database.
936
     *
937
     * @param string       $xref
938
     * @param int          $ged_id
939
     * @param GedcomRecord $record
940
     */
941
    public static function updateNames($xref, $ged_id, GedcomRecord $record)
942
    {
943
        foreach ($record->getAllNames() as $n => $name) {
944
            if ($record instanceof Individual) {
945
                if ($name['givn'] === '@P.N.') {
946
                    $soundex_givn_std = null;
947
                    $soundex_givn_dm  = null;
948
                } else {
949
                    $soundex_givn_std = Soundex::russell($name['givn']);
950
                    $soundex_givn_dm  = Soundex::daitchMokotoff($name['givn']);
951
                }
952
                if ($name['surn'] === '@N.N.') {
953
                    $soundex_surn_std = null;
954
                    $soundex_surn_dm  = null;
955
                } else {
956
                    $soundex_surn_std = Soundex::russell($name['surname']);
957
                    $soundex_surn_dm  = Soundex::daitchMokotoff($name['surname']);
958
                }
959
                Database::prepare(
960
                    "INSERT INTO `##name` (n_file,n_id,n_num,n_type,n_sort,n_full,n_surname,n_surn,n_givn,n_soundex_givn_std,n_soundex_surn_std,n_soundex_givn_dm,n_soundex_surn_dm) VALUES (?, ?, ?, ?, LEFT(?, 255), LEFT(?, 255), LEFT(?, 255), LEFT(?, 255), ?, ?, ?, ?, ?)"
961
                )->execute(array(
962
                    $ged_id, $xref, $n, $name['type'], $name['sort'], $name['fullNN'], $name['surname'], $name['surn'], $name['givn'], $soundex_givn_std, $soundex_surn_std, $soundex_givn_dm, $soundex_surn_dm,
963
                ));
964
            } else {
965
                Database::prepare(
966
                    "INSERT INTO `##name` (n_file,n_id,n_num,n_type,n_sort,n_full) VALUES (?, ?, ?, ?, LEFT(?, 255), LEFT(?, 255))"
967
                )->execute(array(
968
                    $ged_id, $xref, $n, $name['type'], $name['sort'], $name['fullNN'],
969
                ));
970
            }
971
        }
972
    }
973
974
    /**
975
     * Extract inline media data, and convert to media objects.
976
     *
977
     * @param Tree   $tree
978
     * @param string $gedrec
979
     *
980
     * @return string
981
     */
982
    public static function convertInlineMedia(Tree $tree, $gedrec)
983
    {
984
        while (preg_match('/\n1 OBJE(?:\n[2-9].+)+/', $gedrec, $match)) {
985
            $gedrec = str_replace($match[0], self::createMediaObject(1, $match[0], $tree), $gedrec);
986
        }
987
        while (preg_match('/\n2 OBJE(?:\n[3-9].+)+/', $gedrec, $match)) {
988
            $gedrec = str_replace($match[0], self::createMediaObject(2, $match[0], $tree), $gedrec);
989
        }
990
        while (preg_match('/\n3 OBJE(?:\n[4-9].+)+/', $gedrec, $match)) {
991
            $gedrec = str_replace($match[0], self::createMediaObject(3, $match[0], $tree), $gedrec);
992
        }
993
994
        return $gedrec;
995
    }
996
997
    /**
998
     * Create a new media object, from inline media data.
999
     *
1000
     * @param int    $level
1001
     * @param string $gedrec
1002
     * @param Tree   $tree
1003
     *
1004
     * @return string
1005
     */
1006
    public static function createMediaObject($level, $gedrec, Tree $tree)
1007
    {
1008
        if (preg_match('/\n\d FILE (.+)/', $gedrec, $file_match)) {
1009
            $file = $file_match[1];
1010
        } else {
1011
            $file = '';
1012
        }
1013
1014
        if (preg_match('/\n\d TITL (.+)/', $gedrec, $file_match)) {
1015
            $titl = $file_match[1];
1016
        } else {
1017
            $titl = '';
1018
        }
1019
1020
        // Have we already created a media object with the same title/filename?
1021
        $xref = Database::prepare(
1022
            "SELECT m_id FROM `##media` WHERE m_filename = ? AND m_titl = ? AND m_file = ?"
1023
        )->execute(array(
1024
            $file, $titl, $tree->getTreeId(),
1025
        ))->fetchOne();
1026
1027
        if (!$xref) {
1028
            $xref = $tree->getNewXref('OBJE');
1029
            // renumber the lines
1030
            $gedrec = preg_replace_callback('/\n(\d+)/', function ($m) use ($level) {
1031
                return "\n" . ($m[1] - $level);
1032
            }, $gedrec);
1033
            // convert to an object
1034
            $gedrec = str_replace("\n0 OBJE\n", '0 @' . $xref . "@ OBJE\n", $gedrec);
1035
            // Fix Legacy GEDCOMS
1036
            $gedrec = preg_replace('/\n1 FORM (.+)\n1 FILE (.+)\n1 TITL (.+)/', "\n1 FILE $2\n2 FORM $1\n2 TITL $3", $gedrec);
1037
            // Fix FTB GEDCOMS
1038
            $gedrec = preg_replace('/\n1 FORM (.+)\n1 TITL (.+)\n1 FILE (.+)/', "\n1 FILE $3\n2 FORM $1\n2 TITL $2", $gedrec);
1039
            // Create new record
1040
            $record = new Media($xref, $gedrec, null, $tree);
1041
            Database::prepare(
1042
                "INSERT INTO `##media` (m_id, m_ext, m_type, m_titl, m_filename, m_file, m_gedcom) VALUES (?, ?, ?, ?, ?, ?, ?)"
1043
            )->execute(array(
1044
                $xref, $record->extension(), $record->getMediaType(), $record->getTitle(), $record->getFilename(), $tree->getTreeId(), $gedrec,
1045
            ));
1046
        }
1047
1048
        return "\n" . $level . ' OBJE @' . $xref . '@';
1049
    }
1050
1051
    /**
1052
     * Accept all pending changes for a specified record.
1053
     *
1054
     * @param string $xref
1055
     * @param int    $ged_id
1056
     */
1057
    public static function acceptAllChanges($xref, $ged_id)
1058
    {
1059
        $changes = Database::prepare(
1060
            "SELECT change_id, gedcom_name, old_gedcom, new_gedcom" .
1061
            " FROM `##change` c" .
1062
            " JOIN `##gedcom` g USING (gedcom_id)" .
1063
            " WHERE c.status='pending' AND xref=? AND gedcom_id=?" .
1064
            " ORDER BY change_id"
1065
        )->execute(array($xref, $ged_id))->fetchAll();
1066
        foreach ($changes as $change) {
1067
            if (empty($change->new_gedcom)) {
1068
                // delete
1069
                self::updateRecord($change->old_gedcom, $ged_id, true);
1070
            } else {
1071
                // add/update
1072
                self::updateRecord($change->new_gedcom, $ged_id, false);
1073
            }
1074
            Database::prepare(
1075
                "UPDATE `##change`" .
1076
                " SET status='accepted'" .
1077
                " WHERE status='pending' AND xref=? AND gedcom_id=?"
1078
            )->execute(array($xref, $ged_id));
1079
            Log::addEditLog("Accepted change {$change->change_id} for {$xref} / {$change->gedcom_name} into database");
1080
        }
1081
    }
1082
1083
    /**
1084
     * Accept all pending changes for a specified record.
1085
     *
1086
     * @param GedcomRecord $record
1087
     */
1088
    public static function rejectAllChanges(GedcomRecord $record)
1089
    {
1090
        Database::prepare(
1091
            "UPDATE `##change`" .
1092
            " SET status = 'rejected'" .
1093
            " WHERE status = 'pending' AND xref = :xref AND gedcom_id = :tree_id"
1094
        )->execute(array(
1095
            'xref'    => $record->getXref(),
1096
            'tree_id' => $record->getTree()->getTreeId(),
1097
        ));
1098
    }
1099
1100
    /**
1101
     * update a record in the database
1102
     *
1103
     * @param string $gedrec
1104
     * @param int    $ged_id
1105
     * @param bool   $delete
1106
     */
1107
    public static function updateRecord($gedrec, $ged_id, $delete)
1108
    {
1109
        if (preg_match('/^0 @(' . WT_REGEX_XREF . ')@ (' . WT_REGEX_TAG . ')/', $gedrec, $match)) {
1110
            list(, $gid, $type) = $match;
1111
        } elseif (preg_match('/^0 (HEAD)(?:\n|$)/', $gedrec, $match)) {
1112
            // The HEAD record has no XREF.  Any others?
1113
            $gid  = $match[1];
1114
            $type = $match[1];
1115
        } else {
1116
            echo "ERROR: Invalid gedcom record.";
1117
1118
            return;
1119
        }
1120
1121
        // TODO deleting unlinked places can be done more efficiently in a single query
1122
        $placeids =
1123
            Database::prepare("SELECT pl_p_id FROM `##placelinks` WHERE pl_gid=? AND pl_file=?")
1124
                ->execute(array($gid, $ged_id))
1125
                ->fetchOneColumn();
1126
1127
        Database::prepare("DELETE FROM `##placelinks` WHERE pl_gid=? AND pl_file=?")->execute(array($gid, $ged_id));
1128
        Database::prepare("DELETE FROM `##dates`      WHERE d_gid =? AND d_file =?")->execute(array($gid, $ged_id));
1129
1130
        //-- delete any unlinked places
1131
        foreach ($placeids as $p_id) {
1132
            $num =
1133
                Database::prepare("SELECT count(pl_p_id) FROM `##placelinks` WHERE pl_p_id=? AND pl_file=?")
1134
                    ->execute(array($p_id, $ged_id))
1135
                    ->fetchOne();
1136
            if ($num == 0) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $num of type null|string to 0; this is ambiguous as not only 0 == 0 is true, but null == 0 is true, too. Consider using a strict comparison ===.
Loading history...
1137
                Database::prepare("DELETE FROM `##places` WHERE p_id=? AND p_file=?")->execute(array($p_id, $ged_id));
1138
            }
1139
        }
1140
1141
        Database::prepare("DELETE FROM `##name` WHERE n_id=? AND n_file=?")->execute(array($gid, $ged_id));
1142
        Database::prepare("DELETE FROM `##link` WHERE l_from=? AND l_file=?")->execute(array($gid, $ged_id));
1143
1144
        switch ($type) {
1145
            case 'INDI':
1146
                Database::prepare("DELETE FROM `##individuals` WHERE i_id=? AND i_file=?")->execute(array($gid, $ged_id));
1147
                break;
1148
            case 'FAM':
1149
                Database::prepare("DELETE FROM `##families` WHERE f_id=? AND f_file=?")->execute(array($gid, $ged_id));
1150
                break;
1151
            case 'SOUR':
1152
                Database::prepare("DELETE FROM `##sources` WHERE s_id=? AND s_file=?")->execute(array($gid, $ged_id));
1153
                break;
1154
            case 'OBJE':
1155
                Database::prepare("DELETE FROM `##media` WHERE m_id=? AND m_file=?")->execute(array($gid, $ged_id));
1156
                break;
1157
            default:
1158
                Database::prepare("DELETE FROM `##other` WHERE o_id=? AND o_file=?")->execute(array($gid, $ged_id));
1159
                break;
1160
        }
1161
1162
        if (!$delete) {
1163
            self::importRecord($gedrec, Tree::findById($ged_id), true);
1164
        }
1165
    }
1166
}
1167