Completed
Push — openstreetmap ( a371f7...06e980 )
by Greg
17:44 queued 07:41
created

FunctionsEdit::formControlMediaObject()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 6
nc 2
nop 3
dl 0
loc 10
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * webtrees: online genealogy
4
 * Copy§right (C) 2017 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\Auth;
19
use Fisharebest\Webtrees\Bootstrap4;
20
use Fisharebest\Webtrees\Census\Census;
21
use Fisharebest\Webtrees\Census\CensusOfCzechRepublic;
22
use Fisharebest\Webtrees\Census\CensusOfDenmark;
23
use Fisharebest\Webtrees\Census\CensusOfDeutschland;
24
use Fisharebest\Webtrees\Census\CensusOfEngland;
25
use Fisharebest\Webtrees\Census\CensusOfFrance;
26
use Fisharebest\Webtrees\Census\CensusOfScotland;
27
use Fisharebest\Webtrees\Census\CensusOfUnitedStates;
28
use Fisharebest\Webtrees\Census\CensusOfWales;
29
use Fisharebest\Webtrees\Config;
30
use Fisharebest\Webtrees\Date;
31
use Fisharebest\Webtrees\Fact;
32
use Fisharebest\Webtrees\Family;
33
use Fisharebest\Webtrees\Filter;
34
use Fisharebest\Webtrees\FontAwesome;
35
use Fisharebest\Webtrees\GedcomCode\GedcomCodeAdop;
36
use Fisharebest\Webtrees\GedcomCode\GedcomCodeName;
37
use Fisharebest\Webtrees\GedcomCode\GedcomCodePedi;
38
use Fisharebest\Webtrees\GedcomCode\GedcomCodeQuay;
39
use Fisharebest\Webtrees\GedcomCode\GedcomCodeRela;
40
use Fisharebest\Webtrees\GedcomCode\GedcomCodeStat;
41
use Fisharebest\Webtrees\GedcomCode\GedcomCodeTemp;
42
use Fisharebest\Webtrees\GedcomRecord;
43
use Fisharebest\Webtrees\GedcomTag;
44
use Fisharebest\Webtrees\Html;
45
use Fisharebest\Webtrees\I18N;
46
use Fisharebest\Webtrees\Individual;
47
use Fisharebest\Webtrees\Media;
48
use Fisharebest\Webtrees\Module;
49
use Fisharebest\Webtrees\Module\CensusAssistantModule;
50
use Fisharebest\Webtrees\Note;
51
use Fisharebest\Webtrees\Repository;
52
use Fisharebest\Webtrees\Select2;
53
use Fisharebest\Webtrees\Source;
54
use Fisharebest\Webtrees\Tree;
55
use Fisharebest\Webtrees\User;
56
use Ramsey\Uuid\Uuid;
57
58
/**
59
 * Class FunctionsEdit - common functions for editing
60
 */
61
class FunctionsEdit {
62
	/**
63
	 * Function edit_language_checkboxes
64
	 *
65
	 * @param string $parameter_name
66
	 * @param array $accepted_languages
67
	 *
68
	 * @return string
69
	 */
70
	public static function editLanguageCheckboxes($parameter_name, $accepted_languages) {
71
		$html = '';
72
		foreach (I18N::activeLocales() as $locale) {
73
			$html .= '<div class="form-check">';
74
			$html .= '<label title="' . $locale->languageTag() . '">';
75
			$html .= '<input type="checkbox" name="' . $parameter_name . '[]" value="' . $locale->languageTag() . '"';
76
			$html .= in_array($locale->languageTag(), $accepted_languages) ? ' checked>' : '>';
77
			$html .= $locale->endonym();
78
			$html .= '</label>';
79
			$html .= '</div>';
80
		}
81
82
		return $html;
83
	}
84
85
	/**
86
	 * A list of access levels (e.g. for an edit control).
87
	 *
88
	 * @return string[]
89
	 */
90
	public static function optionsAccessLevels() {
91
		return [
92
			Auth::PRIV_PRIVATE => I18N::translate('Show to visitors'),
93
			Auth::PRIV_USER    => I18N::translate('Show to members'),
94
			Auth::PRIV_NONE    => I18N::translate('Show to managers'),
95
			Auth::PRIV_HIDE    => I18N::translate('Hide from everyone'),
96
		];
97
	}
98
99
	/**
100
	 * A list of active languages (e.g. for an edit control).
101
	 *
102
	 * @return string[]
103
	 */
104
	public static function optionsActiveLanguages() {
105
		$languages = [];
106
		foreach (I18N::activeLocales() as $locale) {
107
			$languages[$locale->languageTag()] = $locale->endonym();
108
		}
109
110
		return $languages;
111
	}
112
113
	/**
114
	 * A list of calendar conversions (e.g. for an edit control).
115
	 *
116
	 * @return string[]
117
	 */
118
	public static function optionsCalendarConversions() {
119
		return ['none' => I18N::translate('No calendar conversion')] + Date::calendarNames();
120
	}
121
122
	/**
123
	 * A list of contact methods (e.g. for an edit control).
124
	 *
125
	 * @return string[]
126
	 */
127
	public static function optionsContactMethods() {
128
		return [
129
			'messaging'  => I18N::translate('Internal messaging'),
130
			'messaging2' => I18N::translate('Internal messaging with emails'),
131
			'messaging3' => I18N::translate('webtrees sends emails with no storage'),
132
			'mailto'     => I18N::translate('Mailto link'),
133
			'none'       => I18N::translate('No contact'),
134
		];
135
	}
136
137
	/**
138
	 * A list of hide/show options (e.g. for an edit control).
139
	 *
140
	 * @return string[]
141
	 */
142
	public static function optionsHideShow() {
143
		return [
144
			'0' => I18N::translate('no'),
145
			'1' => I18N::translate('yes'),
146
		];
147
	}
148
149
	/**
150
	 * A list of installed languages (e.g. for an edit control).
151
	 *
152
	 * @return string[]
153
	 */
154
	public static function optionsInstalledLanguages() {
155
		$languages = [];
156
		foreach (I18N::installedLocales() as $locale) {
157
			$languages[$locale->languageTag()] = $locale->endonym();
158
		}
159
160
		return $languages;
161
	}
162
163
	/**
164
	 * A list of integers (e.g. for an edit control).
165
	 *
166
	 * @param int[] $integers
167
	 *
168
	 * @return string[]
169
	 */
170
	public static function numericOptions($integers) {
171
		$array = [];
172
		foreach ($integers as $integer) {
173
			if ($integer === -1) {
174
				$array[$integer] = I18N::translate('All');
175
			} else {
176
				$array[$integer] = I18N::number($integer);
177
			}
178
		}
179
180
		return $array;
181
	}
182
183
	/**
184
	 * A list of no/yes options (e.g. for an edit control).
185
	 *
186
	 * @return string[]
187
	 */
188
	public static function optionsNoYes() {
189
		return [
190
			'0' => I18N::translate('no'),
191
			'1' => I18N::translate('yes'),
192
		];
193
	}
194
195
	/**
196
	 * A list of GEDCOM relationships (e.g. for an edit control).
197
	 *
198
	 * @param string $relationship
199
	 *
200
	 * @return string[]
201
	 */
202
	public static function optionsRelationships($relationship) {
203
		$relationships = GedcomCodeRela::getValues();
204
		// The user is allowed to specify values that aren't in the list.
205
		if (!array_key_exists($relationship, $relationships)) {
206
			$relationships[$relationship] = I18N::translate($relationship);
207
		}
208
209
		return $relationships;
210
	}
211
212
	/**
213
	 * A list of GEDCOM restrictions (e.g. for an edit control).
214
	 *
215
	 * @param bool $include_empty
216
	 *
217
	 * @return string[]
218
	 */
219
	public static function optionsRestrictions($include_empty) {
220
		$options = [
221
			'none'         => I18N::translate('Show to visitors'), // Not valid GEDCOM, but very useful
222
			'privacy'      => I18N::translate('Show to members'),
223
			'confidential' => I18N::translate('Show to managers'),
224
			'locked'       => I18N::translate('Only managers can edit'),
225
		];
226
227
		if ($include_empty) {
228
			$options = ['' => ''] + $options;
229
		}
230
231
		return $options;
232
	}
233
234
	/**
235
	 * A list mail transport options (e.g. for an edit control).
236
	 *
237
	 * @return string[]
238
	 */
239
	public static function optionsMailTransports() {
240
		return [
241
			'internal' => I18N::translate('Use PHP mail to send messages'),
242
			'external' => I18N::translate('Use SMTP to send messages'),
243
		];
244
	}
245
246
	/**
247
	 * A list of temple options (e.g. for an edit control).
248
	 *
249
	 * @return string[]
250
	 */
251
	public static function optionsTemples() {
252
		return ['' => I18N::translate('No temple - living ordinance')] + GedcomCodeTemp::templeNames();
253
	}
254
255
	/**
256
	 * A list of user options (e.g. for an edit control).
257
	 *
258
	 * @return string[]
259
	 */
260
	public static function optionsUsers() {
261
		$options = ['' => '-'];
262
263
		foreach (User::all() as $user) {
264
			$options[$user->getUserName()] = $user->getRealName() . ' - ' . $user->getUserName();
265
		}
266
267
		return $options;
268
	}
269
270
	/**
271
	 * Create a form control to select a family.
272
	 *
273
	 * @param Tree        $tree
274
	 * @param Family|null $family
275
	 * @param string[]    $attributes
276
	 *
277
	 * @return string
278
	 */
279
	public static function formControlFamily(Tree $tree, Family $family = null, array $attributes = []) {
280
		$value   = '';
281
		$options = ['' => ''];
282
283
		if ($family !== null) {
284
			$value   = $family->getXref();
285
			$options = [$value => view('selects/family', ['family' => $family])];
286
		}
287
288
		return Bootstrap4::select($options, $value, Select2::familyConfig($tree) + $attributes);
289
	}
290
291
	/**
292
	 * Create a form control to select a flag.
293
	 *
294
	 * @param string   $flag
295
	 * @param string[] $attributes
296
	 *
297
	 * @return string
298
	 */
299
	public static function formControlFlag($flag, array $attributes = []) {
300
		$value   = '';
301
		$options = ['' => ''];
302
303
		if ($flag !== '') {
304
			$value   = $flag;
305
			$options = [$value => Select2::flagValue($flag)];
306
		}
307
308
		return Bootstrap4::select($options, $value, Select2::flagConfig() + $attributes);
309
	}
310
311
	/**
312
	 * Create a form control to select an individual.
313
	 *
314
	 * @param Tree            $tree
315
	 * @param Individual|null $individual
316
	 * @param string[]        $attributes
317
	 *
318
	 * @return string
319
	 */
320
	public static function formControlIndividual(Tree $tree, Individual $individual = null, array $attributes = []) {
321
		$value   = '';
322
		$options = ['' => ''];
323
324
		if ($individual !== null) {
325
			$value   = $individual->getXref();
326
			$options = [$value => view('selects/individual', ['individual' => $individual])];
327
		}
328
329
		return Bootstrap4::select($options, $value, Select2::individualConfig($tree) + $attributes);
330
	}
331
332
	/**
333
	 * Create a form control to select a media object.
334
	 *
335
	 * @param Tree       $tree
336
	 * @param Media|null $media
337
	 * @param string[]   $attributes
338
	 *
339
	 * @return string
340
	 */
341
	public static function formControlMediaObject(Tree $tree, Media $media = null, array $attributes = []) {
342
		$value   = '';
343
		$options = ['' => ''];
344
345
		if ($media !== null) {
346
			$value   = $media->getXref();
347
			$options = [$value => view('selects/media', ['media' => $media])];
348
		}
349
350
		return Bootstrap4::select($options, $value, Select2::mediaObjectConfig($tree) + $attributes);
351
	}
352
353
	/**
354
	 * Create a form control to select a note.
355
	 *
356
	 * @param Tree          $tree
357
	 * @param Note|null     $note
358
	 * @param string[]|null $attributes
359
	 *
360
	 * @return string
361
	 */
362
	public static function formControlNote(Tree $tree, Note $note = null, array $attributes = []) {
363
		$value   = '';
364
		$options = ['' => ''];
365
366
		if ($note !== null) {
367
			$value   = $note->getXref();
368
			$options = [$value => view('selects/note', ['note' => $note])];
369
		}
370
371
		return Bootstrap4::select($options, $value, Select2::noteConfig($tree) + $attributes);
372
	}
373
374
	/**
375
	 * Create a form control to select a place.
376
	 *
377
	 * @param Tree     $tree
378
	 * @param string   $place
379
	 * @param string[] $attributes
380
	 *
381
	 * @return string
382
	 */
383
	public static function formControlPlace(Tree $tree, $place, array $attributes = []) {
384
		$value   = '';
385
		$options = ['' => ''];
386
387
		if ($place !== '') {
388
			$options = [$place => $place];
389
		}
390
391
		return Bootstrap4::select($options, $value, Select2::placeConfig($tree) + $attributes);
392
	}
393
394
	/**
395
	 * Create a form control to select a repository.
396
	 *
397
	 * @param Tree            $tree
398
	 * @param Repository|null $repository
399
	 * @param string[]        $attributes
400
	 *
401
	 * @return string
402
	 */
403
	public static function formControlRepository(Tree $tree, Repository $repository = null, array $attributes = []) {
404
		$value   = '';
405
		$options = ['' => ''];
406
407
		if ($repository !== null) {
408
			$value   = $repository->getXref();
409
			$options = [$value => view('selects/repository', ['repository' => $repository])];
410
		}
411
412
		return Bootstrap4::select($options, $value, Select2::repositoryConfig($tree) + $attributes);
413
	}
414
415
	/**
416
	 * Create a form control to select a source.
417
	 *
418
	 * @param Tree        $tree
419
	 * @param Source|null $source
420
	 * @param string[]    $attributes
421
	 *
422
	 * @return string
423
	 */
424
	public static function formControlSource(Tree $tree, Source $source = null, array $attributes = []) {
425
		$value   = '';
426
		$options = ['' => ''];
427
428
		if ($source !== null) {
429
			$value   = $source->getXref();
430
			$options = [$value => view('selects/source', ['source' => $source])];
431
		}
432
433
		return Bootstrap4::select($options, $value, Select2::sourceConfig($tree) + $attributes);
434
	}
435
436
	/**
437
	 * Create a form control to select a submitter.
438
	 *
439
	 * @param Tree              $tree
440
	 * @param GedcomRecord|null $submitter
441
	 * @param string[]          $attributes
442
	 *
443
	 * @return string
444
	 */
445
	public static function formControlSubmitter(Tree $tree, GedcomRecord $submitter = null, array $attributes = []) {
446
		$value   = '';
447
		$options = ['' => ''];
448
449
		if ($submitter !== null) {
450
			$value   = $submitter->getXref();
451
			$options = [$value => view('selects/submitter', ['submitter' => $submitter])];
452
		}
453
454
		return Bootstrap4::select($options, $value, Select2::submitterConfig($tree) + $attributes);
455
	}
456
457
	/**
458
	 * Remove all links from $gedrec to $xref, and any sub-tags.
459
	 *
460
	 * @param string $gedrec
461
	 * @param string $xref
462
	 *
463
	 * @return string
464
	 */
465
	public static function removeLinks($gedrec, $xref) {
466
		$gedrec = preg_replace('/\n1 ' . WT_REGEX_TAG . ' @' . $xref . '@(\n[2-9].*)*/', '', $gedrec);
467
		$gedrec = preg_replace('/\n2 ' . WT_REGEX_TAG . ' @' . $xref . '@(\n[3-9].*)*/', '', $gedrec);
468
		$gedrec = preg_replace('/\n3 ' . WT_REGEX_TAG . ' @' . $xref . '@(\n[4-9].*)*/', '', $gedrec);
469
		$gedrec = preg_replace('/\n4 ' . WT_REGEX_TAG . ' @' . $xref . '@(\n[5-9].*)*/', '', $gedrec);
470
		$gedrec = preg_replace('/\n5 ' . WT_REGEX_TAG . ' @' . $xref . '@(\n[6-9].*)*/', '', $gedrec);
471
472
		return $gedrec;
473
	}
474
475
	/**
476
	 * Input addon to generate a calendar widget.
477
	 *
478
	 * @param string $id
479
	 *
480
	 * @return string
481
	 */
482
	public static function inputAddonCalendar($id) {
483
		return
484
			'<div class="input-group-append">' .
485
			'<span class="input-group-text">' .
486
			FontAwesome::linkIcon('calendar', I18N::translate('Select a date'), ['href' => '#', 'onclick' => 'return calendarWidget("caldiv' . $id . '", "' . $id . '");']) .
487
			'</span>' .
488
			'</div>';
489
	}
490
491
	/**
492
	 * Input addon to select a special characterr using a virtual keyboard
493
	 *
494
	 * @param string $id
495
	 *
496
	 * @return string
497
	 */
498
	public static function inputAddonKeyboard($id) {
499
		return
500
			'<div class="input-group-append">' .
501
			'<span class="input-group-text">' .
502
			FontAwesome::linkIcon('keyboard', I18N::translate('Find a special character'), ['class' => 'wt-osk-trigger', 'href' => '#', 'data-id' => $id]) .
503
			'</span>' .
504
			'</div>';
505
	}
506
507
	/**
508
	 * Input addon to generate a help link.
509
	 *
510
	 * @param string $fact
511
	 *
512
	 * @return string
513
	 */
514
	public static function inputAddonHelp($fact) {
515
		return
516
			'<div class="input-group-append">' .
517
			'<span class="input-group-text">' .
518
			FunctionsPrint::helpLink($fact) .
519
			'</span>' .
520
			'</div>';
521
	}
522
523
	/**
524
	 * add a new tag input field
525
	 *
526
	 * called for each fact to be edited on a form.
527
	 * Fact level=0 means a new empty form : data are POSTed by name
528
	 * else data are POSTed using arrays :
529
	 * glevels[] : tag level
530
	 *  islink[] : tag is a link
531
	 *     tag[] : tag name
532
	 *    text[] : tag value
533
	 *
534
	 * @param string $tag fact record to edit (eg 2 DATE xxxxx)
535
	 * @param string $upperlevel optional upper level tag (eg BIRT)
536
	 * @param string $label An optional label to echo instead of the default
537
	 * @param string $extra optional text to display after the input field
538
	 * @param Individual $person For male/female translations
539
	 *
540
	 * @return string
541
	 */
542
	public static function addSimpleTag($tag, $upperlevel = '', $label = '', $extra = null, Individual $person = null) {
543
		global $tags, $xref, $bdm, $action, $WT_TREE;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
544
545
		// Some form fields need access to previous form fields.
546
		static $previous_ids = ['SOUR' => '', 'PLAC' => ''];
547
548
		preg_match('/^(?:(\d+) (' . WT_REGEX_TAG . ') ?(.*))/', $tag, $match);
549
		list(, $level, $fact, $value) = $match;
550
551
		if ($level === '0') {
552
			if ($upperlevel) {
553
				$name = $upperlevel . '_' . $fact;
554
			} else {
555
				$name = $fact;
556
			}
557
		} else {
558
			$name = 'text[]';
559
		}
560
561
		if ($level === '0') {
562
			$id = $fact;
563
		} else {
564
			$id = $fact . Uuid::uuid4();
0 ignored issues
show
Bug introduced by
Are you sure Ramsey\Uuid\Uuid::uuid4() of type Ramsey\Uuid\UuidInterface can be used in concatenation? Consider adding a __toString()-method. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

564
			$id = $fact . /** @scrutinizer ignore-type */ Uuid::uuid4();
Loading history...
565
		}
566
		if ($upperlevel) {
567
			$id = $upperlevel . '_' . $fact . Uuid::uuid4();
568
		}
569
570
		$previous_ids[$fact] = $id;
571
572
		// field value
573
		$islink = (substr($value, 0, 1) === '@' && substr($value, 0, 2) !== '@#');
574
		if ($islink) {
575
			$value = trim($value, '@');
576
		} else {
577
			$value = (string) substr($tag, strlen($fact) + 3);
578
		}
579
		if ($fact === 'REPO' || $fact === 'SOUR' || $fact === 'OBJE' || $fact === 'FAMC') {
580
			$islink = true;
581
		}
582
583
		if ($fact === 'SHARED_NOTE_EDIT' || $fact === 'SHARED_NOTE') {
584
			$islink = true;
585
			$fact   = 'NOTE';
586
		}
587
588
		$row_class = 'form-group row';
589
		switch ($fact) {
590
			case 'DATA':
591
			case 'MAP':
592
				// These GEDCOM tags should have no data, just child tags.
593
				if ($value === '') {
594
					$row_class .= ' d-none';
595
				}
596
				break;
597
			case 'LATI':
598
			case 'LONG':
599
				// Indicate that this row is a child of a previous row, so we can expand/collapse them.
600
				$row_class .= ' child_of_' . $previous_ids['PLAC'];
601
				if ($value === '') {
602
					$row_class .= ' collapse';
603
				}
604
				break;
605
		}
606
607
		$html = '';
608
		$html .= '<div class="' . $row_class . '">';
609
		$html .= '<label class="col-sm-3 col-form-label" for="' . $id . '">';
610
611
		// tag name
612
		if ($label) {
613
			$html .= $label;
614
		} elseif ($upperlevel) {
615
			$html .= GedcomTag::getLabel($upperlevel . ':' . $fact);
616
		} else {
617
			$html .= GedcomTag::getLabel($fact);
618
		}
619
620
		// If using GEDFact-assistant window
621
		if ($action === 'addnewnote_assisted') {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
622
			// Do not print on GEDFact Assistant window
623
		} else {
624
			// Not all facts have help text.
625
			switch ($fact) {
626
				case 'NAME':
627
					if ($upperlevel !== 'REPO' && $upperlevel !== 'UNKNOWN') {
628
						$html .= FunctionsPrint::helpLink($fact);
629
					}
630
					break;
631
				case 'ROMN':
632
				case 'SURN':
633
				case '_HEB':
634
					$html .= FunctionsPrint::helpLink($fact);
635
					break;
636
			}
637
		}
638
		// tag level
639
		if ($level !== '0') {
640
			$html .= '<input type="hidden" name="glevels[]" value="' . $level . '">';
641
			$html .= '<input type="hidden" name="islink[]" value="' . $islink . '">';
642
			$html .= '<input type="hidden" name="tag[]" value="' . $fact . '">';
643
		}
644
		$html .= '</label>';
645
646
		// value
647
		$html .= '<div class="col-sm-9">';
648
649
		// Show names for spouses in MARR/HUSB/AGE and MARR/WIFE/AGE
650
		if ($fact === 'HUSB' || $fact === 'WIFE') {
651
			$family = Family::getInstance($xref, $WT_TREE);
652
			if ($family) {
653
				$spouse_link = $family->getFirstFact($fact);
654
				if ($spouse_link) {
655
					$spouse = $spouse_link->getTarget();
656
					if ($spouse) {
657
						$html .= $spouse->getFullName();
658
					}
659
				}
660
			}
661
		}
662
663
		if (in_array($fact, Config::emptyFacts()) && ($value === '' || $value === 'Y' || $value === 'y')) {
664
			$html .= '<input type="hidden" id="' . $id . '" name="' . $name . '" value="' . $value . '">';
665
666
			if ($fact === 'CENS' && $value === 'Y') {
667
				$html .= self::censusDateSelector(WT_LOCALE, $xref);
668
669
				/** @var CensusAssistantModule $census_assistant */
670
				$census_assistant = Module::getModuleByName('GEDFact_assistant');
671
				$record           = Individual::getInstance($xref, $WT_TREE);
672
				if ($census_assistant !== null && $record instanceof Individual) {
673
					$html .= $census_assistant->createCensusAssistant($record);
674
				}
675
			}
676
		} elseif ($fact === 'NPFX' || $fact === 'NSFX' || $fact === 'SPFX' || $fact === 'NICK') {
677
			$html .= '<input class="form-control" type="text" id="' . $id . '" name="' . $name . '" value="' . e($value) . '" oninput="updatewholename()">';
678
		} elseif ($fact === 'GIVN') {
679
			$html .= '<input class="form-control" type="text" id="' . $id . '" name="' . $name . '" value="' . e($value) . '" data-autocomplete-type="GIVN" oninput="updatewholename()" autofocus>';
680
		} elseif ($fact === 'SURN' || $fact === '_MARNM_SURN') {
681
			$html .= '<input class="form-control" type="text" id="' . $id . '" name="' . $name . '" value="' . e($value) . '" data-autocomplete-type="SURN" oninput="updatewholename()">';
682
		} elseif ($fact === 'ADOP') {
683
			$html .= Bootstrap4::select(GedcomCodeAdop::getValues($person), $value, ['id' => $id, 'name' => $name]);
684
		} elseif ($fact === 'ALIA') {
685
			$html .= self::formControlIndividual($WT_TREE, Individual::getInstance($value, $WT_TREE), ['id' => $id, 'name' => $name]);
686
		} elseif ($fact === 'ASSO' || $fact === '_ASSO') {
687
			$html .=
688
				'<div class="input-group">' .
689
				'<span class="input-group-btn"><button class="btn btn-secondary" type="button" onclick="createNewRecord(' . $id . ')" title="' . I18N::translate('Create an individual') . '"><i class="fas fa-plus"></i></button></span>' .
690
				self::formControlIndividual($WT_TREE, Individual::getInstance($value, $WT_TREE), ['id' => $id, 'name' => $name]) .
691
				'</div>';
692
			if ($level === '1') {
693
				$html .= '<p class="small text-muted">' . I18N::translate('An associate is another individual who was involved with this individual, such as a friend or an employer.') . '</p>';
694
			} else {
695
				$html .= '<p class="small text-muted">' . I18N::translate('An associate is another individual who was involved with this fact or event, such as a witness or a priest.') . '</p>';
696
			}
697
		} elseif ($fact === 'DATE') {
698
			$html .= '<div class="input-group">';
699
			$html .= '<input class="form-control" type="text" id="' . $id . '" name="' . $name . '" value="' . e($value) . '" onchange="valid_date(this)" dir="ltr">';
700
			$html .= self::inputAddonCalendar($id);
701
			$html .= self::inputAddonHelp('DATE');
702
			$html .= '</div>';
703
			$html .= '<div id="caldiv' . $id . '" style="position:absolute;visibility:hidden;background-color:white;z-index:1000"></div>';
704
			$html .= '<p class="text-muted">' . (new Date($value))->display() . '</p>';
705
		} elseif ($fact === 'FAMC') {
706
			$html .=
707
				'<div class="input-group">' .
708
				'<span class="input-group-btn"><button class="btn btn-secondary" type="button" data-toggle="modal" data-target="#modal-create-family" data-element-id="' . $id . '" title="' . I18N::translate('Create a family') . '"><i class="fas fa-plus"></i></button></span>' .
709
				self::formControlFamily($WT_TREE, Family::getInstance($value, $WT_TREE), ['id' => $id, 'name' => $name]) .
710
				'</div>';
711
		} elseif ($fact === 'LATI') {
712
			$html .= '<input class="form-control" type="text" id="' . $id . '" name="' . $name . '" value="' . e($value) . '" oninput="valid_lati_long(this, \'N\', \'S\')">';
713
		} elseif ($fact === 'LONG') {
714
			$html .= '<input class="form-control" type="text" id="' . $id . '" name="' . $name . '" value="' . e($value) . '" oninput="valid_lati_long(this, \'E\', \'W\')">';
715
		} elseif ($fact === 'NOTE' && $islink) {
716
			$html .=
717
				'<div class="input-group">' .
718
				'<span class="input-group-btn">' .
719
				'<button class="btn btn-secondary" type="button" data-toggle="modal" data-target="#wt-ajax-modal" data-href="' . e(route('create-note-object', ['tree' => $WT_TREE->getName()])) . '" data-select-id="' . $id . '" title="' . I18N::translate('Create a shared note') . '">' .
720
				'<i class="fas fa-plus"></i><' .
721
				'/button>' .
722
				'</span>' .
723
				self::formControlNote($WT_TREE, Note::getInstance($value, $WT_TREE), ['id' => $id, 'name' => $name]) .
724
				'</div>';
725
		} elseif ($fact === 'OBJE') {
726
			$html .=
727
				'<div class="input-group">' .
728
				'<span class="input-group-btn"><button class="btn btn-secondary" type="button" data-toggle="modal" data-href="' . e(route('create-media-object', ['tree' => $WT_TREE->getName()])) . '" data-target="#wt-ajax-modal" data-select-id="' . $id . '" title="' . I18N::translate('Create a media object') . '"><i class="fas fa-plus"></i></button></span>' .
729
				self::formControlMediaObject($WT_TREE, Media::getInstance($value, $WT_TREE), ['id' => $id, 'name' => $name]) .
730
				'</div>';
731
		} elseif ($fact === 'PAGE') {
732
			$html .= '<input class="form-control" type="text" id="' . $id . '" name="' . $name . '" value="' . e($value) . '"   data-autocomplete-type="PAGE" data-autocomplete-extra="#' . $previous_ids['SOUR'] . '">';
733
		} elseif ($fact === 'PEDI') {
734
			$html .= Bootstrap4::select(GedcomCodePedi::getValues($person), $value, ['id' => $id, 'name' => $name]);
735
		} elseif ($fact === 'PLAC') {
736
			$html .= '<input ' . Html::attributes([
737
					'autocomplete'          => 'off',
738
					'class'                 => 'form-control',
739
					'id'                    => $id,
740
					'name'                  => $name,
741
					'value'                 => $value,
742
					'type'                  => 'text',
743
					'data-autocomplete-url' => route('autocomplete-place', [
744
						'ged'   => $WT_TREE->getName(),
745
						'query' => 'QUERY',
746
					]),
747
			]) . '>';
748
749
			/** @TODO - typeaheadjs.css doesn't work in an input-group */
750
			$html .= '<div class="input-group">';
751
			$html .= '<div class="input-group-append"><span class="input-group-text">' . FontAwesome::linkIcon('coordinates', I18N::translate('Latitude') . ' / ' . I18N::translate('Longitude'), ['data-toggle' => 'collapse', 'data-target' => '.child_of_' . $id]) . '</span></div>';
752
			$html .= self::inputAddonHelp('PLAC');
753
			$html .= '</div>';
754
			if (Module::getModuleByName('places_assistant')) {
755
				\PlacesAssistantModule::setup_place_subfields($id);
0 ignored issues
show
Bug introduced by
The type PlacesAssistantModule was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
756
				\PlacesAssistantModule::print_place_subfields($id);
757
			}
758
		} elseif ($fact === 'QUAY') {
759
			$html .= Bootstrap4::select(GedcomCodeQuay::getValues(), $value, ['id' => $id, 'name' => $name]);
760
		} elseif ($fact === 'RELA') {
761
			$html .= Bootstrap4::select(FunctionsEdit::optionsRelationships($value), $value, ['id' => $id, 'name' => $name]);
762
		} elseif ($fact === 'REPO') {
763
			$html .=
764
				'<div class="input-group">' .
765
				'<span class="input-group-btn"><button class="btn btn-secondary" type="button" data-toggle="modal" data-href="' . e(route('create-repository', ['tree' => $WT_TREE->getName()])) . '" data-target="#wt-ajax-modal" data-select-id="' . $id . '" title="' . I18N::translate('Create a repository') . '"><i class="fas fa-plus"></i></button></span>' .
766
				self::formControlRepository($WT_TREE, Repository::getInstance($value, $WT_TREE), ['id' => $id, 'name' => $name]) .
767
				'</div>';
768
		} elseif ($fact === 'RESN') {
769
			$html .= '<div class="input-group">';
770
			$html .= Bootstrap4::select(FunctionsEdit::optionsRestrictions(true), $value, ['id' => $id, 'name' => $name]);
771
			$html .= self::inputAddonHelp('RESN');
772
			$html .= '</span>';
773
			$html .= '</div>';
774
		} elseif ($fact === 'SEX') {
775
			if ($value !== 'M' && $value !== 'F') {
776
				$value = 'U';
777
			}
778
			$html .= Bootstrap4::radioButtons($name, ['M' => I18N::translate('Male'), 'F' => I18N::translate('Female'), 'U' => I18N::translateContext('unknown gender', 'Unknown')], $value, true);
779
		} elseif ($fact === 'SOUR') {
780
			$html .=
781
				'<div class="input-group">' .
782
				'<span class="input-group-btn"><button class="btn btn-secondary" type="button" data-toggle="modal" data-href="' . e(route('create-source', ['tree' => $WT_TREE->getName()])) . '" data-target="#wt-ajax-modal" data-select-id="' . $id . '" title="' . I18N::translate('Create a source') . '"><i class="fas fa-plus"></i></button></span>' .
783
				self::formControlSource($WT_TREE, Source::getInstance($value, $WT_TREE), ['id' => $id, 'name' => $name]) .
784
				'</div>';
785
		} elseif ($fact === 'STAT') {
786
			$html .= Bootstrap4::select(GedcomCodeStat::statusNames($upperlevel), $value);
787
		} elseif ($fact === 'SUBM') {
788
			$html .=
789
				'<div class="input-group">' .
790
				'<span class="input-group-btn"><button class="btn btn-secondary" type="button" data-toggle="modal" data-href="' . e(route('create-submitter', ['tree' => $WT_TREE->getName()])) . '" data-target="#wt-ajax-modal" data-select-id="' . $id . '" title="' . I18N::translate('Create a submitter') . '"><i class="fas fa-plus"></i></button></span>' .
791
				self::formControlSubmitter($WT_TREE, GedcomRecord::getInstance($value, $WT_TREE), ['id' => $id, 'name' => $name]) .
792
				'</div>';
793
		} elseif ($fact === 'TEMP') {
794
			$html .= Bootstrap4::select(FunctionsEdit::optionsTemples(), $value, ['id' => $id, 'name' => $name]);
795
		} elseif ($fact === 'TIME') {
796
			$html .= '<input class="form-control" type="text" id="' . $id . '" name="' . $name . '" value="' . e($value) . '" pattern="([0-1][0-9]|2[0-3]):[0-5][0-9](:[0-5][0-9])?" dir="ltr" placeholder="' . /* I18N: Examples of valid time formats (hours:minutes:seconds) */ I18N::translate('hh:mm or hh:mm:ss') . '">';
797
		} elseif ($fact === '_WT_USER') {
798
			$html .= Bootstrap4::select(FunctionsEdit::optionsUsers(), $value, ['id' => $id, 'name' => $name]);
799
		} elseif ($fact === '_PRIM') {
800
			$html .= Bootstrap4::select(['' => '', 'Y' => I18N::translate('always'), 'N' => I18N::translate('never')], $value, ['id' => $id, 'name' => $name]);
801
			$html .= '<p class="small text-muted">' . I18N::translate('Use this image for charts and on the individual’s page.') . '</p>';
802
		} elseif ($fact === 'TYPE' && $level === '3') {
803
			//-- Build the selector for the Media 'TYPE' Fact
804
			$html .= '<select name="text[]"><option selected value="" ></option>';
805
			$selectedValue = strtolower($value);
806
			if (!array_key_exists($selectedValue, GedcomTag::getFileFormTypes())) {
807
				$html .= '<option selected value="' . e($value) . '" >' . e($value) . '</option>';
808
			}
809
			foreach (['' => ''] + GedcomTag::getFileFormTypes() + [] as $typeName => $typeValue) {
810
				$html .= '<option value="' . $typeName . '" ';
811
				if ($selectedValue === $typeName) {
812
					$html .= 'selected';
813
				}
814
				$html .= '>' . $typeValue . '</option>';
815
			}
816
			$html .= '</select>';
817
		} elseif (($fact !== 'NAME' || $upperlevel === 'REPO' || $upperlevel === 'UNKNOWN') && $fact !== '_MARNM') {
818
			if ($fact === 'TEXT' || $fact === 'ADDR' || ($fact === 'NOTE' && !$islink)) {
819
				$html .= '<div class="input-group">';
820
				$html .= '<textarea class="form-control" id="' . $id . '" name="' . $name . '" dir="auto">' . e($value) . '</textarea>';
821
				$html .= '</div>';
822
			} else {
823
				// If using GEDFact-assistant window
824
				$html .= '<input class="form-control" type="text" id="' . $id . '" name="' . $name . '" value="' . e($value) . '">';
825
			}
826
		} else {
827
			// Populated in javascript from sub-tags
828
			$html .= '<input type="hidden" id="' . $id . '" name="' . $name . '" oninput="updateTextName(\'' . $id . '\')" value="' . e($value) . '" class="' . $fact . '">';
829
			$html .= '<span id="' . $id . '_display" dir="auto">' . e($value) . '</span>';
830
			$html .= ' <a href="#edit_name" onclick="convertHidden(\'' . $id . '\'); return false" class="icon-edit_indi" title="' . I18N::translate('Edit the name') . '"></a>';
831
		}
832
		// MARRiage TYPE : hide text field and show a selection list
833
		if ($fact === 'TYPE' && $level === '2' && $tags[0] === 'MARR') {
834
			$html .= '<script>';
835
			$html .= 'document.getElementById(\'' . $id . '\').style.display=\'none\'';
836
			$html .= '</script>';
837
			$html .= '<select id="' . $id . '_sel" oninput="document.getElementById(\'' . $id . '\').value=this.value" >';
838
			foreach (['Unknown', 'Civil', 'Religious', 'Partners'] as $key) {
839
				if ($key === 'Unknown') {
840
					$html .= '<option value="" ';
841
				} else {
842
					$html .= '<option value="' . $key . '" ';
843
				}
844
				$a = strtolower($key);
845
				$b = strtolower($value);
846
				if ($b !== '' && strpos($a, $b) !== false || strpos($b, $a) !== false) {
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: {currentAssign}, Probably Intended Meaning: {alternativeAssign}
Loading history...
847
					$html .= 'selected';
848
				}
849
				$html .= '>' . GedcomTag::getLabel('MARR_' . strtoupper($key)) . '</option>';
850
			}
851
			$html .= '</select>';
852
		} elseif ($fact === 'TYPE' && $level === '0') {
853
			// NAME TYPE : hide text field and show a selection list
854
			$html .= Bootstrap4::select(GedcomCodeName::getValues($person), $value, ['id' => $id, 'name' => $name, 'oninput' => 'document.getElementById(\'' . $id . '\').value=this.value"']);
855
			$html .= '<script>document.getElementById("' . $id . '").style.display="none";</script>';
856
		}
857
858
		// popup links
859
		switch ($fact) {
860
			case 'SOUR':
861
				//-- checkboxes to apply '1 SOUR' to BIRT/MARR/DEAT as '2 SOUR'
862
				if ($level === '1') {
863
					$html .= '<br>';
864
					switch ($WT_TREE->getPreference('PREFER_LEVEL2_SOURCES')) {
865
						case '2': // records
866
						$level1_checked = 'checked';
867
						$level2_checked = '';
868
						break;
869
						case '1': // facts
870
						$level1_checked = '';
871
						$level2_checked = 'checked';
872
						break;
873
						case '0': // none
874
						default:
875
						$level1_checked = '';
876
						$level2_checked = '';
877
						break;
878
					}
879
				if (strpos($bdm, 'B') !== false) {
880
					$html .= ' <label><input type="checkbox" name="SOUR_INDI" ' . $level1_checked . ' value="1">' . I18N::translate('Individual') . '</label>';
881
					if (preg_match_all('/(' . WT_REGEX_TAG . ')/', $WT_TREE->getPreference('QUICK_REQUIRED_FACTS'), $matches)) {
882
						foreach ($matches[1] as $match) {
883
							if (!in_array($match, explode('|', WT_EVENTS_DEAT))) {
884
								$html .= ' <label><input type="checkbox" name="SOUR_' . $match . '" ' . $level2_checked . ' value="1">' . GedcomTag::getLabel($match) . '</label>';
885
							}
886
						}
887
					}
888
				}
889
				if (strpos($bdm, 'D') !== false) {
890
					if (preg_match_all('/(' . WT_REGEX_TAG . ')/', $WT_TREE->getPreference('QUICK_REQUIRED_FACTS'), $matches)) {
891
						foreach ($matches[1] as $match) {
892
							if (in_array($match, explode('|', WT_EVENTS_DEAT))) {
893
								$html .= ' <label><input type="checkbox" name="SOUR_' . $match . '"' . $level2_checked . ' value="1">' . GedcomTag::getLabel($match) . '</label>';
894
							}
895
						}
896
					}
897
				}
898
				if (strpos($bdm, 'M') !== false) {
899
					$html .= ' <label><input type="checkbox" name="SOUR_FAM" ' . $level1_checked . ' value="1">' . I18N::translate('Family') . '</label>';
900
					if (preg_match_all('/(' . WT_REGEX_TAG . ')/', $WT_TREE->getPreference('QUICK_REQUIRED_FAMFACTS'), $matches)) {
901
						foreach ($matches[1] as $match) {
902
							$html .= ' <label><input type="checkbox" name="SOUR_' . $match . '"' . $level2_checked . ' value="1">' . GedcomTag::getLabel($match) . '</label>';
903
						}
904
					}
905
				}
906
			}
907
				break;
908
		}
909
910
		$html .= $extra . '</div></div>';
911
912
		return $html;
913
	}
914
915
	/**
916
	 * Genearate a <select> element, with the dates/places of all known censuses
917
	 *
918
	 *
919
	 * @param string $locale Sort the censuses for this locale
920
	 * @param string $xref   The individual for whom we are adding a census
921
	 *
922
	 * @return string
923
	 */
924
	public static function censusDateSelector($locale, $xref) {
925
		global $controller;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
926
927
		// Show more likely census details at the top of the list.
928
		switch ($locale) {
929
			case 'cs':
930
				$census_places = [new CensusOfCzechRepublic];
931
				break;
932
			case 'en-AU':
933
			case 'en-GB':
934
				$census_places = [new CensusOfEngland, new CensusOfWales, new CensusOfScotland];
935
				break;
936
			case 'en-US':
937
				$census_places = [new CensusOfUnitedStates];
938
				break;
939
			case 'fr':
940
			case 'fr-CA':
941
				$census_places = [new CensusOfFrance];
942
				break;
943
			case 'da':
944
				$census_places = [new CensusOfDenmark];
945
				break;
946
			case 'de':
947
				$census_places = [new CensusOfDeutschland];
948
				break;
949
			default:
950
				$census_places = [];
951
				break;
952
		}
953
		foreach (Census::allCensusPlaces() as $census_place) {
954
			if (!in_array($census_place, $census_places)) {
955
				$census_places[] = $census_place;
956
			}
957
		}
958
959
		$controller->addInlineJavascript('
960
				function selectCensus(el) {
961
					var option = $(":selected", el);
962
					$("input[id^=CENS_DATE]", $(el).closest("form")).val(option.val());
963
					//$("input[id^=CENS_PLAC]", $(el).closest("form")).val(option.data("place"));
964
					var place = option.data("place");
965
					$("select[id^=CENS_PLAC]", $(el).closest("form")).select2().empty().append(new Option(place, place)).val(place).trigger("change");
966
967
					$("input.census-class", $(el).closest("form")).val(option.data("census"));
968
				}
969
			');
970
971
		$options = '<option value="">' . I18N::translate('Census date') . '</option>';
972
973
		foreach ($census_places as $census_place) {
974
			$options .= '<option value=""></option>';
975
			foreach ($census_place->allCensusDates() as $census) {
976
				$date            = new Date($census->censusDate());
977
				$year            = $date->minimumDate()->format('%Y');
978
				$place_hierarchy = explode(', ', $census->censusPlace());
979
				$options .= '<option value="' . $census->censusDate() . '" data-place="' . $census->censusPlace() . '" data-census="' . get_class($census) . '">' . $place_hierarchy[0] . ' ' . $year . '</option>';
980
			}
981
		}
982
983
		return
984
			'<select id="census-selector" class="form-control" onchange="selectCensus(this)">' . $options . '</select>';
985
	}
986
987
	/**
988
	 * Prints collapsable fields to add ASSO/RELA, SOUR, OBJE, etc.
989
	 *
990
	 * @param string $tag
991
	 * @param int    $level
992
	 * @param string $parent_tag
993
	 */
994
	public static function printAddLayer($tag, $level = 2, $parent_tag = '') {
995
		global $WT_TREE;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
996
997
		switch ($tag) {
998
			case 'SOUR':
999
				echo view('cards/add-source-citation', [
1000
					'level'          => $level,
1001
					'full_citations' => $WT_TREE->getPreference('FULL_SOURCES'),
1002
				]);
1003
				break;
1004
1005
			case 'ASSO':
1006
			case 'ASSO2':
1007
				echo view('cards/add-associate', [
1008
					'level' => $level,
1009
				]);
1010
				break;
1011
1012
			case 'NOTE':
1013
				echo view('cards/add-note', [
1014
					'level' => $level,
1015
				]);
1016
				break;
1017
1018
			case 'SHARED_NOTE':
1019
				echo view('cards/add-shared-note', [
1020
					'level'      => $level,
1021
					'parent_tag' => $parent_tag,
1022
				]);
1023
				break;
1024
1025
			case 'OBJE':
1026
				if ($WT_TREE->getPreference('MEDIA_UPLOAD') >= Auth::accessLevel($WT_TREE)) {
1027
					echo view('cards/add-media-object', [
1028
						'level' => $level,
1029
					]);
1030
				}
1031
				break;
1032
1033
			case 'RESN':
1034
				echo view('cards/add-restriction', [
1035
					'level' => $level,
1036
				]);
1037
				break;
1038
		}
1039
	}
1040
1041
	/**
1042
	 * Add some empty tags to create a new fact.
1043
	 *
1044
	 * @param string $fact
1045
	 */
1046
	public static function addSimpleTags($fact) {
1047
		global $WT_TREE;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1048
1049
		// For new individuals, these facts default to "Y"
1050
		if ($fact === 'MARR') {
1051
			echo self::addSimpleTag('0 ' . $fact . ' Y');
1052
		} else {
1053
			echo self::addSimpleTag('0 ' . $fact);
1054
		}
1055
1056
		if (!in_array($fact, Config::nonDateFacts())) {
1057
			echo self::addSimpleTag('0 DATE', $fact, GedcomTag::getLabel($fact . ':DATE'));
1058
			echo self::addSimpleTag('0 RELI', $fact, GedcomTag::getLabel($fact . ':RELI'));
1059
		}
1060
1061
		if (!in_array($fact, Config::nonPlaceFacts())) {
1062
			echo self::addSimpleTag('0 PLAC', $fact, GedcomTag::getLabel($fact . ':PLAC'));
1063
1064
			if (preg_match_all('/(' . WT_REGEX_TAG . ')/', $WT_TREE->getPreference('ADVANCED_PLAC_FACTS'), $match)) {
1065
				foreach ($match[1] as $tag) {
1066
					echo self::addSimpleTag('0 ' . $tag, $fact, GedcomTag::getLabel($fact . ':PLAC:' . $tag));
1067
				}
1068
			}
1069
			echo self::addSimpleTag('0 MAP', $fact);
1070
			echo self::addSimpleTag('0 LATI', $fact);
1071
			echo self::addSimpleTag('0 LONG', $fact);
1072
		}
1073
	}
1074
1075
	/**
1076
	 * Assemble the pieces of a newly created record into gedcom
1077
	 *
1078
	 * @return string
1079
	 */
1080
	public static function addNewName() {
1081
		global $WT_TREE;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1082
1083
		$gedrec = "\n1 NAME " . Filter::post('NAME');
1084
1085
		$tags = ['NPFX', 'GIVN', 'SPFX', 'SURN', 'NSFX'];
1086
1087
		if (preg_match_all('/(' . WT_REGEX_TAG . ')/', $WT_TREE->getPreference('ADVANCED_NAME_FACTS'), $match)) {
1088
			$tags = array_merge($tags, $match[1]);
1089
		}
1090
1091
		// Paternal and Polish and Lithuanian surname traditions can also create a _MARNM
1092
		$SURNAME_TRADITION = $WT_TREE->getPreference('SURNAME_TRADITION');
1093
		if ($SURNAME_TRADITION === 'paternal' || $SURNAME_TRADITION === 'polish' || $SURNAME_TRADITION === 'lithuanian') {
1094
			$tags[] = '_MARNM';
1095
		}
1096
1097
		foreach (array_unique($tags) as $tag) {
1098
			$TAG = Filter::post($tag);
1099
			if ($TAG) {
1100
				$gedrec .= "\n2 {$tag} {$TAG}";
1101
			}
1102
		}
1103
1104
		return $gedrec;
1105
	}
1106
1107
	/**
1108
	 * Create a form to add a sex record.
1109
	 *
1110
	 * @return string
1111
	 */
1112
	public static function addNewSex() {
1113
		switch (Filter::post('SEX', '[MF]', 'U')) {
1114
			case 'M':
1115
				return "\n1 SEX M";
1116
			case 'F':
1117
				return "\n1 SEX F";
1118
			default:
1119
				return "\n1 SEX U";
1120
		}
1121
	}
1122
1123
	/**
1124
	 * Create a form to add a new fact.
1125
	 *
1126
	 * @param string $fact
1127
	 *
1128
	 * @return string
1129
	 */
1130
	public static function addNewFact($fact) {
1131
		global $WT_TREE;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1132
1133
		$FACT = Filter::post($fact);
1134
		$DATE = Filter::post($fact . '_DATE');
1135
		$PLAC = Filter::post($fact . '_PLAC');
0 ignored issues
show
Unused Code introduced by
The assignment to $PLAC is dead and can be removed.
Loading history...
1136
		$PLAC = Filter::post($fact . '_RELI');
1137
		if ($DATE || $PLAC || $FACT && $FACT !== 'Y') {
1138
			if ($FACT && $FACT !== 'Y') {
1139
				$gedrec = "\n1 " . $fact . ' ' . $FACT;
1140
			} else {
1141
				$gedrec = "\n1 " . $fact;
1142
			}
1143
			if ($DATE) {
1144
				$gedrec .= "\n2 DATE " . $DATE;
1145
			}
1146
			if ($PLAC) {
1147
				$gedrec .= "\n2 PLAC " . $PLAC;
1148
1149
				if (preg_match_all('/(' . WT_REGEX_TAG . ')/', $WT_TREE->getPreference('ADVANCED_PLAC_FACTS'), $match)) {
1150
					foreach ($match[1] as $tag) {
1151
						$TAG = Filter::post($fact . '_' . $tag);
1152
						if ($TAG) {
1153
							$gedrec .= "\n3 " . $tag . ' ' . $TAG;
1154
						}
1155
					}
1156
				}
1157
				$LATI = Filter::post($fact . '_LATI');
1158
				$LONG = Filter::post($fact . '_LONG');
1159
				if ($LATI || $LONG) {
1160
					$gedrec .= "\n3 MAP\n4 LATI " . $LATI . "\n4 LONG " . $LONG;
1161
				}
1162
			}
1163
			if (Filter::postBool('SOUR_' . $fact)) {
1164
				return self::updateSource($gedrec, 2);
1165
			} else {
1166
				return $gedrec;
1167
			}
1168
		} elseif ($FACT === 'Y') {
1169
			if (Filter::postBool('SOUR_' . $fact)) {
1170
				return self::updateSource("\n1 " . $fact . ' Y', 2);
1171
			} else {
1172
				return "\n1 " . $fact . ' Y';
1173
			}
1174
		} else {
1175
			return '';
1176
		}
1177
	}
1178
1179
	/**
1180
	 * This function splits the $glevels, $tag, $islink, and $text arrays so that the
1181
	 * entries associated with a SOUR record are separate from everything else.
1182
	 *
1183
	 * Input arrays:
1184
	 * - $glevels[] - an array of the gedcom level for each line that was edited
1185
	 * - $tag[] - an array of the tags for each gedcom line that was edited
1186
	 * - $islink[] - an array of 1 or 0 values to indicate when the text is a link element
1187
	 * - $text[] - an array of the text data for each line
1188
	 *
1189
	 * Output arrays:
1190
	 * ** For the SOUR record:
1191
	 * - $glevelsSOUR[] - an array of the gedcom level for each line that was edited
1192
	 * - $tagSOUR[] - an array of the tags for each gedcom line that was edited
1193
	 * - $islinkSOUR[] - an array of 1 or 0 values to indicate when the text is a link element
1194
	 * - $textSOUR[] - an array of the text data for each line
1195
	 * ** For the remaining records:
1196
	 * - $glevelsRest[] - an array of the gedcom level for each line that was edited
1197
	 * - $tagRest[] - an array of the tags for each gedcom line that was edited
1198
	 * - $islinkRest[] - an array of 1 or 0 values to indicate when the text is a link element
1199
	 * - $textRest[] - an array of the text data for each line
1200
	 */
1201
	public static function splitSource() {
1202
		global $glevels, $tag, $islink, $text;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1203
		global $glevelsSOUR, $tagSOUR, $islinkSOUR, $textSOUR;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1204
		global $glevelsRest, $tagRest, $islinkRest, $textRest;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1205
1206
		$glevelsSOUR = [];
1207
		$tagSOUR     = [];
1208
		$islinkSOUR  = [];
1209
		$textSOUR    = [];
1210
1211
		$glevelsRest = [];
1212
		$tagRest     = [];
1213
		$islinkRest  = [];
1214
		$textRest    = [];
1215
1216
		$inSOUR = false;
1217
1218
		for ($i = 0; $i < count($glevels); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
1219
			if ($inSOUR) {
1220
				if ($levelSOUR < $glevels[$i]) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $levelSOUR does not seem to be defined for all execution paths leading up to this point.
Loading history...
1221
					$dest = 'S';
1222
				} else {
1223
					$inSOUR = false;
1224
					$dest   = 'R';
1225
				}
1226
			} else {
1227
				if ($tag[$i] === 'SOUR') {
1228
					$inSOUR    = true;
1229
					$levelSOUR = $glevels[$i];
1230
					$dest      = 'S';
1231
				} else {
1232
					$dest = 'R';
1233
				}
1234
			}
1235
			if ($dest === 'S') {
1236
				$glevelsSOUR[] = $glevels[$i];
1237
				$tagSOUR[]     = $tag[$i];
1238
				$islinkSOUR[]  = $islink[$i];
1239
				$textSOUR[]    = $text[$i];
1240
			} else {
1241
				$glevelsRest[] = $glevels[$i];
1242
				$tagRest[]     = $tag[$i];
1243
				$islinkRest[]  = $islink[$i];
1244
				$textRest[]    = $text[$i];
1245
			}
1246
		}
1247
	}
1248
1249
	/**
1250
	 * Add new GEDCOM lines from the $xxxSOUR interface update arrays, which
1251
	 * were produced by the splitSOUR() function.
1252
	 * See the FunctionsEdit::handle_updatesges() function for details.
1253
	 *
1254
	 * @param string $inputRec
1255
	 * @param string $levelOverride
1256
	 *
1257
	 * @return string
1258
	 */
1259
	public static function updateSource($inputRec, $levelOverride = 'no') {
1260
		global $glevels, $tag, $islink, $text;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1261
		global $glevelsSOUR, $tagSOUR, $islinkSOUR, $textSOUR;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1262
1263
		if (count($tagSOUR) === 0) {
1264
			return $inputRec; // No update required
1265
		}
1266
1267
		// Save original interface update arrays before replacing them with the xxxSOUR ones
1268
		$glevelsSave = $glevels;
1269
		$tagSave     = $tag;
1270
		$islinkSave  = $islink;
1271
		$textSave    = $text;
1272
1273
		$glevels = $glevelsSOUR;
1274
		$tag     = $tagSOUR;
1275
		$islink  = $islinkSOUR;
1276
		$text    = $textSOUR;
1277
1278
		$myRecord = self::handleUpdates($inputRec, $levelOverride); // Now do the update
1279
1280
		// Restore the original interface update arrays (just in case ...)
1281
		$glevels = $glevelsSave;
1282
		$tag     = $tagSave;
1283
		$islink  = $islinkSave;
1284
		$text    = $textSave;
1285
1286
		return $myRecord;
1287
	}
1288
1289
	/**
1290
	 * Add new GEDCOM lines from the $xxxRest interface update arrays, which
1291
	 * were produced by the splitSOUR() function.
1292
	 * See the FunctionsEdit::handle_updatesges() function for details.
1293
	 *
1294
	 * @param string $inputRec
1295
	 * @param string $levelOverride
1296
	 *
1297
	 * @return string
1298
	 */
1299
	public static function updateRest($inputRec, $levelOverride = 'no') {
1300
		global $glevels, $tag, $islink, $text;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1301
		global $glevelsRest, $tagRest, $islinkRest, $textRest;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1302
1303
		if (count($tagRest) === 0) {
1304
			return $inputRec; // No update required
1305
		}
1306
1307
		// Save original interface update arrays before replacing them with the xxxRest ones
1308
		$glevelsSave = $glevels;
1309
		$tagSave     = $tag;
1310
		$islinkSave  = $islink;
1311
		$textSave    = $text;
1312
1313
		$glevels = $glevelsRest;
1314
		$tag     = $tagRest;
1315
		$islink  = $islinkRest;
1316
		$text    = $textRest;
1317
1318
		$myRecord = self::handleUpdates($inputRec, $levelOverride); // Now do the update
1319
1320
		// Restore the original interface update arrays (just in case ...)
1321
		$glevels = $glevelsSave;
1322
		$tag     = $tagSave;
1323
		$islink  = $islinkSave;
1324
		$text    = $textSave;
1325
1326
		return $myRecord;
1327
	}
1328
1329
	/**
1330
	 * Add new gedcom lines from interface update arrays
1331
	 * The edit_interface and FunctionsEdit::add_simple_tag function produce the following
1332
	 * arrays incoming from the $_POST form
1333
	 * - $glevels[] - an array of the gedcom level for each line that was edited
1334
	 * - $tag[] - an array of the tags for each gedcom line that was edited
1335
	 * - $islink[] - an array of 1 or 0 values to tell whether the text is a link element and should be surrounded by @@
1336
	 * - $text[] - an array of the text data for each line
1337
	 * With these arrays you can recreate the gedcom lines like this
1338
	 * <code>$glevel[0].' '.$tag[0].' '.$text[0]</code>
1339
	 * There will be an index in each of these arrays for each line of the gedcom
1340
	 * fact that is being edited.
1341
	 * If the $text[] array is empty for the given line, then it means that the
1342
	 * user removed that line during editing or that the line is supposed to be
1343
	 * empty (1 DEAT, 1 BIRT) for example. To know if the line should be removed
1344
	 * there is a section of code that looks ahead to the next lines to see if there
1345
	 * are sub lines. For example we don't want to remove the 1 DEAT line if it has
1346
	 * a 2 PLAC or 2 DATE line following it. If there are no sub lines, then the line
1347
	 * can be safely removed.
1348
	 *
1349
	 * @param string $newged the new gedcom record to add the lines to
1350
	 * @param string $levelOverride Override GEDCOM level specified in $glevels[0]
1351
	 *
1352
	 * @return string The updated gedcom record
1353
	 */
1354
	public static function handleUpdates($newged, $levelOverride = 'no') {
1355
		global $glevels, $islink, $tag, $uploaded_files, $text;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1356
1357
		if ($levelOverride === 'no' || count($glevels) === 0) {
1358
			$levelAdjust = 0;
1359
		} else {
1360
			$levelAdjust = $levelOverride - $glevels[0];
1361
		}
1362
1363
		for ($j = 0; $j < count($glevels); $j++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
1364
1365
			// Look for empty SOUR reference with non-empty sub-records.
1366
			// This can happen when the SOUR entry is deleted but its sub-records
1367
			// were incorrectly left intact.
1368
			// The sub-records should be deleted.
1369
			if ($tag[$j] === 'SOUR' && ($text[$j] === '@@' || $text[$j] === '')) {
1370
				$text[$j] = '';
1371
				$k        = $j + 1;
1372
				while (($k < count($glevels)) && ($glevels[$k] > $glevels[$j])) {
1373
					$text[$k] = '';
1374
					$k++;
1375
				}
1376
			}
1377
1378
			if (trim($text[$j]) !== '') {
1379
				$pass = true;
1380
			} else {
1381
				//-- for facts with empty values they must have sub records
1382
				//-- this section checks if they have subrecords
1383
				$k    = $j + 1;
1384
				$pass = false;
1385
				while (($k < count($glevels)) && ($glevels[$k] > $glevels[$j])) {
1386
					if ($text[$k] !== '') {
1387
						if (($tag[$j] !== 'OBJE') || ($tag[$k] === 'FILE')) {
1388
							$pass = true;
1389
							break;
1390
						}
1391
					}
1392
					if (($tag[$k] === 'FILE') && (count($uploaded_files) > 0)) {
1393
						$filename = array_shift($uploaded_files);
1394
						if (!empty($filename)) {
1395
							$text[$k] = $filename;
1396
							$pass     = true;
1397
							break;
1398
						}
1399
					}
1400
					$k++;
1401
				}
1402
			}
1403
1404
			//-- if the value is not empty or it has sub lines
1405
			//--- then write the line to the gedcom record
1406
			//-- we have to let some emtpy text lines pass through... (DEAT, BIRT, etc)
1407
			if ($pass) {
1408
				$newline = $glevels[$j] + $levelAdjust . ' ' . $tag[$j];
1409
				if ($text[$j] !== '') {
1410
					if ($islink[$j]) {
1411
						$newline .= ' @' . $text[$j] . '@';
1412
					} else {
1413
						$newline .= ' ' . $text[$j];
1414
					}
1415
				}
1416
				$newged .= "\n" . str_replace("\n", "\n" . (1 + substr($newline, 0, 1)) . ' CONT ', $newline);
1417
			}
1418
		}
1419
1420
		return $newged;
1421
	}
1422
1423
	/**
1424
	 * builds the form for adding new facts
1425
	 *
1426
	 * @param string $fact the new fact we are adding
1427
	 */
1428
	public static function createAddForm($fact) {
1429
		global $tags, $WT_TREE;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1430
1431
		$tags = [];
1432
1433
		// handle  MARRiage TYPE
1434
		if (substr($fact, 0, 5) === 'MARR_') {
1435
			$tags[0] = 'MARR';
1436
			echo self::addSimpleTag('1 MARR');
1437
			self::insertMissingSubtags($fact);
1438
		} else {
1439
			$tags[0] = $fact;
1440
			if ($fact === '_UID') {
1441
				$fact .= ' ' . GedcomTag::createUid();
1442
			}
1443
			// These new level 1 tags need to be turned into links
1444
			if (in_array($fact, ['ALIA', 'ASSO'])) {
1445
				$fact .= ' @';
1446
			}
1447
			if (in_array($fact, Config::emptyFacts())) {
1448
				echo self::addSimpleTag('1 ' . $fact . ' Y');
1449
			} else {
1450
				echo self::addSimpleTag('1 ' . $fact);
1451
			}
1452
			self::insertMissingSubtags($tags[0]);
1453
			//-- handle the special SOURce case for level 1 sources [ 1759246 ]
1454
			if ($fact === 'SOUR') {
1455
				echo self::addSimpleTag('2 PAGE');
1456
				echo self::addSimpleTag('3 TEXT');
1457
				if ($WT_TREE->getPreference('FULL_SOURCES')) {
1458
					echo self::addSimpleTag('3 DATE', '', GedcomTag::getLabel('DATA:DATE'));
1459
					echo self::addSimpleTag('2 QUAY');
1460
				}
1461
			}
1462
		}
1463
	}
1464
1465
	/**
1466
	 * Create a form to edit a Fact object.
1467
	 *
1468
	 * @param Fact $fact
1469
	 */
1470
	public static function createEditForm(Fact $fact) {
1471
		global $tags;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1472
1473
		$record = $fact->getParent();
1474
1475
		$tags     = [];
1476
		$gedlines = explode("\n", $fact->getGedcom());
1477
1478
		$linenum = 0;
1479
		$fields  = explode(' ', $gedlines[$linenum]);
1480
		$glevel  = $fields[0];
1481
		$level   = $glevel;
1482
1483
		$type       = $fact->getTag();
1484
		$level0type = $record::RECORD_TYPE;
1485
		$level1type = $type;
1486
1487
		$i           = $linenum;
1488
		$inSource    = false;
1489
		$levelSource = 0;
1490
		$add_date    = true;
1491
1492
		// List of tags we would expect at the next level
1493
		// NB insertMissingSubtags() already takes care of the simple cases
1494
		// where a level 1 tag is missing a level 2 tag. Here we only need to
1495
		// handle the more complicated cases.
1496
		$expected_subtags = [
1497
			'SOUR' => ['PAGE', 'DATA'],
1498
			'DATA' => ['TEXT'],
1499
			'PLAC' => ['MAP'],
1500
			'MAP'  => ['LATI', 'LONG'],
1501
		];
1502
		if ($record->getTree()->getPreference('FULL_SOURCES')) {
1503
			$expected_subtags['SOUR'][] = 'QUAY';
1504
			$expected_subtags['DATA'][] = 'DATE';
1505
		}
1506
		if (GedcomCodeTemp::isTagLDS($level1type)) {
1507
			$expected_subtags['STAT'] = ['DATE'];
1508
		}
1509
		if (in_array($level1type, Config::dateAndTime())) {
1510
			$expected_subtags['DATE'] = ['TIME']; // TIME is NOT a valid 5.5.1 tag
1511
		}
1512
		if (preg_match_all('/(' . WT_REGEX_TAG . ')/', $record->getTree()->getPreference('ADVANCED_PLAC_FACTS'), $match)) {
1513
			$expected_subtags['PLAC'] = array_merge($match[1], $expected_subtags['PLAC']);
1514
		}
1515
1516
		$stack = [];
1517
		// Loop on existing tags :
1518
		while (true) {
1519
			// Keep track of our hierarchy, e.g. 1=>BIRT, 2=>PLAC, 3=>FONE
1520
			$stack[$level] = $type;
1521
			// Merge them together, e.g. BIRT:PLAC:FONE
1522
			$label = implode(':', array_slice($stack, 0, $level));
0 ignored issues
show
Bug introduced by
$level of type string is incompatible with the type integer expected by parameter $length of array_slice(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1522
			$label = implode(':', array_slice($stack, 0, /** @scrutinizer ignore-type */ $level));
Loading history...
1523
1524
			$text = '';
1525
			for ($j = 2; $j < count($fields); $j++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
1526
				if ($j > 2) {
1527
					$text .= ' ';
1528
				}
1529
				$text .= $fields[$j];
1530
			}
1531
			$text = rtrim($text);
1532
			while (($i + 1 < count($gedlines)) && (preg_match('/' . ($level + 1) . ' CONT ?(.*)/', $gedlines[$i + 1], $cmatch) > 0)) {
1533
				$text .= "\n" . $cmatch[1];
1534
				$i++;
1535
			}
1536
1537
			if ($type === 'SOUR') {
1538
				$inSource    = true;
1539
				$levelSource = $level;
1540
			} elseif ($levelSource >= $level) {
1541
				$inSource = false;
1542
			}
1543
1544
			if ($type !== 'CONT') {
1545
				$tags[]    = $type;
1546
				$subrecord = $level . ' ' . $type . ' ' . $text;
1547
				if ($inSource && $type === 'DATE') {
1548
					echo self::addSimpleTag($subrecord, '', GedcomTag::getLabel($label, $record));
1549
				} elseif (!$inSource && $type === 'DATE') {
1550
					echo self::addSimpleTag($subrecord, $level1type, GedcomTag::getLabel($label, $record));
1551
					if ($level === '2') {
1552
						// We already have a date - no need to add one.
1553
						$add_date = false;
1554
					}
1555
				} elseif ($type === 'STAT') {
1556
					echo self::addSimpleTag($subrecord, $level1type, GedcomTag::getLabel($label, $record));
1557
				} else {
1558
					echo self::addSimpleTag($subrecord, $level0type, GedcomTag::getLabel($label, $record));
1559
				}
1560
			}
1561
1562
			// Get a list of tags present at the next level
1563
			$subtags = [];
1564
			for ($ii = $i + 1; isset($gedlines[$ii]) && preg_match('/^(\d+) (\S+)/', $gedlines[$ii], $mm) && $mm[1] > $level; ++$ii) {
1565
				if ($mm[1] == $level + 1) {
1566
					$subtags[] = $mm[2];
1567
				}
1568
			}
1569
1570
			// Insert missing tags
1571
			if (!empty($expected_subtags[$type])) {
1572
				foreach ($expected_subtags[$type] as $subtag) {
1573
					if (!in_array($subtag, $subtags)) {
1574
						echo self::addSimpleTag(($level + 1) . ' ' . $subtag, '', GedcomTag::getLabel($label . ':' . $subtag));
1575
						if (!empty($expected_subtags[$subtag])) {
1576
							foreach ($expected_subtags[$subtag] as $subsubtag) {
1577
								echo self::addSimpleTag(($level + 2) . ' ' . $subsubtag, '', GedcomTag::getLabel($label . ':' . $subtag . ':' . $subsubtag));
1578
							}
1579
						}
1580
					}
1581
				}
1582
			}
1583
1584
			$i++;
1585
			if (isset($gedlines[$i])) {
1586
				$fields = explode(' ', $gedlines[$i]);
1587
				$level  = $fields[0];
1588
				if (isset($fields[1])) {
1589
					$type = trim($fields[1]);
1590
				} else {
1591
					$level = 0;
1592
				}
1593
			} else {
1594
				$level = 0;
1595
			}
1596
			if ($level <= $glevel) {
1597
				break;
1598
			}
1599
		}
1600
1601
		if ($level1type !== '_PRIM') {
1602
			self::insertMissingSubtags($level1type, $add_date);
1603
		}
1604
	}
1605
1606
	/**
1607
	 * Populates the global $tags array with any missing sub-tags.
1608
	 *
1609
	 * @param string $level1tag the type of the level 1 gedcom record
1610
	 * @param bool $add_date
1611
	 */
1612
	public static function insertMissingSubtags($level1tag, $add_date = false) {
1613
		global $tags, $WT_TREE;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1614
1615
		// handle  MARRiage TYPE
1616
		$type_val = '';
1617
		if (substr($level1tag, 0, 5) === 'MARR_') {
1618
			$type_val  = substr($level1tag, 5);
1619
			$level1tag = 'MARR';
1620
		}
1621
1622
		foreach (Config::levelTwoTags() as $key => $value) {
1623
			if ($key === 'DATE' && in_array($level1tag, Config::nonDateFacts()) || $key === 'PLAC' && in_array($level1tag, Config::nonPlaceFacts())) {
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: {currentAssign}, Probably Intended Meaning: {alternativeAssign}
Loading history...
1624
				continue;
1625
			}
1626
			if (in_array($level1tag, $value) && !in_array($key, $tags)) {
1627
				if ($key === 'TYPE') {
1628
					echo self::addSimpleTag('2 TYPE ' . $type_val, $level1tag);
1629
				} elseif ($level1tag === '_TODO' && $key === 'DATE') {
1630
					echo self::addSimpleTag('2 ' . $key . ' ' . strtoupper(date('d M Y')), $level1tag);
1631
				} elseif ($level1tag === '_TODO' && $key === '_WT_USER') {
1632
					echo self::addSimpleTag('2 ' . $key . ' ' . Auth::user()->getUserName(), $level1tag);
1633
				} elseif ($level1tag === 'NAME' && strstr($WT_TREE->getPreference('ADVANCED_NAME_FACTS'), $key) !== false) {
1634
					echo self::addSimpleTag('2 ' . $key, $level1tag);
1635
				} elseif ($level1tag !== 'NAME') {
1636
					echo self::addSimpleTag('2 ' . $key, $level1tag);
1637
				}
1638
				// Add level 3/4 tags as appropriate
1639
				switch ($key) {
1640
					case 'PLAC':
1641
						if (preg_match_all('/(' . WT_REGEX_TAG . ')/', $WT_TREE->getPreference('ADVANCED_PLAC_FACTS'), $match)) {
1642
							foreach ($match[1] as $tag) {
1643
								echo self::addSimpleTag('3 ' . $tag, '', GedcomTag::getLabel($level1tag . ':PLAC:' . $tag));
1644
							}
1645
						}
1646
						echo self::addSimpleTag('3 MAP');
1647
						echo self::addSimpleTag('4 LATI');
1648
						echo self::addSimpleTag('4 LONG');
1649
						break;
1650
					case 'FILE':
1651
						echo self::addSimpleTag('3 FORM');
1652
						break;
1653
					case 'EVEN':
1654
						echo self::addSimpleTag('3 DATE');
1655
						echo self::addSimpleTag('3 PLAC');
1656
						break;
1657
					case 'STAT':
1658
						if (GedcomCodeTemp::isTagLDS($level1tag)) {
1659
							echo self::addSimpleTag('3 DATE', '', GedcomTag::getLabel('STAT:DATE'));
1660
						}
1661
						break;
1662
					case 'DATE':
1663
						// TIME is NOT a valid 5.5.1 tag
1664
						if (in_array($level1tag, Config::dateAndTime())) {
1665
							echo self::addSimpleTag('3 TIME');
1666
						}
1667
						break;
1668
					case 'HUSB':
1669
					case 'WIFE':
1670
						echo self::addSimpleTag('3 AGE');
1671
						break;
1672
					case 'FAMC':
1673
						if ($level1tag === 'ADOP') {
1674
							echo self::addSimpleTag('3 ADOP BOTH');
1675
						}
1676
						break;
1677
				}
1678
			} elseif ($key === 'DATE' && $add_date) {
1679
				echo self::addSimpleTag('2 DATE', $level1tag, GedcomTag::getLabel($level1tag . ':DATE'));
1680
			}
1681
		}
1682
		// Do something (anything!) with unrecognized custom tags
1683
		if (substr($level1tag, 0, 1) === '_' && $level1tag !== '_UID' && $level1tag !== '_PRIM' && $level1tag !== '_TODO') {
1684
			foreach (['DATE', 'PLAC', 'ADDR', 'AGNC', 'TYPE', 'AGE'] as $tag) {
1685
				if (!in_array($tag, $tags)) {
1686
					echo self::addSimpleTag('2 ' . $tag);
1687
					if ($tag === 'PLAC') {
1688
						if (preg_match_all('/(' . WT_REGEX_TAG . ')/', $WT_TREE->getPreference('ADVANCED_PLAC_FACTS'), $match)) {
1689
							foreach ($match[1] as $ptag) {
1690
								echo self::addSimpleTag('3 ' . $ptag, '', GedcomTag::getLabel($level1tag . ':PLAC:' . $ptag));
1691
							}
1692
						}
1693
						echo self::addSimpleTag('3 MAP');
1694
						echo self::addSimpleTag('4 LATI');
1695
						echo self::addSimpleTag('4 LONG');
1696
					}
1697
				}
1698
			}
1699
		}
1700
	}
1701
}
1702